]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
elf: Defer all IRELATIVE relocations until after PLT setup
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Thu, 14 May 2026 14:27:08 +0000 (11:27 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Mon, 18 May 2026 16:31:00 +0000 (13:31 -0300)
When a shared library is built with -z lazy and its IFUNC resolver calls
a PLT function, the dynamic linker can crash.  The resolver runs while
the PLT stubs still hold their raw ELF virtual addresses — l_addr has
not yet been added — so the call branches to an unmapped address.

The old code deferred IRELATIVE entries only to the end of the relocation
range currently being processed (via the r2/end2 scan-ahead mechanism in
elf_dynamic_do_Rel).  This was sufficient only when both IRELATIVE and the
JMP_SLOT entries for the PLT functions it needs are in the same section.
On x86-64, aarch64, arm, i386 and most other targets, a file-scope
initialiser of the form

  int (*fptr)(void) = some_ifunc;

causes the linker to place R_*_IRELATIVE in .rela.dyn, while JMP_SLOT
entries for any PLT calls made by the resolver live in .rela.plt.
Processing .rela.dyn before .rela.plt means the resolver fires before the
PLT is usable, regardless of where within .rela.dyn IRELATIVE appears.

Fix this by splitting IRELATIVE processing into a separate, explicitly
deferred pass.  In elf/do-rel.h:

 - Remove the r2/end2 variables and the post-loop IRELATIVE re-scan from
   elf_dynamic_do_Rel.  IRELATIVE entries are now always skipped in the
   non-bootstrap path.

 - Add a new elf_dynamic_do_Rel_irelative function that scans a
   relocation range and calls elf_machine_rel/elf_machine_lazy_rel for
   IRELATIVE and ifunc relocations.

In elf/dynamic-link.h, update _ELF_DYNAMIC_DO_RELOC to use a two-phase
approach for non-bootstrap builds unconditionally (regardless of whether
ranges[1].size is zero):

 Phase 1+2: elf_dynamic_do_Rel over .rela.dyn then .rela.plt — processes
            everything except IRELATIVE/STT_GNU_IFUNC.
 Phase 3+4: elf_dynamic_do_Rel_irelative over .rela.dyn then .rela.plt —
            processes only IRELATIVE, by which point all PLT stubs are
            valid.

This guarantees that IRELATIVE resolvers can call PLT stubs safely
regardless of which section the linker placed R_*_IRELATIVE in.

Add ELF_MACHINE_IRELATIVE to the architectures that were missing it so
the new skip logic in elf_dynamic_do_Rel is compiled for all targets.

This patch addresses the binutils BZ 13302 [1] from the glibc side, and
also fixes the mold-reported issue [2], which shows that IFUNC relocation
placement and processing can work differently across ABIs.

I checked on all ABIs that support IFUNC (x86_64, i686, aarch64, arm,
loongarch, powerpc, riscv, s390, and sparc), some via qemu-system.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=13302
[2] https://github.com/rui314/mold/issues/1550

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
17 files changed:
elf/Makefile
elf/do-rel.h
elf/dynamic-link.h
elf/tst-ifunc-plt-bindnow.c [new file with mode: 0644]
elf/tst-ifunc-plt-dep.c [new file with mode: 0644]
elf/tst-ifunc-plt-dlopen-bindnow.c [new file with mode: 0644]
elf/tst-ifunc-plt-dlopen.c [new file with mode: 0644]
elf/tst-ifunc-plt-lib.c [new file with mode: 0644]
elf/tst-ifunc-plt.c [new file with mode: 0644]
sysdeps/aarch64/dl-machine.h
sysdeps/arm/dl-machine.h
sysdeps/i386/dl-machine.h
sysdeps/powerpc/powerpc32/dl-machine.h
sysdeps/powerpc/powerpc64/dl-machine.h
sysdeps/riscv/dl-machine.h
sysdeps/sparc/sparc32/dl-machine.h
sysdeps/sparc/sparc64/dl-machine.h

index f4d22c159914f6a5cbeb8615e09919febc47b449..0f888887be734289cd5164450014232e1097893c 100644 (file)
@@ -1255,6 +1255,10 @@ ifeq (yes,$(build-shared))
 tests += \
   tst-ifunc-fault-bindnow \
   tst-ifunc-fault-lazy \
+  tst-ifunc-plt \
+  tst-ifunc-plt-bindnow \
+  tst-ifunc-plt-dlopen \
+  tst-ifunc-plt-dlopen-bindnow \
   # tests
 # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
 tests-internal += \
@@ -1317,6 +1321,8 @@ modules-names += \
   ifuncmod1 \
   ifuncmod3 \
   ifuncmod6 \
+  tst-ifunc-plt-dep \
+  tst-ifunc-plt-lib \
   # modules-names
 ifeq (no,$(with-lld))
 modules-names += ifuncmod5
@@ -1796,6 +1802,7 @@ unload4mod1.so-no-z-defs = yes
 ifuncmod1.so-no-z-defs = yes
 ifuncmod5.so-no-z-defs = yes
 ifuncmod6.so-no-z-defs = yes
+tst-ifunc-plt-lib.so-no-z-defs = yes
 tst-auditmod9a.so-no-z-defs = yes
 tst-auditmod9b.so-no-z-defs = yes
 tst-nodelete-uniquemod.so-no-z-defs = yes
@@ -2429,6 +2436,19 @@ $(objpfx)tst-ifunc-fault-bindnow.out: $(objpfx)tst-ifunc-fault-bindnow \
    $(objpfx)ld.so
        $(tst-ifunc-fault-script)
 
+LDFLAGS-tst-ifunc-plt-lib.so = -Wl,-z,lazy
+
+tst-ifunc-plt-bindnow-ENV = LD_BIND_NOW=1
+tst-ifunc-plt-dlopen-bindnow-ENV = LD_BIND_NOW=1
+
+$(objpfx)tst-ifunc-plt-lib.so: $(objpfx)tst-ifunc-plt-dep.so
+$(objpfx)tst-ifunc-plt: $(objpfx)tst-ifunc-plt-lib.so
+$(objpfx)tst-ifunc-plt-bindnow: $(objpfx)tst-ifunc-plt-lib.so
+$(objpfx)tst-ifunc-plt-dlopen.out: \
+  $(objpfx)tst-ifunc-plt-lib.so $(objpfx)tst-ifunc-plt-dep.so
+$(objpfx)tst-ifunc-plt-dlopen-bindnow.out: \
+  $(objpfx)tst-ifunc-plt-lib.so $(objpfx)tst-ifunc-plt-dep.so
+
 $(objpfx)tst-unique1.out: $(objpfx)tst-unique1mod1.so \
                          $(objpfx)tst-unique1mod2.so
 
index d00ffab7e96154456cf43ee9572ceea949cdc89f..c7a8be368f02c4a93c47cdbf03ac0e31ea8fef00 100644 (file)
@@ -23,6 +23,9 @@
 
 #ifdef DO_RELA
 # define elf_dynamic_do_Rel            elf_dynamic_do_Rela
+# define elf_dynamic_do_Rel_irelative  elf_dynamic_do_Rela_irelative
+# define elf_dynamic_is_Rel_irelative  elf_dynamic_is_Rela_irelative
+# define elf_dynamic_Rel_audit_symbind elf_dynamic_Rela_audit_symbind
 # define Rel                           Rela
 # define elf_machine_rel               elf_machine_rela
 # define elf_machine_rel_relative      elf_machine_rela_relative
                            (void *) (l_addr + relative->r_offset))
 #endif
 
+static __always_inline bool
+elf_dynamic_is_Rel_irelative (const ElfW(Rel) *reloc, const ElfW(Sym) *sym)
+{
+#ifdef ELF_MACHINE_IRELATIVE
+  const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
+  return ((sym != NULL
+          && ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC
+          && sym->st_shndx != SHN_UNDEF)
+         || r_type == ELF_MACHINE_IRELATIVE);
+#else
+  return false;
+#endif
+}
+
+static __always_inline void
+elf_dynamic_Rel_audit_symbind (struct link_map *map,
+                              struct r_scope_elem *scope[],
+                              const ElfW(Rel) *reloc, const ElfW(Sym) *sym,
+                              const struct r_found_version *rversion,
+                              void *r_addr_arg)
+{
+#if defined SHARED
+  if (ELFW(R_TYPE) (reloc->r_info) == ELF_MACHINE_JMP_SLOT
+      && GLRO(dl_naudit) > 0)
+    {
+      struct link_map *sym_map
+       = RESOLVE_MAP (map, scope, &sym, rversion, ELF_MACHINE_JMP_SLOT);
+      if (sym != NULL)
+       _dl_audit_symbind (map, NULL, reloc, sym, r_addr_arg, sym_map, false);
+    }
+#endif
+}
+
 /* Perform the relocations in MAP on the running program image as specified
    by RELTAG, SZTAG.  If LAZY is nonzero, this is the first pass on PLT
    relocations; they should be set up to call _dl_runtime_resolve, rather
-   than fully resolved now.  */
+   than fully resolved now.
+
+   IRELATIVE entries are always skipped (non-bootstrap); they are handled
+   separately by elf_dynamic_do_Rel_irelative after all other relocations
+   for both .rel.dyn and .rel.plt have been processed.  */
 
 static inline void __attribute__ ((always_inline))
 elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
                    ElfW(Addr) reladdr, ElfW(Addr) relsize,
                    __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative,
-                   int lazy, int skip_ifunc)
+                   int lazy)
 {
   const ElfW(Rel) *relative = (const void *) reladdr;
   const ElfW(Rel) *r = relative + nrelative;
@@ -65,14 +105,9 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
       void *const r_addr_arg = (void *) (l_addr + r->r_offset);
       const struct r_found_version *rversion = &map->l_versions[ndx];
 
-      elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, skip_ifunc);
+      elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
     }
 #else /* !RTLD_BOOTSTRAP */
-# if defined ELF_MACHINE_IRELATIVE
-  const ElfW(Rel) *r2 = NULL;
-  const ElfW(Rel) *end2 = NULL;
-# endif
-
 #if !defined DO_RELA || !defined ELF_MACHINE_PLT_REL
   /* We never bind lazily during ld.so bootstrap.  Unfortunately gcc is
      not clever enough to see through all the function calls to realize
@@ -81,23 +116,12 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
     {
       /* Doing lazy PLT relocations; they need very little info.  */
       for (; r < end; ++r)
-# ifdef ELF_MACHINE_IRELATIVE
-       if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
-         {
-           if (r2 == NULL)
-             r2 = r;
-           end2 = r;
-         }
-       else
-# endif
-         elf_machine_lazy_rel (map, scope, l_addr, r, skip_ifunc);
-
-# ifdef ELF_MACHINE_IRELATIVE
-      if (r2 != NULL)
-       for (; r2 <= end2; ++r2)
-         if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
-           elf_machine_lazy_rel (map, scope, l_addr, r2, skip_ifunc);
-# endif
+       {
+         const ElfW (Sym) *sym = &symtab[ELFW (R_SYM) (r->r_info)];
+         if (elf_dynamic_is_Rel_irelative (r, sym))
+           continue;
+         elf_machine_lazy_rel (map, scope, l_addr, r, 0);
+       }
     }
   else
 #endif
@@ -125,46 +149,87 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
              const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
              void *const r_addr_arg = (void *) (l_addr + r->r_offset);
              const struct r_found_version *rversion = &map->l_versions[ndx];
-#if defined ELF_MACHINE_IRELATIVE
-             if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
-               {
-                 if (r2 == NULL)
-                   r2 = r;
-                 end2 = r;
-                 continue;
-               }
-#endif
 
+             if (elf_dynamic_is_Rel_irelative (r, sym))
+               continue;
+             elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
+             elf_dynamic_Rel_audit_symbind (map, scope, r, sym, rversion,
+                                            r_addr_arg);
+           }
+       }
+      else
+       {
+         for (; r < end; ++r)
+           {
+             const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
+             void *const r_addr_arg = (void *) (l_addr + r->r_offset);
+
+             if (elf_dynamic_is_Rel_irelative (r, sym))
+               continue;
+             elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg, 0);
+             elf_dynamic_Rel_audit_symbind (map, scope, r, sym, NULL,
+                                            r_addr_arg);
+           }
+       }
+    }
+#endif /* !RTLD_BOOTSTRAP */
+}
+
+/* Process only IRELATIVE entries (and other relocations targeting a defined
+   STT_GNU_IFUNC symbol) in the relocation range [reladdr, reladdr+relsize).
+   The first NRELATIVE entries are R_*_RELATIVE and are skipped without
+   inspection.  When lazy is non-zero the PLT lazy-binding path
+   (elf_machine_lazy_rel) is used, otherwise the full non-lazy path
+   (elf_machine_rel) is used.
+
+   Called by _ELF_DYNAMIC_DO_RELOC after all non-IRELATIVE relocations have
+   been processed for both .rela.dyn and .rela.plt, so that IRELATIVE
+   resolvers may call PLT stubs safely regardless of which section the linker
+   placed R_*_IRELATIVE in.  */
+static __always_inline void
+elf_dynamic_do_Rel_irelative (struct link_map *map,
+                             struct r_scope_elem *scope[],
+                             ElfW(Addr) reladdr, ElfW(Addr) relsize,
+                             __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative,
+                             int lazy, int skip_ifunc)
+{
+# ifdef ELF_MACHINE_IRELATIVE
+  const ElfW(Rel) *r = (const ElfW(Rel) *) reladdr + nrelative;
+  const ElfW(Rel) *end = (const void *) (reladdr + relsize);
+  ElfW(Addr) l_addr = map->l_addr;
+  const ElfW(Sym) *const symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+
+  if (lazy)
+    {
+      for (; r < end; ++r)
+       {
+         const ElfW (Sym) *sym = &symtab[ELFW (R_SYM) (r->r_info)];
+         if (!elf_dynamic_is_Rel_irelative (r, sym))
+           continue;
+         elf_machine_lazy_rel (map, scope, l_addr, r, skip_ifunc);
+       }
+    }
+  else
+    {
+      if (map->l_info[VERSYMIDX (DT_VERSYM)])
+       {
+         const ElfW(Half) *const version =
+           (const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
+
+         for (; r < end; ++r)
+           {
+             const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
+             void *const r_addr_arg = (void *) (l_addr + r->r_offset);
+             if (!elf_dynamic_is_Rel_irelative (r, sym))
+               continue;
+
+             ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
+             const struct r_found_version *rversion = &map->l_versions[ndx];
              elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg,
                               skip_ifunc);
-#if defined SHARED
-             if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-                 && GLRO(dl_naudit) > 0)
-               {
-                 struct link_map *sym_map
-                   = RESOLVE_MAP (map, scope, &sym, rversion,
-                                  ELF_MACHINE_JMP_SLOT);
-                 if (sym != NULL)
-                   _dl_audit_symbind (map, NULL, r, sym, r_addr_arg, sym_map,
-                                      false);
-               }
-#endif
+             elf_dynamic_Rel_audit_symbind (map, scope, r, sym, rversion,
+                                            r_addr_arg);
            }
-
-#if defined ELF_MACHINE_IRELATIVE
-         if (r2 != NULL)
-           for (; r2 <= end2; ++r2)
-             if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
-               {
-                 ElfW(Half) ndx
-                   = version[ELFW(R_SYM) (r2->r_info)] & 0x7fff;
-                 elf_machine_rel (map, scope, r2,
-                                  &symtab[ELFW(R_SYM) (r2->r_info)],
-                                  &map->l_versions[ndx],
-                                  (void *) (l_addr + r2->r_offset),
-                                  skip_ifunc);
-               }
-#endif
        }
       else
        {
@@ -172,46 +237,23 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
            {
              const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
              void *const r_addr_arg = (void *) (l_addr + r->r_offset);
-# ifdef ELF_MACHINE_IRELATIVE
-             if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
-               {
-                 if (r2 == NULL)
-                   r2 = r;
-                 end2 = r;
-                 continue;
-               }
-# endif
+             if (!elf_dynamic_is_Rel_irelative (r, sym))
+               continue;
+
              elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg,
                               skip_ifunc);
-# if defined SHARED
-             if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-                 && GLRO(dl_naudit) > 0)
-               {
-                 struct link_map *sym_map
-                   = RESOLVE_MAP (map, scope, &sym,
-                                  (struct r_found_version *) NULL,
-                                  ELF_MACHINE_JMP_SLOT);
-                 if (sym != NULL)
-                   _dl_audit_symbind (map, NULL, r, sym,r_addr_arg, sym_map,
-                                      false);
-               }
-# endif
+             elf_dynamic_Rel_audit_symbind (map, scope, r, sym, NULL,
+                                            r_addr_arg);
            }
-
-# ifdef ELF_MACHINE_IRELATIVE
-         if (r2 != NULL)
-           for (; r2 <= end2; ++r2)
-             if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
-               elf_machine_rel (map, scope, r2, &symtab[ELFW(R_SYM) (r2->r_info)],
-                                NULL, (void *) (l_addr + r2->r_offset),
-                                skip_ifunc);
-# endif
        }
     }
-#endif /* !RTLD_BOOTSTRAP */
+# endif
 }
 
 #undef elf_dynamic_do_Rel
+#undef elf_dynamic_do_Rel_irelative
+#undef elf_dynamic_is_Rel_irelative
+#undef elf_dynamic_Rel_audit_symbind
 #undef Rel
 #undef elf_machine_rel
 #undef elf_machine_rel_relative
index a46f36b8d4536f543303a5907bcd36c28af248f4..334e241f63c1a8afb0ef7578a498dfd1acb48f1b 100644 (file)
@@ -78,7 +78,8 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
    consumes precisely the very end of the DT_REL*, or DT_JMPREL and DT_REL*
    are completely separate and there is a gap between them.  */
 
-# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, test_rel) \
+# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, \
+                              test_rel)                                      \
   do {                                                                       \
     struct { ElfW(Addr) start, size;                                         \
             __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative; int lazy; }  \
@@ -118,13 +119,34 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
          }                                                                   \
       }                                                                              \
                                                                              \
-      for (int ranges_index = 0; ranges_index < 2; ++ranges_index)           \
-        elf_dynamic_do_##reloc ((map), scope,                                \
-                               ranges[ranges_index].start,                   \
-                               ranges[ranges_index].size,                    \
-                               ranges[ranges_index].nrelative,               \
-                               ranges[ranges_index].lazy,                    \
-                               skip_ifunc);                                  \
+      /* Defer all IRELATIVE relocations until after all non-IRELATIVE       \
+        relocations (including PLT lazy-binding setup) have been processed   \
+        for both sections.  This ensures IRELATIVE resolvers can call PLT    \
+        stubs safely regardless of which section R_*_IRELATIVE was placed in \
+        by the linker.  */                                                   \
+      if (!DO_RTLD_BOOTSTRAP)                                                \
+       {                                                                     \
+         for (int ranges_index = 0; ranges_index < 2; ++ranges_index)        \
+           elf_dynamic_do_##reloc ((map), scope,                             \
+                                   ranges[ranges_index].start,               \
+                                   ranges[ranges_index].size,                \
+                                   ranges[ranges_index].nrelative,           \
+                                   ranges[ranges_index].lazy);               \
+         for (int ranges_index = 0; ranges_index < 2; ++ranges_index)        \
+           elf_dynamic_do_##reloc##_irelative ((map), scope,                 \
+                                               ranges[ranges_index].start,   \
+                                               ranges[ranges_index].size,    \
+                                               ranges[ranges_index].nrelative,\
+                                               ranges[ranges_index].lazy,    \
+                                               skip_ifunc);                  \
+       }                                                                     \
+      else                                                                   \
+       for (int ranges_index = 0; ranges_index < 2; ++ranges_index)          \
+         elf_dynamic_do_##reloc ((map), scope,                               \
+                                 ranges[ranges_index].start,                 \
+                                 ranges[ranges_index].size,                  \
+                                 ranges[ranges_index].nrelative,             \
+                                 ranges[ranges_index].lazy);                 \
   } while (0)
 
 # if ELF_MACHINE_NO_REL || ELF_MACHINE_NO_RELA
diff --git a/elf/tst-ifunc-plt-bindnow.c b/elf/tst-ifunc-plt-bindnow.c
new file mode 100644 (file)
index 0000000..3c5465e
--- /dev/null
@@ -0,0 +1,24 @@
+/* Test that IRELATIVE resolvers may call PLT functions with LD_BIND_NOW=1.
+   Copyright (C) 2019-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/>.  */
+
+/* Same as tst-ifunc-plt, but the test is run with LD_BIND_NOW=1 so the
+   library is processed eagerly.  Under eager binding the JMP_SLOT entries
+   in .rela.plt are resolved by elf_machine_rel during relocation; this
+   exercises the eager path of the deferred IRELATIVE processing.  */
+
+#include "tst-ifunc-plt.c"
diff --git a/elf/tst-ifunc-plt-dep.c b/elf/tst-ifunc-plt-dep.c
new file mode 100644 (file)
index 0000000..9cf2b1b
--- /dev/null
@@ -0,0 +1,23 @@
+/* Dependency library for tst-ifunc-plt.
+   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
+get_value (void)
+{
+  return 42;
+}
diff --git a/elf/tst-ifunc-plt-dlopen-bindnow.c b/elf/tst-ifunc-plt-dlopen-bindnow.c
new file mode 100644 (file)
index 0000000..50c4f65
--- /dev/null
@@ -0,0 +1,45 @@
+/* Test that IRELATIVE resolvers may call PLT functions via dlopen + RTLD_NOW.
+   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/>.  */
+
+
+/* Same as tst-ifunc-plt-dlopen, but uses RTLD_NOW to force eager
+   relocation of the loaded library.  This exercises the dlopen entry
+   point along the eager-binding path.  */
+
+#include <support/xdlfcn.h>
+#include <support/check.h>
+
+typedef int (*fn_t) (void);
+
+static int
+do_test (void)
+{
+  void *handle = xdlopen ("tst-ifunc-plt-lib.so", RTLD_NOW | RTLD_LOCAL);
+
+  fn_t compute_a = (fn_t) xdlsym (handle, "compute_a");
+  TEST_COMPARE (compute_a (), 1);
+
+  fn_t compute_b = (fn_t) xdlsym (handle, "compute_b");
+  TEST_COMPARE (compute_b (), 2);
+
+  xdlclose (handle);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-ifunc-plt-dlopen.c b/elf/tst-ifunc-plt-dlopen.c
new file mode 100644 (file)
index 0000000..ca31b8a
--- /dev/null
@@ -0,0 +1,46 @@
+/* Test that IRELATIVE resolvers may call PLT functions on a dlopen'd library.
+   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/>.  */
+
+
+/* dlopen tst-ifunc-plt-lib.so with RTLD_LAZY and verify that the IFUNC
+   resolver (which calls get_value() via PLT) ran successfully.  This
+   exercises the same _dl_relocate_object code path as startup loading
+   but via the dlopen entry point.  */
+
+#include <support/xdlfcn.h>
+#include <support/check.h>
+
+typedef int (*fn_t) (void);
+
+static int
+do_test (void)
+{
+  void *handle = xdlopen ("tst-ifunc-plt-lib.so", RTLD_LAZY | RTLD_LOCAL);
+
+  fn_t compute_a = (fn_t) xdlsym (handle, "compute_a");
+  TEST_COMPARE (compute_a (), 1);
+
+  fn_t compute_b = (fn_t) xdlsym (handle, "compute_b");
+  TEST_COMPARE (compute_b (), 2);
+
+  xdlclose (handle);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-ifunc-plt-lib.c b/elf/tst-ifunc-plt-lib.c
new file mode 100644 (file)
index 0000000..a9c100a
--- /dev/null
@@ -0,0 +1,56 @@
+/* Shared library for tst-ifunc-plt.
+   Two static IFUNCs whose resolvers both call get_value() via PLT.
+   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/>.  */
+
+
+/* Both resolvers call get_value() via PLT (one JUMP_SLOT entry in
+   .rel{a}.plt).  This verifies that every IRELATIVE entry is deferred
+   until after .rela.plt has been processed, not just the first one.  */
+
+#include <stddef.h>
+
+extern int get_value (void);
+
+static int
+impl_a (void)
+{
+  return 1;
+}
+
+static int
+impl_b (void)
+{
+  return 2;
+}
+
+static int (*
+resolve_a (void)) (void)
+{
+  return get_value () == 42 ? impl_a : NULL;
+}
+
+static int (*
+resolve_b (void)) (void)
+{
+  return get_value () == 42 ? impl_b : NULL;
+}
+
+/* The test is only built for $(have-ifunc), so we can assume HAVE_GCC_IFUNC
+   here.  */
+int compute_a (void) __attribute__ ((ifunc ("resolve_a")));
+int compute_b (void) __attribute__ ((ifunc ("resolve_b")));
diff --git a/elf/tst-ifunc-plt.c b/elf/tst-ifunc-plt.c
new file mode 100644 (file)
index 0000000..7848867
--- /dev/null
@@ -0,0 +1,38 @@
+/* Test that IRELATIVE resolvers may call PLT functions during startup.
+   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/>.  */
+
+
+/* tst-ifunc-plt-multi-lib.so defines two static IFUNCs (compute_a and
+   compute_b), each producing an R_*_IRELATIVE in .rel{a}.dyn, with both
+   resolvers calling get_value() via PLT.  The test verifies that both
+   IRELATIVEs are deferred until after .rel{a}.plt is processed.  */
+
+#include <support/check.h>
+
+extern int compute_a (void);
+extern int compute_b (void);
+
+static int
+do_test (void)
+{
+  TEST_COMPARE (compute_a (), 1);
+  TEST_COMPARE (compute_b (), 2);
+  return 0;
+}
+
+#include <support/test-driver.c>
index 21af8bc56e8707e2e9db4338d98c522821ca6e5b..15651c62f3dca94572dd75c559f38cec29a70673 100644 (file)
@@ -119,6 +119,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
    | (((type) == R_AARCH64_COPY) * ELF_RTYPE_CLASS_COPY))
 
 #define ELF_MACHINE_JMP_SLOT   R_AARCH64_JUMP_SLOT
+#define ELF_MACHINE_IRELATIVE  R_AARCH64_IRELATIVE
 
 #define DL_PLATFORM_INIT dl_platform_init ()
 
index e0065ce73c81ee137c15137c68387a89bfb5cb70..15cced693e38c2721b4207e1d88e3fbcf4d27dc2 100644 (file)
@@ -190,6 +190,7 @@ _dl_start_user:\n\
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT   R_ARM_JUMP_SLOT
+#define ELF_MACHINE_IRELATIVE  R_ARM_IRELATIVE
 
 /* We define an initialization functions.  This is called very early in
    _dl_sysdep_start.  */
index 6657f687917f6a489fbaeb1af3f44b1c8d0fa204..dd49079d75c56848236728ac04186b3b406e8087 100644 (file)
@@ -190,6 +190,7 @@ _dl_start_user:\n\
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT   R_386_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE  R_386_IRELATIVE
 
 /* We define an initialization functions.  This is called very early in
    _dl_sysdep_start.  */
index d7872986367736deca3c6212fd12883752660914..e07a44a5a5ba2013674f0540719c43f71443852d 100644 (file)
@@ -146,6 +146,7 @@ __elf_preferred_address(struct link_map *loader, size_t maplength,
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT   R_PPC_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE  R_PPC_IRELATIVE
 
 /* We define an initialization function to initialize HWCAP/HWCAP2 and
    platform data so it can be copied into the TCB later.  This is called
index 6e3771c91cc66e6525517433007a0860babe07e6..1f5d7a0170be3059a3c2e0f82450a6c479649f2d 100644 (file)
@@ -304,6 +304,7 @@ BODY_PREFIX "_dl_start_user:\n"                                             \
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT   R_PPC64_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE  R_PPC64_IRELATIVE
 
 /* We define an initialization function to initialize HWCAP/HWCAP2 and
    platform data so it can be copied into the TCB later.  This is called
index 8c7312ad981d795f75a58edfb44c4a655412f7d4..babb52af20a3d9384699bd136ccef796e47e9a89 100644 (file)
@@ -42,6 +42,7 @@
 #endif
 
 #define ELF_MACHINE_JMP_SLOT R_RISCV_JUMP_SLOT
+#define ELF_MACHINE_IRELATIVE R_RISCV_IRELATIVE
 
 #define elf_machine_type_class(type)                           \
   ((ELF_RTYPE_CLASS_PLT * ((type) == ELF_MACHINE_JMP_SLOT      \
index 73b347bfdf12935a26d8cf53c19e73fb5ba34914..3771339a04aa0a8a279b8172f3a293a9edc13d07 100644 (file)
@@ -156,6 +156,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT   R_SPARC_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE  R_SPARC_IRELATIVE
 
 /* Undo the sub %sp, 6*4, %sp; add %sp, 22*4, %o0 below to get at the
    value we want in __libc_stack_end.  */
index 856922680a18f9af751070c4c3e202260153ba34..8a1a38d1701868232d6fc9e956cfdd1cd7fa0559 100644 (file)
@@ -119,6 +119,7 @@ elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT   R_SPARC_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE  R_SPARC_IRELATIVE
 
 /* Set up the loaded object described by L so its unrelocated PLT
    entries will jump to the on-demand fixup code in dl-runtime.c.  */