[PATCH v2 2/2] elf: Use the effective lazy mode for the deferred IRELATIVE pass

Adhemerval Zanella <[email protected]> Mon, 3 Aug 2026 11:02:54 -0300
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <[email protected]>
The 63b31c05a8a split relocation processing must agree for the lazy
flag, a mistamatch would change the .rel.plt handling.

This is not an issue for any port currently, but on hppa it may return
a different value: if hppa implements IFUNC support, the second pass would
route PLT entries to its empty lazy handler and leave the descriptors
unrelocated, silently.

Make ELF_DYNAMIC_RELOCATE_PASS take lazy as an int lvalue and store the
effective mode back into it, so the DL_RELOC_IRELATIVE call reuses the
same variable instead of a separately threaded copy.  The two passes can no
longer disagree about the partitioning.  elf_machine_runtime_setup has side
effects, so it must stay a single call.

Checked on x86_64-linux-gnu, and built for all supported architectures.
---
 elf/dl-reloc-static-pie.c | 12 +++++++++---
 elf/dl-reloc.c            |  5 +++--
 elf/dynamic-link.h        | 34 ++++++++++++++++++++++------------
 3 files changed, 34 insertions(+), 17 deletions(-)

diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
index 5dc5a545a8d..a3d984f6020 100644
--- a/elf/dl-reloc-static-pie.c
+++ b/elf/dl-reloc-static-pie.c
@@ -79,8 +79,12 @@ _dl_relocate_static_pie (void)
 # endif
 
   /* Relocate ourselves so we can do normal function calls and data access
-     using the global offset table.  IRELATIVE entries are deferred.  */
-  ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_NORMAL, main_map, NULL, 0, 0, 0);
+     using the global offset table.  IRELATIVE entries are deferred.
+     Lazy binding is never used here, so edr_lazy comes back as 0 and the
+     deferred pass can hardcode the same value.  */
+  int edr_lazy = 0;
+  ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_NORMAL, main_map, NULL, edr_lazy,
+			     0, 0);
 
   /* Initialize _r_debug_extended.  */
   struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE);
@@ -98,7 +102,9 @@ void
 _dl_relocate_static_pie_ifunc (void)
 {
   struct link_map *main_map = _dl_get_dl_main_map ();
-  ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_IRELATIVE, main_map, NULL, 0, 0, 0);
+  int edr_lazy = 0;
+  ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_IRELATIVE, main_map, NULL, edr_lazy,
+			     0, 0);
   main_map->l_relocated = 1;
 }
 #endif
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index fa2f41ac445..bed0c4f844a 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -278,7 +278,8 @@ _dl_relocate_object_no_relro (struct link_map *l, struct r_scope_elem *scope[],
        IFUNC resolvers.  Without this, a resolver would see the unrelocated
        initialiser bytes that were placed into the slot by the early
        _dl_allocate_tls_init.  */
-    ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_NORMAL, l, scope, lazy,
+    int edr_lazy = lazy;
+    ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_NORMAL, l, scope, edr_lazy,
 			       consider_profiling, skip_ifunc);
 
 #ifdef SHARED
@@ -292,7 +293,7 @@ _dl_relocate_object_no_relro (struct link_map *l, struct r_scope_elem *scope[],
       _dl_init_static_tls (l);
 #endif
 
-    ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_IRELATIVE, l, scope, lazy,
+    ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_IRELATIVE, l, scope, edr_lazy,
 			       0, skip_ifunc);
 
     if ((consider_profiling || consider_symbind)
diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
index 0130c63feb1..81a3f0856e0 100644
--- a/elf/dynamic-link.h
+++ b/elf/dynamic-link.h
@@ -235,29 +235,39 @@ enum elf_dynamic_reloc_pass
    processed.  It is orthogonal to SKIP_IFUNC, which suppresses running IFUNC
    resolvers in whichever pass is selected.
 
-   Unless PASS is DL_RELOC_IRELATIVE, this also performs the machine-specific
-   PLT/GOT setup, the DT_RELR relocations, and the ELF_DYNAMIC_AFTER_RELOC
-   hook.  */
+   Unless PASS is DL_RELOC_IRELATIVE, this also performs the
+   machine-specific PLT/GOT setup, the DT_RELR relocations, and the
+   ELF_DYNAMIC_AFTER_RELOC hook.
+
+   LAZY must be an int lvalue.  elf_machine_runtime_setup may downgrade the
+   requested mode (currently only hppa, when it cannot install the lazy
+   trampoline) and it has side effects, so it must run exactly once (the
+   effective mode is stored back into LAZY).  A later DL_RELOC_IRELATIVE
+   call must be handed that same lvalue, so that both passes partition the
+   relocation ranges identically.  */
 # define ELF_DYNAMIC_RELOCATE_PASS(pass, map, scope, lazy, consider_profile,  \
 				   skip_ifunc)				      \
   do {									      \
-    int edr_lazy = (lazy);						      \
     if ((pass) != DL_RELOC_IRELATIVE)					      \
       {									      \
-	edr_lazy = elf_machine_runtime_setup ((map), (scope), (lazy),	      \
-					      (consider_profile));	      \
+	(lazy) = elf_machine_runtime_setup ((map), (scope), (lazy),	      \
+					    (consider_profile));	      \
 	if (!is_rtld_link_map (map) || DO_RTLD_BOOTSTRAP)		      \
 	  ELF_DYNAMIC_DO_RELR (map);					      \
       }									      \
-    ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc, (pass));	      \
-    ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc, (pass));	      \
+    ELF_DYNAMIC_DO_REL ((map), (scope), (lazy), skip_ifunc, (pass));	      \
+    ELF_DYNAMIC_DO_RELA ((map), (scope), (lazy), skip_ifunc, (pass));	      \
     if ((pass) != DL_RELOC_IRELATIVE)					      \
-      ELF_DYNAMIC_AFTER_RELOC ((map), edr_lazy);			      \
+      ELF_DYNAMIC_AFTER_RELOC ((map), (lazy));				      \
   } while (0)
 
-/* Run both passes back to back, for callers with nothing to interleave.  */
+/* Run both passes back to back, for callers with nothing to interleave.
+   Unlike ELF_DYNAMIC_RELOCATE_PASS, LAZY need not be an lvalue.  */
 # define ELF_DYNAMIC_RELOCATE(map, scope, lazy, consider_profile, skip_ifunc) \
-  ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_ALL, (map), (scope), (lazy),	      \
-			     (consider_profile), skip_ifunc)
+  do {									      \
+    int edr_lazy = (lazy);						      \
+    ELF_DYNAMIC_RELOCATE_PASS (DL_RELOC_ALL, (map), (scope), edr_lazy,	      \
+			       (consider_profile), skip_ifunc);		      \
+  } while (0)
 
 #endif
-- 
2.53.0