[PATCH v11 3/5] Fix assert during static startup (BZ 33326)
Adhemerval Zanella <[email protected]> Tue, 28 Jul 2026 18:13:21 -0300
| Newsgroups | gmane.comp.lib.glibc.alpha |
|---|---|
| Message-ID | <[email protected]> |
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.
The dl-symbol-redir-ifunc.h is also expanded to cover strchrnul on
x86_64, s390, powerpc64 (both endianness) and loongarch, mempcpy on
powerpc64be, and memcpy on aarch64. On s390 the redirection is only
issued if the ifunc variant is built, since strchrnul-c.c only renames
the C implementation to STRCHRNUL_DEFAULT when HAVE_STRCHRNUL_IFUNC is
set.
The buffer that backs up the assert message is now allocated through
_dl_mmap, which issues the syscall directly instead of calling __mmap
(setting errno on failure requires the thread pointer).
The abort call now issues __raise_direct instead of raise (the Hurd
port aliases __raise_direct to raise).
On i386, syscalls should not use the vDSO during program startup because
the thread pointer is not yet initialized. This requires __raise_direct,
_dl_writev, and _dl_mmap to be built with I386_USE_SYSENTER set to 0.
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 | 12 ++++++
elf/tst-assert-startup-static.c | 43 +++++++++++++++++++
libio/Makefile | 5 +++
stdlib/Makefile | 5 +++
stdlib/abort.c | 2 +-
string/Makefile | 22 +++++++---
.../aarch64/multiarch/dl-symbol-redir-ifunc.h | 2 +-
sysdeps/aarch64/multiarch/memcpy_generic.S | 4 ++
sysdeps/generic/dl-mmap.h | 37 ++++++++++++++++
sysdeps/htl/raise.c | 2 +
.../lp64/multiarch/dl-symbol-redir-ifunc.h | 1 +
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 | 5 ++-
sysdeps/s390/Makefile | 5 +++
.../s390/multiarch/dl-symbol-redir-ifunc.h | 5 +++
sysdeps/s390/string-bitops.h | 28 ++++++++++++
sysdeps/unix/sysv/linux/Makefile | 12 ++++++
sysdeps/unix/sysv/linux/dl-mmap.h | 40 +++++++++++++++++
sysdeps/unix/sysv/linux/i386/Makefile | 14 ++++++
sysdeps/unix/sysv/linux/i386/dl-mmap.c | 38 ++++++++++++++++
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 | 12 ++++--
.../x86_64/multiarch/dl-symbol-redir-ifunc.h | 18 ++++++--
30 files changed, 437 insertions(+), 25 deletions(-)
create mode 100644 elf/tst-assert-startup-static.c
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/dl-mmap.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
diff --git a/assert/Makefile b/assert/Makefile
index 4c253a344ae..541929c3b67 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 94c5b7e6ed8..9482bcbfca3 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -280,6 +280,7 @@ tests-static-normal := \
# tests-static-normal
tests-static-internal := \
+ tst-assert-startup-static \
tst-atrandom-scrub-static \
tst-dl-printf-static \
tst-dl_find_object-static \
@@ -3734,3 +3735,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 00000000000..e8f9a4b9908
--- /dev/null
+++ b/elf/tst-assert-startup-static.c
@@ -0,0 +1,43 @@
+/* Check if assert works 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 <assert.h>
+#include <stdlib.h>
+
+/* The __libc_assert_fail is used internally for assert() calls. */
+extern _Noreturn __typeof (__assert_fail) __libc_assert_fail;
+
+/* The __tunables_init is called just before self-relocation and TLS setup,
+ so overriding it is a way to reach the assert code at that point. */
+void
+__tunables_init (char **env, char **argv)
+{
+ /* Inside libc, assert() is redirected to __libc_assert_fail. This test is
+ not built as part of libc, so a plain assert() here would call the public
+ __assert_fail instead, which uses __progname and the translation routines
+ and thus is not what the startup code issues. Call the internal routine
+ directly. */
+ __libc_assert_fail ("error", __FILE__, __LINE__, __func__);
+}
+
+int
+main (void)
+{
+ /* Fail with a different error code than abort. */
+ exit (EXIT_FAILURE);
+}
diff --git a/libio/Makefile b/libio/Makefile
index 616107ee105..09b1b907dc2 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -195,6 +195,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 addf7dc99ff..18c8b21910d 100644
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -530,6 +530,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/stdlib/abort.c b/stdlib/abort.c
index cf6eb3eb99f..c868635fe9b 100644
--- a/stdlib/abort.c
+++ b/stdlib/abort.c
@@ -74,7 +74,7 @@ __abort_lock_unlock (const internal_sigset_t *set)
_Noreturn void
abort (void)
{
- raise (SIGABRT);
+ __raise_direct (SIGABRT);
/* There is a SIGABRT handle installed and it returned, or SIGABRT was
blocked or ignored. In this case use a AS-safe lock to prevent sigaction
diff --git a/string/Makefile b/string/Makefile
index aa0b0c2f57a..7f25cf1035d 100644
--- a/string/Makefile
+++ b/string/Makefile
@@ -280,13 +280,21 @@ CFLAGS-test-endian-sign-conversion.c += -Werror -Wsign-conversion
LDFLAGS-tst-xbzero-opt = -z now
LDFLAGS-tst-xmemset-opt = -z now
-# Called during TLS initialization.
-CFLAGS-memcpy.c += $(no-stack-protector)
-CFLAGS-wordcopy.c += $(no-stack-protector)
-# Called during static initialization
-CFLAGS-strncmp.c += $(no-stack-protector)
-CFLAGS-memset.c += $(no-stack-protector)
-CFLAGS-strlen.c += $(no-stack-protector)
+# Called during TLS initialization, and during static library initialization.
+CFLAGS-memcpy.o = $(no-stack-protector)
+CFLAGS-memcpy.op = $(no-stack-protector)
+CFLAGS-wordcopy.o = $(no-stack-protector)
+CFLAGS-wordcopy.op = $(no-stack-protector)
+CFLAGS-strncmp.o = $(no-stack-protector)
+CFLAGS-strncmp.op = $(no-stack-protector)
+CFLAGS-memset.o = $(no-stack-protector)
+CFLAGS-memset.op = $(no-stack-protector)
+CFLAGS-strlen.o = $(no-stack-protector)
+CFLAGS-strlen.op = $(no-stack-protector)
+CFLAGS-strchrnul.o = $(no-stack-protector)
+CFLAGS-strchrnul.op = $(no-stack-protector)
+CFLAGS-mempcpy.o = $(no-stack-protector)
+CFLAGS-mempcpy.op = $(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 0910e321d24..dcf2d5fb625 100644
--- a/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h
@@ -19,10 +19,10 @@
#ifndef _DL_IFUNC_GENERIC_H
#define _DL_IFUNC_GENERIC_H
+asm ("memcpy = __memcpy_generic");
asm ("memset = __memset_generic");
asm ("strlen = __strlen_generic");
#ifndef SHARED
-asm ("memcpy = __memcpy_generic");
asm ("memmove = __memmove_generic");
asm ("memcmp = __memcmp_generic");
#endif
diff --git a/sysdeps/aarch64/multiarch/memcpy_generic.S b/sysdeps/aarch64/multiarch/memcpy_generic.S
index c6d09081f47..d6222683056 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/generic/dl-mmap.h b/sysdeps/generic/dl-mmap.h
new file mode 100644
index 00000000000..b578d56ce49
--- /dev/null
+++ b/sysdeps/generic/dl-mmap.h
@@ -0,0 +1,37 @@
+/* mmap wrapper for early static 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/>. */
+
+#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 issue syscalls). There is no portable way to issue the syscall without
+ going through the errno setting, so a port that needs to run this before
+ the thread pointer is available has to override this header (as Linux
+ does). */
+
+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/htl/raise.c b/sysdeps/htl/raise.c
index 3200f91634e..a369ca17f6a 100644
--- a/sysdeps/htl/raise.c
+++ b/sysdeps/htl/raise.c
@@ -54,3 +54,5 @@ raise (int signo)
libc_hidden_def (raise)
weak_alias (raise, gsignal)
+
+strong_alias (raise, __raise_direct)
diff --git a/sysdeps/loongarch/lp64/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/loongarch/lp64/multiarch/dl-symbol-redir-ifunc.h
index 1cd204c2f69..d2c875533a5 100644
--- a/sysdeps/loongarch/lp64/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/loongarch/lp64/multiarch/dl-symbol-redir-ifunc.h
@@ -22,6 +22,7 @@
#ifndef SHARED
asm ("memset = __memset_aligned");
asm ("memcmp = __memcmp_aligned");
+asm ("__strchrnul = __strchrnul_aligned");
asm ("strlen = __strlen_aligned");
asm ("memcpy = __memcpy_unaligned");
asm ("memmove = __memmove_unaligned");
diff --git a/sysdeps/posix/libc_fatal.c b/sysdeps/posix/libc_fatal.c
index 3f0e302b5ea..1ff20d4feb7 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 3a49b855ca5..60ba2e50d28 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 00000000000..75bd82eab37
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/be/multiarch/dl-symbol-redir-ifunc.h
@@ -0,0 +1,27 @@
+/* Symbol redirection 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 4680092cd8e..03f6f12032a 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 ca09203b291..94e99646a48 100644
--- a/sysdeps/powerpc/powerpc64/multiarch/Makefile
+++ b/sysdeps/powerpc/powerpc64/multiarch/Makefile
@@ -39,4 +39,7 @@ endif
endif
# Called during static initialization
-CFLAGS-strncmp-ppc64.c += $(no-stack-protector)
+CFLAGS-strncmp-ppc64.o = $(no-stack-protector)
+CFLAGS-strncmp-ppc64.op = $(no-stack-protector)
+CFLAGS-strchrnul-ppc64.o = $(no-stack-protector)
+CFLAGS-strchrnul-ppc64.op = $(no-stack-protector)
diff --git a/sysdeps/s390/Makefile b/sysdeps/s390/Makefile
index 481e8347925..ab5302dadf6 100644
--- a/sysdeps/s390/Makefile
+++ b/sysdeps/s390/Makefile
@@ -256,6 +256,11 @@ routines_no_fortify += \
strcat-c \
strncat-c \
# routines_no_fortify
+
+# 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)
endif
ifeq ($(subdir),wcsmbs)
diff --git a/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
index a128cd05bd2..b2ddcac4219 100644
--- a/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/s390/multiarch/dl-symbol-redir-ifunc.h
@@ -21,6 +21,7 @@
#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)
@@ -28,4 +29,8 @@
asm ("memset = " IFUNC_SYMBOL_STR(MEMSET_DEFAULT));
asm ("memcmp = " IFUNC_SYMBOL_STR(MEMCMP_DEFAULT));
+#if HAVE_STRCHRNUL_IFUNC
+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 00000000000..e81126c807a
--- /dev/null
+++ b/sysdeps/s390/string-bitops.h
@@ -0,0 +1,28 @@
+/* Zero byte detection, define whether to use stdbit.h s390 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/>. */
+
+/* s390x supports static-pie, and the libgcc implementation for
+ __builtin_clzl/__builtin_ctzl (__clzdi2) accesses extern data (__clz_tab)
+ that is not marked as hidden, which creates an additional GOT access. The
+ generic strchrnul is used before self-relocation, so the GOT entry can not
+ be resolved yet. */
+#if __ARCH__ > 6
+# define HAVE_BITOPTS_WORKING 1
+#else
+# define HAVE_BITOPTS_WORKING 0
+#endif
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 495e468966c..3df8304dc6d 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -115,6 +115,11 @@ sysdep_routines += \
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
CFLAGS-vmsplice.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/dl-mmap.h b/sysdeps/unix/sysv/linux/dl-mmap.h
new file mode 100644
index 00000000000..c78a124900e
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-mmap.h
@@ -0,0 +1,40 @@
+/* mmap wrapper for early static startup. Linux 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_MMAP_H
+#define _DL_MMAP_H
+
+#include <sys/mman.h>
+#include <sysdep.h>
+#include <mmap_call.h>
+
+static inline void *
+_dl_mmap (void *addr, size_t len, int prot, int flags)
+{
+ long int ret;
+#ifdef __NR_mmap2
+ ret = MMAP_CALL_INTERNAL (mmap2, addr, len, prot, flags, -1, 0);
+#else
+ ret = MMAP_CALL_INTERNAL (mmap, addr, len, prot, flags, -1, 0);
+#endif
+ if (INTERNAL_SYSCALL_ERROR_P (ret))
+ return MAP_FAILED;
+ return (void *) ret;
+}
+
+#endif
diff --git a/sysdeps/unix/sysv/linux/i386/Makefile b/sysdeps/unix/sysv/linux/i386/Makefile
index f1f8c3f44cf..9a70905df84 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 00000000000..b3510b1b20e
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/dl-mmap.c
@@ -0,0 +1,38 @@
+/* mmap wrapper for early static startup. 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 cannot 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 <sysdep.h>
+#include <mmap_call.h>
+
+void *
+_dl_mmap (void *addr, size_t len, int prot, int flags)
+{
+ long int ret = MMAP_CALL_INTERNAL (mmap2, addr, len, prot, flags, -1, 0);
+ if (INTERNAL_SYSCALL_ERROR_P (ret))
+ return MAP_FAILED;
+ return (void *) ret;
+}
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 00000000000..9eb0892ff59
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/dl-mmap.h
@@ -0,0 +1,27 @@
+/* mmap wrapper for early static startup. 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/>. */
+
+#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 00000000000..728e78e849c
--- /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) 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 8327d32374e..d92c74d7126 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 00000000000..375ed80b58d
--- /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 cannot 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 49914663699..bdbd12a2b63 100644
--- a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
+++ b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
@@ -48,8 +48,14 @@ sysdep_routines += \
strrchr-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-strlen-generic.o = $(no-stack-protector)
+CFLAGS-strlen-generic.op = $(no-stack-protector)
+
CFLAGS-memcpy_noalignment.c += -mno-strict-align
-# Called during static initialization
-CFLAGS-memset-generic.c += $(no-stack-protector)
-CFLAGS-memcpy-generic.c += $(no-stack-protector)
endif
diff --git a/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
index 1f3ca20307c..ac59ca8ea83 100644
--- a/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
+++ b/sysdeps/x86_64/multiarch/dl-symbol-redir-ifunc.h
@@ -45,14 +45,14 @@ asm ("memset = " HAVE_MEMSET_IFUNC_GENERIC);
asm ("memcmp = " HAVE_MEMCMP_IFUNC_GENERIC);
#if MINIMUM_X86_ISA_LEVEL >= 4
-# define HAVE_STRCMP_IFUNC_GENERIC "__strlen_evex"
+# define HAVE_STRLEN_IFUNC_GENERIC "__strlen_evex"
#elif MINIMUM_X86_ISA_LEVEL == 3
-# define HAVE_STRCMP_IFUNC_GENERIC "__strlen_avx2"
+# define HAVE_STRLEN_IFUNC_GENERIC "__strlen_avx2"
#else
-# define HAVE_STRCMP_IFUNC_GENERIC "__strlen_sse2"
+# define HAVE_STRLEN_IFUNC_GENERIC "__strlen_sse2"
#endif
-asm ("strlen = " HAVE_STRCMP_IFUNC_GENERIC);
+asm ("strlen = " HAVE_STRLEN_IFUNC_GENERIC);
#if MINIMUM_X86_ISA_LEVEL >= 4
# define HAVE_MEMCPY_IFUNC_GENERIC "__memcpy_evex_unaligned"
@@ -73,6 +73,16 @@ asm ("memmove = " HAVE_MEMMOVE_IFUNC_GENERIC);
asm ("mempcpy = " HAVE_MEMPCPY_IFUNC_GENERIC);
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);
+
#endif /* SHARED */
#endif
--
2.53.0