[PATCH v11 4/5] elf: Allow RPATH/RUNPATH for static-pie (BZ 33326)

Adhemerval Zanella adhemerval.zanella@linaro.org
Tue Jul 28 21:13:22 GMT 2026


The initial static-pie support (commit 9d7a3741c9e59eba87fb) reused
ld.so ELF parsing logic, even though RPATH/RUNPATH should not appear
in the static-pie bootstrap.  With static PIE, RPATH/RUNPATH on the
loader typically indicates a toolchain misconfiguration.  However,
for static PIE, the presence of RPATH/RUNPATH has no impact because
these binaries do not use dynamic linking at runtime.

Fully static binaries have no dynamic section, so RPATH/RUNPATH can not
appear there at all; for static PIE the only consumer is dlopen.  If
static dlopen support is ever removed, this change becomes a no-op.

This change also simplifies elf_get_dynamic_info and removes a
difference between dynamic and static binaries, along with the now
unused STATIC_PIE_BOOTSTRAP.

Tested on aarch64-linux-gnu and x86_64-linux-gnu.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
---
 elf/Makefile                                  | 14 +++++++
 elf/dl-load.c                                 |  2 +-
 elf/dl-reloc-static-pie.c                     |  2 +-
 elf/get-dynamic-info.h                        |  7 +---
 elf/rtld.c                                    |  4 +-
 elf/setup-vdso.h                              |  2 +-
 elf/tst-pie-rpath-mod.c                       | 19 ++++++++++
 elf/tst-pie-rpath-static.c                    | 38 +++++++++++++++++++
 .../tst-pie-rpath-static.script               |  2 +
 9 files changed, 80 insertions(+), 10 deletions(-)
 create mode 100644 elf/tst-pie-rpath-mod.c
 create mode 100644 elf/tst-pie-rpath-static.c
 create mode 100644 elf/tst-pie-rpath-static.root/tst-pie-rpath-static.script

diff --git a/elf/Makefile b/elf/Makefile
index 9482bcbfca3..b27191a09e3 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1263,6 +1263,17 @@ LDFLAGS-tst-pie-address-static += \
   $(load-address-ldflag)=$(pde-load-address)
 endif
 endif
+ifeq (yes,$(enable-static-pie))
+tests-static += \
+  tst-pie-rpath-static \
+  # tests-static
+tests-container += \
+  tst-pie-rpath-static \
+  # tests-container
+modules-names += \
+  tst-pie-rpath-mod \
+  # modules-names
+endif
 ifeq (yes,$(have-protected-data))
 tests += vismain
 tests-pie += vismain
@@ -3746,3 +3757,6 @@ $(objpfx)tst-assert-startup-static.out: $(objpfx)tst-assert-startup-static
 	grep -q 'Fatal glibc error: tst-assert-startup-static' $@ \
 	  && grep -q '^status: 134$$' $@; \
 	  $(evaluate-test)
+
+LDFLAGS-tst-pie-rpath-static += -Wl,-rpath,\$$ORIGIN/tst-pie-rpath-static-subdir
+$(objpfx)tst-pie-rpath-static.out: $(objpfx)tst-pie-rpath-mod.so
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 95404adae94..586af946112 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1319,7 +1319,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   if (l->l_ld != NULL)
     l->l_ld = (ElfW(Dyn) *) ((ElfW(Addr)) l->l_ld + l->l_addr);
 
-  elf_get_dynamic_info (l, false, false);
+  elf_get_dynamic_info (l, false);
 
   /* Make sure we are not dlopen'ing an object that has the
      DF_1_NOOPEN flag set, or a PIE object.  */
diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
index 8463e46147d..ad47b9aef61 100644
--- a/elf/dl-reloc-static-pie.c
+++ b/elf/dl-reloc-static-pie.c
@@ -72,7 +72,7 @@ _dl_relocate_static_pie (void)
   /* Read our own dynamic section and fill in the info array.  */
   main_map->l_ld = ((void *) l_addr + elf_machine_dynamic ());
 
-  elf_get_dynamic_info (main_map, false, true);
+  elf_get_dynamic_info (main_map, false);
 
 # ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
   ELF_MACHINE_BEFORE_RTLD_RELOC (main_map, main_map->l_info);
diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index 46faa34acf4..7368cf454b7 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -26,8 +26,7 @@
 #include <libc-diag.h>
 
 static inline void __attribute__ ((unused, always_inline))
-elf_get_dynamic_info (struct link_map *l, bool bootstrap,
-		      bool static_pie_bootstrap)
+elf_get_dynamic_info (struct link_map *l, bool bootstrap)
 {
 #if __ELF_NATIVE_CLASS == 32
   typedef Elf32_Word d_tag_utype;
@@ -35,10 +34,8 @@ elf_get_dynamic_info (struct link_map *l, bool bootstrap,
   typedef Elf64_Xword d_tag_utype;
 #endif
 
-#ifndef STATIC_PIE_BOOTSTRAP
   if (!bootstrap && l->l_ld == NULL)
     return;
-#endif
 
   ElfW(Dyn) **info = l->l_info;
 
@@ -128,7 +125,7 @@ elf_get_dynamic_info (struct link_map *l, bool bootstrap,
 #endif
   if (info[DT_RELR] != NULL)
     assert (info[DT_RELRENT]->d_un.d_val == sizeof (ElfW(Relr)));
-  if (bootstrap || static_pie_bootstrap)
+  if (bootstrap)
     {
       assert (info[DT_RUNPATH] == NULL);
       assert (info[DT_RPATH] == NULL);
diff --git a/elf/rtld.c b/elf/rtld.c
index fc053df8586..9f668c9f6ed 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -545,7 +545,7 @@ _dl_start (void *arg)
   /* Read our own dynamic section and fill in the info array.  */
   bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic ();
   bootstrap_map.l_ld_readonly = DL_RO_DYN_SECTION;
-  elf_get_dynamic_info (&bootstrap_map, true, false);
+  elf_get_dynamic_info (&bootstrap_map, true);
 
 #if NO_TLS_OFFSET != 0
   bootstrap_map.l_tls_offset = NO_TLS_OFFSET;
@@ -1694,7 +1694,7 @@ dl_main (const ElfW(Phdr) *phdr,
   if (! rtld_is_main)
     {
       /* Extract the contents of the dynamic section for easy access.  */
-      elf_get_dynamic_info (main_map, false, false);
+      elf_get_dynamic_info (main_map, false);
 
       /* If the main map is libc.so, update the base namespace to
 	 refer to this map.  If libc.so is loaded later, this happens
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
index 0dba7072d53..58b77120468 100644
--- a/elf/setup-vdso.h
+++ b/elf/setup-vdso.h
@@ -64,7 +64,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
       l->l_addr = l->l_map_start - l->l_addr;
       l->l_map_end += l->l_addr;
       l->l_ld = (void *) ((ElfW(Addr)) l->l_ld + l->l_addr);
-      elf_get_dynamic_info (l, false, false);
+      elf_get_dynamic_info (l, false);
       _dl_setup_hash (l);
       l->l_relocated = 1;
 
diff --git a/elf/tst-pie-rpath-mod.c b/elf/tst-pie-rpath-mod.c
new file mode 100644
index 00000000000..719eb41e091
--- /dev/null
+++ b/elf/tst-pie-rpath-mod.c
@@ -0,0 +1,19 @@
+/* Check if RPATH/RUNPATH is allowed for static-pie.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+int foo (void) { return 42; }
diff --git a/elf/tst-pie-rpath-static.c b/elf/tst-pie-rpath-static.c
new file mode 100644
index 00000000000..bd445c1e035
--- /dev/null
+++ b/elf/tst-pie-rpath-static.c
@@ -0,0 +1,38 @@
+/* Check if RPATH/RUNPATH is allowed for static-pie.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+/* The module is placed in the directory named by the RUNPATH of this
+   program (tst-pie-rpath-static.root/tst-pie-rpath-static.script), so
+   dlopen can only find it if the RUNPATH is honoured.  */
+#define LIBNAME "tst-pie-rpath-mod.so"
+
+static int
+do_test (void)
+{
+  void *h = xdlopen (LIBNAME, RTLD_NOW);
+  int (*foo)(void) = xdlsym (h, "foo");
+  TEST_COMPARE (foo (), 42);
+  xdlclose (h);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-pie-rpath-static.root/tst-pie-rpath-static.script b/elf/tst-pie-rpath-static.root/tst-pie-rpath-static.script
new file mode 100644
index 00000000000..943045a20f4
--- /dev/null
+++ b/elf/tst-pie-rpath-static.root/tst-pie-rpath-static.script
@@ -0,0 +1,2 @@
+mkdirp 0755 $B/elf/tst-pie-rpath-static-subdir
+cp $B/elf/tst-pie-rpath-mod.so $B/elf/tst-pie-rpath-static-subdir/tst-pie-rpath-mod.so
-- 
2.53.0



More information about the Libc-alpha mailing list