[PATCH] elf: Defer all IRELATIVE relocations until after PLT setup (BZ 20673)
H.J. Lu
hjl.tools@gmail.com
Fri May 8 23:48:50 GMT 2026
On Tue, Apr 28, 2026 at 9:42 PM Adhemerval Zanella
<adhemerval.zanella@linaro.org> wrote:
>
> 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.
>
> I checked on all ABI that support iFUNC (x86_64, i686, aarch64, arm,
> loongarch, powerpc, riscv, s390, and sparc), although on some through
> qemu-system (which should not matter for this case).
>
> It also fixes the mold reported issues [1], which shows an example
> where IFUNC relocation placement and processing can works different
> for different ABIs.
Just to be clear. This patch doesn't fully fix:
https://sourceware.org/bugzilla/show_bug.cgi?id=20673
This fix only deals with IRELATIVE relocation order within an object.
Calling an external function from an IFUC resolver may still crash.
Am I correct?
> [1] https://github.com/rui314/mold/issues/1550
> ---
> elf/Makefile | 12 ++
> elf/do-rel.h | 179 +++++++++++++++----------
> elf/dynamic-link.h | 37 +++--
> elf/tst-ifunc-plt-dep.c | 23 ++++
> elf/tst-ifunc-plt-dlopen.c | 46 +++++++
> elf/tst-ifunc-plt-lib.c | 56 ++++++++
> elf/tst-ifunc-plt.c | 38 ++++++
> sysdeps/aarch64/dl-machine.h | 1 +
> sysdeps/arm/dl-machine.h | 1 +
> sysdeps/i386/dl-machine.h | 1 +
> sysdeps/powerpc/powerpc32/dl-machine.h | 1 +
> sysdeps/powerpc/powerpc64/dl-machine.h | 1 +
> sysdeps/riscv/dl-machine.h | 1 +
> sysdeps/sparc/sparc32/dl-machine.h | 1 +
> sysdeps/sparc/sparc64/dl-machine.h | 1 +
> 15 files changed, 320 insertions(+), 79 deletions(-)
> create mode 100644 elf/tst-ifunc-plt-dep.c
> create mode 100644 elf/tst-ifunc-plt-dlopen.c
> create mode 100644 elf/tst-ifunc-plt-lib.c
> create mode 100644 elf/tst-ifunc-plt.c
>
> diff --git a/elf/Makefile b/elf/Makefile
> index c835eb8156..377ea2c0cc 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -1252,6 +1252,8 @@ ifeq (yes,$(build-shared))
> tests += \
> tst-ifunc-fault-bindnow \
> tst-ifunc-fault-lazy \
> + tst-ifunc-plt \
> + tst-ifunc-plt-dlopen \
> # tests
> # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
> tests-internal += \
> @@ -1314,6 +1316,8 @@ modules-names += \
> ifuncmod1 \
> ifuncmod3 \
> ifuncmod6 \
> + tst-ifunc-plt-dep \
> + tst-ifunc-plt-lib \
> # modules-names
> ifeq (no,$(with-lld))
> modules-names += ifuncmod5
> @@ -1793,6 +1797,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
> @@ -2426,6 +2431,13 @@ $(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
> +
> +$(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-dlopen.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
>
> diff --git a/elf/do-rel.h b/elf/do-rel.h
> index d00ffab7e9..e7912d4ed4 100644
> --- a/elf/do-rel.h
> +++ b/elf/do-rel.h
> @@ -23,6 +23,8 @@
>
> #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 Rel Rela
> # define elf_machine_rel elf_machine_rela
> # define elf_machine_rel_relative elf_machine_rela_relative
> @@ -34,16 +36,34 @@
> (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
> +}
> +
> /* 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 +85,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 +96,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,18 +129,10 @@ 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
>
> - elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg,
> - skip_ifunc);
> + if (elf_dynamic_is_Rel_irelative (r, sym))
> + continue;
> + elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
> #if defined SHARED
> if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
> && GLRO(dl_naudit) > 0)
> @@ -150,21 +146,6 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
> }
> #endif
> }
> -
> -#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,17 +153,10 @@ 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
> - elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg,
> - skip_ifunc);
> +
> + if (elf_dynamic_is_Rel_irelative (r, sym))
> + continue;
> + elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg, 1);
> # if defined SHARED
> if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
> && GLRO(dl_naudit) > 0)
> @@ -197,21 +171,84 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
> }
> # endif
> }
> -
> -# 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 */
> }
>
> +/* Process only IRELATIVE entries in the relocation range
> + [reladdr, reladdr+relsize). 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,
> + int lazy, int skip_ifunc)
> +{
> +# ifdef ELF_MACHINE_IRELATIVE
> + const ElfW(Rel) *r = (const void *) reladdr;
> + 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)];
> + if (!elf_dynamic_is_Rel_irelative (r, sym))
> + continue;
> +
> + ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
> + elf_machine_rel (map, scope, r,
> + &symtab[ELFW(R_SYM) (r->r_info)],
> + &map->l_versions[ndx],
> + (void *) (l_addr + r->r_offset),
> + skip_ifunc);
> + }
> + }
> + else
> + {
> + 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_rel (map, scope, r,
> + &symtab[ELFW(R_SYM) (r->r_info)],
> + NULL,
> + (void *) (l_addr + r->r_offset),
> + skip_ifunc);
> + }
> + }
> + }
> +# endif
> +}
> +
> #undef elf_dynamic_do_Rel
> +#undef elf_dynamic_do_Rel_irelative
> #undef Rel
> #undef elf_machine_rel
> #undef elf_machine_rel_relative
> diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
> index a46f36b8d4..2055d910c6 100644
> --- a/elf/dynamic-link.h
> +++ b/elf/dynamic-link.h
> @@ -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,33 @@ 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].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-dep.c b/elf/tst-ifunc-plt-dep.c
> new file mode 100644
> index 0000000000..9cf2b1b0b2
> --- /dev/null
> +++ b/elf/tst-ifunc-plt-dep.c
> @@ -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.c b/elf/tst-ifunc-plt-dlopen.c
> new file mode 100644
> index 0000000000..ed9d69b69c
> --- /dev/null
> +++ b/elf/tst-ifunc-plt-dlopen.c
> @@ -0,0 +1,46 @@
> +/* Test for BZ #20673 via dlopen.
> + 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
> index 0000000000..650231ff28
> --- /dev/null
> +++ b/elf/tst-ifunc-plt-lib.c
> @@ -0,0 +1,56 @@
> +/* Shared library for tst-ifunc-plt-multi (bug 20673).
> + 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
> index 0000000000..382f90d852
> --- /dev/null
> +++ b/elf/tst-ifunc-plt.c
> @@ -0,0 +1,38 @@
> +/* Test for BZ #20673.
> + 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>
> diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
> index 21af8bc56e..15651c62f3 100644
> --- a/sysdeps/aarch64/dl-machine.h
> +++ b/sysdeps/aarch64/dl-machine.h
> @@ -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 ()
>
> diff --git a/sysdeps/arm/dl-machine.h b/sysdeps/arm/dl-machine.h
> index e0065ce73c..15cced693e 100644
> --- a/sysdeps/arm/dl-machine.h
> +++ b/sysdeps/arm/dl-machine.h
> @@ -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. */
> diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
> index 6657f68791..dd49079d75 100644
> --- a/sysdeps/i386/dl-machine.h
> +++ b/sysdeps/i386/dl-machine.h
> @@ -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. */
> diff --git a/sysdeps/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h
> index d787298636..e07a44a5a5 100644
> --- a/sysdeps/powerpc/powerpc32/dl-machine.h
> +++ b/sysdeps/powerpc/powerpc32/dl-machine.h
> @@ -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
> diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
> index 6e3771c91c..1f5d7a0170 100644
> --- a/sysdeps/powerpc/powerpc64/dl-machine.h
> +++ b/sysdeps/powerpc/powerpc64/dl-machine.h
> @@ -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
> diff --git a/sysdeps/riscv/dl-machine.h b/sysdeps/riscv/dl-machine.h
> index 8c7312ad98..babb52af20 100644
> --- a/sysdeps/riscv/dl-machine.h
> +++ b/sysdeps/riscv/dl-machine.h
> @@ -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 \
> diff --git a/sysdeps/sparc/sparc32/dl-machine.h b/sysdeps/sparc/sparc32/dl-machine.h
> index 73b347bfdf..3771339a04 100644
> --- a/sysdeps/sparc/sparc32/dl-machine.h
> +++ b/sysdeps/sparc/sparc32/dl-machine.h
> @@ -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. */
> diff --git a/sysdeps/sparc/sparc64/dl-machine.h b/sysdeps/sparc/sparc64/dl-machine.h
> index 856922680a..8a1a38d170 100644
> --- a/sysdeps/sparc/sparc64/dl-machine.h
> +++ b/sysdeps/sparc/sparc64/dl-machine.h
> @@ -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. */
> --
> 2.43.0
>
--
H.J.
More information about the Libc-alpha
mailing list