[v8,3/6] Fix assert during static startup (BZ 33326)

Message ID 20260318193454.2466865-4-adhemerval.zanella@linaro.org (mailing list archive)
State New
Headers
Series elf: Allow RPATH/RUNPATH for static-pie |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed

Commit Message

Adhemerval Zanella Netto March 18, 2026, 7:34 p.m. UTC
  The BZ#33326 testcase triggers an assertion during process startup,
which results in a segmentation fault instead of an error message
and process termination with a SIGABRT.  The assert issues
__libc_message_impl, which in turn might call string functions
depending on the ABI (strchrnul, strlen, memcpy/mempcpy), system
calls (writev and mmap), and finally the abort call.

Since each function may be called during process startup, before
self-relocation and/or the thread pointer being set up, the
functions should be built without stack protection.

The dl-symbol-redir-ifunc.h is also expanded to cover
strlen/memcpy/mempcpy/strchrnul on multiple architectures that implement
ifunc.

On i386, syscalls should not use the vDSO ("call *%gs:SYSINFO_OFFSET")
during program statuup because thread pointer is not yet initialized.  This
requires __raise_direct, _dl_writev, and _dl_mmap to use I386_USE_SYSENTER.
To avoid possible sysdep.h I386_USE_SYSENTER redefinition, all
implementations are done on their own translation unit.

The s390x requires not calling libgcc for the generic strchrnul-c (via
ctz/clz macros) due to a libgcc issue: the __clzdi2 builtin is not built
against a hidden reference to __clz_tab, which creates a GOT reference
for static-pie (and it cannot be called before self-relocation).

Creating a test case is challenging. For static-pie, the assert is only
called for ill-formed ELF files on elf_get_dynamic_info and by some targets
on ELF_DYNAMIC_RELOCATE (although not all targets use assert in their
dl-machine.h).  Some targets also issue __libc_fatal on ARCH_SETUP_IREL,
but also only for ill-formatted ELF files.

The test employs a different strategy and overrides the __tunables_init
symbol, which is invoked immediately before self-relocation and TLS setup.
The test is built with -Wl,-z,muldefs to avoid linker issues.

I checked on aarch64, x86_64, i686, s390x (qemu), sparc (qemu),
mips64el (qemu), armhf, riscv, and powerpc.
---
 assert/Makefile                               |  5 +++
 elf/Makefile                                  | 17 +++++++-
 elf/tst-assert-startup-static.c               | 40 +++++++++++++++++++
 libio/Makefile                                |  5 +++
 stdlib/Makefile                               |  5 +++
 string/Makefile                               |  1 +
 .../aarch64/multiarch/dl-symbol-redir-ifunc.h |  1 +
 sysdeps/aarch64/multiarch/memcpy_generic.S    |  4 ++
 .../armv7/multiarch/dl-symbol-redir-ifunc.h   | 27 +++++++++++++
 sysdeps/generic/dl-mmap.h                     | 34 ++++++++++++++++
 .../lp64/multiarch/dl-symbol-redir-ifunc.h    |  2 +
 sysdeps/posix/libc_fatal.c                    | 12 ++++--
 .../powerpc32/power4/multiarch/Makefile       |  5 +++
 .../be/multiarch/dl-symbol-redir-ifunc.h      | 27 +++++++++++++
 .../le/multiarch/dl-symbol-redir-ifunc.h      |  1 +
 sysdeps/powerpc/powerpc64/multiarch/Makefile  |  1 +
 .../riscv/multiarch/dl-symbol-redir-ifunc.h   |  1 +
 sysdeps/s390/Makefile                         |  5 +++
 .../s390/multiarch/dl-symbol-redir-ifunc.h    |  4 ++
 sysdeps/s390/string-bitops.h                  | 27 +++++++++++++
 .../sparcv9/multiarch/dl-symbol-redir-ifunc.h |  3 ++
 .../sparc64/multiarch/dl-symbol-redir-ifunc.h |  3 ++
 sysdeps/unix/sysv/linux/Makefile              | 12 ++++++
 sysdeps/unix/sysv/linux/i386/Makefile         | 14 +++++++
 sysdeps/unix/sysv/linux/i386/dl-mmap.c        | 36 +++++++++++++++++
 sysdeps/unix/sysv/linux/i386/dl-mmap.h        | 27 +++++++++++++
 sysdeps/unix/sysv/linux/i386/dl-writev.c      | 32 +++++++++++++++
 sysdeps/unix/sysv/linux/i386/dl-writev.h      | 15 ++++---
 sysdeps/unix/sysv/linux/i386/raise_direct.c   | 26 ++++++++++++
 .../unix/sysv/linux/riscv/multiarch/Makefile  |  7 ++++
 .../x86_64/multiarch/dl-symbol-redir-ifunc.h  | 32 +++++++++++++++
 31 files changed, 422 insertions(+), 9 deletions(-)
 create mode 100644 elf/tst-assert-startup-static.c
 create mode 100644 sysdeps/arm/armv7/multiarch/dl-symbol-redir-ifunc.h
 create mode 100644 sysdeps/generic/dl-mmap.h
 create mode 100644 sysdeps/powerpc/powerpc64/be/multiarch/dl-symbol-redir-ifunc.h
 create mode 100644 sysdeps/s390/string-bitops.h
 create mode 100644 sysdeps/unix/sysv/linux/i386/dl-mmap.c
 create mode 100644 sysdeps/unix/sysv/linux/i386/dl-mmap.h
 create mode 100644 sysdeps/unix/sysv/linux/i386/dl-writev.c
 create mode 100644 sysdeps/unix/sysv/linux/i386/raise_direct.c
  

Comments

Florian Weimer March 20, 2026, 11:08 a.m. UTC | #1
* Adhemerval Zanella:

> The BZ#33326 testcase triggers an assertion during process startup,
> which results in a segmentation fault instead of an error message
> and process termination with a SIGABRT.  The assert issues
> __libc_message_impl, which in turn might call string functions
> depending on the ABI (strchrnul, strlen, memcpy/mempcpy), system
> calls (writev and mmap), and finally the abort call.
>
> Since each function may be called during process startup, before
> self-relocation and/or the thread pointer being set up, the
> functions should be built without stack protection.
>
> The dl-symbol-redir-ifunc.h is also expanded to cover
> strlen/memcpy/mempcpy/strchrnul on multiple architectures that implement
> ifunc.
>
> On i386, syscalls should not use the vDSO ("call *%gs:SYSINFO_OFFSET")
> during program statuup because thread pointer is not yet initialized.  This

Typo: sta[r]tu[]p

Perhaps: becahse [the] thread pointer

> diff --git a/elf/Makefile b/elf/Makefile
> index 7f039b5563..29d771ef7a 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile

> +tests-special += \
> +  $(objpfx)tst-assert-startup-static.out \
> +  # tests-special
> +

Uhm, this doesn't seem to be in a region of the makefile that is
conditional on run-built-tests?  So it would cause build-many-glibcs.py
failures?

> @@ -3532,7 +3537,6 @@ $(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin
>  
>  $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so
>  
> -
>  ifeq ($(run-built-tests),yes)

Spurious whitespace change.

> diff --git a/elf/tst-assert-startup-static.c b/elf/tst-assert-startup-static.c
> new file mode 100644
> index 0000000000..a3312cd9e5
> --- /dev/null
> +++ b/elf/tst-assert-startup-static.c

> +void __tunables_init (char **env)
> +{
> +/* The assert called by the loader/startup issues __libc_assert_fail instead
> +   of __libc_assert, and __libc_assert_fail does issues the translation
> +   routines (which would require additional handling to be called at this

Maybe: … instead of __[]assert[_fail], and __libc_assert_fail does [not]
  perform [] translation[] (which …

Thanks,
Florian
  
Adhemerval Zanella Netto March 30, 2026, 3:15 p.m. UTC | #2
On 20/03/26 08:08, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>> The BZ#33326 testcase triggers an assertion during process startup,
>> which results in a segmentation fault instead of an error message
>> and process termination with a SIGABRT.  The assert issues
>> __libc_message_impl, which in turn might call string functions
>> depending on the ABI (strchrnul, strlen, memcpy/mempcpy), system
>> calls (writev and mmap), and finally the abort call.
>>
>> Since each function may be called during process startup, before
>> self-relocation and/or the thread pointer being set up, the
>> functions should be built without stack protection.
>>
>> The dl-symbol-redir-ifunc.h is also expanded to cover
>> strlen/memcpy/mempcpy/strchrnul on multiple architectures that implement
>> ifunc.
>>
>> On i386, syscalls should not use the vDSO ("call *%gs:SYSINFO_OFFSET")
>> during program statuup because thread pointer is not yet initialized.  This
> 
> Typo: sta[r]tu[]p
> 
> Perhaps: becahse [the] thread pointer

Ack.

> 
>> diff --git a/elf/Makefile b/elf/Makefile
>> index 7f039b5563..29d771ef7a 100644
>> --- a/elf/Makefile
>> +++ b/elf/Makefile
> 
>> +tests-special += \
>> +  $(objpfx)tst-assert-startup-static.out \
>> +  # tests-special
>> +
> 
> Uhm, this doesn't seem to be in a region of the makefile that is
> conditional on run-built-tests?  So it would cause build-many-glibcs.py
> failures?

Indeed, I will add a run-built-tests if check here.

> 
>> @@ -3532,7 +3537,6 @@ $(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin
>>  
>>  $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so
>>  
>> -
>>  ifeq ($(run-built-tests),yes)
> 
> Spurious whitespace change.

Ack.

> 
>> diff --git a/elf/tst-assert-startup-static.c b/elf/tst-assert-startup-static.c
>> new file mode 100644
>> index 0000000000..a3312cd9e5
>> --- /dev/null
>> +++ b/elf/tst-assert-startup-static.c
> 
>> +void __tunables_init (char **env)
>> +{
>> +/* The assert called by the loader/startup issues __libc_assert_fail instead
>> +   of __libc_assert, and __libc_assert_fail does issues the translation
>> +   routines (which would require additional handling to be called at this
> 
> Maybe: … instead of __[]assert[_fail], and __libc_assert_fail does [not]
>   perform [] translation[] (which …
> 

Ack.

Are you ok with the above remarks fixed? I will move the last patch 
(elf: Raise SIGABRT for assert within ld.so) out of this serie since
it might require some more discussion.
  

Patch

diff --git a/assert/Makefile b/assert/Makefile
index 4c253a344a..541929c3b6 100644
--- a/assert/Makefile
+++ b/assert/Makefile
@@ -33,6 +33,11 @@  routines := \
   assert-perr \
   # routines
 
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-__libc_assert_fail.o = $(no-stack-protector)
+CFLAGS-__libc_assert_fail.op = $(no-stack-protector)
+
 tests := \
   test-assert \
   test-assert-2 \
diff --git a/elf/Makefile b/elf/Makefile
index 7f039b5563..29d771ef7a 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -277,6 +277,7 @@  tests-static-normal := \
   # tests-static-normal
 
 tests-static-internal := \
+  tst-assert-startup-static \
   tst-dl-printf-static \
   tst-dl_find_object-static \
   tst-env-setuid-tunables \
@@ -288,6 +289,10 @@  tests-static-internal := \
   tst-tunables-enable_secure \
   # tests-static-internal
 
+tests-special += \
+  $(objpfx)tst-assert-startup-static.out \
+  # tests-special
+
 CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
 tst-tls1-static-non-pie-no-pie = yes
 
@@ -3532,7 +3537,6 @@  $(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin
 
 $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so
 
-
 ifeq ($(run-built-tests),yes)
 $(objpfx)tst-tls-debug-recursive.out: tst-tls-debug-recursive.sh \
 			      $(objpfx)tst-recursive-tls \
@@ -3554,3 +3558,14 @@  $(objpfx)tst-dl-debug-exclude.out: tst-dl-debug-exclude.sh \
 		 $(objpfx)tst-recursive-tls > $@; \
 	$(evaluate-test)
 endif
+
+CFLAGS-tst-assert-startup-static.c += $(no-stack-protector)
+LDFLAGS-tst-assert-startup-static = -Wl,-z,muldefs
+
+$(objpfx)tst-assert-startup-static.out: $(objpfx)tst-assert-startup-static
+	$(test-program-cmd-before-env) \
+		$(run-program-env) \
+		$< > $@ 2>&1; echo "status: $$?" >> $@; \
+	grep -q 'Fatal glibc error: tst-assert-startup-static' $@ \
+	  && grep -q '^status: 134$$' $@; \
+	  $(evaluate-test)
diff --git a/elf/tst-assert-startup-static.c b/elf/tst-assert-startup-static.c
new file mode 100644
index 0000000000..a3312cd9e5
--- /dev/null
+++ b/elf/tst-assert-startup-static.c
@@ -0,0 +1,40 @@ 
+/* Check if assert work during program 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/>.  */
+
+#include <stdlib.h>
+#include <assert.h>
+
+/* The __tunables_init is called just before self-relocation and TLS setup,
+   and the __libc_assert_fail is used internally for assert() calls.  */
+extern _Noreturn __typeof (__assert_fail) __libc_assert_fail;
+
+void __tunables_init (char **env)
+{
+/* The assert called by the loader/startup issues __libc_assert_fail instead
+   of __libc_assert, and __libc_assert_fail does issues the translation
+   routines (which would require additional handling to be called at this
+   point, like disable stack protection).  So issue the internal routine
+   directly, instead of using assert here.  */
+  __libc_assert_fail ("error", __FILE__, __LINE__, __func__);
+}
+
+int main (int argc, char *argv[])
+{
+  /* Fail with a different error code than abort.  */
+  exit (EXIT_FAILURE);
+}
diff --git a/libio/Makefile b/libio/Makefile
index 08e1e0ec25..ec6b78c495 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -185,6 +185,11 @@  endif
 
 CPPFLAGS += $(libio-mtsafe)
 
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-libc_fatal.o = $(no-stack-protector)
+CFLAGS-libc_fatal.op = $(no-stack-protector)
+
 # Support for exception handling.
 CFLAGS-fileops.c += -fexceptions
 CFLAGS-fputc.c += -fexceptions
diff --git a/stdlib/Makefile b/stdlib/Makefile
index 04596b53b1..8a39121777 100644
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -528,6 +528,11 @@  generated += \
   tst-putenvmod.so \
   # generated
 
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-abort.o = $(no-stack-protector)
+CFLAGS-abort.op = $(no-stack-protector)
+
 CFLAGS-bsearch.c += $(uses-callbacks)
 CFLAGS-qsort.c += $(uses-callbacks)
 CFLAGS-system.c += -fexceptions
diff --git a/string/Makefile b/string/Makefile
index aa0b0c2f57..969726cccb 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -287,6 +287,7 @@  CFLAGS-wordcopy.c += $(no-stack-protector)
 CFLAGS-strncmp.c += $(no-stack-protector)
 CFLAGS-memset.c += $(no-stack-protector)
 CFLAGS-strlen.c += $(no-stack-protector)
+CFLAGS-strchrnul.c += $(no-stack-protector)
 
 ifeq ($(run-built-tests),yes)
 $(objpfx)tst-svc-cmp.out: tst-svc.expect $(objpfx)tst-svc.out
diff --git a/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h
index 9f772614bf..a686fd1f92 100644
--- a/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h
@@ -19,6 +19,7 @@ 
 #ifndef _DL_IFUNC_GENERIC_H
 #define _DL_IFUNC_GENERIC_H
 
+asm ("memcpy = __memcpy_generic");
 asm ("memset = __memset_generic");
 asm ("strlen = __strlen_generic");
 
diff --git a/sysdeps/aarch64/multiarch/memcpy_generic.S b/sysdeps/aarch64/multiarch/memcpy_generic.S
index c6d09081f4..d622268305 100644
--- a/sysdeps/aarch64/multiarch/memcpy_generic.S
+++ b/sysdeps/aarch64/multiarch/memcpy_generic.S
@@ -42,3 +42,7 @@ 
 #endif
 
 #include "../memcpy.S"
+
+#if IS_IN (rtld)
+strong_alias (memcpy, __memcpy_generic)
+#endif
diff --git a/sysdeps/arm/armv7/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/arm/armv7/multiarch/dl-symbol-redir-ifunc.h
new file mode 100644
index 0000000000..4a6c81aa8d
--- /dev/null
+++ b/sysdeps/arm/armv7/multiarch/dl-symbol-redir-ifunc.h
@@ -0,0 +1,27 @@ 
+/* Symbol redirection for loader/static initialization code.  Linux/ARM
+   version.
+   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/>.  */
+
+#ifndef _DL_IFUNC_GENERIC_H
+#define _DL_IFUNC_GENERIC_H
+
+#ifndef __ARM_NEON__
+asm ("memcpy = __memcpy_vfp");
+#endif
+
+#endif
diff --git a/sysdeps/generic/dl-mmap.h b/sysdeps/generic/dl-mmap.h
new file mode 100644
index 0000000000..f786be0930
--- /dev/null
+++ b/sysdeps/generic/dl-mmap.h
@@ -0,0 +1,34 @@ 
+/* mmap wrapper for dynamic loader.
+   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/>.  */
+
+#ifndef _DL_MMAP_H
+#define _DL_MMAP_H
+
+#include <sys/mman.h>
+
+/* This mmap call is used to allocate some memory to backup assert() messages
+   before TLS setup is done (which setup the thread pointer used by some ABIs
+   to issues syscalls).  */
+
+static inline void *
+_dl_mmap (void *addr, size_t len, int prot, int flags)
+{
+  return __mmap (addr, len, prot, flags, -1, 0);
+}
+
+#endif
diff --git a/sysdeps/loongarch/lp64/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/loongarch/lp64/multiarch/dl-symbol-redir-ifunc.h
index 8af00d2846..43f50f8bcc 100644
--- a/sysdeps/loongarch/lp64/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/loongarch/lp64/multiarch/dl-symbol-redir-ifunc.h
@@ -20,8 +20,10 @@ 
 #define _DL_IFUNC_GENERIC_H
 
 #ifndef SHARED
+asm ("memcpy = __memcpy_aligned");
 asm ("memset = __memset_aligned");
 asm ("memcmp = __memcmp_aligned");
+asm ("__strchrnul = __strchrnul_aligned");
 asm ("strlen = __strlen_aligned");
 #endif
 
diff --git a/sysdeps/posix/libc_fatal.c b/sysdeps/posix/libc_fatal.c
index 3f0e302b5e..1ff20d4feb 100644
--- a/sysdeps/posix/libc_fatal.c
+++ b/sysdeps/posix/libc_fatal.c
@@ -16,7 +16,12 @@ 
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+/* Mark symbols hidden in static PIE for early self relocation to work.  */
+#if BUILD_PIE_DEFAULT
+# pragma GCC visibility push(hidden)
+#endif
 #include <dl-writev.h>
+#include <dl-mmap.h>
 #include <assert.h>
 #include <ldsodefs.h>
 #include <setvmaname.h>
@@ -24,6 +29,7 @@ 
 #include <stdio.h>
 #include <sys/uio.h>
 #include <unistd.h>
+#include <dl-symbol-redir-ifunc.h>
 
 #ifdef FATAL_PREPARE_INCLUDE
 #include FATAL_PREPARE_INCLUDE
@@ -113,9 +119,9 @@  __libc_message_impl (const char *vma_name, const char *fmt, ...)
 
       total = ALIGN_UP (total + sizeof (struct abort_msg_s) + 1,
 			GLRO(dl_pagesize));
-      struct abort_msg_s *buf = __mmap (NULL, total,
-					PROT_READ | PROT_WRITE,
-					MAP_ANON | MAP_PRIVATE, -1, 0);
+      struct abort_msg_s *buf = _dl_mmap (NULL, total,
+					  PROT_READ | PROT_WRITE,
+					  MAP_ANON | MAP_PRIVATE);
       if (__glibc_likely (buf != MAP_FAILED))
 	{
 	  buf->size = total;
diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/Makefile b/sysdeps/powerpc/powerpc32/power4/multiarch/Makefile
index 3a49b855ca..60ba2e50d2 100644
--- a/sysdeps/powerpc/powerpc32/power4/multiarch/Makefile
+++ b/sysdeps/powerpc/powerpc32/power4/multiarch/Makefile
@@ -11,4 +11,9 @@  sysdep_routines += memcpy-power7 memcpy-a2 memcpy-power6 memcpy-cell \
 		   strchr-power7 strchr-ppc32 \
 		   wordcopy-power7 wordcopy-ppc32 \
 		   memmove-power7 memmove-ppc
+
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-strchrnul-ppc32.o = $(no-stack-protector)
+CFLAGS-strchrnul-ppc32.op = $(no-stack-protector)
 endif
diff --git a/sysdeps/powerpc/powerpc64/be/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/powerpc/powerpc64/be/multiarch/dl-symbol-redir-ifunc.h
new file mode 100644
index 0000000000..560f57366b
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/be/multiarch/dl-symbol-redir-ifunc.h
@@ -0,0 +1,27 @@ 
+/* Symbol rediretion for loader/static initialization code.
+   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/>.  */
+
+#ifndef _DL_IFUNC_GENERIC_H
+#define _DL_IFUNC_GENERIC_H
+
+#ifndef SHARED
+asm ("__mempcpy = __mempcpy_ppc");
+asm ("__strchrnul = __strchrnul_ppc");
+#endif
+
+#endif
diff --git a/sysdeps/powerpc/powerpc64/le/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/powerpc/powerpc64/le/multiarch/dl-symbol-redir-ifunc.h
index 4680092cd8..03f6f12032 100644
--- a/sysdeps/powerpc/powerpc64/le/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/powerpc/powerpc64/le/multiarch/dl-symbol-redir-ifunc.h
@@ -21,5 +21,6 @@ 
 
 asm ("memset = __memset_power8");
 asm ("__mempcpy = __mempcpy_power7");
+asm ("__strchrnul = __strchrnul_power8");
 
 #endif
diff --git a/sysdeps/powerpc/powerpc64/multiarch/Makefile b/sysdeps/powerpc/powerpc64/multiarch/Makefile
index c9178223a8..0437a05a89 100644
--- a/sysdeps/powerpc/powerpc64/multiarch/Makefile
+++ b/sysdeps/powerpc/powerpc64/multiarch/Makefile
@@ -40,3 +40,4 @@  endif
 
 # Called during static initialization
 CFLAGS-strncmp-ppc64.c += $(no-stack-protector)
+CFLAGS-strchrnul-ppc64.c += $(no-stack-protector)
diff --git a/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
index 24b78711e3..69a0790838 100644
--- a/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/riscv/multiarch/dl-symbol-redir-ifunc.h
@@ -21,6 +21,7 @@ 
 
 #ifndef SHARED
 asm ("memset = __memset_generic");
+asm ("memcpy = __memcpy_generic");
 #endif
 
 #endif
diff --git a/sysdeps/s390/Makefile b/sysdeps/s390/Makefile
index 985b4f25ee..078c0d165d 100644
--- a/sysdeps/s390/Makefile
+++ b/sysdeps/s390/Makefile
@@ -104,6 +104,11 @@  routines_no_fortify += \
   # routines_no_fortify
 endif
 
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-strchrnul-c.o = $(no-stack-protector)
+CFLAGS-strchrnul-c.op = $(no-stack-protector)
+
 ifeq ($(subdir),wcsmbs)
 sysdep_routines += wcslen wcslen-vx wcslen-c \
 		   wcsnlen wcsnlen-vx wcsnlen-c \
diff --git a/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
index a128cd05bd..3721f3d95b 100644
--- a/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
@@ -21,11 +21,15 @@ 
 
 #include <ifunc-memset.h>
 #include <ifunc-memcmp.h>
+#include <ifunc-strchrnul.h>
 
 #define IFUNC_SYMBOL_STR1(s)	#s
 #define IFUNC_SYMBOL_STR(s)	IFUNC_SYMBOL_STR1(s)
 
+#ifndef SHARED
 asm ("memset = " IFUNC_SYMBOL_STR(MEMSET_DEFAULT));
 asm ("memcmp = " IFUNC_SYMBOL_STR(MEMCMP_DEFAULT));
+asm ("__strchrnul = " IFUNC_SYMBOL_STR(STRCHRNUL_DEFAULT));
+#endif
 
 #endif
diff --git a/sysdeps/s390/string-bitops.h b/sysdeps/s390/string-bitops.h
new file mode 100644
index 0000000000..f660d019c7
--- /dev/null
+++ b/sysdeps/s390/string-bitops.h
@@ -0,0 +1,27 @@ 
+/* Zero byte detection, define whether to use stdbit.h
+   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/>.  */
+
+/* s390x support static-pie and the libgcc implementation for
+   __builtin_clzl/__builtin_ctzl might access extern data that is not marked
+   as hidden, which creates additiona GOT access that is used before
+   self-relocation.  */
+#if __ARCH__ > 6
+# define HAVE_BITOPTS_WORKING 1
+#else
+# define HAVE_BITOPTS_WORKING 0
+#endif
diff --git a/sysdeps/sparc/sparc32/sparcv9/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/sparc/sparc32/sparcv9/multiarch/dl-symbol-redir-ifunc.h
index ffe1eee87d..1a76efd542 100644
--- a/sysdeps/sparc/sparc32/sparcv9/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/sparc/sparc32/sparcv9/multiarch/dl-symbol-redir-ifunc.h
@@ -19,6 +19,9 @@ 
 #ifndef _DL_IFUNC_GENERIC_H
 #define _DL_IFUNC_GENERIC_H
 
+#ifndef SHARED
 asm ("memset = __memset_ultra1");
+asm ("memcpy = __memcpy_ultra1");
+#endif
 
 #endif
diff --git a/sysdeps/sparc/sparc64/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/sparc/sparc64/multiarch/dl-symbol-redir-ifunc.h
index ffe1eee87d..1a76efd542 100644
--- a/sysdeps/sparc/sparc64/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/sparc/sparc64/multiarch/dl-symbol-redir-ifunc.h
@@ -19,6 +19,9 @@ 
 #ifndef _DL_IFUNC_GENERIC_H
 #define _DL_IFUNC_GENERIC_H
 
+#ifndef SHARED
 asm ("memset = __memset_ultra1");
+asm ("memcpy = __memcpy_ultra1");
+#endif
 
 #endif
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 8797c5c9b3..18727605ce 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -114,6 +114,11 @@  sysdep_routines += \
   xstat \
   xstat64 \
   # sysdep_routines
+#
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-setvmaname.o = $(no-stack-protector)
+CFLAGS-setvmaname.op = $(no-stack-protector)
 
 CFLAGS-gethostid.c = -fexceptions
 CFLAGS-tee.c = -fexceptions -fasynchronous-unwind-tables
@@ -459,6 +464,13 @@  sysdep_routines += \
   raise_direct \
   # sysdep_routines
 
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-raise.o = $(no-stack-protector)
+CFLAGS-raise.op = $(no-stack-protector)
+CFLAGS-raise_direct.o = $(no-stack-protector)
+CFLAGS-raise_direct.op = $(no-stack-protector)
+
 tests-special += \
   $(objpfx)tst-signal-numbers.out \
   # tests-special
diff --git a/sysdeps/unix/sysv/linux/i386/Makefile b/sysdeps/unix/sysv/linux/i386/Makefile
index f1f8c3f44c..9a70905df8 100644
--- a/sysdeps/unix/sysv/linux/i386/Makefile
+++ b/sysdeps/unix/sysv/linux/i386/Makefile
@@ -28,3 +28,17 @@  ifeq ($(subdir),rt)
 librt-routines += sysdep
 librt-shared-only-routines += sysdep
 endif
+
+ifeq ($(subdir),elf)
+sysdep_routines += \
+  dl-mmap \
+  dl-writev \
+  # sysdep-routines
+
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-dl-mmap.o = $(no-stack-protector)
+CFLAGS-dl-mmap.op = $(no-stack-protector)
+CFLAGS-dl-writev.o = $(no-stack-protector)
+CFLAGS-dl-writev.op = $(no-stack-protector)
+endif
diff --git a/sysdeps/unix/sysv/linux/i386/dl-mmap.c b/sysdeps/unix/sysv/linux/i386/dl-mmap.c
new file mode 100644
index 0000000000..ab6f96300f
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/dl-mmap.c
@@ -0,0 +1,36 @@ 
+/* mmap wrapper for dynamic loader.  Linux/i386 version.
+   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 mmap call is used to allocate some memory to backup assert() messages
+   before TLS setup is done, so it can not use "call *%gs:SYSINFO_OFFSET"
+   during startup in static PIE.  */
+#if BUILD_PIE_DEFAULT
+# define I386_USE_SYSENTER 0
+#endif
+
+#include <sys/mman.h>
+#include <dl-mmap.h>
+#include <mmap_internal.h>
+#include <sysdep.h>
+
+void *
+_dl_mmap (void *addr, size_t len, int prot, int flags)
+{
+  return (void *) MMAP_CALL (mmap2, addr, len, prot, flags, -1, 0);
+}
+
diff --git a/sysdeps/unix/sysv/linux/i386/dl-mmap.h b/sysdeps/unix/sysv/linux/i386/dl-mmap.h
new file mode 100644
index 0000000000..291f92ee14
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/dl-mmap.h
@@ -0,0 +1,27 @@ 
+/* mmap wrapper for dynamic loader.
+   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/>.  */
+
+#ifndef _DL_MMAP_H
+#define _DL_MMAP_H
+
+/* i386 requires out-of-line implementation because it sets
+   I386_USE_SYSENTER to 0 to avoid use the vDSO.  */
+void * _dl_mmap (void *addr, size_t len, int prot, int flags)
+  attribute_hidden;
+
+#endif
diff --git a/sysdeps/unix/sysv/linux/i386/dl-writev.c b/sysdeps/unix/sysv/linux/i386/dl-writev.c
new file mode 100644
index 0000000000..ef6a6a20b8
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/dl-writev.c
@@ -0,0 +1,32 @@ 
+/* writev wrapper for the dynamic linker.  Linux/i386 version.
+   Copyright (C) 2013-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 writev call is used to assert() before TLS setup is done, so it can
+   not use "call *%gs:SYSINFO_OFFSET" during startup in static PIE.  */
+#if BUILD_PIE_DEFAULT
+# define I386_USE_SYSENTER 0
+#endif
+
+#include <dl-writev.h>
+#include <sysdep.h>
+
+ssize_t
+_dl_writev (int fd, const struct iovec *iov, size_t niov)
+{
+  return INTERNAL_SYSCALL_CALL (writev, fd, iov, niov);
+}
diff --git a/sysdeps/unix/sysv/linux/i386/dl-writev.h b/sysdeps/unix/sysv/linux/i386/dl-writev.h
index 8327d32374..d92c74d712 100644
--- a/sysdeps/unix/sysv/linux/i386/dl-writev.h
+++ b/sysdeps/unix/sysv/linux/i386/dl-writev.h
@@ -16,9 +16,14 @@ 
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
-#if BUILD_PIE_DEFAULT
-/* Can't use "call *%gs:SYSINFO_OFFSET" during startup in static PIE.  */
-# define I386_USE_SYSENTER 0
-#endif
+#ifndef _DL_WRITEV_H
+#define _DL_WRITEV_H
 
-#include <sysdeps/unix/sysv/linux/dl-writev.h>
+#include <sys/uio.h>
+
+/* i386 requires out-of-line implementation because it sets
+   I386_USE_SYSENTER to 0 to avoid use the vDSO.  */
+ssize_t _dl_writev (int fd, const struct iovec *iov, size_t niov)
+  attribute_hidden;
+
+#endif
diff --git a/sysdeps/unix/sysv/linux/i386/raise_direct.c b/sysdeps/unix/sysv/linux/i386/raise_direct.c
new file mode 100644
index 0000000000..b23b8e5386
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/raise_direct.c
@@ -0,0 +1,26 @@ 
+/* Internal function to send a signal to itself.  Linux/i386 version.
+   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 is called from abort() (issued by assert()) before TLS setup is done,
+   so it can not use "call *%gs:SYSINFO_OFFSET" during startup in static
+   PIE.  */
+#if BUILD_PIE_DEFAULT
+# define I386_USE_SYSENTER 0
+#endif
+
+#include <sysdeps/unix/sysv/linux/raise_direct.c>
diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
index a865090a53..484ff77f7d 100644
--- a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
+++ b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
@@ -8,6 +8,13 @@  sysdep_routines += \
   memset-vector \
   # sysdep_routines
 
+# Called during static library initialization, so turn stack-protection
+# off for non-shared builds.
+CFLAGS-memset-generic.o = $(no-stack-protector)
+CFLAGS-memset-generic.op = $(no-stack-protector)
+CFLAGS-memcpy-generic.o = $(no-stack-protector)
+CFLAGS-memcpy-generic.op = $(no-stack-protector)
+
 CFLAGS-memcpy_noalignment.c += -mno-strict-align
 # Called during static initialization
 CFLAGS-memset-generic.c += $(no-stack-protector)
diff --git a/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
index b607e525f2..15968896c0 100644
--- a/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
@@ -54,6 +54,38 @@  asm ("memcmp = " HAVE_MEMCMP_IFUNC_GENERIC);
 
 asm ("strlen = " HAVE_STRCMP_IFUNC_GENERIC);
 
+#if MINIMUM_X86_ISA_LEVEL >= 4
+# define HAVE_MEMPCPY_IFUNC_GENERIC "__mempcpy_evex_unaligned"
+#elif MINIMUM_X86_ISA_LEVEL == 3
+# define HAVE_MEMPCPY_IFUNC_GENERIC "__mempcpy_avx_unaligned"
+#else
+# define HAVE_MEMPCPY_IFUNC_GENERIC "__mempcpy_sse2_unaligned"
+#endif
+
+asm ("__mempcpy = " HAVE_MEMPCPY_IFUNC_GENERIC);
+
+
+#if MINIMUM_X86_ISA_LEVEL >= 4
+# define HAVE_STRCHRNUL_IFUNC_GENERIC "__strchrnul_evex"
+#elif MINIMUM_X86_ISA_LEVEL == 3
+# define HAVE_STRCHRNUL_IFUNC_GENERIC "__strchrnul_avx2"
+#else
+# define HAVE_STRCHRNUL_IFUNC_GENERIC "__strchrnul_sse2"
+#endif
+
+asm ("__strchrnul = " HAVE_STRCHRNUL_IFUNC_GENERIC);
+
+
+#if MINIMUM_X86_ISA_LEVEL >= 4
+# define HAVE_STRLEN_IFUNC_GENERIC "__strlen_evex"
+#elif MINIMUM_X86_ISA_LEVEL == 3
+# define HAVE_STRLEN_IFUNC_GENERIC "__strlen_avx2"
+#else
+# define HAVE_STRLEN_IFUNC_GENERIC "__strlen_sse2"
+#endif
+
+asm ("strlen = " HAVE_STRLEN_IFUNC_GENERIC);
+
 #endif /* SHARED */
 
 #endif