]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
elf: Initialize TCB and stack-protector before static IFUNC resolvers (BZ 20680,...
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Fri, 22 May 2026 17:08:13 +0000 (14:08 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Mon, 25 May 2026 12:49:21 +0000 (12:49 +0000)
In static linking the IFUNC IPLT (apply_irel for non-PIE, the IRELATIVE
phase inside _dl_relocate_static_pie for static-pie) ran before
__libc_setup_tls and before _dl_setup_stack_chk_guard.  When a resolver
is compiled with -fstack-protector(-all) its prologue loads the canary
from the TCB (TCB-canary ABIs: x86_64, i386, powerpc, s390) or from
__stack_chk_guard (global-var ABIs).  On the former the resolver
crashed reading an unmapped TCB; on the latter it loaded a zero canary
(no crash, but the check is ineffective).  The same applies to a
resolver that reads any thread-local: it crashes on TCB-canary ABIs and
observes a zero-filled slot on the others (BZ 20680).  The pointer
guard has the same problem (e.g. resolvers that register an atexit
handler).

Reorder csu/libc-start.c so that ARCH_SETUP_TLS, the stack-protector
canary and the pointer guard are set up before any IFUNC resolver
runs.  For static-pie this requires splitting the existing
_dl_relocate_static_pie into two phases so the TCB/canary setup can be
interleaved between the non-IRELATIVE and IRELATIVE passes.

The historical ARCH_SETUP_IREL / ARCH_APPLY_IREL split (introduced for
powerpc so its IFUNC resolvers could read TCB fields like hwcap and
at_platform) is no longer required: TLS is now set up before either
macro runs.  ARCH_APPLY_IREL is removed, ARCH_SETUP_IREL does the work
uniformly on every arch, and the powerpc-specific libc-start.h becomes
redundant.

__libc_setup_tls reaches memcpy / mempcpy via _dl_allocate_tls_init in
elf/dl-tls.c, so it requires update ABI specific dl-symbol-redir-ifunc.h
with memcpy/memmove.

Tests added (each fails pre-fix on TCB-canary ABIs with SIGSEGV; the
static-protector variants additionally fail on global-var ABIs with a
"resolver_canary != main_canary" diagnostic):

  elf/tst-ifunc-bz28817                            static-pie + TLS in
                                                   resolver (BZ 28817)
  elf/tst-ifunc-resolver-protector                 dynamic
  elf/tst-ifunc-resolver-protector-static          static-pie
  elf/tst-ifunc-resolver-protector-static-non-pie  non-PIE static

Checked on aarch64-linux-gnu, arm-linux-gnueabihf, x86_64-linux-gnu,
and i686-linux-gnu

I also ran the ELF tests on qemu system for loongarch64-linux-gnuf64,
powerpc-linux-gnu, powerpc-linux-gnu-power4, powerpc-linux-gnu-soft,
powerpc64-linux-gnu, powerpc64le-linux-gnu, riscv64-linux-gnu, and
s390x-linux-gnu.
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
20 files changed:
csu/libc-start.c
csu/static-reloc.c
elf/Makefile
elf/dl-reloc-static-pie.c
elf/dynamic-link.h
elf/tst-ifunc-bz28817.c [new file with mode: 0644]
elf/tst-ifunc-resolver-protector-mod.c [new file with mode: 0644]
elf/tst-ifunc-resolver-protector-static-mod.c [new file with mode: 0644]
elf/tst-ifunc-resolver-protector-static-non-pie-mod.c [new file with mode: 0644]
elf/tst-ifunc-resolver-protector-static-non-pie.c [new file with mode: 0644]
elf/tst-ifunc-resolver-protector-static.c [new file with mode: 0644]
elf/tst-ifunc-resolver-protector.c [new file with mode: 0644]
sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h
sysdeps/generic/ldsodefs.h
sysdeps/generic/libc-start.h
sysdeps/loongarch/lp64/multiarch/dl-symbol-redir-ifunc.h
sysdeps/unix/sysv/linux/aarch64/libc-start.h
sysdeps/unix/sysv/linux/powerpc/libc-start.h [deleted file]
sysdeps/x86_64/libc-start.h
sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h

index 1c58561bce0c04b10a680c2d4cf3575ca66f4f0c..03d770ef15774e161eee38db0a2590ee9677a1a0 100644 (file)
@@ -268,23 +268,22 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
 
   ARCH_INIT_CPU_FEATURES ();
 
-  /* Do static pie self relocation after tunables and cpu features
-     are setup for ifunc resolvers. Before this point relocations
-     must be avoided.  */
+  /* Do static-pie self relocation for the non-IRELATIVE part after tunables
+     and cpu features are set up.  IFUNC entries are deferred until after the
+     TCB and the stack-protector canary are usable, so that an instrumented
+     resolver does not fault.  Before this point relocations must be
+     avoided.  */
   _dl_relocate_static_pie ();
 
-  /* Perform IREL{,A} relocations.  */
-  ARCH_SETUP_IREL ();
-
-  /* The stack guard goes into the TCB, so initialize it early.  */
+  /* Set up the TCB so that the IFUNC pass below can fire resolvers
+     compiled with stack protection, and so that resolvers reading TLS
+     (errno, __thread variables, powerpc's hwcap / at_platform in the
+     TCB) observe an initialised slot.  */
   ARCH_SETUP_TLS ();
 
-  /* In some architectures, IREL{,A} relocations happen after TLS setup in
-     order to let IFUNC resolvers benefit from TCB information, e.g. powerpc's
-     hwcap and platform fields available in the TCB.  */
-  ARCH_APPLY_IREL ();
-
-  /* Set up the stack checker's canary.  */
+  /* Set up the stack checker's canary.  Must happen before any IFUNC resolver
+     runs so a resolver compiled with stack protection loads a defined
+     canary.  */
   uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
 # ifdef THREAD_SET_STACK_GUARD
   THREAD_SET_STACK_GUARD (stack_chk_guard);
@@ -292,10 +291,6 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
   __stack_chk_guard = stack_chk_guard;
 # endif
 
-  /* Initialize libpthread if linked in.  */
-  if (__pthread_initialize_minimal != NULL)
-    __pthread_initialize_minimal ();
-
   /* Set up the pointer guard value.  */
   uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random,
                                                         stack_chk_guard);
@@ -305,6 +300,17 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
   __pointer_chk_guard_local = pointer_chk_guard;
 # endif
 
+  /* Now that the TCB, canary, and pointer guard are in place, run the
+     deferred IFUNC relocations.  For non-PIE static binaries this is
+     ARCH_SETUP_IREL (apply_irel); for static-pie it is the IRELATIVE
+     phase of _dl_relocate_static_pie above.  */
+  _dl_relocate_static_pie_ifunc ();
+  ARCH_SETUP_IREL ();
+
+  /* Initialize libpthread if linked in.  */
+  if (__pthread_initialize_minimal != NULL)
+    __pthread_initialize_minimal ();
+
 #endif /* !SHARED  */
 
   /* Register the destructor of the dynamic linker if there is any.  */
index 2044ca348641eeb8014bba6299c9814c4a06177f..4c3bc7b3270ce70f1bde61361cea89b2e618c5aa 100644 (file)
@@ -23,4 +23,9 @@ void
 _dl_relocate_static_pie (void)
 {
 }
+
+void
+_dl_relocate_static_pie_ifunc (void)
+{
+}
 #endif
index ff42eb32d41d46dbe192053a20abb8fbc977994e..d7bab52cd9778be69a1afba57e75083a759f20c8 100644 (file)
@@ -289,8 +289,17 @@ tests-static-internal := \
   tst-tunables-enable_secure \
   # tests-static-internal
 
+ifeq (yes,$(have-gcc-ifunc))
+tests-static-internal += \
+  tst-ifunc-resolver-protector-static \
+  tst-ifunc-resolver-protector-static-non-pie \
+  # tests-static-internal
+endif
+
 CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
 tst-tls1-static-non-pie-no-pie = yes
+CRT-tst-ifunc-resolver-protector-static-non-pie := $(csu-objpfx)crt1.o
+tst-ifunc-resolver-protector-static-non-pie-no-pie = yes
 
 tests-container := \
   tst-dl-cache-long-path \
@@ -744,6 +753,19 @@ test-extras += \
   tst-tlsmod17a \
   tst-tlsmod18a \
   # test-extras
+
+ifeq (yes,$(have-gcc-ifunc))
+# The resolver helper needs <tls.h> for the TCB-canary STACK_CHK_GUARD
+# macro, so it must be compiled with MODULE_NAME=testsuite_internal.
+extra-test-objs += \
+  tst-ifunc-resolver-protector-static-mod.o \
+  tst-ifunc-resolver-protector-static-non-pie-mod.o \
+  # extra-test-objs
+test-internal-extras += \
+  tst-ifunc-resolver-protector-static-mod \
+  tst-ifunc-resolver-protector-static-non-pie-mod \
+  # test-internal-extras
+endif
 modules-names += \
   circlemod1 \
   circlemod1a \
@@ -1256,7 +1278,11 @@ tests-ifuncstatic := \
   ifuncmain7static \
   # tests-ifuncstatic
 ifeq (yes,$(have-gcc-ifunc))
-tests-ifuncstatic += ifuncmain9static ifuncmain9picstatic
+tests-ifuncstatic += \
+  ifuncmain9picstatic \
+  ifuncmain9static \
+  tst-ifunc-bz28817 \
+  # tests-ifuncstatic
 endif
 tests-static += $(tests-ifuncstatic)
 tests-internal += $(tests-ifuncstatic)
@@ -1268,6 +1294,7 @@ tests += \
   tst-ifunc-plt-bindnow \
   tst-ifunc-plt-dlopen \
   tst-ifunc-plt-dlopen-bindnow \
+  tst-ifunc-resolver-protector \
   tst-ifunc-tls-init \
   tst-ifunc-tls-write \
   # tests
@@ -1334,6 +1361,7 @@ modules-names += \
   ifuncmod6 \
   tst-ifunc-plt-dep \
   tst-ifunc-plt-lib \
+  tst-ifunc-resolver-protector-mod \
   tst-ifunc-tls-init-lib1 \
   tst-ifunc-tls-init-lib2 \
   tst-ifunc-tls-write-lib \
@@ -1744,6 +1772,19 @@ $(objpfx)tst-nodelete-opened.out: $(objpfx)tst-nodelete-opened-lib.so
 $(objpfx)tst-tlsalign-extern: $(objpfx)tst-tlsalign-vars.o
 $(objpfx)tst-tlsalign-extern-static: $(objpfx)tst-tlsalign-vars.o
 
+# The resolver translation unit must always be compiled with
+# -fstack-protector-all so the canary code is emitted regardless of the
+# default.
+CFLAGS-tst-ifunc-resolver-protector-static-mod.c = -fstack-protector-all
+CFLAGS-tst-ifunc-resolver-protector-static-non-pie-mod.c = -fstack-protector-all
+CFLAGS-tst-ifunc-resolver-protector-mod.c = -fstack-protector-all
+$(objpfx)tst-ifunc-resolver-protector-static: \
+  $(objpfx)tst-ifunc-resolver-protector-static-mod.o
+$(objpfx)tst-ifunc-resolver-protector-static-non-pie: \
+  $(objpfx)tst-ifunc-resolver-protector-static-non-pie-mod.o
+$(objpfx)tst-ifunc-resolver-protector: \
+  $(objpfx)tst-ifunc-resolver-protector-mod.so
+
 tst-null-argv-ENV = LD_DEBUG=all LD_DEBUG_OUTPUT=$(objpfx)tst-null-argv.debug.out
 LDFLAGS-nodel2mod3.so = -Wl,--no-as-needed
 LDFLAGS-reldepmod5.so = -Wl,--no-as-needed
index 63ce609024e4ec5823a2693abd86e681079e0659..8463e46147d1202090efe6c212a93a6b2c657c1a 100644 (file)
 #include "dynamic-link.h"
 #include "get-dynamic-info.h"
 
-/* Relocate static executable with PIE.  */
-
+/* Phase 1: relocate static PIE - non-IRELATIVE pass.  IFUNC resolvers are
+   deferred to _dl_relocate_static_pie_ifunc so that csu/libc-start.c can
+   initialise the TCB (and write the stack-protector canary into it) between
+   the two passes.  */
 void
 _dl_relocate_static_pie (void)
 {
@@ -76,10 +78,9 @@ _dl_relocate_static_pie (void)
   ELF_MACHINE_BEFORE_RTLD_RELOC (main_map, main_map->l_info);
 # endif
 
-  /* Relocate ourselves so we can do normal function calls and
-     data access using the global offset table.  */
-  ELF_DYNAMIC_RELOCATE (main_map, NULL, 0, 0, 0);
-  main_map->l_relocated = 1;
+  /* Relocate ourselves so we can do normal function calls and data access
+     using the global offset table.  IRELATIVE entries are deferred.  */
+  ELF_DYNAMIC_RELOCATE_NOIFUNC (main_map, NULL, 0, 0);
 
   /* Initialize _r_debug_extended.  */
   struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE);
@@ -89,4 +90,15 @@ _dl_relocate_static_pie (void)
      time.  */
   elf_setup_debug_entry (main_map, r);
 }
+
+/* Phase 2: run the deferred IRELATIVE entries for the static-pie main map.
+   Must be called after the TCB is set up and the stack-protector canary is
+   written, so that an instrumented IFUNC resolver does not fault.  */
+void
+_dl_relocate_static_pie_ifunc (void)
+{
+  struct link_map *main_map = _dl_get_dl_main_map ();
+  ELF_DYNAMIC_RELOCATE_IFUNC (main_map, NULL, 0, 0);
+  main_map->l_relocated = 1;
+}
 #endif
index 334e241f63c1a8afb0ef7578a498dfd1acb48f1b..35141acec4bbd2ffc808a598bf1fa47c3a2aaf37 100644 (file)
@@ -78,8 +78,18 @@ 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.  */
 
+/* This controls which sub-passes _ELF_DYNAMIC_DO_RELOC runs.  Used to
+   interleave TLS / stack-protector setup between the two passes so IFUNC
+   resolvers see a fully-initialised TCB.  */
+enum elf_dynamic_reloc_phase
+{
+  DL_RELOC_BOTH     = 0,  /* Non-IRELATIVE pass then IRELATIVE pass.  */
+  DL_RELOC_NOIFUNC  = 1,  /* Non-IRELATIVE pass only.  */
+  DL_RELOC_IFUNC    = 2,  /* IRELATIVE pass only.  */
+};
+
 # define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, \
-                              test_rel)                                      \
+                              test_rel, phase)                               \
   do {                                                                       \
     struct { ElfW(Addr) start, size;                                         \
             __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative; int lazy; }  \
@@ -126,19 +136,21 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
         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);                  \
+         if ((phase) != DL_RELOC_IFUNC)                                      \
+           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);             \
+         if ((phase) != DL_RELOC_NOIFUNC)                                    \
+           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)          \
@@ -158,18 +170,36 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
 # if ! ELF_MACHINE_NO_REL
 #  include "do-rel.h"
 #  define ELF_DYNAMIC_DO_REL(map, scope, lazy, skip_ifunc)           \
-  _ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc, _ELF_CHECK_REL)
+  _ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc,      \
+                        _ELF_CHECK_REL, DL_RELOC_BOTH)
+#  define ELF_DYNAMIC_DO_REL_NOIFUNC(map, scope, lazy)               \
+  _ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, 0,                      \
+                        _ELF_CHECK_REL, DL_RELOC_NOIFUNC)
+#  define ELF_DYNAMIC_DO_REL_IFUNCONLY(map, scope, lazy, skip_ifunc)  \
+  _ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc,      \
+                        _ELF_CHECK_REL, DL_RELOC_IFUNC)
 # else
 #  define ELF_DYNAMIC_DO_REL(map, scope, lazy, skip_ifunc) /* Nothing to do.  */
+#  define ELF_DYNAMIC_DO_REL_NOIFUNC(map, scope, lazy) /* Nothing to do.  */
+#  define ELF_DYNAMIC_DO_REL_IFUNCONLY(map, scope, lazy, skip_ifunc) /* Nothing.  */
 # endif
 
 # if ! ELF_MACHINE_NO_RELA
 #  define DO_RELA
 #  include "do-rel.h"
 #  define ELF_DYNAMIC_DO_RELA(map, scope, lazy, skip_ifunc)          \
-  _ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc, _ELF_CHECK_REL)
+  _ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc,    \
+                        _ELF_CHECK_REL, DL_RELOC_BOTH)
+#  define ELF_DYNAMIC_DO_RELA_NOIFUNC(map, scope, lazy)                      \
+  _ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, 0,            \
+                        _ELF_CHECK_REL, DL_RELOC_NOIFUNC)
+#  define ELF_DYNAMIC_DO_RELA_IFUNCONLY(map, scope, lazy, skip_ifunc) \
+  _ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc,    \
+                        _ELF_CHECK_REL, DL_RELOC_IFUNC)
 # else
 #  define ELF_DYNAMIC_DO_RELA(map, scope, lazy, skip_ifunc) /* Nothing to do.  */
+#  define ELF_DYNAMIC_DO_RELA_NOIFUNC(map, scope, lazy) /* Nothing to do.  */
+#  define ELF_DYNAMIC_DO_RELA_IFUNCONLY(map, scope, lazy, skip_ifunc) /* Nothing.  */
 # endif
 
 # define ELF_DYNAMIC_DO_RELR(map)                                            \
@@ -221,4 +251,26 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
     ELF_DYNAMIC_AFTER_RELOC ((map), (edr_lazy));                             \
   } while (0)
 
+/* Like ELF_DYNAMIC_RELOCATE but only processes the non-IRELATIVE pass.
+   The IRELATIVE pass must be completed later via ELF_DYNAMIC_RELOCATE_IFUNC.
+   Used by the static-pie startup so the TCB and stack-protector canary can
+   be initialised between the two passes.  */
+# define ELF_DYNAMIC_RELOCATE_NOIFUNC(map, scope, lazy, consider_profile)     \
+  do {                                                                       \
+    int edr_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_NOIFUNC ((map), (scope), edr_lazy);                   \
+    ELF_DYNAMIC_DO_RELA_NOIFUNC ((map), (scope), edr_lazy);                  \
+    ELF_DYNAMIC_AFTER_RELOC ((map), (edr_lazy));                             \
+  } while (0)
+
+/* IRELATIVE-only companion to ELF_DYNAMIC_RELOCATE_NOIFUNC.  */
+# define ELF_DYNAMIC_RELOCATE_IFUNC(map, scope, lazy, skip_ifunc)            \
+  do {                                                                       \
+    ELF_DYNAMIC_DO_REL_IFUNCONLY ((map), (scope), (lazy), skip_ifunc);       \
+    ELF_DYNAMIC_DO_RELA_IFUNCONLY ((map), (scope), (lazy), skip_ifunc);              \
+  } while (0)
+
 #endif
diff --git a/elf/tst-ifunc-bz28817.c b/elf/tst-ifunc-bz28817.c
new file mode 100644 (file)
index 0000000..fe6f400
--- /dev/null
@@ -0,0 +1,60 @@
+/* BZ 28817: TLS read from a static-pie IFUNC resolver.
+   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 <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+
+__thread int bar;
+extern __thread int bar_gd asm ("bar")
+  __attribute__ ((tls_model("global-dynamic")));
+static int *bar_ptr;
+
+int foo (void);
+
+static void
+init_foo (void)
+{
+  bar_ptr = &bar_gd;
+}
+
+static int
+my_foo (void)
+{
+  return bar_ptr != NULL;
+}
+
+static __typeof (foo) *
+inhibit_stack_protector
+foo_ifunc (void)
+{
+  init_foo ();
+  __typeof (foo) *res = my_foo;
+  return res;
+};
+__typeof (foo) foo __attribute__ ((ifunc ("foo" "_ifunc")));
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (foo ());
+  TEST_VERIFY (&bar == bar_ptr);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-ifunc-resolver-protector-mod.c b/elf/tst-ifunc-resolver-protector-mod.c
new file mode 100644 (file)
index 0000000..36d11f0
--- /dev/null
@@ -0,0 +1,61 @@
+/* Stack-protector-instrumented IFUNC resolver in a shared library, used
+   by tst-ifunc-resolver-protector.  BZ #27582.
+   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/>.  */
+
+/* Built with -fstack-protector-all so the resolver's prologue/epilogue
+   carries the full canary check.  Reaching the return statement means the
+   canary load did not fault (TCB-canary ABIs) and the canary compare did not
+   call __stack_chk_fail.  The dynamic linker calls security_init() before the
+   main relocation loop, so resolvers fired from startup or dlopen should
+   always observe an initialised canary; this test guards against any future
+   reordering that would break that invariant.  */
+
+#define SENTINEL 0x5A5A1234
+
+static volatile int resolver_ran;
+
+int
+get_resolver_ran (void)
+{
+  return resolver_ran;
+}
+
+static int
+impl_ok (int x)
+{
+  return x + SENTINEL;
+}
+
+typedef int (*fn_t) (int);
+
+static fn_t
+resolver (void)
+{
+  /* Buffer + zero-fill force -fstack-protector-all canary code.  */
+  volatile char buf[32];
+  for (unsigned i = 0; i < sizeof (buf); ++i)
+    buf[i] = 0;
+  resolver_ran = 1;
+  return impl_ok;
+}
+
+int compute (int) __attribute__ ((ifunc ("resolver")));
+
+/* Address taken in DSO data to force an R_*_IRELATIVE in .rela.dyn (a
+   non-PLT relocation against the IFUNC).  */
+int (*fptr) (int) = compute;
diff --git a/elf/tst-ifunc-resolver-protector-static-mod.c b/elf/tst-ifunc-resolver-protector-static-mod.c
new file mode 100644 (file)
index 0000000..b1cb704
--- /dev/null
@@ -0,0 +1,68 @@
+/* Stack-protector-instrumented IFUNC resolver used by
+   tst-ifunc-resolver-protector-static.  BZ #34164.
+   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/>.  */
+
+/* This translation unit is built with -fstack-protector-all so that the
+   compiler instruments the resolver below.  The resolver's prologue loads
+   the canary from the TCB (TCB-canary ABIs) or from __stack_chk_guard
+   (global-var ABIs); the epilogue compares it against the on-stack copy.
+   Pre-fix the prologue load either faulted (TCB-canary) or loaded zero
+   (global-var).
+
+   The resolver also records the canary value via STACK_CHK_GUARD so the
+   test can verify it matches what main observes.  */
+
+#include <stackguard-macros.h>
+#include <stdint.h>
+#include <tls.h>
+
+#define SENTINEL 0x5A5A1234
+
+static volatile uintptr_t resolver_canary;
+
+uintptr_t
+get_resolver_canary (void)
+{
+  return resolver_canary;
+}
+
+static int
+impl_ok (int x)
+{
+  return x + SENTINEL;
+}
+
+typedef int (*fn_t) (int);
+
+static fn_t
+resolver (void)
+{
+  /* Buffer forces -fstack-protector-all to emit canary code even with
+     no other reason to.  */
+  volatile char buf[32];
+  for (unsigned i = 0; i < sizeof (buf); ++i)
+    buf[i] = 0;
+
+  resolver_canary = STACK_CHK_GUARD;
+
+  return impl_ok;
+}
+
+int compute (int) __attribute__ ((ifunc ("resolver")));
+
+int (*fptr) (int) = compute;
diff --git a/elf/tst-ifunc-resolver-protector-static-non-pie-mod.c b/elf/tst-ifunc-resolver-protector-static-non-pie-mod.c
new file mode 100644 (file)
index 0000000..a5d200c
--- /dev/null
@@ -0,0 +1,2 @@
+/* Companion module for tst-ifunc-resolver-protector-static-non-pie.  */
+#include "tst-ifunc-resolver-protector-static-mod.c"
diff --git a/elf/tst-ifunc-resolver-protector-static-non-pie.c b/elf/tst-ifunc-resolver-protector-static-non-pie.c
new file mode 100644 (file)
index 0000000..2f42e91
--- /dev/null
@@ -0,0 +1,5 @@
+/* Same coverage as tst-ifunc-resolver-protector-static, but linked as non-PIE
+   static.  Exercises the apply_irel / __rela_iplt_start path instead of the
+   static-pie _dl_relocate_static_pie_ifunc path.  */
+
+#include "tst-ifunc-resolver-protector-static.c"
diff --git a/elf/tst-ifunc-resolver-protector-static.c b/elf/tst-ifunc-resolver-protector-static.c
new file mode 100644 (file)
index 0000000..e594e1c
--- /dev/null
@@ -0,0 +1,61 @@
+/* Check that a stack-protector-instrumented IFUNC resolver works in a
+   static binary.  BZ #34164.
+   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/>.  */
+
+/* When a static binary's IFUNC resolver is compiled with stack protector, the
+   resolver prologue loads the canary from the TCB (TCB-canary ABIs: x86_64,
+   i386, powerpc, s390) or from the global __stack_chk_guard (other ABIs).
+   The test checks if TCB-canary or the global-var is properly initialized:
+
+   1. The resolver runs without SIGSEGV (TCB-canary) or SIGABRT (canary
+      mismatch).
+
+   2. The resolver records the canary value it observed via STACK_CHK_GUARD,
+      and main asserts it equals the value visible from user code -- catching
+      the "silent zero canary" variant on global-var ABIs.  */
+
+#include <stackguard-macros.h>
+#include <stdint.h>
+#include <tls.h>
+#include <support/check.h>
+
+#define SENTINEL 0x5A5A1234
+
+extern int compute (int);
+extern uintptr_t get_resolver_canary (void);
+
+static int
+do_test (void)
+{
+  /* compute() returns its argument + SENTINEL iff the resolver picked
+     impl_ok, which it does whenever the canary check at the resolver
+     prologue / epilogue did not abort.  */
+  TEST_COMPARE (compute (1), 1 + SENTINEL);
+
+  /* Silent-variant check: the canary the resolver loaded must match the
+     canary do_test reads.  On global-var ABIs, it checks if the stack
+     protector cookie is properly initialised.  */
+  uintptr_t resolver_canary = get_resolver_canary ();
+  uintptr_t main_canary = STACK_CHK_GUARD;
+  TEST_VERIFY (resolver_canary != 0);
+  TEST_COMPARE (resolver_canary, main_canary);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-ifunc-resolver-protector.c b/elf/tst-ifunc-resolver-protector.c
new file mode 100644 (file)
index 0000000..3e085e5
--- /dev/null
@@ -0,0 +1,42 @@
+/* Check that a stack-protector-instrumented IFUNC resolver works in a
+   dynamically-linked binary.  BZ #27582.
+   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/>.  */
+
+/* Dynamic counterpart to tst-ifunc-resolver-protector-static.
+
+   The dynamic linker calls security_init() before the main relocation
+   loop, so a stack-protected resolver fired from startup should observe
+   an initialised canary -- but having a regression test guards against
+   any future reordering that would break that invariant.  */
+
+#include <support/check.h>
+
+#define SENTINEL 0x5A5A1234
+
+extern int compute (int);
+extern int get_resolver_ran (void);
+
+static int
+do_test (void)
+{
+  TEST_COMPARE (compute (1), 1 + SENTINEL);
+  TEST_VERIFY (get_resolver_ran () != 0);
+  return 0;
+}
+
+#include <support/test-driver.c>
index 9f772614bf60e496fd85c59edf4aee5874439ed2..26fcd1977ea338c6108007e185a5aec3316422e1 100644 (file)
@@ -21,5 +21,9 @@
 
 asm ("memset = __memset_generic");
 asm ("strlen = __strlen_generic");
+#ifndef SHARED
+asm ("memcpy = __memcpy_generic");
+asm ("memmove = __memmove_generic");
+#endif
 
 #endif
index 15c4659853960dfa88713fe20b1b5bbcd8a96314..24529db8a1ed45cb76bb7f754de69f6150008a1a 100644 (file)
@@ -1197,10 +1197,15 @@ void __tls_init_tp (void) attribute_hidden;
 void __libc_setup_tls (void);
 
 # if ENABLE_STATIC_PIE
-/* Relocate static executable with PIE.  */
+/* _dl_relocate_static_pie runs every relocation except IRELATIVE.  The
+   second entry point _dl_relocate_static_pie_ifunc must be invoked
+   afterwards -- but only once the TCB and the stack-protector canary
+   are usable -- to fire the IFUNC resolvers.  */
 extern void _dl_relocate_static_pie (void) attribute_hidden;
+extern void _dl_relocate_static_pie_ifunc (void) attribute_hidden;
 # else
 #  define _dl_relocate_static_pie()
+#  define _dl_relocate_static_pie_ifunc()
 # endif
 #endif
 
index a36f1fa71d54b3e17b54cb8d29b9298ca7c673a1..8896198fb2e65a6e811cf81c7cb3424a553ad686 100644 (file)
 #define _LIBC_START_H
 
 #ifndef SHARED
-/* By default we perform STT_GNU_IFUNC resolution *before* TLS
-   initialization, and this means you cannot, without machine
-   knowledge, access TLS from an IFUNC resolver.  */
+/* Static startup runs ARCH_SETUP_TLS (and writes the stack-protector
+   canary) before ARCH_SETUP_IREL, so IFUNC resolvers can read TLS and
+   stack-protector instrumented resolvers do not fault.  */
+#define ARCH_SETUP_TLS()  __libc_setup_tls ()
 #define ARCH_SETUP_IREL() apply_irel ()
-#define ARCH_SETUP_TLS() __libc_setup_tls ()
-#define ARCH_APPLY_IREL()
 #endif /* ! SHARED  */
 
 #endif /* _LIBC_START_H  */
index 8af00d2846b3dc3042bb4d8121c2fb57a9717a45..1cd204c2f6945a880151e828a9beccacb4c6be49 100644 (file)
@@ -23,6 +23,8 @@
 asm ("memset = __memset_aligned");
 asm ("memcmp = __memcmp_aligned");
 asm ("strlen = __strlen_aligned");
+asm ("memcpy = __memcpy_unaligned");
+asm ("memmove = __memmove_unaligned");
 #endif
 
 #endif
index 4ccd13741bc506ba1b0a2735bcab7bd55d4b99c9..53683ee511bf33373c052d1f3297ddd86c25ba74 100644 (file)
@@ -76,7 +76,6 @@ aarch64_libc_setup_tls (void)
 
 # define ARCH_SETUP_IREL() apply_irel ()
 # define ARCH_SETUP_TLS() aarch64_libc_setup_tls ()
-# define ARCH_APPLY_IREL()
 #endif /* ! SHARED  */
 
 #endif /* _LIBC_START_H  */
diff --git a/sysdeps/unix/sysv/linux/powerpc/libc-start.h b/sysdeps/unix/sysv/linux/powerpc/libc-start.h
deleted file mode 100644 (file)
index 67c361a..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/* PowerPC definitions for libc main startup.
-   Copyright (C) 2017-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/>.  */
-
-#ifndef _LIBC_START_H
-#define _LIBC_START_H
-
-#ifndef SHARED
-/* IREL{,A} must happen after TCB initialization in order to allow IFUNC
-   resolvers to read TCB fields, e.g. hwcap and at_platform.  */
-#define ARCH_SETUP_IREL()
-#define ARCH_SETUP_TLS() __libc_setup_tls ()
-#define ARCH_APPLY_IREL() apply_irel ()
-#endif /* ! SHARED  */
-
-#endif /* _LIBC_START_H  */
index 7e38fdef873c6f0b27a9ac43f654a249bbe286ad..036e0e472a8b36c4b04a3ac8b0393fe8d0a55943 100644 (file)
@@ -18,7 +18,6 @@
 
 #ifndef SHARED
 # define ARCH_SETUP_IREL() apply_irel ()
-# define ARCH_APPLY_IREL()
 # ifdef __CET__
 /* Get CET features enabled in the static executable.  */
 
index b607e525f2ce8f48a5468a4347055eec4349d0ee..1f3ca20307c6d90173479f24a66bb88d521fd12c 100644 (file)
@@ -54,6 +54,25 @@ asm ("memcmp = " HAVE_MEMCMP_IFUNC_GENERIC);
 
 asm ("strlen = " HAVE_STRCMP_IFUNC_GENERIC);
 
+#if MINIMUM_X86_ISA_LEVEL >= 4
+# define HAVE_MEMCPY_IFUNC_GENERIC  "__memcpy_evex_unaligned"
+# define HAVE_MEMMOVE_IFUNC_GENERIC "__memmove_evex_unaligned"
+# define HAVE_MEMPCPY_IFUNC_GENERIC "__mempcpy_evex_unaligned"
+#elif MINIMUM_X86_ISA_LEVEL == 3
+# define HAVE_MEMCPY_IFUNC_GENERIC  "__memcpy_avx_unaligned"
+# define HAVE_MEMMOVE_IFUNC_GENERIC "__memmove_avx_unaligned"
+# define HAVE_MEMPCPY_IFUNC_GENERIC "__mempcpy_avx_unaligned"
+#else
+# define HAVE_MEMCPY_IFUNC_GENERIC  "__memcpy_sse2_unaligned"
+# define HAVE_MEMMOVE_IFUNC_GENERIC "__memmove_sse2_unaligned"
+# define HAVE_MEMPCPY_IFUNC_GENERIC "__mempcpy_sse2_unaligned"
+#endif
+
+asm ("memcpy  = " HAVE_MEMCPY_IFUNC_GENERIC);
+asm ("memmove = " HAVE_MEMMOVE_IFUNC_GENERIC);
+asm ("mempcpy = " HAVE_MEMPCPY_IFUNC_GENERIC);
+asm ("__mempcpy = " HAVE_MEMPCPY_IFUNC_GENERIC);
+
 #endif /* SHARED */
 
 #endif