From 77108a50839dfc193e29214581a598e6f6676cd6 Mon Sep 17 00:00:00 2001 From: Rmys Date: Tue, 21 Oct 2025 20:26:40 +0300 Subject: [PATCH] glibc rebuild --- system/base/glibc/actions.py | 0 system/base/glibc/comar/nscd.py | 0 system/base/glibc/comar/package.py | 0 ...pdate-LC_MONETARY-handling-Bug-28845.patch | 0 system/base/glibc/files/bz20338.patch | 0 system/base/glibc/files/bz26534.patch | 0 ...-while-reading-soname-after-patchelf.patch | 0 .../0032-Makerules-fix-MAKEFLAGS-make.patch | 0 .../base/glibc/files/glibc-2.30-fhs-1.patch | 0 .../base/glibc/files/glibc-4e5004-git.patch | 49709 ++++++++++++++++ system/base/glibc/files/glibc32.txt | 0 system/base/glibc/files/host.conf | 0 system/base/glibc/files/locale-gen.txt | 0 .../base/glibc/files/locale-no-archive.patch | 0 system/base/glibc/files/nscd.conf | 0 system/base/glibc/files/nsswitch.conf | 0 system/base/glibc/files/pure64.patch | 0 system/base/glibc/pspec.xml | 9 +- system/base/glibc/translations.xml | 0 19 files changed, 49717 insertions(+), 1 deletion(-) mode change 100644 => 100755 system/base/glibc/actions.py mode change 100644 => 100755 system/base/glibc/comar/nscd.py mode change 100644 => 100755 system/base/glibc/comar/package.py mode change 100644 => 100755 system/base/glibc/files/0001-localedef-Update-LC_MONETARY-handling-Bug-28845.patch mode change 100644 => 100755 system/base/glibc/files/bz20338.patch mode change 100644 => 100755 system/base/glibc/files/bz26534.patch mode change 100644 => 100755 system/base/glibc/files/file-truncated-while-reading-soname-after-patchelf.patch mode change 100644 => 100755 system/base/glibc/files/gentoo/0032-Makerules-fix-MAKEFLAGS-make.patch mode change 100644 => 100755 system/base/glibc/files/glibc-2.30-fhs-1.patch create mode 100644 system/base/glibc/files/glibc-4e5004-git.patch mode change 100644 => 100755 system/base/glibc/files/glibc32.txt mode change 100644 => 100755 system/base/glibc/files/host.conf mode change 100644 => 100755 system/base/glibc/files/locale-gen.txt mode change 100644 => 100755 system/base/glibc/files/locale-no-archive.patch mode change 100644 => 100755 system/base/glibc/files/nscd.conf mode change 100644 => 100755 system/base/glibc/files/nsswitch.conf mode change 100644 => 100755 system/base/glibc/files/pure64.patch mode change 100644 => 100755 system/base/glibc/pspec.xml mode change 100644 => 100755 system/base/glibc/translations.xml diff --git a/system/base/glibc/actions.py b/system/base/glibc/actions.py old mode 100644 new mode 100755 diff --git a/system/base/glibc/comar/nscd.py b/system/base/glibc/comar/nscd.py old mode 100644 new mode 100755 diff --git a/system/base/glibc/comar/package.py b/system/base/glibc/comar/package.py old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/0001-localedef-Update-LC_MONETARY-handling-Bug-28845.patch b/system/base/glibc/files/0001-localedef-Update-LC_MONETARY-handling-Bug-28845.patch old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/bz20338.patch b/system/base/glibc/files/bz20338.patch old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/bz26534.patch b/system/base/glibc/files/bz26534.patch old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/file-truncated-while-reading-soname-after-patchelf.patch b/system/base/glibc/files/file-truncated-while-reading-soname-after-patchelf.patch old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/gentoo/0032-Makerules-fix-MAKEFLAGS-make.patch b/system/base/glibc/files/gentoo/0032-Makerules-fix-MAKEFLAGS-make.patch old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/glibc-2.30-fhs-1.patch b/system/base/glibc/files/glibc-2.30-fhs-1.patch old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/glibc-4e5004-git.patch b/system/base/glibc/files/glibc-4e5004-git.patch new file mode 100644 index 00000000..c82df308 --- /dev/null +++ b/system/base/glibc/files/glibc-4e5004-git.patch @@ -0,0 +1,49709 @@ +diff -Nuar a/assert/assert.c b/assert/assert.c +--- a/assert/assert.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/assert/assert.c 2025-10-20 21:02:20.000000000 +0300 +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -64,7 +65,8 @@ + (void) __fxprintf (NULL, "%s", str); + (void) fflush (stderr); + +- total = (total + 1 + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1); ++ 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); + if (__glibc_likely (buf != MAP_FAILED)) +diff -Nuar a/assert/Makefile b/assert/Makefile +--- a/assert/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/assert/Makefile 2025-10-20 21:02:20.000000000 +0300 +@@ -22,10 +22,23 @@ + + include ../Makeconfig + +-headers := assert.h ++headers := \ ++ assert.h ++ # headers + +-routines := assert assert-perr __assert +-tests := test-assert test-assert-perr tst-assert-c++ tst-assert-g++ ++routines := \ ++ __assert \ ++ assert \ ++ assert-perr \ ++ # routines ++ ++tests := \ ++ test-assert \ ++ test-assert-perr \ ++ tst-assert-c++ \ ++ tst-assert-g++ \ ++ tst-assert-sa-2025-0001 \ ++ # tests + + ifeq ($(have-cxx-thread_local),yes) + CFLAGS-tst-assert-c++.o = -std=c++11 +@@ -33,7 +46,10 @@ + CFLAGS-tst-assert-g++.o = -std=gnu++11 + LDLIBS-tst-assert-g++ = -lstdc++ + else +-tests-unsupported += tst-assert-c++ tst-assert-g++ ++tests-unsupported += \ ++ tst-assert-c++ \ ++ tst-assert-g++ \ ++ # tests-unsupported + endif + + include ../Rules +diff -Nuar a/assert/tst-assert-sa-2025-0001.c b/assert/tst-assert-sa-2025-0001.c +--- a/assert/tst-assert-sa-2025-0001.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/assert/tst-assert-sa-2025-0001.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,92 @@ ++/* Test for CVE-2025-0395. ++ Copyright The GNU Toolchain Authors. ++ 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 ++ . */ ++ ++/* Test that a large enough __progname does not result in a buffer overflow ++ when printing an assertion failure. This was CVE-2025-0395. */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++extern const char *__progname; ++ ++int ++do_test (int argc, char **argv) ++{ ++ ++ support_need_proc ("Reads /proc/self/maps to add guards to writable maps."); ++ ignore_stderr (); ++ ++ /* XXX assumes that the assert is on a 2 digit line number. */ ++ const char *prompt = ": %s:99: do_test: Assertion `argc < 1' failed.\n"; ++ ++ int ret = fprintf (stderr, prompt, __FILE__); ++ if (ret < 0) ++ FAIL_EXIT1 ("fprintf failed: %m\n"); ++ ++ size_t pagesize = getpagesize (); ++ size_t namesize = pagesize - 1 - ret; ++ ++ /* Alter the progname so that the assert message fills the entire page. */ ++ char progname[namesize]; ++ memset (progname, 'A', namesize - 1); ++ progname[namesize - 1] = '\0'; ++ __progname = progname; ++ ++ FILE *f = xfopen ("/proc/self/maps", "r"); ++ char *line = NULL; ++ size_t len = 0; ++ uintptr_t prev_to = 0; ++ ++ /* Pad the beginning of every writable mapping with a PROT_NONE map. This ++ ensures that the mmap in the assert_fail path never ends up below a ++ writable map and will terminate immediately in case of a buffer ++ overflow. */ ++ while (xgetline (&line, &len, f)) ++ { ++ uintptr_t from, to; ++ char perm[4]; ++ ++ sscanf (line, "%" SCNxPTR "-%" SCNxPTR " %c%c%c%c ", ++ &from, &to, ++ &perm[0], &perm[1], &perm[2], &perm[3]); ++ ++ bool writable = (memchr (perm, 'w', 4) != NULL); ++ ++ if (prev_to != 0 && from - prev_to > pagesize && writable) ++ xmmap ((void *) from - pagesize, pagesize, PROT_NONE, ++ MAP_ANONYMOUS | MAP_PRIVATE, 0); ++ ++ prev_to = to; ++ } ++ ++ xfclose (f); ++ ++ assert (argc < 1); ++ return 0; ++} ++ ++#define EXPECTED_SIGNAL SIGABRT ++#define TEST_FUNCTION_ARGV do_test ++#include +diff -Nuar a/bits/socket.h b/bits/socket.h +--- a/bits/socket.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/bits/socket.h 2025-10-20 21:02:20.000000000 +0300 +@@ -245,6 +245,12 @@ + + CMSG_ALIGN (sizeof (struct cmsghdr))) + #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) + ++/* Given a length, return the additional padding necessary such that ++ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ ++#define __CMSG_PADDING(len) ((sizeof (size_t) \ ++ - ((len) & (sizeof (size_t) - 1))) \ ++ & (sizeof (size_t) - 1)) ++ + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + struct cmsghdr *__cmsg) __THROW; + #ifdef __USE_EXTERN_INLINES +@@ -254,18 +260,38 @@ + _EXTERN_INLINE struct cmsghdr * + __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) + { ++ /* We may safely assume that __cmsg lies between __mhdr->msg_control and ++ __mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of __cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; ++ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; ++ ++ size_t __size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (__cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ + return (struct cmsghdr *) 0; + ++ /* There isn't enough space between __cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) ++ < __size_needed) ++ || ((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr ++ - __size_needed) ++ < __cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; ++ ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); +- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control +- + __mhdr->msg_controllen) +- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) +- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) +- /* No more entries. */ +- return (struct cmsghdr *) 0; + return __cmsg; + } + #endif /* Use `extern inline'. */ +diff -Nuar a/bits/wordsize.h b/bits/wordsize.h +--- a/bits/wordsize.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/bits/wordsize.h 2025-10-20 21:02:17.000000000 +0300 +@@ -21,7 +21,9 @@ + #define __WORDSIZE32_PTRDIFF_LONG + + /* Set to 1 in order to force time types to be 32 bits instead of 64 bits in +- struct lastlog and struct utmp{,x} on 64-bit ports. This may be done in ++ struct lastlog and struct utmp{,x}. This may be done in + order to make 64-bit ports compatible with 32-bit ports. Set to 0 for +- 64-bit ports where the time types are 64-bits or for any 32-bit ports. */ ++ 64-bit ports where the time types are 64-bits and new 32-bit ports ++ where time_t is 64 bits, and there is no companion architecture with ++ 32-bit time_t. */ + #define __WORDSIZE_TIME64_COMPAT32 +diff -Nuar a/catgets/open_catalog.c b/catgets/open_catalog.c +--- a/catgets/open_catalog.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/catgets/open_catalog.c 2025-10-20 21:02:20.000000000 +0300 +@@ -39,7 +39,7 @@ + __nl_catd catalog) + { + int fd = -1; +- struct stat64 st; ++ struct __stat64_t64 st; + int swapping; + size_t cnt; + size_t max_offset; +@@ -193,7 +193,7 @@ + return -1; + } + +- if (__builtin_expect (__fstat64 (fd, &st), 0) < 0) ++ if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0)) + goto close_unlock_return; + + if (__builtin_expect (!S_ISREG (st.st_mode), 0) +diff -Nuar a/configure b/configure +--- a/configure 2022-02-03 08:27:54.000000000 +0300 ++++ b/configure 2025-10-20 21:02:20.000000000 +0300 +@@ -730,7 +730,6 @@ + docdir + oldincludedir + includedir +-runstatedir + localstatedir + sharedstatedir + sysconfdir +@@ -845,7 +844,6 @@ + sysconfdir='${prefix}/etc' + sharedstatedir='${prefix}/com' + localstatedir='${prefix}/var' +-runstatedir='${localstatedir}/run' + includedir='${prefix}/include' + oldincludedir='/usr/include' + docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +@@ -1098,15 +1096,6 @@ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + +- -runstatedir | --runstatedir | --runstatedi | --runstated \ +- | --runstate | --runstat | --runsta | --runst | --runs \ +- | --run | --ru | --r) +- ac_prev=runstatedir ;; +- -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ +- | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ +- | --run=* | --ru=* | --r=*) +- runstatedir=$ac_optarg ;; +- + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ +@@ -1244,7 +1233,7 @@ + for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ +- libdir localedir mandir runstatedir ++ libdir localedir mandir + do + eval ac_val=\$$ac_var + # Remove trailing slashes. +@@ -1397,7 +1386,6 @@ + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] +- --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] +@@ -3388,7 +3376,7 @@ + if test "${with_default_link+set}" = set; then : + withval=$with_default_link; use_default_link=$withval + else +- use_default_link=default ++ use_default_link=no + fi + + +@@ -6235,69 +6223,6 @@ + $as_echo "$libc_cv_hashstyle" >&6; } + + +-# The linker's default -shared behavior is good enough if it +-# does these things that our custom linker scripts ensure that +-# all allocated NOTE sections come first. +-if test "$use_default_link" = default; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sufficient default -shared layout" >&5 +-$as_echo_n "checking for sufficient default -shared layout... " >&6; } +-if ${libc_cv_use_default_link+:} false; then : +- $as_echo_n "(cached) " >&6 +-else +- libc_cv_use_default_link=no +- cat > conftest.s <<\EOF +- .section .note.a,"a",%note +- .balign 4 +- .long 4,4,9 +- .string "GNU" +- .string "foo" +- .section .note.b,"a",%note +- .balign 4 +- .long 4,4,9 +- .string "GNU" +- .string "bar" +-EOF +- if { ac_try=' ${CC-cc} $ASFLAGS -shared -o conftest.so conftest.s 1>&5' +- { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 +- (eval $ac_try) 2>&5 +- ac_status=$? +- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 +- test $ac_status = 0; }; } && +- ac_try=`$READELF -S conftest.so | sed -n \ +- '${x;p;} +- s/^ *\[ *[1-9][0-9]*\] *\([^ ][^ ]*\) *\([^ ][^ ]*\) .*$/\2 \1/ +- t a +- b +- : a +- H'` +- then +- libc_seen_a=no libc_seen_b=no +- set -- $ac_try +- while test $# -ge 2 -a "$1" = NOTE; do +- case "$2" in +- .note.a) libc_seen_a=yes ;; +- .note.b) libc_seen_b=yes ;; +- esac +- shift 2 +- done +- case "$libc_seen_a$libc_seen_b" in +- yesyes) +- libc_cv_use_default_link=yes +- ;; +- *) +- echo >&5 "\ +-$libc_seen_a$libc_seen_b from: +-$ac_try" +- ;; +- esac +- fi +- rm -f conftest* +-fi +-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_use_default_link" >&5 +-$as_echo "$libc_cv_use_default_link" >&6; } +- use_default_link=$libc_cv_use_default_link +-fi +- + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLOB_DAT reloc" >&5 + $as_echo_n "checking for GLOB_DAT reloc... " >&6; } + if ${libc_cv_has_glob_dat+:} false; then : +diff -Nuar a/configure.ac b/configure.ac +--- a/configure.ac 2022-02-03 08:27:54.000000000 +0300 ++++ b/configure.ac 2025-10-20 21:02:20.000000000 +0300 +@@ -153,7 +153,7 @@ + AS_HELP_STRING([--with-default-link], + [do not use explicit linker scripts]), + [use_default_link=$withval], +- [use_default_link=default]) ++ [use_default_link=no]) + + dnl Additional build flags injection. + AC_ARG_WITH([nonshared-cflags], +@@ -1402,59 +1402,6 @@ + rm -f conftest*]) + AC_SUBST(libc_cv_hashstyle) + +-# The linker's default -shared behavior is good enough if it +-# does these things that our custom linker scripts ensure that +-# all allocated NOTE sections come first. +-if test "$use_default_link" = default; then +- AC_CACHE_CHECK([for sufficient default -shared layout], +- libc_cv_use_default_link, [dnl +- libc_cv_use_default_link=no +- cat > conftest.s <<\EOF +- .section .note.a,"a",%note +- .balign 4 +- .long 4,4,9 +- .string "GNU" +- .string "foo" +- .section .note.b,"a",%note +- .balign 4 +- .long 4,4,9 +- .string "GNU" +- .string "bar" +-EOF +- if AC_TRY_COMMAND([dnl +- ${CC-cc} $ASFLAGS -shared -o conftest.so conftest.s 1>&AS_MESSAGE_LOG_FD]) && +- ac_try=`$READELF -S conftest.so | sed -n \ +- ['${x;p;} +- s/^ *\[ *[1-9][0-9]*\] *\([^ ][^ ]*\) *\([^ ][^ ]*\) .*$/\2 \1/ +- t a +- b +- : a +- H']` +- then +- libc_seen_a=no libc_seen_b=no +- set -- $ac_try +- while test $# -ge 2 -a "$1" = NOTE; do +- case "$2" in +- .note.a) libc_seen_a=yes ;; +- .note.b) libc_seen_b=yes ;; +- esac +- shift 2 +- done +- case "$libc_seen_a$libc_seen_b" in +- yesyes) +- libc_cv_use_default_link=yes +- ;; +- *) +- echo >&AS_MESSAGE_LOG_FD "\ +-$libc_seen_a$libc_seen_b from: +-$ac_try" +- ;; +- esac +- fi +- rm -f conftest*]) +- use_default_link=$libc_cv_use_default_link +-fi +- + AC_CACHE_CHECK(for GLOB_DAT reloc, + libc_cv_has_glob_dat, [dnl + cat > conftest.c <dladdr1 (address, info, extra, flags); + #endif + +diff -Nuar a/dlfcn/dladdr.c b/dlfcn/dladdr.c +--- a/dlfcn/dladdr.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dladdr.c 2025-10-20 21:02:20.000000000 +0300 +@@ -24,7 +24,7 @@ + __dladdr (const void *address, Dl_info *info) + { + #ifdef SHARED +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dladdr (address, info); + #endif + return _dl_addr (address, info, NULL, NULL); +diff -Nuar a/dlfcn/dlclose.c b/dlfcn/dlclose.c +--- a/dlfcn/dlclose.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dlclose.c 2025-10-20 21:02:20.000000000 +0300 +@@ -24,7 +24,7 @@ + __dlclose (void *handle) + { + #ifdef SHARED +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dlclose (handle); + #endif + +diff -Nuar a/dlfcn/dlerror.c b/dlfcn/dlerror.c +--- a/dlfcn/dlerror.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dlerror.c 2025-10-20 21:02:20.000000000 +0300 +@@ -32,7 +32,7 @@ + __dlerror (void) + { + # ifdef SHARED +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dlerror (); + # endif + +diff -Nuar a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h +--- a/dlfcn/dlfcn.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dlfcn.h 2025-10-20 21:02:20.000000000 +0300 +@@ -164,7 +164,12 @@ + segment, or if the calling thread has not allocated a block for it. */ + RTLD_DI_TLS_DATA = 10, + +- RTLD_DI_MAX = 10 ++ /* Treat ARG as const ElfW(Phdr) **, and store the address of the ++ program header array at that location. The dlinfo call returns ++ the number of program headers in the array. */ ++ RTLD_DI_PHDR = 11, ++ ++ RTLD_DI_MAX = 11 + }; + + +diff -Nuar a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c +--- a/dlfcn/dlinfo.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dlinfo.c 2025-10-20 21:02:20.000000000 +0300 +@@ -28,6 +28,10 @@ + void *handle; + int request; + void *arg; ++ ++ /* This is the value that is returned from dlinfo if no error is ++ signaled. */ ++ int result; + }; + + static void +@@ -40,6 +44,7 @@ + { + case RTLD_DI_CONFIGADDR: + default: ++ args->result = -1; + _dl_signal_error (0, NULL, NULL, N_("unsupported dlinfo request")); + break; + +@@ -75,6 +80,11 @@ + *(void **) args->arg = data; + break; + } ++ ++ case RTLD_DI_PHDR: ++ *(const ElfW(Phdr) **) args->arg = l->l_phdr; ++ args->result = l->l_phnum; ++ break; + } + } + +@@ -82,14 +92,15 @@ + dlinfo_implementation (void *handle, int request, void *arg) + { + struct dlinfo_args args = { handle, request, arg }; +- return _dlerror_run (&dlinfo_doit, &args) ? -1 : 0; ++ _dlerror_run (&dlinfo_doit, &args); ++ return args.result; + } + + #ifdef SHARED + int + ___dlinfo (void *handle, int request, void *arg) + { +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dlinfo (handle, request, arg); + else + return dlinfo_implementation (handle, request, arg); +diff -Nuar a/dlfcn/dlmopen.c b/dlfcn/dlmopen.c +--- a/dlfcn/dlmopen.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dlmopen.c 2025-10-20 21:02:20.000000000 +0300 +@@ -80,7 +80,7 @@ + void * + ___dlmopen (Lmid_t nsid, const char *file, int mode) + { +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dlmopen (nsid, file, mode, RETURN_ADDRESS (0)); + else + return dlmopen_implementation (nsid, file, mode, RETURN_ADDRESS (0)); +diff -Nuar a/dlfcn/dlopen.c b/dlfcn/dlopen.c +--- a/dlfcn/dlopen.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dlopen.c 2025-10-20 21:02:20.000000000 +0300 +@@ -75,7 +75,7 @@ + void * + ___dlopen (const char *file, int mode) + { +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dlopen (file, mode, RETURN_ADDRESS (0)); + else + return dlopen_implementation (file, mode, RETURN_ADDRESS (0)); +@@ -90,7 +90,7 @@ + void * + __dlopen (const char *file, int mode, void *dl_caller) + { +- return dlopen_implementation (file, mode, RETURN_ADDRESS (0)); ++ return dlopen_implementation (file, mode, dl_caller); + } + + void * +diff -Nuar a/dlfcn/dlopenold.c b/dlfcn/dlopenold.c +--- a/dlfcn/dlopenold.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dlopenold.c 2025-10-20 21:02:20.000000000 +0300 +@@ -70,7 +70,7 @@ + mode |= RTLD_LAZY; + args.mode = mode; + +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dlopen (file, mode, RETURN_ADDRESS (0)); + + return _dlerror_run (dlopen_doit, &args) ? NULL : args.new; +diff -Nuar a/dlfcn/dlsym.c b/dlfcn/dlsym.c +--- a/dlfcn/dlsym.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dlsym.c 2025-10-20 21:02:20.000000000 +0300 +@@ -62,7 +62,7 @@ + void * + ___dlsym (void *handle, const char *name) + { +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dlsym (handle, name, RETURN_ADDRESS (0)); + else + return dlsym_implementation (handle, name, RETURN_ADDRESS (0)); +diff -Nuar a/dlfcn/dlvsym.c b/dlfcn/dlvsym.c +--- a/dlfcn/dlvsym.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/dlvsym.c 2025-10-20 21:02:20.000000000 +0300 +@@ -65,7 +65,7 @@ + void * + ___dlvsym (void *handle, const char *name, const char *version) + { +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dlvsym (handle, name, version, + RETURN_ADDRESS (0)); + else +diff -Nuar a/dlfcn/Makefile b/dlfcn/Makefile +--- a/dlfcn/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/dlfcn/Makefile 2025-10-20 21:02:20.000000000 +0300 +@@ -73,6 +73,10 @@ + tststatic4-ENV = $(tststatic-ENV) + tststatic5-ENV = $(tststatic-ENV) + ++tests-internal += \ ++ tst-dlinfo-phdr \ ++ # tests-internal ++ + ifneq (,$(CXX)) + modules-names += bug-atexit3-lib + else +diff -Nuar a/dlfcn/tst-dlinfo-phdr.c b/dlfcn/tst-dlinfo-phdr.c +--- a/dlfcn/tst-dlinfo-phdr.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/dlfcn/tst-dlinfo-phdr.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,125 @@ ++/* Test for dlinfo (RTLD_DI_PHDR). ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* Used to verify that the program header array appears as expected ++ among the dl_iterate_phdr callback invocations. */ ++ ++struct dlip_callback_args ++{ ++ struct link_map *l; /* l->l_addr is used to find the object. */ ++ const ElfW(Phdr) *phdr; /* Expected program header pointed. */ ++ int phnum; /* Expected program header count. */ ++ bool found; /* True if l->l_addr has been found. */ ++}; ++ ++static int ++dlip_callback (struct dl_phdr_info *dlpi, size_t size, void *closure) ++{ ++ TEST_COMPARE (sizeof (*dlpi), size); ++ struct dlip_callback_args *args = closure; ++ ++ if (dlpi->dlpi_addr == args->l->l_addr) ++ { ++ TEST_VERIFY (!args->found); ++ args->found = true; ++ TEST_VERIFY (args->phdr == dlpi->dlpi_phdr); ++ TEST_COMPARE (args->phnum, dlpi->dlpi_phnum); ++ } ++ ++ return 0; ++} ++ ++static int ++do_test (void) ++{ ++ /* Avoid a copy relocation. */ ++ struct r_debug *debug = xdlsym (RTLD_DEFAULT, "_r_debug"); ++ struct link_map *l = (struct link_map *) debug->r_map; ++ TEST_VERIFY_EXIT (l != NULL); ++ ++ do ++ { ++ printf ("info: checking link map %p (%p) for \"%s\"\n", ++ l, l->l_phdr, l->l_name); ++ ++ /* Cause dlerror () to return an error message. */ ++ dlsym (RTLD_DEFAULT, "does-not-exist"); ++ ++ /* Use the extension that link maps are valid dlopen handles. */ ++ const ElfW(Phdr) *phdr; ++ int phnum = dlinfo (l, RTLD_DI_PHDR, &phdr); ++ TEST_VERIFY (phnum >= 0); ++ /* Verify that the error message has been cleared. */ ++ TEST_COMPARE_STRING (dlerror (), NULL); ++ ++ TEST_VERIFY (phdr == l->l_phdr); ++ TEST_COMPARE (phnum, l->l_phnum); ++ ++ /* Check that we can find PT_DYNAMIC among the array. */ ++ { ++ bool dynamic_found = false; ++ for (int i = 0; i < phnum; ++i) ++ if (phdr[i].p_type == PT_DYNAMIC) ++ { ++ dynamic_found = true; ++ TEST_COMPARE ((ElfW(Addr)) l->l_ld, l->l_addr + phdr[i].p_vaddr); ++ } ++ TEST_VERIFY (dynamic_found); ++ } ++ ++ /* Check that dl_iterate_phdr finds the link map with the same ++ program headers. */ ++ { ++ struct dlip_callback_args args = ++ { ++ .l = l, ++ .phdr = phdr, ++ .phnum = phnum, ++ .found = false, ++ }; ++ TEST_COMPARE (dl_iterate_phdr (dlip_callback, &args), 0); ++ TEST_VERIFY (args.found); ++ } ++ ++ if (l->l_prev == NULL) ++ { ++ /* This is the executable, so the information is also ++ available via getauxval. */ ++ TEST_COMPARE_STRING (l->l_name, ""); ++ TEST_VERIFY (phdr == (const ElfW(Phdr) *) getauxval (AT_PHDR)); ++ TEST_COMPARE (phnum, getauxval (AT_PHNUM)); ++ } ++ ++ l = l->l_next; ++ } ++ while (l != NULL); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/elf/dl-audit.c b/elf/dl-audit.c +--- a/elf/dl-audit.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-audit.c 2025-10-20 21:02:20.000000000 +0300 +@@ -257,7 +257,8 @@ + reloc_result->flags = flags; + } + +- DL_FIXUP_BINDNOW_RELOC (value, new_value, sym.st_value); ++ if (flags & LA_SYMB_ALTVALUE) ++ DL_FIXUP_BINDNOW_RELOC (value, new_value, sym.st_value); + } + + void +diff -Nuar a/elf/dl-call_fini.c b/elf/dl-call_fini.c +--- a/elf/dl-call_fini.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/dl-call_fini.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,50 @@ ++/* Invoke DT_FINI and DT_FINI_ARRAY callbacks. ++ Copyright (C) 1996-2022 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 ++ . */ ++ ++#include ++#include ++ ++void ++_dl_call_fini (void *closure_map) ++{ ++ struct link_map *map = closure_map; ++ ++ /* When debugging print a message first. */ ++ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS)) ++ _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", map->l_name, map->l_ns); ++ ++ /* Make sure nothing happens if we are called twice. */ ++ map->l_init_called = 0; ++ ++ ElfW(Dyn) *fini_array = map->l_info[DT_FINI_ARRAY]; ++ if (fini_array != NULL) ++ { ++ ElfW(Addr) *array = (ElfW(Addr) *) (map->l_addr ++ + fini_array->d_un.d_ptr); ++ size_t sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val ++ / sizeof (ElfW(Addr))); ++ ++ while (sz-- > 0) ++ ((fini_t) array[sz]) (); ++ } ++ ++ /* Next try the old-style destructor. */ ++ ElfW(Dyn) *fini = map->l_info[DT_FINI]; ++ if (fini != NULL) ++ DL_CALL_DT_FINI (map, ((void *) map->l_addr + fini->d_un.d_ptr)); ++} +diff -Nuar a/elf/dl-close.c b/elf/dl-close.c +--- a/elf/dl-close.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-close.c 2025-10-20 21:02:20.000000000 +0300 +@@ -36,11 +36,6 @@ + + #include + +- +-/* Type of the constructor functions. */ +-typedef void (*fini_t) (void); +- +- + /* Special l_idx value used to indicate which objects remain loaded. */ + #define IDX_STILL_USED -1 + +@@ -110,31 +105,6 @@ + return false; + } + +-/* Invoke dstructors for CLOSURE (a struct link_map *). Called with +- exception handling temporarily disabled, to make errors fatal. */ +-static void +-call_destructors (void *closure) +-{ +- struct link_map *map = closure; +- +- if (map->l_info[DT_FINI_ARRAY] != NULL) +- { +- ElfW(Addr) *array = +- (ElfW(Addr) *) (map->l_addr +- + map->l_info[DT_FINI_ARRAY]->d_un.d_ptr); +- unsigned int sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val +- / sizeof (ElfW(Addr))); +- +- while (sz-- > 0) +- ((fini_t) array[sz]) (); +- } +- +- /* Next try the old-style destructor. */ +- if (map->l_info[DT_FINI] != NULL) +- DL_CALL_DT_FINI (map, ((void *) map->l_addr +- + map->l_info[DT_FINI]->d_un.d_ptr)); +-} +- + void + _dl_close_worker (struct link_map *map, bool force) + { +@@ -280,17 +250,7 @@ + half-cooked objects. Temporarily disable exception + handling, so that errors are fatal. */ + if (imap->l_init_called) +- { +- /* When debugging print a message first. */ +- if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, +- 0)) +- _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", +- imap->l_name, nsid); +- +- if (imap->l_info[DT_FINI_ARRAY] != NULL +- || imap->l_info[DT_FINI] != NULL) +- _dl_catch_exception (NULL, call_destructors, imap); +- } ++ _dl_catch_exception (NULL, _dl_call_fini, imap); + + #ifdef SHARED + /* Auditing checkpoint: we remove an object. */ +@@ -743,7 +703,7 @@ + if (__glibc_unlikely (newgen == 0)) + _dl_fatal_printf ("TLS generation counter wrapped! Please report as described in "REPORT_BUGS_TO".\n"); + /* Can be read concurrently. */ +- atomic_store_relaxed (&GL(dl_tls_generation), newgen); ++ atomic_store_release (&GL(dl_tls_generation), newgen); + + if (tls_free_end == GL(dl_tls_static_used)) + GL(dl_tls_static_used) = tls_free_start; +diff -Nuar a/elf/dl-deps.c b/elf/dl-deps.c +--- a/elf/dl-deps.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-deps.c 2025-10-20 21:02:20.000000000 +0300 +@@ -489,6 +489,8 @@ + + for (nlist = 0, runp = known; runp; runp = runp->next) + { ++ /* _dl_sort_maps ignores l_faked object, so it is safe to not consider ++ them for nlist. */ + if (__builtin_expect (trace_mode, 0) && runp->map->l_faked) + /* This can happen when we trace the loading. */ + --map->l_searchlist.r_nlist; +diff -Nuar a/elf/dl-early_allocate.c b/elf/dl-early_allocate.c +--- a/elf/dl-early_allocate.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/dl-early_allocate.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,30 @@ ++/* Early memory allocation for the dynamic loader. Generic version. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++ ++void * ++_dl_early_allocate (size_t size) ++{ ++ void *result = __sbrk (size); ++ if (result == (void *) -1) ++ result = NULL; ++ return result; ++} +diff -Nuar a/elf/dl-find_object.c b/elf/dl-find_object.c +--- a/elf/dl-find_object.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-find_object.c 2025-10-20 21:02:20.000000000 +0300 +@@ -46,7 +46,7 @@ + struct dl_find_object_internal internal; + _dl_find_object_from_map (l, &internal); + _dl_find_object_to_external (&internal, result); +- return 1; ++ return 0; + } + + /* Object not found. */ +@@ -465,6 +465,37 @@ + } + rtld_hidden_def (_dl_find_object) + ++/* Subroutine of _dlfo_process_initial to split out noncontigous link ++ maps. NODELETE is the number of used _dlfo_nodelete_mappings ++ elements. It is incremented as needed, and the new NODELETE value ++ is returned. */ ++static size_t ++_dlfo_process_initial_noncontiguous_map (struct link_map *map, ++ size_t nodelete) ++{ ++ struct dl_find_object_internal dlfo; ++ _dl_find_object_from_map (map, &dlfo); ++ ++ /* PT_LOAD segments for a non-contiguous link map are added to the ++ non-closeable mappings. */ ++ const ElfW(Phdr) *ph = map->l_phdr; ++ const ElfW(Phdr) *ph_end = map->l_phdr + map->l_phnum; ++ for (; ph < ph_end; ++ph) ++ if (ph->p_type == PT_LOAD) ++ { ++ if (_dlfo_nodelete_mappings != NULL) ++ { ++ /* Second pass only. */ ++ _dlfo_nodelete_mappings[nodelete] = dlfo; ++ ElfW(Addr) start = ph->p_vaddr + map->l_addr; ++ _dlfo_nodelete_mappings[nodelete].map_start = start; ++ _dlfo_nodelete_mappings[nodelete].map_end = start + ph->p_memsz; ++ } ++ ++nodelete; ++ } ++ return nodelete; ++} ++ + /* _dlfo_process_initial is called twice. First to compute the array + sizes from the initial loaded mappings. Second to fill in the + bases and infos arrays with the (still unsorted) data. Returns the +@@ -476,29 +507,8 @@ + + size_t nodelete = 0; + if (!main_map->l_contiguous) +- { +- struct dl_find_object_internal dlfo; +- _dl_find_object_from_map (main_map, &dlfo); +- +- /* PT_LOAD segments for a non-contiguous are added to the +- non-closeable mappings. */ +- for (const ElfW(Phdr) *ph = main_map->l_phdr, +- *ph_end = main_map->l_phdr + main_map->l_phnum; +- ph < ph_end; ++ph) +- if (ph->p_type == PT_LOAD) +- { +- if (_dlfo_nodelete_mappings != NULL) +- { +- /* Second pass only. */ +- _dlfo_nodelete_mappings[nodelete] = dlfo; +- _dlfo_nodelete_mappings[nodelete].map_start +- = ph->p_vaddr + main_map->l_addr; +- _dlfo_nodelete_mappings[nodelete].map_end +- = _dlfo_nodelete_mappings[nodelete].map_start + ph->p_memsz; +- } +- ++nodelete; +- } +- } ++ /* Contiguous case already handled in _dl_find_object_init. */ ++ nodelete = _dlfo_process_initial_noncontiguous_map (main_map, nodelete); + + size_t loaded = 0; + for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) +@@ -510,11 +520,22 @@ + /* lt_library link maps are implicitly NODELETE. */ + if (l->l_type == lt_library || l->l_nodelete_active) + { +- if (_dlfo_nodelete_mappings != NULL) +- /* Second pass only. */ +- _dl_find_object_from_map +- (l, _dlfo_nodelete_mappings + nodelete); +- ++nodelete; ++ /* The kernel may have loaded ld.so with gaps. */ ++ if (!l->l_contiguous ++#ifdef SHARED ++ && l == &GL(dl_rtld_map) ++#endif ++ ) ++ nodelete ++ = _dlfo_process_initial_noncontiguous_map (l, nodelete); ++ else ++ { ++ if (_dlfo_nodelete_mappings != NULL) ++ /* Second pass only. */ ++ _dl_find_object_from_map ++ (l, _dlfo_nodelete_mappings + nodelete); ++ ++nodelete; ++ } + } + else if (l->l_type == lt_loaded) + { +@@ -756,7 +777,6 @@ + /* Prefer newly loaded link map. */ + assert (loaded_index1 > 0); + _dl_find_object_from_map (loaded[loaded_index1 - 1], dlfo); +- loaded[loaded_index1 - 1]->l_find_object_processed = 1; + --loaded_index1; + } + +@@ -788,6 +808,9 @@ + for (struct link_map *l = new_map; l != NULL; l = l->l_next) + /* Skip proxy maps and already-processed maps. */ + count += l == l->l_real && !l->l_find_object_processed; ++ if (count == 0) ++ return true; ++ + struct link_map **map_array = malloc (count * sizeof (*map_array)); + if (map_array == NULL) + return false; +@@ -797,8 +820,6 @@ + if (l == l->l_real && !l->l_find_object_processed) + map_array[i++] = l; + } +- if (count == 0) +- return true; + + _dl_find_object_link_map_sort (map_array, count); + bool ok = _dl_find_object_update_1 (map_array, count); +diff -Nuar a/elf/dl-find_object.h b/elf/dl-find_object.h +--- a/elf/dl-find_object.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-find_object.h 2025-10-20 21:02:20.000000000 +0300 +@@ -87,7 +87,7 @@ + } + + /* Extract the object location data from a link map and writes it to +- *RESULT using relaxed MO stores. */ ++ *RESULT using relaxed MO stores. Set L->l_find_object_processed. */ + static void __attribute__ ((unused)) + _dl_find_object_from_map (struct link_map *l, + struct dl_find_object_internal *result) +@@ -100,6 +100,8 @@ + atomic_store_relaxed (&result->eh_dbase, (void *) l->l_info[DT_PLTGOT]); + #endif + ++ l->l_find_object_processed = 1; ++ + for (const ElfW(Phdr) *ph = l->l_phdr, *ph_end = l->l_phdr + l->l_phnum; + ph < ph_end; ++ph) + if (ph->p_type == DLFO_EH_SEGMENT_TYPE) +diff -Nuar a/elf/dl-fini.c b/elf/dl-fini.c +--- a/elf/dl-fini.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-fini.c 2025-10-20 21:02:20.000000000 +0300 +@@ -21,11 +21,6 @@ + #include + #include + +- +-/* Type of the constructor functions. */ +-typedef void (*fini_t) (void); +- +- + void + _dl_fini (void) + { +@@ -116,38 +111,7 @@ + + if (l->l_init_called) + { +- /* Make sure nothing happens if we are called twice. */ +- l->l_init_called = 0; +- +- /* Is there a destructor function? */ +- if (l->l_info[DT_FINI_ARRAY] != NULL +- || (ELF_INITFINI && l->l_info[DT_FINI] != NULL)) +- { +- /* When debugging print a message first. */ +- if (__builtin_expect (GLRO(dl_debug_mask) +- & DL_DEBUG_IMPCALLS, 0)) +- _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", +- DSO_FILENAME (l->l_name), +- ns); +- +- /* First see whether an array is given. */ +- if (l->l_info[DT_FINI_ARRAY] != NULL) +- { +- ElfW(Addr) *array = +- (ElfW(Addr) *) (l->l_addr +- + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); +- unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val +- / sizeof (ElfW(Addr))); +- while (i-- > 0) +- ((fini_t) array[i]) (); +- } +- +- /* Next try the old-style destructor. */ +- if (ELF_INITFINI && l->l_info[DT_FINI] != NULL) +- DL_CALL_DT_FINI +- (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr); +- } +- ++ _dl_call_fini (l); + #ifdef SHARED + /* Auditing checkpoint: another object closed. */ + _dl_audit_objclose (l); +diff -Nuar a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c +--- a/elf/dl-hwcaps.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-hwcaps.c 2025-10-20 21:02:20.000000000 +0300 +@@ -193,7 +193,7 @@ + /* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix + and a "/" suffix once stored in the result. */ + hwcaps_counts.maximum_length += strlen (GLIBC_HWCAPS_PREFIX) + 1; +- size_t total = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1) ++ size_t hwcaps_sz = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1) + + hwcaps_counts.total_length); + + /* Count the number of bits set in the masked value. */ +@@ -229,11 +229,12 @@ + assert (m == cnt); + + /* Determine the total size of all strings together. */ ++ size_t total; + if (cnt == 1) +- total += temp[0].len + 1; ++ total = temp[0].len + 1; + else + { +- total += temp[0].len + temp[cnt - 1].len + 2; ++ total = temp[0].len + temp[cnt - 1].len + 2; + if (cnt > 2) + { + total <<= 1; +@@ -255,6 +256,7 @@ + /* This is the overall result, including both glibc-hwcaps + subdirectories and the legacy hwcaps subdirectories using the + power set construction. */ ++ total += hwcaps_sz; + struct r_strlenpair *overall_result + = malloc (*sz * sizeof (*result) + total); + if (overall_result == NULL) +diff -Nuar a/elf/dl-init.c b/elf/dl-init.c +--- a/elf/dl-init.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-init.c 2025-10-20 21:02:20.000000000 +0300 +@@ -25,10 +25,14 @@ + static void + call_init (struct link_map *l, int argc, char **argv, char **env) + { ++ /* Do not run constructors for proxy objects. */ ++ if (l != l->l_real) ++ return; ++ + /* If the object has not been relocated, this is a bug. The + function pointers are invalid in this case. (Executables do not +- need relocation, and neither do proxy objects.) */ +- assert (l->l_real->l_relocated || l->l_real->l_type == lt_executable); ++ need relocation.) */ ++ assert (l->l_relocated || l->l_type == lt_executable); + + if (l->l_init_called) + /* This object is all done. */ +diff -Nuar a/elf/dl-libc.c b/elf/dl-libc.c +--- a/elf/dl-libc.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-libc.c 2025-10-20 21:02:20.000000000 +0300 +@@ -156,7 +156,7 @@ + args.caller_dlopen = RETURN_ADDRESS (0); + + #ifdef SHARED +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->libc_dlopen_mode (name, mode); + #endif + return dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map; +@@ -184,7 +184,7 @@ + args.name = name; + + #ifdef SHARED +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->libc_dlsym (map, name); + #endif + return (dlerror_run (do_dlsym, &args) ? NULL +@@ -198,7 +198,7 @@ + __libc_dlvsym (void *map, const char *name, const char *version) + { + #ifdef SHARED +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->libc_dlvsym (map, name, version); + #endif + +@@ -221,7 +221,7 @@ + __libc_dlclose (void *map) + { + #ifdef SHARED +- if (!rtld_active ()) ++ if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->libc_dlclose (map); + #endif + return dlerror_run (do_dlclose, map); +diff -Nuar a/elf/dl-map-segments.h b/elf/dl-map-segments.h +--- a/elf/dl-map-segments.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-map-segments.h 2025-10-20 21:02:20.000000000 +0300 +@@ -113,6 +113,9 @@ + unallocated. Then jump into the normal segment-mapping loop to + handle the portion of the segment past the end of the file + mapping. */ ++ if (__glibc_unlikely (loadcmds[nloadcmds - 1].mapstart < ++ c->mapend)) ++ return N_("ELF load command address/offset not page-aligned"); + if (__glibc_unlikely + (__mprotect ((caddr_t) (l->l_addr + c->mapend), + loadcmds[nloadcmds - 1].mapstart - c->mapend, +diff -Nuar a/elf/dl-open.c b/elf/dl-open.c +--- a/elf/dl-open.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-open.c 2025-10-20 21:02:20.000000000 +0300 +@@ -405,7 +405,7 @@ + _dl_fatal_printf (N_("\ + TLS generation counter wrapped! Please report this.")); + /* Can be read concurrently. */ +- atomic_store_relaxed (&GL(dl_tls_generation), newgen); ++ atomic_store_release (&GL(dl_tls_generation), newgen); + + /* We need a second pass for static tls data, because + _dl_update_slotinfo must not be run while calls to +@@ -422,8 +422,8 @@ + now, but we can delay updating the DTV. */ + imap->l_need_tls_init = 0; + #ifdef SHARED +- /* Update the slot information data for at least the +- generation of the DSO we are allocating data for. */ ++ /* Update the slot information data for the current ++ generation. */ + + /* FIXME: This can terminate the process on memory + allocation failure. It is not possible to raise +@@ -431,7 +431,7 @@ + _dl_update_slotinfo would have to be split into two + operations, similar to resize_scopes and update_scopes + above. This is related to bug 16134. */ +- _dl_update_slotinfo (imap->l_tls_modid); ++ _dl_update_slotinfo (imap->l_tls_modid, newgen); + #endif + + dl_init_static_tls (imap); +@@ -850,6 +850,7 @@ + ++GL(dl_nns); + } + ++ GL(dl_ns)[nsid].libc_map = NULL; + _dl_debug_update (nsid)->r_state = RT_CONSISTENT; + } + /* Never allow loading a DSO in a namespace which is empty. Such +diff -Nuar a/elf/dl-reloc.c b/elf/dl-reloc.c +--- a/elf/dl-reloc.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-reloc.c 2025-10-20 21:02:20.000000000 +0300 +@@ -111,11 +111,11 @@ + if (map->l_real->l_relocated) + { + #ifdef SHARED ++ /* Update the DTV of the current thread. Note: GL(dl_load_tls_lock) ++ is held here so normal load of the generation counter is valid. */ + if (__builtin_expect (THREAD_DTV()[0].counter != GL(dl_tls_generation), + 0)) +- /* Update the slot information data for at least the generation of +- the DSO we are allocating data for. */ +- (void) _dl_update_slotinfo (map->l_tls_modid); ++ (void) _dl_update_slotinfo (map->l_tls_modid, GL(dl_tls_generation)); + #endif + + dl_init_static_tls (map); +diff -Nuar a/elf/dl-sort-maps.c b/elf/dl-sort-maps.c +--- a/elf/dl-sort-maps.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-sort-maps.c 2025-10-20 21:02:20.000000000 +0300 +@@ -27,12 +27,12 @@ + If FOR_FINI is true, this is called for finishing an object. */ + static void + _dl_sort_maps_original (struct link_map **maps, unsigned int nmaps, +- unsigned int skip, bool for_fini) ++ bool force_first, bool for_fini) + { + /* Allows caller to do the common optimization of skipping the first map, + usually the main binary. */ +- maps += skip; +- nmaps -= skip; ++ maps += force_first; ++ nmaps -= force_first; + + /* A list of one element need not be sorted. */ + if (nmaps <= 1) +@@ -140,7 +140,9 @@ + dfs_traversal (struct link_map ***rpo, struct link_map *map, + bool *do_reldeps) + { +- if (map->l_visited) ++ /* _dl_map_object_deps ignores l_faked objects when calculating the ++ number of maps before calling _dl_sort_maps, ignore them as well. */ ++ if (map->l_visited || map->l_faked) + return; + + map->l_visited = 1; +@@ -180,8 +182,9 @@ + + static void + _dl_sort_maps_dfs (struct link_map **maps, unsigned int nmaps, +- unsigned int skip __attribute__ ((unused)), bool for_fini) ++ bool force_first, bool for_fini) + { ++ struct link_map *first_map = maps[0]; + for (int i = nmaps - 1; i >= 0; i--) + maps[i]->l_visited = 0; + +@@ -206,14 +209,6 @@ + Adjusting the order so that maps[0] is last traversed naturally avoids + this problem. + +- Further, the old "optimization" of skipping the main object at maps[0] +- from the call-site (i.e. _dl_sort_maps(maps+1,nmaps-1)) is in general +- no longer valid, since traversing along object dependency-links +- may "find" the main object even when it is not included in the initial +- order (e.g. a dlopen()'ed shared object can have circular dependencies +- linked back to itself). In such a case, traversing N-1 objects will +- create a N-object result, and raise problems. +- + To summarize, just passing in the full list, and iterating from back + to front makes things much more straightforward. */ + +@@ -272,6 +267,27 @@ + } + + memcpy (maps, rpo, sizeof (struct link_map *) * nmaps); ++ ++ /* Skipping the first object at maps[0] is not valid in general, ++ since traversing along object dependency-links may "find" that ++ first object even when it is not included in the initial order ++ (e.g., a dlopen'ed shared object can have circular dependencies ++ linked back to itself). In such a case, traversing N-1 objects ++ will create a N-object result, and raise problems. Instead, ++ force the object back into first place after sorting. This naive ++ approach may introduce further dependency ordering violations ++ compared to rotating the cycle until the first map is again in ++ the first position, but as there is a cycle, at least one ++ violation is already present. */ ++ if (force_first && maps[0] != first_map) ++ { ++ int i; ++ for (i = 0; maps[i] != first_map; ++i) ++ ; ++ assert (i < nmaps); ++ memmove (&maps[1], maps, i * sizeof (maps[0])); ++ maps[0] = first_map; ++ } + } + + void +@@ -284,7 +300,7 @@ + + void + _dl_sort_maps (struct link_map **maps, unsigned int nmaps, +- unsigned int skip, bool for_fini) ++ bool force_first, bool for_fini) + { + /* It can be tempting to use a static function pointer to store and call + the current selected sorting algorithm routine, but experimentation +@@ -294,9 +310,9 @@ + input cases. A simple if-case with direct function calls appears to + be the fastest. */ + if (__glibc_likely (GLRO(dl_dso_sort_algo) == dso_sort_algorithm_original)) +- _dl_sort_maps_original (maps, nmaps, skip, for_fini); ++ _dl_sort_maps_original (maps, nmaps, force_first, for_fini); + else +- _dl_sort_maps_dfs (maps, nmaps, skip, for_fini); ++ _dl_sort_maps_dfs (maps, nmaps, force_first, for_fini); + } + + #endif /* HAVE_TUNABLES. */ +diff -Nuar a/elf/dl-support.c b/elf/dl-support.c +--- a/elf/dl-support.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-support.c 2025-10-20 21:02:20.000000000 +0300 +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + + extern char *__progname; + char **_dl_argv = &__progname; /* This is checked for some error messages. */ +@@ -241,93 +242,25 @@ + + + #ifdef HAVE_AUX_VECTOR ++#include ++ + int _dl_clktck; + + void + _dl_aux_init (ElfW(auxv_t) *av) + { +- int seen = 0; +- uid_t uid = 0; +- gid_t gid = 0; +- + #ifdef NEED_DL_SYSINFO + /* NB: Avoid RELATIVE relocation in static PIE. */ + GL(dl_sysinfo) = DL_SYSINFO_DEFAULT; + #endif + + _dl_auxv = av; +- for (; av->a_type != AT_NULL; ++av) +- switch (av->a_type) +- { +- case AT_PAGESZ: +- if (av->a_un.a_val != 0) +- GLRO(dl_pagesize) = av->a_un.a_val; +- break; +- case AT_CLKTCK: +- GLRO(dl_clktck) = av->a_un.a_val; +- break; +- case AT_PHDR: +- GL(dl_phdr) = (const void *) av->a_un.a_val; +- break; +- case AT_PHNUM: +- GL(dl_phnum) = av->a_un.a_val; +- break; +- case AT_PLATFORM: +- GLRO(dl_platform) = (void *) av->a_un.a_val; +- break; +- case AT_HWCAP: +- GLRO(dl_hwcap) = (unsigned long int) av->a_un.a_val; +- break; +- case AT_HWCAP2: +- GLRO(dl_hwcap2) = (unsigned long int) av->a_un.a_val; +- break; +- case AT_FPUCW: +- GLRO(dl_fpu_control) = av->a_un.a_val; +- break; +-#ifdef NEED_DL_SYSINFO +- case AT_SYSINFO: +- GL(dl_sysinfo) = av->a_un.a_val; +- break; +-#endif +-#ifdef NEED_DL_SYSINFO_DSO +- case AT_SYSINFO_EHDR: +- GL(dl_sysinfo_dso) = (void *) av->a_un.a_val; +- break; +-#endif +- case AT_UID: +- uid ^= av->a_un.a_val; +- seen |= 1; +- break; +- case AT_EUID: +- uid ^= av->a_un.a_val; +- seen |= 2; +- break; +- case AT_GID: +- gid ^= av->a_un.a_val; +- seen |= 4; +- break; +- case AT_EGID: +- gid ^= av->a_un.a_val; +- seen |= 8; +- break; +- case AT_SECURE: +- seen = -1; +- __libc_enable_secure = av->a_un.a_val; +- __libc_enable_secure_decided = 1; +- break; +- case AT_RANDOM: +- _dl_random = (void *) av->a_un.a_val; +- break; +- case AT_MINSIGSTKSZ: +- _dl_minsigstacksize = av->a_un.a_val; +- break; +- DL_PLATFORM_AUXV +- } +- if (seen == 0xf) +- { +- __libc_enable_secure = uid != 0 || gid != 0; +- __libc_enable_secure_decided = 1; +- } ++ dl_parse_auxv_t auxv_values; ++ /* Use an explicit initialization loop here because memset may not ++ be available yet. */ ++ for (int i = 0; i < array_length (auxv_values); ++i) ++ auxv_values[i] = 0; ++ _dl_parse_auxv (av, auxv_values); + } + #endif + +@@ -339,8 +272,6 @@ + _dl_main_map.l_phdr = GL(dl_phdr); + _dl_main_map.l_phnum = GL(dl_phnum); + +- _dl_verbose = *(getenv ("LD_WARN") ?: "") == '\0' ? 0 : 1; +- + /* Set up the data structures for the system-supplied DSO early, + so they can influence _dl_init_paths. */ + setup_vdso (NULL, NULL); +@@ -348,27 +279,6 @@ + /* With vDSO setup we can initialize the function pointers. */ + setup_vdso_pointers (); + +- /* Initialize the data structures for the search paths for shared +- objects. */ +- _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH", +- /* No glibc-hwcaps selection support in statically +- linked binaries. */ +- NULL, NULL); +- +- /* Remember the last search directory added at startup. */ +- _dl_init_all_dirs = GL(dl_all_dirs); +- +- _dl_lazy = *(getenv ("LD_BIND_NOW") ?: "") == '\0'; +- +- _dl_bind_not = *(getenv ("LD_BIND_NOT") ?: "") != '\0'; +- +- _dl_dynamic_weak = *(getenv ("LD_DYNAMIC_WEAK") ?: "") == '\0'; +- +- _dl_profile_output = getenv ("LD_PROFILE_OUTPUT"); +- if (_dl_profile_output == NULL || _dl_profile_output[0] == '\0') +- _dl_profile_output +- = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0]; +- + if (__libc_enable_secure) + { + static const char unsecure_envvars[] = +@@ -391,6 +301,29 @@ + #endif + } + ++ _dl_verbose = *(getenv ("LD_WARN") ?: "") == '\0' ? 0 : 1; ++ ++ /* Initialize the data structures for the search paths for shared ++ objects. */ ++ _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH", ++ /* No glibc-hwcaps selection support in statically ++ linked binaries. */ ++ NULL, NULL); ++ ++ /* Remember the last search directory added at startup. */ ++ _dl_init_all_dirs = GL(dl_all_dirs); ++ ++ _dl_lazy = *(getenv ("LD_BIND_NOW") ?: "") == '\0'; ++ ++ _dl_bind_not = *(getenv ("LD_BIND_NOT") ?: "") != '\0'; ++ ++ _dl_dynamic_weak = *(getenv ("LD_DYNAMIC_WEAK") ?: "") == '\0'; ++ ++ _dl_profile_output = getenv ("LD_PROFILE_OUTPUT"); ++ if (_dl_profile_output == NULL || _dl_profile_output[0] == '\0') ++ _dl_profile_output ++ = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0]; ++ + #ifdef DL_PLATFORM_INIT + DL_PLATFORM_INIT; + #endif +diff -Nuar a/elf/dl-sysdep.c b/elf/dl-sysdep.c +--- a/elf/dl-sysdep.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-sysdep.c 2025-10-20 21:02:20.000000000 +0300 +@@ -1,4 +1,4 @@ +-/* Operating system support for run-time dynamic linker. Generic Unix version. ++/* Operating system support for run-time dynamic linker. Stub version. + Copyright (C) 1995-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + +@@ -16,352 +16,4 @@ + License along with the GNU C Library; if not, see + . */ + +-/* We conditionalize the whole of this file rather than simply eliding it +- from the static build, because other sysdeps/ versions of this file +- might define things needed by a static build. */ +- +-#ifdef SHARED +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include <_itoa.h> +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-extern char **_environ attribute_hidden; +-extern char _end[] attribute_hidden; +- +-/* Protect SUID program against misuse of file descriptors. */ +-extern void __libc_check_standard_fds (void); +- +-int __libc_enable_secure attribute_relro = 0; +-rtld_hidden_data_def (__libc_enable_secure) +-/* This variable contains the lowest stack address ever used. */ +-void *__libc_stack_end attribute_relro = NULL; +-rtld_hidden_data_def(__libc_stack_end) +-void *_dl_random attribute_relro = NULL; +- +-#ifndef DL_FIND_ARG_COMPONENTS +-# define DL_FIND_ARG_COMPONENTS(cookie, argc, argv, envp, auxp) \ +- do { \ +- void **_tmp; \ +- (argc) = *(long int *) cookie; \ +- (argv) = (char **) ((long int *) cookie + 1); \ +- (envp) = (argv) + (argc) + 1; \ +- for (_tmp = (void **) (envp); *_tmp; ++_tmp) \ +- continue; \ +- (auxp) = (void *) ++_tmp; \ +- } while (0) +-#endif +- +-#ifndef DL_STACK_END +-# define DL_STACK_END(cookie) ((void *) (cookie)) +-#endif +- +-ElfW(Addr) +-_dl_sysdep_start (void **start_argptr, +- void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum, +- ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv)) +-{ +- const ElfW(Phdr) *phdr = NULL; +- ElfW(Word) phnum = 0; +- ElfW(Addr) user_entry; +- ElfW(auxv_t) *av; +-#ifdef HAVE_AUX_SECURE +-# define set_seen(tag) (tag) /* Evaluate for the side effects. */ +-# define set_seen_secure() ((void) 0) +-#else +- uid_t uid = 0; +- gid_t gid = 0; +- unsigned int seen = 0; +-# define set_seen_secure() (seen = -1) +-# ifdef HAVE_AUX_XID +-# define set_seen(tag) (tag) /* Evaluate for the side effects. */ +-# else +-# define M(type) (1 << (type)) +-# define set_seen(tag) seen |= M ((tag)->a_type) +-# endif +-#endif +-#ifdef NEED_DL_SYSINFO +- uintptr_t new_sysinfo = 0; +-#endif +- +- __libc_stack_end = DL_STACK_END (start_argptr); +- DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, _dl_argv, _environ, +- GLRO(dl_auxv)); +- +- user_entry = (ElfW(Addr)) ENTRY_POINT; +- GLRO(dl_platform) = NULL; /* Default to nothing known about the platform. */ +- +- /* NB: Default to a constant CONSTANT_MINSIGSTKSZ. */ +- _Static_assert (__builtin_constant_p (CONSTANT_MINSIGSTKSZ), +- "CONSTANT_MINSIGSTKSZ is constant"); +- GLRO(dl_minsigstacksize) = CONSTANT_MINSIGSTKSZ; +- +- for (av = GLRO(dl_auxv); av->a_type != AT_NULL; set_seen (av++)) +- switch (av->a_type) +- { +- case AT_PHDR: +- phdr = (void *) av->a_un.a_val; +- break; +- case AT_PHNUM: +- phnum = av->a_un.a_val; +- break; +- case AT_PAGESZ: +- GLRO(dl_pagesize) = av->a_un.a_val; +- break; +- case AT_ENTRY: +- user_entry = av->a_un.a_val; +- break; +-#ifndef HAVE_AUX_SECURE +- case AT_UID: +- case AT_EUID: +- uid ^= av->a_un.a_val; +- break; +- case AT_GID: +- case AT_EGID: +- gid ^= av->a_un.a_val; +- break; +-#endif +- case AT_SECURE: +-#ifndef HAVE_AUX_SECURE +- seen = -1; +-#endif +- __libc_enable_secure = av->a_un.a_val; +- break; +- case AT_PLATFORM: +- GLRO(dl_platform) = (void *) av->a_un.a_val; +- break; +- case AT_HWCAP: +- GLRO(dl_hwcap) = (unsigned long int) av->a_un.a_val; +- break; +- case AT_HWCAP2: +- GLRO(dl_hwcap2) = (unsigned long int) av->a_un.a_val; +- break; +- case AT_CLKTCK: +- GLRO(dl_clktck) = av->a_un.a_val; +- break; +- case AT_FPUCW: +- GLRO(dl_fpu_control) = av->a_un.a_val; +- break; +-#ifdef NEED_DL_SYSINFO +- case AT_SYSINFO: +- new_sysinfo = av->a_un.a_val; +- break; +-#endif +-#ifdef NEED_DL_SYSINFO_DSO +- case AT_SYSINFO_EHDR: +- GLRO(dl_sysinfo_dso) = (void *) av->a_un.a_val; +- break; +-#endif +- case AT_RANDOM: +- _dl_random = (void *) av->a_un.a_val; +- break; +- case AT_MINSIGSTKSZ: +- GLRO(dl_minsigstacksize) = av->a_un.a_val; +- break; +- DL_PLATFORM_AUXV +- } +- +- dl_hwcap_check (); +- +-#ifndef HAVE_AUX_SECURE +- if (seen != -1) +- { +- /* Fill in the values we have not gotten from the kernel through the +- auxiliary vector. */ +-# ifndef HAVE_AUX_XID +-# define SEE(UID, var, uid) \ +- if ((seen & M (AT_##UID)) == 0) var ^= __get##uid () +- SEE (UID, uid, uid); +- SEE (EUID, uid, euid); +- SEE (GID, gid, gid); +- SEE (EGID, gid, egid); +-# endif +- +- /* If one of the two pairs of IDs does not match this is a setuid +- or setgid run. */ +- __libc_enable_secure = uid | gid; +- } +-#endif +- +-#ifndef HAVE_AUX_PAGESIZE +- if (GLRO(dl_pagesize) == 0) +- GLRO(dl_pagesize) = __getpagesize (); +-#endif +- +-#ifdef NEED_DL_SYSINFO +- if (new_sysinfo != 0) +- { +-# ifdef NEED_DL_SYSINFO_DSO +- /* Only set the sysinfo value if we also have the vsyscall DSO. */ +- if (GLRO(dl_sysinfo_dso) != 0) +-# endif +- GLRO(dl_sysinfo) = new_sysinfo; +- } +-#endif +- +- __tunables_init (_environ); +- +- /* Initialize DSO sorting algorithm after tunables. */ +- _dl_sort_maps_init (); +- +-#ifdef DL_SYSDEP_INIT +- DL_SYSDEP_INIT; +-#endif +- +-#ifdef DL_PLATFORM_INIT +- DL_PLATFORM_INIT; +-#endif +- +- /* Determine the length of the platform name. */ +- if (GLRO(dl_platform) != NULL) +- GLRO(dl_platformlen) = strlen (GLRO(dl_platform)); +- +- if (__sbrk (0) == _end) +- /* The dynamic linker was run as a program, and so the initial break +- starts just after our bss, at &_end. The malloc in dl-minimal.c +- will consume the rest of this page, so tell the kernel to move the +- break up that far. When the user program examines its break, it +- will see this new value and not clobber our data. */ +- __sbrk (GLRO(dl_pagesize) +- - ((_end - (char *) 0) & (GLRO(dl_pagesize) - 1))); +- +- /* If this is a SUID program we make sure that FDs 0, 1, and 2 are +- allocated. If necessary we are doing it ourself. If it is not +- possible we stop the program. */ +- if (__builtin_expect (__libc_enable_secure, 0)) +- __libc_check_standard_fds (); +- +- (*dl_main) (phdr, phnum, &user_entry, GLRO(dl_auxv)); +- return user_entry; +-} +- +-void +-_dl_sysdep_start_cleanup (void) +-{ +-} +- +-void +-_dl_show_auxv (void) +-{ +- char buf[64]; +- ElfW(auxv_t) *av; +- +- /* Terminate string. */ +- buf[63] = '\0'; +- +- /* The following code assumes that the AT_* values are encoded +- starting from 0 with AT_NULL, 1 for AT_IGNORE, and all other values +- close by (otherwise the array will be too large). In case we have +- to support a platform where these requirements are not fulfilled +- some alternative implementation has to be used. */ +- for (av = GLRO(dl_auxv); av->a_type != AT_NULL; ++av) +- { +- static const struct +- { +- const char label[22]; +- enum { unknown = 0, dec, hex, str, ignore } form : 8; +- } auxvars[] = +- { +- [AT_EXECFD - 2] = { "EXECFD: ", dec }, +- [AT_EXECFN - 2] = { "EXECFN: ", str }, +- [AT_PHDR - 2] = { "PHDR: 0x", hex }, +- [AT_PHENT - 2] = { "PHENT: ", dec }, +- [AT_PHNUM - 2] = { "PHNUM: ", dec }, +- [AT_PAGESZ - 2] = { "PAGESZ: ", dec }, +- [AT_BASE - 2] = { "BASE: 0x", hex }, +- [AT_FLAGS - 2] = { "FLAGS: 0x", hex }, +- [AT_ENTRY - 2] = { "ENTRY: 0x", hex }, +- [AT_NOTELF - 2] = { "NOTELF: ", hex }, +- [AT_UID - 2] = { "UID: ", dec }, +- [AT_EUID - 2] = { "EUID: ", dec }, +- [AT_GID - 2] = { "GID: ", dec }, +- [AT_EGID - 2] = { "EGID: ", dec }, +- [AT_PLATFORM - 2] = { "PLATFORM: ", str }, +- [AT_HWCAP - 2] = { "HWCAP: ", hex }, +- [AT_CLKTCK - 2] = { "CLKTCK: ", dec }, +- [AT_FPUCW - 2] = { "FPUCW: ", hex }, +- [AT_DCACHEBSIZE - 2] = { "DCACHEBSIZE: 0x", hex }, +- [AT_ICACHEBSIZE - 2] = { "ICACHEBSIZE: 0x", hex }, +- [AT_UCACHEBSIZE - 2] = { "UCACHEBSIZE: 0x", hex }, +- [AT_IGNOREPPC - 2] = { "IGNOREPPC", ignore }, +- [AT_SECURE - 2] = { "SECURE: ", dec }, +- [AT_BASE_PLATFORM - 2] = { "BASE_PLATFORM: ", str }, +- [AT_SYSINFO - 2] = { "SYSINFO: 0x", hex }, +- [AT_SYSINFO_EHDR - 2] = { "SYSINFO_EHDR: 0x", hex }, +- [AT_RANDOM - 2] = { "RANDOM: 0x", hex }, +- [AT_HWCAP2 - 2] = { "HWCAP2: 0x", hex }, +- [AT_MINSIGSTKSZ - 2] = { "MINSIGSTKSZ: ", dec }, +- [AT_L1I_CACHESIZE - 2] = { "L1I_CACHESIZE: ", dec }, +- [AT_L1I_CACHEGEOMETRY - 2] = { "L1I_CACHEGEOMETRY: 0x", hex }, +- [AT_L1D_CACHESIZE - 2] = { "L1D_CACHESIZE: ", dec }, +- [AT_L1D_CACHEGEOMETRY - 2] = { "L1D_CACHEGEOMETRY: 0x", hex }, +- [AT_L2_CACHESIZE - 2] = { "L2_CACHESIZE: ", dec }, +- [AT_L2_CACHEGEOMETRY - 2] = { "L2_CACHEGEOMETRY: 0x", hex }, +- [AT_L3_CACHESIZE - 2] = { "L3_CACHESIZE: ", dec }, +- [AT_L3_CACHEGEOMETRY - 2] = { "L3_CACHEGEOMETRY: 0x", hex }, +- }; +- unsigned int idx = (unsigned int) (av->a_type - 2); +- +- if ((unsigned int) av->a_type < 2u +- || (idx < sizeof (auxvars) / sizeof (auxvars[0]) +- && auxvars[idx].form == ignore)) +- continue; +- +- assert (AT_NULL == 0); +- assert (AT_IGNORE == 1); +- +- /* Some entries are handled in a special way per platform. */ +- if (_dl_procinfo (av->a_type, av->a_un.a_val) == 0) +- continue; +- +- if (idx < sizeof (auxvars) / sizeof (auxvars[0]) +- && auxvars[idx].form != unknown) +- { +- const char *val = (char *) av->a_un.a_val; +- +- if (__builtin_expect (auxvars[idx].form, dec) == dec) +- val = _itoa ((unsigned long int) av->a_un.a_val, +- buf + sizeof buf - 1, 10, 0); +- else if (__builtin_expect (auxvars[idx].form, hex) == hex) +- val = _itoa ((unsigned long int) av->a_un.a_val, +- buf + sizeof buf - 1, 16, 0); +- +- _dl_printf ("AT_%s%s\n", auxvars[idx].label, val); +- +- continue; +- } +- +- /* Unknown value: print a generic line. */ +- char buf2[17]; +- buf2[sizeof (buf2) - 1] = '\0'; +- const char *val2 = _itoa ((unsigned long int) av->a_un.a_val, +- buf2 + sizeof buf2 - 1, 16, 0); +- const char *val = _itoa ((unsigned long int) av->a_type, +- buf + sizeof buf - 1, 16, 0); +- _dl_printf ("AT_??? (0x%s): 0x%s\n", val, val2); +- } +-} +- +-#endif ++#error dl-sysdep support missing. +diff -Nuar a/elf/dl-tls.c b/elf/dl-tls.c +--- a/elf/dl-tls.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-tls.c 2025-10-20 21:02:20.000000000 +0300 +@@ -75,6 +75,31 @@ + /* Default for dl_tls_static_optional. */ + #define OPTIONAL_TLS 512 + ++/* Used to count the number of threads currently executing dynamic TLS ++ updates. Used to avoid recursive malloc calls in __tls_get_addr ++ for an interposed malloc that uses global-dynamic TLS (which is not ++ recommended); see _dl_tls_allocate_active checks. This could be a ++ per-thread flag, but would need TLS access in the dynamic linker. */ ++unsigned int _dl_tls_threads_in_update; ++ ++static inline void ++_dl_tls_allocate_begin (void) ++{ ++ atomic_fetch_add_relaxed (&_dl_tls_threads_in_update, 1); ++} ++ ++static inline void ++_dl_tls_allocate_end (void) ++{ ++ atomic_fetch_add_relaxed (&_dl_tls_threads_in_update, -1); ++} ++ ++static inline bool ++_dl_tls_allocate_active (void) ++{ ++ return atomic_load_relaxed (&_dl_tls_threads_in_update) > 0; ++} ++ + /* Compute the static TLS surplus based on the namespace count and the + TLS space that can be used for optimizations. */ + static inline int +@@ -160,6 +185,7 @@ + { + /* Mark the entry as used, so any dependency see it. */ + atomic_store_relaxed (&runp->slotinfo[result - disp].map, l); ++ atomic_store_relaxed (&runp->slotinfo[result - disp].gen, 0); + break; + } + +@@ -430,12 +456,18 @@ + size += TLS_PRE_TCB_SIZE; + #endif + +- /* Perform the allocation. Reserve space for the required alignment +- and the pointer to the original allocation. */ ++ /* Reserve space for the required alignment and the pointer to the ++ original allocation. */ + size_t alignment = GLRO (dl_tls_static_align); ++ ++ /* Perform the allocation. */ ++ _dl_tls_allocate_begin (); + void *allocated = malloc (size + alignment + sizeof (void *)); + if (__glibc_unlikely (allocated == NULL)) +- return NULL; ++ { ++ _dl_tls_allocate_end (); ++ return NULL; ++ } + + /* Perform alignment and allocate the DTV. */ + #if TLS_TCB_AT_TP +@@ -471,6 +503,8 @@ + result = allocate_dtv (result); + if (result == NULL) + free (allocated); ++ ++ _dl_tls_allocate_end (); + return result; + } + +@@ -488,6 +522,7 @@ + size_t newsize = max_modid + DTV_SURPLUS; + size_t oldsize = dtv[-1].counter; + ++ _dl_tls_allocate_begin (); + if (dtv == GL(dl_initial_dtv)) + { + /* This is the initial dtv that was either statically allocated in +@@ -507,6 +542,7 @@ + if (newp == NULL) + oom (); + } ++ _dl_tls_allocate_end (); + + newp[0].counter = newsize; + +@@ -681,7 +717,9 @@ + if (powerof2 (alignment) && alignment <= _Alignof (max_align_t)) + { + /* The alignment is supported by malloc. */ ++ _dl_tls_allocate_begin (); + void *ptr = malloc (size); ++ _dl_tls_allocate_end (); + return (struct dtv_pointer) { ptr, ptr }; + } + +@@ -693,7 +731,10 @@ + + /* Perform the allocation. This is the pointer we need to free + later. */ ++ _dl_tls_allocate_begin (); + void *start = malloc (alloc_size); ++ _dl_tls_allocate_end (); ++ + if (start == NULL) + return (struct dtv_pointer) {}; + +@@ -721,57 +762,57 @@ + + + struct link_map * +-_dl_update_slotinfo (unsigned long int req_modid) ++_dl_update_slotinfo (unsigned long int req_modid, size_t new_gen) + { + struct link_map *the_map = NULL; + dtv_t *dtv = THREAD_DTV (); + +- /* The global dl_tls_dtv_slotinfo array contains for each module +- index the generation counter current when the entry was created. ++ /* CONCURRENCY NOTES: ++ ++ The global dl_tls_dtv_slotinfo_list array contains for each module ++ index the generation counter current when that entry was updated. + This array never shrinks so that all module indices which were +- valid at some time can be used to access it. Before the first +- use of a new module index in this function the array was extended +- appropriately. Access also does not have to be guarded against +- modifications of the array. It is assumed that pointer-size +- values can be read atomically even in SMP environments. It is +- possible that other threads at the same time dynamically load +- code and therefore add to the slotinfo list. This is a problem +- since we must not pick up any information about incomplete work. +- The solution to this is to ignore all dtv slots which were +- created after the one we are currently interested. We know that +- dynamic loading for this module is completed and this is the last +- load operation we know finished. */ +- unsigned long int idx = req_modid; ++ valid at some time can be used to access it. Concurrent loading ++ and unloading of modules can update slotinfo entries or extend ++ the array. The updates happen under the GL(dl_load_tls_lock) and ++ finish with the release store of the generation counter to ++ GL(dl_tls_generation) which is synchronized with the load of ++ new_gen in the caller. So updates up to new_gen are synchronized ++ but updates for later generations may not be. ++ ++ Here we update the thread dtv from old_gen (== dtv[0].counter) to ++ new_gen generation. For this, each dtv[i] entry is either set to ++ an unallocated state (set), or left unmodified (nop). Where (set) ++ may resize the dtv first if modid i >= dtv[-1].counter. The rules ++ for the decision between (set) and (nop) are ++ ++ (1) If slotinfo entry i is concurrently updated then either (set) ++ or (nop) is valid: TLS access cannot use dtv[i] unless it is ++ synchronized with a generation > new_gen. ++ ++ Otherwise, if the generation of slotinfo entry i is gen and the ++ loaded module for this entry is map then ++ ++ (2) If gen <= old_gen then do (nop). ++ ++ (3) If old_gen < gen <= new_gen then ++ (3.1) if map != 0 then (set) ++ (3.2) if map == 0 then either (set) or (nop). ++ ++ Note that (1) cannot be reliably detected, but since both actions ++ are valid it does not have to be. Only (2) and (3.1) cases need ++ to be distinguished for which relaxed mo access of gen and map is ++ enough: their value is synchronized when it matters. ++ ++ Note that a relaxed mo load may give an out-of-thin-air value since ++ it is used in decisions that can affect concurrent stores. But this ++ should only happen if the OOTA value causes UB that justifies the ++ concurrent store of the value. This is not expected to be an issue ++ in practice. */ + struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list); + +- while (idx >= listp->len) +- { +- idx -= listp->len; +- listp = listp->next; +- } +- +- if (dtv[0].counter < listp->slotinfo[idx].gen) ++ if (dtv[0].counter < new_gen) + { +- /* CONCURRENCY NOTES: +- +- Here the dtv needs to be updated to new_gen generation count. +- +- This code may be called during TLS access when GL(dl_load_tls_lock) +- is not held. In that case the user code has to synchronize with +- dlopen and dlclose calls of relevant modules. A module m is +- relevant if the generation of m <= new_gen and dlclose of m is +- synchronized: a memory access here happens after the dlopen and +- before the dlclose of relevant modules. The dtv entries for +- relevant modules need to be updated, other entries can be +- arbitrary. +- +- This e.g. means that the first part of the slotinfo list can be +- accessed race free, but the tail may be concurrently extended. +- Similarly relevant slotinfo entries can be read race free, but +- other entries are racy. However updating a non-relevant dtv +- entry does not affect correctness. For a relevant module m, +- max_modid >= modid of m. */ +- size_t new_gen = listp->slotinfo[idx].gen; + size_t total = 0; + size_t max_modid = atomic_load_relaxed (&GL(dl_tls_max_dtv_idx)); + assert (max_modid >= req_modid); +@@ -784,31 +825,33 @@ + { + size_t modid = total + cnt; + +- /* Later entries are not relevant. */ ++ /* Case (1) for all later modids. */ + if (modid > max_modid) + break; + + size_t gen = atomic_load_relaxed (&listp->slotinfo[cnt].gen); + ++ /* Case (1). */ + if (gen > new_gen) +- /* Not relevant. */ + continue; + +- /* If the entry is older than the current dtv layout we +- know we don't have to handle it. */ ++ /* Case (2) or (1). */ + if (gen <= dtv[0].counter) + continue; + ++ /* Case (3) or (1). */ ++ + /* If there is no map this means the entry is empty. */ + struct link_map *map + = atomic_load_relaxed (&listp->slotinfo[cnt].map); + /* Check whether the current dtv array is large enough. */ + if (dtv[-1].counter < modid) + { ++ /* Case (3.2) or (1). */ + if (map == NULL) + continue; + +- /* Resize the dtv. */ ++ /* Resizing the dtv aborts on failure: bug 16134. */ + dtv = _dl_resize_dtv (dtv, max_modid); + + assert (modid <= dtv[-1].counter); +@@ -819,10 +862,21 @@ + } + + /* If there is currently memory allocate for this +- dtv entry free it. */ ++ dtv entry free it. Note: this is not AS-safe. */ + /* XXX Ideally we will at some point create a memory + pool. */ +- free (dtv[modid].pointer.to_free); ++ /* Avoid calling free on a null pointer. Some mallocs ++ incorrectly use dynamic TLS, and depending on how the ++ free function was compiled, it could call ++ __tls_get_addr before the null pointer check in the ++ free implementation. Checking here papers over at ++ least some dynamic TLS usage by interposed mallocs. */ ++ if (dtv[modid].pointer.to_free != NULL) ++ { ++ _dl_tls_allocate_begin (); ++ free (dtv[modid].pointer.to_free); ++ _dl_tls_allocate_end (); ++ } + dtv[modid].pointer.val = TLS_DTV_UNALLOCATED; + dtv[modid].pointer.to_free = NULL; + +@@ -914,9 +968,9 @@ + + static struct link_map * + __attribute_noinline__ +-update_get_addr (GET_ADDR_ARGS) ++update_get_addr (GET_ADDR_ARGS, size_t gen) + { +- struct link_map *the_map = _dl_update_slotinfo (GET_ADDR_MODULE); ++ struct link_map *the_map = _dl_update_slotinfo (GET_ADDR_MODULE, gen); + dtv_t *dtv = THREAD_DTV (); + + void *p = dtv[GET_ADDR_MODULE].pointer.val; +@@ -946,12 +1000,29 @@ + dtv_t *dtv = THREAD_DTV (); + + /* Update is needed if dtv[0].counter < the generation of the accessed +- module. The global generation counter is used here as it is easier +- to check. Synchronization for the relaxed MO access is guaranteed +- by user code, see CONCURRENCY NOTES in _dl_update_slotinfo. */ ++ module, but the global generation counter is easier to check (which ++ must be synchronized up to the generation of the accessed module by ++ user code doing the TLS access so relaxed mo read is enough). */ + size_t gen = atomic_load_relaxed (&GL(dl_tls_generation)); + if (__glibc_unlikely (dtv[0].counter != gen)) +- return update_get_addr (GET_ADDR_PARAM); ++ { ++ if (_dl_tls_allocate_active () ++ && GET_ADDR_MODULE < _dl_tls_initial_modid_limit) ++ /* This is a reentrant __tls_get_addr call, but we can ++ satisfy it because it's an initially-loaded module ID. ++ These TLS slotinfo slots do not change, so the ++ out-of-date generation counter does not matter. However, ++ if not in a TLS update, still update_get_addr below, to ++ get off the slow path eventually. */ ++ ; ++ else ++ { ++ /* Update DTV up to the global generation, see CONCURRENCY NOTES ++ in _dl_update_slotinfo. */ ++ gen = atomic_load_acquire (&GL(dl_tls_generation)); ++ return update_get_addr (GET_ADDR_PARAM, gen); ++ } ++ } + + void *p = dtv[GET_ADDR_MODULE].pointer.val; + +@@ -960,7 +1031,7 @@ + + return (char *) p + GET_ADDR_OFFSET; + } +-#endif ++#endif /* SHARED */ + + + /* Look up the module's TLS block as for __tls_get_addr, +@@ -1009,6 +1080,25 @@ + return data; + } + ++size_t _dl_tls_initial_modid_limit; ++ ++void ++_dl_tls_initial_modid_limit_setup (void) ++{ ++ struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list); ++ size_t idx; ++ for (idx = 0; idx < listp->len; ++idx) ++ { ++ struct link_map *l = listp->slotinfo[idx].map; ++ if (l == NULL ++ /* The object can be unloaded, so its modid can be ++ reassociated. */ ++ || !(l->l_type == lt_executable || l->l_type == lt_library)) ++ break; ++ } ++ _dl_tls_initial_modid_limit = idx; ++} ++ + + void + _dl_add_to_slotinfo (struct link_map *l, bool do_add) +@@ -1041,9 +1131,11 @@ + the first slot. */ + assert (idx == 0); + ++ _dl_tls_allocate_begin (); + listp = (struct dtv_slotinfo_list *) + malloc (sizeof (struct dtv_slotinfo_list) + + TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo)); ++ _dl_tls_allocate_end (); + if (listp == NULL) + { + /* We ran out of memory while resizing the dtv slotinfo list. */ +diff -Nuar a/elf/dl-tunables.c b/elf/dl-tunables.c +--- a/elf/dl-tunables.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-tunables.c 2025-10-20 21:02:20.000000000 +0300 +@@ -187,11 +187,7 @@ + /* If we reach the end of the string before getting a valid name-value + pair, bail out. */ + if (p[len] == '\0') +- { +- if (__libc_enable_secure) +- tunestr[off] = '\0'; +- return; +- } ++ break; + + /* We did not find a valid name-value pair before encountering the + colon. */ +@@ -251,9 +247,16 @@ + } + } + +- if (p[len] != '\0') +- p += len + 1; ++ /* We reached the end while processing the tunable string. */ ++ if (p[len] == '\0') ++ break; ++ ++ p += len + 1; + } ++ ++ /* Terminate tunestr before we leave. */ ++ if (__libc_enable_secure) ++ tunestr[off] = '\0'; + } + #endif + +diff -Nuar a/elf/dl-tunables.list b/elf/dl-tunables.list +--- a/elf/dl-tunables.list 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dl-tunables.list 2025-10-20 21:02:20.000000000 +0300 +@@ -169,4 +169,17 @@ + default: 2 + } + } ++ ++ gmon { ++ minarcs { ++ type: INT_32 ++ minval: 50 ++ default: 50 ++ } ++ maxarcs { ++ type: INT_32 ++ minval: 50 ++ default: 1048576 ++ } ++ } + } +diff -Nuar a/elf/dso-sort-tests-1.def b/elf/dso-sort-tests-1.def +--- a/elf/dso-sort-tests-1.def 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/dso-sort-tests-1.def 2025-10-20 21:02:20.000000000 +0300 +@@ -64,3 +64,10 @@ + tst-bz15311: {+a;+e;+f;+g;+d;%d;-d;-g;-f;-e;-a};a->b->c->d;d=>[ba];c=>a;b=>e=>a;c=>f=>b;d=>g=>c + output(glibc.rtld.dynamic_sort=1): {+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a()))))));-d[];-g[];-f[];-e[];-a[a1;a->a2;a2->a;b->b1;c->a1;c=>a1 ++output(glibc.rtld.dynamic_sort=1): {+a[a2>a1>a>];+b[b1>b>];-b[];%c(a1());}a1>a>];+b[b1>b>];-b[];%c(a1());} + #include + +-/* If nonzero __libc_enable_secure is already set. */ +-int __libc_enable_secure_decided; + /* Safest assumption, if somehow the initializer isn't run. */ + int __libc_enable_secure = 1; +- +-void +-__libc_init_secure (void) +-{ +- if (__libc_enable_secure_decided == 0) +- __libc_enable_secure = (startup_geteuid () != startup_getuid () +- || startup_getegid () != startup_getgid ()); +-} +diff -Nuar a/elf/ifuncmain1.c b/elf/ifuncmain1.c +--- a/elf/ifuncmain1.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/ifuncmain1.c 2025-10-20 21:02:17.000000000 +0300 +@@ -19,7 +19,14 @@ + #endif + + foo_p foo_ptr = foo; ++ ++/* Address-significant access to protected symbols is not supported in ++ position-dependent mode on several architectures because GCC ++ generates relocations that assume that the address is local to the ++ main program. */ ++#ifdef __PIE__ + foo_p foo_procted_ptr = foo_protected; ++#endif + + extern foo_p get_foo_p (void); + extern foo_p get_foo_hidden_p (void); +@@ -37,12 +44,16 @@ + if ((*foo_ptr) () != -1) + abort (); + ++#ifdef __PIE__ + if (foo_procted_ptr != foo_protected) + abort (); ++#endif + if (foo_protected () != 0) + abort (); ++#ifdef __PIE__ + if ((*foo_procted_ptr) () != 0) + abort (); ++#endif + + p = get_foo_p (); + if (p != foo) +@@ -55,8 +66,10 @@ + abort (); + + p = get_foo_protected_p (); ++#ifdef __PIE__ + if (p != foo_protected) + abort (); ++#endif + if (ret_foo_protected != 0 || (*p) () != ret_foo_protected) + abort (); + +diff -Nuar a/elf/ifuncmain5.c b/elf/ifuncmain5.c +--- a/elf/ifuncmain5.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/ifuncmain5.c 2025-10-20 21:02:17.000000000 +0300 +@@ -14,12 +14,19 @@ + return foo; + } + ++ ++/* Address-significant access to protected symbols is not supported in ++ position-dependent mode on several architectures because GCC ++ generates relocations that assume that the address is local to the ++ main program. */ ++#ifdef __PIE__ + foo_p + __attribute__ ((noinline)) + get_foo_protected (void) + { + return foo_protected; + } ++#endif + + int + main (void) +@@ -30,9 +37,11 @@ + if ((*p) () != -1) + abort (); + ++#ifdef __PIE__ + p = get_foo_protected (); + if ((*p) () != 0) + abort (); ++#endif + + return 0; + } +diff -Nuar a/elf/libtracemod1-1.c b/elf/libtracemod1-1.c +--- a/elf/libtracemod1-1.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/libtracemod1-1.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1 @@ ++/* Empty */ +diff -Nuar a/elf/libtracemod2-1.c b/elf/libtracemod2-1.c +--- a/elf/libtracemod2-1.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/libtracemod2-1.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1 @@ ++/* Empty */ +diff -Nuar a/elf/libtracemod3-1.c b/elf/libtracemod3-1.c +--- a/elf/libtracemod3-1.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/libtracemod3-1.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1 @@ ++/* Empty */ +diff -Nuar a/elf/libtracemod4-1.c b/elf/libtracemod4-1.c +--- a/elf/libtracemod4-1.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/libtracemod4-1.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1 @@ ++/* Empty */ +diff -Nuar a/elf/libtracemod5-1.c b/elf/libtracemod5-1.c +--- a/elf/libtracemod5-1.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/libtracemod5-1.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1 @@ ++/* Empty */ +diff -Nuar a/elf/Makefile b/elf/Makefile +--- a/elf/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/Makefile 2025-10-20 21:02:20.000000000 +0300 +@@ -33,6 +33,7 @@ + $(all-dl-routines) \ + dl-addr \ + dl-addr-obj \ ++ dl-early_allocate \ + dl-error \ + dl-iteratephdr \ + dl-libc \ +@@ -52,6 +53,7 @@ + # profiled libraries. + dl-routines = \ + dl-call-libc-early-init \ ++ dl-call_fini \ + dl-close \ + dl-debug \ + dl-debug-symbols \ +@@ -108,6 +110,7 @@ + # But they are absent from the shared libc, because that code is in ld.so. + elide-routines.os = \ + $(all-dl-routines) \ ++ dl-early_allocate \ + dl-exception \ + dl-origin \ + dl-reloc-static-pie \ +@@ -161,6 +164,11 @@ + CFLAGS-rtld.c += -fno-tree-loop-distribute-patterns + endif + ++ifeq (yes,$(have-loop-to-function)) ++# Likewise, during static library startup, memset is not yet available. ++CFLAGS-dl-support.c = -fno-tree-loop-distribute-patterns ++endif ++ + # Compile rtld itself without stack protection. + # Also compile all routines in the static library that are elided from + # the shared libc because they are in libc.a in the same way. +@@ -170,6 +178,7 @@ + CFLAGS-.os += $(call elide-stack-protector,.os,$(all-rtld-routines)) + + # Add the requested compiler flags to the early startup code. ++CFLAGS-dl-misc.os += $(rtld-early-cflags) + CFLAGS-dl-printf.os += $(rtld-early-cflags) + CFLAGS-dl-setup_hash.os += $(rtld-early-cflags) + CFLAGS-dl-sysdep.os += $(rtld-early-cflags) +@@ -265,6 +274,7 @@ + tst-array1-static \ + tst-array5-static \ + tst-dl-iter-static \ ++ tst-dlopen-sgid \ + tst-dst-static \ + tst-env-setuid \ + tst-env-setuid-tunables \ +@@ -272,6 +282,7 @@ + tst-linkall-static \ + tst-single_threaded-pthread-static \ + tst-single_threaded-static \ ++ tst-tls-allocation-failure-static \ + tst-tlsalign-extern-static \ + tst-tlsalign-static \ + # tests-static-normal +@@ -366,6 +377,8 @@ + tst-align \ + tst-align2 \ + tst-align3 \ ++ tst-audit-tlsdesc \ ++ tst-audit-tlsdesc-dlopen \ + tst-audit1 \ + tst-audit2 \ + tst-audit8 \ +@@ -386,6 +399,7 @@ + tst-audit24d \ + tst-audit25a \ + tst-audit25b \ ++ tst-audit26 \ + tst-auditmany \ + tst-auxobj \ + tst-auxobj-dlopen \ +@@ -399,6 +413,7 @@ + tst-dlmopen4 \ + tst-dlmopen-dlerror \ + tst-dlmopen-gethostbyname \ ++ tst-dlmopen-twice \ + tst-dlopenfail \ + tst-dlopenfail-2 \ + tst-dlopenrpath \ +@@ -425,6 +440,7 @@ + tst-p_align1 \ + tst-p_align2 \ + tst-p_align3 \ ++ tst-recursive-tls \ + tst-relsort1 \ + tst-ro-dynamic \ + tst-rtld-run-static \ +@@ -487,6 +503,8 @@ + tst-dl_find_object \ + tst-dl_find_object-threads \ + tst-dlmopen2 \ ++ tst-link-map-contiguous-ldso \ ++ tst-link-map-contiguous-libc \ + tst-ptrguard1 \ + tst-stackguard1 \ + tst-tls-surplus \ +@@ -498,6 +516,10 @@ + unload2 \ + # tests-internal + ++ifeq ($(build-hardcoded-path-in-tests),yes) ++tests-internal += tst-link-map-contiguous-main ++endif ++ + tests-container += \ + tst-dlopen-self-container \ + tst-dlopen-tlsmodid-container \ +@@ -539,6 +561,39 @@ + endif + endif + ++tests-special += $(objpfx)tst-relro-ldso.out $(objpfx)tst-relro-libc.out ++$(objpfx)tst-relro-ldso.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \ ++ $(objpfx)ld.so ++ $(PYTHON) tst-relro-symbols.py $(objpfx)ld.so \ ++ --required=_rtld_global_ro \ ++ > $@ 2>&1; $(evaluate-test) ++# The optional symbols are present in libc only if the architecture has ++# the GLIBC_2.0 symbol set in libc. ++$(objpfx)tst-relro-libc.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \ ++ $(common-objpfx)libc.so ++ $(PYTHON) tst-relro-symbols.py $(common-objpfx)libc.so \ ++ --required=_IO_cookie_jumps \ ++ --required=_IO_file_jumps \ ++ --required=_IO_file_jumps_maybe_mmap \ ++ --required=_IO_file_jumps_mmap \ ++ --required=_IO_helper_jumps \ ++ --required=_IO_mem_jumps \ ++ --required=_IO_obstack_jumps \ ++ --required=_IO_proc_jumps \ ++ --required=_IO_str_chk_jumps \ ++ --required=_IO_str_jumps \ ++ --required=_IO_strn_jumps \ ++ --required=_IO_wfile_jumps \ ++ --required=_IO_wfile_jumps_maybe_mmap \ ++ --required=_IO_wfile_jumps_mmap \ ++ --required=_IO_wmem_jumps \ ++ --required=_IO_wstr_jumps \ ++ --required=_IO_wstrn_jumps \ ++ --optional=_IO_old_cookie_jumps \ ++ --optional=_IO_old_file_jumps \ ++ --optional=_IO_old_proc_jumps \ ++ > $@ 2>&1; $(evaluate-test) ++ + ifeq ($(run-built-tests),yes) + tests-special += $(objpfx)tst-valgrind-smoke.out + endif +@@ -551,6 +606,7 @@ + tests-special += \ + $(objpfx)noload-mem.out \ + $(objpfx)tst-ldconfig-X.out \ ++ $(objpfx)tst-ldconfig-p.out \ + $(objpfx)tst-leaks1-mem.out \ + $(objpfx)tst-rtld-help.out \ + # tests-special +@@ -613,6 +669,16 @@ + libmarkermod4-2 \ + libmarkermod4-3 \ + libmarkermod4-4 \ ++ libmarkermod5-1 \ ++ libmarkermod5-2 \ ++ libmarkermod5-3 \ ++ libmarkermod5-4 \ ++ libmarkermod5-5 \ ++ libtracemod1-1 \ ++ libtracemod2-1 \ ++ libtracemod3-1 \ ++ libtracemod4-1 \ ++ libtracemod5-1 \ + ltglobmod1 \ + ltglobmod2 \ + neededobj1 \ +@@ -674,6 +740,8 @@ + tst-alignmod3 \ + tst-array2dep \ + tst-array5dep \ ++ tst-audit-tlsdesc-mod1 \ ++ tst-audit-tlsdesc-mod2 \ + tst-audit11mod1 \ + tst-audit11mod2 \ + tst-audit12mod1 \ +@@ -707,6 +775,7 @@ + tst-auditmanymod7 \ + tst-auditmanymod8 \ + tst-auditmanymod9 \ ++ tst-auditmod-tlsdesc \ + tst-auditmod1 \ + tst-auditmod9a \ + tst-auditmod9b \ +@@ -725,6 +794,7 @@ + tst-auditmod24c \ + tst-auditmod24d \ + tst-auditmod25 \ ++ tst-auditmod26 \ + tst-auxvalmod \ + tst-big-note-lib \ + tst-deep1mod1 \ +@@ -742,6 +812,9 @@ + tst-dlmopen1mod \ + tst-dlmopen-dlerror-mod \ + tst-dlmopen-gethostbyname-mod \ ++ tst-dlmopen-twice-mod1 \ ++ tst-dlmopen-twice-mod2 \ ++ tst-dlopen-sgid-mod \ + tst-dlopenfaillinkmod \ + tst-dlopenfailmod1 \ + tst-dlopenfailmod2 \ +@@ -774,6 +847,23 @@ + tst-null-argv-lib \ + tst-p_alignmod-base \ + tst-p_alignmod3 \ ++ tst-recursive-tlsmallocmod \ ++ tst-recursive-tlsmod0 \ ++ tst-recursive-tlsmod1 \ ++ tst-recursive-tlsmod2 \ ++ tst-recursive-tlsmod3 \ ++ tst-recursive-tlsmod4 \ ++ tst-recursive-tlsmod5 \ ++ tst-recursive-tlsmod6 \ ++ tst-recursive-tlsmod7 \ ++ tst-recursive-tlsmod8 \ ++ tst-recursive-tlsmod9 \ ++ tst-recursive-tlsmod10 \ ++ tst-recursive-tlsmod11 \ ++ tst-recursive-tlsmod12 \ ++ tst-recursive-tlsmod13 \ ++ tst-recursive-tlsmod14 \ ++ tst-recursive-tlsmod15 \ + tst-relsort1mod1 \ + tst-relsort1mod2 \ + tst-ro-dynamic-mod \ +@@ -895,23 +985,8 @@ + $(objpfx)tst-gnu2-tls1: $(objpfx)tst-gnu2-tls1mod.so + tst-gnu2-tls1mod.so-no-z-defs = yes + CFLAGS-tst-gnu2-tls1mod.c += -mtls-dialect=gnu2 ++endif # $(have-mtls-dialect-gnu2) + +-tests += tst-audit-tlsdesc tst-audit-tlsdesc-dlopen +-modules-names += tst-audit-tlsdesc-mod1 tst-audit-tlsdesc-mod2 tst-auditmod-tlsdesc +-$(objpfx)tst-audit-tlsdesc: $(objpfx)tst-audit-tlsdesc-mod1.so \ +- $(objpfx)tst-audit-tlsdesc-mod2.so \ +- $(shared-thread-library) +-CFLAGS-tst-audit-tlsdesc-mod1.c += -mtls-dialect=gnu2 +-CFLAGS-tst-audit-tlsdesc-mod2.c += -mtls-dialect=gnu2 +-$(objpfx)tst-audit-tlsdesc-dlopen: $(shared-thread-library) +-$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-audit-tlsdesc-mod1.so \ +- $(objpfx)tst-audit-tlsdesc-mod2.so +-$(objpfx)tst-audit-tlsdesc-mod1.so: $(objpfx)tst-audit-tlsdesc-mod2.so +-$(objpfx)tst-audit-tlsdesc.out: $(objpfx)tst-auditmod-tlsdesc.so +-tst-audit-tlsdesc-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so +-$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-auditmod-tlsdesc.so +-tst-audit-tlsdesc-dlopen-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so +-endif + ifeq (yes,$(have-protected-data)) + modules-names += tst-protected1moda tst-protected1modb + tests += tst-protected1a tst-protected1b +@@ -943,7 +1018,7 @@ + # filtmod1.so, tst-big-note-lib.so, tst-ro-dynamic-mod.so have special + # rules. + modules-names-nobuild := filtmod1 tst-big-note-lib tst-ro-dynamic-mod \ +- tst-audit24bmod1 tst-audit24bmod2.so ++ tst-audit24bmod1 tst-audit24bmod2 + + tests += $(tests-static) + +@@ -1072,6 +1147,11 @@ + $(objpfx)tst-initorder2-cmp.out \ + $(objpfx)tst-unused-dep-cmp.out \ + $(objpfx)tst-unused-dep.out \ ++ $(objpfx)tst-trace1.out \ ++ $(objpfx)tst-trace2.out \ ++ $(objpfx)tst-trace3.out \ ++ $(objpfx)tst-trace4.out \ ++ $(objpfx)tst-trace5.out \ + # tests-special + endif + +@@ -1111,6 +1191,17 @@ + tst-prelink-no-pie = yes + endif + ++tests-special += $(objpfx)tst-glibcelf.out ++$(objpfx)tst-glibcelf.out: tst-glibcelf.py elf.h $(..)/scripts/glibcelf.py \ ++ $(..)/scripts/glibcextract.py ++ PYTHONPATH=$(..)scripts $(PYTHON) tst-glibcelf.py \ ++ --cc="$(CC) $(patsubst -DMODULE_NAME=%,-DMODULE_NAME=testsuite,$(CPPFLAGS))" \ ++ < /dev/null > $@ 2>&1; $(evaluate-test) ++ ++ifeq ($(run-built-tests),yes) ++tests-special += $(objpfx)tst-tls-allocation-failure-static-patched.out ++endif ++ + # The test requires shared _and_ PIE because the executable + # unit test driver must be able to link with the shared object + # that is going to eventually go into an installed DSO. +@@ -1234,8 +1325,7 @@ + $(LINK.o) -nostdlib -nostartfiles -shared -o $@.new \ + $(LDFLAGS-rtld) -Wl,-z,defs $(z-now-$(bind-now)) \ + $(filter-out $(map-file),$^) $(load-map-file) \ +- -Wl,-soname=$(rtld-installed-name) \ +- -Wl,-defsym=_begin=0 ++ -Wl,-soname=$(rtld-installed-name) + $(call after-link,$@.new) + $(READELF) -s $@.new \ + | $(AWK) '($$7 ~ /^UND(|EF)$$/ && $$1 != "0:" && $$4 != "REGISTER") { print; p=1 } END { exit p != 0 }' +@@ -2210,7 +2300,7 @@ + $(objpfx)tst-audit24c: $(objpfx)tst-audit24amod1.so \ + $(objpfx)tst-audit24amod2.so + tst-audit24c-ENV = LD_BIND_NOW=1 LD_AUDIT=$(objpfx)tst-auditmod24c.so +-LDFLAGS-tst-audit24b = -Wl,-z,lazy ++LDFLAGS-tst-audit24c = -Wl,-z,lazy + + $(objpfx)tst-audit24d.out: $(objpfx)tst-auditmod24d.so + $(objpfx)tst-audit24d: $(objpfx)tst-audit24dmod1.so \ +@@ -2242,6 +2332,10 @@ + LDFLAGS-tst-audit25b = -Wl,-z,now + tst-audit25b-ARGS = -- $(host-test-program-cmd) + ++$(objpfx)tst-audit26.out: $(objpfx)tst-auditmod26.so ++$(objpfx)tst-auditmod26.so: $(libsupport) ++tst-audit26-ENV = LD_AUDIT=$(objpfx)tst-auditmod26.so ++ + # tst-sonamemove links against an older implementation of the library. + LDFLAGS-tst-sonamemove-linkmod1.so = \ + -Wl,--version-script=tst-sonamemove-linkmod1.map \ +@@ -2297,6 +2391,11 @@ + '$(run-program-env)' > $@; \ + $(evaluate-test) + ++$(objpfx)tst-ldconfig-p.out : tst-ldconfig-p.sh $(objpfx)ldconfig ++ $(SHELL) $< '$(common-objpfx)' '$(test-wrapper-env)' \ ++ '$(run-program-env)' > $@; \ ++ $(evaluate-test) ++ + # Test static linking of all the libraries we can possibly link + # together. Note that in some configurations this may be less than the + # complete list of libraries we build but we try to maxmimize this list. +@@ -2509,6 +2608,7 @@ + LDFLAGS-libmarkermod2-1.so += -Wl,-soname,libmarkermod2.so + LDFLAGS-libmarkermod3-1.so += -Wl,-soname,libmarkermod3.so + LDFLAGS-libmarkermod4-1.so += -Wl,-soname,libmarkermod4.so ++LDFLAGS-libmarkermod5-1.so += -Wl,-soname,libmarkermod5.so + $(objpfx)libmarkermod%.os : markermodMARKER-VALUE.c + $(compile-command.c) \ + -DMARKER=marker$(firstword $(subst -, ,$*)) \ +@@ -2521,6 +2621,8 @@ + cp $< $@ + $(objpfx)libmarkermod4.so: $(objpfx)libmarkermod4-1.so + cp $< $@ ++$(objpfx)libmarkermod5.so: $(objpfx)libmarkermod5-1.so ++ cp $< $@ + + # tst-glibc-hwcaps-prepend checks that --glibc-hwcaps-prepend is + # preferred over auto-detected subdirectories. +@@ -2733,3 +2835,91 @@ + $(objpfx)tst-p_align3.out: tst-p_align3.sh $(objpfx)tst-p_align3 + $(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \ + $(evaluate-test) ++ ++LDFLAGS-libtracemod1-1.so += -Wl,-soname,libtracemod1.so ++LDFLAGS-libtracemod2-1.so += -Wl,-soname,libtracemod2.so ++LDFLAGS-libtracemod3-1.so += -Wl,-soname,libtracemod3.so ++LDFLAGS-libtracemod4-1.so += -Wl,-soname,libtracemod4.so ++LDFLAGS-libtracemod5-1.so += -Wl,-soname,libtracemod5.so ++ ++$(objpfx)libtracemod1-1.so: $(objpfx)libtracemod2-1.so \ ++ $(objpfx)libtracemod3-1.so ++$(objpfx)libtracemod2-1.so: $(objpfx)libtracemod4-1.so \ ++ $(objpfx)libtracemod5-1.so ++ ++define libtracemod-x ++$(objpfx)libtracemod$(1)/libtracemod$(1).so: $(objpfx)libtracemod$(1)-1.so ++ $$(make-target-directory) ++ cp $$< $$@ ++endef ++libtracemod-suffixes = 1 2 3 4 5 ++$(foreach i,$(libtracemod-suffixes), $(eval $(call libtracemod-x,$(i)))) ++ ++define tst-trace-skeleton ++$(objpfx)tst-trace$(1).out: $(objpfx)libtracemod1/libtracemod1.so \ ++ $(objpfx)libtracemod2/libtracemod2.so \ ++ $(objpfx)libtracemod3/libtracemod3.so \ ++ $(objpfx)libtracemod4/libtracemod4.so \ ++ $(objpfx)libtracemod5/libtracemod5.so \ ++ $(..)scripts/tst-ld-trace.py \ ++ tst-trace$(1).exp ++ ${ $(PYTHON) $(..)scripts/tst-ld-trace.py \ ++ "$(test-wrapper-env) $(elf-objpfx)$(rtld-installed-name) \ ++ --library-path $(common-objpfx):$(strip $(2)) \ ++ $(objpfx)libtracemod1/libtracemod1.so" tst-trace$(1).exp \ ++ } > $$@; $$(evaluate-test) ++endef ++ ++$(eval $(call tst-trace-skeleton,1,)) ++$(eval $(call tst-trace-skeleton,2,\ ++ $(objpfx)libtracemod2)) ++$(eval $(call tst-trace-skeleton,3,\ ++ $(objpfx)libtracemod2:$(objpfx)libtracemod3)) ++$(eval $(call tst-trace-skeleton,4,\ ++ $(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4)) ++$(eval $(call tst-trace-skeleton,5,\ ++ $(objpfx)libtracemod2:$(objpfx)libtracemod3:$(objpfx)libtracemod4:$(objpfx)libtracemod5)) ++ ++$(objpfx)tst-tls-allocation-failure-static-patched: \ ++ $(objpfx)tst-tls-allocation-failure-static $(..)scripts/tst-elf-edit.py ++ cp $< $@ ++ $(PYTHON) $(..)scripts/tst-elf-edit.py --maximize-tls-size $@ ++ ++$(objpfx)tst-tls-allocation-failure-static-patched.out: \ ++ $(objpfx)tst-tls-allocation-failure-static-patched ++ $< > $@ 2>&1; echo "status: $$?" >> $@ ++ grep -q '^Fatal glibc error: Cannot allocate TLS block$$' $@ \ ++ && grep -q '^status: 127$$' $@; \ ++ $(evaluate-test) ++ ++$(objpfx)tst-audit-tlsdesc: $(objpfx)tst-audit-tlsdesc-mod1.so \ ++ $(objpfx)tst-audit-tlsdesc-mod2.so \ ++ $(shared-thread-library) ++ifeq (yes,$(have-mtls-dialect-gnu2)) ++# The test is valid for all TLS types, but we want to exercise GNU2 ++# TLS if possible. ++CFLAGS-tst-audit-tlsdesc-mod1.c += -mtls-dialect=gnu2 ++CFLAGS-tst-audit-tlsdesc-mod2.c += -mtls-dialect=gnu2 ++endif ++$(objpfx)tst-audit-tlsdesc-dlopen: $(shared-thread-library) ++$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-audit-tlsdesc-mod1.so \ ++ $(objpfx)tst-audit-tlsdesc-mod2.so ++$(objpfx)tst-audit-tlsdesc-mod1.so: $(objpfx)tst-audit-tlsdesc-mod2.so ++$(objpfx)tst-audit-tlsdesc.out: $(objpfx)tst-auditmod-tlsdesc.so ++tst-audit-tlsdesc-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so ++$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-auditmod-tlsdesc.so ++tst-audit-tlsdesc-dlopen-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so ++ ++$(objpfx)tst-dlmopen-twice.out: \ ++ $(objpfx)tst-dlmopen-twice-mod1.so \ ++ $(objpfx)tst-dlmopen-twice-mod2.so ++ ++$(objpfx)tst-recursive-tls: $(objpfx)tst-recursive-tlsmallocmod.so ++# More objects than DTV_SURPLUS, to trigger DTV reallocation. ++$(objpfx)tst-recursive-tls.out: \ ++ $(patsubst %,$(objpfx)tst-recursive-tlsmod%.so, \ ++ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) ++$(objpfx)tst-recursive-tlsmod%.os: tst-recursive-tlsmodN.c ++ $(compile-command.c) -DVAR=thread_$* -DFUNC=get_threadvar_$* ++ ++$(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so +diff -Nuar a/elf/rtld.c b/elf/rtld.c +--- a/elf/rtld.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/rtld.c 2025-10-20 21:02:20.000000000 +0300 +@@ -441,8 +441,8 @@ + struct dl_start_final_info *info); + #endif + +-/* These defined magically in the linker script. */ +-extern char _begin[] attribute_hidden; ++/* These are defined magically by the linker. */ ++extern const ElfW(Ehdr) __ehdr_start attribute_hidden; + extern char _etext[] attribute_hidden; + extern char _end[] attribute_hidden; + +@@ -487,7 +487,7 @@ + #endif + _dl_setup_hash (&GL(dl_rtld_map)); + GL(dl_rtld_map).l_real = &GL(dl_rtld_map); +- GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin; ++ GL(dl_rtld_map).l_map_start = (ElfW(Addr)) &__ehdr_start; + GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end; + GL(dl_rtld_map).l_text_end = (ElfW(Addr)) _etext; + /* Copy the TLS related data if necessary. */ +@@ -810,6 +810,8 @@ + _dl_fatal_printf ("\ + cannot allocate TLS data structures for initial thread\n"); + ++ _dl_tls_initial_modid_limit_setup (); ++ + /* Store for detection of the special case by __tls_get_addr + so it knows not to pass this dtv to the normal realloc. */ + GL(dl_initial_dtv) = GET_DTV (tcbp); +@@ -1312,6 +1314,116 @@ + return has_interp; + } + ++/* Set up the program header information for the dynamic linker ++ itself. It can be accessed via _r_debug and dl_iterate_phdr ++ callbacks, and it is used by _dl_find_object. */ ++static void ++rtld_setup_phdr (void) ++{ ++ /* Starting from binutils-2.23, the linker will define the magic ++ symbol __ehdr_start to point to our own ELF header if it is ++ visible in a segment that also includes the phdrs. */ ++ ++ const ElfW(Ehdr) *rtld_ehdr = &__ehdr_start; ++ assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr); ++ assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr))); ++ ++ const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff; ++ ++ GL(dl_rtld_map).l_phdr = rtld_phdr; ++ GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum; ++ ++ ++ GL(dl_rtld_map).l_contiguous = 1; ++ /* The linker may not have produced a contiguous object. The kernel ++ will load the object with actual gaps (unlike the glibc loader ++ for shared objects, which always produces a contiguous mapping). ++ See similar logic in rtld_setup_main_map above. */ ++ { ++ ElfW(Addr) expected_load_address = 0; ++ for (const ElfW(Phdr) *ph = rtld_phdr; ph < &rtld_phdr[rtld_ehdr->e_phnum]; ++ ++ph) ++ if (ph->p_type == PT_LOAD) ++ { ++ ElfW(Addr) mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1); ++ if (GL(dl_rtld_map).l_contiguous && expected_load_address != 0 ++ && expected_load_address != mapstart) ++ GL(dl_rtld_map).l_contiguous = 0; ++ ElfW(Addr) allocend = ph->p_vaddr + ph->p_memsz; ++ /* The next expected address is the page following this load ++ segment. */ ++ expected_load_address = ((allocend + GLRO(dl_pagesize) - 1) ++ & ~(GLRO(dl_pagesize) - 1)); ++ } ++ } ++ ++ /* PT_GNU_RELRO is usually the last phdr. */ ++ size_t cnt = rtld_ehdr->e_phnum; ++ while (cnt-- > 0) ++ if (rtld_phdr[cnt].p_type == PT_GNU_RELRO) ++ { ++ GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr; ++ GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz; ++ break; ++ } ++} ++ ++/* Adjusts the contents of the stack and related globals for the user ++ entry point. The ld.so processed skip_args arguments and bumped ++ _dl_argv and _dl_argc accordingly. Those arguments are removed from ++ argv here. */ ++static void ++_dl_start_args_adjust (int skip_args) ++{ ++ void **sp = (void **) (_dl_argv - skip_args - 1); ++ void **p = sp + skip_args; ++ ++ if (skip_args == 0) ++ return; ++ ++ /* Sanity check. */ ++ intptr_t argc = (intptr_t) sp[0] - skip_args; ++ assert (argc == _dl_argc); ++ ++ /* Adjust argc on stack. */ ++ sp[0] = (void *) (intptr_t) _dl_argc; ++ ++ /* Update globals in rtld. */ ++ _dl_argv -= skip_args; ++ _environ -= skip_args; ++ ++ /* Shuffle argv down. */ ++ do ++ *++sp = *++p; ++ while (*p != NULL); ++ ++ assert (_environ == (char **) (sp + 1)); ++ ++ /* Shuffle envp down. */ ++ do ++ *++sp = *++p; ++ while (*p != NULL); ++ ++#ifdef HAVE_AUX_VECTOR ++ void **auxv = (void **) GLRO(dl_auxv) - skip_args; ++ GLRO(dl_auxv) = (ElfW(auxv_t) *) auxv; /* Aliasing violation. */ ++ assert (auxv == sp + 1); ++ ++ /* Shuffle auxv down. */ ++ ElfW(auxv_t) ax; ++ char *oldp = (char *) (p + 1); ++ char *newp = (char *) (sp + 1); ++ do ++ { ++ memcpy (&ax, oldp, sizeof (ax)); ++ memcpy (newp, &ax, sizeof (ax)); ++ oldp += sizeof (ax); ++ newp += sizeof (ax); ++ } ++ while (ax.a_type != AT_NULL); ++#endif ++} ++ + static void + dl_main (const ElfW(Phdr) *phdr, + ElfW(Word) phnum, +@@ -1366,6 +1478,7 @@ + rtld_is_main = true; + + char *argv0 = NULL; ++ char **orig_argv = _dl_argv; + + /* Note the place where the dynamic linker actually came from. */ + GL(dl_rtld_map).l_name = rtld_progname; +@@ -1380,7 +1493,6 @@ + GLRO(dl_lazy) = -1; + } + +- ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } +@@ -1389,14 +1501,12 @@ + if (state.mode != rtld_mode_help) + state.mode = rtld_mode_verify; + +- ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } + else if (! strcmp (_dl_argv[1], "--inhibit-cache")) + { + GLRO(dl_inhibit_cache) = 1; +- ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } +@@ -1406,7 +1516,6 @@ + state.library_path = _dl_argv[2]; + state.library_path_source = "--library-path"; + +- _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } +@@ -1415,7 +1524,6 @@ + { + GLRO(dl_inhibit_rpath) = _dl_argv[2]; + +- _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } +@@ -1423,14 +1531,12 @@ + { + audit_list_add_string (&state.audit_list, _dl_argv[2]); + +- _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } + else if (! strcmp (_dl_argv[1], "--preload") && _dl_argc > 2) + { + state.preloadarg = _dl_argv[2]; +- _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } +@@ -1438,7 +1544,6 @@ + { + argv0 = _dl_argv[2]; + +- _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } +@@ -1446,7 +1551,6 @@ + && _dl_argc > 2) + { + state.glibc_hwcaps_prepend = _dl_argv[2]; +- _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } +@@ -1454,7 +1558,6 @@ + && _dl_argc > 2) + { + state.glibc_hwcaps_mask = _dl_argv[2]; +- _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } +@@ -1463,7 +1566,6 @@ + { + state.mode = rtld_mode_list_tunables; + +- ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } +@@ -1472,7 +1574,6 @@ + { + state.mode = rtld_mode_list_diagnostics; + +- ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + } +@@ -1518,7 +1619,6 @@ + _dl_usage (ld_so_name, NULL); + } + +- ++_dl_skip_args; + --_dl_argc; + ++_dl_argv; + +@@ -1617,6 +1717,9 @@ + /* Set the argv[0] string now that we've processed the executable. */ + if (argv0 != NULL) + _dl_argv[0] = argv0; ++ ++ /* Adjust arguments for the application entry point. */ ++ _dl_start_args_adjust (_dl_argv - orig_argv); + } + else + { +@@ -1749,34 +1852,7 @@ + if (GLRO(dl_use_load_bias) == (ElfW(Addr)) -2) + GLRO(dl_use_load_bias) = main_map->l_addr == 0 ? -1 : 0; + +- /* Starting from binutils-2.23, the linker will define the magic symbol +- __ehdr_start to point to our own ELF header if it is visible in a +- segment that also includes the phdrs. If that's not available, we use +- the old method that assumes the beginning of the file is part of the +- lowest-addressed PT_LOAD segment. */ +- extern const ElfW(Ehdr) __ehdr_start __attribute__ ((visibility ("hidden"))); +- +- /* Set up the program header information for the dynamic linker +- itself. It is needed in the dl_iterate_phdr callbacks. */ +- const ElfW(Ehdr) *rtld_ehdr = &__ehdr_start; +- assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr); +- assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr))); +- +- const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff; +- +- GL(dl_rtld_map).l_phdr = rtld_phdr; +- GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum; +- +- +- /* PT_GNU_RELRO is usually the last phdr. */ +- size_t cnt = rtld_ehdr->e_phnum; +- while (cnt-- > 0) +- if (rtld_phdr[cnt].p_type == PT_GNU_RELRO) +- { +- GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr; +- GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz; +- break; +- } ++ rtld_setup_phdr (); + + /* Add the dynamic linker to the TLS list if it also uses TLS. */ + if (GL(dl_rtld_map).l_tls_blocksize != 0) +diff -Nuar a/elf/rtld-Rules b/elf/rtld-Rules +--- a/elf/rtld-Rules 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/rtld-Rules 2025-10-20 21:02:20.000000000 +0300 +@@ -52,7 +52,7 @@ + mv -f $@T $@ + + # Use the verbose option of ar and tar when not running silently. +-ifeq "$(findstring s,$(MAKEFLAGS))" "" # if not -s ++ifeq ($(silent-make),no) # if not -s + verbose := v + else # -s + verbose := +diff -Nuar a/elf/tst-audit26.c b/elf/tst-audit26.c +--- a/elf/tst-audit26.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-audit26.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,35 @@ ++/* Check the usability of functions in audit modules. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++ ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ /* Check that the audit module has been loaded. */ ++ void *handle = xdlopen ("mapped to libc", RTLD_LOCAL | RTLD_NOW); ++ TEST_VERIFY (handle ++ == xdlopen (LIBC_SO, RTLD_LOCAL | RTLD_NOW | RTLD_NOLOAD)); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/elf/tst-auditmod24a.c b/elf/tst-auditmod24a.c +--- a/elf/tst-auditmod24a.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/tst-auditmod24a.c 2025-10-20 21:02:20.000000000 +0300 +@@ -110,5 +110,7 @@ + return sym->st_value; + } + +- abort (); ++ if (symname[0] != '\0') ++ abort (); ++ return sym->st_value; + } +diff -Nuar a/elf/tst-auditmod24d.c b/elf/tst-auditmod24d.c +--- a/elf/tst-auditmod24d.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/tst-auditmod24d.c 2025-10-20 21:02:20.000000000 +0300 +@@ -116,5 +116,7 @@ + } + } + +- abort (); ++ if (symname[0] != '\0') ++ abort (); ++ return sym->st_value; + } +diff -Nuar a/elf/tst-auditmod25.c b/elf/tst-auditmod25.c +--- a/elf/tst-auditmod25.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/tst-auditmod25.c 2025-10-20 21:02:20.000000000 +0300 +@@ -72,7 +72,7 @@ + unsigned int *flags, const char *symname) + #endif + { +- if (*refcook != -1 && *defcook != -1) ++ if (*refcook != -1 && *defcook != -1 && symname[0] != '\0') + fprintf (stderr, "la_symbind: %s %u\n", symname, + *flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) ? 1 : 0); + return sym->st_value; +diff -Nuar a/elf/tst-auditmod26.c b/elf/tst-auditmod26.c +--- a/elf/tst-auditmod26.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-auditmod26.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,115 @@ ++/* Check the usability of functions in audit modules. Audit module. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++unsigned int ++la_version (unsigned int current) ++{ ++ /* Exercise various functions. */ ++ ++ /* Check dlopen, dlsym, dlclose. */ ++ void *handle = xdlopen (LIBM_SO, RTLD_LOCAL | RTLD_NOW); ++ void *ptr = xdlsym (handle, "sincos"); ++ TEST_VERIFY (ptr != NULL); ++ ptr = dlsym (handle, "SINCOS"); ++ TEST_VERIFY (ptr == NULL); ++ const char *message = dlerror (); ++ TEST_VERIFY (strstr (message, ": undefined symbol: SINCOS") != NULL); ++ ptr = dlsym (handle, "SINCOS"); ++ TEST_VERIFY (ptr == NULL); ++ xdlclose (handle); ++ TEST_COMPARE_STRING (dlerror (), NULL); ++ ++ handle = xdlopen (LIBC_SO, RTLD_LOCAL | RTLD_NOW | RTLD_NOLOAD); ++ ++ /* Check dlvsym. _exit is unlikely to gain another symbol ++ version. */ ++ TEST_VERIFY (xdlsym (handle, "_exit") ++ == xdlvsym (handle, "_exit", FIRST_VERSION_libc__exit_STRING)); ++ ++ /* Check dlinfo. */ ++ { ++ void *handle2 = NULL; ++ TEST_COMPARE (dlinfo (handle, RTLD_DI_LINKMAP, &handle2), 0); ++ TEST_VERIFY (handle2 == handle); ++ } ++ ++ /* Check dladdr and dladdr1. */ ++ Dl_info info = { }; ++ TEST_VERIFY (dladdr (&_exit, &info) != 0); ++ if (strcmp (info.dli_sname, "_Exit") != 0) /* _Exit is an alias. */ ++ TEST_COMPARE_STRING (info.dli_sname, "_exit"); ++ TEST_VERIFY (info.dli_saddr == &_exit); ++ TEST_VERIFY (strstr (info.dli_fname, LIBC_SO)); ++ void *extra_info; ++ memset (&info, 0, sizeof (info)); ++ TEST_VERIFY (dladdr1 (&_exit, &info, &extra_info, RTLD_DL_LINKMAP) != 0); ++ TEST_VERIFY (extra_info == handle); ++ ++ /* Check _dl_find_object. */ ++ struct dl_find_object dlfo; ++ TEST_COMPARE (_dl_find_object (__builtin_return_address (0), &dlfo), 0); ++ /* "ld.so" is seen with --enable-hardcoded-path-in-tests. */ ++ if (strcmp (basename (dlfo.dlfo_link_map->l_name), "ld.so") != 0) ++ TEST_COMPARE_STRING (basename (dlfo.dlfo_link_map->l_name), LD_SO); ++ TEST_COMPARE (_dl_find_object (dlsym (handle, "environ"), &dlfo), 0); ++ TEST_COMPARE_STRING (basename (dlfo.dlfo_link_map->l_name), LIBC_SO); ++ TEST_COMPARE (_dl_find_object ((void *) 1, &dlfo), -1); ++ TEST_COMPARE (_dl_find_object ((void *) -1, &dlfo), -1); ++ ++ /* Verify that dlmopen creates a new namespace. */ ++ void *dlmopen_handle = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW); ++ TEST_VERIFY (dlmopen_handle != handle); ++ memset (&info, 0, sizeof (info)); ++ extra_info = NULL; ++ ptr = xdlsym (dlmopen_handle, "_exit"); ++ TEST_VERIFY (dladdr1 (ptr, &info, &extra_info, RTLD_DL_LINKMAP) != 0); ++ TEST_VERIFY (extra_info == dlmopen_handle); ++ xdlclose (dlmopen_handle); ++ ++ /* Terminate the process with an error state. This does not happen ++ automatically because the audit module state is not shared with ++ the main program. */ ++ if (support_record_failure_is_failed ()) ++ { ++ fflush (stdout); ++ fflush (stderr); ++ _exit (1); ++ } ++ ++ return LAV_CURRENT; ++} ++ ++char * ++la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag) ++{ ++ if (strcmp (name, "mapped to libc") == 0) ++ return (char *) LIBC_SO; ++ else ++ return (char *) name; ++} +diff -Nuar a/elf/tst-dlmopen-twice.c b/elf/tst-dlmopen-twice.c +--- a/elf/tst-dlmopen-twice.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-dlmopen-twice.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,54 @@ ++/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Main. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++ ++/* Run the test multiple times, to check finding a new namespace while ++ another namespace is already in use. This used to trigger bug 29600. */ ++static void ++recurse (int depth) ++{ ++ if (depth == 0) ++ return; ++ ++ printf ("info: running at depth %d\n", depth); ++ void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod1.so", ++ RTLD_NOW); ++ xdlclose (handle); ++ handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-twice-mod2.so", RTLD_NOW); ++ int (*run_check) (void) = xdlsym (handle, "run_check"); ++ TEST_COMPARE (run_check (), 0); ++ recurse (depth - 1); ++ xdlclose (handle); ++} ++ ++static int ++do_test (void) ++{ ++ /* First run the test without nesting. */ ++ recurse (1); ++ ++ /* Then with nesting. The constant needs to be less than the ++ internal DL_NNS namespace constant. */ ++ recurse (10); ++ return 0; ++} ++ ++#include +diff -Nuar a/elf/tst-dlmopen-twice-mod1.c b/elf/tst-dlmopen-twice-mod1.c +--- a/elf/tst-dlmopen-twice-mod1.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-dlmopen-twice-mod1.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,37 @@ ++/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Module 1. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ puts ("info: tst-dlmopen-twice-mod1.so loaded"); ++ fflush (stdout); ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ puts ("info: tst-dlmopen-twice-mod1.so about to be unloaded"); ++ fflush (stdout); ++} ++ ++/* Large allocation. The second module does not have this, so it ++ should load libc at a different address. */ ++char large_allocate[16 * 1024 * 1024]; +diff -Nuar a/elf/tst-dlmopen-twice-mod2.c b/elf/tst-dlmopen-twice-mod2.c +--- a/elf/tst-dlmopen-twice-mod2.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-dlmopen-twice-mod2.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,50 @@ ++/* Initialization of libc after dlmopen/dlclose/dlmopen (bug 29528). Module 2. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ puts ("info: tst-dlmopen-twice-mod2.so loaded"); ++ fflush (stdout); ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ puts ("info: tst-dlmopen-twice-mod2.so about to be unloaded"); ++ fflush (stdout); ++} ++ ++int ++run_check (void) ++{ ++ puts ("info: about to call isalpha"); ++ fflush (stdout); ++ ++ volatile char ch = 'a'; ++ if (!isalpha (ch)) ++ { ++ puts ("error: isalpha ('a') is not true"); ++ fflush (stdout); ++ return 1; ++ } ++ return 0; ++} +diff -Nuar a/elf/tst-dlopen-sgid.c b/elf/tst-dlopen-sgid.c +--- a/elf/tst-dlopen-sgid.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-dlopen-sgid.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,106 @@ ++/* Test case for ignored LD_LIBRARY_PATH in static startug (bug 32976). ++ Copyright (C) 2025 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* This is the name of our test object. Use a custom module for ++ testing, so that this object does not get picked up from the system ++ path. */ ++static const char dso_name[] = "tst-dlopen-sgid-mod.so"; ++ ++/* Used to mark the recursive invocation. */ ++static const char magic_argument[] = "run-actual-test"; ++ ++static int ++do_test (void) ++{ ++/* Pathname of the directory that receives the shared objects this ++ test attempts to load. */ ++ char *libdir = support_create_temp_directory ("tst-dlopen-sgid-"); ++ ++ /* This is supposed to be ignored and stripped. */ ++ TEST_COMPARE (setenv ("LD_LIBRARY_PATH", libdir, 1), 0); ++ ++ /* Copy of libc.so.6. */ ++ { ++ char *from = xasprintf ("%s/%s", support_objdir_root, LIBC_SO); ++ char *to = xasprintf ("%s/%s", libdir, LIBC_SO); ++ add_temp_file (to); ++ support_copy_file (from, to); ++ free (to); ++ free (from); ++ } ++ ++ /* Copy of the test object. */ ++ { ++ char *from = xasprintf ("%s/elf/%s", support_objdir_root, dso_name); ++ char *to = xasprintf ("%s/%s", libdir, dso_name); ++ add_temp_file (to); ++ support_copy_file (from, to); ++ free (to); ++ free (from); ++ } ++ ++ free (libdir); ++ ++ support_capture_subprogram_self_sgid (magic_argument); ++ ++ return 0; ++} ++ ++static void ++alternative_main (int argc, char **argv) ++{ ++ if (argc == 2 && strcmp (argv[1], magic_argument) == 0) ++ { ++ if (getgid () == getegid ()) ++ /* This can happen if the file system is mounted nosuid. */ ++ FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n", ++ (intmax_t) getgid ()); ++ ++ /* Should be removed due to SGID. */ ++ TEST_COMPARE_STRING (getenv ("LD_LIBRARY_PATH"), NULL); ++ ++ TEST_VERIFY (dlopen (dso_name, RTLD_NOW) == NULL); ++ { ++ const char *message = dlerror (); ++ TEST_COMPARE_STRING (message, ++ "tst-dlopen-sgid-mod.so:" ++ " cannot open shared object file:" ++ " No such file or directory"); ++ } ++ ++ support_record_failure_barrier (); ++ exit (EXIT_SUCCESS); ++ } ++} ++ ++#define PREPARE alternative_main ++#include +diff -Nuar a/elf/tst-dlopen-sgid-mod.c b/elf/tst-dlopen-sgid-mod.c +--- a/elf/tst-dlopen-sgid-mod.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-dlopen-sgid-mod.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1 @@ ++/* Opening this object should not succeed. */ +diff -Nuar a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c +--- a/elf/tst-env-setuid.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/tst-env-setuid.c 2025-10-20 21:02:20.000000000 +0300 +@@ -104,20 +104,14 @@ + if (ret != 0) + exit (1); + +- exit (EXIT_SUCCESS); ++ return 0; + } + else + { + if (test_parent () != 0) + exit (1); + +- int status = support_capture_subprogram_self_sgid (SETGID_CHILD); +- +- if (WEXITSTATUS (status) == EXIT_UNSUPPORTED) +- return EXIT_UNSUPPORTED; +- +- if (!WIFEXITED (status)) +- FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status); ++ support_capture_subprogram_self_sgid (SETGID_CHILD); + + return 0; + } +diff -Nuar a/elf/tst-env-setuid-tunables.c b/elf/tst-env-setuid-tunables.c +--- a/elf/tst-env-setuid-tunables.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/tst-env-setuid-tunables.c 2025-10-20 21:02:20.000000000 +0300 +@@ -52,6 +52,8 @@ + "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096", + "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096", + "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096", ++ "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096", ++ "glibc.malloc.check=2", + "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2", + "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096", + ":glibc.malloc.garbage=2:glibc.malloc.check=1", +@@ -70,6 +72,8 @@ + "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096", + "glibc.malloc.mmap_threshold=4096", + "glibc.malloc.mmap_threshold=4096", ++ "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096", ++ "", + "", + "", + "", +@@ -84,11 +88,18 @@ + const char *val = getenv ("GLIBC_TUNABLES"); + + #if HAVE_TUNABLES ++ printf (" [%d] GLIBC_TUNABLES is %s\n", off, val); ++ fflush (stdout); + if (val != NULL && strcmp (val, resultstrings[off]) == 0) + return 0; + + if (val != NULL) +- printf ("[%d] Unexpected GLIBC_TUNABLES VALUE %s\n", off, val); ++ printf (" [%d] Unexpected GLIBC_TUNABLES VALUE %s, expected %s\n", ++ off, val, resultstrings[off]); ++ else ++ printf (" [%d] GLIBC_TUNABLES environment variable absent\n", off); ++ ++ fflush (stdout); + + return 1; + #else +@@ -116,32 +127,28 @@ + + if (ret != 0) + exit (1); +- +- exit (EXIT_SUCCESS); ++ return 0; + } + else + { +- int ret = 0; +- + /* Spawn tests. */ + for (int i = 0; i < array_length (teststrings); i++) + { + char buf[INT_BUFSIZE_BOUND (int)]; + +- printf ("Spawned test for %s (%d)\n", teststrings[i], i); ++ printf ("[%d] Spawned test for %s\n", i, teststrings[i]); + snprintf (buf, sizeof (buf), "%d\n", i); ++ fflush (stdout); + if (setenv ("GLIBC_TUNABLES", teststrings[i], 1) != 0) +- exit (1); +- +- int status = support_capture_subprogram_self_sgid (buf); +- +- /* Bail out early if unsupported. */ +- if (WEXITSTATUS (status) == EXIT_UNSUPPORTED) +- return EXIT_UNSUPPORTED; ++ { ++ printf (" [%d] Failed to set GLIBC_TUNABLES: %m", i); ++ support_record_failure (); ++ continue; ++ } + +- ret |= status; ++ support_capture_subprogram_self_sgid (buf); + } +- return ret; ++ return 0; + } + } + +diff -Nuar a/elf/tst-glibcelf.py b/elf/tst-glibcelf.py +--- a/elf/tst-glibcelf.py 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-glibcelf.py 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,260 @@ ++#!/usr/bin/python3 ++# Verify scripts/glibcelf.py contents against elf/elf.h. ++# Copyright (C) 2022 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 ++# . ++ ++import argparse ++import enum ++import sys ++ ++import glibcelf ++import glibcextract ++ ++errors_encountered = 0 ++ ++def error(message): ++ global errors_encountered ++ sys.stdout.write('error: {}\n'.format(message)) ++ errors_encountered += 1 ++ ++# The enum constants in glibcelf are expected to have exactly these ++# prefixes. ++expected_constant_prefixes = tuple( ++ 'ELFCLASS ELFDATA EM_ ET_ DT_ PF_ PT_ SHF_ SHN_ SHT_ STB_ STT_'.split()) ++ ++def find_constant_prefix(name): ++ """Returns a matching prefix from expected_constant_prefixes or None.""" ++ for prefix in expected_constant_prefixes: ++ if name.startswith(prefix): ++ return prefix ++ return None ++ ++def find_enum_types(): ++ """A generator for OpenIntEnum and IntFlag classes in glibcelf.""" ++ for obj in vars(glibcelf).values(): ++ if isinstance(obj, type) and obj.__bases__[0] in ( ++ glibcelf._OpenIntEnum, enum.Enum, enum.IntFlag): ++ yield obj ++ ++def check_duplicates(): ++ """Verifies that enum types do not have duplicate values. ++ ++ Different types must have different member names, too. ++ ++ """ ++ global_seen = {} ++ for typ in find_enum_types(): ++ seen = {} ++ last = None ++ for (name, e) in typ.__members__.items(): ++ if e.value in seen: ++ error('{} has {}={} and {}={}'.format( ++ typ, seen[e.value], e.value, name, e.value)) ++ last = e ++ else: ++ seen[e.value] = name ++ if last is not None and last.value > e.value: ++ error('{} has {}={} after {}={}'.format( ++ typ, name, e.value, last.name, last.value)) ++ if name in global_seen: ++ error('{} used in {} and {}'.format( ++ name, global_seen[name], typ)) ++ else: ++ global_seen[name] = typ ++ ++def check_constant_prefixes(): ++ """Check that the constant prefixes match expected_constant_prefixes.""" ++ seen = set() ++ for typ in find_enum_types(): ++ typ_prefix = None ++ for val in typ: ++ prefix = find_constant_prefix(val.name) ++ if prefix is None: ++ error('constant {!r} for {} has unknown prefix'.format( ++ val, typ)) ++ break ++ elif typ_prefix is None: ++ typ_prefix = prefix ++ seen.add(typ_prefix) ++ elif prefix != typ_prefix: ++ error('prefix {!r} for constant {!r}, expected {!r}'.format( ++ prefix, val, typ_prefix)) ++ if typ_prefix is None: ++ error('empty enum type {}'.format(typ)) ++ ++ for prefix in sorted(set(expected_constant_prefixes) - seen): ++ error('missing constant prefix {!r}'.format(prefix)) ++ # Reverse difference is already covered inside the loop. ++ ++def find_elf_h_constants(cc): ++ """Returns a dictionary of relevant constants from .""" ++ return glibcextract.compute_macro_consts( ++ source_text='#include ', ++ cc=cc, ++ macro_re='|'.join( ++ prefix + '.*' for prefix in expected_constant_prefixes)) ++ ++# The first part of the pair is a name of an constant that is ++# dropped from glibcelf. The second part is the constant as it is ++# used in . ++glibcelf_skipped_aliases = ( ++ ('EM_ARC_A5', 'EM_ARC_COMPACT'), ++ ('PF_PARISC_SBP', 'PF_HP_SBP') ++) ++ ++# Constants that provide little value and are not included in ++# glibcelf: *LO*/*HI* range constants, *NUM constants counting the ++# number of constants. Also includes the alias names from ++# glibcelf_skipped_aliases. ++glibcelf_skipped_constants = frozenset( ++ [e[0] for e in glibcelf_skipped_aliases]) | frozenset(""" ++DT_AARCH64_NUM ++DT_ADDRNUM ++DT_ADDRRNGHI ++DT_ADDRRNGLO ++DT_ALPHA_NUM ++DT_ENCODING ++DT_EXTRANUM ++DT_HIOS ++DT_HIPROC ++DT_IA_64_NUM ++DT_LOOS ++DT_LOPROC ++DT_MIPS_NUM ++DT_NUM ++DT_PPC64_NUM ++DT_PPC_NUM ++DT_PROCNUM ++DT_SPARC_NUM ++DT_VALNUM ++DT_VALRNGHI ++DT_VALRNGLO ++DT_VERSIONTAGNUM ++ELFCLASSNUM ++ELFDATANUM ++ET_HIOS ++ET_HIPROC ++ET_LOOS ++ET_LOPROC ++ET_NUM ++PF_MASKOS ++PF_MASKPROC ++PT_HIOS ++PT_HIPROC ++PT_HISUNW ++PT_LOOS ++PT_LOPROC ++PT_LOSUNW ++SHF_MASKOS ++SHF_MASKPROC ++SHN_HIOS ++SHN_HIPROC ++SHN_HIRESERVE ++SHN_LOOS ++SHN_LOPROC ++SHN_LORESERVE ++SHT_HIOS ++SHT_HIPROC ++SHT_HIPROC ++SHT_HISUNW ++SHT_HIUSER ++SHT_LOOS ++SHT_LOPROC ++SHT_LOSUNW ++SHT_LOUSER ++SHT_NUM ++STB_HIOS ++STB_HIPROC ++STB_LOOS ++STB_LOPROC ++STB_NUM ++STT_HIOS ++STT_HIPROC ++STT_LOOS ++STT_LOPROC ++STT_NUM ++""".strip().split()) ++ ++def check_constant_values(cc): ++ """Checks the values of constants against glibcelf.""" ++ ++ glibcelf_constants = { ++ e.name: e for typ in find_enum_types() for e in typ} ++ elf_h_constants = find_elf_h_constants(cc=cc) ++ ++ missing_in_glibcelf = (set(elf_h_constants) - set(glibcelf_constants) ++ - glibcelf_skipped_constants) ++ for name in sorted(missing_in_glibcelf): ++ error('constant {} is missing from glibcelf'.format(name)) ++ ++ unexpected_in_glibcelf = \ ++ set(glibcelf_constants) & glibcelf_skipped_constants ++ for name in sorted(unexpected_in_glibcelf): ++ error('constant {} is supposed to be filtered from glibcelf'.format( ++ name)) ++ ++ missing_in_elf_h = set(glibcelf_constants) - set(elf_h_constants) ++ for name in sorted(missing_in_elf_h): ++ error('constant {} is missing from '.format(name)) ++ ++ expected_in_elf_h = glibcelf_skipped_constants - set(elf_h_constants) ++ for name in expected_in_elf_h: ++ error('filtered constant {} is missing from '.format(name)) ++ ++ for alias_name, name_in_glibcelf in glibcelf_skipped_aliases: ++ if name_in_glibcelf not in glibcelf_constants: ++ error('alias value {} for {} not in glibcelf'.format( ++ name_in_glibcelf, alias_name)) ++ elif (int(elf_h_constants[alias_name]) ++ != glibcelf_constants[name_in_glibcelf].value): ++ error(' has {}={}, glibcelf has {}={}'.format( ++ alias_name, elf_h_constants[alias_name], ++ name_in_glibcelf, glibcelf_constants[name_in_glibcelf])) ++ ++ # Check for value mismatches: ++ for name in sorted(set(glibcelf_constants) & set(elf_h_constants)): ++ glibcelf_value = glibcelf_constants[name].value ++ elf_h_value = int(elf_h_constants[name]) ++ # On 32-bit architectures as some constants that are ++ # parsed as signed, while they are unsigned in glibcelf. So ++ # far, this only affects some flag constants, so special-case ++ # them here. ++ if (glibcelf_value != elf_h_value ++ and not (isinstance(glibcelf_constants[name], enum.IntFlag) ++ and glibcelf_value == 1 << 31 ++ and elf_h_value == -(1 << 31))): ++ error('{}: glibcelf has {!r}, has {!r}'.format( ++ name, glibcelf_value, elf_h_value)) ++ ++def main(): ++ """The main entry point.""" ++ parser = argparse.ArgumentParser( ++ description="Check glibcelf.py and elf.h against each other.") ++ parser.add_argument('--cc', metavar='CC', ++ help='C compiler (including options) to use') ++ args = parser.parse_args() ++ ++ check_duplicates() ++ check_constant_prefixes() ++ check_constant_values(cc=args.cc) ++ ++ if errors_encountered > 0: ++ print("note: errors encountered:", errors_encountered) ++ sys.exit(1) ++ ++if __name__ == '__main__': ++ main() +diff -Nuar a/elf/tst-glibc-hwcaps-cache.script b/elf/tst-glibc-hwcaps-cache.script +--- a/elf/tst-glibc-hwcaps-cache.script 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/tst-glibc-hwcaps-cache.script 2025-10-20 21:02:20.000000000 +0300 +@@ -4,6 +4,7 @@ + cp $B/elf/libmarkermod2-1.so $L/libmarkermod2.so + cp $B/elf/libmarkermod3-1.so $L/libmarkermod3.so + cp $B/elf/libmarkermod4-1.so $L/libmarkermod4.so ++cp $B/elf/libmarkermod5-1.so $L/libmarkermod5.so + + mkdirp 0770 $L/glibc-hwcaps/power9 + cp $B/elf/libmarkermod2-2.so $L/glibc-hwcaps/power9/libmarkermod2.so +@@ -20,6 +21,11 @@ + cp $B/elf/libmarkermod4-2.so $L/glibc-hwcaps/z13/libmarkermod4.so + cp $B/elf/libmarkermod4-3.so $L/glibc-hwcaps/z14/libmarkermod4.so + cp $B/elf/libmarkermod4-4.so $L/glibc-hwcaps/z15/libmarkermod4.so ++mkdirp 0770 $L/glibc-hwcaps/z16 ++cp $B/elf/libmarkermod5-2.so $L/glibc-hwcaps/z13/libmarkermod5.so ++cp $B/elf/libmarkermod5-3.so $L/glibc-hwcaps/z14/libmarkermod5.so ++cp $B/elf/libmarkermod5-4.so $L/glibc-hwcaps/z15/libmarkermod5.so ++cp $B/elf/libmarkermod5-5.so $L/glibc-hwcaps/z16/libmarkermod5.so + + mkdirp 0770 $L/glibc-hwcaps/x86-64-v2 + cp $B/elf/libmarkermod2-2.so $L/glibc-hwcaps/x86-64-v2/libmarkermod2.so +diff -Nuar a/elf/tst-ldconfig-p.sh b/elf/tst-ldconfig-p.sh +--- a/elf/tst-ldconfig-p.sh 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-ldconfig-p.sh 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,77 @@ ++#!/bin/sh ++# Test that ldconfig -p prints something useful. ++# Copyright (C) 2023 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 ++# . ++ ++# Check that the newly built ldconfig -p can dump the system ++# /etc/ld.so.cache file. This should always work even if the ABIs are ++# not compatible, except in a cross-endian build (that presumably ++# involves emulation when running ldconfig). ++ ++common_objpfx=$1 ++test_wrapper_env=$2 ++run_program_env=$3 ++ ++if ! test -r /etc/ld.so.cache; then ++ echo "warning: /etc/ld.so.cache does not exist, test skipped" ++ exit 77 ++fi ++ ++testout="${common_objpfx}elf/tst-ldconfig-p.out" ++# Truncate file. ++: > "$testout" ++ ++${test_wrapper_env} \ ++${run_program_env} \ ++${common_objpfx}elf/ldconfig -p \ ++ $testroot/lib >>"$testout" 2>>"$testout" ++status=$? ++echo "info: ldconfig exit status: $status" >>"$testout" ++ ++errors=0 ++case $status in ++ (0) ++ if head -n 1 "$testout" | \ ++ grep -q "libs found in cache \`/etc/ld.so.cache'\$" ; then ++ echo "info: initial string found" >>"$testout" ++ else ++ echo "error: initial string not found" >>"$testout" ++ errors=1 ++ fi ++ if grep -q "^ libc\.so\..* => " "$testout"; then ++ echo "info: libc.so.* string found" >>"$testout" ++ else ++ echo "error: libc.so.* string not found" >>"$testout" ++ errors=1 ++ fi ++ ;; ++ (1) ++ if head -n 1 "$testout" | \ ++ grep -q ": Cache file has wrong endianness\.$" ; then ++ echo "info: cache file has wrong endianess" >> "$testout" ++ else ++ echo "error: unexpected ldconfig error message" >> "$testout" ++ errors=1 ++ fi ++ ;; ++ (*) ++ echo "error: unexpected exit status" >> "$testout" ++ errors=1 ++ ;; ++esac ++ ++exit $errors +diff -Nuar a/elf/tst-link-map-contiguous-ldso.c b/elf/tst-link-map-contiguous-ldso.c +--- a/elf/tst-link-map-contiguous-ldso.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-link-map-contiguous-ldso.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,98 @@ ++/* Check that _dl_find_object behavior matches up with gaps. ++ Copyright (C) 2025 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ struct link_map *l = xdlopen (LD_SO, RTLD_NOW); ++ if (!l->l_contiguous) ++ { ++ puts ("info: ld.so link map is not contiguous"); ++ ++ /* Try to find holes by probing with mmap. */ ++ int pagesize = getpagesize (); ++ bool gap_found = false; ++ ElfW(Addr) addr = l->l_map_start; ++ TEST_COMPARE (addr % pagesize, 0); ++ while (addr < l->l_map_end) ++ { ++ void *expected = (void *) addr; ++ void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS, -1); ++ struct dl_find_object dlfo; ++ int dlfo_ret = _dl_find_object (expected, &dlfo); ++ if (ptr == expected) ++ { ++ if (dlfo_ret < 0) ++ { ++ TEST_COMPARE (dlfo_ret, -1); ++ printf ("info: hole without mapping data found at %p\n", ptr); ++ } ++ else ++ FAIL ("object \"%s\" found in gap at %p", ++ dlfo.dlfo_link_map->l_name, ptr); ++ gap_found = true; ++ } ++ else if (dlfo_ret == 0) ++ { ++ if ((void *) dlfo.dlfo_link_map != (void *) l) ++ { ++ printf ("info: object \"%s\" found at %p\n", ++ dlfo.dlfo_link_map->l_name, ptr); ++ gap_found = true; ++ } ++ } ++ else ++ TEST_COMPARE (dlfo_ret, -1); ++ xmunmap (ptr, 1); ++ addr += pagesize; ++ } ++ if (!gap_found) ++ FAIL ("no ld.so gap found"); ++ } ++ else ++ { ++ puts ("info: ld.so link map is contiguous"); ++ ++ /* Assert that ld.so is truly contiguous in memory. */ ++ volatile long int *p = (volatile long int *) l->l_map_start; ++ volatile long int *end = (volatile long int *) l->l_map_end; ++ while (p < end) ++ { ++ *p; ++ ++p; ++ } ++ } ++ ++ xdlclose (l); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/elf/tst-link-map-contiguous-libc.c b/elf/tst-link-map-contiguous-libc.c +--- a/elf/tst-link-map-contiguous-libc.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-link-map-contiguous-libc.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,57 @@ ++/* Check that the entire libc.so program image is readable if contiguous. ++ Copyright (C) 2025 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ struct link_map *l = xdlopen (LIBC_SO, RTLD_NOW); ++ ++ /* The dynamic loader fills holes with PROT_NONE mappings. */ ++ if (!l->l_contiguous) ++ FAIL_EXIT1 ("libc.so link map is not contiguous"); ++ ++ /* Direct probing does not work because not everything is readable ++ due to PROT_NONE mappings. */ ++ int pagesize = getpagesize (); ++ ElfW(Addr) addr = l->l_map_start; ++ TEST_COMPARE (addr % pagesize, 0); ++ while (addr < l->l_map_end) ++ { ++ void *expected = (void *) addr; ++ void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS, -1); ++ if (ptr == expected) ++ FAIL ("hole in libc.so memory image after %lu bytes", ++ (unsigned long int) (addr - l->l_map_start)); ++ xmunmap (ptr, 1); ++ addr += pagesize; ++ } ++ ++ xdlclose (l); ++ ++ return 0; ++} ++#include +diff -Nuar a/elf/tst-link-map-contiguous-main.c b/elf/tst-link-map-contiguous-main.c +--- a/elf/tst-link-map-contiguous-main.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-link-map-contiguous-main.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,45 @@ ++/* Check that the entire main program image is readable if contiguous. ++ Copyright (C) 2025 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 ++ . */ ++ ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ struct link_map *l = xdlopen ("", RTLD_NOW); ++ if (!l->l_contiguous) ++ FAIL_UNSUPPORTED ("main link map is not contiguous"); ++ ++ /* This check only works if the kernel loaded the main program. The ++ dynamic loader replaces gaps with PROT_NONE mappings, resulting ++ in faults. */ ++ volatile long int *p = (volatile long int *) l->l_map_start; ++ volatile long int *end = (volatile long int *) l->l_map_end; ++ while (p < end) ++ { ++ *p; ++ ++p; ++ } ++ ++ xdlclose (l); ++ ++ return 0; ++} ++#include +diff -Nuar a/elf/tst-pldd.c b/elf/tst-pldd.c +--- a/elf/tst-pldd.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/tst-pldd.c 2025-10-20 21:02:20.000000000 +0300 +@@ -85,6 +85,8 @@ + static int + do_test (void) + { ++ support_need_proc ("needs /proc/sys/kernel/yama/ptrace_scope and /proc/$child"); ++ + /* Check if our subprocess can be debugged with ptrace. */ + { + int ptrace_scope = support_ptrace_scope (); +@@ -106,14 +108,16 @@ + loader and libc. */ + { + pid_t pid; +- char buffer[512]; +-#define STRINPUT(size) "%" # size "s" ++#define BUFFERLEN 511 ++ char buffer[BUFFERLEN + 1]; ++#define STRINPUT(size) XSTRINPUT(size) ++#define XSTRINPUT(size) "%" # size "s" + + FILE *out = fmemopen (pldd.out.buffer, pldd.out.length, "r"); + TEST_VERIFY (out != NULL); + + /* First line is in the form of : */ +- TEST_COMPARE (fscanf (out, "%u: " STRINPUT (512), &pid, buffer), 2); ++ TEST_COMPARE (fscanf (out, "%u: " STRINPUT (BUFFERLEN), &pid, buffer), 2); + + TEST_COMPARE (pid, *target_pid_ptr); + TEST_COMPARE (strcmp (basename (buffer), "tst-pldd"), 0); +diff -Nuar a/elf/tst-recursive-tls.c b/elf/tst-recursive-tls.c +--- a/elf/tst-recursive-tls.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-recursive-tls.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,60 @@ ++/* Test with interposed malloc with dynamic TLS. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++/* Defined in tst-recursive-tlsmallocmod.so. */ ++extern __thread unsigned int malloc_subsytem_counter; ++ ++static int ++do_test (void) ++{ ++ /* 16 is large enough to exercise the DTV resizing case. */ ++ void *handles[16]; ++ ++ for (unsigned int i = 0; i < array_length (handles); ++i) ++ { ++ /* Re-use the TLS slot for module 0. */ ++ if (i > 0) ++ xdlclose (handles[0]); ++ ++ char soname[30]; ++ snprintf (soname, sizeof (soname), "tst-recursive-tlsmod%u.so", i); ++ handles[i] = xdlopen (soname, RTLD_NOW); ++ ++ if (i > 0) ++ { ++ handles[0] = xdlopen ("tst-recursive-tlsmod0.so", RTLD_NOW); ++ int (*fptr) (void) = xdlsym (handles[0], "get_threadvar_0"); ++ /* May trigger TLS storage allocation using malloc. */ ++ TEST_COMPARE (fptr (), 0); ++ } ++ } ++ ++ for (unsigned int i = 0; i < array_length (handles); ++i) ++ xdlclose (handles[i]); ++ ++ printf ("info: malloc subsystem calls: %u\n", malloc_subsytem_counter); ++ TEST_VERIFY (malloc_subsytem_counter > 0); ++ return 0; ++} ++ ++#include +diff -Nuar a/elf/tst-recursive-tlsmallocmod.c b/elf/tst-recursive-tlsmallocmod.c +--- a/elf/tst-recursive-tlsmallocmod.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-recursive-tlsmallocmod.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,64 @@ ++/* Interposed malloc with dynamic TLS. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++ ++__thread unsigned int malloc_subsytem_counter; ++ ++static __typeof (malloc) *malloc_fptr; ++static __typeof (free) *free_fptr; ++static __typeof (calloc) *calloc_fptr; ++static __typeof (realloc) *realloc_fptr; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ malloc_fptr = dlsym (RTLD_NEXT, "malloc"); ++ free_fptr = dlsym (RTLD_NEXT, "free"); ++ calloc_fptr = dlsym (RTLD_NEXT, "calloc"); ++ realloc_fptr = dlsym (RTLD_NEXT, "realloc"); ++} ++ ++void * ++malloc (size_t size) ++{ ++ ++malloc_subsytem_counter; ++ return malloc_fptr (size); ++} ++ ++void ++free (void *ptr) ++{ ++ ++malloc_subsytem_counter; ++ return free_fptr (ptr); ++} ++ ++void * ++calloc (size_t a, size_t b) ++{ ++ ++malloc_subsytem_counter; ++ return calloc_fptr (a, b); ++} ++ ++void * ++realloc (void *ptr, size_t size) ++{ ++ ++malloc_subsytem_counter; ++ return realloc_fptr (ptr, size); ++} +diff -Nuar a/elf/tst-recursive-tlsmodN.c b/elf/tst-recursive-tlsmodN.c +--- a/elf/tst-recursive-tlsmodN.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-recursive-tlsmodN.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,28 @@ ++/* Test module with global-dynamic TLS. Used to trigger DTV reallocation. ++ Copyright (C) 2024 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 ++ . */ ++ ++/* Compiled with VAR and FUNC set via -D. FUNC requires some ++ relocation against TLS variable VAR. */ ++ ++__thread int VAR; ++ ++int ++FUNC (void) ++{ ++ return VAR; ++} +diff -Nuar a/elf/tst-relro-symbols.py b/elf/tst-relro-symbols.py +--- a/elf/tst-relro-symbols.py 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-relro-symbols.py 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,137 @@ ++#!/usr/bin/python3 ++# Verify that certain symbols are covered by RELRO. ++# Copyright (C) 2022 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 ++# . ++ ++"""Analyze a (shared) object to verify that certain symbols are ++present and covered by the PT_GNU_RELRO segment. ++ ++""" ++ ++import argparse ++import os.path ++import sys ++ ++# Make available glibc Python modules. ++sys.path.append(os.path.join( ++ os.path.dirname(os.path.realpath(__file__)), os.path.pardir, 'scripts')) ++ ++import glibcelf ++ ++def find_relro(path: str, img: glibcelf.Image) -> (int, int): ++ """Discover the address range of the PT_GNU_RELRO segment.""" ++ for phdr in img.phdrs(): ++ if phdr.p_type == glibcelf.Pt.PT_GNU_RELRO: ++ # The computation is not entirely accurate because ++ # _dl_protect_relro in elf/dl-reloc.c rounds both the ++ # start end and downwards using the run-time page size. ++ return phdr.p_vaddr, phdr.p_vaddr + phdr.p_memsz ++ sys.stdout.write('{}: error: no PT_GNU_RELRO segment\n'.format(path)) ++ sys.exit(1) ++ ++def check_in_relro(kind, relro_begin, relro_end, name, start, size, error): ++ """Check if a section or symbol falls within in the RELRO segment.""" ++ end = start + size - 1 ++ if not (relro_begin <= start < end < relro_end): ++ error( ++ '{} {!r} of size {} at 0x{:x} is not in RELRO range [0x{:x}, 0x{:x})'.format( ++ kind, name.decode('UTF-8'), start, size, ++ relro_begin, relro_end)) ++ ++def get_parser(): ++ """Return an argument parser for this script.""" ++ parser = argparse.ArgumentParser(description=__doc__) ++ parser.add_argument('object', help='path to object file to check') ++ parser.add_argument('--required', metavar='NAME', default=(), ++ help='required symbol names', nargs='*') ++ parser.add_argument('--optional', metavar='NAME', default=(), ++ help='required symbol names', nargs='*') ++ return parser ++ ++def main(argv): ++ """The main entry point.""" ++ parser = get_parser() ++ opts = parser.parse_args(argv) ++ img = glibcelf.Image.readfile(opts.object) ++ ++ required_symbols = frozenset([sym.encode('UTF-8') ++ for sym in opts.required]) ++ optional_symbols = frozenset([sym.encode('UTF-8') ++ for sym in opts.optional]) ++ check_symbols = required_symbols | optional_symbols ++ ++ # Tracks the symbols in check_symbols that have been found. ++ symbols_found = set() ++ ++ # Discover the extent of the RELRO segment. ++ relro_begin, relro_end = find_relro(opts.object, img) ++ symbol_table_found = False ++ ++ errors = False ++ def error(msg: str) -> None: ++ """Record an error condition and write a message to standard output.""" ++ nonlocal errors ++ errors = True ++ sys.stdout.write('{}: error: {}\n'.format(opts.object, msg)) ++ ++ # Iterate over section headers to find the symbol table. ++ for shdr in img.shdrs(): ++ if shdr.sh_type == glibcelf.Sht.SHT_SYMTAB: ++ symbol_table_found = True ++ for sym in img.syms(shdr): ++ if sym.st_name in check_symbols: ++ symbols_found.add(sym.st_name) ++ ++ # Validate symbol type, section, and size. ++ if sym.st_info.type != glibcelf.Stt.STT_OBJECT: ++ error('symbol {!r} has wrong type {}'.format( ++ sym.st_name.decode('UTF-8'), sym.st_info.type)) ++ if sym.st_shndx in glibcelf.Shn: ++ error('symbol {!r} has reserved section {}'.format( ++ sym.st_name.decode('UTF-8'), sym.st_shndx)) ++ continue ++ if sym.st_size == 0: ++ error('symbol {!r} has size zero'.format( ++ sym.st_name.decode('UTF-8'))) ++ continue ++ ++ check_in_relro('symbol', relro_begin, relro_end, ++ sym.st_name, sym.st_value, sym.st_size, ++ error) ++ continue # SHT_SYMTAB ++ if shdr.sh_name == b'.data.rel.ro' \ ++ or shdr.sh_name.startswith(b'.data.rel.ro.'): ++ check_in_relro('section', relro_begin, relro_end, ++ shdr.sh_name, shdr.sh_addr, shdr.sh_size, ++ error) ++ continue ++ ++ if required_symbols - symbols_found: ++ for sym in sorted(required_symbols - symbols_found): ++ error('symbol {!r} not found'.format(sym.decode('UTF-8'))) ++ ++ if errors: ++ sys.exit(1) ++ ++ if not symbol_table_found: ++ sys.stdout.write( ++ '{}: warning: no symbol table found (stripped object)\n'.format( ++ opts.object)) ++ sys.exit(77) ++ ++if __name__ == '__main__': ++ main(sys.argv[1:]) +diff -Nuar a/elf/tst-stackguard1.c b/elf/tst-stackguard1.c +--- a/elf/tst-stackguard1.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/elf/tst-stackguard1.c 2025-10-20 21:02:20.000000000 +0300 +@@ -26,6 +26,8 @@ + #include + #include + ++#include ++ + static const char *command; + static bool child; + static uintptr_t stack_chk_guard_copy; +@@ -108,7 +110,8 @@ + dup2 (fds[1], 2); + close (fds[1]); + +- system (command); ++ xsystem (command); ++ + exit (0); + } + +diff -Nuar a/elf/tst-tls-allocation-failure-static.c b/elf/tst-tls-allocation-failure-static.c +--- a/elf/tst-tls-allocation-failure-static.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-tls-allocation-failure-static.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,31 @@ ++/* Base for test program with impossiblyh large PT_TLS segment. ++ Copyright (C) 2022 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 ++ . */ ++ ++/* The test actual binary is patched using scripts/tst-elf-edit.py ++ --maximize-tls-size, and this introduces the expected test ++ allocation failure due to an excessive PT_LS p_memsz value. ++ ++ Patching the binary is required because on some 64-bit targets, TLS ++ relocations can only cover a 32-bit range, and glibc-internal TLS ++ variables such as errno end up outside that range. */ ++ ++int ++main (void) ++{ ++ return 0; ++} +diff -Nuar a/elf/tst-trace1.exp b/elf/tst-trace1.exp +--- a/elf/tst-trace1.exp 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-trace1.exp 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,4 @@ ++ld 1 ++libc 1 ++libtracemod2.so 0 ++libtracemod3.so 0 +diff -Nuar a/elf/tst-trace2.exp b/elf/tst-trace2.exp +--- a/elf/tst-trace2.exp 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-trace2.exp 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,6 @@ ++ld 1 ++libc 1 ++libtracemod2.so 1 ++libtracemod3.so 0 ++libtracemod4.so 0 ++libtracemod5.so 0 +diff -Nuar a/elf/tst-trace3.exp b/elf/tst-trace3.exp +--- a/elf/tst-trace3.exp 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-trace3.exp 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,6 @@ ++ld 1 ++libc 1 ++libtracemod2.so 1 ++libtracemod3.so 1 ++libtracemod4.so 0 ++libtracemod5.so 0 +diff -Nuar a/elf/tst-trace4.exp b/elf/tst-trace4.exp +--- a/elf/tst-trace4.exp 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-trace4.exp 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,6 @@ ++ld 1 ++libc 1 ++libtracemod2.so 1 ++libtracemod3.so 1 ++libtracemod4.so 1 ++libtracemod5.so 0 +diff -Nuar a/elf/tst-trace5.exp b/elf/tst-trace5.exp +--- a/elf/tst-trace5.exp 1970-01-01 02:00:00.000000000 +0200 ++++ b/elf/tst-trace5.exp 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,6 @@ ++ld 1 ++libc 1 ++libtracemod2.so 1 ++libtracemod3.so 1 ++libtracemod4.so 1 ++libtracemod5.so 1 +diff -Nuar a/gmon/gmon.c b/gmon/gmon.c +--- a/gmon/gmon.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/gmon/gmon.c 2025-10-20 21:02:20.000000000 +0300 +@@ -97,11 +97,8 @@ + { + struct gmonparam *p = &_gmonparam; + +- /* Don't change the state if we ran into an error. */ +- if (p->state == GMON_PROF_ERROR) +- return; +- +- if (mode) ++ /* Treat start request as stop if error or gmon not initialized. */ ++ if (mode && p->state != GMON_PROF_ERROR && p->tos != NULL) + { + /* start */ + __profil((void *) p->kcount, p->kcountsize, p->lowpc, s_scale); +@@ -111,7 +108,9 @@ + { + /* stop */ + __profil(NULL, 0, 0, 0); +- p->state = GMON_PROF_OFF; ++ /* Don't change the state if we ran into an error. */ ++ if (p->state != GMON_PROF_ERROR) ++ p->state = GMON_PROF_OFF; + } + } + libc_hidden_def (__moncontrol) +@@ -124,6 +123,19 @@ + int o; + char *cp; + struct gmonparam *p = &_gmonparam; ++ long int minarcs, maxarcs; ++ ++ /* No tunables, we use hardcoded defaults */ ++ minarcs = MINARCS; ++ maxarcs = MAXARCS; ++ ++ /* ++ * If we are incorrectly called twice in a row (without an ++ * intervening call to _mcleanup), ignore the second call to ++ * prevent leaking memory. ++ */ ++ if (p->tos != NULL) ++ return; + + /* + * round lowpc and highpc to multiples of the density we're using +@@ -132,6 +144,8 @@ + p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); + p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER)); + p->textsize = p->highpc - p->lowpc; ++ /* This looks like a typo, but it's here to align the p->froms ++ section. */ + p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms)); + p->hashfraction = HASHFRACTION; + p->log_hashfraction = -1; +@@ -142,12 +156,12 @@ + instead of integer division. Precompute shift amount. */ + p->log_hashfraction = ffs(p->hashfraction * sizeof(*p->froms)) - 1; + } +- p->fromssize = p->textsize / HASHFRACTION; ++ p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms)); + p->tolimit = p->textsize * ARCDENSITY / 100; +- if (p->tolimit < MINARCS) +- p->tolimit = MINARCS; +- else if (p->tolimit > MAXARCS) +- p->tolimit = MAXARCS; ++ if (p->tolimit < minarcs) ++ p->tolimit = minarcs; ++ else if (p->tolimit > maxarcs) ++ p->tolimit = maxarcs; + p->tossize = p->tolimit * sizeof(struct tostruct); + + cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1); +@@ -440,9 +454,14 @@ + { + __moncontrol (0); + +- if (_gmonparam.state != GMON_PROF_ERROR) ++ if (_gmonparam.state != GMON_PROF_ERROR && _gmonparam.tos != NULL) + write_gmon (); + + /* free the memory. */ + free (_gmonparam.tos); ++ ++ /* reset buffer to initial state for safety */ ++ memset(&_gmonparam, 0, sizeof _gmonparam); ++ /* somewhat confusingly, ON=0, OFF=3 */ ++ _gmonparam.state = GMON_PROF_OFF; + } +diff -Nuar a/gmon/Makefile b/gmon/Makefile +--- a/gmon/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/gmon/Makefile 2025-10-20 21:02:20.000000000 +0300 +@@ -1,4 +1,5 @@ +-# Copyright (C) 1995-2022 Free Software Foundation, Inc. ++# Copyright (C) 1995-2023 Free Software Foundation, Inc. ++# Copyright The GNU Toolchain Authors. + # This file is part of the GNU C Library. + + # The GNU C Library is free software; you can redistribute it and/or +@@ -25,7 +26,7 @@ + headers := sys/gmon.h sys/gmon_out.h sys/profil.h + routines := gmon mcount profil sprofil prof-freq + +-tests = tst-sprofil tst-gmon ++tests = tst-sprofil tst-gmon tst-mcleanup + ifeq ($(build-profile),yes) + tests += tst-profile-static + tests-static += tst-profile-static +@@ -56,6 +57,14 @@ + tests-special += $(objpfx)tst-gmon-gprof.out + endif + ++CFLAGS-tst-mcleanup.c := -fno-omit-frame-pointer -pg ++tst-mcleanup-no-pie = yes ++CRT-tst-mcleanup := $(csu-objpfx)g$(start-installed-name) ++tst-mcleanup-ENV := GMON_OUT_PREFIX=$(objpfx)tst-mcleanup.data ++ifeq ($(run-built-tests),yes) ++tests-special += $(objpfx)tst-mcleanup.out ++endif ++ + CFLAGS-tst-gmon-static.c := $(PIE-ccflag) -fno-omit-frame-pointer -pg + CRT-tst-gmon-static := $(csu-objpfx)g$(static-start-installed-name) + tst-gmon-static-no-pie = yes +@@ -103,6 +112,18 @@ + clean-tst-gmon-data: + rm -f $(objpfx)tst-gmon.data.* + ++$(objpfx)tst-mcount-overflow.o: clean-tst-mcount-overflow-data ++clean-tst-mcount-overflow-data: ++ rm -f $(objpfx)tst-mcount-overflow.data.* ++ ++$(objpfx)tst-mcount-overflow-check.out: tst-mcount-overflow-check.sh $(objpfx)tst-mcount-overflow.out ++ $(SHELL) $< $(objpfx)tst-mcount-overflow > $@; \ ++ $(evaluate-test) ++ ++$(objpfx)tst-mcleanup.out: clean-tst-mcleanup-data ++clean-tst-mcleanup-data: ++ rm -f $(objpfx)tst-mcleanup.data.* ++ + $(objpfx)tst-gmon-gprof.out: tst-gmon-gprof.sh $(objpfx)tst-gmon.out + $(SHELL) $< $(GPROF) $(objpfx)tst-gmon $(objpfx)tst-gmon.data.* > $@; \ + $(evaluate-test) +diff -Nuar a/gmon/mcount.c b/gmon/mcount.c +--- a/gmon/mcount.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/gmon/mcount.c 2025-10-20 21:02:20.000000000 +0300 +@@ -41,6 +41,10 @@ + + #include + ++#include ++#include ++#define ERR(s) __write_nocancel (STDERR_FILENO, s, sizeof (s) - 1) ++ + /* + * mcount is called on entry to each function compiled with the profiling + * switch set. _mcount(), which is declared in a machine-dependent way +@@ -170,6 +174,7 @@ + return; + overflow: + p->state = GMON_PROF_ERROR; ++ ERR("mcount: call graph buffer size limit exceeded, gmon.out will not be generated\n"); + return; + } + +diff -Nuar a/gmon/sys/gmon.h b/gmon/sys/gmon.h +--- a/gmon/sys/gmon.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/gmon/sys/gmon.h 2025-10-20 21:02:17.000000000 +0300 +@@ -111,6 +111,8 @@ + * Always allocate at least this many tostructs. This + * hides the inadequacy of the ARCDENSITY heuristic, at least + * for small programs. ++ * ++ * Value can be overridden at runtime by glibc.gmon.minarcs tunable. + */ + #define MINARCS 50 + +@@ -124,8 +126,8 @@ + * Used to be max representable value of ARCINDEX minus 2, but now + * that ARCINDEX is a long, that's too large; we don't really want + * to allow a 48 gigabyte table. +- * The old value of 1<<16 wasn't high enough in practice for large C++ +- * programs; will 1<<20 be adequate for long? FIXME ++ * ++ * Value can be overridden at runtime by glibc.gmon.maxarcs tunable. + */ + #define MAXARCS (1 << 20) + +diff -Nuar a/gmon/tst-mcleanup.c b/gmon/tst-mcleanup.c +--- a/gmon/tst-mcleanup.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/gmon/tst-mcleanup.c 2025-10-20 21:02:17.000000000 +0300 +@@ -0,0 +1,31 @@ ++/* Test program for repeated invocation of _mcleanup ++ Copyright The GNU Toolchain Authors. ++ 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 ++ . */ ++ ++/* Intentionally calls _mcleanup() twice: once manually, it will be ++ called again as an atexit handler. This is incorrect use of the API, ++ but the point of the test is to make sure we don't crash when the ++ API is misused in this way. */ ++ ++#include ++ ++int ++main (void) ++{ ++ _mcleanup(); ++ return 0; ++} +diff -Nuar a/gmon/tst-mcount-overflow.c b/gmon/tst-mcount-overflow.c +--- a/gmon/tst-mcount-overflow.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/gmon/tst-mcount-overflow.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,72 @@ ++/* Test program to trigger mcount overflow in profiling collection. ++ Copyright (C) 2017-2023 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 ++ . */ ++ ++/* Program with sufficiently complex, yet pointless, call graph ++ that it will trigger an mcount overflow, when you set the ++ minarcs/maxarcs tunables to very low values. */ ++ ++#define PREVENT_TAIL_CALL asm volatile ("") ++ ++/* Calls REP(n) macro 16 times, for n=0..15. ++ * You need to define REP(n) before using this. ++ */ ++#define REPS \ ++ REP(0) REP(1) REP(2) REP(3) REP(4) REP(5) REP(6) REP(7) \ ++ REP(8) REP(9) REP(10) REP(11) REP(12) REP(13) REP(14) REP(15) ++ ++/* Defines 16 leaf functions named f1_0 to f1_15 */ ++#define REP(n) \ ++ __attribute__ ((noinline, noclone, weak)) void f1_##n (void) {}; ++REPS ++#undef REP ++ ++/* Calls all 16 leaf functions f1_* in succession */ ++__attribute__ ((noinline, noclone, weak)) void ++f2 (void) ++{ ++# define REP(n) f1_##n(); ++ REPS ++# undef REP ++ PREVENT_TAIL_CALL; ++} ++ ++/* Defines 16 functions named f2_0 to f2_15, which all just call f2 */ ++#define REP(n) \ ++ __attribute__ ((noinline, noclone, weak)) void \ ++ f2_##n (void) { f2(); PREVENT_TAIL_CALL; }; ++REPS ++#undef REP ++ ++__attribute__ ((noinline, noclone, weak)) void ++f3 (int count) ++{ ++ for (int i = 0; i < count; ++i) ++ { ++ /* Calls f1_0(), f2_0(), f1_1(), f2_1(), f3_0(), etc */ ++# define REP(n) f1_##n(); f2_##n(); ++ REPS ++# undef REP ++ } ++} ++ ++int ++main (void) ++{ ++ f3 (1000); ++ return 0; ++} +diff -Nuar a/gmon/tst-mcount-overflow-check.sh b/gmon/tst-mcount-overflow-check.sh +--- a/gmon/tst-mcount-overflow-check.sh 1970-01-01 02:00:00.000000000 +0200 ++++ b/gmon/tst-mcount-overflow-check.sh 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,45 @@ ++#!/bin/sh ++# Test expected messages generated when mcount overflows ++# Copyright (C) 2017-2023 Free Software Foundation, Inc. ++# Copyright The GNU Toolchain Authors. ++# 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 ++# . ++ ++LC_ALL=C ++export LC_ALL ++set -e ++exec 2>&1 ++ ++program="$1" ++ ++check_msg() { ++ if ! grep -q "$1" "$program.out"; then ++ echo "FAIL: expected message not in output: $1" ++ exit 1 ++ fi ++} ++ ++check_msg 'monstartup: maxarcs < minarcs, setting maxarcs = minarcs' ++check_msg 'mcount: call graph buffer size limit exceeded, gmon.out will not be generated' ++ ++for data_file in $1.data.*; do ++ if [ -f "$data_file" ]; then ++ echo "FAIL: expected no data files, but found $data_file" ++ exit 1 ++ fi ++done ++ ++echo PASS +diff -Nuar a/gshadow/Makefile b/gshadow/Makefile +--- a/gshadow/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/gshadow/Makefile 2025-10-20 21:02:20.000000000 +0300 +@@ -26,7 +26,7 @@ + routines = getsgent getsgnam sgetsgent fgetsgent putsgent \ + getsgent_r getsgnam_r sgetsgent_r fgetsgent_r + +-tests = tst-gshadow tst-putsgent tst-fgetsgent_r ++tests = tst-gshadow tst-putsgent tst-fgetsgent_r tst-sgetsgent + + CFLAGS-getsgent_r.c += -fexceptions + CFLAGS-getsgent.c += -fexceptions +diff -Nuar a/gshadow/sgetsgent_r.c b/gshadow/sgetsgent_r.c +--- a/gshadow/sgetsgent_r.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/gshadow/sgetsgent_r.c 2025-10-20 21:02:20.000000000 +0300 +@@ -61,7 +61,10 @@ + buffer[buflen - 1] = '\0'; + sp = strncpy (buffer, string, buflen); + if (buffer[buflen - 1] != '\0') +- return ERANGE; ++ { ++ __set_errno (ERANGE); ++ return ERANGE; ++ } + } + else + sp = (char *) string; +diff -Nuar a/gshadow/tst-sgetsgent.c b/gshadow/tst-sgetsgent.c +--- a/gshadow/tst-sgetsgent.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/gshadow/tst-sgetsgent.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,69 @@ ++/* Test large input for sgetsgent (bug 30151). ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ /* Create a shadow group with 1000 members. */ ++ struct xmemstream mem; ++ xopen_memstream (&mem); ++ const char *passwd = "k+zD0nucwfxAo3sw1NXUj6K5vt5M16+X0TVGdE1uFvq5R8V7efJ"; ++ fprintf (mem.out, "group-name:%s::m0", passwd); ++ for (int i = 1; i < 1000; ++i) ++ fprintf (mem.out, ",m%d", i); ++ xfclose_memstream (&mem); ++ ++ /* Call sgetsgent. */ ++ char *input = mem.buffer; ++ struct sgrp *e = sgetsgent (input); ++ TEST_VERIFY_EXIT (e != NULL); ++ TEST_COMPARE_STRING (e->sg_namp, "group-name"); ++ TEST_COMPARE_STRING (e->sg_passwd, passwd); ++ /* No administrators. */ ++ TEST_COMPARE_STRING (e->sg_adm[0], NULL); ++ /* Check the members list. */ ++ for (int i = 0; i < 1000; ++i) ++ { ++ char *member = xasprintf ("m%d", i); ++ TEST_COMPARE_STRING (e->sg_mem[i], member); ++ free (member); ++ } ++ TEST_COMPARE_STRING (e->sg_mem[1000], NULL); ++ ++ /* Check that putsgent brings back the input string. */ ++ xopen_memstream (&mem); ++ TEST_COMPARE (putsgent (e, mem.out), 0); ++ xfclose_memstream (&mem); ++ /* Compare without the trailing '\n' that putsgent added. */ ++ TEST_COMPARE (mem.buffer[mem.length - 1], '\n'); ++ mem.buffer[mem.length - 1] = '\0'; ++ TEST_COMPARE_STRING (mem.buffer, input); ++ ++ free (mem.buffer); ++ free (input); ++ return 0; ++} ++ ++#include +diff -Nuar a/INSTALL b/INSTALL +--- a/INSTALL 2022-02-03 08:27:54.000000000 +0300 ++++ b/INSTALL 2025-10-20 21:02:20.000000000 +0300 +@@ -90,6 +90,12 @@ + library will still be usable, but functionality may be lost--for + example, you can't build a shared libc with old binutils. + ++'--with-default-link' ++ With '--with-default-link', the build system does not use a custom ++ linker script for linking shared objects. The default is ++ '--without-default-link', because the custom linker script is ++ needed for full RELRO protection. ++ + '--with-nonshared-cflags=CFLAGS' + Use additional compiler flags CFLAGS to build the parts of the + library which are always statically linked into applications and +diff -Nuar a/iconv/gconv_parseconfdir.h b/iconv/gconv_parseconfdir.h +--- a/iconv/gconv_parseconfdir.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/iconv/gconv_parseconfdir.h 2025-10-20 21:02:20.000000000 +0300 +@@ -29,11 +29,14 @@ + # define isspace(__c) __isspace_l ((__c), _nl_C_locobj_ptr) + # define asprintf __asprintf + # define opendir __opendir +-# define readdir __readdir ++# define readdir64 __readdir64 + # define closedir __closedir + # define mempcpy __mempcpy +-# define lstat64 __lstat64 ++# define struct_stat64 struct __stat64_t64 ++# define lstat64 __lstat64_time64 + # define feof_unlocked __feof_unlocked ++#else ++# define struct_stat64 struct stat64 + #endif + + /* Name of the file containing the module information in the directories +@@ -145,8 +148,8 @@ + DIR *confdir = opendir (buf); + if (confdir != NULL) + { +- struct dirent *ent; +- while ((ent = readdir (confdir)) != NULL) ++ struct dirent64 *ent; ++ while ((ent = readdir64 (confdir)) != NULL) + { + if (ent->d_type != DT_REG && ent->d_type != DT_UNKNOWN) + continue; +@@ -158,7 +161,7 @@ + && strcmp (ent->d_name + len - strlen (suffix), suffix) == 0) + { + char *conf; +- struct stat64 st; ++ struct_stat64 st; + if (asprintf (&conf, "%s/%s", buf, ent->d_name) < 0) + continue; + +diff -Nuar a/iconvdata/iso-2022-cn-ext.c b/iconvdata/iso-2022-cn-ext.c +--- a/iconvdata/iso-2022-cn-ext.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/iconvdata/iso-2022-cn-ext.c 2025-10-20 21:02:20.000000000 +0300 +@@ -574,6 +574,12 @@ + { \ + const char *escseq; \ + \ ++ if (outptr + 4 > outend) \ ++ { \ ++ result = __GCONV_FULL_OUTPUT; \ ++ break; \ ++ } \ ++ \ + assert (used == CNS11643_2_set); /* XXX */ \ + escseq = "*H"; \ + *outptr++ = ESC; \ +@@ -587,6 +593,12 @@ + { \ + const char *escseq; \ + \ ++ if (outptr + 4 > outend) \ ++ { \ ++ result = __GCONV_FULL_OUTPUT; \ ++ break; \ ++ } \ ++ \ + assert ((used >> 5) >= 3 && (used >> 5) <= 7); \ + escseq = "+I+J+K+L+M" + ((used >> 5) - 3) * 2; \ + *outptr++ = ESC; \ +diff -Nuar a/iconvdata/Makefile b/iconvdata/Makefile +--- a/iconvdata/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/iconvdata/Makefile 2025-10-20 21:02:20.000000000 +0300 +@@ -75,7 +75,8 @@ + tests = bug-iconv1 bug-iconv2 tst-loading tst-e2big tst-iconv4 bug-iconv4 \ + tst-iconv6 bug-iconv5 bug-iconv6 tst-iconv7 bug-iconv8 bug-iconv9 \ + bug-iconv10 bug-iconv11 bug-iconv12 tst-iconv-big5-hkscs-to-2ucs4 \ +- bug-iconv13 bug-iconv14 bug-iconv15 ++ bug-iconv13 bug-iconv14 bug-iconv15 \ ++ tst-iconv-iso-2022-cn-ext + ifeq ($(have-thread-library),yes) + tests += bug-iconv3 + endif +@@ -330,6 +331,8 @@ + $(addprefix $(objpfx),$(modules.so)) + $(objpfx)bug-iconv15.out: $(addprefix $(objpfx), $(gconv-modules)) \ + $(addprefix $(objpfx),$(modules.so)) ++$(objpfx)tst-iconv-iso-2022-cn-ext.out: $(addprefix $(objpfx), $(gconv-modules)) \ ++ $(addprefix $(objpfx),$(modules.so)) + + $(objpfx)iconv-test.out: run-iconv-test.sh \ + $(addprefix $(objpfx), $(gconv-modules)) \ +diff -Nuar a/iconvdata/tst-iconv-iso-2022-cn-ext.c b/iconvdata/tst-iconv-iso-2022-cn-ext.c +--- a/iconvdata/tst-iconv-iso-2022-cn-ext.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/iconvdata/tst-iconv-iso-2022-cn-ext.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,128 @@ ++/* Verify ISO-2022-CN-EXT does not write out of the bounds. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* The test sets up a two memory page buffer with the second page marked ++ PROT_NONE to trigger a fault if the conversion writes beyond the exact ++ expected amount. Then we carry out various conversions and precisely ++ place the start of the output buffer in order to trigger a SIGSEGV if the ++ process writes anywhere between 1 and page sized bytes more (only one ++ PROT_NONE page is setup as a canary) than expected. These tests exercise ++ all three of the cases in ISO-2022-CN-EXT where the converter must switch ++ character sets and may run out of buffer space while doing the ++ operation. */ ++ ++static int ++do_test (void) ++{ ++ iconv_t cd = iconv_open ("ISO-2022-CN-EXT", "UTF-8"); ++ TEST_VERIFY_EXIT (cd != (iconv_t) -1); ++ ++ char *ntf; ++ size_t ntfsize; ++ char *outbufbase; ++ { ++ int pgz = getpagesize (); ++ TEST_VERIFY_EXIT (pgz > 0); ++ ntfsize = 2 * pgz; ++ ++ ntf = xmmap (NULL, ntfsize, PROT_READ | PROT_WRITE, MAP_PRIVATE ++ | MAP_ANONYMOUS, -1); ++ xmprotect (ntf + pgz, pgz, PROT_NONE); ++ ++ outbufbase = ntf + pgz; ++ } ++ ++ /* Check if SOdesignation escape sequence does not trigger an OOB write. */ ++ { ++ char inbuf[] = "\xe4\xba\xa4\xe6\x8d\xa2"; ++ ++ for (int i = 0; i < 9; i++) ++ { ++ char *inp = inbuf; ++ size_t inleft = sizeof (inbuf) - 1; ++ ++ char *outp = outbufbase - i; ++ size_t outleft = i; ++ ++ TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft) ++ == (size_t) -1); ++ TEST_COMPARE (errno, E2BIG); ++ ++ TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0); ++ } ++ } ++ ++ /* Same as before for SS2designation. */ ++ { ++ char inbuf[] = "㴽 \xe3\xb4\xbd"; ++ ++ for (int i = 0; i < 14; i++) ++ { ++ char *inp = inbuf; ++ size_t inleft = sizeof (inbuf) - 1; ++ ++ char *outp = outbufbase - i; ++ size_t outleft = i; ++ ++ TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft) ++ == (size_t) -1); ++ TEST_COMPARE (errno, E2BIG); ++ ++ TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0); ++ } ++ } ++ ++ /* Same as before for SS3designation. */ ++ { ++ char inbuf[] = "劄 \xe5\x8a\x84"; ++ ++ for (int i = 0; i < 14; i++) ++ { ++ char *inp = inbuf; ++ size_t inleft = sizeof (inbuf) - 1; ++ ++ char *outp = outbufbase - i; ++ size_t outleft = i; ++ ++ TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft) ++ == (size_t) -1); ++ TEST_COMPARE (errno, E2BIG); ++ ++ TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0); ++ } ++ } ++ ++ TEST_VERIFY_EXIT (iconv_close (cd) != -1); ++ ++ xmunmap (ntf, ntfsize); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/include/arpa/nameser.h b/include/arpa/nameser.h +--- a/include/arpa/nameser.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/include/arpa/nameser.h 2025-10-20 21:02:20.000000000 +0300 +@@ -55,6 +55,12 @@ + int __ns_name_unpack (const unsigned char *, const unsigned char *, + const unsigned char *, unsigned char *, size_t) __THROW; + ++/* Like ns_samename, but for uncompressed binary names. Return true ++ if the two arguments compare are equal as case-insensitive domain ++ names. */ ++_Bool __ns_samebinaryname (const unsigned char *, const unsigned char *) ++ attribute_hidden; ++ + #define ns_msg_getflag(handle, flag) \ + (((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift) + +@@ -89,5 +95,105 @@ + extern __typeof (ns_samename) __libc_ns_samename; + libc_hidden_proto (__libc_ns_samename) + ++/* Packet parser helper functions. */ ++ ++/* Verify that P points to an uncompressed domain name in wire format. ++ On success, return the length of the encoded name, including the ++ terminating null byte. On failure, return -1 and set errno. EOM ++ must point one past the last byte in the packet. */ ++int __ns_name_length_uncompressed (const unsigned char *p, ++ const unsigned char *eom) attribute_hidden; ++ ++/* Iterator over the resource records in a DNS packet. */ ++struct ns_rr_cursor ++{ ++ /* These members are not changed after initialization. */ ++ const unsigned char *begin; /* First byte of packet. */ ++ const unsigned char *end; /* One past the last byte of the packet. */ ++ const unsigned char *first_rr; /* First resource record (or packet end). */ ++ ++ /* Advanced towards the end while reading the packet. */ ++ const unsigned char *current; ++}; ++ ++/* Returns the RCODE field from the DNS header. */ ++static inline int ++ns_rr_cursor_rcode (const struct ns_rr_cursor *c) ++{ ++ return c->begin[3] & 0x0f; /* Lower 4 bits at offset 3. */ ++} ++ ++/* Returns the length of the answer section according to the DNS header. */ ++static inline int ++ns_rr_cursor_ancount (const struct ns_rr_cursor *c) ++{ ++ return c->begin[6] * 256 + c->begin[7]; /* 16 bits at offset 6. */ ++} ++ ++/* Returns the length of the authority (name server) section according ++ to the DNS header. */ ++static inline int ++ns_rr_cursor_nscount (const struct ns_rr_cursor *c) ++{ ++ return c->begin[8] * 256 + c->begin[9]; /* 16 bits at offset 8. */ ++} ++ ++/* Returns the length of the additional data section according to the ++ DNS header. */ ++static inline int ++ns_rr_cursor_adcount (const struct ns_rr_cursor *c) ++{ ++ return c->begin[10] * 256 + c->begin[11]; /* 16 bits at offset 10. */ ++} ++ ++/* Returns a pointer to the uncompressed question name in wire ++ format. */ ++static inline const unsigned char * ++ns_rr_cursor_qname (const struct ns_rr_cursor *c) ++{ ++ return c->begin + 12; /* QNAME starts right after the header. */ ++} ++ ++/* Returns the question type of the first and only question. */ ++static inline const int ++ns_rr_cursor_qtype (const struct ns_rr_cursor *c) ++{ ++ /* 16 bits 4 bytes back from the first RR header start. */ ++ return c->first_rr[-4] * 256 + c->first_rr[-3]; ++} ++ ++/* Returns the clss of the first and only question (usally C_IN). */ ++static inline const int ++ns_rr_cursor_qclass (const struct ns_rr_cursor *c) ++{ ++ /* 16 bits 2 bytes back from the first RR header start. */ ++ return c->first_rr[-2] * 256 + c->first_rr[-1]; ++} ++ ++/* Initializes *C to cover the packet [BUF, BUF+LEN). Returns false ++ if LEN is less than sizeof (*HD), if the packet does not contain a ++ full (uncompressed) question, or if the question count is not 1. */ ++_Bool __ns_rr_cursor_init (struct ns_rr_cursor *c, ++ const unsigned char *buf, size_t len) ++ attribute_hidden; ++ ++/* Like ns_rr, but the record owner name is not decoded into text format. */ ++struct ns_rr_wire ++{ ++ unsigned char rname[NS_MAXCDNAME]; /* Owner name of the record. */ ++ uint16_t rtype; /* Resource record type (T_*). */ ++ uint16_t rclass; /* Resource record class (C_*). */ ++ uint32_t ttl; /* Time-to-live field. */ ++ const unsigned char *rdata; /* Start of resource record data. */ ++ uint16_t rdlength; /* Length of the data at rdata, in bytes. */ ++}; ++ ++/* Attempts to parse the record at C into *RR. On success, return ++ true, and C is advanced past the record, and RR->rdata points to ++ the record data. On failure, errno is set to EMSGSIZE, and false ++ is returned. */ ++_Bool __ns_rr_cursor_next (struct ns_rr_cursor *c, struct ns_rr_wire *rr) ++ attribute_hidden; ++ + # endif /* !_ISOMAC */ + #endif +diff -Nuar a/include/bits/stdio2-decl.h b/include/bits/stdio2-decl.h +--- a/include/bits/stdio2-decl.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/include/bits/stdio2-decl.h 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1 @@ ++#include +diff -Nuar a/include/bits/wchar2-decl.h b/include/bits/wchar2-decl.h +--- a/include/bits/wchar2-decl.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/include/bits/wchar2-decl.h 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1 @@ ++#include +diff -Nuar a/include/libc-internal.h b/include/libc-internal.h +--- a/include/libc-internal.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/include/libc-internal.h 2025-10-20 21:02:20.000000000 +0300 +@@ -21,9 +21,6 @@ + + #include + +-/* Initialize the `__libc_enable_secure' flag. */ +-extern void __libc_init_secure (void); +- + /* Discover the tick frequency of the machine if something goes wrong, + we return 0, an impossible hertz. */ + extern int __profile_frequency (void); +diff -Nuar a/include/link.h b/include/link.h +--- a/include/link.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/include/link.h 2025-10-20 21:02:20.000000000 +0300 +@@ -280,6 +280,10 @@ + /* List of object in order of the init and fini calls. */ + struct link_map **l_initfini; + ++ /* Linked list of objects in reverse ELF constructor execution ++ order. Head of list is stored in _dl_init_called_list. */ ++ struct link_map *l_init_called_next; ++ + /* List of the dependencies introduced through symbol binding. */ + struct link_map_reldeps + { +diff -Nuar a/include/register-atfork.h b/include/register-atfork.h +--- a/include/register-atfork.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/include/register-atfork.h 2025-10-20 21:02:20.000000000 +0300 +@@ -26,6 +26,7 @@ + void (*parent_handler) (void); + void (*child_handler) (void); + void *dso_handle; ++ uint64_t id; + }; + + /* Function to call to unregister fork handlers. */ +@@ -39,19 +40,18 @@ + atfork_run_parent + }; + +-/* Run the atfork handlers and lock/unlock the internal lock depending +- of the WHO argument: +- +- - atfork_run_prepare: run all the PREPARE_HANDLER in reverse order of +- insertion and locks the internal lock. +- - atfork_run_child: run all the CHILD_HANDLER and unlocks the internal +- lock. +- - atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal +- lock. +- +- Perform locking only if DO_LOCKING. */ +-extern void __run_fork_handlers (enum __run_fork_handler_type who, +- _Bool do_locking) attribute_hidden; ++/* Run the atfork prepare handlers in the reverse order of registration and ++ return the ID of the last registered handler. If DO_LOCKING is true, the ++ internal lock is held locked upon return. */ ++extern uint64_t __run_prefork_handlers (_Bool do_locking) attribute_hidden; ++ ++/* Given a handler type (parent or child), run all the atfork handlers in ++ the order of registration up to and including the handler with id equal ++ to LASTRUN. If DO_LOCKING is true, the internal lock is unlocked prior ++ to return. */ ++extern void __run_postfork_handlers (enum __run_fork_handler_type who, ++ _Bool do_locking, ++ uint64_t lastrun) attribute_hidden; + + /* C library side function to register new fork handlers. */ + extern int __register_atfork (void (*__prepare) (void), +diff -Nuar a/include/resolv.h b/include/resolv.h +--- a/include/resolv.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/include/resolv.h 2025-10-20 21:02:18.000000000 +0300 +@@ -70,5 +70,8 @@ + extern __typeof (__res_queriesmatch) __libc_res_queriesmatch; + libc_hidden_proto (__libc_res_queriesmatch) + ++/* Variant of res_hnok which operates on binary (but uncompressed) names. */ ++bool __res_binary_hnok (const unsigned char *dn) attribute_hidden; ++ + # endif /* _RESOLV_H_ && !_ISOMAC */ + #endif +diff -Nuar a/include/sys/sysinfo.h b/include/sys/sysinfo.h +--- a/include/sys/sysinfo.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/include/sys/sysinfo.h 2025-10-20 21:02:18.000000000 +0300 +@@ -14,10 +14,6 @@ + extern int __get_nprocs (void); + libc_hidden_proto (__get_nprocs) + +-/* Return the number of available processors which the process can +- be scheduled. */ +-extern int __get_nprocs_sched (void) attribute_hidden; +- + /* Return number of physical pages of memory in the system. */ + extern long int __get_phys_pages (void); + libc_hidden_proto (__get_phys_pages) +diff -Nuar a/include/unistd.h b/include/unistd.h +--- a/include/unistd.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/include/unistd.h 2025-10-20 21:02:20.000000000 +0300 +@@ -192,7 +192,6 @@ + and some functions contained in the C library ignore various + environment variables that normally affect them. */ + extern int __libc_enable_secure attribute_relro; +-extern int __libc_enable_secure_decided; + rtld_hidden_proto (__libc_enable_secure) + + +diff -Nuar a/inet/ruserpass.c b/inet/ruserpass.c +--- a/inet/ruserpass.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/inet/ruserpass.c 2025-10-20 21:02:20.000000000 +0300 +@@ -95,7 +95,7 @@ + char *hdir, *buf, *tmp; + char myname[1024], *mydomain; + int t, usedefault = 0; +- struct stat64 stb; ++ struct __stat64_t64 stb; + + hdir = __libc_secure_getenv("HOME"); + if (hdir == NULL) { +@@ -174,7 +174,7 @@ + break; + case PASSWD: + if (strcmp(*aname, "anonymous") && +- __fstat64(fileno(cfile), &stb) >= 0 && ++ __fstat64_time64(fileno(cfile), &stb) >= 0 && + (stb.st_mode & 077) != 0) { + warnx(_("Error: .netrc file is readable by others.")); + warnx(_("Remove 'password' line or make file unreadable by others.")); +diff -Nuar a/io/Makefile b/io/Makefile +--- a/io/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/io/Makefile 2025-10-20 21:02:20.000000000 +0300 +@@ -59,6 +59,7 @@ + ftw64-time64 \ + closefrom close_range + ++ + others := pwd + test-srcs := ftwtest ftwtest-time64 + tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \ +@@ -80,19 +81,22 @@ + tst-utimensat \ + tst-closefrom \ + tst-close_range \ +- tst-ftw-bz28126 ++ tst-ftw-bz28126 \ ++ tst-fcntl-lock \ ++ tst-fcntl-lock-lfs + + tests-time64 := \ ++ tst-fcntl-time64 \ ++ tst-fts-time64 \ + tst-futimens-time64 \ + tst-futimes-time64\ +- tst-fts-time64 \ ++ tst-futimesat-time64 \ ++ tst-lchmod-time64 \ + tst-lutimes-time64 \ + tst-stat-time64 \ +- tst-futimesat-time64 \ + tst-utime-time64 \ + tst-utimensat-time64 \ + tst-utimes-time64 \ +- tst-fcntl-time64 \ + # tests-time64 + + # Likewise for statx, but we do not need static linking here. +@@ -136,6 +140,7 @@ + + CFLAGS-test-stat.c += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE + CFLAGS-test-lfs.c += -D_LARGEFILE64_SOURCE ++CFLAGS-tst-lchmod.c += -D_FILE_OFFSET_BITS=64 + + test-stat2-ARGS = Makefile . $(objpfx)test-stat2 + +diff -Nuar a/io/tst-fcntl-lock.c b/io/tst-fcntl-lock.c +--- a/io/tst-fcntl-lock.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/io/tst-fcntl-lock.c 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,97 @@ ++/* Test for advisory record locking. ++ Copyright (C) 2023 Free Software Foundation, Inc. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version 2 ++ of the License, or (at your option) any later version. ++ ++ This program 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 General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, see . ++*/ ++ ++#include ++#include ++#include ++ ++/* This is essentially the POSIX lockf. */ ++ ++static int ++fcntl_lockf (int fd, int cmd, off_t len) ++{ ++ struct flock fl = { ++ .l_type = F_WRLCK, ++ .l_whence = SEEK_CUR, ++ .l_len = len ++ }; ++ ++ switch (cmd) ++ { ++ case F_TEST: ++ fl.l_type = F_RDLCK; ++ if (fcntl (fd, F_GETLK, &fl) < 0) ++ return -1; ++ if (fl.l_type == F_UNLCK || fl.l_pid == getpid ()) ++ return 0; ++ errno = EACCES; ++ return -1; ++ ++ case F_ULOCK: ++ fl.l_type = F_UNLCK; ++ return fcntl (fd, F_SETLK, &fl); ++ ++ case F_LOCK: ++ return fcntl (fd, F_SETLKW, &fl); ++ ++ case F_TLOCK: ++ return fcntl (fd, F_SETLK, &fl); ++ } ++ ++ errno = EINVAL; ++ return -1; ++} ++ ++static int ++fcntl64_lockf (int fd, int cmd, off64_t len64) ++ { ++ struct flock64 fl64 = { ++ .l_type = F_WRLCK, ++ .l_whence = SEEK_CUR, ++ .l_len = len64 ++ }; ++ ++ switch (cmd) ++ { ++ case F_TEST: ++ fl64.l_type = F_RDLCK; ++ if (fcntl64 (fd, F_GETLK64, &fl64) < 0) ++ return -1; ++ if (fl64.l_type == F_UNLCK || fl64.l_pid == getpid ()) ++ return 0; ++ errno = EACCES; ++ return -1; ++ ++ case F_ULOCK: ++ fl64.l_type = F_UNLCK; ++ return fcntl64 (fd, F_SETLK64, &fl64); ++ ++ case F_LOCK: ++ return fcntl64 (fd, F_SETLKW64, &fl64); ++ ++ case F_TLOCK: ++ return fcntl64 (fd, F_SETLK64, &fl64); ++ } ++ ++ errno = EINVAL; ++ return -1; ++} ++ ++#define TST_LOCKFD "tst-fcntl-lock." ++#define LOCKF fcntl_lockf ++#define LOCKF64 fcntl64_lockf ++#include "tst-lockf.c" +diff -Nuar a/io/tst-fcntl-lock-lfs.c b/io/tst-fcntl-lock-lfs.c +--- a/io/tst-fcntl-lock-lfs.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/io/tst-fcntl-lock-lfs.c 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define _FILE_OFFSET_BITS 64 ++#include +diff -Nuar a/io/tst-lchmod.c b/io/tst-lchmod.c +--- a/io/tst-lchmod.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/io/tst-lchmod.c 2025-10-20 21:02:20.000000000 +0300 +@@ -67,9 +67,26 @@ + } + + static void ++update_file_time_to_y2038 (const char *fname, int flags) ++{ ++#ifdef CHECK_TIME64 ++ /* Y2038 threshold plus 1 second. */ ++ const struct timespec ts[] = { { 0x80000001LL, 0}, { 0x80000001LL } }; ++ TEST_VERIFY_EXIT (utimensat (AT_FDCWD, fname, ts, flags) == 0); ++#endif ++} ++ ++static void + test_1 (bool do_relative_path, int (*chmod_func) (int fd, const char *, mode_t, int)) + { + char *tempdir = support_create_temp_directory ("tst-lchmod-"); ++#ifdef CHECK_TIME64 ++ if (!support_path_support_time64 (tempdir)) ++ { ++ puts ("info: test skipped, filesystem does not support 64 bit time_t"); ++ return; ++ } ++#endif + + char *path_dangling = xasprintf ("%s/dangling", tempdir); + char *path_file = xasprintf ("%s/file", tempdir); +@@ -93,9 +110,12 @@ + xsymlink ("loop", path_loop); + xsymlink ("target-does-not-exist", path_dangling); + ++ update_file_time_to_y2038 (path_file, 0); ++ update_file_time_to_y2038 (path_to_file, AT_SYMLINK_NOFOLLOW); ++ + /* Check that the modes do not collide with what we will use in the + test. */ +- struct stat64 st; ++ struct stat st; + xstat (path_file, &st); + TEST_VERIFY ((st.st_mode & 0777) != 1); + xlstat (path_to_file, &st); +diff -Nuar a/io/tst-lchmod-time64.c b/io/tst-lchmod-time64.c +--- a/io/tst-lchmod-time64.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/io/tst-lchmod-time64.c 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define CHECK_TIME64 ++#include "tst-lchmod.c" +diff -Nuar a/io/tst-lockf.c b/io/tst-lockf.c +--- a/io/tst-lockf.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/io/tst-lockf.c 2025-10-20 21:02:20.000000000 +0300 +@@ -24,13 +24,23 @@ + #include + #include + ++#ifndef TST_LOCKFD ++# define TST_LOCKFD "tst-lockfd." ++#endif ++#ifndef LOCKF ++# define LOCKF lockf ++#endif ++#ifndef LOCKF64 ++# define LOCKF64 lockf64 ++#endif ++ + static char *temp_filename; + static int temp_fd; + + static void + do_prepare (int argc, char **argv) + { +- temp_fd = create_temp_file ("tst-lockfd.", &temp_filename); ++ temp_fd = create_temp_file (TST_LOCKFD, &temp_filename); + TEST_VERIFY_EXIT (temp_fd != -1); + } + #define PREPARE do_prepare +@@ -40,22 +50,22 @@ + { + /* Check if parent has [0, 1024) locked. */ + TEST_COMPARE (lseek (temp_fd, 0, SEEK_SET), 0); +- TEST_COMPARE (lockf (temp_fd, F_TLOCK, 1024), -1); ++ TEST_COMPARE (LOCKF (temp_fd, F_TLOCK, 1024), -1); + TEST_COMPARE (errno, EAGAIN); +- TEST_COMPARE (lockf (temp_fd, F_TEST, 1024), -1); ++ TEST_COMPARE (LOCKF (temp_fd, F_TEST, 1024), -1); + TEST_COMPARE (errno, EACCES); + /* Also Check if parent has last 1024 bytes locked. */ + TEST_COMPARE (lseek (temp_fd, INT32_MAX-1024, SEEK_SET), INT32_MAX-1024); +- TEST_COMPARE (lockf (temp_fd, F_TEST, 1024), -1); ++ TEST_COMPARE (LOCKF (temp_fd, F_TEST, 1024), -1); + + /* And try to lock [1024, 2048). */ + TEST_COMPARE (lseek (temp_fd, 1024, SEEK_SET), 1024); +- TEST_COMPARE (lockf (temp_fd, F_LOCK, 1024), 0); ++ TEST_COMPARE (LOCKF (temp_fd, F_LOCK, 1024), 0); + + /* Check if non-LFS interface cap access to 32-bif off_t. */ + TEST_COMPARE (lseek64 (temp_fd, (off64_t)INT32_MAX, SEEK_SET), + (off64_t)INT32_MAX); +- TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), 0); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), 0); + } + + static void +@@ -63,32 +73,32 @@ + { + /* Check if parent has [0, 1024) locked. */ + TEST_COMPARE (lseek64 (temp_fd, 0, SEEK_SET), 0); +- TEST_COMPARE (lockf64 (temp_fd, F_TLOCK, 1024), -1); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_TLOCK, 1024), -1); + TEST_COMPARE (errno, EAGAIN); +- TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), -1); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), -1); + TEST_COMPARE (errno, EACCES); + /* Also Check if parent has last 1024 bytes locked. */ + TEST_COMPARE (lseek64 (temp_fd, INT32_MAX-1024, SEEK_SET), INT32_MAX-1024); +- TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), -1); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), -1); + + /* And try to lock [1024, 2048). */ + TEST_COMPARE (lseek64 (temp_fd, 1024, SEEK_SET), 1024); +- TEST_COMPARE (lockf64 (temp_fd, F_LOCK, 1024), 0); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_LOCK, 1024), 0); + + /* And also [INT32_MAX, INT32_MAX+1024). */ + { + off64_t off = (off64_t)INT32_MAX; + TEST_COMPARE (lseek64 (temp_fd, off, SEEK_SET), off); +- TEST_COMPARE (lockf64 (temp_fd, F_LOCK, 1024), 0); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_LOCK, 1024), 0); + } + + /* Check if [INT32_MAX+1024, INT64_MAX) is locked. */ + { + off64_t off = (off64_t)INT32_MAX+1024; + TEST_COMPARE (lseek64 (temp_fd, off, SEEK_SET), off); +- TEST_COMPARE (lockf64 (temp_fd, F_TLOCK, 1024), -1); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_TLOCK, 1024), -1); + TEST_COMPARE (errno, EAGAIN); +- TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), -1); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), -1); + TEST_COMPARE (errno, EACCES); + } + } +@@ -97,38 +107,38 @@ + do_test (void) + { + /* Basic tests to check if a lock can be obtained and checked. */ +- TEST_COMPARE (lockf (temp_fd, F_LOCK, 1024), 0); +- TEST_COMPARE (lockf (temp_fd, F_LOCK, INT32_MAX), 0); +- TEST_COMPARE (lockf (temp_fd, F_TLOCK, 1024), 0); +- TEST_COMPARE (lockf (temp_fd, F_TEST, 1024), 0); ++ TEST_COMPARE (LOCKF (temp_fd, F_LOCK, 1024), 0); ++ TEST_COMPARE (LOCKF (temp_fd, F_LOCK, INT32_MAX), 0); ++ TEST_COMPARE (LOCKF (temp_fd, F_TLOCK, 1024), 0); ++ TEST_COMPARE (LOCKF (temp_fd, F_TEST, 1024), 0); + TEST_COMPARE (lseek (temp_fd, 1024, SEEK_SET), 1024); +- TEST_COMPARE (lockf (temp_fd, F_ULOCK, 1024), 0); ++ TEST_COMPARE (LOCKF (temp_fd, F_ULOCK, 1024), 0); + /* Parent process should have ([0, 1024), [2048, INT32_MAX)) ranges locked. */ + + { + struct support_capture_subprocess result; + result = support_capture_subprocess (do_test_child_lockf, NULL); +- support_capture_subprocess_check (&result, "lockf", 0, sc_allow_none); ++ support_capture_subprocess_check (&result, "LOCKF", 0, sc_allow_none); + } + + if (sizeof (off_t) != sizeof (off64_t)) + { + /* Check if previously locked regions with LFS symbol. */ + TEST_COMPARE (lseek (temp_fd, 0, SEEK_SET), 0); +- TEST_COMPARE (lockf64 (temp_fd, F_LOCK, 1024), 0); +- TEST_COMPARE (lockf64 (temp_fd, F_TLOCK, 1024), 0); +- TEST_COMPARE (lockf64 (temp_fd, F_TEST, 1024), 0); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_LOCK, 1024), 0); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_TLOCK, 1024), 0); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_TEST, 1024), 0); + /* Lock region [INT32_MAX+1024, INT64_MAX). */ + off64_t off = (off64_t)INT32_MAX + 1024; + TEST_COMPARE (lseek64 (temp_fd, off, SEEK_SET), off); +- TEST_COMPARE (lockf64 (temp_fd, F_LOCK, 1024), 0); ++ TEST_COMPARE (LOCKF64 (temp_fd, F_LOCK, 1024), 0); + /* Parent process should have ([0, 1024), [2048, INT32_MAX), + [INT32_MAX+1024, INT64_MAX)) ranges locked. */ + + { + struct support_capture_subprocess result; + result = support_capture_subprocess (do_test_child_lockf64, NULL); +- support_capture_subprocess_check (&result, "lockf", 0, sc_allow_none); ++ support_capture_subprocess_check (&result, "LOCKF", 0, sc_allow_none); + } + } + +diff -Nuar a/io/tst-stat.c b/io/tst-stat.c +--- a/io/tst-stat.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/io/tst-stat.c 2025-10-20 21:02:20.000000000 +0300 +@@ -69,6 +69,10 @@ + TEST_VERIFY_EXIT (fd >= 0); + support_write_file_string (path, "abc"); + ++ /* This should help to prevent delayed allocation, which may result ++ in a spurious stx_blocks/st_blocks difference. */ ++ fsync (fd); ++ + bool check_ns = support_stat_nanoseconds (path); + if (!check_ns) + printf ("warning: timestamp with nanoseconds not supported\n"); +diff -Nuar a/libio/bits/stdio2-decl.h b/libio/bits/stdio2-decl.h +--- a/libio/bits/stdio2-decl.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/libio/bits/stdio2-decl.h 2025-10-20 21:02:20.000000000 +0300 +@@ -0,0 +1,111 @@ ++/* Checking macros for stdio functions. Declarations only. ++ Copyright (C) 2004-2022 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 ++ . */ ++ ++#ifndef _BITS_STDIO2_DEC_H ++#define _BITS_STDIO2_DEC_H 1 ++ ++#ifndef _STDIO_H ++# error "Never include directly; use instead." ++#endif ++ ++extern int __sprintf_chk (char *__restrict __s, int __flag, size_t __slen, ++ const char *__restrict __format, ...) __THROW ++ __attr_access ((__write_only__, 1, 3)); ++extern int __vsprintf_chk (char *__restrict __s, int __flag, size_t __slen, ++ const char *__restrict __format, ++ __gnuc_va_list __ap) __THROW ++ __attr_access ((__write_only__, 1, 3)); ++ ++#if defined __USE_ISOC99 || defined __USE_UNIX98 ++ ++extern int __snprintf_chk (char *__restrict __s, size_t __n, int __flag, ++ size_t __slen, const char *__restrict __format, ++ ...) __THROW ++ __attr_access ((__write_only__, 1, 2)); ++extern int __vsnprintf_chk (char *__restrict __s, size_t __n, int __flag, ++ size_t __slen, const char *__restrict __format, ++ __gnuc_va_list __ap) __THROW ++ __attr_access ((__write_only__, 1, 2)); ++ ++#endif ++ ++#if __USE_FORTIFY_LEVEL > 1 ++ ++extern int __fprintf_chk (FILE *__restrict __stream, int __flag, ++ const char *__restrict __format, ...); ++extern int __printf_chk (int __flag, const char *__restrict __format, ...); ++extern int __vfprintf_chk (FILE *__restrict __stream, int __flag, ++ const char *__restrict __format, __gnuc_va_list __ap); ++extern int __vprintf_chk (int __flag, const char *__restrict __format, ++ __gnuc_va_list __ap); ++ ++# ifdef __USE_XOPEN2K8 ++extern int __dprintf_chk (int __fd, int __flag, const char *__restrict __fmt, ++ ...) __attribute__ ((__format__ (__printf__, 3, 4))); ++extern int __vdprintf_chk (int __fd, int __flag, ++ const char *__restrict __fmt, __gnuc_va_list __arg) ++ __attribute__ ((__format__ (__printf__, 3, 0))); ++# endif ++ ++# ifdef __USE_GNU ++ ++extern int __asprintf_chk (char **__restrict __ptr, int __flag, ++ const char *__restrict __fmt, ...) ++ __THROW __attribute__ ((__format__ (__printf__, 3, 4))) __wur; ++extern int __vasprintf_chk (char **__restrict __ptr, int __flag, ++ const char *__restrict __fmt, __gnuc_va_list __arg) ++ __THROW __attribute__ ((__format__ (__printf__, 3, 0))) __wur; ++extern int __obstack_printf_chk (struct obstack *__restrict __obstack, ++ int __flag, const char *__restrict __format, ++ ...) ++ __THROW __attribute__ ((__format__ (__printf__, 3, 4))); ++extern int __obstack_vprintf_chk (struct obstack *__restrict __obstack, ++ int __flag, ++ const char *__restrict __format, ++ __gnuc_va_list __args) ++ __THROW __attribute__ ((__format__ (__printf__, 3, 0))); ++ ++# endif ++#endif ++ ++#if __GLIBC_USE (DEPRECATED_GETS) ++extern char *__gets_chk (char *__str, size_t) __wur; ++#endif ++ ++extern char *__fgets_chk (char *__restrict __s, size_t __size, int __n, ++ FILE *__restrict __stream) ++ __wur __attr_access ((__write_only__, 1, 3)); ++ ++extern size_t __fread_chk (void *__restrict __ptr, size_t __ptrlen, ++ size_t __size, size_t __n, ++ FILE *__restrict __stream) __wur; ++ ++#ifdef __USE_GNU ++extern char *__fgets_unlocked_chk (char *__restrict __s, size_t __size, ++ int __n, FILE *__restrict __stream) ++ __wur __attr_access ((__write_only__, 1, 3)); ++#endif ++ ++#ifdef __USE_MISC ++# undef fread_unlocked ++extern size_t __fread_unlocked_chk (void *__restrict __ptr, size_t __ptrlen, ++ size_t __size, size_t __n, ++ FILE *__restrict __stream) __wur; ++#endif ++ ++#endif /* bits/stdio2-decl.h. */ +diff -Nuar a/libio/bits/stdio2.h b/libio/bits/stdio2.h +--- a/libio/bits/stdio2.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/libio/bits/stdio2.h 2025-10-20 21:02:20.000000000 +0300 +@@ -23,14 +23,6 @@ + # error "Never include directly; use instead." + #endif + +-extern int __sprintf_chk (char *__restrict __s, int __flag, size_t __slen, +- const char *__restrict __format, ...) __THROW +- __attr_access ((__write_only__, 1, 3)); +-extern int __vsprintf_chk (char *__restrict __s, int __flag, size_t __slen, +- const char *__restrict __format, +- __gnuc_va_list __ap) __THROW +- __attr_access ((__write_only__, 1, 3)); +- + #ifdef __va_arg_pack + __fortify_function int + __NTH (sprintf (char *__restrict __s, const char *__restrict __fmt, ...)) +@@ -54,15 +46,6 @@ + } + + #if defined __USE_ISOC99 || defined __USE_UNIX98 +- +-extern int __snprintf_chk (char *__restrict __s, size_t __n, int __flag, +- size_t __slen, const char *__restrict __format, +- ...) __THROW +- __attr_access ((__write_only__, 1, 2)); +-extern int __vsnprintf_chk (char *__restrict __s, size_t __n, int __flag, +- size_t __slen, const char *__restrict __format, +- __gnuc_va_list __ap) __THROW; +- + # ifdef __va_arg_pack + __fortify_function int + __NTH (snprintf (char *__restrict __s, size_t __n, +@@ -89,15 +72,6 @@ + #endif + + #if __USE_FORTIFY_LEVEL > 1 +- +-extern int __fprintf_chk (FILE *__restrict __stream, int __flag, +- const char *__restrict __format, ...); +-extern int __printf_chk (int __flag, const char *__restrict __format, ...); +-extern int __vfprintf_chk (FILE *__restrict __stream, int __flag, +- const char *__restrict __format, __gnuc_va_list __ap); +-extern int __vprintf_chk (int __flag, const char *__restrict __format, +- __gnuc_va_list __ap); +- + # ifdef __va_arg_pack + __fortify_function int + fprintf (FILE *__restrict __stream, const char *__restrict __fmt, ...) +@@ -136,12 +110,6 @@ + } + + # ifdef __USE_XOPEN2K8 +-extern int __dprintf_chk (int __fd, int __flag, const char *__restrict __fmt, +- ...) __attribute__ ((__format__ (__printf__, 3, 4))); +-extern int __vdprintf_chk (int __fd, int __flag, +- const char *__restrict __fmt, __gnuc_va_list __arg) +- __attribute__ ((__format__ (__printf__, 3, 0))); +- + # ifdef __va_arg_pack + __fortify_function int + dprintf (int __fd, const char *__restrict __fmt, ...) +@@ -162,23 +130,6 @@ + # endif + + # ifdef __USE_GNU +- +-extern int __asprintf_chk (char **__restrict __ptr, int __flag, +- const char *__restrict __fmt, ...) +- __THROW __attribute__ ((__format__ (__printf__, 3, 4))) __wur; +-extern int __vasprintf_chk (char **__restrict __ptr, int __flag, +- const char *__restrict __fmt, __gnuc_va_list __arg) +- __THROW __attribute__ ((__format__ (__printf__, 3, 0))) __wur; +-extern int __obstack_printf_chk (struct obstack *__restrict __obstack, +- int __flag, const char *__restrict __format, +- ...) +- __THROW __attribute__ ((__format__ (__printf__, 3, 4))); +-extern int __obstack_vprintf_chk (struct obstack *__restrict __obstack, +- int __flag, +- const char *__restrict __format, +- __gnuc_va_list __args) +- __THROW __attribute__ ((__format__ (__printf__, 3, 0))); +- + # ifdef __va_arg_pack + __fortify_function int + __NTH (asprintf (char **__restrict __ptr, const char *__restrict __fmt, ...)) +@@ -231,7 +182,6 @@ + #endif + + #if __GLIBC_USE (DEPRECATED_GETS) +-extern char *__gets_chk (char *__str, size_t) __wur; + extern char *__REDIRECT (__gets_warn, (char *__str), gets) + __wur __warnattr ("please use fgets or getline instead, gets can't " + "specify buffer size"); +@@ -245,9 +195,6 @@ + } + #endif + +-extern char *__fgets_chk (char *__restrict __s, size_t __size, int __n, +- FILE *__restrict __stream) +- __wur __attr_access ((__write_only__, 1, 3)); + extern char *__REDIRECT (__fgets_alias, + (char *__restrict __s, int __n, + FILE *__restrict __stream), fgets) +@@ -261,17 +208,14 @@ + __fortify_function __wur __fortified_attr_access (__write_only__, 1, 2) char * + fgets (char *__restrict __s, int __n, FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize (__s); +- if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz)) ++ size_t __sz = __glibc_objsize (__s); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (char), __sz)) + return __fgets_alias (__s, __n, __stream); +- if (__glibc_unsafe_len (__n, sizeof (char), sz)) +- return __fgets_chk_warn (__s, sz, __n, __stream); +- return __fgets_chk (__s, sz, __n, __stream); ++ if (__glibc_unsafe_len (__n, sizeof (char), __sz)) ++ return __fgets_chk_warn (__s, __sz, __n, __stream); ++ return __fgets_chk (__s, __sz, __n, __stream); + } + +-extern size_t __fread_chk (void *__restrict __ptr, size_t __ptrlen, +- size_t __size, size_t __n, +- FILE *__restrict __stream) __wur; + extern size_t __REDIRECT (__fread_alias, + (void *__restrict __ptr, size_t __size, + size_t __n, FILE *__restrict __stream), +@@ -288,18 +232,15 @@ + fread (void *__restrict __ptr, size_t __size, size_t __n, + FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize0 (__ptr); +- if (__glibc_safe_or_unknown_len (__n, __size, sz)) ++ size_t __sz = __glibc_objsize0 (__ptr); ++ if (__glibc_safe_or_unknown_len (__n, __size, __sz)) + return __fread_alias (__ptr, __size, __n, __stream); +- if (__glibc_unsafe_len (__n, __size, sz)) +- return __fread_chk_warn (__ptr, sz, __size, __n, __stream); +- return __fread_chk (__ptr, sz, __size, __n, __stream); ++ if (__glibc_unsafe_len (__n, __size, __sz)) ++ return __fread_chk_warn (__ptr, __sz, __size, __n, __stream); ++ return __fread_chk (__ptr, __sz, __size, __n, __stream); + } + + #ifdef __USE_GNU +-extern char *__fgets_unlocked_chk (char *__restrict __s, size_t __size, +- int __n, FILE *__restrict __stream) +- __wur __attr_access ((__write_only__, 1, 3)); + extern char *__REDIRECT (__fgets_unlocked_alias, + (char *__restrict __s, int __n, + FILE *__restrict __stream), fgets_unlocked) +@@ -313,20 +254,17 @@ + __fortify_function __wur __fortified_attr_access (__write_only__, 1, 2) char * + fgets_unlocked (char *__restrict __s, int __n, FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize (__s); +- if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz)) ++ size_t __sz = __glibc_objsize (__s); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (char), __sz)) + return __fgets_unlocked_alias (__s, __n, __stream); +- if (__glibc_unsafe_len (__n, sizeof (char), sz)) +- return __fgets_unlocked_chk_warn (__s, sz, __n, __stream); +- return __fgets_unlocked_chk (__s, sz, __n, __stream); ++ if (__glibc_unsafe_len (__n, sizeof (char), __sz)) ++ return __fgets_unlocked_chk_warn (__s, __sz, __n, __stream); ++ return __fgets_unlocked_chk (__s, __sz, __n, __stream); + } + #endif + + #ifdef __USE_MISC + # undef fread_unlocked +-extern size_t __fread_unlocked_chk (void *__restrict __ptr, size_t __ptrlen, +- size_t __size, size_t __n, +- FILE *__restrict __stream) __wur; + extern size_t __REDIRECT (__fread_unlocked_alias, + (void *__restrict __ptr, size_t __size, + size_t __n, FILE *__restrict __stream), +@@ -343,8 +281,8 @@ + fread_unlocked (void *__restrict __ptr, size_t __size, size_t __n, + FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize0 (__ptr); +- if (__glibc_safe_or_unknown_len (__n, __size, sz)) ++ size_t __sz = __glibc_objsize0 (__ptr); ++ if (__glibc_safe_or_unknown_len (__n, __size, __sz)) + { + # ifdef __USE_EXTERN_INLINES + if (__builtin_constant_p (__size) +@@ -369,9 +307,9 @@ + # endif + return __fread_unlocked_alias (__ptr, __size, __n, __stream); + } +- if (__glibc_unsafe_len (__n, __size, sz)) +- return __fread_unlocked_chk_warn (__ptr, sz, __size, __n, __stream); +- return __fread_unlocked_chk (__ptr, sz, __size, __n, __stream); ++ if (__glibc_unsafe_len (__n, __size, __sz)) ++ return __fread_unlocked_chk_warn (__ptr, __sz, __size, __n, __stream); ++ return __fread_unlocked_chk (__ptr, __sz, __size, __n, __stream); + + } + #endif +diff -Nuar a/libio/bug-mmap-fflush.c b/libio/bug-mmap-fflush.c +--- a/libio/bug-mmap-fflush.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/libio/bug-mmap-fflush.c 2025-10-20 21:02:18.000000000 +0300 +@@ -4,6 +4,7 @@ + #include + #include + ++#include + + static char *fname; + +@@ -35,14 +36,16 @@ + char buffer[1024]; + + snprintf (buffer, sizeof (buffer), "echo 'From foo@bar.com' > %s", fname); +- system (buffer); ++ xsystem (buffer); ++ + f = fopen (fname, "r"); + fseek (f, 0, SEEK_END); + o = ftello (f); + fseek (f, 0, SEEK_SET); + fflush (f); + snprintf (buffer, sizeof (buffer), "echo 'From bar@baz.edu' >> %s", fname); +- system (buffer); ++ xsystem (buffer); ++ + fseek (f, o, SEEK_SET); + if (fgets (buffer, 1024, f) == NULL) + exit (1); +diff -Nuar a/libio/genops.c b/libio/genops.c +--- a/libio/genops.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/libio/genops.c 2025-10-20 21:02:20.000000000 +0300 +@@ -635,7 +635,7 @@ + { + int result; + +- if (fp->_IO_read_ptr > fp->_IO_read_base ++ if (fp->_IO_read_ptr > fp->_IO_read_base && !_IO_in_backup (fp) + && (unsigned char)fp->_IO_read_ptr[-1] == (unsigned char)c) + { + fp->_IO_read_ptr--; +@@ -796,6 +796,12 @@ + legacy = 1; + #endif + ++ /* Free up the backup area if it was ever allocated. */ ++ if (_IO_have_backup (fp)) ++ _IO_free_backup_area (fp); ++ if (!legacy && fp->_mode > 0 && _IO_have_wbackup (fp)) ++ _IO_free_wbackup_area (fp); ++ + if (! (fp->_flags & _IO_UNBUFFERED) + /* Iff stream is un-orientated, it wasn't used. */ + && (legacy || fp->_mode != 0)) +diff -Nuar a/libio/libioP.h b/libio/libioP.h +--- a/libio/libioP.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/libio/libioP.h 2025-10-20 21:02:20.000000000 +0300 +@@ -529,8 +529,8 @@ + ((__fp)->_wide_data->_IO_write_base \ + = (__fp)->_wide_data->_IO_write_ptr = __p, \ + (__fp)->_wide_data->_IO_write_end = (__ep)) +-#define _IO_have_backup(fp) ((fp)->_IO_save_base != NULL) +-#define _IO_have_wbackup(fp) ((fp)->_wide_data->_IO_save_base != NULL) ++#define _IO_have_backup(fp) ((fp)->_IO_backup_base != NULL) ++#define _IO_have_wbackup(fp) ((fp)->_wide_data->_IO_backup_base != NULL) + #define _IO_in_backup(fp) ((fp)->_flags & _IO_IN_BACKUP) + #define _IO_have_markers(fp) ((fp)->_markers != NULL) + #define _IO_blen(fp) ((fp)->_IO_buf_end - (fp)->_IO_buf_base) +diff -Nuar a/libio/Makefile b/libio/Makefile +--- a/libio/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/libio/Makefile 2025-10-20 21:02:20.000000000 +0300 +@@ -23,7 +23,7 @@ + include ../Makeconfig + + headers := stdio.h \ +- bits/stdio.h bits/stdio2.h bits/stdio-ldbl.h \ ++ bits/stdio.h bits/stdio2.h bits/stdio2-decl.h bits/stdio-ldbl.h \ + bits/types/FILE.h bits/types/__FILE.h bits/types/struct_FILE.h \ + bits/types/__fpos_t.h bits/types/__fpos64_t.h \ + bits/types/cookie_io_functions_t.h +diff -Nuar a/libio/stdio.h b/libio/stdio.h +--- a/libio/stdio.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/libio/stdio.h 2025-10-20 21:02:20.000000000 +0300 +@@ -885,20 +885,27 @@ + extern int __uflow (FILE *); + extern int __overflow (FILE *, int); + ++#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function ++/* Declare all functions from bits/stdio2-decl.h first. */ ++# include ++#endif ++ ++/* The following headers provide asm redirections. These redirections must ++ appear before the first usage of these functions, e.g. in bits/stdio.h. */ ++#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 ++# include ++#endif ++ + /* If we are compiling with optimizing read this file. It contains + several optimizing inline functions and macros. */ + #ifdef __USE_EXTERN_INLINES + # include + #endif + #if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function ++/* Now include the function definitions and redirects too. */ + # include + #endif + +-#include +-#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 +-# include +-#endif +- + __END_DECLS + + #endif /* included. */ +diff -Nuar a/locale/programs/ld-monetary.c b/locale/programs/ld-monetary.c +--- a/locale/programs/ld-monetary.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/locale/programs/ld-monetary.c 2025-10-20 21:02:20.000000000 +0300 +@@ -196,21 +196,105 @@ + } + } + ++ /* Generally speaking there are 3 standards the define the default, ++ warning, and error behaviour of LC_MONETARY. They are ISO/IEC TR 30112, ++ ISO/IEC 9899:2018 (ISO C17), and POSIX.1-2017. Within 30112 we have the ++ definition of a standard i18n FDCC-set, which for LC_MONETARY has the ++ following default values: ++ int_curr_symbol "" ++ currency_symbol "" ++ mon_decimal_point "" i.e. "," ++ mon_thousand_sep "" ++ mon_grouping "\177" i.e. CHAR_MAX ++ positive_sign "" ++ negative_sign "" i.e. "." ++ int_frac_digits -1 ++ frac_digits -1 ++ p_cs_precedes -1 ++ p_sep_by_space -1 ++ n_cs_precedes -1 ++ n_sep_by_space -1 ++ p_sign_posn -1 ++ n_sign_posn -1 ++ Under 30112 a keyword that is not provided implies an empty string "" ++ for string values or a -1 for integer values, and indicates the value ++ is unspecified with no default implied. No errors are considered. ++ The exception is mon_grouping which is a string with a terminating ++ CHAR_MAX. ++ For POSIX Issue 7 we have: ++ https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html ++ and again values not provided default to "" or -1, and indicate the value ++ is not available to the locale. The exception is mon_grouping which is ++ a string with a terminating CHAR_MAX. For the POSIX locale the values of ++ LC_MONETARY should be: ++ int_curr_symbol "" ++ currency_symbol "" ++ mon_decimal_point "" ++ mon_thousands_sep "" ++ mon_grouping "\177" i.e. CHAR_MAX ++ positive_sign "" ++ negative_sign "" ++ int_frac_digits -1 ++ frac_digits -1 ++ p_cs_precedes -1 ++ p_sep_by_space -1 ++ n_cs_precedes -1 ++ n_sep_by_space -1 ++ p_sign_posn -1 ++ n_sign_posn -1 ++ int_p_cs_precedes -1 ++ int_p_sep_by_space -1 ++ int_n_cs_precedes -1 ++ int_n_sep_by_space -1 ++ int_p_sign_posn -1 ++ int_n_sign_posn -1 ++ Like with 30112, POSIX also considers no error if the keywords are ++ missing, only that if the cateory as a whole is missing the referencing ++ of the category results in unspecified behaviour. ++ For ISO C17 there is no default value provided, but the localeconv ++ specification in 7.11.2.1 admits that members of char * type may point ++ to "" to indicate a value is not available or is of length zero. ++ The exception is decimal_point (not mon_decimal_point) which must be a ++ defined non-empty string. The values of char, which are generally ++ mapped to integer values in 30112 and POSIX, must be non-negative ++ numbers that map to CHAR_MAX when a value is not available in the ++ locale. ++ In ISO C17 for the "C" locale all values are empty strings "", or ++ CHAR_MAX, with the exception of decimal_point which is "." (defined ++ in LC_NUMERIC). ISO C17 makes no exception for mon_grouping like ++ 30112 and POSIX, but a value of "" is functionally equivalent to ++ "\177" since neither defines a grouping (though the latter terminates ++ the grouping). ++ ++ Lastly, we must consider the legacy C/POSIX locale that implemented ++ as a builtin in glibc and wether a default value mapping to the ++ C/POSIX locale may benefit the user from a compatibility perspective. ++ ++ Thus given 30112, POSIX, ISO C, and the builtin C/POSIX locale we ++ need to pick appropriate defaults below. */ ++ ++ /* The members of LC_MONETARY are handled in the order of their definition ++ in locale/categories.def. Please keep them in that order. */ ++ ++ /* The purpose of TEST_ELEM is to define a default value for the fields ++ in the category if the field was not defined in the cateory. If the ++ category was present but we didn't see a definition for the field then ++ we also issue a warning, otherwise the only warning you get is the one ++ earlier when a default category is created (completely missing category). ++ This missing field warning is glibc-specific since no standard requires ++ this warning, but we consider it valuable to print a warning for all ++ missing fields in the category. */ + #define TEST_ELEM(cat, initval) \ + if (monetary->cat == NULL) \ + { \ + if (! nothing) \ +- record_error (0, 0, _("%s: field `%s' not defined"), \ +- "LC_MONETARY", #cat); \ ++ record_warning (_("%s: field `%s' not defined"), \ ++ "LC_MONETARY", #cat); \ + monetary->cat = initval; \ + } + ++ /* Keyword: int_curr_symbol. */ + TEST_ELEM (int_curr_symbol, ""); +- TEST_ELEM (currency_symbol, ""); +- TEST_ELEM (mon_thousands_sep, ""); +- TEST_ELEM (positive_sign, ""); +- TEST_ELEM (negative_sign, ""); +- + /* The international currency symbol must come from ISO 4217. */ + if (monetary->int_curr_symbol != NULL) + { +@@ -247,41 +331,63 @@ + } + } + +- /* The decimal point must not be empty. This is not said explicitly +- in POSIX but ANSI C (ISO/IEC 9899) says in 4.4.2.1 it has to be +- != "". */ ++ /* Keyword: currency_symbol */ ++ TEST_ELEM (currency_symbol, ""); ++ ++ /* Keyword: mon_decimal_point */ ++ /* ISO C17 7.11.2.1.3 explicitly allows mon_decimal_point to be the ++ empty string e.g. "". This indicates the value is not available in the ++ current locale or is of zero length. However, if the value was never ++ defined then we issue a warning and use a glibc-specific default. ISO ++ 30112 in the i18n FDCC-Set uses ",", and POSIX Issue 7 in the ++ POSIX locale uses "". It is specific to glibc that the default is ++ "."; we retain this existing behaviour for backwards compatibility. */ + if (monetary->mon_decimal_point == NULL) + { + if (! nothing) +- record_error (0, 0, _("%s: field `%s' not defined"), +- "LC_MONETARY", "mon_decimal_point"); ++ record_warning (_("%s: field `%s' not defined, using defaults"), ++ "LC_MONETARY", "mon_decimal_point"); + monetary->mon_decimal_point = "."; + monetary->mon_decimal_point_wc = L'.'; + } +- else if (monetary->mon_decimal_point[0] == '\0' && ! be_quiet && ! nothing) ++ ++ /* Keyword: mon_thousands_sep */ ++ if (monetary->mon_thousands_sep == NULL) + { +- record_error (0, 0, _("\ +-%s: value for field `%s' must not be an empty string"), +- "LC_MONETARY", "mon_decimal_point"); ++ if (! nothing) ++ record_warning (_("%s: field `%s' not defined, using defaults"), ++ "LC_MONETARY", "mon_thousands_sep"); ++ monetary->mon_thousands_sep = ""; ++ monetary->mon_thousands_sep_wc = L'\0'; + } + ++ /* Keyword: mon_grouping */ + if (monetary->mon_grouping_len == 0) + { + if (! nothing) +- record_error (0, 0, _("%s: field `%s' not defined"), +- "LC_MONETARY", "mon_grouping"); +- ++ record_warning (_("%s: field `%s' not defined"), ++ "LC_MONETARY", "mon_grouping"); ++ /* Missing entries are given 1 element in their bytearray with ++ a value of CHAR_MAX which indicates that "No further grouping ++ is to be performed" (functionally equivalent to ISO C's "C" ++ locale default of ""). */ + monetary->mon_grouping = (char *) "\177"; + monetary->mon_grouping_len = 1; + } + ++ /* Keyword: positive_sign */ ++ TEST_ELEM (positive_sign, ""); ++ ++ /* Keyword: negative_sign */ ++ TEST_ELEM (negative_sign, ""); ++ + #undef TEST_ELEM + #define TEST_ELEM(cat, min, max, initval) \ + if (monetary->cat == -2) \ + { \ + if (! nothing) \ +- record_error (0, 0, _("%s: field `%s' not defined"), \ +- "LC_MONETARY", #cat); \ ++ record_warning (_("%s: field `%s' not defined"), \ ++ "LC_MONETARY", #cat); \ + monetary->cat = initval; \ + } \ + else if ((monetary->cat < min || monetary->cat > max) \ +@@ -300,16 +406,11 @@ + TEST_ELEM (p_sign_posn, -1, 4, -1); + TEST_ELEM (n_sign_posn, -1, 4, -1); + +- /* The non-POSIX.2 extensions are optional. */ +- if (monetary->duo_int_curr_symbol == NULL) +- monetary->duo_int_curr_symbol = monetary->int_curr_symbol; +- if (monetary->duo_currency_symbol == NULL) +- monetary->duo_currency_symbol = monetary->currency_symbol; +- +- if (monetary->duo_int_frac_digits == -2) +- monetary->duo_int_frac_digits = monetary->int_frac_digits; +- if (monetary->duo_frac_digits == -2) +- monetary->duo_frac_digits = monetary->frac_digits; ++ /* Keyword: crncystr */ ++ monetary->crncystr = (char *) xmalloc (strlen (monetary->currency_symbol) ++ + 2); ++ monetary->crncystr[0] = monetary->p_cs_precedes ? '-' : '+'; ++ strcpy (&monetary->crncystr[1], monetary->currency_symbol); + + #undef TEST_ELEM + #define TEST_ELEM(cat, alt, min, max) \ +@@ -327,6 +428,17 @@ + TEST_ELEM (int_p_sign_posn, p_sign_posn, -1, 4); + TEST_ELEM (int_n_sign_posn, n_sign_posn, -1, 4); + ++ /* The non-POSIX.2 extensions are optional. */ ++ if (monetary->duo_int_curr_symbol == NULL) ++ monetary->duo_int_curr_symbol = monetary->int_curr_symbol; ++ if (monetary->duo_currency_symbol == NULL) ++ monetary->duo_currency_symbol = monetary->currency_symbol; ++ ++ if (monetary->duo_int_frac_digits == -2) ++ monetary->duo_int_frac_digits = monetary->int_frac_digits; ++ if (monetary->duo_frac_digits == -2) ++ monetary->duo_frac_digits = monetary->frac_digits; ++ + TEST_ELEM (duo_p_cs_precedes, p_cs_precedes, -1, 1); + TEST_ELEM (duo_p_sep_by_space, p_sep_by_space, -1, 2); + TEST_ELEM (duo_n_cs_precedes, n_cs_precedes, -1, 1); +@@ -349,17 +461,15 @@ + if (monetary->duo_valid_to == 0) + monetary->duo_valid_to = 99991231; + ++ /* Keyword: conversion_rate */ + if (monetary->conversion_rate[0] == 0) + { + monetary->conversion_rate[0] = 1; + monetary->conversion_rate[1] = 1; + } + +- /* Create the crncystr entry. */ +- monetary->crncystr = (char *) xmalloc (strlen (monetary->currency_symbol) +- + 2); +- monetary->crncystr[0] = monetary->p_cs_precedes ? '-' : '+'; +- strcpy (&monetary->crncystr[1], monetary->currency_symbol); ++ /* A value for monetary-decimal-point-wc was set when ++ monetary_decimal_point was set, likewise for monetary-thousands-sep-wc. */ + } + + +diff -Nuar a/locale/programs/locarchive.c b/locale/programs/locarchive.c +--- a/locale/programs/locarchive.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/locale/programs/locarchive.c 2025-10-20 21:02:20.000000000 +0300 +@@ -1397,7 +1397,7 @@ + { + char fullname[fnamelen + 2 * strlen (d->d_name) + 7]; + +- if (d_type == DT_UNKNOWN) ++ if (d_type == DT_UNKNOWN || d_type == DT_LNK) + { + strcpy (stpcpy (stpcpy (fullname, fname), "/"), + d->d_name); +diff -Nuar a/localedata/gen-locale.sh b/localedata/gen-locale.sh +--- a/localedata/gen-locale.sh 2022-02-03 08:27:54.000000000 +0300 ++++ b/localedata/gen-locale.sh 2025-10-20 21:02:21.000000000 +0300 +@@ -54,8 +54,14 @@ + + echo "Generating locale $locale.$charmap: this might take a while..." + +-# Run quietly and force output. +-flags="--quiet -c" ++# Do not force output with '-c', all locales should compile without ++# warning or errors. There is likewise no need to run quietly with ++# '--quiet' since all locales should compile without additional ++# diagnostics. If there are messages printed then we want to see ++# them, fix them, and the associated error or warning. During ++# development it may be beneficialy to put '--quiet -c' here to allow ++# you to develop in-progress locales. ++flags="" + + # For SJIS the charmap is SHIFT_JIS. We just want the locale to have + # a slightly nicer name instead of using "*.SHIFT_SJIS", but that +diff -Nuar a/localedata/Makefile b/localedata/Makefile +--- a/localedata/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/localedata/Makefile 2025-10-20 21:02:20.000000000 +0300 +@@ -468,11 +468,11 @@ + endef + + $(INSTALL-SUPPORTED-LOCALE-ARCHIVE): install-locales-dir +- @flags="-c"; \ ++ @flags=""; \ + $(build-one-locale) + + $(INSTALL-SUPPORTED-LOCALE-FILES): install-locales-dir +- @flags="-c --no-archive --no-hard-links"; \ ++ @flags="--no-archive --no-hard-links"; \ + $(build-one-locale) + + tst-setlocale-ENV = LC_ALL=ja_JP.EUC-JP +diff -Nuar a/localedata/tst-ctype.c b/localedata/tst-ctype.c +--- a/localedata/tst-ctype.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/localedata/tst-ctype.c 2025-10-20 21:02:21.000000000 +0300 +@@ -21,6 +21,8 @@ + #include + #include + ++#include ++ + + static const char lower[] = "abcdefghijklmnopqrstuvwxyz"; + static const char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +@@ -53,19 +55,11 @@ + #define nclasses (sizeof (classes) / sizeof (classes[0])) + + +-#define FAIL(str, args...) \ +- { \ +- printf (" " str "\n", ##args); \ +- ++errors; \ +- } +- +- + static int + do_test (void) + { + const char *cp; + const char *cp2; +- int errors = 0; + char *inpline = NULL; + size_t inplinelen = 0; + char *resline = NULL; +@@ -394,11 +388,8 @@ + { + if (((__ctype_b[(unsigned int) *inp] & classes[n].mask) != 0) + != (*resp != '0')) +- { +- printf (" is%s('%c' = '\\x%02x') %s true\n", inpline, +- *inp, *inp, *resp == '1' ? "not" : "is"); +- ++errors; +- } ++ FAIL (" is%s('%c' = '\\x%02x') %s true\n", inpline, ++ *inp, *inp, *resp == '1' ? "not" : "is"); + ++inp; + ++resp; + } +@@ -408,11 +399,8 @@ + while (*inp != '\0') + { + if (tolower (*inp) != *resp) +- { +- printf (" tolower('%c' = '\\x%02x') != '%c'\n", +- *inp, *inp, *resp); +- ++errors; +- } ++ FAIL (" tolower('%c' = '\\x%02x') != '%c'\n", ++ *inp, *inp, *resp); + ++inp; + ++resp; + } +@@ -422,11 +410,8 @@ + while (*inp != '\0') + { + if (toupper (*inp) != *resp) +- { +- printf (" toupper('%c' = '\\x%02x') != '%c'\n", +- *inp, *inp, *resp); +- ++errors; +- } ++ FAIL (" toupper('%c' = '\\x%02x') != '%c'\n", ++ *inp, *inp, *resp); + ++inp; + ++resp; + } +@@ -436,14 +421,7 @@ + } + + +- if (errors != 0) +- { +- printf (" %d error%s for `%s' locale\n\n\n", errors, +- errors == 1 ? "" : "s", setlocale (LC_ALL, NULL)); +- return 1; +- } +- +- printf (" No errors for `%s' locale\n\n\n", setlocale (LC_ALL, NULL)); ++ printf ("Completed testing for `%s' locale\n\n\n", setlocale (LC_ALL, NULL)); + return 0; + } + +diff -Nuar a/login/Makefile b/login/Makefile +--- a/login/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/login/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -44,7 +44,9 @@ + vpath %.c programs + + tests := tst-utmp tst-utmpx tst-grantpt tst-ptsname tst-getlogin tst-updwtmpx \ +- tst-pututxline-lockfail tst-pututxline-cache ++ tst-pututxline-lockfail tst-pututxline-cache tst-utmp-size tst-utmp-size-64 ++ ++CFLAGS-tst-utmp-size-64.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 + + # Empty compatibility library for old binaries. + extra-libs := libutil +diff -Nuar a/login/tst-utmp-size-64.c b/login/tst-utmp-size-64.c +--- a/login/tst-utmp-size-64.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/login/tst-utmp-size-64.c 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1,2 @@ ++/* The on-disk layout must not change in time64 mode. */ ++#include "tst-utmp-size.c" +diff -Nuar a/login/tst-utmp-size.c b/login/tst-utmp-size.c +--- a/login/tst-utmp-size.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/login/tst-utmp-size.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,33 @@ ++/* Check expected sizes of struct utmp, struct utmpx, struct lastlog. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ _Static_assert (sizeof (struct utmp) == UTMP_SIZE, "struct utmp size"); ++ _Static_assert (sizeof (struct utmpx) == UTMP_SIZE, "struct utmpx size"); ++ _Static_assert (sizeof (struct lastlog) == LASTLOG_SIZE, ++ "struct lastlog size"); ++ return 0; ++} ++ ++#include +diff -Nuar a/Makeconfig b/Makeconfig +--- a/Makeconfig 2022-02-03 08:27:54.000000000 +0300 ++++ b/Makeconfig 2025-10-20 21:02:20.000000000 +0300 +@@ -43,6 +43,22 @@ + $(error objdir must be defined by the build-directory Makefile) + endif + ++# Did we request 'make -s' run? "yes" or "no". ++# Starting from make-4.4 MAKEFLAGS now contains long ++# options like '--shuffle'. To detect presence of 's' ++# we pick first word with short options. Long options ++# are guaranteed to come after whitespace. We use '-' ++# prefix to always have a word before long options ++# even if no short options were passed. ++# Typical MAKEFLAGS values to watch for: ++# "rs --shuffle=42" (silent) ++# " --shuffle" (not silent) ++ifeq ($(findstring s, $(firstword -$(MAKEFLAGS))),) ++silent-make := no ++else ++silent-make := yes ++endif ++ + # Root of the sysdeps tree. + sysdep_dir := $(..)sysdeps + export sysdep_dir := $(sysdep_dir) +@@ -572,10 +588,13 @@ + # before the expansion of LDLIBS-* variables). + + # Tests use -Wl,-rpath instead of -Wl,-rpath-link for +-# build-hardcoded-path-in-tests. ++# build-hardcoded-path-in-tests. Add -Wl,--disable-new-dtags to force ++# DT_RPATH instead of DT_RUNPATH which only applies to DT_NEEDED entries ++# in the executable and doesn't applies to DT_NEEDED entries in shared ++# libraries which are loaded via DT_NEEDED entries in the executable. + ifeq (yes,$(build-hardcoded-path-in-tests)) +-link-libc-tests-rpath-link = $(link-libc-rpath) +-link-test-modules-rpath-link = $(link-libc-rpath) ++link-libc-tests-rpath-link = $(link-libc-rpath) -Wl,--disable-new-dtags ++link-test-modules-rpath-link = $(link-libc-rpath) -Wl,--disable-new-dtags + else + link-libc-tests-rpath-link = $(link-libc-rpath-link) + link-test-modules-rpath-link = +@@ -871,7 +890,7 @@ + # Use 64 bit time_t support for installed programs + installed-modules = nonlib nscd lddlibc4 ldconfig locale_programs \ + iconvprogs libnss_files libnss_compat libnss_db libnss_hesiod \ +- libutil libpcprofile libSegFault ++ libutil libpcprofile libSegFault libnsl + +extra-time-flags = $(if $(filter $(installed-modules),\ + $(in-module)),-D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64) + +@@ -920,7 +939,7 @@ + # umpteen zillion filenames along with it (we use `...' instead) + # but we don't want this echoing done when the user has said + # he doesn't want to see commands echoed by using -s. +-ifneq "$(findstring s,$(MAKEFLAGS))" "" # if -s ++ifeq ($(silent-make),yes) # if -s + +cmdecho := echo >/dev/null + else # not -s + +cmdecho := echo +diff -Nuar a/Makerules b/Makerules +--- a/Makerules 2022-02-03 08:27:54.000000000 +0300 ++++ b/Makerules 2025-10-20 21:02:20.000000000 +0300 +@@ -802,7 +802,7 @@ + # Maximize efficiency by minimizing the number of rules. + .SUFFIXES: # Clear the suffix list. We don't use suffix rules. + # Don't define any builtin rules. +-MAKEFLAGS := $(MAKEFLAGS)r ++MAKEFLAGS := $(MAKEFLAGS) -r + + # Generic rule for making directories. + %/: +@@ -819,7 +819,7 @@ + .PRECIOUS: $(foreach l,$(libtypes),$(patsubst %,$(common-objpfx)$l,c)) + + # Use the verbose option of ar and tar when not running silently. +-ifeq "$(findstring s,$(MAKEFLAGS))" "" # if not -s ++ifeq ($(silent-make),no) # if not -s + verbose := v + else # -s + verbose := +diff -Nuar a/malloc/arena.c b/malloc/arena.c +--- a/malloc/arena.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/malloc/arena.c 2025-10-20 21:02:21.000000000 +0300 +@@ -937,7 +937,7 @@ + narenas_limit = mp_.arena_max; + else if (narenas > mp_.arena_test) + { +- int n = __get_nprocs_sched (); ++ int n = __get_nprocs (); + + if (n >= 1) + narenas_limit = NARENAS_FROM_NCORES (n); +diff -Nuar a/malloc/malloc.c b/malloc/malloc.c +--- a/malloc/malloc.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/malloc/malloc.c 2025-10-20 21:02:21.000000000 +0300 +@@ -292,19 +292,14 @@ + # define __assert_fail(assertion, file, line, function) \ + __malloc_assert(assertion, file, line, function) + +-extern const char *__progname; +- +-static void ++_Noreturn static void + __malloc_assert (const char *assertion, const char *file, unsigned int line, + const char *function) + { +- (void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n", +- __progname, __progname[0] ? ": " : "", +- file, line, +- function ? function : "", function ? ": " : "", +- assertion); +- fflush (stderr); +- abort (); ++ __libc_message (do_abort, "\ ++Fatal glibc error: malloc assertion failure in %s: %s\n", ++ function, assertion); ++ __builtin_unreachable (); + } + #endif + #endif +diff -Nuar a/malloc/tst-realloc.c b/malloc/tst-realloc.c +--- a/malloc/tst-realloc.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/malloc/tst-realloc.c 2025-10-20 21:02:21.000000000 +0300 +@@ -20,15 +20,7 @@ + #include + #include + #include +- +-static int errors = 0; +- +-static void +-merror (const char *msg) +-{ +- ++errors; +- printf ("Error: %s\n", msg); +-} ++#include + + static int + do_test (void) +@@ -51,11 +43,11 @@ + save = errno; + + if (p != NULL) +- merror ("realloc (NULL, -1) succeeded."); ++ FAIL_EXIT1 ("realloc (NULL, -1) succeeded."); + + /* errno should be set to ENOMEM on failure (POSIX). */ + if (p == NULL && save != ENOMEM) +- merror ("errno is not set correctly"); ++ FAIL_EXIT1 ("errno is not set correctly"); + + errno = 0; + +@@ -64,18 +56,18 @@ + save = errno; + + if (p == NULL) +- merror ("realloc (NULL, 10) failed."); ++ FAIL_EXIT1 ("realloc (NULL, 10) failed."); + + free (p); + + p = calloc (20, 1); + if (p == NULL) +- merror ("calloc (20, 1) failed."); ++ FAIL_EXIT1 ("calloc (20, 1) failed."); + + /* Check increasing size preserves contents (C89). */ + p = realloc (p, 200); + if (p == NULL) +- merror ("realloc (p, 200) failed."); ++ FAIL_EXIT1 ("realloc (p, 200) failed."); + + c = p; + ok = 1; +@@ -87,20 +79,20 @@ + } + + if (ok == 0) +- merror ("first 20 bytes were not cleared"); ++ FAIL_EXIT1 ("first 20 bytes were not cleared"); + + free (p); + + p = realloc (NULL, 100); + if (p == NULL) +- merror ("realloc (NULL, 100) failed."); ++ FAIL_EXIT1 ("realloc (NULL, 100) failed."); + + memset (p, 0xff, 100); + + /* Check decreasing size preserves contents (C89). */ + p = realloc (p, 16); + if (p == NULL) +- merror ("realloc (p, 16) failed."); ++ FAIL_EXIT1 ("realloc (p, 16) failed."); + + c = p; + ok = 1; +@@ -112,7 +104,7 @@ + } + + if (ok == 0) +- merror ("first 16 bytes were not correct"); ++ FAIL_EXIT1 ("first 16 bytes were not correct"); + + /* Check failed realloc leaves original untouched (C89). */ + DIAG_PUSH_NEEDS_COMMENT; +@@ -124,7 +116,7 @@ + c = realloc (p, -1); + DIAG_POP_NEEDS_COMMENT; + if (c != NULL) +- merror ("realloc (p, -1) succeeded."); ++ FAIL_EXIT1 ("realloc (p, -1) succeeded."); + + c = p; + ok = 1; +@@ -136,29 +128,21 @@ + } + + if (ok == 0) +- merror ("first 16 bytes were not correct after failed realloc"); ++ FAIL_EXIT1 ("first 16 bytes were not correct after failed realloc"); + +-#if __GNUC_PREREQ (12, 0) +- /* Ignore a valid warning about using a pointer made indeterminate +- by a prior call to realloc(). */ +- DIAG_IGNORE_NEEDS_COMMENT (12, "-Wuse-after-free"); +-#endif + /* realloc (p, 0) frees p (C89) and returns NULL (glibc). */ + p = realloc (p, 0); +-#if __GNUC_PREREQ (12, 0) +- DIAG_POP_NEEDS_COMMENT; +-#endif + if (p != NULL) +- merror ("realloc (p, 0) returned non-NULL."); ++ FAIL_EXIT1 ("realloc (p, 0) returned non-NULL."); + + /* realloc (NULL, 0) acts like malloc (0) (glibc). */ + p = realloc (NULL, 0); + if (p == NULL) +- merror ("realloc (NULL, 0) returned NULL."); ++ FAIL_EXIT1 ("realloc (NULL, 0) returned NULL."); + + free (p); + +- return errors != 0; ++ return 0; + } + + #define TEST_FUNCTION do_test () +diff -Nuar a/manual/dynlink.texi b/manual/dynlink.texi +--- a/manual/dynlink.texi 2022-02-03 08:27:54.000000000 +0300 ++++ b/manual/dynlink.texi 2025-10-20 21:02:21.000000000 +0300 +@@ -22,6 +22,85 @@ + @Theglibc{} provides various functions for querying information from the + dynamic linker. + ++@deftypefun {int} dlinfo (void *@var{handle}, int @var{request}, void *@var{arg}) ++@safety{@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}} ++@standards{GNU, dlfcn.h} ++This function returns information about @var{handle} in the memory ++location @var{arg}, based on @var{request}. The @var{handle} argument ++must be a pointer returned by @code{dlopen} or @code{dlmopen}; it must ++not have been closed by @code{dlclose}. ++ ++On success, @code{dlinfo} returns 0 for most request types; exceptions ++are noted below. If there is an error, the function returns @math{-1}, ++and @code{dlerror} can be used to obtain a corresponding error message. ++ ++The following operations are defined for use with @var{request}: ++ ++@vtable @code ++@item RTLD_DI_LINKMAP ++The corresponding @code{struct link_map} pointer for @var{handle} is ++written to @code{*@var{arg}}. The @var{arg} argument must be the ++address of an object of type @code{struct link_map *}. ++ ++@item RTLD_DI_LMID ++The namespace identifier of @var{handle} is written to ++@code{*@var{arg}}. The @var{arg} argument must be the address of an ++object of type @code{Lmid_t}. ++ ++@item RTLD_DI_ORIGIN ++The value of the @code{$ORIGIN} dynamic string token for @var{handle} is ++written to the character array starting at @var{arg} as a ++null-terminated string. ++ ++This request type should not be used because it is prone to buffer ++overflows. ++ ++@item RTLD_DI_SERINFO ++@itemx RTLD_DI_SERINFOSIZE ++These requests can be used to obtain search path information for ++@var{handle}. For both requests, @var{arg} must point to a ++@code{Dl_serinfo} object. The @code{RTLD_DI_SERINFOSIZE} request must ++be made first; it updates the @code{dls_size} and @code{dls_cnt} members ++of the @code{Dl_serinfo} object. The caller should then allocate memory ++to store at least @code{dls_size} bytes and pass that buffer to a ++@code{RTLD_DI_SERINFO} request. This second request fills the ++@code{dls_serpath} array. The number of array elements was returned in ++the @code{dls_cnt} member in the initial @code{RTLD_DI_SERINFOSIZE} ++request. The caller is responsible for freeing the allocated buffer. ++ ++This interface is prone to buffer overflows in multi-threaded processes ++because the required size can change between the ++@code{RTLD_DI_SERINFOSIZE} and @code{RTLD_DI_SERINFO} requests. ++ ++@item RTLD_DI_TLS_DATA ++This request writes the address of the TLS block (in the current thread) ++for the shared object identified by @var{handle} to @code{*@var{arg}}. ++The argument @var{arg} must be the address of an object of type ++@code{void *}. A null pointer is written if the object does not have ++any associated TLS block. ++ ++@item RTLD_DI_TLS_MODID ++This request writes the TLS module ID for the shared object @var{handle} ++to @code{*@var{arg}}. The argument @var{arg} must be the address of an ++object of type @code{size_t}. The module ID is zero if the object ++does not have an associated TLS block. ++ ++@item RTLD_DI_PHDR ++This request writes the address of the program header array to ++@code{*@var{arg}}. The argument @var{arg} must be the address of an ++object of type @code{const ElfW(Phdr) *} (that is, ++@code{const Elf32_Phdr *} or @code{const Elf64_Phdr *}, as appropriate ++for the current architecture). For this request, the value returned by ++@code{dlinfo} is the number of program headers in the program header ++array. ++@end vtable ++ ++The @code{dlinfo} function is a GNU extension. ++@end deftypefun ++ ++The remainder of this section documents the @code{_dl_find_object} ++function and supporting types and constants. ++ + @deftp {Data Type} {struct dl_find_object} + @standards{GNU, dlfcn.h} + This structure contains information about a main program or loaded +@@ -130,7 +209,6 @@ + @c dladdr1 + @c dlclose + @c dlerror +-@c dlinfo + @c dlmopen + @c dlopen + @c dlsym +diff -Nuar a/manual/getopt.texi b/manual/getopt.texi +--- a/manual/getopt.texi 2022-02-03 08:27:54.000000000 +0300 ++++ b/manual/getopt.texi 2025-10-20 21:02:21.000000000 +0300 +@@ -250,7 +250,8 @@ + + When @code{getopt_long} encounters a long option, it takes actions based + on the @code{flag} and @code{val} fields of the definition of that +-option. ++option. The option name may be abbreviated as long as the abbreviation is ++unique. + + If @code{flag} is a null pointer, then @code{getopt_long} returns the + contents of @code{val} to indicate which option it found. You should +diff -Nuar a/manual/install.texi b/manual/install.texi +--- a/manual/install.texi 2022-02-03 08:27:54.000000000 +0300 ++++ b/manual/install.texi 2025-10-20 21:02:21.000000000 +0300 +@@ -117,6 +117,12 @@ + usable, but functionality may be lost---for example, you can't build a + shared libc with old binutils. + ++@item --with-default-link ++With @code{--with-default-link}, the build system does not use a custom ++linker script for linking shared objects. The default is ++@code{--without-default-link}, because the custom linker script is ++needed for full RELRO protection. ++ + @item --with-nonshared-cflags=@var{cflags} + Use additional compiler flags @var{cflags} to build the parts of the + library which are always statically linked into applications and +diff -Nuar a/manual/llio.texi b/manual/llio.texi +--- a/manual/llio.texi 2022-02-03 08:27:54.000000000 +0300 ++++ b/manual/llio.texi 2025-10-20 21:02:21.000000000 +0300 +@@ -1339,6 +1339,10 @@ + + @item RWF_APPEND + Per-IO synchronization as if the file was opened with @code{O_APPEND} flag. ++ ++@item RWF_NOAPPEND ++This flag allows an offset to be honored, even if the file was opened with ++@code{O_APPEND} flag. + @end vtable + + When the source file is compiled with @code{_FILE_OFFSET_BITS == 64} the +@@ -1777,7 +1781,7 @@ + + @end deftypefun + +-@deftypefun {void *} mremap (void *@var{address}, size_t @var{length}, size_t @var{new_length}, int @var{flag}) ++@deftypefun {void *} mremap (void *@var{address}, size_t @var{length}, size_t @var{new_length}, int @var{flag}, ... /* void *@var{new_address} */) + @standards{GNU, sys/mman.h} + @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}} + +@@ -1786,12 +1790,40 @@ + in the same @code{mmap} statement. A new mapping with the same + characteristics will be returned with the length @var{new_length}. + +-One option is possible, @code{MREMAP_MAYMOVE}. If it is given in +-@var{flags}, the system may remove the existing mapping and create a new +-one of the desired length in another location. ++Possible flags are ++ ++@table @code ++ ++@item MREMAP_MAYMOVE ++If it is given in @var{flags}, the system may remove the existing mapping ++and create a new one of the desired length in another location. ++ ++@item MREMAP_FIXED ++If it is given in @var{flags}, @code{mremap} accepts a fifth argument, ++@code{void *new_address}, which specifies a page-aligned address to ++which the mapping must be moved. Any previous mapping at the address ++range specified by @var{new_address} and @var{new_size} is unmapped. ++ ++@code{MREMAP_FIXED} must be used together with @code{MREMAP_MAYMOVE}. ++ ++@item MREMAP_DONTUNMAP ++If it is given in @var{flags}, @code{mremap} accepts a fifth argument, ++@code{void *new_address}, which specifies a page-aligned address. Any ++previous mapping at the address range specified by @var{new_address} and ++@var{new_size} is unmapped. If @var{new_address} is @code{NULL}, the ++kernel chooses the page-aligned address at which to create the mapping. ++Otherwise, the kernel takes it as a hint about where to place the mapping. ++The mapping at the address range specified by @var{old_address} and ++@var{old_size} isn't unmapped. ++ ++@code{MREMAP_DONTUNMAP} must be used together with @code{MREMAP_MAYMOVE}. ++@var{old_size} must be the same as @var{new_size}. This flag bit is ++Linux-specific. ++ ++@end table + +-The address of the resulting mapping is returned, or @math{-1}. Possible +-error codes include: ++The address of the resulting mapping is returned, or @code{MAP_FAILED}. ++Possible error codes include: + + @table @code + +@@ -1800,7 +1832,7 @@ + the region covers two or more distinct mappings. + + @item EINVAL +-The address given is misaligned or inappropriate. ++Any arguments are inappropriate, including unknown @var{flags} values. + + @item EAGAIN + The region has pages locked, and if extended it would exceed the +diff -Nuar a/manual/process.texi b/manual/process.texi +--- a/manual/process.texi 2022-02-03 08:27:54.000000000 +0300 ++++ b/manual/process.texi 2025-10-20 21:02:21.000000000 +0300 +@@ -68,8 +68,7 @@ + @c CLEANUP_HANDLER @ascuplugin @ascuheap @acsmem + @c libc_cleanup_region_start @ascuplugin @ascuheap @acsmem + @c pthread_cleanup_push_defer @ascuplugin @ascuheap @acsmem +-@c __pthread_testcancel @ascuplugin @ascuheap @acsmem +-@c CANCEL_ENABLED_AND_CANCELED ok ++@c cancel_enabled_and_canceled @ascuplugin @ascuheap @acsmem + @c do_cancel @ascuplugin @ascuheap @acsmem + @c cancel_handler ok + @c kill syscall ok +diff -Nuar a/manual/stdio.texi b/manual/stdio.texi +--- a/manual/stdio.texi 2022-02-03 08:27:54.000000000 +0300 ++++ b/manual/stdio.texi 2025-10-20 21:02:21.000000000 +0300 +@@ -1474,11 +1474,9 @@ + was just read from the same stream. @Theglibc{} supports this + even on files opened in binary mode, but other systems might not. + +-@Theglibc{} only supports one character of pushback---in other +-words, it does not work to call @code{ungetc} twice without doing input +-in between. Other systems might let you push back multiple characters; +-then reading from the stream retrieves the characters in the reverse +-order that they were pushed. ++@Theglibc{} supports pushing back multiple characters; subsequently ++reading from the stream retrieves the characters in the reverse order ++that they were pushed. + + Pushing back characters doesn't alter the file; only the internal + buffering for the stream is affected. If a file positioning function +diff -Nuar a/manual/threads.texi b/manual/threads.texi +--- a/manual/threads.texi 2022-02-03 08:27:54.000000000 +0300 ++++ b/manual/threads.texi 2025-10-20 21:02:21.000000000 +0300 +@@ -1020,8 +1020,12 @@ + failed or has been disabled) or the size of the restartable sequence + registration. This can be different from the size of @code{struct rseq} + if the kernel has extended the size of the registration. If +-registration is successful, @code{__rseq_size} is at least 32 (the +-initial size of @code{struct rseq}). ++registration is successful, @code{__rseq_size} is at least 20 (the ++initially active size of @code{struct rseq}). ++ ++Previous versions of @theglibc{} set this to 32 even if the kernel only ++supported the initial area of 20 bytes because the value included unused ++padding at the end of the restartable sequence area. + @end deftypevar + + @deftypevar {unsigned int} __rseq_flags +diff -Nuar a/manual/tunables.texi b/manual/tunables.texi +--- a/manual/tunables.texi 2022-02-03 08:27:54.000000000 +0300 ++++ b/manual/tunables.texi 2025-10-20 21:02:21.000000000 +0300 +@@ -47,7 +47,7 @@ + glibc.elision.skip_lock_busy: 3 (min: -2147483648, max: 2147483647) + glibc.malloc.top_pad: 0x0 (min: 0x0, max: 0xffffffffffffffff) + glibc.cpu.x86_rep_stosb_threshold: 0x800 (min: 0x1, max: 0xffffffffffffffff) +-glibc.cpu.x86_non_temporal_threshold: 0xc0000 (min: 0x0, max: 0xffffffffffffffff) ++glibc.cpu.x86_non_temporal_threshold: 0xc0000 (min: 0x4040, max: 0x0fffffffffffffff) + glibc.cpu.x86_shstk: + glibc.cpu.hwcap_mask: 0x6 (min: 0x0, max: 0xffffffffffffffff) + glibc.malloc.mmap_max: 0 (min: -2147483648, max: 2147483647) +@@ -502,7 +502,7 @@ + @deftp Tunable glibc.cpu.name + The @code{glibc.cpu.name=xxx} tunable allows the user to tell @theglibc{} to + assume that the CPU is @code{xxx} where xxx may have one of these values: +-@code{generic}, @code{falkor}, @code{thunderxt88}, @code{thunderx2t99}, ++@code{generic}, @code{thunderxt88}, @code{thunderx2t99}, + @code{thunderx2t99p1}, @code{ares}, @code{emag}, @code{kunpeng}, + @code{a64fx}. + +diff -Nuar a/math/test-tgmath2.c b/math/test-tgmath2.c +--- a/math/test-tgmath2.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/math/test-tgmath2.c 2025-10-20 21:02:21.000000000 +0300 +@@ -24,6 +24,8 @@ + #include + #include + ++#include ++ + //#define DEBUG + + typedef complex float cfloat; +@@ -87,13 +89,6 @@ + int count; + int counts[Tlast][C_last]; + +-#define FAIL(str) \ +- do \ +- { \ +- printf ("%s failure on line %d\n", (str), __LINE__); \ +- result = 1; \ +- } \ +- while (0) + #define TEST_TYPE_ONLY(expr, rettype) \ + do \ + { \ +@@ -133,8 +128,6 @@ + int + test_cos (const int Vint4, const long long int Vllong4) + { +- int result = 0; +- + TEST (cos (vfloat1), float, cos); + TEST (cos (vdouble1), double, cos); + TEST (cos (vldouble1), ldouble, cos); +@@ -152,7 +145,7 @@ + TEST (cos (Vcdouble1), cdouble, cos); + TEST (cos (Vcldouble1), cldouble, cos); + +- return result; ++ return 0; + } + + int +diff -Nuar a/misc/bits/syslog.h b/misc/bits/syslog.h +--- a/misc/bits/syslog.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/misc/bits/syslog.h 2025-10-20 21:02:21.000000000 +0300 +@@ -24,6 +24,20 @@ + extern void __syslog_chk (int __pri, int __flag, const char *__fmt, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); + ++#ifdef __USE_MISC ++extern void __vsyslog_chk (int __pri, int __flag, const char *__fmt, ++ __gnuc_va_list __ap) ++ __attribute__ ((__format__ (__printf__, 3, 0))); ++#endif ++ ++#include ++#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 ++# include ++#endif ++ ++/* The following functions must be used only after applying all asm ++ redirections, e.g. long double asm redirections. */ ++ + #ifdef __va_arg_pack + __fortify_function void + syslog (int __pri, const char *__fmt, ...) +@@ -37,10 +51,6 @@ + + + #ifdef __USE_MISC +-extern void __vsyslog_chk (int __pri, int __flag, const char *__fmt, +- __gnuc_va_list __ap) +- __attribute__ ((__format__ (__printf__, 3, 0))); +- + __fortify_function void + vsyslog (int __pri, const char *__fmt, __gnuc_va_list __ap) + { +diff -Nuar a/misc/daemon.c b/misc/daemon.c +--- a/misc/daemon.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/misc/daemon.c 2025-10-20 21:02:21.000000000 +0300 +@@ -61,11 +61,10 @@ + (void)__chdir("/"); + + if (!noclose) { +- struct stat64 st; ++ struct __stat64_t64 st; + + if ((fd = __open_nocancel(_PATH_DEVNULL, O_RDWR, 0)) != -1 +- && (__builtin_expect (__fstat64 (fd, &st), 0) +- == 0)) { ++ && __glibc_likely (__fstat64_time64 (fd, &st) == 0)) { + if (__builtin_expect (S_ISCHR (st.st_mode), 1) != 0 + #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR + && (st.st_rdev +diff -Nuar a/misc/getsysstats.c b/misc/getsysstats.c +--- a/misc/getsysstats.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/misc/getsysstats.c 2025-10-20 21:02:21.000000000 +0300 +@@ -44,12 +44,6 @@ + link_warning (get_nprocs, "warning: get_nprocs will always return 1") + + +-int +-__get_nprocs_sched (void) +-{ +- return 1; +-} +- + long int + __get_phys_pages (void) + { +diff -Nuar a/misc/getusershell.c b/misc/getusershell.c +--- a/misc/getusershell.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/misc/getusershell.c 2025-10-20 21:02:18.000000000 +0300 +@@ -97,7 +97,7 @@ + { + char **sp, *cp; + FILE *fp; +- struct stat64 statb; ++ struct __stat64_t64 statb; + size_t flen; + + free(shells); +@@ -106,7 +106,7 @@ + strings = NULL; + if ((fp = fopen(_PATH_SHELLS, "rce")) == NULL) + goto init_okshells_noclose; +- if (__fstat64(fileno(fp), &statb) == -1) { ++ if (__fstat64_time64(fileno(fp), &statb) == -1) { + init_okshells: + (void)fclose(fp); + init_okshells_noclose: +diff -Nuar a/misc/Makefile b/misc/Makefile +--- a/misc/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/misc/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -90,7 +90,7 @@ + tst-preadvwritev2 tst-preadvwritev64v2 tst-warn-wide \ + tst-ldbl-warn tst-ldbl-error tst-dbl-efgcvt tst-ldbl-efgcvt \ + tst-mntent-autofs tst-syscalls tst-mntent-escape tst-select \ +- tst-ioctl ++ tst-ioctl tst-mremap1 tst-mremap2 + + tests-time64 := \ + tst-select-time64 \ +diff -Nuar a/misc/sys/cdefs.h b/misc/sys/cdefs.h +--- a/misc/sys/cdefs.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/misc/sys/cdefs.h 2025-10-20 21:02:21.000000000 +0300 +@@ -152,6 +152,7 @@ + # define __glibc_objsize(__o) __bos (__o) + #endif + ++#if __USE_FORTIFY_LEVEL > 0 + /* Compile time conditions to choose between the regular, _chk and _chk_warn + variants. These conditions should get evaluated to constant and optimized + away. */ +@@ -162,13 +163,13 @@ + || (__builtin_constant_p (__l) && (__l) > 0)) + + /* Length is known to be safe at compile time if the __L * __S <= __OBJSZ +- condition can be folded to a constant and if it is true. The -1 check is +- redundant because since it implies that __glibc_safe_len_cond is true. */ ++ condition can be folded to a constant and if it is true, or unknown (-1) */ + #define __glibc_safe_or_unknown_len(__l, __s, __osz) \ +- (__glibc_unsigned_or_positive (__l) \ +- && __builtin_constant_p (__glibc_safe_len_cond ((__SIZE_TYPE__) (__l), \ +- __s, __osz)) \ +- && __glibc_safe_len_cond ((__SIZE_TYPE__) (__l), __s, __osz)) ++ ((__builtin_constant_p (__osz) && (__osz) == (__SIZE_TYPE__) -1) \ ++ || (__glibc_unsigned_or_positive (__l) \ ++ && __builtin_constant_p (__glibc_safe_len_cond ((__SIZE_TYPE__) (__l), \ ++ (__s), (__osz))) \ ++ && __glibc_safe_len_cond ((__SIZE_TYPE__) (__l), (__s), (__osz)))) + + /* Conversely, we know at compile time that the length is unsafe if the + __L * __S <= __OBJSZ condition can be folded to a constant and if it is +@@ -187,7 +188,7 @@ + ? __ ## f ## _alias (__VA_ARGS__) \ + : (__glibc_unsafe_len (__l, __s, __osz) \ + ? __ ## f ## _chk_warn (__VA_ARGS__, __osz) \ +- : __ ## f ## _chk (__VA_ARGS__, __osz))) \ ++ : __ ## f ## _chk (__VA_ARGS__, __osz))) + + /* Fortify function f, where object size argument passed to f is the number of + elements and not total size. */ +@@ -197,7 +198,8 @@ + ? __ ## f ## _alias (__VA_ARGS__) \ + : (__glibc_unsafe_len (__l, __s, __osz) \ + ? __ ## f ## _chk_warn (__VA_ARGS__, (__osz) / (__s)) \ +- : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s)))) \ ++ : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s)))) ++#endif + + #if __GNUC_PREREQ (4,3) + # define __warnattr(msg) __attribute__((__warning__ (msg))) +diff -Nuar a/misc/sys/syslog.h b/misc/sys/syslog.h +--- a/misc/sys/syslog.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/misc/sys/syslog.h 2025-10-20 21:02:21.000000000 +0300 +@@ -205,11 +205,11 @@ + /* Define some macros helping to catch buffer overflows. */ + #if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function + # include +-#endif +- +-#include +-#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 +-# include ++#else ++# include ++# if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 ++# include ++# endif + #endif + + __END_DECLS +diff -Nuar a/misc/tst-mremap1.c b/misc/tst-mremap1.c +--- a/misc/tst-mremap1.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/misc/tst-mremap1.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,46 @@ ++/* Test mremap with MREMAP_MAYMOVE. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ size_t old_size = getpagesize (); ++ char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS, -1); ++ old_addr[0] = 1; ++ old_addr[old_size - 1] = 2; ++ ++ /* Test MREMAP_MAYMOVE. */ ++ size_t new_size = old_size + old_size; ++ char *new_addr = mremap (old_addr, old_size, new_size, MREMAP_MAYMOVE); ++ TEST_VERIFY_EXIT (new_addr != MAP_FAILED); ++ new_addr[0] = 1; ++ new_addr[new_size - 1] = 2; ++ xmunmap (new_addr, new_size); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/misc/tst-mremap2.c b/misc/tst-mremap2.c +--- a/misc/tst-mremap2.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/misc/tst-mremap2.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,54 @@ ++/* Test mremap with MREMAP_FIXED. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ size_t old_size = getpagesize (); ++ size_t new_size = old_size + old_size; ++ char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS, -1); ++ old_addr[0] = 1; ++ old_addr[old_size - 1] = 2; ++ ++ char *fixed_addr = xmmap (NULL, new_size, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS, -1); ++ fixed_addr[0] = 1; ++ fixed_addr[new_size - 1] = 2; ++ ++ /* Test MREMAP_FIXED. */ ++ char *new_addr = mremap (old_addr, old_size, new_size, ++ MREMAP_FIXED | MREMAP_MAYMOVE, ++ fixed_addr); ++ if (new_addr == MAP_FAILED) ++ return mremap_failure_exit (errno); ++ new_addr[0] = 1; ++ new_addr[new_size - 1] = 2; ++ xmunmap (new_addr, new_size); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/misc/tst-preadvwritev2-common.c b/misc/tst-preadvwritev2-common.c +--- a/misc/tst-preadvwritev2-common.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/misc/tst-preadvwritev2-common.c 2025-10-20 21:02:21.000000000 +0300 +@@ -34,8 +34,11 @@ + #ifndef RWF_APPEND + # define RWF_APPEND 0 + #endif ++#ifndef RWF_NOAPPEND ++# define RWF_NOAPPEND 0 ++#endif + #define RWF_SUPPORTED (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT \ +- | RWF_APPEND) ++ | RWF_APPEND | RWF_NOAPPEND) + + /* Generic uio_lim.h does not define IOV_MAX. */ + #ifndef IOV_MAX +diff -Nuar a/NEWS b/NEWS +--- a/NEWS 2022-02-03 08:27:54.000000000 +0300 ++++ b/NEWS 2025-10-20 21:02:20.000000000 +0300 +@@ -5,6 +5,132 @@ + Please send GNU C library bug reports via + using `glibc' in the "product" field. + ++Version 2.35.1 ++ ++Major new features: ++ ++* The getent tool now supports the --no-addrconfig option. The output of ++ getent with --no-addrconfig may contain addresses of families not ++ configured on the current host i.e. as-if you had not passed ++ AI_ADDRCONFIG to getaddrinfo calls. ++ ++Deprecated and removed features, and other changes affecting compatibility: ++ ++* __rseq_size now denotes the size of the active rseq area (20 bytes ++ initially), not the size of struct rseq (32 bytes initially). ++ ++The following bugs are resolved with this release: ++ ++ [12154] Do not fail DNS resolution for CNAMEs which are not host names ++ [20975] Deferred cancellation triggers in __check_pf and looses lock leading to deadlock ++ [24816] Fix tst-nss-files-hosts-long on single-stack hosts ++ [25812] Libio vtable protection is sometimes only partially enforced ++ [27576] gmon: improve mcount overflow handling ++ [27821] ungetc: Fix backup buffer leak on program exit ++ [28815] realpath should not copy to resolved buffer on error ++ [28838] FAIL: elf/tst-p_align3 ++ [28846] CMSG_NXTHDR may trigger -Wstrict-overflow warning ++ [28850] linux: __get_nprocs_sched reads uninitialized memory from the stack ++ [28853] libc: tst-spawn6 changes current foreground process group ++ (breaks test isolation) ++ [28857] libc: FAIL: elf/tst-audit24a ++ [28860] build: --enable-kernel=5.1.0 build fails because of missing ++ __convert_scm_timestamps ++ [28865] linux: _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN are inaccurate ++ without /sys and /proc ++ [28868] dynamic-link: Dynamic loader DFS algorithm segfaults on ++ missing libraries ++ [28896] strncmp-avx2-rtm and wcsncmp-avx2-rtm fallback on non-rtm ++ variants when avoiding overflow ++ [28937] New DSO dependency sorter does not put new map first if in a cycle ++ [28953] nss: Protect against errno changes in function lookup ++ [28996] realpath fails to copy partial result on ENOENT and EACCES ++ [29029] nptl: poll() spuriously returns EINTR during thread ++ cancellation and with cancellation disabled ++ [29039] Corrupt DTV after reuse of a TLS module ID following dlclose with unused TLS ++ [29062] elf: Fix memory leak in _dl_find_object_update ++ [29078] functions unusable during early auditing ++ [29097] time: fchmodat does not handle 64 bit time_t for ++ AT_SYMLINK_NOFOLLOW ++ [29109] libc: posix_spawn() always returns 1 (EPERM) on clone() ++ failure ++ [29165] libc: [Regression] broken argv adjustment ++ [29187] dynamic-link: [regression] broken argv adjustment for nios2 ++ [29203] libc: daemon is not y2038 aware ++ [29204] libc: getusershell is not 2038 aware ++ [29207] libc: posix_fallocate fallback implementation is not y2038 ++ [29208] libc: fpathconf(_PC_ASYNC_IO) is not y2038 aware ++ [29209] libc: isfdtype is not y2038 aware ++ [29210] network: ruserpass is not y2038 aware ++ [29211] libc: __open_catalog is not y2038 aware ++ [29213] libc: gconv_parseconfdir is not y2038 aware ++ [29214] nptl: pthread_setcanceltype fails to set type ++ [29225] network: Mistyped define statement in socket/sys/socket.h in ++ line 184 ++ [29305] Conserve NSS buffer space during DNS packet parsing ++ [29402] nscd: nscd: No such file or directory ++ [29415] nscd: Fix netlink cache invalidation if epoll is used ++ [29444] gmon: Fix allocated buffer overflow (bug 29444) ++ [29446] _dlopen now ignores dl_caller argument in static mode ++ [29490] alpha: New __brk_call implementation is broken ++ [29528] elf: Call __libc_early_init for reused namespaces ++ [29537] libc: [2.34 regression]: Alignment issue on m68k when using ++ [29583] Use 64-bit interfaces in gconv_parseconfdir ++ [29600] Do not completely clear reused namespace in dlmopen ++ [29657] libc: Incorrect struct stat for 64-bit time on linux/generic ++ platforms ++ [29730] broken y2038 support in fstatat on MIPS N64 ++ [29771] Restore IPC_64 support in sysvipc *ctl functions ++ [29776] elf/tst-tlsopt-powerpc fails when compiled with -mcpu=power10 ++ [29951] time: Set daylight to 1 for matching DST/offset change ++ [29953] x86: Check minimum/maximum of non_temporal_threshold ++ [30053] time: strftime %s returns -1 after 2038 on 32 bits systems ++ [30081] resolv: Do not wait for non-existing second DNS response after error ++ [30101] gmon: fix memory corruption issues ++ [30151] gshadow: Matching sgetsgent, sgetsgent_r ERANGE handling ++ [30163] posix: Fix system blocks SIGCHLD erroneously ++ [30305] x86_64: Fix asm constraints in feraiseexcept ++ [30477] libc: [RISCV]: time64 does not work on riscv32 ++ [30515] _dl_find_object incorrectly returns 1 during early startup ++ [30745] Slight bug in cache info codes for x86 ++ [30804] F_GETLK, F_SETLK, and F_SETLKW value change for powerpc64 with ++ -D_FILE_OFFSET_BITS=64 ++ [30843] potential use-after-free in getcanonname (CVE-2023-4806) ++ [31476] resolv: Track single-request fallback via _res._flags ++ [31184] FAIL: elf/tst-tlsgap ++ [31185] Incorrect thread point access in _dl_tlsdesc_undefweak and _dl_tlsdesc_dynamic ++ [31890] resolv: Allow short error responses to match any DNS query ++ [31943] _dl_find_object can fail if ld.so contains gaps between load segments ++ [31965] rseq extension mechanism does not work as intended ++ [31968] mremap implementation in C does not handle arguments correctly ++ [32052] Name space violation in fortify wrappers ++ [32137] libio: Attempt wide backup free only for non-legacy code ++ [32470] x86: Avoid integer truncation with large cache sizes ++ [32582] Fix underallocation of abort_msg_s struct (CVE-2025-0395) ++ [32987] elf: Fix subprocess status handling for tst-dlopen-sgid ++ [33185] Fix double-free after allocation failure in regcomp ++ ++Security related changes: ++ ++ CVE-2023-4806: When an NSS plugin only implements the ++ _gethostbyname2_r and _getcanonname_r callbacks, getaddrinfo could use ++ memory that was freed during buffer resizing, potentially causing a ++ crash or read or write to arbitrary memory. ++ ++ CVE-2023-5156: The fix for CVE-2023-4806 introduced a memory leak when ++ an application calls getaddrinfo for AF_INET6 with AI_CANONNAME, ++ AI_ALL and AI_V4MAPPED flags set. ++ ++ CVE-2023-4911: If a tunable of the form NAME=NAME=VAL is passed in the ++ environment of a setuid program and NAME is valid, it may result in a ++ buffer overflow, which could be exploited to achieve escalated ++ privileges. This flaw was introduced in glibc 2.34. ++ ++ CVE-2025-0395: When the assert() function fails, it does not allocate ++ enough space for the assertion failure message string and size ++ information, which may lead to a buffer overflow if the message string ++ size aligns to page size. ++ + Version 2.35 + + Major new features: +@@ -139,6 +265,9 @@ + fortification balanced against additional runtime cost (checking non-constant + bounds). + ++* The audit libraries will avoid unnecessary slowdown if it is not required ++ PLT tracking (by not implementing the la_pltenter or la_pltexit callbacks). ++ + Deprecated and removed features, and other changes affecting compatibility: + + * On x86-64, the LD_PREFER_MAP_32BIT_EXEC environment variable support +@@ -335,6 +464,8 @@ + [28837] libc: FAIL: socket/tst-socket-timestamp-compat + [28847] locale: Empty mon_decimal_point in LC_MONETARY results in non- + empty mon_decimal_point_wc ++ [29069] libc: fstatat64_time64_statx wrapper broken on MIPS N32 with ++ -D_FILE_OFFSET_BITS=64 and -D_TIME_BITS=64 + + + Version 2.34 +@@ -431,9 +562,6 @@ + execute programs that do not have any dynamic dependency (that is, + they are statically linked). This feature is Linux-specific. + +-* The audit libraries will avoid unnecessary slowdown if it is not required +- PLT tracking (by not implementing the la_pltenter or la_pltexit callbacks). +- + Deprecated and removed features, and other changes affecting compatibility: + + * The function pthread_mutex_consistent_np has been deprecated; programs +diff -Nuar a/nis/nis_call.c b/nis/nis_call.c +--- a/nis/nis_call.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nis/nis_call.c 2025-10-20 21:02:21.000000000 +0300 +@@ -574,7 +574,7 @@ + unsigned int size; + unsigned int server_used; + unsigned int current_ep; +- __time64_t expires; ++ time_t expires; + char name[]; + } *nis_server_cache[16]; + static time_t nis_cold_start_mtime; +@@ -583,7 +583,7 @@ + static directory_obj * + nis_server_cache_search (const_nis_name name, int search_parent, + unsigned int *server_used, unsigned int *current_ep, +- struct __timespec64 *now) ++ struct timespec *now) + { + directory_obj *ret = NULL; + int i; +@@ -641,7 +641,7 @@ + static void + nis_server_cache_add (const_nis_name name, int search_parent, + directory_obj *dir, unsigned int server_used, +- unsigned int current_ep, struct __timespec64 *now) ++ unsigned int current_ep, struct timespec *now) + { + struct nis_server_cache **loc; + struct nis_server_cache *new; +@@ -707,7 +707,7 @@ + nis_error result = NIS_SUCCESS; + nis_error status; + directory_obj *obj; +- struct __timespec64 ts; ++ struct timespec ts; + unsigned int server_used = ~0; + unsigned int current_ep = ~0; + +@@ -717,7 +717,7 @@ + if (*dir != NULL) + return NIS_SUCCESS; + +- __clock_gettime64 (CLOCK_REALTIME, &ts); ++ clock_gettime (CLOCK_REALTIME, &ts); + + if ((flags & NO_CACHE) == 0) + *dir = nis_server_cache_search (name, search_parent, &server_used, +diff -Nuar a/nptl/allocatestack.c b/nptl/allocatestack.c +--- a/nptl/allocatestack.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/allocatestack.c 2025-10-20 21:02:21.000000000 +0300 +@@ -119,8 +119,6 @@ + + /* Cancellation handling is back to the default. */ + result->cancelhandling = 0; +- result->cancelstate = PTHREAD_CANCEL_ENABLE; +- result->canceltype = PTHREAD_CANCEL_DEFERRED; + result->cleanup = NULL; + result->setup_failed = 0; + +diff -Nuar a/nptl/cancellation.c b/nptl/cancellation.c +--- a/nptl/cancellation.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/cancellation.c 2025-10-20 21:02:21.000000000 +0300 +@@ -30,19 +30,26 @@ + __pthread_enable_asynccancel (void) + { + struct pthread *self = THREAD_SELF; ++ int oldval = atomic_load_relaxed (&self->cancelhandling); + +- int oldval = THREAD_GETMEM (self, canceltype); +- THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_ASYNCHRONOUS); ++ while (1) ++ { ++ int newval = oldval | CANCELTYPE_BITMASK; + +- int ch = THREAD_GETMEM (self, cancelhandling); ++ if (newval == oldval) ++ break; + +- if (self->cancelstate == PTHREAD_CANCEL_ENABLE +- && (ch & CANCELED_BITMASK) +- && !(ch & EXITING_BITMASK) +- && !(ch & TERMINATED_BITMASK)) +- { +- THREAD_SETMEM (self, result, PTHREAD_CANCELED); +- __do_cancel (); ++ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling, ++ &oldval, newval)) ++ { ++ if (cancel_enabled_and_canceled_and_async (newval)) ++ { ++ self->result = PTHREAD_CANCELED; ++ __do_cancel (); ++ } ++ ++ break; ++ } + } + + return oldval; +@@ -56,10 +63,29 @@ + { + /* If asynchronous cancellation was enabled before we do not have + anything to do. */ +- if (oldtype == PTHREAD_CANCEL_ASYNCHRONOUS) ++ if (oldtype & CANCELTYPE_BITMASK) + return; + + struct pthread *self = THREAD_SELF; +- self->canceltype = PTHREAD_CANCEL_DEFERRED; ++ int newval; ++ int oldval = atomic_load_relaxed (&self->cancelhandling); ++ do ++ { ++ newval = oldval & ~CANCELTYPE_BITMASK; ++ } ++ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling, ++ &oldval, newval)); ++ ++ /* We cannot return when we are being canceled. Upon return the ++ thread might be things which would have to be undone. The ++ following loop should loop until the cancellation signal is ++ delivered. */ ++ while (__glibc_unlikely ((newval & (CANCELING_BITMASK | CANCELED_BITMASK)) ++ == CANCELING_BITMASK)) ++ { ++ futex_wait_simple ((unsigned int *) &self->cancelhandling, newval, ++ FUTEX_PRIVATE); ++ newval = atomic_load_relaxed (&self->cancelhandling); ++ } + } + libc_hidden_def (__pthread_disable_asynccancel) +diff -Nuar a/nptl/cleanup_defer.c b/nptl/cleanup_defer.c +--- a/nptl/cleanup_defer.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/cleanup_defer.c 2025-10-20 21:02:21.000000000 +0300 +@@ -30,9 +30,22 @@ + ibuf->priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf); + ibuf->priv.data.cleanup = THREAD_GETMEM (self, cleanup); + +- /* Disable asynchronous cancellation for now. */ +- ibuf->priv.data.canceltype = THREAD_GETMEM (self, canceltype); +- THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_DEFERRED); ++ int cancelhandling = atomic_load_relaxed (&self->cancelhandling); ++ if (__glibc_unlikely (cancelhandling & CANCELTYPE_BITMASK)) ++ { ++ int newval; ++ do ++ { ++ newval = cancelhandling & ~CANCELTYPE_BITMASK; ++ } ++ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling, ++ &cancelhandling, ++ newval)); ++ } ++ ++ ibuf->priv.data.canceltype = (cancelhandling & CANCELTYPE_BITMASK ++ ? PTHREAD_CANCEL_ASYNCHRONOUS ++ : PTHREAD_CANCEL_DEFERRED); + + /* Store the new cleanup handler info. */ + THREAD_SETMEM (self, cleanup_jmp_buf, (struct pthread_unwind_buf *) buf); +@@ -54,9 +67,26 @@ + + THREAD_SETMEM (self, cleanup_jmp_buf, ibuf->priv.data.prev); + +- THREAD_SETMEM (self, canceltype, ibuf->priv.data.canceltype); +- if (ibuf->priv.data.canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) +- __pthread_testcancel (); ++ if (ibuf->priv.data.canceltype == PTHREAD_CANCEL_DEFERRED) ++ return; ++ ++ int cancelhandling = atomic_load_relaxed (&self->cancelhandling); ++ if ((cancelhandling & CANCELTYPE_BITMASK) == 0) ++ { ++ int newval; ++ do ++ { ++ newval = cancelhandling | CANCELTYPE_BITMASK; ++ } ++ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling, ++ &cancelhandling, newval)); ++ ++ if (cancel_enabled_and_canceled (cancelhandling)) ++ { ++ self->result = PTHREAD_CANCELED; ++ __do_cancel (); ++ } ++ } + } + versioned_symbol (libc, ___pthread_unregister_cancel_restore, + __pthread_unregister_cancel_restore, GLIBC_2_34); +diff -Nuar a/nptl/descr.h b/nptl/descr.h +--- a/nptl/descr.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/descr.h 2025-10-20 21:02:21.000000000 +0300 +@@ -34,7 +34,6 @@ + #include + #include + #include +-#include + + #ifndef TCB_ALIGNMENT + # define TCB_ALIGNMENT 32 +@@ -279,18 +278,27 @@ + + /* Flags determining processing of cancellation. */ + int cancelhandling; ++ /* Bit set if cancellation is disabled. */ ++#define CANCELSTATE_BIT 0 ++#define CANCELSTATE_BITMASK (1 << CANCELSTATE_BIT) ++ /* Bit set if asynchronous cancellation mode is selected. */ ++#define CANCELTYPE_BIT 1 ++#define CANCELTYPE_BITMASK (1 << CANCELTYPE_BIT) ++ /* Bit set if canceling has been initiated. */ ++#define CANCELING_BIT 2 ++#define CANCELING_BITMASK (1 << CANCELING_BIT) + /* Bit set if canceled. */ + #define CANCELED_BIT 3 +-#define CANCELED_BITMASK (0x01 << CANCELED_BIT) ++#define CANCELED_BITMASK (1 << CANCELED_BIT) + /* Bit set if thread is exiting. */ + #define EXITING_BIT 4 +-#define EXITING_BITMASK (0x01 << EXITING_BIT) ++#define EXITING_BITMASK (1 << EXITING_BIT) + /* Bit set if thread terminated and TCB is freed. */ + #define TERMINATED_BIT 5 +-#define TERMINATED_BITMASK (0x01 << TERMINATED_BIT) ++#define TERMINATED_BITMASK (1 << TERMINATED_BIT) + /* Bit set if thread is supposed to change XID. */ + #define SETXID_BIT 6 +-#define SETXID_BITMASK (0x01 << SETXID_BIT) ++#define SETXID_BITMASK (1 << SETXID_BIT) + + /* Flags. Including those copied from the thread attribute. */ + int flags; +@@ -390,14 +398,6 @@ + /* Indicates whether is a C11 thread created by thrd_creat. */ + bool c11; + +- /* Thread cancel state (PTHREAD_CANCEL_ENABLE or +- PTHREAD_CANCEL_DISABLE). */ +- unsigned char cancelstate; +- +- /* Thread cancel type (PTHREAD_CANCEL_DEFERRED or +- PTHREAD_CANCEL_ASYNCHRONOUS). */ +- unsigned char canceltype; +- + /* Used in __pthread_kill_internal to detected a thread that has + exited or is about to exit. exit_lock must only be acquired + after blocking signals. */ +@@ -407,16 +407,45 @@ + /* Used on strsignal. */ + struct tls_internal_t tls_state; + +- /* rseq area registered with the kernel. */ +- struct rseq rseq_area; +- +- /* This member must be last. */ +- char end_padding[]; ++ /* rseq area registered with the kernel. Use a custom definition ++ here to isolate from kernel struct rseq changes. The ++ implementation of sched_getcpu needs acccess to the cpu_id field; ++ the other fields are unused and not included here. */ ++ union ++ { ++ struct ++ { ++ uint32_t cpu_id_start; ++ uint32_t cpu_id; ++ uint64_t rseq_cs; ++ uint32_t flags; ++ }; ++ char pad[32]; /* Original rseq area size. */ ++ } rseq_area __attribute__ ((aligned (32))); + ++ /* Amount of end padding, if any, in this structure. ++ This definition relies on rseq_area being last. */ + #define PTHREAD_STRUCT_END_PADDING \ +- (sizeof (struct pthread) - offsetof (struct pthread, end_padding)) ++ (sizeof (struct pthread) - offsetof (struct pthread, rseq_area) \ ++ + sizeof ((struct pthread) {}.rseq_area)) + } __attribute ((aligned (TCB_ALIGNMENT))); + ++static inline bool ++cancel_enabled_and_canceled (int value) ++{ ++ return (value & (CANCELSTATE_BITMASK | CANCELED_BITMASK | EXITING_BITMASK ++ | TERMINATED_BITMASK)) ++ == CANCELED_BITMASK; ++} ++ ++static inline bool ++cancel_enabled_and_canceled_and_async (int value) ++{ ++ return ((value) & (CANCELSTATE_BITMASK | CANCELTYPE_BITMASK | CANCELED_BITMASK ++ | EXITING_BITMASK | TERMINATED_BITMASK)) ++ == (CANCELTYPE_BITMASK | CANCELED_BITMASK); ++} ++ + /* This yields the pointer that TLS support code calls the thread pointer. */ + #if TLS_TCB_AT_TP + # define TLS_TPADJ(pd) (pd) +diff -Nuar a/nptl/libc-cleanup.c b/nptl/libc-cleanup.c +--- a/nptl/libc-cleanup.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/libc-cleanup.c 2025-10-20 21:02:21.000000000 +0300 +@@ -26,9 +26,24 @@ + + buffer->__prev = THREAD_GETMEM (self, cleanup); + ++ int cancelhandling = atomic_load_relaxed (&self->cancelhandling); ++ + /* Disable asynchronous cancellation for now. */ +- buffer->__canceltype = THREAD_GETMEM (self, canceltype); +- THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_DEFERRED); ++ if (__glibc_unlikely (cancelhandling & CANCELTYPE_BITMASK)) ++ { ++ int newval; ++ do ++ { ++ newval = cancelhandling & ~CANCELTYPE_BITMASK; ++ } ++ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling, ++ &cancelhandling, ++ newval)); ++ } ++ ++ buffer->__canceltype = (cancelhandling & CANCELTYPE_BITMASK ++ ? PTHREAD_CANCEL_ASYNCHRONOUS ++ : PTHREAD_CANCEL_DEFERRED); + + THREAD_SETMEM (self, cleanup, buffer); + } +@@ -41,8 +56,23 @@ + + THREAD_SETMEM (self, cleanup, buffer->__prev); + +- THREAD_SETMEM (self, canceltype, buffer->__canceltype); +- if (buffer->__canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) +- __pthread_testcancel (); ++ int cancelhandling = atomic_load_relaxed (&self->cancelhandling); ++ if (buffer->__canceltype != PTHREAD_CANCEL_DEFERRED ++ && (cancelhandling & CANCELTYPE_BITMASK) == 0) ++ { ++ int newval; ++ do ++ { ++ newval = cancelhandling | CANCELTYPE_BITMASK; ++ } ++ while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling, ++ &cancelhandling, newval)); ++ ++ if (cancel_enabled_and_canceled (cancelhandling)) ++ { ++ self->result = PTHREAD_CANCELED; ++ __do_cancel (); ++ } ++ } + } + libc_hidden_def (__libc_cleanup_pop_restore) +diff -Nuar a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c +--- a/nptl/pthread_cancel.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/pthread_cancel.c 2025-10-20 21:02:21.000000000 +0300 +@@ -42,18 +42,29 @@ + + struct pthread *self = THREAD_SELF; + +- int ch = atomic_load_relaxed (&self->cancelhandling); +- /* Cancelation not enabled, not cancelled, or already exitting. */ +- if (self->cancelstate == PTHREAD_CANCEL_DISABLE +- || (ch & CANCELED_BITMASK) == 0 +- || (ch & EXITING_BITMASK) != 0) +- return; +- +- /* Set the return value. */ +- THREAD_SETMEM (self, result, PTHREAD_CANCELED); +- /* Make sure asynchronous cancellation is still enabled. */ +- if (self->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) +- __do_cancel (); ++ int oldval = atomic_load_relaxed (&self->cancelhandling); ++ while (1) ++ { ++ /* We are canceled now. When canceled by another thread this flag ++ is already set but if the signal is directly send (internally or ++ from another process) is has to be done here. */ ++ int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK; ++ ++ if (oldval == newval || (oldval & EXITING_BITMASK) != 0) ++ /* Already canceled or exiting. */ ++ break; ++ ++ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling, ++ &oldval, newval)) ++ { ++ self->result = PTHREAD_CANCELED; ++ ++ /* Make sure asynchronous cancellation is still enabled. */ ++ if ((oldval & CANCELTYPE_BITMASK) != 0) ++ /* Run the registered destructors and terminate the thread. */ ++ __do_cancel (); ++ } ++ } + } + + int +@@ -92,29 +103,71 @@ + } + #endif + +- int oldch = atomic_fetch_or_acquire (&pd->cancelhandling, CANCELED_BITMASK); +- if ((oldch & CANCELED_BITMASK) != 0) +- return 0; +- +- if (pd == THREAD_SELF) ++ /* Some syscalls are never restarted after being interrupted by a signal ++ handler, regardless of the use of SA_RESTART (they always fail with ++ EINTR). So pthread_cancel cannot send SIGCANCEL unless the cancellation ++ is enabled and set as asynchronous (in this case the cancellation will ++ be acted in the cancellation handler instead by the syscall wrapper). ++ Otherwise the target thread is set as 'cancelling' (CANCELING_BITMASK) ++ by atomically setting 'cancelhandling' and the cancelation will be acted ++ upon on next cancellation entrypoing in the target thread. ++ ++ It also requires to atomically check if cancellation is enabled and ++ asynchronous, so both cancellation state and type are tracked on ++ 'cancelhandling'. */ ++ ++ int result = 0; ++ int oldval = atomic_load_relaxed (&pd->cancelhandling); ++ int newval; ++ do + { +- /* A single-threaded process should be able to kill itself, since there +- is nothing in the POSIX specification that says that it cannot. So +- we set multiple_threads to true so that cancellation points get +- executed. */ +- THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); ++ again: ++ newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK; ++ if (oldval == newval) ++ break; ++ ++ /* If the cancellation is handled asynchronously just send a ++ signal. We avoid this if possible since it's more ++ expensive. */ ++ if (cancel_enabled_and_canceled_and_async (newval)) ++ { ++ /* Mark the cancellation as "in progress". */ ++ int newval2 = oldval | CANCELING_BITMASK; ++ if (!atomic_compare_exchange_weak_acquire (&pd->cancelhandling, ++ &oldval, newval2)) ++ goto again; ++ ++ if (pd == THREAD_SELF) ++ /* This is not merely an optimization: An application may ++ call pthread_cancel (pthread_self ()) without calling ++ pthread_create, so the signal handler may not have been ++ set up for a self-cancel. */ ++ { ++ pd->result = PTHREAD_CANCELED; ++ if ((newval & CANCELTYPE_BITMASK) != 0) ++ __do_cancel (); ++ } ++ else ++ /* The cancellation handler will take care of marking the ++ thread as canceled. */ ++ result = __pthread_kill_internal (th, SIGCANCEL); ++ ++ break; ++ } ++ ++ /* A single-threaded process should be able to kill itself, since ++ there is nothing in the POSIX specification that says that it ++ cannot. So we set multiple_threads to true so that cancellation ++ points get executed. */ ++ THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); + #ifndef TLS_MULTIPLE_THREADS_IN_TCB + __libc_multiple_threads = 1; + #endif +- +- THREAD_SETMEM (pd, result, PTHREAD_CANCELED); +- if (pd->cancelstate == PTHREAD_CANCEL_ENABLE +- && pd->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS) +- __do_cancel (); +- return 0; + } ++ while (!atomic_compare_exchange_weak_acquire (&pd->cancelhandling, &oldval, ++ newval)); + +- return __pthread_kill_internal (th, SIGCANCEL); ++ return result; + } + versioned_symbol (libc, __pthread_cancel, pthread_cancel, GLIBC_2_34); + +diff -Nuar a/nptl/pthread_join_common.c b/nptl/pthread_join_common.c +--- a/nptl/pthread_join_common.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/pthread_join_common.c 2025-10-20 21:02:21.000000000 +0300 +@@ -57,12 +57,9 @@ + if ((pd == self + || (self->joinid == pd + && (pd->cancelhandling +- & (CANCELED_BITMASK | EXITING_BITMASK ++ & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK + | TERMINATED_BITMASK)) == 0)) +- && !(self->cancelstate == PTHREAD_CANCEL_ENABLE +- && (pd->cancelhandling & (CANCELED_BITMASK | EXITING_BITMASK +- | TERMINATED_BITMASK)) +- == CANCELED_BITMASK)) ++ && !cancel_enabled_and_canceled (self->cancelhandling)) + /* This is a deadlock situation. The threads are waiting for each + other to finish. Note that this is a "may" error. To be 100% + sure we catch this error we would have to lock the data +diff -Nuar a/nptl/pthread_mutex_lock.c b/nptl/pthread_mutex_lock.c +--- a/nptl/pthread_mutex_lock.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/pthread_mutex_lock.c 2025-10-20 21:02:21.000000000 +0300 +@@ -138,14 +138,26 @@ + int cnt = 0; + int max_cnt = MIN (max_adaptive_count (), + mutex->__data.__spins * 2 + 10); ++ int spin_count, exp_backoff = 1; ++ unsigned int jitter = get_jitter (); + do + { +- if (cnt++ >= max_cnt) ++ /* In each loop, spin count is exponential backoff plus ++ random jitter, random range is [0, exp_backoff-1]. */ ++ spin_count = exp_backoff + (jitter & (exp_backoff - 1)); ++ cnt += spin_count; ++ if (cnt >= max_cnt) + { ++ /* If cnt exceeds max spin count, just go to wait ++ queue. */ + LLL_MUTEX_LOCK (mutex); + break; + } +- atomic_spin_nop (); ++ do ++ atomic_spin_nop (); ++ while (--spin_count > 0); ++ /* Prepare for next loop. */ ++ exp_backoff = get_next_backoff (exp_backoff); + } + while (LLL_MUTEX_READ_LOCK (mutex) != 0 + || LLL_MUTEX_TRYLOCK (mutex) != 0); +diff -Nuar a/nptl/pthread_setcancelstate.c b/nptl/pthread_setcancelstate.c +--- a/nptl/pthread_setcancelstate.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/pthread_setcancelstate.c 2025-10-20 21:02:21.000000000 +0300 +@@ -30,9 +30,29 @@ + + self = THREAD_SELF; + +- if (oldstate != NULL) +- *oldstate = self->cancelstate; +- self->cancelstate = state; ++ int oldval = atomic_load_relaxed (&self->cancelhandling); ++ while (1) ++ { ++ int newval = (state == PTHREAD_CANCEL_DISABLE ++ ? oldval | CANCELSTATE_BITMASK ++ : oldval & ~CANCELSTATE_BITMASK); ++ ++ if (oldstate != NULL) ++ *oldstate = ((oldval & CANCELSTATE_BITMASK) ++ ? PTHREAD_CANCEL_DISABLE : PTHREAD_CANCEL_ENABLE); ++ ++ if (oldval == newval) ++ break; ++ ++ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling, ++ &oldval, newval)) ++ { ++ if (cancel_enabled_and_canceled_and_async (newval)) ++ __do_cancel (); ++ ++ break; ++ } ++ } + + return 0; + } +diff -Nuar a/nptl/pthread_setcanceltype.c b/nptl/pthread_setcanceltype.c +--- a/nptl/pthread_setcanceltype.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/pthread_setcanceltype.c 2025-10-20 21:02:21.000000000 +0300 +@@ -28,11 +28,32 @@ + + volatile struct pthread *self = THREAD_SELF; + +- if (oldtype != NULL) +- *oldtype = self->canceltype; +- self->canceltype = type; +- if (type == PTHREAD_CANCEL_ASYNCHRONOUS) +- __pthread_testcancel (); ++ int oldval = atomic_load_relaxed (&self->cancelhandling); ++ while (1) ++ { ++ int newval = (type == PTHREAD_CANCEL_ASYNCHRONOUS ++ ? oldval | CANCELTYPE_BITMASK ++ : oldval & ~CANCELTYPE_BITMASK); ++ ++ if (oldtype != NULL) ++ *oldtype = ((oldval & CANCELTYPE_BITMASK) ++ ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED); ++ ++ if (oldval == newval) ++ break; ++ ++ if (atomic_compare_exchange_weak_acquire (&self->cancelhandling, ++ &oldval, newval)) ++ { ++ if (cancel_enabled_and_canceled_and_async (newval)) ++ { ++ THREAD_SETMEM (self, result, PTHREAD_CANCELED); ++ __do_cancel (); ++ } ++ ++ break; ++ } ++ } + + return 0; + } +diff -Nuar a/nptl/pthread_testcancel.c b/nptl/pthread_testcancel.c +--- a/nptl/pthread_testcancel.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/pthread_testcancel.c 2025-10-20 21:02:21.000000000 +0300 +@@ -23,13 +23,10 @@ + ___pthread_testcancel (void) + { + struct pthread *self = THREAD_SELF; +- int cancelhandling = THREAD_GETMEM (self, cancelhandling); +- if (self->cancelstate == PTHREAD_CANCEL_ENABLE +- && (cancelhandling & CANCELED_BITMASK) +- && !(cancelhandling & EXITING_BITMASK) +- && !(cancelhandling & TERMINATED_BITMASK)) ++ int cancelhandling = atomic_load_relaxed (&self->cancelhandling); ++ if (cancel_enabled_and_canceled (cancelhandling)) + { +- THREAD_SETMEM (self, result, PTHREAD_CANCELED); ++ self->result = PTHREAD_CANCELED; + __do_cancel (); + } + } +diff -Nuar a/nptl/tst-cancel7.c b/nptl/tst-cancel7.c +--- a/nptl/tst-cancel7.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/tst-cancel7.c 2025-10-20 21:02:21.000000000 +0300 +@@ -43,7 +43,8 @@ + { + char *cmd = xasprintf ("%s --direct --sem %s --pidfile %s", + command, semfilename, pidfilename); +- system (cmd); ++ if (system (cmd)) ++ FAIL_EXIT1("system call unexpectedly returned"); + /* This call should never return. */ + return NULL; + } +diff -Nuar a/nptl/tst-pthread-getattr.c b/nptl/tst-pthread-getattr.c +--- a/nptl/tst-pthread-getattr.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/tst-pthread-getattr.c 2025-10-20 21:02:21.000000000 +0300 +@@ -28,6 +28,8 @@ + #include + #include + ++#include ++ + /* There is an obscure bug in the kernel due to which RLIMIT_STACK is sometimes + returned as unlimited when it is not, which may cause this test to fail. + There is also the other case where RLIMIT_STACK is intentionally set as +@@ -153,6 +155,8 @@ + static int + do_test (void) + { ++ support_need_proc ("Reads /proc/self/maps to get stack size."); ++ + pagesize = sysconf (_SC_PAGESIZE); + return check_stack_top (); + } +diff -Nuar a/nptl/tst-setuid2.c b/nptl/tst-setuid2.c +--- a/nptl/tst-setuid2.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/tst-setuid2.c 2025-10-20 21:02:21.000000000 +0300 +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -36,30 +37,21 @@ + static void * + thread_func (void *ctx __attribute__ ((unused))) + { +- int ret = pthread_mutex_lock (&mutex); +- if (ret != 0) +- FAIL ("pthread_mutex_lock (thread): %d", ret); +- ++ xpthread_mutex_lock (&mutex); + while (true) + { + if (func_sent != NULL) + { + void (*func) (void) = func_sent; +- ret = pthread_mutex_unlock (&mutex); +- if (ret != 0) +- FAIL ("pthread_mutex_unlock (thread): %d", ret); ++ xpthread_mutex_unlock (&mutex); ++ + func (); +- ret = pthread_mutex_lock (&mutex); +- if (ret != 0) +- FAIL ("pthread_mutex_lock (thread): %d", ret); ++ ++ xpthread_mutex_lock (&mutex); + func_sent = NULL; +- ret = pthread_cond_signal (&cond_recv); +- if (ret != 0) +- FAIL ("pthread_cond_signal (recv): %d", ret); ++ xpthread_cond_signal (&cond_recv); + } +- ret = pthread_cond_wait (&cond_send, &mutex); +- if (ret != 0) +- FAIL ("pthread_cond_wait (send): %d", ret); ++ xpthread_cond_wait (&cond_send, &mutex); + } + return NULL; + } +@@ -67,31 +59,18 @@ + static void + run_on_thread (void (*func) (void)) + { +- int ret = pthread_mutex_lock (&mutex); +- if (ret != 0) +- FAIL ("pthread_mutex_lock (%s): %d", __func__, ret); ++ xpthread_mutex_lock (&mutex); + func_sent = func; +- ret = pthread_mutex_unlock (&mutex); +- if (ret != 0) +- FAIL ("pthread_mutex_unlock (%s): %d", __func__, ret); ++ xpthread_mutex_unlock (&mutex); + +- ret = pthread_cond_signal (&cond_send); +- if (ret != 0) +- FAIL ("pthread_mutex_lock (%s): %d", __func__, ret); +- +- ret = pthread_mutex_lock (&mutex); +- if (ret != 0) +- FAIL ("pthread_mutex_lock (%s): %d", __func__, ret); ++ xpthread_cond_signal (&cond_send); + ++ xpthread_mutex_lock (&mutex); + while (func_sent != NULL) + { +- ret = pthread_cond_wait (&cond_recv, &mutex); +- if (ret != 0) +- FAIL ("pthread_mutex_wait (%s): %d", __func__, ret); ++ xpthread_cond_wait (&cond_recv, &mutex); + } +- ret = pthread_mutex_unlock (&mutex); +- if (ret != 0) +- FAIL ("pthread_mutex_unlock (%s): %d", __func__, ret); ++ xpthread_mutex_unlock (&mutex); + } + + static void +@@ -141,5 +120,4 @@ + return 0; + } + +-#define TEST_FUNCTION do_test () +-#include "../test-skeleton.c" ++#include +diff -Nuar a/nptl/tst-stackguard1.c b/nptl/tst-stackguard1.c +--- a/nptl/tst-stackguard1.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/tst-stackguard1.c 2025-10-20 21:02:21.000000000 +0300 +@@ -27,6 +27,8 @@ + #include + #include + ++#include ++ + static const char *command; + static bool child; + static uintptr_t stack_chk_guard_copy; +@@ -138,7 +140,8 @@ + dup2 (fds[1], 2); + close (fds[1]); + +- system (command); ++ xsystem (command); ++ + exit (0); + } + +diff -Nuar a/nptl/unwind.c b/nptl/unwind.c +--- a/nptl/unwind.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nptl/unwind.c 2025-10-20 21:02:21.000000000 +0300 +@@ -25,7 +25,7 @@ + #include + #include + +-#ifdef _STACK_GROWS_DOWN ++#if _STACK_GROWS_DOWN + # define FRAME_LEFT(frame, other, adj) \ + ((uintptr_t) frame - adj >= (uintptr_t) other - adj) + #elif _STACK_GROWS_UP +diff -Nuar a/nscd/aicache.c b/nscd/aicache.c +--- a/nscd/aicache.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nscd/aicache.c 2025-10-20 21:02:21.000000000 +0300 +@@ -110,11 +110,10 @@ + "gethostbyname4_r"); + if (fct4 != NULL) + { +- struct gaih_addrtuple atmem; + struct gaih_addrtuple *at; + while (1) + { +- at = &atmem; ++ at = NULL; + rc6 = 0; + herrno = 0; + status[1] = DL_CALL_FCT (fct4, (key, &at, +@@ -137,7 +136,7 @@ + goto next_nip; + + /* We found the data. Count the addresses and the size. */ +- for (const struct gaih_addrtuple *at2 = at = &atmem; at2 != NULL; ++ for (const struct gaih_addrtuple *at2 = at; at2 != NULL; + at2 = at2->next) + { + ++naddrs; +diff -Nuar a/nscd/connections.c b/nscd/connections.c +--- a/nscd/connections.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nscd/connections.c 2025-10-20 21:02:21.000000000 +0300 +@@ -2284,7 +2284,8 @@ + sizeof (buf))) != -1) + ; + +- __bump_nl_timestamp (); ++ dbs[hstdb].head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP] ++ = __bump_nl_timestamp (); + } + # endif + else +diff -Nuar a/nscd/netgroupcache.c b/nscd/netgroupcache.c +--- a/nscd/netgroupcache.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nscd/netgroupcache.c 2025-10-20 21:02:21.000000000 +0300 +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #include "../inet/netgroup.h" + #include "nscd.h" +@@ -65,6 +66,16 @@ + char strdata[0]; + }; + ++/* Send a notfound response to FD. Always returns -1 to indicate an ++ ephemeral error. */ ++static time_t ++send_notfound (int fd) ++{ ++ if (fd != -1) ++ TEMP_FAILURE_RETRY (send (fd, ¬found, sizeof (notfound), MSG_NOSIGNAL)); ++ return -1; ++} ++ + /* Sends a notfound message and prepares a notfound dataset to write to the + cache. Returns true if there was enough memory to allocate the dataset and + returns the dataset in DATASETP, total bytes to write in TOTALP and the +@@ -83,8 +94,7 @@ + total = sizeof (notfound); + timeout = time (NULL) + db->negtimeout; + +- if (fd != -1) +- TEMP_FAILURE_RETRY (send (fd, ¬found, total, MSG_NOSIGNAL)); ++ send_notfound (fd); + + dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1); + /* If we cannot permanently store the result, so be it. */ +@@ -109,11 +119,78 @@ + return cacheable; + } + ++struct addgetnetgrentX_scratch ++{ ++ /* This is the result that the caller should use. It can be NULL, ++ point into buffer, or it can be in the cache. */ ++ struct dataset *dataset; ++ ++ struct scratch_buffer buffer; ++ ++ /* Used internally in addgetnetgrentX as a staging area. */ ++ struct scratch_buffer tmp; ++ ++ /* Number of bytes in buffer that are actually used. */ ++ size_t buffer_used; ++}; ++ ++static void ++addgetnetgrentX_scratch_init (struct addgetnetgrentX_scratch *scratch) ++{ ++ scratch->dataset = NULL; ++ scratch_buffer_init (&scratch->buffer); ++ scratch_buffer_init (&scratch->tmp); ++ ++ /* Reserve space for the header. */ ++ scratch->buffer_used = sizeof (struct dataset); ++ static_assert (sizeof (struct dataset) < sizeof (scratch->tmp.__space), ++ "initial buffer space"); ++ memset (scratch->tmp.data, 0, sizeof (struct dataset)); ++} ++ ++static void ++addgetnetgrentX_scratch_free (struct addgetnetgrentX_scratch *scratch) ++{ ++ scratch_buffer_free (&scratch->buffer); ++ scratch_buffer_free (&scratch->tmp); ++} ++ ++/* Copy LENGTH bytes from S into SCRATCH. Returns NULL if SCRATCH ++ could not be resized, otherwise a pointer to the copy. */ ++static char * ++addgetnetgrentX_append_n (struct addgetnetgrentX_scratch *scratch, ++ const char *s, size_t length) ++{ ++ while (true) ++ { ++ size_t remaining = scratch->buffer.length - scratch->buffer_used; ++ if (remaining >= length) ++ break; ++ if (!scratch_buffer_grow_preserve (&scratch->buffer)) ++ return NULL; ++ } ++ char *copy = scratch->buffer.data + scratch->buffer_used; ++ memcpy (copy, s, length); ++ scratch->buffer_used += length; ++ return copy; ++} ++ ++/* Copy S into SCRATCH, including its null terminator. Returns false ++ if SCRATCH could not be resized. */ ++static bool ++addgetnetgrentX_append (struct addgetnetgrentX_scratch *scratch, const char *s) ++{ ++ if (s == NULL) ++ s = ""; ++ return addgetnetgrentX_append_n (scratch, s, strlen (s) + 1) != NULL; ++} ++ ++/* Caller must initialize and free *SCRATCH. If the return value is ++ negative, this function has sent a notfound response. */ + static time_t + addgetnetgrentX (struct database_dyn *db, int fd, request_header *req, + const char *key, uid_t uid, struct hashentry *he, +- struct datahead *dh, struct dataset **resultp, +- void **tofreep) ++ struct datahead *dh, struct addgetnetgrentX_scratch *scratch) + { + if (__glibc_unlikely (debug_level > 0)) + { +@@ -132,14 +209,10 @@ + + char *key_copy = NULL; + struct __netgrent data; +- size_t buflen = MAX (1024, sizeof (*dataset) + req->key_len); +- size_t buffilled = sizeof (*dataset); +- char *buffer = NULL; + size_t nentries = 0; + size_t group_len = strlen (key) + 1; + struct name_list *first_needed + = alloca (sizeof (struct name_list) + group_len); +- *tofreep = NULL; + + if (netgroup_database == NULL + && !__nss_database_get (nss_database_netgroup, &netgroup_database)) +@@ -147,12 +220,10 @@ + /* No such service. */ + cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout, + &key_copy); +- goto writeout; ++ goto maybe_cache_add; + } + + memset (&data, '\0', sizeof (data)); +- buffer = xmalloc (buflen); +- *tofreep = buffer; + first_needed->next = first_needed; + memcpy (first_needed->name, key, group_len); + data.needed_groups = first_needed; +@@ -195,8 +266,8 @@ + while (1) + { + int e; +- status = getfct.f (&data, buffer + buffilled, +- buflen - buffilled - req->key_len, &e); ++ status = getfct.f (&data, scratch->tmp.data, ++ scratch->tmp.length, &e); + if (status == NSS_STATUS_SUCCESS) + { + if (data.type == triple_val) +@@ -204,68 +275,10 @@ + const char *nhost = data.val.triple.host; + const char *nuser = data.val.triple.user; + const char *ndomain = data.val.triple.domain; +- +- size_t hostlen = strlen (nhost ?: "") + 1; +- size_t userlen = strlen (nuser ?: "") + 1; +- size_t domainlen = strlen (ndomain ?: "") + 1; +- +- if (nhost == NULL || nuser == NULL || ndomain == NULL +- || nhost > nuser || nuser > ndomain) +- { +- const char *last = nhost; +- if (last == NULL +- || (nuser != NULL && nuser > last)) +- last = nuser; +- if (last == NULL +- || (ndomain != NULL && ndomain > last)) +- last = ndomain; +- +- size_t bufused +- = (last == NULL +- ? buffilled +- : last + strlen (last) + 1 - buffer); +- +- /* We have to make temporary copies. */ +- size_t needed = hostlen + userlen + domainlen; +- +- if (buflen - req->key_len - bufused < needed) +- { +- buflen += MAX (buflen, 2 * needed); +- /* Save offset in the old buffer. We don't +- bother with the NULL check here since +- we'll do that later anyway. */ +- size_t nhostdiff = nhost - buffer; +- size_t nuserdiff = nuser - buffer; +- size_t ndomaindiff = ndomain - buffer; +- +- char *newbuf = xrealloc (buffer, buflen); +- /* Fix up the triplet pointers into the new +- buffer. */ +- nhost = (nhost ? newbuf + nhostdiff +- : NULL); +- nuser = (nuser ? newbuf + nuserdiff +- : NULL); +- ndomain = (ndomain ? newbuf + ndomaindiff +- : NULL); +- *tofreep = buffer = newbuf; +- } +- +- nhost = memcpy (buffer + bufused, +- nhost ?: "", hostlen); +- nuser = memcpy ((char *) nhost + hostlen, +- nuser ?: "", userlen); +- ndomain = memcpy ((char *) nuser + userlen, +- ndomain ?: "", domainlen); +- } +- +- char *wp = buffer + buffilled; +- wp = memmove (wp, nhost ?: "", hostlen); +- wp += hostlen; +- wp = memmove (wp, nuser ?: "", userlen); +- wp += userlen; +- wp = memmove (wp, ndomain ?: "", domainlen); +- wp += domainlen; +- buffilled = wp - buffer; ++ if (!(addgetnetgrentX_append (scratch, nhost) ++ && addgetnetgrentX_append (scratch, nuser) ++ && addgetnetgrentX_append (scratch, ndomain))) ++ return send_notfound (fd); + ++nentries; + } + else +@@ -317,8 +330,8 @@ + } + else if (status == NSS_STATUS_TRYAGAIN && e == ERANGE) + { +- buflen *= 2; +- *tofreep = buffer = xrealloc (buffer, buflen); ++ if (!scratch_buffer_grow (&scratch->tmp)) ++ return send_notfound (fd); + } + else if (status == NSS_STATUS_RETURN + || status == NSS_STATUS_NOTFOUND +@@ -348,13 +361,20 @@ + { + cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout, + &key_copy); +- goto writeout; ++ goto maybe_cache_add; + } + +- total = buffilled; ++ /* Capture the result size without the key appended. */ ++ total = scratch->buffer_used; ++ ++ /* Make a copy of the key. The scratch buffer must not move after ++ this point. */ ++ key_copy = addgetnetgrentX_append_n (scratch, key, req->key_len); ++ if (key_copy == NULL) ++ return send_notfound (fd); + + /* Fill in the dataset. */ +- dataset = (struct dataset *) buffer; ++ dataset = scratch->buffer.data; + timeout = datahead_init_pos (&dataset->head, total + req->key_len, + total - offsetof (struct dataset, resp), + he == NULL ? 0 : dh->nreloads + 1, +@@ -363,11 +383,7 @@ + dataset->resp.version = NSCD_VERSION; + dataset->resp.found = 1; + dataset->resp.nresults = nentries; +- dataset->resp.result_len = buffilled - sizeof (*dataset); +- +- assert (buflen - buffilled >= req->key_len); +- key_copy = memcpy (buffer + buffilled, key, req->key_len); +- buffilled += req->key_len; ++ dataset->resp.result_len = total - sizeof (*dataset); + + /* Now we can determine whether on refill we have to create a new + record or not. */ +@@ -398,7 +414,7 @@ + if (__glibc_likely (newp != NULL)) + { + /* Adjust pointer into the memory block. */ +- key_copy = (char *) newp + (key_copy - buffer); ++ key_copy = (char *) newp + (key_copy - (char *) dataset); + + dataset = memcpy (newp, dataset, total + req->key_len); + cacheable = true; +@@ -410,14 +426,12 @@ + } + + if (he == NULL && fd != -1) +- { +- /* We write the dataset before inserting it to the database +- since while inserting this thread might block and so would +- unnecessarily let the receiver wait. */ +- writeout: ++ /* We write the dataset before inserting it to the database since ++ while inserting this thread might block and so would ++ unnecessarily let the receiver wait. */ + writeall (fd, &dataset->resp, dataset->head.recsize); +- } + ++ maybe_cache_add: + if (cacheable) + { + /* If necessary, we also propagate the data to disk. */ +@@ -441,7 +455,7 @@ + } + + out: +- *resultp = dataset; ++ scratch->dataset = dataset; + + return timeout; + } +@@ -462,6 +476,9 @@ + if (user != NULL) + key = (char *) rawmemchr (key, '\0') + 1; + const char *domain = *key++ ? key : NULL; ++ struct addgetnetgrentX_scratch scratch; ++ ++ addgetnetgrentX_scratch_init (&scratch); + + if (__glibc_unlikely (debug_level > 0)) + { +@@ -477,12 +494,8 @@ + group, group_len, + db, uid); + time_t timeout; +- void *tofree; + if (result != NULL) +- { +- timeout = result->head.timeout; +- tofree = NULL; +- } ++ timeout = result->head.timeout; + else + { + request_header req_get = +@@ -491,7 +504,10 @@ + .key_len = group_len + }; + timeout = addgetnetgrentX (db, -1, &req_get, group, uid, NULL, NULL, +- &result, &tofree); ++ &scratch); ++ result = scratch.dataset; ++ if (timeout < 0) ++ goto out; + } + + struct indataset +@@ -502,24 +518,26 @@ + = (struct indataset *) mempool_alloc (db, + sizeof (*dataset) + req->key_len, + 1); +- struct indataset dataset_mem; + bool cacheable = true; + if (__glibc_unlikely (dataset == NULL)) + { + cacheable = false; +- dataset = &dataset_mem; ++ /* The alloca is safe because nscd_run_worker verfies that ++ key_len is not larger than MAXKEYLEN. */ ++ dataset = alloca (sizeof (*dataset) + req->key_len); + } + + datahead_init_pos (&dataset->head, sizeof (*dataset) + req->key_len, + sizeof (innetgroup_response_header), +- he == NULL ? 0 : dh->nreloads + 1, result->head.ttl); ++ he == NULL ? 0 : dh->nreloads + 1, ++ result == NULL ? db->negtimeout : result->head.ttl); + /* Set the notfound status and timeout based on the result from + getnetgrent. */ +- dataset->head.notfound = result->head.notfound; ++ dataset->head.notfound = result == NULL || result->head.notfound; + dataset->head.timeout = timeout; + + dataset->resp.version = NSCD_VERSION; +- dataset->resp.found = result->resp.found; ++ dataset->resp.found = result != NULL && result->resp.found; + /* Until we find a matching entry the result is 0. */ + dataset->resp.result = 0; + +@@ -567,7 +585,9 @@ + goto out; + } + +- if (he == NULL) ++ /* addgetnetgrentX may have already sent a notfound response. Do ++ not send another one. */ ++ if (he == NULL && dataset->resp.found) + { + /* We write the dataset before inserting it to the database + since while inserting this thread might block and so would +@@ -601,7 +621,7 @@ + } + + out: +- free (tofree); ++ addgetnetgrentX_scratch_free (&scratch); + return timeout; + } + +@@ -611,11 +631,12 @@ + const char *key, uid_t uid, struct hashentry *he, + struct datahead *dh) + { +- struct dataset *ignore; +- void *tofree; +- time_t timeout = addgetnetgrentX (db, fd, req, key, uid, he, dh, +- &ignore, &tofree); +- free (tofree); ++ struct addgetnetgrentX_scratch scratch; ++ addgetnetgrentX_scratch_init (&scratch); ++ time_t timeout = addgetnetgrentX (db, fd, req, key, uid, he, dh, &scratch); ++ addgetnetgrentX_scratch_free (&scratch); ++ if (timeout < 0) ++ timeout = 0; + return timeout; + } + +@@ -659,5 +680,9 @@ + .key_len = he->len + }; + +- return addinnetgrX (db, -1, &req, db->data + he->key, he->owner, he, dh); ++ time_t timeout = addinnetgrX (db, -1, &req, db->data + he->key, he->owner, ++ he, dh); ++ if (timeout < 0) ++ timeout = 0; ++ return timeout; + } +diff -Nuar a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c +--- a/nscd/nscd_gethst_r.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nscd/nscd_gethst_r.c 2025-10-20 21:02:21.000000000 +0300 +@@ -112,7 +112,7 @@ + if (map == NULL + || (map != NO_MAPPING + && map->head->nscd_certainly_running == 0 +- && map->head->timestamp + MAPPING_TIMEOUT < time_now ())) ++ && map->head->timestamp + MAPPING_TIMEOUT < time64_now ())) + map = __nscd_get_mapping (GETFDHST, "hosts", &__hst_map_handle.mapped); + + if (map == NO_MAPPING) +diff -Nuar a/nscd/nscd.h b/nscd/nscd.h +--- a/nscd/nscd.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/nscd/nscd.h 2025-10-20 21:02:21.000000000 +0300 +@@ -65,7 +65,7 @@ + struct traced_file + { + /* Tracks the last modified time of the traced file. */ +- time_t mtime; ++ __time64_t mtime; + /* Support multiple registered files per database. */ + struct traced_file *next; + int call_res_init; +diff -Nuar a/nss/getent.c b/nss/getent.c +--- a/nss/getent.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nss/getent.c 2025-10-20 21:02:21.000000000 +0300 +@@ -58,6 +58,8 @@ + { + { "service", 's', N_("CONFIG"), 0, N_("Service configuration to be used") }, + { "no-idn", 'i', NULL, 0, N_("disable IDN encoding") }, ++ { "no-addrconfig", 'A', NULL, 0, ++ N_("do not filter out unsupported IPv4/IPv6 addresses (with ahosts*)") }, + { NULL, 0, NULL, 0, NULL }, + }; + +@@ -79,6 +81,9 @@ + /* Additional getaddrinfo flags for IDN encoding. */ + static int idn_flags = AI_IDN | AI_CANONIDN; + ++/* Set to 0 by --no-addrconfig. */ ++static int addrconfig_flags = AI_ADDRCONFIG; ++ + /* Print the version information. */ + static void + print_version (FILE *stream, struct argp_state *state) +@@ -346,7 +351,7 @@ + + struct addrinfo hint; + memset (&hint, '\0', sizeof (hint)); +- hint.ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG | AI_CANONNAME ++ hint.ai_flags = (AI_V4MAPPED | addrconfig_flags | AI_CANONNAME + | idn_flags | xflags); + hint.ai_family = af; + +@@ -905,6 +910,10 @@ + idn_flags = 0; + break; + ++ case 'A': ++ addrconfig_flags = 0; ++ break; ++ + default: + return ARGP_ERR_UNKNOWN; + } +diff -Nuar a/nss/Makefile b/nss/Makefile +--- a/nss/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/nss/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -56,20 +56,32 @@ + + tests-static = tst-field + tests-internal = tst-field +-tests = test-netdb test-digits-dots tst-nss-getpwent bug17079 \ +- tst-nss-test1 \ +- tst-nss-test2 \ +- tst-nss-test4 \ +- tst-nss-test5 +-xtests = bug-erange +- +-tests-container = \ +- tst-nss-compat1 \ +- tst-nss-test3 \ +- tst-nss-files-hosts-long \ +- tst-nss-db-endpwent \ +- tst-nss-db-endgrent \ +- tst-reload1 tst-reload2 ++ ++tests := \ ++ bug17079 \ ++ test-digits-dots \ ++ test-netdb \ ++ tst-nss-getpwent \ ++ tst-nss-test1 \ ++ tst-nss-test2 \ ++ tst-nss-test4 \ ++ tst-nss-test5 \ ++ tst-nss-test_errno \ ++# tests ++ ++xtests = bug-erange ++ ++tests-container := \ ++ tst-nss-compat1 \ ++ tst-nss-db-endgrent \ ++ tst-nss-db-endpwent \ ++ tst-nss-files-hosts-long \ ++ tst-nss-gai-actions \ ++ tst-nss-test3 \ ++ tst-reload1 \ ++ tst-reload2 \ ++ tst-nss-gai-hv2-canonname \ ++# tests-container + + # Tests which need libdl + ifeq (yes,$(build-shared)) +@@ -132,7 +144,17 @@ + ifeq ($(build-static-nss),yes) + tests-static += tst-nss-static + endif +-extra-test-objs += nss_test1.os nss_test2.os ++extra-test-objs += nss_test1.os nss_test2.os nss_test_errno.os \ ++ nss_test_gai_hv2_canonname.os ++ ++ifeq ($(run-built-tests),yes) ++ifneq (no,$(PERL)) ++tests-special += $(objpfx)mtrace-tst-nss-gai-hv2-canonname.out ++endif ++endif ++ ++generated += mtrace-tst-nss-gai-hv2-canonname.out \ ++ tst-nss-gai-hv2-canonname.mtrace + + include ../Rules + +@@ -166,22 +188,34 @@ + + libof-nss_test1 = extramodules + libof-nss_test2 = extramodules ++libof-nss_test_errno = extramodules ++libof-nss_test_gai_hv2_canonname = extramodules + $(objpfx)/libnss_test1.so: $(objpfx)nss_test1.os $(link-libc-deps) + $(build-module) + $(objpfx)/libnss_test2.so: $(objpfx)nss_test2.os $(link-libc-deps) + $(build-module) ++$(objpfx)/libnss_test_errno.so: $(objpfx)nss_test_errno.os $(link-libc-deps) ++ $(build-module) ++$(objpfx)/libnss_test_gai_hv2_canonname.so: \ ++ $(objpfx)nss_test_gai_hv2_canonname.os $(link-libc-deps) ++ $(build-module) + $(objpfx)nss_test2.os : nss_test1.c +-ifdef libnss_test1.so-version +-$(objpfx)/libnss_test1.so$(libnss_test1.so-version): $(objpfx)/libnss_test1.so ++# Use the nss_files suffix for these objects as well. ++$(objpfx)/libnss_test1.so$(libnss_files.so-version): $(objpfx)/libnss_test1.so + $(make-link) +-endif +-ifdef libnss_test2.so-version +-$(objpfx)/libnss_test2.so$(libnss_test2.so-version): $(objpfx)/libnss_test2.so ++$(objpfx)/libnss_test2.so$(libnss_files.so-version): $(objpfx)/libnss_test2.so ++ $(make-link) ++$(objpfx)/libnss_test_errno.so$(libnss_files.so-version): \ ++ $(objpfx)/libnss_test_errno.so ++ $(make-link) ++$(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version): \ ++ $(objpfx)/libnss_test_gai_hv2_canonname.so + $(make-link) +-endif + $(patsubst %,$(objpfx)%.out,$(tests) $(tests-container)) : \ +- $(objpfx)/libnss_test1.so$(libnss_test1.so-version) \ +- $(objpfx)/libnss_test2.so$(libnss_test2.so-version) ++ $(objpfx)/libnss_test1.so$(libnss_files.so-version) \ ++ $(objpfx)/libnss_test2.so$(libnss_files.so-version) \ ++ $(objpfx)/libnss_test_errno.so$(libnss_files.so-version) \ ++ $(objpfx)/libnss_test_gai_hv2_canonname.so$(libnss_files.so-version) + + ifeq (yes,$(have-thread-library)) + $(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library) +@@ -190,6 +224,17 @@ + $(objpfx)tst-nss-files-alias-leak.out: $(objpfx)/libnss_files.so + $(objpfx)tst-nss-files-alias-truncated.out: $(objpfx)/libnss_files.so + ++tst-nss-gai-hv2-canonname-ENV = \ ++ MALLOC_TRACE=$(objpfx)tst-nss-gai-hv2-canonname.mtrace \ ++ LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so ++$(objpfx)mtrace-tst-nss-gai-hv2-canonname.out: \ ++ $(objpfx)tst-nss-gai-hv2-canonname.out ++ { test -r $(objpfx)tst-nss-gai-hv2-canonname.mtrace \ ++ || ( echo "tst-nss-gai-hv2-canonname.mtrace does not exist"; exit 77; ) \ ++ && $(common-objpfx)malloc/mtrace \ ++ $(objpfx)tst-nss-gai-hv2-canonname.mtrace; } > $@; \ ++ $(evaluate-test) ++ + # Disable DT_RUNPATH on NSS tests so that the glibc internal NSS + # functions can load testing NSS modules via DT_RPATH. + LDFLAGS-tst-nss-test1 = -Wl,--disable-new-dtags +@@ -197,3 +242,5 @@ + LDFLAGS-tst-nss-test3 = -Wl,--disable-new-dtags + LDFLAGS-tst-nss-test4 = -Wl,--disable-new-dtags + LDFLAGS-tst-nss-test5 = -Wl,--disable-new-dtags ++LDFLAGS-tst-nss-test_errno = -Wl,--disable-new-dtags ++LDFLAGS-tst-nss-test_gai_hv2_canonname = -Wl,--disable-new-dtags +diff -Nuar a/nss/nss_database.c b/nss/nss_database.c +--- a/nss/nss_database.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nss/nss_database.c 2025-10-20 21:02:21.000000000 +0300 +@@ -420,23 +420,32 @@ + return true; + } + +- /* Before we reload, verify that "/" hasn't changed. We assume that +- errors here are very unlikely, but the chance that we're entering +- a container is also very unlikely, so we err on the side of both +- very unlikely things not happening at the same time. */ +- if (__stat64_time64 ("/", &str) != 0 +- || (local->root_ino != 0 +- && (str.st_ino != local->root_ino +- || str.st_dev != local->root_dev))) ++ int stat_rv = __stat64_time64 ("/", &str); ++ ++ if (local->data.services[database_index] != NULL) + { +- /* Change detected; disable reloading and return current state. */ +- atomic_store_release (&local->data.reload_disabled, 1); +- *result = local->data.services[database_index]; +- __libc_lock_unlock (local->lock); +- return true; ++ /* Before we reload, verify that "/" hasn't changed. We assume that ++ errors here are very unlikely, but the chance that we're entering ++ a container is also very unlikely, so we err on the side of both ++ very unlikely things not happening at the same time. */ ++ if (stat_rv != 0 ++ || (local->root_ino != 0 ++ && (str.st_ino != local->root_ino ++ || str.st_dev != local->root_dev))) ++ { ++ /* Change detected; disable reloading and return current state. */ ++ atomic_store_release (&local->data.reload_disabled, 1); ++ *result = local->data.services[database_index]; ++ __libc_lock_unlock (local->lock); ++ return true; ++ } + } +- local->root_ino = str.st_ino; +- local->root_dev = str.st_dev; ++ if (stat_rv == 0) ++ { ++ local->root_ino = str.st_ino; ++ local->root_dev = str.st_dev; ++ } ++ + __libc_lock_unlock (local->lock); + + /* Avoid overwriting the global configuration until we have loaded +diff -Nuar a/nss/nss_module.c b/nss/nss_module.c +--- a/nss/nss_module.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nss/nss_module.c 2025-10-20 21:02:21.000000000 +0300 +@@ -330,8 +330,18 @@ + void * + __nss_module_get_function (struct nss_module *module, const char *name) + { ++ /* A successful dlopen might clobber errno. */ ++ int saved_errno = errno; ++ + if (!__nss_module_load (module)) +- return NULL; ++ { ++ /* Reporting module load failure is currently inaccurate. See ++ bug 22041. Not changing errno is the conservative choice. */ ++ __set_errno (saved_errno); ++ return NULL; ++ } ++ ++ __set_errno (saved_errno); + + function_name *name_entry = bsearch (name, nss_function_name_array, + array_length (nss_function_name_array), +diff -Nuar a/nss/nss_test_errno.c b/nss/nss_test_errno.c +--- a/nss/nss_test_errno.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/nss/nss_test_errno.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,58 @@ ++/* NSS service provider with errno clobber. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++ ++/* Catch misnamed and functions. */ ++#pragma GCC diagnostic error "-Wmissing-prototypes" ++NSS_DECLARE_MODULE_FUNCTIONS (test_errno) ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ /* An arbitrary error code which is otherwise not used. */ ++ errno = -1009; ++} ++ ++/* Lookup functions for pwd follow that do not return any data. */ ++ ++/* Catch misnamed function definitions. */ ++ ++enum nss_status ++_nss_test_errno_setpwent (int stayopen) ++{ ++ setenv ("_nss_test_errno_setpwent", "yes", 1); ++ return NSS_STATUS_SUCCESS; ++} ++ ++enum nss_status ++_nss_test_errno_getpwent_r (struct passwd *result, ++ char *buffer, size_t size, int *errnop) ++{ ++ setenv ("_nss_test_errno_getpwent_r", "yes", 1); ++ return NSS_STATUS_NOTFOUND; ++} ++ ++enum nss_status ++_nss_test_errno_endpwent (void) ++{ ++ setenv ("_nss_test_errno_endpwent", "yes", 1); ++ return NSS_STATUS_SUCCESS; ++} +diff -Nuar a/nss/nss_test_gai_hv2_canonname.c b/nss/nss_test_gai_hv2_canonname.c +--- a/nss/nss_test_gai_hv2_canonname.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/nss/nss_test_gai_hv2_canonname.c 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1,56 @@ ++/* NSS service provider that only provides gethostbyname2_r. ++ Copyright The GNU Toolchain Authors. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include "nss/tst-nss-gai-hv2-canonname.h" ++ ++/* Catch misnamed and functions. */ ++#pragma GCC diagnostic error "-Wmissing-prototypes" ++NSS_DECLARE_MODULE_FUNCTIONS (test_gai_hv2_canonname) ++ ++extern enum nss_status _nss_files_gethostbyname2_r (const char *, int, ++ struct hostent *, char *, ++ size_t, int *, int *); ++ ++enum nss_status ++_nss_test_gai_hv2_canonname_gethostbyname2_r (const char *name, int af, ++ struct hostent *result, ++ char *buffer, size_t buflen, ++ int *errnop, int *herrnop) ++{ ++ return _nss_files_gethostbyname2_r (name, af, result, buffer, buflen, errnop, ++ herrnop); ++} ++ ++enum nss_status ++_nss_test_gai_hv2_canonname_getcanonname_r (const char *name, char *buffer, ++ size_t buflen, char **result, ++ int *errnop, int *h_errnop) ++{ ++ /* We expect QUERYNAME, which is a small enough string that it shouldn't fail ++ the test. */ ++ if (memcmp (QUERYNAME, name, sizeof (QUERYNAME)) ++ || buflen < sizeof (QUERYNAME)) ++ abort (); ++ ++ strncpy (buffer, name, buflen); ++ *result = buffer; ++ return NSS_STATUS_SUCCESS; ++} +diff -Nuar a/nss/tst-nss-db-endpwent.c b/nss/tst-nss-db-endpwent.c +--- a/nss/tst-nss-db-endpwent.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nss/tst-nss-db-endpwent.c 2025-10-20 21:02:21.000000000 +0300 +@@ -23,6 +23,7 @@ + + #include + #include ++#include + + /* It is entirely allowed to start with a getpwent call without + resetting the state of the service via a call to setpwent. +@@ -55,7 +56,7 @@ + + cmd = xasprintf ("%s/makedb -o /var/db/passwd.db /var/db/passwd.in", + support_bindir_prefix); +- system (cmd); ++ xsystem (cmd); + free (cmd); + + try_it (); +diff -Nuar a/nss/tst-nss-files-hosts-long.c b/nss/tst-nss-files-hosts-long.c +--- a/nss/tst-nss-files-hosts-long.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nss/tst-nss-files-hosts-long.c 2025-10-20 21:02:21.000000000 +0300 +@@ -28,14 +28,15 @@ + { + int ret; + +- /* Run getent to fetch the IPv4 address for host test4. +- This forces /etc/hosts to be parsed. */ +- ret = system("getent ahostsv4 test4"); ++ /* Run getent to fetch the IPv4 address for host test4. This forces ++ /etc/hosts to be parsed. Use --no-addrconfig to return addresses ++ even in an IPv6-only environment. */ ++ ret = system("getent --no-addrconfig ahostsv4 test4"); + if (ret != 0) + FAIL_EXIT1("ahostsv4 failed"); + + /* Likewise for IPv6. */ +- ret = system("getent ahostsv6 test6"); ++ ret = system("getent --no-addrconfig ahostsv6 test6"); + if (ret != 0) + FAIL_EXIT1("ahostsv6 failed"); + +diff -Nuar a/nss/tst-nss-gai-actions.c b/nss/tst-nss-gai-actions.c +--- a/nss/tst-nss-gai-actions.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/nss/tst-nss-gai-actions.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,149 @@ ++/* Test continue and merge NSS actions for getaddrinfo. ++ Copyright The GNU Toolchain Authors. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++enum ++{ ++ ACTION_MERGE = 0, ++ ACTION_CONTINUE, ++}; ++ ++static const char * ++family_str (int family) ++{ ++ switch (family) ++ { ++ case AF_UNSPEC: ++ return "AF_UNSPEC"; ++ case AF_INET: ++ return "AF_INET"; ++ default: ++ __builtin_unreachable (); ++ } ++} ++ ++static const char * ++action_str (int action) ++{ ++ switch (action) ++ { ++ case ACTION_MERGE: ++ return "merge"; ++ case ACTION_CONTINUE: ++ return "continue"; ++ default: ++ __builtin_unreachable (); ++ } ++} ++ ++static void ++do_one_test (int action, int family, bool canon) ++{ ++ struct addrinfo hints = ++ { ++ .ai_family = family, ++ }; ++ ++ struct addrinfo *ai; ++ ++ if (canon) ++ hints.ai_flags = AI_CANONNAME; ++ ++ printf ("***** Testing \"files [SUCCESS=%s] files\" for family %s, %s\n", ++ action_str (action), family_str (family), ++ canon ? "AI_CANONNAME" : ""); ++ ++ int ret = getaddrinfo ("example.org", "80", &hints, &ai); ++ ++ switch (action) ++ { ++ case ACTION_MERGE: ++ if (ret == 0) ++ { ++ char *formatted = support_format_addrinfo (ai, ret); ++ ++ printf ("merge unexpectedly succeeded:\n %s\n", formatted); ++ support_record_failure (); ++ free (formatted); ++ } ++ else ++ return; ++ case ACTION_CONTINUE: ++ { ++ char *formatted = support_format_addrinfo (ai, ret); ++ ++ /* Verify that the result appears exactly once. */ ++ const char *expected = "address: STREAM/TCP 192.0.0.1 80\n" ++ "address: DGRAM/UDP 192.0.0.1 80\n" ++ "address: RAW/IP 192.0.0.1 80\n"; ++ ++ const char *contains = strstr (formatted, expected); ++ const char *contains2 = NULL; ++ ++ if (contains != NULL) ++ contains2 = strstr (contains + strlen (expected), expected); ++ ++ if (contains == NULL || contains2 != NULL) ++ { ++ printf ("continue failed:\n%s\n", formatted); ++ support_record_failure (); ++ } ++ ++ free (formatted); ++ break; ++ } ++ default: ++ __builtin_unreachable (); ++ } ++} ++ ++static void ++do_one_test_set (int action) ++{ ++ char buf[32]; ++ ++ snprintf (buf, sizeof (buf), "files [SUCCESS=%s] files", ++ action_str (action)); ++ __nss_configure_lookup ("hosts", buf); ++ ++ do_one_test (action, AF_UNSPEC, false); ++ do_one_test (action, AF_INET, false); ++ do_one_test (action, AF_INET, true); ++} ++ ++static int ++do_test (void) ++{ ++ do_one_test_set (ACTION_CONTINUE); ++ do_one_test_set (ACTION_MERGE); ++ return 0; ++} ++ ++#include +diff -Nuar a/nss/tst-nss-gai-actions.root/etc/host.conf b/nss/tst-nss-gai-actions.root/etc/host.conf +--- a/nss/tst-nss-gai-actions.root/etc/host.conf 1970-01-01 02:00:00.000000000 +0200 ++++ b/nss/tst-nss-gai-actions.root/etc/host.conf 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1 @@ ++multi on +diff -Nuar a/nss/tst-nss-gai-actions.root/etc/hosts b/nss/tst-nss-gai-actions.root/etc/hosts +--- a/nss/tst-nss-gai-actions.root/etc/hosts 1970-01-01 02:00:00.000000000 +0200 ++++ b/nss/tst-nss-gai-actions.root/etc/hosts 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1,508 @@ ++192.0.0.1 example.org ++192.0.0.2 example.org ++192.0.0.3 example.org ++192.0.0.4 example.org ++192.0.0.5 example.org ++192.0.0.6 example.org ++192.0.0.7 example.org ++192.0.0.8 example.org ++192.0.0.9 example.org ++192.0.0.10 example.org ++192.0.0.11 example.org ++192.0.0.12 example.org ++192.0.0.13 example.org ++192.0.0.14 example.org ++192.0.0.15 example.org ++192.0.0.16 example.org ++192.0.0.17 example.org ++192.0.0.18 example.org ++192.0.0.19 example.org ++192.0.0.20 example.org ++192.0.0.21 example.org ++192.0.0.22 example.org ++192.0.0.23 example.org ++192.0.0.24 example.org ++192.0.0.25 example.org ++192.0.0.26 example.org ++192.0.0.27 example.org ++192.0.0.28 example.org ++192.0.0.29 example.org ++192.0.0.30 example.org ++192.0.0.31 example.org ++192.0.0.32 example.org ++192.0.0.33 example.org ++192.0.0.34 example.org ++192.0.0.35 example.org ++192.0.0.36 example.org ++192.0.0.37 example.org ++192.0.0.38 example.org ++192.0.0.39 example.org ++192.0.0.40 example.org ++192.0.0.41 example.org ++192.0.0.42 example.org ++192.0.0.43 example.org ++192.0.0.44 example.org ++192.0.0.45 example.org ++192.0.0.46 example.org ++192.0.0.47 example.org ++192.0.0.48 example.org ++192.0.0.49 example.org ++192.0.0.50 example.org ++192.0.0.51 example.org ++192.0.0.52 example.org ++192.0.0.53 example.org ++192.0.0.54 example.org ++192.0.0.55 example.org ++192.0.0.56 example.org ++192.0.0.57 example.org ++192.0.0.58 example.org ++192.0.0.59 example.org ++192.0.0.60 example.org ++192.0.0.61 example.org ++192.0.0.62 example.org ++192.0.0.63 example.org ++192.0.0.64 example.org ++192.0.0.65 example.org ++192.0.0.66 example.org ++192.0.0.67 example.org ++192.0.0.68 example.org ++192.0.0.69 example.org ++192.0.0.70 example.org ++192.0.0.71 example.org ++192.0.0.72 example.org ++192.0.0.73 example.org ++192.0.0.74 example.org ++192.0.0.75 example.org ++192.0.0.76 example.org ++192.0.0.77 example.org ++192.0.0.78 example.org ++192.0.0.79 example.org ++192.0.0.80 example.org ++192.0.0.81 example.org ++192.0.0.82 example.org ++192.0.0.83 example.org ++192.0.0.84 example.org ++192.0.0.85 example.org ++192.0.0.86 example.org ++192.0.0.87 example.org ++192.0.0.88 example.org ++192.0.0.89 example.org ++192.0.0.90 example.org ++192.0.0.91 example.org ++192.0.0.92 example.org ++192.0.0.93 example.org ++192.0.0.94 example.org ++192.0.0.95 example.org ++192.0.0.96 example.org ++192.0.0.97 example.org ++192.0.0.98 example.org ++192.0.0.99 example.org ++192.0.0.100 example.org ++192.0.0.101 example.org ++192.0.0.102 example.org ++192.0.0.103 example.org ++192.0.0.104 example.org ++192.0.0.105 example.org ++192.0.0.106 example.org ++192.0.0.107 example.org ++192.0.0.108 example.org ++192.0.0.109 example.org ++192.0.0.110 example.org ++192.0.0.111 example.org ++192.0.0.112 example.org ++192.0.0.113 example.org ++192.0.0.114 example.org ++192.0.0.115 example.org ++192.0.0.116 example.org ++192.0.0.117 example.org ++192.0.0.118 example.org ++192.0.0.119 example.org ++192.0.0.120 example.org ++192.0.0.121 example.org ++192.0.0.122 example.org ++192.0.0.123 example.org ++192.0.0.124 example.org ++192.0.0.125 example.org ++192.0.0.126 example.org ++192.0.0.127 example.org ++192.0.0.128 example.org ++192.0.0.129 example.org ++192.0.0.130 example.org ++192.0.0.131 example.org ++192.0.0.132 example.org ++192.0.0.133 example.org ++192.0.0.134 example.org ++192.0.0.135 example.org ++192.0.0.136 example.org ++192.0.0.137 example.org ++192.0.0.138 example.org ++192.0.0.139 example.org ++192.0.0.140 example.org ++192.0.0.141 example.org ++192.0.0.142 example.org ++192.0.0.143 example.org ++192.0.0.144 example.org ++192.0.0.145 example.org ++192.0.0.146 example.org ++192.0.0.147 example.org ++192.0.0.148 example.org ++192.0.0.149 example.org ++192.0.0.150 example.org ++192.0.0.151 example.org ++192.0.0.152 example.org ++192.0.0.153 example.org ++192.0.0.154 example.org ++192.0.0.155 example.org ++192.0.0.156 example.org ++192.0.0.157 example.org ++192.0.0.158 example.org ++192.0.0.159 example.org ++192.0.0.160 example.org ++192.0.0.161 example.org ++192.0.0.162 example.org ++192.0.0.163 example.org ++192.0.0.164 example.org ++192.0.0.165 example.org ++192.0.0.166 example.org ++192.0.0.167 example.org ++192.0.0.168 example.org ++192.0.0.169 example.org ++192.0.0.170 example.org ++192.0.0.171 example.org ++192.0.0.172 example.org ++192.0.0.173 example.org ++192.0.0.174 example.org ++192.0.0.175 example.org ++192.0.0.176 example.org ++192.0.0.177 example.org ++192.0.0.178 example.org ++192.0.0.179 example.org ++192.0.0.180 example.org ++192.0.0.181 example.org ++192.0.0.182 example.org ++192.0.0.183 example.org ++192.0.0.184 example.org ++192.0.0.185 example.org ++192.0.0.186 example.org ++192.0.0.187 example.org ++192.0.0.188 example.org ++192.0.0.189 example.org ++192.0.0.190 example.org ++192.0.0.191 example.org ++192.0.0.192 example.org ++192.0.0.193 example.org ++192.0.0.194 example.org ++192.0.0.195 example.org ++192.0.0.196 example.org ++192.0.0.197 example.org ++192.0.0.198 example.org ++192.0.0.199 example.org ++192.0.0.200 example.org ++192.0.0.201 example.org ++192.0.0.202 example.org ++192.0.0.203 example.org ++192.0.0.204 example.org ++192.0.0.205 example.org ++192.0.0.206 example.org ++192.0.0.207 example.org ++192.0.0.208 example.org ++192.0.0.209 example.org ++192.0.0.210 example.org ++192.0.0.211 example.org ++192.0.0.212 example.org ++192.0.0.213 example.org ++192.0.0.214 example.org ++192.0.0.215 example.org ++192.0.0.216 example.org ++192.0.0.217 example.org ++192.0.0.218 example.org ++192.0.0.219 example.org ++192.0.0.220 example.org ++192.0.0.221 example.org ++192.0.0.222 example.org ++192.0.0.223 example.org ++192.0.0.224 example.org ++192.0.0.225 example.org ++192.0.0.226 example.org ++192.0.0.227 example.org ++192.0.0.228 example.org ++192.0.0.229 example.org ++192.0.0.230 example.org ++192.0.0.231 example.org ++192.0.0.232 example.org ++192.0.0.233 example.org ++192.0.0.234 example.org ++192.0.0.235 example.org ++192.0.0.236 example.org ++192.0.0.237 example.org ++192.0.0.238 example.org ++192.0.0.239 example.org ++192.0.0.240 example.org ++192.0.0.241 example.org ++192.0.0.242 example.org ++192.0.0.243 example.org ++192.0.0.244 example.org ++192.0.0.245 example.org ++192.0.0.246 example.org ++192.0.0.247 example.org ++192.0.0.248 example.org ++192.0.0.249 example.org ++192.0.0.250 example.org ++192.0.0.251 example.org ++192.0.0.252 example.org ++192.0.0.253 example.org ++192.0.0.254 example.org ++192.0.1.1 example.org ++192.0.1.2 example.org ++192.0.1.3 example.org ++192.0.1.4 example.org ++192.0.1.5 example.org ++192.0.1.6 example.org ++192.0.1.7 example.org ++192.0.1.8 example.org ++192.0.1.9 example.org ++192.0.1.10 example.org ++192.0.1.11 example.org ++192.0.1.12 example.org ++192.0.1.13 example.org ++192.0.1.14 example.org ++192.0.1.15 example.org ++192.0.1.16 example.org ++192.0.1.17 example.org ++192.0.1.18 example.org ++192.0.1.19 example.org ++192.0.1.20 example.org ++192.0.1.21 example.org ++192.0.1.22 example.org ++192.0.1.23 example.org ++192.0.1.24 example.org ++192.0.1.25 example.org ++192.0.1.26 example.org ++192.0.1.27 example.org ++192.0.1.28 example.org ++192.0.1.29 example.org ++192.0.1.30 example.org ++192.0.1.31 example.org ++192.0.1.32 example.org ++192.0.1.33 example.org ++192.0.1.34 example.org ++192.0.1.35 example.org ++192.0.1.36 example.org ++192.0.1.37 example.org ++192.0.1.38 example.org ++192.0.1.39 example.org ++192.0.1.40 example.org ++192.0.1.41 example.org ++192.0.1.42 example.org ++192.0.1.43 example.org ++192.0.1.44 example.org ++192.0.1.45 example.org ++192.0.1.46 example.org ++192.0.1.47 example.org ++192.0.1.48 example.org ++192.0.1.49 example.org ++192.0.1.50 example.org ++192.0.1.51 example.org ++192.0.1.52 example.org ++192.0.1.53 example.org ++192.0.1.54 example.org ++192.0.1.55 example.org ++192.0.1.56 example.org ++192.0.1.57 example.org ++192.0.1.58 example.org ++192.0.1.59 example.org ++192.0.1.60 example.org ++192.0.1.61 example.org ++192.0.1.62 example.org ++192.0.1.63 example.org ++192.0.1.64 example.org ++192.0.1.65 example.org ++192.0.1.66 example.org ++192.0.1.67 example.org ++192.0.1.68 example.org ++192.0.1.69 example.org ++192.0.1.70 example.org ++192.0.1.71 example.org ++192.0.1.72 example.org ++192.0.1.73 example.org ++192.0.1.74 example.org ++192.0.1.75 example.org ++192.0.1.76 example.org ++192.0.1.77 example.org ++192.0.1.78 example.org ++192.0.1.79 example.org ++192.0.1.80 example.org ++192.0.1.81 example.org ++192.0.1.82 example.org ++192.0.1.83 example.org ++192.0.1.84 example.org ++192.0.1.85 example.org ++192.0.1.86 example.org ++192.0.1.87 example.org ++192.0.1.88 example.org ++192.0.1.89 example.org ++192.0.1.90 example.org ++192.0.1.91 example.org ++192.0.1.92 example.org ++192.0.1.93 example.org ++192.0.1.94 example.org ++192.0.1.95 example.org ++192.0.1.96 example.org ++192.0.1.97 example.org ++192.0.1.98 example.org ++192.0.1.99 example.org ++192.0.1.100 example.org ++192.0.1.101 example.org ++192.0.1.102 example.org ++192.0.1.103 example.org ++192.0.1.104 example.org ++192.0.1.105 example.org ++192.0.1.106 example.org ++192.0.1.107 example.org ++192.0.1.108 example.org ++192.0.1.109 example.org ++192.0.1.110 example.org ++192.0.1.111 example.org ++192.0.1.112 example.org ++192.0.1.113 example.org ++192.0.1.114 example.org ++192.0.1.115 example.org ++192.0.1.116 example.org ++192.0.1.117 example.org ++192.0.1.118 example.org ++192.0.1.119 example.org ++192.0.1.120 example.org ++192.0.1.121 example.org ++192.0.1.122 example.org ++192.0.1.123 example.org ++192.0.1.124 example.org ++192.0.1.125 example.org ++192.0.1.126 example.org ++192.0.1.127 example.org ++192.0.1.128 example.org ++192.0.1.129 example.org ++192.0.1.130 example.org ++192.0.1.131 example.org ++192.0.1.132 example.org ++192.0.1.133 example.org ++192.0.1.134 example.org ++192.0.1.135 example.org ++192.0.1.136 example.org ++192.0.1.137 example.org ++192.0.1.138 example.org ++192.0.1.139 example.org ++192.0.1.140 example.org ++192.0.1.141 example.org ++192.0.1.142 example.org ++192.0.1.143 example.org ++192.0.1.144 example.org ++192.0.1.145 example.org ++192.0.1.146 example.org ++192.0.1.147 example.org ++192.0.1.148 example.org ++192.0.1.149 example.org ++192.0.1.150 example.org ++192.0.1.151 example.org ++192.0.1.152 example.org ++192.0.1.153 example.org ++192.0.1.154 example.org ++192.0.1.155 example.org ++192.0.1.156 example.org ++192.0.1.157 example.org ++192.0.1.158 example.org ++192.0.1.159 example.org ++192.0.1.160 example.org ++192.0.1.161 example.org ++192.0.1.162 example.org ++192.0.1.163 example.org ++192.0.1.164 example.org ++192.0.1.165 example.org ++192.0.1.166 example.org ++192.0.1.167 example.org ++192.0.1.168 example.org ++192.0.1.169 example.org ++192.0.1.170 example.org ++192.0.1.171 example.org ++192.0.1.172 example.org ++192.0.1.173 example.org ++192.0.1.174 example.org ++192.0.1.175 example.org ++192.0.1.176 example.org ++192.0.1.177 example.org ++192.0.1.178 example.org ++192.0.1.179 example.org ++192.0.1.180 example.org ++192.0.1.181 example.org ++192.0.1.182 example.org ++192.0.1.183 example.org ++192.0.1.184 example.org ++192.0.1.185 example.org ++192.0.1.186 example.org ++192.0.1.187 example.org ++192.0.1.188 example.org ++192.0.1.189 example.org ++192.0.1.190 example.org ++192.0.1.191 example.org ++192.0.1.192 example.org ++192.0.1.193 example.org ++192.0.1.194 example.org ++192.0.1.195 example.org ++192.0.1.196 example.org ++192.0.1.197 example.org ++192.0.1.198 example.org ++192.0.1.199 example.org ++192.0.1.200 example.org ++192.0.1.201 example.org ++192.0.1.202 example.org ++192.0.1.203 example.org ++192.0.1.204 example.org ++192.0.1.205 example.org ++192.0.1.206 example.org ++192.0.1.207 example.org ++192.0.1.208 example.org ++192.0.1.209 example.org ++192.0.1.210 example.org ++192.0.1.211 example.org ++192.0.1.212 example.org ++192.0.1.213 example.org ++192.0.1.214 example.org ++192.0.1.215 example.org ++192.0.1.216 example.org ++192.0.1.217 example.org ++192.0.1.218 example.org ++192.0.1.219 example.org ++192.0.1.220 example.org ++192.0.1.221 example.org ++192.0.1.222 example.org ++192.0.1.223 example.org ++192.0.1.224 example.org ++192.0.1.225 example.org ++192.0.1.226 example.org ++192.0.1.227 example.org ++192.0.1.228 example.org ++192.0.1.229 example.org ++192.0.1.230 example.org ++192.0.1.231 example.org ++192.0.1.232 example.org ++192.0.1.233 example.org ++192.0.1.234 example.org ++192.0.1.235 example.org ++192.0.1.236 example.org ++192.0.1.237 example.org ++192.0.1.238 example.org ++192.0.1.239 example.org ++192.0.1.240 example.org ++192.0.1.241 example.org ++192.0.1.242 example.org ++192.0.1.243 example.org ++192.0.1.244 example.org ++192.0.1.245 example.org ++192.0.1.246 example.org ++192.0.1.247 example.org ++192.0.1.248 example.org ++192.0.1.249 example.org ++192.0.1.250 example.org ++192.0.1.251 example.org ++192.0.1.252 example.org ++192.0.1.253 example.org ++192.0.1.254 example.org +diff -Nuar a/nss/tst-nss-gai-hv2-canonname.c b/nss/tst-nss-gai-hv2-canonname.c +--- a/nss/tst-nss-gai-hv2-canonname.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/nss/tst-nss-gai-hv2-canonname.c 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1,66 @@ ++/* Test NSS query path for plugins that only implement gethostbyname2 ++ (#30843). ++ Copyright The GNU Toolchain Authors. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "nss/tst-nss-gai-hv2-canonname.h" ++ ++#define PREPARE do_prepare ++ ++static void do_prepare (int a, char **av) ++{ ++ FILE *hosts = xfopen ("/etc/hosts", "w"); ++ for (unsigned i = 2; i < 255; i++) ++ { ++ fprintf (hosts, "ff01::ff02:ff03:%u:2\ttest.example.com\n", i); ++ fprintf (hosts, "192.168.0.%u\ttest.example.com\n", i); ++ } ++ xfclose (hosts); ++} ++ ++static int ++do_test (void) ++{ ++ mtrace (); ++ ++ __nss_configure_lookup ("hosts", "test_gai_hv2_canonname"); ++ ++ struct addrinfo hints = {}; ++ struct addrinfo *result = NULL; ++ ++ hints.ai_family = AF_INET6; ++ hints.ai_flags = AI_ALL | AI_V4MAPPED | AI_CANONNAME; ++ ++ int ret = getaddrinfo (QUERYNAME, NULL, &hints, &result); ++ ++ if (ret != 0) ++ FAIL_EXIT1 ("getaddrinfo failed: %s\n", gai_strerror (ret)); ++ ++ TEST_COMPARE_STRING (result->ai_canonname, QUERYNAME); ++ ++ freeaddrinfo(result); ++ return 0; ++} ++ ++#include +diff -Nuar a/nss/tst-nss-gai-hv2-canonname.h b/nss/tst-nss-gai-hv2-canonname.h +--- a/nss/tst-nss-gai-hv2-canonname.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/nss/tst-nss-gai-hv2-canonname.h 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1 @@ ++#define QUERYNAME "test.example.com" +diff -Nuar a/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script +--- a/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script 1970-01-01 02:00:00.000000000 +0200 ++++ b/nss/tst-nss-gai-hv2-canonname.root/tst-nss-gai-hv2-canonname.script 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1,2 @@ ++cp $B/nss/libnss_test_gai_hv2_canonname.so $L/libnss_test_gai_hv2_canonname.so.2 ++su +diff -Nuar a/nss/tst-nss-test_errno.c b/nss/tst-nss-test_errno.c +--- a/nss/tst-nss-test_errno.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/nss/tst-nss-test_errno.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,61 @@ ++/* getpwent failure when dlopen clobbers errno (bug 28953). ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ __nss_configure_lookup ("passwd", "files test_errno"); ++ ++ errno = 0; ++ setpwent (); ++ TEST_COMPARE (errno, 0); ++ ++ bool root_seen = false; ++ while (true) ++ { ++ errno = 0; ++ struct passwd *e = getpwent (); ++ if (e == NULL) ++ break; ++ if (strcmp (e->pw_name, "root")) ++ root_seen = true; ++ } ++ ++ TEST_COMPARE (errno, 0); ++ TEST_VERIFY (root_seen); ++ ++ errno = 0; ++ endpwent (); ++ TEST_COMPARE (errno, 0); ++ ++ TEST_COMPARE_STRING (getenv ("_nss_test_errno_setpwent"), "yes"); ++ TEST_COMPARE_STRING (getenv ("_nss_test_errno_getpwent_r"), "yes"); ++ TEST_COMPARE_STRING (getenv ("_nss_test_errno_endpwent"), "yes"); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/nss/tst-reload1.c b/nss/tst-reload1.c +--- a/nss/tst-reload1.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nss/tst-reload1.c 2025-10-20 21:02:21.000000000 +0300 +@@ -43,12 +43,12 @@ + + static const char *hostaddr_5[] = + { +- "ABCD", "abcd", "1234", NULL ++ "ABCd", "ABCD", "ABC4", NULL + }; + + static const char *hostaddr_15[] = + { +- "4321", "ghij", NULL ++ "4321", "4322", NULL + }; + + static const char *hostaddr_25[] = +@@ -86,12 +86,12 @@ + + static const char *hostaddr_16[] = + { +- "7890", "a1b2", NULL ++ "7890", "7891", NULL + }; + + static const char *hostaddr_26[] = + { +- "qwer", "tyui", NULL ++ "qwer", "qweR", NULL + }; + + static struct hostent host_table_2[] = { +diff -Nuar a/nss/tst-reload2.c b/nss/tst-reload2.c +--- a/nss/tst-reload2.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nss/tst-reload2.c 2025-10-20 21:02:21.000000000 +0300 +@@ -95,6 +95,8 @@ + char buf1[PATH_MAX]; + char buf2[PATH_MAX]; + ++ support_need_proc ("Our xmkdirp fails if we can't map our uid, which requires /proc."); ++ + sprintf (buf1, "/subdir%s", support_slibdir_prefix); + xmkdirp (buf1, 0777); + +diff -Nuar a/nss/XXX-lookup.c b/nss/XXX-lookup.c +--- a/nss/XXX-lookup.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/nss/XXX-lookup.c 2025-10-20 21:02:21.000000000 +0300 +@@ -15,6 +15,7 @@ + License along with the GNU C Library; if not, see + . */ + ++#include + #include "nsswitch.h" + + /*******************************************************************\ +@@ -54,6 +55,10 @@ + + *ni = DATABASE_NAME_SYMBOL; + ++ /* We want to know about it if we've somehow got a NULL action list; ++ in the past, we had bad state if seccomp interfered with setup. */ ++ assert(*ni != NULL); ++ + return __nss_lookup (ni, fct_name, fct2_name, fctp); + } + libc_hidden_def (DB_LOOKUP_FCT) +diff -Nuar a/posix/fork.c b/posix/fork.c +--- a/posix/fork.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/posix/fork.c 2025-10-20 21:02:21.000000000 +0300 +@@ -46,8 +46,9 @@ + best effort to make is async-signal-safe at least for single-thread + case. */ + bool multiple_threads = __libc_single_threaded == 0; ++ uint64_t lastrun; + +- __run_fork_handlers (atfork_run_prepare, multiple_threads); ++ lastrun = __run_prefork_handlers (multiple_threads); + + struct nss_database_data nss_database_data; + +@@ -105,7 +106,7 @@ + reclaim_stacks (); + + /* Run the handlers registered for the child. */ +- __run_fork_handlers (atfork_run_child, multiple_threads); ++ __run_postfork_handlers (atfork_run_child, multiple_threads, lastrun); + } + else + { +@@ -123,7 +124,7 @@ + } + + /* Run the handlers registered for the parent. */ +- __run_fork_handlers (atfork_run_parent, multiple_threads); ++ __run_postfork_handlers (atfork_run_parent, multiple_threads, lastrun); + + if (pid < 0) + __set_errno (save_errno); +diff -Nuar a/posix/glob.c b/posix/glob.c +--- a/posix/glob.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/posix/glob.c 2025-10-20 21:02:21.000000000 +0300 +@@ -21,13 +21,14 @@ + optimizes away the pattern == NULL test below. */ + # define _GL_ARG_NONNULL(params) + +-# include ++# include + + #endif + + #include + + #include ++#include + #include + #include + #include +@@ -56,6 +57,8 @@ + # define sysconf(id) __sysconf (id) + # define closedir(dir) __closedir (dir) + # define opendir(name) __opendir (name) ++# undef dirfd ++# define dirfd(str) __dirfd (str) + # define readdir(str) __readdir64 (str) + # define getpwnam_r(name, bufp, buf, len, res) \ + __getpwnam_r (name, bufp, buf, len, res) +@@ -69,11 +72,8 @@ + # ifndef GLOB_LSTAT + # define GLOB_LSTAT gl_lstat + # endif +-# ifndef GLOB_STAT64 +-# define GLOB_STAT64 __stat64 +-# endif +-# ifndef GLOB_LSTAT64 +-# define GLOB_LSTAT64 __lstat64 ++# ifndef GLOB_FSTATAT64 ++# define GLOB_FSTATAT64 __fstatat64 + # endif + # include + #else /* !_LIBC */ +@@ -88,8 +88,7 @@ + # define struct_stat struct stat + # define struct_stat64 struct stat + # define GLOB_LSTAT gl_lstat +-# define GLOB_STAT64 stat +-# define GLOB_LSTAT64 lstat ++# define GLOB_FSTATAT64 fstatat + #endif /* _LIBC */ + + #include +@@ -215,7 +214,8 @@ + } ust; + return (__glibc_unlikely (flags & GLOB_ALTDIRFUNC) + ? pglob->GLOB_LSTAT (fullname, &ust.st) +- : GLOB_LSTAT64 (fullname, &ust.st64)); ++ : GLOB_FSTATAT64 (AT_FDCWD, fullname, &ust.st64, ++ AT_SYMLINK_NOFOLLOW)); + } + + /* Set *R = A + B. Return true if the answer is mathematically +@@ -257,7 +257,8 @@ + struct_stat64 st64; + return (__glibc_unlikely (flags & GLOB_ALTDIRFUNC) + ? pglob->gl_stat (filename, &st) == 0 && S_ISDIR (st.st_mode) +- : GLOB_STAT64 (filename, &st64) == 0 && S_ISDIR (st64.st_mode)); ++ : (GLOB_FSTATAT64 (AT_FDCWD, filename, &st64, 0) == 0 ++ && S_ISDIR (st64.st_mode))); + } + + /* Find the end of the sub-pattern in a brace expression. */ +@@ -747,6 +748,8 @@ + else + { + #ifndef WINDOWS32 ++ /* Recognize ~user as a shorthand for the specified user's home ++ directory. */ + char *end_name = strchr (dirname, '/'); + char *user_name; + int malloc_user_name = 0; +@@ -885,7 +888,22 @@ + } + scratch_buffer_free (&pwtmpbuf); + } +-#endif /* !WINDOWS32 */ ++#else /* WINDOWS32 */ ++ /* On native Windows, access to a user's home directory ++ (via GetUserProfileDirectory) or to a user's environment ++ variables (via ExpandEnvironmentStringsForUser) requires ++ the credentials of the user. Therefore we cannot support ++ the ~user syntax on this platform. ++ Handling ~user specially (and treat it like plain ~) if ++ user is getenv ("USERNAME") would not be a good idea, ++ since it would make people think that ~user is supported ++ in general. */ ++ if (flags & GLOB_TILDE_CHECK) ++ { ++ retval = GLOB_NOMATCH; ++ goto out; ++ } ++#endif /* WINDOWS32 */ + } + } + +@@ -1266,6 +1284,8 @@ + { + size_t dirlen = strlen (directory); + void *stream = NULL; ++ struct scratch_buffer s; ++ scratch_buffer_init (&s); + # define GLOBNAMES_MEMBERS(nnames) \ + struct globnames *next; size_t count; char *name[nnames]; + struct globnames { GLOBNAMES_MEMBERS (FLEXIBLE_ARRAY_MEMBER) }; +@@ -1337,6 +1357,7 @@ + } + else + { ++ int dfd = dirfd (stream); + int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) + | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)); + flags |= GLOB_MAGCHAR; +@@ -1364,8 +1385,32 @@ + if (flags & GLOB_ONLYDIR) + switch (readdir_result_type (d)) + { +- case DT_DIR: case DT_LNK: case DT_UNKNOWN: break; + default: continue; ++ case DT_DIR: break; ++ case DT_LNK: case DT_UNKNOWN: ++ /* The filesystem was too lazy to give us a hint, ++ so we have to do it the hard way. */ ++ if (__glibc_unlikely (dfd < 0 || flags & GLOB_ALTDIRFUNC)) ++ { ++ size_t namelen = strlen (d.name); ++ size_t need = dirlen + 1 + namelen + 1; ++ if (s.length < need ++ && !scratch_buffer_set_array_size (&s, need, 1)) ++ goto memory_error; ++ char *p = mempcpy (s.data, directory, dirlen); ++ *p = '/'; ++ p += p[-1] != '/'; ++ memcpy (p, d.name, namelen + 1); ++ if (! is_dir (s.data, flags, pglob)) ++ continue; ++ } ++ else ++ { ++ struct_stat64 st64; ++ if (! (GLOB_FSTATAT64 (dfd, d.name, &st64, 0) == 0 ++ && S_ISDIR (st64.st_mode))) ++ continue; ++ } + } + + if (fnmatch (pattern, d.name, fnm_flags) == 0) +@@ -1497,5 +1542,6 @@ + __set_errno (save); + } + ++ scratch_buffer_free (&s); + return result; + } +diff -Nuar a/posix/Makefile b/posix/Makefile +--- a/posix/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/posix/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -109,7 +109,7 @@ + tst-glob-tilde test-ssize-max tst-spawn4 bug-regex37 \ + bug-regex38 tst-regcomp-truncated tst-spawn-chdir \ + tst-wordexp-nocmd tst-execveat tst-spawn5 \ +- tst-sched_getaffinity tst-spawn6 ++ tst-sched_getaffinity tst-spawn6 tst-regcomp-bracket-free + + # Test for the glob symbol version that was replaced in glibc 2.27. + ifeq ($(have-GLIBC_2.26)$(build-shared),yesyes) +diff -Nuar a/posix/regcomp.c b/posix/regcomp.c +--- a/posix/regcomp.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/posix/regcomp.c 2025-10-20 21:02:21.000000000 +0300 +@@ -3383,6 +3383,7 @@ + { + #ifdef RE_ENABLE_I18N + free_charset (mbcset); ++ mbcset = NULL; + #endif + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; +@@ -3398,7 +3399,8 @@ + parse_bracket_exp_free_return: + re_free (sbcset); + #ifdef RE_ENABLE_I18N +- free_charset (mbcset); ++ if (__glibc_likely (mbcset != NULL)) ++ free_charset (mbcset); + #endif /* RE_ENABLE_I18N */ + return NULL; + } +diff -Nuar a/posix/register-atfork.c b/posix/register-atfork.c +--- a/posix/register-atfork.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/posix/register-atfork.c 2025-10-20 21:02:21.000000000 +0300 +@@ -18,6 +18,8 @@ + #include + #include + #include ++#include ++#include + + #define DYNARRAY_ELEMENT struct fork_handler + #define DYNARRAY_STRUCT fork_handler_list +@@ -26,7 +28,7 @@ + #include + + static struct fork_handler_list fork_handlers; +-static bool fork_handler_init = false; ++static uint64_t fork_handler_counter; + + static int atfork_lock = LLL_LOCK_INITIALIZER; + +@@ -36,11 +38,8 @@ + { + lll_lock (atfork_lock, LLL_PRIVATE); + +- if (!fork_handler_init) +- { +- fork_handler_list_init (&fork_handlers); +- fork_handler_init = true; +- } ++ if (fork_handler_counter == 0) ++ fork_handler_list_init (&fork_handlers); + + struct fork_handler *newp = fork_handler_list_emplace (&fork_handlers); + if (newp != NULL) +@@ -49,6 +48,13 @@ + newp->parent_handler = parent; + newp->child_handler = child; + newp->dso_handle = dso_handle; ++ ++ /* IDs assigned to handlers start at 1 and increment with handler ++ registration. Un-registering a handlers discards the corresponding ++ ID. It is not reused in future registrations. */ ++ if (INT_ADD_OVERFLOW (fork_handler_counter, 1)) ++ __libc_fatal ("fork handler counter overflow"); ++ newp->id = ++fork_handler_counter; + } + + /* Release the lock. */ +@@ -103,37 +109,111 @@ + lll_unlock (atfork_lock, LLL_PRIVATE); + } + +-void +-__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking) ++uint64_t ++__run_prefork_handlers (_Bool do_locking) + { +- struct fork_handler *runp; ++ uint64_t lastrun; + +- if (who == atfork_run_prepare) ++ if (do_locking) ++ lll_lock (atfork_lock, LLL_PRIVATE); ++ ++ /* We run prepare handlers from last to first. After fork, only ++ handlers up to the last handler found here (pre-fork) will be run. ++ Handlers registered during __run_prefork_handlers or ++ __run_postfork_handlers will be positioned after this last handler, and ++ since their prepare handlers won't be run now, their parent/child ++ handlers should also be ignored. */ ++ lastrun = fork_handler_counter; ++ ++ size_t sl = fork_handler_list_size (&fork_handlers); ++ for (size_t i = sl; i > 0;) + { +- if (do_locking) +- lll_lock (atfork_lock, LLL_PRIVATE); +- size_t sl = fork_handler_list_size (&fork_handlers); +- for (size_t i = sl; i > 0; i--) +- { +- runp = fork_handler_list_at (&fork_handlers, i - 1); +- if (runp->prepare_handler != NULL) +- runp->prepare_handler (); +- } ++ struct fork_handler *runp ++ = fork_handler_list_at (&fork_handlers, i - 1); ++ ++ uint64_t id = runp->id; ++ ++ if (runp->prepare_handler != NULL) ++ { ++ if (do_locking) ++ lll_unlock (atfork_lock, LLL_PRIVATE); ++ ++ runp->prepare_handler (); ++ ++ if (do_locking) ++ lll_lock (atfork_lock, LLL_PRIVATE); ++ } ++ ++ /* We unlocked, ran the handler, and locked again. In the ++ meanwhile, one or more deregistrations could have occurred leading ++ to the current (just run) handler being moved up the list or even ++ removed from the list itself. Since handler IDs are guaranteed to ++ to be in increasing order, the next handler has to have: */ ++ ++ /* A. An earlier position than the current one has. */ ++ i--; ++ ++ /* B. A lower ID than the current one does. The code below skips ++ any newly added handlers with higher IDs. */ ++ while (i > 0 ++ && fork_handler_list_at (&fork_handlers, i - 1)->id >= id) ++ i--; + } +- else ++ ++ return lastrun; ++} ++ ++void ++__run_postfork_handlers (enum __run_fork_handler_type who, _Bool do_locking, ++ uint64_t lastrun) ++{ ++ size_t sl = fork_handler_list_size (&fork_handlers); ++ for (size_t i = 0; i < sl;) + { +- size_t sl = fork_handler_list_size (&fork_handlers); +- for (size_t i = 0; i < sl; i++) +- { +- runp = fork_handler_list_at (&fork_handlers, i); +- if (who == atfork_run_child && runp->child_handler) +- runp->child_handler (); +- else if (who == atfork_run_parent && runp->parent_handler) +- runp->parent_handler (); +- } ++ struct fork_handler *runp = fork_handler_list_at (&fork_handlers, i); ++ uint64_t id = runp->id; ++ ++ /* prepare handlers were not run for handlers with ID > LASTRUN. ++ Thus, parent/child handlers will also not be run. */ ++ if (id > lastrun) ++ break; ++ + if (do_locking) +- lll_unlock (atfork_lock, LLL_PRIVATE); ++ lll_unlock (atfork_lock, LLL_PRIVATE); ++ ++ if (who == atfork_run_child && runp->child_handler) ++ runp->child_handler (); ++ else if (who == atfork_run_parent && runp->parent_handler) ++ runp->parent_handler (); ++ ++ if (do_locking) ++ lll_lock (atfork_lock, LLL_PRIVATE); ++ ++ /* We unlocked, ran the handler, and locked again. In the meanwhile, ++ one or more [de]registrations could have occurred. Due to this, ++ the list size must be updated. */ ++ sl = fork_handler_list_size (&fork_handlers); ++ ++ /* The just-run handler could also have moved up the list. */ ++ ++ if (sl > i && fork_handler_list_at (&fork_handlers, i)->id == id) ++ /* The position of the recently run handler hasn't changed. The ++ next handler to be run is an easy increment away. */ ++ i++; ++ else ++ { ++ /* The next handler to be run is the first handler in the list ++ to have an ID higher than the current one. */ ++ for (i = 0; i < sl; i++) ++ { ++ if (fork_handler_list_at (&fork_handlers, i)->id > id) ++ break; ++ } ++ } + } ++ ++ if (do_locking) ++ lll_unlock (atfork_lock, LLL_PRIVATE); + } + + +diff -Nuar a/posix/tst-regcomp-bracket-free.c b/posix/tst-regcomp-bracket-free.c +--- a/posix/tst-regcomp-bracket-free.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/posix/tst-regcomp-bracket-free.c 2025-10-20 21:02:18.000000000 +0300 +@@ -0,0 +1,176 @@ ++/* Test regcomp bracket parsing with injected allocation failures (bug 33185). ++ Copyright (C) 2025 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 ++ . */ ++ ++/* This test invokes regcomp multiple times, failing one memory ++ allocation in each call. The function call should fail with ++ REG_ESPACE (or succeed if it can recover from the allocation ++ failure). Previously, there was double-free bug. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Data structure allocated via MAP_SHARED, so that writes from the ++ subprocess are visible. */ ++struct shared_data ++{ ++ /* Number of tracked allocations performed so far. */ ++ volatile unsigned int allocation_count; ++ ++ /* If this number is reached, one allocation fails. */ ++ volatile unsigned int failing_allocation; ++ ++ /* The subprocess stores the expected name here. */ ++ char name[100]; ++}; ++ ++/* Allocation count in shared mapping. */ ++static struct shared_data *shared; ++ ++/* Returns true if a failure should be injected for this allocation. */ ++static bool ++fail_this_allocation (void) ++{ ++ if (shared != NULL) ++ { ++ unsigned int count = shared->allocation_count; ++ shared->allocation_count = count + 1; ++ return count == shared->failing_allocation; ++ } ++ else ++ return false; ++} ++ ++/* Failure-injecting wrappers for allocation functions used by glibc. */ ++ ++void * ++malloc (size_t size) ++{ ++ if (fail_this_allocation ()) ++ { ++ errno = ENOMEM; ++ return NULL; ++ } ++ extern __typeof (malloc) __libc_malloc; ++ return __libc_malloc (size); ++} ++ ++void * ++calloc (size_t a, size_t b) ++{ ++ if (fail_this_allocation ()) ++ { ++ errno = ENOMEM; ++ return NULL; ++ } ++ extern __typeof (calloc) __libc_calloc; ++ return __libc_calloc (a, b); ++} ++ ++void * ++realloc (void *ptr, size_t size) ++{ ++ if (fail_this_allocation ()) ++ { ++ errno = ENOMEM; ++ return NULL; ++ } ++ extern __typeof (realloc) __libc_realloc; ++ return __libc_realloc (ptr, size); ++} ++ ++/* No-op subprocess to verify that support_isolate_in_subprocess does ++ not perform any heap allocations. */ ++static void ++no_op (void *ignored) ++{ ++} ++ ++/* Perform a regcomp call in a subprocess. Used to count its ++ allocations. */ ++static void ++initialize (void *regexp1) ++{ ++ const char *regexp = regexp1; ++ ++ shared->allocation_count = 0; ++ ++ regex_t reg; ++ TEST_COMPARE (regcomp (®, regexp, 0), 0); ++} ++ ++/* Perform regcomp in a subprocess with fault injection. */ ++static void ++test_in_subprocess (void *regexp1) ++{ ++ const char *regexp = regexp1; ++ unsigned int inject_at = shared->failing_allocation; ++ ++ regex_t reg; ++ int ret = regcomp (®, regexp, 0); ++ ++ if (ret != 0) ++ { ++ TEST_COMPARE (ret, REG_ESPACE); ++ printf ("info: allocation %u failure results in return value %d," ++ " error %s (%d)\n", ++ inject_at, ret, strerrorname_np (errno), errno); ++ } ++} ++ ++static int ++do_test (void) ++{ ++ char regexp[] = "[:alpha:]"; ++ ++ shared = support_shared_allocate (sizeof (*shared)); ++ ++ /* Disable fault injection. */ ++ shared->failing_allocation = ~0U; ++ ++ support_isolate_in_subprocess (no_op, NULL); ++ TEST_COMPARE (shared->allocation_count, 0); ++ ++ support_isolate_in_subprocess (initialize, regexp); ++ ++ /* The number of allocations in the successful case, plus some ++ slack. Once the number of expected allocations is exceeded, ++ injecting further failures does not make a difference. */ ++ unsigned int maximum_allocation_count = shared->allocation_count; ++ printf ("info: successful call performs %u allocations\n", ++ maximum_allocation_count); ++ maximum_allocation_count += 10; ++ ++ for (unsigned int inject_at = 0; inject_at <= maximum_allocation_count; ++ ++inject_at) ++ { ++ shared->allocation_count = 0; ++ shared->failing_allocation = inject_at; ++ support_isolate_in_subprocess (test_in_subprocess, regexp); ++ } ++ ++ support_shared_free (shared); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/posix/tst-spawn6.c b/posix/tst-spawn6.c +--- a/posix/tst-spawn6.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/posix/tst-spawn6.c 2025-10-20 21:02:21.000000000 +0300 +@@ -29,7 +29,14 @@ + #include + #include + #include ++#include + #include ++#include ++ ++#ifndef PATH_MAX ++# define PATH_MAX 1024 ++#endif ++static char ptmxpath[PATH_MAX]; + + static int + handle_restart (const char *argv1, const char *argv2) +@@ -115,7 +122,7 @@ + } + + static int +-do_test (int argc, char *argv[]) ++run_test (int argc, char *argv[]) + { + /* We must have either: + - four parameters left if called initially: +@@ -127,16 +134,7 @@ + + --setgrpr optional + */ + +- if (restart) +- return handle_restart (argv[1], argv[2]); +- +- int tcfd = open64 (_PATH_TTY, O_RDONLY, 0600); +- if (tcfd == -1) +- { +- if (errno == ENXIO) +- FAIL_UNSUPPORTED ("terminal not available, skipping test"); +- FAIL_EXIT1 ("open64 (\"%s\", 0x%x, 0600): %m", _PATH_TTY, O_RDONLY); +- } ++ int tcfd = xopen (ptmxpath, O_RDONLY, 0600); + + /* Check setting the controlling terminal without changing the group. */ + { +@@ -198,5 +196,47 @@ + return 0; + } + ++static int ++do_test (int argc, char *argv[]) ++{ ++ if (restart) ++ return handle_restart (argv[1], argv[2]); ++ ++ pid_t pid = xfork (); ++ if (pid == 0) ++ { ++ /* Create a pseudo-terminal to avoid interfering with the one using by ++ test itself, creates a new session (so there is no controlling ++ terminal), and set the pseudo-terminal as the controlling one. */ ++ int ptmx = posix_openpt (0); ++ if (ptmx == -1) ++ { ++ if (errno == ENXIO) ++ FAIL_UNSUPPORTED ("terminal not available, skipping test"); ++ FAIL_EXIT1 ("posix_openpt (0): %m"); ++ } ++ TEST_VERIFY_EXIT (grantpt (ptmx) == 0); ++ TEST_VERIFY_EXIT (unlockpt (ptmx) == 0); ++ ++ TEST_VERIFY_EXIT (setsid () != -1); ++ TEST_VERIFY_EXIT (ioctl (ptmx, TIOCSCTTY, NULL) == 0); ++ while (dup2 (ptmx, STDIN_FILENO) == -1 && errno == EBUSY) ++ ; ++ while (dup2 (ptmx, STDOUT_FILENO) == -1 && errno == EBUSY) ++ ; ++ while (dup2 (ptmx, STDERR_FILENO) == -1 && errno == EBUSY) ++ ; ++ TEST_VERIFY_EXIT (ptsname_r (ptmx, ptmxpath, sizeof ptmxpath) == 0); ++ xclose (ptmx); ++ ++ run_test (argc, argv); ++ _exit (0); ++ } ++ int status; ++ xwaitpid (pid, &status, 0); ++ TEST_VERIFY (WIFEXITED (status)); ++ exit (0); ++} ++ + #define TEST_FUNCTION_ARGV do_test + #include +diff -Nuar a/posix/tst-truncate-common.c b/posix/tst-truncate-common.c +--- a/posix/tst-truncate-common.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/posix/tst-truncate-common.c 2025-10-20 21:02:21.000000000 +0300 +@@ -21,6 +21,8 @@ + #include + #include + ++#include ++ + static void do_prepare (void); + #define PREPARE(argc, argv) do_prepare () + static int do_test (void); +@@ -42,9 +44,6 @@ + } + } + +-#define FAIL(str) \ +- do { printf ("error: %s (line %d)\n", str, __LINE__); return 1; } while (0) +- + static int + do_test_with_offset (off_t offset) + { +@@ -54,35 +53,35 @@ + memset (buf, 0xcf, sizeof (buf)); + + if (pwrite (temp_fd, buf, sizeof (buf), offset) != sizeof (buf)) +- FAIL ("write failed"); ++ FAIL_RET ("write failed"); + if (fstat (temp_fd, &st) < 0 || st.st_size != (offset + sizeof (buf))) +- FAIL ("initial size wrong"); ++ FAIL_RET ("initial size wrong"); + + if (ftruncate (temp_fd, offset + 800) < 0) +- FAIL ("size reduction with ftruncate failed"); ++ FAIL_RET ("size reduction with ftruncate failed"); + if (fstat (temp_fd, &st) < 0 || st.st_size != (offset + 800)) +- FAIL ("size after reduction with ftruncate is incorrect"); ++ FAIL_RET ("size after reduction with ftruncate is incorrect"); + + /* The following test covers more than POSIX. POSIX does not require + that ftruncate() can increase the file size. But we are testing + Unix systems. */ + if (ftruncate (temp_fd, offset + 1200) < 0) +- FAIL ("size increate with ftruncate failed"); ++ FAIL_RET ("size increate with ftruncate failed"); + if (fstat (temp_fd, &st) < 0 || st.st_size != (offset + 1200)) +- FAIL ("size after increase is incorrect"); ++ FAIL_RET ("size after increase is incorrect"); + + if (truncate (temp_filename, offset + 800) < 0) +- FAIL ("size reduction with truncate failed"); ++ FAIL_RET ("size reduction with truncate failed"); + if (fstat (temp_fd, &st) < 0 || st.st_size != (offset + 800)) +- FAIL ("size after reduction with truncate incorrect"); ++ FAIL_RET ("size after reduction with truncate incorrect"); + + /* The following test covers more than POSIX. POSIX does not require + that truncate() can increase the file size. But we are testing + Unix systems. */ + if (truncate (temp_filename, (offset + 1200)) < 0) +- FAIL ("size increase with truncate failed"); ++ FAIL_RET ("size increase with truncate failed"); + if (fstat (temp_fd, &st) < 0 || st.st_size != (offset + 1200)) +- FAIL ("size increase with truncate is incorrect"); ++ FAIL_RET ("size increase with truncate is incorrect"); + + return 0; + } +diff -Nuar a/resolv/Makefile b/resolv/Makefile +--- a/resolv/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/resolv/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -40,12 +40,16 @@ + inet_pton \ + ns_makecanon \ + ns_name_compress \ ++ ns_name_length_uncompressed \ + ns_name_ntop \ + ns_name_pack \ + ns_name_pton \ + ns_name_skip \ + ns_name_uncompress \ + ns_name_unpack \ ++ ns_rr_cursor_init \ ++ ns_rr_cursor_next \ ++ ns_samebinaryname \ + ns_samename \ + nsap_addr \ + nss_dns_functions \ +@@ -79,11 +83,6 @@ + extra-libs := libresolv libnss_dns + ifeq ($(have-thread-library),yes) + routines += gai_sigqueue +-endif +- +-ifeq ($(have-GLIBC_2.34)$(have-thread-library),yesyes) +-# Empty compatibility library for old binaries. +-extra-libs += libanl + + tests += \ + tst-bug18665 \ +@@ -93,13 +92,18 @@ + tst-ns_name_pton \ + tst-res_hconf_reorder \ + tst-res_hnok \ ++ tst-resolv-aliases \ + tst-resolv-basic \ + tst-resolv-binary \ ++ tst-resolv-byaddr \ + tst-resolv-edns \ ++ tst-resolv-invalid-cname \ + tst-resolv-network \ + tst-resolv-nondecimal \ + tst-resolv-res_init-multi \ + tst-resolv-search \ ++ tst-resolv-semi-failure \ ++ tst-resolv-short-response \ + tst-resolv-trailing \ + + # This test calls __res_context_send directly, which is not exported +@@ -107,6 +111,18 @@ + tests-internal += tst-resolv-txnid-collision + tests-static += tst-resolv-txnid-collision + ++# Likewise for __ns_samebinaryname. ++tests-internal += tst-ns_samebinaryname ++tests-static += tst-ns_samebinaryname ++ ++# Likewise for __ns_name_length_uncompressed. ++tests-internal += tst-ns_name_length_uncompressed ++tests-static += tst-ns_name_length_uncompressed ++ ++# Likewise for struct ns_rr_cursor and its functions. ++tests-internal += tst-ns_rr_cursor ++tests-static += tst-ns_rr_cursor ++ + # These tests need libdl. + ifeq (yes,$(build-shared)) + tests += \ +@@ -144,7 +160,8 @@ + + # This test has dropped packet tests and runs for a long time. + xtests += tst-resolv-rotate +-endif ++endif # $(have-thread-library) ++ + extra-libs-others = $(extra-libs) + libresolv-routines := \ + base64 \ +@@ -168,6 +185,13 @@ + resolv-deprecated \ + # libresolv-routines + ++ifeq ($(have-GLIBC_2.34)$(have-thread-library),yesyes) ++# Empty compatibility library for old binaries. ++extra-libs += libanl ++libanl-routines += libanl-compat ++libanl-shared-only-routines += libanl-compat ++endif ++ + $(libanl-routines-var) += \ + gai_cancel \ + gai_error \ +@@ -177,9 +201,6 @@ + getaddrinfo_a \ + # $(libanl-routines-var) + +-libanl-routines += libanl-compat +-libanl-shared-only-routines += libanl-compat +- + # Pretend that libanl.so is a linker script, so that the symbolic link + # is not installed. + install-lib-ldscripts = libanl.so +@@ -256,8 +277,10 @@ + $(objpfx)tst-resolv-ai_idn-latin1.out: $(gen-locales) + $(objpfx)tst-resolv-ai_idn-nolibidn2.out: \ + $(gen-locales) $(objpfx)tst-no-libidn2.so ++$(objpfx)tst-resolv-aliases: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-binary: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-byaddr: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-res_init: $(objpfx)libresolv.so +@@ -265,10 +288,16 @@ + $(shared-thread-library) + $(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \ + $(shared-thread-library) ++$(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \ ++ $(shared-thread-library) + $(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-search: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-semi-failure: $(objpfx)libresolv.so \ ++ $(shared-thread-library) ++$(objpfx)tst-resolv-short-response: $(objpfx)libresolv.so \ ++ $(shared-thread-library) + $(objpfx)tst-resolv-trailing: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-threads: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-txnid-collision: $(objpfx)libresolv.a \ +diff -Nuar a/resolv/mapv4v6addr.h b/resolv/mapv4v6addr.h +--- a/resolv/mapv4v6addr.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/resolv/mapv4v6addr.h 1970-01-01 02:00:00.000000000 +0200 +@@ -1,69 +0,0 @@ +-/* +- * ++Copyright++ 1985, 1988, 1993 +- * - +- * Copyright (c) 1985, 1988, 1993 +- * The Regents of the University of California. All rights reserved. +- * +- * Redistribution and use in source and binary forms, with or without +- * modification, are permitted provided that the following conditions +- * are met: +- * 1. Redistributions of source code must retain the above copyright +- * notice, this list of conditions and the following disclaimer. +- * 2. Redistributions in binary form must reproduce the above copyright +- * notice, this list of conditions and the following disclaimer in the +- * documentation and/or other materials provided with the distribution. +- * 4. Neither the name of the University nor the names of its contributors +- * may be used to endorse or promote products derived from this software +- * without specific prior written permission. +- * +- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +- * SUCH DAMAGE. +- * - +- * Portions Copyright (c) 1993 by Digital Equipment Corporation. +- * +- * Permission to use, copy, modify, and distribute this software for any +- * purpose with or without fee is hereby granted, provided that the above +- * copyright notice and this permission notice appear in all copies, and that +- * the name of Digital Equipment Corporation not be used in advertising or +- * publicity pertaining to distribution of the document or software without +- * specific, written prior permission. +- * +- * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL +- * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES +- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT +- * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +- * SOFTWARE. +- * - +- * --Copyright-- +- */ +- +-#include +-#include +- +-static void +-map_v4v6_address (const char *src, char *dst) +-{ +- u_char *p = (u_char *) dst; +- int i; +- +- /* Move the IPv4 part to the right position. */ +- memcpy (dst + 12, src, INADDRSZ); +- +- /* Mark this ipv6 addr as a mapped ipv4. */ +- for (i = 0; i < 10; i++) +- *p++ = 0x00; +- *p++ = 0xff; +- *p = 0xff; +-} +diff -Nuar a/resolv/mapv4v6hostent.h b/resolv/mapv4v6hostent.h +--- a/resolv/mapv4v6hostent.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/resolv/mapv4v6hostent.h 1970-01-01 02:00:00.000000000 +0200 +@@ -1,84 +0,0 @@ +-/* +- * ++Copyright++ 1985, 1988, 1993 +- * - +- * Copyright (c) 1985, 1988, 1993 +- * The Regents of the University of California. All rights reserved. +- * +- * Redistribution and use in source and binary forms, with or without +- * modification, are permitted provided that the following conditions +- * are met: +- * 1. Redistributions of source code must retain the above copyright +- * notice, this list of conditions and the following disclaimer. +- * 2. Redistributions in binary form must reproduce the above copyright +- * notice, this list of conditions and the following disclaimer in the +- * documentation and/or other materials provided with the distribution. +- * 4. Neither the name of the University nor the names of its contributors +- * may be used to endorse or promote products derived from this software +- * without specific prior written permission. +- * +- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +- * SUCH DAMAGE. +- * - +- * Portions Copyright (c) 1993 by Digital Equipment Corporation. +- * +- * Permission to use, copy, modify, and distribute this software for any +- * purpose with or without fee is hereby granted, provided that the above +- * copyright notice and this permission notice appear in all copies, and that +- * the name of Digital Equipment Corporation not be used in advertising or +- * publicity pertaining to distribution of the document or software without +- * specific, written prior permission. +- * +- * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL +- * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES +- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT +- * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +- * SOFTWARE. +- * - +- * --Copyright-- +- */ +- +-#include +-#include +- +-typedef union { +- int32_t al; +- char ac; +-} align; +- +-static int +-map_v4v6_hostent (struct hostent *hp, char **bpp, int *lenp) +-{ +- char **ap; +- +- if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ) +- return 0; +- hp->h_addrtype = AF_INET6; +- hp->h_length = IN6ADDRSZ; +- for (ap = hp->h_addr_list; *ap; ap++) +- { +- int i = sizeof (align) - ((u_long) *bpp % sizeof (align)); +- +- if (*lenp < (i + IN6ADDRSZ)) +- /* Out of memory. */ +- return 1; +- *bpp += i; +- *lenp -= i; +- map_v4v6_address (*ap, *bpp); +- *ap = *bpp; +- *bpp += IN6ADDRSZ; +- *lenp -= IN6ADDRSZ; +- } +- return 0; +-} +diff -Nuar a/resolv/ns_name_length_uncompressed.c b/resolv/ns_name_length_uncompressed.c +--- a/resolv/ns_name_length_uncompressed.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/ns_name_length_uncompressed.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,72 @@ ++/* Skip over an uncompressed name in wire format. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++ ++int ++__ns_name_length_uncompressed (const unsigned char *p, ++ const unsigned char *eom) ++{ ++ const unsigned char *start = p; ++ ++ while (true) ++ { ++ if (p == eom) ++ { ++ /* Truncated packet: no room for label length. */ ++ __set_errno (EMSGSIZE); ++ return -1; ++ } ++ ++ unsigned char b = *p; ++ ++p; ++ if (b == 0) ++ { ++ /* Root label. */ ++ size_t length = p - start; ++ if (length > NS_MAXCDNAME) ++ { ++ /* Domain name too long. */ ++ __set_errno (EMSGSIZE); ++ return -1; ++ } ++ return length; ++ } ++ ++ if (b <= 63) ++ { ++ /* Regular label. */ ++ if (b <= eom - p) ++ p += b; ++ else ++ { ++ /* Truncated packet: label incomplete. */ ++ __set_errno (EMSGSIZE); ++ return -1; ++ } ++ } ++ else ++ { ++ /* Compression reference or corrupted label length. */ ++ __set_errno (EMSGSIZE); ++ return -1; ++ } ++ } ++} +diff -Nuar a/resolv/ns_rr_cursor_init.c b/resolv/ns_rr_cursor_init.c +--- a/resolv/ns_rr_cursor_init.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/ns_rr_cursor_init.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,62 @@ ++/* Initialize a simple DNS packet parser. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++bool ++__ns_rr_cursor_init (struct ns_rr_cursor *c, ++ const unsigned char *buf, size_t len) ++{ ++ c->begin = buf; ++ c->end = buf + len; ++ ++ /* Check for header size and 16-bit question count value (it must be 1). */ ++ if (len < 12 || buf[4] != 0 || buf[5] != 1) ++ { ++ __set_errno (EMSGSIZE); ++ c->current = c->end; ++ return false; ++ } ++ c->current = buf + 12; ++ ++ int consumed = __ns_name_length_uncompressed (c->current, c->end); ++ if (consumed < 0) ++ { ++ __set_errno (EMSGSIZE); ++ c->current = c->end; ++ c->first_rr = NULL; ++ return false; ++ } ++ c->current += consumed; ++ ++ /* Ensure there is room for question type and class. */ ++ if (c->end - c->current < 4) ++ { ++ __set_errno (EMSGSIZE); ++ c->current = c->end; ++ c->first_rr = NULL; ++ return false; ++ } ++ c->current += 4; ++ c->first_rr = c->current; ++ ++ return true; ++} +diff -Nuar a/resolv/ns_rr_cursor_next.c b/resolv/ns_rr_cursor_next.c +--- a/resolv/ns_rr_cursor_next.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/ns_rr_cursor_next.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,74 @@ ++/* Simple DNS record parser without textual name decoding. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++bool ++__ns_rr_cursor_next (struct ns_rr_cursor *c, struct ns_rr_wire *rr) ++{ ++ rr->rdata = NULL; ++ ++ /* Extract the record owner name. */ ++ int consumed = __ns_name_unpack (c->begin, c->end, c->current, ++ rr->rname, sizeof (rr->rname)); ++ if (consumed < 0) ++ { ++ memset (rr, 0, sizeof (*rr)); ++ __set_errno (EMSGSIZE); ++ return false; ++ } ++ c->current += consumed; ++ ++ /* Extract the metadata. */ ++ struct ++ { ++ uint16_t rtype; ++ uint16_t rclass; ++ uint32_t ttl; ++ uint16_t rdlength; ++ } __attribute__ ((packed)) metadata; ++ _Static_assert (sizeof (metadata) == 10, "sizeof metadata"); ++ if (c->end - c->current < sizeof (metadata)) ++ { ++ memset (rr, 0, sizeof (*rr)); ++ __set_errno (EMSGSIZE); ++ return false; ++ } ++ memcpy (&metadata, c->current, sizeof (metadata)); ++ c->current += sizeof (metadata); ++ /* Endianess conversion. */ ++ rr->rtype = ntohs (metadata.rtype); ++ rr->rclass = ntohs (metadata.rclass); ++ rr->ttl = ntohl (metadata.ttl); ++ rr->rdlength = ntohs (metadata.rdlength); ++ ++ /* Extract record data. */ ++ if (c->end - c->current < rr->rdlength) ++ { ++ memset (rr, 0, sizeof (*rr)); ++ __set_errno (EMSGSIZE); ++ return false; ++ } ++ rr->rdata = c->current; ++ c->current += rr->rdlength; ++ ++ return true; ++} +diff -Nuar a/resolv/ns_samebinaryname.c b/resolv/ns_samebinaryname.c +--- a/resolv/ns_samebinaryname.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/ns_samebinaryname.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,55 @@ ++/* Compare two binary domain names for quality. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++ ++/* Convert ASCII letters to upper case. */ ++static inline int ++ascii_toupper (unsigned char ch) ++{ ++ if (ch >= 'a' && ch <= 'z') ++ return ch - 'a' + 'A'; ++ else ++ return ch; ++} ++ ++bool ++__ns_samebinaryname (const unsigned char *a, const unsigned char *b) ++{ ++ while (*a != 0 && *b != 0) ++ { ++ if (*a != *b) ++ /* Different label length. */ ++ return false; ++ int labellen = *a; ++ ++a; ++ ++b; ++ for (int i = 0; i < labellen; ++i) ++ { ++ if (*a != *b && ascii_toupper (*a) != ascii_toupper (*b)) ++ /* Different character in label. */ ++ return false; ++ ++a; ++ ++b; ++ } ++ } ++ ++ /* Match if both names are at the root label. */ ++ return *a == 0 && *b == 0; ++} +diff -Nuar a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c +--- a/resolv/nss_dns/dns-host.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/resolv/nss_dns/dns-host.c 2025-10-20 21:02:21.000000000 +0300 +@@ -69,6 +69,7 @@ + * --Copyright-- + */ + ++#include + #include + #include + #include +@@ -86,10 +87,6 @@ + #include + #include + +-/* Get implementations of some internal functions. */ +-#include +-#include +- + #define RESOLVSORT + + #if PACKETSZ > 65536 +@@ -103,25 +100,30 @@ + #endif + #define MAXHOSTNAMELEN 256 + +-/* We need this time later. */ +-typedef union querybuf +-{ +- HEADER hdr; +- u_char buf[MAXPACKET]; +-} querybuf; +- +-static enum nss_status getanswer_r (struct resolv_context *ctx, +- const querybuf *answer, int anslen, +- const char *qname, int qtype, +- struct hostent *result, char *buffer, +- size_t buflen, int *errnop, int *h_errnop, +- int map, int32_t *ttlp, char **canonp); +- +-static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1, +- const querybuf *answer2, int anslen2, +- const char *qname, ++/* For historic reasons, pointers to IP addresses are char *, so use a ++ single list type for addresses and host names. */ ++#define DYNARRAY_STRUCT ptrlist ++#define DYNARRAY_ELEMENT char * ++#define DYNARRAY_PREFIX ptrlist_ ++#include ++ ++static enum nss_status getanswer_r (unsigned char *packet, size_t packetlen, ++ uint16_t qtype, struct alloc_buffer *abuf, ++ struct ptrlist *addresses, ++ struct ptrlist *aliases, ++ int *errnop, int *h_errnop, int32_t *ttlp); ++static void addrsort (struct resolv_context *ctx, char **ap, int num); ++static enum nss_status getanswer_ptr (unsigned char *packet, size_t packetlen, ++ struct alloc_buffer *abuf, ++ char **hnamep, int *errnop, ++ int *h_errnop, int32_t *ttlp); ++ ++static enum nss_status gaih_getanswer (unsigned char *packet1, ++ size_t packet1len, ++ unsigned char *packet2, ++ size_t packet2len, ++ struct alloc_buffer *abuf, + struct gaih_addrtuple **pat, +- char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); + +@@ -175,16 +177,9 @@ + char *buffer, size_t buflen, int *errnop, + int *h_errnop, int32_t *ttlp, char **canonp) + { +- union +- { +- querybuf *buf; +- u_char *ptr; +- } host_buffer; +- querybuf *orig_host_buffer; + char tmp[NS_MAXDNAME]; + int size, type, n; + const char *cp; +- int map = 0; + int olderr = errno; + enum nss_status status; + +@@ -215,10 +210,12 @@ + && (cp = __res_context_hostalias (ctx, name, tmp, sizeof (tmp))) != NULL) + name = cp; + +- host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024); ++ unsigned char dns_packet_buffer[1024]; ++ unsigned char *alt_dns_packet_buffer = dns_packet_buffer; + +- n = __res_context_search (ctx, name, C_IN, type, host_buffer.buf->buf, +- 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL); ++ n = __res_context_search (ctx, name, C_IN, type, ++ dns_packet_buffer, sizeof (dns_packet_buffer), ++ &alt_dns_packet_buffer, NULL, NULL, NULL, NULL); + if (n < 0) + { + switch (errno) +@@ -245,34 +242,79 @@ + *errnop = EAGAIN; + else + __set_errno (olderr); ++ } ++ else ++ { ++ struct alloc_buffer abuf = alloc_buffer_create (buffer, buflen); + +- /* If we are looking for an IPv6 address and mapping is enabled +- by having the RES_USE_INET6 bit in _res.options set, we try +- another lookup. */ +- if (af == AF_INET6 && res_use_inet6 ()) +- n = __res_context_search (ctx, name, C_IN, T_A, host_buffer.buf->buf, +- host_buffer.buf != orig_host_buffer +- ? MAXPACKET : 1024, &host_buffer.ptr, +- NULL, NULL, NULL, NULL); +- +- if (n < 0) ++ struct ptrlist addresses; ++ ptrlist_init (&addresses); ++ struct ptrlist aliases; ++ ptrlist_init (&aliases); ++ ++ status = getanswer_r (alt_dns_packet_buffer, n, type, ++ &abuf, &addresses, &aliases, ++ errnop, h_errnop, ttlp); ++ if (status == NSS_STATUS_SUCCESS) + { +- if (host_buffer.buf != orig_host_buffer) +- free (host_buffer.buf); +- return status; +- } ++ if (ptrlist_has_failed (&addresses) ++ || ptrlist_has_failed (&aliases)) ++ { ++ /* malloc failure. Do not retry using the ERANGE protocol. */ ++ *errnop = ENOMEM; ++ *h_errnop = NETDB_INTERNAL; ++ status = NSS_STATUS_UNAVAIL; ++ } + +- map = 1; ++ /* Reserve the address and alias arrays in the result ++ buffer. Both are NULL-terminated, but the first element ++ of the alias array is stored in h_name, so no extra space ++ for the NULL terminator is needed there. */ ++ result->h_addr_list ++ = alloc_buffer_alloc_array (&abuf, char *, ++ ptrlist_size (&addresses) + 1); ++ result->h_aliases ++ = alloc_buffer_alloc_array (&abuf, char *, ++ ptrlist_size (&aliases)); ++ if (alloc_buffer_has_failed (&abuf)) ++ { ++ /* Retry using the ERANGE protocol. */ ++ *errnop = ERANGE; ++ *h_errnop = NETDB_INTERNAL; ++ status = NSS_STATUS_TRYAGAIN; ++ } ++ else ++ { ++ /* Copy the address list and NULL-terminate it. */ ++ memcpy (result->h_addr_list, ptrlist_begin (&addresses), ++ ptrlist_size (&addresses) * sizeof (char *)); ++ result->h_addr_list[ptrlist_size (&addresses)] = NULL; ++ ++ /* Sort the address list if requested. */ ++ if (type == T_A && __resolv_context_sort_count (ctx) > 0) ++ addrsort (ctx, result->h_addr_list, ptrlist_size (&addresses)); ++ ++ /* Copy the aliases, excluding the last one. */ ++ memcpy (result->h_aliases, ptrlist_begin (&aliases), ++ (ptrlist_size (&aliases) - 1) * sizeof (char *)); ++ result->h_aliases[ptrlist_size (&aliases) - 1] = NULL; ++ ++ /* The last alias goes into h_name. */ ++ assert (ptrlist_size (&aliases) >= 1); ++ result->h_name = ptrlist_end (&aliases)[-1]; ++ ++ /* This is also the canonical name. */ ++ if (canonp != NULL) ++ *canonp = result->h_name; ++ } ++ } + +- result->h_addrtype = AF_INET; +- result->h_length = INADDRSZ; ++ ptrlist_free (&aliases); ++ ptrlist_free (&addresses); + } + +- status = getanswer_r +- (ctx, host_buffer.buf, n, name, type, result, buffer, buflen, +- errnop, h_errnop, map, ttlp, canonp); +- if (host_buffer.buf != orig_host_buffer) +- free (host_buffer.buf); ++ if (alt_dns_packet_buffer != dns_packet_buffer) ++ free (alt_dns_packet_buffer); + return status; + } + +@@ -316,13 +358,8 @@ + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_UNAVAIL; + } +- status = NSS_STATUS_NOTFOUND; +- if (res_use_inet6 ()) +- status = gethostbyname3_context (ctx, name, AF_INET6, result, buffer, +- buflen, errnop, h_errnop, NULL, NULL); +- if (status == NSS_STATUS_NOTFOUND) +- status = gethostbyname3_context (ctx, name, AF_INET, result, buffer, +- buflen, errnop, h_errnop, NULL, NULL); ++ status = gethostbyname3_context (ctx, name, AF_INET, result, buffer, ++ buflen, errnop, h_errnop, NULL, NULL); + __resolv_context_put (ctx); + return status; + } +@@ -357,27 +394,23 @@ + name = cp; + } + +- union +- { +- querybuf *buf; +- u_char *ptr; +- } host_buffer; +- querybuf *orig_host_buffer; +- host_buffer.buf = orig_host_buffer = (querybuf *) alloca (2048); ++ unsigned char dns_packet_buffer[2048]; ++ unsigned char *alt_dns_packet_buffer = dns_packet_buffer; + u_char *ans2p = NULL; + int nans2p = 0; + int resplen2 = 0; + int ans2p_malloced = 0; ++ struct alloc_buffer abuf = alloc_buffer_create (buffer, buflen); + + int olderr = errno; + int n = __res_context_search (ctx, name, C_IN, T_QUERY_A_AND_AAAA, +- host_buffer.buf->buf, 2048, &host_buffer.ptr, +- &ans2p, &nans2p, &resplen2, &ans2p_malloced); ++ dns_packet_buffer, sizeof (dns_packet_buffer), ++ &alt_dns_packet_buffer, &ans2p, &nans2p, ++ &resplen2, &ans2p_malloced); + if (n >= 0) + { +- status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p, +- resplen2, name, pat, buffer, buflen, +- errnop, herrnop, ttlp); ++ status = gaih_getanswer (alt_dns_packet_buffer, n, ans2p, resplen2, ++ &abuf, pat, errnop, herrnop, ttlp); + } + else + { +@@ -408,12 +441,20 @@ + __set_errno (olderr); + } + ++ /* Implement the buffer resizing protocol. */ ++ if (alloc_buffer_has_failed (&abuf)) ++ { ++ *errnop = ERANGE; ++ *herrnop = NETDB_INTERNAL; ++ status = NSS_STATUS_TRYAGAIN; ++ } ++ + /* Check whether ans2p was separately allocated. */ + if (ans2p_malloced) + free (ans2p); + +- if (host_buffer.buf != orig_host_buffer) +- free (host_buffer.buf); ++ if (alt_dns_packet_buffer != dns_packet_buffer) ++ free (alt_dns_packet_buffer); + + __resolv_context_put (ctx); + return status; +@@ -429,36 +470,21 @@ + static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 }; + static const u_char v6local[] = { 0,0, 0,1 }; + const u_char *uaddr = (const u_char *)addr; +- struct host_data +- { +- char *aliases[MAX_NR_ALIASES]; +- unsigned char host_addr[16]; /* IPv4 or IPv6 */ +- char *h_addr_ptrs[MAX_NR_ADDRS + 1]; +- char linebuffer[0]; +- } *host_data = (struct host_data *) buffer; +- union +- { +- querybuf *buf; +- u_char *ptr; +- } host_buffer; +- querybuf *orig_host_buffer; + char qbuf[MAXDNAME+1], *qp = NULL; + size_t size; + int n, status; + int olderr = errno; + +- uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data); +- buffer += pad; +- buflen = buflen > pad ? buflen - pad : 0; +- +- if (__glibc_unlikely (buflen < sizeof (struct host_data))) +- { +- *errnop = ERANGE; +- *h_errnop = NETDB_INTERNAL; +- return NSS_STATUS_TRYAGAIN; +- } +- +- host_data = (struct host_data *) buffer; ++ /* Prepare the allocation buffer. Store the pointer array first, to ++ benefit from buffer alignment. */ ++ struct alloc_buffer abuf = alloc_buffer_create (buffer, buflen); ++ char **address_array = alloc_buffer_alloc_array (&abuf, char *, 2); ++ if (address_array == NULL) ++ { ++ *errnop = ERANGE; ++ *h_errnop = NETDB_INTERNAL; ++ return NSS_STATUS_TRYAGAIN; ++ } + + struct resolv_context *ctx = __resolv_context_get (); + if (ctx == NULL) +@@ -502,8 +528,6 @@ + return NSS_STATUS_UNAVAIL; + } + +- host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024); +- + switch (af) + { + case AF_INET: +@@ -527,36 +551,52 @@ + break; + } + +- n = __res_context_query (ctx, qbuf, C_IN, T_PTR, host_buffer.buf->buf, +- 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL); ++ unsigned char dns_packet_buffer[1024]; ++ unsigned char *alt_dns_packet_buffer = dns_packet_buffer; ++ n = __res_context_query (ctx, qbuf, C_IN, T_PTR, ++ dns_packet_buffer, sizeof (dns_packet_buffer), ++ &alt_dns_packet_buffer, ++ NULL, NULL, NULL, NULL); + if (n < 0) + { + *h_errnop = h_errno; + __set_errno (olderr); +- if (host_buffer.buf != orig_host_buffer) +- free (host_buffer.buf); ++ if (alt_dns_packet_buffer != dns_packet_buffer) ++ free (alt_dns_packet_buffer); + __resolv_context_put (ctx); + return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND; + } + +- status = getanswer_r +- (ctx, host_buffer.buf, n, qbuf, T_PTR, result, buffer, buflen, +- errnop, h_errnop, 0 /* XXX */, ttlp, NULL); +- if (host_buffer.buf != orig_host_buffer) +- free (host_buffer.buf); ++ status = getanswer_ptr (alt_dns_packet_buffer, n, ++ &abuf, &result->h_name, errnop, h_errnop, ttlp); ++ ++ if (alt_dns_packet_buffer != dns_packet_buffer) ++ free (alt_dns_packet_buffer); ++ __resolv_context_put (ctx); ++ + if (status != NSS_STATUS_SUCCESS) +- { +- __resolv_context_put (ctx); +- return status; +- } ++ return status; + ++ /* result->h_name has already been set by getanswer_ptr. */ + result->h_addrtype = af; + result->h_length = len; +- memcpy (host_data->host_addr, addr, len); +- host_data->h_addr_ptrs[0] = (char *) host_data->host_addr; +- host_data->h_addr_ptrs[1] = NULL; ++ /* Increase the alignment to 4, in case there are applications out ++ there that expect at least this level of address alignment. */ ++ address_array[0] = (char *) alloc_buffer_next (&abuf, uint32_t); ++ alloc_buffer_copy_bytes (&abuf, uaddr, len); ++ address_array[1] = NULL; ++ ++ /* This check also covers allocation failure in getanswer_ptr. */ ++ if (alloc_buffer_has_failed (&abuf)) ++ { ++ *errnop = ERANGE; ++ *h_errnop = NETDB_INTERNAL; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ result->h_addr_list = address_array; ++ result->h_aliases = &address_array[1]; /* Points to NULL. */ ++ + *h_errnop = NETDB_SUCCESS; +- __resolv_context_put (ctx); + return NSS_STATUS_SUCCESS; + } + libc_hidden_def (_nss_dns_gethostbyaddr2_r) +@@ -618,650 +658,362 @@ + break; + } + +-static enum nss_status +-getanswer_r (struct resolv_context *ctx, +- const querybuf *answer, int anslen, const char *qname, int qtype, +- struct hostent *result, char *buffer, size_t buflen, +- int *errnop, int *h_errnop, int map, int32_t *ttlp, char **canonp) ++/* Convert the uncompressed, binary domain name CDNAME into its ++ textual representation and add it to the end of ALIASES, allocating ++ space for a copy of the name from ABUF. Skip adding the name if it ++ is not a valid host name, and return false in that case, otherwise ++ true. */ ++static bool ++getanswer_r_store_alias (const unsigned char *cdname, ++ struct alloc_buffer *abuf, ++ struct ptrlist *aliases) + { +- struct host_data +- { +- char *aliases[MAX_NR_ALIASES]; +- unsigned char host_addr[16]; /* IPv4 or IPv6 */ +- char *h_addr_ptrs[0]; +- } *host_data; +- int linebuflen; +- const HEADER *hp; +- const u_char *end_of_message, *cp; +- int n, ancount, qdcount; +- int haveanswer, had_error; +- char *bp, **ap, **hap; +- char tbuf[MAXDNAME]; +- const char *tname; +- int (*name_ok) (const char *); +- u_char packtmp[NS_MAXCDNAME]; +- int have_to_map = 0; +- uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data); +- buffer += pad; +- buflen = buflen > pad ? buflen - pad : 0; +- if (__glibc_unlikely (buflen < sizeof (struct host_data))) +- { +- /* The buffer is too small. */ +- too_small: +- *errnop = ERANGE; +- *h_errnop = NETDB_INTERNAL; +- return NSS_STATUS_TRYAGAIN; +- } +- host_data = (struct host_data *) buffer; +- linebuflen = buflen - sizeof (struct host_data); +- if (buflen - sizeof (struct host_data) != linebuflen) +- linebuflen = INT_MAX; +- +- tname = qname; +- result->h_name = NULL; +- end_of_message = answer->buf + anslen; +- switch (qtype) +- { +- case T_A: +- case T_AAAA: +- name_ok = __libc_res_hnok; +- break; +- case T_PTR: +- name_ok = __libc_res_dnok; +- break; +- default: +- *errnop = ENOENT; +- return NSS_STATUS_UNAVAIL; /* XXX should be abort(); */ +- } ++ /* Filter out domain names that are not host names. */ ++ if (!__res_binary_hnok (cdname)) ++ return false; ++ ++ /* Note: Not NS_MAXCDNAME, so that __ns_name_ntop implicitly checks ++ for length. */ ++ char dname[MAXHOSTNAMELEN + 1]; ++ if (__ns_name_ntop (cdname, dname, sizeof (dname)) < 0) ++ return false; ++ /* Do not report an error on allocation failure, instead store NULL ++ or do nothing. getanswer_r's caller will see NSS_STATUS_SUCCESS ++ and detect the memory allocation failure or buffer space ++ exhaustion, and report it accordingly. */ ++ ptrlist_add (aliases, alloc_buffer_copy_string (abuf, dname)); ++ return true; ++} + +- /* +- * find first satisfactory answer +- */ +- hp = &answer->hdr; +- ancount = ntohs (hp->ancount); +- qdcount = ntohs (hp->qdcount); +- cp = answer->buf + HFIXEDSZ; +- if (__glibc_unlikely (qdcount != 1)) ++static enum nss_status __attribute__ ((noinline)) ++getanswer_r (unsigned char *packet, size_t packetlen, uint16_t qtype, ++ struct alloc_buffer *abuf, ++ struct ptrlist *addresses, struct ptrlist *aliases, ++ int *errnop, int *h_errnop, int32_t *ttlp) ++{ ++ struct ns_rr_cursor c; ++ if (!__ns_rr_cursor_init (&c, packet, packetlen)) + { ++ /* This should not happen because __res_context_query already ++ perfroms response validation. */ + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } +- if (sizeof (struct host_data) + (ancount + 1) * sizeof (char *) >= buflen) +- goto too_small; +- bp = (char *) &host_data->h_addr_ptrs[ancount + 1]; +- linebuflen -= (ancount + 1) * sizeof (char *); +- +- n = __ns_name_unpack (answer->buf, end_of_message, cp, +- packtmp, sizeof packtmp); +- if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) +- { +- if (__glibc_unlikely (errno == EMSGSIZE)) +- goto too_small; + +- n = -1; ++ /* Treat the QNAME just like an alias. Error out if it is not a ++ valid host name. */ ++ if (ns_rr_cursor_rcode (&c) == NXDOMAIN ++ || !getanswer_r_store_alias (ns_rr_cursor_qname (&c), abuf, aliases)) ++ { ++ if (ttlp != NULL) ++ /* No negative caching. */ ++ *ttlp = 0; ++ *h_errnop = HOST_NOT_FOUND; ++ *errnop = ENOENT; ++ return NSS_STATUS_NOTFOUND; + } + +- if (__glibc_unlikely (n < 0)) +- { +- *errnop = errno; +- *h_errnop = NO_RECOVERY; +- return NSS_STATUS_UNAVAIL; +- } +- if (__glibc_unlikely (name_ok (bp) == 0)) +- { +- errno = EBADMSG; +- *errnop = EBADMSG; +- *h_errnop = NO_RECOVERY; +- return NSS_STATUS_UNAVAIL; +- } +- cp += n + QFIXEDSZ; ++ int ancount = ns_rr_cursor_ancount (&c); ++ const unsigned char *expected_name = ns_rr_cursor_qname (&c); ++ /* expected_name may be updated to point into this buffer. */ ++ unsigned char name_buffer[NS_MAXCDNAME]; + +- if (qtype == T_A || qtype == T_AAAA) ++ for (; ancount > 0; --ancount) + { +- /* res_send() has already verified that the query name is the +- * same as the one we sent; this just gets the expanded name +- * (i.e., with the succeeding search-domain tacked on). +- */ +- n = strlen (bp) + 1; /* for the \0 */ +- if (n >= MAXHOSTNAMELEN) ++ struct ns_rr_wire rr; ++ if (!__ns_rr_cursor_next (&c, &rr)) + { + *h_errnop = NO_RECOVERY; +- *errnop = ENOENT; +- return NSS_STATUS_TRYAGAIN; +- } +- result->h_name = bp; +- bp += n; +- linebuflen -= n; +- if (linebuflen < 0) +- goto too_small; +- /* The qname can be abbreviated, but h_name is now absolute. */ +- qname = result->h_name; +- } +- +- ap = host_data->aliases; +- *ap = NULL; +- result->h_aliases = host_data->aliases; +- hap = host_data->h_addr_ptrs; +- *hap = NULL; +- result->h_addr_list = host_data->h_addr_ptrs; +- haveanswer = 0; +- had_error = 0; +- +- while (ancount-- > 0 && cp < end_of_message && had_error == 0) +- { +- int type, class; +- +- n = __ns_name_unpack (answer->buf, end_of_message, cp, +- packtmp, sizeof packtmp); +- if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) +- { +- if (__glibc_unlikely (errno == EMSGSIZE)) +- goto too_small; +- +- n = -1; +- } +- +- if (__glibc_unlikely (n < 0 || (*name_ok) (bp) == 0)) +- { +- ++had_error; +- continue; +- } +- cp += n; /* name */ +- +- if (__glibc_unlikely (cp + 10 > end_of_message)) +- { +- ++had_error; +- continue; +- } +- +- NS_GET16 (type, cp); +- NS_GET16 (class, cp); +- int32_t ttl; +- NS_GET32 (ttl, cp); +- NS_GET16 (n, cp); /* RDATA length. */ +- +- if (end_of_message - cp < n) +- { +- /* RDATA extends beyond the end of the packet. */ +- ++had_error; +- continue; +- } +- +- if (__glibc_unlikely (class != C_IN)) +- { +- /* XXX - debug? syslog? */ +- cp += n; +- continue; /* XXX - had_error++ ? */ ++ return NSS_STATUS_UNAVAIL; + } + +- if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) +- { +- /* A CNAME could also have a TTL entry. */ +- if (ttlp != NULL && ttl < *ttlp) +- *ttlp = ttl; +- +- if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1]) +- continue; +- n = __libc_dn_expand (answer->buf, end_of_message, cp, +- tbuf, sizeof tbuf); +- if (__glibc_unlikely (n < 0 || (*name_ok) (tbuf) == 0)) +- { +- ++had_error; +- continue; +- } +- cp += n; +- /* Store alias. */ +- *ap++ = bp; +- n = strlen (bp) + 1; /* For the \0. */ +- if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) +- { +- ++had_error; +- continue; +- } +- bp += n; +- linebuflen -= n; +- /* Get canonical name. */ +- n = strlen (tbuf) + 1; /* For the \0. */ +- if (__glibc_unlikely (n > linebuflen)) +- goto too_small; +- if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) ++ /* Skip over records with the wrong class. */ ++ if (rr.rclass != C_IN) ++ continue; ++ ++ /* Update TTL for recognized record types. */ ++ if ((rr.rtype == T_CNAME || rr.rtype == qtype) ++ && ttlp != NULL && *ttlp > rr.ttl) ++ *ttlp = rr.ttl; ++ ++ if (rr.rtype == T_CNAME) ++ { ++ /* NB: No check for owner name match, based on historic ++ precedent. Record the CNAME target as the new expected ++ name. */ ++ int n = __ns_name_unpack (c.begin, c.end, rr.rdata, ++ name_buffer, sizeof (name_buffer)); ++ if (n < 0) + { +- ++had_error; +- continue; ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; + } +- result->h_name = bp; +- bp = __mempcpy (bp, tbuf, n); /* Cannot overflow. */ +- linebuflen -= n; +- continue; ++ /* And store the new name as an alias. */ ++ getanswer_r_store_alias (name_buffer, abuf, aliases); ++ expected_name = name_buffer; + } +- +- if (qtype == T_PTR && type == T_CNAME) ++ else if (rr.rtype == qtype ++ && __ns_samebinaryname (rr.rname, expected_name) ++ && rr.rdlength == rrtype_to_rdata_length (qtype)) + { +- /* A CNAME could also have a TTL entry. */ +- if (ttlp != NULL && ttl < *ttlp) +- *ttlp = ttl; +- +- n = __libc_dn_expand (answer->buf, end_of_message, cp, +- tbuf, sizeof tbuf); +- if (__glibc_unlikely (n < 0 || __libc_res_dnok (tbuf) == 0)) +- { +- ++had_error; +- continue; +- } +- cp += n; +- /* Get canonical name. */ +- n = strlen (tbuf) + 1; /* For the \0. */ +- if (__glibc_unlikely (n > linebuflen)) +- goto too_small; +- if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) +- { +- ++had_error; +- continue; +- } +- tname = bp; +- bp = __mempcpy (bp, tbuf, n); /* Cannot overflow. */ +- linebuflen -= n; +- continue; +- } +- +- if (type == T_A && qtype == T_AAAA && map) +- have_to_map = 1; +- else if (__glibc_unlikely (type != qtype)) +- { +- cp += n; +- continue; /* XXX - had_error++ ? */ +- } +- +- switch (type) +- { +- case T_PTR: +- if (__glibc_unlikely (__strcasecmp (tname, bp) != 0)) +- { +- cp += n; +- continue; /* XXX - had_error++ ? */ +- } +- +- n = __ns_name_unpack (answer->buf, end_of_message, cp, +- packtmp, sizeof packtmp); +- if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) +- { +- if (__glibc_unlikely (errno == EMSGSIZE)) +- goto too_small; +- +- n = -1; +- } +- +- if (__glibc_unlikely (n < 0 || __libc_res_hnok (bp) == 0)) +- { +- ++had_error; +- break; +- } +- if (ttlp != NULL && ttl < *ttlp) +- *ttlp = ttl; +- /* bind would put multiple PTR records as aliases, but we don't do +- that. */ +- result->h_name = bp; +- *h_errnop = NETDB_SUCCESS; +- return NSS_STATUS_SUCCESS; +- case T_A: +- case T_AAAA: +- if (__glibc_unlikely (__strcasecmp (result->h_name, bp) != 0)) +- { +- cp += n; +- continue; /* XXX - had_error++ ? */ +- } +- +- /* Stop parsing at a record whose length is incorrect. */ +- if (n != rrtype_to_rdata_length (type)) +- { +- ++had_error; +- break; +- } +- +- /* Skip records of the wrong type. */ +- if (n != result->h_length) +- { +- cp += n; +- continue; +- } +- if (!haveanswer) +- { +- int nn; +- +- /* We compose a single hostent out of the entire chain of +- entries, so the TTL of the hostent is essentially the lowest +- TTL in the chain. */ +- if (ttlp != NULL && ttl < *ttlp) +- *ttlp = ttl; +- if (canonp != NULL) +- *canonp = bp; +- result->h_name = bp; +- nn = strlen (bp) + 1; /* for the \0 */ +- bp += nn; +- linebuflen -= nn; +- } +- +- /* Provide sufficient alignment for both address +- families. */ +- enum { align = 4 }; +- _Static_assert ((align % __alignof__ (struct in_addr)) == 0, +- "struct in_addr alignment"); +- _Static_assert ((align % __alignof__ (struct in6_addr)) == 0, +- "struct in6_addr alignment"); +- { +- char *new_bp = PTR_ALIGN_UP (bp, align); +- linebuflen -= new_bp - bp; +- bp = new_bp; +- } +- +- if (__glibc_unlikely (n > linebuflen)) +- goto too_small; +- bp = __mempcpy (*hap++ = bp, cp, n); +- cp += n; +- linebuflen -= n; +- break; +- default: +- abort (); ++ /* Make a copy of the address and store it. Increase the ++ alignment to 4, in case there are applications out there ++ that expect at least this level of address alignment. */ ++ ptrlist_add (addresses, (char *) alloc_buffer_next (abuf, uint32_t)); ++ alloc_buffer_copy_bytes (abuf, rr.rdata, rr.rdlength); + } +- if (had_error == 0) +- ++haveanswer; + } + +- if (haveanswer > 0) ++ if (ptrlist_size (addresses) == 0) + { +- *ap = NULL; +- *hap = NULL; +- /* +- * Note: we sort even if host can take only one address +- * in its return structures - should give it the "best" +- * address in that case, not some random one +- */ +- if (haveanswer > 1 && qtype == T_A +- && __resolv_context_sort_count (ctx) > 0) +- addrsort (ctx, host_data->h_addr_ptrs, haveanswer); ++ /* No address record found. */ ++ if (ttlp != NULL) ++ /* No caching of negative responses. */ ++ *ttlp = 0; + +- if (result->h_name == NULL) +- { +- n = strlen (qname) + 1; /* For the \0. */ +- if (n > linebuflen) +- goto too_small; +- if (n >= MAXHOSTNAMELEN) +- goto no_recovery; +- result->h_name = bp; +- bp = __mempcpy (bp, qname, n); /* Cannot overflow. */ +- linebuflen -= n; +- } +- +- if (have_to_map) +- if (map_v4v6_hostent (result, &bp, &linebuflen)) +- goto too_small; ++ *h_errnop = NO_RECOVERY; ++ *errnop = ENOENT; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ else ++ { + *h_errnop = NETDB_SUCCESS; + return NSS_STATUS_SUCCESS; + } +- no_recovery: +- *h_errnop = NO_RECOVERY; +- *errnop = ENOENT; +- /* Special case here: if the resolver sent a result but it only +- contains a CNAME while we are looking for a T_A or T_AAAA record, +- we fail with NOTFOUND instead of TRYAGAIN. */ +- return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases +- ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN); + } + +- + static enum nss_status +-gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname, +- struct gaih_addrtuple ***patp, +- char **bufferp, size_t *buflenp, +- int *errnop, int *h_errnop, int32_t *ttlp, int *firstp) ++getanswer_ptr (unsigned char *packet, size_t packetlen, ++ struct alloc_buffer *abuf, char **hnamep, ++ int *errnop, int *h_errnop, int32_t *ttlp) + { +- char *buffer = *bufferp; +- size_t buflen = *buflenp; +- +- struct gaih_addrtuple **pat = *patp; +- const HEADER *hp = &answer->hdr; +- int ancount = ntohs (hp->ancount); +- int qdcount = ntohs (hp->qdcount); +- const u_char *cp = answer->buf + HFIXEDSZ; +- const u_char *end_of_message = answer->buf + anslen; +- if (__glibc_unlikely (qdcount != 1)) ++ struct ns_rr_cursor c; ++ if (!__ns_rr_cursor_init (&c, packet, packetlen)) + { ++ /* This should not happen because __res_context_query already ++ perfroms response validation. */ + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } ++ int ancount = ns_rr_cursor_ancount (&c); ++ const unsigned char *expected_name = ns_rr_cursor_qname (&c); ++ /* expected_name may be updated to point into this buffer. */ ++ unsigned char name_buffer[NS_MAXCDNAME]; + +- u_char packtmp[NS_MAXCDNAME]; +- int n = __ns_name_unpack (answer->buf, end_of_message, cp, +- packtmp, sizeof packtmp); +- /* We unpack the name to check it for validity. But we do not need +- it later. */ +- if (n != -1 && __ns_name_ntop (packtmp, buffer, buflen) == -1) ++ while (ancount > 0) + { +- if (__glibc_unlikely (errno == EMSGSIZE)) ++ struct ns_rr_wire rr; ++ if (!__ns_rr_cursor_next (&c, &rr)) + { +- too_small: +- *errnop = ERANGE; +- *h_errnop = NETDB_INTERNAL; +- return NSS_STATUS_TRYAGAIN; ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; + } + +- n = -1; ++ /* Skip over records with the wrong class. */ ++ if (rr.rclass != C_IN) ++ continue; ++ ++ /* Update TTL for known record types. */ ++ if ((rr.rtype == T_CNAME || rr.rtype == T_PTR) ++ && ttlp != NULL && *ttlp > rr.ttl) ++ *ttlp = rr.ttl; ++ ++ if (rr.rtype == T_CNAME) ++ { ++ /* NB: No check for owner name match, based on historic ++ precedent. Record the CNAME target as the new expected ++ name. */ ++ int n = __ns_name_unpack (c.begin, c.end, rr.rdata, ++ name_buffer, sizeof (name_buffer)); ++ if (n < 0) ++ { ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; ++ } ++ expected_name = name_buffer; ++ } ++ else if (rr.rtype == T_PTR ++ && __ns_samebinaryname (rr.rname, expected_name)) ++ { ++ /* Decompress the target of the PTR record. This is the ++ host name we are looking for. We can only use it if it ++ is syntactically valid. Historically, only one host name ++ is returned here. If the recursive resolver performs DNS ++ record rotation, the returned host name is essentially ++ random, which is why multiple PTR records are rarely ++ used. Use MAXHOSTNAMELEN instead of NS_MAXCDNAME for ++ additional length checking. */ ++ char hname[MAXHOSTNAMELEN + 1]; ++ if (__ns_name_unpack (c.begin, c.end, rr.rdata, ++ name_buffer, sizeof (name_buffer)) < 0 ++ || !__res_binary_hnok (expected_name) ++ || __ns_name_ntop (name_buffer, hname, sizeof (hname)) < 0) ++ { ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; ++ } ++ /* Successful allocation is checked by the caller. */ ++ *hnamep = alloc_buffer_copy_string (abuf, hname); ++ return NSS_STATUS_SUCCESS; ++ } + } + +- if (__glibc_unlikely (n < 0)) +- { +- *errnop = errno; +- *h_errnop = NO_RECOVERY; +- return NSS_STATUS_UNAVAIL; +- } +- if (__glibc_unlikely (__libc_res_hnok (buffer) == 0)) ++ /* No PTR record found. */ ++ if (ttlp != NULL) ++ /* No caching of negative responses. */ ++ *ttlp = 0; ++ ++ *h_errnop = NO_RECOVERY; ++ *errnop = ENOENT; ++ return NSS_STATUS_TRYAGAIN; ++} ++ ++/* Parses DNS data found in PACKETLEN bytes at PACKET in struct ++ gaih_addrtuple address tuples. The new address tuples are linked ++ from **TAILP, with backing store allocated from ABUF, and *TAILP is ++ updated to point where the next tuple pointer should be stored. If ++ TTLP is not null, *TTLP is updated to reflect the minimum TTL. If ++ STORE_CANON is true, the canonical name is stored as part of the ++ first address tuple being written. */ ++static enum nss_status ++gaih_getanswer_slice (unsigned char *packet, size_t packetlen, ++ struct alloc_buffer *abuf, ++ struct gaih_addrtuple ***tailp, ++ int *errnop, int *h_errnop, int32_t *ttlp, ++ bool store_canon) ++{ ++ struct ns_rr_cursor c; ++ if (!__ns_rr_cursor_init (&c, packet, packetlen)) + { +- errno = EBADMSG; +- *errnop = EBADMSG; ++ /* This should not happen because __res_context_query already ++ perfroms response validation. */ + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } +- cp += n + QFIXEDSZ; +- +- int haveanswer = 0; +- int had_error = 0; +- char *canon = NULL; +- char *h_name = NULL; +- int h_namelen = 0; ++ bool haveanswer = false; /* Set to true if at least one address. */ ++ uint16_t qtype = ns_rr_cursor_qtype (&c); ++ int ancount = ns_rr_cursor_ancount (&c); ++ const unsigned char *expected_name = ns_rr_cursor_qname (&c); ++ /* expected_name may be updated to point into this buffer. */ ++ unsigned char name_buffer[NS_MAXCDNAME]; ++ ++ /* This is a pointer to a possibly-compressed name in the packet. ++ Eventually it is equivalent to the canonical name. If needed, it ++ is uncompressed and translated to text form when the first ++ address tuple is encountered. */ ++ const unsigned char *compressed_alias_name = expected_name; + +- if (ancount == 0) ++ if (ancount == 0 || !__res_binary_hnok (compressed_alias_name)) + { + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + +- while (ancount-- > 0 && cp < end_of_message && had_error == 0) ++ for (; ancount > -0; --ancount) + { +- n = __ns_name_unpack (answer->buf, end_of_message, cp, +- packtmp, sizeof packtmp); +- if (n != -1 && +- (h_namelen = __ns_name_ntop (packtmp, buffer, buflen)) == -1) +- { +- if (__glibc_unlikely (errno == EMSGSIZE)) +- goto too_small; +- +- n = -1; +- } +- if (__glibc_unlikely (n < 0 || __libc_res_hnok (buffer) == 0)) ++ struct ns_rr_wire rr; ++ if (!__ns_rr_cursor_next (&c, &rr)) + { +- ++had_error; +- continue; +- } +- if (*firstp && canon == NULL) +- { +- h_name = buffer; +- buffer += h_namelen; +- buflen -= h_namelen; +- } +- +- cp += n; /* name */ +- +- if (__glibc_unlikely (cp + 10 > end_of_message)) +- { +- ++had_error; +- continue; +- } +- +- uint16_t type; +- NS_GET16 (type, cp); +- uint16_t class; +- NS_GET16 (class, cp); +- int32_t ttl; +- NS_GET32 (ttl, cp); +- NS_GET16 (n, cp); /* RDATA length. */ +- +- if (end_of_message - cp < n) +- { +- /* RDATA extends beyond the end of the packet. */ +- ++had_error; +- continue; +- } +- +- if (class != C_IN) +- { +- cp += n; +- continue; ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; + } + +- if (type == T_CNAME) +- { +- char tbuf[MAXDNAME]; +- +- /* A CNAME could also have a TTL entry. */ +- if (ttlp != NULL && ttl < *ttlp) +- *ttlp = ttl; +- +- n = __libc_dn_expand (answer->buf, end_of_message, cp, +- tbuf, sizeof tbuf); +- if (__glibc_unlikely (n < 0 || __libc_res_hnok (tbuf) == 0)) ++ /* Update TTL for known record types. */ ++ if ((rr.rtype == T_CNAME || rr.rtype == qtype) ++ && ttlp != NULL && *ttlp > rr.ttl) ++ *ttlp = rr.ttl; ++ ++ if (rr.rtype == T_CNAME) ++ { ++ /* NB: No check for owner name match, based on historic ++ precedent. Record the CNAME target as the new expected ++ name. */ ++ int n = __ns_name_unpack (c.begin, c.end, rr.rdata, ++ name_buffer, sizeof (name_buffer)); ++ if (n < 0) + { +- ++had_error; +- continue; ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; + } +- cp += n; +- +- if (*firstp) ++ expected_name = name_buffer; ++ if (store_canon && __res_binary_hnok (name_buffer)) ++ /* This name can be used as a canonical name. Do not ++ translate to text form here to conserve buffer space. ++ Point to the compressed name because name_buffer can be ++ overwritten with an unusable name later. */ ++ compressed_alias_name = rr.rdata; ++ } ++ else if (rr.rtype == qtype ++ && __ns_samebinaryname (rr.rname, expected_name) ++ && rr.rdlength == rrtype_to_rdata_length (qtype)) ++ { ++ struct gaih_addrtuple *ntup ++ = alloc_buffer_alloc (abuf, struct gaih_addrtuple); ++ /* Delay error reporting to the callers (they implement the ++ ERANGE buffer resizing handshake). */ ++ if (ntup != NULL) + { +- /* Reclaim buffer space. */ +- if (h_name + h_namelen == buffer) +- { +- buffer = h_name; +- buflen += h_namelen; +- } +- +- n = strlen (tbuf) + 1; +- if (__glibc_unlikely (n > buflen)) +- goto too_small; +- if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) ++ ntup->next = NULL; ++ if (store_canon && compressed_alias_name != NULL) + { +- ++had_error; +- continue; ++ /* This assumes that all the CNAME records come ++ first. Use MAXHOSTNAMELEN instead of ++ NS_MAXCDNAME for additional length checking. ++ However, these checks are not expected to fail ++ because all size NS_MAXCDNAME names should into ++ the hname buffer because no escaping is ++ needed. */ ++ char unsigned nbuf[NS_MAXCDNAME]; ++ char hname[MAXHOSTNAMELEN + 1]; ++ if (__ns_name_unpack (c.begin, c.end, ++ compressed_alias_name, ++ nbuf, sizeof (nbuf)) >= 0 ++ && __ns_name_ntop (nbuf, hname, sizeof (hname)) >= 0) ++ /* Space checking is performed by the callers. */ ++ ntup->name = alloc_buffer_copy_string (abuf, hname); ++ store_canon = false; + } ++ else ++ ntup->name = NULL; ++ if (rr.rdlength == 4) ++ ntup->family = AF_INET; ++ else ++ ntup->family = AF_INET6; ++ memcpy (ntup->addr, rr.rdata, rr.rdlength); ++ ntup->scopeid = 0; ++ ++ /* Link in the new tuple, and update the tail pointer to ++ point to its next field. */ ++ **tailp = ntup; ++ *tailp = &ntup->next; + +- canon = buffer; +- buffer = __mempcpy (buffer, tbuf, n); +- buflen -= n; +- h_namelen = 0; ++ haveanswer = true; + } +- continue; + } +- +- /* Stop parsing if we encounter a record with incorrect RDATA +- length. */ +- if (type == T_A || type == T_AAAA) +- { +- if (n != rrtype_to_rdata_length (type)) +- { +- ++had_error; +- continue; +- } +- } +- else +- { +- /* Skip unknown records. */ +- cp += n; +- continue; +- } +- +- assert (type == T_A || type == T_AAAA); +- if (*pat == NULL) +- { +- uintptr_t pad = (-(uintptr_t) buffer +- % __alignof__ (struct gaih_addrtuple)); +- buffer += pad; +- buflen = buflen > pad ? buflen - pad : 0; +- +- if (__glibc_unlikely (buflen < sizeof (struct gaih_addrtuple))) +- goto too_small; +- +- *pat = (struct gaih_addrtuple *) buffer; +- buffer += sizeof (struct gaih_addrtuple); +- buflen -= sizeof (struct gaih_addrtuple); +- } +- +- (*pat)->name = NULL; +- (*pat)->next = NULL; +- +- if (*firstp) +- { +- /* We compose a single hostent out of the entire chain of +- entries, so the TTL of the hostent is essentially the lowest +- TTL in the chain. */ +- if (ttlp != NULL && ttl < *ttlp) +- *ttlp = ttl; +- +- (*pat)->name = canon ?: h_name; +- +- *firstp = 0; +- } +- +- (*pat)->family = type == T_A ? AF_INET : AF_INET6; +- memcpy ((*pat)->addr, cp, n); +- cp += n; +- (*pat)->scopeid = 0; +- +- pat = &((*pat)->next); +- +- haveanswer = 1; + } + + if (haveanswer) + { +- *patp = pat; +- *bufferp = buffer; +- *buflenp = buflen; +- + *h_errnop = NETDB_SUCCESS; + return NSS_STATUS_SUCCESS; + } +- +- /* Special case here: if the resolver sent a result but it only +- contains a CNAME while we are looking for a T_A or T_AAAA record, +- we fail with NOTFOUND instead of TRYAGAIN. */ +- if (canon != NULL) ++ else + { ++ /* Special case here: if the resolver sent a result but it only ++ contains a CNAME while we are looking for a T_A or T_AAAA ++ record, we fail with NOTFOUND. */ + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } +- +- *h_errnop = NETDB_INTERNAL; +- return NSS_STATUS_TRYAGAIN; + } + + + static enum nss_status +-gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2, +- int anslen2, const char *qname, +- struct gaih_addrtuple **pat, char *buffer, size_t buflen, ++gaih_getanswer (unsigned char *packet1, size_t packet1len, ++ unsigned char *packet2, size_t packet2len, ++ struct alloc_buffer *abuf, struct gaih_addrtuple **pat, + int *errnop, int *h_errnop, int32_t *ttlp) + { +- int first = 1; +- + enum nss_status status = NSS_STATUS_NOTFOUND; + + /* Combining the NSS status of two distinct queries requires some +@@ -1273,7 +1025,10 @@ + between TRYAGAIN (recoverable) and TRYAGAIN' (not-recoverable). + A recoverable TRYAGAIN is almost always due to buffer size issues + and returns ERANGE in errno and the caller is expected to retry +- with a larger buffer. ++ with a larger buffer. (The caller, _nss_dns_gethostbyname4_r, ++ ignores the return status if it detects that the result buffer ++ has been exhausted and generates a TRYAGAIN failure with an ++ ERANGE code.) + + Lastly, you may be tempted to make significant changes to the + conditions in this code to bring about symmetry between responses. +@@ -1353,36 +1108,30 @@ + is a recoverable error we now return TRYAGIN even if the first + response was SUCCESS. */ + +- if (anslen1 > 0) +- status = gaih_getanswer_slice(answer1, anslen1, qname, +- &pat, &buffer, &buflen, +- errnop, h_errnop, ttlp, +- &first); +- +- if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND +- || (status == NSS_STATUS_TRYAGAIN +- /* We want to look at the second answer in case of an +- NSS_STATUS_TRYAGAIN only if the error is non-recoverable, i.e. +- *h_errnop is NO_RECOVERY. If not, and if the failure was due to +- an insufficient buffer (ERANGE), then we need to drop the results +- and pass on the NSS_STATUS_TRYAGAIN to the caller so that it can +- repeat the query with a larger buffer. */ +- && (*errnop != ERANGE || *h_errnop == NO_RECOVERY))) +- && answer2 != NULL && anslen2 > 0) +- { +- enum nss_status status2 = gaih_getanswer_slice(answer2, anslen2, qname, +- &pat, &buffer, &buflen, +- errnop, h_errnop, ttlp, +- &first); ++ if (packet1len > 0) ++ { ++ status = gaih_getanswer_slice (packet1, packet1len, ++ abuf, &pat, errnop, h_errnop, ttlp, true); ++ if (alloc_buffer_has_failed (abuf)) ++ /* Do not try parsing the second packet if a larger result ++ buffer is needed. The caller implements the resizing ++ protocol because *abuf has been exhausted. */ ++ return NSS_STATUS_TRYAGAIN; /* Ignored by the caller. */ ++ } ++ ++ if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND) ++ && packet2 != NULL && packet2len > 0) ++ { ++ enum nss_status status2 ++ = gaih_getanswer_slice (packet2, packet2len, ++ abuf, &pat, errnop, h_errnop, ttlp, ++ /* Success means that data with a ++ canonical name has already been ++ stored. Do not store the name again. */ ++ status != NSS_STATUS_SUCCESS); + /* Use the second response status in some cases. */ + if (status != NSS_STATUS_SUCCESS && status2 != NSS_STATUS_NOTFOUND) + status = status2; +- /* Do not return a truncated second response (unless it was +- unavoidable e.g. unrecoverable TRYAGAIN). */ +- if (status == NSS_STATUS_SUCCESS +- && (status2 == NSS_STATUS_TRYAGAIN +- && *errnop == ERANGE && *h_errnop != NO_RECOVERY)) +- status = NSS_STATUS_TRYAGAIN; + } + + return status; +diff -Nuar a/resolv/README b/resolv/README +--- a/resolv/README 2022-02-03 08:27:54.000000000 +0300 ++++ b/resolv/README 2025-10-20 21:02:19.000000000 +0300 +@@ -146,6 +146,3 @@ + + res_hconf.c and res_hconf.h were contributed by David Mosberger, and + do not come from BIND. +- +-The files gethnamaddr.c, mapv4v6addr.h and mapv4v6hostent.h are +-leftovers from BIND 4.9.7. +diff -Nuar a/resolv/res-name-checking.c b/resolv/res-name-checking.c +--- a/resolv/res-name-checking.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/resolv/res-name-checking.c 2025-10-20 21:02:21.000000000 +0300 +@@ -138,6 +138,12 @@ + return dn[0] > 0 && dn[1] == '-'; + } + ++bool ++__res_binary_hnok (const unsigned char *dn) ++{ ++ return !binary_leading_dash (dn) && binary_hnok (dn); ++} ++ + /* Return 1 if res_hnok is a valid host name. Labels must only + contain [0-9a-zA-Z_-] characters, and the name must not start with + a '-'. The latter is to avoid confusion with program options. */ +@@ -145,11 +151,9 @@ + ___res_hnok (const char *dn) + { + unsigned char buf[NS_MAXCDNAME]; +- if (!printable_string (dn) +- || __ns_name_pton (dn, buf, sizeof (buf)) < 0 +- || binary_leading_dash (buf)) +- return 0; +- return binary_hnok (buf); ++ return (printable_string (dn) ++ && __ns_name_pton (dn, buf, sizeof (buf)) >= 0 ++ && __res_binary_hnok (buf)); + } + versioned_symbol (libc, ___res_hnok, res_hnok, GLIBC_2_34); + versioned_symbol (libc, ___res_hnok, __libc_res_hnok, GLIBC_PRIVATE); +diff -Nuar a/resolv/resolv-internal.h b/resolv/resolv-internal.h +--- a/resolv/resolv-internal.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/resolv/resolv-internal.h 2025-10-20 21:02:21.000000000 +0300 +@@ -26,6 +26,8 @@ + #define RES_F_VC 0x00000001 /* Socket is TCP. */ + #define RES_F_CONN 0x00000002 /* Socket is connected. */ + #define RES_F_EDNS0ERR 0x00000004 /* EDNS0 caused errors. */ ++#define RES_F_SNGLKUP 0x00200000 /* Private version of RES_SNGLKUP. */ ++#define RES_F_SNGLKUPREOP 0x00400000 /* Private version of RES_SNGLKUPREOP. */ + + /* Legacy function. This needs to be removed once all NSS modules + have been adjusted. */ +diff -Nuar a/resolv/res_send.c b/resolv/res_send.c +--- a/resolv/res_send.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/resolv/res_send.c 2025-10-20 21:02:21.000000000 +0300 +@@ -942,9 +942,11 @@ + seconds /= statp->nscount; + if (seconds <= 0) + seconds = 1; +- bool single_request_reopen = (statp->options & RES_SNGLKUPREOP) != 0; +- bool single_request = (((statp->options & RES_SNGLKUP) != 0) +- | single_request_reopen); ++ bool single_request_reopen = ((statp->options & RES_SNGLKUPREOP) ++ || (statp->_flags & RES_F_SNGLKUPREOP)); ++ bool single_request = ((statp->options & RES_SNGLKUP) ++ || (statp->_flags & RES_F_SNGLKUP) ++ || single_request_reopen); + int save_gotsomewhere = *gotsomewhere; + + int retval; +@@ -1001,14 +1003,14 @@ + have received the first answer. */ + if (!single_request) + { +- statp->options |= RES_SNGLKUP; ++ statp->_flags |= RES_F_SNGLKUP; + single_request = true; + *gotsomewhere = save_gotsomewhere; + goto retry; + } + else if (!single_request_reopen) + { +- statp->options |= RES_SNGLKUPREOP; ++ statp->_flags |= RES_F_SNGLKUPREOP; + single_request_reopen = true; + *gotsomewhere = save_gotsomewhere; + __res_iclose (statp, false); +@@ -1192,19 +1194,30 @@ + } + + /* Check for the correct header layout and a matching +- question. */ ++ question. Some recursive resolvers send REFUSED ++ without copying back the question section ++ (producing a response that is only HFIXEDSZ bytes ++ long). Skip query matching in this case. */ ++ bool thisansp_error = (anhp->rcode == SERVFAIL || ++ anhp->rcode == NOTIMP || ++ anhp->rcode == REFUSED); ++ bool skip_query_match = (*thisresplenp == HFIXEDSZ ++ && ntohs (anhp->qdcount) == 0 ++ && thisansp_error); + int matching_query = 0; /* Default to no matching query. */ + if (!recvresp1 + && anhp->id == hp->id +- && __libc_res_queriesmatch (buf, buf + buflen, +- *thisansp, +- *thisansp + *thisanssizp)) ++ && (skip_query_match ++ || __libc_res_queriesmatch (buf, buf + buflen, ++ *thisansp, ++ *thisansp + *thisanssizp))) + matching_query = 1; + if (!recvresp2 + && anhp->id == hp2->id +- && __libc_res_queriesmatch (buf2, buf2 + buflen2, +- *thisansp, +- *thisansp + *thisanssizp)) ++ && (skip_query_match ++ || __libc_res_queriesmatch (buf2, buf2 + buflen2, ++ *thisansp, ++ *thisansp + *thisanssizp))) + matching_query = 2; + if (matching_query == 0) + /* Spurious UDP packet. Drop it and continue +@@ -1214,15 +1227,13 @@ + goto wait; + } + +- if (anhp->rcode == SERVFAIL || +- anhp->rcode == NOTIMP || +- anhp->rcode == REFUSED) { ++ if (thisansp_error) { + next_ns: + if (recvresp1 || (buf2 != NULL && recvresp2)) { + *resplen2 = 0; + return resplen; + } +- if (buf2 != NULL) ++ if (buf2 != NULL && !single_request) + { + /* No data from the first reply. */ + resplen = 0; +diff -Nuar a/resolv/tst-ns_name_length_uncompressed.c b/resolv/tst-ns_name_length_uncompressed.c +--- a/resolv/tst-ns_name_length_uncompressed.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/tst-ns_name_length_uncompressed.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,135 @@ ++/* Test __ns_name_length_uncompressed. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Reference implementation based on other building blocks. */ ++static int ++reference_length (const unsigned char *p, const unsigned char *eom) ++{ ++ unsigned char buf[NS_MAXCDNAME]; ++ int n = __ns_name_unpack (p, eom, p, buf, sizeof (buf)); ++ if (n < 0) ++ return n; ++ const unsigned char *q = buf; ++ if (__ns_name_skip (&q, array_end (buf)) < 0) ++ return -1; ++ if (q - buf != n) ++ /* Compressed name. */ ++ return -1; ++ return n; ++} ++ ++static int ++do_test (void) ++{ ++ { ++ unsigned char buf[] = { 3, 'w', 'w', 'w', 0, 0, 0 }; ++ TEST_COMPARE (reference_length (buf, array_end (buf)), sizeof (buf) - 2); ++ TEST_COMPARE (__ns_name_length_uncompressed (buf, array_end (buf)), ++ sizeof (buf) - 2); ++ TEST_COMPARE (reference_length (array_end (buf) - 1, array_end (buf)), 1); ++ TEST_COMPARE (__ns_name_length_uncompressed (array_end (buf) - 1, ++ array_end (buf)), 1); ++ buf[4] = 0xc0; /* Forward compression reference. */ ++ buf[5] = 0x06; ++ TEST_COMPARE (reference_length (buf, array_end (buf)), -1); ++ TEST_COMPARE (__ns_name_length_uncompressed (buf, array_end (buf)), -1); ++ } ++ ++ struct support_next_to_fault ntf = support_next_to_fault_allocate (300); ++ ++ /* Buffer region with all possible bytes at start and end. */ ++ for (int length = 1; length <= 300; ++length) ++ { ++ unsigned char *end = (unsigned char *) ntf.buffer + ntf.length; ++ unsigned char *start = end - length; ++ memset (start, 'X', length); ++ for (int first = 0; first <= 255; ++first) ++ { ++ *start = first; ++ for (int last = 0; last <= 255; ++last) ++ { ++ start[length - 1] = last; ++ TEST_COMPARE (reference_length (start, end), ++ __ns_name_length_uncompressed (start, end)); ++ } ++ } ++ } ++ ++ /* Poor man's fuzz testing: patch two bytes. */ ++ { ++ unsigned char ref[] = ++ { ++ 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'n', 'e', 't', 0, 0, 0 ++ }; ++ TEST_COMPARE (reference_length (ref, array_end (ref)), 13); ++ TEST_COMPARE (__ns_name_length_uncompressed (ref, array_end (ref)), 13); ++ ++ int good = 0; ++ int bad = 0; ++ for (int length = 1; length <= sizeof (ref); ++length) ++ { ++ unsigned char *end = (unsigned char *) ntf.buffer + ntf.length; ++ unsigned char *start = end - length; ++ memcpy (start, ref, length); ++ ++ for (int patch1_pos = 0; patch1_pos < length; ++patch1_pos) ++ { ++ for (int patch1_value = 0; patch1_value <= 255; ++patch1_value) ++ { ++ start[patch1_pos] = patch1_value; ++ for (int patch2_pos = 0; patch2_pos < length; ++patch2_pos) ++ { ++ for (int patch2_value = 0; patch2_value <= 255; ++ ++patch2_value) ++ { ++ start[patch2_pos] = patch2_value; ++ int expected = reference_length (start, end); ++ errno = EINVAL; ++ int actual ++ = __ns_name_length_uncompressed (start, end); ++ if (actual > 0) ++ ++good; ++ else ++ { ++ TEST_COMPARE (errno, EMSGSIZE); ++ ++bad; ++ } ++ TEST_COMPARE (expected, actual); ++ } ++ start[patch2_pos] = ref[patch2_pos]; ++ } ++ } ++ start[patch1_pos] = ref[patch1_pos]; ++ } ++ } ++ printf ("info: patched inputs with success: %d\n", good); ++ printf ("info: patched inputs with failure: %d\n", bad); ++ } ++ ++ support_next_to_fault_free (&ntf); ++ return 0; ++} ++ ++#include +diff -Nuar a/resolv/tst-ns_rr_cursor.c b/resolv/tst-ns_rr_cursor.c +--- a/resolv/tst-ns_rr_cursor.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/tst-ns_rr_cursor.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,227 @@ ++/* Tests for resource record parsing. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++/* Reference packet for packet parsing. */ ++static const unsigned char valid_packet[] = ++ { 0x11, 0x12, 0x13, 0x14, ++ 0x00, 0x01, /* Question count. */ ++ 0x00, 0x02, /* Answer count. */ ++ 0x21, 0x22, 0x23, 0x24, /* Other counts (not actually in packet). */ ++ 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0, ++ 0x00, 0x1c, /* Question type: AAAA. */ ++ 0x00, 0x01, /* Question class: IN. */ ++ 0xc0, 0x0c, /* Compression reference to QNAME. */ ++ 0x00, 0x1c, /* Record type: AAAA. */ ++ 0x00, 0x01, /* Record class: IN. */ ++ 0x12, 0x34, 0x56, 0x78, /* Record TTL. */ ++ 0x00, 0x10, /* Record data length (16 bytes). */ ++ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, ++ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* IPv6 address. */ ++ 0xc0, 0x0c, /* Compression reference to QNAME. */ ++ 0x00, 0x1c, /* Record type: AAAA. */ ++ 0x00, 0x01, /* Record class: IN. */ ++ 0x11, 0x33, 0x55, 0x77, /* Record TTL. */ ++ 0x00, 0x10, /* Record data length (16 bytes). */ ++ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, ++ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* IPv6 address. */ ++ }; ++ ++/* Special offsets in valid_packet. */ ++enum ++ { ++ offset_of_first_record = 29, ++ offset_of_second_record = 57, ++ }; ++ ++/* Check that parsing valid_packet succeeds. */ ++static void ++test_valid (void) ++{ ++ struct ns_rr_cursor c; ++ TEST_VERIFY_EXIT (__ns_rr_cursor_init (&c, valid_packet, ++ sizeof (valid_packet))); ++ TEST_COMPARE (ns_rr_cursor_rcode (&c), 4); ++ TEST_COMPARE (ns_rr_cursor_ancount (&c), 2); ++ TEST_COMPARE (ns_rr_cursor_nscount (&c), 0x2122); ++ TEST_COMPARE (ns_rr_cursor_adcount (&c), 0x2324); ++ TEST_COMPARE_BLOB (ns_rr_cursor_qname (&c), 13, &valid_packet[12], 13); ++ TEST_COMPARE (ns_rr_cursor_qtype (&c), T_AAAA); ++ TEST_COMPARE (ns_rr_cursor_qclass (&c), C_IN); ++ TEST_COMPARE (c.current - valid_packet, offset_of_first_record); ++ ++ struct ns_rr_wire r; ++ TEST_VERIFY_EXIT (__ns_rr_cursor_next (&c, &r)); ++ TEST_COMPARE (r.rtype, T_AAAA); ++ TEST_COMPARE (r.rclass, C_IN); ++ TEST_COMPARE (r.ttl, 0x12345678); ++ TEST_COMPARE_BLOB (r.rdata, r.rdlength, ++ "\x90\x91\x92\x93\x94\x95\x96\x97" ++ "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f", 16); ++ TEST_COMPARE (c.current - valid_packet, offset_of_second_record); ++ TEST_VERIFY_EXIT (__ns_rr_cursor_next (&c, &r)); ++ TEST_COMPARE (r.rtype, T_AAAA); ++ TEST_COMPARE (r.rclass, C_IN); ++ TEST_COMPARE (r.ttl, 0x11335577); ++ TEST_COMPARE_BLOB (r.rdata, r.rdlength, ++ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7" ++ "\xa8\xa9\xaa\xab\xac\xad\xae\xaf", 16); ++ TEST_VERIFY (c.current == c.end); ++} ++ ++/* Check that trying to parse a packet with a compressed QNAME fails. */ ++static void ++test_compressed_qname (void) ++{ ++ static const unsigned char packet[] = ++ { 0x11, 0x12, 0x13, 0x14, ++ 0x00, 0x01, /* Question count. */ ++ 0x00, 0x00, /* Answer count. */ ++ 0x00, 0x00, 0x00, 0x00, /* Other counts. */ ++ 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0xc0, 0x04, ++ 0x00, 0x01, /* Question type: A. */ ++ 0x00, 0x01, /* Question class: IN. */ ++ }; ++ ++ struct ns_rr_cursor c; ++ TEST_VERIFY_EXIT (!__ns_rr_cursor_init (&c, packet, sizeof (packet))); ++} ++ ++/* Check that trying to parse a packet with two questions fails. */ ++static void ++test_two_questions (void) ++{ ++ static const unsigned char packet[] = ++ { 0x11, 0x12, 0x13, 0x14, ++ 0x00, 0x02, /* Question count. */ ++ 0x00, 0x00, /* Answer count. */ ++ 0x00, 0x00, 0x00, 0x00, /* Other counts. */ ++ 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0xc0, 0x04, ++ 0x00, 0x01, /* Question type: A. */ ++ 0x00, 0x01, /* Question class: IN. */ ++ 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0xc0, 0x04, ++ 0x00, 0x1c, /* Question type: AAAA. */ ++ 0x00, 0x01, /* Question class: IN. */ ++ }; ++ ++ struct ns_rr_cursor c; ++ TEST_VERIFY_EXIT (!__ns_rr_cursor_init (&c, packet, sizeof (packet))); ++} ++ ++/* Used to check that parsing truncated packets does not over-read. */ ++static struct support_next_to_fault ntf; ++ ++/* Truncated packet in the second resource record. */ ++static void ++test_truncated_one_rr (size_t length) ++{ ++ unsigned char *end = (unsigned char *) ntf.buffer - ntf.length; ++ unsigned char *start = end - length; ++ ++ /* Produce the truncated packet. */ ++ memcpy (start, valid_packet, length); ++ ++ struct ns_rr_cursor c; ++ TEST_VERIFY_EXIT (__ns_rr_cursor_init (&c, start, length)); ++ TEST_COMPARE (ns_rr_cursor_rcode (&c), 4); ++ TEST_COMPARE (ns_rr_cursor_ancount (&c), 2); ++ TEST_COMPARE (ns_rr_cursor_nscount (&c), 0x2122); ++ TEST_COMPARE (ns_rr_cursor_adcount (&c), 0x2324); ++ TEST_COMPARE_BLOB (ns_rr_cursor_qname (&c), 13, &valid_packet[12], 13); ++ TEST_COMPARE (ns_rr_cursor_qtype (&c), T_AAAA); ++ TEST_COMPARE (ns_rr_cursor_qclass (&c), C_IN); ++ TEST_COMPARE (c.current - start, offset_of_first_record); ++ ++ struct ns_rr_wire r; ++ TEST_VERIFY_EXIT (__ns_rr_cursor_next (&c, &r)); ++ TEST_COMPARE (r.rtype, T_AAAA); ++ TEST_COMPARE (r.rclass, C_IN); ++ TEST_COMPARE (r.ttl, 0x12345678); ++ TEST_COMPARE_BLOB (r.rdata, r.rdlength, ++ "\x90\x91\x92\x93\x94\x95\x96\x97" ++ "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f", 16); ++ TEST_COMPARE (c.current - start, offset_of_second_record); ++ TEST_VERIFY (!__ns_rr_cursor_next (&c, &r)); ++} ++ ++/* Truncated packet in the first resource record. */ ++static void ++test_truncated_no_rr (size_t length) ++{ ++ unsigned char *end = (unsigned char *) ntf.buffer - ntf.length; ++ unsigned char *start = end - length; ++ ++ /* Produce the truncated packet. */ ++ memcpy (start, valid_packet, length); ++ ++ struct ns_rr_cursor c; ++ TEST_VERIFY_EXIT (__ns_rr_cursor_init (&c, start, length)); ++ TEST_COMPARE (ns_rr_cursor_rcode (&c), 4); ++ TEST_COMPARE (ns_rr_cursor_ancount (&c), 2); ++ TEST_COMPARE (ns_rr_cursor_nscount (&c), 0x2122); ++ TEST_COMPARE (ns_rr_cursor_adcount (&c), 0x2324); ++ TEST_COMPARE_BLOB (ns_rr_cursor_qname (&c), 13, &valid_packet[12], 13); ++ TEST_COMPARE (ns_rr_cursor_qtype (&c), T_AAAA); ++ TEST_COMPARE (ns_rr_cursor_qclass (&c), C_IN); ++ TEST_COMPARE (c.current - start, offset_of_first_record); ++ ++ struct ns_rr_wire r; ++ TEST_VERIFY (!__ns_rr_cursor_next (&c, &r)); ++} ++ ++/* Truncated packet before first resource record. */ ++static void ++test_truncated_before_rr (size_t length) ++{ ++ unsigned char *end = (unsigned char *) ntf.buffer - ntf.length; ++ unsigned char *start = end - length; ++ ++ /* Produce the truncated packet. */ ++ memcpy (start, valid_packet, length); ++ ++ struct ns_rr_cursor c; ++ TEST_VERIFY_EXIT (!__ns_rr_cursor_init (&c, start, length)); ++} ++ ++static int ++do_test (void) ++{ ++ ntf = support_next_to_fault_allocate (sizeof (valid_packet)); ++ ++ test_valid (); ++ test_compressed_qname (); ++ test_two_questions (); ++ ++ for (int length = offset_of_second_record; length < sizeof (valid_packet); ++ ++length) ++ test_truncated_one_rr (length); ++ for (int length = offset_of_first_record; length < offset_of_second_record; ++ ++length) ++ test_truncated_no_rr (length); ++ for (int length = 0; length < offset_of_first_record; ++length) ++ test_truncated_before_rr (length); ++ ++ support_next_to_fault_free (&ntf); ++ return 0; ++} ++ ++#include +diff -Nuar a/resolv/tst-ns_samebinaryname.c b/resolv/tst-ns_samebinaryname.c +--- a/resolv/tst-ns_samebinaryname.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/tst-ns_samebinaryname.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,62 @@ ++/* Test the __ns_samebinaryname function. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* First character denotes the comparison group: All names with the ++ same first character are expected to compare equal. */ ++static const char *const cases[] = ++ { ++ " ", ++ "1\001a", "1\001A", ++ "2\002ab", "2\002aB", "2\002Ab", "2\002AB", ++ "3\001a\002ab", "3\001A\002ab", ++ "w\003www\007example\003com", "w\003Www\007Example\003Com", ++ "w\003WWW\007EXAMPLE\003COM", ++ "W\003WWW", "W\003www", ++ }; ++ ++static int ++do_test (void) ++{ ++ for (int i = 0; i < array_length (cases); ++i) ++ for (int j = 0; j < array_length (cases); ++j) ++ { ++ unsigned char *a = (unsigned char *) &cases[i][1]; ++ unsigned char *b = (unsigned char *) &cases[j][1]; ++ bool actual = __ns_samebinaryname (a, b); ++ bool expected = cases[i][0] == cases[j][0]; ++ if (actual != expected) ++ { ++ char a1[NS_MAXDNAME]; ++ TEST_VERIFY (ns_name_ntop (a, a1, sizeof (a1)) > 0); ++ char b1[NS_MAXDNAME]; ++ TEST_VERIFY (ns_name_ntop (b, b1, sizeof (b1)) > 0); ++ printf ("error: \"%s\" \"%s\": expected %s\n", ++ a1, b1, expected ? "equal" : "unqueal"); ++ support_record_failure (); ++ } ++ } ++ return 0; ++} ++ ++#include +diff -Nuar a/resolv/tst-resolv-aliases.c b/resolv/tst-resolv-aliases.c +--- a/resolv/tst-resolv-aliases.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/tst-resolv-aliases.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,254 @@ ++/* Test alias handling (mainly for gethostbyname). ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tst-resolv-maybe_insert_sig.h" ++ ++/* QNAME format: ++ ++ aADDRESSES-cCNAMES.example.net ++ ++ CNAMES is the length of the CNAME chain, ADDRESSES is the number of ++ addresses in the response. The special value 255 means that there ++ are no addresses, and the RCODE is NXDOMAIN. */ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_COMPARE (qclass, C_IN); ++ if (qtype != T_A) ++ TEST_COMPARE (qtype, T_AAAA); ++ ++ unsigned int addresses, cnames; ++ char *tail; ++ if (sscanf (qname, "a%u-c%u%ms", &addresses, &cnames, &tail) == 3) ++ { ++ if (strcmp (tail, ".example.com") == 0 ++ || strcmp (tail, ".example.net.example.net") == 0 ++ || strcmp (tail, ".example.net.example.com") == 0) ++ /* These only happen after NXDOMAIN. */ ++ TEST_VERIFY (addresses == 255); ++ else if (strcmp (tail, ".example.net") != 0) ++ FAIL_EXIT1 ("invalid QNAME: %s", qname); ++ } ++ free (tail); ++ ++ int rcode; ++ if (addresses == 255) ++ { ++ /* Special case: Use no addresses with NXDOMAIN response. */ ++ rcode = ns_r_nxdomain; ++ addresses = 0; ++ } ++ else ++ rcode = 0; ++ ++ struct resolv_response_flags flags = { .rcode = rcode }; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ maybe_insert_sig (b, qname); ++ ++ /* Provide the requested number of CNAME records. */ ++ char *previous_name = (char *) qname; ++ for (int unique = 0; unique < cnames; ++unique) ++ { ++ resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60); ++ char *new_name = xasprintf ("%d.alias.example", unique); ++ resolv_response_add_name (b, new_name); ++ resolv_response_close_record (b); ++ ++ maybe_insert_sig (b, qname); ++ ++ if (previous_name != qname) ++ free (previous_name); ++ previous_name = new_name; ++ } ++ ++ for (int unique = 0; unique < addresses; ++unique) ++ { ++ resolv_response_open_record (b, previous_name, qclass, qtype, 60); ++ ++ if (qtype == T_A) ++ { ++ char ipv4[4] = {192, 0, 2, 1 + unique}; ++ resolv_response_add_data (b, &ipv4, sizeof (ipv4)); ++ } ++ else if (qtype == T_AAAA) ++ { ++ char ipv6[16] = ++ { ++ 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 1 + unique ++ }; ++ resolv_response_add_data (b, &ipv6, sizeof (ipv6)); ++ } ++ resolv_response_close_record (b); ++ } ++ ++ if (previous_name != qname) ++ free (previous_name); ++} ++ ++static char * ++make_qname (bool do_search, int cnames, int addresses) ++{ ++ return xasprintf ("a%d-c%d%s", ++ addresses, cnames, do_search ? "" : ".example.net"); ++} ++ ++static void ++check_cnames_failure (int af, bool do_search, int cnames, int addresses) ++{ ++ char *qname = make_qname (do_search, cnames, addresses); ++ ++ struct hostent *e; ++ if (af == AF_UNSPEC) ++ e = gethostbyname (qname); ++ else ++ e = gethostbyname2 (qname, af); ++ ++ if (addresses == 0) ++ check_hostent (qname, e, "error: NO_RECOVERY\n"); ++ else ++ check_hostent (qname, e, "error: HOST_NOT_FOUND\n"); ++ ++ free (qname); ++} ++ ++static void ++check (int af, bool do_search, int cnames, int addresses) ++{ ++ char *qname = make_qname (do_search, cnames, addresses); ++ char *fqdn = make_qname (false, cnames, addresses); ++ ++ struct hostent *e; ++ if (af == AF_UNSPEC) ++ e = gethostbyname (qname); ++ else ++ e = gethostbyname2 (qname, af); ++ if (e == NULL) ++ FAIL_EXIT1 ("unexpected failure for %d, %d, %d", af, cnames, addresses); ++ ++ if (af == AF_UNSPEC || af == AF_INET) ++ { ++ TEST_COMPARE (e->h_addrtype, AF_INET); ++ TEST_COMPARE (e->h_length, 4); ++ } ++ else ++ { ++ TEST_COMPARE (e->h_addrtype, AF_INET6); ++ TEST_COMPARE (e->h_length, 16); ++ } ++ ++ for (int i = 0; i < addresses; ++i) ++ { ++ char ipv4[4] = {192, 0, 2, 1 + i}; ++ char ipv6[16] = ++ { 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 + i }; ++ char *expected = e->h_addrtype == AF_INET ? ipv4 : ipv6; ++ TEST_COMPARE_BLOB (e->h_addr_list[i], e->h_length, ++ expected, e->h_length); ++ } ++ TEST_VERIFY (e->h_addr_list[addresses] == NULL); ++ ++ ++ if (cnames == 0) ++ { ++ /* QNAME is fully qualified. */ ++ TEST_COMPARE_STRING (e->h_name, fqdn); ++ TEST_VERIFY (e->h_aliases[0] == NULL); ++ } ++ else ++ { ++ /* Fully-qualified QNAME is demoted to an aliases. */ ++ TEST_COMPARE_STRING (e->h_aliases[0], fqdn); ++ ++ for (int i = 1; i <= cnames; ++i) ++ { ++ char *expected = xasprintf ("%d.alias.example", i - 1); ++ if (i == cnames) ++ TEST_COMPARE_STRING (e->h_name, expected); ++ else ++ TEST_COMPARE_STRING (e->h_aliases[i], expected); ++ free (expected); ++ } ++ TEST_VERIFY (e->h_aliases[cnames] == NULL); ++ } ++ ++ free (fqdn); ++ free (qname); ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response, ++ .search = { "example.net", "example.com" }, ++ }); ++ ++ static const int families[] = { AF_UNSPEC, AF_INET, AF_INET6 }; ++ ++ for (int do_insert_sig = 0; do_insert_sig < 2; ++do_insert_sig) ++ { ++ insert_sig = do_insert_sig; ++ ++ /* If do_search is true, a bare host name (for example, a1-c1) ++ is used. This exercises search path processing and FQDN ++ qualification. */ ++ for (int do_search = 0; do_search < 2; ++do_search) ++ for (const int *paf = families; paf != array_end (families); ++paf) ++ { ++ for (int cnames = 0; cnames <= 100; ++cnames) ++ { ++ check_cnames_failure (*paf, do_search, cnames, 0); ++ /* Now with NXDOMAIN responses. */ ++ check_cnames_failure (*paf, do_search, cnames, 255); ++ } ++ ++ for (int cnames = 0; cnames <= 10; ++cnames) ++ for (int addresses = 1; addresses <= 10; ++addresses) ++ check (*paf, do_search, cnames, addresses); ++ ++ /* The current implementation is limited to 47 aliases. ++ Addresses do not have such a limit. */ ++ check (*paf, do_search, 47, 60); ++ } ++ } ++ ++ resolv_test_end (obj); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/resolv/tst-resolv-byaddr.c b/resolv/tst-resolv-byaddr.c +--- a/resolv/tst-resolv-byaddr.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/tst-resolv-byaddr.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,326 @@ ++/* Test reverse DNS lookup. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tst-resolv-maybe_insert_sig.h" ++ ++/* QNAME format: ++ ++ ADDRESSES.CNAMES...(lots of 0s)...8.b.d.0.1.0.0.2.ip6.arpa. ++ CNAMES|ADDRESSES.2.0.192.in-addr-arpa. ++ ++ For the IPv4 reverse lookup, the address count is in the lower ++ bits. ++ ++ CNAMES is the length of the CNAME chain, ADDRESSES is the number of ++ addresses in the response. The special value 15 means that there ++ are no addresses, and the RCODE is NXDOMAIN. */ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_COMPARE (qclass, C_IN); ++ TEST_COMPARE (qtype, T_PTR); ++ ++ unsigned int addresses, cnames, bits; ++ char *tail; ++ if (strstr (qname, "ip6.arpa") != NULL ++ && sscanf (qname, "%x.%x.%ms", &addresses, &cnames, &tail) == 3) ++ TEST_COMPARE_STRING (tail, "\ ++0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"); ++ else if (sscanf (qname, "%u.%ms", &bits, &tail) == 2) ++ { ++ TEST_COMPARE_STRING (tail, "2.0.192.in-addr.arpa"); ++ addresses = bits & 0x0f; ++ cnames = bits >> 4; ++ } ++ else ++ FAIL_EXIT1 ("invalid QNAME: %s", qname); ++ free (tail); ++ ++ int rcode; ++ if (addresses == 15) ++ { ++ /* Special case: Use no addresses with NXDOMAIN response. */ ++ rcode = ns_r_nxdomain; ++ addresses = 0; ++ } ++ else ++ rcode = 0; ++ ++ struct resolv_response_flags flags = { .rcode = rcode }; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ maybe_insert_sig (b, qname); ++ ++ /* Provide the requested number of CNAME records. */ ++ char *previous_name = (char *) qname; ++ for (int unique = 0; unique < cnames; ++unique) ++ { ++ resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60); ++ char *new_name = xasprintf ("%d.alias.example", unique); ++ resolv_response_add_name (b, new_name); ++ resolv_response_close_record (b); ++ ++ maybe_insert_sig (b, qname); ++ ++ if (previous_name != qname) ++ free (previous_name); ++ previous_name = new_name; ++ } ++ ++ for (int unique = 0; unique < addresses; ++unique) ++ { ++ resolv_response_open_record (b, previous_name, qclass, T_PTR, 60); ++ char *ptr = xasprintf ("unique-%d.cnames-%u.addresses-%u.example", ++ unique, cnames, addresses); ++ resolv_response_add_name (b, ptr); ++ free (ptr); ++ resolv_response_close_record (b); ++ } ++ ++ if (previous_name != qname) ++ free (previous_name); ++} ++ ++/* Used to check that gethostbyaddr_r does not write past the buffer ++ end. */ ++static struct support_next_to_fault ntf; ++ ++/* Perform a gethostbyaddr call and check the result. */ ++static void ++check_gethostbyaddr (const char *address, const char *expected) ++{ ++ unsigned char bytes[16]; ++ unsigned int byteslen; ++ int family; ++ if (strchr (address, ':') != NULL) ++ { ++ family = AF_INET6; ++ byteslen = 16; ++ } ++ else ++ { ++ family = AF_INET; ++ byteslen = 4; ++ } ++ TEST_COMPARE (inet_pton (family, address, bytes), 1); ++ ++ struct hostent *e = gethostbyaddr (bytes, byteslen, family); ++ check_hostent (address, e, expected); ++ ++ if (e == NULL) ++ return; ++ ++ /* Try gethostbyaddr_r with increasing sizes until success. First ++ compute a reasonable minimum buffer size, to avoid many pointless ++ attempts. */ ++ size_t minimum_size = strlen (e->h_name); ++ for (int i = 0; e->h_addr_list[i] != NULL; ++i) ++ minimum_size += e->h_length + sizeof (char *); ++ for (int i = 0; e->h_aliases[i] != NULL; ++i) ++ minimum_size += strlen (e->h_aliases[i]) + 1 + sizeof (char *); ++ ++ /* Gradually increase the size until success. */ ++ for (size_t size = minimum_size; size < ntf.length; ++size) ++ { ++ struct hostent result; ++ int herrno; ++ int ret = gethostbyaddr_r (bytes, byteslen, family, &result, ++ ntf.buffer + ntf.length - size, size, ++ &e, &herrno); ++ if (ret == ERANGE) ++ /* Retry with larger size. */ ++ TEST_COMPARE (herrno, NETDB_INTERNAL); ++ else if (ret == 0) ++ { ++ TEST_VERIFY (size > minimum_size); ++ check_hostent (address, e, expected); ++ return; ++ } ++ else ++ FAIL_EXIT1 ("Unexpected gethostbyaddr_r failure: %d", ret); ++ } ++ ++ FAIL_EXIT1 ("gethostbyaddr_r always failed for: %s", address); ++} ++ ++/* Perform a getnameinfo call and check the result. */ ++static void ++check_getnameinfo (const char *address, const char *expected) ++{ ++ struct sockaddr_in sin = { }; ++ struct sockaddr_in6 sin6 = { }; ++ void *sa; ++ socklen_t salen; ++ if (strchr (address, ':') != NULL) ++ { ++ sin6.sin6_family = AF_INET6; ++ TEST_COMPARE (inet_pton (AF_INET6, address, &sin6.sin6_addr), 1); ++ sin6.sin6_port = htons (80); ++ sa = &sin6; ++ salen = sizeof (sin6); ++ } ++ else ++ { ++ sin.sin_family = AF_INET; ++ TEST_COMPARE (inet_pton (AF_INET, address, &sin.sin_addr), 1); ++ sin.sin_port = htons (80); ++ sa = &sin; ++ salen = sizeof (sin); ++ } ++ ++ char host[64]; ++ char service[64]; ++ int ret = getnameinfo (sa, salen, host, ++ sizeof (host), service, sizeof (service), ++ NI_NAMEREQD | NI_NUMERICSERV); ++ switch (ret) ++ { ++ case 0: ++ TEST_COMPARE_STRING (host, expected); ++ TEST_COMPARE_STRING (service, "80"); ++ break; ++ case EAI_SYSTEM: ++ TEST_COMPARE_STRING (strerror (errno), expected); ++ break; ++ default: ++ TEST_COMPARE_STRING (gai_strerror (ret), expected); ++ } ++} ++ ++static int ++do_test (void) ++{ ++ /* Some reasonably upper bound for the maximum response size. */ ++ ntf = support_next_to_fault_allocate (4096); ++ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response ++ }); ++ ++ for (int do_insert_sig = 0; do_insert_sig < 2; ++do_insert_sig) ++ { ++ insert_sig = do_insert_sig; ++ ++ /* No PTR record, RCODE=0. */ ++ check_gethostbyaddr ("192.0.2.0", "error: NO_RECOVERY\n"); ++ check_getnameinfo ("192.0.2.0", "Name or service not known"); ++ check_gethostbyaddr ("192.0.2.16", "error: NO_RECOVERY\n"); ++ check_getnameinfo ("192.0.2.16", "Name or service not known"); ++ check_gethostbyaddr ("192.0.2.32", "error: NO_RECOVERY\n"); ++ check_getnameinfo ("192.0.2.32", "Name or service not known"); ++ check_gethostbyaddr ("2001:db8::", "error: NO_RECOVERY\n"); ++ check_getnameinfo ("2001:db8::", "Name or service not known"); ++ check_gethostbyaddr ("2001:db8::10", "error: NO_RECOVERY\n"); ++ check_getnameinfo ("2001:db8::10", "Name or service not known"); ++ check_gethostbyaddr ("2001:db8::20", "error: NO_RECOVERY\n"); ++ check_getnameinfo ("2001:db8::20", "Name or service not known"); ++ ++ /* No PTR record, NXDOMAIN. */ ++ check_gethostbyaddr ("192.0.2.15", "error: HOST_NOT_FOUND\n"); ++ check_getnameinfo ("192.0.2.15", "Name or service not known"); ++ check_gethostbyaddr ("192.0.2.31", "error: HOST_NOT_FOUND\n"); ++ check_getnameinfo ("192.0.2.31", "Name or service not known"); ++ check_gethostbyaddr ("192.0.2.47", "error: HOST_NOT_FOUND\n"); ++ check_getnameinfo ("192.0.2.47", "Name or service not known"); ++ check_gethostbyaddr ("2001:db8::f", "error: HOST_NOT_FOUND\n"); ++ check_getnameinfo ("2001:db8::f", "Name or service not known"); ++ check_gethostbyaddr ("2001:db8::1f", "error: HOST_NOT_FOUND\n"); ++ check_getnameinfo ("2001:db8::1f", "Name or service not known"); ++ check_gethostbyaddr ("2001:db8::2f", "error: HOST_NOT_FOUND\n"); ++ check_getnameinfo ("2001:db8::2f", "Name or service not known"); ++ ++ /* Actual response data. Only the first PTR record is returned. */ ++ check_gethostbyaddr ("192.0.2.1", ++ "name: unique-0.cnames-0.addresses-1.example\n" ++ "address: 192.0.2.1\n"); ++ check_getnameinfo ("192.0.2.1", ++ "unique-0.cnames-0.addresses-1.example"); ++ check_gethostbyaddr ("192.0.2.17", ++ "name: unique-0.cnames-1.addresses-1.example\n" ++ "address: 192.0.2.17\n"); ++ check_getnameinfo ("192.0.2.17", ++ "unique-0.cnames-1.addresses-1.example"); ++ check_gethostbyaddr ("192.0.2.18", ++ "name: unique-0.cnames-1.addresses-2.example\n" ++ "address: 192.0.2.18\n"); ++ check_getnameinfo ("192.0.2.18", ++ "unique-0.cnames-1.addresses-2.example"); ++ check_gethostbyaddr ("192.0.2.33", ++ "name: unique-0.cnames-2.addresses-1.example\n" ++ "address: 192.0.2.33\n"); ++ check_getnameinfo ("192.0.2.33", ++ "unique-0.cnames-2.addresses-1.example"); ++ check_gethostbyaddr ("192.0.2.34", ++ "name: unique-0.cnames-2.addresses-2.example\n" ++ "address: 192.0.2.34\n"); ++ check_getnameinfo ("192.0.2.34", ++ "unique-0.cnames-2.addresses-2.example"); ++ ++ /* Same for IPv6 addresses. */ ++ check_gethostbyaddr ("2001:db8::1", ++ "name: unique-0.cnames-0.addresses-1.example\n" ++ "address: 2001:db8::1\n"); ++ check_getnameinfo ("2001:db8::1", ++ "unique-0.cnames-0.addresses-1.example"); ++ check_gethostbyaddr ("2001:db8::11", ++ "name: unique-0.cnames-1.addresses-1.example\n" ++ "address: 2001:db8::11\n"); ++ check_getnameinfo ("2001:db8::11", ++ "unique-0.cnames-1.addresses-1.example"); ++ check_gethostbyaddr ("2001:db8::12", ++ "name: unique-0.cnames-1.addresses-2.example\n" ++ "address: 2001:db8::12\n"); ++ check_getnameinfo ("2001:db8::12", ++ "unique-0.cnames-1.addresses-2.example"); ++ check_gethostbyaddr ("2001:db8::21", ++ "name: unique-0.cnames-2.addresses-1.example\n" ++ "address: 2001:db8::21\n"); ++ check_getnameinfo ("2001:db8::21", ++ "unique-0.cnames-2.addresses-1.example"); ++ check_gethostbyaddr ("2001:db8::22", ++ "name: unique-0.cnames-2.addresses-2.example\n" ++ "address: 2001:db8::22\n"); ++ check_getnameinfo ("2001:db8::22", ++ "unique-0.cnames-2.addresses-2.example"); ++ } ++ ++ resolv_test_end (obj); ++ ++ support_next_to_fault_free (&ntf); ++ return 0; ++} ++ ++#include +diff -Nuar a/resolv/tst-resolv-invalid-cname.c b/resolv/tst-resolv-invalid-cname.c +--- a/resolv/tst-resolv-invalid-cname.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/tst-resolv-invalid-cname.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,406 @@ ++/* Test handling of CNAMEs with non-host domain names (bug 12154). ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Query strings describe the CNAME chain in the response. They have ++ the format "bitsBITS.countCOUNT.example.", where BITS and COUNT are ++ replaced by unsigned decimal numbers. COUNT is the number of CNAME ++ records in the response. BITS has two bits for each CNAME record, ++ describing a special prefix that is added to that CNAME. ++ ++ 0: No special leading label. ++ 1: Starting with "*.". ++ 2: Starting with "-x.". ++ 3: Starting with "star.*.". ++ ++ The first CNAME in the response using the two least significant ++ bits. ++ ++ For PTR queries, the QNAME format is different, it is either ++ COUNT.BITS.168.192.in-addr.arpa. (with BITS and COUNT still ++ decimal), or: ++ ++COUNT.BITS0.BITS1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. ++ ++ where BITS and COUNT are hexadecimal. */ ++ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_COMPARE (qclass, C_IN); ++ ++ /* The only other query type besides A is PTR. */ ++ if (qtype != T_A && qtype != T_AAAA) ++ TEST_COMPARE (qtype, T_PTR); ++ ++ unsigned int bits, bits1, count; ++ char *tail = NULL; ++ if (sscanf (qname, "bits%u.count%u.%ms", &bits, &count, &tail) == 3) ++ TEST_COMPARE_STRING (tail, "example"); ++ else if (strstr (qname, "in-addr.arpa") != NULL ++ && sscanf (qname, "%u.%u.%ms", &bits, &count, &tail) == 3) ++ TEST_COMPARE_STRING (tail, "168.192.in-addr.arpa"); ++ else if (sscanf (qname, "%x.%x.%x.%ms", &bits, &bits1, &count, &tail) == 4) ++ { ++ TEST_COMPARE_STRING (tail, "\ ++0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"); ++ bits |= bits1 << 4; ++ } ++ else ++ FAIL_EXIT1 ("invalid QNAME: %s\n", qname); ++ free (tail); ++ ++ struct resolv_response_flags flags = {}; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ ++ /* Provide the requested number of CNAME records. */ ++ char *previous_name = (char *) qname; ++ unsigned int original_bits = bits; ++ for (int unique = 0; unique < count; ++unique) ++ { ++ resolv_response_open_record (b, previous_name, qclass, T_CNAME, 60); ++ ++ static const char bits_to_prefix[4][8] = { "", "*.", "-x.", "star.*." }; ++ char *new_name = xasprintf ("%sunique%d.example", ++ bits_to_prefix[bits & 3], unique); ++ bits >>= 2; ++ resolv_response_add_name (b, new_name); ++ resolv_response_close_record (b); ++ ++ if (previous_name != qname) ++ free (previous_name); ++ previous_name = new_name; ++ } ++ ++ /* Actual answer record. */ ++ resolv_response_open_record (b, previous_name, qclass, qtype, 60); ++ switch (qtype) ++ { ++ case T_A: ++ { ++ char ipv4[4] = {192, 168, count, original_bits}; ++ resolv_response_add_data (b, &ipv4, sizeof (ipv4)); ++ } ++ break; ++ case T_AAAA: ++ { ++ char ipv6[16] = ++ { ++ 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ count, original_bits ++ }; ++ resolv_response_add_data (b, &ipv6, sizeof (ipv6)); ++ } ++ break; ++ ++ case T_PTR: ++ { ++ char *name = xasprintf ("bits%u.count%u.example", ++ original_bits, count); ++ resolv_response_add_name (b, name); ++ free (name); ++ } ++ break; ++ } ++ resolv_response_close_record (b); ++ ++ if (previous_name != qname) ++ free (previous_name); ++} ++ ++/* Controls which name resolution function is invoked. */ ++enum test_mode ++ { ++ byname, /* gethostbyname. */ ++ byname2, /* gethostbyname2. */ ++ gai, /* getaddrinfo without AI_CANONNAME. */ ++ gai_canon, /* getaddrinfo with AI_CANONNAME. */ ++ ++ test_mode_num /* Number of enum values. */ ++ }; ++ ++static const char * ++test_mode_to_string (enum test_mode mode) ++{ ++ switch (mode) ++ { ++ case byname: ++ return "byname"; ++ case byname2: ++ return "byname2"; ++ case gai: ++ return "gai"; ++ case gai_canon: ++ return "gai_canon"; ++ case test_mode_num: ++ break; /* Report error below. */ ++ } ++ FAIL_EXIT1 ("invalid test_mode: %d", mode); ++} ++ ++/* Append the name and aliases to OUT. */ ++static void ++append_names (FILE *out, const char *qname, int bits, int count, ++ enum test_mode mode) ++{ ++ /* Largest valid index which has a corresponding zero in bits ++ (meaning a syntactically valid CNAME). */ ++ int last_valid_cname = -1; ++ ++ for (int i = 0; i < count; ++i) ++ if ((bits & (3 << (i * 2))) == 0) ++ last_valid_cname = i; ++ ++ if (mode != gai) ++ { ++ const char *label; ++ if (mode == gai_canon) ++ label = "canonname"; ++ else ++ label = "name"; ++ if (last_valid_cname >= 0) ++ fprintf (out, "%s: unique%d.example\n", label, last_valid_cname); ++ else ++ fprintf (out, "%s: %s\n", label, qname); ++ } ++ ++ if (mode == byname || mode == byname2) ++ { ++ if (last_valid_cname >= 0) ++ fprintf (out, "alias: %s\n", qname); ++ for (int i = 0; i < count; ++i) ++ { ++ if ((bits & (3 << (i * 2))) == 0 && i != last_valid_cname) ++ fprintf (out, "alias: unique%d.example\n", i); ++ } ++ } ++} ++ ++/* Append the address information to OUT. */ ++static void ++append_addresses (FILE *out, int af, int bits, int count, enum test_mode mode) ++{ ++ int last = count * 256 + bits; ++ if (mode == gai || mode == gai_canon) ++ { ++ if (af == AF_INET || af == AF_UNSPEC) ++ fprintf (out, "address: STREAM/TCP 192.168.%d.%d 80\n", count, bits); ++ if (af == AF_INET6 || af == AF_UNSPEC) ++ { ++ if (last == 0) ++ fprintf (out, "address: STREAM/TCP 2001:db8:: 80\n"); ++ else ++ fprintf (out, "address: STREAM/TCP 2001:db8::%x 80\n", last); ++ } ++ } ++ else ++ { ++ TEST_VERIFY (af != AF_UNSPEC); ++ if (af == AF_INET) ++ fprintf (out, "address: 192.168.%d.%d\n", count, bits); ++ if (af == AF_INET6) ++ { ++ if (last == 0) ++ fprintf (out, "address: 2001:db8::\n"); ++ else ++ fprintf (out, "address: 2001:db8::%x\n", last); ++ } ++ } ++} ++ ++/* Perform one test using a forward lookup. */ ++static void ++check_forward (int af, int bits, int count, enum test_mode mode) ++{ ++ char *qname = xasprintf ("bits%d.count%d.example", bits, count); ++ char *label = xasprintf ("af=%d bits=%d count=%d mode=%s qname=%s", ++ af, bits, count, test_mode_to_string (mode), qname); ++ ++ struct xmemstream expected; ++ xopen_memstream (&expected); ++ if (mode == gai_canon) ++ fprintf (expected.out, "flags: AI_CANONNAME\n"); ++ append_names (expected.out, qname, bits, count, mode); ++ append_addresses (expected.out, af, bits, count, mode); ++ xfclose_memstream (&expected); ++ ++ if (mode == gai || mode == gai_canon) ++ { ++ struct addrinfo *ai; ++ struct addrinfo hints = ++ { ++ .ai_family = af, ++ .ai_socktype = SOCK_STREAM, ++ }; ++ if (mode == gai_canon) ++ hints.ai_flags |= AI_CANONNAME; ++ int ret = getaddrinfo (qname, "80", &hints, &ai); ++ check_addrinfo (label, ai, ret, expected.buffer); ++ if (ret == 0) ++ freeaddrinfo (ai); ++ } ++ else ++ { ++ struct hostent *e; ++ if (mode == gai) ++ { ++ TEST_COMPARE (af, AF_INET); ++ e = gethostbyname (qname); ++ } ++ else ++ { ++ if (af != AF_INET) ++ TEST_COMPARE (af, AF_INET6); ++ e = gethostbyname2 (qname, af); ++ } ++ check_hostent (label, e, expected.buffer); ++ } ++ ++ free (expected.buffer); ++ free (label); ++ free (qname); ++} ++ ++/* Perform one check using a reverse lookup. */ ++ ++static void ++check_reverse (int af, int bits, int count) ++{ ++ TEST_VERIFY (af == AF_INET || af == AF_INET6); ++ ++ char *label = xasprintf ("af=%d bits=%d count=%d", af, bits, count); ++ char *fqdn = xasprintf ("bits%d.count%d.example", bits, count); ++ ++ struct xmemstream expected; ++ xopen_memstream (&expected); ++ fprintf (expected.out, "name: %s\n", fqdn); ++ append_addresses (expected.out, af, bits, count, byname); ++ xfclose_memstream (&expected); ++ ++ char addr[16] = { 0 }; ++ socklen_t addrlen; ++ if (af == AF_INET) ++ { ++ addr[0] = 192; ++ addr[1] = 168; ++ addr[2] = count; ++ addr[3] = bits; ++ addrlen = 4; ++ } ++ else ++ { ++ addr[0] = 0x20; ++ addr[1] = 0x01; ++ addr[2] = 0x0d; ++ addr[3] = 0xb8; ++ addr[14] = count; ++ addr[15] = bits; ++ addrlen = 16; ++ } ++ ++ struct hostent *e = gethostbyaddr (addr, addrlen, af); ++ check_hostent (label, e, expected.buffer); ++ ++ /* getnameinfo check is different. There is no generic check_* ++ function for it. */ ++ { ++ struct sockaddr_in sin = { }; ++ struct sockaddr_in6 sin6 = { }; ++ void *sa; ++ socklen_t salen; ++ if (af == AF_INET) ++ { ++ sin.sin_family = AF_INET; ++ memcpy (&sin.sin_addr, addr, addrlen); ++ sin.sin_port = htons (80); ++ sa = &sin; ++ salen = sizeof (sin); ++ } ++ else ++ { ++ sin6.sin6_family = AF_INET6; ++ memcpy (&sin6.sin6_addr, addr, addrlen); ++ sin6.sin6_port = htons (80); ++ sa = &sin6; ++ salen = sizeof (sin6); ++ } ++ ++ char host[64]; ++ char service[64]; ++ int ret = getnameinfo (sa, salen, host, ++ sizeof (host), service, sizeof (service), ++ NI_NAMEREQD | NI_NUMERICSERV); ++ TEST_COMPARE (ret, 0); ++ TEST_COMPARE_STRING (host, fqdn); ++ TEST_COMPARE_STRING (service, "80"); ++ } ++ ++ free (expected.buffer); ++ free (fqdn); ++ free (label); ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response ++ }); ++ ++ for (int count = 0; count <= 3; ++count) ++ for (int bits = 0; bits <= 1 << (count * 2); ++bits) ++ { ++ if (count > 0 && bits == count) ++ /* The last bits value is only checked if count == 0. */ ++ continue; ++ ++ for (enum test_mode mode = 0; mode < test_mode_num; ++mode) ++ { ++ check_forward (AF_INET, bits, count, mode); ++ if (mode != byname) ++ check_forward (AF_INET6, bits, count, mode); ++ if (mode == gai || mode == gai_canon) ++ check_forward (AF_UNSPEC, bits, count, mode); ++ } ++ ++ check_reverse (AF_INET, bits, count); ++ check_reverse (AF_INET6, bits, count); ++ } ++ ++ resolv_test_end (obj); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/resolv/tst-resolv-maybe_insert_sig.h b/resolv/tst-resolv-maybe_insert_sig.h +--- a/resolv/tst-resolv-maybe_insert_sig.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/tst-resolv-maybe_insert_sig.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,32 @@ ++/* Code snippet for optionally inserting ignored SIG records in resolver tests. ++ Copyright (C) 2022 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 ++ . */ ++ ++/* Set to true for an alternative pass that inserts (ignored) SIG ++ records. This does not alter the response, so this property is not ++ encoded in the QNAME. The variable needs to be volatile because ++ leaf attributes tell GCC that the response function is not ++ called. */ ++static volatile bool insert_sig; ++ ++static void ++maybe_insert_sig (struct resolv_response_builder *b, const char *owner) ++{ ++ resolv_response_open_record (b, owner, C_IN, T_SIG, 60); ++ resolv_response_add_data (b, "", 1); ++ resolv_response_close_record (b); ++} +diff -Nuar a/resolv/tst-resolv-semi-failure.c b/resolv/tst-resolv-semi-failure.c +--- a/resolv/tst-resolv-semi-failure.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/tst-resolv-semi-failure.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,133 @@ ++/* Test parallel failure/success responses (bug 30081). ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++/* The rcode in the initial response. */ ++static volatile int rcode; ++ ++/* Whether to fail the initial A query (!fail_aaaa) or the initial ++ AAAA query (fail_aaaa). */ ++static volatile bool fail_aaaa; ++ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ /* Handle the failing query. */ ++ if ((fail_aaaa && qtype == T_AAAA) && ctx->server_index == 0) ++ { ++ struct resolv_response_flags flags = {.rcode = rcode}; ++ resolv_response_init (b, flags); ++ return; ++ } ++ ++ /* Otherwise produce a response. */ ++ resolv_response_init (b, (struct resolv_response_flags) {}); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ resolv_response_open_record (b, qname, qclass, qtype, 0); ++ switch (qtype) ++ { ++ case T_A: ++ { ++ char ipv4[4] = {192, 0, 2, 17}; ++ resolv_response_add_data (b, &ipv4, sizeof (ipv4)); ++ } ++ break; ++ case T_AAAA: ++ { ++ char ipv6[16] ++ = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; ++ resolv_response_add_data (b, &ipv6, sizeof (ipv6)); ++ } ++ break; ++ default: ++ FAIL_EXIT1 ("unexpected TYPE%d query", qtype); ++ } ++ resolv_response_close_record (b); ++} ++ ++static void ++check_one (void) ++{ ++ ++ /* The buggy 1-second query timeout results in 30 seconds of delay, ++ which triggers are test timeout failure. */ ++ for (int i = 0; i < 30; ++i) ++ { ++ static const struct addrinfo hints = ++ { ++ .ai_family = AF_UNSPEC, ++ .ai_socktype = SOCK_STREAM, ++ }; ++ struct addrinfo *ai; ++ int ret = getaddrinfo ("www.example", "80", &hints, &ai); ++ const char *expected; ++ if (ret == 0 && ai->ai_next != NULL) ++ expected = ("address: STREAM/TCP 192.0.2.17 80\n" ++ "address: STREAM/TCP 2001:db8::1 80\n"); ++ else ++ /* Only one response because the AAAA lookup failure is ++ treated as an ignoreable error. */ ++ expected = "address: STREAM/TCP 192.0.2.17 80\n"; ++ check_addrinfo ("www.example", ai, ret, expected); ++ if (ret == 0) ++ freeaddrinfo (ai); ++ } ++} ++ ++static int ++do_test (void) ++{ ++ for (int do_single_lookup = 0; do_single_lookup < 2; ++do_single_lookup) ++ { ++ struct resolv_test *aux = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response, ++ }); ++ ++ if (do_single_lookup) ++ _res.options |= RES_SNGLKUP; ++ ++ for (int do_fail_aaaa = 0; do_fail_aaaa < 2; ++do_fail_aaaa) ++ { ++ fail_aaaa = do_fail_aaaa; ++ ++ rcode = 2; /* SERVFAIL. */ ++ check_one (); ++ ++ rcode = 4; /* NOTIMP. */ ++ check_one (); ++ ++ rcode = 5; /* REFUSED. */ ++ check_one (); ++ } ++ ++ resolv_test_end (aux); ++ } ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/resolv/tst-resolv-short-response.c b/resolv/tst-resolv-short-response.c +--- a/resolv/tst-resolv-short-response.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/resolv/tst-resolv-short-response.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,126 @@ ++/* Test for spurious timeouts with short 12-byte responses (bug 31890). ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++/* The rcode in the initial response. */ ++static volatile int rcode; ++ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ switch (ctx->server_index) ++ { ++ case 0: ++ /* First server times out. */ ++ { ++ struct resolv_response_flags flags = {.rcode = rcode}; ++ resolv_response_init (b, flags); ++ } ++ break; ++ case 1: ++ /* Second server sends reply. */ ++ resolv_response_init (b, (struct resolv_response_flags) {}); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ resolv_response_open_record (b, qname, qclass, qtype, 0); ++ switch (qtype) ++ { ++ case T_A: ++ { ++ char ipv4[4] = {192, 0, 2, 17}; ++ resolv_response_add_data (b, &ipv4, sizeof (ipv4)); ++ } ++ break; ++ case T_AAAA: ++ { ++ char ipv6[16] ++ = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; ++ resolv_response_add_data (b, &ipv6, sizeof (ipv6)); ++ } ++ break; ++ default: ++ FAIL_EXIT1 ("unexpected TYPE%d query", qtype); ++ } ++ resolv_response_close_record (b); ++ break; ++ default: ++ FAIL_EXIT1 ("unexpected query to server %d", ctx->server_index); ++ } ++} ++ ++static void ++check_one (void) ++{ ++ ++ /* The buggy 1-second query timeout results in 30 seconds of delay, ++ which triggers a test timeout failure. */ ++ for (int i = 0; i < 10; ++i) ++ { ++ check_hostent ("www.example", gethostbyname ("www.example"), ++ "name: www.example\n" ++ "address: 192.0.2.17\n"); ++ check_hostent ("www.example", gethostbyname2 ("www.example", AF_INET6), ++ "name: www.example\n" ++ "address: 2001:db8::1\n"); ++ static const struct addrinfo hints = ++ { ++ .ai_family = AF_UNSPEC, ++ .ai_socktype = SOCK_STREAM, ++ }; ++ struct addrinfo *ai; ++ int ret = getaddrinfo ("www.example", "80", &hints, &ai); ++ check_addrinfo ("www.example", ai, ret, ++ "address: STREAM/TCP 192.0.2.17 80\n" ++ "address: STREAM/TCP 2001:db8::1 80\n"); ++ if (ret == 0) ++ freeaddrinfo (ai); ++ } ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *aux = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response, ++ }); ++ ++ _res.options |= RES_SNGLKUP; ++ ++ rcode = 2; /* SERVFAIL. */ ++ check_one (); ++ ++ rcode = 4; /* NOTIMP. */ ++ check_one (); ++ ++ rcode = 5; /* REFUSED. */ ++ check_one (); ++ ++ resolv_test_end (aux); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/scripts/dso-ordering-test.py b/scripts/dso-ordering-test.py +--- a/scripts/dso-ordering-test.py 2022-02-03 08:27:54.000000000 +0300 ++++ b/scripts/dso-ordering-test.py 2025-10-20 21:02:21.000000000 +0300 +@@ -707,13 +707,12 @@ + "\t$(compile.c) $(OUTPUT_OPTION)\n") + makefile.write (rule) + +- not_depended_objs = find_objs_not_depended_on(test_descr) +- if not_depended_objs: +- depstr = "" +- for dep in not_depended_objs: +- depstr += (" $(objpfx)" + test_subdir + "/" +- + test_name + "-" + dep + ".so") +- makefile.write("$(objpfx)%s.out:%s\n" % (base_test_name, depstr)) ++ # Ensure that all shared objects are built before running the ++ # test, whether there link-time dependencies or not. ++ depobjs = ["$(objpfx){}/{}-{}.so".format(test_subdir, test_name, dep) ++ for dep in test_descr.objs] ++ makefile.write("$(objpfx){}.out: {}\n".format( ++ base_test_name, " ".join(depobjs))) + + # Add main executable to test-srcs + makefile.write("test-srcs += %s/%s\n" % (test_subdir, test_name)) +diff -Nuar a/scripts/glibcelf.py b/scripts/glibcelf.py +--- a/scripts/glibcelf.py 1970-01-01 02:00:00.000000000 +0200 ++++ b/scripts/glibcelf.py 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,1141 @@ ++#!/usr/bin/python3 ++# ELF support functionality for Python. ++# Copyright (C) 2022 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 ++# . ++ ++"""Basic ELF parser. ++ ++Use Image.readfile(path) to read an ELF file into memory and begin ++parsing it. ++ ++""" ++ ++import collections ++import enum ++import struct ++ ++if not hasattr(enum, 'IntFlag'): ++ import sys ++ sys.stdout.write( ++ 'warning: glibcelf.py needs Python 3.6 for enum support\n') ++ sys.exit(77) ++ ++class _OpenIntEnum(enum.IntEnum): ++ """Integer enumeration that supports arbitrary int values.""" ++ @classmethod ++ def _missing_(cls, value): ++ # See enum.IntFlag._create_pseudo_member_. This allows ++ # creating of enum constants with arbitrary integer values. ++ pseudo_member = int.__new__(cls, value) ++ pseudo_member._name_ = None ++ pseudo_member._value_ = value ++ return pseudo_member ++ ++ def __repr__(self): ++ name = self._name_ ++ if name is not None: ++ # The names have prefixes like SHT_, implying their type. ++ return name ++ return '{}({})'.format(self.__class__.__name__, self._value_) ++ ++ def __str__(self): ++ name = self._name_ ++ if name is not None: ++ return name ++ return str(self._value_) ++ ++class ElfClass(_OpenIntEnum): ++ """ELF word size. Type of EI_CLASS values.""" ++ ELFCLASSNONE = 0 ++ ELFCLASS32 = 1 ++ ELFCLASS64 = 2 ++ ++class ElfData(_OpenIntEnum): ++ """ELF endianess. Type of EI_DATA values.""" ++ ELFDATANONE = 0 ++ ELFDATA2LSB = 1 ++ ELFDATA2MSB = 2 ++ ++class Machine(_OpenIntEnum): ++ """ELF machine type. Type of values in Ehdr.e_machine field.""" ++ EM_NONE = 0 ++ EM_M32 = 1 ++ EM_SPARC = 2 ++ EM_386 = 3 ++ EM_68K = 4 ++ EM_88K = 5 ++ EM_IAMCU = 6 ++ EM_860 = 7 ++ EM_MIPS = 8 ++ EM_S370 = 9 ++ EM_MIPS_RS3_LE = 10 ++ EM_PARISC = 15 ++ EM_VPP500 = 17 ++ EM_SPARC32PLUS = 18 ++ EM_960 = 19 ++ EM_PPC = 20 ++ EM_PPC64 = 21 ++ EM_S390 = 22 ++ EM_SPU = 23 ++ EM_V800 = 36 ++ EM_FR20 = 37 ++ EM_RH32 = 38 ++ EM_RCE = 39 ++ EM_ARM = 40 ++ EM_FAKE_ALPHA = 41 ++ EM_SH = 42 ++ EM_SPARCV9 = 43 ++ EM_TRICORE = 44 ++ EM_ARC = 45 ++ EM_H8_300 = 46 ++ EM_H8_300H = 47 ++ EM_H8S = 48 ++ EM_H8_500 = 49 ++ EM_IA_64 = 50 ++ EM_MIPS_X = 51 ++ EM_COLDFIRE = 52 ++ EM_68HC12 = 53 ++ EM_MMA = 54 ++ EM_PCP = 55 ++ EM_NCPU = 56 ++ EM_NDR1 = 57 ++ EM_STARCORE = 58 ++ EM_ME16 = 59 ++ EM_ST100 = 60 ++ EM_TINYJ = 61 ++ EM_X86_64 = 62 ++ EM_PDSP = 63 ++ EM_PDP10 = 64 ++ EM_PDP11 = 65 ++ EM_FX66 = 66 ++ EM_ST9PLUS = 67 ++ EM_ST7 = 68 ++ EM_68HC16 = 69 ++ EM_68HC11 = 70 ++ EM_68HC08 = 71 ++ EM_68HC05 = 72 ++ EM_SVX = 73 ++ EM_ST19 = 74 ++ EM_VAX = 75 ++ EM_CRIS = 76 ++ EM_JAVELIN = 77 ++ EM_FIREPATH = 78 ++ EM_ZSP = 79 ++ EM_MMIX = 80 ++ EM_HUANY = 81 ++ EM_PRISM = 82 ++ EM_AVR = 83 ++ EM_FR30 = 84 ++ EM_D10V = 85 ++ EM_D30V = 86 ++ EM_V850 = 87 ++ EM_M32R = 88 ++ EM_MN10300 = 89 ++ EM_MN10200 = 90 ++ EM_PJ = 91 ++ EM_OPENRISC = 92 ++ EM_ARC_COMPACT = 93 ++ EM_XTENSA = 94 ++ EM_VIDEOCORE = 95 ++ EM_TMM_GPP = 96 ++ EM_NS32K = 97 ++ EM_TPC = 98 ++ EM_SNP1K = 99 ++ EM_ST200 = 100 ++ EM_IP2K = 101 ++ EM_MAX = 102 ++ EM_CR = 103 ++ EM_F2MC16 = 104 ++ EM_MSP430 = 105 ++ EM_BLACKFIN = 106 ++ EM_SE_C33 = 107 ++ EM_SEP = 108 ++ EM_ARCA = 109 ++ EM_UNICORE = 110 ++ EM_EXCESS = 111 ++ EM_DXP = 112 ++ EM_ALTERA_NIOS2 = 113 ++ EM_CRX = 114 ++ EM_XGATE = 115 ++ EM_C166 = 116 ++ EM_M16C = 117 ++ EM_DSPIC30F = 118 ++ EM_CE = 119 ++ EM_M32C = 120 ++ EM_TSK3000 = 131 ++ EM_RS08 = 132 ++ EM_SHARC = 133 ++ EM_ECOG2 = 134 ++ EM_SCORE7 = 135 ++ EM_DSP24 = 136 ++ EM_VIDEOCORE3 = 137 ++ EM_LATTICEMICO32 = 138 ++ EM_SE_C17 = 139 ++ EM_TI_C6000 = 140 ++ EM_TI_C2000 = 141 ++ EM_TI_C5500 = 142 ++ EM_TI_ARP32 = 143 ++ EM_TI_PRU = 144 ++ EM_MMDSP_PLUS = 160 ++ EM_CYPRESS_M8C = 161 ++ EM_R32C = 162 ++ EM_TRIMEDIA = 163 ++ EM_QDSP6 = 164 ++ EM_8051 = 165 ++ EM_STXP7X = 166 ++ EM_NDS32 = 167 ++ EM_ECOG1X = 168 ++ EM_MAXQ30 = 169 ++ EM_XIMO16 = 170 ++ EM_MANIK = 171 ++ EM_CRAYNV2 = 172 ++ EM_RX = 173 ++ EM_METAG = 174 ++ EM_MCST_ELBRUS = 175 ++ EM_ECOG16 = 176 ++ EM_CR16 = 177 ++ EM_ETPU = 178 ++ EM_SLE9X = 179 ++ EM_L10M = 180 ++ EM_K10M = 181 ++ EM_AARCH64 = 183 ++ EM_AVR32 = 185 ++ EM_STM8 = 186 ++ EM_TILE64 = 187 ++ EM_TILEPRO = 188 ++ EM_MICROBLAZE = 189 ++ EM_CUDA = 190 ++ EM_TILEGX = 191 ++ EM_CLOUDSHIELD = 192 ++ EM_COREA_1ST = 193 ++ EM_COREA_2ND = 194 ++ EM_ARCV2 = 195 ++ EM_OPEN8 = 196 ++ EM_RL78 = 197 ++ EM_VIDEOCORE5 = 198 ++ EM_78KOR = 199 ++ EM_56800EX = 200 ++ EM_BA1 = 201 ++ EM_BA2 = 202 ++ EM_XCORE = 203 ++ EM_MCHP_PIC = 204 ++ EM_INTELGT = 205 ++ EM_KM32 = 210 ++ EM_KMX32 = 211 ++ EM_EMX16 = 212 ++ EM_EMX8 = 213 ++ EM_KVARC = 214 ++ EM_CDP = 215 ++ EM_COGE = 216 ++ EM_COOL = 217 ++ EM_NORC = 218 ++ EM_CSR_KALIMBA = 219 ++ EM_Z80 = 220 ++ EM_VISIUM = 221 ++ EM_FT32 = 222 ++ EM_MOXIE = 223 ++ EM_AMDGPU = 224 ++ EM_RISCV = 243 ++ EM_BPF = 247 ++ EM_CSKY = 252 ++ EM_NUM = 253 ++ EM_ALPHA = 0x9026 ++ ++class Et(_OpenIntEnum): ++ """ELF file type. Type of ET_* values and the Ehdr.e_type field.""" ++ ET_NONE = 0 ++ ET_REL = 1 ++ ET_EXEC = 2 ++ ET_DYN = 3 ++ ET_CORE = 4 ++ ++class Shn(_OpenIntEnum): ++ """ELF reserved section indices.""" ++ SHN_UNDEF = 0 ++ SHN_BEFORE = 0xff00 ++ SHN_AFTER = 0xff01 ++ SHN_ABS = 0xfff1 ++ SHN_COMMON = 0xfff2 ++ SHN_XINDEX = 0xffff ++ ++class ShnMIPS(enum.Enum): ++ """Supplemental SHN_* constants for EM_MIPS.""" ++ SHN_MIPS_ACOMMON = 0xff00 ++ SHN_MIPS_TEXT = 0xff01 ++ SHN_MIPS_DATA = 0xff02 ++ SHN_MIPS_SCOMMON = 0xff03 ++ SHN_MIPS_SUNDEFINED = 0xff04 ++ ++class ShnPARISC(enum.Enum): ++ """Supplemental SHN_* constants for EM_PARISC.""" ++ SHN_PARISC_ANSI_COMMON = 0xff00 ++ SHN_PARISC_HUGE_COMMON = 0xff01 ++ ++class Sht(_OpenIntEnum): ++ """ELF section types. Type of SHT_* values.""" ++ SHT_NULL = 0 ++ SHT_PROGBITS = 1 ++ SHT_SYMTAB = 2 ++ SHT_STRTAB = 3 ++ SHT_RELA = 4 ++ SHT_HASH = 5 ++ SHT_DYNAMIC = 6 ++ SHT_NOTE = 7 ++ SHT_NOBITS = 8 ++ SHT_REL = 9 ++ SHT_SHLIB = 10 ++ SHT_DYNSYM = 11 ++ SHT_INIT_ARRAY = 14 ++ SHT_FINI_ARRAY = 15 ++ SHT_PREINIT_ARRAY = 16 ++ SHT_GROUP = 17 ++ SHT_SYMTAB_SHNDX = 18 ++ SHT_GNU_ATTRIBUTES = 0x6ffffff5 ++ SHT_GNU_HASH = 0x6ffffff6 ++ SHT_GNU_LIBLIST = 0x6ffffff7 ++ SHT_CHECKSUM = 0x6ffffff8 ++ SHT_SUNW_move = 0x6ffffffa ++ SHT_SUNW_COMDAT = 0x6ffffffb ++ SHT_SUNW_syminfo = 0x6ffffffc ++ SHT_GNU_verdef = 0x6ffffffd ++ SHT_GNU_verneed = 0x6ffffffe ++ SHT_GNU_versym = 0x6fffffff ++ ++class ShtALPHA(enum.Enum): ++ """Supplemental SHT_* constants for EM_ALPHA.""" ++ SHT_ALPHA_DEBUG = 0x70000001 ++ SHT_ALPHA_REGINFO = 0x70000002 ++ ++class ShtARM(enum.Enum): ++ """Supplemental SHT_* constants for EM_ARM.""" ++ SHT_ARM_EXIDX = 0x70000001 ++ SHT_ARM_PREEMPTMAP = 0x70000002 ++ SHT_ARM_ATTRIBUTES = 0x70000003 ++ ++class ShtCSKY(enum.Enum): ++ """Supplemental SHT_* constants for EM_CSKY.""" ++ SHT_CSKY_ATTRIBUTES = 0x70000001 ++ ++class ShtIA_64(enum.Enum): ++ """Supplemental SHT_* constants for EM_IA_64.""" ++ SHT_IA_64_EXT = 0x70000000 ++ SHT_IA_64_UNWIND = 0x70000001 ++ ++class ShtMIPS(enum.Enum): ++ """Supplemental SHT_* constants for EM_MIPS.""" ++ SHT_MIPS_LIBLIST = 0x70000000 ++ SHT_MIPS_MSYM = 0x70000001 ++ SHT_MIPS_CONFLICT = 0x70000002 ++ SHT_MIPS_GPTAB = 0x70000003 ++ SHT_MIPS_UCODE = 0x70000004 ++ SHT_MIPS_DEBUG = 0x70000005 ++ SHT_MIPS_REGINFO = 0x70000006 ++ SHT_MIPS_PACKAGE = 0x70000007 ++ SHT_MIPS_PACKSYM = 0x70000008 ++ SHT_MIPS_RELD = 0x70000009 ++ SHT_MIPS_IFACE = 0x7000000b ++ SHT_MIPS_CONTENT = 0x7000000c ++ SHT_MIPS_OPTIONS = 0x7000000d ++ SHT_MIPS_SHDR = 0x70000010 ++ SHT_MIPS_FDESC = 0x70000011 ++ SHT_MIPS_EXTSYM = 0x70000012 ++ SHT_MIPS_DENSE = 0x70000013 ++ SHT_MIPS_PDESC = 0x70000014 ++ SHT_MIPS_LOCSYM = 0x70000015 ++ SHT_MIPS_AUXSYM = 0x70000016 ++ SHT_MIPS_OPTSYM = 0x70000017 ++ SHT_MIPS_LOCSTR = 0x70000018 ++ SHT_MIPS_LINE = 0x70000019 ++ SHT_MIPS_RFDESC = 0x7000001a ++ SHT_MIPS_DELTASYM = 0x7000001b ++ SHT_MIPS_DELTAINST = 0x7000001c ++ SHT_MIPS_DELTACLASS = 0x7000001d ++ SHT_MIPS_DWARF = 0x7000001e ++ SHT_MIPS_DELTADECL = 0x7000001f ++ SHT_MIPS_SYMBOL_LIB = 0x70000020 ++ SHT_MIPS_EVENTS = 0x70000021 ++ SHT_MIPS_TRANSLATE = 0x70000022 ++ SHT_MIPS_PIXIE = 0x70000023 ++ SHT_MIPS_XLATE = 0x70000024 ++ SHT_MIPS_XLATE_DEBUG = 0x70000025 ++ SHT_MIPS_WHIRL = 0x70000026 ++ SHT_MIPS_EH_REGION = 0x70000027 ++ SHT_MIPS_XLATE_OLD = 0x70000028 ++ SHT_MIPS_PDR_EXCEPTION = 0x70000029 ++ SHT_MIPS_XHASH = 0x7000002b ++ ++class ShtPARISC(enum.Enum): ++ """Supplemental SHT_* constants for EM_PARISC.""" ++ SHT_PARISC_EXT = 0x70000000 ++ SHT_PARISC_UNWIND = 0x70000001 ++ SHT_PARISC_DOC = 0x70000002 ++ ++class Pf(enum.IntFlag): ++ """Program header flags. Type of Phdr.p_flags values.""" ++ PF_X = 1 ++ PF_W = 2 ++ PF_R = 4 ++ ++class PfARM(enum.IntFlag): ++ """Supplemental PF_* flags for EM_ARM.""" ++ PF_ARM_SB = 0x10000000 ++ PF_ARM_PI = 0x20000000 ++ PF_ARM_ABS = 0x40000000 ++ ++class PfPARISC(enum.IntFlag): ++ """Supplemental PF_* flags for EM_PARISC.""" ++ PF_HP_PAGE_SIZE = 0x00100000 ++ PF_HP_FAR_SHARED = 0x00200000 ++ PF_HP_NEAR_SHARED = 0x00400000 ++ PF_HP_CODE = 0x01000000 ++ PF_HP_MODIFY = 0x02000000 ++ PF_HP_LAZYSWAP = 0x04000000 ++ PF_HP_SBP = 0x08000000 ++ ++class PfIA_64(enum.IntFlag): ++ """Supplemental PF_* flags for EM_IA_64.""" ++ PF_IA_64_NORECOV = 0x80000000 ++ ++class PfMIPS(enum.IntFlag): ++ """Supplemental PF_* flags for EM_MIPS.""" ++ PF_MIPS_LOCAL = 0x10000000 ++ ++class Shf(enum.IntFlag): ++ """Section flags. Type of Shdr.sh_type values.""" ++ SHF_WRITE = 1 << 0 ++ SHF_ALLOC = 1 << 1 ++ SHF_EXECINSTR = 1 << 2 ++ SHF_MERGE = 1 << 4 ++ SHF_STRINGS = 1 << 5 ++ SHF_INFO_LINK = 1 << 6 ++ SHF_LINK_ORDER = 1 << 7 ++ SHF_OS_NONCONFORMING = 256 ++ SHF_GROUP = 1 << 9 ++ SHF_TLS = 1 << 10 ++ SHF_COMPRESSED = 1 << 11 ++ SHF_GNU_RETAIN = 1 << 21 ++ SHF_ORDERED = 1 << 30 ++ SHF_EXCLUDE = 1 << 31 ++ ++class ShfALPHA(enum.IntFlag): ++ """Supplemental SHF_* constants for EM_ALPHA.""" ++ SHF_ALPHA_GPREL = 0x10000000 ++ ++class ShfARM(enum.IntFlag): ++ """Supplemental SHF_* constants for EM_ARM.""" ++ SHF_ARM_ENTRYSECT = 0x10000000 ++ SHF_ARM_COMDEF = 0x80000000 ++ ++class ShfIA_64(enum.IntFlag): ++ """Supplemental SHF_* constants for EM_IA_64.""" ++ SHF_IA_64_SHORT = 0x10000000 ++ SHF_IA_64_NORECOV = 0x20000000 ++ ++class ShfMIPS(enum.IntFlag): ++ """Supplemental SHF_* constants for EM_MIPS.""" ++ SHF_MIPS_GPREL = 0x10000000 ++ SHF_MIPS_MERGE = 0x20000000 ++ SHF_MIPS_ADDR = 0x40000000 ++ SHF_MIPS_STRINGS = 0x80000000 ++ SHF_MIPS_NOSTRIP = 0x08000000 ++ SHF_MIPS_LOCAL = 0x04000000 ++ SHF_MIPS_NAMES = 0x02000000 ++ SHF_MIPS_NODUPE = 0x01000000 ++ ++class ShfPARISC(enum.IntFlag): ++ """Supplemental SHF_* constants for EM_PARISC.""" ++ SHF_PARISC_SHORT = 0x20000000 ++ SHF_PARISC_HUGE = 0x40000000 ++ SHF_PARISC_SBP = 0x80000000 ++ ++class Stb(_OpenIntEnum): ++ """ELF symbol binding type.""" ++ STB_LOCAL = 0 ++ STB_GLOBAL = 1 ++ STB_WEAK = 2 ++ STB_GNU_UNIQUE = 10 ++ STB_MIPS_SPLIT_COMMON = 13 ++ ++class Stt(_OpenIntEnum): ++ """ELF symbol type.""" ++ STT_NOTYPE = 0 ++ STT_OBJECT = 1 ++ STT_FUNC = 2 ++ STT_SECTION = 3 ++ STT_FILE = 4 ++ STT_COMMON = 5 ++ STT_TLS = 6 ++ STT_GNU_IFUNC = 10 ++ ++class SttARM(enum.Enum): ++ """Supplemental STT_* constants for EM_ARM.""" ++ STT_ARM_TFUNC = 13 ++ STT_ARM_16BIT = 15 ++ ++class SttPARISC(enum.Enum): ++ """Supplemental STT_* constants for EM_PARISC.""" ++ STT_HP_OPAQUE = 11 ++ STT_HP_STUB = 12 ++ STT_PARISC_MILLICODE = 13 ++ ++class SttSPARC(enum.Enum): ++ """Supplemental STT_* constants for EM_SPARC.""" ++ STT_SPARC_REGISTER = 13 ++ ++class SttX86_64(enum.Enum): ++ """Supplemental STT_* constants for EM_X86_64.""" ++ SHT_X86_64_UNWIND = 0x70000001 ++ ++class Pt(_OpenIntEnum): ++ """ELF program header types. Type of Phdr.p_type.""" ++ PT_NULL = 0 ++ PT_LOAD = 1 ++ PT_DYNAMIC = 2 ++ PT_INTERP = 3 ++ PT_NOTE = 4 ++ PT_SHLIB = 5 ++ PT_PHDR = 6 ++ PT_TLS = 7 ++ PT_NUM = 8 ++ PT_GNU_EH_FRAME = 0x6474e550 ++ PT_GNU_STACK = 0x6474e551 ++ PT_GNU_RELRO = 0x6474e552 ++ PT_GNU_PROPERTY = 0x6474e553 ++ PT_SUNWBSS = 0x6ffffffa ++ PT_SUNWSTACK = 0x6ffffffb ++ ++class PtARM(enum.Enum): ++ """Supplemental PT_* constants for EM_ARM.""" ++ PT_ARM_EXIDX = 0x70000001 ++ ++class PtIA_64(enum.Enum): ++ """Supplemental PT_* constants for EM_IA_64.""" ++ PT_IA_64_HP_OPT_ANOT = 0x60000012 ++ PT_IA_64_HP_HSL_ANOT = 0x60000013 ++ PT_IA_64_HP_STACK = 0x60000014 ++ PT_IA_64_ARCHEXT = 0x70000000 ++ PT_IA_64_UNWIND = 0x70000001 ++ ++class PtMIPS(enum.Enum): ++ """Supplemental PT_* constants for EM_MIPS.""" ++ PT_MIPS_REGINFO = 0x70000000 ++ PT_MIPS_RTPROC = 0x70000001 ++ PT_MIPS_OPTIONS = 0x70000002 ++ PT_MIPS_ABIFLAGS = 0x70000003 ++ ++class PtPARISC(enum.Enum): ++ """Supplemental PT_* constants for EM_PARISC.""" ++ PT_HP_TLS = 0x60000000 ++ PT_HP_CORE_NONE = 0x60000001 ++ PT_HP_CORE_VERSION = 0x60000002 ++ PT_HP_CORE_KERNEL = 0x60000003 ++ PT_HP_CORE_COMM = 0x60000004 ++ PT_HP_CORE_PROC = 0x60000005 ++ PT_HP_CORE_LOADABLE = 0x60000006 ++ PT_HP_CORE_STACK = 0x60000007 ++ PT_HP_CORE_SHM = 0x60000008 ++ PT_HP_CORE_MMF = 0x60000009 ++ PT_HP_PARALLEL = 0x60000010 ++ PT_HP_FASTBIND = 0x60000011 ++ PT_HP_OPT_ANNOT = 0x60000012 ++ PT_HP_HSL_ANNOT = 0x60000013 ++ PT_HP_STACK = 0x60000014 ++ PT_PARISC_ARCHEXT = 0x70000000 ++ PT_PARISC_UNWIND = 0x70000001 ++ ++class Dt(_OpenIntEnum): ++ """ELF dynamic segment tags. Type of Dyn.d_val.""" ++ DT_NULL = 0 ++ DT_NEEDED = 1 ++ DT_PLTRELSZ = 2 ++ DT_PLTGOT = 3 ++ DT_HASH = 4 ++ DT_STRTAB = 5 ++ DT_SYMTAB = 6 ++ DT_RELA = 7 ++ DT_RELASZ = 8 ++ DT_RELAENT = 9 ++ DT_STRSZ = 10 ++ DT_SYMENT = 11 ++ DT_INIT = 12 ++ DT_FINI = 13 ++ DT_SONAME = 14 ++ DT_RPATH = 15 ++ DT_SYMBOLIC = 16 ++ DT_REL = 17 ++ DT_RELSZ = 18 ++ DT_RELENT = 19 ++ DT_PLTREL = 20 ++ DT_DEBUG = 21 ++ DT_TEXTREL = 22 ++ DT_JMPREL = 23 ++ DT_BIND_NOW = 24 ++ DT_INIT_ARRAY = 25 ++ DT_FINI_ARRAY = 26 ++ DT_INIT_ARRAYSZ = 27 ++ DT_FINI_ARRAYSZ = 28 ++ DT_RUNPATH = 29 ++ DT_FLAGS = 30 ++ DT_PREINIT_ARRAY = 32 ++ DT_PREINIT_ARRAYSZ = 33 ++ DT_SYMTAB_SHNDX = 34 ++ DT_GNU_PRELINKED = 0x6ffffdf5 ++ DT_GNU_CONFLICTSZ = 0x6ffffdf6 ++ DT_GNU_LIBLISTSZ = 0x6ffffdf7 ++ DT_CHECKSUM = 0x6ffffdf8 ++ DT_PLTPADSZ = 0x6ffffdf9 ++ DT_MOVEENT = 0x6ffffdfa ++ DT_MOVESZ = 0x6ffffdfb ++ DT_FEATURE_1 = 0x6ffffdfc ++ DT_POSFLAG_1 = 0x6ffffdfd ++ DT_SYMINSZ = 0x6ffffdfe ++ DT_SYMINENT = 0x6ffffdff ++ DT_GNU_HASH = 0x6ffffef5 ++ DT_TLSDESC_PLT = 0x6ffffef6 ++ DT_TLSDESC_GOT = 0x6ffffef7 ++ DT_GNU_CONFLICT = 0x6ffffef8 ++ DT_GNU_LIBLIST = 0x6ffffef9 ++ DT_CONFIG = 0x6ffffefa ++ DT_DEPAUDIT = 0x6ffffefb ++ DT_AUDIT = 0x6ffffefc ++ DT_PLTPAD = 0x6ffffefd ++ DT_MOVETAB = 0x6ffffefe ++ DT_SYMINFO = 0x6ffffeff ++ DT_VERSYM = 0x6ffffff0 ++ DT_RELACOUNT = 0x6ffffff9 ++ DT_RELCOUNT = 0x6ffffffa ++ DT_FLAGS_1 = 0x6ffffffb ++ DT_VERDEF = 0x6ffffffc ++ DT_VERDEFNUM = 0x6ffffffd ++ DT_VERNEED = 0x6ffffffe ++ DT_VERNEEDNUM = 0x6fffffff ++ DT_AUXILIARY = 0x7ffffffd ++ DT_FILTER = 0x7fffffff ++ ++class DtAARCH64(enum.Enum): ++ """Supplemental DT_* constants for EM_AARCH64.""" ++ DT_AARCH64_BTI_PLT = 0x70000001 ++ DT_AARCH64_PAC_PLT = 0x70000003 ++ DT_AARCH64_VARIANT_PCS = 0x70000005 ++ ++class DtALPHA(enum.Enum): ++ """Supplemental DT_* constants for EM_ALPHA.""" ++ DT_ALPHA_PLTRO = 0x70000000 ++ ++class DtALTERA_NIOS2(enum.Enum): ++ """Supplemental DT_* constants for EM_ALTERA_NIOS2.""" ++ DT_NIOS2_GP = 0x70000002 ++ ++class DtIA_64(enum.Enum): ++ """Supplemental DT_* constants for EM_IA_64.""" ++ DT_IA_64_PLT_RESERVE = 0x70000000 ++ ++class DtMIPS(enum.Enum): ++ """Supplemental DT_* constants for EM_MIPS.""" ++ DT_MIPS_RLD_VERSION = 0x70000001 ++ DT_MIPS_TIME_STAMP = 0x70000002 ++ DT_MIPS_ICHECKSUM = 0x70000003 ++ DT_MIPS_IVERSION = 0x70000004 ++ DT_MIPS_FLAGS = 0x70000005 ++ DT_MIPS_BASE_ADDRESS = 0x70000006 ++ DT_MIPS_MSYM = 0x70000007 ++ DT_MIPS_CONFLICT = 0x70000008 ++ DT_MIPS_LIBLIST = 0x70000009 ++ DT_MIPS_LOCAL_GOTNO = 0x7000000a ++ DT_MIPS_CONFLICTNO = 0x7000000b ++ DT_MIPS_LIBLISTNO = 0x70000010 ++ DT_MIPS_SYMTABNO = 0x70000011 ++ DT_MIPS_UNREFEXTNO = 0x70000012 ++ DT_MIPS_GOTSYM = 0x70000013 ++ DT_MIPS_HIPAGENO = 0x70000014 ++ DT_MIPS_RLD_MAP = 0x70000016 ++ DT_MIPS_DELTA_CLASS = 0x70000017 ++ DT_MIPS_DELTA_CLASS_NO = 0x70000018 ++ DT_MIPS_DELTA_INSTANCE = 0x70000019 ++ DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a ++ DT_MIPS_DELTA_RELOC = 0x7000001b ++ DT_MIPS_DELTA_RELOC_NO = 0x7000001c ++ DT_MIPS_DELTA_SYM = 0x7000001d ++ DT_MIPS_DELTA_SYM_NO = 0x7000001e ++ DT_MIPS_DELTA_CLASSSYM = 0x70000020 ++ DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021 ++ DT_MIPS_CXX_FLAGS = 0x70000022 ++ DT_MIPS_PIXIE_INIT = 0x70000023 ++ DT_MIPS_SYMBOL_LIB = 0x70000024 ++ DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025 ++ DT_MIPS_LOCAL_GOTIDX = 0x70000026 ++ DT_MIPS_HIDDEN_GOTIDX = 0x70000027 ++ DT_MIPS_PROTECTED_GOTIDX = 0x70000028 ++ DT_MIPS_OPTIONS = 0x70000029 ++ DT_MIPS_INTERFACE = 0x7000002a ++ DT_MIPS_DYNSTR_ALIGN = 0x7000002b ++ DT_MIPS_INTERFACE_SIZE = 0x7000002c ++ DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d ++ DT_MIPS_PERF_SUFFIX = 0x7000002e ++ DT_MIPS_COMPACT_SIZE = 0x7000002f ++ DT_MIPS_GP_VALUE = 0x70000030 ++ DT_MIPS_AUX_DYNAMIC = 0x70000031 ++ DT_MIPS_PLTGOT = 0x70000032 ++ DT_MIPS_RWPLT = 0x70000034 ++ DT_MIPS_RLD_MAP_REL = 0x70000035 ++ DT_MIPS_XHASH = 0x70000036 ++ ++class DtPPC(enum.Enum): ++ """Supplemental DT_* constants for EM_PPC.""" ++ DT_PPC_GOT = 0x70000000 ++ DT_PPC_OPT = 0x70000001 ++ ++class DtPPC64(enum.Enum): ++ """Supplemental DT_* constants for EM_PPC64.""" ++ DT_PPC64_GLINK = 0x70000000 ++ DT_PPC64_OPD = 0x70000001 ++ DT_PPC64_OPDSZ = 0x70000002 ++ DT_PPC64_OPT = 0x70000003 ++ ++class DtSPARC(enum.Enum): ++ """Supplemental DT_* constants for EM_SPARC.""" ++ DT_SPARC_REGISTER = 0x70000001 ++ ++class StInfo: ++ """ELF symbol binding and type. Type of the Sym.st_info field.""" ++ def __init__(self, arg0, arg1=None): ++ if isinstance(arg0, int) and arg1 is None: ++ self.bind = Stb(arg0 >> 4) ++ self.type = Stt(arg0 & 15) ++ else: ++ self.bind = Stb(arg0) ++ self.type = Stt(arg1) ++ ++ def value(self): ++ """Returns the raw value for the bind/type combination.""" ++ return (self.bind.value() << 4) | (self.type.value()) ++ ++# Type in an ELF file. Used for deserialization. ++_Layout = collections.namedtuple('_Layout', 'unpack size') ++ ++def _define_layouts(baseclass: type, layout32: str, layout64: str, ++ types=None, fields32=None): ++ """Assign variants dict to baseclass. ++ ++ The variants dict is indexed by (ElfClass, ElfData) pairs, and its ++ values are _Layout instances. ++ ++ """ ++ struct32 = struct.Struct(layout32) ++ struct64 = struct.Struct(layout64) ++ ++ # Check that the struct formats yield the right number of components. ++ for s in (struct32, struct64): ++ example = s.unpack(b' ' * s.size) ++ if len(example) != len(baseclass._fields): ++ raise ValueError('{!r} yields wrong field count: {} != {}'.format( ++ s.format, len(example), len(baseclass._fields))) ++ ++ # Check that field names in types are correct. ++ if types is None: ++ types = () ++ for n in types: ++ if n not in baseclass._fields: ++ raise ValueError('{} does not have field {!r}'.format( ++ baseclass.__name__, n)) ++ ++ if fields32 is not None \ ++ and set(fields32) != set(baseclass._fields): ++ raise ValueError('{!r} is not a permutation of the fields {!r}'.format( ++ fields32, baseclass._fields)) ++ ++ def unique_name(name, used_names = (set((baseclass.__name__,)) ++ | set(baseclass._fields) ++ | {n.__name__ ++ for n in (types or {}).values()})): ++ """Find a name that is not used for a class or field name.""" ++ candidate = name ++ n = 0 ++ while candidate in used_names: ++ n += 1 ++ candidate = '{}{}'.format(name, n) ++ used_names.add(candidate) ++ return candidate ++ ++ blob_name = unique_name('blob') ++ struct_unpack_name = unique_name('struct_unpack') ++ comps_name = unique_name('comps') ++ ++ layouts = {} ++ for (bits, elfclass, layout, fields) in ( ++ (32, ElfClass.ELFCLASS32, layout32, fields32), ++ (64, ElfClass.ELFCLASS64, layout64, None), ++ ): ++ for (elfdata, structprefix, funcsuffix) in ( ++ (ElfData.ELFDATA2LSB, '<', 'LE'), ++ (ElfData.ELFDATA2MSB, '>', 'BE'), ++ ): ++ env = { ++ baseclass.__name__: baseclass, ++ struct_unpack_name: struct.unpack, ++ } ++ ++ # Add the type converters. ++ if types: ++ for cls in types.values(): ++ env[cls.__name__] = cls ++ ++ funcname = ''.join( ++ ('unpack_', baseclass.__name__, str(bits), funcsuffix)) ++ ++ code = ''' ++def {funcname}({blob_name}): ++'''.format(funcname=funcname, blob_name=blob_name) ++ ++ indent = ' ' * 4 ++ unpack_call = '{}({!r}, {})'.format( ++ struct_unpack_name, structprefix + layout, blob_name) ++ field_names = ', '.join(baseclass._fields) ++ if types is None and fields is None: ++ code += '{}return {}({})\n'.format( ++ indent, baseclass.__name__, unpack_call) ++ else: ++ # Destructuring tuple assignment. ++ if fields is None: ++ code += '{}{} = {}\n'.format( ++ indent, field_names, unpack_call) ++ else: ++ # Use custom field order. ++ code += '{}{} = {}\n'.format( ++ indent, ', '.join(fields), unpack_call) ++ ++ # Perform the type conversions. ++ for n in baseclass._fields: ++ if n in types: ++ code += '{}{} = {}({})\n'.format( ++ indent, n, types[n].__name__, n) ++ # Create the named tuple. ++ code += '{}return {}({})\n'.format( ++ indent, baseclass.__name__, field_names) ++ ++ exec(code, env) ++ layouts[(elfclass, elfdata)] = _Layout( ++ env[funcname], struct.calcsize(layout)) ++ baseclass.layouts = layouts ++ ++ ++# Corresponds to EI_* indices into Elf*_Ehdr.e_indent. ++class Ident(collections.namedtuple('Ident', ++ 'ei_mag ei_class ei_data ei_version ei_osabi ei_abiversion ei_pad')): ++ ++ def __new__(cls, *args): ++ """Construct an object from a blob or its constituent fields.""" ++ if len(args) == 1: ++ return cls.unpack(args[0]) ++ return cls.__base__.__new__(cls, *args) ++ ++ @staticmethod ++ def unpack(blob: memoryview) -> 'Ident': ++ """Parse raws data into a tuple.""" ++ ei_mag, ei_class, ei_data, ei_version, ei_osabi, ei_abiversion, \ ++ ei_pad = struct.unpack('4s5B7s', blob) ++ return Ident(ei_mag, ElfClass(ei_class), ElfData(ei_data), ++ ei_version, ei_osabi, ei_abiversion, ei_pad) ++ size = 16 ++ ++# Corresponds to Elf32_Ehdr and Elf64_Ehdr. ++Ehdr = collections.namedtuple('Ehdr', ++ 'e_ident e_type e_machine e_version e_entry e_phoff e_shoff e_flags' ++ + ' e_ehsize e_phentsize e_phnum e_shentsize e_shnum e_shstrndx') ++_define_layouts(Ehdr, ++ layout32='16s2H5I6H', ++ layout64='16s2HI3QI6H', ++ types=dict(e_ident=Ident, ++ e_machine=Machine, ++ e_type=Et, ++ e_shstrndx=Shn)) ++ ++# Corresponds to Elf32_Phdr and Elf64_Pdhr. Order follows the latter. ++Phdr = collections.namedtuple('Phdr', ++ 'p_type p_flags p_offset p_vaddr p_paddr p_filesz p_memsz p_align') ++_define_layouts(Phdr, ++ layout32='8I', ++ fields32=('p_type', 'p_offset', 'p_vaddr', 'p_paddr', ++ 'p_filesz', 'p_memsz', 'p_flags', 'p_align'), ++ layout64='2I6Q', ++ types=dict(p_type=Pt, p_flags=Pf)) ++ ++ ++# Corresponds to Elf32_Shdr and Elf64_Shdr. ++class Shdr(collections.namedtuple('Shdr', ++ 'sh_name sh_type sh_flags sh_addr sh_offset sh_size sh_link sh_info' ++ + ' sh_addralign sh_entsize')): ++ def resolve(self, strtab: 'StringTable') -> 'Shdr': ++ """Resolve sh_name using a string table.""" ++ return self.__class__(strtab.get(self[0]), *self[1:]) ++_define_layouts(Shdr, ++ layout32='10I', ++ layout64='2I4Q2I2Q', ++ types=dict(sh_type=Sht, ++ sh_flags=Shf, ++ sh_link=Shn)) ++ ++# Corresponds to Elf32_Dyn and Elf64_Dyn. The nesting through the ++# d_un union is skipped, and d_ptr is missing (its representation in ++# Python would be identical to d_val). ++Dyn = collections.namedtuple('Dyn', 'd_tag d_val') ++_define_layouts(Dyn, ++ layout32='2i', ++ layout64='2q', ++ types=dict(d_tag=Dt)) ++ ++# Corresponds to Elf32_Sym and Elf64_Sym. ++class Sym(collections.namedtuple('Sym', ++ 'st_name st_info st_other st_shndx st_value st_size')): ++ def resolve(self, strtab: 'StringTable') -> 'Sym': ++ """Resolve st_name using a string table.""" ++ return self.__class__(strtab.get(self[0]), *self[1:]) ++_define_layouts(Sym, ++ layout32='3I2BH', ++ layout64='I2BH2Q', ++ fields32=('st_name', 'st_value', 'st_size', 'st_info', ++ 'st_other', 'st_shndx'), ++ types=dict(st_shndx=Shn, ++ st_info=StInfo)) ++ ++# Corresponds to Elf32_Rel and Elf64_Rel. ++Rel = collections.namedtuple('Rel', 'r_offset r_info') ++_define_layouts(Rel, ++ layout32='2I', ++ layout64='2Q') ++ ++# Corresponds to Elf32_Rel and Elf64_Rel. ++Rela = collections.namedtuple('Rela', 'r_offset r_info r_addend') ++_define_layouts(Rela, ++ layout32='3I', ++ layout64='3Q') ++ ++class StringTable: ++ """ELF string table.""" ++ def __init__(self, blob): ++ """Create a new string table backed by the data in the blob. ++ ++ blob: a memoryview-like object ++ ++ """ ++ self.blob = blob ++ ++ def get(self, index) -> bytes: ++ """Returns the null-terminated byte string at the index.""" ++ blob = self.blob ++ endindex = index ++ while True: ++ if blob[endindex] == 0: ++ return bytes(blob[index:endindex]) ++ endindex += 1 ++ ++class Image: ++ """ELF image parser.""" ++ def __init__(self, image): ++ """Create an ELF image from binary image data. ++ ++ image: a memoryview-like object that supports efficient range ++ subscripting. ++ ++ """ ++ self.image = image ++ ident = self.read(Ident, 0) ++ classdata = (ident.ei_class, ident.ei_data) ++ # Set self.Ehdr etc. to the subtypes with the right parsers. ++ for typ in (Ehdr, Phdr, Shdr, Dyn, Sym, Rel, Rela): ++ setattr(self, typ.__name__, typ.layouts.get(classdata, None)) ++ ++ if self.Ehdr is not None: ++ self.ehdr = self.read(self.Ehdr, 0) ++ self._shdr_num = self._compute_shdr_num() ++ else: ++ self.ehdr = None ++ self._shdr_num = 0 ++ ++ self._section = {} ++ self._stringtab = {} ++ ++ if self._shdr_num > 0: ++ self._shdr_strtab = self._find_shdr_strtab() ++ else: ++ self._shdr_strtab = None ++ ++ @staticmethod ++ def readfile(path: str) -> 'Image': ++ """Reads the ELF file at the specified path.""" ++ with open(path, 'rb') as inp: ++ return Image(memoryview(inp.read())) ++ ++ def _compute_shdr_num(self) -> int: ++ """Computes the actual number of section headers.""" ++ shnum = self.ehdr.e_shnum ++ if shnum == 0: ++ if self.ehdr.e_shoff == 0 or self.ehdr.e_shentsize == 0: ++ # No section headers. ++ return 0 ++ # Otherwise the extension mechanism is used (which may be ++ # needed because e_shnum is just 16 bits). ++ return self.read(self.Shdr, self.ehdr.e_shoff).sh_size ++ return shnum ++ ++ def _find_shdr_strtab(self) -> StringTable: ++ """Finds the section header string table (maybe via extensions).""" ++ shstrndx = self.ehdr.e_shstrndx ++ if shstrndx == Shn.SHN_XINDEX: ++ shstrndx = self.read(self.Shdr, self.ehdr.e_shoff).sh_link ++ return self._find_stringtab(shstrndx) ++ ++ def read(self, typ: type, offset:int ): ++ """Reads an object at a specific offset. ++ ++ The type must have been enhanced using _define_variants. ++ ++ """ ++ return typ.unpack(self.image[offset: offset + typ.size]) ++ ++ def phdrs(self) -> Phdr: ++ """Generator iterating over the program headers.""" ++ if self.ehdr is None: ++ return ++ size = self.ehdr.e_phentsize ++ if size != self.Phdr.size: ++ raise ValueError('Unexpected Phdr size in ELF header: {} != {}' ++ .format(size, self.Phdr.size)) ++ ++ offset = self.ehdr.e_phoff ++ for _ in range(self.ehdr.e_phnum): ++ yield self.read(self.Phdr, offset) ++ offset += size ++ ++ def shdrs(self, resolve: bool=True) -> Shdr: ++ """Generator iterating over the section headers. ++ ++ If resolve, section names are automatically translated ++ using the section header string table. ++ ++ """ ++ if self._shdr_num == 0: ++ return ++ ++ size = self.ehdr.e_shentsize ++ if size != self.Shdr.size: ++ raise ValueError('Unexpected Shdr size in ELF header: {} != {}' ++ .format(size, self.Shdr.size)) ++ ++ offset = self.ehdr.e_shoff ++ for _ in range(self._shdr_num): ++ shdr = self.read(self.Shdr, offset) ++ if resolve: ++ shdr = shdr.resolve(self._shdr_strtab) ++ yield shdr ++ offset += size ++ ++ def dynamic(self) -> Dyn: ++ """Generator iterating over the dynamic segment.""" ++ for phdr in self.phdrs(): ++ if phdr.p_type == Pt.PT_DYNAMIC: ++ # Pick the first dynamic segment, like the loader. ++ if phdr.p_filesz == 0: ++ # Probably separated debuginfo. ++ return ++ offset = phdr.p_offset ++ end = offset + phdr.p_memsz ++ size = self.Dyn.size ++ while True: ++ next_offset = offset + size ++ if next_offset > end: ++ raise ValueError( ++ 'Dynamic segment size {} is not a multiple of Dyn size {}'.format( ++ phdr.p_memsz, size)) ++ yield self.read(self.Dyn, offset) ++ if next_offset == end: ++ return ++ offset = next_offset ++ ++ def syms(self, shdr: Shdr, resolve: bool=True) -> Sym: ++ """A generator iterating over a symbol table. ++ ++ If resolve, symbol names are automatically translated using ++ the string table for the symbol table. ++ ++ """ ++ assert shdr.sh_type == Sht.SHT_SYMTAB ++ size = shdr.sh_entsize ++ if size != self.Sym.size: ++ raise ValueError('Invalid symbol table entry size {}'.format(size)) ++ offset = shdr.sh_offset ++ end = shdr.sh_offset + shdr.sh_size ++ if resolve: ++ strtab = self._find_stringtab(shdr.sh_link) ++ while offset < end: ++ sym = self.read(self.Sym, offset) ++ if resolve: ++ sym = sym.resolve(strtab) ++ yield sym ++ offset += size ++ if offset != end: ++ raise ValueError('Symbol table is not a multiple of entry size') ++ ++ def lookup_string(self, strtab_index: int, strtab_offset: int) -> bytes: ++ """Looks up a string in a string table identified by its link index.""" ++ try: ++ strtab = self._stringtab[strtab_index] ++ except KeyError: ++ strtab = self._find_stringtab(strtab_index) ++ return strtab.get(strtab_offset) ++ ++ def find_section(self, shndx: Shn) -> Shdr: ++ """Returns the section header for the indexed section. ++ ++ The section name is not resolved. ++ """ ++ try: ++ return self._section[shndx] ++ except KeyError: ++ pass ++ if shndx in Shn: ++ raise ValueError('Reserved section index {}'.format(shndx)) ++ idx = shndx.value ++ if idx < 0 or idx > self._shdr_num: ++ raise ValueError('Section index {} out of range [0, {})'.format( ++ idx, self._shdr_num)) ++ shdr = self.read( ++ self.Shdr, self.ehdr.e_shoff + idx * self.Shdr.size) ++ self._section[shndx] = shdr ++ return shdr ++ ++ def _find_stringtab(self, sh_link: int) -> StringTable: ++ if sh_link in self._stringtab: ++ return self._stringtab ++ if sh_link < 0 or sh_link >= self._shdr_num: ++ raise ValueError('Section index {} out of range [0, {})'.format( ++ sh_link, self._shdr_num)) ++ shdr = self.read( ++ self.Shdr, self.ehdr.e_shoff + sh_link * self.Shdr.size) ++ if shdr.sh_type != Sht.SHT_STRTAB: ++ raise ValueError( ++ 'Section {} is not a string table: {}'.format( ++ sh_link, shdr.sh_type)) ++ strtab = StringTable( ++ self.image[shdr.sh_offset:shdr.sh_offset + shdr.sh_size]) ++ # This could retrain essentially arbitrary amounts of data, ++ # but caching string tables seems important for performance. ++ self._stringtab[sh_link] = strtab ++ return strtab ++ ++ ++__all__ = [name for name in dir() if name[0].isupper()] +diff -Nuar a/scripts/tst-elf-edit.py b/scripts/tst-elf-edit.py +--- a/scripts/tst-elf-edit.py 2022-02-03 08:27:54.000000000 +0300 ++++ b/scripts/tst-elf-edit.py 2025-10-20 21:02:21.000000000 +0300 +@@ -43,9 +43,11 @@ + ELFDATA2LSB=b'\x01' + ELFDATA2MSB=b'\x02' + ++ET_EXEC=2 + ET_DYN=3 + + PT_LOAD=1 ++PT_TLS=7 + + def elf_types_fmts(e_ident): + endian = '<' if e_ident[EI_DATA] == ELFDATA2LSB else '>' +@@ -146,8 +148,15 @@ + else: + phdr.p_align = int(align) + ++def elf_edit_maximize_tls_size(phdr, elfclass): ++ if elfclass == ELFCLASS32: ++ # It is possible that the kernel can allocate half of the ++ # address space, so use something larger. ++ phdr.p_memsz = 0xfff00000 ++ else: ++ phdr.p_memsz = 1 << 63 + +-def elf_edit(f, align): ++def elf_edit(f, opts): + ei_nident_fmt = 'c' * EI_NIDENT + ei_nident_len = struct.calcsize(ei_nident_fmt) + +@@ -172,24 +181,35 @@ + + ehdr = Elf_Ehdr(e_ident) + ehdr.read(f) +- if ehdr.e_type != ET_DYN: +- error('{}: not a shared library'.format(f.name)) ++ if ehdr.e_type not in (ET_EXEC, ET_DYN): ++ error('{}: not an executable or shared library'.format(f.name)) + + phdr = Elf_Phdr(e_ident) ++ maximize_tls_size_done = False + for i in range(0, ehdr.e_phnum): + f.seek(ehdr.e_phoff + i * phdr.len) + phdr.read(f) +- if phdr.p_type == PT_LOAD: +- elf_edit_align(phdr, align) ++ if phdr.p_type == PT_LOAD and opts.align is not None: ++ elf_edit_align(phdr, opts.align) ++ f.seek(ehdr.e_phoff + i * phdr.len) ++ phdr.write(f) ++ break ++ if phdr.p_type == PT_TLS and opts.maximize_tls_size: ++ elf_edit_maximize_tls_size(phdr, e_ident[EI_CLASS]) + f.seek(ehdr.e_phoff + i * phdr.len) + phdr.write(f) ++ maximize_tls_size_done = True + break + ++ if opts.maximize_tls_size and not maximize_tls_size_done: ++ error('{}: TLS maximum size was not updated'.format(f.name)) + + def get_parser(): + parser = argparse.ArgumentParser(description=__doc__) +- parser.add_argument('-a', dest='align', required=True, ++ parser.add_argument('-a', dest='align', + help='How to set the LOAD alignment') ++ parser.add_argument('--maximize-tls-size', action='store_true', ++ help='Set maximum PT_TLS size') + parser.add_argument('output', + help='ELF file to edit') + return parser +@@ -199,7 +219,7 @@ + parser = get_parser() + opts = parser.parse_args(argv) + with open(opts.output, 'r+b') as fout: +- elf_edit(fout, opts.align) ++ elf_edit(fout, opts) + + + if __name__ == '__main__': +diff -Nuar a/scripts/tst-ld-trace.py b/scripts/tst-ld-trace.py +--- a/scripts/tst-ld-trace.py 1970-01-01 02:00:00.000000000 +0200 ++++ b/scripts/tst-ld-trace.py 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,108 @@ ++#!/usr/bin/python3 ++# Dump the output of LD_TRACE_LOADED_OBJECTS in architecture neutral format. ++# Copyright (C) 2022 Free Software Foundation, Inc. ++# Copyright The GNU Toolchain Authors. ++# 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 ++# . ++ ++import argparse ++import os ++import subprocess ++import sys ++ ++try: ++ subprocess.run ++except: ++ class _CompletedProcess: ++ def __init__(self, args, returncode, stdout=None, stderr=None): ++ self.args = args ++ self.returncode = returncode ++ self.stdout = stdout ++ self.stderr = stderr ++ ++ def _run(*popenargs, input=None, timeout=None, check=False, **kwargs): ++ assert(timeout is None) ++ with subprocess.Popen(*popenargs, **kwargs) as process: ++ try: ++ stdout, stderr = process.communicate(input) ++ except: ++ process.kill() ++ process.wait() ++ raise ++ returncode = process.poll() ++ if check and returncode: ++ raise subprocess.CalledProcessError(returncode, popenargs) ++ return _CompletedProcess(popenargs, returncode, stdout, stderr) ++ ++ subprocess.run = _run ++ ++def is_vdso(lib): ++ return lib.startswith('linux-gate') or lib.startswith('linux-vdso') ++ ++ ++def parse_trace(cmd, fref): ++ new_env = os.environ.copy() ++ new_env['LD_TRACE_LOADED_OBJECTS'] = '1' ++ trace_out = subprocess.run(cmd, stdout=subprocess.PIPE, check=True, ++ universal_newlines=True, env=new_env).stdout ++ trace = [] ++ for line in trace_out.splitlines(): ++ line = line.strip() ++ if is_vdso(line): ++ continue ++ fields = line.split('=>' if '=>' in line else ' ') ++ lib = os.path.basename(fields[0].strip()) ++ if lib.startswith('ld'): ++ lib = 'ld' ++ elif lib.startswith('libc'): ++ lib = 'libc' ++ found = 1 if fields[1].strip() != 'not found' else 0 ++ trace += ['{} {}'.format(lib, found)] ++ trace = sorted(trace) ++ ++ reference = sorted(line.replace('\n','') for line in fref.readlines()) ++ ++ ret = 0 if trace == reference else 1 ++ if ret != 0: ++ for i in reference: ++ if i not in trace: ++ print("Only in {}: {}".format(fref.name, i)) ++ for i in trace: ++ if i not in reference: ++ print("Only in trace: {}".format(i)) ++ ++ sys.exit(ret) ++ ++ ++def get_parser(): ++ parser = argparse.ArgumentParser(description=__doc__) ++ parser.add_argument('command', ++ help='comand to run') ++ parser.add_argument('reference', ++ help='reference file to compare') ++ return parser ++ ++ ++def main(argv): ++ parser = get_parser() ++ opts = parser.parse_args(argv) ++ with open(opts.reference, 'r') as fref: ++ # Remove the initial 'env' command. ++ parse_trace(opts.command.split()[1:], fref) ++ ++ ++if __name__ == '__main__': ++ main(sys.argv[1:]) +diff -Nuar a/shlib-versions b/shlib-versions +--- a/shlib-versions 2022-02-03 08:27:54.000000000 +0300 ++++ b/shlib-versions 2025-10-20 21:02:21.000000000 +0300 +@@ -47,11 +47,6 @@ + libnss_hesiod=2 + libnss_db=2 + +-# Tests for NSS. They must have the same NSS_SHLIB_REVISION number as +-# the rest. +-libnss_test1=2 +-libnss_test2=2 +- + # Version for libnsl with YP and NIS+ functions. + libnsl=1 + +diff -Nuar a/socket/bits/socket2.h b/socket/bits/socket2.h +--- a/socket/bits/socket2.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/socket/bits/socket2.h 2025-10-20 21:02:21.000000000 +0300 +@@ -33,12 +33,12 @@ + __fortify_function ssize_t + recv (int __fd, void *__buf, size_t __n, int __flags) + { +- size_t sz = __glibc_objsize0 (__buf); +- if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz)) ++ size_t __sz = __glibc_objsize0 (__buf); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (char), __sz)) + return __recv_alias (__fd, __buf, __n, __flags); +- if (__glibc_unsafe_len (__n, sizeof (char), sz)) +- return __recv_chk_warn (__fd, __buf, __n, sz, __flags); +- return __recv_chk (__fd, __buf, __n, sz, __flags); ++ if (__glibc_unsafe_len (__n, sizeof (char), __sz)) ++ return __recv_chk_warn (__fd, __buf, __n, __sz, __flags); ++ return __recv_chk (__fd, __buf, __n, __sz, __flags); + } + + extern ssize_t __recvfrom_chk (int __fd, void *__restrict __buf, size_t __n, +@@ -61,11 +61,11 @@ + recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags, + __SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len) + { +- size_t sz = __glibc_objsize0 (__buf); +- if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz)) ++ size_t __sz = __glibc_objsize0 (__buf); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (char), __sz)) + return __recvfrom_alias (__fd, __buf, __n, __flags, __addr, __addr_len); +- if (__glibc_unsafe_len (__n, sizeof (char), sz)) +- return __recvfrom_chk_warn (__fd, __buf, __n, sz, __flags, __addr, ++ if (__glibc_unsafe_len (__n, sizeof (char), __sz)) ++ return __recvfrom_chk_warn (__fd, __buf, __n, __sz, __flags, __addr, + __addr_len); +- return __recvfrom_chk (__fd, __buf, __n, sz, __flags, __addr, __addr_len); ++ return __recvfrom_chk (__fd, __buf, __n, __sz, __flags, __addr, __addr_len); + } +diff -Nuar a/socket/Makefile b/socket/Makefile +--- a/socket/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/socket/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -34,6 +34,7 @@ + tests := \ + tst-accept4 \ + tst-sockopt \ ++ tst-cmsghdr \ + # tests + + tests-internal := \ +diff -Nuar a/socket/sys/socket.h b/socket/sys/socket.h +--- a/socket/sys/socket.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/socket/sys/socket.h 2025-10-20 21:02:21.000000000 +0300 +@@ -181,7 +181,7 @@ + # else + extern ssize_t __sendmsg64 (int __fd, const struct msghdr *__message, + int __flags); +-# defien sendmsg __sendmsg64 ++# define sendmsg __sendmsg64 + # endif + #endif + +diff -Nuar a/socket/tst-cmsghdr.c b/socket/tst-cmsghdr.c +--- a/socket/tst-cmsghdr.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/socket/tst-cmsghdr.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,56 @@ ++/* Test ancillary data header creation. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++#define PAYLOAD "Hello, World!" ++ ++/* CMSG_NXTHDR is a macro that calls an inline function defined in ++ bits/socket.h. In case the function cannot be inlined, libc.so carries ++ a copy. Both versions need to be tested. */ ++ ++#define CMSG_NXTHDR_IMPL CMSG_NXTHDR ++#include "tst-cmsghdr-skeleton.c" ++#undef CMSG_NXTHDR_IMPL ++ ++static struct cmsghdr * (* cmsg_nxthdr) (struct msghdr *, struct cmsghdr *); ++ ++#define CMSG_NXTHDR_IMPL cmsg_nxthdr ++#include "tst-cmsghdr-skeleton.c" ++#undef CMSG_NXTHDR_IMPL ++ ++static int ++do_test (void) ++{ ++ static void *handle; ++ ++ run_test_CMSG_NXTHDR (); ++ ++ handle = xdlopen (LIBC_SO, RTLD_LAZY); ++ cmsg_nxthdr = (struct cmsghdr * (*) (struct msghdr *, struct cmsghdr *)) ++ xdlsym (handle, "__cmsg_nxthdr"); ++ ++ run_test_cmsg_nxthdr (); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/socket/tst-cmsghdr-skeleton.c b/socket/tst-cmsghdr-skeleton.c +--- a/socket/tst-cmsghdr-skeleton.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/socket/tst-cmsghdr-skeleton.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,92 @@ ++/* Test ancillary data header creation. ++ Copyright (C) 2022 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 ++ . */ ++ ++/* We use the preprocessor to generate the function/macro tests instead of ++ using indirection because having all the macro expansions alongside ++ each other lets the compiler warn us about suspicious pointer ++ arithmetic across subsequent CMSG_{FIRST,NXT}HDR expansions. */ ++ ++#include ++ ++#define RUN_TEST_CONCAT(suffix) run_test_##suffix ++#define RUN_TEST_FUNCNAME(suffix) RUN_TEST_CONCAT (suffix) ++ ++static void ++RUN_TEST_FUNCNAME (CMSG_NXTHDR_IMPL) (void) ++{ ++ struct msghdr m = {0}; ++ struct cmsghdr *cmsg; ++ char cmsgbuf[3 * CMSG_SPACE (sizeof (PAYLOAD))] = {0}; ++ ++ m.msg_control = cmsgbuf; ++ m.msg_controllen = sizeof (cmsgbuf); ++ ++ /* First header should point to the start of the buffer. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ ++ /* If the first header length consumes the entire buffer, there is no ++ space remaining for additional headers. */ ++ cmsg->cmsg_len = sizeof (cmsgbuf); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ /* The first header length is so big, using it would cause an overflow. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len = SIZE_MAX; ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ /* The first header leaves just enough space to hold another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len = sizeof (cmsgbuf) - sizeof (struct cmsghdr); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ ++ /* The first header leaves space but not enough for another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len ++; ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ /* The second header leaves just enough space to hold another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg->cmsg_len = CMSG_LEN (sizeof (PAYLOAD)); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ cmsg->cmsg_len = sizeof (cmsgbuf) ++ - CMSG_SPACE (sizeof (PAYLOAD)) /* First header. */ ++ - sizeof (struct cmsghdr); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ ++ /* The second header leaves space but not enough for another header. */ ++ cmsg = CMSG_FIRSTHDR (&m); ++ TEST_VERIFY_EXIT ((char *) cmsg == cmsgbuf); ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg != NULL); ++ cmsg->cmsg_len ++; ++ cmsg = CMSG_NXTHDR_IMPL (&m, cmsg); ++ TEST_VERIFY_EXIT (cmsg == NULL); ++ ++ return; ++} +diff -Nuar a/stdio-common/Makefile b/stdio-common/Makefile +--- a/stdio-common/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdio-common/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -22,85 +22,249 @@ + + include ../Makeconfig + +-headers := stdio_ext.h printf.h bits/printf-ldbl.h bits/stdio_lim.h +- +-routines := \ +- ctermid cuserid \ +- _itoa _itowa itoa-digits itoa-udigits itowa-digits \ +- vfprintf vprintf printf_fp reg-printf printf-prs printf_fphex \ +- reg-modifier reg-type \ +- printf_size fprintf printf snprintf sprintf asprintf dprintf \ +- vfwprintf vfscanf vfwscanf \ +- fscanf scanf sscanf \ +- perror psignal \ +- tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname \ +- getline getw putw \ +- remove rename renameat renameat2 \ +- flockfile ftrylockfile funlockfile \ +- isoc99_scanf isoc99_vscanf isoc99_fscanf isoc99_vfscanf isoc99_sscanf \ +- isoc99_vsscanf \ +- psiginfo gentempfd \ +- vfscanf-internal vfwscanf-internal iovfscanf \ +- vfprintf-internal vfwprintf-internal +- +-aux := errlist siglist printf-parsemb printf-parsewc fxprintf +- +-tests := tstscanf test_rdwr test-popen tstgetln test-fseek \ +- temptest tst-fileno test-fwrite tst-ungetc tst-ferror \ +- xbug errnobug \ +- bug1 bug2 bug3 bug4 bug5 bug6 bug7 bug8 bug9 bug10 bug11 bug12 bug13 \ +- tfformat tiformat tllformat tstdiomisc tst-printfsz tst-wc-printf \ +- scanf1 scanf2 scanf3 scanf4 scanf5 scanf7 scanf8 scanf9 scanf10 \ +- scanf11 scanf12 tst-tmpnam tst-cookie tst-obprintf tst-sscanf \ +- tst-swprintf tst-fseek tst-fmemopen test-vfprintf tst-gets \ +- tst-perror tst-sprintf tst-rndseek tst-fdopen tst-fphex bug14 \ +- tst-popen tst-unlockedio tst-fmemopen2 tst-put-error tst-fgets \ +- tst-fwrite bug16 bug17 tst-swscanf tst-sprintf2 bug18 bug18a \ +- bug19 bug19a tst-popen2 scanf13 scanf14 scanf15 bug20 bug21 bug22 \ +- scanf16 scanf17 tst-setvbuf1 tst-grouping bug23 bug24 \ +- bug-vfprintf-nargs tst-long-dbl-fphex tst-fphex-wide tst-sprintf3 \ +- bug25 tst-printf-round bug23-2 bug23-3 bug23-4 bug26 tst-fmemopen3 \ +- tst-printf-bz18872 tst-vfprintf-width-prec tst-fmemopen4 \ +- tst-vfprintf-user-type \ +- tst-vfprintf-mbs-prec \ +- tst-scanf-round \ +- tst-renameat2 tst-bz11319 tst-bz11319-fortify2 \ +- scanf14a scanf16a \ +- tst-printf-bz25691 \ +- tst-vfprintf-width-prec-alloc \ +- tst-printf-fp-free \ +- tst-printf-fp-leak \ +- test-strerr \ +- tst-printf-binary \ +- tst-sprintf-errno \ +- # tests +- +- +-test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble ++headers := \ ++ bits/printf-ldbl.h \ ++ bits/stdio_lim.h \ ++ printf.h \ ++ stdio_ext.h \ ++ # headers ++ ++routines := \ ++ _itoa \ ++ _itowa \ ++ asprintf \ ++ ctermid \ ++ cuserid \ ++ dprintf \ ++ flockfile \ ++ fprintf \ ++ fscanf \ ++ ftrylockfile \ ++ funlockfile \ ++ gentempfd \ ++ getline \ ++ getw \ ++ iovfscanf \ ++ isoc99_fscanf \ ++ isoc99_scanf \ ++ isoc99_sscanf \ ++ isoc99_vfscanf \ ++ isoc99_vscanf \ ++ isoc99_vsscanf \ ++ itoa-digits \ ++ itoa-udigits \ ++ itowa-digits \ ++ perror \ ++ printf \ ++ printf-prs \ ++ printf_fp \ ++ printf_fphex \ ++ printf_size \ ++ psiginfo \ ++ psignal \ ++ putw \ ++ reg-modifier \ ++ reg-printf \ ++ reg-type \ ++ remove \ ++ rename \ ++ renameat \ ++ renameat2 \ ++ scanf \ ++ snprintf \ ++ sprintf \ ++ sscanf \ ++ tempnam \ ++ tempname \ ++ tmpfile \ ++ tmpfile64 tmpnam \ ++ tmpnam_r \ ++ vfprintf \ ++ vfprintf-internal \ ++ vfscanf \ ++ vfscanf-internal \ ++ vfwprintf \ ++ vfwprintf-internal \ ++ vfwscanf \ ++ vfwscanf-internal \ ++ vprintf \ ++ # routines ++ ++aux := \ ++ errlist \ ++ fxprintf \ ++ printf-parsemb \ ++ printf-parsewc \ ++ siglist \ ++ # aux ++ ++tests := \ ++ bug-vfprintf-nargs \ ++ bug1 \ ++ bug10 \ ++ bug11 \ ++ bug12 \ ++ bug13 \ ++ bug14 \ ++ bug16 \ ++ bug17 \ ++ bug18 \ ++ bug18a \ ++ bug19 \ ++ bug19a \ ++ bug2 \ ++ bug20 \ ++ bug21 \ ++ bug22 \ ++ bug23 \ ++ bug23-2 \ ++ bug23-3 \ ++ bug23-4 \ ++ bug24 \ ++ bug25 \ ++ bug26 \ ++ bug3 \ ++ bug4 \ ++ bug5 \ ++ bug6 \ ++ bug7 \ ++ bug8 \ ++ bug9 \ ++ errnobug \ ++ scanf1 \ ++ scanf10 \ ++ scanf11 \ ++ scanf12 \ ++ scanf13 \ ++ scanf14 \ ++ scanf14a \ ++ scanf15 \ ++ scanf16 \ ++ scanf16a \ ++ scanf17 \ ++ scanf2 \ ++ scanf3 \ ++ scanf4 \ ++ scanf5 \ ++ scanf7 \ ++ scanf8 \ ++ scanf9 \ ++ temptest \ ++ test-fseek \ ++ test-fwrite \ ++ test-popen \ ++ test-strerr \ ++ test-vfprintf \ ++ test_rdwr \ ++ tfformat \ ++ tiformat \ ++ tllformat \ ++ tst-bz11319 \ ++ tst-bz11319-fortify2 \ ++ tst-cookie \ ++ tst-fdopen \ ++ tst-ferror \ ++ tst-fgets \ ++ tst-fileno \ ++ tst-fmemopen \ ++ tst-fmemopen2 \ ++ tst-fmemopen3 \ ++ tst-fmemopen4 \ ++ tst-fphex \ ++ tst-fphex-wide \ ++ tst-fseek \ ++ tst-fwrite \ ++ tst-gets \ ++ tst-grouping \ ++ tst-long-dbl-fphex \ ++ tst-obprintf \ ++ tst-perror \ ++ tst-popen \ ++ tst-popen2 \ ++ tst-printf-binary \ ++ tst-printf-bz18872 \ ++ tst-printf-bz25691 \ ++ tst-printf-fp-free \ ++ tst-printf-fp-leak \ ++ tst-printf-round \ ++ tst-printfsz \ ++ tst-put-error \ ++ tst-renameat2 \ ++ tst-rndseek \ ++ tst-scanf-bz27650 \ ++ tst-scanf-round \ ++ tst-setvbuf1 \ ++ tst-sprintf \ ++ tst-sprintf-errno \ ++ tst-sprintf2 \ ++ tst-sprintf3 \ ++ tst-sscanf \ ++ tst-swprintf \ ++ tst-swscanf \ ++ tst-tmpnam \ ++ tst-ungetc \ ++ tst-ungetc-leak \ ++ tst-unlockedio \ ++ tst-vfprintf-mbs-prec \ ++ tst-vfprintf-user-type \ ++ tst-vfprintf-width-prec \ ++ tst-vfprintf-width-prec-alloc \ ++ tst-wc-printf \ ++ tstdiomisc \ ++ tstgetln \ ++ tstscanf \ ++ xbug \ ++ # tests ++ ++test-srcs = \ ++ tst-printf \ ++ tst-printfsz-islongdouble \ ++ tst-unbputc \ ++ # test-srcs + + ifeq ($(run-built-tests),yes) +-tests-special += $(objpfx)tst-unbputc.out $(objpfx)tst-printf.out \ +- $(objpfx)tst-printf-bz18872-mem.out \ +- $(objpfx)tst-setvbuf1-cmp.out \ +- $(objpfx)tst-vfprintf-width-prec-mem.out \ +- $(objpfx)tst-printfsz-islongdouble.out \ +- $(objpfx)tst-printf-bz25691-mem.out \ +- $(objpfx)tst-printf-fp-free-mem.out \ +- $(objpfx)tst-printf-fp-leak-mem.out +-generated += tst-printf-bz18872.c tst-printf-bz18872.mtrace \ +- tst-printf-bz18872-mem.out \ +- tst-vfprintf-width-prec.mtrace tst-vfprintf-width-prec-mem.out \ +- tst-printf-bz25691.mtrace tst-printf-bz25691-mem.out \ +- tst-printf-fp-free.mtrace tst-printf-fp-free-mem.out \ +- tst-printf-fp-leak.mtrace tst-printf-fp-leak-mem.out +-endif ++tests-special += \ ++ $(objpfx)tst-printf-bz18872-mem.out \ ++ $(objpfx)tst-printf-bz25691-mem.out \ ++ $(objpfx)tst-printf-fp-free-mem.out \ ++ $(objpfx)tst-printf-fp-leak-mem.out \ ++ $(objpfx)tst-printf.out \ ++ $(objpfx)tst-printfsz-islongdouble.out \ ++ $(objpfx)tst-setvbuf1-cmp.out \ ++ $(objpfx)tst-unbputc.out \ ++ $(objpfx)tst-ungetc-leak-mem.out \ ++ $(objpfx)tst-vfprintf-width-prec-mem.out \ ++ # tests-special ++ ++generated += \ ++ tst-printf-bz18872-mem.out \ ++ tst-printf-bz18872.c \ ++ tst-printf-bz18872.mtrace \ ++ tst-printf-bz25691-mem.out \ ++ tst-printf-bz25691.mtrace \ ++ tst-printf-fp-free-mem.out \ ++ tst-printf-fp-free.mtrace \ ++ tst-printf-fp-leak-mem.out \ ++ tst-printf-fp-leak.mtrace \ ++ tst-scanf-bz27650.mtrace \ ++ tst-ungetc-leak-mem.out \ ++ tst-ungetc-leak.mtrace \ ++ tst-vfprintf-width-prec-mem.out \ ++ tst-vfprintf-width-prec.mtrace \ ++ # generated ++endif # $(run-built-tests) + + tests-special += $(objpfx)tst-errno-manual.out + + include ../Rules + + ifeq ($(run-built-tests),yes) +-LOCALES := de_DE.ISO-8859-1 de_DE.UTF-8 en_US.ISO-8859-1 ja_JP.EUC-JP ++LOCALES := \ ++ de_DE.ISO-8859-1 \ ++ de_DE.UTF-8 \ ++ en_US.ISO-8859-1 \ ++ ja_JP.EUC-JP \ ++ # LOCALES + include ../gen-locales.mk + + $(objpfx)bug14.out: $(gen-locales) +@@ -127,6 +291,12 @@ + tst-printf-fp-leak-ENV = \ + MALLOC_TRACE=$(objpfx)tst-printf-fp-leak.mtrace \ + LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so ++tst-scanf-bz27650-ENV = \ ++ MALLOC_TRACE=$(objpfx)tst-scanf-bz27650.mtrace \ ++ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so ++tst-ungetc-leak-ENV = \ ++ MALLOC_TRACE=$(objpfx)tst-ungetc-leak.mtrace \ ++ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so + + $(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc + $(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \ +diff -Nuar a/stdio-common/tst-scanf-bz27650.c b/stdio-common/tst-scanf-bz27650.c +--- a/stdio-common/tst-scanf-bz27650.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/stdio-common/tst-scanf-bz27650.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,108 @@ ++/* Test for BZ #27650, formatted input matching beyond INT_MAX. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++/* Produce a stream of more than INT_MAX characters via buffer BUF of ++ size SIZE according to bookkeeping in COOKIE and then return EOF. */ ++ ++static ssize_t ++io_read (void *cookie, char *buf, size_t size) ++{ ++ unsigned int *written = cookie; ++ unsigned int w = *written; ++ ++ if (w > INT_MAX) ++ return 0; ++ ++ memset (buf, 'a', size); ++ *written = w + size; ++ return size; ++} ++ ++/* Consume a stream of more than INT_MAX characters from an artificial ++ input stream of which none is the new line character. The call to ++ fscanf is supposed to complete upon the EOF condition of input, ++ however in the presence of BZ #27650 it will terminate prematurely ++ with characters still outstanding in input. Diagnose the condition ++ and return status accordingly. */ ++ ++int ++do_test (void) ++{ ++ static cookie_io_functions_t io_funcs = { .read = io_read }; ++ unsigned int written = 0; ++ FILE *in; ++ int v; ++ ++ mtrace (); ++ ++ in = fopencookie (&written, "r", io_funcs); ++ if (in == NULL) ++ { ++ FAIL ("fopencookie: %m"); ++ goto out; ++ } ++ ++ v = fscanf (in, "%*[^\n]"); ++ if (ferror (in)) ++ { ++ FAIL ("fscanf: input failure, at %u: %m", written); ++ goto out_close; ++ } ++ else if (v == EOF) ++ { ++ FAIL ("fscanf: unexpected end of file, at %u", written); ++ goto out_close; ++ } ++ ++ if (!feof (in)) ++ { ++ v = fgetc (in); ++ if (ferror (in)) ++ FAIL ("fgetc: input failure: %m"); ++ else if (v == EOF) ++ FAIL ("fgetc: unexpected end of file after missing end of file"); ++ else if (v == '\n') ++ FAIL ("unexpected new line character received"); ++ else ++ FAIL ("character received after end of file expected: \\x%02x", v); ++ } ++ ++out_close: ++ if (fclose (in) != 0) ++ FAIL ("fclose: %m"); ++ ++out: ++ return EXIT_SUCCESS; ++} ++ ++#define TIMEOUT (DEFAULT_TIMEOUT * 8) ++#include +diff -Nuar a/stdio-common/tst-ungetc.c b/stdio-common/tst-ungetc.c +--- a/stdio-common/tst-ungetc.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdio-common/tst-ungetc.c 2025-10-20 21:02:21.000000000 +0300 +@@ -1,70 +1,74 @@ +-/* Test for ungetc bugs. */ ++/* Test for ungetc bugs. ++ Copyright (C) 1996-2024 Free Software Foundation, Inc. ++ Copyright The GNU Toolchain Authors. ++ 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 ++ . */ + + #include + #include +-#include ++#include ++#include ++#include ++#include ++#include + +-#undef assert +-#define assert(x) \ +- if (!(x)) \ +- { \ +- fputs ("test failed: " #x "\n", stderr); \ +- retval = 1; \ +- goto the_end; \ +- } +- +-int +-main (int argc, char *argv[]) ++static int ++do_test (void) + { +- char name[] = "/tmp/tst-ungetc.XXXXXX"; ++ char *name = NULL; + FILE *fp = NULL; +- int retval = 0; + int c; + char buffer[64]; + +- int fd = mkstemp (name); ++ int fd = create_temp_file ("tst-ungetc.", &name); + if (fd == -1) +- { +- printf ("mkstemp failed: %m\n"); +- return 1; +- } +- close (fd); +- fp = fopen (name, "w"); +- assert (fp != NULL) ++ FAIL_EXIT1 ("cannot create temporary file: %m"); ++ xclose (fd); ++ ++ fp = xfopen (name, "w"); + fputs ("bla", fp); +- fclose (fp); +- fp = NULL; ++ xfclose (fp); + +- fp = fopen (name, "r"); +- assert (fp != NULL); +- assert (ungetc ('z', fp) == 'z'); +- assert (getc (fp) == 'z'); +- assert (getc (fp) == 'b'); +- assert (getc (fp) == 'l'); +- assert (ungetc ('m', fp) == 'm'); +- assert (getc (fp) == 'm'); +- assert ((c = getc (fp)) == 'a'); +- assert (getc (fp) == EOF); +- assert (ungetc (c, fp) == c); +- assert (feof (fp) == 0); +- assert (getc (fp) == c); +- assert (getc (fp) == EOF); +- fclose (fp); +- fp = NULL; +- +- fp = fopen (name, "r"); +- assert (fp != NULL); +- assert (getc (fp) == 'b'); +- assert (getc (fp) == 'l'); +- assert (ungetc ('b', fp) == 'b'); +- assert (fread (buffer, 1, 64, fp) == 2); +- assert (buffer[0] == 'b'); +- assert (buffer[1] == 'a'); +- +-the_end: +- if (fp != NULL) +- fclose (fp); +- unlink (name); ++ fp = xfopen (name, "r"); ++ TEST_VERIFY_EXIT (ungetc ('z', fp) == 'z'); ++ TEST_VERIFY_EXIT (getc (fp) == 'z'); ++ TEST_VERIFY_EXIT (getc (fp) == 'b'); ++ TEST_VERIFY_EXIT (getc (fp) == 'l'); ++ TEST_VERIFY_EXIT (ungetc ('m', fp) == 'm'); ++ TEST_VERIFY_EXIT (ungetc ('n', fp) == 'n'); ++ TEST_VERIFY_EXIT (getc (fp) == 'n'); ++ TEST_VERIFY_EXIT (getc (fp) == 'm'); ++ TEST_VERIFY_EXIT ((c = getc (fp)) == 'a'); ++ TEST_VERIFY_EXIT (getc (fp) == EOF); ++ TEST_VERIFY_EXIT (ungetc (c, fp) == c); ++ TEST_VERIFY_EXIT (feof (fp) == 0); ++ TEST_VERIFY_EXIT (getc (fp) == c); ++ TEST_VERIFY_EXIT (getc (fp) == EOF); ++ xfclose (fp); ++ ++ fp = xfopen (name, "r"); ++ TEST_VERIFY_EXIT (getc (fp) == 'b'); ++ TEST_VERIFY_EXIT (getc (fp) == 'l'); ++ TEST_VERIFY_EXIT (ungetc ('b', fp) == 'b'); ++ TEST_VERIFY_EXIT (fread (buffer, 1, 64, fp) == 2); ++ TEST_VERIFY_EXIT (buffer[0] == 'b'); ++ TEST_VERIFY_EXIT (buffer[1] == 'a'); ++ xfclose (fp); + +- return retval; ++ return 0; + } ++ ++#include +diff -Nuar a/stdio-common/tst-ungetc-leak.c b/stdio-common/tst-ungetc-leak.c +--- a/stdio-common/tst-ungetc-leak.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/stdio-common/tst-ungetc-leak.c 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,32 @@ ++/* Test for memory leak with ungetc when stream is unused. ++ Copyright The GNU Toolchain Authors. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ mtrace (); ++ TEST_COMPARE (ungetc('y', stdin), 'y'); ++ return 0; ++} ++ ++#include +diff -Nuar a/stdlib/bits/stdlib.h b/stdlib/bits/stdlib.h +--- a/stdlib/bits/stdlib.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdlib/bits/stdlib.h 2025-10-20 21:02:21.000000000 +0300 +@@ -36,16 +36,16 @@ + __fortify_function __wur char * + __NTH (realpath (const char *__restrict __name, char *__restrict __resolved)) + { +- size_t sz = __glibc_objsize (__resolved); ++ size_t __sz = __glibc_objsize (__resolved); + +- if (sz == (size_t) -1) ++ if (__sz == (size_t) -1) + return __realpath_alias (__name, __resolved); + + #if defined _LIBC_LIMITS_H_ && defined PATH_MAX +- if (__glibc_unsafe_len (PATH_MAX, sizeof (char), sz)) +- return __realpath_chk_warn (__name, __resolved, sz); ++ if (__glibc_unsafe_len (PATH_MAX, sizeof (char), __sz)) ++ return __realpath_chk_warn (__name, __resolved, __sz); + #endif +- return __realpath_chk (__name, __resolved, sz); ++ return __realpath_chk (__name, __resolved, __sz); + } + + +@@ -96,6 +96,11 @@ + const char *__restrict __src, + size_t __len, size_t __dstlen) __THROW + __attr_access ((__write_only__, 1, 3)) __attr_access ((__read_only__, 2)); ++extern size_t __REDIRECT_NTH (__mbstowcs_nulldst, ++ (wchar_t *__restrict __dst, ++ const char *__restrict __src, ++ size_t __len), mbstowcs) ++ __attr_access ((__read_only__, 2)); + extern size_t __REDIRECT_NTH (__mbstowcs_alias, + (wchar_t *__restrict __dst, + const char *__restrict __src, +@@ -112,12 +117,13 @@ + __NTH (mbstowcs (wchar_t *__restrict __dst, const char *__restrict __src, + size_t __len)) + { +- return __glibc_fortify_n (mbstowcs, __len, sizeof (wchar_t), +- __glibc_objsize (__dst), +- __dst, __src, __len); ++ if (__builtin_constant_p (__dst == NULL) && __dst == NULL) ++ return __mbstowcs_nulldst (__dst, __src, __len); ++ else ++ return __glibc_fortify_n (mbstowcs, __len, sizeof (wchar_t), ++ __glibc_objsize (__dst), __dst, __src, __len); + } + +- + extern size_t __wcstombs_chk (char *__restrict __dst, + const wchar_t *__restrict __src, + size_t __len, size_t __dstlen) __THROW +diff -Nuar a/stdlib/canonicalize.c b/stdlib/canonicalize.c +--- a/stdlib/canonicalize.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdlib/canonicalize.c 2025-10-20 21:02:21.000000000 +0300 +@@ -49,6 +49,7 @@ + #else + # define __canonicalize_file_name canonicalize_file_name + # define __realpath realpath ++# define __strdup strdup + # include "pathmax.h" + # define __faccessat faccessat + # if defined _WIN32 && !defined __CYGWIN__ +@@ -176,27 +177,16 @@ + return path_max < 0 ? 1024 : path_max <= IDX_MAX ? path_max : IDX_MAX; + } + +-/* Act like __realpath (see below), with an additional argument +- rname_buf that can be used as temporary storage. ++/* Scratch buffers used by realpath_stk and managed by __realpath. */ ++struct realpath_bufs ++{ ++ struct scratch_buffer rname; ++ struct scratch_buffer extra; ++ struct scratch_buffer link; ++}; + +- If GCC_LINT is defined, do not inline this function with GCC 10.1 +- and later, to avoid creating a pointer to the stack that GCC +- -Wreturn-local-addr incorrectly complains about. See: +- https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93644 +- Although the noinline attribute can hurt performance a bit, no better way +- to pacify GCC is known; even an explicit #pragma does not pacify GCC. +- When the GCC bug is fixed this workaround should be limited to the +- broken GCC versions. */ +-#if __GNUC_PREREQ (10, 1) +-# if defined GCC_LINT || defined lint +-__attribute__ ((__noinline__)) +-# elif __OPTIMIZE__ && !__NO_INLINE__ +-# define GCC_BOGUS_WRETURN_LOCAL_ADDR +-# endif +-#endif + static char * +-realpath_stk (const char *name, char *resolved, +- struct scratch_buffer *rname_buf) ++realpath_stk (const char *name, char *resolved, struct realpath_bufs *bufs) + { + char *dest; + char const *start; +@@ -221,12 +211,7 @@ + return NULL; + } + +- struct scratch_buffer extra_buffer, link_buffer; +- scratch_buffer_init (&extra_buffer); +- scratch_buffer_init (&link_buffer); +- scratch_buffer_init (rname_buf); +- char *rname_on_stack = rname_buf->data; +- char *rname = rname_on_stack; ++ char *rname = bufs->rname.data; + bool end_in_extra_buffer = false; + bool failed = true; + +@@ -236,16 +221,16 @@ + + if (!IS_ABSOLUTE_FILE_NAME (name)) + { +- while (!__getcwd (rname, rname_buf->length)) ++ while (!__getcwd (bufs->rname.data, bufs->rname.length)) + { + if (errno != ERANGE) + { + dest = rname; + goto error; + } +- if (!scratch_buffer_grow (rname_buf)) +- goto error_nomem; +- rname = rname_buf->data; ++ if (!scratch_buffer_grow (&bufs->rname)) ++ return NULL; ++ rname = bufs->rname.data; + } + dest = __rawmemchr (rname, '\0'); + start = name; +@@ -299,13 +284,13 @@ + if (!ISSLASH (dest[-1])) + *dest++ = '/'; + +- while (rname + rname_buf->length - dest ++ while (rname + bufs->rname.length - dest + < startlen + sizeof dir_suffix) + { + idx_t dest_offset = dest - rname; +- if (!scratch_buffer_grow_preserve (rname_buf)) +- goto error_nomem; +- rname = rname_buf->data; ++ if (!scratch_buffer_grow_preserve (&bufs->rname)) ++ return NULL; ++ rname = bufs->rname.data; + dest = rname + dest_offset; + } + +@@ -316,13 +301,13 @@ + ssize_t n; + while (true) + { +- buf = link_buffer.data; +- idx_t bufsize = link_buffer.length; ++ buf = bufs->link.data; ++ idx_t bufsize = bufs->link.length; + n = __readlink (rname, buf, bufsize - 1); + if (n < bufsize - 1) + break; +- if (!scratch_buffer_grow (&link_buffer)) +- goto error_nomem; ++ if (!scratch_buffer_grow (&bufs->link)) ++ return NULL; + } + if (0 <= n) + { +@@ -334,7 +319,7 @@ + + buf[n] = '\0'; + +- char *extra_buf = extra_buffer.data; ++ char *extra_buf = bufs->extra.data; + idx_t end_idx IF_LINT (= 0); + if (end_in_extra_buffer) + end_idx = end - extra_buf; +@@ -342,13 +327,13 @@ + if (INT_ADD_OVERFLOW (len, n)) + { + __set_errno (ENOMEM); +- goto error_nomem; ++ return NULL; + } +- while (extra_buffer.length <= len + n) ++ while (bufs->extra.length <= len + n) + { +- if (!scratch_buffer_grow_preserve (&extra_buffer)) +- goto error_nomem; +- extra_buf = extra_buffer.data; ++ if (!scratch_buffer_grow_preserve (&bufs->extra)) ++ return NULL; ++ extra_buf = bufs->extra.data; + } + if (end_in_extra_buffer) + end = extra_buf + end_idx; +@@ -402,26 +387,28 @@ + *dest++ = '\0'; + if (resolved != NULL) + { +- if (dest - rname <= get_path_max ()) +- rname = strcpy (resolved, rname); +- else if (!failed) ++ /* Copy the full result on success or partial result if failure was due ++ to the path not existing or not being accessible. */ ++ if ((!failed || errno == ENOENT || errno == EACCES) ++ && dest - rname <= get_path_max ()) + { +- failed = true; +- __set_errno (ENAMETOOLONG); ++ strcpy (resolved, rname); ++ if (failed) ++ return NULL; ++ else ++ return resolved; + } ++ if (!failed) ++ __set_errno (ENAMETOOLONG); ++ return NULL; + } +- +-error_nomem: +- scratch_buffer_free (&extra_buffer); +- scratch_buffer_free (&link_buffer); +- +- if (failed || rname == resolved) ++ else + { +- scratch_buffer_free (rname_buf); +- return failed ? NULL : resolved; ++ if (failed) ++ return NULL; ++ else ++ return __strdup (bufs->rname.data); + } +- +- return scratch_buffer_dupfree (rname_buf, dest - rname); + } + + /* Return the canonical absolute name of file NAME. A canonical name +@@ -438,12 +425,15 @@ + char * + __realpath (const char *name, char *resolved) + { +- #ifdef GCC_BOGUS_WRETURN_LOCAL_ADDR +- #warning "GCC might issue a bogus -Wreturn-local-addr warning here." +- #warning "See ." +- #endif +- struct scratch_buffer rname_buffer; +- return realpath_stk (name, resolved, &rname_buffer); ++ struct realpath_bufs bufs; ++ scratch_buffer_init (&bufs.rname); ++ scratch_buffer_init (&bufs.extra); ++ scratch_buffer_init (&bufs.link); ++ char *result = realpath_stk (name, resolved, &bufs); ++ scratch_buffer_free (&bufs.link); ++ scratch_buffer_free (&bufs.extra); ++ scratch_buffer_free (&bufs.rname); ++ return result; + } + libc_hidden_def (__realpath) + versioned_symbol (libc, __realpath, realpath, GLIBC_2_3); +diff -Nuar a/stdlib/exit.c b/stdlib/exit.c +--- a/stdlib/exit.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdlib/exit.c 2025-10-20 21:02:21.000000000 +0300 +@@ -53,7 +53,10 @@ + exit (). */ + while (true) + { +- struct exit_function_list *cur = *listp; ++ struct exit_function_list *cur; ++ ++ restart: ++ cur = *listp; + + if (cur == NULL) + { +@@ -118,7 +121,7 @@ + if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called)) + /* The last exit function, or another thread, has registered + more exit functions. Start the loop over. */ +- continue; ++ goto restart; + } + + *listp = cur->next; +diff -Nuar a/stdlib/Makefile b/stdlib/Makefile +--- a/stdlib/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdlib/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -73,6 +73,7 @@ + test-a64l \ + test-at_quick_exit-race \ + test-atexit-race \ ++ test-atexit-recursive \ + test-bz22786 \ + test-canon \ + test-canon2 \ +@@ -120,6 +121,7 @@ + tst-setcontext7 \ + tst-setcontext8 \ + tst-setcontext9 \ ++ tst-setenv-environ \ + tst-strfmon_l \ + tst-strfrom \ + tst-strfrom-locale \ +@@ -217,6 +219,9 @@ + CFLAGS-tst-makecontext.c += -funwind-tables + CFLAGS-tst-makecontext2.c += $(stack-align-test-flags) + ++CFLAGS-testmb.c += -D_FORTIFY_SOURCE=2 -Wall -Werror ++ ++ + # Run a test on the header files we use. + tests-special += $(objpfx)isomac.out + +diff -Nuar a/stdlib/test-atexit-recursive.c b/stdlib/test-atexit-recursive.c +--- a/stdlib/test-atexit-recursive.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/stdlib/test-atexit-recursive.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,75 @@ ++/* Support file for atexit/exit, etc. race tests (BZ #27749). ++ Copyright (C) 2023 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 ++ . */ ++ ++/* Check that atexit handler registed from another handler still called. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static void ++atexit_cb (void) ++{ ++} ++ ++static void ++atexit_last (void) ++{ ++ _exit (1); ++} ++ ++static void ++atexit_recursive (void) ++{ ++ atexit (&atexit_cb); ++ atexit (&atexit_last); ++} ++ ++_Noreturn static void ++test_and_exit (int count) ++{ ++ for (int i = 0; i < count; ++i) ++ atexit (&atexit_cb); ++ atexit (&atexit_recursive); ++ exit (0); ++} ++ ++static int ++do_test (void) ++{ ++ for (int i = 0; i < 100; ++i) ++ if (xfork () == 0) ++ test_and_exit (i); ++ ++ for (int i = 0; i < 100; ++i) ++ { ++ int status; ++ xwaitpid (0, &status, 0); ++ if (!WIFEXITED (status)) ++ FAIL_EXIT1 ("Failed iterations %d", i); ++ TEST_COMPARE (WEXITSTATUS (status), 1); ++ } ++ ++ return 0; ++} ++ ++#define TEST_FUNCTION do_test ++#include +diff -Nuar a/stdlib/test-canon.c b/stdlib/test-canon.c +--- a/stdlib/test-canon.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdlib/test-canon.c 2025-10-20 21:02:21.000000000 +0300 +@@ -174,7 +174,9 @@ + continue; + } + +- if (!check_path (buf, tests[i].out ? tests[i].out : tests[i].resolved)) ++ /* Verify buf contents if the call succeeded or failed with ENOENT. */ ++ if ((result != NULL || errno == ENOENT) ++ && !check_path (buf, tests[i].out ? tests[i].out : tests[i].resolved)) + { + printf ("%s: flunked test %d (expected resolved `%s', got `%s')\n", + argv[0], i, tests[i].out ? tests[i].out : tests[i].resolved, +diff -Nuar a/stdlib/testmb.c b/stdlib/testmb.c +--- a/stdlib/testmb.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdlib/testmb.c 2025-10-20 21:02:19.000000000 +0300 +@@ -16,6 +16,13 @@ + lose = 1; + } + ++ i = mbstowcs (NULL, "bar", 4); ++ if (!(i == 3 && w[1] == 'a')) ++ { ++ puts ("mbstowcs FAILED2!"); ++ lose = 1; ++ } ++ + mbstowcs (w, "blah", 5); + i = wcstombs (c, w, 10); + if (i != 4) +diff -Nuar a/stdlib/tst-secure-getenv.c b/stdlib/tst-secure-getenv.c +--- a/stdlib/tst-secure-getenv.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdlib/tst-secure-getenv.c 2025-10-20 21:02:21.000000000 +0300 +@@ -57,13 +57,7 @@ + exit (1); + } + +- int status = support_capture_subprogram_self_sgid (MAGIC_ARGUMENT); +- +- if (WEXITSTATUS (status) == EXIT_UNSUPPORTED) +- return EXIT_UNSUPPORTED; +- +- if (!WIFEXITED (status)) +- FAIL_EXIT1 ("Unexpected exit status %d from child process\n", status); ++ support_capture_subprogram_self_sgid (MAGIC_ARGUMENT); + + return 0; + } +@@ -82,6 +76,7 @@ + if (secure_getenv ("PATH") != NULL) + FAIL_EXIT (4, "PATH variable not filtered out\n"); + ++ support_record_failure_barrier (); + exit (EXIT_SUCCESS); + } + } +diff -Nuar a/stdlib/tst-setenv-environ.c b/stdlib/tst-setenv-environ.c +--- a/stdlib/tst-setenv-environ.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/stdlib/tst-setenv-environ.c 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,36 @@ ++/* Test using setenv with updated environ. ++ Copyright (C) 2025 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 ++ . */ ++ ++#include ++#include ++ ++extern char **environ; ++ ++int ++do_test (void) ++{ ++ char *valp; ++ static char *dummy_environ[] = { NULL }; ++ environ = dummy_environ; ++ setenv ("A", "1", 0); ++ valp = getenv ("A"); ++ TEST_VERIFY_EXIT (valp[0] == '1' && valp[1] == '\0'); ++ return 0; ++} ++ ++#include +diff -Nuar a/stdlib/tst-system.c b/stdlib/tst-system.c +--- a/stdlib/tst-system.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/stdlib/tst-system.c 2025-10-20 21:02:21.000000000 +0300 +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + + static char *tmpdir; +@@ -71,6 +72,20 @@ + } + } + ++static void * ++sleep_and_check_sigchld (void *closure) ++{ ++ double *seconds = (double *) closure; ++ char cmd[namemax]; ++ sprintf (cmd, "sleep %lf" , *seconds); ++ TEST_COMPARE (system (cmd), 0); ++ ++ sigset_t blocked = {0}; ++ TEST_COMPARE (sigprocmask (SIG_BLOCK, NULL, &blocked), 0); ++ TEST_COMPARE (sigismember (&blocked, SIGCHLD), 0); ++ return NULL; ++} ++ + static int + do_test (void) + { +@@ -154,6 +169,17 @@ + xchmod (_PATH_BSHELL, st.st_mode); + } + ++ { ++ pthread_t long_sleep_thread = xpthread_create (NULL, ++ sleep_and_check_sigchld, ++ &(double) { 0.2 }); ++ pthread_t short_sleep_thread = xpthread_create (NULL, ++ sleep_and_check_sigchld, ++ &(double) { 0.1 }); ++ xpthread_join (short_sleep_thread); ++ xpthread_join (long_sleep_thread); ++ } ++ + TEST_COMPARE (system (""), 0); + + return 0; +diff -Nuar a/string/bits/string_fortified.h b/string/bits/string_fortified.h +--- a/string/bits/string_fortified.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/string/bits/string_fortified.h 2025-10-20 21:02:21.000000000 +0300 +@@ -107,7 +107,7 @@ + # else + extern char *__stpncpy_chk (char *__dest, const char *__src, size_t __n, + size_t __destlen) __THROW +- __fortified_attr_access ((__write_only__, 1, 3)) ++ __fortified_attr_access (__write_only__, 1, 3) + __attr_access ((__read_only__, 2)); + extern char *__REDIRECT_NTH (__stpncpy_alias, (char *__dest, const char *__src, + size_t __n), stpncpy); +diff -Nuar a/string/test-rawmemchr.c b/string/test-rawmemchr.c +--- a/string/test-rawmemchr.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/string/test-rawmemchr.c 2025-10-20 21:02:21.000000000 +0300 +@@ -17,6 +17,7 @@ + . */ + + #include ++#include + + #define TEST_MAIN + #define TEST_NAME "rawmemchr" +@@ -51,12 +52,44 @@ + } + + static void ++do_test_bz29234 (void) ++{ ++ size_t i, j; ++ char *ptr_start; ++ char *buf = xmmap (0, 8192, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS, -1); ++ ++ memset (buf, -1, 8192); ++ ++ ptr_start = buf + 4096 - 8; ++ ++ /* Out of range matches before the start of a page. */ ++ memset (ptr_start - 8, 0x1, 8); ++ ++ for (j = 0; j < 8; ++j) ++ { ++ for (i = 0; i < 128; ++i) ++ { ++ ptr_start[i + j] = 0x1; ++ ++ FOR_EACH_IMPL (impl, 0) ++ do_one_test (impl, (char *) (ptr_start + j), 0x1, ++ ptr_start + i + j); ++ ++ ptr_start[i + j] = 0xff; ++ } ++ } ++ ++ xmunmap (buf, 8192); ++} ++ ++static void + do_test (size_t align, size_t pos, size_t len, int seek_char) + { + size_t i; + char *result; + +- align &= 7; ++ align &= getpagesize () - 1; + if (align + len >= page_size) + return; + +@@ -114,6 +147,13 @@ + } + } + ++ if (align) ++ { ++ p[align - 1] = seek_char; ++ if (align > 4) ++ p[align - 4] = seek_char; ++ } ++ + assert (pos < len); + size_t r = random (); + if ((r & 31) == 0) +@@ -129,6 +169,13 @@ + result, p); + ret = 1; + } ++ ++ if (align) ++ { ++ p[align - 1] = seek_char; ++ if (align > 4) ++ p[align - 4] = seek_char; ++ } + } + } + +@@ -150,14 +197,22 @@ + do_test (i, 64, 256, 23); + do_test (0, 16 << i, 2048, 0); + do_test (i, 64, 256, 0); ++ ++ do_test (getpagesize () - i, 64, 256, 23); ++ do_test (getpagesize () - i, 64, 256, 0); + } + for (i = 1; i < 32; ++i) + { + do_test (0, i, i + 1, 23); + do_test (0, i, i + 1, 0); ++ ++ do_test (getpagesize () - 7, i, i + 1, 23); ++ do_test (getpagesize () - i / 2, i, i + 1, 23); ++ do_test (getpagesize () - i, i, i + 1, 23); + } + + do_random_tests (); ++ do_test_bz29234 (); + return ret; + } + +diff -Nuar a/string/test-strncmp.c b/string/test-strncmp.c +--- a/string/test-strncmp.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/string/test-strncmp.c 2025-10-20 21:02:21.000000000 +0300 +@@ -434,6 +434,28 @@ + } + } + ++static void ++check4 (void) ++{ ++ /* To trigger bug 28895; We need 1) both s1 and s2 to be within 32 bytes of ++ the end of the page. 2) For there to be no mismatch/null byte before the ++ first page cross. 3) For length (`n`) to be large enough for one string to ++ cross the page. And 4) for there to be either mismatch/null bytes before ++ the start of the strings. */ ++ ++ size_t size = 10; ++ size_t addr_mask = (getpagesize () - 1) ^ (sizeof (CHAR) - 1); ++ CHAR *s1 = (CHAR *)(buf1 + (addr_mask & 0xffa)); ++ CHAR *s2 = (CHAR *)(buf2 + (addr_mask & 0xfed)); ++ int exp_result; ++ ++ STRCPY (s1, L ("tst-tlsmod%")); ++ STRCPY (s2, L ("tst-tls-manydynamic73mod")); ++ exp_result = SIMPLE_STRNCMP (s1, s2, size); ++ FOR_EACH_IMPL (impl, 0) ++ check_result (impl, s1, s2, size, exp_result); ++} ++ + int + test_main (void) + { +@@ -444,6 +466,7 @@ + check1 (); + check2 (); + check3 (); ++ check4 (); + + printf ("%23s", ""); + FOR_EACH_IMPL (impl, 0) +diff -Nuar a/string/test-strnlen.c b/string/test-strnlen.c +--- a/string/test-strnlen.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/string/test-strnlen.c 2025-10-20 21:02:21.000000000 +0300 +@@ -74,7 +74,7 @@ + { + size_t i; + +- align &= 63; ++ align &= (getpagesize () / sizeof (CHAR) - 1); + if ((align + len) * sizeof (CHAR) >= page_size) + return; + +@@ -91,36 +91,50 @@ + static void + do_overflow_tests (void) + { +- size_t i, j, len; ++ size_t i, j, al_idx, repeats, len; + const size_t one = 1; + uintptr_t buf_addr = (uintptr_t) buf1; ++ const size_t alignments[] = { 0, 1, 7, 9, 31, 33, 63, 65, 95, 97, 127, 129 }; + +- for (i = 0; i < 750; ++i) ++ for (al_idx = 0; al_idx < sizeof (alignments) / sizeof (alignments[0]); ++ al_idx++) + { +- do_test (0, i, SIZE_MAX - i, BIG_CHAR); +- do_test (0, i, i - buf_addr, BIG_CHAR); +- do_test (0, i, -buf_addr - i, BIG_CHAR); +- do_test (0, i, SIZE_MAX - buf_addr - i, BIG_CHAR); +- do_test (0, i, SIZE_MAX - buf_addr + i, BIG_CHAR); +- +- len = 0; +- for (j = 8 * sizeof(size_t) - 1; j ; --j) +- { +- len |= one << j; +- do_test (0, i, len - i, BIG_CHAR); +- do_test (0, i, len + i, BIG_CHAR); +- do_test (0, i, len - buf_addr - i, BIG_CHAR); +- do_test (0, i, len - buf_addr + i, BIG_CHAR); +- +- do_test (0, i, ~len - i, BIG_CHAR); +- do_test (0, i, ~len + i, BIG_CHAR); +- do_test (0, i, ~len - buf_addr - i, BIG_CHAR); +- do_test (0, i, ~len - buf_addr + i, BIG_CHAR); +- +- do_test (0, i, -buf_addr, BIG_CHAR); +- do_test (0, i, j - buf_addr, BIG_CHAR); +- do_test (0, i, -buf_addr - j, BIG_CHAR); +- } ++ for (repeats = 0; repeats < 2; ++repeats) ++ { ++ size_t align = repeats ? (getpagesize () - alignments[al_idx]) ++ : alignments[al_idx]; ++ align /= sizeof (CHAR); ++ for (i = 0; i < 750; ++i) ++ { ++ do_test (align, i, SIZE_MAX, BIG_CHAR); ++ ++ do_test (align, i, SIZE_MAX - i, BIG_CHAR); ++ do_test (align, i, i - buf_addr, BIG_CHAR); ++ do_test (align, i, -buf_addr - i, BIG_CHAR); ++ do_test (align, i, SIZE_MAX - buf_addr - i, BIG_CHAR); ++ do_test (align, i, SIZE_MAX - buf_addr + i, BIG_CHAR); ++ ++ len = 0; ++ for (j = 8 * sizeof (size_t) - 1; j; --j) ++ { ++ len |= one << j; ++ do_test (align, i, len, BIG_CHAR); ++ do_test (align, i, len - i, BIG_CHAR); ++ do_test (align, i, len + i, BIG_CHAR); ++ do_test (align, i, len - buf_addr - i, BIG_CHAR); ++ do_test (align, i, len - buf_addr + i, BIG_CHAR); ++ ++ do_test (align, i, ~len - i, BIG_CHAR); ++ do_test (align, i, ~len + i, BIG_CHAR); ++ do_test (align, i, ~len - buf_addr - i, BIG_CHAR); ++ do_test (align, i, ~len - buf_addr + i, BIG_CHAR); ++ ++ do_test (align, i, -buf_addr, BIG_CHAR); ++ do_test (align, i, j - buf_addr, BIG_CHAR); ++ do_test (align, i, -buf_addr - j, BIG_CHAR); ++ } ++ } ++ } + } + } + +diff -Nuar a/support/capture_subprocess.h b/support/capture_subprocess.h +--- a/support/capture_subprocess.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/capture_subprocess.h 2025-10-20 21:02:21.000000000 +0300 +@@ -41,11 +41,12 @@ + struct support_capture_subprocess support_capture_subprogram + (const char *file, char *const argv[]); + +-/* Copy the running program into a setgid binary and run it with CHILD_ID +- argument. If execution is successful, return the exit status of the child +- program, otherwise return a non-zero failure exit code. */ +-int support_capture_subprogram_self_sgid +- (char *child_id); ++/* Copy the running program into a setgid binary and run it with ++ CHILD_ID argument. If the program exits with a non-zero status, ++ exit with that exit status (or status 1 if the program did not exit ++ normally). If the test cannot be performed, exit with ++ EXIT_UNSUPPORTED. */ ++void support_capture_subprogram_self_sgid (const char *child_id); + + /* Deallocate the subprocess data captured by + support_capture_subprocess. */ +diff -Nuar a/support/check.h b/support/check.h +--- a/support/check.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/check.h 2025-10-20 21:02:21.000000000 +0300 +@@ -25,6 +25,11 @@ + __BEGIN_DECLS + + /* Record a test failure, print the failure message to standard output ++ and pass the result of 1 through. */ ++#define FAIL(...) \ ++ support_print_failure_impl (__FILE__, __LINE__, __VA_ARGS__) ++ ++/* Record a test failure, print the failure message to standard output + and return 1. */ + #define FAIL_RET(...) \ + return support_print_failure_impl (__FILE__, __LINE__, __VA_ARGS__) +@@ -202,6 +207,9 @@ + failures or not. */ + int support_record_failure_is_failed (void); + ++/* Terminate the process if any failures have been encountered so far. */ ++void support_record_failure_barrier (void); ++ + __END_DECLS + + #endif /* SUPPORT_CHECK_H */ +diff -Nuar a/support/dtotimespec.c b/support/dtotimespec.c +--- a/support/dtotimespec.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/support/dtotimespec.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,50 @@ ++/* Convert double to timespec. ++ Copyright (C) 2011-2023 Free Software Foundation, Inc. ++ This file is part of the GNU C Library and is also part of gnulib. ++ Patches to this file should be submitted to both projects. ++ ++ 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 ++ . */ ++ ++/* Convert the double value SEC to a struct timespec. Round toward ++ positive infinity. On overflow, return an extremal value. */ ++ ++#include ++#include ++ ++struct timespec ++dtotimespec (double sec) ++{ ++ if (sec <= TYPE_MINIMUM (time_t)) ++ return make_timespec (TYPE_MINIMUM (time_t), 0); ++ else if (sec >= 1.0 + TYPE_MAXIMUM (time_t)) ++ return make_timespec (TYPE_MAXIMUM (time_t), TIMESPEC_HZ - 1); ++ else ++ { ++ time_t s = sec; ++ double frac = TIMESPEC_HZ * (sec - s); ++ long ns = frac; ++ ns += ns < frac; ++ s += ns / TIMESPEC_HZ; ++ ns %= TIMESPEC_HZ; ++ ++ if (ns < 0) ++ { ++ s--; ++ ns += TIMESPEC_HZ; ++ } ++ ++ return make_timespec (s, ns); ++ } ++} +diff -Nuar a/support/dtotimespec-time64.c b/support/dtotimespec-time64.c +--- a/support/dtotimespec-time64.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/support/dtotimespec-time64.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,27 @@ ++/* Convert double to timespec. 64-bit time support. ++ Copyright (C) 2011-2023 Free Software Foundation, Inc. ++ This file is part of the GNU C Library and is also part of gnulib. ++ Patches to this file should be submitted to both projects. ++ ++ 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 ++ . */ ++ ++#include ++ ++#if __TIMESIZE != 64 ++# define timespec __timespec64 ++# define time_t __time64_t ++# define dtotimespec dtotimespec_time64 ++# include "dtotimespec.c" ++#endif +diff -Nuar a/support/Makefile b/support/Makefile +--- a/support/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -32,6 +32,8 @@ + check_hostent \ + check_netent \ + delayed_exit \ ++ dtotimespec \ ++ dtotimespec-time64 \ + ignore_stderr \ + next_to_fault \ + oom_error \ +@@ -64,6 +66,7 @@ + support_format_netent \ + support_isolate_in_subprocess \ + support_mutex_pi_monotonic \ ++ support_need_proc \ + support_path_support_time64 \ + support_process_state \ + support_ptrace \ +@@ -157,6 +160,7 @@ + xpthread_cancel \ + xpthread_check_return \ + xpthread_cond_wait \ ++ xpthread_cond_signal \ + xpthread_create \ + xpthread_detach \ + xpthread_join \ +@@ -203,6 +207,7 @@ + xstrndup \ + xsymlink \ + xsysconf \ ++ xsystem \ + xunlink \ + xuselocale \ + xwaitpid \ +@@ -235,6 +240,24 @@ + CFLAGS-timespec.c += -fexcess-precision=standard + CFLAGS-timespec-time64.c += -fexcess-precision=standard + ++# Ensure that general support files use 64-bit time_t ++CFLAGS-delayed_exit.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-shell-container.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-support_can_chroot.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-support_copy_file.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-support_copy_file_range.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-support_descriptor_supports_holes.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-support_descriptors.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-support_process_state.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-support_stat_nanoseconds.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-support_subprocess.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-support_test_main.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-test-container.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++CFLAGS-xmkdirp.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++# This is required to get an mkstemp which can create large files on some ++# 32-bit platforms. ++CFLAGS-temp_file.c += -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64 ++ + ifeq (,$(CXX)) + LINKS_DSO_PROGRAM = links-dso-program-c + else +diff -Nuar a/support/shell-container.c b/support/shell-container.c +--- a/support/shell-container.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/shell-container.c 2025-10-20 21:02:21.000000000 +0300 +@@ -16,8 +16,6 @@ + License along with the GNU C Library; if not, see + . */ + +-#define _FILE_OFFSET_BITS 64 +- + #include + #include + #include +@@ -39,6 +37,7 @@ + #include + + #include ++#include + + /* Design considerations + +@@ -171,6 +170,32 @@ + return 0; + } + ++/* Emulate the "/bin/sleep" command. No suffix support. Options are ++ ignored. */ ++static int ++sleep_func (char **argv) ++{ ++ if (argv[0] == NULL) ++ { ++ fprintf (stderr, "sleep: missing operand\n"); ++ return 1; ++ } ++ char *endptr = NULL; ++ double sec = strtod (argv[0], &endptr); ++ if (endptr == argv[0] || errno == ERANGE || sec < 0) ++ { ++ fprintf (stderr, "sleep: invalid time interval '%s'\n", argv[0]); ++ return 1; ++ } ++ struct timespec ts = dtotimespec (sec); ++ if (nanosleep (&ts, NULL) < 0) ++ { ++ fprintf (stderr, "sleep: failed to nanosleep: %s\n", strerror (errno)); ++ return 1; ++ } ++ return 0; ++} ++ + /* This is a list of all the built-in commands we understand. */ + static struct { + const char *name; +@@ -181,6 +206,7 @@ + { "cp", copy_func }, + { "exit", exit_func }, + { "kill", kill_func }, ++ { "sleep", sleep_func }, + { NULL, NULL } + }; + +diff -Nuar a/support/support_can_chroot.c b/support/support_can_chroot.c +--- a/support/support_can_chroot.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/support_can_chroot.c 2025-10-20 21:02:21.000000000 +0300 +@@ -29,14 +29,14 @@ + callback (void *closure) + { + int *result = closure; +- struct stat64 before; ++ struct stat before; + xstat ("/dev", &before); + if (chroot ("/dev") != 0) + { + *result = errno; + return; + } +- struct stat64 after; ++ struct stat after; + xstat ("/", &after); + TEST_VERIFY (before.st_dev == after.st_dev); + TEST_VERIFY (before.st_ino == after.st_ino); +diff -Nuar a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c +--- a/support/support_capture_subprocess.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/support_capture_subprocess.c 2025-10-20 21:02:21.000000000 +0300 +@@ -21,12 +21,17 @@ + + #include + #include ++#include ++#include ++#include + #include ++#include + #include + #include + #include + #include + #include ++#include + #include + + static void +@@ -108,100 +113,88 @@ + /* Copies the executable into a restricted directory, so that we can + safely make it SGID with the TARGET group ID. Then runs the + executable. */ +-static int +-copy_and_spawn_sgid (char *child_id, gid_t gid) ++static void ++copy_and_spawn_sgid (const char *child_id, gid_t gid) + { +- char *dirname = xasprintf ("%s/tst-tunables-setuid.%jd", +- test_dir, (intmax_t) getpid ()); ++ char *dirname = support_create_temp_directory ("tst-glibc-sgid-"); + char *execname = xasprintf ("%s/bin", dirname); +- int infd = -1; +- int outfd = -1; +- int ret = 1, status = 1; +- +- TEST_VERIFY (mkdir (dirname, 0700) == 0); +- if (support_record_failure_is_failed ()) +- goto err; ++ add_temp_file (execname); + +- infd = open ("/proc/self/exe", O_RDONLY); +- if (infd < 0) ++ if (access ("/proc/self/exe", R_OK) != 0) + FAIL_UNSUPPORTED ("unsupported: Cannot read binary from procfs\n"); + +- outfd = open (execname, O_WRONLY | O_CREAT | O_EXCL, 0700); +- TEST_VERIFY (outfd >= 0); +- if (support_record_failure_is_failed ()) +- goto err; +- +- char buf[4096]; +- for (;;) +- { +- ssize_t rdcount = read (infd, buf, sizeof (buf)); +- TEST_VERIFY (rdcount >= 0); +- if (support_record_failure_is_failed ()) +- goto err; +- if (rdcount == 0) +- break; +- char *p = buf; +- char *end = buf + rdcount; +- while (p != end) +- { +- ssize_t wrcount = write (outfd, buf, end - p); +- if (wrcount == 0) +- errno = ENOSPC; +- TEST_VERIFY (wrcount > 0); +- if (support_record_failure_is_failed ()) +- goto err; +- p += wrcount; +- } +- } +- TEST_VERIFY (fchown (outfd, getuid (), gid) == 0); +- if (support_record_failure_is_failed ()) +- goto err; +- TEST_VERIFY (fchmod (outfd, 02750) == 0); +- if (support_record_failure_is_failed ()) +- goto err; +- TEST_VERIFY (close (outfd) == 0); +- if (support_record_failure_is_failed ()) +- goto err; +- TEST_VERIFY (close (infd) == 0); +- if (support_record_failure_is_failed ()) +- goto err; ++ support_copy_file ("/proc/self/exe", execname); ++ ++ if (chown (execname, getuid (), gid) != 0) ++ FAIL_UNSUPPORTED ("cannot change group of \"%s\" to %jd: %m", ++ execname, (intmax_t) gid); ++ ++ if (chmod (execname, 02750) != 0) ++ FAIL_UNSUPPORTED ("cannot make \"%s\" SGID: %m ", execname); + + /* We have the binary, now spawn the subprocess. Avoid using + support_subprogram because we only want the program exit status, not the + contents. */ +- ret = 0; +- infd = outfd = -1; + +- char * const args[] = {execname, child_id, NULL}; ++ char * const args[] = {execname, (char *) child_id, NULL}; ++ int status = support_subprogram_wait (args[0], args); + +- status = support_subprogram_wait (args[0], args); ++ free (execname); ++ free (dirname); + +-err: +- if (outfd >= 0) +- close (outfd); +- if (infd >= 0) +- close (infd); +- if (execname != NULL) +- { +- unlink (execname); +- free (execname); +- } +- if (dirname != NULL) ++ if (WIFEXITED (status)) + { +- rmdir (dirname); +- free (dirname); ++ if (WEXITSTATUS (status) == 0) ++ return; ++ else ++ exit (WEXITSTATUS (status)); + } ++ else ++ FAIL_EXIT1 ("subprogram failed with status %d", status); ++} + +- if (ret != 0) +- FAIL_EXIT1("Failed to make sgid executable for test\n"); +- +- return status; ++/* Returns true if a group with NAME has been found, and writes its ++ GID to *TARGET. */ ++static bool ++find_sgid_group (gid_t *target, const char *name) ++{ ++ /* Do not use getgrname_r because it does not work in statically ++ linked binaries if the system libc is different. */ ++ FILE *fp = fopen ("/etc/group", "rce"); ++ if (fp == NULL) ++ return false; ++ __fsetlocking (fp, FSETLOCKING_BYCALLER); ++ ++ bool ok = false; ++ struct scratch_buffer buf; ++ scratch_buffer_init (&buf); ++ while (true) ++ { ++ struct group grp; ++ struct group *result = NULL; ++ int status = fgetgrent_r (fp, &grp, buf.data, buf.length, &result); ++ if (status == 0 && result != NULL) ++ { ++ if (strcmp (result->gr_name, name) == 0) ++ { ++ *target = result->gr_gid; ++ ok = true; ++ break; ++ } ++ } ++ else if (errno != ERANGE) ++ break; ++ else if (!scratch_buffer_grow (&buf)) ++ break; ++ } ++ scratch_buffer_free (&buf); ++ fclose (fp); ++ return ok; + } + +-int +-support_capture_subprogram_self_sgid (char *child_id) ++void ++support_capture_subprogram_self_sgid (const char *child_id) + { +- gid_t target = 0; + const int count = 64; + gid_t groups[count]; + +@@ -213,6 +206,7 @@ + (intmax_t) getuid ()); + + gid_t current = getgid (); ++ gid_t target = current; + for (int i = 0; i < ret; ++i) + { + if (groups[i] != current) +@@ -222,11 +216,18 @@ + } + } + +- if (target == 0) +- FAIL_UNSUPPORTED("Could not find a suitable GID for user %jd\n", +- (intmax_t) getuid ()); ++ if (target == current) ++ { ++ /* If running as root, try to find a harmless group for SGID. */ ++ if (getuid () != 0 ++ || (!find_sgid_group (&target, "nogroup") ++ && !find_sgid_group (&target, "bin") ++ && !find_sgid_group (&target, "daemon"))) ++ FAIL_UNSUPPORTED("Could not find a suitable GID for user %jd\n", ++ (intmax_t) getuid ()); ++ } + +- return copy_and_spawn_sgid (child_id, target); ++ copy_and_spawn_sgid (child_id, target); + } + + void +diff -Nuar a/support/support_copy_file.c b/support/support_copy_file.c +--- a/support/support_copy_file.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/support_copy_file.c 2025-10-20 21:02:21.000000000 +0300 +@@ -24,7 +24,7 @@ + void + support_copy_file (const char *from, const char *to) + { +- struct stat64 st; ++ struct stat st; + xstat (from, &st); + int fd_from = xopen (from, O_RDONLY, 0); + mode_t mode = st.st_mode & 0777; +diff -Nuar a/support/support_descriptor_supports_holes.c b/support/support_descriptor_supports_holes.c +--- a/support/support_descriptor_supports_holes.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/support_descriptor_supports_holes.c 2025-10-20 21:02:21.000000000 +0300 +@@ -40,7 +40,7 @@ + block_headroom = 32, + }; + +- struct stat64 st; ++ struct stat st; + xfstat (fd, &st); + if (!S_ISREG (st.st_mode)) + FAIL_EXIT1 ("descriptor %d does not refer to a regular file", fd); +diff -Nuar a/support/support.h b/support/support.h +--- a/support/support.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/support.h 2025-10-20 21:02:21.000000000 +0300 +@@ -91,6 +91,11 @@ + regular file open for writing, and initially empty. */ + int support_descriptor_supports_holes (int fd); + ++/* Predicates that a test requires a working /proc filesystem. This ++ call will exit with UNSUPPORTED if /proc is not available, printing ++ WHY_MSG as part of the diagnostic. */ ++void support_need_proc (const char *why_msg); ++ + /* Error-checking wrapper functions which terminate the process on + error. */ + +diff -Nuar a/support/support_need_proc.c b/support/support_need_proc.c +--- a/support/support_need_proc.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/support/support_need_proc.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,35 @@ ++/* Indicate that a test requires a working /proc. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++ ++/* We test for /proc/self/maps since that's one of the files that one ++ of our tests actually uses, but the general idea is if Linux's ++ /proc/ (procfs) filesystem is mounted. If not, the process exits ++ with an UNSUPPORTED result code. */ ++ ++void ++support_need_proc (const char *why_msg) ++{ ++#ifdef __linux__ ++ if (access ("/proc/self/maps", R_OK)) ++ FAIL_UNSUPPORTED ("/proc is not available, %s", why_msg); ++#endif ++} +diff -Nuar a/support/support_record_failure.c b/support/support_record_failure.c +--- a/support/support_record_failure.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/support_record_failure.c 2025-10-20 21:02:21.000000000 +0300 +@@ -112,3 +112,13 @@ + synchronization for reliable test error reporting anyway. */ + return __atomic_load_n (&state->failed, __ATOMIC_RELAXED); + } ++ ++void ++support_record_failure_barrier (void) ++{ ++ if (__atomic_load_n (&state->failed, __ATOMIC_RELAXED)) ++ { ++ puts ("error: exiting due to previous errors"); ++ exit (1); ++ } ++} +diff -Nuar a/support/test-container.c b/support/test-container.c +--- a/support/test-container.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/test-container.c 2025-10-20 21:02:21.000000000 +0300 +@@ -16,8 +16,6 @@ + License along with the GNU C Library; if not, see + . */ + +-#define _FILE_OFFSET_BITS 64 +- + #include + #include + #include +@@ -97,6 +95,7 @@ + * mytest.root/mytest.script has a list of "commands" to run: + syntax: + # comment ++ pidns + su + mv FILE FILE + cp FILE FILE +@@ -122,6 +121,8 @@ + + details: + - '#': A comment. ++ - 'pidns': Require a separate PID namespace, prints comment if it can't ++ (default is a shared pid namespace) + - 'su': Enables running test as root in the container. + - 'mv': A minimal move files command. + - 'cp': A minimal copy files command. +@@ -148,7 +149,7 @@ + * Simple, easy to review code (i.e. prefer simple naive code over + complex efficient code) + +- * The current implementation ist parallel-make-safe, but only in ++ * The current implementation is parallel-make-safe, but only in + that it uses a lock to prevent parallel access to the testroot. */ + + +@@ -227,11 +228,37 @@ + return bufs[n]; + } + ++/* Like the above, but put spaces between words. Caller frees. */ ++static char * ++concat_words (char **words, int num_words) ++{ ++ int len = 0; ++ int i; ++ char *rv, *p; ++ ++ for (i = 0; i < num_words; i ++) ++ { ++ len += strlen (words[i]); ++ len ++; ++ } ++ ++ p = rv = (char *) xmalloc (len); ++ ++ for (i = 0; i < num_words; i ++) ++ { ++ if (i > 0) ++ p = stpcpy (p, " "); ++ p = stpcpy (p, words[i]); ++ } ++ ++ return rv; ++} ++ + /* Try to mount SRC onto DEST. */ + static void + trymount (const char *src, const char *dest) + { +- if (mount (src, dest, "", MS_BIND, NULL) < 0) ++ if (mount (src, dest, "", MS_BIND | MS_REC, NULL) < 0) + FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest); + } + +@@ -726,6 +753,9 @@ + gid_t original_gid; + /* If set, the test runs as root instead of the user running the testsuite. */ + int be_su = 0; ++ int require_pidns = 0; ++ const char *pidns_comment = NULL; ++ int do_proc_mounts = 0; + int UMAP; + int GMAP; + /* Used for "%lld %lld 1" so need not be large. */ +@@ -1011,6 +1041,12 @@ + { + be_su = 1; + } ++ else if (nt >= 1 && strcmp (the_words[0], "pidns") == 0) ++ { ++ require_pidns = 1; ++ if (nt > 1) ++ pidns_comment = concat_words (the_words + 1, nt - 1); ++ } + else if (nt == 3 && strcmp (the_words[0], "mkdirp") == 0) + { + long int m; +@@ -1068,7 +1104,8 @@ + + #ifdef CLONE_NEWNS + /* The unshare here gives us our own spaces and capabilities. */ +- if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0) ++ if (unshare (CLONE_NEWUSER | CLONE_NEWNS ++ | (require_pidns ? CLONE_NEWPID : 0)) < 0) + { + /* Older kernels may not support all the options, or security + policy may block this call. */ +@@ -1079,6 +1116,11 @@ + check_for_unshare_hints (); + FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno)); + } ++ /* We're about to exit anyway, it's "safe" to call unshare again ++ just to see if the CLONE_NEWPID caused the error. */ ++ else if (require_pidns && unshare (CLONE_NEWUSER | CLONE_NEWNS) >= 0) ++ FAIL_EXIT1 ("unable to unshare pid ns: %s : %s", strerror (errno), ++ pidns_comment ? pidns_comment : "required by test"); + else + FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno)); + } +@@ -1094,6 +1136,15 @@ + trymount (support_srcdir_root, new_srcdir_path); + trymount (support_objdir_root, new_objdir_path); + ++ /* It may not be possible to mount /proc directly. */ ++ if (! require_pidns) ++ { ++ char *new_proc = concat (new_root_path, "/proc", NULL); ++ xmkdirp (new_proc, 0755); ++ trymount ("/proc", new_proc); ++ do_proc_mounts = 1; ++ } ++ + xmkdirp (concat (new_root_path, "/dev", NULL), 0755); + devmount (new_root_path, "null"); + devmount (new_root_path, "zero"); +@@ -1163,42 +1214,60 @@ + + maybe_xmkdir ("/tmp", 0755); + +- /* Now that we're pid 1 (effectively "root") we can mount /proc */ +- maybe_xmkdir ("/proc", 0777); +- if (mount ("proc", "/proc", "proc", 0, NULL) < 0) +- FAIL_EXIT1 ("Unable to mount /proc: "); +- +- /* We map our original UID to the same UID in the container so we +- can own our own files normally. */ +- UMAP = open ("/proc/self/uid_map", O_WRONLY); +- if (UMAP < 0) +- FAIL_EXIT1 ("can't write to /proc/self/uid_map\n"); +- +- sprintf (tmp, "%lld %lld 1\n", +- (long long) (be_su ? 0 : original_uid), (long long) original_uid); +- write (UMAP, tmp, strlen (tmp)); +- xclose (UMAP); +- +- /* We must disable setgroups () before we can map our groups, else we +- get EPERM. */ +- GMAP = open ("/proc/self/setgroups", O_WRONLY); +- if (GMAP >= 0) ++ if (require_pidns) + { +- /* We support kernels old enough to not have this. */ +- write (GMAP, "deny\n", 5); +- xclose (GMAP); ++ /* Now that we're pid 1 (effectively "root") we can mount /proc */ ++ maybe_xmkdir ("/proc", 0777); ++ if (mount ("proc", "/proc", "proc", 0, NULL) != 0) ++ { ++ /* This happens if we're trying to create a nested container, ++ like if the build is running under podman, and we lack ++ priviledges. ++ ++ Ideally we would WARN here, but that would just add noise to ++ *every* test-container test, and the ones that care should ++ have their own relevent diagnostics. ++ ++ FAIL_EXIT1 ("Unable to mount /proc: "); */ ++ } ++ else ++ do_proc_mounts = 1; + } + +- /* We map our original GID to the same GID in the container so we +- can own our own files normally. */ +- GMAP = open ("/proc/self/gid_map", O_WRONLY); +- if (GMAP < 0) +- FAIL_EXIT1 ("can't write to /proc/self/gid_map\n"); +- +- sprintf (tmp, "%lld %lld 1\n", +- (long long) (be_su ? 0 : original_gid), (long long) original_gid); +- write (GMAP, tmp, strlen (tmp)); +- xclose (GMAP); ++ if (do_proc_mounts) ++ { ++ /* We map our original UID to the same UID in the container so we ++ can own our own files normally. */ ++ UMAP = open ("/proc/self/uid_map", O_WRONLY); ++ if (UMAP < 0) ++ FAIL_EXIT1 ("can't write to /proc/self/uid_map\n"); ++ ++ sprintf (tmp, "%lld %lld 1\n", ++ (long long) (be_su ? 0 : original_uid), (long long) original_uid); ++ write (UMAP, tmp, strlen (tmp)); ++ xclose (UMAP); ++ ++ /* We must disable setgroups () before we can map our groups, else we ++ get EPERM. */ ++ GMAP = open ("/proc/self/setgroups", O_WRONLY); ++ if (GMAP >= 0) ++ { ++ /* We support kernels old enough to not have this. */ ++ write (GMAP, "deny\n", 5); ++ xclose (GMAP); ++ } ++ ++ /* We map our original GID to the same GID in the container so we ++ can own our own files normally. */ ++ GMAP = open ("/proc/self/gid_map", O_WRONLY); ++ if (GMAP < 0) ++ FAIL_EXIT1 ("can't write to /proc/self/gid_map\n"); ++ ++ sprintf (tmp, "%lld %lld 1\n", ++ (long long) (be_su ? 0 : original_gid), (long long) original_gid); ++ write (GMAP, tmp, strlen (tmp)); ++ xclose (GMAP); ++ } + + if (change_cwd) + { +diff -Nuar a/support/timespec.h b/support/timespec.h +--- a/support/timespec.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/timespec.h 2025-10-20 21:02:21.000000000 +0300 +@@ -57,6 +57,8 @@ + struct timespec observed, + double lower_bound, double upper_bound); + ++struct timespec dtotimespec (double sec) __attribute__((const)); ++ + #else + struct timespec __REDIRECT (timespec_add, (struct timespec, struct timespec), + timespec_add_time64); +@@ -82,6 +84,8 @@ + double lower_bound, + double upper_bound), + support_timespec_check_in_range_time64); ++ ++struct timespec __REDIRECT (dtotimespec, (double sec), dtotimespec_time64); + #endif + + /* Check that the timespec on the left represents a time before the +diff -Nuar a/support/xpthread_cond_signal.c b/support/xpthread_cond_signal.c +--- a/support/xpthread_cond_signal.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/support/xpthread_cond_signal.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,26 @@ ++/* pthread_cond_signal with error checking. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++ ++void ++xpthread_cond_signal (pthread_cond_t *cond) ++{ ++ xpthread_check_return ++ ("pthread_cond_signal", pthread_cond_signal (cond)); ++} +diff -Nuar a/support/xstdlib.h b/support/xstdlib.h +--- a/support/xstdlib.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/support/xstdlib.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,31 @@ ++/* Error-checking wrappers for stdlib functions. ++ Copyright (C) 2023 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 ++ . */ ++ ++#ifndef SUPPORT_XSTDLIB_H ++#define SUPPORT_XSTDLIB_H ++ ++#include ++#include ++ ++__BEGIN_DECLS ++ ++void xsystem (const char *cmd); ++ ++__END_DECLS ++ ++#endif /* SUPPORT_XSTDLIB_H */ +diff -Nuar a/support/xsystem.c b/support/xsystem.c +--- a/support/xsystem.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/support/xsystem.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,37 @@ ++/* Error-checking replacement for "system". ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++#include ++ ++#include ++ ++void ++xsystem (const char *cmd) ++{ ++ int ret = system (cmd); ++ ++ if (ret == 0 && cmd == NULL) ++ FAIL_EXIT1 ("Unable to spawn a shell for NULL command"); ++ ++ if (ret == 127) ++ FAIL_EXIT1 ("Child terminated with status 127"); ++ ++ if (ret < 0) ++ FAIL_EXIT1 ("system (\"%s\")", cmd); ++} +diff -Nuar a/support/xthread.h b/support/xthread.h +--- a/support/xthread.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/support/xthread.h 2025-10-20 21:02:21.000000000 +0300 +@@ -62,6 +62,7 @@ + void xpthread_spin_lock (pthread_spinlock_t *lock); + void xpthread_spin_unlock (pthread_spinlock_t *lock); + void xpthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex); ++void xpthread_cond_signal (pthread_cond_t *cond); + pthread_t xpthread_create (pthread_attr_t *attr, + void *(*thread_func) (void *), void *closure); + void xpthread_detach (pthread_t thr); +diff -Nuar a/sysdeps/aarch64/configure b/sysdeps/aarch64/configure +--- a/sysdeps/aarch64/configure 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/configure 2025-10-20 21:02:21.000000000 +0300 +@@ -308,13 +308,14 @@ + # Check if asm support armv8.2-a+sve + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SVE support in assembler" >&5 + $as_echo_n "checking for SVE support in assembler... " >&6; } +-if ${libc_cv_asm_sve+:} false; then : ++if ${libc_cv_aarch64_sve_asm+:} false; then : + $as_echo_n "(cached) " >&6 + else + cat > conftest.s <<\EOF +- ptrue p0.b ++ .arch armv8.2-a+sve ++ ptrue p0.b + EOF +-if { ac_try='${CC-cc} -c -march=armv8.2-a+sve conftest.s 1>&5' ++if { ac_try='${CC-cc} -c conftest.s 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? +@@ -326,8 +327,8 @@ + fi + rm -f conftest* + fi +-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_asm_sve" >&5 +-$as_echo "$libc_cv_asm_sve" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_aarch64_sve_asm" >&5 ++$as_echo "$libc_cv_aarch64_sve_asm" >&6; } + if test $libc_cv_aarch64_sve_asm = yes; then + $as_echo "#define HAVE_AARCH64_SVE_ASM 1" >>confdefs.h + +diff -Nuar a/sysdeps/aarch64/configure.ac b/sysdeps/aarch64/configure.ac +--- a/sysdeps/aarch64/configure.ac 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/configure.ac 2025-10-20 21:02:21.000000000 +0300 +@@ -92,11 +92,12 @@ + LIBC_CONFIG_VAR([aarch64-variant-pcs], [$libc_cv_aarch64_variant_pcs]) + + # Check if asm support armv8.2-a+sve +-AC_CACHE_CHECK(for SVE support in assembler, libc_cv_asm_sve, [dnl ++AC_CACHE_CHECK([for SVE support in assembler], [libc_cv_aarch64_sve_asm], [dnl + cat > conftest.s <<\EOF +- ptrue p0.b ++ .arch armv8.2-a+sve ++ ptrue p0.b + EOF +-if AC_TRY_COMMAND(${CC-cc} -c -march=armv8.2-a+sve conftest.s 1>&AS_MESSAGE_LOG_FD); then ++if AC_TRY_COMMAND(${CC-cc} -c conftest.s 1>&AS_MESSAGE_LOG_FD); then + libc_cv_aarch64_sve_asm=yes + else + libc_cv_aarch64_sve_asm=no +diff -Nuar a/sysdeps/aarch64/dl-trampoline.S b/sysdeps/aarch64/dl-trampoline.S +--- a/sysdeps/aarch64/dl-trampoline.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/dl-trampoline.S 2025-10-20 21:02:21.000000000 +0300 +@@ -298,12 +298,11 @@ + stp x2, x3, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*1] + stp x4, x5, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*2] + stp x6, x7, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*3] +- str x8, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*4] + stp q0, q1, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*0] + stp q2, q3, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*1] + stp q4, q5, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*2] + stp q6, q7, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*3] +- str xzr, [X29, #OFFSET_RV + DL_OFFSET_RG_VPCS] ++ str xzr, [X29, #OFFSET_RV + DL_OFFSET_RV_VPCS] + + /* Setup call to pltexit */ + ldp x0, x1, [x29, #OFFSET_SAVED_CALL_X0] +@@ -315,7 +314,6 @@ + ldp x2, x3, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*1] + ldp x4, x5, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*2] + ldp x6, x7, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*3] +- ldr x8, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*4] + ldp q0, q1, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*0] + ldp q2, q3, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*1] + ldp q4, q5, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*2] +diff -Nuar a/sysdeps/aarch64/memchr.S b/sysdeps/aarch64/memchr.S +--- a/sysdeps/aarch64/memchr.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/memchr.S 2025-10-20 21:02:21.000000000 +0300 +@@ -30,7 +30,6 @@ + # define MEMCHR __memchr + #endif + +-/* Arguments and results. */ + #define srcin x0 + #define chrin w1 + #define cntin x2 +@@ -41,24 +40,21 @@ + #define synd x5 + #define shift x6 + #define tmp x7 +-#define wtmp w7 + + #define vrepchr v0 + #define qdata q1 + #define vdata v1 + #define vhas_chr v2 +-#define vrepmask v3 +-#define vend v4 +-#define dend d4 ++#define vend v3 ++#define dend d3 + + /* + Core algorithm: +- For each 16-byte chunk we calculate a 64-bit syndrome value with four bits +- per byte. For even bytes, bits 0-3 are set if the relevant byte matched the +- requested character or the byte is NUL. Bits 4-7 must be zero. Bits 4-7 are +- set likewise for odd bytes so that adjacent bytes can be merged. Since the +- bits in the syndrome reflect the order in which things occur in the original +- string, counting trailing zeros identifies exactly which byte matched. */ ++ For each 16-byte chunk we calculate a 64-bit nibble mask value with four bits ++ per byte. We take 4 bits of every comparison byte with shift right and narrow ++ by 4 instruction. Since the bits in the nibble mask reflect the order in ++ which things occur in the original string, counting leading zeros identifies ++ exactly which byte matched. */ + + ENTRY (MEMCHR) + PTR_ARG (0) +@@ -67,55 +63,53 @@ + cbz cntin, L(nomatch) + ld1 {vdata.16b}, [src] + dup vrepchr.16b, chrin +- mov wtmp, 0xf00f +- dup vrepmask.8h, wtmp + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b + lsl shift, srcin, 2 +- and vhas_chr.16b, vhas_chr.16b, vrepmask.16b +- addp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ ++ shrn vend.8b, vhas_chr.8h, 4 /* 128->64 */ + fmov synd, dend + lsr synd, synd, shift + cbz synd, L(start_loop) + + rbit synd, synd + clz synd, synd +- add result, srcin, synd, lsr 2 + cmp cntin, synd, lsr 2 ++ add result, srcin, synd, lsr 2 + csel result, result, xzr, hi + ret + ++ .p2align 3 + L(start_loop): + sub tmp, src, srcin +- add tmp, tmp, 16 ++ add tmp, tmp, 17 + subs cntrem, cntin, tmp +- b.ls L(nomatch) ++ b.lo L(nomatch) + + /* Make sure that it won't overread by a 16-byte chunk */ +- add tmp, cntrem, 15 +- tbnz tmp, 4, L(loop32_2) +- ++ tbz cntrem, 4, L(loop32_2) ++ sub src, src, 16 + .p2align 4 + L(loop32): +- ldr qdata, [src, 16]! ++ ldr qdata, [src, 32]! + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b + umaxp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ + fmov synd, dend + cbnz synd, L(end) + + L(loop32_2): +- ldr qdata, [src, 16]! +- subs cntrem, cntrem, 32 ++ ldr qdata, [src, 16] + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b +- b.ls L(end) ++ subs cntrem, cntrem, 32 ++ b.lo L(end_2) + umaxp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ + fmov synd, dend + cbz synd, L(loop32) ++L(end_2): ++ add src, src, 16 + L(end): +- and vhas_chr.16b, vhas_chr.16b, vrepmask.16b +- addp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ ++ shrn vend.8b, vhas_chr.8h, 4 /* 128->64 */ ++ sub cntrem, src, srcin + fmov synd, dend +- add tmp, srcin, cntin +- sub cntrem, tmp, src ++ sub cntrem, cntin, cntrem + #ifndef __AARCH64EB__ + rbit synd, synd + #endif +diff -Nuar a/sysdeps/aarch64/memcpy.S b/sysdeps/aarch64/memcpy.S +--- a/sysdeps/aarch64/memcpy.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/memcpy.S 2025-10-20 21:02:21.000000000 +0300 +@@ -1,4 +1,5 @@ +-/* Copyright (C) 2012-2022 Free Software Foundation, Inc. ++/* Generic optimized memcpy using SIMD. ++ Copyright (C) 2012-2022 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + +@@ -20,7 +21,7 @@ + + /* Assumptions: + * +- * ARMv8-a, AArch64, unaligned accesses. ++ * ARMv8-a, AArch64, Advanced SIMD, unaligned accesses. + * + */ + +@@ -36,21 +37,18 @@ + #define B_l x8 + #define B_lw w8 + #define B_h x9 +-#define C_l x10 + #define C_lw w10 +-#define C_h x11 +-#define D_l x12 +-#define D_h x13 +-#define E_l x14 +-#define E_h x15 +-#define F_l x16 +-#define F_h x17 +-#define G_l count +-#define G_h dst +-#define H_l src +-#define H_h srcend + #define tmp1 x14 + ++#define A_q q0 ++#define B_q q1 ++#define C_q q2 ++#define D_q q3 ++#define E_q q4 ++#define F_q q5 ++#define G_q q6 ++#define H_q q7 ++ + #ifndef MEMMOVE + # define MEMMOVE memmove + #endif +@@ -69,10 +67,9 @@ + Large copies use a software pipelined loop processing 64 bytes per + iteration. The destination pointer is 16-byte aligned to minimize + unaligned accesses. The loop tail is handled by always copying 64 bytes +- from the end. +-*/ ++ from the end. */ + +-ENTRY_ALIGN (MEMCPY, 6) ++ENTRY (MEMCPY) + PTR_ARG (0) + PTR_ARG (1) + SIZE_ARG (2) +@@ -87,10 +84,10 @@ + /* Small copies: 0..32 bytes. */ + cmp count, 16 + b.lo L(copy16) +- ldp A_l, A_h, [src] +- ldp D_l, D_h, [srcend, -16] +- stp A_l, A_h, [dstin] +- stp D_l, D_h, [dstend, -16] ++ ldr A_q, [src] ++ ldr B_q, [srcend, -16] ++ str A_q, [dstin] ++ str B_q, [dstend, -16] + ret + + /* Copy 8-15 bytes. */ +@@ -102,7 +99,6 @@ + str A_h, [dstend, -8] + ret + +- .p2align 3 + /* Copy 4-7 bytes. */ + L(copy8): + tbz count, 2, L(copy4) +@@ -128,87 +124,69 @@ + .p2align 4 + /* Medium copies: 33..128 bytes. */ + L(copy32_128): +- ldp A_l, A_h, [src] +- ldp B_l, B_h, [src, 16] +- ldp C_l, C_h, [srcend, -32] +- ldp D_l, D_h, [srcend, -16] ++ ldp A_q, B_q, [src] ++ ldp C_q, D_q, [srcend, -32] + cmp count, 64 + b.hi L(copy128) +- stp A_l, A_h, [dstin] +- stp B_l, B_h, [dstin, 16] +- stp C_l, C_h, [dstend, -32] +- stp D_l, D_h, [dstend, -16] ++ stp A_q, B_q, [dstin] ++ stp C_q, D_q, [dstend, -32] + ret + + .p2align 4 + /* Copy 65..128 bytes. */ + L(copy128): +- ldp E_l, E_h, [src, 32] +- ldp F_l, F_h, [src, 48] ++ ldp E_q, F_q, [src, 32] + cmp count, 96 + b.ls L(copy96) +- ldp G_l, G_h, [srcend, -64] +- ldp H_l, H_h, [srcend, -48] +- stp G_l, G_h, [dstend, -64] +- stp H_l, H_h, [dstend, -48] ++ ldp G_q, H_q, [srcend, -64] ++ stp G_q, H_q, [dstend, -64] + L(copy96): +- stp A_l, A_h, [dstin] +- stp B_l, B_h, [dstin, 16] +- stp E_l, E_h, [dstin, 32] +- stp F_l, F_h, [dstin, 48] +- stp C_l, C_h, [dstend, -32] +- stp D_l, D_h, [dstend, -16] ++ stp A_q, B_q, [dstin] ++ stp E_q, F_q, [dstin, 32] ++ stp C_q, D_q, [dstend, -32] + ret + +- .p2align 4 ++ /* Align loop64 below to 16 bytes. */ ++ nop ++ + /* Copy more than 128 bytes. */ + L(copy_long): +- /* Copy 16 bytes and then align dst to 16-byte alignment. */ +- ldp D_l, D_h, [src] +- and tmp1, dstin, 15 +- bic dst, dstin, 15 +- sub src, src, tmp1 ++ /* Copy 16 bytes and then align src to 16-byte alignment. */ ++ ldr D_q, [src] ++ and tmp1, src, 15 ++ bic src, src, 15 ++ sub dst, dstin, tmp1 + add count, count, tmp1 /* Count is now 16 too large. */ +- ldp A_l, A_h, [src, 16] +- stp D_l, D_h, [dstin] +- ldp B_l, B_h, [src, 32] +- ldp C_l, C_h, [src, 48] +- ldp D_l, D_h, [src, 64]! ++ ldp A_q, B_q, [src, 16] ++ str D_q, [dstin] ++ ldp C_q, D_q, [src, 48] + subs count, count, 128 + 16 /* Test and readjust count. */ + b.ls L(copy64_from_end) +- + L(loop64): +- stp A_l, A_h, [dst, 16] +- ldp A_l, A_h, [src, 16] +- stp B_l, B_h, [dst, 32] +- ldp B_l, B_h, [src, 32] +- stp C_l, C_h, [dst, 48] +- ldp C_l, C_h, [src, 48] +- stp D_l, D_h, [dst, 64]! +- ldp D_l, D_h, [src, 64]! ++ stp A_q, B_q, [dst, 16] ++ ldp A_q, B_q, [src, 80] ++ stp C_q, D_q, [dst, 48] ++ ldp C_q, D_q, [src, 112] ++ add src, src, 64 ++ add dst, dst, 64 + subs count, count, 64 + b.hi L(loop64) + + /* Write the last iteration and copy 64 bytes from the end. */ + L(copy64_from_end): +- ldp E_l, E_h, [srcend, -64] +- stp A_l, A_h, [dst, 16] +- ldp A_l, A_h, [srcend, -48] +- stp B_l, B_h, [dst, 32] +- ldp B_l, B_h, [srcend, -32] +- stp C_l, C_h, [dst, 48] +- ldp C_l, C_h, [srcend, -16] +- stp D_l, D_h, [dst, 64] +- stp E_l, E_h, [dstend, -64] +- stp A_l, A_h, [dstend, -48] +- stp B_l, B_h, [dstend, -32] +- stp C_l, C_h, [dstend, -16] ++ ldp E_q, F_q, [srcend, -64] ++ stp A_q, B_q, [dst, 16] ++ ldp A_q, B_q, [srcend, -32] ++ stp C_q, D_q, [dst, 48] ++ stp E_q, F_q, [dstend, -64] ++ stp A_q, B_q, [dstend, -32] + ret + + END (MEMCPY) + libc_hidden_builtin_def (MEMCPY) + +-ENTRY_ALIGN (MEMMOVE, 4) ++ ++ENTRY (MEMMOVE) + PTR_ARG (0) + PTR_ARG (1) + SIZE_ARG (2) +@@ -220,64 +198,56 @@ + cmp count, 32 + b.hi L(copy32_128) + +- /* Small copies: 0..32 bytes. */ ++ /* Small moves: 0..32 bytes. */ + cmp count, 16 + b.lo L(copy16) +- ldp A_l, A_h, [src] +- ldp D_l, D_h, [srcend, -16] +- stp A_l, A_h, [dstin] +- stp D_l, D_h, [dstend, -16] ++ ldr A_q, [src] ++ ldr B_q, [srcend, -16] ++ str A_q, [dstin] ++ str B_q, [dstend, -16] + ret + +- .p2align 4 + L(move_long): + /* Only use backward copy if there is an overlap. */ + sub tmp1, dstin, src +- cbz tmp1, L(copy0) ++ cbz tmp1, L(move0) + cmp tmp1, count + b.hs L(copy_long) + + /* Large backwards copy for overlapping copies. +- Copy 16 bytes and then align dst to 16-byte alignment. */ +- ldp D_l, D_h, [srcend, -16] +- and tmp1, dstend, 15 +- sub srcend, srcend, tmp1 ++ Copy 16 bytes and then align srcend to 16-byte alignment. */ ++L(copy_long_backwards): ++ ldr D_q, [srcend, -16] ++ and tmp1, srcend, 15 ++ bic srcend, srcend, 15 + sub count, count, tmp1 +- ldp A_l, A_h, [srcend, -16] +- stp D_l, D_h, [dstend, -16] +- ldp B_l, B_h, [srcend, -32] +- ldp C_l, C_h, [srcend, -48] +- ldp D_l, D_h, [srcend, -64]! ++ ldp A_q, B_q, [srcend, -32] ++ str D_q, [dstend, -16] ++ ldp C_q, D_q, [srcend, -64] + sub dstend, dstend, tmp1 + subs count, count, 128 + b.ls L(copy64_from_start) + + L(loop64_backwards): +- stp A_l, A_h, [dstend, -16] +- ldp A_l, A_h, [srcend, -16] +- stp B_l, B_h, [dstend, -32] +- ldp B_l, B_h, [srcend, -32] +- stp C_l, C_h, [dstend, -48] +- ldp C_l, C_h, [srcend, -48] +- stp D_l, D_h, [dstend, -64]! +- ldp D_l, D_h, [srcend, -64]! ++ str B_q, [dstend, -16] ++ str A_q, [dstend, -32] ++ ldp A_q, B_q, [srcend, -96] ++ str D_q, [dstend, -48] ++ str C_q, [dstend, -64]! ++ ldp C_q, D_q, [srcend, -128] ++ sub srcend, srcend, 64 + subs count, count, 64 + b.hi L(loop64_backwards) + + /* Write the last iteration and copy 64 bytes from the start. */ + L(copy64_from_start): +- ldp G_l, G_h, [src, 48] +- stp A_l, A_h, [dstend, -16] +- ldp A_l, A_h, [src, 32] +- stp B_l, B_h, [dstend, -32] +- ldp B_l, B_h, [src, 16] +- stp C_l, C_h, [dstend, -48] +- ldp C_l, C_h, [src] +- stp D_l, D_h, [dstend, -64] +- stp G_l, G_h, [dstin, 48] +- stp A_l, A_h, [dstin, 32] +- stp B_l, B_h, [dstin, 16] +- stp C_l, C_h, [dstin] ++ ldp E_q, F_q, [src, 32] ++ stp A_q, B_q, [dstend, -32] ++ ldp A_q, B_q, [src] ++ stp C_q, D_q, [dstend, -64] ++ stp E_q, F_q, [dstin, 32] ++ stp A_q, B_q, [dstin] ++L(move0): + ret + + END (MEMMOVE) +diff -Nuar a/sysdeps/aarch64/memrchr.S b/sysdeps/aarch64/memrchr.S +--- a/sysdeps/aarch64/memrchr.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/memrchr.S 2025-10-20 21:02:21.000000000 +0300 +@@ -26,7 +26,6 @@ + * MTE compatible. + */ + +-/* Arguments and results. */ + #define srcin x0 + #define chrin w1 + #define cntin x2 +@@ -37,7 +36,6 @@ + #define synd x5 + #define shift x6 + #define tmp x7 +-#define wtmp w7 + #define end x8 + #define endm1 x9 + +@@ -45,18 +43,16 @@ + #define qdata q1 + #define vdata v1 + #define vhas_chr v2 +-#define vrepmask v3 +-#define vend v4 +-#define dend d4 ++#define vend v3 ++#define dend d3 + + /* + Core algorithm: +- For each 16-byte chunk we calculate a 64-bit syndrome value with four bits +- per byte. For even bytes, bits 0-3 are set if the relevant byte matched the +- requested character or the byte is NUL. Bits 4-7 must be zero. Bits 4-7 are +- set likewise for odd bytes so that adjacent bytes can be merged. Since the +- bits in the syndrome reflect the order in which things occur in the original +- string, counting trailing zeros identifies exactly which byte matched. */ ++ For each 16-byte chunk we calculate a 64-bit nibble mask value with four bits ++ per byte. We take 4 bits of every comparison byte with shift right and narrow ++ by 4 instruction. Since the bits in the nibble mask reflect the order in ++ which things occur in the original string, counting leading zeros identifies ++ exactly which byte matched. */ + + ENTRY (__memrchr) + PTR_ARG (0) +@@ -67,12 +63,9 @@ + cbz cntin, L(nomatch) + ld1 {vdata.16b}, [src] + dup vrepchr.16b, chrin +- mov wtmp, 0xf00f +- dup vrepmask.8h, wtmp + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b + neg shift, end, lsl 2 +- and vhas_chr.16b, vhas_chr.16b, vrepmask.16b +- addp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ ++ shrn vend.8b, vhas_chr.8h, 4 /* 128->64 */ + fmov synd, dend + lsl synd, synd, shift + cbz synd, L(start_loop) +@@ -83,34 +76,36 @@ + csel result, result, xzr, hi + ret + ++ nop + L(start_loop): +- sub tmp, end, src +- subs cntrem, cntin, tmp ++ subs cntrem, src, srcin + b.ls L(nomatch) + + /* Make sure that it won't overread by a 16-byte chunk */ +- add tmp, cntrem, 15 +- tbnz tmp, 4, L(loop32_2) ++ sub cntrem, cntrem, 1 ++ tbz cntrem, 4, L(loop32_2) ++ add src, src, 16 + +- .p2align 4 ++ .p2align 5 + L(loop32): +- ldr qdata, [src, -16]! ++ ldr qdata, [src, -32]! + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b + umaxp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ + fmov synd, dend + cbnz synd, L(end) + + L(loop32_2): +- ldr qdata, [src, -16]! ++ ldr qdata, [src, -16] + subs cntrem, cntrem, 32 + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b +- b.ls L(end) ++ b.lo L(end_2) + umaxp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ + fmov synd, dend + cbz synd, L(loop32) ++L(end_2): ++ sub src, src, 16 + L(end): +- and vhas_chr.16b, vhas_chr.16b, vrepmask.16b +- addp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ ++ shrn vend.8b, vhas_chr.8h, 4 /* 128->64 */ + fmov synd, dend + + add tmp, src, 15 +diff -Nuar a/sysdeps/aarch64/memset.S b/sysdeps/aarch64/memset.S +--- a/sysdeps/aarch64/memset.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/memset.S 2025-10-20 21:02:21.000000000 +0300 +@@ -1,4 +1,5 @@ +-/* Copyright (C) 2012-2022 Free Software Foundation, Inc. ++/* Generic optimized memset using SIMD. ++ Copyright (C) 2012-2024 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + +@@ -17,7 +18,6 @@ + . */ + + #include +-#include "memset-reg.h" + + #ifndef MEMSET + # define MEMSET memset +@@ -25,167 +25,117 @@ + + /* Assumptions: + * +- * ARMv8-a, AArch64, unaligned accesses ++ * ARMv8-a, AArch64, Advanced SIMD, unaligned accesses. + * + */ + +-ENTRY_ALIGN (MEMSET, 6) ++#define dstin x0 ++#define val x1 ++#define valw w1 ++#define count x2 ++#define dst x3 ++#define dstend x4 ++#define zva_val x5 ++#define off x3 ++#define dstend2 x5 + ++ENTRY (MEMSET) + PTR_ARG (0) + SIZE_ARG (2) + + dup v0.16B, valw ++ cmp count, 16 ++ b.lo L(set_small) ++ + add dstend, dstin, count ++ cmp count, 64 ++ b.hs L(set_128) + +- cmp count, 96 +- b.hi L(set_long) +- cmp count, 16 +- b.hs L(set_medium) +- mov val, v0.D[0] ++ /* Set 16..63 bytes. */ ++ mov off, 16 ++ and off, off, count, lsr 1 ++ sub dstend2, dstend, off ++ str q0, [dstin] ++ str q0, [dstin, off] ++ str q0, [dstend2, -16] ++ str q0, [dstend, -16] ++ ret + ++ .p2align 4 + /* Set 0..15 bytes. */ +- tbz count, 3, 1f +- str val, [dstin] +- str val, [dstend, -8] +- ret +- nop +-1: tbz count, 2, 2f +- str valw, [dstin] +- str valw, [dstend, -4] ++L(set_small): ++ add dstend, dstin, count ++ cmp count, 4 ++ b.lo 2f ++ lsr off, count, 3 ++ sub dstend2, dstend, off, lsl 2 ++ str s0, [dstin] ++ str s0, [dstin, off, lsl 2] ++ str s0, [dstend2, -4] ++ str s0, [dstend, -4] + ret ++ ++ /* Set 0..3 bytes. */ + 2: cbz count, 3f ++ lsr off, count, 1 + strb valw, [dstin] +- tbz count, 1, 3f +- strh valw, [dstend, -2] ++ strb valw, [dstin, off] ++ strb valw, [dstend, -1] + 3: ret + +- /* Set 17..96 bytes. */ +-L(set_medium): +- str q0, [dstin] +- tbnz count, 6, L(set96) +- str q0, [dstend, -16] +- tbz count, 5, 1f +- str q0, [dstin, 16] +- str q0, [dstend, -32] +-1: ret +- + .p2align 4 +- /* Set 64..96 bytes. Write 64 bytes from the start and +- 32 bytes from the end. */ +-L(set96): +- str q0, [dstin, 16] ++L(set_128): ++ bic dst, dstin, 15 ++ cmp count, 128 ++ b.hi L(set_long) ++ stp q0, q0, [dstin] + stp q0, q0, [dstin, 32] ++ stp q0, q0, [dstend, -64] + stp q0, q0, [dstend, -32] + ret + +- .p2align 3 +- nop ++ .p2align 4 + L(set_long): +- and valw, valw, 255 +- bic dst, dstin, 15 + str q0, [dstin] +- cmp count, 256 +- ccmp valw, 0, 0, cs +- b.eq L(try_zva) +-L(no_zva): +- sub count, dstend, dst /* Count is 16 too large. */ +- sub dst, dst, 16 /* Dst is biased by -32. */ +- sub count, count, 64 + 16 /* Adjust count and bias for loop. */ +-1: stp q0, q0, [dst, 32] +- stp q0, q0, [dst, 64]! +-L(tail64): +- subs count, count, 64 +- b.hi 1b +-2: stp q0, q0, [dstend, -64] +- stp q0, q0, [dstend, -32] +- ret +- +-L(try_zva): +-#ifdef ZVA_MACRO +- zva_macro +-#else +- .p2align 3 +- mrs tmp1, dczid_el0 +- tbnz tmp1w, 4, L(no_zva) +- and tmp1w, tmp1w, 15 +- cmp tmp1w, 4 /* ZVA size is 64 bytes. */ +- b.ne L(zva_128) +- +- /* Write the first and last 64 byte aligned block using stp rather +- than using DC ZVA. This is faster on some cores. +- */ +-L(zva_64): + str q0, [dst, 16] ++ tst valw, 255 ++ b.ne L(no_zva) ++#ifndef ZVA64_ONLY ++ mrs zva_val, dczid_el0 ++ and zva_val, zva_val, 31 ++ cmp zva_val, 4 /* ZVA size is 64 bytes. */ ++ b.ne L(no_zva) ++#endif + stp q0, q0, [dst, 32] +- bic dst, dst, 63 +- stp q0, q0, [dst, 64] +- stp q0, q0, [dst, 96] +- sub count, dstend, dst /* Count is now 128 too large. */ +- sub count, count, 128+64+64 /* Adjust count and bias for loop. */ +- add dst, dst, 128 +- nop +-1: dc zva, dst +- add dst, dst, 64 +- subs count, count, 64 +- b.hi 1b +- stp q0, q0, [dst, 0] +- stp q0, q0, [dst, 32] ++ bic dst, dstin, 63 ++ sub count, dstend, dst /* Count is now 64 too large. */ ++ sub count, count, 64 + 64 /* Adjust count and bias for loop. */ ++ ++ /* Write last bytes before ZVA loop. */ + stp q0, q0, [dstend, -64] + stp q0, q0, [dstend, -32] ++ ++ .p2align 4 ++L(zva64_loop): ++ add dst, dst, 64 ++ dc zva, dst ++ subs count, count, 64 ++ b.hi L(zva64_loop) + ret + + .p2align 3 +-L(zva_128): +- cmp tmp1w, 5 /* ZVA size is 128 bytes. */ +- b.ne L(zva_other) +- +- str q0, [dst, 16] ++L(no_zva): ++ sub count, dstend, dst /* Count is 32 too large. */ ++ sub count, count, 64 + 32 /* Adjust count and bias for loop. */ ++L(no_zva_loop): + stp q0, q0, [dst, 32] + stp q0, q0, [dst, 64] +- stp q0, q0, [dst, 96] +- bic dst, dst, 127 +- sub count, dstend, dst /* Count is now 128 too large. */ +- sub count, count, 128+128 /* Adjust count and bias for loop. */ +- add dst, dst, 128 +-1: dc zva, dst +- add dst, dst, 128 +- subs count, count, 128 +- b.hi 1b +- stp q0, q0, [dstend, -128] +- stp q0, q0, [dstend, -96] ++ add dst, dst, 64 ++ subs count, count, 64 ++ b.hi L(no_zva_loop) + stp q0, q0, [dstend, -64] + stp q0, q0, [dstend, -32] + ret + +-L(zva_other): +- mov tmp2w, 4 +- lsl zva_lenw, tmp2w, tmp1w +- add tmp1, zva_len, 64 /* Max alignment bytes written. */ +- cmp count, tmp1 +- blo L(no_zva) +- +- sub tmp2, zva_len, 1 +- add tmp1, dst, zva_len +- add dst, dst, 16 +- subs count, tmp1, dst /* Actual alignment bytes to write. */ +- bic tmp1, tmp1, tmp2 /* Aligned dc zva start address. */ +- beq 2f +-1: stp q0, q0, [dst], 64 +- stp q0, q0, [dst, -32] +- subs count, count, 64 +- b.hi 1b +-2: mov dst, tmp1 +- sub count, dstend, tmp1 /* Remaining bytes to write. */ +- subs count, count, zva_len +- b.lo 4f +-3: dc zva, dst +- add dst, dst, zva_len +- subs count, count, zva_len +- b.hs 3b +-4: add count, count, zva_len +- sub dst, dst, 32 /* Bias dst for tail loop. */ +- b L(tail64) +-#endif +- + END (MEMSET) + libc_hidden_builtin_def (MEMSET) +diff -Nuar a/sysdeps/aarch64/multiarch/ifunc-impl-list.c b/sysdeps/aarch64/multiarch/ifunc-impl-list.c +--- a/sysdeps/aarch64/multiarch/ifunc-impl-list.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/ifunc-impl-list.c 2025-10-20 21:02:21.000000000 +0300 +@@ -25,7 +25,7 @@ + #include + + /* Maximum number of IFUNC implementations. */ +-#define MAX_IFUNC 7 ++#define MAX_IFUNC 8 + + size_t + __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, +@@ -41,30 +41,30 @@ + IFUNC_IMPL (i, name, memcpy, + IFUNC_IMPL_ADD (array, i, memcpy, 1, __memcpy_thunderx) + IFUNC_IMPL_ADD (array, i, memcpy, !bti, __memcpy_thunderx2) +- IFUNC_IMPL_ADD (array, i, memcpy, 1, __memcpy_falkor) +- IFUNC_IMPL_ADD (array, i, memcpy, 1, __memcpy_simd) + #if HAVE_AARCH64_SVE_ASM + IFUNC_IMPL_ADD (array, i, memcpy, sve, __memcpy_a64fx) ++ IFUNC_IMPL_ADD (array, i, memcpy, sve, __memcpy_sve) + #endif ++ IFUNC_IMPL_ADD (array, i, memcpy, mops, __memcpy_mops) + IFUNC_IMPL_ADD (array, i, memcpy, 1, __memcpy_generic)) + IFUNC_IMPL (i, name, memmove, + IFUNC_IMPL_ADD (array, i, memmove, 1, __memmove_thunderx) + IFUNC_IMPL_ADD (array, i, memmove, !bti, __memmove_thunderx2) +- IFUNC_IMPL_ADD (array, i, memmove, 1, __memmove_falkor) +- IFUNC_IMPL_ADD (array, i, memmove, 1, __memmove_simd) + #if HAVE_AARCH64_SVE_ASM + IFUNC_IMPL_ADD (array, i, memmove, sve, __memmove_a64fx) ++ IFUNC_IMPL_ADD (array, i, memmove, sve, __memmove_sve) + #endif ++ IFUNC_IMPL_ADD (array, i, memmove, mops, __memmove_mops) + IFUNC_IMPL_ADD (array, i, memmove, 1, __memmove_generic)) + IFUNC_IMPL (i, name, memset, +- /* Enable this on non-falkor processors too so that other cores +- can do a comparative analysis with __memset_generic. */ +- IFUNC_IMPL_ADD (array, i, memset, (zva_size == 64), __memset_falkor) +- IFUNC_IMPL_ADD (array, i, memset, (zva_size == 64), __memset_emag) ++ IFUNC_IMPL_ADD (array, i, memset, (zva_size == 64), __memset_zva64) ++ IFUNC_IMPL_ADD (array, i, memset, 1, __memset_emag) + IFUNC_IMPL_ADD (array, i, memset, 1, __memset_kunpeng) + #if HAVE_AARCH64_SVE_ASM +- IFUNC_IMPL_ADD (array, i, memset, sve, __memset_a64fx) ++ IFUNC_IMPL_ADD (array, i, memset, sve && !bti && zva_size == 256, __memset_a64fx) ++ IFUNC_IMPL_ADD (array, i, memset, sve && zva_size == 64, __memset_sve_zva64) + #endif ++ IFUNC_IMPL_ADD (array, i, memset, mops, __memset_mops) + IFUNC_IMPL_ADD (array, i, memset, 1, __memset_generic)) + IFUNC_IMPL (i, name, memchr, + IFUNC_IMPL_ADD (array, i, memchr, !mte, __memchr_nosimd) +@@ -72,7 +72,7 @@ + + IFUNC_IMPL (i, name, strlen, + IFUNC_IMPL_ADD (array, i, strlen, !mte, __strlen_asimd) +- IFUNC_IMPL_ADD (array, i, strlen, 1, __strlen_mte)) ++ IFUNC_IMPL_ADD (array, i, strlen, 1, __strlen_generic)) + + return i; + } +diff -Nuar a/sysdeps/aarch64/multiarch/init-arch.h b/sysdeps/aarch64/multiarch/init-arch.h +--- a/sysdeps/aarch64/multiarch/init-arch.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/init-arch.h 2025-10-20 21:02:21.000000000 +0300 +@@ -35,4 +35,8 @@ + bool __attribute__((unused)) mte = \ + MTE_ENABLED (); \ + bool __attribute__((unused)) sve = \ +- GLRO(dl_aarch64_cpu_features).sve; ++ GLRO(dl_aarch64_cpu_features).sve; \ ++ bool __attribute__((unused)) prefer_sve_ifuncs = \ ++ GLRO(dl_aarch64_cpu_features).prefer_sve_ifuncs; \ ++ bool __attribute__((unused)) mops = \ ++ GLRO(dl_aarch64_cpu_features).mops; +diff -Nuar a/sysdeps/aarch64/multiarch/Makefile b/sysdeps/aarch64/multiarch/Makefile +--- a/sysdeps/aarch64/multiarch/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -1,8 +1,22 @@ + ifeq ($(subdir),string) +-sysdep_routines += memcpy_generic memcpy_advsimd memcpy_thunderx memcpy_thunderx2 \ +- memcpy_falkor memcpy_a64fx \ +- memset_generic memset_falkor memset_emag memset_kunpeng \ +- memset_a64fx \ +- memchr_generic memchr_nosimd \ +- strlen_mte strlen_asimd ++sysdep_routines += \ ++ memchr_generic \ ++ memchr_nosimd \ ++ memcpy_a64fx \ ++ memcpy_generic \ ++ memcpy_mops \ ++ memcpy_sve \ ++ memcpy_thunderx \ ++ memcpy_thunderx2 \ ++ memmove_mops \ ++ memset_a64fx \ ++ memset_emag \ ++ memset_generic \ ++ memset_kunpeng \ ++ memset_mops \ ++ memset_sve_zva64 \ ++ memset_zva64 \ ++ strlen_asimd \ ++ strlen_generic \ ++# sysdep_routines + endif +diff -Nuar a/sysdeps/aarch64/multiarch/memchr_nosimd.S b/sysdeps/aarch64/multiarch/memchr_nosimd.S +--- a/sysdeps/aarch64/multiarch/memchr_nosimd.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memchr_nosimd.S 2025-10-20 21:02:21.000000000 +0300 +@@ -26,10 +26,6 @@ + * Use base integer registers. + */ + +-#ifndef MEMCHR +-# define MEMCHR __memchr_nosimd +-#endif +- + /* Arguments and results. */ + #define srcin x0 + #define chrin x1 +@@ -62,7 +58,7 @@ + #define REP8_7f 0x7f7f7f7f7f7f7f7f + + +-ENTRY_ALIGN (MEMCHR, 6) ++ENTRY (__memchr_nosimd) + + PTR_ARG (0) + SIZE_ARG (2) +@@ -219,5 +215,4 @@ + mov result, 0 + ret + +-END (MEMCHR) +-libc_hidden_builtin_def (MEMCHR) ++END (__memchr_nosimd) +diff -Nuar a/sysdeps/aarch64/multiarch/memcpy_a64fx.S b/sysdeps/aarch64/multiarch/memcpy_a64fx.S +--- a/sysdeps/aarch64/multiarch/memcpy_a64fx.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memcpy_a64fx.S 2025-10-20 21:02:21.000000000 +0300 +@@ -39,9 +39,6 @@ + #define vlen8 x8 + + #if HAVE_AARCH64_SVE_ASM +-# if IS_IN (libc) +-# define MEMCPY __memcpy_a64fx +-# define MEMMOVE __memmove_a64fx + + .arch armv8.2-a+sve + +@@ -97,7 +94,7 @@ + #undef BTI_C + #define BTI_C + +-ENTRY (MEMCPY) ++ENTRY (__memcpy_a64fx) + + PTR_ARG (0) + PTR_ARG (1) +@@ -234,11 +231,10 @@ + st1b z3.b, p0, [dstend, -1, mul vl] + ret + +-END (MEMCPY) +-libc_hidden_builtin_def (MEMCPY) ++END (__memcpy_a64fx) + + +-ENTRY_ALIGN (MEMMOVE, 4) ++ENTRY_ALIGN (__memmove_a64fx, 4) + + PTR_ARG (0) + PTR_ARG (1) +@@ -307,7 +303,5 @@ + mov dst, dstin + b L(last_bytes) + +-END (MEMMOVE) +-libc_hidden_builtin_def (MEMMOVE) +-# endif /* IS_IN (libc) */ ++END (__memmove_a64fx) + #endif /* HAVE_AARCH64_SVE_ASM */ +diff -Nuar a/sysdeps/aarch64/multiarch/memcpy_advsimd.S b/sysdeps/aarch64/multiarch/memcpy_advsimd.S +--- a/sysdeps/aarch64/multiarch/memcpy_advsimd.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memcpy_advsimd.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,248 +0,0 @@ +-/* Generic optimized memcpy using SIMD. +- Copyright (C) 2020-2022 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 +- . */ +- +-#include +- +-/* Assumptions: +- * +- * ARMv8-a, AArch64, Advanced SIMD, unaligned accesses. +- * +- */ +- +-#define dstin x0 +-#define src x1 +-#define count x2 +-#define dst x3 +-#define srcend x4 +-#define dstend x5 +-#define A_l x6 +-#define A_lw w6 +-#define A_h x7 +-#define B_l x8 +-#define B_lw w8 +-#define B_h x9 +-#define C_lw w10 +-#define tmp1 x14 +- +-#define A_q q0 +-#define B_q q1 +-#define C_q q2 +-#define D_q q3 +-#define E_q q4 +-#define F_q q5 +-#define G_q q6 +-#define H_q q7 +- +- +-/* This implementation supports both memcpy and memmove and shares most code. +- It uses unaligned accesses and branchless sequences to keep the code small, +- simple and improve performance. +- +- Copies are split into 3 main cases: small copies of up to 32 bytes, medium +- copies of up to 128 bytes, and large copies. The overhead of the overlap +- check in memmove is negligible since it is only required for large copies. +- +- Large copies use a software pipelined loop processing 64 bytes per +- iteration. The destination pointer is 16-byte aligned to minimize +- unaligned accesses. The loop tail is handled by always copying 64 bytes +- from the end. */ +- +-ENTRY (__memcpy_simd) +- PTR_ARG (0) +- PTR_ARG (1) +- SIZE_ARG (2) +- +- add srcend, src, count +- add dstend, dstin, count +- cmp count, 128 +- b.hi L(copy_long) +- cmp count, 32 +- b.hi L(copy32_128) +- +- /* Small copies: 0..32 bytes. */ +- cmp count, 16 +- b.lo L(copy16) +- ldr A_q, [src] +- ldr B_q, [srcend, -16] +- str A_q, [dstin] +- str B_q, [dstend, -16] +- ret +- +- /* Copy 8-15 bytes. */ +-L(copy16): +- tbz count, 3, L(copy8) +- ldr A_l, [src] +- ldr A_h, [srcend, -8] +- str A_l, [dstin] +- str A_h, [dstend, -8] +- ret +- +- /* Copy 4-7 bytes. */ +-L(copy8): +- tbz count, 2, L(copy4) +- ldr A_lw, [src] +- ldr B_lw, [srcend, -4] +- str A_lw, [dstin] +- str B_lw, [dstend, -4] +- ret +- +- /* Copy 0..3 bytes using a branchless sequence. */ +-L(copy4): +- cbz count, L(copy0) +- lsr tmp1, count, 1 +- ldrb A_lw, [src] +- ldrb C_lw, [srcend, -1] +- ldrb B_lw, [src, tmp1] +- strb A_lw, [dstin] +- strb B_lw, [dstin, tmp1] +- strb C_lw, [dstend, -1] +-L(copy0): +- ret +- +- .p2align 4 +- /* Medium copies: 33..128 bytes. */ +-L(copy32_128): +- ldp A_q, B_q, [src] +- ldp C_q, D_q, [srcend, -32] +- cmp count, 64 +- b.hi L(copy128) +- stp A_q, B_q, [dstin] +- stp C_q, D_q, [dstend, -32] +- ret +- +- .p2align 4 +- /* Copy 65..128 bytes. */ +-L(copy128): +- ldp E_q, F_q, [src, 32] +- cmp count, 96 +- b.ls L(copy96) +- ldp G_q, H_q, [srcend, -64] +- stp G_q, H_q, [dstend, -64] +-L(copy96): +- stp A_q, B_q, [dstin] +- stp E_q, F_q, [dstin, 32] +- stp C_q, D_q, [dstend, -32] +- ret +- +- /* Align loop64 below to 16 bytes. */ +- nop +- +- /* Copy more than 128 bytes. */ +-L(copy_long): +- /* Copy 16 bytes and then align src to 16-byte alignment. */ +- ldr D_q, [src] +- and tmp1, src, 15 +- bic src, src, 15 +- sub dst, dstin, tmp1 +- add count, count, tmp1 /* Count is now 16 too large. */ +- ldp A_q, B_q, [src, 16] +- str D_q, [dstin] +- ldp C_q, D_q, [src, 48] +- subs count, count, 128 + 16 /* Test and readjust count. */ +- b.ls L(copy64_from_end) +-L(loop64): +- stp A_q, B_q, [dst, 16] +- ldp A_q, B_q, [src, 80] +- stp C_q, D_q, [dst, 48] +- ldp C_q, D_q, [src, 112] +- add src, src, 64 +- add dst, dst, 64 +- subs count, count, 64 +- b.hi L(loop64) +- +- /* Write the last iteration and copy 64 bytes from the end. */ +-L(copy64_from_end): +- ldp E_q, F_q, [srcend, -64] +- stp A_q, B_q, [dst, 16] +- ldp A_q, B_q, [srcend, -32] +- stp C_q, D_q, [dst, 48] +- stp E_q, F_q, [dstend, -64] +- stp A_q, B_q, [dstend, -32] +- ret +- +-END (__memcpy_simd) +-libc_hidden_builtin_def (__memcpy_simd) +- +- +-ENTRY (__memmove_simd) +- PTR_ARG (0) +- PTR_ARG (1) +- SIZE_ARG (2) +- +- add srcend, src, count +- add dstend, dstin, count +- cmp count, 128 +- b.hi L(move_long) +- cmp count, 32 +- b.hi L(copy32_128) +- +- /* Small moves: 0..32 bytes. */ +- cmp count, 16 +- b.lo L(copy16) +- ldr A_q, [src] +- ldr B_q, [srcend, -16] +- str A_q, [dstin] +- str B_q, [dstend, -16] +- ret +- +-L(move_long): +- /* Only use backward copy if there is an overlap. */ +- sub tmp1, dstin, src +- cbz tmp1, L(move0) +- cmp tmp1, count +- b.hs L(copy_long) +- +- /* Large backwards copy for overlapping copies. +- Copy 16 bytes and then align srcend to 16-byte alignment. */ +-L(copy_long_backwards): +- ldr D_q, [srcend, -16] +- and tmp1, srcend, 15 +- bic srcend, srcend, 15 +- sub count, count, tmp1 +- ldp A_q, B_q, [srcend, -32] +- str D_q, [dstend, -16] +- ldp C_q, D_q, [srcend, -64] +- sub dstend, dstend, tmp1 +- subs count, count, 128 +- b.ls L(copy64_from_start) +- +-L(loop64_backwards): +- str B_q, [dstend, -16] +- str A_q, [dstend, -32] +- ldp A_q, B_q, [srcend, -96] +- str D_q, [dstend, -48] +- str C_q, [dstend, -64]! +- ldp C_q, D_q, [srcend, -128] +- sub srcend, srcend, 64 +- subs count, count, 64 +- b.hi L(loop64_backwards) +- +- /* Write the last iteration and copy 64 bytes from the start. */ +-L(copy64_from_start): +- ldp E_q, F_q, [src, 32] +- stp A_q, B_q, [dstend, -32] +- ldp A_q, B_q, [src] +- stp C_q, D_q, [dstend, -64] +- stp E_q, F_q, [dstin, 32] +- stp A_q, B_q, [dstin] +-L(move0): +- ret +- +-END (__memmove_simd) +-libc_hidden_builtin_def (__memmove_simd) +diff -Nuar a/sysdeps/aarch64/multiarch/memcpy.c b/sysdeps/aarch64/multiarch/memcpy.c +--- a/sysdeps/aarch64/multiarch/memcpy.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memcpy.c 2025-10-20 21:02:21.000000000 +0300 +@@ -29,31 +29,38 @@ + extern __typeof (__redirect_memcpy) __libc_memcpy; + + extern __typeof (__redirect_memcpy) __memcpy_generic attribute_hidden; +-extern __typeof (__redirect_memcpy) __memcpy_simd attribute_hidden; + extern __typeof (__redirect_memcpy) __memcpy_thunderx attribute_hidden; + extern __typeof (__redirect_memcpy) __memcpy_thunderx2 attribute_hidden; +-extern __typeof (__redirect_memcpy) __memcpy_falkor attribute_hidden; +-# if HAVE_AARCH64_SVE_ASM + extern __typeof (__redirect_memcpy) __memcpy_a64fx attribute_hidden; +-# endif ++extern __typeof (__redirect_memcpy) __memcpy_sve attribute_hidden; ++extern __typeof (__redirect_memcpy) __memcpy_mops attribute_hidden; ++ ++static inline __typeof (__redirect_memcpy) * ++select_memcpy_ifunc (void) ++{ ++ INIT_ARCH (); ++ ++ if (mops) ++ return __memcpy_mops; ++ ++ if (sve && HAVE_AARCH64_SVE_ASM) ++ { ++ if (IS_A64FX (midr)) ++ return __memcpy_a64fx; ++ return prefer_sve_ifuncs ? __memcpy_sve : __memcpy_generic; ++ } ++ ++ if (IS_THUNDERX (midr)) ++ return __memcpy_thunderx; ++ ++ if (IS_THUNDERX2 (midr) || IS_THUNDERX2PA (midr)) ++ return __memcpy_thunderx2; ++ ++ return __memcpy_generic; ++} ++ ++libc_ifunc (__libc_memcpy, select_memcpy_ifunc ()); + +-libc_ifunc (__libc_memcpy, +- (IS_THUNDERX (midr) +- ? __memcpy_thunderx +- : (IS_FALKOR (midr) || IS_PHECDA (midr) +- ? __memcpy_falkor +- : (IS_THUNDERX2 (midr) || IS_THUNDERX2PA (midr) +- ? __memcpy_thunderx2 +- : (IS_NEOVERSE_N1 (midr) || IS_NEOVERSE_N2 (midr) +- || IS_NEOVERSE_V1 (midr) +- ? __memcpy_simd +-# if HAVE_AARCH64_SVE_ASM +- : (IS_A64FX (midr) && sve +- ? __memcpy_a64fx +- : __memcpy_generic)))))); +-# else +- : __memcpy_generic))))); +-# endif + # undef memcpy + strong_alias (__libc_memcpy, memcpy); + #endif +diff -Nuar a/sysdeps/aarch64/multiarch/memcpy_falkor.S b/sysdeps/aarch64/multiarch/memcpy_falkor.S +--- a/sysdeps/aarch64/multiarch/memcpy_falkor.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memcpy_falkor.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,315 +0,0 @@ +-/* Optimized memcpy for Qualcomm Falkor processor. +- Copyright (C) 2017-2022 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 +- . */ +- +-#include +- +-/* Assumptions: +- +- ARMv8-a, AArch64, falkor, unaligned accesses. */ +- +-#define dstin x0 +-#define src x1 +-#define count x2 +-#define dst x3 +-#define srcend x4 +-#define dstend x5 +-#define tmp1 x14 +-#define A_x x6 +-#define B_x x7 +-#define A_w w6 +-#define B_w w7 +- +-#define A_q q0 +-#define B_q q1 +-#define C_q q2 +-#define D_q q3 +-#define E_q q4 +-#define F_q q5 +-#define G_q q6 +-#define H_q q7 +-#define Q_q q6 +-#define S_q q22 +- +-/* Copies are split into 3 main cases: +- +- 1. Small copies of up to 32 bytes +- 2. Medium copies of 33..128 bytes which are fully unrolled +- 3. Large copies of more than 128 bytes. +- +- Large copies align the source to a quad word and use an unrolled loop +- processing 64 bytes per iteration. +- +- FALKOR-SPECIFIC DESIGN: +- +- The smallest copies (32 bytes or less) focus on optimal pipeline usage, +- which is why the redundant copies of 0-3 bytes have been replaced with +- conditionals, since the former would unnecessarily break across multiple +- issue groups. The medium copy group has been enlarged to 128 bytes since +- bumping up the small copies up to 32 bytes allows us to do that without +- cost and also allows us to reduce the size of the prep code before loop64. +- +- The copy loop uses only one register q0. This is to ensure that all loads +- hit a single hardware prefetcher which can get correctly trained to prefetch +- a single stream. +- +- The non-temporal stores help optimize cache utilization. */ +- +-#if IS_IN (libc) +-ENTRY_ALIGN (__memcpy_falkor, 6) +- +- PTR_ARG (0) +- PTR_ARG (1) +- SIZE_ARG (2) +- +- cmp count, 32 +- add srcend, src, count +- add dstend, dstin, count +- b.ls L(copy32) +- cmp count, 128 +- b.hi L(copy_long) +- +- /* Medium copies: 33..128 bytes. */ +-L(copy128): +- sub tmp1, count, 1 +- ldr A_q, [src] +- ldr B_q, [src, 16] +- ldr C_q, [srcend, -32] +- ldr D_q, [srcend, -16] +- tbz tmp1, 6, 1f +- ldr E_q, [src, 32] +- ldr F_q, [src, 48] +- ldr G_q, [srcend, -64] +- ldr H_q, [srcend, -48] +- str G_q, [dstend, -64] +- str H_q, [dstend, -48] +- str E_q, [dstin, 32] +- str F_q, [dstin, 48] +-1: +- str A_q, [dstin] +- str B_q, [dstin, 16] +- str C_q, [dstend, -32] +- str D_q, [dstend, -16] +- ret +- +- .p2align 4 +- /* Small copies: 0..32 bytes. */ +-L(copy32): +- /* 16-32 */ +- cmp count, 16 +- b.lo 1f +- ldr A_q, [src] +- ldr B_q, [srcend, -16] +- str A_q, [dstin] +- str B_q, [dstend, -16] +- ret +- .p2align 4 +-1: +- /* 8-15 */ +- tbz count, 3, 1f +- ldr A_x, [src] +- ldr B_x, [srcend, -8] +- str A_x, [dstin] +- str B_x, [dstend, -8] +- ret +- .p2align 4 +-1: +- /* 4-7 */ +- tbz count, 2, 1f +- ldr A_w, [src] +- ldr B_w, [srcend, -4] +- str A_w, [dstin] +- str B_w, [dstend, -4] +- ret +- .p2align 4 +-1: +- /* 2-3 */ +- tbz count, 1, 1f +- ldrh A_w, [src] +- ldrh B_w, [srcend, -2] +- strh A_w, [dstin] +- strh B_w, [dstend, -2] +- ret +- .p2align 4 +-1: +- /* 0-1 */ +- tbz count, 0, 1f +- ldrb A_w, [src] +- strb A_w, [dstin] +-1: +- ret +- +- /* Align SRC to 16 bytes and copy; that way at least one of the +- accesses is aligned throughout the copy sequence. +- +- The count is off by 0 to 15 bytes, but this is OK because we trim +- off the last 64 bytes to copy off from the end. Due to this the +- loop never runs out of bounds. */ +- +- .p2align 4 +- nop /* Align loop64 below. */ +-L(copy_long): +- ldr A_q, [src] +- sub count, count, 64 + 16 +- and tmp1, src, 15 +- str A_q, [dstin] +- bic src, src, 15 +- sub dst, dstin, tmp1 +- add count, count, tmp1 +- +-L(loop64): +- ldr A_q, [src, 16]! +- str A_q, [dst, 16] +- ldr A_q, [src, 16]! +- subs count, count, 64 +- str A_q, [dst, 32] +- ldr A_q, [src, 16]! +- str A_q, [dst, 48] +- ldr A_q, [src, 16]! +- str A_q, [dst, 64]! +- b.hi L(loop64) +- +- /* Write the last full set of 64 bytes. The remainder is at most 64 +- bytes, so it is safe to always copy 64 bytes from the end even if +- there is just 1 byte left. */ +- ldr E_q, [srcend, -64] +- str E_q, [dstend, -64] +- ldr D_q, [srcend, -48] +- str D_q, [dstend, -48] +- ldr C_q, [srcend, -32] +- str C_q, [dstend, -32] +- ldr B_q, [srcend, -16] +- str B_q, [dstend, -16] +- ret +- +-END (__memcpy_falkor) +-libc_hidden_builtin_def (__memcpy_falkor) +- +- +-/* RATIONALE: +- +- The move has 4 distinct parts: +- * Small moves of 32 bytes and under. +- * Medium sized moves of 33-128 bytes (fully unrolled). +- * Large moves where the source address is higher than the destination +- (forward copies) +- * Large moves where the destination address is higher than the source +- (copy backward, or move). +- +- We use only two registers q6 and q22 for the moves and move 32 bytes at a +- time to correctly train the hardware prefetcher for better throughput. +- +- For small and medium cases memcpy is used. */ +- +-ENTRY_ALIGN (__memmove_falkor, 6) +- +- PTR_ARG (0) +- PTR_ARG (1) +- SIZE_ARG (2) +- +- cmp count, 32 +- add srcend, src, count +- add dstend, dstin, count +- b.ls L(copy32) +- cmp count, 128 +- b.ls L(copy128) +- sub tmp1, dstin, src +- ccmp tmp1, count, 2, hi +- b.lo L(move_long) +- +- /* CASE: Copy Forwards +- +- Align src to 16 byte alignment so that we don't cross cache line +- boundaries on both loads and stores. There are at least 128 bytes +- to copy, so copy 16 bytes unaligned and then align. The loop +- copies 32 bytes per iteration and prefetches one iteration ahead. */ +- +- ldr S_q, [src] +- and tmp1, src, 15 +- bic src, src, 15 +- sub dst, dstin, tmp1 +- add count, count, tmp1 /* Count is now 16 too large. */ +- ldr Q_q, [src, 16]! +- str S_q, [dstin] +- ldr S_q, [src, 16]! +- sub count, count, 32 + 32 + 16 /* Test and readjust count. */ +- +- .p2align 4 +-1: +- subs count, count, 32 +- str Q_q, [dst, 16] +- ldr Q_q, [src, 16]! +- str S_q, [dst, 32]! +- ldr S_q, [src, 16]! +- b.hi 1b +- +- /* Copy 32 bytes from the end before writing the data prefetched in the +- last loop iteration. */ +-2: +- ldr B_q, [srcend, -32] +- ldr C_q, [srcend, -16] +- str Q_q, [dst, 16] +- str S_q, [dst, 32] +- str B_q, [dstend, -32] +- str C_q, [dstend, -16] +- ret +- +- /* CASE: Copy Backwards +- +- Align srcend to 16 byte alignment so that we don't cross cache line +- boundaries on both loads and stores. There are at least 128 bytes +- to copy, so copy 16 bytes unaligned and then align. The loop +- copies 32 bytes per iteration and prefetches one iteration ahead. */ +- +- .p2align 4 +- nop +- nop +-L(move_long): +- cbz tmp1, 3f /* Return early if src == dstin */ +- ldr S_q, [srcend, -16] +- and tmp1, srcend, 15 +- sub srcend, srcend, tmp1 +- ldr Q_q, [srcend, -16]! +- str S_q, [dstend, -16] +- sub count, count, tmp1 +- ldr S_q, [srcend, -16]! +- sub dstend, dstend, tmp1 +- sub count, count, 32 + 32 +- +-1: +- subs count, count, 32 +- str Q_q, [dstend, -16] +- ldr Q_q, [srcend, -16]! +- str S_q, [dstend, -32]! +- ldr S_q, [srcend, -16]! +- b.hi 1b +- +- /* Copy 32 bytes from the start before writing the data prefetched in the +- last loop iteration. */ +- +- ldr B_q, [src, 16] +- ldr C_q, [src] +- str Q_q, [dstend, -16] +- str S_q, [dstend, -32] +- str B_q, [dstin, 16] +- str C_q, [dstin] +-3: ret +- +-END (__memmove_falkor) +-libc_hidden_builtin_def (__memmove_falkor) +-#endif +diff -Nuar a/sysdeps/aarch64/multiarch/memcpy_mops.S b/sysdeps/aarch64/multiarch/memcpy_mops.S +--- a/sysdeps/aarch64/multiarch/memcpy_mops.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/aarch64/multiarch/memcpy_mops.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,39 @@ ++/* Optimized memcpy for MOPS. ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++ ++/* Assumptions: ++ * ++ * AArch64, MOPS. ++ * ++ */ ++ ++ENTRY (__memcpy_mops) ++ PTR_ARG (0) ++ PTR_ARG (1) ++ SIZE_ARG (2) ++ ++ mov x3, x0 ++ .inst 0x19010443 /* cpyfp [x3]!, [x1]!, x2! */ ++ .inst 0x19410443 /* cpyfm [x3]!, [x1]!, x2! */ ++ .inst 0x19810443 /* cpyfe [x3]!, [x1]!, x2! */ ++ ret ++ ++END (__memcpy_mops) +diff -Nuar a/sysdeps/aarch64/multiarch/memcpy_sve.S b/sysdeps/aarch64/multiarch/memcpy_sve.S +--- a/sysdeps/aarch64/multiarch/memcpy_sve.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/aarch64/multiarch/memcpy_sve.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,210 @@ ++/* Optimized memcpy for SVE. ++ Copyright (C) 2021-2022 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 ++ . */ ++ ++#include ++ ++/* Assumptions: ++ * ++ * ARMv8-a, AArch64, Advanced SIMD, SVE, unaligned accesses. ++ * ++ */ ++ ++#define dstin x0 ++#define src x1 ++#define count x2 ++#define dst x3 ++#define srcend x4 ++#define dstend x5 ++#define tmp1 x6 ++#define vlen x6 ++ ++#define A_q q0 ++#define B_q q1 ++#define C_q q2 ++#define D_q q3 ++#define E_q q4 ++#define F_q q5 ++#define G_q q6 ++#define H_q q7 ++ ++/* This implementation supports both memcpy and memmove and shares most code. ++ It uses unaligned accesses and branchless sequences to keep the code small, ++ simple and improve performance. ++ ++ Copies are split into 3 main cases: small copies of up to 32 bytes, medium ++ copies of up to 128 bytes, and large copies. The overhead of the overlap ++ check in memmove is negligible since it is only required for large copies. ++ ++ Large copies use a software pipelined loop processing 64 bytes per iteration. ++ The source pointer is 16-byte aligned to minimize unaligned accesses. ++ The loop tail is handled by always copying 64 bytes from the end. ++*/ ++ ++#if HAVE_AARCH64_SVE_ASM ++ ++ .arch armv8.2-a+sve ++ ++ENTRY (__memcpy_sve) ++ PTR_ARG (0) ++ PTR_ARG (1) ++ SIZE_ARG (2) ++ ++ cmp count, 128 ++ b.hi L(copy_long) ++ cntb vlen ++ cmp count, vlen, lsl 1 ++ b.hi L(copy32_128) ++ whilelo p0.b, xzr, count ++ whilelo p1.b, vlen, count ++ ld1b z0.b, p0/z, [src, 0, mul vl] ++ ld1b z1.b, p1/z, [src, 1, mul vl] ++ st1b z0.b, p0, [dstin, 0, mul vl] ++ st1b z1.b, p1, [dstin, 1, mul vl] ++ ret ++ ++ /* Medium copies: 33..128 bytes. */ ++L(copy32_128): ++ add srcend, src, count ++ add dstend, dstin, count ++ ldp A_q, B_q, [src] ++ ldp C_q, D_q, [srcend, -32] ++ cmp count, 64 ++ b.hi L(copy128) ++ stp A_q, B_q, [dstin] ++ stp C_q, D_q, [dstend, -32] ++ ret ++ ++ /* Copy 65..128 bytes. */ ++L(copy128): ++ ldp E_q, F_q, [src, 32] ++ cmp count, 96 ++ b.ls L(copy96) ++ ldp G_q, H_q, [srcend, -64] ++ stp G_q, H_q, [dstend, -64] ++L(copy96): ++ stp A_q, B_q, [dstin] ++ stp E_q, F_q, [dstin, 32] ++ stp C_q, D_q, [dstend, -32] ++ ret ++ ++ .p2align 4 ++ /* Copy more than 128 bytes. */ ++L(copy_long): ++ add srcend, src, count ++ add dstend, dstin, count ++ ++ /* Copy 16 bytes and then align src to 16-byte alignment. */ ++ ldr D_q, [src] ++ and tmp1, src, 15 ++ bic src, src, 15 ++ sub dst, dstin, tmp1 ++ add count, count, tmp1 /* Count is now 16 too large. */ ++ ldp A_q, B_q, [src, 16] ++ str D_q, [dstin] ++ ldp C_q, D_q, [src, 48] ++ subs count, count, 128 + 16 /* Test and readjust count. */ ++ b.ls L(copy64_from_end) ++L(loop64): ++ stp A_q, B_q, [dst, 16] ++ ldp A_q, B_q, [src, 80] ++ stp C_q, D_q, [dst, 48] ++ ldp C_q, D_q, [src, 112] ++ add src, src, 64 ++ add dst, dst, 64 ++ subs count, count, 64 ++ b.hi L(loop64) ++ ++ /* Write the last iteration and copy 64 bytes from the end. */ ++L(copy64_from_end): ++ ldp E_q, F_q, [srcend, -64] ++ stp A_q, B_q, [dst, 16] ++ ldp A_q, B_q, [srcend, -32] ++ stp C_q, D_q, [dst, 48] ++ stp E_q, F_q, [dstend, -64] ++ stp A_q, B_q, [dstend, -32] ++ ret ++ ++END (__memcpy_sve) ++ ++ ++ENTRY (__memmove_sve) ++ PTR_ARG (0) ++ PTR_ARG (1) ++ SIZE_ARG (2) ++ ++ cmp count, 128 ++ b.hi L(move_long) ++ cntb vlen ++ cmp count, vlen, lsl 1 ++ b.hi L(copy32_128) ++ whilelo p0.b, xzr, count ++ whilelo p1.b, vlen, count ++ ld1b z0.b, p0/z, [src, 0, mul vl] ++ ld1b z1.b, p1/z, [src, 1, mul vl] ++ st1b z0.b, p0, [dstin, 0, mul vl] ++ st1b z1.b, p1, [dstin, 1, mul vl] ++ ret ++ ++ .p2align 4 ++L(move_long): ++ add srcend, src, count ++ add dstend, dstin, count ++ /* Only use backward copy if there is an overlap. */ ++ sub tmp1, dstin, src ++ cbz tmp1, L(return) ++ cmp tmp1, count ++ b.hs L(copy_long) ++ ++ /* Large backwards copy for overlapping copies. ++ Copy 16 bytes and then align srcend to 16-byte alignment. */ ++ ldr D_q, [srcend, -16] ++ and tmp1, srcend, 15 ++ bic srcend, srcend, 15 ++ sub count, count, tmp1 ++ ldp A_q, B_q, [srcend, -32] ++ str D_q, [dstend, -16] ++ ldp C_q, D_q, [srcend, -64] ++ sub dstend, dstend, tmp1 ++ subs count, count, 128 ++ b.ls L(copy64_from_start) ++ ++L(loop64_backwards): ++ str B_q, [dstend, -16] ++ str A_q, [dstend, -32] ++ ldp A_q, B_q, [srcend, -96] ++ str D_q, [dstend, -48] ++ str C_q, [dstend, -64]! ++ ldp C_q, D_q, [srcend, -128] ++ sub srcend, srcend, 64 ++ subs count, count, 64 ++ b.hi L(loop64_backwards) ++ ++ /* Write the last iteration and copy 64 bytes from the start. */ ++L(copy64_from_start): ++ ldp E_q, F_q, [src, 32] ++ stp A_q, B_q, [dstend, -32] ++ ldp A_q, B_q, [src] ++ stp C_q, D_q, [dstend, -64] ++ stp E_q, F_q, [dstin, 32] ++ stp A_q, B_q, [dstin] ++L(return): ++ ret ++ ++END (__memmove_sve) ++#endif +diff -Nuar a/sysdeps/aarch64/multiarch/memcpy_thunderx2.S b/sysdeps/aarch64/multiarch/memcpy_thunderx2.S +--- a/sysdeps/aarch64/multiarch/memcpy_thunderx2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memcpy_thunderx2.S 2025-10-20 21:02:21.000000000 +0300 +@@ -75,27 +75,12 @@ + #define I_v v16 + #define J_v v17 + +-#ifndef MEMMOVE +-# define MEMMOVE memmove +-#endif +-#ifndef MEMCPY +-# define MEMCPY memcpy +-#endif +- +-#if IS_IN (libc) +- +-#undef MEMCPY +-#define MEMCPY __memcpy_thunderx2 +-#undef MEMMOVE +-#define MEMMOVE __memmove_thunderx2 +- +- + /* Overlapping large forward memmoves use a loop that copies backwards. + Otherwise memcpy is used. Small moves branch to memcopy16 directly. + The longer memcpy cases fall through to the memcpy head. + */ + +-ENTRY_ALIGN (MEMMOVE, 6) ++ENTRY (__memmove_thunderx2) + + PTR_ARG (0) + PTR_ARG (1) +@@ -109,8 +94,7 @@ + ccmp tmp1, count, 2, hi + b.lo L(move_long) + +-END (MEMMOVE) +-libc_hidden_builtin_def (MEMMOVE) ++END (__memmove_thunderx2) + + + /* Copies are split into 3 main cases: small copies of up to 16 bytes, +@@ -124,8 +108,7 @@ + + #define MEMCPY_PREFETCH_LDR 640 + +- .p2align 4 +-ENTRY (MEMCPY) ++ENTRY (__memcpy_thunderx2) + + PTR_ARG (0) + PTR_ARG (1) +@@ -449,7 +432,7 @@ + 3: ret + + +-END (MEMCPY) ++END (__memcpy_thunderx2) + .section .rodata + .p2align 4 + +@@ -472,6 +455,3 @@ + .word L(ext_size_13) -. + .word L(ext_size_14) -. + .word L(ext_size_15) -. +- +-libc_hidden_builtin_def (MEMCPY) +-#endif +diff -Nuar a/sysdeps/aarch64/multiarch/memcpy_thunderx.S b/sysdeps/aarch64/multiarch/memcpy_thunderx.S +--- a/sysdeps/aarch64/multiarch/memcpy_thunderx.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memcpy_thunderx.S 2025-10-20 21:02:21.000000000 +0300 +@@ -65,21 +65,7 @@ + Overlapping large forward memmoves use a loop that copies backwards. + */ + +-#ifndef MEMMOVE +-# define MEMMOVE memmove +-#endif +-#ifndef MEMCPY +-# define MEMCPY memcpy +-#endif +- +-#if IS_IN (libc) +- +-# undef MEMCPY +-# define MEMCPY __memcpy_thunderx +-# undef MEMMOVE +-# define MEMMOVE __memmove_thunderx +- +-ENTRY_ALIGN (MEMMOVE, 6) ++ENTRY (__memmove_thunderx) + + PTR_ARG (0) + PTR_ARG (1) +@@ -91,9 +77,9 @@ + b.lo L(move_long) + + /* Common case falls through into memcpy. */ +-END (MEMMOVE) +-libc_hidden_builtin_def (MEMMOVE) +-ENTRY (MEMCPY) ++END (__memmove_thunderx) ++ ++ENTRY (__memcpy_thunderx) + + PTR_ARG (0) + PTR_ARG (1) +@@ -316,7 +302,4 @@ + stp C_l, C_h, [dstin] + 3: ret + +-END (MEMCPY) +-libc_hidden_builtin_def (MEMCPY) +- +-#endif ++END (__memcpy_thunderx) +diff -Nuar a/sysdeps/aarch64/multiarch/memmove.c b/sysdeps/aarch64/multiarch/memmove.c +--- a/sysdeps/aarch64/multiarch/memmove.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memmove.c 2025-10-20 21:02:21.000000000 +0300 +@@ -29,31 +29,38 @@ + extern __typeof (__redirect_memmove) __libc_memmove; + + extern __typeof (__redirect_memmove) __memmove_generic attribute_hidden; +-extern __typeof (__redirect_memmove) __memmove_simd attribute_hidden; + extern __typeof (__redirect_memmove) __memmove_thunderx attribute_hidden; + extern __typeof (__redirect_memmove) __memmove_thunderx2 attribute_hidden; +-extern __typeof (__redirect_memmove) __memmove_falkor attribute_hidden; +-# if HAVE_AARCH64_SVE_ASM + extern __typeof (__redirect_memmove) __memmove_a64fx attribute_hidden; +-# endif ++extern __typeof (__redirect_memmove) __memmove_sve attribute_hidden; ++extern __typeof (__redirect_memmove) __memmove_mops attribute_hidden; ++ ++static inline __typeof (__redirect_memmove) * ++select_memmove_ifunc (void) ++{ ++ INIT_ARCH (); ++ ++ if (mops) ++ return __memmove_mops; ++ ++ if (sve && HAVE_AARCH64_SVE_ASM) ++ { ++ if (IS_A64FX (midr)) ++ return __memmove_a64fx; ++ return prefer_sve_ifuncs ? __memmove_sve : __memmove_generic; ++ } ++ ++ if (IS_THUNDERX (midr)) ++ return __memmove_thunderx; ++ ++ if (IS_THUNDERX2 (midr) || IS_THUNDERX2PA (midr)) ++ return __memmove_thunderx2; ++ ++ return __memmove_generic; ++} ++ ++libc_ifunc (__libc_memmove, select_memmove_ifunc ()); + +-libc_ifunc (__libc_memmove, +- (IS_THUNDERX (midr) +- ? __memmove_thunderx +- : (IS_FALKOR (midr) || IS_PHECDA (midr) +- ? __memmove_falkor +- : (IS_THUNDERX2 (midr) || IS_THUNDERX2PA (midr) +- ? __memmove_thunderx2 +- : (IS_NEOVERSE_N1 (midr) || IS_NEOVERSE_N2 (midr) +- || IS_NEOVERSE_V1 (midr) +- ? __memmove_simd +-# if HAVE_AARCH64_SVE_ASM +- : (IS_A64FX (midr) && sve +- ? __memmove_a64fx +- : __memmove_generic)))))); +-# else +- : __memmove_generic))))); +-# endif + # undef memmove + strong_alias (__libc_memmove, memmove); + #endif +diff -Nuar a/sysdeps/aarch64/multiarch/memmove_mops.S b/sysdeps/aarch64/multiarch/memmove_mops.S +--- a/sysdeps/aarch64/multiarch/memmove_mops.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/aarch64/multiarch/memmove_mops.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,39 @@ ++/* Optimized memmove for MOPS. ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++ ++/* Assumptions: ++ * ++ * AArch64, MOPS. ++ * ++ */ ++ ++ENTRY (__memmove_mops) ++ PTR_ARG (0) ++ PTR_ARG (1) ++ SIZE_ARG (2) ++ ++ mov x3, x0 ++ .inst 0x1d010443 /* cpyp [x3]!, [x1]!, x2! */ ++ .inst 0x1d410443 /* cpym [x3]!, [x1]!, x2! */ ++ .inst 0x1d810443 /* cpye [x3]!, [x1]!, x2! */ ++ ret ++ ++END (__memmove_mops) +diff -Nuar a/sysdeps/aarch64/multiarch/memset_a64fx.S b/sysdeps/aarch64/multiarch/memset_a64fx.S +--- a/sysdeps/aarch64/multiarch/memset_a64fx.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memset_a64fx.S 2025-10-20 21:02:21.000000000 +0300 +@@ -33,8 +33,6 @@ + #define vector_length x9 + + #if HAVE_AARCH64_SVE_ASM +-# if IS_IN (libc) +-# define MEMSET __memset_a64fx + + .arch armv8.2-a+sve + +@@ -49,7 +47,7 @@ + #undef BTI_C + #define BTI_C + +-ENTRY (MEMSET) ++ENTRY (__memset_a64fx) + PTR_ARG (0) + SIZE_ARG (2) + +@@ -166,8 +164,6 @@ + add count, count, CACHE_LINE_SIZE + b L(last) + +-END (MEMSET) +-libc_hidden_builtin_def (MEMSET) ++END (__memset_a64fx) + +-#endif /* IS_IN (libc) */ + #endif /* HAVE_AARCH64_SVE_ASM */ +diff -Nuar a/sysdeps/aarch64/multiarch/memset_base64.S b/sysdeps/aarch64/multiarch/memset_base64.S +--- a/sysdeps/aarch64/multiarch/memset_base64.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memset_base64.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,186 +0,0 @@ +-/* Copyright (C) 2018-2022 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 +- . */ +- +-#include +-#include "memset-reg.h" +- +-#ifndef MEMSET +-# define MEMSET __memset_base64 +-#endif +- +-/* To disable DC ZVA, set this threshold to 0. */ +-#ifndef DC_ZVA_THRESHOLD +-# define DC_ZVA_THRESHOLD 512 +-#endif +- +-/* Assumptions: +- * +- * ARMv8-a, AArch64, unaligned accesses +- * +- */ +- +-ENTRY_ALIGN (MEMSET, 6) +- +- PTR_ARG (0) +- SIZE_ARG (2) +- +- bfi valw, valw, 8, 8 +- bfi valw, valw, 16, 16 +- bfi val, val, 32, 32 +- +- add dstend, dstin, count +- +- cmp count, 96 +- b.hi L(set_long) +- cmp count, 16 +- b.hs L(set_medium) +- +- /* Set 0..15 bytes. */ +- tbz count, 3, 1f +- str val, [dstin] +- str val, [dstend, -8] +- ret +- +- .p2align 3 +-1: tbz count, 2, 2f +- str valw, [dstin] +- str valw, [dstend, -4] +- ret +-2: cbz count, 3f +- strb valw, [dstin] +- tbz count, 1, 3f +- strh valw, [dstend, -2] +-3: ret +- +- .p2align 3 +- /* Set 16..96 bytes. */ +-L(set_medium): +- stp val, val, [dstin] +- tbnz count, 6, L(set96) +- stp val, val, [dstend, -16] +- tbz count, 5, 1f +- stp val, val, [dstin, 16] +- stp val, val, [dstend, -32] +-1: ret +- +- .p2align 4 +- /* Set 64..96 bytes. Write 64 bytes from the start and +- 32 bytes from the end. */ +-L(set96): +- stp val, val, [dstin, 16] +- stp val, val, [dstin, 32] +- stp val, val, [dstin, 48] +- stp val, val, [dstend, -32] +- stp val, val, [dstend, -16] +- ret +- +- .p2align 4 +-L(set_long): +- stp val, val, [dstin] +- bic dst, dstin, 15 +-#if DC_ZVA_THRESHOLD +- cmp count, DC_ZVA_THRESHOLD +- ccmp val, 0, 0, cs +- b.eq L(zva_64) +-#endif +- /* Small-size or non-zero memset does not use DC ZVA. */ +- sub count, dstend, dst +- +- /* +- * Adjust count and bias for loop. By substracting extra 1 from count, +- * it is easy to use tbz instruction to check whether loop tailing +- * count is less than 33 bytes, so as to bypass 2 unneccesary stps. +- */ +- sub count, count, 64+16+1 +- +-#if DC_ZVA_THRESHOLD +- /* Align loop on 16-byte boundary, this might be friendly to i-cache. */ +- nop +-#endif +- +-1: stp val, val, [dst, 16] +- stp val, val, [dst, 32] +- stp val, val, [dst, 48] +- stp val, val, [dst, 64]! +- subs count, count, 64 +- b.hs 1b +- +- tbz count, 5, 1f /* Remaining count is less than 33 bytes? */ +- stp val, val, [dst, 16] +- stp val, val, [dst, 32] +-1: stp val, val, [dstend, -32] +- stp val, val, [dstend, -16] +- ret +- +-#if DC_ZVA_THRESHOLD +- .p2align 3 +-L(zva_64): +- stp val, val, [dst, 16] +- stp val, val, [dst, 32] +- stp val, val, [dst, 48] +- bic dst, dst, 63 +- +- /* +- * Previous memory writes might cross cache line boundary, and cause +- * cache line partially dirty. Zeroing this kind of cache line using +- * DC ZVA will incur extra cost, for it requires loading untouched +- * part of the line from memory before zeoring. +- * +- * So, write the first 64 byte aligned block using stp to force +- * fully dirty cache line. +- */ +- stp val, val, [dst, 64] +- stp val, val, [dst, 80] +- stp val, val, [dst, 96] +- stp val, val, [dst, 112] +- +- sub count, dstend, dst +- /* +- * Adjust count and bias for loop. By substracting extra 1 from count, +- * it is easy to use tbz instruction to check whether loop tailing +- * count is less than 33 bytes, so as to bypass 2 unneccesary stps. +- */ +- sub count, count, 128+64+64+1 +- add dst, dst, 128 +- nop +- +- /* DC ZVA sets 64 bytes each time. */ +-1: dc zva, dst +- add dst, dst, 64 +- subs count, count, 64 +- b.hs 1b +- +- /* +- * Write the last 64 byte aligned block using stp to force fully +- * dirty cache line. +- */ +- stp val, val, [dst, 0] +- stp val, val, [dst, 16] +- stp val, val, [dst, 32] +- stp val, val, [dst, 48] +- +- tbz count, 5, 1f /* Remaining count is less than 33 bytes? */ +- stp val, val, [dst, 64] +- stp val, val, [dst, 80] +-1: stp val, val, [dstend, -32] +- stp val, val, [dstend, -16] +- ret +-#endif +- +-END (MEMSET) +-libc_hidden_builtin_def (MEMSET) +diff -Nuar a/sysdeps/aarch64/multiarch/memset.c b/sysdeps/aarch64/multiarch/memset.c +--- a/sysdeps/aarch64/multiarch/memset.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memset.c 2025-10-20 21:02:21.000000000 +0300 +@@ -28,28 +28,44 @@ + + extern __typeof (__redirect_memset) __libc_memset; + +-extern __typeof (__redirect_memset) __memset_falkor attribute_hidden; ++extern __typeof (__redirect_memset) __memset_zva64 attribute_hidden; + extern __typeof (__redirect_memset) __memset_emag attribute_hidden; + extern __typeof (__redirect_memset) __memset_kunpeng attribute_hidden; +-# if HAVE_AARCH64_SVE_ASM + extern __typeof (__redirect_memset) __memset_a64fx attribute_hidden; +-# endif + extern __typeof (__redirect_memset) __memset_generic attribute_hidden; ++extern __typeof (__redirect_memset) __memset_mops attribute_hidden; ++extern __typeof (__redirect_memset) __memset_sve_zva64 attribute_hidden; + +-libc_ifunc (__libc_memset, +- IS_KUNPENG920 (midr) +- ?__memset_kunpeng +- : ((IS_FALKOR (midr) || IS_PHECDA (midr)) && zva_size == 64 +- ? __memset_falkor +- : (IS_EMAG (midr) && zva_size == 64 +- ? __memset_emag +-# if HAVE_AARCH64_SVE_ASM +- : (IS_A64FX (midr) && sve +- ? __memset_a64fx +- : __memset_generic)))); +-# else +- : __memset_generic))); +-# endif ++static inline __typeof (__redirect_memset) * ++select_memset_ifunc (void) ++{ ++ INIT_ARCH (); ++ ++ if (mops) ++ return __memset_mops; ++ ++ if (sve && HAVE_AARCH64_SVE_ASM) ++ { ++ if (IS_A64FX (midr) && zva_size == 256) ++ return __memset_a64fx; ++ ++ if (prefer_sve_ifuncs && zva_size == 64) ++ return __memset_sve_zva64; ++ } ++ ++ if (IS_KUNPENG920 (midr)) ++ return __memset_kunpeng; ++ ++ if (IS_EMAG (midr)) ++ return __memset_emag; ++ ++ if (zva_size == 64) ++ return __memset_zva64; ++ ++ return __memset_generic; ++} ++ ++libc_ifunc (__libc_memset, select_memset_ifunc ()); + + # undef memset + strong_alias (__libc_memset, memset); +diff -Nuar a/sysdeps/aarch64/multiarch/memset_emag.S b/sysdeps/aarch64/multiarch/memset_emag.S +--- a/sysdeps/aarch64/multiarch/memset_emag.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memset_emag.S 2025-10-20 21:02:21.000000000 +0300 +@@ -18,19 +18,95 @@ + . */ + + #include ++#include "memset-reg.h" + +-#if IS_IN (libc) +-# define MEMSET __memset_emag +- +-/* +- * Using DC ZVA to zero memory does not produce better performance if +- * memory size is not very large, especially when there are multiple +- * processes/threads contending memory/cache. Here we set threshold to +- * zero to disable using DC ZVA, which is good for multi-process/thread +- * workloads. ++/* Assumptions: ++ * ++ * ARMv8-a, AArch64, unaligned accesses ++ * + */ + +-# define DC_ZVA_THRESHOLD 0 ++ENTRY (__memset_emag) ++ ++ PTR_ARG (0) ++ SIZE_ARG (2) ++ ++ bfi valw, valw, 8, 8 ++ bfi valw, valw, 16, 16 ++ bfi val, val, 32, 32 ++ ++ add dstend, dstin, count ++ ++ cmp count, 96 ++ b.hi L(set_long) ++ cmp count, 16 ++ b.hs L(set_medium) ++ ++ /* Set 0..15 bytes. */ ++ tbz count, 3, 1f ++ str val, [dstin] ++ str val, [dstend, -8] ++ ret ++ ++ .p2align 3 ++1: tbz count, 2, 2f ++ str valw, [dstin] ++ str valw, [dstend, -4] ++ ret ++2: cbz count, 3f ++ strb valw, [dstin] ++ tbz count, 1, 3f ++ strh valw, [dstend, -2] ++3: ret ++ ++ .p2align 3 ++ /* Set 16..96 bytes. */ ++L(set_medium): ++ stp val, val, [dstin] ++ tbnz count, 6, L(set96) ++ stp val, val, [dstend, -16] ++ tbz count, 5, 1f ++ stp val, val, [dstin, 16] ++ stp val, val, [dstend, -32] ++1: ret ++ ++ .p2align 4 ++ /* Set 64..96 bytes. Write 64 bytes from the start and ++ 32 bytes from the end. */ ++L(set96): ++ stp val, val, [dstin, 16] ++ stp val, val, [dstin, 32] ++ stp val, val, [dstin, 48] ++ stp val, val, [dstend, -32] ++ stp val, val, [dstend, -16] ++ ret ++ ++ .p2align 4 ++L(set_long): ++ stp val, val, [dstin] ++ bic dst, dstin, 15 ++ /* Small-size or non-zero memset does not use DC ZVA. */ ++ sub count, dstend, dst ++ ++ /* ++ * Adjust count and bias for loop. By subtracting extra 1 from count, ++ * it is easy to use tbz instruction to check whether loop tailing ++ * count is less than 33 bytes, so as to bypass 2 unnecessary stps. ++ */ ++ sub count, count, 64+16+1 ++ ++1: stp val, val, [dst, 16] ++ stp val, val, [dst, 32] ++ stp val, val, [dst, 48] ++ stp val, val, [dst, 64]! ++ subs count, count, 64 ++ b.hs 1b ++ ++ tbz count, 5, 1f /* Remaining count is less than 33 bytes? */ ++ stp val, val, [dst, 16] ++ stp val, val, [dst, 32] ++1: stp val, val, [dstend, -32] ++ stp val, val, [dstend, -16] ++ ret + +-# include "./memset_base64.S" +-#endif ++END (__memset_emag) +diff -Nuar a/sysdeps/aarch64/multiarch/memset_falkor.S b/sysdeps/aarch64/multiarch/memset_falkor.S +--- a/sysdeps/aarch64/multiarch/memset_falkor.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memset_falkor.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,54 +0,0 @@ +-/* Memset for falkor. +- Copyright (C) 2017-2022 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 +- . */ +- +-#include +-#include +- +-/* Reading dczid_el0 is expensive on falkor so move it into the ifunc +- resolver and assume ZVA size of 64 bytes. The IFUNC resolver takes care to +- use this function only when ZVA is enabled. */ +- +-#if IS_IN (libc) +-.macro zva_macro +- .p2align 4 +- /* Write the first and last 64 byte aligned block using stp rather +- than using DC ZVA. This is faster on some cores. */ +- str q0, [dst, 16] +- stp q0, q0, [dst, 32] +- bic dst, dst, 63 +- stp q0, q0, [dst, 64] +- stp q0, q0, [dst, 96] +- sub count, dstend, dst /* Count is now 128 too large. */ +- sub count, count, 128+64+64 /* Adjust count and bias for loop. */ +- add dst, dst, 128 +-1: dc zva, dst +- add dst, dst, 64 +- subs count, count, 64 +- b.hi 1b +- stp q0, q0, [dst, 0] +- stp q0, q0, [dst, 32] +- stp q0, q0, [dstend, -64] +- stp q0, q0, [dstend, -32] +- ret +-.endm +- +-# define ZVA_MACRO zva_macro +-# define MEMSET __memset_falkor +-# include +-#endif +diff -Nuar a/sysdeps/aarch64/multiarch/memset_generic.S b/sysdeps/aarch64/multiarch/memset_generic.S +--- a/sysdeps/aarch64/multiarch/memset_generic.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memset_generic.S 2025-10-20 21:02:21.000000000 +0300 +@@ -21,9 +21,15 @@ + + #if IS_IN (libc) + # define MEMSET __memset_generic ++ ++/* Do not hide the generic version of memset, we use it internally. */ ++# undef libc_hidden_builtin_def ++# define libc_hidden_builtin_def(name) ++ + /* Add a hidden definition for use within libc.so. */ + # ifdef SHARED + .globl __GI_memset; __GI_memset = __memset_generic + # endif +-# include + #endif ++ ++#include <../memset.S> +diff -Nuar a/sysdeps/aarch64/multiarch/memset_kunpeng.S b/sysdeps/aarch64/multiarch/memset_kunpeng.S +--- a/sysdeps/aarch64/multiarch/memset_kunpeng.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/memset_kunpeng.S 2025-10-20 21:02:21.000000000 +0300 +@@ -20,16 +20,13 @@ + #include + #include + +-#if IS_IN (libc) +-# define MEMSET __memset_kunpeng +- + /* Assumptions: + * + * ARMv8-a, AArch64, unaligned accesses + * + */ + +-ENTRY_ALIGN (MEMSET, 6) ++ENTRY (__memset_kunpeng) + + PTR_ARG (0) + SIZE_ARG (2) +@@ -108,6 +105,4 @@ + stp q0, q0, [dstend, -32] + ret + +-END (MEMSET) +-libc_hidden_builtin_def (MEMSET) +-#endif ++END (__memset_kunpeng) +diff -Nuar a/sysdeps/aarch64/multiarch/memset_mops.S b/sysdeps/aarch64/multiarch/memset_mops.S +--- a/sysdeps/aarch64/multiarch/memset_mops.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/aarch64/multiarch/memset_mops.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,38 @@ ++/* Optimized memset for MOPS. ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++ ++/* Assumptions: ++ * ++ * AArch64, MOPS. ++ * ++ */ ++ ++ENTRY (__memset_mops) ++ PTR_ARG (0) ++ SIZE_ARG (2) ++ ++ mov x3, x0 ++ .inst 0x19c10443 /* setp [x3]!, x2!, x1 */ ++ .inst 0x19c14443 /* setm [x3]!, x2!, x1 */ ++ .inst 0x19c18443 /* sete [x3]!, x2!, x1 */ ++ ret ++ ++END (__memset_mops) +diff -Nuar a/sysdeps/aarch64/multiarch/memset_sve_zva64.S b/sysdeps/aarch64/multiarch/memset_sve_zva64.S +--- a/sysdeps/aarch64/multiarch/memset_sve_zva64.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/aarch64/multiarch/memset_sve_zva64.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,123 @@ ++/* Optimized memset for SVE. ++ Copyright (C) 2025 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 ++ . */ ++ ++#include ++ ++/* Assumptions: ++ * ++ * ARMv8-a, AArch64, Advanced SIMD, SVE, unaligned accesses. ++ * ZVA size is 64. ++ */ ++ ++#if HAVE_AARCH64_SVE_ASM ++ ++.arch armv8.2-a+sve ++ ++#define dstin x0 ++#define val x1 ++#define valw w1 ++#define count x2 ++#define dst x3 ++#define dstend x4 ++#define zva_val x5 ++#define vlen x5 ++#define off x3 ++#define dstend2 x5 ++ ++ENTRY (__memset_sve_zva64) ++ dup v0.16B, valw ++ cmp count, 16 ++ b.lo L(set_16) ++ ++ add dstend, dstin, count ++ cmp count, 64 ++ b.hs L(set_128) ++ ++ /* Set 16..63 bytes. */ ++ mov off, 16 ++ and off, off, count, lsr 1 ++ sub dstend2, dstend, off ++ str q0, [dstin] ++ str q0, [dstin, off] ++ str q0, [dstend2, -16] ++ str q0, [dstend, -16] ++ ret ++ ++ .p2align 4 ++L(set_16): ++ whilelo p0.b, xzr, count ++ st1b z0.b, p0, [dstin] ++ ret ++ ++ .p2align 4 ++L(set_128): ++ bic dst, dstin, 15 ++ cmp count, 128 ++ b.hi L(set_long) ++ stp q0, q0, [dstin] ++ stp q0, q0, [dstin, 32] ++ stp q0, q0, [dstend, -64] ++ stp q0, q0, [dstend, -32] ++ ret ++ ++ .p2align 4 ++L(set_long): ++ cmp count, 256 ++ b.lo L(no_zva) ++ tst valw, 255 ++ b.ne L(no_zva) ++ ++ str q0, [dstin] ++ str q0, [dst, 16] ++ bic dst, dstin, 31 ++ stp q0, q0, [dst, 32] ++ bic dst, dstin, 63 ++ sub count, dstend, dst /* Count is now 64 too large. */ ++ sub count, count, 128 /* Adjust count and bias for loop. */ ++ ++ sub x8, dstend, 1 /* Write last bytes before ZVA loop. */ ++ bic x8, x8, 15 ++ stp q0, q0, [x8, -48] ++ str q0, [x8, -16] ++ str q0, [dstend, -16] ++ ++ .p2align 4 ++L(zva64_loop): ++ add dst, dst, 64 ++ dc zva, dst ++ subs count, count, 64 ++ b.hi L(zva64_loop) ++ ret ++ ++L(no_zva): ++ str q0, [dstin] ++ sub count, dstend, dst /* Count is 16 too large. */ ++ sub count, count, 64 + 16 /* Adjust count and bias for loop. */ ++L(no_zva_loop): ++ stp q0, q0, [dst, 16] ++ stp q0, q0, [dst, 48] ++ add dst, dst, 64 ++ subs count, count, 64 ++ b.hi L(no_zva_loop) ++ stp q0, q0, [dstend, -64] ++ stp q0, q0, [dstend, -32] ++ ret ++ ++END (__memset_sve_zva64) ++#endif +diff -Nuar a/sysdeps/aarch64/multiarch/memset_zva64.S b/sysdeps/aarch64/multiarch/memset_zva64.S +--- a/sysdeps/aarch64/multiarch/memset_zva64.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/aarch64/multiarch/memset_zva64.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,27 @@ ++/* Optimized memset for zva size = 64. ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++ ++#define ZVA64_ONLY 1 ++#define MEMSET __memset_zva64 ++#undef libc_hidden_builtin_def ++#define libc_hidden_builtin_def(X) ++ ++#include "../memset.S" +diff -Nuar a/sysdeps/aarch64/multiarch/rtld-memset.S b/sysdeps/aarch64/multiarch/rtld-memset.S +--- a/sysdeps/aarch64/multiarch/rtld-memset.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/rtld-memset.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,25 +0,0 @@ +-/* Memset for aarch64, for the dynamic linker. +- Copyright (C) 2017-2022 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 +- . */ +- +-#include +- +-#if IS_IN (rtld) +-# define MEMSET memset +-# include +-#endif +diff -Nuar a/sysdeps/aarch64/multiarch/strlen_asimd.S b/sysdeps/aarch64/multiarch/strlen_asimd.S +--- a/sysdeps/aarch64/multiarch/strlen_asimd.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/strlen_asimd.S 2025-10-20 21:02:21.000000000 +0300 +@@ -48,6 +48,7 @@ + #define tmp x2 + #define tmpw w2 + #define synd x3 ++#define syndw w3 + #define shift x4 + + /* For the first 32 bytes, NUL detection works on the principle that +@@ -87,7 +88,6 @@ + + ENTRY (__strlen_asimd) + PTR_ARG (0) +- + and tmp1, srcin, MIN_PAGE_SIZE - 1 + cmp tmp1, MIN_PAGE_SIZE - 32 + b.hi L(page_cross) +@@ -123,7 +123,6 @@ + add len, len, tmp1, lsr 3 + ret + +- .p2align 3 + /* Look for a NUL byte at offset 16..31 in the string. */ + L(bytes16_31): + ldp data1, data2, [srcin, 16] +@@ -151,6 +150,7 @@ + add len, len, tmp1, lsr 3 + ret + ++ nop + L(loop_entry): + bic src, srcin, 31 + +@@ -166,18 +166,12 @@ + /* Low 32 bits of synd are non-zero if a NUL was found in datav1. */ + cmeq maskv.16b, datav1.16b, 0 + sub len, src, srcin +- tst synd, 0xffffffff +- b.ne 1f ++ cbnz syndw, 1f + cmeq maskv.16b, datav2.16b, 0 + add len, len, 16 + 1: + /* Generate a bitmask and compute correct byte offset. */ +-#ifdef __AARCH64EB__ +- bic maskv.8h, 0xf0 +-#else +- bic maskv.8h, 0x0f, lsl 8 +-#endif +- umaxp maskv.16b, maskv.16b, maskv.16b ++ shrn maskv.8b, maskv.8h, 4 + fmov synd, maskd + #ifndef __AARCH64EB__ + rbit synd, synd +@@ -186,8 +180,6 @@ + add len, len, tmp, lsr 2 + ret + +- .p2align 4 +- + L(page_cross): + bic src, srcin, 31 + mov tmpw, 0x0c03 +@@ -211,4 +203,3 @@ + ret + + END (__strlen_asimd) +-libc_hidden_builtin_def (__strlen_asimd) +diff -Nuar a/sysdeps/aarch64/multiarch/strlen.c b/sysdeps/aarch64/multiarch/strlen.c +--- a/sysdeps/aarch64/multiarch/strlen.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/strlen.c 2025-10-20 21:02:21.000000000 +0300 +@@ -28,10 +28,10 @@ + + extern __typeof (__redirect_strlen) __strlen; + +-extern __typeof (__redirect_strlen) __strlen_mte attribute_hidden; ++extern __typeof (__redirect_strlen) __strlen_generic attribute_hidden; + extern __typeof (__redirect_strlen) __strlen_asimd attribute_hidden; + +-libc_ifunc (__strlen, (mte ? __strlen_mte : __strlen_asimd)); ++libc_ifunc (__strlen, (mte ? __strlen_generic : __strlen_asimd)); + + # undef strlen + strong_alias (__strlen, strlen); +diff -Nuar a/sysdeps/aarch64/multiarch/strlen_generic.S b/sysdeps/aarch64/multiarch/strlen_generic.S +--- a/sysdeps/aarch64/multiarch/strlen_generic.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/aarch64/multiarch/strlen_generic.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,39 @@ ++/* A Generic Optimized strlen implementation for AARCH64. ++ Copyright (C) 2018-2022 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 ++ . */ ++ ++/* The actual strlen code is in ../strlen.S. If we are building libc this file ++ defines __strlen_generic. Otherwise the include of ../strlen.S will define ++ the normal __strlen entry points. */ ++ ++#include ++ ++#if IS_IN (libc) ++ ++# define STRLEN __strlen_generic ++ ++/* Do not hide the generic version of strlen, we use it internally. */ ++# undef libc_hidden_builtin_def ++# define libc_hidden_builtin_def(name) ++ ++# ifdef SHARED ++/* It doesn't make sense to send libc-internal strlen calls through a PLT. */ ++ .globl __GI_strlen; __GI_strlen = __strlen_generic ++# endif ++#endif ++ ++#include "../strlen.S" +diff -Nuar a/sysdeps/aarch64/multiarch/strlen_mte.S b/sysdeps/aarch64/multiarch/strlen_mte.S +--- a/sysdeps/aarch64/multiarch/strlen_mte.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/multiarch/strlen_mte.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,39 +0,0 @@ +-/* A Generic Optimized strlen implementation for AARCH64. +- Copyright (C) 2018-2022 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 +- . */ +- +-/* The actual strlen code is in ../strlen.S. If we are building libc this file +- defines __strlen_mte. Otherwise the include of ../strlen.S will define +- the normal __strlen entry points. */ +- +-#include +- +-#if IS_IN (libc) +- +-# define STRLEN __strlen_mte +- +-/* Do not hide the generic version of strlen, we use it internally. */ +-# undef libc_hidden_builtin_def +-# define libc_hidden_builtin_def(name) +- +-# ifdef SHARED +-/* It doesn't make sense to send libc-internal strlen calls through a PLT. */ +- .globl __GI_strlen; __GI_strlen = __strlen_mte +-# endif +-#endif +- +-#include "../strlen.S" +diff -Nuar a/sysdeps/aarch64/rawmemchr.S b/sysdeps/aarch64/rawmemchr.S +--- a/sysdeps/aarch64/rawmemchr.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/rawmemchr.S 2025-10-20 21:02:21.000000000 +0300 +@@ -31,7 +31,7 @@ + + L(do_strlen): + mov x15, x30 +- cfi_return_column (x15) ++ cfi_register (x30, x15) + mov x14, x0 + bl __strlen + add x0, x14, x0 +diff -Nuar a/sysdeps/aarch64/strchrnul.S b/sysdeps/aarch64/strchrnul.S +--- a/sysdeps/aarch64/strchrnul.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/strchrnul.S 2025-10-20 21:02:21.000000000 +0300 +@@ -33,38 +33,32 @@ + #define src x2 + #define tmp1 x1 + #define tmp2 x3 +-#define tmp2w w3 + + #define vrepchr v0 + #define vdata v1 + #define qdata q1 + #define vhas_nul v2 + #define vhas_chr v3 +-#define vrepmask v4 +-#define vend v5 +-#define dend d5 ++#define vend v4 ++#define dend d4 + +-/* Core algorithm: +- +- For each 16-byte chunk we calculate a 64-bit syndrome value with four bits +- per byte. For even bytes, bits 0-3 are set if the relevant byte matched the +- requested character or the byte is NUL. Bits 4-7 must be zero. Bits 4-7 are +- set likewise for odd bytes so that adjacent bytes can be merged. Since the +- bits in the syndrome reflect the order in which things occur in the original +- string, counting trailing zeros identifies exactly which byte matched. */ ++/* ++ Core algorithm: ++ For each 16-byte chunk we calculate a 64-bit nibble mask value with four bits ++ per byte. We take 4 bits of every comparison byte with shift right and narrow ++ by 4 instruction. Since the bits in the nibble mask reflect the order in ++ which things occur in the original string, counting leading zeros identifies ++ exactly which byte matched. */ + + ENTRY (__strchrnul) + PTR_ARG (0) + bic src, srcin, 15 + dup vrepchr.16b, chrin + ld1 {vdata.16b}, [src] +- mov tmp2w, 0xf00f +- dup vrepmask.8h, tmp2w + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b + cmhs vhas_chr.16b, vhas_chr.16b, vdata.16b + lsl tmp2, srcin, 2 +- and vhas_chr.16b, vhas_chr.16b, vrepmask.16b +- addp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ ++ shrn vend.8b, vhas_chr.8h, 4 /* 128->64 */ + fmov tmp1, dend + lsr tmp1, tmp1, tmp2 /* Mask padding bits. */ + cbz tmp1, L(loop) +@@ -76,15 +70,22 @@ + + .p2align 4 + L(loop): +- ldr qdata, [src, 16]! ++ ldr qdata, [src, 16] ++ cmeq vhas_chr.16b, vdata.16b, vrepchr.16b ++ cmhs vhas_chr.16b, vhas_chr.16b, vdata.16b ++ umaxp vend.16b, vhas_chr.16b, vhas_chr.16b ++ fmov tmp1, dend ++ cbnz tmp1, L(end) ++ ldr qdata, [src, 32]! + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b + cmhs vhas_chr.16b, vhas_chr.16b, vdata.16b + umaxp vend.16b, vhas_chr.16b, vhas_chr.16b + fmov tmp1, dend + cbz tmp1, L(loop) +- +- and vhas_chr.16b, vhas_chr.16b, vrepmask.16b +- addp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ ++ sub src, src, 16 ++L(end): ++ shrn vend.8b, vhas_chr.8h, 4 /* 128->64 */ ++ add src, src, 16 + fmov tmp1, dend + #ifndef __AARCH64EB__ + rbit tmp1, tmp1 +diff -Nuar a/sysdeps/aarch64/strchr.S b/sysdeps/aarch64/strchr.S +--- a/sysdeps/aarch64/strchr.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/strchr.S 2025-10-20 21:02:21.000000000 +0300 +@@ -32,8 +32,7 @@ + + #define src x2 + #define tmp1 x1 +-#define wtmp2 w3 +-#define tmp3 x3 ++#define tmp2 x3 + + #define vrepchr v0 + #define vdata v1 +@@ -41,39 +40,30 @@ + #define vhas_nul v2 + #define vhas_chr v3 + #define vrepmask v4 +-#define vrepmask2 v5 +-#define vend v6 +-#define dend d6 ++#define vend v5 ++#define dend d5 + + /* Core algorithm. + + For each 16-byte chunk we calculate a 64-bit syndrome value with four bits +- per byte. For even bytes, bits 0-1 are set if the relevant byte matched the +- requested character, bits 2-3 are set if the byte is NUL (or matched), and +- bits 4-7 are not used and must be zero if none of bits 0-3 are set). Odd +- bytes set bits 4-7 so that adjacent bytes can be merged. Since the bits +- in the syndrome reflect the order in which things occur in the original +- string, counting trailing zeros identifies exactly which byte matched. */ ++ per byte. Bits 0-1 are set if the relevant byte matched the requested ++ character, bits 2-3 are set if the byte is NUL or matched. Count trailing ++ zeroes gives the position of the matching byte if it is a multiple of 4. ++ If it is not a multiple of 4, there was no match. */ + + ENTRY (strchr) + PTR_ARG (0) + bic src, srcin, 15 + dup vrepchr.16b, chrin + ld1 {vdata.16b}, [src] +- mov wtmp2, 0x3003 +- dup vrepmask.8h, wtmp2 ++ movi vrepmask.16b, 0x33 + cmeq vhas_nul.16b, vdata.16b, 0 + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b +- mov wtmp2, 0xf00f +- dup vrepmask2.8h, wtmp2 +- + bit vhas_nul.16b, vhas_chr.16b, vrepmask.16b +- and vhas_nul.16b, vhas_nul.16b, vrepmask2.16b +- lsl tmp3, srcin, 2 +- addp vend.16b, vhas_nul.16b, vhas_nul.16b /* 128->64 */ +- ++ lsl tmp2, srcin, 2 ++ shrn vend.8b, vhas_nul.8h, 4 /* 128->64 */ + fmov tmp1, dend +- lsr tmp1, tmp1, tmp3 ++ lsr tmp1, tmp1, tmp2 + cbz tmp1, L(loop) + + rbit tmp1, tmp1 +@@ -87,28 +77,34 @@ + + .p2align 4 + L(loop): +- ldr qdata, [src, 16]! ++ ldr qdata, [src, 16] ++ cmeq vhas_chr.16b, vdata.16b, vrepchr.16b ++ cmhs vhas_nul.16b, vhas_chr.16b, vdata.16b ++ umaxp vend.16b, vhas_nul.16b, vhas_nul.16b ++ fmov tmp1, dend ++ cbnz tmp1, L(end) ++ ldr qdata, [src, 32]! + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b + cmhs vhas_nul.16b, vhas_chr.16b, vdata.16b + umaxp vend.16b, vhas_nul.16b, vhas_nul.16b + fmov tmp1, dend + cbz tmp1, L(loop) ++ sub src, src, 16 ++L(end): + + #ifdef __AARCH64EB__ + bif vhas_nul.16b, vhas_chr.16b, vrepmask.16b +- and vhas_nul.16b, vhas_nul.16b, vrepmask2.16b +- addp vend.16b, vhas_nul.16b, vhas_nul.16b /* 128->64 */ ++ shrn vend.8b, vhas_nul.8h, 4 /* 128->64 */ + fmov tmp1, dend + #else + bit vhas_nul.16b, vhas_chr.16b, vrepmask.16b +- and vhas_nul.16b, vhas_nul.16b, vrepmask2.16b +- addp vend.16b, vhas_nul.16b, vhas_nul.16b /* 128->64 */ ++ shrn vend.8b, vhas_nul.8h, 4 /* 128->64 */ + fmov tmp1, dend + rbit tmp1, tmp1 + #endif ++ add src, src, 16 + clz tmp1, tmp1 +- /* Tmp1 is an even multiple of 2 if the target character was +- found first. Otherwise we've found the end of string. */ ++ /* Tmp1 is a multiple of 4 if the target character was found. */ + tst tmp1, 2 + add result, src, tmp1, lsr 2 + csel result, result, xzr, eq +diff -Nuar a/sysdeps/aarch64/strcpy.S b/sysdeps/aarch64/strcpy.S +--- a/sysdeps/aarch64/strcpy.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/strcpy.S 2025-10-20 21:02:21.000000000 +0300 +@@ -30,7 +30,6 @@ + * MTE compatible. + */ + +-/* Arguments and results. */ + #define dstin x0 + #define srcin x1 + #define result x0 +@@ -40,7 +39,6 @@ + #define len x4 + #define synd x4 + #define tmp x5 +-#define wtmp w5 + #define shift x5 + #define data1 x6 + #define dataw1 w6 +@@ -50,9 +48,8 @@ + #define dataq q0 + #define vdata v0 + #define vhas_nul v1 +-#define vrepmask v2 +-#define vend v3 +-#define dend d3 ++#define vend v2 ++#define dend d2 + #define dataq2 q1 + + #ifdef BUILD_STPCPY +@@ -63,34 +60,29 @@ + # define IFSTPCPY(X,...) + #endif + +-/* Core algorithm: +- +- For each 16-byte chunk we calculate a 64-bit syndrome value with four bits +- per byte. For even bytes, bits 0-3 are set if the relevant byte matched the +- requested character or the byte is NUL. Bits 4-7 must be zero. Bits 4-7 are +- set likewise for odd bytes so that adjacent bytes can be merged. Since the +- bits in the syndrome reflect the order in which things occur in the original +- string, counting trailing zeros identifies exactly which byte matched. */ ++/* ++ Core algorithm: ++ For each 16-byte chunk we calculate a 64-bit nibble mask value with four bits ++ per byte. We take 4 bits of every comparison byte with shift right and narrow ++ by 4 instruction. Since the bits in the nibble mask reflect the order in ++ which things occur in the original string, counting leading zeros identifies ++ exactly which byte matched. */ + + ENTRY (STRCPY) + PTR_ARG (0) + PTR_ARG (1) + bic src, srcin, 15 +- mov wtmp, 0xf00f + ld1 {vdata.16b}, [src] +- dup vrepmask.8h, wtmp + cmeq vhas_nul.16b, vdata.16b, 0 + lsl shift, srcin, 2 +- and vhas_nul.16b, vhas_nul.16b, vrepmask.16b +- addp vend.16b, vhas_nul.16b, vhas_nul.16b ++ shrn vend.8b, vhas_nul.8h, 4 + fmov synd, dend + lsr synd, synd, shift + cbnz synd, L(tail) + + ldr dataq, [src, 16]! + cmeq vhas_nul.16b, vdata.16b, 0 +- and vhas_nul.16b, vhas_nul.16b, vrepmask.16b +- addp vend.16b, vhas_nul.16b, vhas_nul.16b ++ shrn vend.8b, vhas_nul.8h, 4 + fmov synd, dend + cbz synd, L(start_loop) + +@@ -109,13 +101,10 @@ + IFSTPCPY (add result, dstin, len) + ret + +- .p2align 4,,8 + L(tail): + rbit synd, synd + clz len, synd + lsr len, len, 2 +- +- .p2align 4 + L(less16): + tbz len, 3, L(less8) + sub tmp, len, 7 +@@ -148,32 +137,37 @@ + + .p2align 4 + L(start_loop): +- sub len, src, srcin ++ sub tmp, srcin, dstin + ldr dataq2, [srcin] +- add dst, dstin, len ++ sub dst, src, tmp + str dataq2, [dstin] +- +- .p2align 5 + L(loop): +- str dataq, [dst], 16 +- ldr dataq, [src, 16]! ++ str dataq, [dst], 32 ++ ldr dataq, [src, 16] ++ cmeq vhas_nul.16b, vdata.16b, 0 ++ umaxp vend.16b, vhas_nul.16b, vhas_nul.16b ++ fmov synd, dend ++ cbnz synd, L(loopend) ++ str dataq, [dst, -16] ++ ldr dataq, [src, 32]! + cmeq vhas_nul.16b, vdata.16b, 0 + umaxp vend.16b, vhas_nul.16b, vhas_nul.16b + fmov synd, dend + cbz synd, L(loop) +- +- and vhas_nul.16b, vhas_nul.16b, vrepmask.16b +- addp vend.16b, vhas_nul.16b, vhas_nul.16b /* 128->64 */ ++ add dst, dst, 16 ++L(loopend): ++ shrn vend.8b, vhas_nul.8h, 4 /* 128->64 */ + fmov synd, dend ++ sub dst, dst, 31 + #ifndef __AARCH64EB__ + rbit synd, synd + #endif + clz len, synd + lsr len, len, 2 +- sub tmp, len, 15 +- ldr dataq, [src, tmp] +- str dataq, [dst, tmp] +- IFSTPCPY (add result, dst, len) ++ add dst, dst, len ++ ldr dataq, [dst, tmp] ++ str dataq, [dst] ++ IFSTPCPY (add result, dst, 15) + ret + + END (STRCPY) +diff -Nuar a/sysdeps/aarch64/strlen.S b/sysdeps/aarch64/strlen.S +--- a/sysdeps/aarch64/strlen.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/strlen.S 2025-10-20 21:02:21.000000000 +0300 +@@ -1,4 +1,5 @@ +-/* Copyright (C) 2012-2022 Free Software Foundation, Inc. ++/* Generic optimized strlen using SIMD. ++ Copyright (C) 2012-2024 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + +@@ -34,61 +35,72 @@ + #define src x1 + #define synd x2 + #define tmp x3 +-#define wtmp w3 + #define shift x4 + + #define data q0 + #define vdata v0 + #define vhas_nul v1 +-#define vrepmask v2 +-#define vend v3 +-#define dend d3 ++#define vend v2 ++#define dend d2 + + /* Core algorithm: +- +- For each 16-byte chunk we calculate a 64-bit syndrome value with four bits +- per byte. For even bytes, bits 0-3 are set if the relevant byte matched the +- requested character or the byte is NUL. Bits 4-7 must be zero. Bits 4-7 are +- set likewise for odd bytes so that adjacent bytes can be merged. Since the +- bits in the syndrome reflect the order in which things occur in the original +- string, counting trailing zeros identifies exactly which byte matched. */ ++ Process the string in 16-byte aligned chunks. Compute a 64-bit mask with ++ four bits per byte using the shrn instruction. A count trailing zeros then ++ identifies the first zero byte. */ + + ENTRY (STRLEN) + PTR_ARG (0) + bic src, srcin, 15 +- mov wtmp, 0xf00f + ld1 {vdata.16b}, [src] +- dup vrepmask.8h, wtmp + cmeq vhas_nul.16b, vdata.16b, 0 + lsl shift, srcin, 2 +- and vhas_nul.16b, vhas_nul.16b, vrepmask.16b +- addp vend.16b, vhas_nul.16b, vhas_nul.16b /* 128->64 */ ++ shrn vend.8b, vhas_nul.8h, 4 /* 128->64 */ + fmov synd, dend + lsr synd, synd, shift +- cbz synd, L(loop) ++ cbz synd, L(next16) + + rbit synd, synd + clz result, synd + lsr result, result, 2 + ret + +- .p2align 5 +-L(loop): +- ldr data, [src, 16]! ++L(next16): ++ ldr data, [src, 16] + cmeq vhas_nul.16b, vdata.16b, 0 +- umaxp vend.16b, vhas_nul.16b, vhas_nul.16b ++ shrn vend.8b, vhas_nul.8h, 4 /* 128->64 */ + fmov synd, dend + cbz synd, L(loop) +- +- and vhas_nul.16b, vhas_nul.16b, vrepmask.16b +- addp vend.16b, vhas_nul.16b, vhas_nul.16b /* 128->64 */ ++ add src, src, 16 ++#ifndef __AARCH64EB__ ++ rbit synd, synd ++#endif + sub result, src, srcin ++ clz tmp, synd ++ add result, result, tmp, lsr 2 ++ ret ++ ++ .p2align 5 ++L(loop): ++ ldr data, [src, 32]! ++ cmeq vhas_nul.16b, vdata.16b, 0 ++ addhn vend.8b, vhas_nul.8h, vhas_nul.8h + fmov synd, dend ++ cbnz synd, L(loop_end) ++ ldr data, [src, 16] ++ cmeq vhas_nul.16b, vdata.16b, 0 ++ addhn vend.8b, vhas_nul.8h, vhas_nul.8h ++ fmov synd, dend ++ cbz synd, L(loop) ++ add src, src, 16 ++L(loop_end): ++ sub result, shift, src, lsl 2 /* (srcin - src) << 2. */ + #ifndef __AARCH64EB__ + rbit synd, synd ++ sub result, result, 3 + #endif + clz tmp, synd +- add result, result, tmp, lsr 2 ++ sub result, tmp, result ++ lsr result, result, 2 + ret + + END (STRLEN) +diff -Nuar a/sysdeps/aarch64/strnlen.S b/sysdeps/aarch64/strnlen.S +--- a/sysdeps/aarch64/strnlen.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/strnlen.S 2025-10-20 21:02:21.000000000 +0300 +@@ -33,39 +33,30 @@ + #define src x2 + #define synd x3 + #define shift x4 +-#define wtmp w4 + #define tmp x4 + #define cntrem x5 + + #define qdata q0 + #define vdata v0 + #define vhas_chr v1 +-#define vrepmask v2 +-#define vend v3 +-#define dend d3 ++#define vend v2 ++#define dend d2 + + /* + Core algorithm: +- +- For each 16-byte chunk we calculate a 64-bit syndrome value with four bits +- per byte. For even bytes, bits 0-3 are set if the relevant byte matched the +- requested character or the byte is NUL. Bits 4-7 must be zero. Bits 4-7 are +- set likewise for odd bytes so that adjacent bytes can be merged. Since the +- bits in the syndrome reflect the order in which things occur in the original +- string, counting trailing zeros identifies exactly which byte matched. */ ++ Process the string in 16-byte aligned chunks. Compute a 64-bit mask with ++ four bits per byte using the shrn instruction. A count trailing zeros then ++ identifies the first zero byte. */ + + ENTRY (__strnlen) + PTR_ARG (0) + SIZE_ARG (1) + bic src, srcin, 15 +- mov wtmp, 0xf00f + cbz cntin, L(nomatch) +- ld1 {vdata.16b}, [src], 16 +- dup vrepmask.8h, wtmp ++ ld1 {vdata.16b}, [src] + cmeq vhas_chr.16b, vdata.16b, 0 + lsl shift, srcin, 2 +- and vhas_chr.16b, vhas_chr.16b, vrepmask.16b +- addp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ ++ shrn vend.8b, vhas_chr.8h, 4 /* 128->64 */ + fmov synd, dend + lsr synd, synd, shift + cbz synd, L(start_loop) +@@ -77,37 +68,40 @@ + csel result, cntin, result, ls + ret + ++L(nomatch): ++ mov result, cntin ++ ret ++ + L(start_loop): + sub tmp, src, srcin ++ add tmp, tmp, 17 + subs cntrem, cntin, tmp +- b.ls L(nomatch) ++ b.lo L(nomatch) + + /* Make sure that it won't overread by a 16-byte chunk */ +- add tmp, cntrem, 15 +- tbnz tmp, 4, L(loop32_2) +- ++ tbz cntrem, 4, L(loop32_2) ++ sub src, src, 16 + .p2align 5 + L(loop32): +- ldr qdata, [src], 16 ++ ldr qdata, [src, 32]! + cmeq vhas_chr.16b, vdata.16b, 0 + umaxp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ + fmov synd, dend + cbnz synd, L(end) + L(loop32_2): +- ldr qdata, [src], 16 ++ ldr qdata, [src, 16] + subs cntrem, cntrem, 32 + cmeq vhas_chr.16b, vdata.16b, 0 +- b.ls L(end) ++ b.lo L(end_2) + umaxp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ + fmov synd, dend + cbz synd, L(loop32) +- ++L(end_2): ++ add src, src, 16 + L(end): +- and vhas_chr.16b, vhas_chr.16b, vrepmask.16b +- addp vend.16b, vhas_chr.16b, vhas_chr.16b /* 128->64 */ +- sub src, src, 16 +- mov synd, vend.d[0] ++ shrn vend.8b, vhas_chr.8h, 4 /* 128->64 */ + sub result, src, srcin ++ fmov synd, dend + #ifndef __AARCH64EB__ + rbit synd, synd + #endif +@@ -117,10 +111,6 @@ + csel result, cntin, result, ls + ret + +-L(nomatch): +- mov result, cntin +- ret +- + END (__strnlen) + libc_hidden_def (__strnlen) + weak_alias (__strnlen, strnlen) +diff -Nuar a/sysdeps/aarch64/strrchr.S b/sysdeps/aarch64/strrchr.S +--- a/sysdeps/aarch64/strrchr.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/aarch64/strrchr.S 2025-10-20 21:02:21.000000000 +0300 +@@ -22,19 +22,16 @@ + + /* Assumptions: + * +- * ARMv8-a, AArch64 +- * Neon Available. ++ * ARMv8-a, AArch64, Advanced SIMD. + * MTE compatible. + */ + +-/* Arguments and results. */ + #define srcin x0 + #define chrin w1 + #define result x0 + + #define src x2 + #define tmp x3 +-#define wtmp w3 + #define synd x3 + #define shift x4 + #define src_match x4 +@@ -46,7 +43,6 @@ + #define vhas_nul v2 + #define vhas_chr v3 + #define vrepmask v4 +-#define vrepmask2 v5 + #define vend v5 + #define dend d5 + +@@ -58,59 +54,71 @@ + the relevant byte matched the requested character; bits 2-3 are set + if the relevant byte matched the NUL end of string. */ + +-ENTRY(strrchr) ++ENTRY (strrchr) + PTR_ARG (0) + bic src, srcin, 15 + dup vrepchr.16b, chrin +- mov wtmp, 0x3003 +- dup vrepmask.8h, wtmp +- tst srcin, 15 +- beq L(loop1) +- +- ld1 {vdata.16b}, [src], 16 ++ movi vrepmask.16b, 0x33 ++ ld1 {vdata.16b}, [src] + cmeq vhas_nul.16b, vdata.16b, 0 + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b +- mov wtmp, 0xf00f +- dup vrepmask2.8h, wtmp + bit vhas_nul.16b, vhas_chr.16b, vrepmask.16b +- and vhas_nul.16b, vhas_nul.16b, vrepmask2.16b +- addp vend.16b, vhas_nul.16b, vhas_nul.16b ++ shrn vend.8b, vhas_nul.8h, 4 + lsl shift, srcin, 2 + fmov synd, dend + lsr synd, synd, shift + lsl synd, synd, shift + ands nul_match, synd, 0xcccccccccccccccc + bne L(tail) +- cbnz synd, L(loop2) ++ cbnz synd, L(loop2_start) + +- .p2align 5 ++ .p2align 4 + L(loop1): +- ld1 {vdata.16b}, [src], 16 ++ ldr q1, [src, 16] ++ cmeq vhas_chr.16b, vdata.16b, vrepchr.16b ++ cmhs vhas_nul.16b, vhas_chr.16b, vdata.16b ++ umaxp vend.16b, vhas_nul.16b, vhas_nul.16b ++ fmov synd, dend ++ cbnz synd, L(loop1_end) ++ ldr q1, [src, 32]! + cmeq vhas_chr.16b, vdata.16b, vrepchr.16b + cmhs vhas_nul.16b, vhas_chr.16b, vdata.16b + umaxp vend.16b, vhas_nul.16b, vhas_nul.16b + fmov synd, dend + cbz synd, L(loop1) +- ++ sub src, src, 16 ++L(loop1_end): ++ add src, src, 16 + cmeq vhas_nul.16b, vdata.16b, 0 ++#ifdef __AARCH64EB__ ++ bif vhas_nul.16b, vhas_chr.16b, vrepmask.16b ++ shrn vend.8b, vhas_nul.8h, 4 ++ fmov synd, dend ++ rbit synd, synd ++#else + bit vhas_nul.16b, vhas_chr.16b, vrepmask.16b +- bic vhas_nul.8h, 0x0f, lsl 8 +- addp vend.16b, vhas_nul.16b, vhas_nul.16b ++ shrn vend.8b, vhas_nul.8h, 4 + fmov synd, dend ++#endif + ands nul_match, synd, 0xcccccccccccccccc +- beq L(loop2) +- ++ beq L(loop2_start) + L(tail): + sub nul_match, nul_match, 1 + and chr_match, synd, 0x3333333333333333 + ands chr_match, chr_match, nul_match +- sub result, src, 1 ++ add result, src, 15 + clz tmp, chr_match + sub result, result, tmp, lsr 2 + csel result, result, xzr, ne + ret + + .p2align 4 ++ nop ++ nop ++L(loop2_start): ++ add src, src, 16 ++ bic vrepmask.8h, 0xf0 ++ + L(loop2): + cmp synd, 0 + csel src_match, src, src_match, ne +diff -Nuar a/sysdeps/arc/utmp-size.h b/sysdeps/arc/utmp-size.h +--- a/sysdeps/arc/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/arc/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,3 @@ ++/* arc has less padding than other architectures with 64-bit time_t. */ ++#define UTMP_SIZE 392 ++#define LASTLOG_SIZE 296 +diff -Nuar a/sysdeps/arm/bits/wordsize.h b/sysdeps/arm/bits/wordsize.h +--- a/sysdeps/arm/bits/wordsize.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/arm/bits/wordsize.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,21 @@ ++/* Copyright (C) 1999-2024 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 ++ . */ ++ ++#define __WORDSIZE 32 ++#define __WORDSIZE_TIME64_COMPAT32 1 ++#define __WORDSIZE32_SIZE_ULONG 0 ++#define __WORDSIZE32_PTRDIFF_LONG 0 +diff -Nuar a/sysdeps/arm/find_exidx.c b/sysdeps/arm/find_exidx.c +--- a/sysdeps/arm/find_exidx.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/arm/find_exidx.c 2025-10-20 21:02:21.000000000 +0300 +@@ -15,65 +15,17 @@ + License along with the GNU C Library. If not, see + . */ + ++#include + #include +-#include +- +-struct unw_eh_callback_data +-{ +- _Unwind_Ptr pc; +- _Unwind_Ptr exidx_start; +- int exidx_len; +-}; +- +- +-/* Callback to determins if the PC lies within an object, and remember the +- location of the exception index table if it does. */ +- +-static int +-find_exidx_callback (struct dl_phdr_info * info, size_t size, void * ptr) +-{ +- struct unw_eh_callback_data * data; +- const ElfW(Phdr) *phdr; +- int i; +- int match; +- _Unwind_Ptr load_base; +- +- data = (struct unw_eh_callback_data *) ptr; +- load_base = info->dlpi_addr; +- phdr = info->dlpi_phdr; +- +- match = 0; +- for (i = info->dlpi_phnum; i > 0; i--, phdr++) +- { +- if (phdr->p_type == PT_LOAD) +- { +- _Unwind_Ptr vaddr = phdr->p_vaddr + load_base; +- if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) +- match = 1; +- } +- else if (phdr->p_type == PT_ARM_EXIDX) +- { +- data->exidx_start = (_Unwind_Ptr) (phdr->p_vaddr + load_base); +- data->exidx_len = phdr->p_memsz; +- } +- } +- +- return match; +-} +- + + /* Find the exception index table containing PC. */ + + _Unwind_Ptr + __gnu_Unwind_Find_exidx (_Unwind_Ptr pc, int * pcount) + { +- struct unw_eh_callback_data data; +- +- data.pc = pc; +- data.exidx_start = 0; +- if (__dl_iterate_phdr (find_exidx_callback, &data) <= 0) ++ struct dl_find_object data; ++ if (GLRO(dl_find_object) ((void *) pc, &data) < 0) + return 0; +- +- *pcount = data.exidx_len / 8; +- return data.exidx_start; ++ *pcount = data.dlfo_eh_count; ++ return (_Unwind_Ptr) data.dlfo_eh_frame; + } +diff -Nuar a/sysdeps/arm/utmp-size.h b/sysdeps/arm/utmp-size.h +--- a/sysdeps/arm/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/arm/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/csky/bits/wordsize.h b/sysdeps/csky/bits/wordsize.h +--- a/sysdeps/csky/bits/wordsize.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/csky/bits/wordsize.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,21 @@ ++/* Copyright (C) 1999-2024 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 ++ . */ ++ ++#define __WORDSIZE 32 ++#define __WORDSIZE_TIME64_COMPAT32 1 ++#define __WORDSIZE32_SIZE_ULONG 0 ++#define __WORDSIZE32_PTRDIFF_LONG 0 +diff -Nuar a/sysdeps/csky/utmp-size.h b/sysdeps/csky/utmp-size.h +--- a/sysdeps/csky/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/csky/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/generic/fast-jitter.h b/sysdeps/generic/fast-jitter.h +--- a/sysdeps/generic/fast-jitter.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/generic/fast-jitter.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,42 @@ ++/* Fallback for fast jitter just return 0. ++ Copyright (C) 2019-2022 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 ++ . */ ++ ++#ifndef _FAST_JITTER_H ++# define _FAST_JITTER_H ++ ++# include ++# include ++ ++/* Baseline just return 0. We could create jitter using a clock or ++ 'random_bits' but that may imply a syscall and the goal of ++ 'get_fast_jitter' is minimal overhead "randomness" when such ++ randomness helps performance. Adding high overhead the function ++ defeats the purpose. */ ++static inline uint32_t ++get_fast_jitter (void) ++{ ++# if HP_TIMING_INLINE ++ hp_timing_t jitter; ++ HP_TIMING_NOW (jitter); ++ return (uint32_t) jitter; ++# else ++ return 0; ++# endif ++} ++ ++#endif +diff -Nuar a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +--- a/sysdeps/generic/ldsodefs.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/generic/ldsodefs.h 2025-10-20 21:02:21.000000000 +0300 +@@ -105,6 +105,9 @@ + DT_PREINIT_ARRAY. */ + typedef void (*dl_init_t) (int, char **, char **); + ++/* Type of a constructor function, in DT_FINI, DT_FINI_ARRAY. */ ++typedef void (*fini_t) (void); ++ + /* On some architectures a pointer to a function is not just a pointer + to the actual code of the function but rather an architecture + specific descriptor. */ +@@ -1121,9 +1124,16 @@ + initializer functions have completed. */ + extern void _dl_fini (void) attribute_hidden; + +-/* Sort array MAPS according to dependencies of the contained objects. */ ++/* Invoke the DT_FINI_ARRAY and DT_FINI destructors for MAP, which ++ must be a struct link_map *. Can be used as an argument to ++ _dl_catch_exception. */ ++void _dl_call_fini (void *map) attribute_hidden; ++ ++/* Sort array MAPS according to dependencies of the contained objects. ++ If FORCE_FIRST, MAPS[0] keeps its place even if the dependencies ++ say otherwise. */ + extern void _dl_sort_maps (struct link_map **maps, unsigned int nmaps, +- unsigned int skip, bool for_fini) attribute_hidden; ++ bool force_first, bool for_fini) attribute_hidden; + + /* The dynamic linker calls this function before and having changing + any shared object mappings. The `r_state' member of `struct r_debug' +@@ -1254,6 +1264,11 @@ + # endif + #endif + ++/* Perform early memory allocation, avoding a TCB dependency. ++ Terminate the process if allocation fails. May attempt to use ++ brk. */ ++void *_dl_early_allocate (size_t size) attribute_hidden; ++ + /* Initialize the DSO sort algorithm to use. */ + #if !HAVE_TUNABLES + static inline void +@@ -1325,9 +1340,24 @@ + + /* Update slot information data for at least the generation of the + module with the given index. */ +-extern struct link_map *_dl_update_slotinfo (unsigned long int req_modid) ++extern struct link_map *_dl_update_slotinfo (unsigned long int req_modid, ++ size_t gen) + attribute_hidden; + ++/* The last TLS module ID that is initially loaded, plus 1. TLS ++ addresses for modules with IDs lower than that can be obtained from ++ the DTV even if its generation is outdated. */ ++extern size_t _dl_tls_initial_modid_limit attribute_hidden attribute_relro; ++ ++/* Compute _dl_tls_initial_modid_limit. To be called after initial ++ relocation. */ ++void _dl_tls_initial_modid_limit_setup (void) attribute_hidden; ++ ++/* Number of threads currently in a TLS update. This is used to ++ detect reentrant __tls_get_addr calls without a per-thread ++ flag. */ ++extern unsigned int _dl_tls_threads_in_update attribute_hidden; ++ + /* Look up the module's TLS block as for __tls_get_addr, + but never touch anything. Return null if it's not allocated yet. */ + extern void *_dl_tls_get_addr_soft (struct link_map *l) attribute_hidden; +diff -Nuar a/sysdeps/generic/libc-lock-arch.h b/sysdeps/generic/libc-lock-arch.h +--- a/sysdeps/generic/libc-lock-arch.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/generic/libc-lock-arch.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,25 @@ ++/* Private libc-internal arch-specific definitions. Generic version. ++ Copyright (C) 2022 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; see the file COPYING.LIB. If ++ not, see . */ ++ ++#ifndef _LIBC_LOCK_ARCH_H ++#define _LIBC_LOCK_ARCH_H ++ ++/* The default definition uses the natural alignment from the lock type. */ ++#define __LIBC_LOCK_ALIGNMENT ++ ++#endif +diff -Nuar a/sysdeps/generic/mremap-failure.h b/sysdeps/generic/mremap-failure.h +--- a/sysdeps/generic/mremap-failure.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/generic/mremap-failure.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,25 @@ ++/* mremap failure handling. Generic version. ++ Copyright (C) 2024 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 ++ . */ ++ ++/* Return exit value on mremap failure with errno ERR. */ ++ ++static int ++mremap_failure_exit (int err) ++{ ++ return EXIT_FAILURE; ++} +diff -Nuar a/sysdeps/generic/startup.h b/sysdeps/generic/startup.h +--- a/sysdeps/generic/startup.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/generic/startup.h 2025-10-20 21:02:21.000000000 +0300 +@@ -23,27 +23,3 @@ + + /* Use macro instead of inline function to avoid including . */ + #define _startup_fatal(message) __libc_fatal ((message)) +- +-static inline uid_t +-startup_getuid (void) +-{ +- return __getuid (); +-} +- +-static inline uid_t +-startup_geteuid (void) +-{ +- return __geteuid (); +-} +- +-static inline gid_t +-startup_getgid (void) +-{ +- return __getgid (); +-} +- +-static inline gid_t +-startup_getegid (void) +-{ +- return __getegid (); +-} +diff -Nuar a/sysdeps/generic/utmp-size.h b/sysdeps/generic/utmp-size.h +--- a/sysdeps/generic/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/generic/utmp-size.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,23 @@ ++/* Expected sizes of utmp-related structures stored in files. 64-bit version. ++ Copyright (C) 2024 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 ++ . */ ++ ++/* Expected size, in bytes, of struct utmp and struct utmpx. */ ++#define UTMP_SIZE 400 ++ ++/* Expected size, in bytes, of struct lastlog. */ ++#define LASTLOG_SIZE 296 +diff -Nuar a/sysdeps/hppa/dl-fptr.c b/sysdeps/hppa/dl-fptr.c +--- a/sysdeps/hppa/dl-fptr.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/hppa/dl-fptr.c 2025-10-20 21:02:21.000000000 +0300 +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -351,21 +352,20 @@ + { + ElfW(Addr) addr = (ElfW(Addr)) address; + ElfW(Word) reloc_arg; +- volatile unsigned int *desc; +- unsigned int *gptr; ++ unsigned int *desc, *gptr; + + /* Return ADDR if the least-significant two bits of ADDR are not consistent + with ADDR being a linker defined function pointer. The normal value for + a code address in a backtrace is 3. */ +- if (((unsigned int) addr & 3) != 2) ++ if (((uintptr_t) addr & 3) != 2) + return addr; + + /* Handle special case where ADDR points to page 0. */ +- if ((unsigned int) addr < 4096) ++ if ((uintptr_t) addr < 4096) + return addr; + + /* Clear least-significant two bits from descriptor address. */ +- desc = (unsigned int *) ((unsigned int) addr & ~3); ++ desc = (unsigned int *) ((uintptr_t) addr & ~3); + if (!_dl_read_access_allowed (desc)) + return addr; + +@@ -376,7 +376,7 @@ + /* Then load first word of candidate descriptor. It should be a pointer + with word alignment and point to memory that can be read. */ + gptr = (unsigned int *) desc[0]; +- if (((unsigned int) gptr & 3) != 0 ++ if (((uintptr_t) gptr & 3) != 0 + || !_dl_read_access_allowed (gptr)) + return addr; + +@@ -400,10 +400,11 @@ + + /* If gp has been resolved, we need to hunt for relocation offset. */ + if (!(reloc_arg & PA_GP_RELOC)) +- reloc_arg = _dl_fix_reloc_arg (addr, l); ++ reloc_arg = _dl_fix_reloc_arg ((struct fdesc *) addr, l); + + _dl_fixup (l, reloc_arg); + } + + return (ElfW(Addr)) desc[0]; + } ++rtld_hidden_def (_dl_lookup_address) +diff -Nuar a/sysdeps/hppa/dl-lookupcfg.h b/sysdeps/hppa/dl-lookupcfg.h +--- a/sysdeps/hppa/dl-lookupcfg.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/hppa/dl-lookupcfg.h 2025-10-20 21:02:21.000000000 +0300 +@@ -30,6 +30,7 @@ + #define DL_SYMBOL_ADDRESS(map, ref) _dl_symbol_address(map, ref) + + Elf32_Addr _dl_lookup_address (const void *address); ++rtld_hidden_proto (_dl_lookup_address) + + #define DL_LOOKUP_ADDRESS(addr) _dl_lookup_address ((const void *) addr) + +@@ -79,7 +80,9 @@ + /* Extract the code address from a fixup value */ + #define DL_FIXUP_VALUE_CODE_ADDR(value) ((value).ip) + #define DL_FIXUP_VALUE_ADDR(value) ((uintptr_t) &(value)) +-#define DL_FIXUP_ADDR_VALUE(addr) (*(struct fdesc *) (addr)) ++/* Clear the plabel bit to get the actual address of the descriptor. */ ++#define DL_FIXUP_ADDR_VALUE(addr) \ ++ (*(DL_FIXUP_VALUE_TYPE *) ((uintptr_t) (addr) & ~2)) + #define DL_FIXUP_BINDNOW_ADDR_VALUE(addr) (addr) +-#define DL_FIXUP_BINDNOW_RELOC(value, new_value, st_value) \ +- (*value) = *(struct fdesc *) (st_value) ++#define DL_FIXUP_BINDNOW_RELOC(value, new_value, st_value) \ ++ *(value) = *(DL_FIXUP_VALUE_TYPE *) ((uintptr_t) (new_value) & ~2) +diff -Nuar a/sysdeps/hppa/dl-machine.h b/sysdeps/hppa/dl-machine.h +--- a/sysdeps/hppa/dl-machine.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/hppa/dl-machine.h 2025-10-20 21:02:21.000000000 +0300 +@@ -176,6 +176,15 @@ + Elf32_Addr i[2]; + } sig = {{0x00,0xc0,0xff,0xee, 0xde,0xad,0xbe,0xef}}; + ++ /* Initialize dp register for main executable. */ ++ if (l->l_main_map) ++ { ++ register Elf32_Addr dp asm ("%r27"); ++ ++ dp = D_PTR (l, l_info[DT_PLTGOT]); ++ asm volatile ("" : : "r" (dp)); ++ } ++ + /* If we don't have a PLT we can just skip all this... */ + if (__builtin_expect (l->l_info[DT_JMPREL] == NULL,0)) + return lazy; +@@ -355,10 +364,6 @@ + "_start:\n" \ + /* The kernel does not give us an initial stack frame. */ \ + " ldo 64(%sp),%sp\n" \ +- /* Save the relevant arguments (yes, those are the correct \ +- registers, the kernel is weird) in their stack slots. */ \ +-" stw %r25,-40(%sp)\n" /* argc */ \ +-" stw %r24,-44(%sp)\n" /* argv */ \ + \ + /* We need the LTP, and we need it now. \ + $PIC_pcrel$0 points 8 bytes past the current instruction, \ +@@ -416,12 +421,7 @@ + So, obviously, we can't just pass %sp to _dl_start. That's \ + okay, argv-4 will do just fine. \ + \ +- The pleasant part of this is that if we need to skip \ +- arguments we can just decrement argc and move argv, because \ +- the stack pointer is utterly unrelated to the location of \ +- the environment and argument vectors. */ \ +- \ +- /* This is always within range so we'll be okay. */ \ ++ This is always within range so we'll be okay. */ \ + " bl _dl_start,%rp\n" \ + " ldo -4(%r24),%r26\n" \ + \ +@@ -431,32 +431,34 @@ + /* Save the entry point in %r3. */ \ + " copy %ret0,%r3\n" \ + \ +- /* See if we were called as a command with the executable file \ +- name as an extra leading argument. */ \ +-" addil LT'_dl_skip_args,%r19\n" \ +-" ldw RT'_dl_skip_args(%r1),%r20\n" \ +-" ldw 0(%r20),%r20\n" \ +- \ +-" ldw -40(%sp),%r25\n" /* argc */ \ +-" comib,= 0,%r20,.Lnofix\n" /* FIXME: Mispredicted branch */\ +-" ldw -44(%sp),%r24\n" /* argv (delay slot) */ \ +- \ +-" sub %r25,%r20,%r25\n" \ +-" stw %r25,-40(%sp)\n" \ +-" sh2add %r20,%r24,%r24\n" \ +-" stw %r24,-44(%sp)\n" \ ++ /* The loader adjusts argc, argv, env, and the aux vectors \ ++ directly on the stack to remove any arguments used for \ ++ direct loader invocation. Thus, argc and argv must be \ ++ reloaded from from _dl_argc and _dl_argv. */ \ + \ +-".Lnofix:\n" \ ++ /* Load main_map from _rtld_local and setup dp. */ \ + " addil LT'_rtld_local,%r19\n" \ + " ldw RT'_rtld_local(%r1),%r26\n" \ + " bl set_dp, %r2\n" \ + " ldw 0(%r26),%r26\n" \ ++" copy %ret0,%r26\n" \ ++ \ ++ /* Load argc from _dl_argc. */ \ ++" addil LT'_dl_argc,%r19\n" \ ++" ldw RT'_dl_argc(%r1),%r20\n" \ ++" ldw 0(%r20),%r25\n" \ ++" stw %r25,-40(%sp)\n" \ + \ +- /* Call _dl_init(_dl_loaded, argc, argv, envp). */ \ +-" copy %r28,%r26\n" \ ++ /* Same for argv with _dl_argv. */ \ ++" addil LT'_dl_argv,%r19\n" \ ++" ldw RT'_dl_argv(%r1),%r20\n" \ ++" ldw 0(%r20),%r24\n" \ ++" stw %r24,-44(%sp)\n" \ + \ + /* envp = argv + argc + 1 */ \ + " sh2add %r25,%r24,%r23\n" \ ++ \ ++ /* Call _dl_init(main_map, argc, argv, envp). */ \ + " bl _dl_init,%r2\n" \ + " ldo 4(%r23),%r23\n" /* delay slot */ \ + \ +diff -Nuar a/sysdeps/hppa/dl-runtime.c b/sysdeps/hppa/dl-runtime.c +--- a/sysdeps/hppa/dl-runtime.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/hppa/dl-runtime.c 2025-10-20 21:02:21.000000000 +0300 +@@ -25,8 +25,7 @@ + return that to the caller. The caller will continue on to call + _dl_fixup with the relocation offset. */ + +-ElfW(Word) +-attribute_hidden __attribute ((noinline)) DL_ARCH_FIXUP_ATTRIBUTE ++ElfW(Word) __attribute ((noinline)) DL_ARCH_FIXUP_ATTRIBUTE + _dl_fix_reloc_arg (struct fdesc *fptr, struct link_map *l) + { + Elf32_Addr l_addr, iplt, jmprel, end_jmprel, r_type; +@@ -52,3 +51,4 @@ + ABORT_INSTRUCTION; + return 0; + } ++rtld_hidden_def (_dl_fix_reloc_arg) +diff -Nuar a/sysdeps/hppa/dl-runtime.h b/sysdeps/hppa/dl-runtime.h +--- a/sysdeps/hppa/dl-runtime.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/hppa/dl-runtime.h 2025-10-20 21:02:21.000000000 +0300 +@@ -17,6 +17,9 @@ + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + ++ElfW(Word) _dl_fix_reloc_arg (struct fdesc *, struct link_map *); ++rtld_hidden_proto (_dl_fix_reloc_arg) ++ + /* Clear PA_GP_RELOC bit in relocation offset. */ + static inline uintptr_t + reloc_offset (uintptr_t plt0, uintptr_t pltn) +diff -Nuar a/sysdeps/hppa/utmp-size.h b/sysdeps/hppa/utmp-size.h +--- a/sysdeps/hppa/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/hppa/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/i386/fpu/libm-test-ulps b/sysdeps/i386/fpu/libm-test-ulps +--- a/sysdeps/i386/fpu/libm-test-ulps 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/i386/fpu/libm-test-ulps 2025-10-20 21:02:21.000000000 +0300 +@@ -668,7 +668,7 @@ + + Function: Imaginary part of "clog10": + double: 2 +-float: 1 ++float: 2 + float128: 2 + ldouble: 2 + +diff -Nuar a/sysdeps/i386/i686/fpu/multiarch/libm-test-ulps b/sysdeps/i386/i686/fpu/multiarch/libm-test-ulps +--- a/sysdeps/i386/i686/fpu/multiarch/libm-test-ulps 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/i386/i686/fpu/multiarch/libm-test-ulps 2025-10-20 21:02:21.000000000 +0300 +@@ -668,7 +668,7 @@ + + Function: Imaginary part of "clog10": + double: 2 +-float: 1 ++float: 2 + float128: 2 + ldouble: 2 + +diff -Nuar a/sysdeps/ieee754/dbl-64/math_config.h b/sysdeps/ieee754/dbl-64/math_config.h +--- a/sysdeps/ieee754/dbl-64/math_config.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/ieee754/dbl-64/math_config.h 2025-10-20 21:02:21.000000000 +0300 +@@ -134,10 +134,11 @@ + extern const struct exp_data + { + double invln2N; +- double shift; + double negln2hiN; + double negln2loN; + double poly[4]; /* Last four coefficients. */ ++ double shift; ++ + double exp2_shift; + double exp2_poly[EXP2_POLY_ORDER]; + uint64_t tab[2*(1 << EXP_TABLE_BITS)]; +diff -Nuar a/sysdeps/ieee754/dbl-64/s_expm1.c b/sysdeps/ieee754/dbl-64/s_expm1.c +--- a/sysdeps/ieee754/dbl-64/s_expm1.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/ieee754/dbl-64/s_expm1.c 2025-10-20 21:02:19.000000000 +0300 +@@ -130,6 +130,11 @@ + 4.00821782732936239552e-06, /* 3ED0CFCA 86E65239 */ + -2.01099218183624371326e-07 }; /* BE8AFDB7 6E09C32D */ + ++#ifndef SECTION ++# define SECTION ++#endif ++ ++SECTION + double + __expm1 (double x) + { +@@ -258,4 +263,6 @@ + } + return y; + } ++#ifndef __expm1 + libm_alias_double (__expm1, expm1) ++#endif +diff -Nuar a/sysdeps/ieee754/dbl-64/s_log1p.c b/sysdeps/ieee754/dbl-64/s_log1p.c +--- a/sysdeps/ieee754/dbl-64/s_log1p.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/ieee754/dbl-64/s_log1p.c 2025-10-20 21:02:19.000000000 +0300 +@@ -99,6 +99,11 @@ + + static const double zero = 0.0; + ++#ifndef SECTION ++# define SECTION ++#endif ++ ++SECTION + double + __log1p (double x) + { +diff -Nuar a/sysdeps/ieee754/flt-32/math_config.h b/sysdeps/ieee754/flt-32/math_config.h +--- a/sysdeps/ieee754/flt-32/math_config.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/ieee754/flt-32/math_config.h 2025-10-20 21:02:21.000000000 +0300 +@@ -126,9 +126,9 @@ + uint64_t tab[1 << EXP2F_TABLE_BITS]; + double shift_scaled; + double poly[EXP2F_POLY_ORDER]; +- double shift; + double invln2_scaled; + double poly_scaled[EXP2F_POLY_ORDER]; ++ double shift; + } __exp2f_data attribute_hidden; + + #define LOGF_TABLE_BITS 4 +diff -Nuar a/sysdeps/m68k/bits/wordsize.h b/sysdeps/m68k/bits/wordsize.h +--- a/sysdeps/m68k/bits/wordsize.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/m68k/bits/wordsize.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,21 @@ ++/* Copyright (C) 1999-2024 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 ++ . */ ++ ++#define __WORDSIZE 32 ++#define __WORDSIZE_TIME64_COMPAT32 1 ++#define __WORDSIZE32_SIZE_ULONG 0 ++#define __WORDSIZE32_PTRDIFF_LONG 0 +diff -Nuar a/sysdeps/m68k/dl-machine.h b/sysdeps/m68k/dl-machine.h +--- a/sysdeps/m68k/dl-machine.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/m68k/dl-machine.h 2025-10-20 21:02:21.000000000 +0300 +@@ -234,6 +234,11 @@ + + switch (r_type) + { ++ case R_68K_GLOB_DAT: ++ case R_68K_JMP_SLOT: ++ *reloc_addr = value; ++ break; ++#ifndef RTLD_BOOTSTRAP + case R_68K_COPY: + if (sym == NULL) + /* This can happen in trace mode if an object could not be +@@ -252,10 +257,6 @@ + memcpy (reloc_addr_arg, (void *) value, + MIN (sym->st_size, refsym->st_size)); + break; +- case R_68K_GLOB_DAT: +- case R_68K_JMP_SLOT: +- *reloc_addr = value; +- break; + case R_68K_8: + *(char *) reloc_addr = value + reloc->r_addend; + break; +@@ -276,7 +277,6 @@ + case R_68K_PC32: + *reloc_addr = value + reloc->r_addend - (Elf32_Addr) reloc_addr; + break; +-#ifndef RTLD_BOOTSTRAP + case R_68K_TLS_DTPMOD32: + /* Get the information from the link map returned by the + resolv function. */ +@@ -294,9 +294,9 @@ + *reloc_addr = TLS_TPREL_VALUE (sym_map, sym, reloc); + } + break; +-#endif /* !RTLD_BOOTSTRAP */ + case R_68K_NONE: /* Alright, Wilbur. */ + break; ++#endif /* !RTLD_BOOTSTRAP */ + default: + _dl_reloc_bad_type (map, r_type, 0); + break; +diff -Nuar a/sysdeps/m68k/utmp-size.h b/sysdeps/m68k/utmp-size.h +--- a/sysdeps/m68k/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/m68k/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,3 @@ ++/* m68k has 2-byte alignment. */ ++#define UTMP_SIZE 382 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/mach/getsysstats.c b/sysdeps/mach/getsysstats.c +--- a/sysdeps/mach/getsysstats.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/mach/getsysstats.c 2025-10-20 21:02:21.000000000 +0300 +@@ -62,12 +62,6 @@ + libc_hidden_def (__get_nprocs) + weak_alias (__get_nprocs, get_nprocs) + +-int +-__get_nprocs_sched (void) +-{ +- return __get_nprocs (); +-} +- + /* Return the number of physical pages on the system. */ + long int + __get_phys_pages (void) +diff -Nuar a/sysdeps/mach/hurd/bits/socket.h b/sysdeps/mach/hurd/bits/socket.h +--- a/sysdeps/mach/hurd/bits/socket.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/mach/hurd/bits/socket.h 2025-10-20 21:02:21.000000000 +0300 +@@ -249,6 +249,12 @@ + + CMSG_ALIGN (sizeof (struct cmsghdr))) + #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) + ++/* Given a length, return the additional padding necessary such that ++ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ ++#define __CMSG_PADDING(len) ((sizeof (size_t) \ ++ - ((len) & (sizeof (size_t) - 1))) \ ++ & (sizeof (size_t) - 1)) ++ + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + struct cmsghdr *__cmsg) __THROW; + #ifdef __USE_EXTERN_INLINES +@@ -258,18 +264,38 @@ + _EXTERN_INLINE struct cmsghdr * + __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) + { ++ /* We may safely assume that __cmsg lies between __mhdr->msg_control and ++ __mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of __cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; ++ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; ++ ++ size_t __size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (__cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ + return (struct cmsghdr *) 0; + ++ /* There isn't enough space between __cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) ++ < __size_needed) ++ || ((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr ++ - __size_needed) ++ < __cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; ++ ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); +- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control +- + __mhdr->msg_controllen) +- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) +- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) +- /* No more entries. */ +- return (struct cmsghdr *) 0; + return __cmsg; + } + #endif /* Use `extern inline'. */ +diff -Nuar a/sysdeps/mach/hurd/dl-sysdep.c b/sysdeps/mach/hurd/dl-sysdep.c +--- a/sysdeps/mach/hurd/dl-sysdep.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/mach/hurd/dl-sysdep.c 2025-10-20 21:02:21.000000000 +0300 +@@ -76,6 +76,7 @@ + { + void go (intptr_t *argdata) + { ++ char *orig_argv0; + char **p; + + /* Cache the information in various global variables. */ +@@ -84,6 +85,8 @@ + _environ = &_dl_argv[_dl_argc + 1]; + for (p = _environ; *p++;); /* Skip environ pointers and terminator. */ + ++ orig_argv0 = _dl_argv[0]; ++ + if ((void *) p == _dl_argv[0]) + { + static struct hurd_startup_data nodata; +@@ -173,30 +176,23 @@ + + /* The call above might screw a few things up. + +- First of all, if _dl_skip_args is nonzero, we are ignoring +- the first few arguments. However, if we have no Hurd startup +- data, it is the magical convention that ARGV[0] == P. The ++ P is the location after the terminating NULL of the list of ++ environment variables. It has to point to the Hurd startup ++ data or if that's missing then P == ARGV[0] must hold. The + startup code in init-first.c will get confused if this is not + the case, so we must rearrange things to make it so. We'll +- overwrite the origional ARGV[0] at P with ARGV[_dl_skip_args]. ++ recompute P and move the Hurd data or the new ARGV[0] there. + +- Secondly, if we need to be secure, it removes some dangerous +- environment variables. If we have no Hurd startup date this +- changes P (since that's the location after the terminating +- NULL in the list of environment variables). We do the same +- thing as in the first case but make sure we recalculate P. +- If we do have Hurd startup data, we have to move the data +- such that it starts just after the terminating NULL in the +- environment list. ++ Note: directly invoked ld.so can move arguments and env vars. + + We use memmove, since the locations might overlap. */ +- if (__libc_enable_secure || _dl_skip_args) +- { +- char **newp; + +- for (newp = _environ; *newp++;); ++ char **newp; ++ for (newp = _environ; *newp++;); + +- if (_dl_argv[-_dl_skip_args] == (char *) p) ++ if (newp != p || _dl_argv[0] != orig_argv0) ++ { ++ if (orig_argv0 == (char *) p) + { + if ((char *) newp != _dl_argv[0]) + { +diff -Nuar a/sysdeps/mach/hurd/enbl-secure.c b/sysdeps/mach/hurd/enbl-secure.c +--- a/sysdeps/mach/hurd/enbl-secure.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/mach/hurd/enbl-secure.c 1970-01-01 02:00:00.000000000 +0200 +@@ -1,30 +0,0 @@ +-/* Define and initialize the `__libc_enable_secure' flag. Hurd version. +- Copyright (C) 1998-2022 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 +- . */ +- +-/* There is no need for this file in the Hurd; it is just a placeholder +- to prevent inclusion of the sysdeps/generic version. +- In the shared library, the `__libc_enable_secure' variable is defined +- by the dynamic linker in dl-sysdep.c and set there. +- In the static library, it is defined in init-first.c and set there. */ +- +-#include +- +-void +-__libc_init_secure (void) +-{ +-} +diff -Nuar a/sysdeps/mach/hurd/i386/init-first.c b/sysdeps/mach/hurd/i386/init-first.c +--- a/sysdeps/mach/hurd/i386/init-first.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/mach/hurd/i386/init-first.c 2025-10-20 21:02:21.000000000 +0300 +@@ -38,10 +38,6 @@ + unsigned long int __hurd_threadvar_stack_offset; + unsigned long int __hurd_threadvar_stack_mask; + +-#ifndef SHARED +-int __libc_enable_secure; +-#endif +- + extern int __libc_argc attribute_hidden; + extern char **__libc_argv attribute_hidden; + extern char **_dl_argv; +diff -Nuar a/sysdeps/microblaze/bits/wordsize.h b/sysdeps/microblaze/bits/wordsize.h +--- a/sysdeps/microblaze/bits/wordsize.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/microblaze/bits/wordsize.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,21 @@ ++/* Copyright (C) 1999-2024 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 ++ . */ ++ ++#define __WORDSIZE 32 ++#define __WORDSIZE_TIME64_COMPAT32 1 ++#define __WORDSIZE32_SIZE_ULONG 0 ++#define __WORDSIZE32_PTRDIFF_LONG 0 +diff -Nuar a/sysdeps/microblaze/utmp-size.h b/sysdeps/microblaze/utmp-size.h +--- a/sysdeps/microblaze/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/microblaze/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/mips/bits/wordsize.h b/sysdeps/mips/bits/wordsize.h +--- a/sysdeps/mips/bits/wordsize.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/mips/bits/wordsize.h 2025-10-20 21:02:21.000000000 +0300 +@@ -19,11 +19,7 @@ + + #define __WORDSIZE _MIPS_SZPTR + +-#if _MIPS_SIM == _ABI64 +-# define __WORDSIZE_TIME64_COMPAT32 1 +-#else +-# define __WORDSIZE_TIME64_COMPAT32 0 +-#endif ++#define __WORDSIZE_TIME64_COMPAT32 1 + + #if __WORDSIZE == 32 + #define __WORDSIZE32_SIZE_ULONG 0 +diff -Nuar a/sysdeps/mips/utmp-size.h b/sysdeps/mips/utmp-size.h +--- a/sysdeps/mips/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/mips/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/nios2/bits/wordsize.h b/sysdeps/nios2/bits/wordsize.h +--- a/sysdeps/nios2/bits/wordsize.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/nios2/bits/wordsize.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,21 @@ ++/* Copyright (C) 1999-2024 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 ++ . */ ++ ++#define __WORDSIZE 32 ++#define __WORDSIZE_TIME64_COMPAT32 1 ++#define __WORDSIZE32_SIZE_ULONG 0 ++#define __WORDSIZE32_PTRDIFF_LONG 0 +diff -Nuar a/sysdeps/nios2/dl-machine.h b/sysdeps/nios2/dl-machine.h +--- a/sysdeps/nios2/dl-machine.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/nios2/dl-machine.h 2025-10-20 21:02:21.000000000 +0300 +@@ -129,52 +129,22 @@ + callr r8\n\ + mov gp, r2\n\ + \n\ +- /* Find the number of arguments to skip. */\n\ +- ldw r8, %got(_dl_skip_args)(r22)\n\ +- ldw r8, 0(r8)\n\ +-\n\ + /* Find the main_map from the GOT. */\n\ + ldw r4, %got(_rtld_local)(r22)\n\ + ldw r4, 0(r4)\n\ + \n\ +- /* Find argc. */\n\ +- ldw r5, 0(sp)\n\ +- sub r5, r5, r8\n\ +- stw r5, 0(sp)\n\ +-\n\ +- /* Find the first unskipped argument. */\n\ +- slli r8, r8, 2\n\ +- addi r6, sp, 4\n\ +- add r9, r6, r8\n\ +- mov r10, r6\n\ +-\n\ +- /* Shuffle argv down. */\n\ +-3: ldw r11, 0(r9)\n\ +- stw r11, 0(r10)\n\ +- addi r9, r9, 4\n\ +- addi r10, r10, 4\n\ +- bne r11, zero, 3b\n\ ++ /* Load adjusted argc. */\n\ ++ ldw r2, %got(_dl_argc)(r22)\n\ ++ ldw r5, 0(r2)\n\ + \n\ +- /* Shuffle envp down. */\n\ +- mov r7, r10\n\ +-4: ldw r11, 0(r9)\n\ +- stw r11, 0(r10)\n\ +- addi r9, r9, 4\n\ +- addi r10, r10, 4\n\ +- bne r11, zero, 4b\n\ +-\n\ +- /* Shuffle auxv down. */\n\ +-5: ldw r11, 4(r9)\n\ +- stw r11, 4(r10)\n\ +- ldw r11, 0(r9)\n\ +- stw r11, 0(r10)\n\ +- addi r9, r9, 8\n\ +- addi r10, r10, 8\n\ +- bne r11, zero, 5b\n\ +-\n\ +- /* Update _dl_argv. */\n\ ++ /* Load adjsuted argv. */\n\ + ldw r2, %got(_dl_argv)(r22)\n\ +- stw r6, 0(r2)\n\ ++ ldw r6, 0(r2)\n\ ++\n\ ++ /* envp = argv + argc + 1 */\n\ ++ addi r7, r5, 1\n\ ++ slli r7, r7, 2\n\ ++ add r7, r7, r6\n\ + \n\ + /* Call _dl_init through the PLT. */\n\ + ldw r8, %call(_dl_init)(r22)\n\ +diff -Nuar a/sysdeps/nios2/utmp-size.h b/sysdeps/nios2/utmp-size.h +--- a/sysdeps/nios2/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/nios2/utmp-size.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/nptl/dl-tls_init_tp.c b/sysdeps/nptl/dl-tls_init_tp.c +--- a/sysdeps/nptl/dl-tls_init_tp.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/nptl/dl-tls_init_tp.c 2025-10-20 21:02:21.000000000 +0300 +@@ -45,8 +45,6 @@ + #endif + + const unsigned int __rseq_flags; +-const unsigned int __rseq_size attribute_relro; +-const ptrdiff_t __rseq_offset attribute_relro; + + void + __tls_pre_init_tp (void) +@@ -106,12 +104,7 @@ + do_rseq = TUNABLE_GET (rseq, int, NULL); + #endif + if (rseq_register_current_thread (pd, do_rseq)) +- { +- /* We need a writable view of the variables. They are in +- .data.relro and are not yet write-protected. */ +- extern unsigned int size __asm__ ("__rseq_size"); +- size = sizeof (pd->rseq_area); +- } ++ _rseq_size = RSEQ_AREA_SIZE_INITIAL_USED; + + #ifdef RSEQ_SIG + /* This should be a compile-time constant, but the current +@@ -119,8 +112,7 @@ + all targets support __thread_pointer, so set __rseq_offset only + if thre rseq registration may have happened because RSEQ_SIG is + defined. */ +- extern ptrdiff_t offset __asm__ ("__rseq_offset"); +- offset = (char *) &pd->rseq_area - (char *) __thread_pointer (); ++ _rseq_offset = (char *) &pd->rseq_area - (char *) __thread_pointer (); + #endif + } + +@@ -128,7 +120,4 @@ + It will be bigger than it actually is, but for unwind.c/pt-longjmp.c + purposes this is good enough. */ + THREAD_SETMEM (pd, stackblock_size, (size_t) __libc_stack_end); +- +- THREAD_SETMEM (pd, cancelstate, PTHREAD_CANCEL_ENABLE); +- THREAD_SETMEM (pd, canceltype, PTHREAD_CANCEL_DEFERRED); + } +diff -Nuar a/sysdeps/nptl/libc-lock.h b/sysdeps/nptl/libc-lock.h +--- a/sysdeps/nptl/libc-lock.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/nptl/libc-lock.h 2025-10-20 21:02:21.000000000 +0300 +@@ -22,6 +22,7 @@ + #include + #define __need_NULL + #include ++#include + + + /* Mutex type. */ +@@ -29,7 +30,12 @@ + # if (!IS_IN (libc) && !IS_IN (libpthread)) || !defined _LIBC + typedef struct { pthread_mutex_t mutex; } __libc_lock_recursive_t; + # else +-typedef struct { int lock; int cnt; void *owner; } __libc_lock_recursive_t; ++typedef struct ++{ ++ int lock __LIBC_LOCK_ALIGNMENT; ++ int cnt; ++ void *owner; ++} __libc_lock_recursive_t; + # endif + #else + typedef struct __libc_lock_recursive_opaque__ __libc_lock_recursive_t; +diff -Nuar a/sysdeps/nptl/libc-lockP.h b/sysdeps/nptl/libc-lockP.h +--- a/sysdeps/nptl/libc-lockP.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/nptl/libc-lockP.h 2025-10-20 21:02:21.000000000 +0300 +@@ -32,9 +32,10 @@ + ld.so might be used on old kernels with a different libc.so. */ + #include + #include ++#include + + /* Mutex type. */ +-typedef int __libc_lock_t; ++typedef int __libc_lock_t __LIBC_LOCK_ALIGNMENT; + typedef struct { pthread_mutex_t mutex; } __rtld_lock_recursive_t; + typedef pthread_rwlock_t __libc_rwlock_t; + +diff -Nuar a/sysdeps/nptl/pthread_mutex_backoff.h b/sysdeps/nptl/pthread_mutex_backoff.h +--- a/sysdeps/nptl/pthread_mutex_backoff.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/nptl/pthread_mutex_backoff.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,35 @@ ++/* Pthread mutex backoff configuration. ++ Copyright (C) 2022 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 ++ . */ ++#ifndef _PTHREAD_MUTEX_BACKOFF_H ++#define _PTHREAD_MUTEX_BACKOFF_H 1 ++ ++static inline unsigned int ++get_jitter (void) ++{ ++ /* Arch dependent random jitter, return 0 disables random. */ ++ return 0; ++} ++ ++static inline int ++get_next_backoff (int backoff) ++{ ++ /* Next backoff, return 1 disables mutex backoff. */ ++ return 1; ++} ++ ++#endif +diff -Nuar a/sysdeps/nptl/pthreadP.h b/sysdeps/nptl/pthreadP.h +--- a/sysdeps/nptl/pthreadP.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/nptl/pthreadP.h 2025-10-20 21:02:21.000000000 +0300 +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include "pthread_mutex_conf.h" + + +@@ -275,7 +276,7 @@ + struct pthread *self = THREAD_SELF; + + /* Make sure we get no more cancellations. */ +- THREAD_ATOMIC_BIT_SET (self, cancelhandling, EXITING_BIT); ++ atomic_bit_set (&self->cancelhandling, EXITING_BIT); + + __pthread_unwind ((__pthread_unwind_buf_t *) + THREAD_GETMEM (self, cleanup_jmp_buf)); +diff -Nuar a/sysdeps/or1k/utmp-size.h b/sysdeps/or1k/utmp-size.h +--- a/sysdeps/or1k/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/or1k/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,3 @@ ++/* or1k has less padding than other architectures with 64-bit time_t. */ ++#define UTMP_SIZE 392 ++#define LASTLOG_SIZE 296 +diff -Nuar a/sysdeps/posix/fpathconf.c b/sysdeps/posix/fpathconf.c +--- a/sysdeps/posix/fpathconf.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/posix/fpathconf.c 2025-10-20 21:02:21.000000000 +0300 +@@ -131,9 +131,9 @@ + #ifdef _POSIX_ASYNC_IO + { + /* AIO is only allowed on regular files and block devices. */ +- struct stat64 st; ++ struct __stat64_t64 st; + +- if (__fstat64 (fd, &st) < 0 ++ if (__fstat64_time64 (fd, &st) < 0 + || (! S_ISREG (st.st_mode) && ! S_ISBLK (st.st_mode))) + return -1; + else +diff -Nuar a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c +--- a/sysdeps/posix/getaddrinfo.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/posix/getaddrinfo.c 2025-10-20 21:02:21.000000000 +0300 +@@ -100,14 +100,12 @@ + + struct gaih_servtuple + { +- struct gaih_servtuple *next; + int socktype; + int protocol; + int port; ++ bool set; + }; + +-static const struct gaih_servtuple nullserv; +- + + struct gaih_typeproto + { +@@ -118,6 +116,15 @@ + char name[8]; + }; + ++struct gaih_result ++{ ++ struct gaih_addrtuple *at; ++ char *canon; ++ char *h_name; ++ bool free_at; ++ bool got_ipv6; ++}; ++ + /* Values for `protoflag'. */ + #define GAI_PROTO_NOSERVICE 1 + #define GAI_PROTO_PROTOANY 2 +@@ -153,6 +160,15 @@ + .ai_next = NULL + }; + ++static void ++gaih_result_reset (struct gaih_result *res) ++{ ++ if (res->free_at) ++ free (res->at); ++ free (res->canon); ++ free (res->h_name); ++ memset (res, 0, sizeof (*res)); ++} + + static int + gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, +@@ -180,28 +196,21 @@ + } + while (r); + +- st->next = NULL; + st->socktype = tp->socktype; + st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) + ? req->ai_protocol : tp->protocol); + st->port = s->s_port; ++ st->set = true; + + return 0; + } + +-/* Convert struct hostent to a list of struct gaih_addrtuple objects. +- h_name is not copied, and the struct hostent object must not be +- deallocated prematurely. *RESULT must be NULL or a pointer to a +- linked-list. The new addresses are appended at the end. */ ++/* Convert struct hostent to a list of struct gaih_addrtuple objects. The new ++ addresses are appended to the tuple array in RES. */ + static bool +-convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, +- int family, +- struct hostent *h, +- struct gaih_addrtuple **result) ++convert_hostent_to_gaih_addrtuple (const struct addrinfo *req, int family, ++ struct hostent *h, struct gaih_result *res) + { +- while (*result) +- result = &(*result)->next; +- + /* Count the number of addresses in h->h_addr_list. */ + size_t count = 0; + for (char **p = h->h_addr_list; *p != NULL; ++p) +@@ -212,10 +221,41 @@ + if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr)) + return true; + +- struct gaih_addrtuple *array = calloc (count, sizeof (*array)); ++ struct gaih_addrtuple *array = res->at; ++ size_t old = 0; ++ ++ while (array != NULL) ++ { ++ old++; ++ array = array->next; ++ } ++ ++ array = realloc (res->at, (old + count) * sizeof (*array)); ++ + if (array == NULL) + return false; + ++ res->got_ipv6 = family == AF_INET6; ++ res->at = array; ++ res->free_at = true; ++ ++ /* Duplicate h_name because it may get reclaimed when the underlying storage ++ is freed. */ ++ if (res->h_name == NULL) ++ { ++ res->h_name = __strdup (h->h_name); ++ if (res->h_name == NULL) ++ return false; ++ } ++ ++ /* Update the next pointers on reallocation. */ ++ for (size_t i = 0; i < old; i++) ++ array[i].next = array + i + 1; ++ ++ array += old; ++ ++ memset (array, 0, count * sizeof (*array)); ++ + for (size_t i = 0; i < count; ++i) + { + if (family == AF_INET && req->ai_family == AF_INET6) +@@ -232,73 +272,59 @@ + } + array[i].next = array + i + 1; + } +- array[0].name = h->h_name; + array[count - 1].next = NULL; + +- *result = array; + return true; + } + +-#define gethosts(_family) \ +- { \ +- struct hostent th; \ +- char *localcanon = NULL; \ +- no_data = 0; \ +- while (1) \ +- { \ +- status = DL_CALL_FCT (fct, (name, _family, &th, \ +- tmpbuf->data, tmpbuf->length, \ +- &errno, &h_errno, NULL, &localcanon)); \ +- if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL \ +- || errno != ERANGE) \ +- break; \ +- if (!scratch_buffer_grow (tmpbuf)) \ +- { \ +- __resolv_context_put (res_ctx); \ +- result = -EAI_MEMORY; \ +- goto free_and_return; \ +- } \ +- } \ +- if (status == NSS_STATUS_NOTFOUND \ +- || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) \ +- { \ +- if (h_errno == NETDB_INTERNAL) \ +- { \ +- __resolv_context_put (res_ctx); \ +- result = -EAI_SYSTEM; \ +- goto free_and_return; \ +- } \ +- if (h_errno == TRY_AGAIN) \ +- no_data = EAI_AGAIN; \ +- else \ +- no_data = h_errno == NO_DATA; \ +- } \ +- else if (status == NSS_STATUS_SUCCESS) \ +- { \ +- if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, &addrmem)) \ +- { \ +- __resolv_context_put (res_ctx); \ +- result = -EAI_SYSTEM; \ +- goto free_and_return; \ +- } \ +- *pat = addrmem; \ +- \ +- if (localcanon != NULL && canon == NULL) \ +- { \ +- canonbuf = __strdup (localcanon); \ +- if (canonbuf == NULL) \ +- { \ +- __resolv_context_put (res_ctx); \ +- result = -EAI_SYSTEM; \ +- goto free_and_return; \ +- } \ +- canon = canonbuf; \ +- } \ +- if (_family == AF_INET6 && *pat != NULL) \ +- got_ipv6 = true; \ +- } \ +- } ++static int ++gethosts (nss_gethostbyname3_r fct, int family, const char *name, ++ const struct addrinfo *req, struct scratch_buffer *tmpbuf, ++ struct gaih_result *res, enum nss_status *statusp, int *no_datap) ++{ ++ struct hostent th; ++ char *localcanon = NULL; ++ enum nss_status status; + ++ *no_datap = 0; ++ while (1) ++ { ++ *statusp = status = DL_CALL_FCT (fct, (name, family, &th, ++ tmpbuf->data, tmpbuf->length, ++ &errno, &h_errno, NULL, ++ &localcanon)); ++ if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL ++ || errno != ERANGE) ++ break; ++ if (!scratch_buffer_grow (tmpbuf)) ++ return -EAI_MEMORY; ++ } ++ if (status == NSS_STATUS_NOTFOUND ++ || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) ++ { ++ if (h_errno == NETDB_INTERNAL) ++ return -EAI_SYSTEM; ++ if (h_errno == TRY_AGAIN) ++ *no_datap = EAI_AGAIN; ++ else ++ *no_datap = h_errno == NO_DATA; ++ } ++ else if (status == NSS_STATUS_SUCCESS) ++ { ++ if (!convert_hostent_to_gaih_addrtuple (req, family, &th, res)) ++ return -EAI_MEMORY; ++ ++ if (localcanon != NULL && res->canon == NULL) ++ { ++ char *canonbuf = __strdup (localcanon); ++ if (canonbuf == NULL) ++ return -EAI_MEMORY; ++ res->canon = canonbuf; ++ } ++ } ++ ++ return 0; ++} + + /* This function is called if a canonical name is requested, but if + the service function did not provide it. It tries to obtain the +@@ -307,15 +333,15 @@ + memory allocation failure. The returned string is allocated on the + heap; the caller has to free it. */ + static char * +-getcanonname (nss_action_list nip, struct gaih_addrtuple *at, const char *name) ++getcanonname (nss_action_list nip, const char *hname, const char *name) + { + nss_getcanonname_r *cfct = __nss_lookup_function (nip, "getcanonname_r"); + char *s = (char *) name; + if (cfct != NULL) + { + char buf[256]; +- if (DL_CALL_FCT (cfct, (at->name ?: name, buf, sizeof (buf), +- &s, &errno, &h_errno)) != NSS_STATUS_SUCCESS) ++ if (DL_CALL_FCT (cfct, (hname ?: name, buf, sizeof (buf), &s, &errno, ++ &h_errno)) != NSS_STATUS_SUCCESS) + /* If the canonical name cannot be determined, use the passed + string. */ + s = (char *) name; +@@ -323,21 +349,47 @@ + return __strdup (name); + } + ++/* Process looked up canonical name and if necessary, decode to IDNA. Result ++ is a new string written to CANONP and the earlier string is freed. */ ++ + static int +-gaih_inet (const char *name, const struct gaih_service *service, +- const struct addrinfo *req, struct addrinfo **pai, +- unsigned int *naddrs, struct scratch_buffer *tmpbuf) ++process_canonname (const struct addrinfo *req, const char *orig_name, ++ struct gaih_result *res) + { +- const struct gaih_typeproto *tp = gaih_inet_typeproto; +- struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv; +- struct gaih_addrtuple *at = NULL; +- bool got_ipv6 = false; +- const char *canon = NULL; +- const char *orig_name = name; ++ char *canon = res->canon; + +- /* Reserve stack memory for the scratch buffer in the getaddrinfo +- function. */ +- size_t alloca_used = sizeof (struct scratch_buffer); ++ if ((req->ai_flags & AI_CANONNAME) != 0) ++ { ++ bool do_idn = req->ai_flags & AI_CANONIDN; ++ if (do_idn) ++ { ++ char *out; ++ int rc = __idna_from_dns_encoding (canon ?: orig_name, &out); ++ if (rc == 0) ++ { ++ free (canon); ++ canon = out; ++ } ++ else if (rc == EAI_IDN_ENCODE) ++ /* Use the punycode name as a fallback. */ ++ do_idn = false; ++ else ++ return -rc; ++ } ++ if (!do_idn && canon == NULL && (canon = __strdup (orig_name)) == NULL) ++ return -EAI_MEMORY; ++ } ++ ++ res->canon = canon; ++ return 0; ++} ++ ++static int ++get_servtuples (const struct gaih_service *service, const struct addrinfo *req, ++ struct gaih_servtuple *st, struct scratch_buffer *tmpbuf) ++{ ++ int i; ++ const struct gaih_typeproto *tp = gaih_inet_typeproto; + + if (req->ai_protocol || req->ai_socktype) + { +@@ -359,747 +411,792 @@ + } + } + +- int port = 0; +- if (service != NULL) ++ if (service != NULL && (tp->protoflag & GAI_PROTO_NOSERVICE) != 0) ++ return -EAI_SERVICE; ++ ++ if (service == NULL || service->num >= 0) + { +- if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) +- return -EAI_SERVICE; ++ int port = service != NULL ? htons (service->num) : 0; + +- if (service->num < 0) ++ if (req->ai_socktype || req->ai_protocol) + { +- if (tp->name[0]) +- { +- st = (struct gaih_servtuple *) +- alloca_account (sizeof (struct gaih_servtuple), alloca_used); ++ st[0].socktype = tp->socktype; ++ st[0].protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) ++ ? req->ai_protocol : tp->protocol); ++ st[0].port = port; ++ st[0].set = true; + +- int rc = gaih_inet_serv (service->name, tp, req, st, tmpbuf); +- if (__glibc_unlikely (rc != 0)) +- return rc; +- } +- else +- { +- struct gaih_servtuple **pst = &st; +- for (tp++; tp->name[0]; tp++) +- { +- struct gaih_servtuple *newp; ++ return 0; ++ } ++ ++ /* Neither socket type nor protocol is set. Return all socket types ++ we know about. */ ++ for (i = 0, ++tp; tp->name[0]; ++tp) ++ if (tp->defaultflag) ++ { ++ st[i].socktype = tp->socktype; ++ st[i].protocol = tp->protocol; ++ st[i].port = port; ++ st[i++].set = true; ++ } + +- if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) +- continue; ++ return 0; ++ } + +- if (req->ai_socktype != 0 +- && req->ai_socktype != tp->socktype) +- continue; +- if (req->ai_protocol != 0 +- && !(tp->protoflag & GAI_PROTO_PROTOANY) +- && req->ai_protocol != tp->protocol) +- continue; +- +- newp = (struct gaih_servtuple *) +- alloca_account (sizeof (struct gaih_servtuple), +- alloca_used); +- +- if (gaih_inet_serv (service->name, +- tp, req, newp, tmpbuf) != 0) +- continue; ++ if (tp->name[0]) ++ return gaih_inet_serv (service->name, tp, req, st, tmpbuf); + +- *pst = newp; +- pst = &(newp->next); +- } +- if (st == (struct gaih_servtuple *) &nullserv) +- return -EAI_SERVICE; +- } +- } +- else ++ for (i = 0, tp++; tp->name[0]; tp++) ++ { ++ if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0) ++ continue; ++ ++ if (req->ai_socktype != 0 ++ && req->ai_socktype != tp->socktype) ++ continue; ++ if (req->ai_protocol != 0 ++ && !(tp->protoflag & GAI_PROTO_PROTOANY) ++ && req->ai_protocol != tp->protocol) ++ continue; ++ ++ if (gaih_inet_serv (service->name, ++ tp, req, &st[i], tmpbuf) != 0) ++ continue; ++ ++ i++; ++ } ++ ++ if (!st[0].set) ++ return -EAI_SERVICE; ++ ++ return 0; ++} ++ ++#ifdef USE_NSCD ++/* Query addresses from nscd cache, returning a non-zero value on error. ++ RES members have the lookup result; RES->AT is NULL if there were no errors ++ but also no results. */ ++ ++static int ++get_nscd_addresses (const char *name, const struct addrinfo *req, ++ struct gaih_result *res) ++{ ++ if (__nss_not_use_nscd_hosts > 0 ++ && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY) ++ __nss_not_use_nscd_hosts = 0; ++ ++ res->at = NULL; ++ ++ if (__nss_not_use_nscd_hosts || __nss_database_custom[NSS_DBSIDX_hosts]) ++ return 0; ++ ++ /* Try to use nscd. */ ++ struct nscd_ai_result *air = NULL; ++ int err = __nscd_getai (name, &air, &h_errno); ++ ++ if (__glibc_unlikely (air == NULL)) ++ { ++ /* The database contains a negative entry. */ ++ if (err == 0) ++ return -EAI_NONAME; ++ if (__nss_not_use_nscd_hosts == 0) + { +- port = htons (service->num); +- goto got_port; ++ if (h_errno == NETDB_INTERNAL && errno == ENOMEM) ++ return -EAI_MEMORY; ++ if (h_errno == TRY_AGAIN) ++ return -EAI_AGAIN; ++ return -EAI_SYSTEM; + } ++ return 0; + } +- else ++ ++ /* Transform into gaih_addrtuple list. */ ++ int result = 0; ++ char *addrs = air->addrs; ++ ++ struct gaih_addrtuple *addrfree = calloc (air->naddrs, sizeof (*addrfree)); ++ struct gaih_addrtuple *at = calloc (air->naddrs, sizeof (*at)); ++ if (at == NULL) + { +- got_port: ++ result = -EAI_MEMORY; ++ goto out; ++ } + +- if (req->ai_socktype || req->ai_protocol) ++ res->free_at = true; ++ ++ int count = 0; ++ for (int i = 0; i < air->naddrs; ++i) ++ { ++ socklen_t size = (air->family[i] == AF_INET ++ ? INADDRSZ : IN6ADDRSZ); ++ ++ if (!((air->family[i] == AF_INET ++ && req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED) != 0) ++ || req->ai_family == AF_UNSPEC ++ || air->family[i] == req->ai_family)) + { +- st = alloca_account (sizeof (struct gaih_servtuple), alloca_used); +- st->next = NULL; +- st->socktype = tp->socktype; +- st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) +- ? req->ai_protocol : tp->protocol); +- st->port = port; ++ /* Skip over non-matching result. */ ++ addrs += size; ++ continue; + } +- else ++ ++ if (air->family[i] == AF_INET && req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED)) + { +- /* Neither socket type nor protocol is set. Return all socket types +- we know about. */ +- struct gaih_servtuple **lastp = &st; +- for (++tp; tp->name[0]; ++tp) +- if (tp->defaultflag) +- { +- struct gaih_servtuple *newp; +- +- newp = alloca_account (sizeof (struct gaih_servtuple), +- alloca_used); +- newp->next = NULL; +- newp->socktype = tp->socktype; +- newp->protocol = tp->protocol; +- newp->port = port; +- +- *lastp = newp; +- lastp = &newp->next; +- } ++ at[count].family = AF_INET6; ++ at[count].addr[3] = *(uint32_t *) addrs; ++ at[count].addr[2] = htonl (0xffff); + } ++ else if (req->ai_family == AF_UNSPEC ++ || air->family[count] == req->ai_family) ++ { ++ at[count].family = air->family[count]; ++ memcpy (at[count].addr, addrs, size); ++ if (air->family[count] == AF_INET6) ++ res->got_ipv6 = true; ++ } ++ at[count].next = at + count + 1; ++ count++; ++ addrs += size; + } + +- bool malloc_name = false; +- struct gaih_addrtuple *addrmem = NULL; +- char *canonbuf = NULL; ++ if ((req->ai_flags & AI_CANONNAME) && air->canon != NULL) ++ { ++ char *canonbuf = __strdup (air->canon); ++ if (canonbuf == NULL) ++ { ++ result = -EAI_MEMORY; ++ goto out; ++ } ++ res->canon = canonbuf; ++ } ++ ++ if (count == 0) ++ { ++ result = -EAI_NONAME; ++ goto out; ++ } ++ ++ at[count - 1].next = NULL; ++ ++ res->at = at; ++ ++out: ++ free (air); ++ if (result != 0) ++ { ++ free (at); ++ res->free_at = false; ++ } ++ ++ return result; ++} ++#endif ++ ++static int ++get_nss_addresses (const char *name, const struct addrinfo *req, ++ struct scratch_buffer *tmpbuf, struct gaih_result *res) ++{ ++ int no_data = 0; ++ int no_inet6_data = 0; ++ nss_action_list nip; ++ enum nss_status inet6_status = NSS_STATUS_UNAVAIL; ++ enum nss_status status = NSS_STATUS_UNAVAIL; ++ int no_more; ++ struct resolv_context *res_ctx = NULL; ++ bool do_merge = false; + int result = 0; + +- if (name != NULL) ++ no_more = !__nss_database_get (nss_database_hosts, &nip); ++ ++ /* If we are looking for both IPv4 and IPv6 address we don't ++ want the lookup functions to automatically promote IPv4 ++ addresses to IPv6 addresses, so we use the no_inet6 ++ function variant. */ ++ res_ctx = __resolv_context_get (); ++ if (res_ctx == NULL) ++ no_more = 1; ++ ++ while (!no_more) + { +- at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); +- at->family = AF_UNSPEC; +- at->scopeid = 0; +- at->next = NULL; ++ /* Always start afresh; continue should discard previous results ++ and the hosts database does not support merge. */ ++ gaih_result_reset (res); + +- if (req->ai_flags & AI_IDN) ++ if (do_merge) + { +- char *out; +- result = __idna_to_dns_encoding (name, &out); +- if (result != 0) +- return -result; +- name = out; +- malloc_name = true; ++ __set_h_errno (NETDB_INTERNAL); ++ __set_errno (EBUSY); ++ break; + } + +- if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0) +- { +- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) +- at->family = AF_INET; +- else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED)) +- { +- at->addr[3] = at->addr[0]; +- at->addr[2] = htonl (0xffff); +- at->addr[1] = 0; +- at->addr[0] = 0; +- at->family = AF_INET6; +- } +- else +- { +- result = -EAI_ADDRFAMILY; +- goto free_and_return; +- } ++ no_data = 0; ++ nss_gethostbyname4_r *fct4 = NULL; + +- if (req->ai_flags & AI_CANONNAME) +- canon = name; +- } +- else if (at->family == AF_UNSPEC) ++ /* gethostbyname4_r sends out parallel A and AAAA queries and ++ is thus only suitable for PF_UNSPEC. */ ++ if (req->ai_family == PF_UNSPEC) ++ fct4 = __nss_lookup_function (nip, "gethostbyname4_r"); ++ ++ if (fct4 != NULL) + { +- char *scope_delim = strchr (name, SCOPE_DELIMITER); +- int e; +- if (scope_delim == NULL) +- e = inet_pton (AF_INET6, name, at->addr); +- else +- e = __inet_pton_length (AF_INET6, name, scope_delim - name, +- at->addr); +- if (e > 0) ++ while (1) + { +- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) +- at->family = AF_INET6; +- else if (req->ai_family == AF_INET +- && IN6_IS_ADDR_V4MAPPED (at->addr)) +- { +- at->addr[0] = at->addr[3]; +- at->family = AF_INET; +- } +- else ++ status = DL_CALL_FCT (fct4, (name, &res->at, ++ tmpbuf->data, tmpbuf->length, ++ &errno, &h_errno, ++ NULL)); ++ if (status == NSS_STATUS_SUCCESS) ++ break; ++ /* gethostbyname4_r may write into AT, so reset it. */ ++ res->at = NULL; ++ if (status != NSS_STATUS_TRYAGAIN ++ || errno != ERANGE || h_errno != NETDB_INTERNAL) + { +- result = -EAI_ADDRFAMILY; +- goto free_and_return; ++ if (h_errno == TRY_AGAIN) ++ no_data = EAI_AGAIN; ++ else ++ no_data = h_errno == NO_DATA; ++ break; + } + +- if (scope_delim != NULL +- && __inet6_scopeid_pton ((struct in6_addr *) at->addr, +- scope_delim + 1, +- &at->scopeid) != 0) ++ if (!scratch_buffer_grow (tmpbuf)) + { +- result = -EAI_NONAME; +- goto free_and_return; ++ __resolv_context_put (res_ctx); ++ result = -EAI_MEMORY; ++ goto out; + } +- +- if (req->ai_flags & AI_CANONNAME) +- canon = name; + } +- } + +- if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0) +- { +- struct gaih_addrtuple **pat = &at; +- int no_data = 0; +- int no_inet6_data = 0; +- nss_action_list nip; +- enum nss_status inet6_status = NSS_STATUS_UNAVAIL; +- enum nss_status status = NSS_STATUS_UNAVAIL; +- int no_more; +- struct resolv_context *res_ctx = NULL; +- +- /* If we do not have to look for IPv6 addresses or the canonical +- name, use the simple, old functions, which do not support +- IPv6 scope ids, nor retrieving the canonical name. */ +- if (req->ai_family == AF_INET +- && (req->ai_flags & AI_CANONNAME) == 0) ++ if (status == NSS_STATUS_SUCCESS) + { +- int rc; +- struct hostent th; +- struct hostent *h; ++ assert (!no_data); ++ no_data = 1; + +- while (1) ++ if ((req->ai_flags & AI_CANONNAME) != 0 && res->canon == NULL) + { +- rc = __gethostbyname2_r (name, AF_INET, &th, +- tmpbuf->data, tmpbuf->length, +- &h, &h_errno); +- if (rc != ERANGE || h_errno != NETDB_INTERNAL) +- break; +- if (!scratch_buffer_grow (tmpbuf)) ++ char *canonbuf = __strdup (res->at->name); ++ if (canonbuf == NULL) + { ++ __resolv_context_put (res_ctx); + result = -EAI_MEMORY; +- goto free_and_return; ++ goto out; + } ++ res->canon = canonbuf; + } + +- if (rc == 0) ++ struct gaih_addrtuple **pat = &res->at; ++ ++ while (*pat != NULL) + { +- if (h != NULL) ++ if ((*pat)->family == AF_INET ++ && req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED) != 0) + { +- /* We found data, convert it. */ +- if (!convert_hostent_to_gaih_addrtuple +- (req, AF_INET, h, &addrmem)) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- *pat = addrmem; ++ uint32_t *pataddr = (*pat)->addr; ++ (*pat)->family = AF_INET6; ++ pataddr[3] = pataddr[0]; ++ pataddr[2] = htonl (0xffff); ++ pataddr[1] = 0; ++ pataddr[0] = 0; ++ pat = &((*pat)->next); ++ no_data = 0; + } +- else ++ else if (req->ai_family == AF_UNSPEC ++ || (*pat)->family == req->ai_family) + { +- if (h_errno == NO_DATA) +- result = -EAI_NODATA; +- else +- result = -EAI_NONAME; +- goto free_and_return; ++ pat = &((*pat)->next); ++ ++ no_data = 0; ++ if (req->ai_family == AF_INET6) ++ res->got_ipv6 = true; + } +- } +- else +- { +- if (h_errno == NETDB_INTERNAL) +- result = -EAI_SYSTEM; +- else if (h_errno == TRY_AGAIN) +- result = -EAI_AGAIN; + else +- /* We made requests but they turned out no data. +- The name is known, though. */ +- result = -EAI_NODATA; +- +- goto free_and_return; ++ *pat = ((*pat)->next); + } +- +- goto process_list; + } + +-#ifdef USE_NSCD +- if (__nss_not_use_nscd_hosts > 0 +- && ++__nss_not_use_nscd_hosts > NSS_NSCD_RETRY) +- __nss_not_use_nscd_hosts = 0; ++ no_inet6_data = no_data; ++ } ++ else ++ { ++ nss_gethostbyname3_r *fct = NULL; ++ if (req->ai_flags & AI_CANONNAME) ++ /* No need to use this function if we do not look for ++ the canonical name. The function does not exist in ++ all NSS modules and therefore the lookup would ++ often fail. */ ++ fct = __nss_lookup_function (nip, "gethostbyname3_r"); ++ if (fct == NULL) ++ /* We are cheating here. The gethostbyname2_r ++ function does not have the same interface as ++ gethostbyname3_r but the extra arguments the ++ latter takes are added at the end. So the ++ gethostbyname2_r code will just ignore them. */ ++ fct = __nss_lookup_function (nip, "gethostbyname2_r"); + +- if (!__nss_not_use_nscd_hosts +- && !__nss_database_custom[NSS_DBSIDX_hosts]) ++ if (fct != NULL) + { +- /* Try to use nscd. */ +- struct nscd_ai_result *air = NULL; +- int err = __nscd_getai (name, &air, &h_errno); +- if (air != NULL) ++ if (req->ai_family == AF_INET6 ++ || req->ai_family == AF_UNSPEC) + { +- /* Transform into gaih_addrtuple list. */ +- bool added_canon = (req->ai_flags & AI_CANONNAME) == 0; +- char *addrs = air->addrs; +- +- addrmem = calloc (air->naddrs, sizeof (*addrmem)); +- if (addrmem == NULL) ++ if ((result = gethosts (fct, AF_INET6, name, req, tmpbuf, ++ res, &status, &no_data)) != 0) + { +- result = -EAI_MEMORY; +- goto free_and_return; ++ __resolv_context_put (res_ctx); ++ goto out; + } +- +- struct gaih_addrtuple *addrfree = addrmem; +- for (int i = 0; i < air->naddrs; ++i) ++ no_inet6_data = no_data; ++ inet6_status = status; ++ } ++ if (req->ai_family == AF_INET ++ || req->ai_family == AF_UNSPEC ++ || (req->ai_family == AF_INET6 ++ && (req->ai_flags & AI_V4MAPPED) ++ /* Avoid generating the mapped addresses if we ++ know we are not going to need them. */ ++ && ((req->ai_flags & AI_ALL) || !res->got_ipv6))) ++ { ++ if ((result = gethosts (fct, AF_INET, name, req, tmpbuf, ++ res, &status, &no_data)) != 0) + { +- socklen_t size = (air->family[i] == AF_INET +- ? INADDRSZ : IN6ADDRSZ); +- +- if (!((air->family[i] == AF_INET +- && req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED) != 0) +- || req->ai_family == AF_UNSPEC +- || air->family[i] == req->ai_family)) +- { +- /* Skip over non-matching result. */ +- addrs += size; +- continue; +- } +- +- if (*pat == NULL) +- { +- *pat = addrfree++; +- (*pat)->scopeid = 0; +- } +- uint32_t *pataddr = (*pat)->addr; +- (*pat)->next = NULL; +- if (added_canon || air->canon == NULL) +- (*pat)->name = NULL; +- else if (canonbuf == NULL) +- { +- canonbuf = __strdup (air->canon); +- if (canonbuf == NULL) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- canon = (*pat)->name = canonbuf; +- } +- +- if (air->family[i] == AF_INET +- && req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED)) +- { +- (*pat)->family = AF_INET6; +- pataddr[3] = *(uint32_t *) addrs; +- pataddr[2] = htonl (0xffff); +- pataddr[1] = 0; +- pataddr[0] = 0; +- pat = &((*pat)->next); +- added_canon = true; +- } +- else if (req->ai_family == AF_UNSPEC +- || air->family[i] == req->ai_family) +- { +- (*pat)->family = air->family[i]; +- memcpy (pataddr, addrs, size); +- pat = &((*pat)->next); +- added_canon = true; +- if (air->family[i] == AF_INET6) +- got_ipv6 = true; +- } +- addrs += size; ++ __resolv_context_put (res_ctx); ++ goto out; + } + +- free (air); +- +- if (at->family == AF_UNSPEC) ++ if (req->ai_family == AF_INET) + { +- result = -EAI_NONAME; +- goto free_and_return; ++ no_inet6_data = no_data; ++ inet6_status = status; + } +- +- goto process_list; + } +- else if (err == 0) +- /* The database contains a negative entry. */ +- goto free_and_return; +- else if (__nss_not_use_nscd_hosts == 0) +- { +- if (h_errno == NETDB_INTERNAL && errno == ENOMEM) +- result = -EAI_MEMORY; +- else if (h_errno == TRY_AGAIN) +- result = -EAI_AGAIN; +- else +- result = -EAI_SYSTEM; +- +- goto free_and_return; +- } +- } +-#endif +- +- no_more = !__nss_database_get (nss_database_hosts, &nip); +- +- /* If we are looking for both IPv4 and IPv6 address we don't +- want the lookup functions to automatically promote IPv4 +- addresses to IPv6 addresses, so we use the no_inet6 +- function variant. */ +- res_ctx = __resolv_context_get (); +- if (res_ctx == NULL) +- no_more = 1; +- +- while (!no_more) +- { +- no_data = 0; +- nss_gethostbyname4_r *fct4 = NULL; +- +- /* gethostbyname4_r sends out parallel A and AAAA queries and +- is thus only suitable for PF_UNSPEC. */ +- if (req->ai_family == PF_UNSPEC) +- fct4 = __nss_lookup_function (nip, "gethostbyname4_r"); + +- if (fct4 != NULL) ++ /* If we found one address for AF_INET or AF_INET6, ++ don't continue the search. */ ++ if (inet6_status == NSS_STATUS_SUCCESS ++ || status == NSS_STATUS_SUCCESS) + { +- while (1) ++ if ((req->ai_flags & AI_CANONNAME) != 0 ++ && res->canon == NULL) + { +- status = DL_CALL_FCT (fct4, (name, pat, +- tmpbuf->data, tmpbuf->length, +- &errno, &h_errno, +- NULL)); +- if (status == NSS_STATUS_SUCCESS) +- break; +- if (status != NSS_STATUS_TRYAGAIN +- || errno != ERANGE || h_errno != NETDB_INTERNAL) +- { +- if (h_errno == TRY_AGAIN) +- no_data = EAI_AGAIN; +- else +- no_data = h_errno == NO_DATA; +- break; +- } +- +- if (!scratch_buffer_grow (tmpbuf)) ++ char *canonbuf = getcanonname (nip, res->h_name, name); ++ if (canonbuf == NULL) + { + __resolv_context_put (res_ctx); + result = -EAI_MEMORY; +- goto free_and_return; ++ goto out; + } ++ res->canon = canonbuf; + } ++ status = NSS_STATUS_SUCCESS; ++ } ++ else ++ { ++ /* We can have different states for AF_INET and ++ AF_INET6. Try to find a useful one for both. */ ++ if (inet6_status == NSS_STATUS_TRYAGAIN) ++ status = NSS_STATUS_TRYAGAIN; ++ else if (status == NSS_STATUS_UNAVAIL ++ && inet6_status != NSS_STATUS_UNAVAIL) ++ status = inet6_status; ++ } ++ } ++ else ++ { ++ /* Could not locate any of the lookup functions. ++ The NSS lookup code does not consistently set ++ errno, so we need to supply our own error ++ code here. The root cause could either be a ++ resource allocation failure, or a missing ++ service function in the DSO (so it should not ++ be listed in /etc/nsswitch.conf). Assume the ++ former, and return EBUSY. */ ++ status = NSS_STATUS_UNAVAIL; ++ __set_h_errno (NETDB_INTERNAL); ++ __set_errno (EBUSY); ++ } ++ } + +- if (status == NSS_STATUS_SUCCESS) +- { +- assert (!no_data); +- no_data = 1; ++ if (nss_next_action (nip, status) == NSS_ACTION_RETURN) ++ break; + +- if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL) +- canon = (*pat)->name; ++ /* The hosts database does not support MERGE. */ ++ if (nss_next_action (nip, status) == NSS_ACTION_MERGE) ++ do_merge = true; ++ ++ nip++; ++ if (nip->module == NULL) ++ no_more = -1; ++ } + +- while (*pat != NULL) +- { +- if ((*pat)->family == AF_INET +- && req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED) != 0) +- { +- uint32_t *pataddr = (*pat)->addr; +- (*pat)->family = AF_INET6; +- pataddr[3] = pataddr[0]; +- pataddr[2] = htonl (0xffff); +- pataddr[1] = 0; +- pataddr[0] = 0; +- pat = &((*pat)->next); +- no_data = 0; +- } +- else if (req->ai_family == AF_UNSPEC +- || (*pat)->family == req->ai_family) +- { +- pat = &((*pat)->next); +- +- no_data = 0; +- if (req->ai_family == AF_INET6) +- got_ipv6 = true; +- } +- else +- *pat = ((*pat)->next); +- } +- } ++ __resolv_context_put (res_ctx); + +- no_inet6_data = no_data; +- } +- else +- { +- nss_gethostbyname3_r *fct = NULL; +- if (req->ai_flags & AI_CANONNAME) +- /* No need to use this function if we do not look for +- the canonical name. The function does not exist in +- all NSS modules and therefore the lookup would +- often fail. */ +- fct = __nss_lookup_function (nip, "gethostbyname3_r"); +- if (fct == NULL) +- /* We are cheating here. The gethostbyname2_r +- function does not have the same interface as +- gethostbyname3_r but the extra arguments the +- latter takes are added at the end. So the +- gethostbyname2_r code will just ignore them. */ +- fct = __nss_lookup_function (nip, "gethostbyname2_r"); ++ /* If we have a failure which sets errno, report it using ++ EAI_SYSTEM. */ ++ if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) ++ && h_errno == NETDB_INTERNAL) ++ { ++ result = -EAI_SYSTEM; ++ goto out; ++ } + +- if (fct != NULL) +- { +- if (req->ai_family == AF_INET6 +- || req->ai_family == AF_UNSPEC) +- { +- gethosts (AF_INET6); +- no_inet6_data = no_data; +- inet6_status = status; +- } +- if (req->ai_family == AF_INET +- || req->ai_family == AF_UNSPEC +- || (req->ai_family == AF_INET6 +- && (req->ai_flags & AI_V4MAPPED) +- /* Avoid generating the mapped addresses if we +- know we are not going to need them. */ +- && ((req->ai_flags & AI_ALL) || !got_ipv6))) +- { +- gethosts (AF_INET); ++ if (no_data != 0 && no_inet6_data != 0) ++ { ++ /* If both requests timed out report this. */ ++ if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) ++ result = -EAI_AGAIN; ++ else ++ /* We made requests but they turned out no data. The name ++ is known, though. */ ++ result = -EAI_NODATA; ++ } + +- if (req->ai_family == AF_INET) +- { +- no_inet6_data = no_data; +- inet6_status = status; +- } +- } ++out: ++ if (result != 0) ++ gaih_result_reset (res); ++ return result; ++} + +- /* If we found one address for AF_INET or AF_INET6, +- don't continue the search. */ +- if (inet6_status == NSS_STATUS_SUCCESS +- || status == NSS_STATUS_SUCCESS) +- { +- if ((req->ai_flags & AI_CANONNAME) != 0 +- && canon == NULL) +- { +- canonbuf = getcanonname (nip, at, name); +- if (canonbuf == NULL) +- { +- __resolv_context_put (res_ctx); +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- canon = canonbuf; +- } +- status = NSS_STATUS_SUCCESS; +- } +- else +- { +- /* We can have different states for AF_INET and +- AF_INET6. Try to find a useful one for both. */ +- if (inet6_status == NSS_STATUS_TRYAGAIN) +- status = NSS_STATUS_TRYAGAIN; +- else if (status == NSS_STATUS_UNAVAIL +- && inet6_status != NSS_STATUS_UNAVAIL) +- status = inet6_status; +- } +- } +- else +- { +- /* Could not locate any of the lookup functions. +- The NSS lookup code does not consistently set +- errno, so we need to supply our own error +- code here. The root cause could either be a +- resource allocation failure, or a missing +- service function in the DSO (so it should not +- be listed in /etc/nsswitch.conf). Assume the +- former, and return EBUSY. */ +- status = NSS_STATUS_UNAVAIL; +- __set_h_errno (NETDB_INTERNAL); +- __set_errno (EBUSY); +- } +- } ++/* Convert numeric addresses to binary into RES. On failure, RES->AT is set to ++ NULL and an error code is returned. If AI_NUMERIC_HOST is not requested and ++ the function cannot determine a result, RES->AT is set to NULL and 0 ++ returned. */ + +- if (nss_next_action (nip, status) == NSS_ACTION_RETURN) +- break; ++static int ++text_to_binary_address (const char *name, const struct addrinfo *req, ++ struct gaih_result *res) ++{ ++ struct gaih_addrtuple *at = res->at; ++ int result = 0; + +- nip++; +- if (nip->module == NULL) +- no_more = -1; +- } ++ assert (at != NULL); + +- __resolv_context_put (res_ctx); ++ memset (at->addr, 0, sizeof (at->addr)); ++ if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0) ++ { ++ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) ++ at->family = AF_INET; ++ else if (req->ai_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED)) ++ { ++ at->addr[3] = at->addr[0]; ++ at->addr[2] = htonl (0xffff); ++ at->addr[1] = 0; ++ at->addr[0] = 0; ++ at->family = AF_INET6; ++ } ++ else ++ { ++ result = -EAI_ADDRFAMILY; ++ goto out; ++ } + +- /* If we have a failure which sets errno, report it using +- EAI_SYSTEM. */ +- if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL) +- && h_errno == NETDB_INTERNAL) ++ if (req->ai_flags & AI_CANONNAME) ++ { ++ char *canonbuf = __strdup (name); ++ if (canonbuf == NULL) + { +- result = -EAI_SYSTEM; +- goto free_and_return; ++ result = -EAI_MEMORY; ++ goto out; + } ++ res->canon = canonbuf; ++ } ++ return 0; ++ } + +- if (no_data != 0 && no_inet6_data != 0) +- { +- /* If both requests timed out report this. */ +- if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) +- result = -EAI_AGAIN; +- else +- /* We made requests but they turned out no data. The name +- is known, though. */ +- result = -EAI_NODATA; ++ char *scope_delim = strchr (name, SCOPE_DELIMITER); ++ int e; + +- goto free_and_return; +- } ++ if (scope_delim == NULL) ++ e = inet_pton (AF_INET6, name, at->addr); ++ else ++ e = __inet_pton_length (AF_INET6, name, scope_delim - name, at->addr); ++ ++ if (e > 0) ++ { ++ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) ++ at->family = AF_INET6; ++ else if (req->ai_family == AF_INET ++ && IN6_IS_ADDR_V4MAPPED (at->addr)) ++ { ++ at->addr[0] = at->addr[3]; ++ at->family = AF_INET; ++ } ++ else ++ { ++ result = -EAI_ADDRFAMILY; ++ goto out; + } + +- process_list: +- if (at->family == AF_UNSPEC) ++ if (scope_delim != NULL ++ && __inet6_scopeid_pton ((struct in6_addr *) at->addr, ++ scope_delim + 1, &at->scopeid) != 0) + { + result = -EAI_NONAME; +- goto free_and_return; ++ goto out; + } ++ ++ if (req->ai_flags & AI_CANONNAME) ++ { ++ char *canonbuf = __strdup (name); ++ if (canonbuf == NULL) ++ { ++ result = -EAI_MEMORY; ++ goto out; ++ } ++ res->canon = canonbuf; ++ } ++ return 0; + } +- else ++ ++ if ((req->ai_flags & AI_NUMERICHOST)) ++ result = -EAI_NONAME; ++ ++out: ++ res->at = NULL; ++ return result; ++} ++ ++/* If possible, call the simple, old functions, which do not support IPv6 scope ++ ids, nor retrieving the canonical name. */ ++ ++static int ++try_simple_gethostbyname (const char *name, const struct addrinfo *req, ++ struct scratch_buffer *tmpbuf, ++ struct gaih_result *res) ++{ ++ res->at = NULL; ++ ++ if (req->ai_family != AF_INET || (req->ai_flags & AI_CANONNAME) != 0) ++ return 0; ++ ++ int rc; ++ struct hostent th; ++ struct hostent *h; ++ ++ while (1) + { +- struct gaih_addrtuple *atr; +- atr = at = alloca_account (sizeof (struct gaih_addrtuple), alloca_used); +- memset (at, '\0', sizeof (struct gaih_addrtuple)); ++ rc = __gethostbyname2_r (name, AF_INET, &th, tmpbuf->data, ++ tmpbuf->length, &h, &h_errno); ++ if (rc != ERANGE || h_errno != NETDB_INTERNAL) ++ break; ++ if (!scratch_buffer_grow (tmpbuf)) ++ return -EAI_MEMORY; ++ } + +- if (req->ai_family == AF_UNSPEC) ++ if (rc == 0) ++ { ++ if (h != NULL) + { +- at->next = __alloca (sizeof (struct gaih_addrtuple)); +- memset (at->next, '\0', sizeof (struct gaih_addrtuple)); ++ /* We found data, convert it. RES->AT from the conversion will ++ either be an allocated block or NULL, both of which are safe to ++ pass to free (). */ ++ if (!convert_hostent_to_gaih_addrtuple (req, AF_INET, h, res)) ++ return -EAI_MEMORY; ++ ++ res->free_at = true; ++ return 0; + } ++ if (h_errno == NO_DATA) ++ return -EAI_NODATA; + +- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) ++ return -EAI_NONAME; ++ } ++ ++ if (h_errno == NETDB_INTERNAL) ++ return -EAI_SYSTEM; ++ if (h_errno == TRY_AGAIN) ++ return -EAI_AGAIN; ++ ++ /* We made requests but they turned out no data. ++ The name is known, though. */ ++ return -EAI_NODATA; ++} ++ ++/* Add local address information into RES. RES->AT is assumed to have enough ++ space for two tuples and is zeroed out. */ ++ ++static void ++get_local_addresses (const struct addrinfo *req, struct gaih_result *res) ++{ ++ struct gaih_addrtuple *atr = res->at; ++ if (req->ai_family == AF_UNSPEC) ++ res->at->next = res->at + 1; ++ ++ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) ++ { ++ res->at->family = AF_INET6; ++ if ((req->ai_flags & AI_PASSIVE) == 0) ++ memcpy (res->at->addr, &in6addr_loopback, sizeof (struct in6_addr)); ++ atr = res->at->next; ++ } ++ ++ if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) ++ { ++ atr->family = AF_INET; ++ if ((req->ai_flags & AI_PASSIVE) == 0) ++ atr->addr[0] = htonl (INADDR_LOOPBACK); ++ } ++} ++ ++/* Generate results in PAI and its count in NADDRS. Return 0 on success or an ++ error code on failure. */ ++ ++static int ++generate_addrinfo (const struct addrinfo *req, struct gaih_result *res, ++ const struct gaih_servtuple *st, struct addrinfo **pai, ++ unsigned int *naddrs) ++{ ++ size_t socklen; ++ sa_family_t family; ++ ++ /* Buffer is the size of an unformatted IPv6 address in printable format. */ ++ for (struct gaih_addrtuple *at = res->at; at != NULL; at = at->next) ++ { ++ family = at->family; ++ if (family == AF_INET6) + { +- at->family = AF_INET6; +- if ((req->ai_flags & AI_PASSIVE) == 0) +- memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr)); +- atr = at->next; ++ socklen = sizeof (struct sockaddr_in6); ++ ++ /* If we looked up IPv4 mapped address discard them here if ++ the caller isn't interested in all address and we have ++ found at least one IPv6 address. */ ++ if (res->got_ipv6 ++ && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED ++ && IN6_IS_ADDR_V4MAPPED (at->addr)) ++ continue; + } ++ else ++ socklen = sizeof (struct sockaddr_in); + +- if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET) ++ for (int i = 0; st[i].set; i++) + { +- atr->family = AF_INET; +- if ((req->ai_flags & AI_PASSIVE) == 0) +- atr->addr[0] = htonl (INADDR_LOOPBACK); ++ struct addrinfo *ai; ++ ai = *pai = malloc (sizeof (struct addrinfo) + socklen); ++ if (ai == NULL) ++ return -EAI_MEMORY; ++ ++ ai->ai_flags = req->ai_flags; ++ ai->ai_family = family; ++ ai->ai_socktype = st[i].socktype; ++ ai->ai_protocol = st[i].protocol; ++ ai->ai_addrlen = socklen; ++ ai->ai_addr = (void *) (ai + 1); ++ ++ /* We only add the canonical name once. */ ++ ai->ai_canonname = res->canon; ++ res->canon = NULL; ++ ++#ifdef _HAVE_SA_LEN ++ ai->ai_addr->sa_len = socklen; ++#endif /* _HAVE_SA_LEN */ ++ ai->ai_addr->sa_family = family; ++ ++ /* In case of an allocation error the list must be NULL ++ terminated. */ ++ ai->ai_next = NULL; ++ ++ if (family == AF_INET6) ++ { ++ struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) ai->ai_addr; ++ sin6p->sin6_port = st[i].port; ++ sin6p->sin6_flowinfo = 0; ++ memcpy (&sin6p->sin6_addr, at->addr, sizeof (struct in6_addr)); ++ sin6p->sin6_scope_id = at->scopeid; ++ } ++ else ++ { ++ struct sockaddr_in *sinp = (struct sockaddr_in *) ai->ai_addr; ++ sinp->sin_port = st[i].port; ++ memcpy (&sinp->sin_addr, at->addr, sizeof (struct in_addr)); ++ memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); ++ } ++ ++ pai = &(ai->ai_next); + } ++ ++ ++*naddrs; + } ++ return 0; ++} + +- { +- struct gaih_servtuple *st2; +- struct gaih_addrtuple *at2 = at; +- size_t socklen; +- sa_family_t family; +- +- /* +- buffer is the size of an unformatted IPv6 address in printable format. +- */ +- while (at2 != NULL) +- { +- /* Only the first entry gets the canonical name. */ +- if (at2 == at && (req->ai_flags & AI_CANONNAME) != 0) +- { +- if (canon == NULL) +- /* If the canonical name cannot be determined, use +- the passed in string. */ +- canon = orig_name; +- +- bool do_idn = req->ai_flags & AI_CANONIDN; +- if (do_idn) +- { +- char *out; +- int rc = __idna_from_dns_encoding (canon, &out); +- if (rc == 0) +- canon = out; +- else if (rc == EAI_IDN_ENCODE) +- /* Use the punycode name as a fallback. */ +- do_idn = false; +- else +- { +- result = -rc; +- goto free_and_return; +- } +- } +- if (!do_idn) +- { +- if (canonbuf != NULL) +- /* We already allocated the string using malloc, but +- the buffer is now owned by canon. */ +- canonbuf = NULL; +- else +- { +- canon = __strdup (canon); +- if (canon == NULL) +- { +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- } +- } +- } ++static int ++gaih_inet (const char *name, const struct gaih_service *service, ++ const struct addrinfo *req, struct addrinfo **pai, ++ unsigned int *naddrs, struct scratch_buffer *tmpbuf) ++{ ++ struct gaih_servtuple st[sizeof (gaih_inet_typeproto) ++ / sizeof (struct gaih_typeproto)] = {0}; + +- family = at2->family; +- if (family == AF_INET6) +- { +- socklen = sizeof (struct sockaddr_in6); ++ const char *orig_name = name; + +- /* If we looked up IPv4 mapped address discard them here if +- the caller isn't interested in all address and we have +- found at least one IPv6 address. */ +- if (got_ipv6 +- && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED +- && IN6_IS_ADDR_V4MAPPED (at2->addr)) +- goto ignore; +- } +- else +- socklen = sizeof (struct sockaddr_in); ++ int rc; ++ if ((rc = get_servtuples (service, req, st, tmpbuf)) != 0) ++ return rc; + +- for (st2 = st; st2 != NULL; st2 = st2->next) +- { +- struct addrinfo *ai; +- ai = *pai = malloc (sizeof (struct addrinfo) + socklen); +- if (ai == NULL) +- { +- free ((char *) canon); +- result = -EAI_MEMORY; +- goto free_and_return; +- } +- +- ai->ai_flags = req->ai_flags; +- ai->ai_family = family; +- ai->ai_socktype = st2->socktype; +- ai->ai_protocol = st2->protocol; +- ai->ai_addrlen = socklen; +- ai->ai_addr = (void *) (ai + 1); +- +- /* We only add the canonical name once. */ +- ai->ai_canonname = (char *) canon; +- canon = NULL; ++ bool malloc_name = false; ++ struct gaih_addrtuple *addrmem = NULL; ++ int result = 0; + +-#ifdef _HAVE_SA_LEN +- ai->ai_addr->sa_len = socklen; +-#endif /* _HAVE_SA_LEN */ +- ai->ai_addr->sa_family = family; ++ struct gaih_result res = {0}; ++ struct gaih_addrtuple local_at[2] = {0}; + +- /* In case of an allocation error the list must be NULL +- terminated. */ +- ai->ai_next = NULL; +- +- if (family == AF_INET6) +- { +- struct sockaddr_in6 *sin6p = +- (struct sockaddr_in6 *) ai->ai_addr; +- +- sin6p->sin6_port = st2->port; +- sin6p->sin6_flowinfo = 0; +- memcpy (&sin6p->sin6_addr, +- at2->addr, sizeof (struct in6_addr)); +- sin6p->sin6_scope_id = at2->scopeid; +- } +- else +- { +- struct sockaddr_in *sinp = +- (struct sockaddr_in *) ai->ai_addr; +- sinp->sin_port = st2->port; +- memcpy (&sinp->sin_addr, +- at2->addr, sizeof (struct in_addr)); +- memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero)); +- } ++ res.at = local_at; + +- pai = &(ai->ai_next); +- } ++ if (__glibc_unlikely (name == NULL)) ++ { ++ get_local_addresses (req, &res); ++ goto process_list; ++ } ++ ++ if (req->ai_flags & AI_IDN) ++ { ++ char *out; ++ result = __idna_to_dns_encoding (name, &out); ++ if (result != 0) ++ return -result; ++ name = out; ++ malloc_name = true; ++ } ++ ++ if ((result = text_to_binary_address (name, req, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; ++ ++ if ((result = try_simple_gethostbyname (name, req, tmpbuf, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; ++ ++#ifdef USE_NSCD ++ if ((result = get_nscd_addresses (name, req, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; ++#endif + +- ++*naddrs; ++ if ((result = get_nss_addresses (name, req, tmpbuf, &res)) != 0) ++ goto free_and_return; ++ else if (res.at != NULL) ++ goto process_list; ++ ++ /* None of the lookups worked, so name not found. */ ++ result = -EAI_NONAME; ++ goto free_and_return; ++ ++process_list: ++ /* Set up the canonical name if we need it. */ ++ if ((result = process_canonname (req, orig_name, &res)) != 0) ++ goto free_and_return; + +- ignore: +- at2 = at2->next; +- } +- } ++ result = generate_addrinfo (req, &res, st, pai, naddrs); + +- free_and_return: ++free_and_return: + if (malloc_name) + free ((char *) name); + free (addrmem); +- free (canonbuf); ++ gaih_result_reset (&res); + + return result; + } +diff -Nuar a/sysdeps/posix/isfdtype.c b/sysdeps/posix/isfdtype.c +--- a/sysdeps/posix/isfdtype.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/posix/isfdtype.c 2025-10-20 21:02:21.000000000 +0300 +@@ -24,12 +24,12 @@ + int + isfdtype (int fildes, int fdtype) + { +- struct stat64 st; ++ struct __stat64_t64 st; + int result; + + { + int save_error = errno; +- result = __fstat64 (fildes, &st); ++ result = __fstat64_time64 (fildes, &st); + __set_errno (save_error); + } + +diff -Nuar a/sysdeps/posix/libc_fatal.c b/sysdeps/posix/libc_fatal.c +--- a/sysdeps/posix/libc_fatal.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/posix/libc_fatal.c 2025-10-20 21:02:21.000000000 +0300 +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -125,8 +126,8 @@ + + if ((action & do_abort)) + { +- total = ((total + 1 + GLRO(dl_pagesize) - 1) +- & ~(GLRO(dl_pagesize) - 1)); ++ 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); +diff -Nuar a/sysdeps/posix/posix_fallocate64.c b/sysdeps/posix/posix_fallocate64.c +--- a/sysdeps/posix/posix_fallocate64.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/posix/posix_fallocate64.c 2025-10-20 21:02:21.000000000 +0300 +@@ -30,7 +30,7 @@ + int + __posix_fallocate64_l64 (int fd, __off64_t offset, __off64_t len) + { +- struct stat64 st; ++ struct __stat64_t64 st; + + if (offset < 0 || len < 0) + return EINVAL; +@@ -48,7 +48,7 @@ + } + + /* We have to make sure that this is really a regular file. */ +- if (__fstat64 (fd, &st) != 0) ++ if (__fstat64_time64 (fd, &st) != 0) + return EBADF; + if (S_ISFIFO (st.st_mode)) + return ESPIPE; +diff -Nuar a/sysdeps/posix/posix_fallocate.c b/sysdeps/posix/posix_fallocate.c +--- a/sysdeps/posix/posix_fallocate.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/posix/posix_fallocate.c 2025-10-20 21:02:21.000000000 +0300 +@@ -30,7 +30,7 @@ + int + posix_fallocate (int fd, __off_t offset, __off_t len) + { +- struct stat64 st; ++ struct __stat64_t64 st; + + if (offset < 0 || len < 0) + return EINVAL; +@@ -48,7 +48,7 @@ + } + + /* We have to make sure that this is really a regular file. */ +- if (__fstat64 (fd, &st) != 0) ++ if (__fstat64_time64 (fd, &st) != 0) + return EBADF; + if (S_ISFIFO (st.st_mode)) + return ESPIPE; +diff -Nuar a/sysdeps/posix/system.c b/sysdeps/posix/system.c +--- a/sysdeps/posix/system.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/posix/system.c 2025-10-20 21:02:21.000000000 +0300 +@@ -179,16 +179,16 @@ + as if the shell had terminated using _exit(127). */ + status = W_EXITCODE (127, 0); + ++ /* sigaction can not fail with SIGINT/SIGQUIT used with old ++ disposition. Same applies for sigprocmask. */ + DO_LOCK (); + if (SUB_REF () == 0) + { +- /* sigaction can not fail with SIGINT/SIGQUIT used with old +- disposition. Same applies for sigprocmask. */ + __sigaction (SIGINT, &intr, NULL); + __sigaction (SIGQUIT, &quit, NULL); +- __sigprocmask (SIG_SETMASK, &omask, NULL); + } + DO_UNLOCK (); ++ __sigprocmask (SIG_SETMASK, &omask, NULL); + + if (ret != 0) + __set_errno (ret); +diff -Nuar a/sysdeps/powerpc/mod-tlsopt-powerpc.c b/sysdeps/powerpc/mod-tlsopt-powerpc.c +--- a/sysdeps/powerpc/mod-tlsopt-powerpc.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/powerpc/mod-tlsopt-powerpc.c 2025-10-20 21:02:19.000000000 +0300 +@@ -22,7 +22,11 @@ + tls_index *tls_arg; + #ifdef __powerpc64__ + register unsigned long thread_pointer __asm__ ("r13"); +- asm ("addi %0,2,foo@got@tlsgd" : "=r" (tls_arg)); ++# ifdef __PCREL__ ++ asm ("paddi %0,0,foo@got@tlsgd@pcrel,1" : "=b" (tls_arg)); ++# else ++ asm ("addi %0,2,foo@got@tlsgd" : "=b" (tls_arg)); ++# endif + #else + register unsigned long thread_pointer __asm__ ("r2"); + asm ("bcl 20,31,1f\n1:\t" +diff -Nuar a/sysdeps/powerpc/powerpc32/bits/wordsize.h b/sysdeps/powerpc/powerpc32/bits/wordsize.h +--- a/sysdeps/powerpc/powerpc32/bits/wordsize.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/powerpc/powerpc32/bits/wordsize.h 2025-10-20 21:02:19.000000000 +0300 +@@ -2,10 +2,9 @@ + + #if defined __powerpc64__ + # define __WORDSIZE 64 +-# define __WORDSIZE_TIME64_COMPAT32 1 + #else + # define __WORDSIZE 32 +-# define __WORDSIZE_TIME64_COMPAT32 0 + # define __WORDSIZE32_SIZE_ULONG 0 + # define __WORDSIZE32_PTRDIFF_LONG 0 + #endif ++#define __WORDSIZE_TIME64_COMPAT32 1 +diff -Nuar a/sysdeps/powerpc/powerpc64/bits/wordsize.h b/sysdeps/powerpc/powerpc64/bits/wordsize.h +--- a/sysdeps/powerpc/powerpc64/bits/wordsize.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/powerpc/powerpc64/bits/wordsize.h 2025-10-20 21:02:19.000000000 +0300 +@@ -2,10 +2,9 @@ + + #if defined __powerpc64__ + # define __WORDSIZE 64 +-# define __WORDSIZE_TIME64_COMPAT32 1 + #else + # define __WORDSIZE 32 +-# define __WORDSIZE_TIME64_COMPAT32 0 + # define __WORDSIZE32_SIZE_ULONG 0 + # define __WORDSIZE32_PTRDIFF_LONG 0 + #endif ++#define __WORDSIZE_TIME64_COMPAT32 1 +diff -Nuar a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h +--- a/sysdeps/powerpc/powerpc64/dl-machine.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/powerpc/powerpc64/dl-machine.h 2025-10-20 21:02:21.000000000 +0300 +@@ -79,6 +79,7 @@ + static inline Elf64_Addr + elf_machine_load_address (void) __attribute__ ((const)); + ++#ifndef __PCREL__ + static inline Elf64_Addr + elf_machine_load_address (void) + { +@@ -106,6 +107,24 @@ + /* Then subtract off the load address offset. */ + return runtime_dynamic - elf_machine_load_address() ; + } ++#else /* __PCREL__ */ ++/* In PCREL mode, r2 may have been clobbered. Rely on relative ++ relocations instead. */ ++ ++static inline ElfW(Addr) ++elf_machine_load_address (void) ++{ ++ extern const ElfW(Ehdr) __ehdr_start attribute_hidden; ++ return (ElfW(Addr)) &__ehdr_start; ++} ++ ++static inline ElfW(Addr) ++elf_machine_dynamic (void) ++{ ++ extern ElfW(Dyn) _DYNAMIC[] attribute_hidden; ++ return (ElfW(Addr)) _DYNAMIC - elf_machine_load_address (); ++} ++#endif /* __PCREL__ */ + + /* The PLT uses Elf64_Rela relocs. */ + #define elf_machine_relplt elf_machine_rela +diff -Nuar a/sysdeps/powerpc/powerpc64/le/power9/strncpy.S b/sysdeps/powerpc/powerpc64/le/power9/strncpy.S +--- a/sysdeps/powerpc/powerpc64/le/power9/strncpy.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/powerpc/powerpc64/le/power9/strncpy.S 2025-10-20 21:02:21.000000000 +0300 +@@ -352,7 +352,7 @@ + cmpldi cr6,r5,16 /* Check if length was reached. */ + ble cr6,L(zero_padding_end) + +- stxv v18,0(r11) ++ stxv 32+v18,0(r11) + addi r11,r11,16 + addi r5,r5,-16 + +@@ -360,7 +360,7 @@ + + L(zero_padding_end): + sldi r10,r5,56 /* stxvl wants size in top 8 bits */ +- stxvl v18,r11,r10 /* Partial store */ ++ stxvl 32+v18,r11,r10 /* Partial store */ + blr + + .align 4 +diff -Nuar a/sysdeps/powerpc/utmp-size.h b/sysdeps/powerpc/utmp-size.h +--- a/sysdeps/powerpc/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/powerpc/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile +--- a/sysdeps/pthread/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/pthread/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -69,6 +69,7 @@ + tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 tst-cancel16 \ + tst-cancel18 tst-cancel19 tst-cancel20 tst-cancel21 \ + tst-cancel22 tst-cancel23 tst-cancel26 tst-cancel27 tst-cancel28 \ ++ tst-cancel29 \ + tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 \ + tst-clock1 \ + tst-cond-except \ +@@ -125,6 +126,7 @@ + tst-pthread-raise-blocked-self \ + tst-pthread_kill-exited \ + tst-pthread_kill-exiting \ ++ tst-cancel30 \ + # tests + + tests-time64 := \ +@@ -153,16 +155,36 @@ + tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 + + ifeq ($(build-shared),yes) +-tests += tst-atfork2 tst-pt-tls4 tst-_res1 tst-fini1 tst-create1 ++tests += \ ++ tst-atfork2 \ ++ tst-pt-tls4 \ ++ tst-_res1 \ ++ tst-fini1 \ ++ tst-create1 \ ++ tst-atfork3 \ ++ tst-atfork4 \ ++# tests ++ + tests-nolibpthread += tst-fini1 + endif + +-modules-names += tst-atfork2mod tst-tls4moda tst-tls4modb \ +- tst-_res1mod1 tst-_res1mod2 tst-fini1mod \ +- tst-create1mod ++modules-names += \ ++ tst-atfork2mod \ ++ tst-tls4moda \ ++ tst-tls4modb \ ++ tst-_res1mod1 \ ++ tst-_res1mod2 \ ++ tst-fini1mod \ ++ tst-create1mod \ ++ tst-atfork3mod \ ++ tst-atfork4mod \ ++# module-names ++ + test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names))) + + tst-atfork2mod.so-no-z-defs = yes ++tst-atfork3mod.so-no-z-defs = yes ++tst-atfork4mod.so-no-z-defs = yes + tst-create1mod.so-no-z-defs = yes + + ifeq ($(build-shared),yes) +@@ -225,8 +247,18 @@ + LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so + $(objpfx)tst-atfork2mod.so: $(shared-thread-library) + ++$(objpfx)tst-atfork3: $(shared-thread-library) ++LDFLAGS-tst-atfork3 = -rdynamic ++$(objpfx)tst-atfork3mod.so: $(shared-thread-library) ++ ++$(objpfx)tst-atfork4: $(shared-thread-library) ++LDFLAGS-tst-atfork4 = -rdynamic ++$(objpfx)tst-atfork4mod.so: $(shared-thread-library) ++ + ifeq ($(build-shared),yes) + $(objpfx)tst-atfork2.out: $(objpfx)tst-atfork2mod.so ++$(objpfx)tst-atfork3.out: $(objpfx)tst-atfork3mod.so ++$(objpfx)tst-atfork4.out: $(objpfx)tst-atfork4mod.so + endif + + ifeq ($(build-shared),yes) +diff -Nuar a/sysdeps/pthread/tst-atfork3.c b/sysdeps/pthread/tst-atfork3.c +--- a/sysdeps/pthread/tst-atfork3.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/pthread/tst-atfork3.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,118 @@ ++/* Check if pthread_atfork handler can call dlclose (BZ#24595). ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* Check if pthread_atfork handlers do not deadlock when calling a function ++ that might alter the internal fork handle list, such as dlclose. ++ ++ The test registers a callback set with pthread_atfork(), dlopen() a shared ++ library (nptl/tst-atfork3mod.c), calls an exported symbol from the library ++ (which in turn also registers atfork handlers), and calls fork to trigger ++ the callbacks. */ ++ ++static void *handler; ++static bool run_dlclose_prepare; ++static bool run_dlclose_parent; ++static bool run_dlclose_child; ++ ++static void ++prepare (void) ++{ ++ if (run_dlclose_prepare) ++ xdlclose (handler); ++} ++ ++static void ++parent (void) ++{ ++ if (run_dlclose_parent) ++ xdlclose (handler); ++} ++ ++static void ++child (void) ++{ ++ if (run_dlclose_child) ++ xdlclose (handler); ++} ++ ++static void ++proc_func (void *closure) ++{ ++} ++ ++static void ++do_test_generic (bool dlclose_prepare, bool dlclose_parent, bool dlclose_child) ++{ ++ run_dlclose_prepare = dlclose_prepare; ++ run_dlclose_parent = dlclose_parent; ++ run_dlclose_child = dlclose_child; ++ ++ handler = xdlopen ("tst-atfork3mod.so", RTLD_NOW); ++ ++ int (*atfork3mod_func)(void); ++ atfork3mod_func = xdlsym (handler, "atfork3mod_func"); ++ ++ atfork3mod_func (); ++ ++ struct support_capture_subprocess proc ++ = support_capture_subprocess (proc_func, NULL); ++ support_capture_subprocess_check (&proc, "tst-atfork3", 0, sc_allow_none); ++ ++ handler = atfork3mod_func = NULL; ++ ++ support_capture_subprocess_free (&proc); ++} ++ ++static void * ++thread_func (void *closure) ++{ ++ return NULL; ++} ++ ++static int ++do_test (void) ++{ ++ { ++ /* Make the process acts as multithread. */ ++ pthread_attr_t attr; ++ xpthread_attr_init (&attr); ++ xpthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); ++ xpthread_create (&attr, thread_func, NULL); ++ } ++ ++ TEST_COMPARE (pthread_atfork (prepare, parent, child), 0); ++ ++ do_test_generic (true /* prepare */, false /* parent */, false /* child */); ++ do_test_generic (false /* prepare */, true /* parent */, false /* child */); ++ do_test_generic (false /* prepare */, false /* parent */, true /* child */); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/sysdeps/pthread/tst-atfork3mod.c b/sysdeps/pthread/tst-atfork3mod.c +--- a/sysdeps/pthread/tst-atfork3mod.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/pthread/tst-atfork3mod.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,44 @@ ++/* Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++ ++#include ++ ++static void ++mod_prepare (void) ++{ ++} ++ ++static void ++mod_parent (void) ++{ ++} ++ ++static void ++mod_child (void) ++{ ++} ++ ++int atfork3mod_func (void) ++{ ++ TEST_COMPARE (pthread_atfork (mod_prepare, mod_parent, mod_child), 0); ++ ++ return 0; ++} +diff -Nuar a/sysdeps/pthread/tst-atfork4.c b/sysdeps/pthread/tst-atfork4.c +--- a/sysdeps/pthread/tst-atfork4.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/pthread/tst-atfork4.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,128 @@ ++/* pthread_atfork supports handlers that call pthread_atfork or dlclose. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static void * ++thread_func (void *x) ++{ ++ return NULL; ++} ++ ++static unsigned int second_atfork_handler_runcount = 0; ++ ++static void ++second_atfork_handler (void) ++{ ++ second_atfork_handler_runcount++; ++} ++ ++static void *h = NULL; ++ ++static unsigned int atfork_handler_runcount = 0; ++ ++static void ++prepare (void) ++{ ++ /* These atfork handlers are registered while atfork handlers are being ++ executed and thus will not be executed during the corresponding ++ fork. */ ++ TEST_VERIFY_EXIT (pthread_atfork (second_atfork_handler, ++ second_atfork_handler, ++ second_atfork_handler) == 0); ++ ++ /* This will de-register the atfork handlers registered by the dlopen'd ++ library and so they will not be executed. */ ++ if (h != NULL) ++ { ++ xdlclose (h); ++ h = NULL; ++ } ++ ++ atfork_handler_runcount++; ++} ++ ++static void ++after (void) ++{ ++ atfork_handler_runcount++; ++} ++ ++static int ++do_test (void) ++{ ++ /* Make sure __libc_single_threaded is 0. */ ++ pthread_attr_t attr; ++ xpthread_attr_init (&attr); ++ xpthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); ++ xpthread_create (&attr, thread_func, NULL); ++ ++ void (*reg_atfork_handlers) (void); ++ ++ h = xdlopen ("tst-atfork4mod.so", RTLD_LAZY); ++ ++ reg_atfork_handlers = xdlsym (h, "reg_atfork_handlers"); ++ ++ reg_atfork_handlers (); ++ ++ /* We register our atfork handlers *after* loading the module so that our ++ prepare handler is called first at fork, where we then dlclose the ++ module before its prepare handler has a chance to be called. */ ++ TEST_VERIFY_EXIT (pthread_atfork (prepare, after, after) == 0); ++ ++ pid_t pid = xfork (); ++ ++ /* Both the parent and the child processes should observe this. */ ++ TEST_VERIFY_EXIT (atfork_handler_runcount == 2); ++ TEST_VERIFY_EXIT (second_atfork_handler_runcount == 0); ++ ++ if (pid > 0) ++ { ++ int childstat; ++ ++ xwaitpid (-1, &childstat, 0); ++ TEST_VERIFY_EXIT (WIFEXITED (childstat) ++ && WEXITSTATUS (childstat) == 0); ++ ++ /* This time, the second set of atfork handlers should also be called ++ since the handlers are already in place before fork is called. */ ++ ++ pid = xfork (); ++ ++ TEST_VERIFY_EXIT (atfork_handler_runcount == 4); ++ TEST_VERIFY_EXIT (second_atfork_handler_runcount == 2); ++ ++ if (pid > 0) ++ { ++ xwaitpid (-1, &childstat, 0); ++ TEST_VERIFY_EXIT (WIFEXITED (childstat) ++ && WEXITSTATUS (childstat) == 0); ++ } ++ } ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/sysdeps/pthread/tst-atfork4mod.c b/sysdeps/pthread/tst-atfork4mod.c +--- a/sysdeps/pthread/tst-atfork4mod.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/pthread/tst-atfork4mod.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,48 @@ ++/* pthread_atfork supports handlers that call pthread_atfork or dlclose. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++ ++/* This dynamically loaded library simply registers its atfork handlers when ++ asked to. The atfork handlers should never be executed because the ++ library is unloaded before fork is called by the test program. */ ++ ++static void ++prepare (void) ++{ ++ abort (); ++} ++ ++static void ++parent (void) ++{ ++ abort (); ++} ++ ++static void ++child (void) ++{ ++ abort (); ++} ++ ++void ++reg_atfork_handlers (void) ++{ ++ pthread_atfork (prepare, parent, child); ++} +diff -Nuar a/sysdeps/pthread/tst-cancel29.c b/sysdeps/pthread/tst-cancel29.c +--- a/sysdeps/pthread/tst-cancel29.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/pthread/tst-cancel29.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,207 @@ ++/* Check if a thread that disables cancellation and which call functions ++ that might be interrupted by a signal do not see the internal SIGCANCEL. ++ ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* On Linux some interfaces are never restarted after being interrupted by ++ a signal handler, regardless of the use of SA_RESTART. It means that ++ if asynchronous cancellation is not enabled, the pthread_cancel can not ++ set the internal SIGCANCEL otherwise the interface might see a spurious ++ EINTR failure. */ ++ ++static pthread_barrier_t b; ++ ++/* Cleanup handling test. */ ++static int cl_called; ++static void ++cl (void *arg) ++{ ++ ++cl_called; ++} ++ ++static void * ++tf_sigtimedwait (void *arg) ++{ ++ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); ++ xpthread_barrier_wait (&b); ++ ++ int r; ++ pthread_cleanup_push (cl, NULL); ++ ++ sigset_t mask; ++ sigemptyset (&mask); ++ r = sigtimedwait (&mask, NULL, &(struct timespec) { 0, 250000000 }); ++ if (r != -1) ++ return (void*) -1; ++ if (errno != EAGAIN) ++ return (void*) -2; ++ ++ pthread_cleanup_pop (0); ++ return NULL; ++} ++ ++static void * ++tf_poll (void *arg) ++{ ++ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); ++ xpthread_barrier_wait (&b); ++ ++ int r; ++ pthread_cleanup_push (cl, NULL); ++ ++ r = poll (NULL, 0, 250); ++ if (r != 0) ++ return (void*) -1; ++ ++ pthread_cleanup_pop (0); ++ return NULL; ++} ++ ++static void * ++tf_ppoll (void *arg) ++{ ++ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); ++ ++ xpthread_barrier_wait (&b); ++ ++ int r; ++ pthread_cleanup_push (cl, NULL); ++ ++ r = ppoll (NULL, 0, &(struct timespec) { 0, 250000000 }, NULL); ++ if (r != 0) ++ return (void*) -1; ++ ++ pthread_cleanup_pop (0); ++ return NULL; ++} ++ ++static void * ++tf_select (void *arg) ++{ ++ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); ++ xpthread_barrier_wait (&b); ++ ++ int r; ++ pthread_cleanup_push (cl, NULL); ++ ++ r = select (0, NULL, NULL, NULL, &(struct timeval) { 0, 250000 }); ++ if (r != 0) ++ return (void*) -1; ++ ++ pthread_cleanup_pop (0); ++ return NULL; ++} ++ ++static void * ++tf_pselect (void *arg) ++{ ++ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); ++ xpthread_barrier_wait (&b); ++ ++ int r; ++ pthread_cleanup_push (cl, NULL); ++ ++ r = pselect (0, NULL, NULL, NULL, &(struct timespec) { 0, 250000000 }, NULL); ++ if (r != 0) ++ return (void*) -1; ++ ++ pthread_cleanup_pop (0); ++ return NULL; ++} ++ ++static void * ++tf_clock_nanosleep (void *arg) ++{ ++ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); ++ xpthread_barrier_wait (&b); ++ ++ int r; ++ pthread_cleanup_push (cl, NULL); ++ ++ r = clock_nanosleep (CLOCK_REALTIME, 0, &(struct timespec) { 0, 250000000 }, ++ NULL); ++ if (r != 0) ++ return (void*) -1; ++ ++ pthread_cleanup_pop (0); ++ return NULL; ++} ++ ++struct cancel_test_t ++{ ++ const char *name; ++ void * (*cf) (void *); ++} tests[] = ++{ ++ { "sigtimedwait", tf_sigtimedwait, }, ++ { "poll", tf_poll, }, ++ { "ppoll", tf_ppoll, }, ++ { "select", tf_select, }, ++ { "pselect", tf_pselect , }, ++ { "clock_nanosleep", tf_clock_nanosleep, }, ++}; ++ ++static int ++do_test (void) ++{ ++ for (int i = 0; i < array_length (tests); i++) ++ { ++ xpthread_barrier_init (&b, NULL, 2); ++ ++ cl_called = 0; ++ ++ pthread_t th = xpthread_create (NULL, tests[i].cf, NULL); ++ ++ xpthread_barrier_wait (&b); ++ ++ struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 }; ++ while (nanosleep (&ts, &ts) != 0) ++ continue; ++ ++ xpthread_cancel (th); ++ ++ void *status = xpthread_join (th); ++ if (status != NULL) ++ printf ("test '%s' failed: %" PRIdPTR "\n", tests[i].name, ++ (intptr_t) status); ++ TEST_VERIFY (status == NULL); ++ ++ xpthread_barrier_destroy (&b); ++ ++ TEST_COMPARE (cl_called, 0); ++ ++ printf ("in-time cancel test of '%s' successful\n", tests[i].name); ++ } ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/sysdeps/pthread/tst-cancel30.c b/sysdeps/pthread/tst-cancel30.c +--- a/sysdeps/pthread/tst-cancel30.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/pthread/tst-cancel30.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,82 @@ ++/* Check if printf like functions does not disable asynchronous cancellation ++ mode (BZ#29214). ++ ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static pthread_barrier_t b; ++ ++static void * ++tf (void *arg) ++{ ++ int old; ++ ++ TEST_COMPARE (pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL), 0); ++ ++ TEST_COMPARE (pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &old), 0); ++ TEST_COMPARE (old, PTHREAD_CANCEL_ASYNCHRONOUS); ++ ++ /* Check if internal lock cleanup routines restore the cancellation type ++ correctly. */ ++ printf ("...\n"); ++ TEST_COMPARE (pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &old), 0); ++ TEST_COMPARE (old, PTHREAD_CANCEL_ASYNCHRONOUS); ++ ++ xpthread_barrier_wait (&b); ++ ++ /* Wait indefinitely for cancellation, which only works if asynchronous ++ cancellation is enabled. */ ++#ifdef SYS_pause ++ syscall (SYS_pause); ++#elif defined SYS_ppoll || defined SYS_ppoll_time64 ++# ifndef SYS_ppoll_time64 ++# define SYS_ppoll_time64 SYS_ppoll ++# endif ++ syscall (SYS_ppoll_time64, NULL, 0, NULL, NULL); ++#else ++ for (;;); ++#endif ++ ++ return 0; ++} ++ ++static int ++do_test (void) ++{ ++ xpthread_barrier_init (&b, NULL, 2); ++ ++ pthread_t th = xpthread_create (NULL, tf, NULL); ++ ++ xpthread_barrier_wait (&b); ++ ++ xpthread_cancel (th); ++ ++ void *status = xpthread_join (th); ++ TEST_VERIFY (status == PTHREAD_CANCELED); ++ ++ return 0; ++} ++ ++/* There is no need to wait full TIMEOUT if asynchronous is not working. */ ++#define TIMEOUT 3 ++#include +diff -Nuar a/sysdeps/pthread/tst-setuid3.c b/sysdeps/pthread/tst-setuid3.c +--- a/sysdeps/pthread/tst-setuid3.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/pthread/tst-setuid3.c 2025-10-20 21:02:21.000000000 +0300 +@@ -15,24 +15,19 @@ + License along with the GNU C Library; if not, see + . */ + +-#include + #include + #include + #include + #include + ++#include ++ + /* The test must run under a non-privileged user ID. */ + static const uid_t test_uid = 1; + + static pthread_barrier_t barrier1; + static pthread_barrier_t barrier2; + +-#define FAIL(fmt, ...) \ +- do { printf ("FAIL: " fmt "\n", __VA_ARGS__); _exit (1); } while (0) +- +-#define FAIL_ERR(fmt, ...) \ +- do { printf ("FAIL: " fmt ": %m\n", __VA_ARGS__); _exit (1); } while (0) +- + /* True if x is not a successful return code from pthread_barrier_wait. */ + static inline bool + is_invalid_barrier_ret (int x) +@@ -45,10 +40,10 @@ + { + int ret = pthread_barrier_wait (&barrier1); + if (is_invalid_barrier_ret (ret)) +- FAIL ("pthread_barrier_wait (barrier1) (on thread): %d", ret); ++ FAIL_EXIT1 ("pthread_barrier_wait (barrier1) (on thread): %d", ret); + ret = pthread_barrier_wait (&barrier2); + if (is_invalid_barrier_ret (ret)) +- FAIL ("pthread_barrier_wait (barrier2) (on thread): %d", ret); ++ FAIL_EXIT1 ("pthread_barrier_wait (barrier2) (on thread): %d", ret); + return NULL; + } + +@@ -59,13 +54,13 @@ + switch (ret) + { + case 0: +- FAIL ("setuid succeeded unexpectedly in phase %d", phase); ++ FAIL_EXIT1 ("setuid succeeded unexpectedly in phase %d", phase); + case -1: + if (errno != EPERM) +- FAIL_ERR ("setuid phase %d", phase); ++ FAIL_EXIT1 ("setuid phase %d: %m", phase); + break; + default: +- FAIL ("invalid setuid return value in phase %d: %d", phase, ret); ++ FAIL_EXIT1 ("invalid setuid return value in phase %d: %d", phase, ret); + } + } + +@@ -74,42 +69,42 @@ + { + if (getuid () == 0) + if (setuid (test_uid) != 0) +- FAIL_ERR ("setuid (%u)", (unsigned) test_uid); ++ FAIL_EXIT1 ("setuid (%u): %m", (unsigned) test_uid); + if (setuid (getuid ())) +- FAIL_ERR ("setuid (%s)", "getuid ()"); ++ FAIL_EXIT1 ("setuid (%s): %m", "getuid ()"); + setuid_failure (1); + + int ret = pthread_barrier_init (&barrier1, NULL, 2); + if (ret != 0) +- FAIL ("pthread_barrier_init (barrier1): %d", ret); ++ FAIL_EXIT1 ("pthread_barrier_init (barrier1): %d", ret); + ret = pthread_barrier_init (&barrier2, NULL, 2); + if (ret != 0) +- FAIL ("pthread_barrier_init (barrier2): %d", ret); ++ FAIL_EXIT1 ("pthread_barrier_init (barrier2): %d", ret); + + pthread_t thread; + ret = pthread_create (&thread, NULL, thread_func, NULL); + if (ret != 0) +- FAIL ("pthread_create: %d", ret); ++ FAIL_EXIT1 ("pthread_create: %d", ret); + + /* Ensure that the thread is running properly. */ + ret = pthread_barrier_wait (&barrier1); + if (is_invalid_barrier_ret (ret)) +- FAIL ("pthread_barrier_wait (barrier1): %d", ret); ++ FAIL_EXIT1 ("pthread_barrier_wait (barrier1): %d", ret); + + setuid_failure (2); + + /* Check success case. */ + if (setuid (getuid ()) != 0) +- FAIL_ERR ("setuid (%s)", "getuid ()"); ++ FAIL_EXIT1 ("setuid (%s): %m", "getuid ()"); + + /* Shutdown. */ + ret = pthread_barrier_wait (&barrier2); + if (is_invalid_barrier_ret (ret)) +- FAIL ("pthread_barrier_wait (barrier2): %d", ret); ++ FAIL_EXIT1 ("pthread_barrier_wait (barrier2): %d", ret); + + ret = pthread_join (thread, NULL); + if (ret != 0) +- FAIL ("pthread_join: %d", ret); ++ FAIL_EXIT1 ("pthread_join: %d", ret); + + return 0; + } +diff -Nuar a/sysdeps/riscv/rv64/rvd/libm-test-ulps b/sysdeps/riscv/rv64/rvd/libm-test-ulps +--- a/sysdeps/riscv/rv64/rvd/libm-test-ulps 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/riscv/rv64/rvd/libm-test-ulps 2025-10-20 21:02:21.000000000 +0300 +@@ -1077,7 +1077,7 @@ + + Function: "j0_upward": + double: 9 +-float: 8 ++float: 9 + ldouble: 7 + + Function: "j1": +diff -Nuar a/sysdeps/riscv/utmp-size.h b/sysdeps/riscv/utmp-size.h +--- a/sysdeps/riscv/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/riscv/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/s390/dl-procinfo.c b/sysdeps/s390/dl-procinfo.c +--- a/sysdeps/s390/dl-procinfo.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/s390/dl-procinfo.c 2025-10-20 21:02:21.000000000 +0300 +@@ -63,11 +63,12 @@ + #if !defined PROCINFO_DECL && defined SHARED + ._dl_s390_platforms + #else +-PROCINFO_CLASS const char _dl_s390_platforms[10][7] ++PROCINFO_CLASS const char _dl_s390_platforms[11][7] + #endif + #ifndef PROCINFO_DECL + = { +- "g5", "z900", "z990", "z9-109", "z10", "z196", "zEC12", "z13", "z14", "z15" ++ "g5", "z900", "z990", "z9-109", "z10", "z196", "zEC12", "z13", "z14", "z15", ++ "z16" + } + #endif + #if !defined SHARED || defined PROCINFO_DECL +diff -Nuar a/sysdeps/s390/dl-procinfo.h b/sysdeps/s390/dl-procinfo.h +--- a/sysdeps/s390/dl-procinfo.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/s390/dl-procinfo.h 2025-10-20 21:02:21.000000000 +0300 +@@ -22,7 +22,7 @@ + + #define _DL_HWCAP_COUNT 23 + +-#define _DL_PLATFORMS_COUNT 10 ++#define _DL_PLATFORMS_COUNT 11 + + /* The kernel provides up to 32 capability bits with elf_hwcap. */ + #define _DL_FIRST_PLATFORM 32 +diff -Nuar a/sysdeps/s390/s390-64/configure b/sysdeps/s390/s390-64/configure +--- a/sysdeps/s390/s390-64/configure 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/s390/s390-64/configure 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,122 @@ ++# This file is generated from configure.ac by Autoconf. DO NOT EDIT! ++ # Local configure fragment for sysdeps/s390/s390-64. ++ ++# Minimal checking for static PIE support in ld. ++# Compare to ld testcase/bugzilla: ++# /ld/testsuite/ld-elf/pr22263-1.rd ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for s390-specific static PIE requirements" >&5 ++$as_echo_n "checking for s390-specific static PIE requirements... " >&6; } ++if { as_var=\ ++libc_cv_s390x_staticpie_req; eval \${$as_var+:} false; }; then : ++ $as_echo_n "(cached) " >&6 ++else ++ cat > conftest1.c < conftest2.c <&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; } \ ++ && { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS -fPIE -c conftest2.c -o conftest2.o' ++ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; } \ ++ && { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS -pie -o conftest conftest1.o conftest2.o' ++ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; } \ ++ && { ac_try='! readelf -Wr conftest | grep R_390_TLS_TPOFF' ++ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; } ++ then ++ libc_cv_s390x_staticpie_req=yes ++ fi ++ rm -rf conftest.* ++fi ++eval ac_res=\$\ ++libc_cv_s390x_staticpie_req ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 ++$as_echo "$ac_res" >&6; } ++if test $libc_cv_s390x_staticpie_req = yes; then ++ # Static PIE is supported only on 64bit. ++ # Ensure you also have those patches for: ++ # - binutils (ld) ++ # - "[PR ld/22263] s390: Avoid dynamic TLS relocs in PIE" ++ # https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=26b1426577b5dcb32d149c64cca3e603b81948a9 ++ # (Tested by configure check above) ++ # Otherwise there will be a R_390_TLS_TPOFF relocation, which fails to ++ # be processed in _dl_relocate_static_pie() as static TLS map is not setup. ++ # - "s390: Add DT_JMPREL pointing to .rela.[i]plt with static-pie" ++ # https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=d942d8db12adf4c9e5c7d9ed6496a779ece7149e ++ # (We can't test it in configure as we are not able to link a static PIE ++ # executable if the system glibc lacks static PIE support) ++ # Otherwise there won't be DT_JMPREL, DT_PLTRELA, DT_PLTRELASZ entries ++ # and the IFUNC symbols are not processed, which leads to crashes. ++ # ++ # - kernel (the mentioned links to the commits belong to 5.19 merge window): ++ # - "s390/mmap: increase stack/mmap gap to 128MB" ++ # https://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git/commit/?h=features&id=f2f47d0ef72c30622e62471903ea19446ea79ee2 ++ # - "s390/vdso: move vdso mapping to its own function" ++ # https://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git/commit/?h=features&id=57761da4dc5cd60bed2c81ba0edb7495c3c740b8 ++ # - "s390/vdso: map vdso above stack" ++ # https://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git/commit/?h=features&id=9e37a2e8546f9e48ea76c839116fa5174d14e033 ++ # - "s390/vdso: add vdso randomization" ++ # https://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git/commit/?h=features&id=41cd81abafdc4e58a93fcb677712a76885e3ca25 ++ # (We can't test the kernel of the target system) ++ # Otherwise if /proc/sys/kernel/randomize_va_space is turned off (0), ++ # static PIE executables like ldconfig will crash. While startup sbrk is ++ # used to enlarge the HEAP. Unfortunately the underlying brk syscall fails ++ # as there is not enough space after the HEAP. Then the address of the TLS ++ # image is invalid and the following memcpy in __libc_setup_tls() leads ++ # to a segfault. ++ # If /proc/sys/kernel/randomize_va_space is activated (default: 2), there ++ # is enough space after HEAP. ++ # ++ # - glibc ++ # - "Linux: Define MMAP_CALL_INTERNAL" ++ # https://sourceware.org/git/?p=glibc.git;a=commit;h=c1b68685d438373efe64e5f076f4215723004dfb ++ # - "i386: Remove OPTIMIZE_FOR_GCC_5 from Linux libc-do-syscall.S" ++ # https://sourceware.org/git/?p=glibc.git;a=commit;h=6e5c7a1e262961adb52443ab91bd2c9b72316402 ++ # - "i386: Honor I386_USE_SYSENTER for 6-argument Linux system calls" ++ # https://sourceware.org/git/?p=glibc.git;a=commit;h=60f0f2130d30cfd008ca39743027f1e200592dff ++ # - "ia64: Always define IA64_USE_NEW_STUB as a flag macro" ++ # https://sourceware.org/git/?p=glibc.git;a=commit;h=18bd9c3d3b1b6a9182698c85354578d1d58e9d64 ++ # - "Linux: Implement a useful version of _startup_fatal" ++ # https://sourceware.org/git/?p=glibc.git;a=commit;h=a2a6bce7d7e52c1c34369a7da62c501cc350bc31 ++ # - "Linux: Introduce __brk_call for invoking the brk system call" ++ # https://sourceware.org/git/?p=glibc.git;a=commit;h=b57ab258c1140bc45464b4b9908713e3e0ee35aa ++ # - "csu: Implement and use _dl_early_allocate during static startup" ++ # https://sourceware.org/git/?p=glibc.git;a=commit;h=f787e138aa0bf677bf74fa2a08595c446292f3d7 ++ # The mentioned patch series by Florian Weimer avoids the mentioned failing ++ # sbrk syscall by falling back to mmap. ++ $as_echo "#define SUPPORT_STATIC_PIE 1" >>confdefs.h ++ ++fi +diff -Nuar a/sysdeps/s390/s390-64/configure.ac b/sysdeps/s390/s390-64/configure.ac +--- a/sysdeps/s390/s390-64/configure.ac 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/s390/s390-64/configure.ac 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,92 @@ ++GLIBC_PROVIDES dnl See aclocal.m4 in the top level source directory. ++# Local configure fragment for sysdeps/s390/s390-64. ++ ++# Minimal checking for static PIE support in ld. ++# Compare to ld testcase/bugzilla: ++# /ld/testsuite/ld-elf/pr22263-1.rd ++AC_CACHE_CHECK([for s390-specific static PIE requirements], \ ++[libc_cv_s390x_staticpie_req], [dnl ++ cat > conftest1.c < conftest2.c <= 13 ++# if GCCMACRO__ARCH__ >= 14 ++ if (!(GLRO(dl_hwcap) & HWCAP_S390_VXRS_PDE2)) ++ _dl_fatal_printf ("\ ++Fatal glibc error: CPU lacks VXRS_PDE2 support (z16 or later required)\n"); ++# elif GCCMACRO__ARCH__ >= 13 + if (!(GLRO(dl_hwcap) & HWCAP_S390_VXRS_EXT2)) + _dl_fatal_printf ("\ + Fatal glibc error: CPU lacks VXRS_EXT2 support (z15 or later required)\n"); +diff -Nuar a/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c b/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c +--- a/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c 2025-10-20 21:02:21.000000000 +0300 +@@ -19,8 +19,8 @@ + #include + #include + +-const char _dl_hwcaps_subdirs[] = "z15:z14:z13"; +-enum { subdirs_count = 3 }; /* Number of components in _dl_hwcaps_subdirs. */ ++const char _dl_hwcaps_subdirs[] = "z16:z15:z14:z13"; ++enum { subdirs_count = 4 }; /* Number of components in _dl_hwcaps_subdirs. */ + + uint32_t + _dl_hwcaps_subdirs_active (void) +@@ -50,5 +50,12 @@ + return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); + ++active; + ++ /* z16. ++ Note: We do not list HWCAP_S390_NNPA here as, according to the Principles of ++ Operation, those instructions may be replaced or removed in future. */ ++ if (!(GLRO (dl_hwcap) & HWCAP_S390_VXRS_PDE2)) ++ return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); ++ ++active; ++ + return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); + } +diff -Nuar a/sysdeps/s390/s390-64/Makefile b/sysdeps/s390/s390-64/Makefile +--- a/sysdeps/s390/s390-64/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/s390/s390-64/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -7,8 +7,11 @@ + CFLAGS-dl-load.c += -Wno-unused + CFLAGS-dl-reloc.c += -Wno-unused + +-$(objpfx)tst-glibc-hwcaps: $(objpfx)libmarkermod2-1.so \ +- $(objpfx)libmarkermod3-1.so $(objpfx)libmarkermod4-1.so ++$(objpfx)tst-glibc-hwcaps: \ ++ $(objpfx)libmarkermod2-1.so \ ++ $(objpfx)libmarkermod3-1.so \ ++ $(objpfx)libmarkermod4-1.so \ ++ $(objpfx)libmarkermod5-1.so + $(objpfx)tst-glibc-hwcaps.out: \ + $(objpfx)libmarkermod2.so \ + $(objpfx)glibc-hwcaps/z13/libmarkermod2.so \ +@@ -19,6 +22,11 @@ + $(objpfx)glibc-hwcaps/z13/libmarkermod4.so \ + $(objpfx)glibc-hwcaps/z14/libmarkermod4.so \ + $(objpfx)glibc-hwcaps/z15/libmarkermod4.so \ ++ $(objpfx)libmarkermod5.so \ ++ $(objpfx)glibc-hwcaps/z13/libmarkermod5.so \ ++ $(objpfx)glibc-hwcaps/z14/libmarkermod5.so \ ++ $(objpfx)glibc-hwcaps/z15/libmarkermod5.so \ ++ $(objpfx)glibc-hwcaps/z16/libmarkermod5.so + + $(objpfx)glibc-hwcaps/z13/libmarkermod2.so: $(objpfx)libmarkermod2-2.so + $(make-target-directory) +@@ -38,6 +46,19 @@ + $(objpfx)glibc-hwcaps/z15/libmarkermod4.so: $(objpfx)libmarkermod4-4.so + $(make-target-directory) + cp $< $@ ++$(objpfx)glibc-hwcaps/z13/libmarkermod5.so: $(objpfx)libmarkermod5-2.so ++ $(make-target-directory) ++ cp $< $@ ++$(objpfx)glibc-hwcaps/z14/libmarkermod5.so: $(objpfx)libmarkermod5-3.so ++ $(make-target-directory) ++ cp $< $@ ++$(objpfx)glibc-hwcaps/z15/libmarkermod5.so: $(objpfx)libmarkermod5-4.so ++ $(make-target-directory) ++ cp $< $@ ++$(objpfx)glibc-hwcaps/z16/libmarkermod5.so: $(objpfx)libmarkermod5-5.so ++ $(make-target-directory) ++ cp $< $@ ++ + + ifeq (no,$(build-hardcoded-path-in-tests)) + # This is an ld.so.cache test, and RPATH/RUNPATH in the executable +diff -Nuar a/sysdeps/s390/s390-64/start.S b/sysdeps/s390/s390-64/start.S +--- a/sysdeps/s390/s390-64/start.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/s390/s390-64/start.S 2025-10-20 21:02:21.000000000 +0300 +@@ -84,10 +84,25 @@ + + /* Ok, now branch to the libc main routine. */ + #ifdef PIC ++# ifdef SHARED ++ /* Used for dynamic linked position independent executable. ++ => Scrt1.o */ + larl %r2,main@GOTENT # load pointer to main + lg %r2,0(%r2) ++# else ++ /* Used for dynamic linked position dependent executable. ++ => crt1.o (glibc configured without --disable-default-pie: ++ PIC is defined) ++ Or for static linked position independent executable. ++ => rcrt1.o (only available if glibc configured without ++ --disable-default-pie: PIC is defined) */ ++ larl %r2,__wrap_main ++# endif + brasl %r14,__libc_start_main@plt + #else ++ /* Used for dynamic/static linked position dependent executable. ++ => crt1.o (glibc configured with --disable-default-pie: ++ PIC and SHARED are not defined) */ + larl %r2,main # load pointer to main + brasl %r14,__libc_start_main + #endif +@@ -97,6 +112,19 @@ + + cfi_endproc + ++#if defined PIC && !defined SHARED ++ /* When main is not defined in the executable but in a shared library ++ then a wrapper is needed in crt1.o of the static-pie enabled libc, ++ because crt1.o and rcrt1.o share code and the later must avoid the ++ use of GOT relocations before __libc_start_main is called. */ ++__wrap_main: ++ cfi_startproc ++ larl %r1,main@GOTENT # load pointer to main ++ lg %r1,0(%r1) ++ br %r1 ++ cfi_endproc ++#endif ++ + /* Define a symbol for the first piece of initialized data. */ + .data + .globl __data_start +diff -Nuar a/sysdeps/s390/s390-64/tst-glibc-hwcaps.c b/sysdeps/s390/s390-64/tst-glibc-hwcaps.c +--- a/sysdeps/s390/s390-64/tst-glibc-hwcaps.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/s390/s390-64/tst-glibc-hwcaps.c 2025-10-20 21:02:21.000000000 +0300 +@@ -25,6 +25,7 @@ + extern int marker2 (void); + extern int marker3 (void); + extern int marker4 (void); ++extern int marker5 (void); + + /* Return the arch level, 10 for the baseline libmarkermod*.so's. */ + static int +@@ -63,9 +64,11 @@ + return 12; + if (strcmp (platform, "z15") == 0) + return 13; ++ if (strcmp (platform, "z16") == 0) ++ return 14; + printf ("warning: unrecognized AT_PLATFORM value: %s\n", platform); +- /* Assume that the new platform supports z15. */ +- return 13; ++ /* Assume that the new platform supports z16. */ ++ return 14; + } + + static int +@@ -76,6 +79,7 @@ + TEST_COMPARE (marker2 (), MIN (level - 9, 2)); + TEST_COMPARE (marker3 (), MIN (level - 9, 3)); + TEST_COMPARE (marker4 (), MIN (level - 9, 4)); ++ TEST_COMPARE (marker5 (), MIN (level - 9, 5)); + return 0; + } + +diff -Nuar a/sysdeps/s390/wcsncmp-vx.S b/sysdeps/s390/wcsncmp-vx.S +--- a/sysdeps/s390/wcsncmp-vx.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/s390/wcsncmp-vx.S 2025-10-20 21:02:21.000000000 +0300 +@@ -59,14 +59,7 @@ + sllg %r4,%r4,2 /* Convert character-count to byte-count. */ + locgrne %r4,%r1 /* Use max byte-count, if bit 0/1 was one. */ + +- /* Check first character without vector load. */ +- lghi %r5,4 /* current_len = 4 bytes. */ +- /* Check s1/2[0]. */ +- lt %r0,0(%r2) +- l %r1,0(%r3) +- je .Lend_cmp_one_char +- crjne %r0,%r1,.Lend_cmp_one_char +- ++ lghi %r5,0 /* current_len = 0 bytes. */ + .Lloop: + vlbb %v17,0(%r5,%r3),6 /* Load s2 to block boundary. */ + vlbb %v16,0(%r5,%r2),6 /* Load s1 to block boundary. */ +@@ -167,7 +160,6 @@ + srl %r4,2 /* And convert it to character-index. */ + vlgvf %r0,%v16,0(%r4) /* Load character-values. */ + vlgvf %r1,%v17,0(%r4) +-.Lend_cmp_one_char: + cr %r0,%r1 + je .Lend_equal + lghi %r2,1 +diff -Nuar a/sysdeps/sh/bits/wordsize.h b/sysdeps/sh/bits/wordsize.h +--- a/sysdeps/sh/bits/wordsize.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/sh/bits/wordsize.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,21 @@ ++/* Copyright (C) 1999-2024 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 ++ . */ ++ ++#define __WORDSIZE 32 ++#define __WORDSIZE_TIME64_COMPAT32 1 ++#define __WORDSIZE32_SIZE_ULONG 0 ++#define __WORDSIZE32_PTRDIFF_LONG 0 +diff -Nuar a/sysdeps/sh/utmp-size.h b/sysdeps/sh/utmp-size.h +--- a/sysdeps/sh/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/sh/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/sparc/sparc32/bits/wordsize.h b/sysdeps/sparc/sparc32/bits/wordsize.h +--- a/sysdeps/sparc/sparc32/bits/wordsize.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/sparc/sparc32/bits/wordsize.h 2025-10-20 21:02:19.000000000 +0300 +@@ -1,11 +1,6 @@ + /* Determine the wordsize from the preprocessor defines. */ + +-#if defined __arch64__ || defined __sparcv9 +-# define __WORDSIZE 64 +-# define __WORDSIZE_TIME64_COMPAT32 1 +-#else +-# define __WORDSIZE 32 +-# define __WORDSIZE_TIME64_COMPAT32 0 +-# define __WORDSIZE32_SIZE_ULONG 0 +-# define __WORDSIZE32_PTRDIFF_LONG 0 +-#endif ++#define __WORDSIZE 32 ++#define __WORDSIZE_TIME64_COMPAT32 1 ++#define __WORDSIZE32_SIZE_ULONG 0 ++#define __WORDSIZE32_PTRDIFF_LONG 0 +diff -Nuar a/sysdeps/sparc/sparc64/bits/wordsize.h b/sysdeps/sparc/sparc64/bits/wordsize.h +--- a/sysdeps/sparc/sparc64/bits/wordsize.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/sparc/sparc64/bits/wordsize.h 2025-10-20 21:02:19.000000000 +0300 +@@ -2,10 +2,9 @@ + + #if defined __arch64__ || defined __sparcv9 + # define __WORDSIZE 64 +-# define __WORDSIZE_TIME64_COMPAT32 1 + #else + # define __WORDSIZE 32 +-# define __WORDSIZE_TIME64_COMPAT32 0 + # define __WORDSIZE32_SIZE_ULONG 0 + # define __WORDSIZE32_PTRDIFF_LONG 0 + #endif ++#define __WORDSIZE_TIME64_COMPAT32 1 +diff -Nuar a/sysdeps/sparc/utmp-size.h b/sysdeps/sparc/utmp-size.h +--- a/sysdeps/sparc/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/sparc/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/unix/sysv/linux/aarch64/arch-syscall.h b/sysdeps/unix/sysv/linux/aarch64/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/aarch64/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/aarch64/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -236,6 +236,7 @@ + #define __NR_sendmsg 211 + #define __NR_sendto 206 + #define __NR_set_mempolicy 237 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 99 + #define __NR_set_tid_address 96 + #define __NR_setdomainname 162 +diff -Nuar a/sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h b/sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h +--- a/sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h 2025-10-20 21:02:21.000000000 +0300 +@@ -75,3 +75,27 @@ + #define HWCAP2_BTI (1 << 17) + #define HWCAP2_MTE (1 << 18) + #define HWCAP2_ECV (1 << 19) ++#define HWCAP2_AFP (1 << 20) ++#define HWCAP2_RPRES (1 << 21) ++#define HWCAP2_MTE3 (1 << 22) ++#define HWCAP2_SME (1 << 23) ++#define HWCAP2_SME_I16I64 (1 << 24) ++#define HWCAP2_SME_F64F64 (1 << 25) ++#define HWCAP2_SME_I8I32 (1 << 26) ++#define HWCAP2_SME_F16F32 (1 << 27) ++#define HWCAP2_SME_B16F32 (1 << 28) ++#define HWCAP2_SME_F32F32 (1 << 29) ++#define HWCAP2_SME_FA64 (1 << 30) ++#define HWCAP2_WFXT (1UL << 31) ++#define HWCAP2_EBF16 (1UL << 32) ++#define HWCAP2_SVE_EBF16 (1UL << 33) ++#define HWCAP2_CSSC (1UL << 34) ++#define HWCAP2_RPRFM (1UL << 35) ++#define HWCAP2_SVE2P1 (1UL << 36) ++#define HWCAP2_SME2 (1UL << 37) ++#define HWCAP2_SME2P1 (1UL << 38) ++#define HWCAP2_SME_I16I32 (1UL << 39) ++#define HWCAP2_SME_BI32I32 (1UL << 40) ++#define HWCAP2_SME_B16B16 (1UL << 41) ++#define HWCAP2_SME_F16F16 (1UL << 42) ++#define HWCAP2_MOPS (1UL << 43) +diff -Nuar a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c +--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c 2025-10-20 21:02:21.000000000 +0300 +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #define DCZID_DZP_MASK (1 << 4) + #define DCZID_BS_MASK (0xf) +@@ -38,11 +39,9 @@ + }; + + static struct cpu_list cpu_list[] = { +- {"falkor", 0x510FC000}, + {"thunderxt88", 0x430F0A10}, + {"thunderx2t99", 0x431F0AF0}, + {"thunderx2t99p1", 0x420F5160}, +- {"phecda", 0x680F0000}, + {"ares", 0x411FD0C0}, + {"emag", 0x503F0001}, + {"kunpeng920", 0x481FD010}, +@@ -61,6 +60,46 @@ + } + #endif + ++#if __LINUX_KERNEL_VERSION < 0x060200 ++ ++/* Return true if we prefer using SVE in string ifuncs. Old kernels disable ++ SVE after every system call which results in unnecessary traps if memcpy ++ uses SVE. This is true for kernels between 4.15.0 and before 6.2.0, except ++ for 5.14.0 which was patched. For these versions return false to avoid using ++ SVE ifuncs. ++ Parse the kernel version into a 24-bit kernel.major.minor value without ++ calling any library functions. If uname() is not supported or if the version ++ format is not recognized, assume the kernel is modern and return true. */ ++ ++static inline bool ++prefer_sve_ifuncs (void) ++{ ++ struct utsname buf; ++ const char *p = &buf.release[0]; ++ int kernel = 0; ++ int val; ++ ++ if (__uname (&buf) < 0) ++ return true; ++ ++ for (int shift = 16; shift >= 0; shift -= 8) ++ { ++ for (val = 0; *p >= '0' && *p <= '9'; p++) ++ val = val * 10 + *p - '0'; ++ kernel |= (val & 255) << shift; ++ if (*p++ != '.') ++ break; ++ } ++ ++ if (kernel >= 0x060200 || kernel == 0x050e00) ++ return true; ++ if (kernel >= 0x040f00) ++ return false; ++ return true; ++} ++ ++#endif ++ + static inline void + init_cpu_features (struct cpu_features *cpu_features) + { +@@ -120,4 +159,14 @@ + + /* Check if SVE is supported. */ + cpu_features->sve = GLRO (dl_hwcap) & HWCAP_SVE; ++ ++ cpu_features->prefer_sve_ifuncs = cpu_features->sve; ++ ++#if __LINUX_KERNEL_VERSION < 0x060200 ++ if (cpu_features->sve) ++ cpu_features->prefer_sve_ifuncs = prefer_sve_ifuncs (); ++#endif ++ ++ /* Check if MOPS is supported. */ ++ cpu_features->mops = GLRO (dl_hwcap2) & HWCAP2_MOPS; + } +diff -Nuar a/sysdeps/unix/sysv/linux/aarch64/cpu-features.h b/sysdeps/unix/sysv/linux/aarch64/cpu-features.h +--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.h 2025-10-20 21:02:21.000000000 +0300 +@@ -47,11 +47,6 @@ + #define IS_THUNDERX2(midr) (MIDR_IMPLEMENTOR(midr) == 'C' \ + && MIDR_PARTNUM(midr) == 0xaf) + +-#define IS_FALKOR(midr) (MIDR_IMPLEMENTOR(midr) == 'Q' \ +- && MIDR_PARTNUM(midr) == 0xc00) +- +-#define IS_PHECDA(midr) (MIDR_IMPLEMENTOR(midr) == 'h' \ +- && MIDR_PARTNUM(midr) == 0x000) + #define IS_NEOVERSE_N1(midr) (MIDR_IMPLEMENTOR(midr) == 'A' \ + && MIDR_PARTNUM(midr) == 0xd0c) + #define IS_NEOVERSE_N2(midr) (MIDR_IMPLEMENTOR(midr) == 'A' \ +@@ -76,6 +71,8 @@ + /* Currently, the GLIBC memory tagging tunable only defines 8 bits. */ + uint8_t mte_state; + bool sve; ++ bool prefer_sve_ifuncs; ++ bool mops; + }; + + #endif /* _CPU_FEATURES_AARCH64_H */ +diff -Nuar a/sysdeps/unix/sysv/linux/alpha/arch-syscall.h b/sysdeps/unix/sysv/linux/alpha/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/alpha/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/alpha/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -391,6 +391,7 @@ + #define __NR_sendmsg 114 + #define __NR_sendto 133 + #define __NR_set_mempolicy 431 ++#define __NR_set_mempolicy_home_node 560 + #define __NR_set_robust_list 466 + #define __NR_set_tid_address 411 + #define __NR_setdomainname 166 +diff -Nuar a/sysdeps/unix/sysv/linux/alpha/brk.c b/sysdeps/unix/sysv/linux/alpha/brk.c +--- a/sysdeps/unix/sysv/linux/alpha/brk.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/alpha/brk.c 1970-01-01 02:00:00.000000000 +0200 +@@ -1,38 +0,0 @@ +-/* Change data segment size. Linux/Alpha. +- Copyright (C) 2020-2022 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 +- . */ +- +-#include +-#include +-#include +- +-void *__curbrk = 0; +- +-int +-__brk (void *addr) +-{ +- /* Alpha brk returns -ENOMEM in case of failure. */ +- __curbrk = (void *) INTERNAL_SYSCALL_CALL (brk, addr); +- if ((unsigned long) __curbrk == -ENOMEM) +- { +- __set_errno (ENOMEM); +- return -1; +- } +- +- return 0; +-} +-weak_alias (__brk, brk) +diff -Nuar a/sysdeps/unix/sysv/linux/alpha/brk_call.h b/sysdeps/unix/sysv/linux/alpha/brk_call.h +--- a/sysdeps/unix/sysv/linux/alpha/brk_call.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/alpha/brk_call.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,27 @@ ++/* Invoke the brk system call. Alpha version. ++ Copyright (C) 2022 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 ++ . */ ++ ++static inline void * ++__brk_call (void *addr) ++{ ++ unsigned long int result = INTERNAL_SYSCALL_CALL (brk, addr); ++ if (result == -ENOMEM) ++ /* Mimic the generic error reporting behavior. */ ++ result = INTERNAL_SYSCALL_CALL (brk, 0); ++ return (void *) result; ++} +diff -Nuar a/sysdeps/unix/sysv/linux/alpha/dl-auxv.h b/sysdeps/unix/sysv/linux/alpha/dl-auxv.h +--- a/sysdeps/unix/sysv/linux/alpha/dl-auxv.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/alpha/dl-auxv.h 2025-10-20 21:02:21.000000000 +0300 +@@ -20,16 +20,8 @@ + + extern long __libc_alpha_cache_shape[4]; + +-#define DL_PLATFORM_AUXV \ +- case AT_L1I_CACHESHAPE: \ +- __libc_alpha_cache_shape[0] = av->a_un.a_val; \ +- break; \ +- case AT_L1D_CACHESHAPE: \ +- __libc_alpha_cache_shape[1] = av->a_un.a_val; \ +- break; \ +- case AT_L2_CACHESHAPE: \ +- __libc_alpha_cache_shape[2] = av->a_un.a_val; \ +- break; \ +- case AT_L3_CACHESHAPE: \ +- __libc_alpha_cache_shape[3] = av->a_un.a_val; \ +- break; ++#define DL_PLATFORM_AUXV \ ++ __libc_alpha_cache_shape[0] = auxv_values[AT_L1I_CACHESHAPE]; \ ++ __libc_alpha_cache_shape[1] = auxv_values[AT_L1D_CACHESHAPE]; \ ++ __libc_alpha_cache_shape[2] = auxv_values[AT_L2_CACHESHAPE]; \ ++ __libc_alpha_cache_shape[3] = auxv_values[AT_L3_CACHESHAPE]; +diff -Nuar a/sysdeps/unix/sysv/linux/arc/arch-syscall.h b/sysdeps/unix/sysv/linux/arc/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/arc/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/arc/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -238,6 +238,7 @@ + #define __NR_sendmsg 211 + #define __NR_sendto 206 + #define __NR_set_mempolicy 237 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 99 + #define __NR_set_tid_address 96 + #define __NR_setdomainname 162 +diff -Nuar a/sysdeps/unix/sysv/linux/arm/arch-syscall.h b/sysdeps/unix/sysv/linux/arm/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/arm/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/arm/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -302,6 +302,7 @@ + #define __NR_sendmsg 296 + #define __NR_sendto 290 + #define __NR_set_mempolicy 321 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 338 + #define __NR_set_tid_address 256 + #define __NR_set_tls 983045 +diff -Nuar a/sysdeps/unix/sysv/linux/arm/bits/struct_stat.h b/sysdeps/unix/sysv/linux/arm/bits/struct_stat.h +--- a/sysdeps/unix/sysv/linux/arm/bits/struct_stat.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/arm/bits/struct_stat.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,139 @@ ++/* Definition for struct stat. Linux/arm version. ++ Copyright (C) 2020-2022 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 ++ . */ ++ ++#if !defined _SYS_STAT_H && !defined _FCNTL_H ++# error "Never include directly; use instead." ++#endif ++ ++#ifndef _BITS_STRUCT_STAT_H ++#define _BITS_STRUCT_STAT_H 1 ++ ++#include ++#include ++ ++struct stat ++ { ++#ifdef __USE_TIME_BITS64 ++# include ++#else ++ __dev_t st_dev; /* Device. */ ++ unsigned short int __pad1; ++# ifndef __USE_FILE_OFFSET64 ++ __ino_t st_ino; /* File serial number. */ ++# else ++ __ino_t __st_ino; /* 32bit file serial number. */ ++# endif ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ unsigned short int __pad2; ++# ifndef __USE_FILE_OFFSET64 ++ __off_t st_size; /* Size of file, in bytes. */ ++# else ++ __off64_t st_size; /* Size of file, in bytes. */ ++# endif ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ ++# ifndef __USE_FILE_OFFSET64 ++ __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ ++# else ++ __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ ++# endif ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# define st_atime st_atim.tv_sec /* Backward compatibility. */ ++# define st_mtime st_mtim.tv_sec ++# define st_ctime st_ctim.tv_sec ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++# ifndef __USE_FILE_OFFSET64 ++ unsigned long int __glibc_reserved4; ++ unsigned long int __glibc_reserved5; ++# else ++ __ino64_t st_ino; /* File serial number. */ ++# endif ++#endif /* __USE_TIME_BITS64 */ ++ }; ++ ++#ifdef __USE_LARGEFILE64 ++struct stat64 ++ { ++# ifdef __USE_TIME_BITS64 ++# include ++# else ++ __dev_t st_dev; /* Device. */ ++ unsigned int __pad1; ++ ++ __ino_t __st_ino; /* 32bit file serial number. */ ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ unsigned int __pad2; ++ __off64_t st_size; /* Size of file, in bytes. */ ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ ++ __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++ __ino64_t st_ino; /* File serial number. */ ++# endif /* __USE_TIME_BITS64 */ ++ }; ++#endif ++ ++/* Tell code we have these members. */ ++#define _STATBUF_ST_BLKSIZE ++#define _STATBUF_ST_RDEV ++/* Nanosecond resolution time values are supported. */ ++#define _STATBUF_ST_NSEC ++ ++ ++#endif /* _BITS_STRUCT_STAT_H */ +diff -Nuar a/sysdeps/unix/sysv/linux/bits/fcntl-linux.h b/sysdeps/unix/sysv/linux/bits/fcntl-linux.h +--- a/sysdeps/unix/sysv/linux/bits/fcntl-linux.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/bits/fcntl-linux.h 2025-10-20 21:02:21.000000000 +0300 +@@ -101,7 +101,7 @@ + #endif + + #ifndef F_GETLK +-# ifndef __USE_FILE_OFFSET64 ++# if !defined __USE_FILE_OFFSET64 && __TIMESIZE != 64 + # define F_GETLK 5 /* Get record locking info. */ + # define F_SETLK 6 /* Set record locking info (non-blocking). */ + # define F_SETLKW 7 /* Set record locking info (blocking). */ +diff -Nuar a/sysdeps/unix/sysv/linux/bits/socket.h b/sysdeps/unix/sysv/linux/bits/socket.h +--- a/sysdeps/unix/sysv/linux/bits/socket.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/bits/socket.h 2025-10-20 21:02:21.000000000 +0300 +@@ -169,6 +169,8 @@ + #define SOL_KCM 281 + #define SOL_TLS 282 + #define SOL_XDP 283 ++#define SOL_MPTCP 284 ++#define SOL_MCTP 285 + + /* Maximum queue length specifiable by listen. */ + #define SOMAXCONN 4096 +@@ -304,6 +306,12 @@ + + CMSG_ALIGN (sizeof (struct cmsghdr))) + #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) + ++/* Given a length, return the additional padding necessary such that ++ len + __CMSG_PADDING(len) == CMSG_ALIGN (len). */ ++#define __CMSG_PADDING(len) ((sizeof (size_t) \ ++ - ((len) & (sizeof (size_t) - 1))) \ ++ & (sizeof (size_t) - 1)) ++ + extern struct cmsghdr *__cmsg_nxthdr (struct msghdr *__mhdr, + struct cmsghdr *__cmsg) __THROW; + #ifdef __USE_EXTERN_INLINES +@@ -313,18 +321,38 @@ + _EXTERN_INLINE struct cmsghdr * + __NTH (__cmsg_nxthdr (struct msghdr *__mhdr, struct cmsghdr *__cmsg)) + { ++ /* We may safely assume that __cmsg lies between __mhdr->msg_control and ++ __mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of __cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * __msg_control_ptr = (unsigned char *) __mhdr->msg_control; ++ unsigned char * __cmsg_ptr = (unsigned char *) __cmsg; ++ ++ size_t __size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (__cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) __cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ + return (struct cmsghdr *) 0; + ++ /* There isn't enough space between __cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr) ++ < __size_needed) ++ || ((size_t) ++ (__msg_control_ptr + __mhdr->msg_controllen - __cmsg_ptr ++ - __size_needed) ++ < __cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; ++ ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + __cmsg = (struct cmsghdr *) ((unsigned char *) __cmsg + + CMSG_ALIGN (__cmsg->cmsg_len)); +- if ((unsigned char *) (__cmsg + 1) > ((unsigned char *) __mhdr->msg_control +- + __mhdr->msg_controllen) +- || ((unsigned char *) __cmsg + CMSG_ALIGN (__cmsg->cmsg_len) +- > ((unsigned char *) __mhdr->msg_control + __mhdr->msg_controllen))) +- /* No more entries. */ +- return (struct cmsghdr *) 0; + return __cmsg; + } + #endif /* Use `extern inline'. */ +diff -Nuar a/sysdeps/unix/sysv/linux/bits/struct_stat.h b/sysdeps/unix/sysv/linux/bits/struct_stat.h +--- a/sysdeps/unix/sysv/linux/bits/struct_stat.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/bits/struct_stat.h 2025-10-20 21:02:21.000000000 +0300 +@@ -26,37 +26,36 @@ + #include + #include + +-struct stat +- { +-#ifdef __USE_TIME_BITS64 +-# include +-#else +- __dev_t st_dev; /* Device. */ +- unsigned short int __pad1; +-# ifndef __USE_FILE_OFFSET64 +- __ino_t st_ino; /* File serial number. */ +-# else +- __ino_t __st_ino; /* 32bit file serial number. */ ++#if defined __USE_FILE_OFFSET64 ++# define __field64(type, type64, name) type64 name ++#elif __WORDSIZE == 64 || defined __INO_T_MATCHES_INO64_T ++# if defined __INO_T_MATCHES_INO64_T && !defined __OFF_T_MATCHES_OFF64_T ++# error "ino_t and off_t must both be the same type" + # endif +- __mode_t st_mode; /* File mode. */ +- __nlink_t st_nlink; /* Link count. */ +- __uid_t st_uid; /* User ID of the file's owner. */ +- __gid_t st_gid; /* Group ID of the file's group.*/ +- __dev_t st_rdev; /* Device number, if device. */ +- unsigned short int __pad2; +-# ifndef __USE_FILE_OFFSET64 +- __off_t st_size; /* Size of file, in bytes. */ +-# else +- __off64_t st_size; /* Size of file, in bytes. */ +-# endif +- __blksize_t st_blksize; /* Optimal block size for I/O. */ ++# define __field64(type, type64, name) type name ++#elif __BYTE_ORDER == __LITTLE_ENDIAN ++# define __field64(type, type64, name) \ ++ type name __attribute__((__aligned__ (__alignof__ (type64)))); int __##name##_pad ++#else ++# define __field64(type, type64, name) \ ++ int __##name##_pad __attribute__((__aligned__ (__alignof__ (type64)))); type name ++#endif + +-# ifndef __USE_FILE_OFFSET64 +- __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ +-# else +- __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ +-# endif +-# ifdef __USE_XOPEN2K8 ++struct stat ++ { ++ __dev_t st_dev; /* Device. */ ++ __field64(__ino_t, __ino64_t, st_ino); /* File serial number. */ ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ __dev_t __pad1; ++ __field64(__off_t, __off64_t, st_size); /* Size of file, in bytes. */ ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ int __pad2; ++ __field64(__blkcnt_t, __blkcnt64_t, st_blocks); /* 512-byte blocks */ ++#ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the +@@ -66,47 +65,38 @@ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +-# define st_atime st_atim.tv_sec /* Backward compatibility. */ +-# define st_mtime st_mtim.tv_sec +-# define st_ctime st_ctim.tv_sec +-# else ++# define st_atime st_atim.tv_sec /* Backward compatibility. */ ++# define st_mtime st_mtim.tv_sec ++# define st_ctime st_ctim.tv_sec ++#else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +-# endif +-# ifndef __USE_FILE_OFFSET64 +- unsigned long int __glibc_reserved4; +- unsigned long int __glibc_reserved5; +-# else +- __ino64_t st_ino; /* File serial number. */ +-# endif +-#endif /* __USE_TIME_BITS64 */ ++#endif ++ int __glibc_reserved[2]; + }; + ++#undef __field64 ++ + #ifdef __USE_LARGEFILE64 + struct stat64 + { +-# ifdef __USE_TIME_BITS64 +-# include +-# else +- __dev_t st_dev; /* Device. */ +- unsigned int __pad1; +- +- __ino_t __st_ino; /* 32bit file serial number. */ +- __mode_t st_mode; /* File mode. */ +- __nlink_t st_nlink; /* Link count. */ +- __uid_t st_uid; /* User ID of the file's owner. */ +- __gid_t st_gid; /* Group ID of the file's group.*/ +- __dev_t st_rdev; /* Device number, if device. */ +- unsigned int __pad2; +- __off64_t st_size; /* Size of file, in bytes. */ +- __blksize_t st_blksize; /* Optimal block size for I/O. */ +- +- __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ +-# ifdef __USE_XOPEN2K8 ++ __dev_t st_dev; /* Device. */ ++ __ino64_t st_ino; /* File serial number. */ ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ __dev_t __pad1; ++ __off64_t st_size; /* Size of file, in bytes. */ ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ int __pad2; ++ __blkcnt64_t st_blocks; /* Nr. 512-byte blocks allocated. */ ++#ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the +@@ -116,16 +106,15 @@ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +-# else ++#else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +-# endif +- __ino64_t st_ino; /* File serial number. */ +-# endif /* __USE_TIME_BITS64 */ ++#endif ++ int __glibc_reserved[2]; + }; + #endif + +@@ -135,5 +124,4 @@ + /* Nanosecond resolution time values are supported. */ + #define _STATBUF_ST_NSEC + +- + #endif /* _BITS_STRUCT_STAT_H */ +diff -Nuar a/sysdeps/unix/sysv/linux/bits/uio-ext.h b/sysdeps/unix/sysv/linux/bits/uio-ext.h +--- a/sysdeps/unix/sysv/linux/bits/uio-ext.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/bits/uio-ext.h 2025-10-20 21:02:21.000000000 +0300 +@@ -47,6 +47,7 @@ + #define RWF_SYNC 0x00000004 /* per-IO O_SYNC. */ + #define RWF_NOWAIT 0x00000008 /* per-IO nonblocking mode. */ + #define RWF_APPEND 0x00000010 /* per-IO O_APPEND. */ ++#define RWF_NOAPPEND 0x00000020 /* per-IO negation of O_APPEND */ + + __END_DECLS + +diff -Nuar a/sysdeps/unix/sysv/linux/brk.c b/sysdeps/unix/sysv/linux/brk.c +--- a/sysdeps/unix/sysv/linux/brk.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/brk.c 2025-10-20 21:02:21.000000000 +0300 +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + /* This must be initialized data because commons can't have aliases. */ + void *__curbrk = 0; +@@ -33,7 +34,7 @@ + int + __brk (void *addr) + { +- __curbrk = (void *) INTERNAL_SYSCALL_CALL (brk, addr); ++ __curbrk = __brk_call (addr); + if (__curbrk < addr) + { + __set_errno (ENOMEM); +diff -Nuar a/sysdeps/unix/sysv/linux/brk_call.h b/sysdeps/unix/sysv/linux/brk_call.h +--- a/sysdeps/unix/sysv/linux/brk_call.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/brk_call.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,25 @@ ++/* Invoke the brk system call. Generic Linux version. ++ Copyright (C) 2022 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 ++ . */ ++ ++static inline void * ++__brk_call (void *addr) ++{ ++ /* The default implementation reports errors through an unchanged ++ break. */ ++ return (void *) INTERNAL_SYSCALL_CALL (brk, addr); ++} +diff -Nuar a/sysdeps/unix/sysv/linux/check_pf.c b/sysdeps/unix/sysv/linux/check_pf.c +--- a/sysdeps/unix/sysv/linux/check_pf.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/check_pf.c 2025-10-20 21:02:21.000000000 +0300 +@@ -292,6 +292,14 @@ + return NULL; + } + ++#ifdef __EXCEPTIONS ++static void ++cancel_handler (void *arg __attribute__((unused))) ++{ ++ /* Release the lock. */ ++ __libc_lock_unlock (lock); ++} ++#endif + + void + attribute_hidden +@@ -304,6 +312,10 @@ + struct cached_data *olddata = NULL; + struct cached_data *data = NULL; + ++#ifdef __EXCEPTIONS ++ /* Make sure that lock is released when the thread is cancelled. */ ++ __libc_cleanup_push (cancel_handler, NULL); ++#endif + __libc_lock_lock (lock); + + if (cache_valid_p ()) +@@ -338,6 +350,9 @@ + } + } + ++#ifdef __EXCEPTIONS ++ __libc_cleanup_pop (0); ++#endif + __libc_lock_unlock (lock); + + if (data != NULL) +diff -Nuar a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c +--- a/sysdeps/unix/sysv/linux/cmsg_nxthdr.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/cmsg_nxthdr.c 2025-10-20 21:02:21.000000000 +0300 +@@ -23,18 +23,38 @@ + struct cmsghdr * + __cmsg_nxthdr (struct msghdr *mhdr, struct cmsghdr *cmsg) + { ++ /* We may safely assume that cmsg lies between mhdr->msg_control and ++ mhdr->msg_controllen because the user is required to obtain the first ++ cmsg via CMSG_FIRSTHDR, set its length, then obtain subsequent cmsgs ++ via CMSG_NXTHDR, setting lengths along the way. However, we don't yet ++ trust the value of cmsg->cmsg_len and therefore do not use it in any ++ pointer arithmetic until we check its value. */ ++ ++ unsigned char * msg_control_ptr = (unsigned char *) mhdr->msg_control; ++ unsigned char * cmsg_ptr = (unsigned char *) cmsg; ++ ++ size_t size_needed = sizeof (struct cmsghdr) ++ + __CMSG_PADDING (cmsg->cmsg_len); ++ ++ /* The current header is malformed, too small to be a full header. */ + if ((size_t) cmsg->cmsg_len < sizeof (struct cmsghdr)) +- /* The kernel header does this so there may be a reason. */ +- return NULL; ++ return (struct cmsghdr *) 0; ++ ++ /* There isn't enough space between cmsg and the end of the buffer to ++ hold the current cmsg *and* the next one. */ ++ if (((size_t) ++ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr) ++ < size_needed) ++ || ((size_t) ++ (msg_control_ptr + mhdr->msg_controllen - cmsg_ptr ++ - size_needed) ++ < cmsg->cmsg_len)) ++ ++ return (struct cmsghdr *) 0; + ++ /* Now, we trust cmsg_len and can use it to find the next header. */ + cmsg = (struct cmsghdr *) ((unsigned char *) cmsg + + CMSG_ALIGN (cmsg->cmsg_len)); +- if ((unsigned char *) (cmsg + 1) > ((unsigned char *) mhdr->msg_control +- + mhdr->msg_controllen) +- || ((unsigned char *) cmsg + CMSG_ALIGN (cmsg->cmsg_len) +- > ((unsigned char *) mhdr->msg_control + mhdr->msg_controllen))) +- /* No more entries. */ +- return NULL; + return cmsg; + } + libc_hidden_def (__cmsg_nxthdr) +diff -Nuar a/sysdeps/unix/sysv/linux/convert_scm_timestamps.c b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c +--- a/sysdeps/unix/sysv/linux/convert_scm_timestamps.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c 2025-10-20 21:02:21.000000000 +0300 +@@ -16,9 +16,9 @@ + License along with the GNU C Library; if not, see + . */ + +-#include ++#include + +-#ifndef __ASSUME_TIME64_SYSCALLS ++#if __TIMESIZE != 64 + # include + # include + # include +diff -Nuar a/sysdeps/unix/sysv/linux/csky/arch-syscall.h b/sysdeps/unix/sysv/linux/csky/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/csky/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/csky/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -250,6 +250,7 @@ + #define __NR_sendmsg 211 + #define __NR_sendto 206 + #define __NR_set_mempolicy 237 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 99 + #define __NR_set_thread_area 244 + #define __NR_set_tid_address 96 +diff -Nuar a/sysdeps/unix/sysv/linux/csky/bits/struct_stat.h b/sysdeps/unix/sysv/linux/csky/bits/struct_stat.h +--- a/sysdeps/unix/sysv/linux/csky/bits/struct_stat.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/csky/bits/struct_stat.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,135 @@ ++/* Definition for struct stat. Linux/csky version. ++ Copyright (C) 2020-2022 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 ++ . */ ++ ++#if !defined _SYS_STAT_H && !defined _FCNTL_H ++# error "Never include directly; use instead." ++#endif ++ ++#ifndef _BITS_STRUCT_STAT_H ++#define _BITS_STRUCT_STAT_H 1 ++ ++#include ++#include ++ ++#if defined __USE_FILE_OFFSET64 ++# define __field64(type, type64, name) type64 name ++#elif __WORDSIZE == 64 || defined __INO_T_MATCHES_INO64_T ++# if defined __INO_T_MATCHES_INO64_T && !defined __OFF_T_MATCHES_OFF64_T ++# error "ino_t and off_t must both be the same type" ++# endif ++# define __field64(type, type64, name) type name ++#elif __BYTE_ORDER == __LITTLE_ENDIAN ++# define __field64(type, type64, name) \ ++ type name __attribute__((__aligned__ (__alignof__ (type64)))); int __##name##_pad ++#else ++# define __field64(type, type64, name) \ ++ int __##name##_pad __attribute__((__aligned__ (__alignof__ (type64)))); type name ++#endif ++ ++struct stat ++ { ++#ifdef __USE_TIME_BITS64 ++# include ++#else ++ __dev_t st_dev; /* Device. */ ++ __field64(__ino_t, __ino64_t, st_ino); /* File serial number. */ ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ __dev_t __pad1; ++ __field64(__off_t, __off64_t, st_size); /* Size of file, in bytes. */ ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ int __pad2; ++ __field64(__blkcnt_t, __blkcnt64_t, st_blocks); /* 512-byte blocks */ ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# define st_atime st_atim.tv_sec /* Backward compatibility. */ ++# define st_mtime st_mtim.tv_sec ++# define st_ctime st_ctim.tv_sec ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++ int __glibc_reserved[2]; ++#endif ++ }; ++ ++#undef __field64 ++ ++#ifdef __USE_LARGEFILE64 ++struct stat64 ++ { ++# ifdef __USE_TIME_BITS64 ++# include ++# else ++ __dev_t st_dev; /* Device. */ ++ __ino64_t st_ino; /* File serial number. */ ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ __dev_t __pad1; ++ __off64_t st_size; /* Size of file, in bytes. */ ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ int __pad2; ++ __blkcnt64_t st_blocks; /* Nr. 512-byte blocks allocated. */ ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++ int __glibc_reserved[2]; ++# endif ++ }; ++#endif ++ ++/* Tell code we have these members. */ ++#define _STATBUF_ST_BLKSIZE ++#define _STATBUF_ST_RDEV ++/* Nanosecond resolution time values are supported. */ ++#define _STATBUF_ST_NSEC ++ ++#endif /* _BITS_STRUCT_STAT_H */ +diff -Nuar a/sysdeps/unix/sysv/linux/dl-early_allocate.c b/sysdeps/unix/sysv/linux/dl-early_allocate.c +--- a/sysdeps/unix/sysv/linux/dl-early_allocate.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/dl-early_allocate.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,82 @@ ++/* Early memory allocation for the dynamic loader. Generic version. ++ Copyright (C) 2022 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 ++ . */ ++ ++/* Mark symbols hidden in static PIE for early self relocation to work. */ ++#if BUILD_PIE_DEFAULT ++# pragma GCC visibility push(hidden) ++#endif ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* Defined in brk.c. */ ++extern void *__curbrk; ++ ++void * ++_dl_early_allocate (size_t size) ++{ ++ void *result; ++ ++ if (__curbrk != NULL) ++ /* If the break has been initialized, brk must have run before, ++ so just call it once more. */ ++ { ++ result = __sbrk (size); ++ if (result == (void *) -1) ++ result = NULL; ++ } ++ else ++ { ++ /* If brk has not been invoked, there is no need to update ++ __curbrk. The first call to brk will take care of that. */ ++ void *previous = __brk_call (0); ++ result = __brk_call (previous + size); ++ if (result == previous) ++ result = NULL; ++ else ++ result = previous; ++ } ++ ++ /* If brk fails, fall back to mmap. This can happen due to ++ unfortunate ASLR layout decisions and kernel bugs, particularly ++ for static PIE. */ ++ if (result == NULL) ++ { ++ long int ret; ++ int prot = PROT_READ | PROT_WRITE; ++ int flags = MAP_PRIVATE | MAP_ANONYMOUS; ++#ifdef __NR_mmap2 ++ ret = MMAP_CALL_INTERNAL (mmap2, 0, size, prot, flags, -1, 0); ++#else ++ ret = MMAP_CALL_INTERNAL (mmap, 0, size, prot, flags, -1, 0); ++#endif ++ if (INTERNAL_SYSCALL_ERROR_P (ret)) ++ result = NULL; ++ else ++ result = (void *) ret; ++ } ++ ++ return result; ++} +diff -Nuar a/sysdeps/unix/sysv/linux/dl-parse_auxv.h b/sysdeps/unix/sysv/linux/dl-parse_auxv.h +--- a/sysdeps/unix/sysv/linux/dl-parse_auxv.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/dl-parse_auxv.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,61 @@ ++/* Parse the Linux auxiliary vector. ++ Copyright (C) 1995-2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++typedef ElfW(Addr) dl_parse_auxv_t[AT_MINSIGSTKSZ + 1]; ++ ++/* Copy the auxiliary vector into AUXV_VALUES and set up GLRO ++ variables. */ ++static inline ++void _dl_parse_auxv (ElfW(auxv_t) *av, dl_parse_auxv_t auxv_values) ++{ ++ auxv_values[AT_ENTRY] = (ElfW(Addr)) ENTRY_POINT; ++ auxv_values[AT_PAGESZ] = EXEC_PAGESIZE; ++ auxv_values[AT_FPUCW] = _FPU_DEFAULT; ++ ++ /* NB: Default to a constant CONSTANT_MINSIGSTKSZ. */ ++ _Static_assert (__builtin_constant_p (CONSTANT_MINSIGSTKSZ), ++ "CONSTANT_MINSIGSTKSZ is constant"); ++ auxv_values[AT_MINSIGSTKSZ] = CONSTANT_MINSIGSTKSZ; ++ ++ for (; av->a_type != AT_NULL; av++) ++ if (av->a_type <= AT_MINSIGSTKSZ) ++ auxv_values[av->a_type] = av->a_un.a_val; ++ ++ GLRO(dl_pagesize) = auxv_values[AT_PAGESZ]; ++ __libc_enable_secure = auxv_values[AT_SECURE]; ++ GLRO(dl_platform) = (void *) auxv_values[AT_PLATFORM]; ++ GLRO(dl_hwcap) = auxv_values[AT_HWCAP]; ++ GLRO(dl_hwcap2) = auxv_values[AT_HWCAP2]; ++ GLRO(dl_clktck) = auxv_values[AT_CLKTCK]; ++ GLRO(dl_fpu_control) = auxv_values[AT_FPUCW]; ++ _dl_random = (void *) auxv_values[AT_RANDOM]; ++ GLRO(dl_minsigstacksize) = auxv_values[AT_MINSIGSTKSZ]; ++ GLRO(dl_sysinfo_dso) = (void *) auxv_values[AT_SYSINFO_EHDR]; ++#ifdef NEED_DL_SYSINFO ++ if (GLRO(dl_sysinfo_dso) != NULL) ++ GLRO(dl_sysinfo) = auxv_values[AT_SYSINFO]; ++#endif ++ ++ DL_PLATFORM_AUXV ++} +diff -Nuar a/sysdeps/unix/sysv/linux/dl-rseq-symbols.S b/sysdeps/unix/sysv/linux/dl-rseq-symbols.S +--- a/sysdeps/unix/sysv/linux/dl-rseq-symbols.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/dl-rseq-symbols.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,64 @@ ++/* Define symbols used by rseq. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++ ++#if __WORDSIZE == 64 ++#define RSEQ_OFFSET_SIZE 8 ++#else ++#define RSEQ_OFFSET_SIZE 4 ++#endif ++ ++/* Some targets define a macro to denote the zero register. */ ++#undef zero ++ ++/* Define 2 symbols: '__rseq_size' is public const and '_rseq_size' (an ++ alias of '__rseq_size') is hidden and writable for internal use by the ++ dynamic linker which will initialize the value both symbols point to ++ before copy relocations take place. */ ++ ++ .globl __rseq_size ++ .type __rseq_size, %object ++ .size __rseq_size, 4 ++ .hidden _rseq_size ++ .globl _rseq_size ++ .type _rseq_size, %object ++ .size _rseq_size, 4 ++ .section .data.rel.ro ++ .balign 4 ++__rseq_size: ++_rseq_size: ++ .zero 4 ++ ++/* Define 2 symbols: '__rseq_offset' is public const and '_rseq_offset' (an ++ alias of '__rseq_offset') is hidden and writable for internal use by the ++ dynamic linker which will initialize the value both symbols point to ++ before copy relocations take place. */ ++ ++ .globl __rseq_offset ++ .type __rseq_offset, %object ++ .size __rseq_offset, RSEQ_OFFSET_SIZE ++ .hidden _rseq_offset ++ .globl _rseq_offset ++ .type _rseq_offset, %object ++ .size _rseq_offset, RSEQ_OFFSET_SIZE ++ .section .data.rel.ro ++ .balign RSEQ_OFFSET_SIZE ++__rseq_offset: ++_rseq_offset: ++ .zero RSEQ_OFFSET_SIZE +diff -Nuar a/sysdeps/unix/sysv/linux/dl-sysdep.c b/sysdeps/unix/sysv/linux/dl-sysdep.c +--- a/sysdeps/unix/sysv/linux/dl-sysdep.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/dl-sysdep.c 2025-10-20 21:02:21.000000000 +0300 +@@ -16,35 +16,247 @@ + License along with the GNU C Library; if not, see + . */ + +-/* Linux needs some special initialization, but otherwise uses +- the generic dynamic linker system interface code. */ +- +-#include ++#include <_itoa.h> ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + #include +-#include +-#include +-#include + #include ++#include ++#include + #include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include + + #ifdef SHARED +-# define DL_SYSDEP_INIT frob_brk () ++extern char **_environ attribute_hidden; ++extern char _end[] attribute_hidden; ++ ++/* Protect SUID program against misuse of file descriptors. */ ++extern void __libc_check_standard_fds (void); ++ ++int __libc_enable_secure attribute_relro = 0; ++rtld_hidden_data_def (__libc_enable_secure) ++/* This variable contains the lowest stack address ever used. */ ++void *__libc_stack_end attribute_relro = NULL; ++rtld_hidden_data_def(__libc_stack_end) ++void *_dl_random attribute_relro = NULL; ++ ++#ifndef DL_STACK_END ++# define DL_STACK_END(cookie) ((void *) (cookie)) ++#endif + +-static inline void +-frob_brk (void) ++/* Arguments passed to dl_main. */ ++struct dl_main_arguments + { +- __brk (0); /* Initialize the break. */ ++ const ElfW(Phdr) *phdr; ++ ElfW(Word) phnum; ++ ElfW(Addr) user_entry; ++}; ++ ++/* Separate function, so that dl_main can be called without the large ++ array on the stack. */ ++static void ++_dl_sysdep_parse_arguments (void **start_argptr, ++ struct dl_main_arguments *args) ++{ ++ _dl_argc = (intptr_t) *start_argptr; ++ _dl_argv = (char **) (start_argptr + 1); /* Necessary aliasing violation. */ ++ _environ = _dl_argv + _dl_argc + 1; ++ for (char **tmp = _environ; ; ++tmp) ++ if (*tmp == NULL) ++ { ++ /* Another necessary aliasing violation. */ ++ GLRO(dl_auxv) = (ElfW(auxv_t) *) (tmp + 1); ++ break; ++ } ++ ++ dl_parse_auxv_t auxv_values = { 0, }; ++ _dl_parse_auxv (GLRO(dl_auxv), auxv_values); ++ ++ args->phdr = (const ElfW(Phdr) *) auxv_values[AT_PHDR]; ++ args->phnum = auxv_values[AT_PHNUM]; ++ args->user_entry = auxv_values[AT_ENTRY]; + } + +-# include ++ElfW(Addr) ++_dl_sysdep_start (void **start_argptr, ++ void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum, ++ ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv)) ++{ ++ __libc_stack_end = DL_STACK_END (start_argptr); ++ ++ struct dl_main_arguments dl_main_args; ++ _dl_sysdep_parse_arguments (start_argptr, &dl_main_args); ++ ++ dl_hwcap_check (); ++ ++ __tunables_init (_environ); ++ ++ /* Initialize DSO sorting algorithm after tunables. */ ++ _dl_sort_maps_init (); ++ ++ __brk (0); /* Initialize the break. */ ++ ++#ifdef DL_PLATFORM_INIT ++ DL_PLATFORM_INIT; + #endif + ++ /* Determine the length of the platform name. */ ++ if (GLRO(dl_platform) != NULL) ++ GLRO(dl_platformlen) = strlen (GLRO(dl_platform)); ++ ++ if (__sbrk (0) == _end) ++ /* The dynamic linker was run as a program, and so the initial break ++ starts just after our bss, at &_end. The malloc in dl-minimal.c ++ will consume the rest of this page, so tell the kernel to move the ++ break up that far. When the user program examines its break, it ++ will see this new value and not clobber our data. */ ++ __sbrk (GLRO(dl_pagesize) ++ - ((_end - (char *) 0) & (GLRO(dl_pagesize) - 1))); ++ ++ /* If this is a SUID program we make sure that FDs 0, 1, and 2 are ++ allocated. If necessary we are doing it ourself. If it is not ++ possible we stop the program. */ ++ if (__builtin_expect (__libc_enable_secure, 0)) ++ __libc_check_standard_fds (); ++ ++ (*dl_main) (dl_main_args.phdr, dl_main_args.phnum, ++ &dl_main_args.user_entry, GLRO(dl_auxv)); ++ return dl_main_args.user_entry; ++} ++ ++void ++_dl_sysdep_start_cleanup (void) ++{ ++} ++ ++void ++_dl_show_auxv (void) ++{ ++ char buf[64]; ++ ElfW(auxv_t) *av; ++ ++ /* Terminate string. */ ++ buf[63] = '\0'; ++ ++ /* The following code assumes that the AT_* values are encoded ++ starting from 0 with AT_NULL, 1 for AT_IGNORE, and all other values ++ close by (otherwise the array will be too large). In case we have ++ to support a platform where these requirements are not fulfilled ++ some alternative implementation has to be used. */ ++ for (av = GLRO(dl_auxv); av->a_type != AT_NULL; ++av) ++ { ++ static const struct ++ { ++ const char label[22]; ++ enum { unknown = 0, dec, hex, str, ignore } form : 8; ++ } auxvars[] = ++ { ++ [AT_EXECFD - 2] = { "EXECFD: ", dec }, ++ [AT_EXECFN - 2] = { "EXECFN: ", str }, ++ [AT_PHDR - 2] = { "PHDR: 0x", hex }, ++ [AT_PHENT - 2] = { "PHENT: ", dec }, ++ [AT_PHNUM - 2] = { "PHNUM: ", dec }, ++ [AT_PAGESZ - 2] = { "PAGESZ: ", dec }, ++ [AT_BASE - 2] = { "BASE: 0x", hex }, ++ [AT_FLAGS - 2] = { "FLAGS: 0x", hex }, ++ [AT_ENTRY - 2] = { "ENTRY: 0x", hex }, ++ [AT_NOTELF - 2] = { "NOTELF: ", hex }, ++ [AT_UID - 2] = { "UID: ", dec }, ++ [AT_EUID - 2] = { "EUID: ", dec }, ++ [AT_GID - 2] = { "GID: ", dec }, ++ [AT_EGID - 2] = { "EGID: ", dec }, ++ [AT_PLATFORM - 2] = { "PLATFORM: ", str }, ++ [AT_HWCAP - 2] = { "HWCAP: ", hex }, ++ [AT_CLKTCK - 2] = { "CLKTCK: ", dec }, ++ [AT_FPUCW - 2] = { "FPUCW: ", hex }, ++ [AT_DCACHEBSIZE - 2] = { "DCACHEBSIZE: 0x", hex }, ++ [AT_ICACHEBSIZE - 2] = { "ICACHEBSIZE: 0x", hex }, ++ [AT_UCACHEBSIZE - 2] = { "UCACHEBSIZE: 0x", hex }, ++ [AT_IGNOREPPC - 2] = { "IGNOREPPC", ignore }, ++ [AT_SECURE - 2] = { "SECURE: ", dec }, ++ [AT_BASE_PLATFORM - 2] = { "BASE_PLATFORM: ", str }, ++ [AT_SYSINFO - 2] = { "SYSINFO: 0x", hex }, ++ [AT_SYSINFO_EHDR - 2] = { "SYSINFO_EHDR: 0x", hex }, ++ [AT_RANDOM - 2] = { "RANDOM: 0x", hex }, ++ [AT_HWCAP2 - 2] = { "HWCAP2: 0x", hex }, ++ [AT_MINSIGSTKSZ - 2] = { "MINSIGSTKSZ: ", dec }, ++ [AT_L1I_CACHESIZE - 2] = { "L1I_CACHESIZE: ", dec }, ++ [AT_L1I_CACHEGEOMETRY - 2] = { "L1I_CACHEGEOMETRY: 0x", hex }, ++ [AT_L1D_CACHESIZE - 2] = { "L1D_CACHESIZE: ", dec }, ++ [AT_L1D_CACHEGEOMETRY - 2] = { "L1D_CACHEGEOMETRY: 0x", hex }, ++ [AT_L2_CACHESIZE - 2] = { "L2_CACHESIZE: ", dec }, ++ [AT_L2_CACHEGEOMETRY - 2] = { "L2_CACHEGEOMETRY: 0x", hex }, ++ [AT_L3_CACHESIZE - 2] = { "L3_CACHESIZE: ", dec }, ++ [AT_L3_CACHEGEOMETRY - 2] = { "L3_CACHEGEOMETRY: 0x", hex }, ++ }; ++ unsigned int idx = (unsigned int) (av->a_type - 2); ++ ++ if ((unsigned int) av->a_type < 2u ++ || (idx < sizeof (auxvars) / sizeof (auxvars[0]) ++ && auxvars[idx].form == ignore)) ++ continue; ++ ++ assert (AT_NULL == 0); ++ assert (AT_IGNORE == 1); ++ ++ /* Some entries are handled in a special way per platform. */ ++ if (_dl_procinfo (av->a_type, av->a_un.a_val) == 0) ++ continue; ++ ++ if (idx < sizeof (auxvars) / sizeof (auxvars[0]) ++ && auxvars[idx].form != unknown) ++ { ++ const char *val = (char *) av->a_un.a_val; ++ ++ if (__builtin_expect (auxvars[idx].form, dec) == dec) ++ val = _itoa ((unsigned long int) av->a_un.a_val, ++ buf + sizeof buf - 1, 10, 0); ++ else if (__builtin_expect (auxvars[idx].form, hex) == hex) ++ val = _itoa ((unsigned long int) av->a_un.a_val, ++ buf + sizeof buf - 1, 16, 0); ++ ++ _dl_printf ("AT_%s%s\n", auxvars[idx].label, val); ++ ++ continue; ++ } ++ ++ /* Unknown value: print a generic line. */ ++ char buf2[17]; ++ buf2[sizeof (buf2) - 1] = '\0'; ++ const char *val2 = _itoa ((unsigned long int) av->a_un.a_val, ++ buf2 + sizeof buf2 - 1, 16, 0); ++ const char *val = _itoa ((unsigned long int) av->a_type, ++ buf + sizeof buf - 1, 16, 0); ++ _dl_printf ("AT_??? (0x%s): 0x%s\n", val, val2); ++ } ++} ++ ++#endif /* SHARED */ ++ + + int + attribute_hidden + _dl_discover_osversion (void) + { +-#if defined NEED_DL_SYSINFO_DSO && defined SHARED ++#ifdef SHARED + if (GLRO(dl_sysinfo_map) != NULL) + { + /* If the kernel-supplied DSO contains a note indicating the kernel's +@@ -75,7 +287,7 @@ + } + } + } +-#endif ++#endif /* SHARED */ + + char bufmem[64]; + char *buf = bufmem; +diff -Nuar a/sysdeps/unix/sysv/linux/faccessat.c b/sysdeps/unix/sysv/linux/faccessat.c +--- a/sysdeps/unix/sysv/linux/faccessat.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/faccessat.c 2025-10-20 21:02:21.000000000 +0300 +@@ -39,8 +39,8 @@ + if ((flag == 0 || ((flag & ~AT_EACCESS) == 0 && ! __libc_enable_secure))) + return INLINE_SYSCALL (faccessat, 3, fd, file, mode); + +- struct stat64 stats; +- if (__fstatat64 (fd, file, &stats, flag & AT_SYMLINK_NOFOLLOW)) ++ struct __stat64_t64 stats; ++ if (__fstatat64_time64 (fd, file, &stats, flag & AT_SYMLINK_NOFOLLOW)) + return -1; + + mode &= (X_OK | W_OK | R_OK); /* Clear any bogus bits. */ +diff -Nuar a/sysdeps/unix/sysv/linux/fchmodat.c b/sysdeps/unix/sysv/linux/fchmodat.c +--- a/sysdeps/unix/sysv/linux/fchmodat.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/fchmodat.c 2025-10-20 21:02:21.000000000 +0300 +@@ -48,8 +48,8 @@ + + /* Use fstatat because fstat does not work on O_PATH descriptors + before Linux 3.6. */ +- struct stat64 st; +- if (__fstatat64 (pathfd, "", &st, AT_EMPTY_PATH) != 0) ++ struct __stat64_t64 st; ++ if (__fstatat64_time64 (pathfd, "", &st, AT_EMPTY_PATH) != 0) + { + __close_nocancel (pathfd); + return -1; +diff -Nuar a/sysdeps/unix/sysv/linux/generic/bits/struct_stat.h b/sysdeps/unix/sysv/linux/generic/bits/struct_stat.h +--- a/sysdeps/unix/sysv/linux/generic/bits/struct_stat.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/generic/bits/struct_stat.h 1970-01-01 02:00:00.000000000 +0200 +@@ -1,127 +0,0 @@ +-/* Definition for struct stat. +- Copyright (C) 2020-2022 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 +- . */ +- +-#if !defined _SYS_STAT_H && !defined _FCNTL_H +-# error "Never include directly; use instead." +-#endif +- +-#ifndef _BITS_STRUCT_STAT_H +-#define _BITS_STRUCT_STAT_H 1 +- +-#include +-#include +- +-#if defined __USE_FILE_OFFSET64 +-# define __field64(type, type64, name) type64 name +-#elif __WORDSIZE == 64 || defined __INO_T_MATCHES_INO64_T +-# if defined __INO_T_MATCHES_INO64_T && !defined __OFF_T_MATCHES_OFF64_T +-# error "ino_t and off_t must both be the same type" +-# endif +-# define __field64(type, type64, name) type name +-#elif __BYTE_ORDER == __LITTLE_ENDIAN +-# define __field64(type, type64, name) \ +- type name __attribute__((__aligned__ (__alignof__ (type64)))); int __##name##_pad +-#else +-# define __field64(type, type64, name) \ +- int __##name##_pad __attribute__((__aligned__ (__alignof__ (type64)))); type name +-#endif +- +-struct stat +- { +- __dev_t st_dev; /* Device. */ +- __field64(__ino_t, __ino64_t, st_ino); /* File serial number. */ +- __mode_t st_mode; /* File mode. */ +- __nlink_t st_nlink; /* Link count. */ +- __uid_t st_uid; /* User ID of the file's owner. */ +- __gid_t st_gid; /* Group ID of the file's group.*/ +- __dev_t st_rdev; /* Device number, if device. */ +- __dev_t __pad1; +- __field64(__off_t, __off64_t, st_size); /* Size of file, in bytes. */ +- __blksize_t st_blksize; /* Optimal block size for I/O. */ +- int __pad2; +- __field64(__blkcnt_t, __blkcnt64_t, st_blocks); /* 512-byte blocks */ +-#ifdef __USE_XOPEN2K8 +- /* Nanosecond resolution timestamps are stored in a format +- equivalent to 'struct timespec'. This is the type used +- whenever possible but the Unix namespace rules do not allow the +- identifier 'timespec' to appear in the header. +- Therefore we have to handle the use of this header in strictly +- standard-compliant sources special. */ +- struct timespec st_atim; /* Time of last access. */ +- struct timespec st_mtim; /* Time of last modification. */ +- struct timespec st_ctim; /* Time of last status change. */ +-# define st_atime st_atim.tv_sec /* Backward compatibility. */ +-# define st_mtime st_mtim.tv_sec +-# define st_ctime st_ctim.tv_sec +-#else +- __time_t st_atime; /* Time of last access. */ +- unsigned long int st_atimensec; /* Nscecs of last access. */ +- __time_t st_mtime; /* Time of last modification. */ +- unsigned long int st_mtimensec; /* Nsecs of last modification. */ +- __time_t st_ctime; /* Time of last status change. */ +- unsigned long int st_ctimensec; /* Nsecs of last status change. */ +-#endif +- int __glibc_reserved[2]; +- }; +- +-#undef __field64 +- +-#ifdef __USE_LARGEFILE64 +-struct stat64 +- { +- __dev_t st_dev; /* Device. */ +- __ino64_t st_ino; /* File serial number. */ +- __mode_t st_mode; /* File mode. */ +- __nlink_t st_nlink; /* Link count. */ +- __uid_t st_uid; /* User ID of the file's owner. */ +- __gid_t st_gid; /* Group ID of the file's group.*/ +- __dev_t st_rdev; /* Device number, if device. */ +- __dev_t __pad1; +- __off64_t st_size; /* Size of file, in bytes. */ +- __blksize_t st_blksize; /* Optimal block size for I/O. */ +- int __pad2; +- __blkcnt64_t st_blocks; /* Nr. 512-byte blocks allocated. */ +-#ifdef __USE_XOPEN2K8 +- /* Nanosecond resolution timestamps are stored in a format +- equivalent to 'struct timespec'. This is the type used +- whenever possible but the Unix namespace rules do not allow the +- identifier 'timespec' to appear in the header. +- Therefore we have to handle the use of this header in strictly +- standard-compliant sources special. */ +- struct timespec st_atim; /* Time of last access. */ +- struct timespec st_mtim; /* Time of last modification. */ +- struct timespec st_ctim; /* Time of last status change. */ +-#else +- __time_t st_atime; /* Time of last access. */ +- unsigned long int st_atimensec; /* Nscecs of last access. */ +- __time_t st_mtime; /* Time of last modification. */ +- unsigned long int st_mtimensec; /* Nsecs of last modification. */ +- __time_t st_ctime; /* Time of last status change. */ +- unsigned long int st_ctimensec; /* Nsecs of last status change. */ +-#endif +- int __glibc_reserved[2]; +- }; +-#endif +- +-/* Tell code we have these members. */ +-#define _STATBUF_ST_BLKSIZE +-#define _STATBUF_ST_RDEV +-/* Nanosecond resolution time values are supported. */ +-#define _STATBUF_ST_NSEC +- +-#endif /* _BITS_STRUCT_STAT_H */ +diff -Nuar a/sysdeps/unix/sysv/linux/getsysstats.c b/sysdeps/unix/sysv/linux/getsysstats.c +--- a/sysdeps/unix/sysv/linux/getsysstats.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/getsysstats.c 2025-10-20 21:02:21.000000000 +0300 +@@ -30,7 +30,7 @@ + #include + #include + +-int ++static int + __get_nprocs_sched (void) + { + enum +@@ -44,15 +44,14 @@ + int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, cpu_bits_size, + cpu_bits); + if (r > 0) +- return CPU_COUNT_S (cpu_bits_size, (cpu_set_t*) cpu_bits); ++ return CPU_COUNT_S (r, (cpu_set_t*) cpu_bits); + else if (r == -EINVAL) + /* The input buffer is still not enough to store the number of cpus. This + is an arbitrary values assuming such systems should be rare and there + is no offline cpus. */ + return max_num_cpus; +- /* Some other error. 2 is conservative (not a uniprocessor system, so +- atomics are needed). */ +- return 2; ++ /* Some other error. */ ++ return 0; + } + + static char * +@@ -108,22 +107,19 @@ + } + + static int +-get_nproc_stat (char *buffer, size_t buffer_size) ++get_nproc_stat (void) + { ++ enum { buffer_size = 1024 }; ++ char buffer[buffer_size]; + char *buffer_end = buffer + buffer_size; + char *cp = buffer_end; + char *re = buffer_end; +- +- /* Default to an SMP system in case we cannot obtain an accurate +- number. */ +- int result = 2; ++ int result = 0; + + const int flags = O_RDONLY | O_CLOEXEC; + int fd = __open_nocancel ("/proc/stat", flags); + if (fd != -1) + { +- result = 0; +- + char *l; + while ((l = next_line (fd, buffer, &cp, &re, buffer_end)) != NULL) + /* The current format of /proc/stat has all the cpu* entries +@@ -139,8 +135,8 @@ + return result; + } + +-int +-__get_nprocs (void) ++static int ++get_nprocs_cpu_online (void) + { + enum { buffer_size = 1024 }; + char buffer[buffer_size]; +@@ -179,7 +175,8 @@ + } + } + +- result += m - n + 1; ++ if (m >= n) ++ result += m - n + 1; + + l = endp; + if (l < re && *l == ',') +@@ -188,28 +185,18 @@ + while (l < re && *l != '\n'); + + __close_nocancel_nostatus (fd); +- +- if (result > 0) +- return result; + } + +- return get_nproc_stat (buffer, buffer_size); ++ return result; + } +-libc_hidden_def (__get_nprocs) +-weak_alias (__get_nprocs, get_nprocs) +- + +-/* On some architectures it is possible to distinguish between configured +- and active cpus. */ +-int +-__get_nprocs_conf (void) ++static int ++get_nprocs_cpu (void) + { +- /* Try to use the sysfs filesystem. It has actual information about +- online processors. */ ++ int count = 0; + DIR *dir = __opendir ("/sys/devices/system/cpu"); + if (dir != NULL) + { +- int count = 0; + struct dirent64 *d; + + while ((d = __readdir64 (dir)) != NULL) +@@ -224,12 +211,57 @@ + + __closedir (dir); + +- return count; + } ++ return count; ++} + +- enum { buffer_size = 1024 }; +- char buffer[buffer_size]; +- return get_nproc_stat (buffer, buffer_size); ++static int ++get_nprocs_fallback (void) ++{ ++ int result; ++ ++ /* Try /proc/stat first. */ ++ result = get_nproc_stat (); ++ if (result != 0) ++ return result; ++ ++ /* Try sched_getaffinity. */ ++ result = __get_nprocs_sched (); ++ if (result != 0) ++ return result; ++ ++ /* We failed to obtain an accurate number. Be conservative: return ++ the smallest number meaning that this is not a uniprocessor system, ++ so atomics are needed. */ ++ return 2; ++} ++ ++int ++__get_nprocs (void) ++{ ++ /* Try /sys/devices/system/cpu/online first. */ ++ int result = get_nprocs_cpu_online (); ++ if (result != 0) ++ return result; ++ ++ /* Fall back to /proc/stat and sched_getaffinity. */ ++ return get_nprocs_fallback (); ++} ++libc_hidden_def (__get_nprocs) ++weak_alias (__get_nprocs, get_nprocs) ++ ++/* On some architectures it is possible to distinguish between configured ++ and active cpus. */ ++int ++__get_nprocs_conf (void) ++{ ++ /* Try /sys/devices/system/cpu/ first. */ ++ int result = get_nprocs_cpu (); ++ if (result != 0) ++ return result; ++ ++ /* Fall back to /proc/stat and sched_getaffinity. */ ++ return get_nprocs_fallback (); + } + libc_hidden_def (__get_nprocs_conf) + weak_alias (__get_nprocs_conf, get_nprocs_conf) +diff -Nuar a/sysdeps/unix/sysv/linux/glob64-time64.c b/sysdeps/unix/sysv/linux/glob64-time64.c +--- a/sysdeps/unix/sysv/linux/glob64-time64.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/glob64-time64.c 2025-10-20 21:02:21.000000000 +0300 +@@ -37,6 +37,7 @@ + # define GLOB_LSTAT gl_lstat + # define GLOB_STAT64 __stat64_time64 + # define GLOB_LSTAT64 __lstat64_time64 ++# define GLOB_FSTATAT64 __fstatat64_time64 + + # define COMPILE_GLOB64 1 + +diff -Nuar a/sysdeps/unix/sysv/linux/hppa/arch-syscall.h b/sysdeps/unix/sysv/linux/hppa/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/hppa/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/hppa/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -289,6 +289,7 @@ + #define __NR_sendmsg 183 + #define __NR_sendto 82 + #define __NR_set_mempolicy 262 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 289 + #define __NR_set_tid_address 237 + #define __NR_setdomainname 121 +diff -Nuar a/sysdeps/unix/sysv/linux/hppa/bits/struct_stat.h b/sysdeps/unix/sysv/linux/hppa/bits/struct_stat.h +--- a/sysdeps/unix/sysv/linux/hppa/bits/struct_stat.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/hppa/bits/struct_stat.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,139 @@ ++/* Definition for struct stat. Linux/hppa version. ++ Copyright (C) 2020-2022 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 ++ . */ ++ ++#if !defined _SYS_STAT_H && !defined _FCNTL_H ++# error "Never include directly; use instead." ++#endif ++ ++#ifndef _BITS_STRUCT_STAT_H ++#define _BITS_STRUCT_STAT_H 1 ++ ++#include ++#include ++ ++struct stat ++ { ++#ifdef __USE_TIME_BITS64 ++# include ++#else ++ __dev_t st_dev; /* Device. */ ++ unsigned short int __pad1; ++# ifndef __USE_FILE_OFFSET64 ++ __ino_t st_ino; /* File serial number. */ ++# else ++ __ino_t __st_ino; /* 32bit file serial number. */ ++# endif ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ unsigned short int __pad2; ++# ifndef __USE_FILE_OFFSET64 ++ __off_t st_size; /* Size of file, in bytes. */ ++# else ++ __off64_t st_size; /* Size of file, in bytes. */ ++# endif ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ ++# ifndef __USE_FILE_OFFSET64 ++ __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ ++# else ++ __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ ++# endif ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# define st_atime st_atim.tv_sec /* Backward compatibility. */ ++# define st_mtime st_mtim.tv_sec ++# define st_ctime st_ctim.tv_sec ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++# ifndef __USE_FILE_OFFSET64 ++ unsigned long int __glibc_reserved4; ++ unsigned long int __glibc_reserved5; ++# else ++ __ino64_t st_ino; /* File serial number. */ ++# endif ++#endif /* __USE_TIME_BITS64 */ ++ }; ++ ++#ifdef __USE_LARGEFILE64 ++struct stat64 ++ { ++# ifdef __USE_TIME_BITS64 ++# include ++# else ++ __dev_t st_dev; /* Device. */ ++ unsigned int __pad1; ++ ++ __ino_t __st_ino; /* 32bit file serial number. */ ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ unsigned int __pad2; ++ __off64_t st_size; /* Size of file, in bytes. */ ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ ++ __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++ __ino64_t st_ino; /* File serial number. */ ++# endif /* __USE_TIME_BITS64 */ ++ }; ++#endif ++ ++/* Tell code we have these members. */ ++#define _STATBUF_ST_BLKSIZE ++#define _STATBUF_ST_RDEV ++/* Nanosecond resolution time values are supported. */ ++#define _STATBUF_ST_NSEC ++ ++ ++#endif /* _BITS_STRUCT_STAT_H */ +diff -Nuar a/sysdeps/unix/sysv/linux/hppa/bits/wordsize.h b/sysdeps/unix/sysv/linux/hppa/bits/wordsize.h +--- a/sysdeps/unix/sysv/linux/hppa/bits/wordsize.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/hppa/bits/wordsize.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,21 @@ ++/* Copyright (C) 1999-2024 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 ++ . */ ++ ++#define __WORDSIZE 32 ++#define __WORDSIZE_TIME64_COMPAT32 1 ++#define __WORDSIZE32_SIZE_ULONG 0 ++#define __WORDSIZE32_PTRDIFF_LONG 0 +diff -Nuar a/sysdeps/unix/sysv/linux/hppa/getcontext.S b/sysdeps/unix/sysv/linux/hppa/getcontext.S +--- a/sysdeps/unix/sysv/linux/hppa/getcontext.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/hppa/getcontext.S 2025-10-20 21:02:21.000000000 +0300 +@@ -21,22 +21,28 @@ + #include "ucontext_i.h" + + +- /* Trampoline function. Non-standard calling ABI. */ ++ /* Trampoline function. Non-standard calling ABI. */ + /* Can not use ENTRY(__getcontext_ret) here. */ + .type __getcontext_ret, @function + .hidden __getcontext_ret + __getcontext_ret: + .proc + .callinfo FRAME=0,NO_CALLS +- /* r26-r23 contain original r3-r6, but because setcontext +- does not reload r3-r6 (it's using them as temporaries) +- we must save them elsewhere and swap them back in. */ +- copy %r23, %r3 +- copy %r24, %r4 +- copy %r25, %r5 +- copy %r26, %r6 +- /* r20 contains original return pointer. */ +- bv 0(%r20) ++ /* Because setcontext does not reload r3-r6 (it's using them ++ as temporaries), we must load them ourself. */ ++ ldw oR3(%r26), %r3 ++ ldw oR4(%r26), %r4 ++ ldw oR5(%r26), %r5 ++ ldw oR6(%r26), %r6 ++ ++ /* Also reload registers clobbered by $$dyncall. */ ++ ldw oR21(%r26), %r21 ++ ldw oR22(%r26), %r22 ++ ldw oR31(%r26), %r31 ++ ++ /* oR0 contains original return pointer. */ ++ ldw oR0(%r26), %rp ++ bv 0(%rp) + copy %r0, %ret0 + .procend + .size __getcontext_ret, .-__getcontext_ret +@@ -64,13 +70,13 @@ + stw %r17, oR17(%r26) + stw %r18, oR18(%r26) + stw %r19, oR19(%r26) +- /* stw %r20, oR20(%r26) - used for trampoline. */ ++ stw %r20, oR20(%r26) + stw %r21, oR21(%r26) + stw %r22, oR22(%r26) +- /* stw %r23, oR23(%r26) - used for trampoline. */ +- /* stw %r24, oR24(%r26) - used for trampoline. */ +- /* stw %r25, oR25(%r26) - used for trampoline. */ +- /* stw %r26, oR26(%r26) - used for trampoline. */ ++ stw %r23, oR23(%r26) ++ stw %r24, oR24(%r26) ++ stw %r25, oR25(%r26) ++ stw %r26, oR26(%r26) + stw %r27, oR27(%r26) + stw %r28, oR28(%r26) + stw %r29, oR29(%r26) +@@ -89,7 +95,10 @@ + stw %r0, oIASQ1(%r26) + stw %r0, oIAOQ0(%r26) + stw %r0, oIAOQ1(%r26) +- stw %r0, oSAR(%r26) /* used as flag in swapcontext(). */ ++ ++ /* Save SAR register. */ ++ mfctl %sar, %r1 ++ stw %r1, oSAR(%r26) /* MSB used as flag in swapcontext(). */ + + + /* Store floating-point regs. */ +@@ -137,15 +146,12 @@ + stw %r19, -32(%sp) + .cfi_offset 19, 32 + #endif ++ stw %ret1, -60(%sp) ++ .cfi_offset 29, 4 + + /* Set up the trampoline registers. +- r20, r23, r24, r25, r26 and r2 are clobbered +- by call to getcontext() anyway. Reuse them. */ +- stw %r2, oR20(%r26) +- stw %r3, oR23(%r26) +- stw %r4, oR24(%r26) +- stw %r5, oR25(%r26) +- stw %r6, oR26(%r26) ++ Use oR0 context slot to save return value. */ ++ stw %r2, oR0(%r26) + #ifdef PIC + addil LT%__getcontext_ret, %r19 + ldw RT%__getcontext_ret(%r1), %r1 +@@ -167,6 +173,7 @@ + #ifdef PIC + ldw -32(%sp), %r19 + #endif ++ ldw -60(%sp), %ret1 + bv %r0(%r2) + ldwm -64(%sp), %r4 + END(__getcontext) +diff -Nuar a/sysdeps/unix/sysv/linux/hppa/kernel-features.h b/sysdeps/unix/sysv/linux/hppa/kernel-features.h +--- a/sysdeps/unix/sysv/linux/hppa/kernel-features.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/hppa/kernel-features.h 2025-10-20 21:02:21.000000000 +0300 +@@ -30,3 +30,6 @@ + + #undef __ASSUME_CLONE_DEFAULT + #define __ASSUME_CLONE_BACKWARDS 1 ++ ++/* QEMU does not support set_robust_list. */ ++#undef __ASSUME_SET_ROBUST_LIST +diff -Nuar a/sysdeps/unix/sysv/linux/hppa/setcontext.S b/sysdeps/unix/sysv/linux/hppa/setcontext.S +--- a/sysdeps/unix/sysv/linux/hppa/setcontext.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/hppa/setcontext.S 2025-10-20 21:02:21.000000000 +0300 +@@ -33,6 +33,8 @@ + stw %r19, -32(%sp) + .cfi_offset 19, 32 + #endif ++ stw %ret1, -60(%sp) ++ .cfi_offset 29, 4 + + /* Save ucp. */ + copy %r26, %r3 +@@ -73,7 +75,7 @@ + ldw oR18(%r3), %r18 + ldw oR19(%r3), %r19 + ldw oR20(%r3), %r20 +- ldw oR21(%r3), %r21 ++ ldw oR21(%r3), %r21 /* maybe clobbered by dyncall */ + /* ldw oR22(%r3), %r22 - dyncall arg. */ + ldw oR23(%r3), %r23 + ldw oR24(%r3), %r24 +@@ -85,6 +87,10 @@ + ldw oR30(%r3), %sp + /* ldw oR31(%r3), %r31 - dyncall scratch register */ + ++ /* Restore SAR register. */ ++ ldw oSAR(%r3), %r22 ++ mtsar %r22 ++ + /* Restore floating-point registers. */ + ldo oFPREGS31(%r3), %r22 + fldds 0(%r22), %fr31 +@@ -154,6 +160,7 @@ + #ifdef PIC + ldw -32(%r30), %r19 + #endif ++ ldw -60(%r30), %ret1 + bv %r0(%r2) + ldwm -64(%r30), %r3 + L(pseudo_end): +diff -Nuar a/sysdeps/unix/sysv/linux/hppa/swapcontext.c b/sysdeps/unix/sysv/linux/hppa/swapcontext.c +--- a/sysdeps/unix/sysv/linux/hppa/swapcontext.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/hppa/swapcontext.c 1970-01-01 02:00:00.000000000 +0200 +@@ -1,41 +0,0 @@ +-/* Swap to new context. +- Copyright (C) 2008-2022 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 +- . */ +- +-#include +- +-extern int __getcontext (ucontext_t *ucp); +-extern int __setcontext (const ucontext_t *ucp); +- +-int +-__swapcontext (ucontext_t *oucp, const ucontext_t *ucp) +-{ +- /* Save the current machine context to oucp. */ +- __getcontext (oucp); +- +- /* mark sc_sar flag to skip the setcontext call on reactivation. */ +- if (oucp->uc_mcontext.sc_sar == 0) { +- oucp->uc_mcontext.sc_sar++; +- +- /* Restore the machine context in ucp. */ +- __setcontext (ucp); +- } +- +- return 0; +-} +- +-weak_alias (__swapcontext, swapcontext) +diff -Nuar a/sysdeps/unix/sysv/linux/hppa/swapcontext.S b/sysdeps/unix/sysv/linux/hppa/swapcontext.S +--- a/sysdeps/unix/sysv/linux/hppa/swapcontext.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/hppa/swapcontext.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,72 @@ ++/* Swap to new context. ++ Copyright (C) 2008-2022 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 ++ . */ ++ ++#include ++#include "ucontext_i.h" ++ ++ .text ++ENTRY(__swapcontext) ++ ++ /* Copy rp to ret0 (r28). */ ++ copy %rp,%ret0 ++ ++ /* Create a frame. */ ++ ldo 64(%sp),%sp ++ .cfi_def_cfa_offset -64 ++ ++ /* Save the current machine context to oucp. */ ++ bl __getcontext,%rp ++ ++ /* Copy oucp to register ret1 (r29). __getcontext saves and ++ restores it on a normal return. It is restored from oR29 ++ on reactivation. */ ++ copy %r26,%ret1 ++ ++ /* Pop frame. */ ++ ldo -64(%sp),%sp ++ .cfi_def_cfa_offset 0 ++ ++ /* Load return pointer from oR28. */ ++ ldw oR28(%ret1),%rp ++ ++ /* Return if error. */ ++ or,= %r0,%ret0,%r0 ++ bv,n %r0(%rp) ++ ++ /* Load sc_sar flag. */ ++ ldb oSAR(%ret1),%r20 ++ ++ /* Return if oucp context has been reactivated. */ ++ or,= %r0,%r20,%r0 ++ bv,n %r0(%rp) ++ ++ /* Mark sc_sar flag. */ ++ ldi 1,%r20 ++ stb %r20,oSAR(%ret1) ++ ++ /* Activate the machine context in ucp. */ ++ bl __setcontext,%rp ++ ldw oR25(%ret1),%r26 ++ ++ /* Load return pointer. */ ++ ldw oR28(%ret1),%rp ++ bv,n %r0(%rp) ++ ++END(__swapcontext) ++ ++weak_alias (__swapcontext, swapcontext) +diff -Nuar a/sysdeps/unix/sysv/linux/i386/arch-syscall.h b/sysdeps/unix/sysv/linux/i386/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/i386/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/i386/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -323,6 +323,7 @@ + #define __NR_sendmsg 370 + #define __NR_sendto 369 + #define __NR_set_mempolicy 276 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 311 + #define __NR_set_thread_area 243 + #define __NR_set_tid_address 258 +diff -Nuar a/sysdeps/unix/sysv/linux/i386/libc-do-syscall-int80.S b/sysdeps/unix/sysv/linux/i386/libc-do-syscall-int80.S +--- a/sysdeps/unix/sysv/linux/i386/libc-do-syscall-int80.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/i386/libc-do-syscall-int80.S 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,25 @@ ++/* Out-of-line syscall stub for six-argument syscalls from C. For static PIE. ++ Copyright (C) 2022 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 ++ . */ ++ ++#ifndef SHARED ++# define I386_USE_SYSENTER 0 ++# include ++ ++# define __libc_do_syscall __libc_do_syscall_int80 ++# include "libc-do-syscall.S" ++#endif +diff -Nuar a/sysdeps/unix/sysv/linux/i386/libc-do-syscall.S b/sysdeps/unix/sysv/linux/i386/libc-do-syscall.S +--- a/sysdeps/unix/sysv/linux/i386/libc-do-syscall.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/i386/libc-do-syscall.S 2025-10-20 21:02:21.000000000 +0300 +@@ -18,8 +18,6 @@ + + #include + +-#ifndef OPTIMIZE_FOR_GCC_5 +- + /* %eax, %ecx, %edx and %esi contain the values expected by the kernel. + %edi points to a structure with the values of %ebx, %edi and %ebp. */ + +@@ -50,4 +48,3 @@ + cfi_restore (ebx) + ret + END (__libc_do_syscall) +-#endif +diff -Nuar a/sysdeps/unix/sysv/linux/i386/Makefile b/sysdeps/unix/sysv/linux/i386/Makefile +--- a/sysdeps/unix/sysv/linux/i386/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/i386/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -14,7 +14,7 @@ + endif + + ifeq ($(subdir),io) +-sysdep_routines += libc-do-syscall ++sysdep_routines += libc-do-syscall libc-do-syscall-int80 + endif + + ifeq ($(subdir),stdlib) +diff -Nuar a/sysdeps/unix/sysv/linux/i386/startup.h b/sysdeps/unix/sysv/linux/i386/startup.h +--- a/sysdeps/unix/sysv/linux/i386/startup.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/i386/startup.h 2025-10-20 21:02:21.000000000 +0300 +@@ -1,5 +1,5 @@ + /* Linux/i386 definitions of functions used by static libc main startup. +- Copyright (C) 2017-2022 Free Software Foundation, Inc. ++ Copyright (C) 2022 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 +@@ -16,46 +16,7 @@ + License along with the GNU C Library; if not, see + . */ + +-#if BUILD_PIE_DEFAULT +-/* Can't use "call *%gs:SYSINFO_OFFSET" during statup in static PIE. */ +-# define I386_USE_SYSENTER 0 ++/* Can't use "call *%gs:SYSINFO_OFFSET" during startup. */ ++#define I386_USE_SYSENTER 0 + +-# include +-# include +- +-__attribute__ ((__noreturn__)) +-static inline void +-_startup_fatal (const char *message __attribute__ ((unused))) +-{ +- /* This is only called very early during startup in static PIE. +- FIXME: How can it be improved? */ +- ABORT_INSTRUCTION; +- __builtin_unreachable (); +-} +- +-static inline uid_t +-startup_getuid (void) +-{ +- return (uid_t) INTERNAL_SYSCALL_CALL (getuid32); +-} +- +-static inline uid_t +-startup_geteuid (void) +-{ +- return (uid_t) INTERNAL_SYSCALL_CALL (geteuid32); +-} +- +-static inline gid_t +-startup_getgid (void) +-{ +- return (gid_t) INTERNAL_SYSCALL_CALL (getgid32); +-} +- +-static inline gid_t +-startup_getegid (void) +-{ +- return (gid_t) INTERNAL_SYSCALL_CALL (getegid32); +-} +-#else +-# include_next +-#endif ++#include_next +diff -Nuar a/sysdeps/unix/sysv/linux/i386/sysdep.h b/sysdeps/unix/sysv/linux/i386/sysdep.h +--- a/sysdeps/unix/sysv/linux/i386/sysdep.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/i386/sysdep.h 2025-10-20 21:02:21.000000000 +0300 +@@ -42,6 +42,15 @@ + # endif + #endif + ++#if !I386_USE_SYSENTER && IS_IN (libc) && !defined SHARED ++/* Inside static libc, we have two versions. For compilation units ++ with !I386_USE_SYSENTER, the vDSO entry mechanism cannot be ++ used. */ ++# define I386_DO_SYSCALL_STRING "__libc_do_syscall_int80" ++#else ++# define I386_DO_SYSCALL_STRING "__libc_do_syscall" ++#endif ++ + #ifdef __ASSEMBLER__ + + /* Linux uses a negative return value to indicate syscall errors, +@@ -301,7 +310,7 @@ + }; \ + asm volatile ( \ + "movl %1, %%eax\n\t" \ +- "call __libc_do_syscall" \ ++ "call " I386_DO_SYSCALL_STRING \ + : "=a" (resultvar) \ + : "i" (__NR_##name), "c" (arg2), "d" (arg3), "S" (arg4), "D" (&_xv) \ + : "memory", "cc") +@@ -315,7 +324,7 @@ + }; \ + asm volatile ( \ + "movl %1, %%eax\n\t" \ +- "call __libc_do_syscall" \ ++ "call " I386_DO_SYSCALL_STRING \ + : "=a" (resultvar) \ + : "a" (name), "c" (arg2), "d" (arg3), "S" (arg4), "D" (&_xv) \ + : "memory", "cc") +diff -Nuar a/sysdeps/unix/sysv/linux/ia64/arch-syscall.h b/sysdeps/unix/sysv/linux/ia64/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/ia64/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/ia64/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -272,6 +272,7 @@ + #define __NR_sendmsg 1205 + #define __NR_sendto 1199 + #define __NR_set_mempolicy 1261 ++#define __NR_set_mempolicy_home_node 1474 + #define __NR_set_robust_list 1298 + #define __NR_set_tid_address 1233 + #define __NR_setdomainname 1129 +diff -Nuar a/sysdeps/unix/sysv/linux/ia64/brk.c b/sysdeps/unix/sysv/linux/ia64/brk.c +--- a/sysdeps/unix/sysv/linux/ia64/brk.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/ia64/brk.c 2025-10-20 21:02:21.000000000 +0300 +@@ -16,7 +16,6 @@ + License along with the GNU C Library; if not, see + . */ + +-#include +-/* brk is used by statup before TCB is properly set. */ +-#undef USE_DL_SYSINFO ++/* brk is used by startup before TCB is properly set up. */ ++#define IA64_USE_NEW_STUB 0 + #include +diff -Nuar a/sysdeps/unix/sysv/linux/ia64/Makefile b/sysdeps/unix/sysv/linux/ia64/Makefile +--- a/sysdeps/unix/sysv/linux/ia64/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/ia64/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -1,3 +1,9 @@ ++ifeq ($(subdir),elf) ++# ia64 does not support PT_GNU_RELRO. ++test-xfail-tst-relro-ldso = yes ++test-xfail-tst-relro-libc = yes ++endif ++ + ifeq ($(subdir),misc) + sysdep_headers += sys/rse.h + endif +diff -Nuar a/sysdeps/unix/sysv/linux/ia64/startup.h b/sysdeps/unix/sysv/linux/ia64/startup.h +--- a/sysdeps/unix/sysv/linux/ia64/startup.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/ia64/startup.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,22 @@ ++/* Linux/ia64 definitions of functions used by static libc main startup. ++ Copyright (C) 2022 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 ++ . */ ++ ++/* This code is used before the TCB is set up. */ ++#define IA64_USE_NEW_STUB 0 ++ ++#include_next +diff -Nuar a/sysdeps/unix/sysv/linux/ia64/sysdep.h b/sysdeps/unix/sysv/linux/ia64/sysdep.h +--- a/sysdeps/unix/sysv/linux/ia64/sysdep.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/ia64/sysdep.h 2025-10-20 21:02:21.000000000 +0300 +@@ -44,12 +44,15 @@ + #undef SYS_ify + #define SYS_ify(syscall_name) __NR_##syscall_name + +-#if defined USE_DL_SYSINFO \ +- && (IS_IN (libc) \ +- || IS_IN (libpthread) || IS_IN (librt)) +-# define IA64_USE_NEW_STUB +-#else +-# undef IA64_USE_NEW_STUB ++#ifndef IA64_USE_NEW_STUB ++# if defined USE_DL_SYSINFO && IS_IN (libc) ++# define IA64_USE_NEW_STUB 1 ++# else ++# define IA64_USE_NEW_STUB 0 ++# endif ++#endif ++#if IA64_USE_NEW_STUB && !USE_DL_SYSINFO ++# error IA64_USE_NEW_STUB needs USE_DL_SYSINFO + #endif + + #ifdef __ASSEMBLER__ +@@ -101,7 +104,7 @@ + mov r15=num; \ + break __IA64_BREAK_SYSCALL + +-#ifdef IA64_USE_NEW_STUB ++#if IA64_USE_NEW_STUB + # ifdef SHARED + # define DO_CALL(num) \ + .prologue; \ +@@ -185,7 +188,7 @@ + (non-negative) errno on error or the return value on success. + */ + +-#ifdef IA64_USE_NEW_STUB ++#if IA64_USE_NEW_STUB + + # define INTERNAL_SYSCALL_NCS(name, nr, args...) \ + ({ \ +@@ -277,7 +280,7 @@ + #define ASM_OUTARGS_5 ASM_OUTARGS_4, "=r" (_out4) + #define ASM_OUTARGS_6 ASM_OUTARGS_5, "=r" (_out5) + +-#ifdef IA64_USE_NEW_STUB ++#if IA64_USE_NEW_STUB + #define ASM_ARGS_0 + #define ASM_ARGS_1 ASM_ARGS_0, "4" (_out0) + #define ASM_ARGS_2 ASM_ARGS_1, "5" (_out1) +@@ -313,7 +316,7 @@ + /* Branch registers. */ \ + "b6" + +-#ifdef IA64_USE_NEW_STUB ++#if IA64_USE_NEW_STUB + # define ASM_CLOBBERS_6 ASM_CLOBBERS_6_COMMON + #else + # define ASM_CLOBBERS_6 ASM_CLOBBERS_6_COMMON , "b7" +diff -Nuar a/sysdeps/unix/sysv/linux/ipc_priv.h b/sysdeps/unix/sysv/linux/ipc_priv.h +--- a/sysdeps/unix/sysv/linux/ipc_priv.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/ipc_priv.h 2025-10-20 21:02:21.000000000 +0300 +@@ -63,4 +63,10 @@ + # define __IPC_TIME64 0 + #endif + ++#if __IPC_TIME64 || defined __ASSUME_SYSVIPC_BROKEN_MODE_T ++# define IPC_CTL_NEED_TRANSLATION 1 ++#else ++# define IPC_CTL_NEED_TRANSLATION 0 ++#endif ++ + #include +diff -Nuar a/sysdeps/unix/sysv/linux/ldsodefs.h b/sysdeps/unix/sysv/linux/ldsodefs.h +--- a/sysdeps/unix/sysv/linux/ldsodefs.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/ldsodefs.h 2025-10-20 21:02:21.000000000 +0300 +@@ -24,16 +24,4 @@ + /* Get the real definitions. */ + #include_next + +-/* We can assume that the kernel always provides the AT_UID, AT_EUID, +- AT_GID, and AT_EGID values in the auxiliary vector from 2.4.0 or so on. */ +-#define HAVE_AUX_XID +- +-/* We can assume that the kernel always provides the AT_SECURE value +- in the auxiliary vector from 2.5.74 or so on. */ +-#define HAVE_AUX_SECURE +- +-/* Starting with one of the 2.4.0 pre-releases the Linux kernel passes +- up the page size information. */ +-#define HAVE_AUX_PAGESIZE +- + #endif /* ldsodefs.h */ +diff -Nuar a/sysdeps/unix/sysv/linux/m68k/arch-syscall.h b/sysdeps/unix/sysv/linux/m68k/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/m68k/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/m68k/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -310,6 +310,7 @@ + #define __NR_sendmsg 367 + #define __NR_sendto 366 + #define __NR_set_mempolicy 270 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 304 + #define __NR_set_thread_area 334 + #define __NR_set_tid_address 253 +diff -Nuar a/sysdeps/unix/sysv/linux/m68k/libc-lock-arch.h b/sysdeps/unix/sysv/linux/m68k/libc-lock-arch.h +--- a/sysdeps/unix/sysv/linux/m68k/libc-lock-arch.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/m68k/libc-lock-arch.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,25 @@ ++/* Private libc-internal arch-specific definitions. m68k version. ++ Copyright (C) 2022 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; see the file COPYING.LIB. If ++ not, see . */ ++ ++#ifndef _LIBC_LOCK_ARCH_H ++#define _LIBC_LOCK_ARCH_H ++ ++/* Linux enforces 4-bytes alignment on futex inputs. */ ++#define __LIBC_LOCK_ALIGNMENT __attribute__ ((__aligned__ (4))) ++ ++#endif +diff -Nuar a/sysdeps/unix/sysv/linux/m68k/sysdep.h b/sysdeps/unix/sysv/linux/m68k/sysdep.h +--- a/sysdeps/unix/sysv/linux/m68k/sysdep.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/m68k/sysdep.h 2025-10-20 21:02:21.000000000 +0300 +@@ -299,8 +299,6 @@ + #define PTR_MANGLE(var) (void) (var) + #define PTR_DEMANGLE(var) (void) (var) + +-#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO + /* M68K needs system-supplied DSO to access TLS helpers + even when statically linked. */ +-# define NEED_STATIC_SYSINFO_DSO 1 +-#endif ++#define NEED_STATIC_SYSINFO_DSO 1 +diff -Nuar a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile +--- a/sysdeps/unix/sysv/linux/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/Makefile 2025-10-20 21:02:21.000000000 +0300 +@@ -126,6 +126,8 @@ + tst-prctl \ + tst-scm_rights \ + tst-epoll \ ++ tst-getauxval \ ++ tst-linux-mremap1 \ + # tests + + # Test for the symbol version of fcntl that was replaced in glibc 2.28. +@@ -325,6 +327,8 @@ + netrom/netrom.h netpacket/packet.h netrose/rose.h \ + neteconet/ec.h netiucv/iucv.h + sysdep_routines += netlink_assert_response ++ ++CFLAGS-check_pf.c += -fexceptions + endif + + # Don't compile the ctype glue code, since there is no old non-GNU C library. +@@ -365,6 +369,7 @@ + + ifeq ($(subdir),elf) + sysdep-rtld-routines += dl-brk dl-sbrk dl-getcwd dl-openat64 dl-opendir ++dl-routines += dl-rseq-symbols + + libof-lddlibc4 = lddlibc4 + +diff -Nuar a/sysdeps/unix/sysv/linux/microblaze/arch-syscall.h b/sysdeps/unix/sysv/linux/microblaze/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/microblaze/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/microblaze/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -326,6 +326,7 @@ + #define __NR_sendmsg 360 + #define __NR_sendto 353 + #define __NR_set_mempolicy 276 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 311 + #define __NR_set_thread_area 243 + #define __NR_set_tid_address 258 +diff -Nuar a/sysdeps/unix/sysv/linux/mips/bits/struct_stat.h b/sysdeps/unix/sysv/linux/mips/bits/struct_stat.h +--- a/sysdeps/unix/sysv/linux/mips/bits/struct_stat.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/mips/bits/struct_stat.h 2025-10-20 21:02:21.000000000 +0300 +@@ -131,27 +131,30 @@ + + struct stat + { ++# ifdef __USE_TIME_BITS64 ++# include ++# else + __dev_t st_dev; + int st_pad1[3]; /* Reserved for st_dev expansion */ +-# ifndef __USE_FILE_OFFSET64 ++# ifndef __USE_FILE_OFFSET64 + __ino_t st_ino; +-# else ++# else + __ino64_t st_ino; +-# endif ++# endif + __mode_t st_mode; + __nlink_t st_nlink; + __uid_t st_uid; + __gid_t st_gid; + __dev_t st_rdev; +-# if !defined __USE_FILE_OFFSET64 ++# if !defined __USE_FILE_OFFSET64 + unsigned int st_pad2[2]; /* Reserved for st_rdev expansion */ + __off_t st_size; + int st_pad3; +-# else ++# else + unsigned int st_pad2[3]; /* Reserved for st_rdev expansion */ + __off64_t st_size; +-# endif +-# ifdef __USE_XOPEN2K8 ++# endif ++# ifdef __USE_XOPEN2K8 + /* Nanosecond resolution timestamps are stored in a format + equivalent to 'struct timespec'. This is the type used + whenever possible but the Unix namespace rules do not allow the +@@ -161,30 +164,34 @@ + struct timespec st_atim; /* Time of last access. */ + struct timespec st_mtim; /* Time of last modification. */ + struct timespec st_ctim; /* Time of last status change. */ +-# define st_atime st_atim.tv_sec /* Backward compatibility. */ +-# define st_mtime st_mtim.tv_sec +-# define st_ctime st_ctim.tv_sec +-# else ++# define st_atime st_atim.tv_sec /* Backward compatibility. */ ++# define st_mtime st_mtim.tv_sec ++# define st_ctime st_ctim.tv_sec ++# else + __time_t st_atime; /* Time of last access. */ + unsigned long int st_atimensec; /* Nscecs of last access. */ + __time_t st_mtime; /* Time of last modification. */ + unsigned long int st_mtimensec; /* Nsecs of last modification. */ + __time_t st_ctime; /* Time of last status change. */ + unsigned long int st_ctimensec; /* Nsecs of last status change. */ +-# endif ++# endif + __blksize_t st_blksize; + unsigned int st_pad4; +-# ifndef __USE_FILE_OFFSET64 ++# ifndef __USE_FILE_OFFSET64 + __blkcnt_t st_blocks; +-# else ++# else + __blkcnt64_t st_blocks; +-# endif ++# endif + int st_pad5[14]; ++# endif + }; + + #ifdef __USE_LARGEFILE64 + struct stat64 + { ++# ifdef __USE_TIME_BITS64 ++# include ++# else + __dev_t st_dev; + unsigned int st_pad1[3]; /* Reserved for st_dev expansion */ + __ino64_t st_ino; +@@ -217,6 +224,7 @@ + unsigned int st_pad3; + __blkcnt64_t st_blocks; + int st_pad4[14]; ++# endif /* __USE_TIME_BITS64 */ + }; + #endif + +diff -Nuar a/sysdeps/unix/sysv/linux/mips/mips32/arch-syscall.h b/sysdeps/unix/sysv/linux/mips/mips32/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/mips/mips32/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/mips/mips32/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -308,6 +308,7 @@ + #define __NR_sendmsg 4179 + #define __NR_sendto 4180 + #define __NR_set_mempolicy 4270 ++#define __NR_set_mempolicy_home_node 4450 + #define __NR_set_robust_list 4309 + #define __NR_set_thread_area 4283 + #define __NR_set_tid_address 4252 +diff -Nuar a/sysdeps/unix/sysv/linux/mips/mips64/n32/arch-syscall.h b/sysdeps/unix/sysv/linux/mips/mips64/n32/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -288,6 +288,7 @@ + #define __NR_sendmsg 6045 + #define __NR_sendto 6043 + #define __NR_set_mempolicy 6233 ++#define __NR_set_mempolicy_home_node 6450 + #define __NR_set_robust_list 6272 + #define __NR_set_thread_area 6246 + #define __NR_set_tid_address 6213 +diff -Nuar a/sysdeps/unix/sysv/linux/mips/mips64/n64/arch-syscall.h b/sysdeps/unix/sysv/linux/mips/mips64/n64/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/arch-syscall.h 2025-10-20 21:02:21.000000000 +0300 +@@ -270,6 +270,7 @@ + #define __NR_sendmsg 5045 + #define __NR_sendto 5043 + #define __NR_set_mempolicy 5229 ++#define __NR_set_mempolicy_home_node 5450 + #define __NR_set_robust_list 5268 + #define __NR_set_thread_area 5242 + #define __NR_set_tid_address 5212 +diff -Nuar a/sysdeps/unix/sysv/linux/mips/mips64/n64/fstatat.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/fstatat.c +--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/fstatat.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/fstatat.c 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,51 @@ ++/* Get file status. Linux/MIPSn64 version. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++ ++/* Different than other ABIs, mips64 has different layouts for non-LFS ++ and LFS struct stat. */ ++int ++__fstatat (int fd, const char *file, struct stat *buf, int flag) ++{ ++ struct __stat64_t64 st64; ++ int r = __fstatat64_time64 (fd, file, &st64, flag); ++ if (r == 0) ++ { ++ /* Clear internal pad and reserved fields. */ ++ memset (buf, 0, sizeof (*buf)); ++ ++ buf->st_dev = st64.st_dev; ++ buf->st_ino = st64.st_ino; ++ buf->st_mode = st64.st_mode; ++ buf->st_nlink = st64.st_nlink; ++ buf->st_uid = st64.st_uid; ++ buf->st_gid = st64.st_gid; ++ buf->st_rdev = st64.st_rdev; ++ buf->st_size = st64.st_size; ++ buf->st_blksize = st64.st_blksize; ++ buf->st_blocks = st64.st_blocks; ++ buf->st_atim = st64.st_atim; ++ buf->st_mtim = st64.st_mtim; ++ buf->st_ctim = st64.st_ctim; ++ } ++ return r; ++} ++ ++weak_alias (__fstatat, fstatat) +diff -Nuar a/sysdeps/unix/sysv/linux/mmap_call.h b/sysdeps/unix/sysv/linux/mmap_call.h +--- a/sysdeps/unix/sysv/linux/mmap_call.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/mmap_call.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,22 @@ ++/* Generic definition of MMAP_CALL and MMAP_CALL_INTERNAL. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#define MMAP_CALL(__nr, __addr, __len, __prot, __flags, __fd, __offset) \ ++ INLINE_SYSCALL_CALL (__nr, __addr, __len, __prot, __flags, __fd, __offset) ++#define MMAP_CALL_INTERNAL(__nr, __addr, __len, __prot, __flags, __fd, __offset) \ ++ INTERNAL_SYSCALL_CALL (__nr, __addr, __len, __prot, __flags, __fd, __offset) +diff -Nuar a/sysdeps/unix/sysv/linux/mmap_internal.h b/sysdeps/unix/sysv/linux/mmap_internal.h +--- a/sysdeps/unix/sysv/linux/mmap_internal.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/mmap_internal.h 2025-10-20 21:02:21.000000000 +0300 +@@ -42,10 +42,6 @@ + /* Do not accept offset not multiple of page size. */ + #define MMAP_OFF_LOW_MASK (MMAP2_PAGE_UNIT - 1) + +-/* An architecture may override this. */ +-#ifndef MMAP_CALL +-# define MMAP_CALL(__nr, __addr, __len, __prot, __flags, __fd, __offset) \ +- INLINE_SYSCALL_CALL (__nr, __addr, __len, __prot, __flags, __fd, __offset) +-#endif ++#include + + #endif /* MMAP_INTERNAL_LINUX_H */ +diff -Nuar a/sysdeps/unix/sysv/linux/mq_timedreceive.c b/sysdeps/unix/sysv/linux/mq_timedreceive.c +--- a/sysdeps/unix/sysv/linux/mq_timedreceive.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/mq_timedreceive.c 2025-10-20 21:02:21.000000000 +0300 +@@ -41,7 +41,7 @@ + { + int r = SYSCALL_CANCEL (mq_timedreceive_time64, mqdes, msg_ptr, msg_len, + msg_prio, abs_timeout); +- if (r == 0 || errno != ENOSYS) ++ if (r >= 0 || errno != ENOSYS) + return r; + __set_errno (EOVERFLOW); + return -1; +diff -Nuar a/sysdeps/unix/sysv/linux/mremap.c b/sysdeps/unix/sysv/linux/mremap.c +--- a/sysdeps/unix/sysv/linux/mremap.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/mremap.c 2025-10-20 21:02:22.000000000 +0300 +@@ -20,6 +20,12 @@ + #include + #include + #include ++#include ++ ++#define MREMAP_KNOWN_BITS \ ++ (MREMAP_MAYMOVE \ ++ | MREMAP_FIXED \ ++ | MREMAP_DONTUNMAP) + + void * + __mremap (void *addr, size_t old_len, size_t new_len, int flags, ...) +@@ -27,7 +33,13 @@ + va_list va; + void *new_addr = NULL; + +- if (flags & MREMAP_FIXED) ++ if (flags & ~(MREMAP_KNOWN_BITS)) ++ { ++ __set_errno (EINVAL); ++ return MAP_FAILED; ++ } ++ ++ if (flags & (MREMAP_FIXED | MREMAP_DONTUNMAP)) + { + va_start (va, flags); + new_addr = va_arg (va, void *); +diff -Nuar a/sysdeps/unix/sysv/linux/mremap-failure.h b/sysdeps/unix/sysv/linux/mremap-failure.h +--- a/sysdeps/unix/sysv/linux/mremap-failure.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/mremap-failure.h 2025-10-20 21:02:21.000000000 +0300 +@@ -0,0 +1,30 @@ ++/* mremap failure handling. Linux version. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++ ++/* Return exit value on mremap failure with errno ERR. */ ++ ++static int ++mremap_failure_exit (int err) ++{ ++ if (err != EINVAL) ++ return EXIT_FAILURE; ++ ++ return EXIT_UNSUPPORTED; ++} +diff -Nuar a/sysdeps/unix/sysv/linux/msgctl.c b/sysdeps/unix/sysv/linux/msgctl.c +--- a/sysdeps/unix/sysv/linux/msgctl.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/msgctl.c 2025-10-20 21:02:22.000000000 +0300 +@@ -85,11 +85,19 @@ + int + __msgctl64 (int msqid, int cmd, struct __msqid64_ds *buf) + { +-#if __IPC_TIME64 ++#if IPC_CTL_NEED_TRANSLATION ++# if __IPC_TIME64 + struct kernel_msqid64_ds ksemid, *arg = NULL; +-#else ++# else + msgctl_arg_t *arg; +-#endif ++# endif ++ ++ /* Some applications pass the __IPC_64 flag in cmd, to invoke ++ previously unsupported commands back when there was no EINVAL ++ error checking in glibc. Mask the flag for the switch statements ++ below. msgctl_syscall adds back the __IPC_64 flag for the actual ++ system call. */ ++ cmd &= ~__IPC_64; + + switch (cmd) + { +@@ -101,19 +109,19 @@ + case IPC_STAT: + case MSG_STAT: + case MSG_STAT_ANY: +-#if __IPC_TIME64 ++# if __IPC_TIME64 + if (buf != NULL) + { + msqid64_to_kmsqid64 (buf, &ksemid); + arg = &ksemid; + } +-# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T ++# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T + if (cmd == IPC_SET) + arg->msg_perm.mode *= 0x10000U; +-# endif +-#else ++# endif ++# else + arg = buf; +-#endif ++# endif + break; + + case IPC_INFO: +@@ -137,21 +145,25 @@ + case IPC_STAT: + case MSG_STAT: + case MSG_STAT_ANY: +-#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T ++# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T + arg->msg_perm.mode >>= 16; +-#else ++# else + /* Old Linux kernel versions might not clear the mode padding. */ + if (sizeof ((struct msqid_ds){0}.msg_perm.mode) + != sizeof (__kernel_mode_t)) + arg->msg_perm.mode &= 0xFFFF; +-#endif ++# endif + +-#if __IPC_TIME64 ++# if __IPC_TIME64 + kmsqid64_to_msqid64 (arg, buf); +-#endif ++# endif + } + + return ret; ++ ++#else /* !IPC_CTL_NEED_TRANSLATION */ ++ return msgctl_syscall (msqid, cmd, buf); ++#endif + } + #if __TIMESIZE != 64 + libc_hidden_def (__msgctl64) +diff -Nuar a/sysdeps/unix/sysv/linux/nios2/arch-syscall.h b/sysdeps/unix/sysv/linux/nios2/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/nios2/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/nios2/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -250,6 +250,7 @@ + #define __NR_sendmsg 211 + #define __NR_sendto 206 + #define __NR_set_mempolicy 237 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 99 + #define __NR_set_tid_address 96 + #define __NR_setdomainname 162 +diff -Nuar a/sysdeps/unix/sysv/linux/nios2/bits/struct_stat.h b/sysdeps/unix/sysv/linux/nios2/bits/struct_stat.h +--- a/sysdeps/unix/sysv/linux/nios2/bits/struct_stat.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/nios2/bits/struct_stat.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,135 @@ ++/* Definition for struct stat. Linux/nios2 version. ++ Copyright (C) 2020-2022 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 ++ . */ ++ ++#if !defined _SYS_STAT_H && !defined _FCNTL_H ++# error "Never include directly; use instead." ++#endif ++ ++#ifndef _BITS_STRUCT_STAT_H ++#define _BITS_STRUCT_STAT_H 1 ++ ++#include ++#include ++ ++#if defined __USE_FILE_OFFSET64 ++# define __field64(type, type64, name) type64 name ++#elif __WORDSIZE == 64 || defined __INO_T_MATCHES_INO64_T ++# if defined __INO_T_MATCHES_INO64_T && !defined __OFF_T_MATCHES_OFF64_T ++# error "ino_t and off_t must both be the same type" ++# endif ++# define __field64(type, type64, name) type name ++#elif __BYTE_ORDER == __LITTLE_ENDIAN ++# define __field64(type, type64, name) \ ++ type name __attribute__((__aligned__ (__alignof__ (type64)))); int __##name##_pad ++#else ++# define __field64(type, type64, name) \ ++ int __##name##_pad __attribute__((__aligned__ (__alignof__ (type64)))); type name ++#endif ++ ++struct stat ++ { ++#ifdef __USE_TIME_BITS64 ++# include ++#else ++ __dev_t st_dev; /* Device. */ ++ __field64(__ino_t, __ino64_t, st_ino); /* File serial number. */ ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ __dev_t __pad1; ++ __field64(__off_t, __off64_t, st_size); /* Size of file, in bytes. */ ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ int __pad2; ++ __field64(__blkcnt_t, __blkcnt64_t, st_blocks); /* 512-byte blocks */ ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# define st_atime st_atim.tv_sec /* Backward compatibility. */ ++# define st_mtime st_mtim.tv_sec ++# define st_ctime st_ctim.tv_sec ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++ int __glibc_reserved[2]; ++#endif ++ }; ++ ++#undef __field64 ++ ++#ifdef __USE_LARGEFILE64 ++struct stat64 ++ { ++# ifdef __USE_TIME_BITS64 ++# include ++# else ++ __dev_t st_dev; /* Device. */ ++ __ino64_t st_ino; /* File serial number. */ ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ __dev_t __pad1; ++ __off64_t st_size; /* Size of file, in bytes. */ ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ int __pad2; ++ __blkcnt64_t st_blocks; /* Nr. 512-byte blocks allocated. */ ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++ int __glibc_reserved[2]; ++# endif ++ }; ++#endif ++ ++/* Tell code we have these members. */ ++#define _STATBUF_ST_BLKSIZE ++#define _STATBUF_ST_RDEV ++/* Nanosecond resolution time values are supported. */ ++#define _STATBUF_ST_NSEC ++ ++#endif /* _BITS_STRUCT_STAT_H */ +diff -Nuar a/sysdeps/unix/sysv/linux/or1k/arch-syscall.h b/sysdeps/unix/sysv/linux/or1k/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/or1k/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/or1k/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -251,6 +251,7 @@ + #define __NR_sendmsg 211 + #define __NR_sendto 206 + #define __NR_set_mempolicy 237 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 99 + #define __NR_set_tid_address 96 + #define __NR_setdomainname 162 +diff -Nuar a/sysdeps/unix/sysv/linux/pathconf.c b/sysdeps/unix/sysv/linux/pathconf.c +--- a/sysdeps/unix/sysv/linux/pathconf.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/pathconf.c 2025-10-20 21:02:22.000000000 +0300 +@@ -110,8 +110,8 @@ + && strcmp (mntbuf.mnt_type, "ext4") != 0) + continue; + +- struct stat64 fsst; +- if (__stat64 (mntbuf.mnt_dir, &fsst) >= 0 ++ struct __stat64_t64 fsst; ++ if (__stat64_time64 (mntbuf.mnt_dir, &fsst) >= 0 + && st.st_dev == fsst.st_dev) + { + if (strcmp (mntbuf.mnt_type, "ext4") == 0) +diff -Nuar a/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h b/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h +--- a/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/powerpc/bits/fcntl.h 2025-10-20 21:02:22.000000000 +0300 +@@ -33,6 +33,12 @@ + # define __O_LARGEFILE 0200000 + #endif + ++#if __WORDSIZE == 64 && !defined __USE_FILE_OFFSET64 ++# define F_GETLK 5 ++# define F_SETLK 6 ++# define F_SETLKW 7 ++#endif ++ + struct flock + { + short int l_type; /* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK. */ +diff -Nuar a/sysdeps/unix/sysv/linux/powerpc/bits/wordsize.h b/sysdeps/unix/sysv/linux/powerpc/bits/wordsize.h +--- a/sysdeps/unix/sysv/linux/powerpc/bits/wordsize.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/powerpc/bits/wordsize.h 2025-10-20 21:02:19.000000000 +0300 +@@ -2,10 +2,9 @@ + + #if defined __powerpc64__ + # define __WORDSIZE 64 +-# define __WORDSIZE_TIME64_COMPAT32 1 + #else + # define __WORDSIZE 32 +-# define __WORDSIZE_TIME64_COMPAT32 0 + # define __WORDSIZE32_SIZE_ULONG 0 + # define __WORDSIZE32_PTRDIFF_LONG 0 + #endif ++#define __WORDSIZE_TIME64_COMPAT32 1 +diff -Nuar a/sysdeps/unix/sysv/linux/powerpc/dl-auxv.h b/sysdeps/unix/sysv/linux/powerpc/dl-auxv.h +--- a/sysdeps/unix/sysv/linux/powerpc/dl-auxv.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/powerpc/dl-auxv.h 2025-10-20 21:02:22.000000000 +0300 +@@ -16,15 +16,5 @@ + License along with the GNU C Library; if not, see + . */ + +-#include +- +-#if IS_IN (libc) && !defined SHARED +-int GLRO(dl_cache_line_size); +-#endif +- +-/* Scan the Aux Vector for the "Data Cache Block Size" entry and assign it +- to dl_cache_line_size. */ +-#define DL_PLATFORM_AUXV \ +- case AT_DCACHEBSIZE: \ +- GLRO(dl_cache_line_size) = av->a_un.a_val; \ +- break; ++#define DL_PLATFORM_AUXV \ ++ GLRO(dl_cache_line_size) = auxv_values[AT_DCACHEBSIZE]; +diff -Nuar a/sysdeps/unix/sysv/linux/powerpc/dl-support.c b/sysdeps/unix/sysv/linux/powerpc/dl-support.c +--- a/sysdeps/unix/sysv/linux/powerpc/dl-support.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/powerpc/dl-support.c 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,4 @@ ++#include ++ ++/* Populated from the auxiliary vector. */ ++int _dl_cache_line_size; +diff -Nuar a/sysdeps/unix/sysv/linux/powerpc/powerpc32/arch-syscall.h b/sysdeps/unix/sysv/linux/powerpc/powerpc32/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -319,6 +319,7 @@ + #define __NR_sendmsg 341 + #define __NR_sendto 335 + #define __NR_set_mempolicy 261 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 300 + #define __NR_set_tid_address 232 + #define __NR_setdomainname 121 +diff -Nuar a/sysdeps/unix/sysv/linux/powerpc/powerpc64/arch-syscall.h b/sysdeps/unix/sysv/linux/powerpc/powerpc64/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -298,6 +298,7 @@ + #define __NR_sendmsg 341 + #define __NR_sendto 335 + #define __NR_set_mempolicy 261 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 300 + #define __NR_set_tid_address 232 + #define __NR_setdomainname 121 +diff -Nuar a/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h b/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/riscv/rv32/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -122,6 +122,7 @@ + #define __NR_mbind 235 + #define __NR_membarrier 283 + #define __NR_memfd_create 279 ++#define __NR_memfd_secret 447 + #define __NR_migrate_pages 238 + #define __NR_mincore 232 + #define __NR_mkdirat 34 +@@ -228,6 +229,7 @@ + #define __NR_sendmsg 211 + #define __NR_sendto 206 + #define __NR_set_mempolicy 237 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 99 + #define __NR_set_tid_address 96 + #define __NR_setdomainname 162 +diff -Nuar a/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h b/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -127,6 +127,7 @@ + #define __NR_mbind 235 + #define __NR_membarrier 283 + #define __NR_memfd_create 279 ++#define __NR_memfd_secret 447 + #define __NR_migrate_pages 238 + #define __NR_mincore 232 + #define __NR_mkdirat 34 +@@ -235,6 +236,7 @@ + #define __NR_sendmsg 211 + #define __NR_sendto 206 + #define __NR_set_mempolicy 237 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 99 + #define __NR_set_tid_address 96 + #define __NR_setdomainname 162 +diff -Nuar a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h +--- a/sysdeps/unix/sysv/linux/rseq-internal.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/rseq-internal.h 2025-10-20 21:02:22.000000000 +0300 +@@ -25,18 +25,48 @@ + #include + #include + ++/* 32 is the initially required value for the area size. The ++ actually used rseq size may be less (20 bytes initially). */ ++#define RSEQ_AREA_SIZE_INITIAL 32 ++#define RSEQ_AREA_SIZE_INITIAL_USED 20 ++ ++/* The variables are in .data.relro but are not yet write-protected. */ ++extern unsigned int _rseq_size attribute_hidden; ++extern ptrdiff_t _rseq_offset attribute_hidden; ++ + #ifdef RSEQ_SIG + static inline bool + rseq_register_current_thread (struct pthread *self, bool do_rseq) + { + if (do_rseq) + { ++ unsigned int size; ++#if IS_IN (rtld) ++ /* Use the hidden symbol in ld.so. */ ++ size = _rseq_size; ++#else ++ size = __rseq_size; ++#endif ++ if (size < RSEQ_AREA_SIZE_INITIAL) ++ /* The initial implementation used only 20 bytes out of 32, ++ but still expected size 32. */ ++ size = RSEQ_AREA_SIZE_INITIAL; ++ ++ /* Initialize the rseq fields that are read by the kernel on ++ registration, there is no guarantee that struct pthread is ++ cleared on all architectures. */ ++ THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_UNINITIALIZED); ++ THREAD_SETMEM (self, rseq_area.cpu_id_start, 0); ++ THREAD_SETMEM (self, rseq_area.rseq_cs, 0); ++ THREAD_SETMEM (self, rseq_area.flags, 0); ++ + int ret = INTERNAL_SYSCALL_CALL (rseq, &self->rseq_area, +- sizeof (self->rseq_area), +- 0, RSEQ_SIG); ++ size, 0, RSEQ_SIG); + if (!INTERNAL_SYSCALL_ERROR_P (ret)) + return true; + } ++ /* When rseq is disabled by tunables or the registration fails, inform ++ userspace by setting 'cpu_id' to RSEQ_CPU_ID_REGISTRATION_FAILED. */ + THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED); + return false; + } +diff -Nuar a/sysdeps/unix/sysv/linux/s390/mmap_call.h b/sysdeps/unix/sysv/linux/s390/mmap_call.h +--- a/sysdeps/unix/sysv/linux/s390/mmap_call.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/s390/mmap_call.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,32 @@ ++/* mmap - map files or devices into memory. Linux/s390 version. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#define MMAP_CALL(__nr, __addr, __len, __prot, __flags, __fd, __offset) \ ++ ({ \ ++ long int __args[6] = { (long int) (__addr), (long int) (__len), \ ++ (long int) (__prot), (long int) (__flags), \ ++ (long int) (__fd), (long int) (__offset) }; \ ++ INLINE_SYSCALL_CALL (__nr, __args); \ ++ }) ++#define MMAP_CALL_INTERNAL(__nr, __addr, __len, __prot, __flags, __fd, __offset) \ ++ ({ \ ++ long int __args[6] = { (long int) (__addr), (long int) (__len), \ ++ (long int) (__prot), (long int) (__flags), \ ++ (long int) (__fd), (long int) (__offset) }; \ ++ INTERNAL_SYSCALL_CALL (__nr, __args); \ ++ }) +diff -Nuar a/sysdeps/unix/sysv/linux/s390/mmap_internal.h b/sysdeps/unix/sysv/linux/s390/mmap_internal.h +--- a/sysdeps/unix/sysv/linux/s390/mmap_internal.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/s390/mmap_internal.h 1970-01-01 02:00:00.000000000 +0200 +@@ -1,32 +0,0 @@ +-/* mmap - map files or devices into memory. Linux/s390 version. +- Copyright (C) 2017-2022 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 +- . */ +- +-#ifndef MMAP_S390_INTERNAL_H +-# define MMAP_S390_INTERNAL_H +- +-#define MMAP_CALL(__nr, __addr, __len, __prot, __flags, __fd, __offset) \ +- ({ \ +- long int __args[6] = { (long int) (__addr), (long int) (__len), \ +- (long int) (__prot), (long int) (__flags), \ +- (long int) (__fd), (long int) (__offset) }; \ +- INLINE_SYSCALL_CALL (__nr, __args); \ +- }) +- +-#include_next +- +-#endif +diff -Nuar a/sysdeps/unix/sysv/linux/s390/s390-32/arch-syscall.h b/sysdeps/unix/sysv/linux/s390/s390-32/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/s390/s390-32/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/s390/s390-32/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -311,6 +311,7 @@ + #define __NR_sendmsg 370 + #define __NR_sendto 369 + #define __NR_set_mempolicy 270 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 304 + #define __NR_set_tid_address 252 + #define __NR_setdomainname 121 +diff -Nuar a/sysdeps/unix/sysv/linux/s390/s390-64/arch-syscall.h b/sysdeps/unix/sysv/linux/s390/s390-64/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/s390/s390-64/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/s390/s390-64/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -278,6 +278,7 @@ + #define __NR_sendmsg 370 + #define __NR_sendto 369 + #define __NR_set_mempolicy 270 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 304 + #define __NR_set_tid_address 252 + #define __NR_setdomainname 121 +diff -Nuar a/sysdeps/unix/sysv/linux/sched_getcpu.c b/sysdeps/unix/sysv/linux/sched_getcpu.c +--- a/sysdeps/unix/sysv/linux/sched_getcpu.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/sched_getcpu.c 2025-10-20 21:02:22.000000000 +0300 +@@ -33,17 +33,9 @@ + return r == -1 ? r : cpu; + } + +-#ifdef RSEQ_SIG + int + sched_getcpu (void) + { + int cpu_id = THREAD_GETMEM_VOLATILE (THREAD_SELF, rseq_area.cpu_id); + return __glibc_likely (cpu_id >= 0) ? cpu_id : vsyscall_sched_getcpu (); + } +-#else /* RSEQ_SIG */ +-int +-sched_getcpu (void) +-{ +- return vsyscall_sched_getcpu (); +-} +-#endif /* RSEQ_SIG */ +diff -Nuar a/sysdeps/unix/sysv/linux/semctl.c b/sysdeps/unix/sysv/linux/semctl.c +--- a/sysdeps/unix/sysv/linux/semctl.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/semctl.c 2025-10-20 21:02:22.000000000 +0300 +@@ -140,6 +140,13 @@ + union semun64 arg64 = { 0 }; + va_list ap; + ++ /* Some applications pass the __IPC_64 flag in cmd, to invoke ++ previously unsupported commands back when there was no EINVAL ++ error checking in glibc. Mask the flag for the switch statements ++ below. semctl_syscall adds back the __IPC_64 flag for the actual ++ system call. */ ++ cmd &= ~__IPC_64; ++ + /* Get the argument only if required. */ + switch (cmd) + { +diff -Nuar a/sysdeps/unix/sysv/linux/sh/arch-syscall.h b/sysdeps/unix/sysv/linux/sh/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/sh/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/sh/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -303,6 +303,7 @@ + #define __NR_sendmsg 355 + #define __NR_sendto 349 + #define __NR_set_mempolicy 276 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 311 + #define __NR_set_tid_address 258 + #define __NR_setdomainname 121 +diff -Nuar a/sysdeps/unix/sysv/linux/sh/bits/struct_stat.h b/sysdeps/unix/sysv/linux/sh/bits/struct_stat.h +--- a/sysdeps/unix/sysv/linux/sh/bits/struct_stat.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/sh/bits/struct_stat.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,139 @@ ++/* Definition for struct stat. Linux/sh version. ++ Copyright (C) 2020-2022 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 ++ . */ ++ ++#if !defined _SYS_STAT_H && !defined _FCNTL_H ++# error "Never include directly; use instead." ++#endif ++ ++#ifndef _BITS_STRUCT_STAT_H ++#define _BITS_STRUCT_STAT_H 1 ++ ++#include ++#include ++ ++struct stat ++ { ++#ifdef __USE_TIME_BITS64 ++# include ++#else ++ __dev_t st_dev; /* Device. */ ++ unsigned short int __pad1; ++# ifndef __USE_FILE_OFFSET64 ++ __ino_t st_ino; /* File serial number. */ ++# else ++ __ino_t __st_ino; /* 32bit file serial number. */ ++# endif ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ unsigned short int __pad2; ++# ifndef __USE_FILE_OFFSET64 ++ __off_t st_size; /* Size of file, in bytes. */ ++# else ++ __off64_t st_size; /* Size of file, in bytes. */ ++# endif ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ ++# ifndef __USE_FILE_OFFSET64 ++ __blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */ ++# else ++ __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ ++# endif ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# define st_atime st_atim.tv_sec /* Backward compatibility. */ ++# define st_mtime st_mtim.tv_sec ++# define st_ctime st_ctim.tv_sec ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++# ifndef __USE_FILE_OFFSET64 ++ unsigned long int __glibc_reserved4; ++ unsigned long int __glibc_reserved5; ++# else ++ __ino64_t st_ino; /* File serial number. */ ++# endif ++#endif /* __USE_TIME_BITS64 */ ++ }; ++ ++#ifdef __USE_LARGEFILE64 ++struct stat64 ++ { ++# ifdef __USE_TIME_BITS64 ++# include ++# else ++ __dev_t st_dev; /* Device. */ ++ unsigned int __pad1; ++ ++ __ino_t __st_ino; /* 32bit file serial number. */ ++ __mode_t st_mode; /* File mode. */ ++ __nlink_t st_nlink; /* Link count. */ ++ __uid_t st_uid; /* User ID of the file's owner. */ ++ __gid_t st_gid; /* Group ID of the file's group.*/ ++ __dev_t st_rdev; /* Device number, if device. */ ++ unsigned int __pad2; ++ __off64_t st_size; /* Size of file, in bytes. */ ++ __blksize_t st_blksize; /* Optimal block size for I/O. */ ++ ++ __blkcnt64_t st_blocks; /* Number 512-byte blocks allocated. */ ++# ifdef __USE_XOPEN2K8 ++ /* Nanosecond resolution timestamps are stored in a format ++ equivalent to 'struct timespec'. This is the type used ++ whenever possible but the Unix namespace rules do not allow the ++ identifier 'timespec' to appear in the header. ++ Therefore we have to handle the use of this header in strictly ++ standard-compliant sources special. */ ++ struct timespec st_atim; /* Time of last access. */ ++ struct timespec st_mtim; /* Time of last modification. */ ++ struct timespec st_ctim; /* Time of last status change. */ ++# else ++ __time_t st_atime; /* Time of last access. */ ++ unsigned long int st_atimensec; /* Nscecs of last access. */ ++ __time_t st_mtime; /* Time of last modification. */ ++ unsigned long int st_mtimensec; /* Nsecs of last modification. */ ++ __time_t st_ctime; /* Time of last status change. */ ++ unsigned long int st_ctimensec; /* Nsecs of last status change. */ ++# endif ++ __ino64_t st_ino; /* File serial number. */ ++# endif /* __USE_TIME_BITS64 */ ++ }; ++#endif ++ ++/* Tell code we have these members. */ ++#define _STATBUF_ST_BLKSIZE ++#define _STATBUF_ST_RDEV ++/* Nanosecond resolution time values are supported. */ ++#define _STATBUF_ST_NSEC ++ ++ ++#endif /* _BITS_STRUCT_STAT_H */ +diff -Nuar a/sysdeps/unix/sysv/linux/shmctl.c b/sysdeps/unix/sysv/linux/shmctl.c +--- a/sysdeps/unix/sysv/linux/shmctl.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/shmctl.c 2025-10-20 21:02:22.000000000 +0300 +@@ -85,11 +85,19 @@ + int + __shmctl64 (int shmid, int cmd, struct __shmid64_ds *buf) + { +-#if __IPC_TIME64 ++#if IPC_CTL_NEED_TRANSLATION ++# if __IPC_TIME64 + struct kernel_shmid64_ds kshmid, *arg = NULL; +-#else ++# else + shmctl_arg_t *arg; +-#endif ++# endif ++ ++ /* Some applications pass the __IPC_64 flag in cmd, to invoke ++ previously unsupported commands back when there was no EINVAL ++ error checking in glibc. Mask the flag for the switch statements ++ below. shmctl_syscall adds back the __IPC_64 flag for the actual ++ system call. */ ++ cmd &= ~__IPC_64; + + switch (cmd) + { +@@ -103,19 +111,19 @@ + case IPC_STAT: + case SHM_STAT: + case SHM_STAT_ANY: +-#if __IPC_TIME64 ++# if __IPC_TIME64 + if (buf != NULL) + { + shmid64_to_kshmid64 (buf, &kshmid); + arg = &kshmid; + } +-# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T ++# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T + if (cmd == IPC_SET) + arg->shm_perm.mode *= 0x10000U; +-# endif +-#else ++# endif ++# else + arg = buf; +-#endif ++# endif + break; + + case IPC_INFO: +@@ -140,21 +148,25 @@ + case IPC_STAT: + case SHM_STAT: + case SHM_STAT_ANY: +-#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T ++# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T + arg->shm_perm.mode >>= 16; +-#else ++# else + /* Old Linux kernel versions might not clear the mode padding. */ + if (sizeof ((struct shmid_ds){0}.shm_perm.mode) + != sizeof (__kernel_mode_t)) + arg->shm_perm.mode &= 0xFFFF; +-#endif ++# endif + +-#if __IPC_TIME64 ++# if __IPC_TIME64 + kshmid64_to_shmid64 (arg, buf); +-#endif ++# endif + } + + return ret; ++ ++#else /* !IPC_CTL_NEED_TRANSLATION */ ++ return shmctl_syscall (shmid, cmd, buf); ++#endif + } + #if __TIMESIZE != 64 + libc_hidden_def (__shmctl64) +diff -Nuar a/sysdeps/unix/sysv/linux/sparc/bits/wordsize.h b/sysdeps/unix/sysv/linux/sparc/bits/wordsize.h +--- a/sysdeps/unix/sysv/linux/sparc/bits/wordsize.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/sparc/bits/wordsize.h 2025-10-20 21:02:19.000000000 +0300 +@@ -2,10 +2,9 @@ + + #if defined __arch64__ || defined __sparcv9 + # define __WORDSIZE 64 +-# define __WORDSIZE_TIME64_COMPAT32 1 + #else + # define __WORDSIZE 32 + # define __WORDSIZE32_SIZE_ULONG 0 + # define __WORDSIZE32_PTRDIFF_LONG 0 +-# define __WORDSIZE_TIME64_COMPAT32 0 + #endif ++#define __WORDSIZE_TIME64_COMPAT32 1 +diff -Nuar a/sysdeps/unix/sysv/linux/sparc/brk.c b/sysdeps/unix/sysv/linux/sparc/brk.c +--- a/sysdeps/unix/sysv/linux/sparc/brk.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/sparc/brk.c 1970-01-01 02:00:00.000000000 +0200 +@@ -1,58 +0,0 @@ +-/* Change data segment. Linux SPARC version. +- Copyright (C) 2021-2022 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 +- . */ +- +-#include +-#include +-#include +- +-/* This must be initialized data because commons can't have aliases. */ +-void *__curbrk = 0; +- +-#if HAVE_INTERNAL_BRK_ADDR_SYMBOL +-/* Old braindamage in GCC's crtstuff.c requires this symbol in an attempt +- to work around different old braindamage in the old Linux ELF dynamic +- linker. */ +-weak_alias (__curbrk, ___brk_addr) +-#endif +- +-#ifdef __arch64__ +-# define SYSCALL_NUM "0x6d" +-#else +-# define SYSCALL_NUM "0x10" +-#endif +- +-int +-__brk (void *addr) +-{ +- register long int g1 asm ("g1") = __NR_brk; +- register long int o0 asm ("o0") = (long int) addr; +- asm volatile ("ta " SYSCALL_NUM +- : "=r"(o0) +- : "r"(g1), "0"(o0) +- : "cc"); +- __curbrk = (void *) o0; +- +- if (__curbrk < addr) +- { +- __set_errno (ENOMEM); +- return -1; +- } +- +- return 0; +-} +-weak_alias (__brk, brk) +diff -Nuar a/sysdeps/unix/sysv/linux/sparc/brk_call.h b/sysdeps/unix/sysv/linux/sparc/brk_call.h +--- a/sysdeps/unix/sysv/linux/sparc/brk_call.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/sparc/brk_call.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,35 @@ ++/* Invoke the brk system call. Sparc version. ++ Copyright (C) 2022 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 ++ . */ ++ ++#ifdef __arch64__ ++# define SYSCALL_NUM "0x6d" ++#else ++# define SYSCALL_NUM "0x10" ++#endif ++ ++static inline void * ++__brk_call (void *addr) ++{ ++ register long int g1 asm ("g1") = __NR_brk; ++ register long int o0 asm ("o0") = (long int) addr; ++ asm volatile ("ta " SYSCALL_NUM ++ : "=r"(o0) ++ : "r"(g1), "0"(o0) ++ : "cc"); ++ return (void *) o0; ++} +diff -Nuar a/sysdeps/unix/sysv/linux/sparc/sparc32/arch-syscall.h b/sysdeps/unix/sysv/linux/sparc/sparc32/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/sparc/sparc32/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/sparc/sparc32/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -310,6 +310,7 @@ + #define __NR_sendmsg 114 + #define __NR_sendto 133 + #define __NR_set_mempolicy 305 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 300 + #define __NR_set_tid_address 166 + #define __NR_setdomainname 163 +diff -Nuar a/sysdeps/unix/sysv/linux/sparc/sparc64/arch-syscall.h b/sysdeps/unix/sysv/linux/sparc/sparc64/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/sparc/sparc64/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/sparc/sparc64/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -286,6 +286,7 @@ + #define __NR_sendmsg 114 + #define __NR_sendto 133 + #define __NR_set_mempolicy 305 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 300 + #define __NR_set_tid_address 166 + #define __NR_setdomainname 163 +diff -Nuar a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c +--- a/sysdeps/unix/sysv/linux/spawni.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/spawni.c 2025-10-20 21:02:22.000000000 +0300 +@@ -409,7 +409,7 @@ + __waitpid (new_pid, NULL, 0); + } + else +- ec = -new_pid; ++ ec = errno; + + __munmap (stack, stack_size); + +diff -Nuar a/sysdeps/unix/sysv/linux/startup.h b/sysdeps/unix/sysv/linux/startup.h +--- a/sysdeps/unix/sysv/linux/startup.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/startup.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,39 @@ ++/* Linux definitions of functions used by static libc main startup. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#ifdef SHARED ++# include_next ++#else ++# include ++ ++/* Avoid a run-time invocation of strlen. */ ++#define _startup_fatal(message) \ ++ do \ ++ { \ ++ size_t __message_length = __builtin_strlen (message); \ ++ if (! __builtin_constant_p (__message_length)) \ ++ { \ ++ extern void _startup_fatal_not_constant (void); \ ++ _startup_fatal_not_constant (); \ ++ } \ ++ INTERNAL_SYSCALL_CALL (write, STDERR_FILENO, (message), \ ++ __message_length); \ ++ INTERNAL_SYSCALL_CALL (exit_group, 127); \ ++ } \ ++ while (0) ++#endif /* !SHARED */ +diff -Nuar a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list +--- a/sysdeps/unix/sysv/linux/syscall-names.list 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/syscall-names.list 2025-10-20 21:02:22.000000000 +0300 +@@ -21,8 +21,8 @@ + # This file can list all potential system calls. The names are only + # used if the installed kernel headers also provide them. + +-# The list of system calls is current as of Linux 5.16. +-kernel 5.16 ++# The list of system calls is current as of Linux 5.19. ++kernel 5.19 + + FAST_atomic_update + FAST_cmpxchg +@@ -524,6 +524,7 @@ + sendmsg + sendto + set_mempolicy ++set_mempolicy_home_node + set_robust_list + set_thread_area + set_tid_address +diff -Nuar a/sysdeps/unix/sysv/linux/tst-getauxval.c b/sysdeps/unix/sysv/linux/tst-getauxval.c +--- a/sysdeps/unix/sysv/linux/tst-getauxval.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/tst-getauxval.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,74 @@ ++/* Basic test for getauxval. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++static int missing; ++static int mismatch; ++ ++static void ++check_nonzero (unsigned long t, const char *s) ++{ ++ unsigned long v = getauxval (t); ++ printf ("%s: %lu (0x%lx)\n", s, v, v); ++ if (v == 0) ++ missing++; ++} ++ ++static void ++check_eq (unsigned long t, const char *s, unsigned long want) ++{ ++ unsigned long v = getauxval (t); ++ printf ("%s: %lu want: %lu\n", s, v, want); ++ if (v != want) ++ mismatch++; ++} ++ ++#define NZ(x) check_nonzero (x, #x) ++#define EQ(x, want) check_eq (x, #x, want) ++ ++static int ++do_test (void) ++{ ++ /* These auxv entries should be non-zero on Linux. */ ++ NZ (AT_PHDR); ++ NZ (AT_PHENT); ++ NZ (AT_PHNUM); ++ NZ (AT_PAGESZ); ++ NZ (AT_ENTRY); ++ NZ (AT_CLKTCK); ++ NZ (AT_RANDOM); ++ NZ (AT_EXECFN); ++ if (missing) ++ FAIL_EXIT1 ("Found %d missing auxv entries.\n", missing); ++ ++ /* Check against syscalls. */ ++ EQ (AT_UID, getuid ()); ++ EQ (AT_EUID, geteuid ()); ++ EQ (AT_GID, getgid ()); ++ EQ (AT_EGID, getegid ()); ++ if (mismatch) ++ FAIL_EXIT1 ("Found %d mismatching auxv entries.\n", mismatch); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/sysdeps/unix/sysv/linux/tst-linux-mremap1.c b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c +--- a/sysdeps/unix/sysv/linux/tst-linux-mremap1.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/unix/sysv/linux/tst-linux-mremap1.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,63 @@ ++/* Test mremap with MREMAP_DONTUNMAP. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ size_t old_size = getpagesize (); ++ size_t new_size = old_size; ++ char *old_addr = xmmap (NULL, old_size, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS, -1); ++ old_addr[0] = 1; ++ old_addr[old_size - 1] = 2; ++ ++ /* Create an available 64-page mmap region. */ ++ size_t fixed_size = old_size * 64; ++ char *fixed_addr = xmmap (NULL, fixed_size, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS, -1); ++ xmunmap (fixed_addr, fixed_size); ++ ++ /* Add 3 * pagesize. */ ++ fixed_size += 3 * old_size; ++ ++ /* Test MREMAP_DONTUNMAP. It should return FIXED_ADDR created above. */ ++ char *new_addr = mremap (old_addr, old_size, new_size, ++ MREMAP_DONTUNMAP | MREMAP_MAYMOVE, ++ fixed_addr); ++ if (new_addr == MAP_FAILED) ++ return mremap_failure_exit (errno); ++ TEST_VERIFY_EXIT (fixed_addr == new_addr); ++ old_addr[0] = 3; ++ old_addr[old_size - 1] = 4; ++ new_addr[0] = 1; ++ new_addr[new_size - 1] = 2; ++ xmunmap (new_addr, new_size); ++ xmunmap (old_addr, old_size); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/sysdeps/unix/sysv/linux/tst-mman-consts.py b/sysdeps/unix/sysv/linux/tst-mman-consts.py +--- a/sysdeps/unix/sysv/linux/tst-mman-consts.py 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/tst-mman-consts.py 2025-10-20 21:02:22.000000000 +0300 +@@ -33,7 +33,7 @@ + help='C compiler (including options) to use') + args = parser.parse_args() + linux_version_headers = glibcsyscalls.linux_kernel_version(args.cc) +- linux_version_glibc = (5, 15) ++ linux_version_glibc = (5, 17) + sys.exit(glibcextract.compare_macro_consts( + '#define _GNU_SOURCE 1\n' + '#include \n', +diff -Nuar a/sysdeps/unix/sysv/linux/tst-rseq.c b/sysdeps/unix/sysv/linux/tst-rseq.c +--- a/sysdeps/unix/sysv/linux/tst-rseq.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/tst-rseq.c 2025-10-20 21:02:22.000000000 +0300 +@@ -29,6 +29,7 @@ + # include + # include + # include ++# include + # include + # include + # include "tst-rseq.h" +@@ -42,7 +43,8 @@ + TEST_COMPARE (__rseq_flags, 0); + TEST_VERIFY ((char *) __thread_pointer () + __rseq_offset + == (char *) &pd->rseq_area); +- TEST_COMPARE (__rseq_size, sizeof (pd->rseq_area)); ++ /* The current implementation only supports the initial size. */ ++ TEST_COMPARE (__rseq_size, 20); + } + + static void +@@ -52,6 +54,12 @@ + { + FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test"); + } ++ printf ("info: __rseq_size: %u\n", __rseq_size); ++ printf ("info: __rseq_offset: %td\n", __rseq_offset); ++ printf ("info: __rseq_flags: %u\n", __rseq_flags); ++ printf ("info: getauxval (AT_RSEQ_FEATURE_SIZE): %ld\n", ++ getauxval (AT_RSEQ_FEATURE_SIZE)); ++ printf ("info: getauxval (AT_RSEQ_ALIGN): %ld\n", getauxval (AT_RSEQ_ALIGN)); + do_rseq_main_test (); + } + #else /* RSEQ_SIG */ +diff -Nuar a/sysdeps/unix/sysv/linux/tst-rseq-disable.c b/sysdeps/unix/sysv/linux/tst-rseq-disable.c +--- a/sysdeps/unix/sysv/linux/tst-rseq-disable.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/tst-rseq-disable.c 2025-10-20 21:02:22.000000000 +0300 +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + + #ifdef RSEQ_SIG +diff -Nuar a/sysdeps/unix/sysv/linux/tst-socket-timestamp-compat.c b/sysdeps/unix/sysv/linux/tst-socket-timestamp-compat.c +--- a/sysdeps/unix/sysv/linux/tst-socket-timestamp-compat.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/tst-socket-timestamp-compat.c 2025-10-20 21:02:22.000000000 +0300 +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + /* AF_INET socket and address used to receive data. */ + static int srv; +@@ -88,7 +89,7 @@ + /* Enable 32 bit timeval precision and check if no 64 bit timeval stamp + is created. */ + { +- int r = setsockopt (srv, SOL_SOCKET, SO_TIMESTAMP_OLD, &(int){1}, ++ int r = setsockopt (srv, SOL_SOCKET, COMPAT_SO_TIMESTAMP_OLD, &(int){1}, + sizeof (int)); + TEST_VERIFY_EXIT (r != -1); + +@@ -103,10 +104,10 @@ + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + +- if (sizeof (time_t) > 4 && cmsg->cmsg_type == SO_TIMESTAMP_NEW) ++ if (sizeof (time_t) > 4 && cmsg->cmsg_type == COMPAT_SO_TIMESTAMP_NEW) + found_timestamp = true; + else +- TEST_VERIFY (cmsg->cmsg_type != SO_TIMESTAMP_NEW); ++ TEST_VERIFY (cmsg->cmsg_type != COMPAT_SO_TIMESTAMP_NEW); + } + + TEST_COMPARE (found_timestamp, sizeof (time_t) > 4); +@@ -114,7 +115,7 @@ + + /* Same as before, but for timespec. */ + { +- int r = setsockopt (srv, SOL_SOCKET, SO_TIMESTAMPNS_OLD, &(int){1}, ++ int r = setsockopt (srv, SOL_SOCKET, COMPAT_SO_TIMESTAMPNS_OLD, &(int){1}, + sizeof (int)); + TEST_VERIFY_EXIT (r != -1); + +@@ -129,10 +130,10 @@ + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + +- if (sizeof (time_t) > 4 && cmsg->cmsg_type == SO_TIMESTAMPNS_NEW) ++ if (sizeof (time_t) > 4 && cmsg->cmsg_type == COMPAT_SO_TIMESTAMPNS_NEW) + found_timestamp = true; + else +- TEST_VERIFY (cmsg->cmsg_type != SO_TIMESTAMPNS_NEW); ++ TEST_VERIFY (cmsg->cmsg_type != COMPAT_SO_TIMESTAMPNS_NEW); + } + + TEST_COMPARE (found_timestamp, sizeof (time_t) > 4); +@@ -151,7 +152,7 @@ + /* Enable 32 bit timeval precision and check if no 64 bit timeval stamp + is created. */ + { +- int r = setsockopt (srv, SOL_SOCKET, SO_TIMESTAMP_OLD, &(int){1}, ++ int r = setsockopt (srv, SOL_SOCKET, COMPAT_SO_TIMESTAMP_OLD, &(int){1}, + sizeof (int)); + TEST_VERIFY_EXIT (r != -1); + +@@ -172,10 +173,10 @@ + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + +- if (sizeof (time_t) > 4 && cmsg->cmsg_type == SO_TIMESTAMP_NEW) ++ if (sizeof (time_t) > 4 && cmsg->cmsg_type == COMPAT_SO_TIMESTAMP_NEW) + found_timestamp = true; + else +- TEST_VERIFY (cmsg->cmsg_type != SO_TIMESTAMP_NEW); ++ TEST_VERIFY (cmsg->cmsg_type != COMPAT_SO_TIMESTAMP_NEW); + } + + if (sizeof (time_t) > 4) +@@ -192,7 +193,7 @@ + + /* Same as before, but for timespec. */ + { +- int r = setsockopt (srv, SOL_SOCKET, SO_TIMESTAMPNS_OLD, &(int){1}, ++ int r = setsockopt (srv, SOL_SOCKET, COMPAT_SO_TIMESTAMPNS_OLD, &(int){1}, + sizeof (int)); + TEST_VERIFY_EXIT (r != -1); + +@@ -213,10 +214,10 @@ + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + +- if (sizeof (time_t) > 4 && cmsg->cmsg_type == SO_TIMESTAMPNS_NEW) ++ if (sizeof (time_t) > 4 && cmsg->cmsg_type == COMPAT_SO_TIMESTAMPNS_NEW) + found_timestamp = true; + else +- TEST_VERIFY (cmsg->cmsg_type != SO_TIMESTAMPNS_NEW); ++ TEST_VERIFY (cmsg->cmsg_type != COMPAT_SO_TIMESTAMPNS_NEW); + } + + if (sizeof (time_t) > 4) +diff -Nuar a/sysdeps/unix/sysv/linux/x86_64/64/arch-syscall.h b/sysdeps/unix/sysv/linux/x86_64/64/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/x86_64/64/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/x86_64/64/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -278,6 +278,7 @@ + #define __NR_sendmsg 46 + #define __NR_sendto 44 + #define __NR_set_mempolicy 238 ++#define __NR_set_mempolicy_home_node 450 + #define __NR_set_robust_list 273 + #define __NR_set_thread_area 205 + #define __NR_set_tid_address 218 +diff -Nuar a/sysdeps/unix/sysv/linux/x86_64/x32/arch-syscall.h b/sysdeps/unix/sysv/linux/x86_64/x32/arch-syscall.h +--- a/sysdeps/unix/sysv/linux/x86_64/x32/arch-syscall.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/unix/sysv/linux/x86_64/x32/arch-syscall.h 2025-10-20 21:02:22.000000000 +0300 +@@ -270,6 +270,7 @@ + #define __NR_sendmsg 1073742342 + #define __NR_sendto 1073741868 + #define __NR_set_mempolicy 1073742062 ++#define __NR_set_mempolicy_home_node 1073742274 + #define __NR_set_robust_list 1073742354 + #define __NR_set_thread_area 1073742029 + #define __NR_set_tid_address 1073742042 +diff -Nuar a/sysdeps/x86/bits/wordsize.h b/sysdeps/x86/bits/wordsize.h +--- a/sysdeps/x86/bits/wordsize.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86/bits/wordsize.h 2025-10-20 21:02:19.000000000 +0300 +@@ -8,10 +8,9 @@ + #define __WORDSIZE32_PTRDIFF_LONG 0 + #endif + ++#define __WORDSIZE_TIME64_COMPAT32 1 ++ + #ifdef __x86_64__ +-# define __WORDSIZE_TIME64_COMPAT32 1 + /* Both x86-64 and x32 use the 64-bit system call interface. */ + # define __SYSCALL_WORDSIZE 64 +-#else +-# define __WORDSIZE_TIME64_COMPAT32 0 + #endif +diff -Nuar a/sysdeps/x86/dl-cacheinfo.h b/sysdeps/x86/dl-cacheinfo.h +--- a/sysdeps/x86/dl-cacheinfo.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86/dl-cacheinfo.h 2025-10-20 21:02:22.000000000 +0300 +@@ -187,7 +187,7 @@ + ++round; + } + /* There is no other cache information anywhere else. */ +- break; ++ return -1; + } + else + { +@@ -257,28 +257,23 @@ + + /* OK, we can use the CPUID instruction to get all info about the + caches. */ +- unsigned int cnt = 0; +- unsigned int max = 1; + long int result = 0; + bool no_level_2_or_3 = false; + bool has_level_2 = false; ++ unsigned int eax; ++ unsigned int ebx; ++ unsigned int ecx; ++ unsigned int edx; ++ __cpuid (2, eax, ebx, ecx, edx); + +- while (cnt++ < max) ++ /* The low byte of EAX of CPUID leaf 2 should always return 1 and it ++ should be ignored. If it isn't 1, use CPUID leaf 4 instead. */ ++ if ((eax & 0xff) != 1) ++ return intel_check_word (name, 0xff, &has_level_2, &no_level_2_or_3, ++ cpu_features); ++ else + { +- unsigned int eax; +- unsigned int ebx; +- unsigned int ecx; +- unsigned int edx; +- __cpuid (2, eax, ebx, ecx, edx); +- +- /* The low byte of EAX in the first round contain the number of +- rounds we have to make. At least one, the one we are already +- doing. */ +- if (cnt == 1) +- { +- max = eax & 0xff; +- eax &= 0xffffff00; +- } ++ eax &= 0xffffff00; + + /* Process the individual registers' value. */ + result = intel_check_word (name, eax, &has_level_2, +@@ -478,7 +473,7 @@ + } + + static void +-get_common_cache_info (long int *shared_ptr, unsigned int *threads_ptr, ++get_common_cache_info (long int *shared_ptr, long int * shared_per_thread_ptr, unsigned int *threads_ptr, + long int core) + { + unsigned int eax; +@@ -497,6 +492,7 @@ + unsigned int family = cpu_features->basic.family; + unsigned int model = cpu_features->basic.model; + long int shared = *shared_ptr; ++ long int shared_per_thread = *shared_per_thread_ptr; + unsigned int threads = *threads_ptr; + bool inclusive_cache = true; + bool support_count_mask = true; +@@ -512,6 +508,7 @@ + /* Try L2 otherwise. */ + level = 2; + shared = core; ++ shared_per_thread = core; + threads_l2 = 0; + threads_l3 = -1; + } +@@ -668,29 +665,27 @@ + } + else + { +-intel_bug_no_cache_info: +- /* Assume that all logical threads share the highest cache +- level. */ +- threads +- = ((cpu_features->features[CPUID_INDEX_1].cpuid.ebx >> 16) +- & 0xff); +- } +- +- /* Cap usage of highest cache level to the number of supported +- threads. */ +- if (shared > 0 && threads > 0) +- shared /= threads; ++ intel_bug_no_cache_info: ++ /* Assume that all logical threads share the highest cache ++ level. */ ++ threads = ((cpu_features->features[CPUID_INDEX_1].cpuid.ebx >> 16) ++ & 0xff); ++ } ++ /* Get per-thread size of highest level cache. */ ++ if (shared_per_thread > 0 && threads > 0) ++ shared_per_thread /= threads; + } + + /* Account for non-inclusive L2 and L3 caches. */ + if (!inclusive_cache) + { +- if (threads_l2 > 0) +- core /= threads_l2; ++ long int core_per_thread = threads_l2 > 0 ? (core / threads_l2) : core; ++ shared_per_thread += core_per_thread; + shared += core; + } + + *shared_ptr = shared; ++ *shared_per_thread_ptr = shared_per_thread; + *threads_ptr = threads; + } + +@@ -704,6 +699,7 @@ + int max_cpuid_ex; + long int data = -1; + long int shared = -1; ++ long int shared_per_thread = -1; + long int core = -1; + unsigned int threads = 0; + unsigned long int level1_icache_size = -1; +@@ -724,6 +720,7 @@ + data = handle_intel (_SC_LEVEL1_DCACHE_SIZE, cpu_features); + core = handle_intel (_SC_LEVEL2_CACHE_SIZE, cpu_features); + shared = handle_intel (_SC_LEVEL3_CACHE_SIZE, cpu_features); ++ shared_per_thread = shared; + + level1_icache_size + = handle_intel (_SC_LEVEL1_ICACHE_SIZE, cpu_features); +@@ -747,13 +744,14 @@ + level4_cache_size + = handle_intel (_SC_LEVEL4_CACHE_SIZE, cpu_features); + +- get_common_cache_info (&shared, &threads, core); ++ get_common_cache_info (&shared, &shared_per_thread, &threads, core); + } + else if (cpu_features->basic.kind == arch_kind_zhaoxin) + { + data = handle_zhaoxin (_SC_LEVEL1_DCACHE_SIZE); + core = handle_zhaoxin (_SC_LEVEL2_CACHE_SIZE); + shared = handle_zhaoxin (_SC_LEVEL3_CACHE_SIZE); ++ shared_per_thread = shared; + + level1_icache_size = handle_zhaoxin (_SC_LEVEL1_ICACHE_SIZE); + level1_icache_linesize = handle_zhaoxin (_SC_LEVEL1_ICACHE_LINESIZE); +@@ -767,13 +765,14 @@ + level3_cache_assoc = handle_zhaoxin (_SC_LEVEL3_CACHE_ASSOC); + level3_cache_linesize = handle_zhaoxin (_SC_LEVEL3_CACHE_LINESIZE); + +- get_common_cache_info (&shared, &threads, core); ++ get_common_cache_info (&shared, &shared_per_thread, &threads, core); + } + else if (cpu_features->basic.kind == arch_kind_amd) + { + data = handle_amd (_SC_LEVEL1_DCACHE_SIZE); + core = handle_amd (_SC_LEVEL2_CACHE_SIZE); + shared = handle_amd (_SC_LEVEL3_CACHE_SIZE); ++ shared_per_thread = shared; + + level1_icache_size = handle_amd (_SC_LEVEL1_ICACHE_SIZE); + level1_icache_linesize = handle_amd (_SC_LEVEL1_ICACHE_LINESIZE); +@@ -791,8 +790,11 @@ + __cpuid (0x80000000, max_cpuid_ex, ebx, ecx, edx); + + if (shared <= 0) +- /* No shared L3 cache. All we have is the L2 cache. */ +- shared = core; ++ { ++ /* No shared L3 cache. All we have is the L2 cache. */ ++ shared = core; ++ shared_per_thread = core; ++ } + else + { + /* Figure out the number of logical threads that share L3. */ +@@ -816,7 +818,7 @@ + /* Cap usage of highest cache level to the number of + supported threads. */ + if (threads > 0) +- shared /= threads; ++ shared_per_thread /= threads; + + /* Get shared cache per ccx for Zen architectures. */ + if (cpu_features->basic.family >= 0x17) +@@ -827,12 +829,13 @@ + __cpuid_count (0x8000001D, 0x3, eax, ebx, ecx, edx); + + unsigned int threads_per_ccx = ((eax >> 14) & 0xfff) + 1; +- shared *= threads_per_ccx; ++ shared_per_thread *= threads_per_ccx; + } + else + { + /* Account for exclusive L2 and L3 caches. */ + shared += core; ++ shared_per_thread += core; + } + } + } +@@ -850,26 +853,55 @@ + cpu_features->level3_cache_linesize = level3_cache_linesize; + cpu_features->level4_cache_size = level4_cache_size; + +- /* The default setting for the non_temporal threshold is 3/4 of one +- thread's share of the chip's cache. For most Intel and AMD processors +- with an initial release date between 2017 and 2020, a thread's typical +- share of the cache is from 500 KBytes to 2 MBytes. Using the 3/4 +- threshold leaves 125 KBytes to 500 KBytes of the thread's data +- in cache after a maximum temporal copy, which will maintain +- in cache a reasonable portion of the thread's stack and other +- active data. If the threshold is set higher than one thread's +- share of the cache, it has a substantial risk of negatively +- impacting the performance of other threads running on the chip. */ +- unsigned long int non_temporal_threshold = shared * 3 / 4; ++ /* The default setting for the non_temporal threshold is 1/4 of size ++ of the chip's cache. For most Intel and AMD processors with an ++ initial release date between 2017 and 2023, a thread's typical ++ share of the cache is from 18-64MB. Using the 1/4 L3 is meant to ++ estimate the point where non-temporal stores begin out-competing ++ REP MOVSB. As well the point where the fact that non-temporal ++ stores are forced back to main memory would already occurred to the ++ majority of the lines in the copy. Note, concerns about the ++ entire L3 cache being evicted by the copy are mostly alleviated ++ by the fact that modern HW detects streaming patterns and ++ provides proper LRU hints so that the maximum thrashing ++ capped at 1/associativity. */ ++ unsigned long int non_temporal_threshold = shared / 4; ++ ++ /* If the computed non_temporal_threshold <= 3/4 * per-thread L3, we most ++ likely have incorrect/incomplete cache info in which case, default to ++ 3/4 * per-thread L3 to avoid regressions. */ ++ unsigned long int non_temporal_threshold_lowbound ++ = shared_per_thread * 3 / 4; ++ if (non_temporal_threshold < non_temporal_threshold_lowbound) ++ non_temporal_threshold = non_temporal_threshold_lowbound; ++ ++ /* If no ERMS, we use the per-thread L3 chunking. Normal cacheable stores run ++ a higher risk of actually thrashing the cache as they don't have a HW LRU ++ hint. As well, their performance in highly parallel situations is ++ noticeably worse. */ ++ if (!CPU_FEATURE_USABLE_P (cpu_features, ERMS)) ++ non_temporal_threshold = non_temporal_threshold_lowbound; ++ /* SIZE_MAX >> 4 because memmove-vec-unaligned-erms right-shifts the value of ++ 'x86_non_temporal_threshold' by `LOG_4X_MEMCPY_THRESH` (4) and it is best ++ if that operation cannot overflow. Minimum of 0x4040 (16448) because the ++ L(large_memset_4x) loops need 64-byte to cache align and enough space for ++ at least 1 iteration of 4x PAGE_SIZE unrolled loop. Both values are ++ reflected in the manual. */ ++ unsigned long int maximum_non_temporal_threshold = SIZE_MAX >> 4; ++ unsigned long int minimum_non_temporal_threshold = 0x4040; ++ if (non_temporal_threshold < minimum_non_temporal_threshold) ++ non_temporal_threshold = minimum_non_temporal_threshold; ++ else if (non_temporal_threshold > maximum_non_temporal_threshold) ++ non_temporal_threshold = maximum_non_temporal_threshold; + + #if HAVE_TUNABLES + /* NB: The REP MOVSB threshold must be greater than VEC_SIZE * 8. */ +- unsigned int minimum_rep_movsb_threshold; ++ unsigned long int minimum_rep_movsb_threshold; + #endif + /* NB: The default REP MOVSB threshold is 4096 * (VEC_SIZE / 16) for + VEC_SIZE == 64 or 32. For VEC_SIZE == 16, the default REP MOVSB + threshold is 2048 * (VEC_SIZE / 16). */ +- unsigned int rep_movsb_threshold; ++ unsigned long int rep_movsb_threshold; + if (CPU_FEATURE_USABLE_P (cpu_features, AVX512F) + && !CPU_FEATURE_PREFERRED_P (cpu_features, Prefer_No_AVX512)) + { +@@ -898,18 +930,6 @@ + if (CPU_FEATURE_USABLE_P (cpu_features, FSRM)) + rep_movsb_threshold = 2112; + +- unsigned long int rep_movsb_stop_threshold; +- /* ERMS feature is implemented from AMD Zen3 architecture and it is +- performing poorly for data above L2 cache size. Henceforth, adding +- an upper bound threshold parameter to limit the usage of Enhanced +- REP MOVSB operations and setting its value to L2 cache size. */ +- if (cpu_features->basic.kind == arch_kind_amd) +- rep_movsb_stop_threshold = core; +- /* Setting the upper bound of ERMS to the computed value of +- non-temporal threshold for architectures other than AMD. */ +- else +- rep_movsb_stop_threshold = non_temporal_threshold; +- + /* The default threshold to use Enhanced REP STOSB. */ + unsigned long int rep_stosb_threshold = 2048; + +@@ -927,8 +947,8 @@ + shared = tunable_size; + + tunable_size = TUNABLE_GET (x86_non_temporal_threshold, long int, NULL); +- /* NB: Ignore the default value 0. */ +- if (tunable_size != 0) ++ if (tunable_size > minimum_non_temporal_threshold ++ && tunable_size <= maximum_non_temporal_threshold) + non_temporal_threshold = tunable_size; + + tunable_size = TUNABLE_GET (x86_rep_movsb_threshold, long int, NULL); +@@ -944,13 +964,26 @@ + TUNABLE_SET_WITH_BOUNDS (x86_data_cache_size, data, 0, SIZE_MAX); + TUNABLE_SET_WITH_BOUNDS (x86_shared_cache_size, shared, 0, SIZE_MAX); + TUNABLE_SET_WITH_BOUNDS (x86_non_temporal_threshold, non_temporal_threshold, +- 0, SIZE_MAX); ++ minimum_non_temporal_threshold, ++ maximum_non_temporal_threshold); + TUNABLE_SET_WITH_BOUNDS (x86_rep_movsb_threshold, rep_movsb_threshold, + minimum_rep_movsb_threshold, SIZE_MAX); + TUNABLE_SET_WITH_BOUNDS (x86_rep_stosb_threshold, rep_stosb_threshold, 1, + SIZE_MAX); + #endif + ++ unsigned long int rep_movsb_stop_threshold; ++ /* ERMS feature is implemented from AMD Zen3 architecture and it is ++ performing poorly for data above L2 cache size. Henceforth, adding ++ an upper bound threshold parameter to limit the usage of Enhanced ++ REP MOVSB operations and setting its value to L2 cache size. */ ++ if (cpu_features->basic.kind == arch_kind_amd) ++ rep_movsb_stop_threshold = core; ++ /* Setting the upper bound of ERMS to the computed value of ++ non-temporal threshold for architectures other than AMD. */ ++ else ++ rep_movsb_stop_threshold = non_temporal_threshold; ++ + cpu_features->data_cache_size = data; + cpu_features->shared_cache_size = shared; + cpu_features->non_temporal_threshold = non_temporal_threshold; +diff -Nuar a/sysdeps/x86/get-isa-level.h b/sysdeps/x86/get-isa-level.h +--- a/sysdeps/x86/get-isa-level.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86/get-isa-level.h 2025-10-20 21:02:22.000000000 +0300 +@@ -47,6 +47,8 @@ + isa_level |= GNU_PROPERTY_X86_ISA_1_V2; + if (CPU_FEATURE_USABLE_P (cpu_features, AVX) + && CPU_FEATURE_USABLE_P (cpu_features, AVX2) ++ && CPU_FEATURE_USABLE_P (cpu_features, BMI1) ++ && CPU_FEATURE_USABLE_P (cpu_features, BMI2) + && CPU_FEATURE_USABLE_P (cpu_features, F16C) + && CPU_FEATURE_USABLE_P (cpu_features, FMA) + && CPU_FEATURE_USABLE_P (cpu_features, LZCNT) +diff -Nuar a/sysdeps/x86/isa-level.c b/sysdeps/x86/isa-level.c +--- a/sysdeps/x86/isa-level.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86/isa-level.c 2025-10-20 21:02:22.000000000 +0300 +@@ -47,7 +47,8 @@ + # endif + + # if ISA_V2 && defined __AVX__ && defined __AVX2__ && defined __F16C__ \ +- && defined __FMA__ && defined __LZCNT__ && defined HAVE_X86_MOVBE ++ && defined __FMA__ && defined __LZCNT__ && defined HAVE_X86_MOVBE \ ++ && defined __BMI__ && defined __BMI2__ + /* NB: ISAs in x86-64 ISA level v3 are used. */ + # define ISA_V3 GNU_PROPERTY_X86_ISA_1_V3 + # else +diff -Nuar a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile +--- a/sysdeps/x86/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86/Makefile 2025-10-20 21:02:22.000000000 +0300 +@@ -10,38 +10,53 @@ + CFLAGS-dl-get-cpu-features.os += $(rtld-early-cflags) + CFLAGS-get-cpuid-feature-leaf.o += $(no-stack-protector) + +-tests += tst-get-cpu-features tst-get-cpu-features-static \ +- tst-cpu-features-cpuinfo tst-cpu-features-cpuinfo-static \ +- tst-cpu-features-supports tst-cpu-features-supports-static +-tests-static += tst-get-cpu-features-static \ +- tst-cpu-features-cpuinfo-static \ +- tst-cpu-features-supports-static ++tests += \ ++ tst-get-cpu-features \ ++ tst-get-cpu-features-static \ ++ tst-cpu-features-cpuinfo \ ++ tst-cpu-features-cpuinfo-static \ ++ tst-cpu-features-supports \ ++ tst-cpu-features-supports-static \ ++# tests ++tests-static += \ ++ tst-get-cpu-features-static \ ++ tst-cpu-features-cpuinfo-static \ ++ tst-cpu-features-supports-static \ ++# tests-static + ifeq (yes,$(have-ifunc)) + ifeq (yes,$(have-gcc-ifunc)) + tests += \ + tst-ifunc-isa-1 \ +- tst-ifunc-isa-1-static ++ tst-ifunc-isa-1-static \ ++# tests + tests-static += \ +- tst-ifunc-isa-1-static ++ tst-ifunc-isa-1-static \ ++# tests-static + test-xfail-tst-ifunc-isa-1 = $(with-lld) + test-xfail-tst-ifunc-isa-1-static = $(with-lld) + ifneq ($(have-tunables),no) + tests += \ + tst-ifunc-isa-2 \ +- tst-ifunc-isa-2-static ++ tst-ifunc-isa-2-static \ ++# tests + tests-static += \ +- tst-ifunc-isa-2-static ++ tst-ifunc-isa-2-static \ ++# tests-static + test-xfail-tst-ifunc-isa-2 = $(with-lld) + test-xfail-tst-ifunc-isa-2-static = $(with-lld) + endif + endif + endif + ifeq (yes,$(enable-x86-isa-level)) +-tests += tst-isa-level-1 +-modules-names += tst-isa-level-mod-1-baseline \ +- tst-isa-level-mod-1-v2 \ +- tst-isa-level-mod-1-v3 \ +- tst-isa-level-mod-1-v4 \ ++tests += \ ++ tst-isa-level-1 \ ++# tests ++modules-names += \ ++ tst-isa-level-mod-1-baseline \ ++ tst-isa-level-mod-1-v2 \ ++ tst-isa-level-mod-1-v3 \ ++ tst-isa-level-mod-1-v4 \ ++# modules-names + + # X86 ISA level baseline + CFLAGS-tst-isa-level-mod-1-baseline.c += -DINCLUDE_X86_ISA_LEVEL \ +@@ -72,7 +87,9 @@ + endif + + ifeq ($(subdir),math) +-tests += tst-ldbl-nonnormal-printf ++tests += \ ++ tst-ldbl-nonnormal-printf \ ++# tests + endif # $(subdir) == math + + ifeq ($(subdir),setjmp) +@@ -80,7 +97,9 @@ + sysdep_routines += __longjmp_cancel + ifneq ($(enable-cet),no) + ifneq ($(have-tunables),no) +-tests += tst-setjmp-cet ++tests += \ ++ tst-setjmp-cet \ ++# tests + tst-setjmp-cet-ENV = GLIBC_TUNABLES=glibc.cpu.x86_ibt=on:glibc.cpu.x86_shstk=on + endif + endif +@@ -99,7 +118,9 @@ + tst-strcpy-rtm \ + tst-strlen-rtm \ + tst-strncmp-rtm \ +- tst-strrchr-rtm ++ tst-strrchr-rtm \ ++ tst-wcsncmp-rtm \ ++# tests + + CFLAGS-tst-memchr-rtm.c += -mrtm + CFLAGS-tst-memcmp-rtm.c += -mrtm +@@ -109,30 +130,56 @@ + CFLAGS-tst-strchr-rtm.c += -mrtm + CFLAGS-tst-strcpy-rtm.c += -mrtm + CFLAGS-tst-strlen-rtm.c += -mrtm +-CFLAGS-tst-strncmp-rtm.c += -mrtm ++CFLAGS-tst-strncmp-rtm.c += -mrtm -Wno-error + CFLAGS-tst-strrchr-rtm.c += -mrtm ++CFLAGS-tst-wcsncmp-rtm.c += -mrtm -Wno-error + endif + + ifneq ($(enable-cet),no) + ifeq ($(subdir),elf) + sysdep-dl-routines += dl-cet + +-tests += tst-cet-legacy-1 tst-cet-legacy-1a tst-cet-legacy-2 \ +- tst-cet-legacy-2a tst-cet-legacy-3 tst-cet-legacy-4 \ +- tst-cet-legacy-5a tst-cet-legacy-6a tst-cet-legacy-7 \ +- tst-cet-legacy-8 tst-cet-legacy-9 tst-cet-legacy-9-static \ +- tst-cet-legacy-10 tst-cet-legacy-10-static +-tests-static += tst-cet-legacy-9-static tst-cet-legacy-10-static ++tests += \ ++ tst-cet-legacy-1 \ ++ tst-cet-legacy-1a \ ++ tst-cet-legacy-2 \ ++ tst-cet-legacy-2a \ ++ tst-cet-legacy-3 \ ++ tst-cet-legacy-4 \ ++ tst-cet-legacy-5a \ ++ tst-cet-legacy-6a \ ++ tst-cet-legacy-7 \ ++ tst-cet-legacy-8 \ ++ tst-cet-legacy-9 \ ++ tst-cet-legacy-9-static \ ++ tst-cet-legacy-10 \ ++ tst-cet-legacy-10-static \ ++# tests ++tests-static += \ ++ tst-cet-legacy-9-static \ ++ tst-cet-legacy-10-static \ ++# tests-static + tst-cet-legacy-1a-ARGS = -- $(host-test-program-cmd) + ifneq (no,$(have-tunables)) +-tests += tst-cet-legacy-4a tst-cet-legacy-4b tst-cet-legacy-4c \ +- tst-cet-legacy-5b tst-cet-legacy-6b +-endif +-modules-names += tst-cet-legacy-mod-1 tst-cet-legacy-mod-2 \ +- tst-cet-legacy-mod-4 tst-cet-legacy-mod-5a \ +- tst-cet-legacy-mod-5b tst-cet-legacy-mod-5c \ +- tst-cet-legacy-mod-6a tst-cet-legacy-mod-6b \ +- tst-cet-legacy-mod-6c ++tests += \ ++ tst-cet-legacy-4a \ ++ tst-cet-legacy-4b \ ++ tst-cet-legacy-4c \ ++ tst-cet-legacy-5b \ ++ tst-cet-legacy-6b \ ++# tests ++endif ++modules-names += \ ++ tst-cet-legacy-mod-1 \ ++ tst-cet-legacy-mod-2 \ ++ tst-cet-legacy-mod-4 \ ++ tst-cet-legacy-mod-5a \ ++ tst-cet-legacy-mod-5b \ ++ tst-cet-legacy-mod-5c \ ++ tst-cet-legacy-mod-6a \ ++ tst-cet-legacy-mod-6b \ ++ tst-cet-legacy-mod-6c \ ++# modules-names + + CFLAGS-tst-cet-legacy-2.c += -fcf-protection=branch + CFLAGS-tst-cet-legacy-2a.c += -fcf-protection +@@ -242,7 +289,9 @@ + ifeq ($(subdir),posix) + tests += \ + tst-sysconf-cache-linesize \ +- tst-sysconf-cache-linesize-static ++ tst-sysconf-cache-linesize-static \ ++# tests + tests-static += \ +- tst-sysconf-cache-linesize-static ++ tst-sysconf-cache-linesize-static \ ++# tests-static + endif +diff -Nuar a/sysdeps/x86/sysdep.h b/sysdeps/x86/sysdep.h +--- a/sysdeps/x86/sysdep.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86/sysdep.h 2025-10-20 21:02:22.000000000 +0300 +@@ -111,7 +111,8 @@ + /* Local label name for asm code. */ + #ifndef L + /* ELF-like local names start with `.L'. */ +-# define L(name) .L##name ++# define LOCAL_LABEL(name) .L##name ++# define L(name) LOCAL_LABEL(name) + #endif + + #define atom_text_section .section ".text.atom", "ax" +diff -Nuar a/sysdeps/x86/tst-strncmp-rtm.c b/sysdeps/x86/tst-strncmp-rtm.c +--- a/sysdeps/x86/tst-strncmp-rtm.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86/tst-strncmp-rtm.c 2025-10-20 21:02:22.000000000 +0300 +@@ -16,20 +16,35 @@ + License along with the GNU C Library; if not, see + . */ + ++#include + #include + ++#ifdef WIDE ++# define CHAR wchar_t ++# define MEMSET wmemset ++# define STRNCMP wcsncmp ++# define TEST_NAME "wcsncmp" ++#else /* !WIDE */ ++# define CHAR char ++# define MEMSET memset ++# define STRNCMP strncmp ++# define TEST_NAME "strncmp" ++#endif /* !WIDE */ ++ ++ ++ + #define LOOP 3000 + #define STRING_SIZE 1024 +-char string1[STRING_SIZE]; +-char string2[STRING_SIZE]; ++CHAR string1[STRING_SIZE]; ++CHAR string2[STRING_SIZE]; + + __attribute__ ((noinline, noclone)) + static int + prepare (void) + { +- memset (string1, 'a', STRING_SIZE - 1); +- memset (string2, 'a', STRING_SIZE - 1); +- if (strncmp (string1, string2, STRING_SIZE) == 0) ++ MEMSET (string1, 'a', STRING_SIZE - 1); ++ MEMSET (string2, 'a', STRING_SIZE - 1); ++ if (STRNCMP (string1, string2, STRING_SIZE) == 0) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +@@ -39,7 +54,27 @@ + static int + function (void) + { +- if (strncmp (string1, string2, STRING_SIZE) == 0) ++ if (STRNCMP (string1, string2, STRING_SIZE) == 0) ++ return 0; ++ else ++ return 1; ++} ++ ++__attribute__ ((noinline, noclone)) ++static int ++function_overflow (void) ++{ ++ if (STRNCMP (string1, string2, SIZE_MAX) == 0) ++ return 0; ++ else ++ return 1; ++} ++ ++__attribute__ ((noinline, noclone)) ++static int ++function_overflow2 (void) ++{ ++ if (STRNCMP (string1, string2, SIZE_MAX >> 4) == 0) + return 0; + else + return 1; +@@ -48,5 +83,14 @@ + static int + do_test (void) + { +- return do_test_1 ("strncmp", LOOP, prepare, function); ++ int status = do_test_1 (TEST_NAME, LOOP, prepare, function); ++ if (status != EXIT_SUCCESS) ++ return status; ++ status = do_test_1 (TEST_NAME, LOOP, prepare, function_overflow); ++ if (status != EXIT_SUCCESS) ++ return status; ++ status = do_test_1 (TEST_NAME, LOOP, prepare, function_overflow2); ++ if (status != EXIT_SUCCESS) ++ return status; ++ return status; + } +diff -Nuar a/sysdeps/x86/tst-wcsncmp-rtm.c b/sysdeps/x86/tst-wcsncmp-rtm.c +--- a/sysdeps/x86/tst-wcsncmp-rtm.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86/tst-wcsncmp-rtm.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,21 @@ ++/* Test case for wcsncmp inside a transactionally executing RTM region. ++ Copyright (C) 2022 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 ++ . */ ++ ++#define WIDE 1 ++#include ++#include "tst-strncmp-rtm.c" +diff -Nuar a/sysdeps/x86/utmp-size.h b/sysdeps/x86/utmp-size.h +--- a/sysdeps/x86/utmp-size.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86/utmp-size.h 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++#define UTMP_SIZE 384 ++#define LASTLOG_SIZE 292 +diff -Nuar a/sysdeps/x86_64/bzero.S b/sysdeps/x86_64/bzero.S +--- a/sysdeps/x86_64/bzero.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/bzero.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1 +0,0 @@ +-/* Implemented in memset.S. */ +diff -Nuar a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h +--- a/sysdeps/x86_64/dl-machine.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/dl-machine.h 2025-10-20 21:02:22.000000000 +0300 +@@ -339,11 +339,13 @@ + # endif + /* Set to symbol size plus addend. */ + value = sym->st_size; ++ *reloc_addr = value + reloc->r_addend; ++ break; + # endif +- /* Fall through. */ ++ + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: +- *reloc_addr = value + reloc->r_addend; ++ *reloc_addr = value; + break; + + # ifndef RESOLVE_CONFLICT_FIND_MAP +diff -Nuar a/sysdeps/x86_64/dl-tls.c b/sysdeps/x86_64/dl-tls.c +--- a/sysdeps/x86_64/dl-tls.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/dl-tls.c 2025-10-20 21:02:22.000000000 +0300 +@@ -40,9 +40,12 @@ + { + dtv_t *dtv = THREAD_DTV (); + +- size_t gen = atomic_load_relaxed (&GL(dl_tls_generation)); +- if (__glibc_unlikely (dtv[0].counter != gen)) +- return update_get_addr (GET_ADDR_PARAM); ++ size_t gen = atomic_load_acquire (&GL(dl_tls_generation)); ++ if (__glibc_unlikely (dtv[0].counter != gen) ++ /* See comment in __tls_get_addr in elf/dl-tls.c. */ ++ && !(_dl_tls_allocate_active () ++ && GET_ADDR_MODULE < _dl_tls_initial_modid_limit)) ++ return update_get_addr (GET_ADDR_PARAM, gen); + + return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL); + } +diff -Nuar a/sysdeps/x86_64/dl-tlsdesc.S b/sysdeps/x86_64/dl-tlsdesc.S +--- a/sysdeps/x86_64/dl-tlsdesc.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/dl-tlsdesc.S 2025-10-20 21:02:22.000000000 +0300 +@@ -61,7 +61,7 @@ + _dl_tlsdesc_undefweak: + _CET_ENDBR + movq 8(%rax), %rax +- subq %fs:0, %rax ++ sub %fs:0, %RAX_LP + ret + cfi_endproc + .size _dl_tlsdesc_undefweak, .-_dl_tlsdesc_undefweak +@@ -102,7 +102,7 @@ + /* Preserve call-clobbered registers that we modify. + We need two scratch regs anyway. */ + movq %rsi, -16(%rsp) +- movq %fs:DTV_OFFSET, %rsi ++ mov %fs:DTV_OFFSET, %RSI_LP + movq %rdi, -8(%rsp) + movq TLSDESC_ARG(%rax), %rdi + movq (%rsi), %rax +@@ -116,7 +116,7 @@ + addq TLSDESC_MODOFF(%rdi), %rax + .Lret: + movq -16(%rsp), %rsi +- subq %fs:0, %rax ++ sub %fs:0, %RAX_LP + movq -8(%rsp), %rdi + ret + .Lslow: +diff -Nuar a/sysdeps/x86_64/ffsll.c b/sysdeps/x86_64/ffsll.c +--- a/sysdeps/x86_64/ffsll.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/ffsll.c 2025-10-20 21:02:22.000000000 +0300 +@@ -26,13 +26,13 @@ + ffsll (long long int x) + { + long long int cnt; +- long long int tmp; + +- asm ("bsfq %2,%0\n" /* Count low bits in X and store in %1. */ +- "cmoveq %1,%0\n" /* If number was zero, use -1 as result. */ +- : "=&r" (cnt), "=r" (tmp) : "rm" (x), "1" (-1)); ++ asm ("mov $-1,%k0\n" /* Initialize cnt to -1. */ ++ "bsf %1,%0\n" /* Count low bits in x and store in cnt. */ ++ "inc %k0\n" /* Increment cnt by 1. */ ++ : "=&r" (cnt) : "r" (x)); + +- return cnt + 1; ++ return cnt; + } + + #ifndef __ILP32__ +diff -Nuar a/sysdeps/x86_64/fpu/fraiseexcpt.c b/sysdeps/x86_64/fpu/fraiseexcpt.c +--- a/sysdeps/x86_64/fpu/fraiseexcpt.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/fpu/fraiseexcpt.c 2025-10-20 21:02:22.000000000 +0300 +@@ -33,7 +33,7 @@ + /* One example of an invalid operation is 0.0 / 0.0. */ + float f = 0.0; + +- __asm__ __volatile__ ("divss %0, %0 " : : "x" (f)); ++ __asm__ __volatile__ ("divss %0, %0 " : "+x" (f)); + (void) &f; + } + +@@ -43,7 +43,7 @@ + float f = 1.0; + float g = 0.0; + +- __asm__ __volatile__ ("divss %1, %0" : : "x" (f), "x" (g)); ++ __asm__ __volatile__ ("divss %1, %0" : "+x" (f) : "x" (g)); + (void) &f; + } + +diff -Nuar a/sysdeps/x86_64/fpu/multiarch/e_log2.c b/sysdeps/x86_64/fpu/multiarch/e_log2.c +--- a/sysdeps/x86_64/fpu/multiarch/e_log2.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/fpu/multiarch/e_log2.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,43 @@ ++/* Multiple versions of log2. ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++#include ++ ++extern double __redirect_log2 (double); ++ ++#define SYMBOL_NAME log2 ++#include "ifunc-fma.h" ++ ++libc_ifunc_redirected (__redirect_log2, __log2, IFUNC_SELECTOR ()); ++ ++#ifdef SHARED ++__hidden_ver1 (__log2, __GI___log2, __redirect_log2) ++ __attribute__ ((visibility ("hidden"))); ++ ++versioned_symbol (libm, __ieee754_log2, log2, GLIBC_2_29); ++libm_alias_double_other (__log2, log2) ++#else ++libm_alias_double (__log2, log2) ++#endif ++ ++strong_alias (__log2, __ieee754_log2) ++libm_alias_finite (__log2, __log2) ++ ++#define __log2 __log2_sse2 ++#include +diff -Nuar a/sysdeps/x86_64/fpu/multiarch/e_log2-fma.c b/sysdeps/x86_64/fpu/multiarch/e_log2-fma.c +--- a/sysdeps/x86_64/fpu/multiarch/e_log2-fma.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/fpu/multiarch/e_log2-fma.c 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,3 @@ ++#define __log2 __log2_fma ++ ++#include +diff -Nuar a/sysdeps/x86_64/fpu/multiarch/Makefile b/sysdeps/x86_64/fpu/multiarch/Makefile +--- a/sysdeps/x86_64/fpu/multiarch/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/fpu/multiarch/Makefile 2025-10-20 21:02:22.000000000 +0300 +@@ -1,30 +1,76 @@ + ifeq ($(subdir),math) +-libm-sysdep_routines += s_floor-c s_ceil-c s_floorf-c s_ceilf-c \ +- s_rint-c s_rintf-c s_nearbyint-c s_nearbyintf-c \ +- s_roundeven-c s_roundevenf-c s_trunc-c s_truncf-c +- +-libm-sysdep_routines += s_ceil-sse4_1 s_ceilf-sse4_1 s_floor-sse4_1 \ +- s_floorf-sse4_1 s_nearbyint-sse4_1 \ +- s_nearbyintf-sse4_1 s_roundeven-sse4_1 \ +- s_roundevenf-sse4_1 s_rint-sse4_1 s_rintf-sse4_1 \ +- s_trunc-sse4_1 s_truncf-sse4_1 +- +-libm-sysdep_routines += e_exp-fma e_log-fma e_pow-fma s_atan-fma \ +- e_asin-fma e_atan2-fma s_sin-fma s_tan-fma ++libm-sysdep_routines += \ ++ s_ceil-c \ ++ s_ceilf-c \ ++ s_floor-c \ ++ s_floorf-c \ ++ s_rint-c \ ++ s_rintf-c \ ++ s_nearbyint-c \ ++ s_nearbyintf-c \ ++ s_roundeven-c \ ++ s_roundevenf-c \ ++ s_trunc-c \ ++ s_truncf-c \ ++# libm-sysdep_routines ++ ++libm-sysdep_routines += \ ++ s_ceil-sse4_1 \ ++ s_ceilf-sse4_1 \ ++ s_floor-sse4_1 \ ++ s_floorf-sse4_1 \ ++ s_nearbyint-sse4_1 \ ++ s_nearbyintf-sse4_1 \ ++ s_roundeven-sse4_1 \ ++ s_roundevenf-sse4_1 \ ++ s_rint-sse4_1 \ ++ s_rintf-sse4_1 \ ++ s_trunc-sse4_1 \ ++ s_truncf-sse4_1 \ ++# libm-sysdep_routines ++ ++libm-sysdep_routines += \ ++ e_asin-fma \ ++ e_atan2-fma \ ++ e_exp-fma \ ++ e_log-fma \ ++ e_log2-fma \ ++ e_pow-fma \ ++ s_atan-fma \ ++ s_expm1-fma \ ++ s_log1p-fma \ ++ s_sin-fma \ ++ s_tan-fma \ ++# libm-sysdep_routines + + CFLAGS-e_asin-fma.c = -mfma -mavx2 + CFLAGS-e_atan2-fma.c = -mfma -mavx2 + CFLAGS-e_exp-fma.c = -mfma -mavx2 + CFLAGS-e_log-fma.c = -mfma -mavx2 ++CFLAGS-e_log2-fma.c = -mfma -mavx2 + CFLAGS-e_pow-fma.c = -mfma -mavx2 + CFLAGS-s_atan-fma.c = -mfma -mavx2 ++CFLAGS-s_expm1-fma.c = -mfma -mavx2 ++CFLAGS-s_log1p-fma.c = -mfma -mavx2 + CFLAGS-s_sin-fma.c = -mfma -mavx2 + CFLAGS-s_tan-fma.c = -mfma -mavx2 + +-libm-sysdep_routines += s_sinf-sse2 s_cosf-sse2 s_sincosf-sse2 +- +-libm-sysdep_routines += e_exp2f-fma e_expf-fma e_log2f-fma e_logf-fma \ +- e_powf-fma s_sinf-fma s_cosf-fma s_sincosf-fma ++libm-sysdep_routines += \ ++ s_cosf-sse2 \ ++ s_sincosf-sse2 \ ++ s_sinf-sse2 \ ++# libm-sysdep_routines ++ ++libm-sysdep_routines += \ ++ e_exp2f-fma \ ++ e_expf-fma \ ++ e_log2f-fma \ ++ e_logf-fma \ ++ e_powf-fma \ ++ s_cosf-fma \ ++ s_sincosf-fma \ ++ s_sinf-fma \ ++# libm-sysdep_routines + + CFLAGS-e_exp2f-fma.c = -mfma -mavx2 + CFLAGS-e_expf-fma.c = -mfma -mavx2 +@@ -35,8 +81,16 @@ + CFLAGS-s_cosf-fma.c = -mfma -mavx2 + CFLAGS-s_sincosf-fma.c = -mfma -mavx2 + +-libm-sysdep_routines += e_exp-fma4 e_log-fma4 e_pow-fma4 s_atan-fma4 \ +- e_asin-fma4 e_atan2-fma4 s_sin-fma4 s_tan-fma4 ++libm-sysdep_routines += \ ++ e_exp-fma4 \ ++ e_log-fma4 \ ++ e_pow-fma4 \ ++ e_asin-fma4 \ ++ s_atan-fma4 \ ++ e_atan2-fma4 \ ++ s_sin-fma4 \ ++ s_tan-fma4 \ ++# libm-sysdep_routines + + CFLAGS-e_asin-fma4.c = -mfma4 + CFLAGS-e_atan2-fma4.c = -mfma4 +@@ -47,8 +101,14 @@ + CFLAGS-s_sin-fma4.c = -mfma4 + CFLAGS-s_tan-fma4.c = -mfma4 + +-libm-sysdep_routines += e_exp-avx e_log-avx s_atan-avx \ +- e_atan2-avx s_sin-avx s_tan-avx ++libm-sysdep_routines += \ ++ e_exp-avx \ ++ e_log-avx \ ++ s_atan-avx \ ++ e_atan2-avx \ ++ s_sin-avx \ ++ s_tan-avx \ ++# libm-sysdep_routines + + CFLAGS-e_atan2-avx.c = -msse2avx -DSSE2AVX + CFLAGS-e_exp-avx.c = -msse2avx -DSSE2AVX +diff -Nuar a/sysdeps/x86_64/fpu/multiarch/s_expm1.c b/sysdeps/x86_64/fpu/multiarch/s_expm1.c +--- a/sysdeps/x86_64/fpu/multiarch/s_expm1.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/fpu/multiarch/s_expm1.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,36 @@ ++/* Multiple versions of expm1. ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++ ++extern double __redirect_expm1 (double); ++ ++#define SYMBOL_NAME expm1 ++#include "ifunc-fma.h" ++ ++libc_ifunc_redirected (__redirect_expm1, __expm1, IFUNC_SELECTOR ()); ++libm_alias_double (__expm1, expm1) ++ ++#define __expm1 __expm1_sse2 ++ ++/* NB: __expm1 may be expanded to __expm1_sse2 in the following ++ prototypes. */ ++extern long double __expm1l (long double); ++extern long double __expm1f128 (long double); ++ ++#include +diff -Nuar a/sysdeps/x86_64/fpu/multiarch/s_expm1-fma.c b/sysdeps/x86_64/fpu/multiarch/s_expm1-fma.c +--- a/sysdeps/x86_64/fpu/multiarch/s_expm1-fma.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/fpu/multiarch/s_expm1-fma.c 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,10 @@ ++#define __expm1 __expm1_fma ++ ++/* NB: __expm1 may be expanded to __expm1_fma in the following ++ prototypes. */ ++extern long double __expm1l (long double); ++extern long double __expm1f128 (long double); ++ ++#define SECTION __attribute__ ((section (".text.fma"))) ++ ++#include +diff -Nuar a/sysdeps/x86_64/fpu/multiarch/s_log1p.c b/sysdeps/x86_64/fpu/multiarch/s_log1p.c +--- a/sysdeps/x86_64/fpu/multiarch/s_log1p.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/fpu/multiarch/s_log1p.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,29 @@ ++/* Multiple versions of log1p. ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++ ++extern double __redirect_log1p (double); ++ ++#define SYMBOL_NAME log1p ++#include "ifunc-fma.h" ++ ++libc_ifunc_redirected (__redirect_log1p, __log1p, IFUNC_SELECTOR ()); ++ ++#define __log1p __log1p_sse2 ++#include +diff -Nuar a/sysdeps/x86_64/fpu/multiarch/s_log1p-fma.c b/sysdeps/x86_64/fpu/multiarch/s_log1p-fma.c +--- a/sysdeps/x86_64/fpu/multiarch/s_log1p-fma.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/fpu/multiarch/s_log1p-fma.c 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,4 @@ ++#define __log1p __log1p_fma ++#define SECTION __attribute__ ((section (".text.fma"))) ++ ++#include +diff -Nuar a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile +--- a/sysdeps/x86_64/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/Makefile 2025-10-20 21:02:22.000000000 +0300 +@@ -181,6 +181,15 @@ + + tests-internal += tst-x86-64-tls-1 + ++tests-special += $(objpfx)check-dt-x86-64-plt.out ++ ++$(objpfx)check-dt-x86-64-plt.out: $(common-objpfx)libc.so ++ LC_ALL=C $(READELF) -V -W $< \ ++ | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ ++ | grep GLIBC_ABI_DT_X86_64_PLT > $@; \ ++ $(evaluate-test) ++generated += check-dt-x86-64-plt.out ++ + endif # $(subdir) == elf + + ifeq ($(subdir),csu) +diff -Nuar a/sysdeps/x86_64/memcmpeq.S b/sysdeps/x86_64/memcmpeq.S +--- a/sysdeps/x86_64/memcmpeq.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/memcmpeq.S 2025-10-20 21:02:22.000000000 +0300 +@@ -16,6 +16,6 @@ + License along with the GNU C Library; if not, see + . */ + +-#define memcmp __memcmpeq ++#define MEMCMP __memcmpeq + #define USE_AS_MEMCMPEQ 1 + #include "multiarch/memcmp-sse2.S" +diff -Nuar a/sysdeps/x86_64/memcmp.S b/sysdeps/x86_64/memcmp.S +--- a/sysdeps/x86_64/memcmp.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/memcmp.S 2025-10-20 21:02:22.000000000 +0300 +@@ -18,395 +18,571 @@ + + #include + +- .text +-ENTRY (memcmp) +-#ifdef __ILP32__ +- /* Clear the upper 32 bits. */ +- movl %edx, %edx ++#ifdef USE_AS_WMEMCMP ++# define PCMPEQ pcmpeqd ++# define CHAR_SIZE 4 ++# define SIZE_OFFSET (0) ++#else ++# define PCMPEQ pcmpeqb ++# define CHAR_SIZE 1 + #endif +- test %RDX_LP, %RDX_LP +- jz L(finz) +- cmpq $1, %rdx +- jbe L(finr1b) +- subq %rdi, %rsi +- movq %rdx, %r10 +- cmpq $32, %r10 +- jae L(gt32) +- /* Handle small chunks and last block of less than 32 bytes. */ +-L(small): +- testq $1, %r10 +- jz L(s2b) +- movzbl (%rdi), %eax +- movzbl (%rdi, %rsi), %edx +- subq $1, %r10 +- je L(finz1) +- addq $1, %rdi +- subl %edx, %eax +- jnz L(exit) +-L(s2b): +- testq $2, %r10 +- jz L(s4b) +- movzwl (%rdi), %eax +- movzwl (%rdi, %rsi), %edx +- subq $2, %r10 ++ + #ifdef USE_AS_MEMCMPEQ +- je L(finz1) ++# define SIZE_OFFSET (0) ++# define CHECK_CMP(x, y) subl x, y + #else +- je L(fin2_7) ++# ifndef SIZE_OFFSET ++# define SIZE_OFFSET (CHAR_PER_VEC * 2) ++# endif ++# define CHECK_CMP(x, y) cmpl x, y + #endif +- addq $2, %rdi +- cmpl %edx, %eax +-#ifdef USE_AS_MEMCMPEQ +- jnz L(neq_early) ++ ++#define VEC_SIZE 16 ++#define CHAR_PER_VEC (VEC_SIZE / CHAR_SIZE) ++ ++#ifndef MEMCMP ++# define MEMCMP memcmp ++#endif ++ ++ .text ++ENTRY(MEMCMP) ++# ifdef __ILP32__ ++ /* Clear the upper 32 bits. */ ++ movl %edx, %edx ++# endif ++#ifdef USE_AS_WMEMCMP ++ /* Use 0xffff to test for mismatches on pmovmskb bitmask. Store ++ in ecx for code size. This is preferable to using `incw` as ++ it avoids partial register stalls on older hardware (pre ++ SnB). */ ++ movl $0xffff, %ecx ++#endif ++ cmpq $CHAR_PER_VEC, %rdx ++ ja L(more_1x_vec) ++ ++#ifdef USE_AS_WMEMCMP ++ /* saves a byte of code keeping the fall through path n = [2, 4] ++ in the initial cache line. */ ++ decl %edx ++ jle L(cmp_0_1) ++ ++ movq (%rsi), %xmm0 ++ movq (%rdi), %xmm1 ++ PCMPEQ %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ subl %ecx, %eax ++ jnz L(ret_nonzero_vec_start_0) ++ ++ movq -4(%rsi, %rdx, CHAR_SIZE), %xmm0 ++ movq -4(%rdi, %rdx, CHAR_SIZE), %xmm1 ++ PCMPEQ %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ subl %ecx, %eax ++ jnz L(ret_nonzero_vec_end_0_adj) + #else +- jnz L(fin2_7) ++ cmpl $8, %edx ++ ja L(cmp_9_16) ++ ++ cmpl $4, %edx ++ jb L(cmp_0_3) ++ ++# ifdef USE_AS_MEMCMPEQ ++ movl (%rsi), %eax ++ subl (%rdi), %eax ++ ++ movl -4(%rsi, %rdx), %esi ++ subl -4(%rdi, %rdx), %esi ++ ++ orl %esi, %eax ++ ret ++# else ++ /* Combine comparisons for lo and hi 4-byte comparisons. */ ++ movl -4(%rsi, %rdx), %ecx ++ movl -4(%rdi, %rdx), %eax ++ shlq $32, %rcx ++ shlq $32, %rax ++ movl (%rsi), %esi ++ movl (%rdi), %edi ++ orq %rsi, %rcx ++ orq %rdi, %rax ++ /* Only compute proper return if not-equal. */ ++ cmpq %rcx, %rax ++ jnz L(ret_nonzero) ++ xorl %eax, %eax ++ ret ++# endif ++ ++ .p2align 4,, 10 ++L(cmp_9_16): ++# ifdef USE_AS_MEMCMPEQ ++ movq (%rsi), %rax ++ subq (%rdi), %rax ++ ++ movq -8(%rsi, %rdx), %rcx ++ subq -8(%rdi, %rdx), %rcx ++ orq %rcx, %rax ++ /* Convert 64 bit -> 32 bit boolean (we should have made the ABI ++ return long). */ ++ setnz %cl ++ movzbl %cl, %eax ++# else ++ movq (%rsi), %rcx ++ movq (%rdi), %rax ++ /* Only compute proper return if not-equal. */ ++ cmpq %rcx, %rax ++ jnz L(ret_nonzero) ++ ++ movq -8(%rsi, %rdx, CHAR_SIZE), %rcx ++ movq -8(%rdi, %rdx, CHAR_SIZE), %rax ++ /* Only compute proper return if not-equal. */ ++ cmpq %rcx, %rax ++ jnz L(ret_nonzero) ++ xorl %eax, %eax ++# endif + #endif +-L(s4b): +- testq $4, %r10 +- jz L(s8b) +- movl (%rdi), %eax +- movl (%rdi, %rsi), %edx +- subq $4, %r10 +-#ifdef USE_AS_MEMCMPEQ +- je L(finz1) ++ ret ++ ++ .p2align 4,, 8 ++L(cmp_0_1): ++ /* Flag set by earlier comparison against 1. */ ++ jne L(cmp_0_0) ++#ifdef USE_AS_WMEMCMP ++ movl (%rdi), %ecx ++ xorl %edx, %edx ++ cmpl (%rsi), %ecx ++ je L(cmp_0_0) ++ setg %dl ++ leal -1(%rdx, %rdx), %eax + #else +- je L(fin2_7) ++ movzbl (%rdi), %eax ++ movzbl (%rsi), %ecx ++ subl %ecx, %eax + #endif +- addq $4, %rdi +- cmpl %edx, %eax +-#ifdef USE_AS_MEMCMPEQ +- jnz L(neq_early) ++ ret ++ ++ /* Fits in aligning bytes. */ ++L(cmp_0_0): ++ xorl %eax, %eax ++ ret ++ ++#ifdef USE_AS_WMEMCMP ++ .p2align 4 ++L(ret_nonzero_vec_start_0): ++ bsfl %eax, %eax ++ movl (%rdi, %rax), %ecx ++ xorl %edx, %edx ++ cmpl (%rsi, %rax), %ecx ++ /* NB: no partial register stall here because xorl zero idiom ++ above. */ ++ setg %dl ++ leal -1(%rdx, %rdx), %eax ++ ret + #else +- jnz L(fin2_7) ++ ++# ifndef USE_AS_MEMCMPEQ ++ .p2align 4,, 14 ++L(ret_nonzero): ++ /* Need to bswap to get proper return without branch. */ ++ bswapq %rcx ++ bswapq %rax ++ subq %rcx, %rax ++ sbbl %eax, %eax ++ orl $1, %eax ++ ret ++# endif ++ ++ .p2align 4 ++L(cmp_0_3): ++# ifdef USE_AS_MEMCMPEQ ++ /* No reason to add to dependency chain on rdx. Saving a the ++ bytes here doesn't change number of fetch blocks. */ ++ cmpl $1, %edx ++ jbe L(cmp_0_1) ++# else ++ /* We need the code size to prevent taking an extra fetch block. ++ */ ++ decl %edx ++ jle L(cmp_0_1) ++# endif ++ movzwl (%rsi), %ecx ++ movzwl (%rdi), %eax ++ ++# ifdef USE_AS_MEMCMPEQ ++ subl %ecx, %eax ++ ++ movzbl -1(%rsi, %rdx), %esi ++ movzbl -1(%rdi, %rdx), %edi ++ subl %edi, %esi ++ orl %esi, %eax ++# else ++ bswapl %ecx ++ bswapl %eax ++ ++ /* Implicit right shift by one. We just need to displace the ++ sign bits. */ ++ shrl %ecx ++ shrl %eax ++ ++ /* Eat a partial register stall here. Saves code stopping ++ L(cmp_0_3) from bleeding into the next fetch block and saves ++ an ALU. */ ++ movb (%rsi, %rdx), %cl ++ movzbl (%rdi, %rdx), %edi ++ orl %edi, %eax ++ subl %ecx, %eax ++# endif ++ ret + #endif +-L(s8b): +- testq $8, %r10 +- jz L(s16b) +- movq (%rdi), %rax +- movq (%rdi, %rsi), %rdx +- subq $8, %r10 +-#ifdef USE_AS_MEMCMPEQ +- je L(sub_return8) ++ ++ .p2align 5 ++L(more_1x_vec): ++#ifndef USE_AS_WMEMCMP ++ /* Use 0xffff to test for mismatches on pmovmskb bitmask. Store ++ in ecx for code size. This is preferable to using `incw` as ++ it avoids partial register stalls on older hardware (pre ++ SnB). */ ++ movl $0xffff, %ecx ++#endif ++ movups (%rsi), %xmm0 ++ movups (%rdi), %xmm1 ++ PCMPEQ %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ subl %ecx, %eax ++ jnz L(ret_nonzero_vec_start_0) ++#if SIZE_OFFSET == 0 ++ cmpq $(CHAR_PER_VEC * 2), %rdx + #else +- je L(fin2_7) ++ /* Offset rdx. Saves just enough code size to keep the ++ L(last_2x_vec) case and the non-zero return in a single ++ cache line. */ ++ subq $(CHAR_PER_VEC * 2), %rdx ++#endif ++ ja L(more_2x_vec) ++ ++ movups (VEC_SIZE * -1 + SIZE_OFFSET)(%rsi, %rdx, CHAR_SIZE), %xmm0 ++ movups (VEC_SIZE * -1 + SIZE_OFFSET)(%rdi, %rdx, CHAR_SIZE), %xmm1 ++ PCMPEQ %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ subl %ecx, %eax ++#ifndef USE_AS_MEMCMPEQ ++ /* Don't use `incw ax` as machines this code runs on are liable ++ to have partial register stall. */ ++ jnz L(ret_nonzero_vec_end_0) ++#else ++ /* Various return targets for memcmpeq. Will always be hot in ++ Icache and get short encoding. */ ++L(ret_nonzero_vec_start_1): ++L(ret_nonzero_vec_start_0): ++L(ret_nonzero_vec_end_0): + #endif +- addq $8, %rdi +- cmpq %rdx, %rax +-#ifdef USE_AS_MEMCMPEQ +- jnz L(neq_early) ++ ret ++ ++#ifndef USE_AS_MEMCMPEQ ++# ifdef USE_AS_WMEMCMP ++ .p2align 4 ++L(ret_nonzero_vec_end_0_adj): ++ addl $3, %edx ++# else ++ .p2align 4,, 8 ++# endif ++L(ret_nonzero_vec_end_0): ++ bsfl %eax, %eax ++# ifdef USE_AS_WMEMCMP ++ leal (%rax, %rdx, CHAR_SIZE), %eax ++ movl (VEC_SIZE * -1 + SIZE_OFFSET)(%rdi, %rax), %ecx ++ xorl %edx, %edx ++ cmpl (VEC_SIZE * -1 + SIZE_OFFSET)(%rsi, %rax), %ecx ++ /* NB: no partial register stall here because xorl zero idiom ++ above. */ ++ setg %dl ++ leal -1(%rdx, %rdx), %eax ++# else ++ /* Use `addq` instead of `addl` here so that even if `rax` + `rdx` ++ is negative value of the sum will be usable as a 64-bit offset ++ (negative 32-bit numbers zero-extend to a large and often ++ out-of-bounds 64-bit offsets). Note that `rax` + `rdx` >= 0 is ++ an invariant when `memcmp` is used correctly, but if the input ++ strings `rsi`/`rdi` are concurrently modified as the function ++ runs (there is a Data-Race) it is possible for `rax` + `rdx` to ++ be negative. Given that there is virtually no extra to cost ++ using `addq` instead of `addl` we may as well protect the ++ data-race case. */ ++ addq %rdx, %rax ++ movzbl (VEC_SIZE * -1 + SIZE_OFFSET)(%rsi, %rax), %ecx ++ movzbl (VEC_SIZE * -1 + SIZE_OFFSET)(%rdi, %rax), %eax ++ subl %ecx, %eax ++# endif ++ ret ++# ifndef USE_AS_WMEMCMP ++ .p2align 4,, 10 ++L(ret_nonzero_vec_start_0): ++ bsfl %eax, %eax ++ movzbl (%rsi, %rax), %ecx ++ movzbl (%rdi, %rax), %eax ++ subl %ecx, %eax ++ ret ++# endif + #else +- jnz L(fin2_7) + #endif +-L(s16b): +- movdqu (%rdi), %xmm1 +- movdqu (%rdi, %rsi), %xmm0 +- pcmpeqb %xmm0, %xmm1 ++ ++ .p2align 5 ++L(more_2x_vec): ++ movups (VEC_SIZE * 1)(%rsi), %xmm0 ++ movups (VEC_SIZE * 1)(%rdi), %xmm1 ++ PCMPEQ %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ subl %ecx, %eax ++ jnz L(ret_nonzero_vec_start_1) ++ ++ cmpq $(CHAR_PER_VEC * 4 - SIZE_OFFSET), %rdx ++ jbe L(last_2x_vec) ++ ++ cmpq $(CHAR_PER_VEC * 8 - SIZE_OFFSET), %rdx ++ ja L(more_8x_vec) ++ ++ /* Do comparisons for [65, 96] and [97, 128] 2x VEC at a time. ++ This can harm performance if non-zero return in [65, 80] or ++ [97, 112] but helps performance otherwise. Generally zero- ++ return is hotter. */ ++ movups (VEC_SIZE * 2)(%rsi), %xmm0 ++ movups (VEC_SIZE * 2)(%rdi), %xmm1 ++ PCMPEQ %xmm0, %xmm1 ++ movups (VEC_SIZE * 3)(%rsi), %xmm2 ++ movups (VEC_SIZE * 3)(%rdi), %xmm3 ++ PCMPEQ %xmm2, %xmm3 ++ pand %xmm1, %xmm3 ++ ++ pmovmskb %xmm3, %eax ++ CHECK_CMP (%ecx, %eax) ++ jnz L(ret_nonzero_vec_start_2_3) ++ ++ cmpl $(CHAR_PER_VEC * 6 - SIZE_OFFSET), %edx ++ jbe L(last_2x_vec) ++ ++ movups (VEC_SIZE * 4)(%rsi), %xmm0 ++ movups (VEC_SIZE * 4)(%rdi), %xmm1 ++ PCMPEQ %xmm0, %xmm1 ++ movups (VEC_SIZE * 5)(%rsi), %xmm2 ++ movups (VEC_SIZE * 5)(%rdi), %xmm3 ++ PCMPEQ %xmm2, %xmm3 ++ pand %xmm1, %xmm3 ++ ++ pmovmskb %xmm3, %eax ++ CHECK_CMP (%ecx, %eax) + #ifdef USE_AS_MEMCMPEQ +- pmovmskb %xmm1, %eax +- subl $0xffff, %eax ++ jz L(last_2x_vec) + ret + #else +- pmovmskb %xmm1, %edx +- xorl %eax, %eax +- subl $0xffff, %edx +- jz L(finz) +- bsfl %edx, %ecx +- leaq (%rdi, %rcx), %rcx +- movzbl (%rcx), %eax +- movzbl (%rsi, %rcx), %edx +- jmp L(finz1) ++ jnz L(ret_nonzero_vec_start_4_5) + #endif +- .p2align 4,, 4 +-L(finr1b): +- movzbl (%rdi), %eax +- movzbl (%rsi), %edx +-L(finz1): +- subl %edx, %eax +-L(exit): ++ .p2align 4 ++L(last_2x_vec): ++ movups (VEC_SIZE * -2 + SIZE_OFFSET)(%rsi, %rdx, CHAR_SIZE), %xmm0 ++ movups (VEC_SIZE * -2 + SIZE_OFFSET)(%rdi, %rdx, CHAR_SIZE), %xmm1 ++ PCMPEQ %xmm0, %xmm1 ++ movups (VEC_SIZE * -1 + SIZE_OFFSET)(%rsi, %rdx, CHAR_SIZE), %xmm2 ++ movups (VEC_SIZE * -1 + SIZE_OFFSET)(%rdi, %rdx, CHAR_SIZE), %xmm3 ++ PCMPEQ %xmm2, %xmm3 ++ pand %xmm1, %xmm3 ++ pmovmskb %xmm3, %eax ++ subl %ecx, %eax ++#ifdef USE_AS_MEMCMPEQ ++ /* Various return targets for memcmpeq. Will always be hot in ++ Icache and get short encoding. */ ++L(ret_nonzero_vec_start_2_3): ++L(ret_nonzero_vec_start_4_5): + ret +-#ifdef USE_AS_MEMCMPEQ +- .p2align 4,, 4 +-L(sub_return8): +- subq %rdx, %rax +- movl %eax, %edx +- shrq $32, %rax +- orl %edx, %eax +- ret +-#else +- .p2align 4,, 4 +-L(fin2_7): +- cmpq %rdx, %rax +- jz L(finz) +- movq %rax, %r11 +- subq %rdx, %r11 +- bsfq %r11, %rcx +- sarq $3, %rcx +- salq $3, %rcx +- sarq %cl, %rax +- movzbl %al, %eax +- sarq %cl, %rdx +- movzbl %dl, %edx +- subl %edx, %eax ++#else ++ jnz L(ret_nonzero_vec_end_1) + ret +-#endif +- .p2align 4,, 4 +-L(finz): +- xorl %eax, %eax ++ ++ .p2align 4,, 8 ++L(ret_nonzero_vec_end_1): ++ pmovmskb %xmm1, %ecx ++ /* High 16 bits of eax guranteed to be all ones. Rotate them in ++ to we can do `or + not` with just `xor`. */ ++ rorl $16, %eax ++ xorl %ecx, %eax ++ /* Partial register stall. */ ++ ++ bsfl %eax, %eax ++# ifdef USE_AS_WMEMCMP ++ leal (%rax, %rdx, CHAR_SIZE), %eax ++ movl (VEC_SIZE * -2 + SIZE_OFFSET)(%rdi, %rax), %ecx ++ xorl %edx, %edx ++ cmpl (VEC_SIZE * -2 + SIZE_OFFSET)(%rsi, %rax), %ecx ++ /* NB: no partial register stall here because xorl zero idiom ++ above. */ ++ setg %dl ++ leal -1(%rdx, %rdx), %eax ++# else ++ addl %edx, %eax ++ movzbl (VEC_SIZE * -2 + SIZE_OFFSET)(%rsi, %rax), %ecx ++ movzbl (VEC_SIZE * -2 + SIZE_OFFSET)(%rdi, %rax), %eax ++ subl %ecx, %eax ++# endif + ret +-#ifdef USE_AS_MEMCMPEQ +- .p2align 4,, 4 +-L(neq_early): +- movl $1, %eax +- ret +-#endif +- /* For blocks bigger than 32 bytes +- 1. Advance one of the addr pointer to be 16B aligned. +- 2. Treat the case of both addr pointers aligned to 16B +- separately to avoid movdqu. +- 3. Handle any blocks of greater than 64 consecutive bytes with +- unrolling to reduce branches. +- 4. At least one addr pointer is 16B aligned, use memory version +- of pcmbeqb. +- */ +- .p2align 4,, 4 +-L(gt32): +- movq %rdx, %r11 +- addq %rdi, %r11 +- movq %rdi, %r8 +- +- andq $15, %r8 +- jz L(16am) +- /* Both pointers may be misaligned. */ +- movdqu (%rdi), %xmm1 +- movdqu (%rdi, %rsi), %xmm0 +- pcmpeqb %xmm0, %xmm1 +- pmovmskb %xmm1, %edx +- subl $0xffff, %edx +- jnz L(neq) +- neg %r8 +- leaq 16(%rdi, %r8), %rdi +-L(16am): +- /* Handle two 16B aligned pointers separately. */ +- testq $15, %rsi +- jz L(ATR) +- testq $16, %rdi +- jz L(A32) +- movdqu (%rdi, %rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +-L(A32): +- movq %r11, %r10 +- andq $-32, %r10 +- cmpq %r10, %rdi +- jae L(mt16) +- /* Pre-unroll to be ready for unrolled 64B loop. */ +- testq $32, %rdi +- jz L(A64) +- movdqu (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqu (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +-L(A64): +- movq %r11, %r10 +- andq $-64, %r10 +- cmpq %r10, %rdi +- jae L(mt32) +- +-L(A64main): +- movdqu (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqu (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqu (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqu (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- cmpq %rdi, %r10 +- jne L(A64main) +- +-L(mt32): +- movq %r11, %r10 +- andq $-32, %r10 +- cmpq %r10, %rdi +- jae L(mt16) +- +-L(A32main): +- movdqu (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqu (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- cmpq %rdi, %r10 +- jne L(A32main) +-L(mt16): +- subq %rdi, %r11 +- je L(finz) +- movq %r11, %r10 +- jmp L(small) + +- .p2align 4,, 4 +-L(neq): +-#ifdef USE_AS_MEMCMPEQ +- movl $1, %eax +- ret +-#else +- bsfl %edx, %ecx +- movzbl (%rdi, %rcx), %eax +- addq %rdi, %rsi +- movzbl (%rsi,%rcx), %edx +- jmp L(finz1) +-#endif +- +- .p2align 4,, 4 +-L(ATR): +- movq %r11, %r10 +- andq $-32, %r10 +- cmpq %r10, %rdi +- jae L(mt16) +- testq $16, %rdi +- jz L(ATR32) +- +- movdqa (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- cmpq %rdi, %r10 +- je L(mt16) +- +-L(ATR32): +- movq %r11, %r10 +- andq $-64, %r10 +- testq $32, %rdi +- jz L(ATR64) +- +- movdqa (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqa (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +-L(ATR64): +- cmpq %rdi, %r10 +- je L(mt32) +- +-L(ATR64main): +- movdqa (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqa (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqa (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqa (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- cmpq %rdi, %r10 +- jne L(ATR64main) +- +- movq %r11, %r10 +- andq $-32, %r10 +- cmpq %r10, %rdi +- jae L(mt16) +- +-L(ATR32res): +- movdqa (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- movdqa (%rdi,%rsi), %xmm0 +- pcmpeqb (%rdi), %xmm0 +- pmovmskb %xmm0, %edx +- subl $0xffff, %edx +- jnz L(neq) +- addq $16, %rdi +- +- cmpq %r10, %rdi +- jne L(ATR32res) +- +- subq %rdi, %r11 +- je L(finz) +- movq %r11, %r10 +- jmp L(small) +- /* Align to 16byte to improve instruction fetch. */ +- .p2align 4,, 4 +-END(memcmp) ++ .p2align 4 ++L(ret_nonzero_vec_start_4_5): ++ pmovmskb %xmm1, %edx ++ sall $16, %eax ++ leal 1(%rax, %rdx), %eax ++ bsfl %eax, %eax ++# ifdef USE_AS_WMEMCMP ++ movl (VEC_SIZE * 4)(%rdi, %rax), %ecx ++ xorl %edx, %edx ++ cmpl (VEC_SIZE * 4)(%rsi, %rax), %ecx ++ /* NB: no partial register stall here because xorl zero idiom ++ above. */ ++ setg %dl ++ leal -1(%rdx, %rdx), %eax ++# else ++ movzbl (VEC_SIZE * 4)(%rsi, %rax), %ecx ++ movzbl (VEC_SIZE * 4)(%rdi, %rax), %eax ++ subl %ecx, %eax ++# endif ++ ret ++ ++ .p2align 4,, 8 ++L(ret_nonzero_vec_start_1): ++ bsfl %eax, %eax ++# ifdef USE_AS_WMEMCMP ++ movl (VEC_SIZE * 1)(%rdi, %rax), %ecx ++ xorl %edx, %edx ++ cmpl (VEC_SIZE * 1)(%rsi, %rax), %ecx ++ /* NB: no partial register stall here because xorl zero idiom ++ above. */ ++ setg %dl ++ leal -1(%rdx, %rdx), %eax ++# else ++ movzbl (VEC_SIZE * 1)(%rsi, %rax), %ecx ++ movzbl (VEC_SIZE * 1)(%rdi, %rax), %eax ++ subl %ecx, %eax ++# endif ++ ret ++#endif + ++ .p2align 4 ++L(more_8x_vec): ++ subq %rdi, %rsi ++ leaq (VEC_SIZE * -6 + SIZE_OFFSET)(%rdi, %rdx, CHAR_SIZE), %rdx ++ andq $(VEC_SIZE * -1), %rdi ++ addq %rdi, %rsi ++ .p2align 4 ++L(loop_4x): ++ movups (VEC_SIZE * 2)(%rsi), %xmm0 ++ movups (VEC_SIZE * 3)(%rsi), %xmm1 ++ ++ PCMPEQ (VEC_SIZE * 2)(%rdi), %xmm0 ++ PCMPEQ (VEC_SIZE * 3)(%rdi), %xmm1 ++ ++ movups (VEC_SIZE * 4)(%rsi), %xmm2 ++ movups (VEC_SIZE * 5)(%rsi), %xmm3 ++ ++ PCMPEQ (VEC_SIZE * 4)(%rdi), %xmm2 ++ PCMPEQ (VEC_SIZE * 5)(%rdi), %xmm3 ++ ++ pand %xmm0, %xmm1 ++ pand %xmm2, %xmm3 ++ pand %xmm1, %xmm3 ++ ++ pmovmskb %xmm3, %eax ++ subl %ecx, %eax ++ jnz L(ret_nonzero_loop) ++ ++ addq $(VEC_SIZE * 4), %rdi ++ addq $(VEC_SIZE * 4), %rsi ++ cmpq %rdi, %rdx ++ ja L(loop_4x) ++ /* Get remaining length in edx. */ ++ subl %edi, %edx ++ /* Restore offset so we can reuse L(last_2x_vec). */ ++ addl $(VEC_SIZE * 6 - SIZE_OFFSET), %edx ++#ifdef USE_AS_WMEMCMP ++ shrl $2, %edx ++#endif ++ cmpl $(CHAR_PER_VEC * 4 - SIZE_OFFSET), %edx ++ jbe L(last_2x_vec) ++ ++ ++ movups (VEC_SIZE * 2)(%rsi), %xmm0 ++ movups (VEC_SIZE * 2)(%rdi), %xmm1 ++ PCMPEQ %xmm0, %xmm1 ++ movups (VEC_SIZE * 3)(%rsi), %xmm2 ++ movups (VEC_SIZE * 3)(%rdi), %xmm3 ++ PCMPEQ %xmm2, %xmm3 ++ pand %xmm1, %xmm3 ++ ++ pmovmskb %xmm3, %eax ++ CHECK_CMP (%ecx, %eax) ++ jz L(last_2x_vec) + #ifdef USE_AS_MEMCMPEQ +-libc_hidden_def (memcmp) ++L(ret_nonzero_loop): ++ ret + #else +-# undef bcmp +-weak_alias (memcmp, bcmp) +-libc_hidden_builtin_def (memcmp) ++ ++ .p2align 4 ++L(ret_nonzero_vec_start_2_3): ++ pmovmskb %xmm1, %edx ++ sall $16, %eax ++ leal 1(%rax, %rdx), %eax ++ ++ bsfl %eax, %eax ++# ifdef USE_AS_WMEMCMP ++ movl (VEC_SIZE * 2)(%rdi, %rax), %ecx ++ xorl %edx, %edx ++ cmpl (VEC_SIZE * 2)(%rsi, %rax), %ecx ++ /* NB: no partial register stall here because xorl zero idiom ++ above. */ ++ setg %dl ++ leal -1(%rdx, %rdx), %eax ++# else ++ movzbl (VEC_SIZE * 2)(%rsi, %rax), %ecx ++ movzbl (VEC_SIZE * 2)(%rdi, %rax), %eax ++ subl %ecx, %eax ++# endif ++ ret ++ ++ .p2align 4 ++L(ret_nonzero_loop): ++ pmovmskb %xmm0, %ecx ++ pmovmskb %xmm1, %edx ++ sall $(VEC_SIZE * 1), %edx ++ leal 1(%rcx, %rdx), %edx ++ pmovmskb %xmm2, %ecx ++ /* High 16 bits of eax guranteed to be all ones. Rotate them in ++ to we can do `or + not` with just `xor`. */ ++ rorl $16, %eax ++ xorl %ecx, %eax ++ ++ salq $32, %rax ++ orq %rdx, %rax ++ ++ bsfq %rax, %rax ++# ifdef USE_AS_WMEMCMP ++ movl (VEC_SIZE * 2)(%rdi, %rax), %ecx ++ xorl %edx, %edx ++ cmpl (VEC_SIZE * 2)(%rsi, %rax), %ecx ++ /* NB: no partial register stall here because xorl zero idiom ++ above. */ ++ setg %dl ++ leal -1(%rdx, %rdx), %eax ++# else ++ movzbl (VEC_SIZE * 2)(%rsi, %rax), %ecx ++ movzbl (VEC_SIZE * 2)(%rdi, %rax), %eax ++ subl %ecx, %eax ++# endif ++ ret ++#endif ++END(MEMCMP) ++ ++#ifndef USE_AS_WMEMCMP ++# ifdef USE_AS_MEMCMPEQ ++libc_hidden_def (MEMCMP) ++# else ++# undef bcmp ++weak_alias (MEMCMP, bcmp) ++libc_hidden_builtin_def (MEMCMP) ++# endif + #endif +diff -Nuar a/sysdeps/x86_64/memrchr.S b/sysdeps/x86_64/memrchr.S +--- a/sysdeps/x86_64/memrchr.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/memrchr.S 2025-10-20 21:02:22.000000000 +0300 +@@ -18,362 +18,333 @@ + . */ + + #include ++#define VEC_SIZE 16 ++#define PAGE_SIZE 4096 + + .text +-ENTRY (__memrchr) +- movd %esi, %xmm1 +- +- sub $16, %RDX_LP +- jbe L(length_less16) +- +- punpcklbw %xmm1, %xmm1 +- punpcklbw %xmm1, %xmm1 +- +- add %RDX_LP, %RDI_LP +- pshufd $0, %xmm1, %xmm1 +- +- movdqu (%rdi), %xmm0 +- pcmpeqb %xmm1, %xmm0 +- +-/* Check if there is a match. */ +- pmovmskb %xmm0, %eax +- test %eax, %eax +- jnz L(matches0) +- +- sub $64, %rdi +- mov %edi, %ecx +- and $15, %ecx +- jz L(loop_prolog) +- +- add $16, %rdi +- add $16, %rdx +- and $-16, %rdi +- sub %rcx, %rdx +- +- .p2align 4 +-L(loop_prolog): +- sub $64, %rdx +- jbe L(exit_loop) +- +- movdqa 48(%rdi), %xmm0 +- pcmpeqb %xmm1, %xmm0 +- pmovmskb %xmm0, %eax +- test %eax, %eax +- jnz L(matches48) +- +- movdqa 32(%rdi), %xmm2 +- pcmpeqb %xmm1, %xmm2 +- pmovmskb %xmm2, %eax +- test %eax, %eax +- jnz L(matches32) +- +- movdqa 16(%rdi), %xmm3 +- pcmpeqb %xmm1, %xmm3 +- pmovmskb %xmm3, %eax +- test %eax, %eax +- jnz L(matches16) +- +- movdqa (%rdi), %xmm4 +- pcmpeqb %xmm1, %xmm4 +- pmovmskb %xmm4, %eax +- test %eax, %eax +- jnz L(matches0) +- +- sub $64, %rdi +- sub $64, %rdx +- jbe L(exit_loop) +- +- movdqa 48(%rdi), %xmm0 +- pcmpeqb %xmm1, %xmm0 +- pmovmskb %xmm0, %eax +- test %eax, %eax +- jnz L(matches48) +- +- movdqa 32(%rdi), %xmm2 +- pcmpeqb %xmm1, %xmm2 +- pmovmskb %xmm2, %eax +- test %eax, %eax +- jnz L(matches32) +- +- movdqa 16(%rdi), %xmm3 +- pcmpeqb %xmm1, %xmm3 +- pmovmskb %xmm3, %eax +- test %eax, %eax +- jnz L(matches16) +- +- movdqa (%rdi), %xmm3 +- pcmpeqb %xmm1, %xmm3 +- pmovmskb %xmm3, %eax +- test %eax, %eax +- jnz L(matches0) +- +- mov %edi, %ecx +- and $63, %ecx +- jz L(align64_loop) +- +- add $64, %rdi +- add $64, %rdx +- and $-64, %rdi +- sub %rcx, %rdx +- +- .p2align 4 +-L(align64_loop): +- sub $64, %rdi +- sub $64, %rdx +- jbe L(exit_loop) +- +- movdqa (%rdi), %xmm0 +- movdqa 16(%rdi), %xmm2 +- movdqa 32(%rdi), %xmm3 +- movdqa 48(%rdi), %xmm4 +- +- pcmpeqb %xmm1, %xmm0 +- pcmpeqb %xmm1, %xmm2 +- pcmpeqb %xmm1, %xmm3 +- pcmpeqb %xmm1, %xmm4 +- +- pmaxub %xmm3, %xmm0 +- pmaxub %xmm4, %xmm2 +- pmaxub %xmm0, %xmm2 +- pmovmskb %xmm2, %eax +- +- test %eax, %eax +- jz L(align64_loop) +- +- pmovmskb %xmm4, %eax +- test %eax, %eax +- jnz L(matches48) +- +- pmovmskb %xmm3, %eax +- test %eax, %eax +- jnz L(matches32) +- +- movdqa 16(%rdi), %xmm2 +- +- pcmpeqb %xmm1, %xmm2 +- pcmpeqb (%rdi), %xmm1 +- +- pmovmskb %xmm2, %eax +- test %eax, %eax +- jnz L(matches16) +- +- pmovmskb %xmm1, %eax +- bsr %eax, %eax +- +- add %rdi, %rax +- ret +- +- .p2align 4 +-L(exit_loop): +- add $64, %edx +- cmp $32, %edx +- jbe L(exit_loop_32) +- +- movdqa 48(%rdi), %xmm0 +- pcmpeqb %xmm1, %xmm0 +- pmovmskb %xmm0, %eax +- test %eax, %eax +- jnz L(matches48) +- +- movdqa 32(%rdi), %xmm2 +- pcmpeqb %xmm1, %xmm2 +- pmovmskb %xmm2, %eax +- test %eax, %eax +- jnz L(matches32) +- +- movdqa 16(%rdi), %xmm3 +- pcmpeqb %xmm1, %xmm3 +- pmovmskb %xmm3, %eax +- test %eax, %eax +- jnz L(matches16_1) +- cmp $48, %edx +- jbe L(return_null) +- +- pcmpeqb (%rdi), %xmm1 +- pmovmskb %xmm1, %eax +- test %eax, %eax +- jnz L(matches0_1) +- xor %eax, %eax +- ret +- +- .p2align 4 +-L(exit_loop_32): +- movdqa 48(%rdi), %xmm0 +- pcmpeqb %xmm1, %xmm0 +- pmovmskb %xmm0, %eax +- test %eax, %eax +- jnz L(matches48_1) +- cmp $16, %edx +- jbe L(return_null) +- +- pcmpeqb 32(%rdi), %xmm1 +- pmovmskb %xmm1, %eax +- test %eax, %eax +- jnz L(matches32_1) +- xor %eax, %eax +- ret +- +- .p2align 4 +-L(matches0): +- bsr %eax, %eax +- add %rdi, %rax +- ret +- +- .p2align 4 +-L(matches16): +- bsr %eax, %eax +- lea 16(%rax, %rdi), %rax +- ret +- +- .p2align 4 +-L(matches32): +- bsr %eax, %eax +- lea 32(%rax, %rdi), %rax +- ret +- +- .p2align 4 +-L(matches48): +- bsr %eax, %eax +- lea 48(%rax, %rdi), %rax +- ret +- +- .p2align 4 +-L(matches0_1): +- bsr %eax, %eax +- sub $64, %rdx +- add %rax, %rdx +- jl L(return_null) +- add %rdi, %rax +- ret +- +- .p2align 4 +-L(matches16_1): +- bsr %eax, %eax +- sub $48, %rdx +- add %rax, %rdx +- jl L(return_null) +- lea 16(%rdi, %rax), %rax +- ret +- +- .p2align 4 +-L(matches32_1): +- bsr %eax, %eax +- sub $32, %rdx +- add %rax, %rdx +- jl L(return_null) +- lea 32(%rdi, %rax), %rax +- ret +- +- .p2align 4 +-L(matches48_1): +- bsr %eax, %eax +- sub $16, %rdx +- add %rax, %rdx +- jl L(return_null) +- lea 48(%rdi, %rax), %rax +- ret +- +- .p2align 4 +-L(return_null): +- xor %eax, %eax +- ret +- +- .p2align 4 +-L(length_less16_offset0): +- test %edx, %edx +- jz L(return_null) +- +- mov %dl, %cl +- pcmpeqb (%rdi), %xmm1 +- +- mov $1, %edx +- sal %cl, %edx +- sub $1, %edx +- +- pmovmskb %xmm1, %eax +- +- and %edx, %eax +- test %eax, %eax +- jz L(return_null) +- +- bsr %eax, %eax +- add %rdi, %rax +- ret +- +- .p2align 4 +-L(length_less16): +- punpcklbw %xmm1, %xmm1 +- punpcklbw %xmm1, %xmm1 +- +- add $16, %edx +- +- pshufd $0, %xmm1, %xmm1 +- +- mov %edi, %ecx +- and $15, %ecx +- jz L(length_less16_offset0) +- +- mov %cl, %dh +- mov %ecx, %esi +- add %dl, %dh +- and $-16, %rdi +- +- sub $16, %dh +- ja L(length_less16_part2) +- +- pcmpeqb (%rdi), %xmm1 +- pmovmskb %xmm1, %eax +- +- sar %cl, %eax +- mov %dl, %cl +- +- mov $1, %edx +- sal %cl, %edx +- sub $1, %edx +- +- and %edx, %eax +- test %eax, %eax +- jz L(return_null) +- +- bsr %eax, %eax +- add %rdi, %rax +- add %rsi, %rax +- ret +- +- .p2align 4 +-L(length_less16_part2): +- movdqa 16(%rdi), %xmm2 +- pcmpeqb %xmm1, %xmm2 +- pmovmskb %xmm2, %eax +- +- mov %dh, %cl +- mov $1, %edx +- sal %cl, %edx +- sub $1, %edx +- +- and %edx, %eax +- +- test %eax, %eax +- jnz L(length_less16_part2_return) +- +- pcmpeqb (%rdi), %xmm1 +- pmovmskb %xmm1, %eax +- +- mov %esi, %ecx +- sar %cl, %eax +- test %eax, %eax +- jz L(return_null) +- +- bsr %eax, %eax +- add %rdi, %rax +- add %rsi, %rax +- ret +- +- .p2align 4 +-L(length_less16_part2_return): +- bsr %eax, %eax +- lea 16(%rax, %rdi), %rax ++ENTRY_P2ALIGN(__memrchr, 6) ++#ifdef __ILP32__ ++ /* Clear upper bits. */ ++ mov %RDX_LP, %RDX_LP ++#endif ++ movd %esi, %xmm0 ++ ++ /* Get end pointer. */ ++ leaq (%rdx, %rdi), %rcx ++ ++ punpcklbw %xmm0, %xmm0 ++ punpcklwd %xmm0, %xmm0 ++ pshufd $0, %xmm0, %xmm0 ++ ++ /* Check if we can load 1x VEC without cross a page. */ ++ testl $(PAGE_SIZE - VEC_SIZE), %ecx ++ jz L(page_cross) ++ ++ /* NB: This load happens regardless of whether rdx (len) is zero. Since ++ it doesn't cross a page and the standard gurantees any pointer have ++ at least one-valid byte this load must be safe. For the entire ++ history of the x86 memrchr implementation this has been possible so ++ no code "should" be relying on a zero-length check before this load. ++ The zero-length check is moved to the page cross case because it is ++ 1) pretty cold and including it pushes the hot case len <= VEC_SIZE ++ into 2-cache lines. */ ++ movups -(VEC_SIZE)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ ++ subq $VEC_SIZE, %rdx ++ ja L(more_1x_vec) ++L(ret_vec_x0_test): ++ /* Zero-flag set if eax (src) is zero. Destination unchanged if src is ++ zero. */ ++ bsrl %eax, %eax ++ jz L(ret_0) ++ /* Check if the CHAR match is in bounds. Need to truly zero `eax` here ++ if out of bounds. */ ++ addl %edx, %eax ++ jl L(zero_0) ++ /* Since we subtracted VEC_SIZE from rdx earlier we can just add to base ++ ptr. */ ++ addq %rdi, %rax ++L(ret_0): ++ ret ++ ++ .p2align 4,, 5 ++L(ret_vec_x0): ++ bsrl %eax, %eax ++ leaq -(VEC_SIZE)(%rcx, %rax), %rax ++ ret ++ ++ .p2align 4,, 2 ++L(zero_0): ++ xorl %eax, %eax ++ ret ++ ++ ++ .p2align 4,, 8 ++L(more_1x_vec): ++ testl %eax, %eax ++ jnz L(ret_vec_x0) ++ ++ /* Align rcx (pointer to string). */ ++ decq %rcx ++ andq $-VEC_SIZE, %rcx ++ ++ movq %rcx, %rdx ++ /* NB: We could consistenyl save 1-byte in this pattern with `movaps ++ %xmm0, %xmm1; pcmpeq IMM8(r), %xmm1; ...`. The reason against it is ++ it adds more frontend uops (even if the moves can be eliminated) and ++ some percentage of the time actual backend uops. */ ++ movaps -(VEC_SIZE)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ subq %rdi, %rdx ++ pmovmskb %xmm1, %eax ++ ++ cmpq $(VEC_SIZE * 2), %rdx ++ ja L(more_2x_vec) ++L(last_2x_vec): ++ subl $VEC_SIZE, %edx ++ jbe L(ret_vec_x0_test) ++ ++ testl %eax, %eax ++ jnz L(ret_vec_x0) ++ ++ movaps -(VEC_SIZE * 2)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ ++ subl $VEC_SIZE, %edx ++ bsrl %eax, %eax ++ jz L(ret_1) ++ addl %edx, %eax ++ jl L(zero_0) ++ addq %rdi, %rax ++L(ret_1): ++ ret ++ ++ /* Don't align. Otherwise lose 2-byte encoding in jump to L(page_cross) ++ causes the hot pause (length <= VEC_SIZE) to span multiple cache ++ lines. Naturally aligned % 16 to 8-bytes. */ ++L(page_cross): ++ /* Zero length check. */ ++ testq %rdx, %rdx ++ jz L(zero_0) ++ ++ leaq -1(%rcx), %r8 ++ andq $-(VEC_SIZE), %r8 ++ ++ movaps (%r8), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %esi ++ /* Shift out negative alignment (because we are starting from endptr and ++ working backwards). */ ++ negl %ecx ++ /* 32-bit shift but VEC_SIZE=16 so need to mask the shift count ++ explicitly. */ ++ andl $(VEC_SIZE - 1), %ecx ++ shl %cl, %esi ++ movzwl %si, %eax ++ leaq (%rdi, %rdx), %rcx ++ cmpq %rdi, %r8 ++ ja L(more_1x_vec) ++ subl $VEC_SIZE, %edx ++ bsrl %eax, %eax ++ jz L(ret_2) ++ addl %edx, %eax ++ jl L(zero_1) ++ addq %rdi, %rax ++L(ret_2): ++ ret ++ ++ /* Fits in aliging bytes. */ ++L(zero_1): ++ xorl %eax, %eax ++ ret ++ ++ .p2align 4,, 5 ++L(ret_vec_x1): ++ bsrl %eax, %eax ++ leaq -(VEC_SIZE * 2)(%rcx, %rax), %rax ++ ret ++ ++ .p2align 4,, 8 ++L(more_2x_vec): ++ testl %eax, %eax ++ jnz L(ret_vec_x0) ++ ++ movaps -(VEC_SIZE * 2)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ testl %eax, %eax ++ jnz L(ret_vec_x1) ++ ++ ++ movaps -(VEC_SIZE * 3)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ ++ subq $(VEC_SIZE * 4), %rdx ++ ja L(more_4x_vec) ++ ++ addl $(VEC_SIZE), %edx ++ jle L(ret_vec_x2_test) ++ ++L(last_vec): ++ testl %eax, %eax ++ jnz L(ret_vec_x2) ++ ++ movaps -(VEC_SIZE * 4)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ ++ subl $(VEC_SIZE), %edx ++ bsrl %eax, %eax ++ jz L(ret_3) ++ addl %edx, %eax ++ jl L(zero_2) ++ addq %rdi, %rax ++L(ret_3): ++ ret ++ ++ .p2align 4,, 6 ++L(ret_vec_x2_test): ++ bsrl %eax, %eax ++ jz L(zero_2) ++ addl %edx, %eax ++ jl L(zero_2) ++ addq %rdi, %rax ++ ret ++ ++L(zero_2): ++ xorl %eax, %eax ++ ret ++ ++ ++ .p2align 4,, 5 ++L(ret_vec_x2): ++ bsrl %eax, %eax ++ leaq -(VEC_SIZE * 3)(%rcx, %rax), %rax ++ ret ++ ++ .p2align 4,, 5 ++L(ret_vec_x3): ++ bsrl %eax, %eax ++ leaq -(VEC_SIZE * 4)(%rcx, %rax), %rax ++ ret ++ ++ .p2align 4,, 8 ++L(more_4x_vec): ++ testl %eax, %eax ++ jnz L(ret_vec_x2) ++ ++ movaps -(VEC_SIZE * 4)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ ++ testl %eax, %eax ++ jnz L(ret_vec_x3) ++ ++ addq $-(VEC_SIZE * 4), %rcx ++ cmpq $(VEC_SIZE * 4), %rdx ++ jbe L(last_4x_vec) ++ ++ /* Offset everything by 4x VEC_SIZE here to save a few bytes at the end ++ keeping the code from spilling to the next cache line. */ ++ addq $(VEC_SIZE * 4 - 1), %rcx ++ andq $-(VEC_SIZE * 4), %rcx ++ leaq (VEC_SIZE * 4)(%rdi), %rdx ++ andq $-(VEC_SIZE * 4), %rdx ++ ++ .p2align 4,, 11 ++L(loop_4x_vec): ++ movaps (VEC_SIZE * -1)(%rcx), %xmm1 ++ movaps (VEC_SIZE * -2)(%rcx), %xmm2 ++ movaps (VEC_SIZE * -3)(%rcx), %xmm3 ++ movaps (VEC_SIZE * -4)(%rcx), %xmm4 ++ pcmpeqb %xmm0, %xmm1 ++ pcmpeqb %xmm0, %xmm2 ++ pcmpeqb %xmm0, %xmm3 ++ pcmpeqb %xmm0, %xmm4 ++ ++ por %xmm1, %xmm2 ++ por %xmm3, %xmm4 ++ por %xmm2, %xmm4 ++ ++ pmovmskb %xmm4, %esi ++ testl %esi, %esi ++ jnz L(loop_end) ++ ++ addq $-(VEC_SIZE * 4), %rcx ++ cmpq %rdx, %rcx ++ jne L(loop_4x_vec) ++ ++ subl %edi, %edx ++ ++ /* Ends up being 1-byte nop. */ ++ .p2align 4,, 2 ++L(last_4x_vec): ++ movaps -(VEC_SIZE)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ ++ cmpl $(VEC_SIZE * 2), %edx ++ jbe L(last_2x_vec) ++ ++ testl %eax, %eax ++ jnz L(ret_vec_x0) ++ ++ ++ movaps -(VEC_SIZE * 2)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ ++ testl %eax, %eax ++ jnz L(ret_vec_end) ++ ++ movaps -(VEC_SIZE * 3)(%rcx), %xmm1 ++ pcmpeqb %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ ++ subl $(VEC_SIZE * 3), %edx ++ ja L(last_vec) ++ bsrl %eax, %eax ++ jz L(ret_4) ++ addl %edx, %eax ++ jl L(zero_3) ++ addq %rdi, %rax ++L(ret_4): ++ ret ++ ++ /* Ends up being 1-byte nop. */ ++ .p2align 4,, 3 ++L(loop_end): ++ pmovmskb %xmm1, %eax ++ sall $16, %eax ++ jnz L(ret_vec_end) ++ ++ pmovmskb %xmm2, %eax ++ testl %eax, %eax ++ jnz L(ret_vec_end) ++ ++ pmovmskb %xmm3, %eax ++ /* Combine last 2 VEC matches. If ecx (VEC3) is zero (no CHAR in VEC3) ++ then it won't affect the result in esi (VEC4). If ecx is non-zero ++ then CHAR in VEC3 and bsrq will use that position. */ ++ sall $16, %eax ++ orl %esi, %eax ++ bsrl %eax, %eax ++ leaq -(VEC_SIZE * 4)(%rcx, %rax), %rax ++ ret ++ ++L(ret_vec_end): ++ bsrl %eax, %eax ++ leaq (VEC_SIZE * -2)(%rax, %rcx), %rax ++ ret ++ /* Use in L(last_4x_vec). In the same cache line. This is just a spare ++ aligning bytes. */ ++L(zero_3): ++ xorl %eax, %eax + ret +- +-END (__memrchr) ++ /* 2-bytes from next cache line. */ ++END(__memrchr) + weak_alias (__memrchr, memrchr) +diff -Nuar a/sysdeps/x86_64/memset.S b/sysdeps/x86_64/memset.S +--- a/sysdeps/x86_64/memset.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/memset.S 2025-10-20 21:02:22.000000000 +0300 +@@ -1,4 +1,4 @@ +-/* memset/bzero -- set memory area to CH/0 ++/* memset -- set memory area to CH/0 + Optimized version for x86-64. + Copyright (C) 2002-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. +@@ -28,17 +28,23 @@ + #define VMOVU movups + #define VMOVA movaps + +-#define MEMSET_VDUP_TO_VEC0_AND_SET_RETURN(d, r) \ ++# define MEMSET_SET_VEC0_AND_SET_RETURN(d, r) \ + movd d, %xmm0; \ + movq r, %rax; \ + punpcklbw %xmm0, %xmm0; \ + punpcklwd %xmm0, %xmm0; \ + pshufd $0, %xmm0, %xmm0 + +-#define WMEMSET_VDUP_TO_VEC0_AND_SET_RETURN(d, r) \ ++# define WMEMSET_SET_VEC0_AND_SET_RETURN(d, r) \ + movd d, %xmm0; \ +- movq r, %rax; \ +- pshufd $0, %xmm0, %xmm0 ++ pshufd $0, %xmm0, %xmm0; \ ++ movq r, %rax ++ ++# define MEMSET_VDUP_TO_VEC0_HIGH() ++# define MEMSET_VDUP_TO_VEC0_LOW() ++ ++# define WMEMSET_VDUP_TO_VEC0_HIGH() ++# define WMEMSET_VDUP_TO_VEC0_LOW() + + #define SECTION(p) p + +diff -Nuar a/sysdeps/x86_64/multiarch/avx-rtm-vecs.h b/sysdeps/x86_64/multiarch/avx-rtm-vecs.h +--- a/sysdeps/x86_64/multiarch/avx-rtm-vecs.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/avx-rtm-vecs.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,35 @@ ++/* Common config for AVX-RTM VECs ++ All versions must be listed in ifunc-impl-list.c. ++ Copyright (C) 2022 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 ++ . */ ++ ++#ifndef _AVX_RTM_VECS_H ++#define _AVX_RTM_VECS_H 1 ++ ++#define COND_VZEROUPPER COND_VZEROUPPER_XTEST ++#define ZERO_UPPER_VEC_REGISTERS_RETURN \ ++ ZERO_UPPER_VEC_REGISTERS_RETURN_XTEST ++ ++#define VZEROUPPER_RETURN jmp L(return_vzeroupper) ++ ++#define USE_WITH_RTM 1 ++#include "avx-vecs.h" ++ ++#undef SECTION ++#define SECTION(p) p##.avx.rtm ++ ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/avx-vecs.h b/sysdeps/x86_64/multiarch/avx-vecs.h +--- a/sysdeps/x86_64/multiarch/avx-vecs.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/avx-vecs.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,47 @@ ++/* Common config for AVX VECs ++ All versions must be listed in ifunc-impl-list.c. ++ Copyright (C) 2022 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 ++ . */ ++ ++#ifndef _AVX_VECS_H ++#define _AVX_VECS_H 1 ++ ++#ifdef VEC_SIZE ++# error "Multiple VEC configs included!" ++#endif ++ ++#define VEC_SIZE 32 ++#include "vec-macros.h" ++ ++#define USE_WITH_AVX 1 ++#define SECTION(p) p##.avx ++ ++/* 4-byte mov instructions with AVX2. */ ++#define MOV_SIZE 4 ++/* 1 (ret) + 3 (vzeroupper). */ ++#define RET_SIZE 4 ++#define VZEROUPPER vzeroupper ++ ++#define VMOVU vmovdqu ++#define VMOVA vmovdqa ++#define VMOVNT vmovntdq ++ ++/* Often need to access xmm portion. */ ++#define VEC_xmm VEC_any_xmm ++#define VEC VEC_any_ymm ++ ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/bcopy.S b/sysdeps/x86_64/multiarch/bcopy.S +--- a/sysdeps/x86_64/multiarch/bcopy.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/bcopy.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,7 +0,0 @@ +-#include +- +- .text +-ENTRY(bcopy) +- xchg %rdi, %rsi +- jmp __libc_memmove /* Branch to IFUNC memmove. */ +-END(bcopy) +diff -Nuar a/sysdeps/x86_64/multiarch/evex256-vecs.h b/sysdeps/x86_64/multiarch/evex256-vecs.h +--- a/sysdeps/x86_64/multiarch/evex256-vecs.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/evex256-vecs.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,35 @@ ++/* Common config for EVEX256 VECs ++ All versions must be listed in ifunc-impl-list.c. ++ Copyright (C) 2022 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 ++ . */ ++ ++#ifndef _EVEX256_VECS_H ++#define _EVEX256_VECS_H 1 ++ ++#ifdef VEC_SIZE ++# error "Multiple VEC configs included!" ++#endif ++ ++#define VEC_SIZE 32 ++#include "evex-vecs-common.h" ++ ++#define USE_WITH_EVEX256 1 ++#define SECTION(p) p##.evex ++ ++#define VEC VEC_ymm ++ ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/evex512-vecs.h b/sysdeps/x86_64/multiarch/evex512-vecs.h +--- a/sysdeps/x86_64/multiarch/evex512-vecs.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/evex512-vecs.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,35 @@ ++/* Common config for EVEX512 VECs ++ All versions must be listed in ifunc-impl-list.c. ++ Copyright (C) 2022 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 ++ . */ ++ ++#ifndef _EVEX512_VECS_H ++#define _EVEX512_VECS_H 1 ++ ++#ifdef VEC_SIZE ++# error "Multiple VEC configs included!" ++#endif ++ ++#define VEC_SIZE 64 ++#include "evex-vecs-common.h" ++ ++#define USE_WITH_EVEX512 1 ++#define SECTION(p) p##.evex512 ++ ++#define VEC VEC_zmm ++ ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/evex-vecs-common.h b/sysdeps/x86_64/multiarch/evex-vecs-common.h +--- a/sysdeps/x86_64/multiarch/evex-vecs-common.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/evex-vecs-common.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,39 @@ ++/* Common config for EVEX256 and EVEX512 VECs ++ All versions must be listed in ifunc-impl-list.c. ++ Copyright (C) 2022 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 ++ . */ ++ ++#ifndef _EVEX_VECS_COMMON_H ++#define _EVEX_VECS_COMMON_H 1 ++ ++#include "vec-macros.h" ++ ++/* 6-byte mov instructions with EVEX. */ ++#define MOV_SIZE 6 ++/* No vzeroupper needed. */ ++#define RET_SIZE 1 ++#define VZEROUPPER ++ ++#define VMOVU vmovdqu64 ++#define VMOVA vmovdqa64 ++#define VMOVNT vmovntdq ++ ++#define VEC_xmm VEC_hi_xmm ++#define VEC_ymm VEC_hi_ymm ++#define VEC_zmm VEC_hi_zmm ++ ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/ifunc-avx2.h b/sysdeps/x86_64/multiarch/ifunc-avx2.h +--- a/sysdeps/x86_64/multiarch/ifunc-avx2.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/ifunc-avx2.h 2025-10-20 21:02:22.000000000 +0300 +@@ -30,7 +30,9 @@ + const struct cpu_features* cpu_features = __get_cpu_features (); + + if (CPU_FEATURE_USABLE_P (cpu_features, AVX2) ++ && CPU_FEATURE_USABLE_P (cpu_features, BMI1) + && CPU_FEATURE_USABLE_P (cpu_features, BMI2) ++ && CPU_FEATURE_USABLE_P (cpu_features, LZCNT) + && CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load)) + { + if (CPU_FEATURE_USABLE_P (cpu_features, AVX512VL) +diff -Nuar a/sysdeps/x86_64/multiarch/ifunc-impl-list.c b/sysdeps/x86_64/multiarch/ifunc-impl-list.c +--- a/sysdeps/x86_64/multiarch/ifunc-impl-list.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/ifunc-impl-list.c 2025-10-20 21:02:22.000000000 +0300 +@@ -59,10 +59,12 @@ + /* Support sysdeps/x86_64/multiarch/memchr.c. */ + IFUNC_IMPL (i, name, memchr, + IFUNC_IMPL_ADD (array, i, memchr, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), + __memchr_avx2) + IFUNC_IMPL_ADD (array, i, memchr, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (RTM)), + __memchr_avx2_rtm) + IFUNC_IMPL_ADD (array, i, memchr, +@@ -96,8 +98,6 @@ + && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (MOVBE)), + __memcmp_evex_movbe) +- IFUNC_IMPL_ADD (array, i, memcmp, CPU_FEATURE_USABLE (SSE4_1), +- __memcmp_sse4_1) + IFUNC_IMPL_ADD (array, i, memcmp, CPU_FEATURE_USABLE (SSSE3), + __memcmp_ssse3) + IFUNC_IMPL_ADD (array, i, memcmp, 1, __memcmp_sse2)) +@@ -192,15 +192,21 @@ + /* Support sysdeps/x86_64/multiarch/memrchr.c. */ + IFUNC_IMPL (i, name, memrchr, + IFUNC_IMPL_ADD (array, i, memrchr, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) ++ && CPU_FEATURE_USABLE (LZCNT)), + __memrchr_avx2) + IFUNC_IMPL_ADD (array, i, memrchr, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) ++ && CPU_FEATURE_USABLE (LZCNT) + && CPU_FEATURE_USABLE (RTM)), + __memrchr_avx2_rtm) + IFUNC_IMPL_ADD (array, i, memrchr, + (CPU_FEATURE_USABLE (AVX512VL) +- && CPU_FEATURE_USABLE (AVX512BW)), ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2) ++ && CPU_FEATURE_USABLE (LZCNT)), + __memrchr_evex) + + IFUNC_IMPL_ADD (array, i, memrchr, 1, __memrchr_sse2)) +@@ -303,10 +309,12 @@ + /* Support sysdeps/x86_64/multiarch/rawmemchr.c. */ + IFUNC_IMPL (i, name, rawmemchr, + IFUNC_IMPL_ADD (array, i, rawmemchr, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), + __rawmemchr_avx2) + IFUNC_IMPL_ADD (array, i, rawmemchr, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (RTM)), + __rawmemchr_avx2_rtm) + IFUNC_IMPL_ADD (array, i, rawmemchr, +@@ -337,6 +345,11 @@ + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI2)), + __strlen_evex) ++ IFUNC_IMPL_ADD (array, i, strlen, ++ (CPU_FEATURE_USABLE (AVX512VL) ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strlen_evex512) + IFUNC_IMPL_ADD (array, i, strlen, 1, __strlen_sse2)) + + /* Support sysdeps/x86_64/multiarch/strnlen.c. */ +@@ -355,6 +368,11 @@ + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI2)), + __strnlen_evex) ++ IFUNC_IMPL_ADD (array, i, strnlen, ++ (CPU_FEATURE_USABLE (AVX512VL) ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strnlen_evex512) + IFUNC_IMPL_ADD (array, i, strnlen, 1, __strnlen_sse2)) + + /* Support sysdeps/x86_64/multiarch/stpncpy.c. */ +@@ -395,8 +413,19 @@ + /* Support sysdeps/x86_64/multiarch/strcasecmp_l.c. */ + IFUNC_IMPL (i, name, strcasecmp, + IFUNC_IMPL_ADD (array, i, strcasecmp, +- CPU_FEATURE_USABLE (AVX), +- __strcasecmp_avx) ++ (CPU_FEATURE_USABLE (AVX512VL) ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strcasecmp_evex) ++ IFUNC_IMPL_ADD (array, i, strcasecmp, ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strcasecmp_avx2) ++ IFUNC_IMPL_ADD (array, i, strcasecmp, ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) ++ && CPU_FEATURE_USABLE (RTM)), ++ __strcasecmp_avx2_rtm) + IFUNC_IMPL_ADD (array, i, strcasecmp, + CPU_FEATURE_USABLE (SSE4_2), + __strcasecmp_sse42) +@@ -407,9 +436,20 @@ + + /* Support sysdeps/x86_64/multiarch/strcasecmp_l.c. */ + IFUNC_IMPL (i, name, strcasecmp_l, +- IFUNC_IMPL_ADD (array, i, strcasecmp_l, +- CPU_FEATURE_USABLE (AVX), +- __strcasecmp_l_avx) ++ IFUNC_IMPL_ADD (array, i, strcasecmp, ++ (CPU_FEATURE_USABLE (AVX512VL) ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strcasecmp_l_evex) ++ IFUNC_IMPL_ADD (array, i, strcasecmp, ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strcasecmp_l_avx2) ++ IFUNC_IMPL_ADD (array, i, strcasecmp, ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) ++ && CPU_FEATURE_USABLE (RTM)), ++ __strcasecmp_l_avx2_rtm) + IFUNC_IMPL_ADD (array, i, strcasecmp_l, + CPU_FEATURE_USABLE (SSE4_2), + __strcasecmp_l_sse42) +@@ -476,25 +516,33 @@ + /* Support sysdeps/x86_64/multiarch/strrchr.c. */ + IFUNC_IMPL (i, name, strrchr, + IFUNC_IMPL_ADD (array, i, strrchr, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI1) ++ && CPU_FEATURE_USABLE (BMI2)), + __strrchr_avx2) + IFUNC_IMPL_ADD (array, i, strrchr, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI1) ++ && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (RTM)), + __strrchr_avx2_rtm) + IFUNC_IMPL_ADD (array, i, strrchr, + (CPU_FEATURE_USABLE (AVX512VL) +- && CPU_FEATURE_USABLE (AVX512BW)), ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI1) ++ && CPU_FEATURE_USABLE (BMI2)), + __strrchr_evex) + IFUNC_IMPL_ADD (array, i, strrchr, 1, __strrchr_sse2)) + + /* Support sysdeps/x86_64/multiarch/strcmp.c. */ + IFUNC_IMPL (i, name, strcmp, + IFUNC_IMPL_ADD (array, i, strcmp, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), + __strcmp_avx2) + IFUNC_IMPL_ADD (array, i, strcmp, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (RTM)), + __strcmp_avx2_rtm) + IFUNC_IMPL_ADD (array, i, strcmp, +@@ -535,8 +583,19 @@ + /* Support sysdeps/x86_64/multiarch/strncase_l.c. */ + IFUNC_IMPL (i, name, strncasecmp, + IFUNC_IMPL_ADD (array, i, strncasecmp, +- CPU_FEATURE_USABLE (AVX), +- __strncasecmp_avx) ++ (CPU_FEATURE_USABLE (AVX512VL) ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strncasecmp_evex) ++ IFUNC_IMPL_ADD (array, i, strncasecmp, ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strncasecmp_avx2) ++ IFUNC_IMPL_ADD (array, i, strncasecmp, ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) ++ && CPU_FEATURE_USABLE (RTM)), ++ __strncasecmp_avx2_rtm) + IFUNC_IMPL_ADD (array, i, strncasecmp, + CPU_FEATURE_USABLE (SSE4_2), + __strncasecmp_sse42) +@@ -548,9 +607,20 @@ + + /* Support sysdeps/x86_64/multiarch/strncase_l.c. */ + IFUNC_IMPL (i, name, strncasecmp_l, +- IFUNC_IMPL_ADD (array, i, strncasecmp_l, +- CPU_FEATURE_USABLE (AVX), +- __strncasecmp_l_avx) ++ IFUNC_IMPL_ADD (array, i, strncasecmp, ++ (CPU_FEATURE_USABLE (AVX512VL) ++ & CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strncasecmp_l_evex) ++ IFUNC_IMPL_ADD (array, i, strncasecmp, ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strncasecmp_l_avx2) ++ IFUNC_IMPL_ADD (array, i, strncasecmp, ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) ++ && CPU_FEATURE_USABLE (RTM)), ++ __strncasecmp_l_avx2_rtm) + IFUNC_IMPL_ADD (array, i, strncasecmp_l, + CPU_FEATURE_USABLE (SSE4_2), + __strncasecmp_l_sse42) +@@ -611,6 +681,12 @@ + + /* Support sysdeps/x86_64/multiarch/strstr.c. */ + IFUNC_IMPL (i, name, strstr, ++ IFUNC_IMPL_ADD (array, i, strstr, ++ (CPU_FEATURE_USABLE (AVX512VL) ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (AVX512DQ) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __strstr_avx512) + IFUNC_IMPL_ADD (array, i, strstr, 1, __strstr_sse2_unaligned) + IFUNC_IMPL_ADD (array, i, strstr, 1, __strstr_sse2)) + +@@ -635,15 +711,20 @@ + /* Support sysdeps/x86_64/multiarch/wcsrchr.c. */ + IFUNC_IMPL (i, name, wcsrchr, + IFUNC_IMPL_ADD (array, i, wcsrchr, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI1) ++ && CPU_FEATURE_USABLE (BMI2)), + __wcsrchr_avx2) + IFUNC_IMPL_ADD (array, i, wcsrchr, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI1) ++ && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (RTM)), + __wcsrchr_avx2_rtm) + IFUNC_IMPL_ADD (array, i, wcsrchr, + (CPU_FEATURE_USABLE (AVX512VL) + && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI1) + && CPU_FEATURE_USABLE (BMI2)), + __wcsrchr_evex) + IFUNC_IMPL_ADD (array, i, wcsrchr, 1, __wcsrchr_sse2)) +@@ -651,10 +732,12 @@ + /* Support sysdeps/x86_64/multiarch/wcscmp.c. */ + IFUNC_IMPL (i, name, wcscmp, + IFUNC_IMPL_ADD (array, i, wcscmp, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), + __wcscmp_avx2) + IFUNC_IMPL_ADD (array, i, wcscmp, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (RTM)), + __wcscmp_avx2_rtm) + IFUNC_IMPL_ADD (array, i, wcscmp, +@@ -667,10 +750,12 @@ + /* Support sysdeps/x86_64/multiarch/wcsncmp.c. */ + IFUNC_IMPL (i, name, wcsncmp, + IFUNC_IMPL_ADD (array, i, wcsncmp, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), + __wcsncmp_avx2) + IFUNC_IMPL_ADD (array, i, wcsncmp, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (RTM)), + __wcsncmp_avx2_rtm) + IFUNC_IMPL_ADD (array, i, wcsncmp, +@@ -703,6 +788,11 @@ + && CPU_FEATURE_USABLE (BMI2)), + __wcslen_evex) + IFUNC_IMPL_ADD (array, i, wcslen, ++ (CPU_FEATURE_USABLE (AVX512VL) ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __wcslen_evex512) ++ IFUNC_IMPL_ADD (array, i, wcslen, + CPU_FEATURE_USABLE (SSE4_1), + __wcslen_sse4_1) + IFUNC_IMPL_ADD (array, i, wcslen, 1, __wcslen_sse2)) +@@ -724,6 +814,11 @@ + && CPU_FEATURE_USABLE (BMI2)), + __wcsnlen_evex) + IFUNC_IMPL_ADD (array, i, wcsnlen, ++ (CPU_FEATURE_USABLE (AVX512VL) ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2)), ++ __wcsnlen_evex512) ++ IFUNC_IMPL_ADD (array, i, wcsnlen, + CPU_FEATURE_USABLE (SSE4_1), + __wcsnlen_sse4_1) + IFUNC_IMPL_ADD (array, i, wcsnlen, 1, __wcsnlen_sse2)) +@@ -731,10 +826,12 @@ + /* Support sysdeps/x86_64/multiarch/wmemchr.c. */ + IFUNC_IMPL (i, name, wmemchr, + IFUNC_IMPL_ADD (array, i, wmemchr, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), + __wmemchr_avx2) + IFUNC_IMPL_ADD (array, i, wmemchr, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (RTM)), + __wmemchr_avx2_rtm) + IFUNC_IMPL_ADD (array, i, wmemchr, +@@ -768,8 +865,6 @@ + && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (MOVBE)), + __wmemcmp_evex_movbe) +- IFUNC_IMPL_ADD (array, i, wmemcmp, CPU_FEATURE_USABLE (SSE4_1), +- __wmemcmp_sse4_1) + IFUNC_IMPL_ADD (array, i, wmemcmp, CPU_FEATURE_USABLE (SSSE3), + __wmemcmp_ssse3) + IFUNC_IMPL_ADD (array, i, wmemcmp, 1, __wmemcmp_sse2)) +@@ -972,15 +1067,18 @@ + /* Support sysdeps/x86_64/multiarch/strncmp.c. */ + IFUNC_IMPL (i, name, strncmp, + IFUNC_IMPL_ADD (array, i, strncmp, +- CPU_FEATURE_USABLE (AVX2), ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2)), + __strncmp_avx2) + IFUNC_IMPL_ADD (array, i, strncmp, + (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (RTM)), + __strncmp_avx2_rtm) + IFUNC_IMPL_ADD (array, i, strncmp, + (CPU_FEATURE_USABLE (AVX512VL) +- && CPU_FEATURE_USABLE (AVX512BW)), ++ && CPU_FEATURE_USABLE (AVX512BW) ++ && CPU_FEATURE_USABLE (BMI2)), + __strncmp_evex) + IFUNC_IMPL_ADD (array, i, strncmp, CPU_FEATURE_USABLE (SSE4_2), + __strncmp_sse42) +@@ -997,6 +1095,10 @@ + CPU_FEATURE_USABLE (AVX2), + __wmemset_chk_avx2_unaligned) + IFUNC_IMPL_ADD (array, i, __wmemset_chk, ++ (CPU_FEATURE_USABLE (AVX2) ++ && CPU_FEATURE_USABLE (RTM)), ++ __wmemset_chk_avx2_unaligned_rtm) ++ IFUNC_IMPL_ADD (array, i, __wmemset_chk, + (CPU_FEATURE_USABLE (AVX512VL) + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (BMI2)), +diff -Nuar a/sysdeps/x86_64/multiarch/ifunc-memcmp.h b/sysdeps/x86_64/multiarch/ifunc-memcmp.h +--- a/sysdeps/x86_64/multiarch/ifunc-memcmp.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/ifunc-memcmp.h 2025-10-20 21:02:22.000000000 +0300 +@@ -21,7 +21,6 @@ + + extern __typeof (REDIRECT_NAME) OPTIMIZE (sse2) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (ssse3) attribute_hidden; +-extern __typeof (REDIRECT_NAME) OPTIMIZE (sse4_1) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (avx2_movbe) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (avx2_movbe_rtm) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (evex_movbe) attribute_hidden; +@@ -47,9 +46,6 @@ + return OPTIMIZE (avx2_movbe); + } + +- if (CPU_FEATURE_USABLE_P (cpu_features, SSE4_1)) +- return OPTIMIZE (sse4_1); +- + if (CPU_FEATURE_USABLE_P (cpu_features, SSSE3)) + return OPTIMIZE (ssse3); + +diff -Nuar a/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h b/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h +--- a/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/ifunc-strcasecmp.h 2025-10-20 21:02:22.000000000 +0300 +@@ -22,15 +22,29 @@ + extern __typeof (REDIRECT_NAME) OPTIMIZE (sse2) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (ssse3) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (sse42) attribute_hidden; +-extern __typeof (REDIRECT_NAME) OPTIMIZE (avx) attribute_hidden; ++extern __typeof (REDIRECT_NAME) OPTIMIZE (avx2) attribute_hidden; ++extern __typeof (REDIRECT_NAME) OPTIMIZE (avx2_rtm) attribute_hidden; ++extern __typeof (REDIRECT_NAME) OPTIMIZE (evex) attribute_hidden; + + static inline void * + IFUNC_SELECTOR (void) + { + const struct cpu_features* cpu_features = __get_cpu_features (); + +- if (CPU_FEATURE_USABLE_P (cpu_features, AVX)) +- return OPTIMIZE (avx); ++ if (CPU_FEATURE_USABLE_P (cpu_features, AVX2) ++ && CPU_FEATURE_USABLE_P (cpu_features, BMI2) ++ && CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load)) ++ { ++ if (CPU_FEATURE_USABLE_P (cpu_features, AVX512VL) ++ && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW)) ++ return OPTIMIZE (evex); ++ ++ if (CPU_FEATURE_USABLE_P (cpu_features, RTM)) ++ return OPTIMIZE (avx2_rtm); ++ ++ if (!CPU_FEATURES_ARCH_P (cpu_features, Prefer_No_VZEROUPPER)) ++ return OPTIMIZE (avx2); ++ } + + if (CPU_FEATURE_USABLE_P (cpu_features, SSE4_2) + && !CPU_FEATURES_ARCH_P (cpu_features, Slow_SSE4_2)) +diff -Nuar a/sysdeps/x86_64/multiarch/Makefile b/sysdeps/x86_64/multiarch/Makefile +--- a/sysdeps/x86_64/multiarch/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/Makefile 2025-10-20 21:02:22.000000000 +0300 +@@ -1,127 +1,199 @@ + ifeq ($(subdir),string) + +-sysdep_routines += strncat-c stpncpy-c strncpy-c \ +- strcmp-sse2 strcmp-sse2-unaligned strcmp-ssse3 \ +- strcmp-sse4_2 strcmp-avx2 \ +- strncmp-sse2 strncmp-ssse3 strncmp-sse4_2 strncmp-avx2 \ +- memchr-sse2 rawmemchr-sse2 memchr-avx2 rawmemchr-avx2 \ +- memrchr-sse2 memrchr-avx2 \ +- memcmp-sse2 \ +- memcmpeq-sse2 \ +- memcmp-avx2-movbe \ +- memcmpeq-avx2 \ +- memcmp-sse4 memcpy-ssse3 \ +- memmove-ssse3 \ +- memcpy-ssse3-back \ +- memmove-ssse3-back \ +- memmove-avx512-no-vzeroupper \ +- strcasecmp_l-sse2 strcasecmp_l-ssse3 \ +- strcasecmp_l-sse4_2 strcasecmp_l-avx \ +- strncase_l-sse2 strncase_l-ssse3 \ +- strncase_l-sse4_2 strncase_l-avx \ +- strchr-sse2 strchrnul-sse2 strchr-avx2 strchrnul-avx2 \ +- strrchr-sse2 strrchr-avx2 \ +- strlen-sse2 strnlen-sse2 strlen-avx2 strnlen-avx2 \ +- strcat-avx2 strncat-avx2 \ +- strcat-ssse3 strncat-ssse3\ +- strcpy-avx2 strncpy-avx2 \ +- strcpy-sse2 stpcpy-sse2 \ +- strcpy-ssse3 strncpy-ssse3 stpcpy-ssse3 stpncpy-ssse3 \ +- strcpy-sse2-unaligned strncpy-sse2-unaligned \ +- stpcpy-sse2-unaligned stpncpy-sse2-unaligned \ +- stpcpy-avx2 stpncpy-avx2 \ +- strcat-sse2 \ +- strcat-sse2-unaligned strncat-sse2-unaligned \ +- strchr-sse2-no-bsf memcmp-ssse3 strstr-sse2-unaligned \ +- strcspn-sse2 strpbrk-sse2 strspn-sse2 \ +- strcspn-c strpbrk-c strspn-c varshift \ +- memset-avx512-no-vzeroupper \ +- memmove-sse2-unaligned-erms \ +- memmove-avx-unaligned-erms \ +- memmove-avx512-unaligned-erms \ +- memset-sse2-unaligned-erms \ +- memset-avx2-unaligned-erms \ +- memset-avx512-unaligned-erms \ +- memchr-avx2-rtm \ +- memcmp-avx2-movbe-rtm \ +- memcmpeq-avx2-rtm \ +- memmove-avx-unaligned-erms-rtm \ +- memrchr-avx2-rtm \ +- memset-avx2-unaligned-erms-rtm \ +- rawmemchr-avx2-rtm \ +- strchr-avx2-rtm \ +- strcmp-avx2-rtm \ +- strchrnul-avx2-rtm \ +- stpcpy-avx2-rtm \ +- stpncpy-avx2-rtm \ +- strcat-avx2-rtm \ +- strcpy-avx2-rtm \ +- strlen-avx2-rtm \ +- strncat-avx2-rtm \ +- strncmp-avx2-rtm \ +- strncpy-avx2-rtm \ +- strnlen-avx2-rtm \ +- strrchr-avx2-rtm \ +- memchr-evex \ +- memcmp-evex-movbe \ +- memcmpeq-evex \ +- memmove-evex-unaligned-erms \ +- memrchr-evex \ +- memset-evex-unaligned-erms \ +- rawmemchr-evex \ +- stpcpy-evex \ +- stpncpy-evex \ +- strcat-evex \ +- strchr-evex \ +- strchrnul-evex \ +- strcmp-evex \ +- strcpy-evex \ +- strlen-evex \ +- strncat-evex \ +- strncmp-evex \ +- strncpy-evex \ +- strnlen-evex \ +- strrchr-evex \ +- memchr-evex-rtm \ +- rawmemchr-evex-rtm ++sysdep_routines += \ ++ memchr-avx2 \ ++ memchr-avx2-rtm \ ++ memchr-evex \ ++ memchr-evex-rtm \ ++ memchr-sse2 \ ++ memcmp-avx2-movbe \ ++ memcmp-avx2-movbe-rtm \ ++ memcmp-evex-movbe \ ++ memcmp-sse2 \ ++ memcmp-ssse3 \ ++ memcmpeq-avx2 \ ++ memcmpeq-avx2-rtm \ ++ memcmpeq-evex \ ++ memcmpeq-sse2 \ ++ memcpy-ssse3 \ ++ memcpy-ssse3-back \ ++ memmove-avx-unaligned-erms \ ++ memmove-avx-unaligned-erms-rtm \ ++ memmove-avx512-no-vzeroupper \ ++ memmove-avx512-unaligned-erms \ ++ memmove-erms \ ++ memmove-evex-unaligned-erms \ ++ memmove-sse2-unaligned-erms \ ++ memmove-ssse3 \ ++ memmove-ssse3-back \ ++ memrchr-avx2 \ ++ memrchr-avx2-rtm \ ++ memrchr-evex \ ++ memrchr-sse2 \ ++ memset-avx2-unaligned-erms \ ++ memset-avx2-unaligned-erms-rtm \ ++ memset-avx512-no-vzeroupper \ ++ memset-avx512-unaligned-erms \ ++ memset-erms \ ++ memset-evex-unaligned-erms \ ++ memset-sse2-unaligned-erms \ ++ rawmemchr-avx2 \ ++ rawmemchr-avx2-rtm \ ++ rawmemchr-evex \ ++ rawmemchr-evex-rtm \ ++ rawmemchr-sse2 \ ++ stpcpy-avx2 \ ++ stpcpy-avx2-rtm \ ++ stpcpy-evex \ ++ stpcpy-sse2 \ ++ stpcpy-sse2-unaligned \ ++ stpcpy-ssse3 \ ++ stpncpy-avx2 \ ++ stpncpy-avx2-rtm \ ++ stpncpy-c \ ++ stpncpy-evex \ ++ stpncpy-sse2-unaligned \ ++ stpncpy-ssse3 \ ++ strcasecmp_l-avx2 \ ++ strcasecmp_l-avx2-rtm \ ++ strcasecmp_l-evex \ ++ strcasecmp_l-sse2 \ ++ strcasecmp_l-sse4_2 \ ++ strcasecmp_l-ssse3 \ ++ strcat-avx2 \ ++ strcat-avx2-rtm \ ++ strcat-evex \ ++ strcat-sse2 \ ++ strcat-sse2-unaligned \ ++ strcat-ssse3 \ ++ strchr-avx2 \ ++ strchr-avx2-rtm \ ++ strchr-evex \ ++ strchr-sse2 \ ++ strchr-sse2-no-bsf \ ++ strchrnul-avx2 \ ++ strchrnul-avx2-rtm \ ++ strchrnul-evex \ ++ strchrnul-sse2 \ ++ strcmp-avx2 \ ++ strcmp-avx2-rtm \ ++ strcmp-evex \ ++ strcmp-sse2 \ ++ strcmp-sse2-unaligned \ ++ strcmp-sse4_2 \ ++ strcmp-ssse3 \ ++ strcpy-avx2 \ ++ strcpy-avx2-rtm \ ++ strcpy-evex \ ++ strcpy-sse2 \ ++ strcpy-sse2-unaligned \ ++ strcpy-ssse3 \ ++ strcspn-c \ ++ strcspn-sse2 \ ++ strlen-avx2 \ ++ strlen-avx2-rtm \ ++ strlen-evex \ ++ strlen-evex512 \ ++ strlen-sse2 \ ++ strncase_l-avx2 \ ++ strncase_l-avx2-rtm \ ++ strncase_l-evex \ ++ strncase_l-sse2 \ ++ strncase_l-sse4_2 \ ++ strncase_l-ssse3 \ ++ strncat-avx2 \ ++ strncat-avx2-rtm \ ++ strncat-c \ ++ strncat-evex \ ++ strncat-sse2-unaligned \ ++ strncat-ssse3 \ ++ strncmp-avx2 \ ++ strncmp-avx2-rtm \ ++ strncmp-evex \ ++ strncmp-sse2 \ ++ strncmp-sse4_2 \ ++ strncmp-ssse3 \ ++ strncpy-avx2 \ ++ strncpy-avx2-rtm \ ++ strncpy-c \ ++ strncpy-evex \ ++ strncpy-sse2-unaligned \ ++ strncpy-ssse3 \ ++ strnlen-avx2 \ ++ strnlen-avx2-rtm \ ++ strnlen-evex \ ++ strnlen-evex512 \ ++ strnlen-sse2 \ ++ strpbrk-c \ ++ strpbrk-sse2 \ ++ strrchr-avx2 \ ++ strrchr-avx2-rtm \ ++ strrchr-evex \ ++ strrchr-sse2 \ ++ strspn-c \ ++ strspn-sse2 \ ++ strstr-avx512 \ ++ strstr-sse2-unaligned \ ++ varshift \ ++# sysdep_routines + CFLAGS-varshift.c += -msse4 + CFLAGS-strcspn-c.c += -msse4 + CFLAGS-strpbrk-c.c += -msse4 + CFLAGS-strspn-c.c += -msse4 ++CFLAGS-strstr-avx512.c += -mavx512f -mavx512vl -mavx512dq -mavx512bw -mbmi -mbmi2 -O3 + endif + + ifeq ($(subdir),wcsmbs) +-sysdep_routines += wmemcmp-sse4 wmemcmp-ssse3 wmemcmp-c \ +- wmemcmp-avx2-movbe \ +- wmemchr-sse2 wmemchr-avx2 \ +- wcscmp-sse2 wcscmp-avx2 \ +- wcsncmp-sse2 wcsncmp-avx2 \ +- wcscpy-ssse3 wcscpy-c \ +- wcschr-sse2 wcschr-avx2 \ +- wcsrchr-sse2 wcsrchr-avx2 \ +- wcslen-sse2 wcslen-sse4_1 wcslen-avx2 \ +- wcsnlen-c wcsnlen-sse4_1 wcsnlen-avx2 \ +- wcschr-avx2-rtm \ +- wcscmp-avx2-rtm \ +- wcslen-avx2-rtm \ +- wcsncmp-avx2-rtm \ +- wcsnlen-avx2-rtm \ +- wcsrchr-avx2-rtm \ +- wmemchr-avx2-rtm \ +- wmemcmp-avx2-movbe-rtm \ +- wcschr-evex \ +- wcscmp-evex \ +- wcslen-evex \ +- wcsncmp-evex \ +- wcsnlen-evex \ +- wcsrchr-evex \ +- wmemchr-evex \ +- wmemcmp-evex-movbe \ +- wmemchr-evex-rtm ++sysdep_routines += \ ++ wcschr-avx2 \ ++ wcschr-avx2-rtm \ ++ wcschr-evex \ ++ wcschr-sse2 \ ++ wcscmp-avx2 \ ++ wcscmp-avx2-rtm \ ++ wcscmp-evex \ ++ wcscmp-sse2 \ ++ wcscpy-c \ ++ wcscpy-ssse3 \ ++ wcslen-avx2 \ ++ wcslen-avx2-rtm \ ++ wcslen-evex \ ++ wcslen-evex512 \ ++ wcslen-sse2 \ ++ wcslen-sse4_1 \ ++ wcsncmp-avx2 \ ++ wcsncmp-avx2-rtm \ ++ wcsncmp-evex \ ++ wcsncmp-sse2 \ ++ wcsnlen-avx2 \ ++ wcsnlen-avx2-rtm \ ++ wcsnlen-c \ ++ wcsnlen-evex \ ++ wcsnlen-evex512 \ ++ wcsnlen-sse4_1 \ ++ wcsrchr-avx2 \ ++ wcsrchr-avx2-rtm \ ++ wcsrchr-evex \ ++ wcsrchr-sse2 \ ++ wmemchr-avx2 \ ++ wmemchr-avx2-rtm \ ++ wmemchr-evex \ ++ wmemchr-evex-rtm \ ++ wmemchr-sse2 \ ++ wmemcmp-avx2-movbe \ ++ wmemcmp-avx2-movbe-rtm \ ++ wmemcmp-evex-movbe \ ++ wmemcmp-sse2 \ ++ wmemcmp-ssse3 \ ++# sysdep_routines + endif + + ifeq ($(subdir),debug) +-sysdep_routines += memcpy_chk-nonshared mempcpy_chk-nonshared \ +- memmove_chk-nonshared memset_chk-nonshared \ +- wmemset_chk-nonshared ++sysdep_routines += \ ++ memcpy_chk-nonshared \ ++ memmove_chk-nonshared \ ++ mempcpy_chk-nonshared \ ++ memset_chk-nonshared \ ++ wmemset_chk-nonshared \ ++# sysdep_routines + endif +diff -Nuar a/sysdeps/x86_64/multiarch/memchr-avx2-rtm.S b/sysdeps/x86_64/multiarch/memchr-avx2-rtm.S +--- a/sysdeps/x86_64/multiarch/memchr-avx2-rtm.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memchr-avx2-rtm.S 2025-10-20 21:02:19.000000000 +0300 +@@ -2,6 +2,7 @@ + # define MEMCHR __memchr_avx2_rtm + #endif + ++#define COND_VZEROUPPER COND_VZEROUPPER_XTEST + #define ZERO_UPPER_VEC_REGISTERS_RETURN \ + ZERO_UPPER_VEC_REGISTERS_RETURN_XTEST + +diff -Nuar a/sysdeps/x86_64/multiarch/memchr-avx2.S b/sysdeps/x86_64/multiarch/memchr-avx2.S +--- a/sysdeps/x86_64/multiarch/memchr-avx2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memchr-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -57,7 +57,7 @@ + # define CHAR_PER_VEC (VEC_SIZE / CHAR_SIZE) + + .section SECTION(.text),"ax",@progbits +-ENTRY (MEMCHR) ++ENTRY_P2ALIGN (MEMCHR, 5) + # ifndef USE_AS_RAWMEMCHR + /* Check for zero length. */ + # ifdef __ILP32__ +@@ -87,12 +87,14 @@ + # endif + testl %eax, %eax + jz L(aligned_more) +- tzcntl %eax, %eax ++ bsfl %eax, %eax + addq %rdi, %rax +- VZEROUPPER_RETURN ++L(return_vzeroupper): ++ ZERO_UPPER_VEC_REGISTERS_RETURN ++ + + # ifndef USE_AS_RAWMEMCHR +- .p2align 5 ++ .p2align 4 + L(first_vec_x0): + /* Check if first match was before length. */ + tzcntl %eax, %eax +@@ -100,58 +102,31 @@ + /* NB: Multiply length by 4 to get byte count. */ + sall $2, %edx + # endif +- xorl %ecx, %ecx ++ COND_VZEROUPPER ++ /* Use branch instead of cmovcc so L(first_vec_x0) fits in one fetch ++ block. branch here as opposed to cmovcc is not that costly. Common ++ usage of memchr is to check if the return was NULL (if string was ++ known to contain CHAR user would use rawmemchr). This branch will be ++ highly correlated with the user branch and can be used by most ++ modern branch predictors to predict the user branch. */ + cmpl %eax, %edx +- leaq (%rdi, %rax), %rax +- cmovle %rcx, %rax +- VZEROUPPER_RETURN +- +-L(null): +- xorl %eax, %eax +- ret +-# endif +- .p2align 4 +-L(cross_page_boundary): +- /* Save pointer before aligning as its original value is +- necessary for computer return address if byte is found or +- adjusting length if it is not and this is memchr. */ +- movq %rdi, %rcx +- /* Align data to VEC_SIZE - 1. ALGN_PTR_REG is rcx for memchr +- and rdi for rawmemchr. */ +- orq $(VEC_SIZE - 1), %ALGN_PTR_REG +- VPCMPEQ -(VEC_SIZE - 1)(%ALGN_PTR_REG), %ymm0, %ymm1 +- vpmovmskb %ymm1, %eax +-# ifndef USE_AS_RAWMEMCHR +- /* Calculate length until end of page (length checked for a +- match). */ +- leaq 1(%ALGN_PTR_REG), %rsi +- subq %RRAW_PTR_REG, %rsi +-# ifdef USE_AS_WMEMCHR +- /* NB: Divide bytes by 4 to get wchar_t count. */ +- shrl $2, %esi +-# endif +-# endif +- /* Remove the leading bytes. */ +- sarxl %ERAW_PTR_REG, %eax, %eax +-# ifndef USE_AS_RAWMEMCHR +- /* Check the end of data. */ +- cmpq %rsi, %rdx +- jbe L(first_vec_x0) ++ jle L(null) ++ addq %rdi, %rax ++ ret + # endif +- testl %eax, %eax +- jz L(cross_page_continue) +- tzcntl %eax, %eax +- addq %RRAW_PTR_REG, %rax +-L(return_vzeroupper): +- ZERO_UPPER_VEC_REGISTERS_RETURN + +- .p2align 4 ++ .p2align 4,, 10 + L(first_vec_x1): +- tzcntl %eax, %eax ++ bsfl %eax, %eax + incq %rdi + addq %rdi, %rax + VZEROUPPER_RETURN +- ++# ifndef USE_AS_RAWMEMCHR ++ /* First in aligning bytes here. */ ++L(null): ++ xorl %eax, %eax ++ ret ++# endif + .p2align 4 + L(first_vec_x2): + tzcntl %eax, %eax +@@ -340,7 +315,7 @@ + incq %rdi + addq %rdi, %rax + VZEROUPPER_RETURN +- .p2align 4 ++ .p2align 4,, 6 + L(set_zero_end): + xorl %eax, %eax + VZEROUPPER_RETURN +@@ -428,5 +403,39 @@ + VZEROUPPER_RETURN + # endif + ++ .p2align 4 ++L(cross_page_boundary): ++ /* Save pointer before aligning as its original value is necessary for ++ computer return address if byte is found or adjusting length if it ++ is not and this is memchr. */ ++ movq %rdi, %rcx ++ /* Align data to VEC_SIZE - 1. ALGN_PTR_REG is rcx for memchr ++ and rdi for rawmemchr. */ ++ orq $(VEC_SIZE - 1), %ALGN_PTR_REG ++ VPCMPEQ -(VEC_SIZE - 1)(%ALGN_PTR_REG), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %eax ++# ifndef USE_AS_RAWMEMCHR ++ /* Calculate length until end of page (length checked for a match). */ ++ leaq 1(%ALGN_PTR_REG), %rsi ++ subq %RRAW_PTR_REG, %rsi ++# ifdef USE_AS_WMEMCHR ++ /* NB: Divide bytes by 4 to get wchar_t count. */ ++ shrl $2, %esi ++# endif ++# endif ++ /* Remove the leading bytes. */ ++ sarxl %ERAW_PTR_REG, %eax, %eax ++# ifndef USE_AS_RAWMEMCHR ++ /* Check the end of data. */ ++ cmpq %rsi, %rdx ++ jbe L(first_vec_x0) ++# endif ++ testl %eax, %eax ++ jz L(cross_page_continue) ++ bsfl %eax, %eax ++ addq %RRAW_PTR_REG, %rax ++ VZEROUPPER_RETURN ++ ++ + END (MEMCHR) + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/memchr-evex.S b/sysdeps/x86_64/multiarch/memchr-evex.S +--- a/sysdeps/x86_64/multiarch/memchr-evex.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memchr-evex.S 2025-10-20 21:02:22.000000000 +0300 +@@ -88,7 +88,7 @@ + # define PAGE_SIZE 4096 + + .section SECTION(.text),"ax",@progbits +-ENTRY (MEMCHR) ++ENTRY_P2ALIGN (MEMCHR, 6) + # ifndef USE_AS_RAWMEMCHR + /* Check for zero length. */ + test %RDX_LP, %RDX_LP +@@ -131,22 +131,24 @@ + xorl %eax, %eax + ret + +- .p2align 5 ++ .p2align 4 + L(first_vec_x0): +- /* Check if first match was before length. */ +- tzcntl %eax, %eax +- xorl %ecx, %ecx +- cmpl %eax, %edx +- leaq (%rdi, %rax, CHAR_SIZE), %rax +- cmovle %rcx, %rax ++ /* Check if first match was before length. NB: tzcnt has false data- ++ dependency on destination. eax already had a data-dependency on esi ++ so this should have no affect here. */ ++ tzcntl %eax, %esi ++# ifdef USE_AS_WMEMCHR ++ leaq (%rdi, %rsi, CHAR_SIZE), %rdi ++# else ++ addq %rsi, %rdi ++# endif ++ xorl %eax, %eax ++ cmpl %esi, %edx ++ cmovg %rdi, %rax + ret +-# else +- /* NB: first_vec_x0 is 17 bytes which will leave +- cross_page_boundary (which is relatively cold) close enough +- to ideal alignment. So only realign L(cross_page_boundary) if +- rawmemchr. */ +- .p2align 4 + # endif ++ ++ .p2align 4 + L(cross_page_boundary): + /* Save pointer before aligning as its original value is + necessary for computer return address if byte is found or +@@ -400,10 +402,14 @@ + L(zero_end): + ret + ++L(set_zero_end): ++ xorl %eax, %eax ++ ret + + .p2align 4 + L(first_vec_x1_check): +- tzcntl %eax, %eax ++ /* eax must be non-zero. Use bsfl to save code size. */ ++ bsfl %eax, %eax + /* Adjust length. */ + subl $-(CHAR_PER_VEC * 4), %edx + /* Check if match within remaining length. */ +@@ -412,9 +418,6 @@ + /* NB: Multiply bytes by CHAR_SIZE to get the wchar_t count. */ + leaq VEC_SIZE(%rdi, %rax, CHAR_SIZE), %rax + ret +-L(set_zero_end): +- xorl %eax, %eax +- ret + + .p2align 4 + L(loop_4x_vec_end): +@@ -464,7 +467,7 @@ + # endif + ret + +- .p2align 4 ++ .p2align 4,, 10 + L(last_vec_x1_return): + tzcntl %eax, %eax + # if defined USE_AS_WMEMCHR || RET_OFFSET != 0 +@@ -496,6 +499,7 @@ + # endif + + # ifndef USE_AS_RAWMEMCHR ++ .p2align 4,, 5 + L(last_4x_vec_or_less_cmpeq): + VPCMP $0, (VEC_SIZE * 5)(%rdi), %YMMMATCH, %k0 + kmovd %k0, %eax +@@ -546,7 +550,7 @@ + # endif + andl %ecx, %eax + jz L(zero_end2) +- tzcntl %eax, %eax ++ bsfl %eax, %eax + leaq (VEC_SIZE * 4)(%rdi, %rax, CHAR_SIZE), %rax + L(zero_end2): + ret +@@ -562,6 +566,6 @@ + leaq (VEC_SIZE * 3)(%rdi, %rax, CHAR_SIZE), %rax + ret + # endif +- ++ /* 7 bytes from next cache line. */ + END (MEMCHR) + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/memcmp-avx2-movbe.S b/sysdeps/x86_64/multiarch/memcmp-avx2-movbe.S +--- a/sysdeps/x86_64/multiarch/memcmp-avx2-movbe.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memcmp-avx2-movbe.S 2025-10-20 21:02:22.000000000 +0300 +@@ -429,22 +429,21 @@ + # ifndef USE_AS_WMEMCMP + cmpl $8, %edx + jae L(between_8_15) ++ /* Fall through for [4, 7]. */ + cmpl $4, %edx +- jae L(between_4_7) ++ jb L(between_2_3) + +- /* Load as big endian to avoid branches. */ +- movzwl (%rdi), %eax +- movzwl (%rsi), %ecx +- shll $8, %eax +- shll $8, %ecx +- bswap %eax +- bswap %ecx +- movzbl -1(%rdi, %rdx), %edi +- movzbl -1(%rsi, %rdx), %esi +- orl %edi, %eax +- orl %esi, %ecx +- /* Subtraction is okay because the upper 8 bits are zero. */ +- subl %ecx, %eax ++ movbe (%rdi), %eax ++ movbe (%rsi), %ecx ++ shlq $32, %rax ++ shlq $32, %rcx ++ movbe -4(%rdi, %rdx), %edi ++ movbe -4(%rsi, %rdx), %esi ++ orq %rdi, %rax ++ orq %rsi, %rcx ++ subq %rcx, %rax ++ /* Fast path for return zero. */ ++ jnz L(ret_nonzero) + /* No ymm register was touched. */ + ret + +@@ -457,9 +456,33 @@ + /* No ymm register was touched. */ + ret + ++ .p2align 4,, 5 ++L(ret_nonzero): ++ sbbl %eax, %eax ++ orl $1, %eax ++ /* No ymm register was touched. */ ++ ret ++ ++ .p2align 4,, 2 ++L(zero): ++ xorl %eax, %eax ++ /* No ymm register was touched. */ ++ ret ++ + .p2align 4 + L(between_8_15): +-# endif ++ movbe (%rdi), %rax ++ movbe (%rsi), %rcx ++ subq %rcx, %rax ++ jnz L(ret_nonzero) ++ movbe -8(%rdi, %rdx), %rax ++ movbe -8(%rsi, %rdx), %rcx ++ subq %rcx, %rax ++ /* Fast path for return zero. */ ++ jnz L(ret_nonzero) ++ /* No ymm register was touched. */ ++ ret ++# else + /* If USE_AS_WMEMCMP fall through into 8-15 byte case. */ + vmovq (%rdi), %xmm1 + vmovq (%rsi), %xmm2 +@@ -475,16 +498,13 @@ + VPCMPEQ %xmm1, %xmm2, %xmm2 + vpmovmskb %xmm2, %eax + subl $0xffff, %eax ++ /* Fast path for return zero. */ + jnz L(return_vec_0) + /* No ymm register was touched. */ + ret ++# endif + +- .p2align 4 +-L(zero): +- xorl %eax, %eax +- ret +- +- .p2align 4 ++ .p2align 4,, 10 + L(between_16_31): + /* From 16 to 31 bytes. No branch when size == 16. */ + vmovdqu (%rsi), %xmm2 +@@ -501,11 +521,17 @@ + VPCMPEQ (%rdi), %xmm2, %xmm2 + vpmovmskb %xmm2, %eax + subl $0xffff, %eax ++ /* Fast path for return zero. */ + jnz L(return_vec_0) + /* No ymm register was touched. */ + ret + + # ifdef USE_AS_WMEMCMP ++ .p2align 4,, 2 ++L(zero): ++ xorl %eax, %eax ++ ret ++ + .p2align 4 + L(one_or_less): + jb L(zero) +@@ -520,22 +546,20 @@ + # else + + .p2align 4 +-L(between_4_7): +- /* Load as big endian with overlapping movbe to avoid branches. +- */ +- movbe (%rdi), %eax +- movbe (%rsi), %ecx +- shlq $32, %rax +- shlq $32, %rcx +- movbe -4(%rdi, %rdx), %edi +- movbe -4(%rsi, %rdx), %esi +- orq %rdi, %rax +- orq %rsi, %rcx +- subq %rcx, %rax +- jz L(zero_4_7) +- sbbl %eax, %eax +- orl $1, %eax +-L(zero_4_7): ++L(between_2_3): ++ /* Load as big endian to avoid branches. */ ++ movzwl (%rdi), %eax ++ movzwl (%rsi), %ecx ++ bswap %eax ++ bswap %ecx ++ shrl %eax ++ shrl %ecx ++ movzbl -1(%rdi, %rdx), %edi ++ movzbl -1(%rsi, %rdx), %esi ++ orl %edi, %eax ++ orl %esi, %ecx ++ /* Subtraction is okay because the upper bit is zero. */ ++ subl %ecx, %eax + /* No ymm register was touched. */ + ret + # endif +diff -Nuar a/sysdeps/x86_64/multiarch/memcmpeq-sse2.S b/sysdeps/x86_64/multiarch/memcmpeq-sse2.S +--- a/sysdeps/x86_64/multiarch/memcmpeq-sse2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memcmpeq-sse2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -16,8 +16,10 @@ + License along with the GNU C Library; if not, see + . */ + +-#ifndef memcmp +-# define memcmp __memcmpeq_sse2 ++#if IS_IN (libc) ++# define MEMCMP __memcmpeq_sse2 ++#else ++# define MEMCMP __memcmpeq + #endif + #define USE_AS_MEMCMPEQ 1 + #include "memcmp-sse2.S" +diff -Nuar a/sysdeps/x86_64/multiarch/memcmp-sse2.S b/sysdeps/x86_64/multiarch/memcmp-sse2.S +--- a/sysdeps/x86_64/multiarch/memcmp-sse2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memcmp-sse2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -17,8 +17,8 @@ + . */ + + #if IS_IN (libc) +-# ifndef memcmp +-# define memcmp __memcmp_sse2 ++# ifndef MEMCMP ++# define MEMCMP __memcmp_sse2 + # endif + + # ifdef SHARED +diff -Nuar a/sysdeps/x86_64/multiarch/memcmp-sse4.S b/sysdeps/x86_64/multiarch/memcmp-sse4.S +--- a/sysdeps/x86_64/multiarch/memcmp-sse4.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memcmp-sse4.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,803 +0,0 @@ +-/* memcmp with SSE4.1, wmemcmp with SSE4.1 +- Copyright (C) 2010-2022 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 +- . */ +- +-#if IS_IN (libc) +- +-# include +- +-# ifndef MEMCMP +-# define MEMCMP __memcmp_sse4_1 +-# endif +- +-#ifdef USE_AS_WMEMCMP +-# define CMPEQ pcmpeqd +-# define CHAR_SIZE 4 +-#else +-# define CMPEQ pcmpeqb +-# define CHAR_SIZE 1 +-#endif +- +- +-/* Warning! +- wmemcmp has to use SIGNED comparison for elements. +- memcmp has to use UNSIGNED comparison for elemnts. +-*/ +- +- .section .text.sse4.1,"ax",@progbits +-ENTRY (MEMCMP) +-# ifdef USE_AS_WMEMCMP +- shl $2, %RDX_LP +-# elif defined __ILP32__ +- /* Clear the upper 32 bits. */ +- mov %edx, %edx +-# endif +- cmp $79, %RDX_LP +- ja L(79bytesormore) +- +- cmp $CHAR_SIZE, %RDX_LP +- jbe L(firstbyte) +- +- /* N in (CHAR_SIZE, 79) bytes. */ +- cmpl $32, %edx +- ja L(more_32_bytes) +- +- cmpl $16, %edx +- jae L(16_to_32_bytes) +- +-# ifndef USE_AS_WMEMCMP +- cmpl $8, %edx +- jae L(8_to_16_bytes) +- +- cmpl $4, %edx +- jb L(2_to_3_bytes) +- +- movl (%rdi), %eax +- movl (%rsi), %ecx +- +- bswap %eax +- bswap %ecx +- +- shlq $32, %rax +- shlq $32, %rcx +- +- movl -4(%rdi, %rdx), %edi +- movl -4(%rsi, %rdx), %esi +- +- bswap %edi +- bswap %esi +- +- orq %rdi, %rax +- orq %rsi, %rcx +- subq %rcx, %rax +- cmovne %edx, %eax +- sbbl %ecx, %ecx +- orl %ecx, %eax +- ret +- +- .p2align 4,, 8 +-L(2_to_3_bytes): +- movzwl (%rdi), %eax +- movzwl (%rsi), %ecx +- shll $8, %eax +- shll $8, %ecx +- bswap %eax +- bswap %ecx +- movzbl -1(%rdi, %rdx), %edi +- movzbl -1(%rsi, %rdx), %esi +- orl %edi, %eax +- orl %esi, %ecx +- subl %ecx, %eax +- ret +- +- .p2align 4,, 8 +-L(8_to_16_bytes): +- movq (%rdi), %rax +- movq (%rsi), %rcx +- +- bswap %rax +- bswap %rcx +- +- subq %rcx, %rax +- jne L(8_to_16_bytes_done) +- +- movq -8(%rdi, %rdx), %rax +- movq -8(%rsi, %rdx), %rcx +- +- bswap %rax +- bswap %rcx +- +- subq %rcx, %rax +- +-L(8_to_16_bytes_done): +- cmovne %edx, %eax +- sbbl %ecx, %ecx +- orl %ecx, %eax +- ret +-# else +- xorl %eax, %eax +- movl (%rdi), %ecx +- cmpl (%rsi), %ecx +- jne L(8_to_16_bytes_done) +- movl 4(%rdi), %ecx +- cmpl 4(%rsi), %ecx +- jne L(8_to_16_bytes_done) +- movl -4(%rdi, %rdx), %ecx +- cmpl -4(%rsi, %rdx), %ecx +- jne L(8_to_16_bytes_done) +- ret +-# endif +- +- .p2align 4,, 3 +-L(ret_zero): +- xorl %eax, %eax +-L(zero): +- ret +- +- .p2align 4,, 8 +-L(firstbyte): +- jb L(ret_zero) +-# ifdef USE_AS_WMEMCMP +- xorl %eax, %eax +- movl (%rdi), %ecx +- cmpl (%rsi), %ecx +- je L(zero) +-L(8_to_16_bytes_done): +- setg %al +- leal -1(%rax, %rax), %eax +-# else +- movzbl (%rdi), %eax +- movzbl (%rsi), %ecx +- sub %ecx, %eax +-# endif +- ret +- +- .p2align 4 +-L(vec_return_begin_48): +- addq $16, %rdi +- addq $16, %rsi +-L(vec_return_begin_32): +- bsfl %eax, %eax +-# ifdef USE_AS_WMEMCMP +- movl 32(%rdi, %rax), %ecx +- xorl %edx, %edx +- cmpl 32(%rsi, %rax), %ecx +- setg %dl +- leal -1(%rdx, %rdx), %eax +-# else +- movzbl 32(%rsi, %rax), %ecx +- movzbl 32(%rdi, %rax), %eax +- subl %ecx, %eax +-# endif +- ret +- +- .p2align 4 +-L(vec_return_begin_16): +- addq $16, %rdi +- addq $16, %rsi +-L(vec_return_begin): +- bsfl %eax, %eax +-# ifdef USE_AS_WMEMCMP +- movl (%rdi, %rax), %ecx +- xorl %edx, %edx +- cmpl (%rsi, %rax), %ecx +- setg %dl +- leal -1(%rdx, %rdx), %eax +-# else +- movzbl (%rsi, %rax), %ecx +- movzbl (%rdi, %rax), %eax +- subl %ecx, %eax +-# endif +- ret +- +- .p2align 4 +-L(vec_return_end_16): +- subl $16, %edx +-L(vec_return_end): +- bsfl %eax, %eax +- addl %edx, %eax +-# ifdef USE_AS_WMEMCMP +- movl -16(%rdi, %rax), %ecx +- xorl %edx, %edx +- cmpl -16(%rsi, %rax), %ecx +- setg %dl +- leal -1(%rdx, %rdx), %eax +-# else +- movzbl -16(%rsi, %rax), %ecx +- movzbl -16(%rdi, %rax), %eax +- subl %ecx, %eax +-# endif +- ret +- +- .p2align 4,, 8 +-L(more_32_bytes): +- movdqu (%rdi), %xmm0 +- movdqu (%rsi), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqu 16(%rdi), %xmm0 +- movdqu 16(%rsi), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_16) +- +- cmpl $64, %edx +- jbe L(32_to_64_bytes) +- movdqu 32(%rdi), %xmm0 +- movdqu 32(%rsi), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_32) +- +- .p2align 4,, 6 +-L(32_to_64_bytes): +- movdqu -32(%rdi, %rdx), %xmm0 +- movdqu -32(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end_16) +- +- movdqu -16(%rdi, %rdx), %xmm0 +- movdqu -16(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end) +- ret +- +- .p2align 4 +-L(16_to_32_bytes): +- movdqu (%rdi), %xmm0 +- movdqu (%rsi), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqu -16(%rdi, %rdx), %xmm0 +- movdqu -16(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end) +- ret +- +- +- .p2align 4 +-L(79bytesormore): +- movdqu (%rdi), %xmm0 +- movdqu (%rsi), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- +- mov %rsi, %rcx +- and $-16, %rsi +- add $16, %rsi +- sub %rsi, %rcx +- +- sub %rcx, %rdi +- add %rcx, %rdx +- test $0xf, %rdi +- jz L(2aligned) +- +- cmp $128, %rdx +- ja L(128bytesormore) +- +- .p2align 4,, 6 +-L(less128bytes): +- movdqu (%rdi), %xmm1 +- CMPEQ (%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqu 16(%rdi), %xmm1 +- CMPEQ 16(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_16) +- +- movdqu 32(%rdi), %xmm1 +- CMPEQ 32(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_32) +- +- movdqu 48(%rdi), %xmm1 +- CMPEQ 48(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_48) +- +- cmp $96, %rdx +- jb L(32_to_64_bytes) +- +- addq $64, %rdi +- addq $64, %rsi +- subq $64, %rdx +- +- .p2align 4,, 6 +-L(last_64_bytes): +- movdqu (%rdi), %xmm1 +- CMPEQ (%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqu 16(%rdi), %xmm1 +- CMPEQ 16(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_16) +- +- movdqu -32(%rdi, %rdx), %xmm0 +- movdqu -32(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end_16) +- +- movdqu -16(%rdi, %rdx), %xmm0 +- movdqu -16(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end) +- ret +- +- .p2align 4 +-L(128bytesormore): +- cmp $256, %rdx +- ja L(unaligned_loop) +-L(less256bytes): +- movdqu (%rdi), %xmm1 +- CMPEQ (%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqu 16(%rdi), %xmm1 +- CMPEQ 16(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_16) +- +- movdqu 32(%rdi), %xmm1 +- CMPEQ 32(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_32) +- +- movdqu 48(%rdi), %xmm1 +- CMPEQ 48(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_48) +- +- addq $64, %rdi +- addq $64, %rsi +- +- movdqu (%rdi), %xmm1 +- CMPEQ (%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqu 16(%rdi), %xmm1 +- CMPEQ 16(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_16) +- +- movdqu 32(%rdi), %xmm1 +- CMPEQ 32(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_32) +- +- movdqu 48(%rdi), %xmm1 +- CMPEQ 48(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_48) +- +- addq $-128, %rdx +- subq $-64, %rsi +- subq $-64, %rdi +- +- cmp $64, %rdx +- ja L(less128bytes) +- +- cmp $32, %rdx +- ja L(last_64_bytes) +- +- movdqu -32(%rdi, %rdx), %xmm0 +- movdqu -32(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end_16) +- +- movdqu -16(%rdi, %rdx), %xmm0 +- movdqu -16(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end) +- ret +- +- .p2align 4 +-L(unaligned_loop): +-# ifdef DATA_CACHE_SIZE_HALF +- mov $DATA_CACHE_SIZE_HALF, %R8_LP +-# else +- mov __x86_data_cache_size_half(%rip), %R8_LP +-# endif +- movq %r8, %r9 +- addq %r8, %r8 +- addq %r9, %r8 +- cmpq %r8, %rdx +- ja L(L2_L3_cache_unaligned) +- sub $64, %rdx +- .p2align 4 +-L(64bytesormore_loop): +- movdqu (%rdi), %xmm0 +- movdqu 16(%rdi), %xmm1 +- movdqu 32(%rdi), %xmm2 +- movdqu 48(%rdi), %xmm3 +- +- CMPEQ (%rsi), %xmm0 +- CMPEQ 16(%rsi), %xmm1 +- CMPEQ 32(%rsi), %xmm2 +- CMPEQ 48(%rsi), %xmm3 +- +- pand %xmm0, %xmm1 +- pand %xmm2, %xmm3 +- pand %xmm1, %xmm3 +- +- pmovmskb %xmm3, %eax +- incw %ax +- jnz L(64bytesormore_loop_end) +- +- add $64, %rsi +- add $64, %rdi +- sub $64, %rdx +- ja L(64bytesormore_loop) +- +- .p2align 4,, 6 +-L(loop_tail): +- addq %rdx, %rdi +- movdqu (%rdi), %xmm0 +- movdqu 16(%rdi), %xmm1 +- movdqu 32(%rdi), %xmm2 +- movdqu 48(%rdi), %xmm3 +- +- addq %rdx, %rsi +- movdqu (%rsi), %xmm4 +- movdqu 16(%rsi), %xmm5 +- movdqu 32(%rsi), %xmm6 +- movdqu 48(%rsi), %xmm7 +- +- CMPEQ %xmm4, %xmm0 +- CMPEQ %xmm5, %xmm1 +- CMPEQ %xmm6, %xmm2 +- CMPEQ %xmm7, %xmm3 +- +- pand %xmm0, %xmm1 +- pand %xmm2, %xmm3 +- pand %xmm1, %xmm3 +- +- pmovmskb %xmm3, %eax +- incw %ax +- jnz L(64bytesormore_loop_end) +- ret +- +-L(L2_L3_cache_unaligned): +- subq $64, %rdx +- .p2align 4 +-L(L2_L3_unaligned_128bytes_loop): +- prefetchnta 0x1c0(%rdi) +- prefetchnta 0x1c0(%rsi) +- +- movdqu (%rdi), %xmm0 +- movdqu 16(%rdi), %xmm1 +- movdqu 32(%rdi), %xmm2 +- movdqu 48(%rdi), %xmm3 +- +- CMPEQ (%rsi), %xmm0 +- CMPEQ 16(%rsi), %xmm1 +- CMPEQ 32(%rsi), %xmm2 +- CMPEQ 48(%rsi), %xmm3 +- +- pand %xmm0, %xmm1 +- pand %xmm2, %xmm3 +- pand %xmm1, %xmm3 +- +- pmovmskb %xmm3, %eax +- incw %ax +- jnz L(64bytesormore_loop_end) +- +- add $64, %rsi +- add $64, %rdi +- sub $64, %rdx +- ja L(L2_L3_unaligned_128bytes_loop) +- jmp L(loop_tail) +- +- +- /* This case is for machines which are sensitive for unaligned +- * instructions. */ +- .p2align 4 +-L(2aligned): +- cmp $128, %rdx +- ja L(128bytesormorein2aligned) +-L(less128bytesin2aligned): +- movdqa (%rdi), %xmm1 +- CMPEQ (%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqa 16(%rdi), %xmm1 +- CMPEQ 16(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_16) +- +- movdqa 32(%rdi), %xmm1 +- CMPEQ 32(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_32) +- +- movdqa 48(%rdi), %xmm1 +- CMPEQ 48(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_48) +- +- cmp $96, %rdx +- jb L(32_to_64_bytes) +- +- addq $64, %rdi +- addq $64, %rsi +- subq $64, %rdx +- +- .p2align 4,, 6 +-L(aligned_last_64_bytes): +- movdqa (%rdi), %xmm1 +- CMPEQ (%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqa 16(%rdi), %xmm1 +- CMPEQ 16(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_16) +- +- movdqu -32(%rdi, %rdx), %xmm0 +- movdqu -32(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end_16) +- +- movdqu -16(%rdi, %rdx), %xmm0 +- movdqu -16(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end) +- ret +- +- .p2align 4 +-L(128bytesormorein2aligned): +- cmp $256, %rdx +- ja L(aligned_loop) +-L(less256bytesin2alinged): +- movdqa (%rdi), %xmm1 +- CMPEQ (%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqa 16(%rdi), %xmm1 +- CMPEQ 16(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_16) +- +- movdqa 32(%rdi), %xmm1 +- CMPEQ 32(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_32) +- +- movdqa 48(%rdi), %xmm1 +- CMPEQ 48(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_48) +- +- addq $64, %rdi +- addq $64, %rsi +- +- movdqa (%rdi), %xmm1 +- CMPEQ (%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin) +- +- movdqa 16(%rdi), %xmm1 +- CMPEQ 16(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_16) +- +- movdqa 32(%rdi), %xmm1 +- CMPEQ 32(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_32) +- +- movdqa 48(%rdi), %xmm1 +- CMPEQ 48(%rsi), %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_begin_48) +- +- addq $-128, %rdx +- subq $-64, %rsi +- subq $-64, %rdi +- +- cmp $64, %rdx +- ja L(less128bytesin2aligned) +- +- cmp $32, %rdx +- ja L(aligned_last_64_bytes) +- +- movdqu -32(%rdi, %rdx), %xmm0 +- movdqu -32(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end_16) +- +- movdqu -16(%rdi, %rdx), %xmm0 +- movdqu -16(%rsi, %rdx), %xmm1 +- CMPEQ %xmm0, %xmm1 +- pmovmskb %xmm1, %eax +- incw %ax +- jnz L(vec_return_end) +- ret +- +- .p2align 4 +-L(aligned_loop): +-# ifdef DATA_CACHE_SIZE_HALF +- mov $DATA_CACHE_SIZE_HALF, %R8_LP +-# else +- mov __x86_data_cache_size_half(%rip), %R8_LP +-# endif +- movq %r8, %r9 +- addq %r8, %r8 +- addq %r9, %r8 +- cmpq %r8, %rdx +- ja L(L2_L3_cache_aligned) +- +- sub $64, %rdx +- .p2align 4 +-L(64bytesormore_loopin2aligned): +- movdqa (%rdi), %xmm0 +- movdqa 16(%rdi), %xmm1 +- movdqa 32(%rdi), %xmm2 +- movdqa 48(%rdi), %xmm3 +- +- CMPEQ (%rsi), %xmm0 +- CMPEQ 16(%rsi), %xmm1 +- CMPEQ 32(%rsi), %xmm2 +- CMPEQ 48(%rsi), %xmm3 +- +- pand %xmm0, %xmm1 +- pand %xmm2, %xmm3 +- pand %xmm1, %xmm3 +- +- pmovmskb %xmm3, %eax +- incw %ax +- jnz L(64bytesormore_loop_end) +- add $64, %rsi +- add $64, %rdi +- sub $64, %rdx +- ja L(64bytesormore_loopin2aligned) +- jmp L(loop_tail) +- +-L(L2_L3_cache_aligned): +- subq $64, %rdx +- .p2align 4 +-L(L2_L3_aligned_128bytes_loop): +- prefetchnta 0x1c0(%rdi) +- prefetchnta 0x1c0(%rsi) +- movdqa (%rdi), %xmm0 +- movdqa 16(%rdi), %xmm1 +- movdqa 32(%rdi), %xmm2 +- movdqa 48(%rdi), %xmm3 +- +- CMPEQ (%rsi), %xmm0 +- CMPEQ 16(%rsi), %xmm1 +- CMPEQ 32(%rsi), %xmm2 +- CMPEQ 48(%rsi), %xmm3 +- +- pand %xmm0, %xmm1 +- pand %xmm2, %xmm3 +- pand %xmm1, %xmm3 +- +- pmovmskb %xmm3, %eax +- incw %ax +- jnz L(64bytesormore_loop_end) +- +- addq $64, %rsi +- addq $64, %rdi +- subq $64, %rdx +- ja L(L2_L3_aligned_128bytes_loop) +- jmp L(loop_tail) +- +- .p2align 4 +-L(64bytesormore_loop_end): +- pmovmskb %xmm0, %ecx +- incw %cx +- jnz L(loop_end_ret) +- +- pmovmskb %xmm1, %ecx +- notw %cx +- sall $16, %ecx +- jnz L(loop_end_ret) +- +- pmovmskb %xmm2, %ecx +- notw %cx +- shlq $32, %rcx +- jnz L(loop_end_ret) +- +- addq $48, %rdi +- addq $48, %rsi +- movq %rax, %rcx +- +- .p2align 4,, 6 +-L(loop_end_ret): +- bsfq %rcx, %rcx +-# ifdef USE_AS_WMEMCMP +- movl (%rdi, %rcx), %eax +- xorl %edx, %edx +- cmpl (%rsi, %rcx), %eax +- setg %dl +- leal -1(%rdx, %rdx), %eax +-# else +- movzbl (%rdi, %rcx), %eax +- movzbl (%rsi, %rcx), %ecx +- subl %ecx, %eax +-# endif +- ret +-END (MEMCMP) +-#endif +diff -Nuar a/sysdeps/x86_64/multiarch/memmove-erms.S b/sysdeps/x86_64/multiarch/memmove-erms.S +--- a/sysdeps/x86_64/multiarch/memmove-erms.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/memmove-erms.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,72 @@ ++/* memcpy/mempcpy/memmove implement with rep movsb ++ Copyright (C) 2022 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 ++ . */ ++ ++ ++#include ++ ++#if defined USE_MULTIARCH && IS_IN (libc) ++ .text ++ENTRY (__mempcpy_chk_erms) ++ cmp %RDX_LP, %RCX_LP ++ jb HIDDEN_JUMPTARGET (__chk_fail) ++END (__mempcpy_chk_erms) ++ ++/* Only used to measure performance of REP MOVSB. */ ++ENTRY (__mempcpy_erms) ++ mov %RDI_LP, %RAX_LP ++ /* Skip zero length. */ ++ test %RDX_LP, %RDX_LP ++ jz 2f ++ add %RDX_LP, %RAX_LP ++ jmp L(start_movsb) ++END (__mempcpy_erms) ++ ++ENTRY (__memmove_chk_erms) ++ cmp %RDX_LP, %RCX_LP ++ jb HIDDEN_JUMPTARGET (__chk_fail) ++END (__memmove_chk_erms) ++ ++ENTRY (__memmove_erms) ++ movq %rdi, %rax ++ /* Skip zero length. */ ++ test %RDX_LP, %RDX_LP ++ jz 2f ++L(start_movsb): ++ mov %RDX_LP, %RCX_LP ++ cmp %RSI_LP, %RDI_LP ++ jb 1f ++ /* Source == destination is less common. */ ++ je 2f ++ lea (%rsi,%rcx), %RDX_LP ++ cmp %RDX_LP, %RDI_LP ++ jb L(movsb_backward) ++1: ++ rep movsb ++2: ++ ret ++L(movsb_backward): ++ leaq -1(%rdi,%rcx), %rdi ++ leaq -1(%rsi,%rcx), %rsi ++ std ++ rep movsb ++ cld ++ ret ++END (__memmove_erms) ++strong_alias (__memmove_erms, __memcpy_erms) ++strong_alias (__memmove_chk_erms, __memcpy_chk_erms) ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S b/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S +--- a/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S 2025-10-20 21:02:22.000000000 +0300 +@@ -118,7 +118,13 @@ + # define LARGE_LOAD_SIZE (VEC_SIZE * 4) + #endif + +-/* Amount to shift rdx by to compare for memcpy_large_4x. */ ++/* Amount to shift __x86_shared_non_temporal_threshold by for ++ bound for memcpy_large_4x. This is essentially use to to ++ indicate that the copy is far beyond the scope of L3 ++ (assuming no user config x86_non_temporal_threshold) and to ++ use a more aggressively unrolled loop. NB: before ++ increasing the value also update initialization of ++ x86_non_temporal_threshold. */ + #ifndef LOG_4X_MEMCPY_THRESH + # define LOG_4X_MEMCPY_THRESH 4 + #endif +@@ -233,56 +239,6 @@ + #endif + #if defined USE_MULTIARCH && IS_IN (libc) + END (MEMMOVE_SYMBOL (__memmove, unaligned)) +-# if VEC_SIZE == 16 +-ENTRY (__mempcpy_chk_erms) +- cmp %RDX_LP, %RCX_LP +- jb HIDDEN_JUMPTARGET (__chk_fail) +-END (__mempcpy_chk_erms) +- +-/* Only used to measure performance of REP MOVSB. */ +-ENTRY (__mempcpy_erms) +- mov %RDI_LP, %RAX_LP +- /* Skip zero length. */ +- test %RDX_LP, %RDX_LP +- jz 2f +- add %RDX_LP, %RAX_LP +- jmp L(start_movsb) +-END (__mempcpy_erms) +- +-ENTRY (__memmove_chk_erms) +- cmp %RDX_LP, %RCX_LP +- jb HIDDEN_JUMPTARGET (__chk_fail) +-END (__memmove_chk_erms) +- +-ENTRY (__memmove_erms) +- movq %rdi, %rax +- /* Skip zero length. */ +- test %RDX_LP, %RDX_LP +- jz 2f +-L(start_movsb): +- mov %RDX_LP, %RCX_LP +- cmp %RSI_LP, %RDI_LP +- jb 1f +- /* Source == destination is less common. */ +- je 2f +- lea (%rsi,%rcx), %RDX_LP +- cmp %RDX_LP, %RDI_LP +- jb L(movsb_backward) +-1: +- rep movsb +-2: +- ret +-L(movsb_backward): +- leaq -1(%rdi,%rcx), %rdi +- leaq -1(%rsi,%rcx), %rsi +- std +- rep movsb +- cld +- ret +-END (__memmove_erms) +-strong_alias (__memmove_erms, __memcpy_erms) +-strong_alias (__memmove_chk_erms, __memcpy_chk_erms) +-# endif + + # ifdef SHARED + ENTRY (MEMMOVE_CHK_SYMBOL (__mempcpy_chk, unaligned_erms)) +@@ -724,9 +680,14 @@ + .p2align 4,, 10 + #if (defined USE_MULTIARCH || VEC_SIZE == 16) && IS_IN (libc) + L(large_memcpy_2x_check): +- cmp __x86_rep_movsb_threshold(%rip), %RDX_LP +- jb L(more_8x_vec_check) ++ /* Entry from L(large_memcpy_2x) has a redundant load of ++ __x86_shared_non_temporal_threshold(%rip). L(large_memcpy_2x) ++ is only use for the non-erms memmove which is generally less ++ common. */ + L(large_memcpy_2x): ++ mov __x86_shared_non_temporal_threshold(%rip), %R11_LP ++ cmp %R11_LP, %RDX_LP ++ jb L(more_8x_vec_check) + /* To reach this point it is impossible for dst > src and + overlap. Remaining to check is src > dst and overlap. rcx + already contains dst - src. Negate rcx to get src - dst. If +@@ -774,18 +735,21 @@ + /* ecx contains -(dst - src). not ecx will return dst - src - 1 + which works for testing aliasing. */ + notl %ecx ++ movq %rdx, %r10 + testl $(PAGE_SIZE - VEC_SIZE * 8), %ecx + jz L(large_memcpy_4x) + +- movq %rdx, %r10 +- shrq $LOG_4X_MEMCPY_THRESH, %r10 +- cmp __x86_shared_non_temporal_threshold(%rip), %r10 ++ /* r11 has __x86_shared_non_temporal_threshold. Shift it left ++ by LOG_4X_MEMCPY_THRESH to get L(large_memcpy_4x) threshold. ++ */ ++ shlq $LOG_4X_MEMCPY_THRESH, %r11 ++ cmp %r11, %rdx + jae L(large_memcpy_4x) + + /* edx will store remainder size for copying tail. */ + andl $(PAGE_SIZE * 2 - 1), %edx + /* r10 stores outer loop counter. */ +- shrq $((LOG_PAGE_SIZE + 1) - LOG_4X_MEMCPY_THRESH), %r10 ++ shrq $(LOG_PAGE_SIZE + 1), %r10 + /* Copy 4x VEC at a time from 2 pages. */ + .p2align 4 + L(loop_large_memcpy_2x_outer): +@@ -850,7 +814,6 @@ + + .p2align 4 + L(large_memcpy_4x): +- movq %rdx, %r10 + /* edx will store remainder size for copying tail. */ + andl $(PAGE_SIZE * 4 - 1), %edx + /* r10 stores outer loop counter. */ +diff -Nuar a/sysdeps/x86_64/multiarch/memrchr-avx2-rtm.S b/sysdeps/x86_64/multiarch/memrchr-avx2-rtm.S +--- a/sysdeps/x86_64/multiarch/memrchr-avx2-rtm.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memrchr-avx2-rtm.S 2025-10-20 21:02:19.000000000 +0300 +@@ -2,6 +2,7 @@ + # define MEMRCHR __memrchr_avx2_rtm + #endif + ++#define COND_VZEROUPPER COND_VZEROUPPER_XTEST + #define ZERO_UPPER_VEC_REGISTERS_RETURN \ + ZERO_UPPER_VEC_REGISTERS_RETURN_XTEST + +diff -Nuar a/sysdeps/x86_64/multiarch/memrchr-avx2.S b/sysdeps/x86_64/multiarch/memrchr-avx2.S +--- a/sysdeps/x86_64/multiarch/memrchr-avx2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memrchr-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -21,340 +21,318 @@ + # include + + # ifndef MEMRCHR +-# define MEMRCHR __memrchr_avx2 ++# define MEMRCHR __memrchr_avx2 + # endif + + # ifndef VZEROUPPER +-# define VZEROUPPER vzeroupper ++# define VZEROUPPER vzeroupper + # endif + + # ifndef SECTION + # define SECTION(p) p##.avx + # endif + +-# define VEC_SIZE 32 ++# define VEC_SIZE 32 ++# define PAGE_SIZE 4096 ++ .section SECTION(.text), "ax", @progbits ++ENTRY_P2ALIGN(MEMRCHR, 6) ++# ifdef __ILP32__ ++ /* Clear upper bits. */ ++ and %RDX_LP, %RDX_LP ++# else ++ test %RDX_LP, %RDX_LP ++# endif ++ jz L(zero_0) + +- .section SECTION(.text),"ax",@progbits +-ENTRY (MEMRCHR) +- /* Broadcast CHAR to YMM0. */ + vmovd %esi, %xmm0 +- vpbroadcastb %xmm0, %ymm0 +- +- sub $VEC_SIZE, %RDX_LP +- jbe L(last_vec_or_less) +- +- add %RDX_LP, %RDI_LP ++ /* Get end pointer. Minus one for two reasons. 1) It is necessary for a ++ correct page cross check and 2) it correctly sets up end ptr to be ++ subtract by lzcnt aligned. */ ++ leaq -1(%rdx, %rdi), %rax + +- /* Check the last VEC_SIZE bytes. */ +- vpcmpeqb (%rdi), %ymm0, %ymm1 +- vpmovmskb %ymm1, %eax +- testl %eax, %eax +- jnz L(last_vec_x0) +- +- subq $(VEC_SIZE * 4), %rdi +- movl %edi, %ecx +- andl $(VEC_SIZE - 1), %ecx +- jz L(aligned_more) +- +- /* Align data for aligned loads in the loop. */ +- addq $VEC_SIZE, %rdi +- addq $VEC_SIZE, %rdx +- andq $-VEC_SIZE, %rdi +- subq %rcx, %rdx +- +- .p2align 4 +-L(aligned_more): +- subq $(VEC_SIZE * 4), %rdx +- jbe L(last_4x_vec_or_less) ++ vpbroadcastb %xmm0, %ymm0 + +- /* Check the last 4 * VEC_SIZE. Only one VEC_SIZE at a time +- since data is only aligned to VEC_SIZE. */ +- vpcmpeqb (VEC_SIZE * 3)(%rdi), %ymm0, %ymm1 +- vpmovmskb %ymm1, %eax +- testl %eax, %eax +- jnz L(last_vec_x3) +- +- vpcmpeqb (VEC_SIZE * 2)(%rdi), %ymm0, %ymm2 +- vpmovmskb %ymm2, %eax +- testl %eax, %eax +- jnz L(last_vec_x2) +- +- vpcmpeqb VEC_SIZE(%rdi), %ymm0, %ymm3 +- vpmovmskb %ymm3, %eax +- testl %eax, %eax +- jnz L(last_vec_x1) +- +- vpcmpeqb (%rdi), %ymm0, %ymm4 +- vpmovmskb %ymm4, %eax +- testl %eax, %eax +- jnz L(last_vec_x0) +- +- /* Align data to 4 * VEC_SIZE for loop with fewer branches. +- There are some overlaps with above if data isn't aligned +- to 4 * VEC_SIZE. */ +- movl %edi, %ecx +- andl $(VEC_SIZE * 4 - 1), %ecx +- jz L(loop_4x_vec) +- +- addq $(VEC_SIZE * 4), %rdi +- addq $(VEC_SIZE * 4), %rdx +- andq $-(VEC_SIZE * 4), %rdi +- subq %rcx, %rdx ++ /* Check if we can load 1x VEC without cross a page. */ ++ testl $(PAGE_SIZE - VEC_SIZE), %eax ++ jz L(page_cross) ++ ++ vpcmpeqb -(VEC_SIZE - 1)(%rax), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ cmpq $VEC_SIZE, %rdx ++ ja L(more_1x_vec) ++ ++L(ret_vec_x0_test): ++ /* If ecx is zero (no matches) lzcnt will set it 32 (VEC_SIZE) which ++ will gurantee edx (len) is less than it. */ ++ lzcntl %ecx, %ecx ++ ++ /* Hoist vzeroupper (not great for RTM) to save code size. This allows ++ all logic for edx (len) <= VEC_SIZE to fit in first cache line. */ ++ COND_VZEROUPPER ++ cmpl %ecx, %edx ++ jle L(zero_0) ++ subq %rcx, %rax ++ ret + +- .p2align 4 +-L(loop_4x_vec): +- /* Compare 4 * VEC at a time forward. */ +- subq $(VEC_SIZE * 4), %rdi +- subq $(VEC_SIZE * 4), %rdx +- jbe L(last_4x_vec_or_less) ++ /* Fits in aligning bytes of first cache line. */ ++L(zero_0): ++ xorl %eax, %eax ++ ret + +- vmovdqa (%rdi), %ymm1 +- vmovdqa VEC_SIZE(%rdi), %ymm2 +- vmovdqa (VEC_SIZE * 2)(%rdi), %ymm3 +- vmovdqa (VEC_SIZE * 3)(%rdi), %ymm4 +- +- vpcmpeqb %ymm1, %ymm0, %ymm1 +- vpcmpeqb %ymm2, %ymm0, %ymm2 +- vpcmpeqb %ymm3, %ymm0, %ymm3 +- vpcmpeqb %ymm4, %ymm0, %ymm4 +- +- vpor %ymm1, %ymm2, %ymm5 +- vpor %ymm3, %ymm4, %ymm6 +- vpor %ymm5, %ymm6, %ymm5 +- +- vpmovmskb %ymm5, %eax +- testl %eax, %eax +- jz L(loop_4x_vec) +- +- /* There is a match. */ +- vpmovmskb %ymm4, %eax +- testl %eax, %eax +- jnz L(last_vec_x3) +- +- vpmovmskb %ymm3, %eax +- testl %eax, %eax +- jnz L(last_vec_x2) +- +- vpmovmskb %ymm2, %eax +- testl %eax, %eax +- jnz L(last_vec_x1) +- +- vpmovmskb %ymm1, %eax +- bsrl %eax, %eax +- addq %rdi, %rax ++ .p2align 4,, 9 ++L(ret_vec_x0): ++ lzcntl %ecx, %ecx ++ subq %rcx, %rax + L(return_vzeroupper): + ZERO_UPPER_VEC_REGISTERS_RETURN + +- .p2align 4 +-L(last_4x_vec_or_less): +- addl $(VEC_SIZE * 4), %edx +- cmpl $(VEC_SIZE * 2), %edx +- jbe L(last_2x_vec) +- +- vpcmpeqb (VEC_SIZE * 3)(%rdi), %ymm0, %ymm1 +- vpmovmskb %ymm1, %eax +- testl %eax, %eax +- jnz L(last_vec_x3) +- +- vpcmpeqb (VEC_SIZE * 2)(%rdi), %ymm0, %ymm2 +- vpmovmskb %ymm2, %eax +- testl %eax, %eax +- jnz L(last_vec_x2) +- +- vpcmpeqb VEC_SIZE(%rdi), %ymm0, %ymm3 +- vpmovmskb %ymm3, %eax +- testl %eax, %eax +- jnz L(last_vec_x1_check) +- cmpl $(VEC_SIZE * 3), %edx +- jbe L(zero) +- +- vpcmpeqb (%rdi), %ymm0, %ymm4 +- vpmovmskb %ymm4, %eax +- testl %eax, %eax +- jz L(zero) +- bsrl %eax, %eax +- subq $(VEC_SIZE * 4), %rdx +- addq %rax, %rdx +- jl L(zero) +- addq %rdi, %rax +- VZEROUPPER_RETURN +- +- .p2align 4 ++ .p2align 4,, 10 ++L(more_1x_vec): ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0) ++ ++ /* Align rax (string pointer). */ ++ andq $-VEC_SIZE, %rax ++ ++ /* Recompute remaining length after aligning. */ ++ movq %rax, %rdx ++ /* Need this comparison next no matter what. */ ++ vpcmpeqb -(VEC_SIZE)(%rax), %ymm0, %ymm1 ++ subq %rdi, %rdx ++ decq %rax ++ vpmovmskb %ymm1, %ecx ++ /* Fall through for short (hotter than length). */ ++ cmpq $(VEC_SIZE * 2), %rdx ++ ja L(more_2x_vec) + L(last_2x_vec): +- vpcmpeqb (VEC_SIZE * 3)(%rdi), %ymm0, %ymm1 +- vpmovmskb %ymm1, %eax +- testl %eax, %eax +- jnz L(last_vec_x3_check) + cmpl $VEC_SIZE, %edx +- jbe L(zero) ++ jbe L(ret_vec_x0_test) + +- vpcmpeqb (VEC_SIZE * 2)(%rdi), %ymm0, %ymm1 +- vpmovmskb %ymm1, %eax +- testl %eax, %eax +- jz L(zero) +- bsrl %eax, %eax +- subq $(VEC_SIZE * 2), %rdx +- addq %rax, %rdx +- jl L(zero) +- addl $(VEC_SIZE * 2), %eax +- addq %rdi, %rax +- VZEROUPPER_RETURN ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0) + +- .p2align 4 +-L(last_vec_x0): +- bsrl %eax, %eax +- addq %rdi, %rax +- VZEROUPPER_RETURN +- +- .p2align 4 +-L(last_vec_x1): +- bsrl %eax, %eax +- addl $VEC_SIZE, %eax +- addq %rdi, %rax +- VZEROUPPER_RETURN ++ vpcmpeqb -(VEC_SIZE * 2 - 1)(%rax), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ /* 64-bit lzcnt. This will naturally add 32 to position. */ ++ lzcntq %rcx, %rcx ++ COND_VZEROUPPER ++ cmpl %ecx, %edx ++ jle L(zero_0) ++ subq %rcx, %rax ++ ret + +- .p2align 4 +-L(last_vec_x2): +- bsrl %eax, %eax +- addl $(VEC_SIZE * 2), %eax +- addq %rdi, %rax +- VZEROUPPER_RETURN + +- .p2align 4 +-L(last_vec_x3): +- bsrl %eax, %eax +- addl $(VEC_SIZE * 3), %eax +- addq %rdi, %rax ++ /* Inexpensive place to put this regarding code size / target alignments ++ / ICache NLP. Necessary for 2-byte encoding of jump to page cross ++ case which in turn is necessary for hot path (len <= VEC_SIZE) to fit ++ in first cache line. */ ++L(page_cross): ++ movq %rax, %rsi ++ andq $-VEC_SIZE, %rsi ++ vpcmpeqb (%rsi), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ /* Shift out negative alignment (because we are starting from endptr and ++ working backwards). */ ++ movl %eax, %r8d ++ /* notl because eax already has endptr - 1. (-x = ~(x - 1)). */ ++ notl %r8d ++ shlxl %r8d, %ecx, %ecx ++ cmpq %rdi, %rsi ++ ja L(more_1x_vec) ++ lzcntl %ecx, %ecx ++ COND_VZEROUPPER ++ cmpl %ecx, %edx ++ jle L(zero_0) ++ subq %rcx, %rax + ret ++ .p2align 4,, 11 ++L(ret_vec_x1): ++ /* This will naturally add 32 to position. */ ++ lzcntq %rcx, %rcx ++ subq %rcx, %rax ++ VZEROUPPER_RETURN ++ .p2align 4,, 10 ++L(more_2x_vec): ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0) ++ ++ vpcmpeqb -(VEC_SIZE * 2 - 1)(%rax), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ testl %ecx, %ecx ++ jnz L(ret_vec_x1) ++ ++ ++ /* Needed no matter what. */ ++ vpcmpeqb -(VEC_SIZE * 3 - 1)(%rax), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx + +- .p2align 4 +-L(last_vec_x1_check): +- bsrl %eax, %eax +- subq $(VEC_SIZE * 3), %rdx +- addq %rax, %rdx +- jl L(zero) +- addl $VEC_SIZE, %eax +- addq %rdi, %rax +- VZEROUPPER_RETURN ++ subq $(VEC_SIZE * 4), %rdx ++ ja L(more_4x_vec) + +- .p2align 4 +-L(last_vec_x3_check): +- bsrl %eax, %eax +- subq $VEC_SIZE, %rdx +- addq %rax, %rdx +- jl L(zero) +- addl $(VEC_SIZE * 3), %eax +- addq %rdi, %rax +- VZEROUPPER_RETURN ++ cmpl $(VEC_SIZE * -1), %edx ++ jle L(ret_vec_x2_test) + +- .p2align 4 +-L(zero): +- xorl %eax, %eax +- VZEROUPPER_RETURN ++L(last_vec): ++ testl %ecx, %ecx ++ jnz L(ret_vec_x2) ++ ++ /* Needed no matter what. */ ++ vpcmpeqb -(VEC_SIZE * 4 - 1)(%rax), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ lzcntl %ecx, %ecx ++ subq $(VEC_SIZE * 3), %rax ++ COND_VZEROUPPER ++ subq %rcx, %rax ++ cmpq %rax, %rdi ++ ja L(zero_2) ++ ret + +- .p2align 4 +-L(null): ++ /* First in aligning bytes. */ ++L(zero_2): + xorl %eax, %eax + ret + +- .p2align 4 +-L(last_vec_or_less_aligned): +- movl %edx, %ecx ++ .p2align 4,, 4 ++L(ret_vec_x2_test): ++ lzcntl %ecx, %ecx ++ subq $(VEC_SIZE * 2), %rax ++ COND_VZEROUPPER ++ subq %rcx, %rax ++ cmpq %rax, %rdi ++ ja L(zero_2) ++ ret + +- vpcmpeqb (%rdi), %ymm0, %ymm1 + +- movl $1, %edx +- /* Support rdx << 32. */ +- salq %cl, %rdx +- subq $1, %rdx ++ .p2align 4,, 11 ++L(ret_vec_x2): ++ /* ecx must be non-zero. */ ++ bsrl %ecx, %ecx ++ leaq (VEC_SIZE * -3 + 1)(%rcx, %rax), %rax ++ VZEROUPPER_RETURN + +- vpmovmskb %ymm1, %eax ++ .p2align 4,, 14 ++L(ret_vec_x3): ++ /* ecx must be non-zero. */ ++ bsrl %ecx, %ecx ++ leaq (VEC_SIZE * -4 + 1)(%rcx, %rax), %rax ++ VZEROUPPER_RETURN + +- /* Remove the trailing bytes. */ +- andl %edx, %eax +- testl %eax, %eax +- jz L(zero) + +- bsrl %eax, %eax +- addq %rdi, %rax +- VZEROUPPER_RETURN + + .p2align 4 +-L(last_vec_or_less): +- addl $VEC_SIZE, %edx ++L(more_4x_vec): ++ testl %ecx, %ecx ++ jnz L(ret_vec_x2) + +- /* Check for zero length. */ +- testl %edx, %edx +- jz L(null) +- +- movl %edi, %ecx +- andl $(VEC_SIZE - 1), %ecx +- jz L(last_vec_or_less_aligned) +- +- movl %ecx, %esi +- movl %ecx, %r8d +- addl %edx, %esi +- andq $-VEC_SIZE, %rdi +- +- subl $VEC_SIZE, %esi +- ja L(last_vec_2x_aligned) +- +- /* Check the last VEC. */ +- vpcmpeqb (%rdi), %ymm0, %ymm1 +- vpmovmskb %ymm1, %eax +- +- /* Remove the leading and trailing bytes. */ +- sarl %cl, %eax +- movl %edx, %ecx +- +- movl $1, %edx +- sall %cl, %edx +- subl $1, %edx +- +- andl %edx, %eax +- testl %eax, %eax +- jz L(zero) +- +- bsrl %eax, %eax +- addq %rdi, %rax +- addq %r8, %rax +- VZEROUPPER_RETURN ++ vpcmpeqb -(VEC_SIZE * 4 - 1)(%rax), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx + +- .p2align 4 +-L(last_vec_2x_aligned): +- movl %esi, %ecx ++ testl %ecx, %ecx ++ jnz L(ret_vec_x3) + +- /* Check the last VEC. */ +- vpcmpeqb VEC_SIZE(%rdi), %ymm0, %ymm1 ++ /* Check if near end before re-aligning (otherwise might do an ++ unnecissary loop iteration). */ ++ addq $-(VEC_SIZE * 4), %rax ++ cmpq $(VEC_SIZE * 4), %rdx ++ jbe L(last_4x_vec) + +- movl $1, %edx +- sall %cl, %edx +- subl $1, %edx ++ /* Align rax to (VEC_SIZE - 1). */ ++ orq $(VEC_SIZE * 4 - 1), %rax ++ movq %rdi, %rdx ++ /* Get endptr for loop in rdx. NB: Can't just do while rax > rdi because ++ lengths that overflow can be valid and break the comparison. */ ++ orq $(VEC_SIZE * 4 - 1), %rdx + +- vpmovmskb %ymm1, %eax ++ .p2align 4 ++L(loop_4x_vec): ++ /* Need this comparison next no matter what. */ ++ vpcmpeqb -(VEC_SIZE * 1 - 1)(%rax), %ymm0, %ymm1 ++ vpcmpeqb -(VEC_SIZE * 2 - 1)(%rax), %ymm0, %ymm2 ++ vpcmpeqb -(VEC_SIZE * 3 - 1)(%rax), %ymm0, %ymm3 ++ vpcmpeqb -(VEC_SIZE * 4 - 1)(%rax), %ymm0, %ymm4 ++ ++ vpor %ymm1, %ymm2, %ymm2 ++ vpor %ymm3, %ymm4, %ymm4 ++ vpor %ymm2, %ymm4, %ymm4 ++ vpmovmskb %ymm4, %esi ++ ++ testl %esi, %esi ++ jnz L(loop_end) ++ ++ addq $(VEC_SIZE * -4), %rax ++ cmpq %rdx, %rax ++ jne L(loop_4x_vec) ++ ++ subl %edi, %edx ++ incl %edx ++ ++L(last_4x_vec): ++ /* Used no matter what. */ ++ vpcmpeqb -(VEC_SIZE * 1 - 1)(%rax), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx + +- /* Remove the trailing bytes. */ +- andl %edx, %eax ++ cmpl $(VEC_SIZE * 2), %edx ++ jbe L(last_2x_vec) + +- testl %eax, %eax +- jnz L(last_vec_x1) ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0_end) + +- /* Check the second last VEC. */ +- vpcmpeqb (%rdi), %ymm0, %ymm1 ++ vpcmpeqb -(VEC_SIZE * 2 - 1)(%rax), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ testl %ecx, %ecx ++ jnz L(ret_vec_x1_end) ++ ++ /* Used no matter what. */ ++ vpcmpeqb -(VEC_SIZE * 3 - 1)(%rax), %ymm0, %ymm1 ++ vpmovmskb %ymm1, %ecx + +- movl %r8d, %ecx ++ cmpl $(VEC_SIZE * 3), %edx ++ ja L(last_vec) + +- vpmovmskb %ymm1, %eax ++ lzcntl %ecx, %ecx ++ subq $(VEC_SIZE * 2), %rax ++ COND_VZEROUPPER ++ subq %rcx, %rax ++ cmpq %rax, %rdi ++ jbe L(ret0) ++ xorl %eax, %eax ++L(ret0): ++ ret + +- /* Remove the leading bytes. Must use unsigned right shift for +- bsrl below. */ +- shrl %cl, %eax +- testl %eax, %eax +- jz L(zero) + +- bsrl %eax, %eax +- addq %rdi, %rax +- addq %r8, %rax ++ .p2align 4 ++L(loop_end): ++ vpmovmskb %ymm1, %ecx ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0_end) ++ ++ vpmovmskb %ymm2, %ecx ++ testl %ecx, %ecx ++ jnz L(ret_vec_x1_end) ++ ++ vpmovmskb %ymm3, %ecx ++ /* Combine last 2 VEC matches. If ecx (VEC3) is zero (no CHAR in VEC3) ++ then it won't affect the result in esi (VEC4). If ecx is non-zero ++ then CHAR in VEC3 and bsrq will use that position. */ ++ salq $32, %rcx ++ orq %rsi, %rcx ++ bsrq %rcx, %rcx ++ leaq (VEC_SIZE * -4 + 1)(%rcx, %rax), %rax ++ VZEROUPPER_RETURN ++ ++ .p2align 4,, 4 ++L(ret_vec_x1_end): ++ /* 64-bit version will automatically add 32 (VEC_SIZE). */ ++ lzcntq %rcx, %rcx ++ subq %rcx, %rax ++ VZEROUPPER_RETURN ++ ++ .p2align 4,, 4 ++L(ret_vec_x0_end): ++ lzcntl %ecx, %ecx ++ subq %rcx, %rax + VZEROUPPER_RETURN +-END (MEMRCHR) ++ ++ /* 2 bytes until next cache line. */ ++END(MEMRCHR) + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/memrchr-evex.S b/sysdeps/x86_64/multiarch/memrchr-evex.S +--- a/sysdeps/x86_64/multiarch/memrchr-evex.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memrchr-evex.S 2025-10-20 21:02:22.000000000 +0300 +@@ -19,319 +19,316 @@ + #if IS_IN (libc) + + # include +- +-# define VMOVA vmovdqa64 +- +-# define YMMMATCH ymm16 +- +-# define VEC_SIZE 32 +- +- .section .text.evex,"ax",@progbits +-ENTRY (__memrchr_evex) +- /* Broadcast CHAR to YMMMATCH. */ +- vpbroadcastb %esi, %YMMMATCH +- +- sub $VEC_SIZE, %RDX_LP +- jbe L(last_vec_or_less) +- +- add %RDX_LP, %RDI_LP +- +- /* Check the last VEC_SIZE bytes. */ +- vpcmpb $0, (%rdi), %YMMMATCH, %k1 +- kmovd %k1, %eax +- testl %eax, %eax +- jnz L(last_vec_x0) +- +- subq $(VEC_SIZE * 4), %rdi +- movl %edi, %ecx +- andl $(VEC_SIZE - 1), %ecx +- jz L(aligned_more) +- +- /* Align data for aligned loads in the loop. */ +- addq $VEC_SIZE, %rdi +- addq $VEC_SIZE, %rdx +- andq $-VEC_SIZE, %rdi +- subq %rcx, %rdx +- +- .p2align 4 +-L(aligned_more): +- subq $(VEC_SIZE * 4), %rdx +- jbe L(last_4x_vec_or_less) +- +- /* Check the last 4 * VEC_SIZE. Only one VEC_SIZE at a time +- since data is only aligned to VEC_SIZE. */ +- vpcmpb $0, (VEC_SIZE * 3)(%rdi), %YMMMATCH, %k1 +- kmovd %k1, %eax +- testl %eax, %eax +- jnz L(last_vec_x3) +- +- vpcmpb $0, (VEC_SIZE * 2)(%rdi), %YMMMATCH, %k2 +- kmovd %k2, %eax +- testl %eax, %eax +- jnz L(last_vec_x2) +- +- vpcmpb $0, VEC_SIZE(%rdi), %YMMMATCH, %k3 +- kmovd %k3, %eax +- testl %eax, %eax +- jnz L(last_vec_x1) +- +- vpcmpb $0, (%rdi), %YMMMATCH, %k4 +- kmovd %k4, %eax +- testl %eax, %eax +- jnz L(last_vec_x0) +- +- /* Align data to 4 * VEC_SIZE for loop with fewer branches. +- There are some overlaps with above if data isn't aligned +- to 4 * VEC_SIZE. */ +- movl %edi, %ecx +- andl $(VEC_SIZE * 4 - 1), %ecx +- jz L(loop_4x_vec) +- +- addq $(VEC_SIZE * 4), %rdi +- addq $(VEC_SIZE * 4), %rdx +- andq $-(VEC_SIZE * 4), %rdi +- subq %rcx, %rdx +- +- .p2align 4 +-L(loop_4x_vec): +- /* Compare 4 * VEC at a time forward. */ +- subq $(VEC_SIZE * 4), %rdi +- subq $(VEC_SIZE * 4), %rdx +- jbe L(last_4x_vec_or_less) +- +- vpcmpb $0, (%rdi), %YMMMATCH, %k1 +- vpcmpb $0, VEC_SIZE(%rdi), %YMMMATCH, %k2 +- kord %k1, %k2, %k5 +- vpcmpb $0, (VEC_SIZE * 2)(%rdi), %YMMMATCH, %k3 +- vpcmpb $0, (VEC_SIZE * 3)(%rdi), %YMMMATCH, %k4 +- +- kord %k3, %k4, %k6 +- kortestd %k5, %k6 +- jz L(loop_4x_vec) +- +- /* There is a match. */ +- kmovd %k4, %eax +- testl %eax, %eax +- jnz L(last_vec_x3) +- +- kmovd %k3, %eax +- testl %eax, %eax +- jnz L(last_vec_x2) +- +- kmovd %k2, %eax +- testl %eax, %eax +- jnz L(last_vec_x1) +- +- kmovd %k1, %eax +- bsrl %eax, %eax +- addq %rdi, %rax ++# include "evex256-vecs.h" ++# if VEC_SIZE != 32 ++# error "VEC_SIZE != 32 unimplemented" ++# endif ++ ++# ifndef MEMRCHR ++# define MEMRCHR __memrchr_evex ++# endif ++ ++# define PAGE_SIZE 4096 ++# define VECMATCH VEC(0) ++ ++ .section SECTION(.text), "ax", @progbits ++ENTRY_P2ALIGN(MEMRCHR, 6) ++# ifdef __ILP32__ ++ /* Clear upper bits. */ ++ and %RDX_LP, %RDX_LP ++# else ++ test %RDX_LP, %RDX_LP ++# endif ++ jz L(zero_0) ++ ++ /* Get end pointer. Minus one for two reasons. 1) It is necessary for a ++ correct page cross check and 2) it correctly sets up end ptr to be ++ subtract by lzcnt aligned. */ ++ leaq -1(%rdi, %rdx), %rax ++ vpbroadcastb %esi, %VECMATCH ++ ++ /* Check if we can load 1x VEC without cross a page. */ ++ testl $(PAGE_SIZE - VEC_SIZE), %eax ++ jz L(page_cross) ++ ++ /* Don't use rax for pointer here because EVEX has better encoding with ++ offset % VEC_SIZE == 0. */ ++ vpcmpb $0, -(VEC_SIZE)(%rdi, %rdx), %VECMATCH, %k0 ++ kmovd %k0, %ecx ++ ++ /* Fall through for rdx (len) <= VEC_SIZE (expect small sizes). */ ++ cmpq $VEC_SIZE, %rdx ++ ja L(more_1x_vec) ++L(ret_vec_x0_test): ++ ++ /* If ecx is zero (no matches) lzcnt will set it 32 (VEC_SIZE) which ++ will guarantee edx (len) is less than it. */ ++ lzcntl %ecx, %ecx ++ cmpl %ecx, %edx ++ jle L(zero_0) ++ subq %rcx, %rax + ret + +- .p2align 4 +-L(last_4x_vec_or_less): +- addl $(VEC_SIZE * 4), %edx +- cmpl $(VEC_SIZE * 2), %edx +- jbe L(last_2x_vec) +- +- vpcmpb $0, (VEC_SIZE * 3)(%rdi), %YMMMATCH, %k1 +- kmovd %k1, %eax +- testl %eax, %eax +- jnz L(last_vec_x3) +- +- vpcmpb $0, (VEC_SIZE * 2)(%rdi), %YMMMATCH, %k2 +- kmovd %k2, %eax +- testl %eax, %eax +- jnz L(last_vec_x2) +- +- vpcmpb $0, VEC_SIZE(%rdi), %YMMMATCH, %k3 +- kmovd %k3, %eax +- testl %eax, %eax +- jnz L(last_vec_x1_check) +- cmpl $(VEC_SIZE * 3), %edx +- jbe L(zero) ++ /* Fits in aligning bytes of first cache line. */ ++L(zero_0): ++ xorl %eax, %eax ++ ret + +- vpcmpb $0, (%rdi), %YMMMATCH, %k4 +- kmovd %k4, %eax +- testl %eax, %eax +- jz L(zero) +- bsrl %eax, %eax +- subq $(VEC_SIZE * 4), %rdx +- addq %rax, %rdx +- jl L(zero) +- addq %rdi, %rax ++ .p2align 4,, 9 ++L(ret_vec_x0_dec): ++ decq %rax ++L(ret_vec_x0): ++ lzcntl %ecx, %ecx ++ subq %rcx, %rax + ret + +- .p2align 4 +-L(last_2x_vec): +- vpcmpb $0, (VEC_SIZE * 3)(%rdi), %YMMMATCH, %k1 +- kmovd %k1, %eax +- testl %eax, %eax +- jnz L(last_vec_x3_check) +- cmpl $VEC_SIZE, %edx +- jbe L(zero) ++ .p2align 4,, 10 ++L(more_1x_vec): ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0) + +- vpcmpb $0, (VEC_SIZE * 2)(%rdi), %YMMMATCH, %k1 +- kmovd %k1, %eax +- testl %eax, %eax +- jz L(zero) +- bsrl %eax, %eax +- subq $(VEC_SIZE * 2), %rdx +- addq %rax, %rdx +- jl L(zero) +- addl $(VEC_SIZE * 2), %eax +- addq %rdi, %rax +- ret ++ /* Align rax (pointer to string). */ ++ andq $-VEC_SIZE, %rax + +- .p2align 4 +-L(last_vec_x0): +- bsrl %eax, %eax +- addq %rdi, %rax +- ret ++ /* Recompute length after aligning. */ ++ movq %rax, %rdx + +- .p2align 4 +-L(last_vec_x1): +- bsrl %eax, %eax +- addl $VEC_SIZE, %eax +- addq %rdi, %rax +- ret ++ /* Need no matter what. */ ++ vpcmpb $0, -(VEC_SIZE)(%rax), %VECMATCH, %k0 ++ kmovd %k0, %ecx + +- .p2align 4 +-L(last_vec_x2): +- bsrl %eax, %eax +- addl $(VEC_SIZE * 2), %eax +- addq %rdi, %rax +- ret ++ subq %rdi, %rdx + +- .p2align 4 +-L(last_vec_x3): +- bsrl %eax, %eax +- addl $(VEC_SIZE * 3), %eax +- addq %rdi, %rax +- ret ++ cmpq $(VEC_SIZE * 2), %rdx ++ ja L(more_2x_vec) ++L(last_2x_vec): + +- .p2align 4 +-L(last_vec_x1_check): +- bsrl %eax, %eax +- subq $(VEC_SIZE * 3), %rdx +- addq %rax, %rdx +- jl L(zero) +- addl $VEC_SIZE, %eax +- addq %rdi, %rax +- ret ++ /* Must dec rax because L(ret_vec_x0_test) expects it. */ ++ decq %rax ++ cmpl $VEC_SIZE, %edx ++ jbe L(ret_vec_x0_test) + +- .p2align 4 +-L(last_vec_x3_check): +- bsrl %eax, %eax +- subq $VEC_SIZE, %rdx +- addq %rax, %rdx +- jl L(zero) +- addl $(VEC_SIZE * 3), %eax +- addq %rdi, %rax +- ret ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0) + +- .p2align 4 +-L(zero): ++ /* Don't use rax for pointer here because EVEX has better encoding with ++ offset % VEC_SIZE == 0. */ ++ vpcmpb $0, -(VEC_SIZE * 2)(%rdi, %rdx), %VECMATCH, %k0 ++ kmovd %k0, %ecx ++ /* NB: 64-bit lzcnt. This will naturally add 32 to position. */ ++ lzcntq %rcx, %rcx ++ cmpl %ecx, %edx ++ jle L(zero_0) ++ subq %rcx, %rax ++ ret ++ ++ /* Inexpensive place to put this regarding code size / target alignments ++ / ICache NLP. Necessary for 2-byte encoding of jump to page cross ++ case which in turn is necessary for hot path (len <= VEC_SIZE) to fit ++ in first cache line. */ ++L(page_cross): ++ movq %rax, %rsi ++ andq $-VEC_SIZE, %rsi ++ vpcmpb $0, (%rsi), %VECMATCH, %k0 ++ kmovd %k0, %r8d ++ /* Shift out negative alignment (because we are starting from endptr and ++ working backwards). */ ++ movl %eax, %ecx ++ /* notl because eax already has endptr - 1. (-x = ~(x - 1)). */ ++ notl %ecx ++ shlxl %ecx, %r8d, %ecx ++ cmpq %rdi, %rsi ++ ja L(more_1x_vec) ++ lzcntl %ecx, %ecx ++ cmpl %ecx, %edx ++ jle L(zero_1) ++ subq %rcx, %rax ++ ret ++ ++ /* Continue creating zero labels that fit in aligning bytes and get ++ 2-byte encoding / are in the same cache line as condition. */ ++L(zero_1): + xorl %eax, %eax + ret + +- .p2align 4 +-L(last_vec_or_less_aligned): +- movl %edx, %ecx +- +- vpcmpb $0, (%rdi), %YMMMATCH, %k1 +- +- movl $1, %edx +- /* Support rdx << 32. */ +- salq %cl, %rdx +- subq $1, %rdx +- +- kmovd %k1, %eax ++ .p2align 4,, 8 ++L(ret_vec_x1): ++ /* This will naturally add 32 to position. */ ++ bsrl %ecx, %ecx ++ leaq -(VEC_SIZE * 2)(%rcx, %rax), %rax ++ ret ++ ++ .p2align 4,, 8 ++L(more_2x_vec): ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0_dec) ++ ++ vpcmpb $0, -(VEC_SIZE * 2)(%rax), %VECMATCH, %k0 ++ kmovd %k0, %ecx ++ testl %ecx, %ecx ++ jnz L(ret_vec_x1) ++ ++ /* Need no matter what. */ ++ vpcmpb $0, -(VEC_SIZE * 3)(%rax), %VECMATCH, %k0 ++ kmovd %k0, %ecx + +- /* Remove the trailing bytes. */ +- andl %edx, %eax +- testl %eax, %eax +- jz L(zero) +- +- bsrl %eax, %eax +- addq %rdi, %rax +- ret +- +- .p2align 4 +-L(last_vec_or_less): +- addl $VEC_SIZE, %edx ++ subq $(VEC_SIZE * 4), %rdx ++ ja L(more_4x_vec) + +- /* Check for zero length. */ +- testl %edx, %edx +- jz L(zero) +- +- movl %edi, %ecx +- andl $(VEC_SIZE - 1), %ecx +- jz L(last_vec_or_less_aligned) +- +- movl %ecx, %esi +- movl %ecx, %r8d +- addl %edx, %esi +- andq $-VEC_SIZE, %rdi +- +- subl $VEC_SIZE, %esi +- ja L(last_vec_2x_aligned) +- +- /* Check the last VEC. */ +- vpcmpb $0, (%rdi), %YMMMATCH, %k1 +- kmovd %k1, %eax +- +- /* Remove the leading and trailing bytes. */ +- sarl %cl, %eax +- movl %edx, %ecx +- +- movl $1, %edx +- sall %cl, %edx +- subl $1, %edx +- +- andl %edx, %eax +- testl %eax, %eax +- jz L(zero) +- +- bsrl %eax, %eax +- addq %rdi, %rax +- addq %r8, %rax +- ret ++ cmpl $(VEC_SIZE * -1), %edx ++ jle L(ret_vec_x2_test) ++L(last_vec): ++ testl %ecx, %ecx ++ jnz L(ret_vec_x2) ++ ++ ++ /* Need no matter what. */ ++ vpcmpb $0, -(VEC_SIZE * 4)(%rax), %VECMATCH, %k0 ++ kmovd %k0, %ecx ++ lzcntl %ecx, %ecx ++ subq $(VEC_SIZE * 3 + 1), %rax ++ subq %rcx, %rax ++ cmpq %rax, %rdi ++ ja L(zero_1) ++ ret ++ ++ .p2align 4,, 8 ++L(ret_vec_x2_test): ++ lzcntl %ecx, %ecx ++ subq $(VEC_SIZE * 2 + 1), %rax ++ subq %rcx, %rax ++ cmpq %rax, %rdi ++ ja L(zero_1) ++ ret ++ ++ .p2align 4,, 8 ++L(ret_vec_x2): ++ bsrl %ecx, %ecx ++ leaq -(VEC_SIZE * 3)(%rcx, %rax), %rax ++ ret ++ ++ .p2align 4,, 8 ++L(ret_vec_x3): ++ bsrl %ecx, %ecx ++ leaq -(VEC_SIZE * 4)(%rcx, %rax), %rax ++ ret ++ ++ .p2align 4,, 8 ++L(more_4x_vec): ++ testl %ecx, %ecx ++ jnz L(ret_vec_x2) ++ ++ vpcmpb $0, -(VEC_SIZE * 4)(%rax), %VECMATCH, %k0 ++ kmovd %k0, %ecx ++ ++ testl %ecx, %ecx ++ jnz L(ret_vec_x3) ++ ++ /* Check if near end before re-aligning (otherwise might do an ++ unnecessary loop iteration). */ ++ addq $-(VEC_SIZE * 4), %rax ++ cmpq $(VEC_SIZE * 4), %rdx ++ jbe L(last_4x_vec) ++ ++ decq %rax ++ andq $-(VEC_SIZE * 4), %rax ++ movq %rdi, %rdx ++ /* Get endptr for loop in rdx. NB: Can't just do while rax > rdi because ++ lengths that overflow can be valid and break the comparison. */ ++ andq $-(VEC_SIZE * 4), %rdx + + .p2align 4 +-L(last_vec_2x_aligned): +- movl %esi, %ecx +- +- /* Check the last VEC. */ +- vpcmpb $0, VEC_SIZE(%rdi), %YMMMATCH, %k1 ++L(loop_4x_vec): ++ /* Store 1 were not-equals and 0 where equals in k1 (used to mask later ++ on). */ ++ vpcmpb $4, (VEC_SIZE * 3)(%rax), %VECMATCH, %k1 ++ ++ /* VEC(2/3) will have zero-byte where we found a CHAR. */ ++ vpxorq (VEC_SIZE * 2)(%rax), %VECMATCH, %VEC(2) ++ vpxorq (VEC_SIZE * 1)(%rax), %VECMATCH, %VEC(3) ++ vpcmpb $0, (VEC_SIZE * 0)(%rax), %VECMATCH, %k4 ++ ++ /* Combine VEC(2/3) with min and maskz with k1 (k1 has zero bit where ++ CHAR is found and VEC(2/3) have zero-byte where CHAR is found. */ ++ vpminub %VEC(2), %VEC(3), %VEC(3){%k1}{z} ++ vptestnmb %VEC(3), %VEC(3), %k2 ++ ++ /* Any 1s and we found CHAR. */ ++ kortestd %k2, %k4 ++ jnz L(loop_end) ++ ++ addq $-(VEC_SIZE * 4), %rax ++ cmpq %rdx, %rax ++ jne L(loop_4x_vec) ++ ++ /* Need to re-adjust rdx / rax for L(last_4x_vec). */ ++ subq $-(VEC_SIZE * 4), %rdx ++ movq %rdx, %rax ++ subl %edi, %edx ++L(last_4x_vec): ++ ++ /* Used no matter what. */ ++ vpcmpb $0, (VEC_SIZE * -1)(%rax), %VECMATCH, %k0 ++ kmovd %k0, %ecx + +- movl $1, %edx +- sall %cl, %edx +- subl $1, %edx ++ cmpl $(VEC_SIZE * 2), %edx ++ jbe L(last_2x_vec) + +- kmovd %k1, %eax ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0_dec) + +- /* Remove the trailing bytes. */ +- andl %edx, %eax + +- testl %eax, %eax +- jnz L(last_vec_x1) ++ vpcmpb $0, (VEC_SIZE * -2)(%rax), %VECMATCH, %k0 ++ kmovd %k0, %ecx + +- /* Check the second last VEC. */ +- vpcmpb $0, (%rdi), %YMMMATCH, %k1 ++ testl %ecx, %ecx ++ jnz L(ret_vec_x1) + +- movl %r8d, %ecx ++ /* Used no matter what. */ ++ vpcmpb $0, (VEC_SIZE * -3)(%rax), %VECMATCH, %k0 ++ kmovd %k0, %ecx + +- kmovd %k1, %eax ++ cmpl $(VEC_SIZE * 3), %edx ++ ja L(last_vec) + +- /* Remove the leading bytes. Must use unsigned right shift for +- bsrl below. */ +- shrl %cl, %eax +- testl %eax, %eax +- jz L(zero) ++ lzcntl %ecx, %ecx ++ subq $(VEC_SIZE * 2 + 1), %rax ++ subq %rcx, %rax ++ cmpq %rax, %rdi ++ jbe L(ret_1) ++ xorl %eax, %eax ++L(ret_1): ++ ret + +- bsrl %eax, %eax +- addq %rdi, %rax +- addq %r8, %rax ++ .p2align 4,, 6 ++L(loop_end): ++ kmovd %k1, %ecx ++ notl %ecx ++ testl %ecx, %ecx ++ jnz L(ret_vec_x0_end) ++ ++ vptestnmb %VEC(2), %VEC(2), %k0 ++ kmovd %k0, %ecx ++ testl %ecx, %ecx ++ jnz L(ret_vec_x1_end) ++ ++ kmovd %k2, %ecx ++ kmovd %k4, %esi ++ /* Combine last 2 VEC matches. If ecx (VEC3) is zero (no CHAR in VEC3) ++ then it won't affect the result in esi (VEC4). If ecx is non-zero ++ then CHAR in VEC3 and bsrq will use that position. */ ++ salq $32, %rcx ++ orq %rsi, %rcx ++ bsrq %rcx, %rcx ++ addq %rcx, %rax ++ ret ++ .p2align 4,, 4 ++L(ret_vec_x0_end): ++ addq $(VEC_SIZE), %rax ++L(ret_vec_x1_end): ++ bsrl %ecx, %ecx ++ leaq (VEC_SIZE * 2)(%rax, %rcx), %rax + ret +-END (__memrchr_evex) ++ ++END(MEMRCHR) + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/memset-avx2-unaligned-erms.S b/sysdeps/x86_64/multiarch/memset-avx2-unaligned-erms.S +--- a/sysdeps/x86_64/multiarch/memset-avx2-unaligned-erms.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memset-avx2-unaligned-erms.S 2025-10-20 21:02:22.000000000 +0300 +@@ -10,15 +10,18 @@ + # define VMOVU vmovdqu + # define VMOVA vmovdqa + +-# define MEMSET_VDUP_TO_VEC0_AND_SET_RETURN(d, r) \ ++# define MEMSET_SET_VEC0_AND_SET_RETURN(d, r) \ + vmovd d, %xmm0; \ +- movq r, %rax; \ +- vpbroadcastb %xmm0, %ymm0 ++ movq r, %rax; + +-# define WMEMSET_VDUP_TO_VEC0_AND_SET_RETURN(d, r) \ +- vmovd d, %xmm0; \ +- movq r, %rax; \ +- vpbroadcastd %xmm0, %ymm0 ++# define WMEMSET_SET_VEC0_AND_SET_RETURN(d, r) \ ++ MEMSET_SET_VEC0_AND_SET_RETURN(d, r) ++ ++# define MEMSET_VDUP_TO_VEC0_HIGH() vpbroadcastb %xmm0, %ymm0 ++# define MEMSET_VDUP_TO_VEC0_LOW() vpbroadcastb %xmm0, %xmm0 ++ ++# define WMEMSET_VDUP_TO_VEC0_HIGH() vpbroadcastd %xmm0, %ymm0 ++# define WMEMSET_VDUP_TO_VEC0_LOW() vpbroadcastd %xmm0, %xmm0 + + # ifndef SECTION + # define SECTION(p) p##.avx +@@ -30,5 +33,6 @@ + # define WMEMSET_SYMBOL(p,s) p##_avx2_##s + # endif + ++# define USE_XMM_LESS_VEC + # include "memset-vec-unaligned-erms.S" + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/memset-avx512-unaligned-erms.S b/sysdeps/x86_64/multiarch/memset-avx512-unaligned-erms.S +--- a/sysdeps/x86_64/multiarch/memset-avx512-unaligned-erms.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memset-avx512-unaligned-erms.S 2025-10-20 21:02:22.000000000 +0300 +@@ -15,13 +15,19 @@ + + # define VZEROUPPER + +-# define MEMSET_VDUP_TO_VEC0_AND_SET_RETURN(d, r) \ +- movq r, %rax; \ +- vpbroadcastb d, %VEC0 +- +-# define WMEMSET_VDUP_TO_VEC0_AND_SET_RETURN(d, r) \ +- movq r, %rax; \ +- vpbroadcastd d, %VEC0 ++# define MEMSET_SET_VEC0_AND_SET_RETURN(d, r) \ ++ vpbroadcastb d, %VEC0; \ ++ movq r, %rax ++ ++# define WMEMSET_SET_VEC0_AND_SET_RETURN(d, r) \ ++ vpbroadcastd d, %VEC0; \ ++ movq r, %rax ++ ++# define MEMSET_VDUP_TO_VEC0_HIGH() ++# define MEMSET_VDUP_TO_VEC0_LOW() ++ ++# define WMEMSET_VDUP_TO_VEC0_HIGH() ++# define WMEMSET_VDUP_TO_VEC0_LOW() + + # define SECTION(p) p##.evex512 + # define MEMSET_SYMBOL(p,s) p##_avx512_##s +diff -Nuar a/sysdeps/x86_64/multiarch/memset-erms.S b/sysdeps/x86_64/multiarch/memset-erms.S +--- a/sysdeps/x86_64/multiarch/memset-erms.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/memset-erms.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,44 @@ ++/* memset implement with rep stosb ++ Copyright (C) 2022 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 ++ . */ ++ ++ ++#include ++ ++#if defined USE_MULTIARCH && IS_IN (libc) ++ .text ++ENTRY (__memset_chk_erms) ++ cmp %RDX_LP, %RCX_LP ++ jb HIDDEN_JUMPTARGET (__chk_fail) ++END (__memset_chk_erms) ++ ++/* Only used to measure performance of REP STOSB. */ ++ENTRY (__memset_erms) ++ /* Skip zero length. */ ++ test %RDX_LP, %RDX_LP ++ jz L(stosb_return_zero) ++ mov %RDX_LP, %RCX_LP ++ movzbl %sil, %eax ++ mov %RDI_LP, %RDX_LP ++ rep stosb ++ mov %RDX_LP, %RAX_LP ++ ret ++L(stosb_return_zero): ++ movq %rdi, %rax ++ ret ++END (__memset_erms) ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/memset-evex-unaligned-erms.S b/sysdeps/x86_64/multiarch/memset-evex-unaligned-erms.S +--- a/sysdeps/x86_64/multiarch/memset-evex-unaligned-erms.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memset-evex-unaligned-erms.S 2025-10-20 21:02:22.000000000 +0300 +@@ -15,13 +15,19 @@ + + # define VZEROUPPER + +-# define MEMSET_VDUP_TO_VEC0_AND_SET_RETURN(d, r) \ +- movq r, %rax; \ +- vpbroadcastb d, %VEC0 +- +-# define WMEMSET_VDUP_TO_VEC0_AND_SET_RETURN(d, r) \ +- movq r, %rax; \ +- vpbroadcastd d, %VEC0 ++# define MEMSET_SET_VEC0_AND_SET_RETURN(d, r) \ ++ vpbroadcastb d, %VEC0; \ ++ movq r, %rax ++ ++# define WMEMSET_SET_VEC0_AND_SET_RETURN(d, r) \ ++ vpbroadcastd d, %VEC0; \ ++ movq r, %rax ++ ++# define MEMSET_VDUP_TO_VEC0_HIGH() ++# define MEMSET_VDUP_TO_VEC0_LOW() ++ ++# define WMEMSET_VDUP_TO_VEC0_HIGH() ++# define WMEMSET_VDUP_TO_VEC0_LOW() + + # define SECTION(p) p##.evex + # define MEMSET_SYMBOL(p,s) p##_evex_##s +diff -Nuar a/sysdeps/x86_64/multiarch/memset-sse2-unaligned-erms.S b/sysdeps/x86_64/multiarch/memset-sse2-unaligned-erms.S +--- a/sysdeps/x86_64/multiarch/memset-sse2-unaligned-erms.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memset-sse2-unaligned-erms.S 2025-10-20 21:02:22.000000000 +0300 +@@ -30,9 +30,7 @@ + # endif + + # undef weak_alias +-# define weak_alias(original, alias) \ +- .weak bzero; bzero = __bzero +- ++# define weak_alias(original, alias) + # undef strong_alias + # define strong_alias(ignored1, ignored2) + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/memset-vec-unaligned-erms.S b/sysdeps/x86_64/multiarch/memset-vec-unaligned-erms.S +--- a/sysdeps/x86_64/multiarch/memset-vec-unaligned-erms.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/memset-vec-unaligned-erms.S 2025-10-20 21:02:22.000000000 +0300 +@@ -1,4 +1,4 @@ +-/* memset/bzero with unaligned store and rep stosb ++/* memset with unaligned store and rep stosb + Copyright (C) 2016-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + +@@ -58,8 +58,10 @@ + #ifndef MOVQ + # if VEC_SIZE > 16 + # define MOVQ vmovq ++# define MOVD vmovd + # else + # define MOVQ movq ++# define MOVD movd + # endif + #endif + +@@ -72,9 +74,29 @@ + #if defined USE_WITH_EVEX || defined USE_WITH_AVX512 + # define END_REG rcx + # define LOOP_REG rdi ++# define LESS_VEC_REG rax + #else + # define END_REG rdi + # define LOOP_REG rdx ++# define LESS_VEC_REG rdi ++#endif ++ ++#ifdef USE_XMM_LESS_VEC ++# define XMM_SMALL 1 ++#else ++# define XMM_SMALL 0 ++#endif ++ ++#ifdef USE_LESS_VEC_MASK_STORE ++# define SET_REG64 rcx ++# define SET_REG32 ecx ++# define SET_REG16 cx ++# define SET_REG8 cl ++#else ++# define SET_REG64 rsi ++# define SET_REG32 esi ++# define SET_REG16 si ++# define SET_REG8 sil + #endif + + #define PAGE_SIZE 4096 +@@ -88,18 +110,7 @@ + # error SECTION is not defined! + #endif + +- .section SECTION(.text),"ax",@progbits +-#if VEC_SIZE == 16 && IS_IN (libc) +-ENTRY (__bzero) +- mov %RDI_LP, %RAX_LP /* Set return value. */ +- mov %RSI_LP, %RDX_LP /* Set n. */ +- xorl %esi, %esi +- pxor %XMM0, %XMM0 +- jmp L(entry_from_bzero) +-END (__bzero) +-weak_alias (__bzero, bzero) +-#endif +- ++ .section SECTION(.text), "ax", @progbits + #if IS_IN (libc) + # if defined SHARED + ENTRY_CHK (WMEMSET_CHK_SYMBOL (__wmemset_chk, unaligned)) +@@ -110,8 +121,12 @@ + + ENTRY (WMEMSET_SYMBOL (__wmemset, unaligned)) + shl $2, %RDX_LP +- WMEMSET_VDUP_TO_VEC0_AND_SET_RETURN (%esi, %rdi) +- jmp L(entry_from_bzero) ++ WMEMSET_SET_VEC0_AND_SET_RETURN (%esi, %rdi) ++ WMEMSET_VDUP_TO_VEC0_LOW() ++ cmpq $VEC_SIZE, %rdx ++ jb L(less_vec_from_wmemset) ++ WMEMSET_VDUP_TO_VEC0_HIGH() ++ jmp L(entry_from_wmemset) + END (WMEMSET_SYMBOL (__wmemset, unaligned)) + #endif + +@@ -123,14 +138,15 @@ + #endif + + ENTRY (MEMSET_SYMBOL (__memset, unaligned)) +- MEMSET_VDUP_TO_VEC0_AND_SET_RETURN (%esi, %rdi) ++ MEMSET_SET_VEC0_AND_SET_RETURN (%esi, %rdi) + # ifdef __ILP32__ + /* Clear the upper 32 bits. */ + mov %edx, %edx + # endif +-L(entry_from_bzero): + cmpq $VEC_SIZE, %rdx + jb L(less_vec) ++ MEMSET_VDUP_TO_VEC0_HIGH() ++L(entry_from_wmemset): + cmpq $(VEC_SIZE * 2), %rdx + ja L(more_2x_vec) + /* From VEC and to 2 * VEC. No branch when size == VEC_SIZE. */ +@@ -140,37 +156,6 @@ + #if defined USE_MULTIARCH && IS_IN (libc) + END (MEMSET_SYMBOL (__memset, unaligned)) + +-# if VEC_SIZE == 16 +-ENTRY (__memset_chk_erms) +- cmp %RDX_LP, %RCX_LP +- jb HIDDEN_JUMPTARGET (__chk_fail) +-END (__memset_chk_erms) +- +-/* Only used to measure performance of REP STOSB. */ +-ENTRY (__memset_erms) +- /* Skip zero length. */ +- test %RDX_LP, %RDX_LP +- jnz L(stosb) +- movq %rdi, %rax +- ret +-# else +-/* Provide a hidden symbol to debugger. */ +- .hidden MEMSET_SYMBOL (__memset, erms) +-ENTRY (MEMSET_SYMBOL (__memset, erms)) +-# endif +-L(stosb): +- mov %RDX_LP, %RCX_LP +- movzbl %sil, %eax +- mov %RDI_LP, %RDX_LP +- rep stosb +- mov %RDX_LP, %RAX_LP +- VZEROUPPER_RETURN +-# if VEC_SIZE == 16 +-END (__memset_erms) +-# else +-END (MEMSET_SYMBOL (__memset, erms)) +-# endif +- + # if defined SHARED && IS_IN (libc) + ENTRY_CHK (MEMSET_CHK_SYMBOL (__memset_chk, unaligned_erms)) + cmp %RDX_LP, %RCX_LP +@@ -179,27 +164,27 @@ + # endif + + ENTRY_P2ALIGN (MEMSET_SYMBOL (__memset, unaligned_erms), 6) +- MEMSET_VDUP_TO_VEC0_AND_SET_RETURN (%esi, %rdi) ++ MEMSET_SET_VEC0_AND_SET_RETURN (%esi, %rdi) + # ifdef __ILP32__ + /* Clear the upper 32 bits. */ + mov %edx, %edx + # endif + cmp $VEC_SIZE, %RDX_LP + jb L(less_vec) ++ MEMSET_VDUP_TO_VEC0_HIGH () + cmp $(VEC_SIZE * 2), %RDX_LP + ja L(stosb_more_2x_vec) +- /* From VEC and to 2 * VEC. No branch when size == VEC_SIZE. +- */ +- VMOVU %VEC(0), (%rax) +- VMOVU %VEC(0), -VEC_SIZE(%rax, %rdx) ++ /* From VEC and to 2 * VEC. No branch when size == VEC_SIZE. */ ++ VMOVU %VEC(0), (%rdi) ++ VMOVU %VEC(0), (VEC_SIZE * -1)(%rdi, %rdx) + VZEROUPPER_RETURN + #endif + +- .p2align 4,, 10 ++ .p2align 4,, 4 + L(last_2x_vec): + #ifdef USE_LESS_VEC_MASK_STORE +- VMOVU %VEC(0), (VEC_SIZE * 2 + LOOP_4X_OFFSET)(%rcx) +- VMOVU %VEC(0), (VEC_SIZE * 3 + LOOP_4X_OFFSET)(%rcx) ++ VMOVU %VEC(0), (VEC_SIZE * -2)(%rdi, %rdx) ++ VMOVU %VEC(0), (VEC_SIZE * -1)(%rdi, %rdx) + #else + VMOVU %VEC(0), (VEC_SIZE * -2)(%rdi) + VMOVU %VEC(0), (VEC_SIZE * -1)(%rdi) +@@ -212,6 +197,7 @@ + #ifdef USE_LESS_VEC_MASK_STORE + .p2align 4,, 10 + L(less_vec): ++L(less_vec_from_wmemset): + /* Less than 1 VEC. */ + # if VEC_SIZE != 16 && VEC_SIZE != 32 && VEC_SIZE != 64 + # error Unsupported VEC_SIZE! +@@ -262,28 +248,18 @@ + /* Fallthrough goes to L(loop_4x_vec). Tests for memset (2x, 4x] + and (4x, 8x] jump to target. */ + L(more_2x_vec): +- +- /* Two different methods of setting up pointers / compare. The +- two methods are based on the fact that EVEX/AVX512 mov +- instructions take more bytes then AVX2/SSE2 mov instructions. As +- well that EVEX/AVX512 machines also have fast LEA_BID. Both +- setup and END_REG to avoid complex address mode. For EVEX/AVX512 +- this saves code size and keeps a few targets in one fetch block. +- For AVX2/SSE2 this helps prevent AGU bottlenecks. */ +-#if defined USE_WITH_EVEX || defined USE_WITH_AVX512 +- /* If EVEX/AVX512 compute END_REG - (VEC_SIZE * 4 + +- LOOP_4X_OFFSET) with LEA_BID. */ +- +- /* END_REG is rcx for EVEX/AVX512. */ +- leaq -(VEC_SIZE * 4 + LOOP_4X_OFFSET)(%rdi, %rdx), %END_REG +-#endif +- +- /* Stores to first 2x VEC before cmp as any path forward will +- require it. */ +- VMOVU %VEC(0), (%rax) +- VMOVU %VEC(0), VEC_SIZE(%rax) ++ /* Store next 2x vec regardless. */ ++ VMOVU %VEC(0), (%rdi) ++ VMOVU %VEC(0), (VEC_SIZE * 1)(%rdi) + + ++ /* Two different methods of setting up pointers / compare. The two ++ methods are based on the fact that EVEX/AVX512 mov instructions take ++ more bytes then AVX2/SSE2 mov instructions. As well that EVEX/AVX512 ++ machines also have fast LEA_BID. Both setup and END_REG to avoid complex ++ address mode. For EVEX/AVX512 this saves code size and keeps a few ++ targets in one fetch block. For AVX2/SSE2 this helps prevent AGU ++ bottlenecks. */ + #if !(defined USE_WITH_EVEX || defined USE_WITH_AVX512) + /* If AVX2/SSE2 compute END_REG (rdi) with ALU. */ + addq %rdx, %END_REG +@@ -292,6 +268,15 @@ + cmpq $(VEC_SIZE * 4), %rdx + jbe L(last_2x_vec) + ++ ++#if defined USE_WITH_EVEX || defined USE_WITH_AVX512 ++ /* If EVEX/AVX512 compute END_REG - (VEC_SIZE * 4 + LOOP_4X_OFFSET) with ++ LEA_BID. */ ++ ++ /* END_REG is rcx for EVEX/AVX512. */ ++ leaq -(VEC_SIZE * 4 + LOOP_4X_OFFSET)(%rdi, %rdx), %END_REG ++#endif ++ + /* Store next 2x vec regardless. */ + VMOVU %VEC(0), (VEC_SIZE * 2)(%rax) + VMOVU %VEC(0), (VEC_SIZE * 3)(%rax) +@@ -316,7 +301,7 @@ + leaq (VEC_SIZE * 4)(%rax), %LOOP_REG + #endif + /* Align dst for loop. */ +- andq $(VEC_SIZE * -2), %LOOP_REG ++ andq $(VEC_SIZE * -1), %LOOP_REG + .p2align 4 + L(loop): + VMOVA %VEC(0), LOOP_4X_OFFSET(%LOOP_REG) +@@ -355,65 +340,93 @@ + /* Define L(less_vec) only if not otherwise defined. */ + .p2align 4 + L(less_vec): ++ /* Broadcast esi to partial register (i.e VEC_SIZE == 32 broadcast to ++ xmm). This is only does anything for AVX2. */ ++ MEMSET_VDUP_TO_VEC0_LOW () ++L(less_vec_from_wmemset): + #endif + L(cross_page): + #if VEC_SIZE > 32 + cmpl $32, %edx +- jae L(between_32_63) ++ jge L(between_32_63) + #endif + #if VEC_SIZE > 16 + cmpl $16, %edx +- jae L(between_16_31) ++ jge L(between_16_31) ++#endif ++#ifndef USE_XMM_LESS_VEC ++ MOVQ %XMM0, %SET_REG64 + #endif +- MOVQ %XMM0, %rdi + cmpl $8, %edx +- jae L(between_8_15) ++ jge L(between_8_15) + cmpl $4, %edx +- jae L(between_4_7) ++ jge L(between_4_7) + cmpl $1, %edx +- ja L(between_2_3) +- jb L(return) +- movb %sil, (%rax) +- VZEROUPPER_RETURN ++ jg L(between_2_3) ++ jl L(between_0_0) ++ movb %SET_REG8, (%LESS_VEC_REG) ++L(between_0_0): ++ ret + +- /* Align small targets only if not doing so would cross a fetch +- line. */ ++ /* Align small targets only if not doing so would cross a fetch line. ++ */ + #if VEC_SIZE > 32 + .p2align 4,, SMALL_MEMSET_ALIGN(MOV_SIZE, RET_SIZE) + /* From 32 to 63. No branch when size == 32. */ + L(between_32_63): +- VMOVU %YMM0, (%rax) +- VMOVU %YMM0, -32(%rax, %rdx) ++ VMOVU %YMM0, (%LESS_VEC_REG) ++ VMOVU %YMM0, -32(%LESS_VEC_REG, %rdx) + VZEROUPPER_RETURN + #endif + + #if VEC_SIZE >= 32 +- .p2align 4,, SMALL_MEMSET_ALIGN(MOV_SIZE, RET_SIZE) ++ .p2align 4,, SMALL_MEMSET_ALIGN(MOV_SIZE, 1) + L(between_16_31): + /* From 16 to 31. No branch when size == 16. */ +- VMOVU %XMM0, (%rax) +- VMOVU %XMM0, -16(%rax, %rdx) +- VZEROUPPER_RETURN ++ VMOVU %XMM0, (%LESS_VEC_REG) ++ VMOVU %XMM0, -16(%LESS_VEC_REG, %rdx) ++ ret + #endif + +- .p2align 4,, SMALL_MEMSET_ALIGN(3, RET_SIZE) ++ /* Move size is 3 for SSE2, EVEX, and AVX512. Move size is 4 for AVX2. ++ */ ++ .p2align 4,, SMALL_MEMSET_ALIGN(3 + XMM_SMALL, 1) + L(between_8_15): + /* From 8 to 15. No branch when size == 8. */ +- movq %rdi, (%rax) +- movq %rdi, -8(%rax, %rdx) +- VZEROUPPER_RETURN ++#ifdef USE_XMM_LESS_VEC ++ MOVQ %XMM0, (%rdi) ++ MOVQ %XMM0, -8(%rdi, %rdx) ++#else ++ movq %SET_REG64, (%LESS_VEC_REG) ++ movq %SET_REG64, -8(%LESS_VEC_REG, %rdx) ++#endif ++ ret + +- .p2align 4,, SMALL_MEMSET_ALIGN(2, RET_SIZE) ++ /* Move size is 2 for SSE2, EVEX, and AVX512. Move size is 4 for AVX2. ++ */ ++ .p2align 4,, SMALL_MEMSET_ALIGN(2 << XMM_SMALL, 1) + L(between_4_7): + /* From 4 to 7. No branch when size == 4. */ +- movl %edi, (%rax) +- movl %edi, -4(%rax, %rdx) +- VZEROUPPER_RETURN ++#ifdef USE_XMM_LESS_VEC ++ MOVD %XMM0, (%rdi) ++ MOVD %XMM0, -4(%rdi, %rdx) ++#else ++ movl %SET_REG32, (%LESS_VEC_REG) ++ movl %SET_REG32, -4(%LESS_VEC_REG, %rdx) ++#endif ++ ret + +- .p2align 4,, SMALL_MEMSET_ALIGN(3, RET_SIZE) ++ /* 4 * XMM_SMALL for the third mov for AVX2. */ ++ .p2align 4,, 4 * XMM_SMALL + SMALL_MEMSET_ALIGN(3, 1) + L(between_2_3): + /* From 2 to 3. No branch when size == 2. */ +- movw %di, (%rax) +- movb %dil, -1(%rax, %rdx) +- VZEROUPPER_RETURN ++#ifdef USE_XMM_LESS_VEC ++ movb %SET_REG8, (%rdi) ++ movb %SET_REG8, 1(%rdi) ++ movb %SET_REG8, -1(%rdi, %rdx) ++#else ++ movw %SET_REG16, (%LESS_VEC_REG) ++ movb %SET_REG8, -1(%LESS_VEC_REG, %rdx) ++#endif ++ ret + END (MEMSET_SYMBOL (__memset, unaligned_erms)) +diff -Nuar a/sysdeps/x86_64/multiarch/sse2-vecs.h b/sysdeps/x86_64/multiarch/sse2-vecs.h +--- a/sysdeps/x86_64/multiarch/sse2-vecs.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/sse2-vecs.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,47 @@ ++/* Common config for SSE2 VECs ++ All versions must be listed in ifunc-impl-list.c. ++ Copyright (C) 2022 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 ++ . */ ++ ++#ifndef _SSE2_VECS_H ++#define _SSE2_VECS_H 1 ++ ++#ifdef VEC_SIZE ++# error "Multiple VEC configs included!" ++#endif ++ ++#define VEC_SIZE 16 ++#include "vec-macros.h" ++ ++#define USE_WITH_SSE2 1 ++#define SECTION(p) p ++ ++/* 3-byte mov instructions with SSE2. */ ++#define MOV_SIZE 3 ++/* No vzeroupper needed. */ ++#define RET_SIZE 1 ++#define VZEROUPPER ++ ++#define VMOVU movups ++#define VMOVA movaps ++#define VMOVNT movntdq ++ ++#define VEC_xmm VEC_any_xmm ++#define VEC VEC_any_xmm ++ ++ ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/strcasecmp_l-avx2-rtm.S b/sysdeps/x86_64/multiarch/strcasecmp_l-avx2-rtm.S +--- a/sysdeps/x86_64/multiarch/strcasecmp_l-avx2-rtm.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strcasecmp_l-avx2-rtm.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,15 @@ ++#ifndef STRCMP ++# define STRCMP __strcasecmp_l_avx2_rtm ++#endif ++ ++#define _GLABEL(x) x ## _rtm ++#define GLABEL(x) _GLABEL(x) ++ ++#define ZERO_UPPER_VEC_REGISTERS_RETURN \ ++ ZERO_UPPER_VEC_REGISTERS_RETURN_XTEST ++ ++#define VZEROUPPER_RETURN jmp L(return_vzeroupper) ++ ++#define SECTION(p) p##.avx.rtm ++ ++#include "strcasecmp_l-avx2.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strcasecmp_l-avx2.S b/sysdeps/x86_64/multiarch/strcasecmp_l-avx2.S +--- a/sysdeps/x86_64/multiarch/strcasecmp_l-avx2.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strcasecmp_l-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,23 @@ ++/* strcasecmp_l optimized with AVX2. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#ifndef STRCMP ++# define STRCMP __strcasecmp_l_avx2 ++#endif ++#define USE_AS_STRCASECMP_L ++#include "strcmp-avx2.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strcasecmp_l-avx.S b/sysdeps/x86_64/multiarch/strcasecmp_l-avx.S +--- a/sysdeps/x86_64/multiarch/strcasecmp_l-avx.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strcasecmp_l-avx.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,22 +0,0 @@ +-/* strcasecmp_l optimized with AVX. +- Copyright (C) 2017-2022 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 +- . */ +- +-#define STRCMP_SSE42 __strcasecmp_l_avx +-#define USE_AVX 1 +-#define USE_AS_STRCASECMP_L +-#include "strcmp-sse42.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strcasecmp_l-evex.S b/sysdeps/x86_64/multiarch/strcasecmp_l-evex.S +--- a/sysdeps/x86_64/multiarch/strcasecmp_l-evex.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strcasecmp_l-evex.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,23 @@ ++/* strcasecmp_l optimized with EVEX. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#ifndef STRCMP ++# define STRCMP __strcasecmp_l_evex ++#endif ++#define USE_AS_STRCASECMP_L ++#include "strcmp-evex.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strchr-avx2.S b/sysdeps/x86_64/multiarch/strchr-avx2.S +--- a/sysdeps/x86_64/multiarch/strchr-avx2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strchr-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -48,13 +48,13 @@ + # define PAGE_SIZE 4096 + + .section SECTION(.text),"ax",@progbits +-ENTRY (STRCHR) ++ENTRY_P2ALIGN (STRCHR, 5) + /* Broadcast CHAR to YMM0. */ + vmovd %esi, %xmm0 + movl %edi, %eax + andl $(PAGE_SIZE - 1), %eax + VPBROADCAST %xmm0, %ymm0 +- vpxor %xmm9, %xmm9, %xmm9 ++ vpxor %xmm1, %xmm1, %xmm1 + + /* Check if we cross page boundary with one vector load. */ + cmpl $(PAGE_SIZE - VEC_SIZE), %eax +@@ -62,37 +62,29 @@ + + /* Check the first VEC_SIZE bytes. Search for both CHAR and the + null byte. */ +- vmovdqu (%rdi), %ymm8 +- VPCMPEQ %ymm8, %ymm0, %ymm1 +- VPCMPEQ %ymm8, %ymm9, %ymm2 +- vpor %ymm1, %ymm2, %ymm1 +- vpmovmskb %ymm1, %eax ++ vmovdqu (%rdi), %ymm2 ++ VPCMPEQ %ymm2, %ymm0, %ymm3 ++ VPCMPEQ %ymm2, %ymm1, %ymm2 ++ vpor %ymm3, %ymm2, %ymm3 ++ vpmovmskb %ymm3, %eax + testl %eax, %eax + jz L(aligned_more) + tzcntl %eax, %eax + # ifndef USE_AS_STRCHRNUL +- /* Found CHAR or the null byte. */ ++ /* Found CHAR or the null byte. */ + cmp (%rdi, %rax), %CHAR_REG ++ /* NB: Use a branch instead of cmovcc here. The expectation is ++ that with strchr the user will branch based on input being ++ null. Since this branch will be 100% predictive of the user ++ branch a branch miss here should save what otherwise would ++ be branch miss in the user code. Otherwise using a branch 1) ++ saves code size and 2) is faster in highly predictable ++ environments. */ + jne L(zero) + # endif + addq %rdi, %rax +- VZEROUPPER_RETURN +- +- /* .p2align 5 helps keep performance more consistent if ENTRY() +- alignment % 32 was either 16 or 0. As well this makes the +- alignment % 32 of the loop_4x_vec fixed which makes tuning it +- easier. */ +- .p2align 5 +-L(first_vec_x4): +- tzcntl %eax, %eax +- addq $(VEC_SIZE * 3 + 1), %rdi +-# ifndef USE_AS_STRCHRNUL +- /* Found CHAR or the null byte. */ +- cmp (%rdi, %rax), %CHAR_REG +- jne L(zero) +-# endif +- addq %rdi, %rax +- VZEROUPPER_RETURN ++L(return_vzeroupper): ++ ZERO_UPPER_VEC_REGISTERS_RETURN + + # ifndef USE_AS_STRCHRNUL + L(zero): +@@ -103,7 +95,8 @@ + + .p2align 4 + L(first_vec_x1): +- tzcntl %eax, %eax ++ /* Use bsf to save code size. */ ++ bsfl %eax, %eax + incq %rdi + # ifndef USE_AS_STRCHRNUL + /* Found CHAR or the null byte. */ +@@ -113,9 +106,10 @@ + addq %rdi, %rax + VZEROUPPER_RETURN + +- .p2align 4 ++ .p2align 4,, 10 + L(first_vec_x2): +- tzcntl %eax, %eax ++ /* Use bsf to save code size. */ ++ bsfl %eax, %eax + addq $(VEC_SIZE + 1), %rdi + # ifndef USE_AS_STRCHRNUL + /* Found CHAR or the null byte. */ +@@ -125,9 +119,10 @@ + addq %rdi, %rax + VZEROUPPER_RETURN + +- .p2align 4 ++ .p2align 4,, 8 + L(first_vec_x3): +- tzcntl %eax, %eax ++ /* Use bsf to save code size. */ ++ bsfl %eax, %eax + addq $(VEC_SIZE * 2 + 1), %rdi + # ifndef USE_AS_STRCHRNUL + /* Found CHAR or the null byte. */ +@@ -137,6 +132,21 @@ + addq %rdi, %rax + VZEROUPPER_RETURN + ++ .p2align 4,, 10 ++L(first_vec_x4): ++ /* Use bsf to save code size. */ ++ bsfl %eax, %eax ++ addq $(VEC_SIZE * 3 + 1), %rdi ++# ifndef USE_AS_STRCHRNUL ++ /* Found CHAR or the null byte. */ ++ cmp (%rdi, %rax), %CHAR_REG ++ jne L(zero) ++# endif ++ addq %rdi, %rax ++ VZEROUPPER_RETURN ++ ++ ++ + .p2align 4 + L(aligned_more): + /* Align data to VEC_SIZE - 1. This is the same number of +@@ -146,90 +156,92 @@ + L(cross_page_continue): + /* Check the next 4 * VEC_SIZE. Only one VEC_SIZE at a time + since data is only aligned to VEC_SIZE. */ +- vmovdqa 1(%rdi), %ymm8 +- VPCMPEQ %ymm8, %ymm0, %ymm1 +- VPCMPEQ %ymm8, %ymm9, %ymm2 +- vpor %ymm1, %ymm2, %ymm1 +- vpmovmskb %ymm1, %eax ++ vmovdqa 1(%rdi), %ymm2 ++ VPCMPEQ %ymm2, %ymm0, %ymm3 ++ VPCMPEQ %ymm2, %ymm1, %ymm2 ++ vpor %ymm3, %ymm2, %ymm3 ++ vpmovmskb %ymm3, %eax + testl %eax, %eax + jnz L(first_vec_x1) + +- vmovdqa (VEC_SIZE + 1)(%rdi), %ymm8 +- VPCMPEQ %ymm8, %ymm0, %ymm1 +- VPCMPEQ %ymm8, %ymm9, %ymm2 +- vpor %ymm1, %ymm2, %ymm1 +- vpmovmskb %ymm1, %eax ++ vmovdqa (VEC_SIZE + 1)(%rdi), %ymm2 ++ VPCMPEQ %ymm2, %ymm0, %ymm3 ++ VPCMPEQ %ymm2, %ymm1, %ymm2 ++ vpor %ymm3, %ymm2, %ymm3 ++ vpmovmskb %ymm3, %eax + testl %eax, %eax + jnz L(first_vec_x2) + +- vmovdqa (VEC_SIZE * 2 + 1)(%rdi), %ymm8 +- VPCMPEQ %ymm8, %ymm0, %ymm1 +- VPCMPEQ %ymm8, %ymm9, %ymm2 +- vpor %ymm1, %ymm2, %ymm1 +- vpmovmskb %ymm1, %eax ++ vmovdqa (VEC_SIZE * 2 + 1)(%rdi), %ymm2 ++ VPCMPEQ %ymm2, %ymm0, %ymm3 ++ VPCMPEQ %ymm2, %ymm1, %ymm2 ++ vpor %ymm3, %ymm2, %ymm3 ++ vpmovmskb %ymm3, %eax + testl %eax, %eax + jnz L(first_vec_x3) + +- vmovdqa (VEC_SIZE * 3 + 1)(%rdi), %ymm8 +- VPCMPEQ %ymm8, %ymm0, %ymm1 +- VPCMPEQ %ymm8, %ymm9, %ymm2 +- vpor %ymm1, %ymm2, %ymm1 +- vpmovmskb %ymm1, %eax ++ vmovdqa (VEC_SIZE * 3 + 1)(%rdi), %ymm2 ++ VPCMPEQ %ymm2, %ymm0, %ymm3 ++ VPCMPEQ %ymm2, %ymm1, %ymm2 ++ vpor %ymm3, %ymm2, %ymm3 ++ vpmovmskb %ymm3, %eax + testl %eax, %eax + jnz L(first_vec_x4) +- /* Align data to VEC_SIZE * 4 - 1. */ +- addq $(VEC_SIZE * 4 + 1), %rdi +- andq $-(VEC_SIZE * 4), %rdi ++ /* Align data to VEC_SIZE * 4 - 1. */ ++ incq %rdi ++ orq $(VEC_SIZE * 4 - 1), %rdi + .p2align 4 + L(loop_4x_vec): + /* Compare 4 * VEC at a time forward. */ +- vmovdqa (%rdi), %ymm5 +- vmovdqa (VEC_SIZE)(%rdi), %ymm6 +- vmovdqa (VEC_SIZE * 2)(%rdi), %ymm7 +- vmovdqa (VEC_SIZE * 3)(%rdi), %ymm8 ++ vmovdqa 1(%rdi), %ymm6 ++ vmovdqa (VEC_SIZE + 1)(%rdi), %ymm7 + + /* Leaves only CHARS matching esi as 0. */ +- vpxor %ymm5, %ymm0, %ymm1 + vpxor %ymm6, %ymm0, %ymm2 + vpxor %ymm7, %ymm0, %ymm3 +- vpxor %ymm8, %ymm0, %ymm4 + +- VPMINU %ymm1, %ymm5, %ymm1 + VPMINU %ymm2, %ymm6, %ymm2 + VPMINU %ymm3, %ymm7, %ymm3 +- VPMINU %ymm4, %ymm8, %ymm4 + +- VPMINU %ymm1, %ymm2, %ymm5 +- VPMINU %ymm3, %ymm4, %ymm6 ++ vmovdqa (VEC_SIZE * 2 + 1)(%rdi), %ymm6 ++ vmovdqa (VEC_SIZE * 3 + 1)(%rdi), %ymm7 ++ ++ vpxor %ymm6, %ymm0, %ymm4 ++ vpxor %ymm7, %ymm0, %ymm5 + +- VPMINU %ymm5, %ymm6, %ymm6 ++ VPMINU %ymm4, %ymm6, %ymm4 ++ VPMINU %ymm5, %ymm7, %ymm5 + +- VPCMPEQ %ymm6, %ymm9, %ymm6 +- vpmovmskb %ymm6, %ecx ++ VPMINU %ymm2, %ymm3, %ymm6 ++ VPMINU %ymm4, %ymm5, %ymm7 ++ ++ VPMINU %ymm6, %ymm7, %ymm7 ++ ++ VPCMPEQ %ymm7, %ymm1, %ymm7 ++ vpmovmskb %ymm7, %ecx + subq $-(VEC_SIZE * 4), %rdi + testl %ecx, %ecx + jz L(loop_4x_vec) + +- +- VPCMPEQ %ymm1, %ymm9, %ymm1 +- vpmovmskb %ymm1, %eax ++ VPCMPEQ %ymm2, %ymm1, %ymm2 ++ vpmovmskb %ymm2, %eax + testl %eax, %eax + jnz L(last_vec_x0) + + +- VPCMPEQ %ymm5, %ymm9, %ymm2 +- vpmovmskb %ymm2, %eax ++ VPCMPEQ %ymm3, %ymm1, %ymm3 ++ vpmovmskb %ymm3, %eax + testl %eax, %eax + jnz L(last_vec_x1) + +- VPCMPEQ %ymm3, %ymm9, %ymm3 +- vpmovmskb %ymm3, %eax ++ VPCMPEQ %ymm4, %ymm1, %ymm4 ++ vpmovmskb %ymm4, %eax + /* rcx has combined result from all 4 VEC. It will only be used + if the first 3 other VEC all did not contain a match. */ + salq $32, %rcx + orq %rcx, %rax + tzcntq %rax, %rax +- subq $(VEC_SIZE * 2), %rdi ++ subq $(VEC_SIZE * 2 - 1), %rdi + # ifndef USE_AS_STRCHRNUL + /* Found CHAR or the null byte. */ + cmp (%rdi, %rax), %CHAR_REG +@@ -239,10 +251,11 @@ + VZEROUPPER_RETURN + + +- .p2align 4 ++ .p2align 4,, 10 + L(last_vec_x0): +- tzcntl %eax, %eax +- addq $-(VEC_SIZE * 4), %rdi ++ /* Use bsf to save code size. */ ++ bsfl %eax, %eax ++ addq $-(VEC_SIZE * 4 - 1), %rdi + # ifndef USE_AS_STRCHRNUL + /* Found CHAR or the null byte. */ + cmp (%rdi, %rax), %CHAR_REG +@@ -251,16 +264,11 @@ + addq %rdi, %rax + VZEROUPPER_RETURN + +-# ifndef USE_AS_STRCHRNUL +-L(zero_end): +- xorl %eax, %eax +- VZEROUPPER_RETURN +-# endif + +- .p2align 4 ++ .p2align 4,, 10 + L(last_vec_x1): + tzcntl %eax, %eax +- subq $(VEC_SIZE * 3), %rdi ++ subq $(VEC_SIZE * 3 - 1), %rdi + # ifndef USE_AS_STRCHRNUL + /* Found CHAR or the null byte. */ + cmp (%rdi, %rax), %CHAR_REG +@@ -269,18 +277,23 @@ + addq %rdi, %rax + VZEROUPPER_RETURN + ++# ifndef USE_AS_STRCHRNUL ++L(zero_end): ++ xorl %eax, %eax ++ VZEROUPPER_RETURN ++# endif + + /* Cold case for crossing page with first load. */ +- .p2align 4 ++ .p2align 4,, 8 + L(cross_page_boundary): + movq %rdi, %rdx + /* Align rdi to VEC_SIZE - 1. */ + orq $(VEC_SIZE - 1), %rdi +- vmovdqa -(VEC_SIZE - 1)(%rdi), %ymm8 +- VPCMPEQ %ymm8, %ymm0, %ymm1 +- VPCMPEQ %ymm8, %ymm9, %ymm2 +- vpor %ymm1, %ymm2, %ymm1 +- vpmovmskb %ymm1, %eax ++ vmovdqa -(VEC_SIZE - 1)(%rdi), %ymm2 ++ VPCMPEQ %ymm2, %ymm0, %ymm3 ++ VPCMPEQ %ymm2, %ymm1, %ymm2 ++ vpor %ymm3, %ymm2, %ymm3 ++ vpmovmskb %ymm3, %eax + /* Remove the leading bytes. sarxl only uses bits [5:0] of COUNT + so no need to manually mod edx. */ + sarxl %edx, %eax, %eax +@@ -291,13 +304,10 @@ + xorl %ecx, %ecx + /* Found CHAR or the null byte. */ + cmp (%rdx, %rax), %CHAR_REG +- leaq (%rdx, %rax), %rax +- cmovne %rcx, %rax +-# else +- addq %rdx, %rax ++ jne L(zero_end) + # endif +-L(return_vzeroupper): +- ZERO_UPPER_VEC_REGISTERS_RETURN ++ addq %rdx, %rax ++ VZEROUPPER_RETURN + + END (STRCHR) +-# endif ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/strchr-evex.S b/sysdeps/x86_64/multiarch/strchr-evex.S +--- a/sysdeps/x86_64/multiarch/strchr-evex.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strchr-evex.S 2025-10-20 21:02:22.000000000 +0300 +@@ -30,6 +30,7 @@ + # ifdef USE_AS_WCSCHR + # define VPBROADCAST vpbroadcastd + # define VPCMP vpcmpd ++# define VPTESTN vptestnmd + # define VPMINU vpminud + # define CHAR_REG esi + # define SHIFT_REG ecx +@@ -37,6 +38,7 @@ + # else + # define VPBROADCAST vpbroadcastb + # define VPCMP vpcmpb ++# define VPTESTN vptestnmb + # define VPMINU vpminub + # define CHAR_REG sil + # define SHIFT_REG edx +@@ -61,13 +63,11 @@ + # define CHAR_PER_VEC (VEC_SIZE / CHAR_SIZE) + + .section .text.evex,"ax",@progbits +-ENTRY (STRCHR) ++ENTRY_P2ALIGN (STRCHR, 5) + /* Broadcast CHAR to YMM0. */ + VPBROADCAST %esi, %YMM0 + movl %edi, %eax + andl $(PAGE_SIZE - 1), %eax +- vpxorq %XMMZERO, %XMMZERO, %XMMZERO +- + /* Check if we cross page boundary with one vector load. + Otherwise it is safe to use an unaligned load. */ + cmpl $(PAGE_SIZE - VEC_SIZE), %eax +@@ -81,11 +81,23 @@ + vpxorq %YMM1, %YMM0, %YMM2 + VPMINU %YMM2, %YMM1, %YMM2 + /* Each bit in K0 represents a CHAR or a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM2, %k0 ++ VPTESTN %YMM2, %YMM2, %k0 + kmovd %k0, %eax + testl %eax, %eax + jz L(aligned_more) + tzcntl %eax, %eax ++# ifndef USE_AS_STRCHRNUL ++ /* Found CHAR or the null byte. */ ++ cmp (%rdi, %rax, CHAR_SIZE), %CHAR_REG ++ /* NB: Use a branch instead of cmovcc here. The expectation is ++ that with strchr the user will branch based on input being ++ null. Since this branch will be 100% predictive of the user ++ branch a branch miss here should save what otherwise would ++ be branch miss in the user code. Otherwise using a branch 1) ++ saves code size and 2) is faster in highly predictable ++ environments. */ ++ jne L(zero) ++# endif + # ifdef USE_AS_WCSCHR + /* NB: Multiply wchar_t count by 4 to get the number of bytes. + */ +@@ -93,37 +105,11 @@ + # else + addq %rdi, %rax + # endif +-# ifndef USE_AS_STRCHRNUL +- /* Found CHAR or the null byte. */ +- cmp (%rax), %CHAR_REG +- jne L(zero) +-# endif + ret + +- /* .p2align 5 helps keep performance more consistent if ENTRY() +- alignment % 32 was either 16 or 0. As well this makes the +- alignment % 32 of the loop_4x_vec fixed which makes tuning it +- easier. */ +- .p2align 5 +-L(first_vec_x3): +- tzcntl %eax, %eax +-# ifndef USE_AS_STRCHRNUL +- /* Found CHAR or the null byte. */ +- cmp (VEC_SIZE * 3)(%rdi, %rax, CHAR_SIZE), %CHAR_REG +- jne L(zero) +-# endif +- /* NB: Multiply sizeof char type (1 or 4) to get the number of +- bytes. */ +- leaq (VEC_SIZE * 3)(%rdi, %rax, CHAR_SIZE), %rax +- ret + +-# ifndef USE_AS_STRCHRNUL +-L(zero): +- xorl %eax, %eax +- ret +-# endif + +- .p2align 4 ++ .p2align 4,, 10 + L(first_vec_x4): + # ifndef USE_AS_STRCHRNUL + /* Check to see if first match was CHAR (k0) or null (k1). */ +@@ -144,9 +130,18 @@ + leaq (VEC_SIZE * 4)(%rdi, %rax, CHAR_SIZE), %rax + ret + ++# ifndef USE_AS_STRCHRNUL ++L(zero): ++ xorl %eax, %eax ++ ret ++# endif ++ ++ + .p2align 4 + L(first_vec_x1): +- tzcntl %eax, %eax ++ /* Use bsf here to save 1-byte keeping keeping the block in 1x ++ fetch block. eax guranteed non-zero. */ ++ bsfl %eax, %eax + # ifndef USE_AS_STRCHRNUL + /* Found CHAR or the null byte. */ + cmp (VEC_SIZE)(%rdi, %rax, CHAR_SIZE), %CHAR_REG +@@ -158,7 +153,7 @@ + leaq (VEC_SIZE)(%rdi, %rax, CHAR_SIZE), %rax + ret + +- .p2align 4 ++ .p2align 4,, 10 + L(first_vec_x2): + # ifndef USE_AS_STRCHRNUL + /* Check to see if first match was CHAR (k0) or null (k1). */ +@@ -179,6 +174,21 @@ + leaq (VEC_SIZE * 2)(%rdi, %rax, CHAR_SIZE), %rax + ret + ++ .p2align 4,, 10 ++L(first_vec_x3): ++ /* Use bsf here to save 1-byte keeping keeping the block in 1x ++ fetch block. eax guranteed non-zero. */ ++ bsfl %eax, %eax ++# ifndef USE_AS_STRCHRNUL ++ /* Found CHAR or the null byte. */ ++ cmp (VEC_SIZE * 3)(%rdi, %rax, CHAR_SIZE), %CHAR_REG ++ jne L(zero) ++# endif ++ /* NB: Multiply sizeof char type (1 or 4) to get the number of ++ bytes. */ ++ leaq (VEC_SIZE * 3)(%rdi, %rax, CHAR_SIZE), %rax ++ ret ++ + .p2align 4 + L(aligned_more): + /* Align data to VEC_SIZE. */ +@@ -195,7 +205,7 @@ + vpxorq %YMM1, %YMM0, %YMM2 + VPMINU %YMM2, %YMM1, %YMM2 + /* Each bit in K0 represents a CHAR or a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM2, %k0 ++ VPTESTN %YMM2, %YMM2, %k0 + kmovd %k0, %eax + testl %eax, %eax + jnz L(first_vec_x1) +@@ -206,7 +216,7 @@ + /* Each bit in K0 represents a CHAR in YMM1. */ + VPCMP $0, %YMM1, %YMM0, %k0 + /* Each bit in K1 represents a CHAR in YMM1. */ +- VPCMP $0, %YMM1, %YMMZERO, %k1 ++ VPTESTN %YMM1, %YMM1, %k1 + kortestd %k0, %k1 + jnz L(first_vec_x2) + +@@ -215,7 +225,7 @@ + vpxorq %YMM1, %YMM0, %YMM2 + VPMINU %YMM2, %YMM1, %YMM2 + /* Each bit in K0 represents a CHAR or a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM2, %k0 ++ VPTESTN %YMM2, %YMM2, %k0 + kmovd %k0, %eax + testl %eax, %eax + jnz L(first_vec_x3) +@@ -224,7 +234,7 @@ + /* Each bit in K0 represents a CHAR in YMM1. */ + VPCMP $0, %YMM1, %YMM0, %k0 + /* Each bit in K1 represents a CHAR in YMM1. */ +- VPCMP $0, %YMM1, %YMMZERO, %k1 ++ VPTESTN %YMM1, %YMM1, %k1 + kortestd %k0, %k1 + jnz L(first_vec_x4) + +@@ -265,33 +275,33 @@ + VPMINU %YMM3, %YMM4, %YMM4 + VPMINU %YMM2, %YMM4, %YMM4{%k4}{z} + +- VPCMP $0, %YMMZERO, %YMM4, %k1 ++ VPTESTN %YMM4, %YMM4, %k1 + kmovd %k1, %ecx + subq $-(VEC_SIZE * 4), %rdi + testl %ecx, %ecx + jz L(loop_4x_vec) + +- VPCMP $0, %YMMZERO, %YMM1, %k0 ++ VPTESTN %YMM1, %YMM1, %k0 + kmovd %k0, %eax + testl %eax, %eax + jnz L(last_vec_x1) + +- VPCMP $0, %YMMZERO, %YMM2, %k0 ++ VPTESTN %YMM2, %YMM2, %k0 + kmovd %k0, %eax + testl %eax, %eax + jnz L(last_vec_x2) + +- VPCMP $0, %YMMZERO, %YMM3, %k0 ++ VPTESTN %YMM3, %YMM3, %k0 + kmovd %k0, %eax + /* Combine YMM3 matches (eax) with YMM4 matches (ecx). */ + # ifdef USE_AS_WCSCHR + sall $8, %ecx + orl %ecx, %eax +- tzcntl %eax, %eax ++ bsfl %eax, %eax + # else + salq $32, %rcx + orq %rcx, %rax +- tzcntq %rax, %rax ++ bsfq %rax, %rax + # endif + # ifndef USE_AS_STRCHRNUL + /* Check if match was CHAR or null. */ +@@ -303,28 +313,28 @@ + leaq (VEC_SIZE * 2)(%rdi, %rax, CHAR_SIZE), %rax + ret + +-# ifndef USE_AS_STRCHRNUL +-L(zero_end): +- xorl %eax, %eax +- ret ++ .p2align 4,, 8 ++L(last_vec_x1): ++ bsfl %eax, %eax ++# ifdef USE_AS_WCSCHR ++ /* NB: Multiply wchar_t count by 4 to get the number of bytes. ++ */ ++ leaq (%rdi, %rax, CHAR_SIZE), %rax ++# else ++ addq %rdi, %rax + # endif + +- .p2align 4 +-L(last_vec_x1): +- tzcntl %eax, %eax + # ifndef USE_AS_STRCHRNUL + /* Check if match was null. */ +- cmp (%rdi, %rax, CHAR_SIZE), %CHAR_REG ++ cmp (%rax), %CHAR_REG + jne L(zero_end) + # endif +- /* NB: Multiply sizeof char type (1 or 4) to get the number of +- bytes. */ +- leaq (%rdi, %rax, CHAR_SIZE), %rax ++ + ret + +- .p2align 4 ++ .p2align 4,, 8 + L(last_vec_x2): +- tzcntl %eax, %eax ++ bsfl %eax, %eax + # ifndef USE_AS_STRCHRNUL + /* Check if match was null. */ + cmp (VEC_SIZE)(%rdi, %rax, CHAR_SIZE), %CHAR_REG +@@ -336,7 +346,7 @@ + ret + + /* Cold case for crossing page with first load. */ +- .p2align 4 ++ .p2align 4,, 8 + L(cross_page_boundary): + movq %rdi, %rdx + /* Align rdi. */ +@@ -346,9 +356,9 @@ + vpxorq %YMM1, %YMM0, %YMM2 + VPMINU %YMM2, %YMM1, %YMM2 + /* Each bit in K0 represents a CHAR or a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM2, %k0 ++ VPTESTN %YMM2, %YMM2, %k0 + kmovd %k0, %eax +- /* Remove the leading bits. */ ++ /* Remove the leading bits. */ + # ifdef USE_AS_WCSCHR + movl %edx, %SHIFT_REG + /* NB: Divide shift count by 4 since each bit in K1 represent 4 +@@ -360,12 +370,8 @@ + /* If eax is zero continue. */ + testl %eax, %eax + jz L(cross_page_continue) +- tzcntl %eax, %eax +-# ifndef USE_AS_STRCHRNUL +- /* Check to see if match was CHAR or null. */ +- cmp (%rdx, %rax, CHAR_SIZE), %CHAR_REG +- jne L(zero_end) +-# endif ++ bsfl %eax, %eax ++ + # ifdef USE_AS_WCSCHR + /* NB: Multiply wchar_t count by 4 to get the number of + bytes. */ +@@ -373,7 +379,15 @@ + # else + addq %rdx, %rax + # endif ++# ifndef USE_AS_STRCHRNUL ++ /* Check to see if match was CHAR or null. */ ++ cmp (%rax), %CHAR_REG ++ je L(cross_page_ret) ++L(zero_end): ++ xorl %eax, %eax ++L(cross_page_ret): ++# endif + ret + + END (STRCHR) +-# endif ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/strcmp-avx2.S b/sysdeps/x86_64/multiarch/strcmp-avx2.S +--- a/sysdeps/x86_64/multiarch/strcmp-avx2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strcmp-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -20,45 +20,146 @@ + + # include + ++# if defined USE_AS_STRCASECMP_L ++# include "locale-defines.h" ++# endif ++ + # ifndef STRCMP + # define STRCMP __strcmp_avx2 + # endif + + # define PAGE_SIZE 4096 + +-/* VEC_SIZE = Number of bytes in a ymm register */ ++ /* VEC_SIZE = Number of bytes in a ymm register. */ + # define VEC_SIZE 32 + +-/* Shift for dividing by (VEC_SIZE * 4). */ +-# define DIVIDE_BY_VEC_4_SHIFT 7 +-# if (VEC_SIZE * 4) != (1 << DIVIDE_BY_VEC_4_SHIFT) +-# error (VEC_SIZE * 4) != (1 << DIVIDE_BY_VEC_4_SHIFT) +-# endif ++# define VMOVU vmovdqu ++# define VMOVA vmovdqa + + # ifdef USE_AS_WCSCMP +-/* Compare packed dwords. */ ++ /* Compare packed dwords. */ + # define VPCMPEQ vpcmpeqd +-/* Compare packed dwords and store minimum. */ ++ /* Compare packed dwords and store minimum. */ + # define VPMINU vpminud +-/* 1 dword char == 4 bytes. */ ++ /* 1 dword char == 4 bytes. */ + # define SIZE_OF_CHAR 4 + # else +-/* Compare packed bytes. */ ++ /* Compare packed bytes. */ + # define VPCMPEQ vpcmpeqb +-/* Compare packed bytes and store minimum. */ ++ /* Compare packed bytes and store minimum. */ + # define VPMINU vpminub +-/* 1 byte char == 1 byte. */ ++ /* 1 byte char == 1 byte. */ + # define SIZE_OF_CHAR 1 + # endif + ++# ifdef USE_AS_STRNCMP ++# define LOOP_REG r9d ++# define LOOP_REG64 r9 ++ ++# define OFFSET_REG8 r9b ++# define OFFSET_REG r9d ++# define OFFSET_REG64 r9 ++# else ++# define LOOP_REG edx ++# define LOOP_REG64 rdx ++ ++# define OFFSET_REG8 dl ++# define OFFSET_REG edx ++# define OFFSET_REG64 rdx ++# endif ++ + # ifndef VZEROUPPER + # define VZEROUPPER vzeroupper + # endif + ++# if defined USE_AS_STRNCMP ++# define VEC_OFFSET 0 ++# else ++# define VEC_OFFSET (-VEC_SIZE) ++# endif ++ ++# ifdef USE_AS_STRCASECMP_L ++# define BYTE_LOOP_REG OFFSET_REG ++# else ++# define BYTE_LOOP_REG ecx ++# endif ++ ++# ifdef USE_AS_STRCASECMP_L ++# ifdef USE_AS_STRNCMP ++# define STRCASECMP __strncasecmp_avx2 ++# define LOCALE_REG rcx ++# define LOCALE_REG_LP RCX_LP ++# define STRCASECMP_NONASCII __strncasecmp_l_nonascii ++# else ++# define STRCASECMP __strcasecmp_avx2 ++# define LOCALE_REG rdx ++# define LOCALE_REG_LP RDX_LP ++# define STRCASECMP_NONASCII __strcasecmp_l_nonascii ++# endif ++# endif ++ ++# define xmmZERO xmm15 ++# define ymmZERO ymm15 ++ ++# define LCASE_MIN_ymm %ymm10 ++# define LCASE_MAX_ymm %ymm11 ++# define CASE_ADD_ymm %ymm12 ++ ++# define LCASE_MIN_xmm %xmm10 ++# define LCASE_MAX_xmm %xmm11 ++# define CASE_ADD_xmm %xmm12 ++ ++ /* r11 is never use elsewhere so this is safe to maintain. */ ++# define TOLOWER_BASE %r11 ++ + # ifndef SECTION + # define SECTION(p) p##.avx + # endif + ++# ifdef USE_AS_STRCASECMP_L ++# define REG(x, y) x ## y ++# define TOLOWER(reg1_in, reg1_out, reg2_in, reg2_out, ext) \ ++ vpaddb REG(LCASE_MIN_, ext), reg1_in, REG(%ext, 8); \ ++ vpaddb REG(LCASE_MIN_, ext), reg2_in, REG(%ext, 9); \ ++ vpcmpgtb REG(LCASE_MAX_, ext), REG(%ext, 8), REG(%ext, 8); \ ++ vpcmpgtb REG(LCASE_MAX_, ext), REG(%ext, 9), REG(%ext, 9); \ ++ vpandn REG(CASE_ADD_, ext), REG(%ext, 8), REG(%ext, 8); \ ++ vpandn REG(CASE_ADD_, ext), REG(%ext, 9), REG(%ext, 9); \ ++ vpaddb REG(%ext, 8), reg1_in, reg1_out; \ ++ vpaddb REG(%ext, 9), reg2_in, reg2_out ++ ++# define TOLOWER_gpr(src, dst) movl (TOLOWER_BASE, src, 4), dst ++# define TOLOWER_ymm(...) TOLOWER(__VA_ARGS__, ymm) ++# define TOLOWER_xmm(...) TOLOWER(__VA_ARGS__, xmm) ++ ++# define CMP_R1_R2(s1_reg, s2_reg, scratch_reg, reg_out, ext) \ ++ TOLOWER (s1_reg, scratch_reg, s2_reg, s2_reg, ext); \ ++ VPCMPEQ scratch_reg, s2_reg, reg_out ++ ++# define CMP_R1_S2(s1_reg, s2_mem, scratch_reg, reg_out, ext) \ ++ VMOVU s2_mem, reg_out; \ ++ CMP_R1_R2(s1_reg, reg_out, scratch_reg, reg_out, ext) ++ ++# define CMP_R1_R2_ymm(...) CMP_R1_R2(__VA_ARGS__, ymm) ++# define CMP_R1_R2_xmm(...) CMP_R1_R2(__VA_ARGS__, xmm) ++ ++# define CMP_R1_S2_ymm(...) CMP_R1_S2(__VA_ARGS__, ymm) ++# define CMP_R1_S2_xmm(...) CMP_R1_S2(__VA_ARGS__, xmm) ++ ++# else ++# define TOLOWER_gpr(...) ++# define TOLOWER_ymm(...) ++# define TOLOWER_xmm(...) ++ ++# define CMP_R1_R2_ymm(s1_reg, s2_reg, scratch_reg, reg_out) \ ++ VPCMPEQ s2_reg, s1_reg, reg_out ++ ++# define CMP_R1_R2_xmm(...) CMP_R1_R2_ymm(__VA_ARGS__) ++ ++# define CMP_R1_S2_ymm(...) CMP_R1_R2_ymm(__VA_ARGS__) ++# define CMP_R1_S2_xmm(...) CMP_R1_R2_xmm(__VA_ARGS__) ++# endif ++ + /* Warning! + wcscmp/wcsncmp have to use SIGNED comparison for elements. + strcmp/strncmp have to use UNSIGNED comparison for elements. +@@ -79,783 +180,1142 @@ + the maximum offset is reached before a difference is found, zero is + returned. */ + +- .section SECTION(.text),"ax",@progbits +-ENTRY (STRCMP) ++ .section SECTION(.text), "ax", @progbits ++ .align 16 ++ .type STRCMP, @function ++ .globl STRCMP ++ .hidden STRCMP ++ ++# ifndef GLABEL ++# define GLABEL(...) __VA_ARGS__ ++# endif ++ ++# ifdef USE_AS_STRCASECMP_L ++ENTRY (GLABEL(STRCASECMP)) ++ movq __libc_tsd_LOCALE@gottpoff(%rip), %rax ++ mov %fs:(%rax), %LOCALE_REG_LP ++ ++ /* Either 1 or 5 bytes (dependeing if CET is enabled). */ ++ .p2align 4 ++END (GLABEL(STRCASECMP)) ++ /* FALLTHROUGH to strcasecmp/strncasecmp_l. */ ++# endif ++ ++ .p2align 4 ++STRCMP: ++ cfi_startproc ++ _CET_ENDBR ++ CALL_MCOUNT ++ ++# if defined USE_AS_STRCASECMP_L ++ /* We have to fall back on the C implementation for locales with ++ encodings not matching ASCII for single bytes. */ ++# if LOCALE_T___LOCALES != 0 || LC_CTYPE != 0 ++ mov LOCALE_T___LOCALES + LC_CTYPE * LP_SIZE(%LOCALE_REG), %RAX_LP ++# else ++ mov (%LOCALE_REG), %RAX_LP ++# endif ++ testl $1, LOCALE_DATA_VALUES + _NL_CTYPE_NONASCII_CASE * SIZEOF_VALUES(%rax) ++ jne STRCASECMP_NONASCII ++ leaq _nl_C_LC_CTYPE_tolower + 128 * 4(%rip), TOLOWER_BASE ++# endif ++ + # ifdef USE_AS_STRNCMP +- /* Check for simple cases (0 or 1) in offset. */ ++ /* Don't overwrite LOCALE_REG (rcx) until we have pass ++ L(one_or_less). Otherwise we might use the wrong locale in ++ the OVERFLOW_STRCMP (strcasecmp_l). */ ++# ifdef __ILP32__ ++ /* Clear the upper 32 bits. */ ++ movl %edx, %edx ++# endif + cmp $1, %RDX_LP +- je L(char0) +- jb L(zero) ++ /* Signed comparison intentional. We use this branch to also ++ test cases where length >= 2^63. These very large sizes can be ++ handled with strcmp as there is no way for that length to ++ actually bound the buffer. */ ++ jle L(one_or_less) + # ifdef USE_AS_WCSCMP +-# ifndef __ILP32__ + movq %rdx, %rcx +- /* Check if length could overflow when multiplied by +- sizeof(wchar_t). Checking top 8 bits will cover all potential +- overflow cases as well as redirect cases where its impossible to +- length to bound a valid memory region. In these cases just use +- 'wcscmp'. */ ++ ++ /* Multiplying length by sizeof(wchar_t) can result in overflow. ++ Check if that is possible. All cases where overflow are possible ++ are cases where length is large enough that it can never be a ++ bound on valid memory so just use wcscmp. */ + shrq $56, %rcx +- jnz __wcscmp_avx2 +-# endif +- /* Convert units: from wide to byte char. */ +- shl $2, %RDX_LP ++ jnz OVERFLOW_STRCMP ++ ++ leaq (, %rdx, 4), %rdx + # endif +- /* Register %r11 tracks the maximum offset. */ +- mov %RDX_LP, %R11_LP ++# endif ++ vpxor %xmmZERO, %xmmZERO, %xmmZERO ++# if defined USE_AS_STRCASECMP_L ++ .section .rodata.cst32, "aM", @progbits, 32 ++ .align 32 ++L(lcase_min): ++ .quad 0x3f3f3f3f3f3f3f3f ++ .quad 0x3f3f3f3f3f3f3f3f ++ .quad 0x3f3f3f3f3f3f3f3f ++ .quad 0x3f3f3f3f3f3f3f3f ++L(lcase_max): ++ .quad 0x9999999999999999 ++ .quad 0x9999999999999999 ++ .quad 0x9999999999999999 ++ .quad 0x9999999999999999 ++L(case_add): ++ .quad 0x2020202020202020 ++ .quad 0x2020202020202020 ++ .quad 0x2020202020202020 ++ .quad 0x2020202020202020 ++ .previous ++ ++ vmovdqa L(lcase_min)(%rip), LCASE_MIN_ymm ++ vmovdqa L(lcase_max)(%rip), LCASE_MAX_ymm ++ vmovdqa L(case_add)(%rip), CASE_ADD_ymm + # endif + movl %edi, %eax +- xorl %edx, %edx +- /* Make %xmm7 (%ymm7) all zeros in this function. */ +- vpxor %xmm7, %xmm7, %xmm7 + orl %esi, %eax +- andl $(PAGE_SIZE - 1), %eax +- cmpl $(PAGE_SIZE - (VEC_SIZE * 4)), %eax +- jg L(cross_page) +- /* Start comparing 4 vectors. */ +- vmovdqu (%rdi), %ymm1 +- VPCMPEQ (%rsi), %ymm1, %ymm0 +- VPMINU %ymm1, %ymm0, %ymm0 +- VPCMPEQ %ymm7, %ymm0, %ymm0 +- vpmovmskb %ymm0, %ecx +- testl %ecx, %ecx +- je L(next_3_vectors) +- tzcntl %ecx, %edx ++ sall $20, %eax ++ /* Check if s1 or s2 may cross a page in next 4x VEC loads. */ ++ cmpl $((PAGE_SIZE -(VEC_SIZE * 4)) << 20), %eax ++ ja L(page_cross) ++ ++L(no_page_cross): ++ /* Safe to compare 4x vectors. */ ++ VMOVU (%rdi), %ymm0 ++ /* 1s where s1 and s2 equal. Just VPCMPEQ if its not strcasecmp. ++ Otherwise converts ymm0 and load from rsi to lower. ymm2 is ++ scratch and ymm1 is the return. */ ++ CMP_R1_S2_ymm (%ymm0, (%rsi), %ymm2, %ymm1) ++ /* 1s at null CHAR. */ ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ /* 1s where s1 and s2 equal AND not null CHAR. */ ++ vpandn %ymm1, %ymm2, %ymm1 ++ ++ /* All 1s -> keep going, any 0s -> return. */ ++ vpmovmskb %ymm1, %ecx + # ifdef USE_AS_STRNCMP +- /* Return 0 if the mismatched index (%rdx) is after the maximum +- offset (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) ++ cmpq $VEC_SIZE, %rdx ++ jbe L(vec_0_test_len) + # endif ++ ++ /* All 1s represents all equals. incl will overflow to zero in ++ all equals case. Otherwise 1s will carry until position of first ++ mismatch. */ ++ incl %ecx ++ jz L(more_3x_vec) ++ ++ .p2align 4,, 4 ++L(return_vec_0): ++ tzcntl %ecx, %ecx + # ifdef USE_AS_WCSCMP ++ movl (%rdi, %rcx), %edx + xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- je L(return) +-L(wcscmp_return): ++ cmpl (%rsi, %rcx), %edx ++ je L(ret0) + setl %al + negl %eax + orl $1, %eax +-L(return): + # else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax ++ movzbl (%rdi, %rcx), %eax ++ movzbl (%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax + # endif ++L(ret0): + L(return_vzeroupper): + ZERO_UPPER_VEC_REGISTERS_RETURN + +- .p2align 4 +-L(return_vec_size): +- tzcntl %ecx, %edx + # ifdef USE_AS_STRNCMP +- /* Return 0 if the mismatched index (%rdx + VEC_SIZE) is after +- the maximum offset (%r11). */ +- addq $VEC_SIZE, %rdx +- cmpq %r11, %rdx +- jae L(zero) +-# ifdef USE_AS_WCSCMP ++ .p2align 4,, 8 ++L(vec_0_test_len): ++ notl %ecx ++ bzhil %edx, %ecx, %eax ++ jnz L(return_vec_0) ++ /* Align if will cross fetch block. */ ++ .p2align 4,, 2 ++L(ret_zero): + xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax +-# endif +-# else ++ VZEROUPPER_RETURN ++ ++ .p2align 4,, 5 ++L(one_or_less): ++# ifdef USE_AS_STRCASECMP_L ++ /* Set locale argument for strcasecmp. */ ++ movq %LOCALE_REG, %rdx ++# endif ++ jb L(ret_zero) ++ /* 'nbe' covers the case where length is negative (large ++ unsigned). */ ++ jnbe OVERFLOW_STRCMP + # ifdef USE_AS_WCSCMP ++ movl (%rdi), %edx + xorl %eax, %eax +- movl VEC_SIZE(%rdi, %rdx), %ecx +- cmpl VEC_SIZE(%rsi, %rdx), %ecx +- jne L(wcscmp_return) ++ cmpl (%rsi), %edx ++ je L(ret1) ++ setl %al ++ negl %eax ++ orl $1, %eax + # else +- movzbl VEC_SIZE(%rdi, %rdx), %eax +- movzbl VEC_SIZE(%rsi, %rdx), %edx +- subl %edx, %eax ++ movzbl (%rdi), %eax ++ movzbl (%rsi), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax + # endif ++L(ret1): ++ ret + # endif +- VZEROUPPER_RETURN + +- .p2align 4 +-L(return_2_vec_size): +- tzcntl %ecx, %edx +-# ifdef USE_AS_STRNCMP +- /* Return 0 if the mismatched index (%rdx + 2 * VEC_SIZE) is +- after the maximum offset (%r11). */ +- addq $(VEC_SIZE * 2), %rdx +- cmpq %r11, %rdx +- jae L(zero) +-# ifdef USE_AS_WCSCMP ++ .p2align 4,, 10 ++L(return_vec_1): ++ tzcntl %ecx, %ecx ++# ifdef USE_AS_STRNCMP ++ /* rdx must be > CHAR_PER_VEC so save to subtract w.o fear of ++ overflow. */ ++ addq $-VEC_SIZE, %rdx ++ cmpq %rcx, %rdx ++ jbe L(ret_zero) ++# endif ++# ifdef USE_AS_WCSCMP ++ movl VEC_SIZE(%rdi, %rcx), %edx + xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax +-# endif ++ cmpl VEC_SIZE(%rsi, %rcx), %edx ++ je L(ret2) ++ setl %al ++ negl %eax ++ orl $1, %eax + # else +-# ifdef USE_AS_WCSCMP +- xorl %eax, %eax +- movl (VEC_SIZE * 2)(%rdi, %rdx), %ecx +- cmpl (VEC_SIZE * 2)(%rsi, %rdx), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (VEC_SIZE * 2)(%rdi, %rdx), %eax +- movzbl (VEC_SIZE * 2)(%rsi, %rdx), %edx +- subl %edx, %eax +-# endif ++ movzbl VEC_SIZE(%rdi, %rcx), %eax ++ movzbl VEC_SIZE(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax + # endif ++L(ret2): + VZEROUPPER_RETURN + +- .p2align 4 +-L(return_3_vec_size): +- tzcntl %ecx, %edx ++ .p2align 4,, 10 + # ifdef USE_AS_STRNCMP +- /* Return 0 if the mismatched index (%rdx + 3 * VEC_SIZE) is +- after the maximum offset (%r11). */ +- addq $(VEC_SIZE * 3), %rdx +- cmpq %r11, %rdx +- jae L(zero) +-# ifdef USE_AS_WCSCMP ++L(return_vec_3): ++ salq $32, %rcx ++# endif ++ ++L(return_vec_2): ++# ifndef USE_AS_STRNCMP ++ tzcntl %ecx, %ecx ++# else ++ tzcntq %rcx, %rcx ++ cmpq %rcx, %rdx ++ jbe L(ret_zero) ++# endif ++ ++# ifdef USE_AS_WCSCMP ++ movl (VEC_SIZE * 2)(%rdi, %rcx), %edx + xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax +-# endif ++ cmpl (VEC_SIZE * 2)(%rsi, %rcx), %edx ++ je L(ret3) ++ setl %al ++ negl %eax ++ orl $1, %eax + # else ++ movzbl (VEC_SIZE * 2)(%rdi, %rcx), %eax ++ movzbl (VEC_SIZE * 2)(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++# endif ++L(ret3): ++ VZEROUPPER_RETURN ++ ++# ifndef USE_AS_STRNCMP ++ .p2align 4,, 10 ++L(return_vec_3): ++ tzcntl %ecx, %ecx + # ifdef USE_AS_WCSCMP ++ movl (VEC_SIZE * 3)(%rdi, %rcx), %edx + xorl %eax, %eax +- movl (VEC_SIZE * 3)(%rdi, %rdx), %ecx +- cmpl (VEC_SIZE * 3)(%rsi, %rdx), %ecx +- jne L(wcscmp_return) ++ cmpl (VEC_SIZE * 3)(%rsi, %rcx), %edx ++ je L(ret4) ++ setl %al ++ negl %eax ++ orl $1, %eax + # else +- movzbl (VEC_SIZE * 3)(%rdi, %rdx), %eax +- movzbl (VEC_SIZE * 3)(%rsi, %rdx), %edx +- subl %edx, %eax ++ movzbl (VEC_SIZE * 3)(%rdi, %rcx), %eax ++ movzbl (VEC_SIZE * 3)(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax + # endif +-# endif ++L(ret4): + VZEROUPPER_RETURN ++# endif ++ ++ .p2align 4,, 10 ++L(more_3x_vec): ++ /* Safe to compare 4x vectors. */ ++ VMOVU VEC_SIZE(%rdi), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, VEC_SIZE(%rsi), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ incl %ecx ++ jnz L(return_vec_1) + +- .p2align 4 +-L(next_3_vectors): +- vmovdqu VEC_SIZE(%rdi), %ymm6 +- VPCMPEQ VEC_SIZE(%rsi), %ymm6, %ymm3 +- VPMINU %ymm6, %ymm3, %ymm3 +- VPCMPEQ %ymm7, %ymm3, %ymm3 +- vpmovmskb %ymm3, %ecx +- testl %ecx, %ecx +- jne L(return_vec_size) +- vmovdqu (VEC_SIZE * 2)(%rdi), %ymm5 +- vmovdqu (VEC_SIZE * 3)(%rdi), %ymm4 +- vmovdqu (VEC_SIZE * 3)(%rsi), %ymm0 +- VPCMPEQ (VEC_SIZE * 2)(%rsi), %ymm5, %ymm2 +- VPMINU %ymm5, %ymm2, %ymm2 +- VPCMPEQ %ymm4, %ymm0, %ymm0 +- VPCMPEQ %ymm7, %ymm2, %ymm2 +- vpmovmskb %ymm2, %ecx +- testl %ecx, %ecx +- jne L(return_2_vec_size) +- VPMINU %ymm4, %ymm0, %ymm0 +- VPCMPEQ %ymm7, %ymm0, %ymm0 +- vpmovmskb %ymm0, %ecx +- testl %ecx, %ecx +- jne L(return_3_vec_size) +-L(main_loop_header): +- leaq (VEC_SIZE * 4)(%rdi), %rdx +- movl $PAGE_SIZE, %ecx +- /* Align load via RAX. */ +- andq $-(VEC_SIZE * 4), %rdx +- subq %rdi, %rdx +- leaq (%rdi, %rdx), %rax + # ifdef USE_AS_STRNCMP +- /* Starting from this point, the maximum offset, or simply the +- 'offset', DECREASES by the same amount when base pointers are +- moved forward. Return 0 when: +- 1) On match: offset <= the matched vector index. +- 2) On mistmach, offset is before the mistmatched index. ++ subq $(VEC_SIZE * 2), %rdx ++ jbe L(ret_zero) ++# endif ++ ++ VMOVU (VEC_SIZE * 2)(%rdi), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, (VEC_SIZE * 2)(%rsi), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ incl %ecx ++ jnz L(return_vec_2) ++ ++ VMOVU (VEC_SIZE * 3)(%rdi), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, (VEC_SIZE * 3)(%rsi), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ incl %ecx ++ jnz L(return_vec_3) ++ ++# ifdef USE_AS_STRNCMP ++ cmpq $(VEC_SIZE * 2), %rdx ++ jbe L(ret_zero) ++# endif ++ ++# ifdef USE_AS_WCSCMP ++ /* any non-zero positive value that doesn't inference with 0x1. + */ +- subq %rdx, %r11 +- jbe L(zero) ++ movl $2, %r8d ++ ++# else ++ xorl %r8d, %r8d + # endif +- addq %rsi, %rdx +- movq %rdx, %rsi +- andl $(PAGE_SIZE - 1), %esi +- /* Number of bytes before page crossing. */ +- subq %rsi, %rcx +- /* Number of VEC_SIZE * 4 blocks before page crossing. */ +- shrq $DIVIDE_BY_VEC_4_SHIFT, %rcx +- /* ESI: Number of VEC_SIZE * 4 blocks before page crossing. */ +- movl %ecx, %esi +- jmp L(loop_start) + +- .p2align 4 +-L(loop): ++ /* The prepare labels are various entry points from the page ++ cross logic. */ ++L(prepare_loop): ++ + # ifdef USE_AS_STRNCMP +- /* Base pointers are moved forward by 4 * VEC_SIZE. Decrease +- the maximum offset (%r11) by the same amount. */ +- subq $(VEC_SIZE * 4), %r11 +- jbe L(zero) +-# endif +- addq $(VEC_SIZE * 4), %rax +- addq $(VEC_SIZE * 4), %rdx +-L(loop_start): +- testl %esi, %esi +- leal -1(%esi), %esi +- je L(loop_cross_page) +-L(back_to_loop): +- /* Main loop, comparing 4 vectors are a time. */ +- vmovdqa (%rax), %ymm0 +- vmovdqa VEC_SIZE(%rax), %ymm3 +- VPCMPEQ (%rdx), %ymm0, %ymm4 +- VPCMPEQ VEC_SIZE(%rdx), %ymm3, %ymm1 +- VPMINU %ymm0, %ymm4, %ymm4 +- VPMINU %ymm3, %ymm1, %ymm1 +- vmovdqa (VEC_SIZE * 2)(%rax), %ymm2 +- VPMINU %ymm1, %ymm4, %ymm0 +- vmovdqa (VEC_SIZE * 3)(%rax), %ymm3 +- VPCMPEQ (VEC_SIZE * 2)(%rdx), %ymm2, %ymm5 +- VPCMPEQ (VEC_SIZE * 3)(%rdx), %ymm3, %ymm6 +- VPMINU %ymm2, %ymm5, %ymm5 +- VPMINU %ymm3, %ymm6, %ymm6 +- VPMINU %ymm5, %ymm0, %ymm0 +- VPMINU %ymm6, %ymm0, %ymm0 +- VPCMPEQ %ymm7, %ymm0, %ymm0 +- +- /* Test each mask (32 bits) individually because for VEC_SIZE +- == 32 is not possible to OR the four masks and keep all bits +- in a 64-bit integer register, differing from SSE2 strcmp +- where ORing is possible. */ +- vpmovmskb %ymm0, %ecx +- testl %ecx, %ecx +- je L(loop) +- VPCMPEQ %ymm7, %ymm4, %ymm0 +- vpmovmskb %ymm0, %edi +- testl %edi, %edi +- je L(test_vec) +- tzcntl %edi, %ecx ++ /* Store N + (VEC_SIZE * 4) and place check at the begining of ++ the loop. */ ++ leaq (VEC_SIZE * 2)(%rdi, %rdx), %rdx ++# endif ++L(prepare_loop_no_len): ++ ++ /* Align s1 and adjust s2 accordingly. */ ++ subq %rdi, %rsi ++ andq $-(VEC_SIZE * 4), %rdi ++ addq %rdi, %rsi ++ + # ifdef USE_AS_STRNCMP +- cmpq %rcx, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax +-# endif +-# else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax +-# endif ++ subq %rdi, %rdx + # endif +- VZEROUPPER_RETURN + ++L(prepare_loop_aligned): ++ /* eax stores distance from rsi to next page cross. These cases ++ need to be handled specially as the 4x loop could potentially ++ read memory past the length of s1 or s2 and across a page ++ boundary. */ ++ movl $-(VEC_SIZE * 4), %eax ++ subl %esi, %eax ++ andl $(PAGE_SIZE - 1), %eax ++ ++ /* Loop 4x comparisons at a time. */ + .p2align 4 +-L(test_vec): ++L(loop): ++ ++ /* End condition for strncmp. */ + # ifdef USE_AS_STRNCMP +- /* The first vector matched. Return 0 if the maximum offset +- (%r11) <= VEC_SIZE. */ +- cmpq $VEC_SIZE, %r11 +- jbe L(zero) ++ subq $(VEC_SIZE * 4), %rdx ++ jbe L(ret_zero) + # endif +- VPCMPEQ %ymm7, %ymm1, %ymm1 ++ ++ subq $-(VEC_SIZE * 4), %rdi ++ subq $-(VEC_SIZE * 4), %rsi ++ ++ /* Check if rsi loads will cross a page boundary. */ ++ addl $-(VEC_SIZE * 4), %eax ++ jnb L(page_cross_during_loop) ++ ++ /* Loop entry after handling page cross during loop. */ ++L(loop_skip_page_cross_check): ++ VMOVA (VEC_SIZE * 0)(%rdi), %ymm0 ++ VMOVA (VEC_SIZE * 1)(%rdi), %ymm2 ++ VMOVA (VEC_SIZE * 2)(%rdi), %ymm4 ++ VMOVA (VEC_SIZE * 3)(%rdi), %ymm6 ++ ++ /* ymm1 all 1s where s1 and s2 equal. All 0s otherwise. */ ++ CMP_R1_S2_ymm (%ymm0, (VEC_SIZE * 0)(%rsi), %ymm3, %ymm1) ++ CMP_R1_S2_ymm (%ymm2, (VEC_SIZE * 1)(%rsi), %ymm5, %ymm3) ++ CMP_R1_S2_ymm (%ymm4, (VEC_SIZE * 2)(%rsi), %ymm7, %ymm5) ++ CMP_R1_S2_ymm (%ymm6, (VEC_SIZE * 3)(%rsi), %ymm13, %ymm7) ++ ++ /* If any mismatches or null CHAR then 0 CHAR, otherwise non- ++ zero. */ ++ vpand %ymm0, %ymm1, %ymm1 ++ ++ ++ vpand %ymm2, %ymm3, %ymm3 ++ vpand %ymm4, %ymm5, %ymm5 ++ vpand %ymm6, %ymm7, %ymm7 ++ ++ VPMINU %ymm1, %ymm3, %ymm3 ++ VPMINU %ymm5, %ymm7, %ymm7 ++ ++ /* Reduce all 0 CHARs for the 4x VEC into ymm7. */ ++ VPMINU %ymm3, %ymm7, %ymm7 ++ ++ /* If any 0 CHAR then done. */ ++ VPCMPEQ %ymm7, %ymmZERO, %ymm7 ++ vpmovmskb %ymm7, %LOOP_REG ++ testl %LOOP_REG, %LOOP_REG ++ jz L(loop) ++ ++ /* Find which VEC has the mismatch of end of string. */ ++ VPCMPEQ %ymm1, %ymmZERO, %ymm1 + vpmovmskb %ymm1, %ecx + testl %ecx, %ecx +- je L(test_2_vec) +- tzcntl %ecx, %edi +-# ifdef USE_AS_STRNCMP +- addq $VEC_SIZE, %rdi +- cmpq %rdi, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (%rsi, %rdi), %ecx +- cmpl (%rdx, %rdi), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rdi), %eax +- movzbl (%rdx, %rdi), %edx +- subl %edx, %eax +-# endif +-# else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl VEC_SIZE(%rsi, %rdi), %ecx +- cmpl VEC_SIZE(%rdx, %rdi), %ecx +- jne L(wcscmp_return) +-# else +- movzbl VEC_SIZE(%rax, %rdi), %eax +- movzbl VEC_SIZE(%rdx, %rdi), %edx +- subl %edx, %eax +-# endif +-# endif +- VZEROUPPER_RETURN ++ jnz L(return_vec_0_end) + +- .p2align 4 +-L(test_2_vec): ++ ++ VPCMPEQ %ymm3, %ymmZERO, %ymm3 ++ vpmovmskb %ymm3, %ecx ++ testl %ecx, %ecx ++ jnz L(return_vec_1_end) ++ ++L(return_vec_2_3_end): + # ifdef USE_AS_STRNCMP +- /* The first 2 vectors matched. Return 0 if the maximum offset +- (%r11) <= 2 * VEC_SIZE. */ +- cmpq $(VEC_SIZE * 2), %r11 +- jbe L(zero) ++ subq $(VEC_SIZE * 2), %rdx ++ jbe L(ret_zero_end) + # endif +- VPCMPEQ %ymm7, %ymm5, %ymm5 ++ ++ VPCMPEQ %ymm5, %ymmZERO, %ymm5 + vpmovmskb %ymm5, %ecx + testl %ecx, %ecx +- je L(test_3_vec) +- tzcntl %ecx, %edi ++ jnz L(return_vec_2_end) ++ ++ /* LOOP_REG contains matches for null/mismatch from the loop. If ++ VEC 0,1,and 2 all have no null and no mismatches then mismatch ++ must entirely be from VEC 3 which is fully represented by ++ LOOP_REG. */ ++ tzcntl %LOOP_REG, %LOOP_REG ++ + # ifdef USE_AS_STRNCMP +- addq $(VEC_SIZE * 2), %rdi +- cmpq %rdi, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ subl $-(VEC_SIZE), %LOOP_REG ++ cmpq %LOOP_REG64, %rdx ++ jbe L(ret_zero_end) ++# endif ++ ++# ifdef USE_AS_WCSCMP ++ movl (VEC_SIZE * 2 - VEC_OFFSET)(%rdi, %LOOP_REG64), %ecx + xorl %eax, %eax +- movl (%rsi, %rdi), %ecx +- cmpl (%rdx, %rdi), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rdi), %eax +- movzbl (%rdx, %rdi), %edx +- subl %edx, %eax +-# endif ++ cmpl (VEC_SIZE * 2 - VEC_OFFSET)(%rsi, %LOOP_REG64), %ecx ++ je L(ret5) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax + # else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (VEC_SIZE * 2)(%rsi, %rdi), %ecx +- cmpl (VEC_SIZE * 2)(%rdx, %rdi), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (VEC_SIZE * 2)(%rax, %rdi), %eax +- movzbl (VEC_SIZE * 2)(%rdx, %rdi), %edx +- subl %edx, %eax +-# endif ++ movzbl (VEC_SIZE * 2 - VEC_OFFSET)(%rdi, %LOOP_REG64), %eax ++ movzbl (VEC_SIZE * 2 - VEC_OFFSET)(%rsi, %LOOP_REG64), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif ++L(ret5): + VZEROUPPER_RETURN + +- .p2align 4 +-L(test_3_vec): + # ifdef USE_AS_STRNCMP +- /* The first 3 vectors matched. Return 0 if the maximum offset +- (%r11) <= 3 * VEC_SIZE. */ +- cmpq $(VEC_SIZE * 3), %r11 +- jbe L(zero) +-# endif +- VPCMPEQ %ymm7, %ymm6, %ymm6 +- vpmovmskb %ymm6, %esi +- tzcntl %esi, %ecx +-# ifdef USE_AS_STRNCMP +- addq $(VEC_SIZE * 3), %rcx +- cmpq %rcx, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ .p2align 4,, 2 ++L(ret_zero_end): + xorl %eax, %eax +- movl (%rsi, %rcx), %esi +- cmpl (%rdx, %rcx), %esi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax +-# endif ++ VZEROUPPER_RETURN ++# endif ++ ++ ++ /* The L(return_vec_N_end) differ from L(return_vec_N) in that ++ they use the value of `r8` to negate the return value. This is ++ because the page cross logic can swap `rdi` and `rsi`. */ ++ .p2align 4,, 10 ++# ifdef USE_AS_STRNCMP ++L(return_vec_1_end): ++ salq $32, %rcx ++# endif ++L(return_vec_0_end): ++# ifndef USE_AS_STRNCMP ++ tzcntl %ecx, %ecx + # else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ tzcntq %rcx, %rcx ++ cmpq %rcx, %rdx ++ jbe L(ret_zero_end) ++# endif ++ ++# ifdef USE_AS_WCSCMP ++ movl (%rdi, %rcx), %edx + xorl %eax, %eax +- movl (VEC_SIZE * 3)(%rsi, %rcx), %esi +- cmpl (VEC_SIZE * 3)(%rdx, %rcx), %esi +- jne L(wcscmp_return) +-# else +- movzbl (VEC_SIZE * 3)(%rax, %rcx), %eax +- movzbl (VEC_SIZE * 3)(%rdx, %rcx), %edx +- subl %edx, %eax +-# endif ++ cmpl (%rsi, %rcx), %edx ++ je L(ret6) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax ++# else ++ movzbl (%rdi, %rcx), %eax ++ movzbl (%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif ++L(ret6): + VZEROUPPER_RETURN + +- .p2align 4 +-L(loop_cross_page): +- xorl %r10d, %r10d +- movq %rdx, %rcx +- /* Align load via RDX. We load the extra ECX bytes which should +- be ignored. */ +- andl $((VEC_SIZE * 4) - 1), %ecx +- /* R10 is -RCX. */ +- subq %rcx, %r10 +- +- /* This works only if VEC_SIZE * 2 == 64. */ +-# if (VEC_SIZE * 2) != 64 +-# error (VEC_SIZE * 2) != 64 +-# endif +- +- /* Check if the first VEC_SIZE * 2 bytes should be ignored. */ +- cmpl $(VEC_SIZE * 2), %ecx +- jge L(loop_cross_page_2_vec) +- +- vmovdqu (%rax, %r10), %ymm2 +- vmovdqu VEC_SIZE(%rax, %r10), %ymm3 +- VPCMPEQ (%rdx, %r10), %ymm2, %ymm0 +- VPCMPEQ VEC_SIZE(%rdx, %r10), %ymm3, %ymm1 +- VPMINU %ymm2, %ymm0, %ymm0 +- VPMINU %ymm3, %ymm1, %ymm1 +- VPCMPEQ %ymm7, %ymm0, %ymm0 +- VPCMPEQ %ymm7, %ymm1, %ymm1 +- +- vpmovmskb %ymm0, %edi +- vpmovmskb %ymm1, %esi +- +- salq $32, %rsi +- xorq %rsi, %rdi +- +- /* Since ECX < VEC_SIZE * 2, simply skip the first ECX bytes. */ +- shrq %cl, %rdi +- +- testq %rdi, %rdi +- je L(loop_cross_page_2_vec) +- tzcntq %rdi, %rcx +-# ifdef USE_AS_STRNCMP +- cmpq %rcx, %r11 +- jbe L(zero) ++# ifndef USE_AS_STRNCMP ++ .p2align 4,, 10 ++L(return_vec_1_end): ++ tzcntl %ecx, %ecx + # ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ movl VEC_SIZE(%rdi, %rcx), %edx + xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) ++ cmpl VEC_SIZE(%rsi, %rcx), %edx ++ je L(ret7) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax + # else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax ++ movzbl VEC_SIZE(%rdi, %rcx), %eax ++ movzbl VEC_SIZE(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif +-# else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++L(ret7): ++ VZEROUPPER_RETURN ++# endif ++ ++ .p2align 4,, 10 ++L(return_vec_2_end): ++ tzcntl %ecx, %ecx ++# ifdef USE_AS_STRNCMP ++ cmpq %rcx, %rdx ++ jbe L(ret_zero_page_cross) ++# endif ++# ifdef USE_AS_WCSCMP ++ movl (VEC_SIZE * 2)(%rdi, %rcx), %edx + xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax +-# endif ++ cmpl (VEC_SIZE * 2)(%rsi, %rcx), %edx ++ je L(ret11) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax ++# else ++ movzbl (VEC_SIZE * 2)(%rdi, %rcx), %eax ++ movzbl (VEC_SIZE * 2)(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif ++L(ret11): + VZEROUPPER_RETURN + +- .p2align 4 +-L(loop_cross_page_2_vec): +- /* The first VEC_SIZE * 2 bytes match or are ignored. */ +- vmovdqu (VEC_SIZE * 2)(%rax, %r10), %ymm2 +- vmovdqu (VEC_SIZE * 3)(%rax, %r10), %ymm3 +- VPCMPEQ (VEC_SIZE * 2)(%rdx, %r10), %ymm2, %ymm5 +- VPMINU %ymm2, %ymm5, %ymm5 +- VPCMPEQ (VEC_SIZE * 3)(%rdx, %r10), %ymm3, %ymm6 +- VPCMPEQ %ymm7, %ymm5, %ymm5 +- VPMINU %ymm3, %ymm6, %ymm6 +- VPCMPEQ %ymm7, %ymm6, %ymm6 + +- vpmovmskb %ymm5, %edi +- vpmovmskb %ymm6, %esi ++ /* Page cross in rsi in next 4x VEC. */ + +- salq $32, %rsi +- xorq %rsi, %rdi ++ /* TODO: Improve logic here. */ ++ .p2align 4,, 10 ++L(page_cross_during_loop): ++ /* eax contains [distance_from_page - (VEC_SIZE * 4)]. */ ++ ++ /* Optimistically rsi and rdi and both aligned inwhich case we ++ don't need any logic here. */ ++ cmpl $-(VEC_SIZE * 4), %eax ++ /* Don't adjust eax before jumping back to loop and we will ++ never hit page cross case again. */ ++ je L(loop_skip_page_cross_check) ++ ++ /* Check if we can safely load a VEC. */ ++ cmpl $-(VEC_SIZE * 3), %eax ++ jle L(less_1x_vec_till_page_cross) ++ ++ VMOVA (%rdi), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, (%rsi), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ incl %ecx ++ jnz L(return_vec_0_end) + +- xorl %r8d, %r8d +- /* If ECX > VEC_SIZE * 2, skip ECX - (VEC_SIZE * 2) bytes. */ +- subl $(VEC_SIZE * 2), %ecx +- jle 1f +- /* Skip ECX bytes. */ +- shrq %cl, %rdi +- /* R8 has number of bytes skipped. */ +- movl %ecx, %r8d +-1: +- /* Before jumping back to the loop, set ESI to the number of +- VEC_SIZE * 4 blocks before page crossing. */ +- movl $(PAGE_SIZE / (VEC_SIZE * 4) - 1), %esi +- +- testq %rdi, %rdi +-# ifdef USE_AS_STRNCMP +- /* At this point, if %rdi value is 0, it already tested +- VEC_SIZE*4+%r10 byte starting from %rax. This label +- checks whether strncmp maximum offset reached or not. */ +- je L(string_nbyte_offset_check) +-# else +- je L(back_to_loop) +-# endif +- tzcntq %rdi, %rcx +- addq %r10, %rcx +- /* Adjust for number of bytes skipped. */ +- addq %r8, %rcx +-# ifdef USE_AS_STRNCMP +- addq $(VEC_SIZE * 2), %rcx +- subq %rcx, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ /* if distance >= 2x VEC then eax > -(VEC_SIZE * 2). */ ++ cmpl $-(VEC_SIZE * 2), %eax ++ jg L(more_2x_vec_till_page_cross) ++ ++ .p2align 4,, 4 ++L(less_1x_vec_till_page_cross): ++ subl $-(VEC_SIZE * 4), %eax ++ /* Guranteed safe to read from rdi - VEC_SIZE here. The only ++ concerning case is first iteration if incoming s1 was near start ++ of a page and s2 near end. If s1 was near the start of the page ++ we already aligned up to nearest VEC_SIZE * 4 so gurnateed safe ++ to read back -VEC_SIZE. If rdi is truly at the start of a page ++ here, it means the previous page (rdi - VEC_SIZE) has already ++ been loaded earlier so must be valid. */ ++ VMOVU -VEC_SIZE(%rdi, %rax), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, -VEC_SIZE(%rsi, %rax), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ ++ /* Mask of potentially valid bits. The lower bits can be out of ++ range comparisons (but safe regarding page crosses). */ ++ movl $-1, %r10d ++ shlxl %esi, %r10d, %r10d ++ notl %ecx ++ ++# ifdef USE_AS_STRNCMP ++ cmpq %rax, %rdx ++ jbe L(return_page_cross_end_check) ++# endif ++ movl %eax, %OFFSET_REG ++ addl $(PAGE_SIZE - VEC_SIZE * 4), %eax ++ ++ andl %r10d, %ecx ++ jz L(loop_skip_page_cross_check) ++ ++ .p2align 4,, 3 ++L(return_page_cross_end): ++ tzcntl %ecx, %ecx ++ ++# ifdef USE_AS_STRNCMP ++ leal -VEC_SIZE(%OFFSET_REG64, %rcx), %ecx ++L(return_page_cross_cmp_mem): ++# else ++ addl %OFFSET_REG, %ecx ++# endif ++# ifdef USE_AS_WCSCMP ++ movl VEC_OFFSET(%rdi, %rcx), %edx + xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax +-# endif ++ cmpl VEC_OFFSET(%rsi, %rcx), %edx ++ je L(ret8) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax + # else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ movzbl VEC_OFFSET(%rdi, %rcx), %eax ++ movzbl VEC_OFFSET(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax ++# endif ++L(ret8): ++ VZEROUPPER_RETURN ++ ++# ifdef USE_AS_STRNCMP ++ .p2align 4,, 10 ++L(return_page_cross_end_check): ++ andl %r10d, %ecx ++ tzcntl %ecx, %ecx ++ leal -VEC_SIZE(%rax, %rcx), %ecx ++ cmpl %ecx, %edx ++ ja L(return_page_cross_cmp_mem) + xorl %eax, %eax +- movl (VEC_SIZE * 2)(%rsi, %rcx), %edi +- cmpl (VEC_SIZE * 2)(%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (VEC_SIZE * 2)(%rax, %rcx), %eax +- movzbl (VEC_SIZE * 2)(%rdx, %rcx), %edx +- subl %edx, %eax +-# endif ++ VZEROUPPER_RETURN + # endif ++ ++ ++ .p2align 4,, 10 ++L(more_2x_vec_till_page_cross): ++ /* If more 2x vec till cross we will complete a full loop ++ iteration here. */ ++ ++ VMOVU VEC_SIZE(%rdi), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, VEC_SIZE(%rsi), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ incl %ecx ++ jnz L(return_vec_1_end) ++ ++# ifdef USE_AS_STRNCMP ++ cmpq $(VEC_SIZE * 2), %rdx ++ jbe L(ret_zero_in_loop_page_cross) ++# endif ++ ++ subl $-(VEC_SIZE * 4), %eax ++ ++ /* Safe to include comparisons from lower bytes. */ ++ VMOVU -(VEC_SIZE * 2)(%rdi, %rax), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, -(VEC_SIZE * 2)(%rsi, %rax), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ incl %ecx ++ jnz L(return_vec_page_cross_0) ++ ++ VMOVU -(VEC_SIZE * 1)(%rdi, %rax), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, -(VEC_SIZE * 1)(%rsi, %rax), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ incl %ecx ++ jnz L(return_vec_page_cross_1) ++ ++# ifdef USE_AS_STRNCMP ++ /* Must check length here as length might proclude reading next ++ page. */ ++ cmpq %rax, %rdx ++ jbe L(ret_zero_in_loop_page_cross) ++# endif ++ ++ /* Finish the loop. */ ++ VMOVA (VEC_SIZE * 2)(%rdi), %ymm4 ++ VMOVA (VEC_SIZE * 3)(%rdi), %ymm6 ++ ++ CMP_R1_S2_ymm (%ymm4, (VEC_SIZE * 2)(%rsi), %ymm7, %ymm5) ++ CMP_R1_S2_ymm (%ymm6, (VEC_SIZE * 3)(%rsi), %ymm13, %ymm7) ++ vpand %ymm4, %ymm5, %ymm5 ++ vpand %ymm6, %ymm7, %ymm7 ++ VPMINU %ymm5, %ymm7, %ymm7 ++ VPCMPEQ %ymm7, %ymmZERO, %ymm7 ++ vpmovmskb %ymm7, %LOOP_REG ++ testl %LOOP_REG, %LOOP_REG ++ jnz L(return_vec_2_3_end) ++ ++ /* Best for code size to include ucond-jmp here. Would be faster ++ if this case is hot to duplicate the L(return_vec_2_3_end) code ++ as fall-through and have jump back to loop on mismatch ++ comparison. */ ++ subq $-(VEC_SIZE * 4), %rdi ++ subq $-(VEC_SIZE * 4), %rsi ++ addl $(PAGE_SIZE - VEC_SIZE * 8), %eax ++# ifdef USE_AS_STRNCMP ++ subq $(VEC_SIZE * 4), %rdx ++ ja L(loop_skip_page_cross_check) ++L(ret_zero_in_loop_page_cross): ++ xorl %eax, %eax + VZEROUPPER_RETURN ++# else ++ jmp L(loop_skip_page_cross_check) ++# endif + ++ ++ .p2align 4,, 10 ++L(return_vec_page_cross_0): ++ addl $-VEC_SIZE, %eax ++L(return_vec_page_cross_1): ++ tzcntl %ecx, %ecx + # ifdef USE_AS_STRNCMP +-L(string_nbyte_offset_check): +- leaq (VEC_SIZE * 4)(%r10), %r10 +- cmpq %r10, %r11 +- jbe L(zero) +- jmp L(back_to_loop) ++ leal -VEC_SIZE(%rax, %rcx), %ecx ++ cmpq %rcx, %rdx ++ jbe L(ret_zero_in_loop_page_cross) ++# else ++ addl %eax, %ecx + # endif + +- .p2align 4 +-L(cross_page_loop): +- /* Check one byte/dword at a time. */ + # ifdef USE_AS_WCSCMP +- cmpl %ecx, %eax ++ movl VEC_OFFSET(%rdi, %rcx), %edx ++ xorl %eax, %eax ++ cmpl VEC_OFFSET(%rsi, %rcx), %edx ++ je L(ret9) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax + # else ++ movzbl VEC_OFFSET(%rdi, %rcx), %eax ++ movzbl VEC_OFFSET(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) + subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif +- jne L(different) +- addl $SIZE_OF_CHAR, %edx +- cmpl $(VEC_SIZE * 4), %edx +- je L(main_loop_header) +-# ifdef USE_AS_STRNCMP +- cmpq %r11, %rdx +- jae L(zero) ++L(ret9): ++ VZEROUPPER_RETURN ++ ++ ++ .p2align 4,, 10 ++L(page_cross): ++# ifndef USE_AS_STRNCMP ++ /* If both are VEC aligned we don't need any special logic here. ++ Only valid for strcmp where stop condition is guranteed to be ++ reachable by just reading memory. */ ++ testl $((VEC_SIZE - 1) << 20), %eax ++ jz L(no_page_cross) + # endif ++ ++ movl %edi, %eax ++ movl %esi, %ecx ++ andl $(PAGE_SIZE - 1), %eax ++ andl $(PAGE_SIZE - 1), %ecx ++ ++ xorl %OFFSET_REG, %OFFSET_REG ++ ++ /* Check which is closer to page cross, s1 or s2. */ ++ cmpl %eax, %ecx ++ jg L(page_cross_s2) ++ ++ /* The previous page cross check has false positives. Check for ++ true positive as page cross logic is very expensive. */ ++ subl $(PAGE_SIZE - VEC_SIZE * 4), %eax ++ jbe L(no_page_cross) ++ ++ /* Set r8 to not interfere with normal return value (rdi and rsi ++ did not swap). */ + # ifdef USE_AS_WCSCMP +- movl (%rdi, %rdx), %eax +- movl (%rsi, %rdx), %ecx ++ /* any non-zero positive value that doesn't inference with 0x1. ++ */ ++ movl $2, %r8d + # else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %ecx ++ xorl %r8d, %r8d + # endif +- /* Check null char. */ +- testl %eax, %eax +- jne L(cross_page_loop) +- /* Since %eax == 0, subtract is OK for both SIGNED and UNSIGNED +- comparisons. */ +- subl %ecx, %eax +-# ifndef USE_AS_WCSCMP +-L(different): ++ ++ /* Check if less than 1x VEC till page cross. */ ++ subl $(VEC_SIZE * 3), %eax ++ jg L(less_1x_vec_till_page) ++ ++ /* If more than 1x VEC till page cross, loop throuh safely ++ loadable memory until within 1x VEC of page cross. */ ++ ++ .p2align 4,, 10 ++L(page_cross_loop): ++ ++ VMOVU (%rdi, %OFFSET_REG64), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, (%rsi, %OFFSET_REG64), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ incl %ecx ++ ++ jnz L(check_ret_vec_page_cross) ++ addl $VEC_SIZE, %OFFSET_REG ++# ifdef USE_AS_STRNCMP ++ cmpq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross) + # endif +- VZEROUPPER_RETURN ++ addl $VEC_SIZE, %eax ++ jl L(page_cross_loop) + ++ subl %eax, %OFFSET_REG ++ /* OFFSET_REG has distance to page cross - VEC_SIZE. Guranteed ++ to not cross page so is safe to load. Since we have already ++ loaded at least 1 VEC from rsi it is also guranteed to be ++ safe. */ ++ ++ VMOVU (%rdi, %OFFSET_REG64), %ymm0 ++ CMP_R1_S2_ymm (%ymm0, (%rsi, %OFFSET_REG64), %ymm2, %ymm1) ++ VPCMPEQ %ymm0, %ymmZERO, %ymm2 ++ vpandn %ymm1, %ymm2, %ymm1 ++ vpmovmskb %ymm1, %ecx ++ ++# ifdef USE_AS_STRNCMP ++ leal VEC_SIZE(%OFFSET_REG64), %eax ++ cmpq %rax, %rdx ++ jbe L(check_ret_vec_page_cross2) ++ addq %rdi, %rdx ++# endif ++ incl %ecx ++ jz L(prepare_loop_no_len) ++ ++ .p2align 4,, 4 ++L(ret_vec_page_cross): ++# ifndef USE_AS_STRNCMP ++L(check_ret_vec_page_cross): ++# endif ++ tzcntl %ecx, %ecx ++ addl %OFFSET_REG, %ecx ++L(ret_vec_page_cross_cont): + # ifdef USE_AS_WCSCMP +- .p2align 4 +-L(different): +- /* Use movl to avoid modifying EFLAGS. */ +- movl $0, %eax ++ movl (%rdi, %rcx), %edx ++ xorl %eax, %eax ++ cmpl (%rsi, %rcx), %edx ++ je L(ret12) + setl %al + negl %eax +- orl $1, %eax +- VZEROUPPER_RETURN ++ xorl %r8d, %eax ++# else ++ movzbl (%rdi, %rcx), %eax ++ movzbl (%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif ++L(ret12): ++ VZEROUPPER_RETURN + + # ifdef USE_AS_STRNCMP +- .p2align 4 +-L(zero): ++ .p2align 4,, 10 ++L(check_ret_vec_page_cross2): ++ incl %ecx ++L(check_ret_vec_page_cross): ++ tzcntl %ecx, %ecx ++ addl %OFFSET_REG, %ecx ++ cmpq %rcx, %rdx ++ ja L(ret_vec_page_cross_cont) ++ .p2align 4,, 2 ++L(ret_zero_page_cross): + xorl %eax, %eax + VZEROUPPER_RETURN ++# endif + +- .p2align 4 +-L(char0): +-# ifdef USE_AS_WCSCMP +- xorl %eax, %eax +- movl (%rdi), %ecx +- cmpl (%rsi), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rsi), %ecx +- movzbl (%rdi), %eax +- subl %ecx, %eax +-# endif +- VZEROUPPER_RETURN ++ .p2align 4,, 4 ++L(page_cross_s2): ++ /* Ensure this is a true page cross. */ ++ subl $(PAGE_SIZE - VEC_SIZE * 4), %ecx ++ jbe L(no_page_cross) ++ ++ ++ movl %ecx, %eax ++ movq %rdi, %rcx ++ movq %rsi, %rdi ++ movq %rcx, %rsi ++ ++ /* set r8 to negate return value as rdi and rsi swapped. */ ++# ifdef USE_AS_WCSCMP ++ movl $-4, %r8d ++# else ++ movl $-1, %r8d + # endif ++ xorl %OFFSET_REG, %OFFSET_REG + +- .p2align 4 +-L(last_vector): +- addq %rdx, %rdi +- addq %rdx, %rsi +-# ifdef USE_AS_STRNCMP +- subq %rdx, %r11 ++ /* Check if more than 1x VEC till page cross. */ ++ subl $(VEC_SIZE * 3), %eax ++ jle L(page_cross_loop) ++ ++ .p2align 4,, 6 ++L(less_1x_vec_till_page): ++ /* Find largest load size we can use. */ ++ cmpl $16, %eax ++ ja L(less_16_till_page) ++ ++ VMOVU (%rdi), %xmm0 ++ CMP_R1_S2_xmm (%xmm0, (%rsi), %xmm2, %xmm1) ++ VPCMPEQ %xmm0, %xmmZERO, %xmm2 ++ vpandn %xmm1, %xmm2, %xmm1 ++ vpmovmskb %ymm1, %ecx ++ incw %cx ++ jnz L(check_ret_vec_page_cross) ++ movl $16, %OFFSET_REG ++# ifdef USE_AS_STRNCMP ++ cmpq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross_slow_case0) ++ subl %eax, %OFFSET_REG ++# else ++ /* Explicit check for 16 byte alignment. */ ++ subl %eax, %OFFSET_REG ++ jz L(prepare_loop) + # endif +- tzcntl %ecx, %edx ++ ++ VMOVU (%rdi, %OFFSET_REG64), %xmm0 ++ CMP_R1_S2_xmm (%xmm0, (%rsi, %OFFSET_REG64), %xmm2, %xmm1) ++ VPCMPEQ %xmm0, %xmmZERO, %xmm2 ++ vpandn %xmm1, %xmm2, %xmm1 ++ vpmovmskb %ymm1, %ecx ++ incw %cx ++ jnz L(check_ret_vec_page_cross) ++ + # ifdef USE_AS_STRNCMP +- cmpq %r11, %rdx +- jae L(zero) ++ addl $16, %OFFSET_REG ++ subq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross_slow_case0) ++ subq $-(VEC_SIZE * 4), %rdx ++ ++ leaq -(VEC_SIZE * 4)(%rdi, %OFFSET_REG64), %rdi ++ leaq -(VEC_SIZE * 4)(%rsi, %OFFSET_REG64), %rsi ++# else ++ leaq (16 - VEC_SIZE * 4)(%rdi, %OFFSET_REG64), %rdi ++ leaq (16 - VEC_SIZE * 4)(%rsi, %OFFSET_REG64), %rsi + # endif +-# ifdef USE_AS_WCSCMP ++ jmp L(prepare_loop_aligned) ++ ++# ifdef USE_AS_STRNCMP ++ .p2align 4,, 2 ++L(ret_zero_page_cross_slow_case0): + xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax ++ ret + # endif +- VZEROUPPER_RETURN + +- /* Comparing on page boundary region requires special treatment: +- It must done one vector at the time, starting with the wider +- ymm vector if possible, if not, with xmm. If fetching 16 bytes +- (xmm) still passes the boundary, byte comparison must be done. +- */ +- .p2align 4 +-L(cross_page): +- /* Try one ymm vector at a time. */ +- cmpl $(PAGE_SIZE - VEC_SIZE), %eax +- jg L(cross_page_1_vector) +-L(loop_1_vector): +- vmovdqu (%rdi, %rdx), %ymm1 +- VPCMPEQ (%rsi, %rdx), %ymm1, %ymm0 +- VPMINU %ymm1, %ymm0, %ymm0 +- VPCMPEQ %ymm7, %ymm0, %ymm0 +- vpmovmskb %ymm0, %ecx +- testl %ecx, %ecx +- jne L(last_vector) + +- addl $VEC_SIZE, %edx ++ .p2align 4,, 10 ++L(less_16_till_page): ++ /* Find largest load size we can use. */ ++ cmpl $24, %eax ++ ja L(less_8_till_page) ++ ++ vmovq (%rdi), %xmm0 ++ vmovq (%rsi), %xmm1 ++ VPCMPEQ %xmm0, %xmmZERO, %xmm2 ++ CMP_R1_R2_xmm (%xmm0, %xmm1, %xmm3, %xmm1) ++ vpandn %xmm1, %xmm2, %xmm1 ++ vpmovmskb %ymm1, %ecx ++ incb %cl ++ jnz L(check_ret_vec_page_cross) ++ + +- addl $VEC_SIZE, %eax + # ifdef USE_AS_STRNCMP +- /* Return 0 if the current offset (%rdx) >= the maximum offset +- (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) +-# endif +- cmpl $(PAGE_SIZE - VEC_SIZE), %eax +- jle L(loop_1_vector) +-L(cross_page_1_vector): +- /* Less than 32 bytes to check, try one xmm vector. */ +- cmpl $(PAGE_SIZE - 16), %eax +- jg L(cross_page_1_xmm) +- vmovdqu (%rdi, %rdx), %xmm1 +- VPCMPEQ (%rsi, %rdx), %xmm1, %xmm0 +- VPMINU %xmm1, %xmm0, %xmm0 +- VPCMPEQ %xmm7, %xmm0, %xmm0 +- vpmovmskb %xmm0, %ecx +- testl %ecx, %ecx +- jne L(last_vector) ++ cmpq $8, %rdx ++ jbe L(ret_zero_page_cross_slow_case0) ++# endif ++ movl $24, %OFFSET_REG ++ /* Explicit check for 16 byte alignment. */ ++ subl %eax, %OFFSET_REG + +- addl $16, %edx +-# ifndef USE_AS_WCSCMP +- addl $16, %eax +-# endif +-# ifdef USE_AS_STRNCMP +- /* Return 0 if the current offset (%rdx) >= the maximum offset +- (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) +-# endif +- +-L(cross_page_1_xmm): +-# ifndef USE_AS_WCSCMP +- /* Less than 16 bytes to check, try 8 byte vector. NB: No need +- for wcscmp nor wcsncmp since wide char is 4 bytes. */ +- cmpl $(PAGE_SIZE - 8), %eax +- jg L(cross_page_8bytes) +- vmovq (%rdi, %rdx), %xmm1 +- vmovq (%rsi, %rdx), %xmm0 +- VPCMPEQ %xmm0, %xmm1, %xmm0 +- VPMINU %xmm1, %xmm0, %xmm0 +- VPCMPEQ %xmm7, %xmm0, %xmm0 +- vpmovmskb %xmm0, %ecx +- /* Only last 8 bits are valid. */ +- andl $0xff, %ecx +- testl %ecx, %ecx +- jne L(last_vector) + +- addl $8, %edx +- addl $8, %eax +-# ifdef USE_AS_STRNCMP +- /* Return 0 if the current offset (%rdx) >= the maximum offset +- (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) +-# endif +- +-L(cross_page_8bytes): +- /* Less than 8 bytes to check, try 4 byte vector. */ +- cmpl $(PAGE_SIZE - 4), %eax +- jg L(cross_page_4bytes) +- vmovd (%rdi, %rdx), %xmm1 +- vmovd (%rsi, %rdx), %xmm0 +- VPCMPEQ %xmm0, %xmm1, %xmm0 +- VPMINU %xmm1, %xmm0, %xmm0 +- VPCMPEQ %xmm7, %xmm0, %xmm0 +- vpmovmskb %xmm0, %ecx +- /* Only last 4 bits are valid. */ +- andl $0xf, %ecx +- testl %ecx, %ecx +- jne L(last_vector) + +- addl $4, %edx +-# ifdef USE_AS_STRNCMP +- /* Return 0 if the current offset (%rdx) >= the maximum offset +- (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) +-# endif ++ vmovq (%rdi, %OFFSET_REG64), %xmm0 ++ vmovq (%rsi, %OFFSET_REG64), %xmm1 ++ VPCMPEQ %xmm0, %xmmZERO, %xmm2 ++ CMP_R1_R2_xmm (%xmm0, %xmm1, %xmm3, %xmm1) ++ vpandn %xmm1, %xmm2, %xmm1 ++ vpmovmskb %ymm1, %ecx ++ incb %cl ++ jnz L(check_ret_vec_page_cross) + +-L(cross_page_4bytes): +-# endif +- /* Less than 4 bytes to check, try one byte/dword at a time. */ + # ifdef USE_AS_STRNCMP +- cmpq %r11, %rdx +- jae L(zero) ++ addl $8, %OFFSET_REG ++ subq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross_slow_case0) ++ subq $-(VEC_SIZE * 4), %rdx ++ ++ leaq -(VEC_SIZE * 4)(%rdi, %OFFSET_REG64), %rdi ++ leaq -(VEC_SIZE * 4)(%rsi, %OFFSET_REG64), %rsi ++# else ++ leaq (8 - VEC_SIZE * 4)(%rdi, %OFFSET_REG64), %rdi ++ leaq (8 - VEC_SIZE * 4)(%rsi, %OFFSET_REG64), %rsi + # endif ++ jmp L(prepare_loop_aligned) ++ ++ ++ .p2align 4,, 10 ++L(less_8_till_page): + # ifdef USE_AS_WCSCMP +- movl (%rdi, %rdx), %eax +- movl (%rsi, %rdx), %ecx ++ /* If using wchar then this is the only check before we reach ++ the page boundary. */ ++ movl (%rdi), %eax ++ movl (%rsi), %ecx ++ cmpl %ecx, %eax ++ jnz L(ret_less_8_wcs) ++# ifdef USE_AS_STRNCMP ++ addq %rdi, %rdx ++ /* We already checked for len <= 1 so cannot hit that case here. ++ */ ++# endif ++ testl %eax, %eax ++ jnz L(prepare_loop_no_len) ++ ret ++ ++ .p2align 4,, 8 ++L(ret_less_8_wcs): ++ setl %OFFSET_REG8 ++ negl %OFFSET_REG ++ movl %OFFSET_REG, %eax ++ xorl %r8d, %eax ++ ret ++ + # else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %ecx ++ ++ /* Find largest load size we can use. */ ++ cmpl $28, %eax ++ ja L(less_4_till_page) ++ ++ vmovd (%rdi), %xmm0 ++ vmovd (%rsi), %xmm1 ++ VPCMPEQ %xmm0, %xmmZERO, %xmm2 ++ CMP_R1_R2_xmm (%xmm0, %xmm1, %xmm3, %xmm1) ++ vpandn %xmm1, %xmm2, %xmm1 ++ vpmovmskb %ymm1, %ecx ++ subl $0xf, %ecx ++ jnz L(check_ret_vec_page_cross) ++ ++# ifdef USE_AS_STRNCMP ++ cmpq $4, %rdx ++ jbe L(ret_zero_page_cross_slow_case1) ++# endif ++ movl $28, %OFFSET_REG ++ /* Explicit check for 16 byte alignment. */ ++ subl %eax, %OFFSET_REG ++ ++ ++ ++ vmovd (%rdi, %OFFSET_REG64), %xmm0 ++ vmovd (%rsi, %OFFSET_REG64), %xmm1 ++ VPCMPEQ %xmm0, %xmmZERO, %xmm2 ++ CMP_R1_R2_xmm (%xmm0, %xmm1, %xmm3, %xmm1) ++ vpandn %xmm1, %xmm2, %xmm1 ++ vpmovmskb %ymm1, %ecx ++ subl $0xf, %ecx ++ jnz L(check_ret_vec_page_cross) ++ ++# ifdef USE_AS_STRNCMP ++ addl $4, %OFFSET_REG ++ subq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross_slow_case1) ++ subq $-(VEC_SIZE * 4), %rdx ++ ++ leaq -(VEC_SIZE * 4)(%rdi, %OFFSET_REG64), %rdi ++ leaq -(VEC_SIZE * 4)(%rsi, %OFFSET_REG64), %rsi ++# else ++ leaq (4 - VEC_SIZE * 4)(%rdi, %OFFSET_REG64), %rdi ++ leaq (4 - VEC_SIZE * 4)(%rsi, %OFFSET_REG64), %rsi ++# endif ++ jmp L(prepare_loop_aligned) ++ ++# ifdef USE_AS_STRNCMP ++ .p2align 4,, 2 ++L(ret_zero_page_cross_slow_case1): ++ xorl %eax, %eax ++ ret ++# endif ++ ++ .p2align 4,, 10 ++L(less_4_till_page): ++ subq %rdi, %rsi ++ /* Extremely slow byte comparison loop. */ ++L(less_4_loop): ++ movzbl (%rdi), %eax ++ movzbl (%rsi, %rdi), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %BYTE_LOOP_REG) ++ subl %BYTE_LOOP_REG, %eax ++ jnz L(ret_less_4_loop) ++ testl %ecx, %ecx ++ jz L(ret_zero_4_loop) ++# ifdef USE_AS_STRNCMP ++ decq %rdx ++ jz L(ret_zero_4_loop) ++# endif ++ incq %rdi ++ /* end condition is reach page boundary (rdi is aligned). */ ++ testl $31, %edi ++ jnz L(less_4_loop) ++ leaq -(VEC_SIZE * 4)(%rdi, %rsi), %rsi ++ addq $-(VEC_SIZE * 4), %rdi ++# ifdef USE_AS_STRNCMP ++ subq $-(VEC_SIZE * 4), %rdx ++# endif ++ jmp L(prepare_loop_aligned) ++ ++L(ret_zero_4_loop): ++ xorl %eax, %eax ++ ret ++L(ret_less_4_loop): ++ xorl %r8d, %eax ++ subl %r8d, %eax ++ ret + # endif +- testl %eax, %eax +- jne L(cross_page_loop) +- subl %ecx, %eax +- VZEROUPPER_RETURN +-END (STRCMP) ++ cfi_endproc ++ .size STRCMP, .-STRCMP + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/strcmp.c b/sysdeps/x86_64/multiarch/strcmp.c +--- a/sysdeps/x86_64/multiarch/strcmp.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strcmp.c 2025-10-20 21:02:22.000000000 +0300 +@@ -29,6 +29,7 @@ + extern __typeof (REDIRECT_NAME) OPTIMIZE (sse2) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (sse2_unaligned) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (ssse3) attribute_hidden; ++extern __typeof (REDIRECT_NAME) OPTIMIZE (sse42) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (avx2) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (avx2_rtm) attribute_hidden; + extern __typeof (REDIRECT_NAME) OPTIMIZE (evex) attribute_hidden; +@@ -39,11 +40,11 @@ + const struct cpu_features* cpu_features = __get_cpu_features (); + + if (CPU_FEATURE_USABLE_P (cpu_features, AVX2) ++ && CPU_FEATURE_USABLE_P (cpu_features, BMI2) + && CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load)) + { + if (CPU_FEATURE_USABLE_P (cpu_features, AVX512VL) +- && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW) +- && CPU_FEATURE_USABLE_P (cpu_features, BMI2)) ++ && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW)) + return OPTIMIZE (evex); + + if (CPU_FEATURE_USABLE_P (cpu_features, RTM)) +@@ -53,6 +54,10 @@ + return OPTIMIZE (avx2); + } + ++ if (CPU_FEATURE_USABLE_P (cpu_features, SSE4_2) ++ && !CPU_FEATURES_ARCH_P (cpu_features, Slow_SSE4_2)) ++ return OPTIMIZE (sse42); ++ + if (CPU_FEATURES_ARCH_P (cpu_features, Fast_Unaligned_Load)) + return OPTIMIZE (sse2_unaligned); + +diff -Nuar a/sysdeps/x86_64/multiarch/strcmp-evex.S b/sysdeps/x86_64/multiarch/strcmp-evex.S +--- a/sysdeps/x86_64/multiarch/strcmp-evex.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strcmp-evex.S 2025-10-20 21:02:22.000000000 +0300 +@@ -19,6 +19,9 @@ + #if IS_IN (libc) + + # include ++# if defined USE_AS_STRCASECMP_L ++# include "locale-defines.h" ++# endif + + # ifndef STRCMP + # define STRCMP __strcmp_evex +@@ -26,54 +29,165 @@ + + # define PAGE_SIZE 4096 + +-/* VEC_SIZE = Number of bytes in a ymm register */ ++ /* VEC_SIZE = Number of bytes in a ymm register. */ + # define VEC_SIZE 32 ++# define CHAR_PER_VEC (VEC_SIZE / SIZE_OF_CHAR) + +-/* Shift for dividing by (VEC_SIZE * 4). */ +-# define DIVIDE_BY_VEC_4_SHIFT 7 +-# if (VEC_SIZE * 4) != (1 << DIVIDE_BY_VEC_4_SHIFT) +-# error (VEC_SIZE * 4) != (1 << DIVIDE_BY_VEC_4_SHIFT) +-# endif +- +-# define VMOVU vmovdqu64 +-# define VMOVA vmovdqa64 ++# define VMOVU vmovdqu64 ++# define VMOVA vmovdqa64 + + # ifdef USE_AS_WCSCMP +-/* Compare packed dwords. */ +-# define VPCMP vpcmpd ++# ifndef OVERFLOW_STRCMP ++# define OVERFLOW_STRCMP __wcscmp_evex ++# endif ++ ++# define TESTEQ subl $0xff, ++ /* Compare packed dwords. */ ++# define VPCMP vpcmpd + # define VPMINU vpminud + # define VPTESTM vptestmd +-# define SHIFT_REG32 r8d +-# define SHIFT_REG64 r8 +-/* 1 dword char == 4 bytes. */ ++# define VPTESTNM vptestnmd ++ /* 1 dword char == 4 bytes. */ + # define SIZE_OF_CHAR 4 + # else +-/* Compare packed bytes. */ +-# define VPCMP vpcmpb ++# ifndef OVERFLOW_STRCMP ++# define OVERFLOW_STRCMP __strcmp_evex ++# endif ++ ++# define TESTEQ incl ++ /* Compare packed bytes. */ ++# define VPCMP vpcmpb + # define VPMINU vpminub + # define VPTESTM vptestmb +-# define SHIFT_REG32 ecx +-# define SHIFT_REG64 rcx +-/* 1 byte char == 1 byte. */ ++# define VPTESTNM vptestnmb ++ /* 1 byte char == 1 byte. */ + # define SIZE_OF_CHAR 1 + # endif + +-# define XMMZERO xmm16 +-# define XMM0 xmm17 +-# define XMM1 xmm18 +- +-# define YMMZERO ymm16 +-# define YMM0 ymm17 +-# define YMM1 ymm18 +-# define YMM2 ymm19 +-# define YMM3 ymm20 +-# define YMM4 ymm21 +-# define YMM5 ymm22 +-# define YMM6 ymm23 +-# define YMM7 ymm24 +-# define YMM8 ymm25 +-# define YMM9 ymm26 +-# define YMM10 ymm27 ++# ifdef USE_AS_STRNCMP ++# define LOOP_REG r9d ++# define LOOP_REG64 r9 ++ ++# define OFFSET_REG8 r9b ++# define OFFSET_REG r9d ++# define OFFSET_REG64 r9 ++# else ++# define LOOP_REG edx ++# define LOOP_REG64 rdx ++ ++# define OFFSET_REG8 dl ++# define OFFSET_REG edx ++# define OFFSET_REG64 rdx ++# endif ++ ++# if defined USE_AS_STRNCMP || defined USE_AS_WCSCMP ++# define VEC_OFFSET 0 ++# else ++# define VEC_OFFSET (-VEC_SIZE) ++# endif ++ ++# define XMM0 xmm17 ++# define XMM1 xmm18 ++ ++# define XMM10 xmm27 ++# define XMM11 xmm28 ++# define XMM12 xmm29 ++# define XMM13 xmm30 ++# define XMM14 xmm31 ++ ++ ++# define YMM0 ymm17 ++# define YMM1 ymm18 ++# define YMM2 ymm19 ++# define YMM3 ymm20 ++# define YMM4 ymm21 ++# define YMM5 ymm22 ++# define YMM6 ymm23 ++# define YMM7 ymm24 ++# define YMM8 ymm25 ++# define YMM9 ymm26 ++# define YMM10 ymm27 ++# define YMM11 ymm28 ++# define YMM12 ymm29 ++# define YMM13 ymm30 ++# define YMM14 ymm31 ++ ++# ifdef USE_AS_STRCASECMP_L ++# define BYTE_LOOP_REG OFFSET_REG ++# else ++# define BYTE_LOOP_REG ecx ++# endif ++ ++# ifdef USE_AS_STRCASECMP_L ++# ifdef USE_AS_STRNCMP ++# define STRCASECMP __strncasecmp_evex ++# define LOCALE_REG rcx ++# define LOCALE_REG_LP RCX_LP ++# define STRCASECMP_NONASCII __strncasecmp_l_nonascii ++# else ++# define STRCASECMP __strcasecmp_evex ++# define LOCALE_REG rdx ++# define LOCALE_REG_LP RDX_LP ++# define STRCASECMP_NONASCII __strcasecmp_l_nonascii ++# endif ++# endif ++ ++# define LCASE_MIN_YMM %YMM12 ++# define LCASE_MAX_YMM %YMM13 ++# define CASE_ADD_YMM %YMM14 ++ ++# define LCASE_MIN_XMM %XMM12 ++# define LCASE_MAX_XMM %XMM13 ++# define CASE_ADD_XMM %XMM14 ++ ++ /* NB: wcsncmp uses r11 but strcasecmp is never used in ++ conjunction with wcscmp. */ ++# define TOLOWER_BASE %r11 ++ ++# ifdef USE_AS_STRCASECMP_L ++# define _REG(x, y) x ## y ++# define REG(x, y) _REG(x, y) ++# define TOLOWER(reg1, reg2, ext) \ ++ vpsubb REG(LCASE_MIN_, ext), reg1, REG(%ext, 10); \ ++ vpsubb REG(LCASE_MIN_, ext), reg2, REG(%ext, 11); \ ++ vpcmpub $1, REG(LCASE_MAX_, ext), REG(%ext, 10), %k5; \ ++ vpcmpub $1, REG(LCASE_MAX_, ext), REG(%ext, 11), %k6; \ ++ vpaddb reg1, REG(CASE_ADD_, ext), reg1{%k5}; \ ++ vpaddb reg2, REG(CASE_ADD_, ext), reg2{%k6} ++ ++# define TOLOWER_gpr(src, dst) movl (TOLOWER_BASE, src, 4), dst ++# define TOLOWER_YMM(...) TOLOWER(__VA_ARGS__, YMM) ++# define TOLOWER_XMM(...) TOLOWER(__VA_ARGS__, XMM) ++ ++# define CMP_R1_R2(s1_reg, s2_reg, reg_out, ext) \ ++ TOLOWER (s1_reg, s2_reg, ext); \ ++ VPCMP $0, s1_reg, s2_reg, reg_out ++ ++# define CMP_R1_S2(s1_reg, s2_mem, s2_reg, reg_out, ext) \ ++ VMOVU s2_mem, s2_reg; \ ++ CMP_R1_R2(s1_reg, s2_reg, reg_out, ext) ++ ++# define CMP_R1_R2_YMM(...) CMP_R1_R2(__VA_ARGS__, YMM) ++# define CMP_R1_R2_XMM(...) CMP_R1_R2(__VA_ARGS__, XMM) ++ ++# define CMP_R1_S2_YMM(...) CMP_R1_S2(__VA_ARGS__, YMM) ++# define CMP_R1_S2_XMM(...) CMP_R1_S2(__VA_ARGS__, XMM) ++ ++# else ++# define TOLOWER_gpr(...) ++# define TOLOWER_YMM(...) ++# define TOLOWER_XMM(...) ++ ++# define CMP_R1_R2_YMM(s1_reg, s2_reg, reg_out) \ ++ VPCMP $0, s2_reg, s1_reg, reg_out ++ ++# define CMP_R1_R2_XMM(...) CMP_R1_R2_YMM(__VA_ARGS__) ++ ++# define CMP_R1_S2_YMM(s1_reg, s2_mem, unused, reg_out) \ ++ VPCMP $0, s2_mem, s1_reg, reg_out ++ ++# define CMP_R1_S2_XMM(...) CMP_R1_S2_YMM(__VA_ARGS__) ++# endif + + /* Warning! + wcscmp/wcsncmp have to use SIGNED comparison for elements. +@@ -96,985 +210,1208 @@ + the maximum offset is reached before a difference is found, zero is + returned. */ + +- .section .text.evex,"ax",@progbits +-ENTRY (STRCMP) +-# ifdef USE_AS_STRNCMP +- /* Check for simple cases (0 or 1) in offset. */ +- cmp $1, %RDX_LP +- je L(char0) +- jb L(zero) +-# ifdef USE_AS_WCSCMP +-# ifndef __ILP32__ +- movq %rdx, %rcx +- /* Check if length could overflow when multiplied by +- sizeof(wchar_t). Checking top 8 bits will cover all potential +- overflow cases as well as redirect cases where its impossible to +- length to bound a valid memory region. In these cases just use +- 'wcscmp'. */ +- shrq $56, %rcx +- jnz __wcscmp_evex ++ .section .text.evex, "ax", @progbits ++ .align 16 ++ .type STRCMP, @function ++ .globl STRCMP ++ .hidden STRCMP ++ ++# ifdef USE_AS_STRCASECMP_L ++ENTRY (STRCASECMP) ++ movq __libc_tsd_LOCALE@gottpoff(%rip), %rax ++ mov %fs:(%rax), %LOCALE_REG_LP ++ ++ /* Either 1 or 5 bytes (dependeing if CET is enabled). */ ++ .p2align 4 ++END (STRCASECMP) ++ /* FALLTHROUGH to strcasecmp/strncasecmp_l. */ ++# endif ++ ++ .p2align 4 ++STRCMP: ++ cfi_startproc ++ _CET_ENDBR ++ CALL_MCOUNT ++ ++# if defined USE_AS_STRCASECMP_L ++ /* We have to fall back on the C implementation for locales with ++ encodings not matching ASCII for single bytes. */ ++# if LOCALE_T___LOCALES != 0 || LC_CTYPE != 0 ++ mov LOCALE_T___LOCALES + LC_CTYPE * LP_SIZE(%LOCALE_REG), %RAX_LP ++# else ++ mov (%LOCALE_REG), %RAX_LP + # endif +- /* Convert units: from wide to byte char. */ +- shl $2, %RDX_LP ++ testl $1, LOCALE_DATA_VALUES + _NL_CTYPE_NONASCII_CASE * SIZEOF_VALUES(%rax) ++ jne STRCASECMP_NONASCII ++ leaq _nl_C_LC_CTYPE_tolower + 128 * 4(%rip), TOLOWER_BASE ++# endif ++ ++# ifdef USE_AS_STRNCMP ++ /* Don't overwrite LOCALE_REG (rcx) until we have pass ++ L(one_or_less). Otherwise we might use the wrong locale in ++ the OVERFLOW_STRCMP (strcasecmp_l). */ ++# ifdef __ILP32__ ++ /* Clear the upper 32 bits. */ ++ movl %edx, %edx + # endif +- /* Register %r11 tracks the maximum offset. */ +- mov %RDX_LP, %R11_LP ++ cmp $1, %RDX_LP ++ /* Signed comparison intentional. We use this branch to also ++ test cases where length >= 2^63. These very large sizes can be ++ handled with strcmp as there is no way for that length to ++ actually bound the buffer. */ ++ jle L(one_or_less) ++# endif ++ ++# if defined USE_AS_STRCASECMP_L ++ .section .rodata.cst32, "aM", @progbits, 32 ++ .align 32 ++L(lcase_min): ++ .quad 0x4141414141414141 ++ .quad 0x4141414141414141 ++ .quad 0x4141414141414141 ++ .quad 0x4141414141414141 ++L(lcase_max): ++ .quad 0x1a1a1a1a1a1a1a1a ++ .quad 0x1a1a1a1a1a1a1a1a ++ .quad 0x1a1a1a1a1a1a1a1a ++ .quad 0x1a1a1a1a1a1a1a1a ++L(case_add): ++ .quad 0x2020202020202020 ++ .quad 0x2020202020202020 ++ .quad 0x2020202020202020 ++ .quad 0x2020202020202020 ++ .previous ++ ++ vmovdqa64 L(lcase_min)(%rip), LCASE_MIN_YMM ++ vmovdqa64 L(lcase_max)(%rip), LCASE_MAX_YMM ++ vmovdqa64 L(case_add)(%rip), CASE_ADD_YMM + # endif ++ + movl %edi, %eax +- xorl %edx, %edx +- /* Make %XMMZERO (%YMMZERO) all zeros in this function. */ +- vpxorq %XMMZERO, %XMMZERO, %XMMZERO + orl %esi, %eax +- andl $(PAGE_SIZE - 1), %eax +- cmpl $(PAGE_SIZE - (VEC_SIZE * 4)), %eax +- jg L(cross_page) +- /* Start comparing 4 vectors. */ +- VMOVU (%rdi), %YMM0 ++ /* Shift out the bits irrelivant to page boundary ([63:12]). */ ++ sall $20, %eax ++ /* Check if s1 or s2 may cross a page in next 4x VEC loads. */ ++ cmpl $((PAGE_SIZE -(VEC_SIZE * 4)) << 20), %eax ++ ja L(page_cross) + +- /* Each bit set in K2 represents a non-null CHAR in YMM0. */ ++L(no_page_cross): ++ /* Safe to compare 4x vectors. */ ++ VMOVU (%rdi), %YMM0 + VPTESTM %YMM0, %YMM0, %k2 +- + /* Each bit cleared in K1 represents a mismatch or a null CHAR + in YMM0 and 32 bytes at (%rsi). */ +- VPCMP $0, (%rsi), %YMM0, %k1{%k2} +- ++ CMP_R1_S2_YMM (%YMM0, (%rsi), %YMM1, %k1){%k2} + kmovd %k1, %ecx +-# ifdef USE_AS_WCSCMP +- subl $0xff, %ecx +-# else +- incl %ecx +-# endif +- je L(next_3_vectors) +- tzcntl %ecx, %edx +-# ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %edx +-# endif + # ifdef USE_AS_STRNCMP +- /* Return 0 if the mismatched index (%rdx) is after the maximum +- offset (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) ++ cmpq $CHAR_PER_VEC, %rdx ++ jbe L(vec_0_test_len) + # endif ++ ++ /* TESTEQ is `incl` for strcmp/strncmp and `subl $0xff` for ++ wcscmp/wcsncmp. */ ++ ++ /* All 1s represents all equals. TESTEQ will overflow to zero in ++ all equals case. Otherwise 1s will carry until position of first ++ mismatch. */ ++ TESTEQ %ecx ++ jz L(more_3x_vec) ++ ++ .p2align 4,, 4 ++L(return_vec_0): ++ tzcntl %ecx, %ecx + # ifdef USE_AS_WCSCMP ++ movl (%rdi, %rcx, SIZE_OF_CHAR), %edx + xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- je L(return) +-L(wcscmp_return): ++ cmpl (%rsi, %rcx, SIZE_OF_CHAR), %edx ++ je L(ret0) + setl %al + negl %eax + orl $1, %eax +-L(return): + # else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax ++ movzbl (%rdi, %rcx), %eax ++ movzbl (%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax + # endif ++L(ret0): + ret + +-L(return_vec_size): +- tzcntl %ecx, %edx +-# ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %edx +-# endif + # ifdef USE_AS_STRNCMP +- /* Return 0 if the mismatched index (%rdx + VEC_SIZE) is after +- the maximum offset (%r11). */ +- addq $VEC_SIZE, %rdx +- cmpq %r11, %rdx +- jae L(zero) +-# ifdef USE_AS_WCSCMP ++ .p2align 4,, 4 ++L(vec_0_test_len): ++ notl %ecx ++ bzhil %edx, %ecx, %eax ++ jnz L(return_vec_0) ++ /* Align if will cross fetch block. */ ++ .p2align 4,, 2 ++L(ret_zero): + xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax +-# endif +-# else ++ ret ++ ++ .p2align 4,, 5 ++L(one_or_less): ++# ifdef USE_AS_STRCASECMP_L ++ /* Set locale argument for strcasecmp. */ ++ movq %LOCALE_REG, %rdx ++# endif ++ jb L(ret_zero) ++ /* 'nbe' covers the case where length is negative (large ++ unsigned). */ ++ jnbe OVERFLOW_STRCMP + # ifdef USE_AS_WCSCMP ++ movl (%rdi), %edx + xorl %eax, %eax +- movl VEC_SIZE(%rdi, %rdx), %ecx +- cmpl VEC_SIZE(%rsi, %rdx), %ecx +- jne L(wcscmp_return) ++ cmpl (%rsi), %edx ++ je L(ret1) ++ setl %al ++ negl %eax ++ orl $1, %eax + # else +- movzbl VEC_SIZE(%rdi, %rdx), %eax +- movzbl VEC_SIZE(%rsi, %rdx), %edx +- subl %edx, %eax ++ movzbl (%rdi), %eax ++ movzbl (%rsi), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax + # endif +-# endif ++L(ret1): + ret ++# endif + +-L(return_2_vec_size): +- tzcntl %ecx, %edx ++ .p2align 4,, 10 ++L(return_vec_1): ++ tzcntl %ecx, %ecx ++# ifdef USE_AS_STRNCMP ++ /* rdx must be > CHAR_PER_VEC so its safe to subtract without ++ worrying about underflow. */ ++ addq $-CHAR_PER_VEC, %rdx ++ cmpq %rcx, %rdx ++ jbe L(ret_zero) ++# endif + # ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %edx ++ movl VEC_SIZE(%rdi, %rcx, SIZE_OF_CHAR), %edx ++ xorl %eax, %eax ++ cmpl VEC_SIZE(%rsi, %rcx, SIZE_OF_CHAR), %edx ++ je L(ret2) ++ setl %al ++ negl %eax ++ orl $1, %eax ++# else ++ movzbl VEC_SIZE(%rdi, %rcx), %eax ++ movzbl VEC_SIZE(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax + # endif ++L(ret2): ++ ret ++ ++ .p2align 4,, 10 + # ifdef USE_AS_STRNCMP +- /* Return 0 if the mismatched index (%rdx + 2 * VEC_SIZE) is +- after the maximum offset (%r11). */ +- addq $(VEC_SIZE * 2), %rdx +- cmpq %r11, %rdx +- jae L(zero) +-# ifdef USE_AS_WCSCMP +- xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- jne L(wcscmp_return) ++L(return_vec_3): ++# if CHAR_PER_VEC <= 16 ++ sall $CHAR_PER_VEC, %ecx + # else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax ++ salq $CHAR_PER_VEC, %rcx + # endif ++# endif ++L(return_vec_2): ++# if (CHAR_PER_VEC <= 16) || !(defined USE_AS_STRNCMP) ++ tzcntl %ecx, %ecx + # else +-# ifdef USE_AS_WCSCMP +- xorl %eax, %eax +- movl (VEC_SIZE * 2)(%rdi, %rdx), %ecx +- cmpl (VEC_SIZE * 2)(%rsi, %rdx), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (VEC_SIZE * 2)(%rdi, %rdx), %eax +- movzbl (VEC_SIZE * 2)(%rsi, %rdx), %edx +- subl %edx, %eax +-# endif ++ tzcntq %rcx, %rcx + # endif +- ret + +-L(return_3_vec_size): +- tzcntl %ecx, %edx +-# ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %edx +-# endif + # ifdef USE_AS_STRNCMP +- /* Return 0 if the mismatched index (%rdx + 3 * VEC_SIZE) is +- after the maximum offset (%r11). */ +- addq $(VEC_SIZE * 3), %rdx +- cmpq %r11, %rdx +- jae L(zero) +-# ifdef USE_AS_WCSCMP ++ cmpq %rcx, %rdx ++ jbe L(ret_zero) ++# endif ++ ++# ifdef USE_AS_WCSCMP ++ movl (VEC_SIZE * 2)(%rdi, %rcx, SIZE_OF_CHAR), %edx + xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax +-# endif ++ cmpl (VEC_SIZE * 2)(%rsi, %rcx, SIZE_OF_CHAR), %edx ++ je L(ret3) ++ setl %al ++ negl %eax ++ orl $1, %eax + # else ++ movzbl (VEC_SIZE * 2)(%rdi, %rcx), %eax ++ movzbl (VEC_SIZE * 2)(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++# endif ++L(ret3): ++ ret ++ ++# ifndef USE_AS_STRNCMP ++ .p2align 4,, 10 ++L(return_vec_3): ++ tzcntl %ecx, %ecx + # ifdef USE_AS_WCSCMP ++ movl (VEC_SIZE * 3)(%rdi, %rcx, SIZE_OF_CHAR), %edx + xorl %eax, %eax +- movl (VEC_SIZE * 3)(%rdi, %rdx), %ecx +- cmpl (VEC_SIZE * 3)(%rsi, %rdx), %ecx +- jne L(wcscmp_return) ++ cmpl (VEC_SIZE * 3)(%rsi, %rcx, SIZE_OF_CHAR), %edx ++ je L(ret4) ++ setl %al ++ negl %eax ++ orl $1, %eax + # else +- movzbl (VEC_SIZE * 3)(%rdi, %rdx), %eax +- movzbl (VEC_SIZE * 3)(%rsi, %rdx), %edx +- subl %edx, %eax ++ movzbl (VEC_SIZE * 3)(%rdi, %rcx), %eax ++ movzbl (VEC_SIZE * 3)(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax + # endif +-# endif ++L(ret4): + ret ++# endif + +- .p2align 4 +-L(next_3_vectors): +- VMOVU VEC_SIZE(%rdi), %YMM0 +- /* Each bit set in K2 represents a non-null CHAR in YMM0. */ ++ /* 32 byte align here ensures the main loop is ideally aligned ++ for DSB. */ ++ .p2align 5 ++L(more_3x_vec): ++ /* Safe to compare 4x vectors. */ ++ VMOVU (VEC_SIZE)(%rdi), %YMM0 + VPTESTM %YMM0, %YMM0, %k2 +- /* Each bit cleared in K1 represents a mismatch or a null CHAR +- in YMM0 and 32 bytes at VEC_SIZE(%rsi). */ +- VPCMP $0, VEC_SIZE(%rsi), %YMM0, %k1{%k2} ++ CMP_R1_S2_YMM (%YMM0, VEC_SIZE(%rsi), %YMM1, %k1){%k2} + kmovd %k1, %ecx +-# ifdef USE_AS_WCSCMP +- subl $0xff, %ecx +-# else +- incl %ecx ++ TESTEQ %ecx ++ jnz L(return_vec_1) ++ ++# ifdef USE_AS_STRNCMP ++ subq $(CHAR_PER_VEC * 2), %rdx ++ jbe L(ret_zero) + # endif +- jne L(return_vec_size) + + VMOVU (VEC_SIZE * 2)(%rdi), %YMM0 +- /* Each bit set in K2 represents a non-null CHAR in YMM0. */ + VPTESTM %YMM0, %YMM0, %k2 +- /* Each bit cleared in K1 represents a mismatch or a null CHAR +- in YMM0 and 32 bytes at (VEC_SIZE * 2)(%rsi). */ +- VPCMP $0, (VEC_SIZE * 2)(%rsi), %YMM0, %k1{%k2} ++ CMP_R1_S2_YMM (%YMM0, (VEC_SIZE * 2)(%rsi), %YMM1, %k1){%k2} + kmovd %k1, %ecx +-# ifdef USE_AS_WCSCMP +- subl $0xff, %ecx +-# else +- incl %ecx +-# endif +- jne L(return_2_vec_size) ++ TESTEQ %ecx ++ jnz L(return_vec_2) + + VMOVU (VEC_SIZE * 3)(%rdi), %YMM0 +- /* Each bit set in K2 represents a non-null CHAR in YMM0. */ + VPTESTM %YMM0, %YMM0, %k2 +- /* Each bit cleared in K1 represents a mismatch or a null CHAR +- in YMM0 and 32 bytes at (VEC_SIZE * 2)(%rsi). */ +- VPCMP $0, (VEC_SIZE * 3)(%rsi), %YMM0, %k1{%k2} ++ CMP_R1_S2_YMM (%YMM0, (VEC_SIZE * 3)(%rsi), %YMM1, %k1){%k2} + kmovd %k1, %ecx ++ TESTEQ %ecx ++ jnz L(return_vec_3) ++ ++# ifdef USE_AS_STRNCMP ++ cmpq $(CHAR_PER_VEC * 2), %rdx ++ jbe L(ret_zero) ++# endif ++ ++ + # ifdef USE_AS_WCSCMP +- subl $0xff, %ecx ++ /* any non-zero positive value that doesn't inference with 0x1. ++ */ ++ movl $2, %r8d ++ + # else +- incl %ecx ++ xorl %r8d, %r8d + # endif +- jne L(return_3_vec_size) +-L(main_loop_header): +- leaq (VEC_SIZE * 4)(%rdi), %rdx +- movl $PAGE_SIZE, %ecx +- /* Align load via RAX. */ +- andq $-(VEC_SIZE * 4), %rdx +- subq %rdi, %rdx +- leaq (%rdi, %rdx), %rax ++ ++ /* The prepare labels are various entry points from the page ++ cross logic. */ ++L(prepare_loop): ++ + # ifdef USE_AS_STRNCMP +- /* Starting from this point, the maximum offset, or simply the +- 'offset', DECREASES by the same amount when base pointers are +- moved forward. Return 0 when: +- 1) On match: offset <= the matched vector index. +- 2) On mistmach, offset is before the mistmatched index. +- */ +- subq %rdx, %r11 +- jbe L(zero) ++# ifdef USE_AS_WCSCMP ++L(prepare_loop_no_len): ++ movl %edi, %ecx ++ andl $(VEC_SIZE * 4 - 1), %ecx ++ shrl $2, %ecx ++ leaq (CHAR_PER_VEC * 2)(%rdx, %rcx), %rdx ++# else ++ /* Store N + (VEC_SIZE * 4) and place check at the begining of ++ the loop. */ ++ leaq (VEC_SIZE * 2)(%rdi, %rdx), %rdx ++L(prepare_loop_no_len): ++# endif ++# else ++L(prepare_loop_no_len): ++# endif ++ ++ /* Align s1 and adjust s2 accordingly. */ ++ subq %rdi, %rsi ++ andq $-(VEC_SIZE * 4), %rdi ++L(prepare_loop_readj): ++ addq %rdi, %rsi ++# if (defined USE_AS_STRNCMP) && !(defined USE_AS_WCSCMP) ++ subq %rdi, %rdx + # endif +- addq %rsi, %rdx +- movq %rdx, %rsi +- andl $(PAGE_SIZE - 1), %esi +- /* Number of bytes before page crossing. */ +- subq %rsi, %rcx +- /* Number of VEC_SIZE * 4 blocks before page crossing. */ +- shrq $DIVIDE_BY_VEC_4_SHIFT, %rcx +- /* ESI: Number of VEC_SIZE * 4 blocks before page crossing. */ +- movl %ecx, %esi +- jmp L(loop_start) + ++L(prepare_loop_aligned): ++ /* eax stores distance from rsi to next page cross. These cases ++ need to be handled specially as the 4x loop could potentially ++ read memory past the length of s1 or s2 and across a page ++ boundary. */ ++ movl $-(VEC_SIZE * 4), %eax ++ subl %esi, %eax ++ andl $(PAGE_SIZE - 1), %eax ++ ++ ++ /* Loop 4x comparisons at a time. */ + .p2align 4 + L(loop): ++ ++ /* End condition for strncmp. */ + # ifdef USE_AS_STRNCMP +- /* Base pointers are moved forward by 4 * VEC_SIZE. Decrease +- the maximum offset (%r11) by the same amount. */ +- subq $(VEC_SIZE * 4), %r11 +- jbe L(zero) +-# endif +- addq $(VEC_SIZE * 4), %rax +- addq $(VEC_SIZE * 4), %rdx +-L(loop_start): +- testl %esi, %esi +- leal -1(%esi), %esi +- je L(loop_cross_page) +-L(back_to_loop): +- /* Main loop, comparing 4 vectors are a time. */ +- VMOVA (%rax), %YMM0 +- VMOVA VEC_SIZE(%rax), %YMM2 +- VMOVA (VEC_SIZE * 2)(%rax), %YMM4 +- VMOVA (VEC_SIZE * 3)(%rax), %YMM6 ++ subq $(CHAR_PER_VEC * 4), %rdx ++ jbe L(ret_zero) ++# endif ++ ++ subq $-(VEC_SIZE * 4), %rdi ++ subq $-(VEC_SIZE * 4), %rsi ++ ++ /* Check if rsi loads will cross a page boundary. */ ++ addl $-(VEC_SIZE * 4), %eax ++ jnb L(page_cross_during_loop) ++ ++ /* Loop entry after handling page cross during loop. */ ++L(loop_skip_page_cross_check): ++ VMOVA (VEC_SIZE * 0)(%rdi), %YMM0 ++ VMOVA (VEC_SIZE * 1)(%rdi), %YMM2 ++ VMOVA (VEC_SIZE * 2)(%rdi), %YMM4 ++ VMOVA (VEC_SIZE * 3)(%rdi), %YMM6 + + VPMINU %YMM0, %YMM2, %YMM8 + VPMINU %YMM4, %YMM6, %YMM9 + +- /* A zero CHAR in YMM8 means that there is a null CHAR. */ +- VPMINU %YMM8, %YMM9, %YMM8 ++ /* A zero CHAR in YMM9 means that there is a null CHAR. */ ++ VPMINU %YMM8, %YMM9, %YMM9 + +- /* Each bit set in K1 represents a non-null CHAR in YMM8. */ +- VPTESTM %YMM8, %YMM8, %k1 ++ /* Each bit set in K1 represents a non-null CHAR in YMM9. */ ++ VPTESTM %YMM9, %YMM9, %k1 ++# ifndef USE_AS_STRCASECMP_L ++ vpxorq (VEC_SIZE * 0)(%rsi), %YMM0, %YMM1 ++ vpxorq (VEC_SIZE * 1)(%rsi), %YMM2, %YMM3 ++ vpxorq (VEC_SIZE * 2)(%rsi), %YMM4, %YMM5 ++ /* Ternary logic to xor (VEC_SIZE * 3)(%rsi) with YMM6 while ++ oring with YMM1. Result is stored in YMM6. */ ++ vpternlogd $0xde, (VEC_SIZE * 3)(%rsi), %YMM1, %YMM6 ++# else ++ VMOVU (VEC_SIZE * 0)(%rsi), %YMM1 ++ TOLOWER_YMM (%YMM0, %YMM1) ++ VMOVU (VEC_SIZE * 1)(%rsi), %YMM3 ++ TOLOWER_YMM (%YMM2, %YMM3) ++ VMOVU (VEC_SIZE * 2)(%rsi), %YMM5 ++ TOLOWER_YMM (%YMM4, %YMM5) ++ VMOVU (VEC_SIZE * 3)(%rsi), %YMM7 ++ TOLOWER_YMM (%YMM6, %YMM7) ++ vpxorq %YMM0, %YMM1, %YMM1 ++ vpxorq %YMM2, %YMM3, %YMM3 ++ vpxorq %YMM4, %YMM5, %YMM5 ++ vpternlogd $0xde, %YMM7, %YMM1, %YMM6 ++# endif ++ /* Or together YMM3, YMM5, and YMM6. */ ++ vpternlogd $0xfe, %YMM3, %YMM5, %YMM6 ++ ++ ++ /* A non-zero CHAR in YMM6 represents a mismatch. */ ++ VPTESTNM %YMM6, %YMM6, %k0{%k1} ++ kmovd %k0, %LOOP_REG ++ ++ TESTEQ %LOOP_REG ++ jz L(loop) + +- /* (YMM ^ YMM): A non-zero CHAR represents a mismatch. */ +- vpxorq (%rdx), %YMM0, %YMM1 +- vpxorq VEC_SIZE(%rdx), %YMM2, %YMM3 +- vpxorq (VEC_SIZE * 2)(%rdx), %YMM4, %YMM5 +- vpxorq (VEC_SIZE * 3)(%rdx), %YMM6, %YMM7 +- +- vporq %YMM1, %YMM3, %YMM9 +- vporq %YMM5, %YMM7, %YMM10 +- +- /* A non-zero CHAR in YMM9 represents a mismatch. */ +- vporq %YMM9, %YMM10, %YMM9 +- +- /* Each bit cleared in K0 represents a mismatch or a null CHAR. */ +- VPCMP $0, %YMMZERO, %YMM9, %k0{%k1} +- kmovd %k0, %ecx +-# ifdef USE_AS_WCSCMP +- subl $0xff, %ecx +-# else +- incl %ecx +-# endif +- je L(loop) + +- /* Each bit set in K1 represents a non-null CHAR in YMM0. */ ++ /* Find which VEC has the mismatch of end of string. */ + VPTESTM %YMM0, %YMM0, %k1 +- /* Each bit cleared in K0 represents a mismatch or a null CHAR +- in YMM0 and (%rdx). */ +- VPCMP $0, %YMMZERO, %YMM1, %k0{%k1} ++ VPTESTNM %YMM1, %YMM1, %k0{%k1} + kmovd %k0, %ecx +-# ifdef USE_AS_WCSCMP +- subl $0xff, %ecx +-# else +- incl %ecx ++ TESTEQ %ecx ++ jnz L(return_vec_0_end) ++ ++ VPTESTM %YMM2, %YMM2, %k1 ++ VPTESTNM %YMM3, %YMM3, %k0{%k1} ++ kmovd %k0, %ecx ++ TESTEQ %ecx ++ jnz L(return_vec_1_end) ++ ++ ++ /* Handle VEC 2 and 3 without branches. */ ++L(return_vec_2_3_end): ++# ifdef USE_AS_STRNCMP ++ subq $(CHAR_PER_VEC * 2), %rdx ++ jbe L(ret_zero_end) + # endif +- je L(test_vec) +- tzcntl %ecx, %ecx +-# ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %ecx ++ ++ VPTESTM %YMM4, %YMM4, %k1 ++ VPTESTNM %YMM5, %YMM5, %k0{%k1} ++ kmovd %k0, %ecx ++ TESTEQ %ecx ++# if CHAR_PER_VEC <= 16 ++ sall $CHAR_PER_VEC, %LOOP_REG ++ orl %ecx, %LOOP_REG ++# else ++ salq $CHAR_PER_VEC, %LOOP_REG64 ++ orq %rcx, %LOOP_REG64 + # endif +-# ifdef USE_AS_STRNCMP +- cmpq %rcx, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax +-# endif ++L(return_vec_3_end): ++ /* LOOP_REG contains matches for null/mismatch from the loop. If ++ VEC 0,1,and 2 all have no null and no mismatches then mismatch ++ must entirely be from VEC 3 which is fully represented by ++ LOOP_REG. */ ++# if CHAR_PER_VEC <= 16 ++ tzcntl %LOOP_REG, %LOOP_REG + # else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax +-# endif ++ tzcntq %LOOP_REG64, %LOOP_REG64 + # endif +- ret +- +- .p2align 4 +-L(test_vec): + # ifdef USE_AS_STRNCMP +- /* The first vector matched. Return 0 if the maximum offset +- (%r11) <= VEC_SIZE. */ +- cmpq $VEC_SIZE, %r11 +- jbe L(zero) ++ cmpq %LOOP_REG64, %rdx ++ jbe L(ret_zero_end) + # endif +- /* Each bit set in K1 represents a non-null CHAR in YMM2. */ +- VPTESTM %YMM2, %YMM2, %k1 +- /* Each bit cleared in K0 represents a mismatch or a null CHAR +- in YMM2 and VEC_SIZE(%rdx). */ +- VPCMP $0, %YMMZERO, %YMM3, %k0{%k1} +- kmovd %k0, %ecx ++ + # ifdef USE_AS_WCSCMP +- subl $0xff, %ecx ++ movl (VEC_SIZE * 2)(%rdi, %LOOP_REG64, SIZE_OF_CHAR), %ecx ++ xorl %eax, %eax ++ cmpl (VEC_SIZE * 2)(%rsi, %LOOP_REG64, SIZE_OF_CHAR), %ecx ++ je L(ret5) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax + # else +- incl %ecx +-# endif +- je L(test_2_vec) +- tzcntl %ecx, %edi +-# ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %edi ++ movzbl (VEC_SIZE * 2)(%rdi, %LOOP_REG64), %eax ++ movzbl (VEC_SIZE * 2)(%rsi, %LOOP_REG64), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif ++L(ret5): ++ ret ++ + # ifdef USE_AS_STRNCMP +- addq $VEC_SIZE, %rdi +- cmpq %rdi, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ .p2align 4,, 2 ++L(ret_zero_end): + xorl %eax, %eax +- movl (%rsi, %rdi), %ecx +- cmpl (%rdx, %rdi), %ecx +- jne L(wcscmp_return) ++ ret ++# endif ++ ++ ++ /* The L(return_vec_N_end) differ from L(return_vec_N) in that ++ they use the value of `r8` to negate the return value. This is ++ because the page cross logic can swap `rdi` and `rsi`. */ ++ .p2align 4,, 10 ++# ifdef USE_AS_STRNCMP ++L(return_vec_1_end): ++# if CHAR_PER_VEC <= 16 ++ sall $CHAR_PER_VEC, %ecx + # else +- movzbl (%rax, %rdi), %eax +- movzbl (%rdx, %rdi), %edx +- subl %edx, %eax ++ salq $CHAR_PER_VEC, %rcx + # endif ++# endif ++L(return_vec_0_end): ++# if (CHAR_PER_VEC <= 16) || !(defined USE_AS_STRNCMP) ++ tzcntl %ecx, %ecx + # else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl VEC_SIZE(%rsi, %rdi), %ecx +- cmpl VEC_SIZE(%rdx, %rdi), %ecx +- jne L(wcscmp_return) +-# else +- movzbl VEC_SIZE(%rax, %rdi), %eax +- movzbl VEC_SIZE(%rdx, %rdi), %edx +- subl %edx, %eax +-# endif ++ tzcntq %rcx, %rcx + # endif +- ret + +- .p2align 4 +-L(test_2_vec): + # ifdef USE_AS_STRNCMP +- /* The first 2 vectors matched. Return 0 if the maximum offset +- (%r11) <= 2 * VEC_SIZE. */ +- cmpq $(VEC_SIZE * 2), %r11 +- jbe L(zero) +-# endif +- /* Each bit set in K1 represents a non-null CHAR in YMM4. */ +- VPTESTM %YMM4, %YMM4, %k1 +- /* Each bit cleared in K0 represents a mismatch or a null CHAR +- in YMM4 and (VEC_SIZE * 2)(%rdx). */ +- VPCMP $0, %YMMZERO, %YMM5, %k0{%k1} +- kmovd %k0, %ecx +-# ifdef USE_AS_WCSCMP +- subl $0xff, %ecx +-# else +- incl %ecx ++ cmpq %rcx, %rdx ++ jbe L(ret_zero_end) + # endif +- je L(test_3_vec) +- tzcntl %ecx, %edi ++ + # ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %edi +-# endif +-# ifdef USE_AS_STRNCMP +- addq $(VEC_SIZE * 2), %rdi +- cmpq %rdi, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ movl (%rdi, %rcx, SIZE_OF_CHAR), %edx + xorl %eax, %eax +- movl (%rsi, %rdi), %ecx +- cmpl (%rdx, %rdi), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rdi), %eax +- movzbl (%rdx, %rdi), %edx +- subl %edx, %eax +-# endif +-# else ++ cmpl (%rsi, %rcx, SIZE_OF_CHAR), %edx ++ je L(ret6) ++ setl %al ++ negl %eax ++ /* This is the non-zero case for `eax` so just xorl with `r8d` ++ flip is `rdi` and `rsi` where swapped. */ ++ xorl %r8d, %eax ++# else ++ movzbl (%rdi, %rcx), %eax ++ movzbl (%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ /* Flip `eax` if `rdi` and `rsi` where swapped in page cross ++ logic. Subtract `r8d` after xor for zero case. */ ++ xorl %r8d, %eax ++ subl %r8d, %eax ++# endif ++L(ret6): ++ ret ++ ++# ifndef USE_AS_STRNCMP ++ .p2align 4,, 10 ++L(return_vec_1_end): ++ tzcntl %ecx, %ecx + # ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ movl VEC_SIZE(%rdi, %rcx, SIZE_OF_CHAR), %edx + xorl %eax, %eax +- movl (VEC_SIZE * 2)(%rsi, %rdi), %ecx +- cmpl (VEC_SIZE * 2)(%rdx, %rdi), %ecx +- jne L(wcscmp_return) ++ cmpl VEC_SIZE(%rsi, %rcx, SIZE_OF_CHAR), %edx ++ je L(ret7) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax + # else +- movzbl (VEC_SIZE * 2)(%rax, %rdi), %eax +- movzbl (VEC_SIZE * 2)(%rdx, %rdi), %edx +- subl %edx, %eax ++ movzbl VEC_SIZE(%rdi, %rcx), %eax ++ movzbl VEC_SIZE(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif +-# endif ++L(ret7): + ret ++# endif ++ ++ ++ /* Page cross in rsi in next 4x VEC. */ ++ ++ /* TODO: Improve logic here. */ ++ .p2align 4,, 10 ++L(page_cross_during_loop): ++ /* eax contains [distance_from_page - (VEC_SIZE * 4)]. */ ++ ++ /* Optimistically rsi and rdi and both aligned in which case we ++ don't need any logic here. */ ++ cmpl $-(VEC_SIZE * 4), %eax ++ /* Don't adjust eax before jumping back to loop and we will ++ never hit page cross case again. */ ++ je L(loop_skip_page_cross_check) ++ ++ /* Check if we can safely load a VEC. */ ++ cmpl $-(VEC_SIZE * 3), %eax ++ jle L(less_1x_vec_till_page_cross) ++ ++ VMOVA (%rdi), %YMM0 ++ VPTESTM %YMM0, %YMM0, %k2 ++ CMP_R1_S2_YMM (%YMM0, (%rsi), %YMM1, %k1){%k2} ++ kmovd %k1, %ecx ++ TESTEQ %ecx ++ jnz L(return_vec_0_end) ++ ++ /* if distance >= 2x VEC then eax > -(VEC_SIZE * 2). */ ++ cmpl $-(VEC_SIZE * 2), %eax ++ jg L(more_2x_vec_till_page_cross) ++ ++ .p2align 4,, 4 ++L(less_1x_vec_till_page_cross): ++ subl $-(VEC_SIZE * 4), %eax ++ /* Guranteed safe to read from rdi - VEC_SIZE here. The only ++ concerning case is first iteration if incoming s1 was near start ++ of a page and s2 near end. If s1 was near the start of the page ++ we already aligned up to nearest VEC_SIZE * 4 so gurnateed safe ++ to read back -VEC_SIZE. If rdi is truly at the start of a page ++ here, it means the previous page (rdi - VEC_SIZE) has already ++ been loaded earlier so must be valid. */ ++ VMOVU -VEC_SIZE(%rdi, %rax), %YMM0 ++ VPTESTM %YMM0, %YMM0, %k2 ++ CMP_R1_S2_YMM (%YMM0, -VEC_SIZE(%rsi, %rax), %YMM1, %k1){%k2} ++ /* Mask of potentially valid bits. The lower bits can be out of ++ range comparisons (but safe regarding page crosses). */ + +- .p2align 4 +-L(test_3_vec): +-# ifdef USE_AS_STRNCMP +- /* The first 3 vectors matched. Return 0 if the maximum offset +- (%r11) <= 3 * VEC_SIZE. */ +- cmpq $(VEC_SIZE * 3), %r11 +- jbe L(zero) +-# endif +- /* Each bit set in K1 represents a non-null CHAR in YMM6. */ +- VPTESTM %YMM6, %YMM6, %k1 +- /* Each bit cleared in K0 represents a mismatch or a null CHAR +- in YMM6 and (VEC_SIZE * 3)(%rdx). */ +- VPCMP $0, %YMMZERO, %YMM7, %k0{%k1} +- kmovd %k0, %ecx + # ifdef USE_AS_WCSCMP +- subl $0xff, %ecx ++ movl $-1, %r10d ++ movl %esi, %ecx ++ andl $(VEC_SIZE - 1), %ecx ++ shrl $2, %ecx ++ shlxl %ecx, %r10d, %ecx ++ movzbl %cl, %r10d + # else +- incl %ecx +-# endif +- tzcntl %ecx, %ecx +-# ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %ecx ++ movl $-1, %ecx ++ shlxl %esi, %ecx, %r10d + # endif ++ ++ kmovd %k1, %ecx ++ notl %ecx ++ ++ + # ifdef USE_AS_STRNCMP +- addq $(VEC_SIZE * 3), %rcx +- cmpq %rcx, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (%rsi, %rcx), %esi +- cmpl (%rdx, %rcx), %esi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax +-# endif +-# else + # ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (VEC_SIZE * 3)(%rsi, %rcx), %esi +- cmpl (VEC_SIZE * 3)(%rdx, %rcx), %esi +- jne L(wcscmp_return) ++ /* NB: strcasecmp not used with WCSCMP so this access to r11 is ++ safe. */ ++ movl %eax, %r11d ++ shrl $2, %r11d ++ cmpq %r11, %rdx + # else +- movzbl (VEC_SIZE * 3)(%rax, %rcx), %eax +- movzbl (VEC_SIZE * 3)(%rdx, %rcx), %edx +- subl %edx, %eax ++ cmpq %rax, %rdx + # endif ++ jbe L(return_page_cross_end_check) + # endif +- ret ++ movl %eax, %OFFSET_REG + +- .p2align 4 +-L(loop_cross_page): +- xorl %r10d, %r10d +- movq %rdx, %rcx +- /* Align load via RDX. We load the extra ECX bytes which should +- be ignored. */ +- andl $((VEC_SIZE * 4) - 1), %ecx +- /* R10 is -RCX. */ +- subq %rcx, %r10 +- +- /* This works only if VEC_SIZE * 2 == 64. */ +-# if (VEC_SIZE * 2) != 64 +-# error (VEC_SIZE * 2) != 64 +-# endif +- +- /* Check if the first VEC_SIZE * 2 bytes should be ignored. */ +- cmpl $(VEC_SIZE * 2), %ecx +- jge L(loop_cross_page_2_vec) ++ /* Readjust eax before potentially returning to the loop. */ ++ addl $(PAGE_SIZE - VEC_SIZE * 4), %eax + +- VMOVU (%rax, %r10), %YMM2 +- VMOVU VEC_SIZE(%rax, %r10), %YMM3 ++ andl %r10d, %ecx ++ jz L(loop_skip_page_cross_check) + +- /* Each bit set in K2 represents a non-null CHAR in YMM2. */ +- VPTESTM %YMM2, %YMM2, %k2 +- /* Each bit cleared in K1 represents a mismatch or a null CHAR +- in YMM2 and 32 bytes at (%rdx, %r10). */ +- VPCMP $0, (%rdx, %r10), %YMM2, %k1{%k2} +- kmovd %k1, %r9d +- /* Don't use subl since it is the lower 16/32 bits of RDI +- below. */ +- notl %r9d +-# ifdef USE_AS_WCSCMP +- /* Only last 8 bits are valid. */ +- andl $0xff, %r9d +-# endif ++ .p2align 4,, 3 ++L(return_page_cross_end): ++ tzcntl %ecx, %ecx + +- /* Each bit set in K4 represents a non-null CHAR in YMM3. */ +- VPTESTM %YMM3, %YMM3, %k4 +- /* Each bit cleared in K3 represents a mismatch or a null CHAR +- in YMM3 and 32 bytes at VEC_SIZE(%rdx, %r10). */ +- VPCMP $0, VEC_SIZE(%rdx, %r10), %YMM3, %k3{%k4} +- kmovd %k3, %edi +- /* Must use notl %edi here as lower bits are for CHAR +- comparisons potentially out of range thus can be 0 without +- indicating mismatch. */ +- notl %edi +-# ifdef USE_AS_WCSCMP +- /* Don't use subl since it is the upper 8 bits of EDI below. */ +- andl $0xff, %edi ++# if (defined USE_AS_STRNCMP) || (defined USE_AS_WCSCMP) ++ leal -VEC_SIZE(%OFFSET_REG64, %rcx, SIZE_OF_CHAR), %ecx ++L(return_page_cross_cmp_mem): ++# else ++ addl %OFFSET_REG, %ecx + # endif +- +-# ifdef USE_AS_WCSCMP +- /* NB: Each bit in EDI/R9D represents 4-byte element. */ +- sall $8, %edi +- /* NB: Divide shift count by 4 since each bit in K1 represent 4 +- bytes. */ +- movl %ecx, %SHIFT_REG32 +- sarl $2, %SHIFT_REG32 +- +- /* Each bit in EDI represents a null CHAR or a mismatch. */ +- orl %r9d, %edi +-# else +- salq $32, %rdi +- +- /* Each bit in RDI represents a null CHAR or a mismatch. */ +- orq %r9, %rdi +-# endif +- +- /* Since ECX < VEC_SIZE * 2, simply skip the first ECX bytes. */ +- shrxq %SHIFT_REG64, %rdi, %rdi +- testq %rdi, %rdi +- je L(loop_cross_page_2_vec) +- tzcntq %rdi, %rcx + # ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %ecx ++ movl VEC_OFFSET(%rdi, %rcx), %edx ++ xorl %eax, %eax ++ cmpl VEC_OFFSET(%rsi, %rcx), %edx ++ je L(ret8) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax ++# else ++ movzbl VEC_OFFSET(%rdi, %rcx), %eax ++ movzbl VEC_OFFSET(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif ++L(ret8): ++ ret ++ + # ifdef USE_AS_STRNCMP +- cmpq %rcx, %r11 +- jbe L(zero) ++ .p2align 4,, 10 ++L(return_page_cross_end_check): ++ andl %r10d, %ecx ++ tzcntl %ecx, %ecx ++ leal -VEC_SIZE(%rax, %rcx, SIZE_OF_CHAR), %ecx + # ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax ++ sall $2, %edx + # endif +-# else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ cmpl %ecx, %edx ++ ja L(return_page_cross_cmp_mem) + xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax +-# endif +-# endif + ret ++# endif + +- .p2align 4 +-L(loop_cross_page_2_vec): +- /* The first VEC_SIZE * 2 bytes match or are ignored. */ +- VMOVU (VEC_SIZE * 2)(%rax, %r10), %YMM0 +- VMOVU (VEC_SIZE * 3)(%rax, %r10), %YMM1 + ++ .p2align 4,, 10 ++L(more_2x_vec_till_page_cross): ++ /* If more 2x vec till cross we will complete a full loop ++ iteration here. */ ++ ++ VMOVA VEC_SIZE(%rdi), %YMM0 + VPTESTM %YMM0, %YMM0, %k2 +- /* Each bit cleared in K1 represents a mismatch or a null CHAR +- in YMM0 and 32 bytes at (VEC_SIZE * 2)(%rdx, %r10). */ +- VPCMP $0, (VEC_SIZE * 2)(%rdx, %r10), %YMM0, %k1{%k2} +- kmovd %k1, %r9d +- /* Don't use subl since it is the lower 16/32 bits of RDI +- below. */ +- notl %r9d +-# ifdef USE_AS_WCSCMP +- /* Only last 8 bits are valid. */ +- andl $0xff, %r9d +-# endif ++ CMP_R1_S2_YMM (%YMM0, VEC_SIZE(%rsi), %YMM1, %k1){%k2} ++ kmovd %k1, %ecx ++ TESTEQ %ecx ++ jnz L(return_vec_1_end) + +- VPTESTM %YMM1, %YMM1, %k4 +- /* Each bit cleared in K3 represents a mismatch or a null CHAR +- in YMM1 and 32 bytes at (VEC_SIZE * 3)(%rdx, %r10). */ +- VPCMP $0, (VEC_SIZE * 3)(%rdx, %r10), %YMM1, %k3{%k4} +- kmovd %k3, %edi +- /* Must use notl %edi here as lower bits are for CHAR +- comparisons potentially out of range thus can be 0 without +- indicating mismatch. */ +- notl %edi +-# ifdef USE_AS_WCSCMP +- /* Don't use subl since it is the upper 8 bits of EDI below. */ +- andl $0xff, %edi ++# ifdef USE_AS_STRNCMP ++ cmpq $(CHAR_PER_VEC * 2), %rdx ++ jbe L(ret_zero_in_loop_page_cross) + # endif + +-# ifdef USE_AS_WCSCMP +- /* NB: Each bit in EDI/R9D represents 4-byte element. */ +- sall $8, %edi ++ subl $-(VEC_SIZE * 4), %eax + +- /* Each bit in EDI represents a null CHAR or a mismatch. */ +- orl %r9d, %edi +-# else +- salq $32, %rdi ++ /* Safe to include comparisons from lower bytes. */ ++ VMOVU -(VEC_SIZE * 2)(%rdi, %rax), %YMM0 ++ VPTESTM %YMM0, %YMM0, %k2 ++ CMP_R1_S2_YMM (%YMM0, -(VEC_SIZE * 2)(%rsi, %rax), %YMM1, %k1){%k2} ++ kmovd %k1, %ecx ++ TESTEQ %ecx ++ jnz L(return_vec_page_cross_0) + +- /* Each bit in RDI represents a null CHAR or a mismatch. */ +- orq %r9, %rdi ++ VMOVU -(VEC_SIZE * 1)(%rdi, %rax), %YMM0 ++ VPTESTM %YMM0, %YMM0, %k2 ++ CMP_R1_S2_YMM (%YMM0, -(VEC_SIZE * 1)(%rsi, %rax), %YMM1, %k1){%k2} ++ kmovd %k1, %ecx ++ TESTEQ %ecx ++ jnz L(return_vec_page_cross_1) ++ ++# ifdef USE_AS_STRNCMP ++ /* Must check length here as length might proclude reading next ++ page. */ ++# ifdef USE_AS_WCSCMP ++ /* NB: strcasecmp not used with WCSCMP so this access to r11 is ++ safe. */ ++ movl %eax, %r11d ++ shrl $2, %r11d ++ cmpq %r11, %rdx ++# else ++ cmpq %rax, %rdx ++# endif ++ jbe L(ret_zero_in_loop_page_cross) + # endif + +- xorl %r8d, %r8d +- /* If ECX > VEC_SIZE * 2, skip ECX - (VEC_SIZE * 2) bytes. */ +- subl $(VEC_SIZE * 2), %ecx +- jle 1f +- /* R8 has number of bytes skipped. */ +- movl %ecx, %r8d +-# ifdef USE_AS_WCSCMP +- /* NB: Divide shift count by 4 since each bit in RDI represent 4 +- bytes. */ +- sarl $2, %ecx +- /* Skip ECX bytes. */ +- shrl %cl, %edi +-# else +- /* Skip ECX bytes. */ +- shrq %cl, %rdi +-# endif +-1: +- /* Before jumping back to the loop, set ESI to the number of +- VEC_SIZE * 4 blocks before page crossing. */ +- movl $(PAGE_SIZE / (VEC_SIZE * 4) - 1), %esi +- +- testq %rdi, %rdi +-# ifdef USE_AS_STRNCMP +- /* At this point, if %rdi value is 0, it already tested +- VEC_SIZE*4+%r10 byte starting from %rax. This label +- checks whether strncmp maximum offset reached or not. */ +- je L(string_nbyte_offset_check) ++ /* Finish the loop. */ ++ VMOVA (VEC_SIZE * 2)(%rdi), %YMM4 ++ VMOVA (VEC_SIZE * 3)(%rdi), %YMM6 ++ VPMINU %YMM4, %YMM6, %YMM9 ++ VPTESTM %YMM9, %YMM9, %k1 ++# ifndef USE_AS_STRCASECMP_L ++ vpxorq (VEC_SIZE * 2)(%rsi), %YMM4, %YMM5 ++ /* YMM6 = YMM5 | ((VEC_SIZE * 3)(%rsi) ^ YMM6). */ ++ vpternlogd $0xde, (VEC_SIZE * 3)(%rsi), %YMM5, %YMM6 ++# else ++ VMOVU (VEC_SIZE * 2)(%rsi), %YMM5 ++ TOLOWER_YMM (%YMM4, %YMM5) ++ VMOVU (VEC_SIZE * 3)(%rsi), %YMM7 ++ TOLOWER_YMM (%YMM6, %YMM7) ++ vpxorq %YMM4, %YMM5, %YMM5 ++ vpternlogd $0xde, %YMM7, %YMM5, %YMM6 ++# endif ++ VPTESTNM %YMM6, %YMM6, %k0{%k1} ++ kmovd %k0, %LOOP_REG ++ TESTEQ %LOOP_REG ++ jnz L(return_vec_2_3_end) ++ ++ /* Best for code size to include ucond-jmp here. Would be faster ++ if this case is hot to duplicate the L(return_vec_2_3_end) code ++ as fall-through and have jump back to loop on mismatch ++ comparison. */ ++ subq $-(VEC_SIZE * 4), %rdi ++ subq $-(VEC_SIZE * 4), %rsi ++ addl $(PAGE_SIZE - VEC_SIZE * 8), %eax ++# ifdef USE_AS_STRNCMP ++ subq $(CHAR_PER_VEC * 4), %rdx ++ ja L(loop_skip_page_cross_check) ++L(ret_zero_in_loop_page_cross): ++ xorl %eax, %eax ++ ret + # else +- je L(back_to_loop) +-# endif +- tzcntq %rdi, %rcx +-# ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %ecx ++ jmp L(loop_skip_page_cross_check) + # endif +- addq %r10, %rcx +- /* Adjust for number of bytes skipped. */ +- addq %r8, %rcx +-# ifdef USE_AS_STRNCMP +- addq $(VEC_SIZE * 2), %rcx +- subq %rcx, %r11 +- jbe L(zero) +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi +- xorl %eax, %eax +- movl (%rsi, %rcx), %edi +- cmpl (%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (%rax, %rcx), %eax +- movzbl (%rdx, %rcx), %edx +- subl %edx, %eax ++ ++ ++ .p2align 4,, 10 ++L(return_vec_page_cross_0): ++ addl $-VEC_SIZE, %eax ++L(return_vec_page_cross_1): ++ tzcntl %ecx, %ecx ++# if defined USE_AS_STRNCMP || defined USE_AS_WCSCMP ++ leal -VEC_SIZE(%rax, %rcx, SIZE_OF_CHAR), %ecx ++# ifdef USE_AS_STRNCMP ++# ifdef USE_AS_WCSCMP ++ /* Must divide ecx instead of multiply rdx due to overflow. */ ++ movl %ecx, %eax ++ shrl $2, %eax ++ cmpq %rax, %rdx ++# else ++ cmpq %rcx, %rdx ++# endif ++ jbe L(ret_zero_in_loop_page_cross) + # endif + # else +-# ifdef USE_AS_WCSCMP +- movq %rax, %rsi ++ addl %eax, %ecx ++# endif ++ ++# ifdef USE_AS_WCSCMP ++ movl VEC_OFFSET(%rdi, %rcx), %edx + xorl %eax, %eax +- movl (VEC_SIZE * 2)(%rsi, %rcx), %edi +- cmpl (VEC_SIZE * 2)(%rdx, %rcx), %edi +- jne L(wcscmp_return) +-# else +- movzbl (VEC_SIZE * 2)(%rax, %rcx), %eax +- movzbl (VEC_SIZE * 2)(%rdx, %rcx), %edx +- subl %edx, %eax +-# endif ++ cmpl VEC_OFFSET(%rsi, %rcx), %edx ++ je L(ret9) ++ setl %al ++ negl %eax ++ xorl %r8d, %eax ++# else ++ movzbl VEC_OFFSET(%rdi, %rcx), %eax ++ movzbl VEC_OFFSET(%rsi, %rcx), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif ++L(ret9): + ret + +-# ifdef USE_AS_STRNCMP +-L(string_nbyte_offset_check): +- leaq (VEC_SIZE * 4)(%r10), %r10 +- cmpq %r10, %r11 +- jbe L(zero) +- jmp L(back_to_loop) ++ ++ .p2align 4,, 10 ++L(page_cross): ++# ifndef USE_AS_STRNCMP ++ /* If both are VEC aligned we don't need any special logic here. ++ Only valid for strcmp where stop condition is guranteed to be ++ reachable by just reading memory. */ ++ testl $((VEC_SIZE - 1) << 20), %eax ++ jz L(no_page_cross) + # endif + +- .p2align 4 +-L(cross_page_loop): +- /* Check one byte/dword at a time. */ ++ movl %edi, %eax ++ movl %esi, %ecx ++ andl $(PAGE_SIZE - 1), %eax ++ andl $(PAGE_SIZE - 1), %ecx ++ ++ xorl %OFFSET_REG, %OFFSET_REG ++ ++ /* Check which is closer to page cross, s1 or s2. */ ++ cmpl %eax, %ecx ++ jg L(page_cross_s2) ++ ++ /* The previous page cross check has false positives. Check for ++ true positive as page cross logic is very expensive. */ ++ subl $(PAGE_SIZE - VEC_SIZE * 4), %eax ++ jbe L(no_page_cross) ++ ++ ++ /* Set r8 to not interfere with normal return value (rdi and rsi ++ did not swap). */ + # ifdef USE_AS_WCSCMP +- cmpl %ecx, %eax ++ /* any non-zero positive value that doesn't inference with 0x1. ++ */ ++ movl $2, %r8d + # else +- subl %ecx, %eax ++ xorl %r8d, %r8d + # endif +- jne L(different) +- addl $SIZE_OF_CHAR, %edx +- cmpl $(VEC_SIZE * 4), %edx +- je L(main_loop_header) ++ ++ /* Check if less than 1x VEC till page cross. */ ++ subl $(VEC_SIZE * 3), %eax ++ jg L(less_1x_vec_till_page) ++ ++ ++ /* If more than 1x VEC till page cross, loop throuh safely ++ loadable memory until within 1x VEC of page cross. */ ++ .p2align 4,, 8 ++L(page_cross_loop): ++ VMOVU (%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %YMM0 ++ VPTESTM %YMM0, %YMM0, %k2 ++ CMP_R1_S2_YMM (%YMM0, (%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %YMM1, %k1){%k2} ++ kmovd %k1, %ecx ++ TESTEQ %ecx ++ jnz L(check_ret_vec_page_cross) ++ addl $CHAR_PER_VEC, %OFFSET_REG + # ifdef USE_AS_STRNCMP +- cmpq %r11, %rdx +- jae L(zero) ++ cmpq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross) + # endif ++ addl $VEC_SIZE, %eax ++ jl L(page_cross_loop) ++ + # ifdef USE_AS_WCSCMP +- movl (%rdi, %rdx), %eax +- movl (%rsi, %rdx), %ecx +-# else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %ecx ++ shrl $2, %eax + # endif +- /* Check null CHAR. */ +- testl %eax, %eax +- jne L(cross_page_loop) +- /* Since %eax == 0, subtract is OK for both SIGNED and UNSIGNED +- comparisons. */ +- subl %ecx, %eax +-# ifndef USE_AS_WCSCMP +-L(different): ++ ++ ++ subl %eax, %OFFSET_REG ++ /* OFFSET_REG has distance to page cross - VEC_SIZE. Guranteed ++ to not cross page so is safe to load. Since we have already ++ loaded at least 1 VEC from rsi it is also guranteed to be safe. ++ */ ++ VMOVU (%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %YMM0 ++ VPTESTM %YMM0, %YMM0, %k2 ++ CMP_R1_S2_YMM (%YMM0, (%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %YMM1, %k1){%k2} ++ ++ kmovd %k1, %ecx ++# ifdef USE_AS_STRNCMP ++ leal CHAR_PER_VEC(%OFFSET_REG64), %eax ++ cmpq %rax, %rdx ++ jbe L(check_ret_vec_page_cross2) ++# ifdef USE_AS_WCSCMP ++ addq $-(CHAR_PER_VEC * 2), %rdx ++# else ++ addq %rdi, %rdx ++# endif + # endif +- ret ++ TESTEQ %ecx ++ jz L(prepare_loop_no_len) + ++ .p2align 4,, 4 ++L(ret_vec_page_cross): ++# ifndef USE_AS_STRNCMP ++L(check_ret_vec_page_cross): ++# endif ++ tzcntl %ecx, %ecx ++ addl %OFFSET_REG, %ecx ++L(ret_vec_page_cross_cont): + # ifdef USE_AS_WCSCMP +- .p2align 4 +-L(different): +- /* Use movl to avoid modifying EFLAGS. */ +- movl $0, %eax ++ movl (%rdi, %rcx, SIZE_OF_CHAR), %edx ++ xorl %eax, %eax ++ cmpl (%rsi, %rcx, SIZE_OF_CHAR), %edx ++ je L(ret12) + setl %al + negl %eax +- orl $1, %eax +- ret ++ xorl %r8d, %eax ++# else ++ movzbl (%rdi, %rcx, SIZE_OF_CHAR), %eax ++ movzbl (%rsi, %rcx, SIZE_OF_CHAR), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %ecx) ++ subl %ecx, %eax ++ xorl %r8d, %eax ++ subl %r8d, %eax + # endif ++L(ret12): ++ ret ++ + + # ifdef USE_AS_STRNCMP +- .p2align 4 +-L(zero): ++ .p2align 4,, 10 ++L(check_ret_vec_page_cross2): ++ TESTEQ %ecx ++L(check_ret_vec_page_cross): ++ tzcntl %ecx, %ecx ++ addl %OFFSET_REG, %ecx ++ cmpq %rcx, %rdx ++ ja L(ret_vec_page_cross_cont) ++ .p2align 4,, 2 ++L(ret_zero_page_cross): + xorl %eax, %eax + ret ++# endif + +- .p2align 4 +-L(char0): +-# ifdef USE_AS_WCSCMP +- xorl %eax, %eax +- movl (%rdi), %ecx +- cmpl (%rsi), %ecx +- jne L(wcscmp_return) +-# else +- movzbl (%rsi), %ecx +- movzbl (%rdi), %eax +- subl %ecx, %eax +-# endif +- ret ++ .p2align 4,, 4 ++L(page_cross_s2): ++ /* Ensure this is a true page cross. */ ++ subl $(PAGE_SIZE - VEC_SIZE * 4), %ecx ++ jbe L(no_page_cross) ++ ++ ++ movl %ecx, %eax ++ movq %rdi, %rcx ++ movq %rsi, %rdi ++ movq %rcx, %rsi ++ ++ /* set r8 to negate return value as rdi and rsi swapped. */ ++# ifdef USE_AS_WCSCMP ++ movl $-4, %r8d ++# else ++ movl $-1, %r8d + # endif ++ xorl %OFFSET_REG, %OFFSET_REG + +- .p2align 4 +-L(last_vector): +- addq %rdx, %rdi +- addq %rdx, %rsi +-# ifdef USE_AS_STRNCMP +- subq %rdx, %r11 ++ /* Check if more than 1x VEC till page cross. */ ++ subl $(VEC_SIZE * 3), %eax ++ jle L(page_cross_loop) ++ ++ .p2align 4,, 6 ++L(less_1x_vec_till_page): ++# ifdef USE_AS_WCSCMP ++ shrl $2, %eax + # endif +- tzcntl %ecx, %edx ++ /* Find largest load size we can use. */ ++ cmpl $(16 / SIZE_OF_CHAR), %eax ++ ja L(less_16_till_page) ++ ++ /* Use 16 byte comparison. */ ++ vmovdqu (%rdi), %xmm0 ++ VPTESTM %xmm0, %xmm0, %k2 ++ CMP_R1_S2_XMM (%xmm0, (%rsi), %xmm1, %k1){%k2} ++ kmovd %k1, %ecx + # ifdef USE_AS_WCSCMP +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- sall $2, %edx ++ subl $0xf, %ecx ++# else ++ incw %cx + # endif ++ jnz L(check_ret_vec_page_cross) ++ movl $(16 / SIZE_OF_CHAR), %OFFSET_REG + # ifdef USE_AS_STRNCMP +- cmpq %r11, %rdx +- jae L(zero) +-# endif ++ cmpq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross_slow_case0) ++ subl %eax, %OFFSET_REG ++# else ++ /* Explicit check for 16 byte alignment. */ ++ subl %eax, %OFFSET_REG ++ jz L(prepare_loop) ++# endif ++ vmovdqu (%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %xmm0 ++ VPTESTM %xmm0, %xmm0, %k2 ++ CMP_R1_S2_XMM (%xmm0, (%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %xmm1, %k1){%k2} ++ kmovd %k1, %ecx + # ifdef USE_AS_WCSCMP +- xorl %eax, %eax +- movl (%rdi, %rdx), %ecx +- cmpl (%rsi, %rdx), %ecx +- jne L(wcscmp_return) ++ subl $0xf, %ecx ++# else ++ incw %cx ++# endif ++ jnz L(check_ret_vec_page_cross) ++# ifdef USE_AS_STRNCMP ++ addl $(16 / SIZE_OF_CHAR), %OFFSET_REG ++ subq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross_slow_case0) ++ subq $-(CHAR_PER_VEC * 4), %rdx ++ ++ leaq -(VEC_SIZE * 4)(%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %rdi ++ leaq -(VEC_SIZE * 4)(%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %rsi + # else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %edx +- subl %edx, %eax ++ leaq (16 - VEC_SIZE * 4)(%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %rdi ++ leaq (16 - VEC_SIZE * 4)(%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %rsi + # endif ++ jmp L(prepare_loop_aligned) ++ ++# ifdef USE_AS_STRNCMP ++ .p2align 4,, 2 ++L(ret_zero_page_cross_slow_case0): ++ xorl %eax, %eax + ret ++# endif + +- /* Comparing on page boundary region requires special treatment: +- It must done one vector at the time, starting with the wider +- ymm vector if possible, if not, with xmm. If fetching 16 bytes +- (xmm) still passes the boundary, byte comparison must be done. +- */ +- .p2align 4 +-L(cross_page): +- /* Try one ymm vector at a time. */ +- cmpl $(PAGE_SIZE - VEC_SIZE), %eax +- jg L(cross_page_1_vector) +-L(loop_1_vector): +- VMOVU (%rdi, %rdx), %YMM0 + +- VPTESTM %YMM0, %YMM0, %k2 +- /* Each bit cleared in K1 represents a mismatch or a null CHAR +- in YMM0 and 32 bytes at (%rsi, %rdx). */ +- VPCMP $0, (%rsi, %rdx), %YMM0, %k1{%k2} ++ .p2align 4,, 10 ++L(less_16_till_page): ++ cmpl $(24 / SIZE_OF_CHAR), %eax ++ ja L(less_8_till_page) ++ ++ /* Use 8 byte comparison. */ ++ vmovq (%rdi), %xmm0 ++ vmovq (%rsi), %xmm1 ++ VPTESTM %xmm0, %xmm0, %k2 ++ CMP_R1_R2_XMM (%xmm0, %xmm1, %k1){%k2} + kmovd %k1, %ecx + # ifdef USE_AS_WCSCMP +- subl $0xff, %ecx ++ subl $0x3, %ecx + # else +- incl %ecx ++ incb %cl + # endif +- jne L(last_vector) ++ jnz L(check_ret_vec_page_cross) + +- addl $VEC_SIZE, %edx + +- addl $VEC_SIZE, %eax + # ifdef USE_AS_STRNCMP +- /* Return 0 if the current offset (%rdx) >= the maximum offset +- (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) ++ cmpq $(8 / SIZE_OF_CHAR), %rdx ++ jbe L(ret_zero_page_cross_slow_case0) + # endif +- cmpl $(PAGE_SIZE - VEC_SIZE), %eax +- jle L(loop_1_vector) +-L(cross_page_1_vector): +- /* Less than 32 bytes to check, try one xmm vector. */ +- cmpl $(PAGE_SIZE - 16), %eax +- jg L(cross_page_1_xmm) +- VMOVU (%rdi, %rdx), %XMM0 ++ movl $(24 / SIZE_OF_CHAR), %OFFSET_REG ++ subl %eax, %OFFSET_REG + +- VPTESTM %YMM0, %YMM0, %k2 +- /* Each bit cleared in K1 represents a mismatch or a null CHAR +- in XMM0 and 16 bytes at (%rsi, %rdx). */ +- VPCMP $0, (%rsi, %rdx), %XMM0, %k1{%k2} ++ vmovq (%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %xmm0 ++ vmovq (%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %xmm1 ++ VPTESTM %xmm0, %xmm0, %k2 ++ CMP_R1_R2_XMM (%xmm0, %xmm1, %k1){%k2} + kmovd %k1, %ecx + # ifdef USE_AS_WCSCMP +- subl $0xf, %ecx ++ subl $0x3, %ecx + # else +- subl $0xffff, %ecx ++ incb %cl + # endif +- jne L(last_vector) ++ jnz L(check_ret_vec_page_cross) ++ + +- addl $16, %edx +-# ifndef USE_AS_WCSCMP +- addl $16, %eax +-# endif + # ifdef USE_AS_STRNCMP +- /* Return 0 if the current offset (%rdx) >= the maximum offset +- (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) ++ addl $(8 / SIZE_OF_CHAR), %OFFSET_REG ++ subq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross_slow_case0) ++ subq $-(CHAR_PER_VEC * 4), %rdx ++ ++ leaq -(VEC_SIZE * 4)(%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %rdi ++ leaq -(VEC_SIZE * 4)(%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %rsi ++# else ++ leaq (8 - VEC_SIZE * 4)(%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %rdi ++ leaq (8 - VEC_SIZE * 4)(%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %rsi + # endif ++ jmp L(prepare_loop_aligned) + +-L(cross_page_1_xmm): +-# ifndef USE_AS_WCSCMP +- /* Less than 16 bytes to check, try 8 byte vector. NB: No need +- for wcscmp nor wcsncmp since wide char is 4 bytes. */ +- cmpl $(PAGE_SIZE - 8), %eax +- jg L(cross_page_8bytes) +- vmovq (%rdi, %rdx), %XMM0 +- vmovq (%rsi, %rdx), %XMM1 + +- VPTESTM %YMM0, %YMM0, %k2 +- /* Each bit cleared in K1 represents a mismatch or a null CHAR +- in XMM0 and XMM1. */ +- VPCMP $0, %XMM1, %XMM0, %k1{%k2} +- kmovb %k1, %ecx ++ ++ ++ .p2align 4,, 10 ++L(less_8_till_page): + # ifdef USE_AS_WCSCMP +- subl $0x3, %ecx ++ /* If using wchar then this is the only check before we reach ++ the page boundary. */ ++ movl (%rdi), %eax ++ movl (%rsi), %ecx ++ cmpl %ecx, %eax ++ jnz L(ret_less_8_wcs) ++# ifdef USE_AS_STRNCMP ++ addq $-(CHAR_PER_VEC * 2), %rdx ++ /* We already checked for len <= 1 so cannot hit that case here. ++ */ ++# endif ++ testl %eax, %eax ++ jnz L(prepare_loop) ++ ret ++ ++ .p2align 4,, 8 ++L(ret_less_8_wcs): ++ setl %OFFSET_REG8 ++ negl %OFFSET_REG ++ movl %OFFSET_REG, %eax ++ xorl %r8d, %eax ++ ret ++ + # else +- subl $0xff, %ecx +-# endif +- jne L(last_vector) ++ cmpl $28, %eax ++ ja L(less_4_till_page) ++ ++ vmovd (%rdi), %xmm0 ++ vmovd (%rsi), %xmm1 ++ VPTESTM %xmm0, %xmm0, %k2 ++ CMP_R1_R2_XMM (%xmm0, %xmm1, %k1){%k2} ++ kmovd %k1, %ecx ++ subl $0xf, %ecx ++ jnz L(check_ret_vec_page_cross) + +- addl $8, %edx +- addl $8, %eax + # ifdef USE_AS_STRNCMP +- /* Return 0 if the current offset (%rdx) >= the maximum offset +- (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) ++ cmpq $4, %rdx ++ jbe L(ret_zero_page_cross_slow_case1) + # endif ++ movl $(28 / SIZE_OF_CHAR), %OFFSET_REG ++ subl %eax, %OFFSET_REG + +-L(cross_page_8bytes): +- /* Less than 8 bytes to check, try 4 byte vector. */ +- cmpl $(PAGE_SIZE - 4), %eax +- jg L(cross_page_4bytes) +- vmovd (%rdi, %rdx), %XMM0 +- vmovd (%rsi, %rdx), %XMM1 +- +- VPTESTM %YMM0, %YMM0, %k2 +- /* Each bit cleared in K1 represents a mismatch or a null CHAR +- in XMM0 and XMM1. */ +- VPCMP $0, %XMM1, %XMM0, %k1{%k2} ++ vmovd (%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %xmm0 ++ vmovd (%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %xmm1 ++ VPTESTM %xmm0, %xmm0, %k2 ++ CMP_R1_R2_XMM (%xmm0, %xmm1, %k1){%k2} + kmovd %k1, %ecx +-# ifdef USE_AS_WCSCMP +- subl $0x1, %ecx +-# else + subl $0xf, %ecx +-# endif +- jne L(last_vector) ++ jnz L(check_ret_vec_page_cross) ++# ifdef USE_AS_STRNCMP ++ addl $(4 / SIZE_OF_CHAR), %OFFSET_REG ++ subq %OFFSET_REG64, %rdx ++ jbe L(ret_zero_page_cross_slow_case1) ++ subq $-(CHAR_PER_VEC * 4), %rdx ++ ++ leaq -(VEC_SIZE * 4)(%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %rdi ++ leaq -(VEC_SIZE * 4)(%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %rsi ++# else ++ leaq (4 - VEC_SIZE * 4)(%rdi, %OFFSET_REG64, SIZE_OF_CHAR), %rdi ++ leaq (4 - VEC_SIZE * 4)(%rsi, %OFFSET_REG64, SIZE_OF_CHAR), %rsi ++# endif ++ jmp L(prepare_loop_aligned) ++ + +- addl $4, %edx + # ifdef USE_AS_STRNCMP +- /* Return 0 if the current offset (%rdx) >= the maximum offset +- (%r11). */ +- cmpq %r11, %rdx +- jae L(zero) ++ .p2align 4,, 2 ++L(ret_zero_page_cross_slow_case1): ++ xorl %eax, %eax ++ ret + # endif + +-L(cross_page_4bytes): +-# endif +- /* Less than 4 bytes to check, try one byte/dword at a time. */ +-# ifdef USE_AS_STRNCMP +- cmpq %r11, %rdx +- jae L(zero) +-# endif +-# ifdef USE_AS_WCSCMP +- movl (%rdi, %rdx), %eax +- movl (%rsi, %rdx), %ecx +-# else +- movzbl (%rdi, %rdx), %eax +- movzbl (%rsi, %rdx), %ecx +-# endif +- testl %eax, %eax +- jne L(cross_page_loop) +- subl %ecx, %eax ++ .p2align 4,, 10 ++L(less_4_till_page): ++ subq %rdi, %rsi ++ /* Extremely slow byte comparison loop. */ ++L(less_4_loop): ++ movzbl (%rdi), %eax ++ movzbl (%rsi, %rdi), %ecx ++ TOLOWER_gpr (%rax, %eax) ++ TOLOWER_gpr (%rcx, %BYTE_LOOP_REG) ++ subl %BYTE_LOOP_REG, %eax ++ jnz L(ret_less_4_loop) ++ testl %ecx, %ecx ++ jz L(ret_zero_4_loop) ++# ifdef USE_AS_STRNCMP ++ decq %rdx ++ jz L(ret_zero_4_loop) ++# endif ++ incq %rdi ++ /* end condition is reach page boundary (rdi is aligned). */ ++ testl $31, %edi ++ jnz L(less_4_loop) ++ leaq -(VEC_SIZE * 4)(%rdi, %rsi), %rsi ++ addq $-(VEC_SIZE * 4), %rdi ++# ifdef USE_AS_STRNCMP ++ subq $-(CHAR_PER_VEC * 4), %rdx ++# endif ++ jmp L(prepare_loop_aligned) ++ ++L(ret_zero_4_loop): ++ xorl %eax, %eax ++ ret ++L(ret_less_4_loop): ++ xorl %r8d, %eax ++ subl %r8d, %eax + ret +-END (STRCMP) ++# endif ++ cfi_endproc ++ .size STRCMP, .-STRCMP + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/strcmp-sse42.S b/sysdeps/x86_64/multiarch/strcmp-sse42.S +--- a/sysdeps/x86_64/multiarch/strcmp-sse42.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strcmp-sse42.S 2025-10-20 21:02:22.000000000 +0300 +@@ -41,13 +41,8 @@ + # define UPDATE_STRNCMP_COUNTER + #endif + +-#ifdef USE_AVX +-# define SECTION avx +-# define GLABEL(l) l##_avx +-#else +-# define SECTION sse4.2 +-# define GLABEL(l) l##_sse42 +-#endif ++#define SECTION sse4.2 ++#define GLABEL(l) l##_sse42 + + #define LABEL(l) .L##l + +@@ -88,9 +83,8 @@ + movq __libc_tsd_LOCALE@gottpoff(%rip),%rax + mov %fs:(%rax),%RDX_LP + +- // XXX 5 byte should be before the function +- /* 5-byte NOP. */ +- .byte 0x0f,0x1f,0x44,0x00,0x00 ++ /* Either 1 or 5 bytes (dependeing if CET is enabled). */ ++ .p2align 4 + END (GLABEL(__strcasecmp)) + /* FALLTHROUGH to strcasecmp_l. */ + #endif +@@ -99,29 +93,14 @@ + movq __libc_tsd_LOCALE@gottpoff(%rip),%rax + mov %fs:(%rax),%RCX_LP + +- // XXX 5 byte should be before the function +- /* 5-byte NOP. */ +- .byte 0x0f,0x1f,0x44,0x00,0x00 ++ /* Either 1 or 5 bytes (dependeing if CET is enabled). */ ++ .p2align 4 + END (GLABEL(__strncasecmp)) + /* FALLTHROUGH to strncasecmp_l. */ + #endif + + +-#ifdef USE_AVX +-# define movdqa vmovdqa +-# define movdqu vmovdqu +-# define pmovmskb vpmovmskb +-# define pcmpistri vpcmpistri +-# define psubb vpsubb +-# define pcmpeqb vpcmpeqb +-# define psrldq vpsrldq +-# define pslldq vpslldq +-# define palignr vpalignr +-# define pxor vpxor +-# define D(arg) arg, arg +-#else +-# define D(arg) arg +-#endif ++#define arg arg + + STRCMP_SSE42: + cfi_startproc +@@ -169,27 +148,22 @@ + #if defined USE_AS_STRCASECMP_L || defined USE_AS_STRNCASECMP_L + .section .rodata.cst16,"aM",@progbits,16 + .align 16 +-LABEL(belowupper): +- .quad 0x4040404040404040 +- .quad 0x4040404040404040 +-LABEL(topupper): +-# ifdef USE_AVX +- .quad 0x5a5a5a5a5a5a5a5a +- .quad 0x5a5a5a5a5a5a5a5a +-# else +- .quad 0x5b5b5b5b5b5b5b5b +- .quad 0x5b5b5b5b5b5b5b5b +-# endif +-LABEL(touppermask): ++LABEL(lcase_min): ++ .quad 0x3f3f3f3f3f3f3f3f ++ .quad 0x3f3f3f3f3f3f3f3f ++LABEL(lcase_max): ++ .quad 0x9999999999999999 ++ .quad 0x9999999999999999 ++LABEL(case_add): + .quad 0x2020202020202020 + .quad 0x2020202020202020 + .previous +- movdqa LABEL(belowupper)(%rip), %xmm4 +-# define UCLOW_reg %xmm4 +- movdqa LABEL(topupper)(%rip), %xmm5 +-# define UCHIGH_reg %xmm5 +- movdqa LABEL(touppermask)(%rip), %xmm6 +-# define LCQWORD_reg %xmm6 ++ movdqa LABEL(lcase_min)(%rip), %xmm4 ++# define LCASE_MIN_reg %xmm4 ++ movdqa LABEL(lcase_max)(%rip), %xmm5 ++# define LCASE_MAX_reg %xmm5 ++ movdqa LABEL(case_add)(%rip), %xmm6 ++# define CASE_ADD_reg %xmm6 + #endif + cmp $0x30, %ecx + ja LABEL(crosscache)/* rsi: 16-byte load will cross cache line */ +@@ -198,43 +172,26 @@ + movdqu (%rdi), %xmm1 + movdqu (%rsi), %xmm2 + #if defined USE_AS_STRCASECMP_L || defined USE_AS_STRNCASECMP_L +-# ifdef USE_AVX +-# define TOLOWER(reg1, reg2) \ +- vpcmpgtb UCLOW_reg, reg1, %xmm7; \ +- vpcmpgtb UCHIGH_reg, reg1, %xmm8; \ +- vpcmpgtb UCLOW_reg, reg2, %xmm9; \ +- vpcmpgtb UCHIGH_reg, reg2, %xmm10; \ +- vpandn %xmm7, %xmm8, %xmm8; \ +- vpandn %xmm9, %xmm10, %xmm10; \ +- vpand LCQWORD_reg, %xmm8, %xmm8; \ +- vpand LCQWORD_reg, %xmm10, %xmm10; \ +- vpor reg1, %xmm8, reg1; \ +- vpor reg2, %xmm10, reg2 +-# else +-# define TOLOWER(reg1, reg2) \ +- movdqa reg1, %xmm7; \ +- movdqa UCHIGH_reg, %xmm8; \ +- movdqa reg2, %xmm9; \ +- movdqa UCHIGH_reg, %xmm10; \ +- pcmpgtb UCLOW_reg, %xmm7; \ +- pcmpgtb reg1, %xmm8; \ +- pcmpgtb UCLOW_reg, %xmm9; \ +- pcmpgtb reg2, %xmm10; \ +- pand %xmm8, %xmm7; \ +- pand %xmm10, %xmm9; \ +- pand LCQWORD_reg, %xmm7; \ +- pand LCQWORD_reg, %xmm9; \ +- por %xmm7, reg1; \ +- por %xmm9, reg2 +-# endif ++# define TOLOWER(reg1, reg2) \ ++ movdqa LCASE_MIN_reg, %xmm7; \ ++ movdqa LCASE_MIN_reg, %xmm8; \ ++ paddb reg1, %xmm7; \ ++ paddb reg2, %xmm8; \ ++ pcmpgtb LCASE_MAX_reg, %xmm7; \ ++ pcmpgtb LCASE_MAX_reg, %xmm8; \ ++ pandn CASE_ADD_reg, %xmm7; \ ++ pandn CASE_ADD_reg, %xmm8; \ ++ paddb %xmm7, reg1; \ ++ paddb %xmm8, reg2 ++ + TOLOWER (%xmm1, %xmm2) + #else + # define TOLOWER(reg1, reg2) + #endif +- pxor %xmm0, D(%xmm0) /* clear %xmm0 for null char checks */ +- pcmpeqb %xmm1, D(%xmm0) /* Any null chars? */ +- pcmpeqb %xmm2, D(%xmm1) /* compare first 16 bytes for equality */ +- psubb %xmm0, D(%xmm1) /* packed sub of comparison results*/ ++ pxor %xmm0, %xmm0 /* clear %xmm0 for null char checks */ ++ pcmpeqb %xmm1, %xmm0 /* Any null chars? */ ++ pcmpeqb %xmm2, %xmm1 /* compare first 16 bytes for equality */ ++ psubb %xmm0, %xmm1 /* packed sub of comparison results*/ + pmovmskb %xmm1, %edx + sub $0xffff, %edx /* if first 16 bytes are same, edx == 0xffff */ + jnz LABEL(less16bytes)/* If not, find different value or null char */ +@@ -258,7 +215,7 @@ + xor %r8d, %r8d + and $0xf, %ecx /* offset of rsi */ + and $0xf, %eax /* offset of rdi */ +- pxor %xmm0, D(%xmm0) /* clear %xmm0 for null char check */ ++ pxor %xmm0, %xmm0 /* clear %xmm0 for null char check */ + cmp %eax, %ecx + je LABEL(ashr_0) /* rsi and rdi relative offset same */ + ja LABEL(bigger) +@@ -272,7 +229,7 @@ + sub %rcx, %r9 + lea LABEL(unaligned_table)(%rip), %r10 + movslq (%r10, %r9,4), %r9 +- pcmpeqb %xmm1, D(%xmm0) /* Any null chars? */ ++ pcmpeqb %xmm1, %xmm0 /* Any null chars? */ + lea (%r10, %r9), %r10 + _CET_NOTRACK jmp *%r10 /* jump to corresponding case */ + +@@ -285,15 +242,15 @@ + LABEL(ashr_0): + + movdqa (%rsi), %xmm1 +- pcmpeqb %xmm1, D(%xmm0) /* Any null chars? */ ++ pcmpeqb %xmm1, %xmm0 /* Any null chars? */ + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L +- pcmpeqb (%rdi), D(%xmm1) /* compare 16 bytes for equality */ ++ pcmpeqb (%rdi), %xmm1 /* compare 16 bytes for equality */ + #else + movdqa (%rdi), %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm2, D(%xmm1) /* compare 16 bytes for equality */ ++ pcmpeqb %xmm2, %xmm1 /* compare 16 bytes for equality */ + #endif +- psubb %xmm0, D(%xmm1) /* packed sub of comparison results*/ ++ psubb %xmm0, %xmm1 /* packed sub of comparison results*/ + pmovmskb %xmm1, %r9d + shr %cl, %edx /* adjust 0xffff for offset */ + shr %cl, %r9d /* adjust for 16-byte offset */ +@@ -373,10 +330,10 @@ + */ + .p2align 4 + LABEL(ashr_1): +- pslldq $15, D(%xmm2) /* shift first string to align with second */ ++ pslldq $15, %xmm2 /* shift first string to align with second */ + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) /* compare 16 bytes for equality */ +- psubb %xmm0, D(%xmm2) /* packed sub of comparison results*/ ++ pcmpeqb %xmm1, %xmm2 /* compare 16 bytes for equality */ ++ psubb %xmm0, %xmm2 /* packed sub of comparison results*/ + pmovmskb %xmm2, %r9d + shr %cl, %edx /* adjust 0xffff for offset */ + shr %cl, %r9d /* adjust for 16-byte offset */ +@@ -404,7 +361,7 @@ + + LABEL(nibble_ashr_1_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $1, -16(%rdi, %rdx), D(%xmm0) ++ palignr $1, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -423,7 +380,7 @@ + jg LABEL(nibble_ashr_1_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $1, -16(%rdi, %rdx), D(%xmm0) ++ palignr $1, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -443,7 +400,7 @@ + LABEL(nibble_ashr_1_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $1, D(%xmm0) ++ psrldq $1, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -461,10 +418,10 @@ + */ + .p2align 4 + LABEL(ashr_2): +- pslldq $14, D(%xmm2) ++ pslldq $14, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -492,7 +449,7 @@ + + LABEL(nibble_ashr_2_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $2, -16(%rdi, %rdx), D(%xmm0) ++ palignr $2, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -511,7 +468,7 @@ + jg LABEL(nibble_ashr_2_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $2, -16(%rdi, %rdx), D(%xmm0) ++ palignr $2, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -531,7 +488,7 @@ + LABEL(nibble_ashr_2_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $2, D(%xmm0) ++ psrldq $2, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -549,10 +506,10 @@ + */ + .p2align 4 + LABEL(ashr_3): +- pslldq $13, D(%xmm2) ++ pslldq $13, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -580,7 +537,7 @@ + + LABEL(nibble_ashr_3_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $3, -16(%rdi, %rdx), D(%xmm0) ++ palignr $3, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -599,7 +556,7 @@ + jg LABEL(nibble_ashr_3_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $3, -16(%rdi, %rdx), D(%xmm0) ++ palignr $3, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -619,7 +576,7 @@ + LABEL(nibble_ashr_3_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $3, D(%xmm0) ++ psrldq $3, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -637,10 +594,10 @@ + */ + .p2align 4 + LABEL(ashr_4): +- pslldq $12, D(%xmm2) ++ pslldq $12, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -669,7 +626,7 @@ + + LABEL(nibble_ashr_4_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $4, -16(%rdi, %rdx), D(%xmm0) ++ palignr $4, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -688,7 +645,7 @@ + jg LABEL(nibble_ashr_4_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $4, -16(%rdi, %rdx), D(%xmm0) ++ palignr $4, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -708,7 +665,7 @@ + LABEL(nibble_ashr_4_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $4, D(%xmm0) ++ psrldq $4, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -726,10 +683,10 @@ + */ + .p2align 4 + LABEL(ashr_5): +- pslldq $11, D(%xmm2) ++ pslldq $11, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -758,7 +715,7 @@ + + LABEL(nibble_ashr_5_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $5, -16(%rdi, %rdx), D(%xmm0) ++ palignr $5, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -778,7 +735,7 @@ + + movdqa (%rdi, %rdx), %xmm0 + +- palignr $5, -16(%rdi, %rdx), D(%xmm0) ++ palignr $5, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -798,7 +755,7 @@ + LABEL(nibble_ashr_5_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $5, D(%xmm0) ++ psrldq $5, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -816,10 +773,10 @@ + */ + .p2align 4 + LABEL(ashr_6): +- pslldq $10, D(%xmm2) ++ pslldq $10, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -848,7 +805,7 @@ + + LABEL(nibble_ashr_6_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $6, -16(%rdi, %rdx), D(%xmm0) ++ palignr $6, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -867,7 +824,7 @@ + jg LABEL(nibble_ashr_6_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $6, -16(%rdi, %rdx), D(%xmm0) ++ palignr $6, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -887,7 +844,7 @@ + LABEL(nibble_ashr_6_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $6, D(%xmm0) ++ psrldq $6, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -905,10 +862,10 @@ + */ + .p2align 4 + LABEL(ashr_7): +- pslldq $9, D(%xmm2) ++ pslldq $9, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -937,7 +894,7 @@ + + LABEL(nibble_ashr_7_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $7, -16(%rdi, %rdx), D(%xmm0) ++ palignr $7, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -956,7 +913,7 @@ + jg LABEL(nibble_ashr_7_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $7, -16(%rdi, %rdx), D(%xmm0) ++ palignr $7, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a,(%rsi,%rdx), %xmm0 + #else +@@ -976,7 +933,7 @@ + LABEL(nibble_ashr_7_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $7, D(%xmm0) ++ psrldq $7, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -994,10 +951,10 @@ + */ + .p2align 4 + LABEL(ashr_8): +- pslldq $8, D(%xmm2) ++ pslldq $8, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -1026,7 +983,7 @@ + + LABEL(nibble_ashr_8_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $8, -16(%rdi, %rdx), D(%xmm0) ++ palignr $8, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1045,7 +1002,7 @@ + jg LABEL(nibble_ashr_8_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $8, -16(%rdi, %rdx), D(%xmm0) ++ palignr $8, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1065,7 +1022,7 @@ + LABEL(nibble_ashr_8_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $8, D(%xmm0) ++ psrldq $8, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -1083,10 +1040,10 @@ + */ + .p2align 4 + LABEL(ashr_9): +- pslldq $7, D(%xmm2) ++ pslldq $7, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -1116,7 +1073,7 @@ + LABEL(nibble_ashr_9_restart_use): + movdqa (%rdi, %rdx), %xmm0 + +- palignr $9, -16(%rdi, %rdx), D(%xmm0) ++ palignr $9, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1135,7 +1092,7 @@ + jg LABEL(nibble_ashr_9_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $9, -16(%rdi, %rdx), D(%xmm0) ++ palignr $9, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1155,7 +1112,7 @@ + LABEL(nibble_ashr_9_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $9, D(%xmm0) ++ psrldq $9, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -1173,10 +1130,10 @@ + */ + .p2align 4 + LABEL(ashr_10): +- pslldq $6, D(%xmm2) ++ pslldq $6, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -1205,7 +1162,7 @@ + + LABEL(nibble_ashr_10_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $10, -16(%rdi, %rdx), D(%xmm0) ++ palignr $10, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1224,7 +1181,7 @@ + jg LABEL(nibble_ashr_10_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $10, -16(%rdi, %rdx), D(%xmm0) ++ palignr $10, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1244,7 +1201,7 @@ + LABEL(nibble_ashr_10_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $10, D(%xmm0) ++ psrldq $10, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -1262,10 +1219,10 @@ + */ + .p2align 4 + LABEL(ashr_11): +- pslldq $5, D(%xmm2) ++ pslldq $5, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -1294,7 +1251,7 @@ + + LABEL(nibble_ashr_11_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $11, -16(%rdi, %rdx), D(%xmm0) ++ palignr $11, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1313,7 +1270,7 @@ + jg LABEL(nibble_ashr_11_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $11, -16(%rdi, %rdx), D(%xmm0) ++ palignr $11, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1333,7 +1290,7 @@ + LABEL(nibble_ashr_11_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $11, D(%xmm0) ++ psrldq $11, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -1351,10 +1308,10 @@ + */ + .p2align 4 + LABEL(ashr_12): +- pslldq $4, D(%xmm2) ++ pslldq $4, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -1383,7 +1340,7 @@ + + LABEL(nibble_ashr_12_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $12, -16(%rdi, %rdx), D(%xmm0) ++ palignr $12, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1402,7 +1359,7 @@ + jg LABEL(nibble_ashr_12_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $12, -16(%rdi, %rdx), D(%xmm0) ++ palignr $12, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1422,7 +1379,7 @@ + LABEL(nibble_ashr_12_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $12, D(%xmm0) ++ psrldq $12, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -1440,10 +1397,10 @@ + */ + .p2align 4 + LABEL(ashr_13): +- pslldq $3, D(%xmm2) ++ pslldq $3, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -1473,7 +1430,7 @@ + + LABEL(nibble_ashr_13_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $13, -16(%rdi, %rdx), D(%xmm0) ++ palignr $13, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1492,7 +1449,7 @@ + jg LABEL(nibble_ashr_13_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $13, -16(%rdi, %rdx), D(%xmm0) ++ palignr $13, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1512,7 +1469,7 @@ + LABEL(nibble_ashr_13_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $13, D(%xmm0) ++ psrldq $13, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -1530,10 +1487,10 @@ + */ + .p2align 4 + LABEL(ashr_14): +- pslldq $2, D(%xmm2) ++ pslldq $2, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -1563,7 +1520,7 @@ + + LABEL(nibble_ashr_14_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $14, -16(%rdi, %rdx), D(%xmm0) ++ palignr $14, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1582,7 +1539,7 @@ + jg LABEL(nibble_ashr_14_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $14, -16(%rdi, %rdx), D(%xmm0) ++ palignr $14, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1602,7 +1559,7 @@ + LABEL(nibble_ashr_14_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $14, D(%xmm0) ++ psrldq $14, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +@@ -1620,10 +1577,10 @@ + */ + .p2align 4 + LABEL(ashr_15): +- pslldq $1, D(%xmm2) ++ pslldq $1, %xmm2 + TOLOWER (%xmm1, %xmm2) +- pcmpeqb %xmm1, D(%xmm2) +- psubb %xmm0, D(%xmm2) ++ pcmpeqb %xmm1, %xmm2 ++ psubb %xmm0, %xmm2 + pmovmskb %xmm2, %r9d + shr %cl, %edx + shr %cl, %r9d +@@ -1655,7 +1612,7 @@ + + LABEL(nibble_ashr_15_restart_use): + movdqa (%rdi, %rdx), %xmm0 +- palignr $15, -16(%rdi, %rdx), D(%xmm0) ++ palignr $15, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1674,7 +1631,7 @@ + jg LABEL(nibble_ashr_15_use) + + movdqa (%rdi, %rdx), %xmm0 +- palignr $15, -16(%rdi, %rdx), D(%xmm0) ++ palignr $15, -16(%rdi, %rdx), %xmm0 + #if !defined USE_AS_STRCASECMP_L && !defined USE_AS_STRNCASECMP_L + pcmpistri $0x1a, (%rsi,%rdx), %xmm0 + #else +@@ -1694,7 +1651,7 @@ + LABEL(nibble_ashr_15_use): + sub $0x1000, %r10 + movdqa -16(%rdi, %rdx), %xmm0 +- psrldq $15, D(%xmm0) ++ psrldq $15, %xmm0 + pcmpistri $0x3a,%xmm0, %xmm0 + #if defined USE_AS_STRNCMP || defined USE_AS_STRNCASECMP_L + cmp %r11, %rcx +diff -Nuar a/sysdeps/x86_64/multiarch/strcspn-c.c b/sysdeps/x86_64/multiarch/strcspn-c.c +--- a/sysdeps/x86_64/multiarch/strcspn-c.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strcspn-c.c 2025-10-20 21:02:22.000000000 +0300 +@@ -84,83 +84,74 @@ + RETURN (NULL, strlen (s)); + + const char *aligned; +- __m128i mask; +- int offset = (int) ((size_t) a & 15); ++ __m128i mask, maskz, zero; ++ unsigned int maskz_bits; ++ unsigned int offset = (unsigned int) ((size_t) a & 15); ++ zero = _mm_set1_epi8 (0); + if (offset != 0) + { + /* Load masks. */ + aligned = (const char *) ((size_t) a & -16L); + __m128i mask0 = _mm_load_si128 ((__m128i *) aligned); +- +- mask = __m128i_shift_right (mask0, offset); ++ maskz = _mm_cmpeq_epi8 (mask0, zero); + + /* Find where the NULL terminator is. */ +- int length = _mm_cmpistri (mask, mask, 0x3a); +- if (length == 16 - offset) +- { +- /* There is no NULL terminator. */ +- __m128i mask1 = _mm_load_si128 ((__m128i *) (aligned + 16)); +- int index = _mm_cmpistri (mask1, mask1, 0x3a); +- length += index; +- +- /* Don't use SSE4.2 if the length of A > 16. */ +- if (length > 16) +- return STRCSPN_SSE2 (s, a); +- +- if (index != 0) +- { +- /* Combine mask0 and mask1. We could play games with +- palignr, but frankly this data should be in L1 now +- so do the merge via an unaligned load. */ +- mask = _mm_loadu_si128 ((__m128i *) a); +- } +- } ++ maskz_bits = _mm_movemask_epi8 (maskz) >> offset; ++ if (maskz_bits != 0) ++ { ++ mask = __m128i_shift_right (mask0, offset); ++ offset = (unsigned int) ((size_t) s & 15); ++ if (offset) ++ goto start_unaligned; ++ ++ aligned = s; ++ goto start_loop; ++ } + } +- else +- { +- /* A is aligned. */ +- mask = _mm_load_si128 ((__m128i *) a); + +- /* Find where the NULL terminator is. */ +- int length = _mm_cmpistri (mask, mask, 0x3a); +- if (length == 16) +- { +- /* There is no NULL terminator. Don't use SSE4.2 if the length +- of A > 16. */ +- if (a[16] != 0) +- return STRCSPN_SSE2 (s, a); +- } ++ /* A is aligned. */ ++ mask = _mm_loadu_si128 ((__m128i *) a); ++ /* Find where the NULL terminator is. */ ++ maskz = _mm_cmpeq_epi8 (mask, zero); ++ maskz_bits = _mm_movemask_epi8 (maskz); ++ if (maskz_bits == 0) ++ { ++ /* There is no NULL terminator. Don't use SSE4.2 if the length ++ of A > 16. */ ++ if (a[16] != 0) ++ return STRCSPN_SSE2 (s, a); + } + +- offset = (int) ((size_t) s & 15); ++ aligned = s; ++ offset = (unsigned int) ((size_t) s & 15); + if (offset != 0) + { ++ start_unaligned: + /* Check partial string. */ + aligned = (const char *) ((size_t) s & -16L); + __m128i value = _mm_load_si128 ((__m128i *) aligned); + + value = __m128i_shift_right (value, offset); + +- int length = _mm_cmpistri (mask, value, 0x2); ++ unsigned int length = _mm_cmpistri (mask, value, 0x2); + /* No need to check ZFlag since ZFlag is always 1. */ +- int cflag = _mm_cmpistrc (mask, value, 0x2); ++ unsigned int cflag = _mm_cmpistrc (mask, value, 0x2); + if (cflag) + RETURN ((char *) (s + length), length); + /* Find where the NULL terminator is. */ +- int index = _mm_cmpistri (value, value, 0x3a); ++ unsigned int index = _mm_cmpistri (value, value, 0x3a); + if (index < 16 - offset) + RETURN (NULL, index); + aligned += 16; + } +- else +- aligned = s; + ++start_loop: + while (1) + { + __m128i value = _mm_load_si128 ((__m128i *) aligned); +- int index = _mm_cmpistri (mask, value, 0x2); +- int cflag = _mm_cmpistrc (mask, value, 0x2); +- int zflag = _mm_cmpistrz (mask, value, 0x2); ++ unsigned int index = _mm_cmpistri (mask, value, 0x2); ++ unsigned int cflag = _mm_cmpistrc (mask, value, 0x2); ++ unsigned int zflag = _mm_cmpistrz (mask, value, 0x2); + if (cflag) + RETURN ((char *) (aligned + index), (size_t) (aligned + index - s)); + if (zflag) +diff -Nuar a/sysdeps/x86_64/multiarch/strcspn-sse2.c b/sysdeps/x86_64/multiarch/strcspn-sse2.c +--- a/sysdeps/x86_64/multiarch/strcspn-sse2.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strcspn-sse2.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,28 @@ ++/* strcspn. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#if IS_IN (libc) ++ ++# include ++# define STRCSPN __strcspn_sse2 ++ ++# undef libc_hidden_builtin_def ++# define libc_hidden_builtin_def(STRCSPN) ++#endif ++ ++#include +diff -Nuar a/sysdeps/x86_64/multiarch/strcspn-sse2.S b/sysdeps/x86_64/multiarch/strcspn-sse2.S +--- a/sysdeps/x86_64/multiarch/strcspn-sse2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strcspn-sse2.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,28 +0,0 @@ +-/* strcspn optimized with SSE2. +- Copyright (C) 2017-2022 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 +- . */ +- +-#if IS_IN (libc) +- +-# include +-# define strcspn __strcspn_sse2 +- +-# undef libc_hidden_builtin_def +-# define libc_hidden_builtin_def(strcspn) +-#endif +- +-#include +diff -Nuar a/sysdeps/x86_64/multiarch/strlen-avx2.S b/sysdeps/x86_64/multiarch/strlen-avx2.S +--- a/sysdeps/x86_64/multiarch/strlen-avx2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strlen-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -542,14 +542,11 @@ + L(cross_page_less_vec): + tzcntl %eax, %eax + # ifdef USE_AS_WCSLEN +- /* NB: Multiply length by 4 to get byte count. */ +- sall $2, %esi ++ /* NB: Divide by 4 to convert from byte-count to length. */ ++ shrl $2, %eax + # endif + cmpq %rax, %rsi + cmovb %esi, %eax +-# ifdef USE_AS_WCSLEN +- shrl $2, %eax +-# endif + VZEROUPPER_RETURN + # endif + +diff -Nuar a/sysdeps/x86_64/multiarch/strlen-evex512.S b/sysdeps/x86_64/multiarch/strlen-evex512.S +--- a/sysdeps/x86_64/multiarch/strlen-evex512.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strlen-evex512.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,7 @@ ++#ifndef STRLEN ++# define STRLEN __strlen_evex512 ++#endif ++ ++#define VEC_SIZE 64 ++ ++#include "strlen-evex-base.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strlen-evex-base.S b/sysdeps/x86_64/multiarch/strlen-evex-base.S +--- a/sysdeps/x86_64/multiarch/strlen-evex-base.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strlen-evex-base.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,302 @@ ++/* Placeholder function, not used by any processor at the moment. ++ Copyright (C) 2022 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 ++ . */ ++ ++#if IS_IN (libc) ++ ++# include ++ ++# ifdef USE_AS_WCSLEN ++# define VPCMP vpcmpd ++# define VPTESTN vptestnmd ++# define VPMINU vpminud ++# define CHAR_SIZE 4 ++# else ++# define VPCMP vpcmpb ++# define VPTESTN vptestnmb ++# define VPMINU vpminub ++# define CHAR_SIZE 1 ++# endif ++ ++# define XMM0 xmm16 ++# define PAGE_SIZE 4096 ++# define CHAR_PER_VEC (VEC_SIZE / CHAR_SIZE) ++ ++# if VEC_SIZE == 64 ++# define KMOV kmovq ++# define KORTEST kortestq ++# define RAX rax ++# define RCX rcx ++# define RDX rdx ++# define SHR shrq ++# define TEXTSUFFIX evex512 ++# define VMM0 zmm16 ++# define VMM1 zmm17 ++# define VMM2 zmm18 ++# define VMM3 zmm19 ++# define VMM4 zmm20 ++# define VMOVA vmovdqa64 ++# elif VEC_SIZE == 32 ++/* Currently Unused. */ ++# define KMOV kmovd ++# define KORTEST kortestd ++# define RAX eax ++# define RCX ecx ++# define RDX edx ++# define SHR shrl ++# define TEXTSUFFIX evex256 ++# define VMM0 ymm16 ++# define VMM1 ymm17 ++# define VMM2 ymm18 ++# define VMM3 ymm19 ++# define VMM4 ymm20 ++# define VMOVA vmovdqa32 ++# endif ++ ++ .section .text.TEXTSUFFIX, "ax", @progbits ++/* Aligning entry point to 64 byte, provides better performance for ++ one vector length string. */ ++ENTRY_P2ALIGN (STRLEN, 6) ++# ifdef USE_AS_STRNLEN ++ /* Check zero length. */ ++ test %RSI_LP, %RSI_LP ++ jz L(ret_max) ++# ifdef __ILP32__ ++ /* Clear the upper 32 bits. */ ++ movl %esi, %esi ++# endif ++# endif ++ ++ movl %edi, %eax ++ vpxorq %XMM0, %XMM0, %XMM0 ++ andl $(PAGE_SIZE - 1), %eax ++ cmpl $(PAGE_SIZE - VEC_SIZE), %eax ++ ja L(page_cross) ++ ++ /* Compare [w]char for null, mask bit will be set for match. */ ++ VPCMP $0, (%rdi), %VMM0, %k0 ++ KMOV %k0, %RAX ++ test %RAX, %RAX ++ jz L(align_more) ++ ++ bsf %RAX, %RAX ++# ifdef USE_AS_STRNLEN ++ cmpq %rsi, %rax ++ cmovnb %rsi, %rax ++# endif ++ ret ++ ++ /* At this point vector max length reached. */ ++# ifdef USE_AS_STRNLEN ++ .p2align 4,,3 ++L(ret_max): ++ movq %rsi, %rax ++ ret ++# endif ++ ++L(align_more): ++ leaq VEC_SIZE(%rdi), %rax ++ /* Align rax to VEC_SIZE. */ ++ andq $-VEC_SIZE, %rax ++# ifdef USE_AS_STRNLEN ++ movq %rax, %rdx ++ subq %rdi, %rdx ++# ifdef USE_AS_WCSLEN ++ SHR $2, %RDX ++# endif ++ /* At this point rdx contains [w]chars already compared. */ ++ subq %rsi, %rdx ++ jae L(ret_max) ++ negq %rdx ++ /* At this point rdx contains number of w[char] needs to go. ++ Now onwards rdx will keep decrementing with each compare. */ ++# endif ++ ++ /* Loop unroll 4 times for 4 vector loop. */ ++ VPCMP $0, (%rax), %VMM0, %k0 ++ KMOV %k0, %RCX ++ test %RCX, %RCX ++ jnz L(ret_vec_x1) ++ ++# ifdef USE_AS_STRNLEN ++ subq $CHAR_PER_VEC, %rdx ++ jbe L(ret_max) ++# endif ++ ++ VPCMP $0, VEC_SIZE(%rax), %VMM0, %k0 ++ KMOV %k0, %RCX ++ test %RCX, %RCX ++ jnz L(ret_vec_x2) ++ ++# ifdef USE_AS_STRNLEN ++ subq $CHAR_PER_VEC, %rdx ++ jbe L(ret_max) ++# endif ++ ++ VPCMP $0, (VEC_SIZE * 2)(%rax), %VMM0, %k0 ++ KMOV %k0, %RCX ++ test %RCX, %RCX ++ jnz L(ret_vec_x3) ++ ++# ifdef USE_AS_STRNLEN ++ subq $CHAR_PER_VEC, %rdx ++ jbe L(ret_max) ++# endif ++ ++ VPCMP $0, (VEC_SIZE * 3)(%rax), %VMM0, %k0 ++ KMOV %k0, %RCX ++ test %RCX, %RCX ++ jnz L(ret_vec_x4) ++ ++# ifdef USE_AS_STRNLEN ++ subq $CHAR_PER_VEC, %rdx ++ jbe L(ret_max) ++ /* Save pointer before 4 x VEC_SIZE alignment. */ ++ movq %rax, %rcx ++# endif ++ ++ /* Align address to VEC_SIZE * 4 for loop. */ ++ andq $-(VEC_SIZE * 4), %rax ++ ++# ifdef USE_AS_STRNLEN ++ subq %rax, %rcx ++# ifdef USE_AS_WCSLEN ++ SHR $2, %RCX ++# endif ++ /* rcx contains number of [w]char will be recompared due to ++ alignment fixes. rdx must be incremented by rcx to offset ++ alignment adjustment. */ ++ addq %rcx, %rdx ++ /* Need jump as we don't want to add/subtract rdx for first ++ iteration of 4 x VEC_SIZE aligned loop. */ ++ jmp L(loop_entry) ++# endif ++ ++ .p2align 4,,11 ++L(loop): ++# ifdef USE_AS_STRNLEN ++ subq $(CHAR_PER_VEC * 4), %rdx ++ jbe L(ret_max) ++L(loop_entry): ++# endif ++ /* VPMINU and VPCMP combination provide better performance as ++ compared to alternative combinations. */ ++ VMOVA (VEC_SIZE * 4)(%rax), %VMM1 ++ VPMINU (VEC_SIZE * 5)(%rax), %VMM1, %VMM2 ++ VMOVA (VEC_SIZE * 6)(%rax), %VMM3 ++ VPMINU (VEC_SIZE * 7)(%rax), %VMM3, %VMM4 ++ ++ VPTESTN %VMM2, %VMM2, %k0 ++ VPTESTN %VMM4, %VMM4, %k1 ++ ++ subq $-(VEC_SIZE * 4), %rax ++ KORTEST %k0, %k1 ++ jz L(loop) ++ ++ VPTESTN %VMM1, %VMM1, %k2 ++ KMOV %k2, %RCX ++ test %RCX, %RCX ++ jnz L(ret_vec_x1) ++ ++ KMOV %k0, %RCX ++ /* At this point, if k0 is non zero, null char must be in the ++ second vector. */ ++ test %RCX, %RCX ++ jnz L(ret_vec_x2) ++ ++ VPTESTN %VMM3, %VMM3, %k3 ++ KMOV %k3, %RCX ++ test %RCX, %RCX ++ jnz L(ret_vec_x3) ++ /* At this point null [w]char must be in the fourth vector so no ++ need to check. */ ++ KMOV %k1, %RCX ++ ++ /* Fourth, third, second vector terminating are pretty much ++ same, implemented this way to avoid branching and reuse code ++ from pre loop exit condition. */ ++L(ret_vec_x4): ++ bsf %RCX, %RCX ++ subq %rdi, %rax ++# ifdef USE_AS_WCSLEN ++ subq $-(VEC_SIZE * 3), %rax ++ shrq $2, %rax ++ addq %rcx, %rax ++# else ++ leaq (VEC_SIZE * 3)(%rcx, %rax), %rax ++# endif ++# ifdef USE_AS_STRNLEN ++ cmpq %rsi, %rax ++ cmovnb %rsi, %rax ++# endif ++ ret ++ ++L(ret_vec_x3): ++ bsf %RCX, %RCX ++ subq %rdi, %rax ++# ifdef USE_AS_WCSLEN ++ subq $-(VEC_SIZE * 2), %rax ++ shrq $2, %rax ++ addq %rcx, %rax ++# else ++ leaq (VEC_SIZE * 2)(%rcx, %rax), %rax ++# endif ++# ifdef USE_AS_STRNLEN ++ cmpq %rsi, %rax ++ cmovnb %rsi, %rax ++# endif ++ ret ++ ++L(ret_vec_x2): ++ subq $-VEC_SIZE, %rax ++L(ret_vec_x1): ++ bsf %RCX, %RCX ++ subq %rdi, %rax ++# ifdef USE_AS_WCSLEN ++ shrq $2, %rax ++# endif ++ addq %rcx, %rax ++# ifdef USE_AS_STRNLEN ++ cmpq %rsi, %rax ++ cmovnb %rsi, %rax ++# endif ++ ret ++ ++L(page_cross): ++ movl %eax, %ecx ++# ifdef USE_AS_WCSLEN ++ andl $(VEC_SIZE - 1), %ecx ++ sarl $2, %ecx ++# endif ++ /* ecx contains number of w[char] to be skipped as a result ++ of address alignment. */ ++ xorq %rdi, %rax ++ VPCMP $0, (PAGE_SIZE - VEC_SIZE)(%rax), %VMM0, %k0 ++ KMOV %k0, %RAX ++ /* Ignore number of character for alignment adjustment. */ ++ SHR %cl, %RAX ++ jz L(align_more) ++ ++ bsf %RAX, %RAX ++# ifdef USE_AS_STRNLEN ++ cmpq %rsi, %rax ++ cmovnb %rsi, %rax ++# endif ++ ret ++ ++END (STRLEN) ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/strlen-vec.S b/sysdeps/x86_64/multiarch/strlen-vec.S +--- a/sysdeps/x86_64/multiarch/strlen-vec.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strlen-vec.S 2025-10-20 21:02:22.000000000 +0300 +@@ -28,6 +28,10 @@ + # define SHIFT_RETURN + #endif + ++#ifndef SECTION ++# define SECTION(p) p ++#endif ++ + /* Long lived register in strlen(s), strnlen(s, n) are: + + %xmm3 - zero +@@ -37,7 +41,7 @@ + */ + + +-.text ++ .section SECTION(.text),"ax",@progbits + ENTRY(strlen) + + /* Test 64 bytes from %rax for zero. Save result as bitmask in %rdx. */ +diff -Nuar a/sysdeps/x86_64/multiarch/strncase_l-avx2-rtm.S b/sysdeps/x86_64/multiarch/strncase_l-avx2-rtm.S +--- a/sysdeps/x86_64/multiarch/strncase_l-avx2-rtm.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strncase_l-avx2-rtm.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,16 @@ ++#ifndef STRCMP ++# define STRCMP __strncasecmp_l_avx2_rtm ++#endif ++ ++#define _GLABEL(x) x ## _rtm ++#define GLABEL(x) _GLABEL(x) ++ ++#define ZERO_UPPER_VEC_REGISTERS_RETURN \ ++ ZERO_UPPER_VEC_REGISTERS_RETURN_XTEST ++ ++#define VZEROUPPER_RETURN jmp L(return_vzeroupper) ++ ++#define SECTION(p) p##.avx.rtm ++#define OVERFLOW_STRCMP __strcasecmp_l_avx2_rtm ++ ++#include "strncase_l-avx2.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strncase_l-avx2.S b/sysdeps/x86_64/multiarch/strncase_l-avx2.S +--- a/sysdeps/x86_64/multiarch/strncase_l-avx2.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strncase_l-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,27 @@ ++/* strncasecmp_l optimized with AVX2. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#ifndef STRCMP ++# define STRCMP __strncasecmp_l_avx2 ++#endif ++#define USE_AS_STRCASECMP_L ++#define USE_AS_STRNCMP ++#ifndef OVERFLOW_STRCMP ++# define OVERFLOW_STRCMP __strcasecmp_l_avx2 ++#endif ++#include "strcmp-avx2.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strncase_l-avx.S b/sysdeps/x86_64/multiarch/strncase_l-avx.S +--- a/sysdeps/x86_64/multiarch/strncase_l-avx.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strncase_l-avx.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,22 +0,0 @@ +-/* strncasecmp_l optimized with AVX. +- Copyright (C) 2017-2022 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 +- . */ +- +-#define STRCMP_SSE42 __strncasecmp_l_avx +-#define USE_AVX 1 +-#define USE_AS_STRNCASECMP_L +-#include "strcmp-sse42.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strncase_l-evex.S b/sysdeps/x86_64/multiarch/strncase_l-evex.S +--- a/sysdeps/x86_64/multiarch/strncase_l-evex.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strncase_l-evex.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,25 @@ ++/* strncasecmp_l optimized with EVEX. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#ifndef STRCMP ++# define STRCMP __strncasecmp_l_evex ++#endif ++#define OVERFLOW_STRCMP __strcasecmp_l_evex ++#define USE_AS_STRCASECMP_L ++#define USE_AS_STRNCMP ++#include "strcmp-evex.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strncmp-avx2-rtm.S b/sysdeps/x86_64/multiarch/strncmp-avx2-rtm.S +--- a/sysdeps/x86_64/multiarch/strncmp-avx2-rtm.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strncmp-avx2-rtm.S 2025-10-20 21:02:22.000000000 +0300 +@@ -1,3 +1,4 @@ + #define STRCMP __strncmp_avx2_rtm + #define USE_AS_STRNCMP 1 ++#define OVERFLOW_STRCMP __strcmp_avx2_rtm + #include "strcmp-avx2-rtm.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strncmp-avx2.S b/sysdeps/x86_64/multiarch/strncmp-avx2.S +--- a/sysdeps/x86_64/multiarch/strncmp-avx2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strncmp-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -1,3 +1,4 @@ + #define STRCMP __strncmp_avx2 + #define USE_AS_STRNCMP 1 ++#define OVERFLOW_STRCMP __strcmp_avx2 + #include "strcmp-avx2.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strncmp.c b/sysdeps/x86_64/multiarch/strncmp.c +--- a/sysdeps/x86_64/multiarch/strncmp.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strncmp.c 2025-10-20 21:02:22.000000000 +0300 +@@ -39,11 +39,11 @@ + const struct cpu_features* cpu_features = __get_cpu_features (); + + if (CPU_FEATURE_USABLE_P (cpu_features, AVX2) ++ && CPU_FEATURE_USABLE_P (cpu_features, BMI2) + && CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load)) + { + if (CPU_FEATURE_USABLE_P (cpu_features, AVX512VL) +- && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW) +- && CPU_FEATURE_USABLE_P (cpu_features, BMI2)) ++ && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW)) + return OPTIMIZE (evex); + + if (CPU_FEATURE_USABLE_P (cpu_features, RTM)) +diff -Nuar a/sysdeps/x86_64/multiarch/strncmp-sse4_2.S b/sysdeps/x86_64/multiarch/strncmp-sse4_2.S +--- a/sysdeps/x86_64/multiarch/strncmp-sse4_2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strncmp-sse4_2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -16,6 +16,8 @@ + License along with the GNU C Library; if not, see + . */ + +-#define STRCMP_SSE42 __strncmp_sse42 +-#define USE_AS_STRNCMP +-#include "strcmp-sse42.S" ++#if IS_IN (libc) ++# define STRCMP_SSE42 __strncmp_sse42 ++# define USE_AS_STRNCMP ++# include "strcmp-sse42.S" ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/strnlen-evex512.S b/sysdeps/x86_64/multiarch/strnlen-evex512.S +--- a/sysdeps/x86_64/multiarch/strnlen-evex512.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strnlen-evex512.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,4 @@ ++#define STRLEN __strnlen_evex512 ++#define USE_AS_STRNLEN 1 ++ ++#include "strlen-evex512.S" +diff -Nuar a/sysdeps/x86_64/multiarch/strpbrk-sse2.c b/sysdeps/x86_64/multiarch/strpbrk-sse2.c +--- a/sysdeps/x86_64/multiarch/strpbrk-sse2.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strpbrk-sse2.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,28 @@ ++/* strpbrk. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#if IS_IN (libc) ++ ++# include ++# define STRPBRK __strpbrk_sse2 ++ ++# undef libc_hidden_builtin_def ++# define libc_hidden_builtin_def(STRPBRK) ++#endif ++ ++#include +diff -Nuar a/sysdeps/x86_64/multiarch/strpbrk-sse2.S b/sysdeps/x86_64/multiarch/strpbrk-sse2.S +--- a/sysdeps/x86_64/multiarch/strpbrk-sse2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strpbrk-sse2.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,29 +0,0 @@ +-/* strpbrk optimized with SSE2. +- Copyright (C) 2017-2022 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 +- . */ +- +-#if IS_IN (libc) +- +-# include +-# define strcspn __strpbrk_sse2 +- +-# undef libc_hidden_builtin_def +-# define libc_hidden_builtin_def(strpbrk) +-#endif +- +-#define USE_AS_STRPBRK +-#include +diff -Nuar a/sysdeps/x86_64/multiarch/strrchr-avx2.S b/sysdeps/x86_64/multiarch/strrchr-avx2.S +--- a/sysdeps/x86_64/multiarch/strrchr-avx2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strrchr-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -27,9 +27,13 @@ + # ifdef USE_AS_WCSRCHR + # define VPBROADCAST vpbroadcastd + # define VPCMPEQ vpcmpeqd ++# define VPMIN vpminud ++# define CHAR_SIZE 4 + # else + # define VPBROADCAST vpbroadcastb + # define VPCMPEQ vpcmpeqb ++# define VPMIN vpminub ++# define CHAR_SIZE 1 + # endif + + # ifndef VZEROUPPER +@@ -41,196 +45,304 @@ + # endif + + # define VEC_SIZE 32 ++# define PAGE_SIZE 4096 + +- .section SECTION(.text),"ax",@progbits +-ENTRY (STRRCHR) +- movd %esi, %xmm4 +- movl %edi, %ecx ++ .section SECTION(.text), "ax", @progbits ++ENTRY(STRRCHR) ++ movd %esi, %xmm7 ++ movl %edi, %eax + /* Broadcast CHAR to YMM4. */ +- VPBROADCAST %xmm4, %ymm4 ++ VPBROADCAST %xmm7, %ymm7 + vpxor %xmm0, %xmm0, %xmm0 + +- /* Check if we may cross page boundary with one vector load. */ +- andl $(2 * VEC_SIZE - 1), %ecx +- cmpl $VEC_SIZE, %ecx +- ja L(cros_page_boundary) ++ /* Shift here instead of `andl` to save code size (saves a fetch ++ block). */ ++ sall $20, %eax ++ cmpl $((PAGE_SIZE - VEC_SIZE) << 20), %eax ++ ja L(cross_page) + ++L(page_cross_continue): + vmovdqu (%rdi), %ymm1 +- VPCMPEQ %ymm1, %ymm0, %ymm2 +- VPCMPEQ %ymm1, %ymm4, %ymm3 +- vpmovmskb %ymm2, %ecx +- vpmovmskb %ymm3, %eax +- addq $VEC_SIZE, %rdi +- +- testl %eax, %eax +- jnz L(first_vec) +- ++ /* Check end of string match. */ ++ VPCMPEQ %ymm1, %ymm0, %ymm6 ++ vpmovmskb %ymm6, %ecx + testl %ecx, %ecx +- jnz L(return_null) +- +- andq $-VEC_SIZE, %rdi +- xorl %edx, %edx +- jmp L(aligned_loop) ++ jz L(aligned_more) + +- .p2align 4 +-L(first_vec): +- /* Check if there is a nul CHAR. */ +- testl %ecx, %ecx +- jnz L(char_and_nul_in_first_vec) +- +- /* Remember the match and keep searching. */ +- movl %eax, %edx +- movq %rdi, %rsi +- andq $-VEC_SIZE, %rdi +- jmp L(aligned_loop) ++ /* Only check match with search CHAR if needed. */ ++ VPCMPEQ %ymm1, %ymm7, %ymm1 ++ vpmovmskb %ymm1, %eax ++ /* Check if match before first zero. */ ++ blsmskl %ecx, %ecx ++ andl %ecx, %eax ++ jz L(ret0) ++ bsrl %eax, %eax ++ addq %rdi, %rax ++ /* We are off by 3 for wcsrchr if search CHAR is non-zero. If ++ search CHAR is zero we are correct. Either way `andq ++ -CHAR_SIZE, %rax` gets the correct result. */ ++# ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++# endif ++L(ret0): ++L(return_vzeroupper): ++ ZERO_UPPER_VEC_REGISTERS_RETURN + +- .p2align 4 +-L(cros_page_boundary): +- andl $(VEC_SIZE - 1), %ecx +- andq $-VEC_SIZE, %rdi +- vmovdqa (%rdi), %ymm1 +- VPCMPEQ %ymm1, %ymm0, %ymm2 +- VPCMPEQ %ymm1, %ymm4, %ymm3 +- vpmovmskb %ymm2, %edx +- vpmovmskb %ymm3, %eax +- shrl %cl, %edx +- shrl %cl, %eax +- addq $VEC_SIZE, %rdi ++ /* Returns for first vec x1/x2 have hard coded backward search ++ path for earlier matches. */ ++ .p2align 4,, 10 ++L(first_vec_x1): ++ VPCMPEQ %ymm2, %ymm7, %ymm6 ++ vpmovmskb %ymm6, %eax ++ blsmskl %ecx, %ecx ++ andl %ecx, %eax ++ jnz L(first_vec_x1_return) ++ ++ .p2align 4,, 4 ++L(first_vec_x0_test): ++ VPCMPEQ %ymm1, %ymm7, %ymm6 ++ vpmovmskb %ymm6, %eax ++ testl %eax, %eax ++ jz L(ret1) ++ bsrl %eax, %eax ++ addq %r8, %rax ++# ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++# endif ++L(ret1): ++ VZEROUPPER_RETURN + +- /* Check if there is a CHAR. */ ++ .p2align 4,, 10 ++L(first_vec_x0_x1_test): ++ VPCMPEQ %ymm2, %ymm7, %ymm6 ++ vpmovmskb %ymm6, %eax ++ /* Check ymm2 for search CHAR match. If no match then check ymm1 ++ before returning. */ + testl %eax, %eax +- jnz L(found_char) ++ jz L(first_vec_x0_test) ++ .p2align 4,, 4 ++L(first_vec_x1_return): ++ bsrl %eax, %eax ++ leaq 1(%rdi, %rax), %rax ++# ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++# endif ++ VZEROUPPER_RETURN ++ + +- testl %edx, %edx +- jnz L(return_null) ++ .p2align 4,, 10 ++L(first_vec_x2): ++ VPCMPEQ %ymm3, %ymm7, %ymm6 ++ vpmovmskb %ymm6, %eax ++ blsmskl %ecx, %ecx ++ /* If no in-range search CHAR match in ymm3 then need to check ++ ymm1/ymm2 for an earlier match (we delay checking search ++ CHAR matches until needed). */ ++ andl %ecx, %eax ++ jz L(first_vec_x0_x1_test) ++ bsrl %eax, %eax ++ leaq (VEC_SIZE + 1)(%rdi, %rax), %rax ++# ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++# endif ++ VZEROUPPER_RETURN + +- jmp L(aligned_loop) + + .p2align 4 +-L(found_char): +- testl %edx, %edx +- jnz L(char_and_nul) +- +- /* Remember the match and keep searching. */ +- movl %eax, %edx +- leaq (%rdi, %rcx), %rsi ++L(aligned_more): ++ /* Save original pointer if match was in VEC 0. */ ++ movq %rdi, %r8 ++ ++ /* Align src. */ ++ orq $(VEC_SIZE - 1), %rdi ++ vmovdqu 1(%rdi), %ymm2 ++ VPCMPEQ %ymm2, %ymm0, %ymm6 ++ vpmovmskb %ymm6, %ecx ++ testl %ecx, %ecx ++ jnz L(first_vec_x1) + ++ vmovdqu (VEC_SIZE + 1)(%rdi), %ymm3 ++ VPCMPEQ %ymm3, %ymm0, %ymm6 ++ vpmovmskb %ymm6, %ecx ++ testl %ecx, %ecx ++ jnz L(first_vec_x2) ++ ++ /* Save pointer again before realigning. */ ++ movq %rdi, %rsi ++ addq $(VEC_SIZE + 1), %rdi ++ andq $-(VEC_SIZE * 2), %rdi + .p2align 4 +-L(aligned_loop): +- vmovdqa (%rdi), %ymm1 +- VPCMPEQ %ymm1, %ymm0, %ymm2 +- addq $VEC_SIZE, %rdi +- VPCMPEQ %ymm1, %ymm4, %ymm3 +- vpmovmskb %ymm2, %ecx +- vpmovmskb %ymm3, %eax +- orl %eax, %ecx +- jnz L(char_nor_null) ++L(first_aligned_loop): ++ /* Do 2x VEC at a time. Any more and the cost of finding the ++ match outweights loop benefit. */ ++ vmovdqa (VEC_SIZE * 0)(%rdi), %ymm4 ++ vmovdqa (VEC_SIZE * 1)(%rdi), %ymm5 ++ ++ VPCMPEQ %ymm4, %ymm7, %ymm6 ++ VPMIN %ymm4, %ymm5, %ymm8 ++ VPCMPEQ %ymm5, %ymm7, %ymm10 ++ vpor %ymm6, %ymm10, %ymm5 ++ VPCMPEQ %ymm8, %ymm0, %ymm8 ++ vpor %ymm5, %ymm8, %ymm9 ++ ++ vpmovmskb %ymm9, %eax ++ addq $(VEC_SIZE * 2), %rdi ++ /* No zero or search CHAR. */ ++ testl %eax, %eax ++ jz L(first_aligned_loop) + +- vmovdqa (%rdi), %ymm1 +- VPCMPEQ %ymm1, %ymm0, %ymm2 +- add $VEC_SIZE, %rdi +- VPCMPEQ %ymm1, %ymm4, %ymm3 +- vpmovmskb %ymm2, %ecx +- vpmovmskb %ymm3, %eax +- orl %eax, %ecx +- jnz L(char_nor_null) ++ /* If no zero CHAR then go to second loop (this allows us to ++ throw away all prior work). */ ++ vpmovmskb %ymm8, %ecx ++ testl %ecx, %ecx ++ jz L(second_aligned_loop_prep) + +- vmovdqa (%rdi), %ymm1 +- VPCMPEQ %ymm1, %ymm0, %ymm2 +- addq $VEC_SIZE, %rdi +- VPCMPEQ %ymm1, %ymm4, %ymm3 +- vpmovmskb %ymm2, %ecx +- vpmovmskb %ymm3, %eax +- orl %eax, %ecx +- jnz L(char_nor_null) ++ /* Search char could be zero so we need to get the true match. ++ */ ++ vpmovmskb %ymm5, %eax ++ testl %eax, %eax ++ jnz L(first_aligned_loop_return) + +- vmovdqa (%rdi), %ymm1 +- VPCMPEQ %ymm1, %ymm0, %ymm2 +- addq $VEC_SIZE, %rdi +- VPCMPEQ %ymm1, %ymm4, %ymm3 +- vpmovmskb %ymm2, %ecx ++ .p2align 4,, 4 ++L(first_vec_x1_or_x2): ++ VPCMPEQ %ymm3, %ymm7, %ymm3 ++ VPCMPEQ %ymm2, %ymm7, %ymm2 + vpmovmskb %ymm3, %eax +- orl %eax, %ecx +- jz L(aligned_loop) ++ vpmovmskb %ymm2, %edx ++ /* Use add for macro-fusion. */ ++ addq %rax, %rdx ++ jz L(first_vec_x0_test) ++ /* NB: We could move this shift to before the branch and save a ++ bit of code size / performance on the fall through. The ++ branch leads to the null case which generally seems hotter ++ than char in first 3x VEC. */ ++ salq $32, %rax ++ addq %rdx, %rax ++ bsrq %rax, %rax ++ leaq 1(%rsi, %rax), %rax ++# ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++# endif ++ VZEROUPPER_RETURN + +- .p2align 4 +-L(char_nor_null): +- /* Find a CHAR or a nul CHAR in a loop. */ +- testl %eax, %eax +- jnz L(match) +-L(return_value): +- testl %edx, %edx +- jz L(return_null) +- movl %edx, %eax +- movq %rsi, %rdi ++ .p2align 4,, 8 ++L(first_aligned_loop_return): ++ VPCMPEQ %ymm4, %ymm0, %ymm4 ++ vpmovmskb %ymm4, %edx ++ salq $32, %rcx ++ orq %rdx, %rcx ++ ++ vpmovmskb %ymm10, %eax ++ vpmovmskb %ymm6, %edx ++ salq $32, %rax ++ orq %rdx, %rax ++ blsmskq %rcx, %rcx ++ andq %rcx, %rax ++ jz L(first_vec_x1_or_x2) + ++ bsrq %rax, %rax ++ leaq -(VEC_SIZE * 2)(%rdi, %rax), %rax + # ifdef USE_AS_WCSRCHR +- /* Keep the first bit for each matching CHAR for bsr. */ +- andl $0x11111111, %eax ++ andq $-CHAR_SIZE, %rax + # endif +- bsrl %eax, %eax +- leaq -VEC_SIZE(%rdi, %rax), %rax +-L(return_vzeroupper): +- ZERO_UPPER_VEC_REGISTERS_RETURN ++ VZEROUPPER_RETURN + ++ /* Search char cannot be zero. */ + .p2align 4 +-L(match): +- /* Find a CHAR. Check if there is a nul CHAR. */ +- vpmovmskb %ymm2, %ecx +- testl %ecx, %ecx +- jnz L(find_nul) +- +- /* Remember the match and keep searching. */ +- movl %eax, %edx ++L(second_aligned_loop_set_furthest_match): ++ /* Save VEC and pointer from most recent match. */ ++L(second_aligned_loop_prep): + movq %rdi, %rsi +- jmp L(aligned_loop) ++ vmovdqu %ymm6, %ymm2 ++ vmovdqu %ymm10, %ymm3 + + .p2align 4 +-L(find_nul): +-# ifdef USE_AS_WCSRCHR +- /* Keep the first bit for each matching CHAR for bsr. */ +- andl $0x11111111, %ecx +- andl $0x11111111, %eax +-# endif +- /* Mask out any matching bits after the nul CHAR. */ +- movl %ecx, %r8d +- subl $1, %r8d +- xorl %ecx, %r8d +- andl %r8d, %eax +- testl %eax, %eax +- /* If there is no CHAR here, return the remembered one. */ +- jz L(return_value) +- bsrl %eax, %eax +- leaq -VEC_SIZE(%rdi, %rax), %rax +- VZEROUPPER_RETURN ++L(second_aligned_loop): ++ /* Search 2x at at time. */ ++ vmovdqa (VEC_SIZE * 0)(%rdi), %ymm4 ++ vmovdqa (VEC_SIZE * 1)(%rdi), %ymm5 ++ ++ VPCMPEQ %ymm4, %ymm7, %ymm6 ++ VPMIN %ymm4, %ymm5, %ymm1 ++ VPCMPEQ %ymm5, %ymm7, %ymm10 ++ vpor %ymm6, %ymm10, %ymm5 ++ VPCMPEQ %ymm1, %ymm0, %ymm1 ++ vpor %ymm5, %ymm1, %ymm9 + +- .p2align 4 +-L(char_and_nul): +- /* Find both a CHAR and a nul CHAR. */ +- addq %rcx, %rdi +- movl %edx, %ecx +-L(char_and_nul_in_first_vec): +-# ifdef USE_AS_WCSRCHR +- /* Keep the first bit for each matching CHAR for bsr. */ +- andl $0x11111111, %ecx +- andl $0x11111111, %eax +-# endif +- /* Mask out any matching bits after the nul CHAR. */ +- movl %ecx, %r8d +- subl $1, %r8d +- xorl %ecx, %r8d +- andl %r8d, %eax ++ vpmovmskb %ymm9, %eax ++ addq $(VEC_SIZE * 2), %rdi + testl %eax, %eax +- /* Return null pointer if the nul CHAR comes first. */ +- jz L(return_null) +- bsrl %eax, %eax +- leaq -VEC_SIZE(%rdi, %rax), %rax ++ jz L(second_aligned_loop) ++ vpmovmskb %ymm1, %ecx ++ testl %ecx, %ecx ++ jz L(second_aligned_loop_set_furthest_match) ++ vpmovmskb %ymm5, %eax ++ testl %eax, %eax ++ jnz L(return_new_match) ++ ++ /* This is the hot patch. We know CHAR is inbounds and that ++ ymm3/ymm2 have latest match. */ ++ .p2align 4,, 4 ++L(return_old_match): ++ vpmovmskb %ymm3, %eax ++ vpmovmskb %ymm2, %edx ++ salq $32, %rax ++ orq %rdx, %rax ++ bsrq %rax, %rax ++ /* Search char cannot be zero so safe to just use lea for ++ wcsrchr. */ ++ leaq (VEC_SIZE * -2 -(CHAR_SIZE - 1))(%rsi, %rax), %rax + VZEROUPPER_RETURN + +- .p2align 4 +-L(return_null): +- xorl %eax, %eax ++ /* Last iteration also potentially has a match. */ ++ .p2align 4,, 8 ++L(return_new_match): ++ VPCMPEQ %ymm4, %ymm0, %ymm4 ++ vpmovmskb %ymm4, %edx ++ salq $32, %rcx ++ orq %rdx, %rcx ++ ++ vpmovmskb %ymm10, %eax ++ vpmovmskb %ymm6, %edx ++ salq $32, %rax ++ orq %rdx, %rax ++ blsmskq %rcx, %rcx ++ andq %rcx, %rax ++ jz L(return_old_match) ++ bsrq %rax, %rax ++ /* Search char cannot be zero so safe to just use lea for ++ wcsrchr. */ ++ leaq (VEC_SIZE * -2 -(CHAR_SIZE - 1))(%rdi, %rax), %rax + VZEROUPPER_RETURN + +-END (STRRCHR) ++ .p2align 4,, 4 ++L(cross_page): ++ movq %rdi, %rsi ++ andq $-VEC_SIZE, %rsi ++ vmovdqu (%rsi), %ymm1 ++ VPCMPEQ %ymm1, %ymm0, %ymm6 ++ vpmovmskb %ymm6, %ecx ++ /* Shift out zero CHAR matches that are before the begining of ++ src (rdi). */ ++ shrxl %edi, %ecx, %ecx ++ testl %ecx, %ecx ++ jz L(page_cross_continue) ++ VPCMPEQ %ymm1, %ymm7, %ymm1 ++ vpmovmskb %ymm1, %eax ++ ++ /* Shift out search CHAR matches that are before the begining of ++ src (rdi). */ ++ shrxl %edi, %eax, %eax ++ blsmskl %ecx, %ecx ++ /* Check if any search CHAR match in range. */ ++ andl %ecx, %eax ++ jz L(ret2) ++ bsrl %eax, %eax ++ addq %rdi, %rax ++# ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++# endif ++L(ret2): ++ VZEROUPPER_RETURN ++END(STRRCHR) + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/strrchr-evex.S b/sysdeps/x86_64/multiarch/strrchr-evex.S +--- a/sysdeps/x86_64/multiarch/strrchr-evex.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strrchr-evex.S 2025-10-20 21:02:22.000000000 +0300 +@@ -24,242 +24,351 @@ + # define STRRCHR __strrchr_evex + # endif + +-# define VMOVU vmovdqu64 +-# define VMOVA vmovdqa64 ++# define VMOVU vmovdqu64 ++# define VMOVA vmovdqa64 + + # ifdef USE_AS_WCSRCHR ++# define SHIFT_REG esi ++ ++# define kunpck kunpckbw ++# define kmov_2x kmovd ++# define maskz_2x ecx ++# define maskm_2x eax ++# define CHAR_SIZE 4 ++# define VPMIN vpminud ++# define VPTESTN vptestnmd + # define VPBROADCAST vpbroadcastd +-# define VPCMP vpcmpd +-# define SHIFT_REG r8d ++# define VPCMP vpcmpd + # else ++# define SHIFT_REG edi ++ ++# define kunpck kunpckdq ++# define kmov_2x kmovq ++# define maskz_2x rcx ++# define maskm_2x rax ++ ++# define CHAR_SIZE 1 ++# define VPMIN vpminub ++# define VPTESTN vptestnmb + # define VPBROADCAST vpbroadcastb +-# define VPCMP vpcmpb +-# define SHIFT_REG ecx ++# define VPCMP vpcmpb + # endif + + # define XMMZERO xmm16 + # define YMMZERO ymm16 + # define YMMMATCH ymm17 +-# define YMM1 ymm18 ++# define YMMSAVE ymm18 ++ ++# define YMM1 ymm19 ++# define YMM2 ymm20 ++# define YMM3 ymm21 ++# define YMM4 ymm22 ++# define YMM5 ymm23 ++# define YMM6 ymm24 ++# define YMM7 ymm25 ++# define YMM8 ymm26 + +-# define VEC_SIZE 32 + +- .section .text.evex,"ax",@progbits +-ENTRY (STRRCHR) +- movl %edi, %ecx ++# define VEC_SIZE 32 ++# define PAGE_SIZE 4096 ++ .section .text.evex, "ax", @progbits ++ENTRY(STRRCHR) ++ movl %edi, %eax + /* Broadcast CHAR to YMMMATCH. */ + VPBROADCAST %esi, %YMMMATCH + +- vpxorq %XMMZERO, %XMMZERO, %XMMZERO +- +- /* Check if we may cross page boundary with one vector load. */ +- andl $(2 * VEC_SIZE - 1), %ecx +- cmpl $VEC_SIZE, %ecx +- ja L(cros_page_boundary) ++ andl $(PAGE_SIZE - 1), %eax ++ cmpl $(PAGE_SIZE - VEC_SIZE), %eax ++ jg L(cross_page_boundary) + ++L(page_cross_continue): + VMOVU (%rdi), %YMM1 +- +- /* Each bit in K0 represents a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM1, %k0 +- /* Each bit in K1 represents a CHAR in YMM1. */ +- VPCMP $0, %YMMMATCH, %YMM1, %k1 ++ /* k0 has a 1 for each zero CHAR in YMM1. */ ++ VPTESTN %YMM1, %YMM1, %k0 + kmovd %k0, %ecx +- kmovd %k1, %eax +- +- addq $VEC_SIZE, %rdi +- +- testl %eax, %eax +- jnz L(first_vec) +- + testl %ecx, %ecx +- jnz L(return_null) +- +- andq $-VEC_SIZE, %rdi +- xorl %edx, %edx +- jmp L(aligned_loop) +- +- .p2align 4 +-L(first_vec): +- /* Check if there is a null byte. */ +- testl %ecx, %ecx +- jnz L(char_and_nul_in_first_vec) +- +- /* Remember the match and keep searching. */ +- movl %eax, %edx +- movq %rdi, %rsi +- andq $-VEC_SIZE, %rdi +- jmp L(aligned_loop) +- +- .p2align 4 +-L(cros_page_boundary): +- andl $(VEC_SIZE - 1), %ecx +- andq $-VEC_SIZE, %rdi ++ jz L(aligned_more) ++ /* fallthrough: zero CHAR in first VEC. */ + ++ /* K1 has a 1 for each search CHAR match in YMM1. */ ++ VPCMP $0, %YMMMATCH, %YMM1, %k1 ++ kmovd %k1, %eax ++ /* Build mask up until first zero CHAR (used to mask of ++ potential search CHAR matches past the end of the string). ++ */ ++ blsmskl %ecx, %ecx ++ andl %ecx, %eax ++ jz L(ret0) ++ /* Get last match (the `andl` removed any out of bounds ++ matches). */ ++ bsrl %eax, %eax + # ifdef USE_AS_WCSRCHR +- /* NB: Divide shift count by 4 since each bit in K1 represent 4 +- bytes. */ +- movl %ecx, %SHIFT_REG +- sarl $2, %SHIFT_REG ++ leaq (%rdi, %rax, CHAR_SIZE), %rax ++# else ++ addq %rdi, %rax + # endif ++L(ret0): ++ ret + +- VMOVA (%rdi), %YMM1 +- +- /* Each bit in K0 represents a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM1, %k0 +- /* Each bit in K1 represents a CHAR in YMM1. */ ++ /* Returns for first vec x1/x2/x3 have hard coded backward ++ search path for earlier matches. */ ++ .p2align 4,, 6 ++L(first_vec_x1): ++ VPCMP $0, %YMMMATCH, %YMM2, %k1 ++ kmovd %k1, %eax ++ blsmskl %ecx, %ecx ++ /* eax non-zero if search CHAR in range. */ ++ andl %ecx, %eax ++ jnz L(first_vec_x1_return) ++ ++ /* fallthrough: no match in YMM2 then need to check for earlier ++ matches (in YMM1). */ ++ .p2align 4,, 4 ++L(first_vec_x0_test): + VPCMP $0, %YMMMATCH, %YMM1, %k1 +- kmovd %k0, %edx + kmovd %k1, %eax +- +- shrxl %SHIFT_REG, %edx, %edx +- shrxl %SHIFT_REG, %eax, %eax +- addq $VEC_SIZE, %rdi +- +- /* Check if there is a CHAR. */ + testl %eax, %eax +- jnz L(found_char) +- +- testl %edx, %edx +- jnz L(return_null) +- +- jmp L(aligned_loop) ++ jz L(ret1) ++ bsrl %eax, %eax ++# ifdef USE_AS_WCSRCHR ++ leaq (%rsi, %rax, CHAR_SIZE), %rax ++# else ++ addq %rsi, %rax ++# endif ++L(ret1): ++ ret + +- .p2align 4 +-L(found_char): +- testl %edx, %edx +- jnz L(char_and_nul) +- +- /* Remember the match and keep searching. */ +- movl %eax, %edx +- leaq (%rdi, %rcx), %rsi ++ .p2align 4,, 10 ++L(first_vec_x1_or_x2): ++ VPCMP $0, %YMM3, %YMMMATCH, %k3 ++ VPCMP $0, %YMM2, %YMMMATCH, %k2 ++ /* K2 and K3 have 1 for any search CHAR match. Test if any ++ matches between either of them. Otherwise check YMM1. */ ++ kortestd %k2, %k3 ++ jz L(first_vec_x0_test) ++ ++ /* Guranteed that YMM2 and YMM3 are within range so merge the ++ two bitmasks then get last result. */ ++ kunpck %k2, %k3, %k3 ++ kmovq %k3, %rax ++ bsrq %rax, %rax ++ leaq (VEC_SIZE)(%r8, %rax, CHAR_SIZE), %rax ++ ret + +- .p2align 4 +-L(aligned_loop): +- VMOVA (%rdi), %YMM1 +- addq $VEC_SIZE, %rdi +- +- /* Each bit in K0 represents a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM1, %k0 +- /* Each bit in K1 represents a CHAR in YMM1. */ +- VPCMP $0, %YMMMATCH, %YMM1, %k1 +- kmovd %k0, %ecx ++ .p2align 4,, 6 ++L(first_vec_x3): ++ VPCMP $0, %YMMMATCH, %YMM4, %k1 + kmovd %k1, %eax +- orl %eax, %ecx +- jnz L(char_nor_null) ++ blsmskl %ecx, %ecx ++ /* If no search CHAR match in range check YMM1/YMM2/YMM3. */ ++ andl %ecx, %eax ++ jz L(first_vec_x1_or_x2) ++ bsrl %eax, %eax ++ leaq (VEC_SIZE * 3)(%rdi, %rax, CHAR_SIZE), %rax ++ ret + +- VMOVA (%rdi), %YMM1 +- add $VEC_SIZE, %rdi ++ .p2align 4,, 6 ++L(first_vec_x0_x1_test): ++ VPCMP $0, %YMMMATCH, %YMM2, %k1 ++ kmovd %k1, %eax ++ /* Check YMM2 for last match first. If no match try YMM1. */ ++ testl %eax, %eax ++ jz L(first_vec_x0_test) ++ .p2align 4,, 4 ++L(first_vec_x1_return): ++ bsrl %eax, %eax ++ leaq (VEC_SIZE)(%rdi, %rax, CHAR_SIZE), %rax ++ ret + +- /* Each bit in K0 represents a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM1, %k0 +- /* Each bit in K1 represents a CHAR in YMM1. */ +- VPCMP $0, %YMMMATCH, %YMM1, %k1 +- kmovd %k0, %ecx ++ .p2align 4,, 10 ++L(first_vec_x2): ++ VPCMP $0, %YMMMATCH, %YMM3, %k1 + kmovd %k1, %eax +- orl %eax, %ecx +- jnz L(char_nor_null) ++ blsmskl %ecx, %ecx ++ /* Check YMM3 for last match first. If no match try YMM2/YMM1. ++ */ ++ andl %ecx, %eax ++ jz L(first_vec_x0_x1_test) ++ bsrl %eax, %eax ++ leaq (VEC_SIZE * 2)(%rdi, %rax, CHAR_SIZE), %rax ++ ret + +- VMOVA (%rdi), %YMM1 +- addq $VEC_SIZE, %rdi + +- /* Each bit in K0 represents a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM1, %k0 +- /* Each bit in K1 represents a CHAR in YMM1. */ +- VPCMP $0, %YMMMATCH, %YMM1, %k1 ++ .p2align 4 ++L(aligned_more): ++ /* Need to keep original pointer incase YMM1 has last match. */ ++ movq %rdi, %rsi ++ andq $-VEC_SIZE, %rdi ++ VMOVU VEC_SIZE(%rdi), %YMM2 ++ VPTESTN %YMM2, %YMM2, %k0 + kmovd %k0, %ecx +- kmovd %k1, %eax +- orl %eax, %ecx +- jnz L(char_nor_null) ++ testl %ecx, %ecx ++ jnz L(first_vec_x1) + +- VMOVA (%rdi), %YMM1 +- addq $VEC_SIZE, %rdi ++ VMOVU (VEC_SIZE * 2)(%rdi), %YMM3 ++ VPTESTN %YMM3, %YMM3, %k0 ++ kmovd %k0, %ecx ++ testl %ecx, %ecx ++ jnz L(first_vec_x2) + +- /* Each bit in K0 represents a null byte in YMM1. */ +- VPCMP $0, %YMMZERO, %YMM1, %k0 +- /* Each bit in K1 represents a CHAR in YMM1. */ +- VPCMP $0, %YMMMATCH, %YMM1, %k1 ++ VMOVU (VEC_SIZE * 3)(%rdi), %YMM4 ++ VPTESTN %YMM4, %YMM4, %k0 + kmovd %k0, %ecx +- kmovd %k1, %eax +- orl %eax, %ecx +- jz L(aligned_loop) ++ movq %rdi, %r8 ++ testl %ecx, %ecx ++ jnz L(first_vec_x3) + ++ andq $-(VEC_SIZE * 2), %rdi + .p2align 4 +-L(char_nor_null): +- /* Find a CHAR or a null byte in a loop. */ ++L(first_aligned_loop): ++ /* Preserve YMM1, YMM2, YMM3, and YMM4 until we can gurantee ++ they don't store a match. */ ++ VMOVA (VEC_SIZE * 4)(%rdi), %YMM5 ++ VMOVA (VEC_SIZE * 5)(%rdi), %YMM6 ++ ++ VPCMP $0, %YMM5, %YMMMATCH, %k2 ++ vpxord %YMM6, %YMMMATCH, %YMM7 ++ ++ VPMIN %YMM5, %YMM6, %YMM8 ++ VPMIN %YMM8, %YMM7, %YMM7 ++ ++ VPTESTN %YMM7, %YMM7, %k1 ++ subq $(VEC_SIZE * -2), %rdi ++ kortestd %k1, %k2 ++ jz L(first_aligned_loop) ++ ++ VPCMP $0, %YMM6, %YMMMATCH, %k3 ++ VPTESTN %YMM8, %YMM8, %k1 ++ ktestd %k1, %k1 ++ jz L(second_aligned_loop_prep) ++ ++ kortestd %k2, %k3 ++ jnz L(return_first_aligned_loop) ++ ++ .p2align 4,, 6 ++L(first_vec_x1_or_x2_or_x3): ++ VPCMP $0, %YMM4, %YMMMATCH, %k4 ++ kmovd %k4, %eax + testl %eax, %eax +- jnz L(match) +-L(return_value): +- testl %edx, %edx +- jz L(return_null) +- movl %edx, %eax +- movq %rsi, %rdi ++ jz L(first_vec_x1_or_x2) + bsrl %eax, %eax +-# ifdef USE_AS_WCSRCHR +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- leaq -VEC_SIZE(%rdi, %rax, 4), %rax +-# else +- leaq -VEC_SIZE(%rdi, %rax), %rax +-# endif ++ leaq (VEC_SIZE * 3)(%r8, %rax, CHAR_SIZE), %rax + ret + +- .p2align 4 +-L(match): +- /* Find a CHAR. Check if there is a null byte. */ +- kmovd %k0, %ecx +- testl %ecx, %ecx +- jnz L(find_nul) ++ .p2align 4,, 8 ++L(return_first_aligned_loop): ++ VPTESTN %YMM5, %YMM5, %k0 ++ kunpck %k0, %k1, %k0 ++ kmov_2x %k0, %maskz_2x ++ ++ blsmsk %maskz_2x, %maskz_2x ++ kunpck %k2, %k3, %k3 ++ kmov_2x %k3, %maskm_2x ++ and %maskz_2x, %maskm_2x ++ jz L(first_vec_x1_or_x2_or_x3) + +- /* Remember the match and keep searching. */ +- movl %eax, %edx ++ bsr %maskm_2x, %maskm_2x ++ leaq (VEC_SIZE * 2)(%rdi, %rax, CHAR_SIZE), %rax ++ ret ++ ++ .p2align 4 ++ /* We can throw away the work done for the first 4x checks here ++ as we have a later match. This is the 'fast' path persay. ++ */ ++L(second_aligned_loop_prep): ++L(second_aligned_loop_set_furthest_match): + movq %rdi, %rsi +- jmp L(aligned_loop) ++ kunpck %k2, %k3, %k4 + + .p2align 4 +-L(find_nul): +- /* Mask out any matching bits after the null byte. */ +- movl %ecx, %r8d +- subl $1, %r8d +- xorl %ecx, %r8d +- andl %r8d, %eax +- testl %eax, %eax +- /* If there is no CHAR here, return the remembered one. */ +- jz L(return_value) +- bsrl %eax, %eax ++L(second_aligned_loop): ++ VMOVU (VEC_SIZE * 4)(%rdi), %YMM1 ++ VMOVU (VEC_SIZE * 5)(%rdi), %YMM2 ++ ++ VPCMP $0, %YMM1, %YMMMATCH, %k2 ++ vpxord %YMM2, %YMMMATCH, %YMM3 ++ ++ VPMIN %YMM1, %YMM2, %YMM4 ++ VPMIN %YMM3, %YMM4, %YMM3 ++ ++ VPTESTN %YMM3, %YMM3, %k1 ++ subq $(VEC_SIZE * -2), %rdi ++ kortestd %k1, %k2 ++ jz L(second_aligned_loop) ++ ++ VPCMP $0, %YMM2, %YMMMATCH, %k3 ++ VPTESTN %YMM4, %YMM4, %k1 ++ ktestd %k1, %k1 ++ jz L(second_aligned_loop_set_furthest_match) ++ ++ kortestd %k2, %k3 ++ /* branch here because there is a significant advantage interms ++ of output dependency chance in using edx. */ ++ jnz L(return_new_match) ++L(return_old_match): ++ kmovq %k4, %rax ++ bsrq %rax, %rax ++ leaq (VEC_SIZE * 2)(%rsi, %rax, CHAR_SIZE), %rax ++ ret ++ ++L(return_new_match): ++ VPTESTN %YMM1, %YMM1, %k0 ++ kunpck %k0, %k1, %k0 ++ kmov_2x %k0, %maskz_2x ++ ++ blsmsk %maskz_2x, %maskz_2x ++ kunpck %k2, %k3, %k3 ++ kmov_2x %k3, %maskm_2x ++ and %maskz_2x, %maskm_2x ++ jz L(return_old_match) ++ ++ bsr %maskm_2x, %maskm_2x ++ leaq (VEC_SIZE * 2)(%rdi, %rax, CHAR_SIZE), %rax ++ ret ++ ++L(cross_page_boundary): ++ /* eax contains all the page offset bits of src (rdi). `xor rdi, ++ rax` sets pointer will all page offset bits cleared so ++ offset of (PAGE_SIZE - VEC_SIZE) will get last aligned VEC ++ before page cross (guranteed to be safe to read). Doing this ++ as opposed to `movq %rdi, %rax; andq $-VEC_SIZE, %rax` saves ++ a bit of code size. */ ++ xorq %rdi, %rax ++ VMOVU (PAGE_SIZE - VEC_SIZE)(%rax), %YMM1 ++ VPTESTN %YMM1, %YMM1, %k0 ++ kmovd %k0, %ecx ++ ++ /* Shift out zero CHAR matches that are before the begining of ++ src (rdi). */ + # ifdef USE_AS_WCSRCHR +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- leaq -VEC_SIZE(%rdi, %rax, 4), %rax +-# else +- leaq -VEC_SIZE(%rdi, %rax), %rax ++ movl %edi, %esi ++ andl $(VEC_SIZE - 1), %esi ++ shrl $2, %esi + # endif +- ret ++ shrxl %SHIFT_REG, %ecx, %ecx + +- .p2align 4 +-L(char_and_nul): +- /* Find both a CHAR and a null byte. */ +- addq %rcx, %rdi +- movl %edx, %ecx +-L(char_and_nul_in_first_vec): +- /* Mask out any matching bits after the null byte. */ +- movl %ecx, %r8d +- subl $1, %r8d +- xorl %ecx, %r8d +- andl %r8d, %eax +- testl %eax, %eax +- /* Return null pointer if the null byte comes first. */ +- jz L(return_null) ++ testl %ecx, %ecx ++ jz L(page_cross_continue) ++ ++ /* Found zero CHAR so need to test for search CHAR. */ ++ VPCMP $0, %YMMMATCH, %YMM1, %k1 ++ kmovd %k1, %eax ++ /* Shift out search CHAR matches that are before the begining of ++ src (rdi). */ ++ shrxl %SHIFT_REG, %eax, %eax ++ ++ /* Check if any search CHAR match in range. */ ++ blsmskl %ecx, %ecx ++ andl %ecx, %eax ++ jz L(ret3) + bsrl %eax, %eax + # ifdef USE_AS_WCSRCHR +- /* NB: Multiply wchar_t count by 4 to get the number of bytes. */ +- leaq -VEC_SIZE(%rdi, %rax, 4), %rax ++ leaq (%rdi, %rax, CHAR_SIZE), %rax + # else +- leaq -VEC_SIZE(%rdi, %rax), %rax ++ addq %rdi, %rax + # endif ++L(ret3): + ret + +- .p2align 4 +-L(return_null): +- xorl %eax, %eax +- ret +- +-END (STRRCHR) ++END(STRRCHR) + #endif +diff -Nuar a/sysdeps/x86_64/multiarch/strrchr-sse2.S b/sysdeps/x86_64/multiarch/strrchr-sse2.S +--- a/sysdeps/x86_64/multiarch/strrchr-sse2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strrchr-sse2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -17,7 +17,7 @@ + . */ + + #if IS_IN (libc) +-# define strrchr __strrchr_sse2 ++# define STRRCHR __strrchr_sse2 + + # undef weak_alias + # define weak_alias(strrchr, rindex) +diff -Nuar a/sysdeps/x86_64/multiarch/strspn-c.c b/sysdeps/x86_64/multiarch/strspn-c.c +--- a/sysdeps/x86_64/multiarch/strspn-c.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strspn-c.c 2025-10-20 21:02:22.000000000 +0300 +@@ -62,81 +62,73 @@ + return 0; + + const char *aligned; +- __m128i mask; +- int offset = (int) ((size_t) a & 15); ++ __m128i mask, maskz, zero; ++ unsigned int maskz_bits; ++ unsigned int offset = (int) ((size_t) a & 15); ++ zero = _mm_set1_epi8 (0); + if (offset != 0) + { + /* Load masks. */ + aligned = (const char *) ((size_t) a & -16L); + __m128i mask0 = _mm_load_si128 ((__m128i *) aligned); +- +- mask = __m128i_shift_right (mask0, offset); ++ maskz = _mm_cmpeq_epi8 (mask0, zero); + + /* Find where the NULL terminator is. */ +- int length = _mm_cmpistri (mask, mask, 0x3a); +- if (length == 16 - offset) +- { +- /* There is no NULL terminator. */ +- __m128i mask1 = _mm_load_si128 ((__m128i *) (aligned + 16)); +- int index = _mm_cmpistri (mask1, mask1, 0x3a); +- length += index; +- +- /* Don't use SSE4.2 if the length of A > 16. */ +- if (length > 16) +- return __strspn_sse2 (s, a); +- +- if (index != 0) +- { +- /* Combine mask0 and mask1. We could play games with +- palignr, but frankly this data should be in L1 now +- so do the merge via an unaligned load. */ +- mask = _mm_loadu_si128 ((__m128i *) a); +- } +- } ++ maskz_bits = _mm_movemask_epi8 (maskz) >> offset; ++ if (maskz_bits != 0) ++ { ++ mask = __m128i_shift_right (mask0, offset); ++ offset = (unsigned int) ((size_t) s & 15); ++ if (offset) ++ goto start_unaligned; ++ ++ aligned = s; ++ goto start_loop; ++ } + } +- else +- { +- /* A is aligned. */ +- mask = _mm_load_si128 ((__m128i *) a); + +- /* Find where the NULL terminator is. */ +- int length = _mm_cmpistri (mask, mask, 0x3a); +- if (length == 16) +- { +- /* There is no NULL terminator. Don't use SSE4.2 if the length +- of A > 16. */ +- if (a[16] != 0) +- return __strspn_sse2 (s, a); +- } ++ /* A is aligned. */ ++ mask = _mm_loadu_si128 ((__m128i *) a); ++ ++ /* Find where the NULL terminator is. */ ++ maskz = _mm_cmpeq_epi8 (mask, zero); ++ maskz_bits = _mm_movemask_epi8 (maskz); ++ if (maskz_bits == 0) ++ { ++ /* There is no NULL terminator. Don't use SSE4.2 if the length ++ of A > 16. */ ++ if (a[16] != 0) ++ return __strspn_sse2 (s, a); + } ++ aligned = s; ++ offset = (unsigned int) ((size_t) s & 15); + +- offset = (int) ((size_t) s & 15); + if (offset != 0) + { ++ start_unaligned: + /* Check partial string. */ + aligned = (const char *) ((size_t) s & -16L); + __m128i value = _mm_load_si128 ((__m128i *) aligned); ++ __m128i adj_value = __m128i_shift_right (value, offset); + +- value = __m128i_shift_right (value, offset); +- +- int length = _mm_cmpistri (mask, value, 0x12); ++ unsigned int length = _mm_cmpistri (mask, adj_value, 0x12); + /* No need to check CFlag since it is always 1. */ + if (length < 16 - offset) + return length; + /* Find where the NULL terminator is. */ +- int index = _mm_cmpistri (value, value, 0x3a); +- if (index < 16 - offset) ++ maskz = _mm_cmpeq_epi8 (value, zero); ++ maskz_bits = _mm_movemask_epi8 (maskz) >> offset; ++ if (maskz_bits != 0) + return length; + aligned += 16; + } +- else +- aligned = s; + ++start_loop: + while (1) + { + __m128i value = _mm_load_si128 ((__m128i *) aligned); +- int index = _mm_cmpistri (mask, value, 0x12); +- int cflag = _mm_cmpistrc (mask, value, 0x12); ++ unsigned int index = _mm_cmpistri (mask, value, 0x12); ++ unsigned int cflag = _mm_cmpistrc (mask, value, 0x12); + if (cflag) + return (size_t) (aligned + index - s); + aligned += 16; +diff -Nuar a/sysdeps/x86_64/multiarch/strspn-sse2.c b/sysdeps/x86_64/multiarch/strspn-sse2.c +--- a/sysdeps/x86_64/multiarch/strspn-sse2.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strspn-sse2.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,28 @@ ++/* strspn. ++ Copyright (C) 2017-2022 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 ++ . */ ++ ++#if IS_IN (libc) ++ ++# include ++# define STRSPN __strspn_sse2 ++ ++# undef libc_hidden_builtin_def ++# define libc_hidden_builtin_def(STRSPN) ++#endif ++ ++#include +diff -Nuar a/sysdeps/x86_64/multiarch/strspn-sse2.S b/sysdeps/x86_64/multiarch/strspn-sse2.S +--- a/sysdeps/x86_64/multiarch/strspn-sse2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strspn-sse2.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,28 +0,0 @@ +-/* strspn optimized with SSE2. +- Copyright (C) 2017-2022 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 +- . */ +- +-#if IS_IN (libc) +- +-# include +-# define strspn __strspn_sse2 +- +-# undef libc_hidden_builtin_def +-# define libc_hidden_builtin_def(strspn) +-#endif +- +-#include +diff -Nuar a/sysdeps/x86_64/multiarch/strstr-avx512.c b/sysdeps/x86_64/multiarch/strstr-avx512.c +--- a/sysdeps/x86_64/multiarch/strstr-avx512.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/strstr-avx512.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,218 @@ ++/* strstr optimized with 512-bit AVX-512 instructions ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++#define FULL_MMASK64 0xffffffffffffffff ++#define ONE_64BIT 0x1ull ++#define ZMM_SIZE_IN_BYTES 64 ++#define PAGESIZE 4096 ++ ++#define cvtmask64_u64(...) (uint64_t) (__VA_ARGS__) ++#define kshiftri_mask64(x, y) ((x) >> (y)) ++#define kand_mask64(x, y) ((x) & (y)) ++ ++/* ++ Returns the index of the first edge within the needle, returns 0 if no edge ++ is found. Example: 'ab' is the first edge in 'aaaaaaaaaabaarddg' ++ */ ++static inline size_t ++find_edge_in_needle (const char *ned) ++{ ++ size_t ind = 0; ++ while (ned[ind + 1] != '\0') ++ { ++ if (ned[ind] != ned[ind + 1]) ++ return ind; ++ else ++ ind = ind + 1; ++ } ++ return 0; ++} ++ ++/* ++ Compare needle with haystack byte by byte at specified location ++ */ ++static inline bool ++verify_string_match (const char *hay, const size_t hay_index, const char *ned, ++ size_t ind) ++{ ++ while (ned[ind] != '\0') ++ { ++ if (ned[ind] != hay[hay_index + ind]) ++ return false; ++ ind = ind + 1; ++ } ++ return true; ++} ++ ++/* ++ Compare needle with haystack at specified location. The first 64 bytes are ++ compared using a ZMM register. ++ */ ++static inline bool ++verify_string_match_avx512 (const char *hay, const size_t hay_index, ++ const char *ned, const __mmask64 ned_mask, ++ const __m512i ned_zmm) ++{ ++ /* check first 64 bytes using zmm and then scalar */ ++ __m512i hay_zmm = _mm512_loadu_si512 (hay + hay_index); // safe to do so ++ __mmask64 match = _mm512_mask_cmpneq_epi8_mask (ned_mask, hay_zmm, ned_zmm); ++ if (match != 0x0) // failed the first few chars ++ return false; ++ else if (ned_mask == FULL_MMASK64) ++ return verify_string_match (hay, hay_index, ned, ZMM_SIZE_IN_BYTES); ++ return true; ++} ++ ++char * ++__strstr_avx512 (const char *haystack, const char *ned) ++{ ++ char first = ned[0]; ++ if (first == '\0') ++ return (char *)haystack; ++ if (ned[1] == '\0') ++ return (char *)strchr (haystack, ned[0]); ++ ++ size_t edge = find_edge_in_needle (ned); ++ ++ /* ensure haystack is as long as the pos of edge in needle */ ++ for (int ii = 0; ii < edge; ++ii) ++ { ++ if (haystack[ii] == '\0') ++ return NULL; ++ } ++ ++ /* ++ Load 64 bytes of the needle and save it to a zmm register ++ Read one cache line at a time to avoid loading across a page boundary ++ */ ++ __mmask64 ned_load_mask = _bzhi_u64 ( ++ FULL_MMASK64, 64 - ((uintptr_t) (ned) & 63)); ++ __m512i ned_zmm = _mm512_maskz_loadu_epi8 (ned_load_mask, ned); ++ __mmask64 ned_nullmask ++ = _mm512_mask_testn_epi8_mask (ned_load_mask, ned_zmm, ned_zmm); ++ ++ if (__glibc_unlikely (ned_nullmask == 0x0)) ++ { ++ ned_zmm = _mm512_loadu_si512 (ned); ++ ned_nullmask = _mm512_testn_epi8_mask (ned_zmm, ned_zmm); ++ ned_load_mask = ned_nullmask ^ (ned_nullmask - ONE_64BIT); ++ if (ned_nullmask != 0x0) ++ ned_load_mask = ned_load_mask >> 1; ++ } ++ else ++ { ++ ned_load_mask = ned_nullmask ^ (ned_nullmask - ONE_64BIT); ++ ned_load_mask = ned_load_mask >> 1; ++ } ++ const __m512i ned0 = _mm512_set1_epi8 (ned[edge]); ++ const __m512i ned1 = _mm512_set1_epi8 (ned[edge + 1]); ++ ++ /* ++ Read the bytes of haystack in the current cache line ++ */ ++ size_t hay_index = edge; ++ __mmask64 loadmask = _bzhi_u64 ( ++ FULL_MMASK64, 64 - ((uintptr_t) (haystack + hay_index) & 63)); ++ /* First load is a partial cache line */ ++ __m512i hay0 = _mm512_maskz_loadu_epi8 (loadmask, haystack + hay_index); ++ /* Search for NULL and compare only till null char */ ++ uint64_t nullmask ++ = cvtmask64_u64 (_mm512_mask_testn_epi8_mask (loadmask, hay0, hay0)); ++ uint64_t cmpmask = nullmask ^ (nullmask - ONE_64BIT); ++ cmpmask = cmpmask & cvtmask64_u64 (loadmask); ++ /* Search for the 2 charaters of needle */ ++ __mmask64 k0 = _mm512_cmpeq_epi8_mask (hay0, ned0); ++ __mmask64 k1 = _mm512_cmpeq_epi8_mask (hay0, ned1); ++ k1 = kshiftri_mask64 (k1, 1); ++ /* k2 masks tell us if both chars from needle match */ ++ uint64_t k2 = cvtmask64_u64 (kand_mask64 (k0, k1)) & cmpmask; ++ /* For every match, search for the entire needle for a full match */ ++ while (k2) ++ { ++ uint64_t bitcount = _tzcnt_u64 (k2); ++ k2 = _blsr_u64 (k2); ++ size_t match_pos = hay_index + bitcount - edge; ++ if (((uintptr_t) (haystack + match_pos) & (PAGESIZE - 1)) ++ < PAGESIZE - 1 - ZMM_SIZE_IN_BYTES) ++ { ++ /* ++ * Use vector compare as long as you are not crossing a page ++ */ ++ if (verify_string_match_avx512 (haystack, match_pos, ned, ++ ned_load_mask, ned_zmm)) ++ return (char *)haystack + match_pos; ++ } ++ else ++ { ++ if (verify_string_match (haystack, match_pos, ned, 0)) ++ return (char *)haystack + match_pos; ++ } ++ } ++ /* We haven't checked for potential match at the last char yet */ ++ haystack = (const char *)(((uintptr_t) (haystack + hay_index) | 63)); ++ hay_index = 0; ++ ++ /* ++ Loop over one cache line at a time to prevent reading over page ++ boundary ++ */ ++ __m512i hay1; ++ while (nullmask == 0) ++ { ++ hay0 = _mm512_loadu_si512 (haystack + hay_index); ++ hay1 = _mm512_load_si512 (haystack + hay_index ++ + 1); // Always 64 byte aligned ++ nullmask = cvtmask64_u64 (_mm512_testn_epi8_mask (hay1, hay1)); ++ /* Compare only till null char */ ++ cmpmask = nullmask ^ (nullmask - ONE_64BIT); ++ k0 = _mm512_cmpeq_epi8_mask (hay0, ned0); ++ k1 = _mm512_cmpeq_epi8_mask (hay1, ned1); ++ /* k2 masks tell us if both chars from needle match */ ++ k2 = cvtmask64_u64 (kand_mask64 (k0, k1)) & cmpmask; ++ /* For every match, compare full strings for potential match */ ++ while (k2) ++ { ++ uint64_t bitcount = _tzcnt_u64 (k2); ++ k2 = _blsr_u64 (k2); ++ size_t match_pos = hay_index + bitcount - edge; ++ if (((uintptr_t) (haystack + match_pos) & (PAGESIZE - 1)) ++ < PAGESIZE - 1 - ZMM_SIZE_IN_BYTES) ++ { ++ /* ++ * Use vector compare as long as you are not crossing a page ++ */ ++ if (verify_string_match_avx512 (haystack, match_pos, ned, ++ ned_load_mask, ned_zmm)) ++ return (char *)haystack + match_pos; ++ } ++ else ++ { ++ /* Compare byte by byte */ ++ if (verify_string_match (haystack, match_pos, ned, 0)) ++ return (char *)haystack + match_pos; ++ } ++ } ++ hay_index += ZMM_SIZE_IN_BYTES; ++ } ++ return NULL; ++} +diff -Nuar a/sysdeps/x86_64/multiarch/strstr.c b/sysdeps/x86_64/multiarch/strstr.c +--- a/sysdeps/x86_64/multiarch/strstr.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/strstr.c 2025-10-20 21:02:22.000000000 +0300 +@@ -35,16 +35,32 @@ + + extern __typeof (__redirect_strstr) __strstr_sse2_unaligned attribute_hidden; + extern __typeof (__redirect_strstr) __strstr_sse2 attribute_hidden; ++extern __typeof (__redirect_strstr) __strstr_avx512 attribute_hidden; + + #include "init-arch.h" + + /* Avoid DWARF definition DIE on ifunc symbol so that GDB can handle + ifunc symbol properly. */ + extern __typeof (__redirect_strstr) __libc_strstr; +-libc_ifunc (__libc_strstr, +- HAS_ARCH_FEATURE (Fast_Unaligned_Load) +- ? __strstr_sse2_unaligned +- : __strstr_sse2) + ++static inline void * ++IFUNC_SELECTOR (void) ++{ ++ const struct cpu_features *cpu_features = __get_cpu_features (); ++ ++ if (!CPU_FEATURES_ARCH_P (cpu_features, Prefer_No_AVX512) ++ && CPU_FEATURE_USABLE_P (cpu_features, AVX512VL) ++ && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW) ++ && CPU_FEATURE_USABLE_P (cpu_features, AVX512DQ) ++ && CPU_FEATURE_USABLE_P (cpu_features, BMI2)) ++ return __strstr_avx512; ++ ++ if (CPU_FEATURES_ARCH_P (cpu_features, Fast_Unaligned_Load)) ++ return __strstr_sse2_unaligned; ++ ++ return __strstr_sse2; ++} ++ ++libc_ifunc_redirected (__redirect_strstr, __libc_strstr, IFUNC_SELECTOR ()); + #undef strstr + strong_alias (__libc_strstr, strstr) +diff -Nuar a/sysdeps/x86_64/multiarch/varshift.c b/sysdeps/x86_64/multiarch/varshift.c +--- a/sysdeps/x86_64/multiarch/varshift.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/varshift.c 2025-10-20 21:02:22.000000000 +0300 +@@ -16,9 +16,10 @@ + License along with the GNU C Library; if not, see + . */ + +-#include "varshift.h" ++#include + +-const int8_t ___m128i_shift_right[31] attribute_hidden = ++const int8_t ___m128i_shift_right[31] attribute_hidden ++ __attribute__((aligned(32))) = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +diff -Nuar a/sysdeps/x86_64/multiarch/varshift.h b/sysdeps/x86_64/multiarch/varshift.h +--- a/sysdeps/x86_64/multiarch/varshift.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/varshift.h 2025-10-20 21:02:22.000000000 +0300 +@@ -19,7 +19,8 @@ + #include + #include + +-extern const int8_t ___m128i_shift_right[31] attribute_hidden; ++extern const int8_t ___m128i_shift_right[31] attribute_hidden ++ __attribute__ ((aligned (32))); + + static __inline__ __m128i + __m128i_shift_right (__m128i value, unsigned long int offset) +diff -Nuar a/sysdeps/x86_64/multiarch/vec-macros.h b/sysdeps/x86_64/multiarch/vec-macros.h +--- a/sysdeps/x86_64/multiarch/vec-macros.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/vec-macros.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,90 @@ ++/* Macro helpers for VEC_{type}({vec_num}) ++ All versions must be listed in ifunc-impl-list.c. ++ Copyright (C) 2022 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 ++ . */ ++ ++#ifndef _VEC_MACROS_H ++#define _VEC_MACROS_H 1 ++ ++#ifndef VEC_SIZE ++# error "Never include this file directly. Always include a vector config." ++#endif ++ ++/* Defines so we can use SSE2 / AVX2 / EVEX / EVEX512 encoding with same ++ VEC(N) values. */ ++#define VEC_hi_xmm0 xmm16 ++#define VEC_hi_xmm1 xmm17 ++#define VEC_hi_xmm2 xmm18 ++#define VEC_hi_xmm3 xmm19 ++#define VEC_hi_xmm4 xmm20 ++#define VEC_hi_xmm5 xmm21 ++#define VEC_hi_xmm6 xmm22 ++#define VEC_hi_xmm7 xmm23 ++#define VEC_hi_xmm8 xmm24 ++#define VEC_hi_xmm9 xmm25 ++#define VEC_hi_xmm10 xmm26 ++#define VEC_hi_xmm11 xmm27 ++#define VEC_hi_xmm12 xmm28 ++#define VEC_hi_xmm13 xmm29 ++#define VEC_hi_xmm14 xmm30 ++#define VEC_hi_xmm15 xmm31 ++ ++#define VEC_hi_ymm0 ymm16 ++#define VEC_hi_ymm1 ymm17 ++#define VEC_hi_ymm2 ymm18 ++#define VEC_hi_ymm3 ymm19 ++#define VEC_hi_ymm4 ymm20 ++#define VEC_hi_ymm5 ymm21 ++#define VEC_hi_ymm6 ymm22 ++#define VEC_hi_ymm7 ymm23 ++#define VEC_hi_ymm8 ymm24 ++#define VEC_hi_ymm9 ymm25 ++#define VEC_hi_ymm10 ymm26 ++#define VEC_hi_ymm11 ymm27 ++#define VEC_hi_ymm12 ymm28 ++#define VEC_hi_ymm13 ymm29 ++#define VEC_hi_ymm14 ymm30 ++#define VEC_hi_ymm15 ymm31 ++ ++#define VEC_hi_zmm0 zmm16 ++#define VEC_hi_zmm1 zmm17 ++#define VEC_hi_zmm2 zmm18 ++#define VEC_hi_zmm3 zmm19 ++#define VEC_hi_zmm4 zmm20 ++#define VEC_hi_zmm5 zmm21 ++#define VEC_hi_zmm6 zmm22 ++#define VEC_hi_zmm7 zmm23 ++#define VEC_hi_zmm8 zmm24 ++#define VEC_hi_zmm9 zmm25 ++#define VEC_hi_zmm10 zmm26 ++#define VEC_hi_zmm11 zmm27 ++#define VEC_hi_zmm12 zmm28 ++#define VEC_hi_zmm13 zmm29 ++#define VEC_hi_zmm14 zmm30 ++#define VEC_hi_zmm15 zmm31 ++ ++#define PRIMITIVE_VEC(vec, num) vec##num ++ ++#define VEC_any_xmm(i) PRIMITIVE_VEC(xmm, i) ++#define VEC_any_ymm(i) PRIMITIVE_VEC(ymm, i) ++#define VEC_any_zmm(i) PRIMITIVE_VEC(zmm, i) ++ ++#define VEC_hi_xmm(i) PRIMITIVE_VEC(VEC_hi_xmm, i) ++#define VEC_hi_ymm(i) PRIMITIVE_VEC(VEC_hi_ymm, i) ++#define VEC_hi_zmm(i) PRIMITIVE_VEC(VEC_hi_zmm, i) ++ ++#endif +diff -Nuar a/sysdeps/x86_64/multiarch/wcslen-evex512.S b/sysdeps/x86_64/multiarch/wcslen-evex512.S +--- a/sysdeps/x86_64/multiarch/wcslen-evex512.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/wcslen-evex512.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,4 @@ ++#define STRLEN __wcslen_evex512 ++#define USE_AS_WCSLEN 1 ++ ++#include "strlen-evex512.S" +diff -Nuar a/sysdeps/x86_64/multiarch/wcslen-sse4_1.S b/sysdeps/x86_64/multiarch/wcslen-sse4_1.S +--- a/sysdeps/x86_64/multiarch/wcslen-sse4_1.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/wcslen-sse4_1.S 2025-10-20 21:02:22.000000000 +0300 +@@ -1,4 +1,5 @@ + #define AS_WCSLEN + #define strlen __wcslen_sse4_1 ++#define SECTION(p) p##.sse4.1 + + #include "strlen-vec.S" +diff -Nuar a/sysdeps/x86_64/multiarch/wcsncmp-avx2-rtm.S b/sysdeps/x86_64/multiarch/wcsncmp-avx2-rtm.S +--- a/sysdeps/x86_64/multiarch/wcsncmp-avx2-rtm.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/wcsncmp-avx2-rtm.S 2025-10-20 21:02:22.000000000 +0300 +@@ -1,5 +1,5 @@ + #define STRCMP __wcsncmp_avx2_rtm + #define USE_AS_STRNCMP 1 + #define USE_AS_WCSCMP 1 +- ++#define OVERFLOW_STRCMP __wcscmp_avx2_rtm + #include "strcmp-avx2-rtm.S" +diff -Nuar a/sysdeps/x86_64/multiarch/wcsncmp-avx2.S b/sysdeps/x86_64/multiarch/wcsncmp-avx2.S +--- a/sysdeps/x86_64/multiarch/wcsncmp-avx2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/wcsncmp-avx2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -1,5 +1,5 @@ + #define STRCMP __wcsncmp_avx2 + #define USE_AS_STRNCMP 1 + #define USE_AS_WCSCMP 1 +- ++#define OVERFLOW_STRCMP __wcscmp_avx2 + #include "strcmp-avx2.S" +diff -Nuar a/sysdeps/x86_64/multiarch/wcsnlen-evex512.S b/sysdeps/x86_64/multiarch/wcsnlen-evex512.S +--- a/sysdeps/x86_64/multiarch/wcsnlen-evex512.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/wcsnlen-evex512.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,5 @@ ++#define STRLEN __wcsnlen_evex512 ++#define USE_AS_WCSLEN 1 ++#define USE_AS_STRNLEN 1 ++ ++#include "strlen-evex512.S" +diff -Nuar a/sysdeps/x86_64/multiarch/wcsnlen-sse4_1.S b/sysdeps/x86_64/multiarch/wcsnlen-sse4_1.S +--- a/sysdeps/x86_64/multiarch/wcsnlen-sse4_1.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/wcsnlen-sse4_1.S 2025-10-20 21:02:22.000000000 +0300 +@@ -1,5 +1,6 @@ + #define AS_WCSLEN + #define AS_STRNLEN + #define strlen __wcsnlen_sse4_1 ++#define SECTION(p) p##.sse4.1 + + #include "strlen-vec.S" +diff -Nuar a/sysdeps/x86_64/multiarch/wcsrchr-sse2.S b/sysdeps/x86_64/multiarch/wcsrchr-sse2.S +--- a/sysdeps/x86_64/multiarch/wcsrchr-sse2.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/wcsrchr-sse2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -17,7 +17,6 @@ + . */ + + #if IS_IN (libc) +-# define wcsrchr __wcsrchr_sse2 ++# define STRRCHR __wcsrchr_sse2 + #endif +- + #include "../wcsrchr.S" +diff -Nuar a/sysdeps/x86_64/multiarch/wmemcmp-c.c b/sysdeps/x86_64/multiarch/wmemcmp-c.c +--- a/sysdeps/x86_64/multiarch/wmemcmp-c.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/multiarch/wmemcmp-c.c 1970-01-01 02:00:00.000000000 +0200 +@@ -1,9 +0,0 @@ +-#if IS_IN (libc) +-# include +- +-# define WMEMCMP __wmemcmp_sse2 +- +-extern __typeof (wmemcmp) __wmemcmp_sse2; +-#endif +- +-#include "wcsmbs/wmemcmp.c" +diff -Nuar a/sysdeps/x86_64/multiarch/wmemcmp-sse2.S b/sysdeps/x86_64/multiarch/wmemcmp-sse2.S +--- a/sysdeps/x86_64/multiarch/wmemcmp-sse2.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/multiarch/wmemcmp-sse2.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,21 @@ ++/* wmemcmp optimized with SSE2. ++ Copyright (C) 2022 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 ++ . */ ++ ++#define USE_AS_WMEMCMP 1 ++#define MEMCMP __wmemcmp_sse2 ++#include "../memcmp.S" +diff -Nuar a/sysdeps/x86_64/nptl/pthread_mutex_backoff.h b/sysdeps/x86_64/nptl/pthread_mutex_backoff.h +--- a/sysdeps/x86_64/nptl/pthread_mutex_backoff.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/nptl/pthread_mutex_backoff.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,39 @@ ++/* Pthread mutex backoff configuration. ++ Copyright (C) 2022 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 ++ . */ ++#ifndef _PTHREAD_MUTEX_BACKOFF_H ++#define _PTHREAD_MUTEX_BACKOFF_H 1 ++ ++#include ++ ++static inline unsigned int ++get_jitter (void) ++{ ++ return get_fast_jitter (); ++} ++ ++#define MAX_BACKOFF 16 ++ ++static inline int ++get_next_backoff (int backoff) ++{ ++ /* Binary expontial backoff. Limiting max backoff ++ can reduce latency in large critical section. */ ++ return (backoff < MAX_BACKOFF) ? backoff << 1 : backoff; ++} ++ ++#endif +diff -Nuar a/sysdeps/x86_64/strcmp.S b/sysdeps/x86_64/strcmp.S +--- a/sysdeps/x86_64/strcmp.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/strcmp.S 2025-10-20 21:02:22.000000000 +0300 +@@ -75,9 +75,8 @@ + movq __libc_tsd_LOCALE@gottpoff(%rip),%rax + mov %fs:(%rax),%RDX_LP + +- // XXX 5 byte should be before the function +- /* 5-byte NOP. */ +- .byte 0x0f,0x1f,0x44,0x00,0x00 ++ /* Either 1 or 5 bytes (dependeing if CET is enabled). */ ++ .p2align 4 + END2 (__strcasecmp) + # ifndef NO_NOLOCALE_ALIAS + weak_alias (__strcasecmp, strcasecmp) +@@ -94,9 +93,8 @@ + movq __libc_tsd_LOCALE@gottpoff(%rip),%rax + mov %fs:(%rax),%RCX_LP + +- // XXX 5 byte should be before the function +- /* 5-byte NOP. */ +- .byte 0x0f,0x1f,0x44,0x00,0x00 ++ /* Either 1 or 5 bytes (dependeing if CET is enabled). */ ++ .p2align 4 + END2 (__strncasecmp) + # ifndef NO_NOLOCALE_ALIAS + weak_alias (__strncasecmp, strncasecmp) +@@ -146,22 +144,22 @@ + #if defined USE_AS_STRCASECMP_L || defined USE_AS_STRNCASECMP_L + .section .rodata.cst16,"aM",@progbits,16 + .align 16 +-.Lbelowupper: +- .quad 0x4040404040404040 +- .quad 0x4040404040404040 +-.Ltopupper: +- .quad 0x5b5b5b5b5b5b5b5b +- .quad 0x5b5b5b5b5b5b5b5b +-.Ltouppermask: ++.Llcase_min: ++ .quad 0x3f3f3f3f3f3f3f3f ++ .quad 0x3f3f3f3f3f3f3f3f ++.Llcase_max: ++ .quad 0x9999999999999999 ++ .quad 0x9999999999999999 ++.Lcase_add: + .quad 0x2020202020202020 + .quad 0x2020202020202020 + .previous +- movdqa .Lbelowupper(%rip), %xmm5 +-# define UCLOW_reg %xmm5 +- movdqa .Ltopupper(%rip), %xmm6 +-# define UCHIGH_reg %xmm6 +- movdqa .Ltouppermask(%rip), %xmm7 +-# define LCQWORD_reg %xmm7 ++ movdqa .Llcase_min(%rip), %xmm5 ++# define LCASE_MIN_reg %xmm5 ++ movdqa .Llcase_max(%rip), %xmm6 ++# define LCASE_MAX_reg %xmm6 ++ movdqa .Lcase_add(%rip), %xmm7 ++# define CASE_ADD_reg %xmm7 + #endif + cmp $0x30, %ecx + ja LABEL(crosscache) /* rsi: 16-byte load will cross cache line */ +@@ -172,22 +170,18 @@ + movhpd 8(%rdi), %xmm1 + movhpd 8(%rsi), %xmm2 + #if defined USE_AS_STRCASECMP_L || defined USE_AS_STRNCASECMP_L +-# define TOLOWER(reg1, reg2) \ +- movdqa reg1, %xmm8; \ +- movdqa UCHIGH_reg, %xmm9; \ +- movdqa reg2, %xmm10; \ +- movdqa UCHIGH_reg, %xmm11; \ +- pcmpgtb UCLOW_reg, %xmm8; \ +- pcmpgtb reg1, %xmm9; \ +- pcmpgtb UCLOW_reg, %xmm10; \ +- pcmpgtb reg2, %xmm11; \ +- pand %xmm9, %xmm8; \ +- pand %xmm11, %xmm10; \ +- pand LCQWORD_reg, %xmm8; \ +- pand LCQWORD_reg, %xmm10; \ +- por %xmm8, reg1; \ +- por %xmm10, reg2 +- TOLOWER (%xmm1, %xmm2) ++# define TOLOWER(reg1, reg2) \ ++ movdqa LCASE_MIN_reg, %xmm8; \ ++ movdqa LCASE_MIN_reg, %xmm9; \ ++ paddb reg1, %xmm8; \ ++ paddb reg2, %xmm9; \ ++ pcmpgtb LCASE_MAX_reg, %xmm8; \ ++ pcmpgtb LCASE_MAX_reg, %xmm9; \ ++ pandn CASE_ADD_reg, %xmm8; \ ++ pandn CASE_ADD_reg, %xmm9; \ ++ paddb %xmm8, reg1; \ ++ paddb %xmm9, reg2 ++ TOLOWER (%xmm1, %xmm2) + #else + # define TOLOWER(reg1, reg2) + #endif +diff -Nuar a/sysdeps/x86_64/strcspn.S b/sysdeps/x86_64/strcspn.S +--- a/sysdeps/x86_64/strcspn.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/strcspn.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,119 +0,0 @@ +-/* strcspn (str, ss) -- Return the length of the initial segment of STR +- which contains no characters from SS. +- For AMD x86-64. +- Copyright (C) 1994-2022 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 +- . */ +- +-#include +-#include "asm-syntax.h" +- +- .text +-ENTRY (strcspn) +- +- movq %rdi, %rdx /* Save SRC. */ +- +- /* First we create a table with flags for all possible characters. +- For the ASCII (7bit/8bit) or ISO-8859-X character sets which are +- supported by the C string functions we have 256 characters. +- Before inserting marks for the stop characters we clear the whole +- table. */ +- movq %rdi, %r8 /* Save value. */ +- subq $256, %rsp /* Make space for 256 bytes. */ +- cfi_adjust_cfa_offset(256) +- movl $32, %ecx /* 32*8 bytes = 256 bytes. */ +- movq %rsp, %rdi +- xorl %eax, %eax /* We store 0s. */ +- cld +- rep +- stosq +- +- movq %rsi, %rax /* Setup skipset. */ +- +-/* For understanding the following code remember that %rcx == 0 now. +- Although all the following instruction only modify %cl we always +- have a correct zero-extended 64-bit value in %rcx. */ +- +- .p2align 4 +-L(2): movb (%rax), %cl /* get byte from skipset */ +- testb %cl, %cl /* is NUL char? */ +- jz L(1) /* yes => start compare loop */ +- movb %cl, (%rsp,%rcx) /* set corresponding byte in skipset table */ +- +- movb 1(%rax), %cl /* get byte from skipset */ +- testb $0xff, %cl /* is NUL char? */ +- jz L(1) /* yes => start compare loop */ +- movb %cl, (%rsp,%rcx) /* set corresponding byte in skipset table */ +- +- movb 2(%rax), %cl /* get byte from skipset */ +- testb $0xff, %cl /* is NUL char? */ +- jz L(1) /* yes => start compare loop */ +- movb %cl, (%rsp,%rcx) /* set corresponding byte in skipset table */ +- +- movb 3(%rax), %cl /* get byte from skipset */ +- addq $4, %rax /* increment skipset pointer */ +- movb %cl, (%rsp,%rcx) /* set corresponding byte in skipset table */ +- testb $0xff, %cl /* is NUL char? */ +- jnz L(2) /* no => process next dword from skipset */ +- +-L(1): leaq -4(%rdx), %rax /* prepare loop */ +- +- /* We use a neat trick for the following loop. Normally we would +- have to test for two termination conditions +- 1. a character in the skipset was found +- and +- 2. the end of the string was found +- But as a sign that the character is in the skipset we store its +- value in the table. But the value of NUL is NUL so the loop +- terminates for NUL in every case. */ +- +- .p2align 4 +-L(3): addq $4, %rax /* adjust pointer for full loop round */ +- +- movb (%rax), %cl /* get byte from string */ +- cmpb %cl, (%rsp,%rcx) /* is it contained in skipset? */ +- je L(4) /* yes => return */ +- +- movb 1(%rax), %cl /* get byte from string */ +- cmpb %cl, (%rsp,%rcx) /* is it contained in skipset? */ +- je L(5) /* yes => return */ +- +- movb 2(%rax), %cl /* get byte from string */ +- cmpb %cl, (%rsp,%rcx) /* is it contained in skipset? */ +- jz L(6) /* yes => return */ +- +- movb 3(%rax), %cl /* get byte from string */ +- cmpb %cl, (%rsp,%rcx) /* is it contained in skipset? */ +- jne L(3) /* no => start loop again */ +- +- incq %rax /* adjust pointer */ +-L(6): incq %rax +-L(5): incq %rax +- +-L(4): addq $256, %rsp /* remove skipset */ +- cfi_adjust_cfa_offset(-256) +-#ifdef USE_AS_STRPBRK +- xorl %edx,%edx +- orb %cl, %cl /* was last character NUL? */ +- cmovzq %rdx, %rax /* Yes: return NULL */ +-#else +- subq %rdx, %rax /* we have to return the number of valid +- characters, so compute distance to first +- non-valid character */ +-#endif +- ret +-END (strcspn) +-libc_hidden_builtin_def (strcspn) +diff -Nuar a/sysdeps/x86_64/strpbrk.S b/sysdeps/x86_64/strpbrk.S +--- a/sysdeps/x86_64/strpbrk.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/strpbrk.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,3 +0,0 @@ +-#define strcspn strpbrk +-#define USE_AS_STRPBRK +-#include +diff -Nuar a/sysdeps/x86_64/strrchr.S b/sysdeps/x86_64/strrchr.S +--- a/sysdeps/x86_64/strrchr.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/strrchr.S 2025-10-20 21:02:22.000000000 +0300 +@@ -19,210 +19,360 @@ + + #include + ++#ifndef STRRCHR ++# define STRRCHR strrchr ++#endif ++ ++#ifdef USE_AS_WCSRCHR ++# define PCMPEQ pcmpeqd ++# define CHAR_SIZE 4 ++# define PMINU pminud ++#else ++# define PCMPEQ pcmpeqb ++# define CHAR_SIZE 1 ++# define PMINU pminub ++#endif ++ ++#define PAGE_SIZE 4096 ++#define VEC_SIZE 16 ++ + .text +-ENTRY (strrchr) +- movd %esi, %xmm1 ++ENTRY(STRRCHR) ++ movd %esi, %xmm0 + movq %rdi, %rax +- andl $4095, %eax +- punpcklbw %xmm1, %xmm1 +- cmpq $4032, %rax +- punpcklwd %xmm1, %xmm1 +- pshufd $0, %xmm1, %xmm1 ++ andl $(PAGE_SIZE - 1), %eax ++#ifndef USE_AS_WCSRCHR ++ punpcklbw %xmm0, %xmm0 ++ punpcklwd %xmm0, %xmm0 ++#endif ++ pshufd $0, %xmm0, %xmm0 ++ cmpl $(PAGE_SIZE - VEC_SIZE), %eax + ja L(cross_page) +- movdqu (%rdi), %xmm0 ++ ++L(cross_page_continue): ++ movups (%rdi), %xmm1 + pxor %xmm2, %xmm2 +- movdqa %xmm0, %xmm3 +- pcmpeqb %xmm1, %xmm0 +- pcmpeqb %xmm2, %xmm3 +- pmovmskb %xmm0, %ecx +- pmovmskb %xmm3, %edx +- testq %rdx, %rdx +- je L(next_48_bytes) +- leaq -1(%rdx), %rax +- xorq %rdx, %rax +- andq %rcx, %rax +- je L(exit) +- bsrq %rax, %rax ++ PCMPEQ %xmm1, %xmm2 ++ pmovmskb %xmm2, %ecx ++ testl %ecx, %ecx ++ jz L(aligned_more) ++ ++ PCMPEQ %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ leal -1(%rcx), %edx ++ xorl %edx, %ecx ++ andl %ecx, %eax ++ jz L(ret0) ++ bsrl %eax, %eax + addq %rdi, %rax ++ /* We are off by 3 for wcsrchr if search CHAR is non-zero. If ++ search CHAR is zero we are correct. Either way `andq ++ -CHAR_SIZE, %rax` gets the correct result. */ ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif ++L(ret0): + ret + ++ /* Returns for first vec x1/x2 have hard coded backward search ++ path for earlier matches. */ + .p2align 4 +-L(next_48_bytes): +- movdqu 16(%rdi), %xmm4 +- movdqa %xmm4, %xmm5 +- movdqu 32(%rdi), %xmm3 +- pcmpeqb %xmm1, %xmm4 +- pcmpeqb %xmm2, %xmm5 +- movdqu 48(%rdi), %xmm0 +- pmovmskb %xmm5, %edx +- movdqa %xmm3, %xmm5 +- pcmpeqb %xmm1, %xmm3 +- pcmpeqb %xmm2, %xmm5 +- pcmpeqb %xmm0, %xmm2 +- salq $16, %rdx +- pmovmskb %xmm3, %r8d +- pmovmskb %xmm5, %eax +- pmovmskb %xmm2, %esi +- salq $32, %r8 +- salq $32, %rax +- pcmpeqb %xmm1, %xmm0 +- orq %rdx, %rax +- movq %rsi, %rdx +- pmovmskb %xmm4, %esi +- salq $48, %rdx +- salq $16, %rsi +- orq %r8, %rsi +- orq %rcx, %rsi +- pmovmskb %xmm0, %ecx +- salq $48, %rcx +- orq %rcx, %rsi +- orq %rdx, %rax +- je L(loop_header2) +- leaq -1(%rax), %rcx +- xorq %rax, %rcx +- andq %rcx, %rsi +- je L(exit) +- bsrq %rsi, %rsi +- leaq (%rdi,%rsi), %rax ++L(first_vec_x0_test): ++ PCMPEQ %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ testl %eax, %eax ++ jz L(ret0) ++ bsrl %eax, %eax ++ addq %r8, %rax ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif + ret + + .p2align 4 +-L(loop_header2): +- testq %rsi, %rsi +- movq %rdi, %rcx +- je L(no_c_found) +-L(loop_header): +- addq $64, %rdi +- pxor %xmm7, %xmm7 +- andq $-64, %rdi +- jmp L(loop_entry) +- +- .p2align 4 +-L(loop64): +- testq %rdx, %rdx +- cmovne %rdx, %rsi +- cmovne %rdi, %rcx +- addq $64, %rdi +-L(loop_entry): +- movdqa 32(%rdi), %xmm3 +- pxor %xmm6, %xmm6 +- movdqa 48(%rdi), %xmm2 +- movdqa %xmm3, %xmm0 +- movdqa 16(%rdi), %xmm4 +- pminub %xmm2, %xmm0 +- movdqa (%rdi), %xmm5 +- pminub %xmm4, %xmm0 +- pminub %xmm5, %xmm0 +- pcmpeqb %xmm7, %xmm0 +- pmovmskb %xmm0, %eax +- movdqa %xmm5, %xmm0 +- pcmpeqb %xmm1, %xmm0 +- pmovmskb %xmm0, %r9d +- movdqa %xmm4, %xmm0 +- pcmpeqb %xmm1, %xmm0 +- pmovmskb %xmm0, %edx +- movdqa %xmm3, %xmm0 +- pcmpeqb %xmm1, %xmm0 +- salq $16, %rdx +- pmovmskb %xmm0, %r10d +- movdqa %xmm2, %xmm0 +- pcmpeqb %xmm1, %xmm0 +- salq $32, %r10 +- orq %r10, %rdx +- pmovmskb %xmm0, %r8d +- orq %r9, %rdx +- salq $48, %r8 +- orq %r8, %rdx ++L(first_vec_x1): ++ PCMPEQ %xmm0, %xmm2 ++ pmovmskb %xmm2, %eax ++ leal -1(%rcx), %edx ++ xorl %edx, %ecx ++ andl %ecx, %eax ++ jz L(first_vec_x0_test) ++ bsrl %eax, %eax ++ leaq (VEC_SIZE)(%rdi, %rax), %rax ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif ++ ret ++ ++ .p2align 4 ++L(first_vec_x1_test): ++ PCMPEQ %xmm0, %xmm2 ++ pmovmskb %xmm2, %eax + testl %eax, %eax +- je L(loop64) +- pcmpeqb %xmm6, %xmm4 +- pcmpeqb %xmm6, %xmm3 +- pcmpeqb %xmm6, %xmm5 +- pmovmskb %xmm4, %eax +- pmovmskb %xmm3, %r10d +- pcmpeqb %xmm6, %xmm2 +- pmovmskb %xmm5, %r9d +- salq $32, %r10 +- salq $16, %rax +- pmovmskb %xmm2, %r8d +- orq %r10, %rax +- orq %r9, %rax +- salq $48, %r8 +- orq %r8, %rax +- leaq -1(%rax), %r8 +- xorq %rax, %r8 +- andq %r8, %rdx +- cmovne %rdi, %rcx +- cmovne %rdx, %rsi +- bsrq %rsi, %rsi +- leaq (%rcx,%rsi), %rax ++ jz L(first_vec_x0_test) ++ bsrl %eax, %eax ++ leaq (VEC_SIZE)(%rdi, %rax), %rax ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif ++ ret ++ ++ .p2align 4 ++L(first_vec_x2): ++ PCMPEQ %xmm0, %xmm3 ++ pmovmskb %xmm3, %eax ++ leal -1(%rcx), %edx ++ xorl %edx, %ecx ++ andl %ecx, %eax ++ jz L(first_vec_x1_test) ++ bsrl %eax, %eax ++ leaq (VEC_SIZE * 2)(%rdi, %rax), %rax ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif ++ ret ++ ++ .p2align 4 ++L(aligned_more): ++ /* Save original pointer if match was in VEC 0. */ ++ movq %rdi, %r8 ++ andq $-VEC_SIZE, %rdi ++ ++ movaps VEC_SIZE(%rdi), %xmm2 ++ pxor %xmm3, %xmm3 ++ PCMPEQ %xmm2, %xmm3 ++ pmovmskb %xmm3, %ecx ++ testl %ecx, %ecx ++ jnz L(first_vec_x1) ++ ++ movaps (VEC_SIZE * 2)(%rdi), %xmm3 ++ pxor %xmm4, %xmm4 ++ PCMPEQ %xmm3, %xmm4 ++ pmovmskb %xmm4, %ecx ++ testl %ecx, %ecx ++ jnz L(first_vec_x2) ++ ++ addq $VEC_SIZE, %rdi ++ /* Save pointer again before realigning. */ ++ movq %rdi, %rsi ++ andq $-(VEC_SIZE * 2), %rdi ++ .p2align 4 ++L(first_loop): ++ /* Do 2x VEC at a time. */ ++ movaps (VEC_SIZE * 2)(%rdi), %xmm4 ++ movaps (VEC_SIZE * 3)(%rdi), %xmm5 ++ /* Since SSE2 no pminud so wcsrchr needs seperate logic for ++ detecting zero. Note if this is found to be a bottleneck it ++ may be worth adding an SSE4.1 wcsrchr implementation. */ ++#ifdef USE_AS_WCSRCHR ++ movaps %xmm5, %xmm6 ++ pxor %xmm8, %xmm8 ++ ++ PCMPEQ %xmm8, %xmm5 ++ PCMPEQ %xmm4, %xmm8 ++ por %xmm5, %xmm8 ++#else ++ movaps %xmm5, %xmm6 ++ PMINU %xmm4, %xmm5 ++#endif ++ ++ movaps %xmm4, %xmm9 ++ PCMPEQ %xmm0, %xmm4 ++ PCMPEQ %xmm0, %xmm6 ++ movaps %xmm6, %xmm7 ++ por %xmm4, %xmm6 ++#ifndef USE_AS_WCSRCHR ++ pxor %xmm8, %xmm8 ++ PCMPEQ %xmm5, %xmm8 ++#endif ++ pmovmskb %xmm8, %ecx ++ pmovmskb %xmm6, %eax ++ ++ addq $(VEC_SIZE * 2), %rdi ++ /* Use `addl` 1) so we can undo it with `subl` and 2) it can ++ macro-fuse with `jz`. */ ++ addl %ecx, %eax ++ jz L(first_loop) ++ ++ /* Check if there is zero match. */ ++ testl %ecx, %ecx ++ jz L(second_loop_match) ++ ++ /* Check if there was a match in last iteration. */ ++ subl %ecx, %eax ++ jnz L(new_match) ++ ++L(first_loop_old_match): ++ PCMPEQ %xmm0, %xmm2 ++ PCMPEQ %xmm0, %xmm3 ++ pmovmskb %xmm2, %ecx ++ pmovmskb %xmm3, %eax ++ addl %eax, %ecx ++ jz L(first_vec_x0_test) ++ /* NB: We could move this shift to before the branch and save a ++ bit of code size / performance on the fall through. The ++ branch leads to the null case which generally seems hotter ++ than char in first 3x VEC. */ ++ sall $16, %eax ++ orl %ecx, %eax ++ ++ bsrl %eax, %eax ++ addq %rsi, %rax ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif + ret + + .p2align 4 +-L(no_c_found): +- movl $1, %esi +- xorl %ecx, %ecx +- jmp L(loop_header) ++L(new_match): ++ pxor %xmm6, %xmm6 ++ PCMPEQ %xmm9, %xmm6 ++ pmovmskb %xmm6, %eax ++ sall $16, %ecx ++ orl %eax, %ecx ++ ++ /* We can't reuse either of the old comparisons as since we mask ++ of zeros after first zero (instead of using the full ++ comparison) we can't gurantee no interference between match ++ after end of string and valid match. */ ++ pmovmskb %xmm4, %eax ++ pmovmskb %xmm7, %edx ++ sall $16, %edx ++ orl %edx, %eax ++ ++ leal -1(%ecx), %edx ++ xorl %edx, %ecx ++ andl %ecx, %eax ++ jz L(first_loop_old_match) ++ bsrl %eax, %eax ++ addq %rdi, %rax ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif ++ ret + ++ /* Save minimum state for getting most recent match. We can ++ throw out all previous work. */ + .p2align 4 +-L(exit): +- xorl %eax, %eax ++L(second_loop_match): ++ movq %rdi, %rsi ++ movaps %xmm4, %xmm2 ++ movaps %xmm7, %xmm3 ++ ++ .p2align 4 ++L(second_loop): ++ movaps (VEC_SIZE * 2)(%rdi), %xmm4 ++ movaps (VEC_SIZE * 3)(%rdi), %xmm5 ++ /* Since SSE2 no pminud so wcsrchr needs seperate logic for ++ detecting zero. Note if this is found to be a bottleneck it ++ may be worth adding an SSE4.1 wcsrchr implementation. */ ++#ifdef USE_AS_WCSRCHR ++ movaps %xmm5, %xmm6 ++ pxor %xmm8, %xmm8 ++ ++ PCMPEQ %xmm8, %xmm5 ++ PCMPEQ %xmm4, %xmm8 ++ por %xmm5, %xmm8 ++#else ++ movaps %xmm5, %xmm6 ++ PMINU %xmm4, %xmm5 ++#endif ++ ++ movaps %xmm4, %xmm9 ++ PCMPEQ %xmm0, %xmm4 ++ PCMPEQ %xmm0, %xmm6 ++ movaps %xmm6, %xmm7 ++ por %xmm4, %xmm6 ++#ifndef USE_AS_WCSRCHR ++ pxor %xmm8, %xmm8 ++ PCMPEQ %xmm5, %xmm8 ++#endif ++ ++ pmovmskb %xmm8, %ecx ++ pmovmskb %xmm6, %eax ++ ++ addq $(VEC_SIZE * 2), %rdi ++ /* Either null term or new occurence of CHAR. */ ++ addl %ecx, %eax ++ jz L(second_loop) ++ ++ /* No null term so much be new occurence of CHAR. */ ++ testl %ecx, %ecx ++ jz L(second_loop_match) ++ ++ ++ subl %ecx, %eax ++ jnz L(second_loop_new_match) ++ ++L(second_loop_old_match): ++ pmovmskb %xmm2, %ecx ++ pmovmskb %xmm3, %eax ++ sall $16, %eax ++ orl %ecx, %eax ++ bsrl %eax, %eax ++ addq %rsi, %rax ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif + ret + + .p2align 4 ++L(second_loop_new_match): ++ pxor %xmm6, %xmm6 ++ PCMPEQ %xmm9, %xmm6 ++ pmovmskb %xmm6, %eax ++ sall $16, %ecx ++ orl %eax, %ecx ++ ++ /* We can't reuse either of the old comparisons as since we mask ++ of zeros after first zero (instead of using the full ++ comparison) we can't gurantee no interference between match ++ after end of string and valid match. */ ++ pmovmskb %xmm4, %eax ++ pmovmskb %xmm7, %edx ++ sall $16, %edx ++ orl %edx, %eax ++ ++ leal -1(%ecx), %edx ++ xorl %edx, %ecx ++ andl %ecx, %eax ++ jz L(second_loop_old_match) ++ bsrl %eax, %eax ++ addq %rdi, %rax ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif ++ ret ++ ++ .p2align 4,, 4 + L(cross_page): +- movq %rdi, %rax +- pxor %xmm0, %xmm0 +- andq $-64, %rax +- movdqu (%rax), %xmm5 +- movdqa %xmm5, %xmm6 +- movdqu 16(%rax), %xmm4 +- pcmpeqb %xmm1, %xmm5 +- pcmpeqb %xmm0, %xmm6 +- movdqu 32(%rax), %xmm3 +- pmovmskb %xmm6, %esi +- movdqa %xmm4, %xmm6 +- movdqu 48(%rax), %xmm2 +- pcmpeqb %xmm1, %xmm4 +- pcmpeqb %xmm0, %xmm6 +- pmovmskb %xmm6, %edx +- movdqa %xmm3, %xmm6 +- pcmpeqb %xmm1, %xmm3 +- pcmpeqb %xmm0, %xmm6 +- pcmpeqb %xmm2, %xmm0 +- salq $16, %rdx +- pmovmskb %xmm3, %r9d +- pmovmskb %xmm6, %r8d +- pmovmskb %xmm0, %ecx +- salq $32, %r9 +- salq $32, %r8 +- pcmpeqb %xmm1, %xmm2 +- orq %r8, %rdx +- salq $48, %rcx +- pmovmskb %xmm5, %r8d +- orq %rsi, %rdx +- pmovmskb %xmm4, %esi +- orq %rcx, %rdx +- pmovmskb %xmm2, %ecx +- salq $16, %rsi +- salq $48, %rcx +- orq %r9, %rsi +- orq %r8, %rsi +- orq %rcx, %rsi ++ movq %rdi, %rsi ++ andq $-VEC_SIZE, %rsi ++ movaps (%rsi), %xmm1 ++ pxor %xmm2, %xmm2 ++ PCMPEQ %xmm1, %xmm2 ++ pmovmskb %xmm2, %edx + movl %edi, %ecx +- subl %eax, %ecx +- shrq %cl, %rdx +- shrq %cl, %rsi +- testq %rdx, %rdx +- je L(loop_header2) +- leaq -1(%rdx), %rax +- xorq %rdx, %rax +- andq %rax, %rsi +- je L(exit) +- bsrq %rsi, %rax ++ andl $(VEC_SIZE - 1), %ecx ++ sarl %cl, %edx ++ jz L(cross_page_continue) ++ PCMPEQ %xmm0, %xmm1 ++ pmovmskb %xmm1, %eax ++ sarl %cl, %eax ++ leal -1(%rdx), %ecx ++ xorl %edx, %ecx ++ andl %ecx, %eax ++ jz L(ret1) ++ bsrl %eax, %eax + addq %rdi, %rax ++#ifdef USE_AS_WCSRCHR ++ andq $-CHAR_SIZE, %rax ++#endif ++L(ret1): + ret +-END (strrchr) ++END(STRRCHR) + +-weak_alias (strrchr, rindex) +-libc_hidden_builtin_def (strrchr) ++#ifndef USE_AS_WCSRCHR ++ weak_alias (STRRCHR, rindex) ++ libc_hidden_builtin_def (STRRCHR) ++#endif +diff -Nuar a/sysdeps/x86_64/strspn.S b/sysdeps/x86_64/strspn.S +--- a/sysdeps/x86_64/strspn.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/strspn.S 1970-01-01 02:00:00.000000000 +0200 +@@ -1,112 +0,0 @@ +-/* strspn (str, ss) -- Return the length of the initial segment of STR +- which contains only characters from SS. +- For AMD x86-64. +- Copyright (C) 1994-2022 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 +- . */ +- +-#include +- +- .text +-ENTRY (strspn) +- +- movq %rdi, %rdx /* Save SRC. */ +- +- /* First we create a table with flags for all possible characters. +- For the ASCII (7bit/8bit) or ISO-8859-X character sets which are +- supported by the C string functions we have 256 characters. +- Before inserting marks for the stop characters we clear the whole +- table. */ +- movq %rdi, %r8 /* Save value. */ +- subq $256, %rsp /* Make space for 256 bytes. */ +- cfi_adjust_cfa_offset(256) +- movl $32, %ecx /* 32*8 bytes = 256 bytes. */ +- movq %rsp, %rdi +- xorl %eax, %eax /* We store 0s. */ +- cld +- rep +- stosq +- +- movq %rsi, %rax /* Setup stopset. */ +- +-/* For understanding the following code remember that %rcx == 0 now. +- Although all the following instruction only modify %cl we always +- have a correct zero-extended 64-bit value in %rcx. */ +- +- .p2align 4 +-L(2): movb (%rax), %cl /* get byte from stopset */ +- testb %cl, %cl /* is NUL char? */ +- jz L(1) /* yes => start compare loop */ +- movb %cl, (%rsp,%rcx) /* set corresponding byte in stopset table */ +- +- movb 1(%rax), %cl /* get byte from stopset */ +- testb $0xff, %cl /* is NUL char? */ +- jz L(1) /* yes => start compare loop */ +- movb %cl, (%rsp,%rcx) /* set corresponding byte in stopset table */ +- +- movb 2(%rax), %cl /* get byte from stopset */ +- testb $0xff, %cl /* is NUL char? */ +- jz L(1) /* yes => start compare loop */ +- movb %cl, (%rsp,%rcx) /* set corresponding byte in stopset table */ +- +- movb 3(%rax), %cl /* get byte from stopset */ +- addq $4, %rax /* increment stopset pointer */ +- movb %cl, (%rsp,%rcx) /* set corresponding byte in stopset table */ +- testb $0xff, %cl /* is NUL char? */ +- jnz L(2) /* no => process next dword from stopset */ +- +-L(1): leaq -4(%rdx), %rax /* prepare loop */ +- +- /* We use a neat trick for the following loop. Normally we would +- have to test for two termination conditions +- 1. a character in the stopset was found +- and +- 2. the end of the string was found +- But as a sign that the character is in the stopset we store its +- value in the table. But the value of NUL is NUL so the loop +- terminates for NUL in every case. */ +- +- .p2align 4 +-L(3): addq $4, %rax /* adjust pointer for full loop round */ +- +- movb (%rax), %cl /* get byte from string */ +- testb %cl, (%rsp,%rcx) /* is it contained in skipset? */ +- jz L(4) /* no => return */ +- +- movb 1(%rax), %cl /* get byte from string */ +- testb %cl, (%rsp,%rcx) /* is it contained in skipset? */ +- jz L(5) /* no => return */ +- +- movb 2(%rax), %cl /* get byte from string */ +- testb %cl, (%rsp,%rcx) /* is it contained in skipset? */ +- jz L(6) /* no => return */ +- +- movb 3(%rax), %cl /* get byte from string */ +- testb %cl, (%rsp,%rcx) /* is it contained in skipset? */ +- jnz L(3) /* yes => start loop again */ +- +- incq %rax /* adjust pointer */ +-L(6): incq %rax +-L(5): incq %rax +- +-L(4): addq $256, %rsp /* remove stopset */ +- cfi_adjust_cfa_offset(-256) +- subq %rdx, %rax /* we have to return the number of valid +- characters, so compute distance to first +- non-valid character */ +- ret +-END (strspn) +-libc_hidden_builtin_def (strspn) +diff -Nuar a/sysdeps/x86_64/sysdep.h b/sysdeps/x86_64/sysdep.h +--- a/sysdeps/x86_64/sysdep.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/sysdep.h 2025-10-20 21:02:22.000000000 +0300 +@@ -99,13 +99,31 @@ + to avoid RTM abort triggered by VZEROUPPER inside transactionally. */ + #define ZERO_UPPER_VEC_REGISTERS_RETURN_XTEST \ + xtest; \ +- jz 1f; \ +- vzeroall; \ ++ jnz 1f; \ ++ vzeroupper; \ + ret; \ + 1: \ +- vzeroupper; \ ++ vzeroall; \ + ret + ++/* Can be used to replace vzeroupper that is not directly before a ++ return. This is useful when hoisting a vzeroupper from multiple ++ return paths to decrease the total number of vzerouppers and code ++ size. */ ++#define COND_VZEROUPPER_XTEST \ ++ xtest; \ ++ jz 1f; \ ++ vzeroall; \ ++ jmp 2f; \ ++1: \ ++ vzeroupper; \ ++2: ++ ++/* In RTM define this as COND_VZEROUPPER_XTEST. */ ++#ifndef COND_VZEROUPPER ++# define COND_VZEROUPPER vzeroupper ++#endif ++ + /* Zero upper vector registers and return. */ + #ifndef ZERO_UPPER_VEC_REGISTERS_RETURN + # define ZERO_UPPER_VEC_REGISTERS_RETURN \ +diff -Nuar a/sysdeps/x86_64/Versions b/sysdeps/x86_64/Versions +--- a/sysdeps/x86_64/Versions 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/Versions 2025-10-20 21:02:19.000000000 +0300 +@@ -5,6 +5,11 @@ + GLIBC_2.13 { + __fentry__; + } ++ GLIBC_ABI_DT_X86_64_PLT { ++ # This symbol is used only for empty version map and will be removed ++ # by scripts/versions.awk. ++ __placeholder_only_for_empty_version_map; ++ } + } + libm { + GLIBC_2.1 { +diff -Nuar a/sysdeps/x86_64/wcslen.S b/sysdeps/x86_64/wcslen.S +--- a/sysdeps/x86_64/wcslen.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/wcslen.S 2025-10-20 21:02:22.000000000 +0300 +@@ -40,82 +40,82 @@ + pxor %xmm0, %xmm0 + + lea 32(%rdi), %rax +- lea 16(%rdi), %rcx ++ addq $16, %rdi + and $-16, %rax + + pcmpeqd (%rax), %xmm0 + pmovmskb %xmm0, %edx + pxor %xmm1, %xmm1 ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm1 + pmovmskb %xmm1, %edx + pxor %xmm2, %xmm2 ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm2 + pmovmskb %xmm2, %edx + pxor %xmm3, %xmm3 ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm3 + pmovmskb %xmm3, %edx ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm0 + pmovmskb %xmm0, %edx ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm1 + pmovmskb %xmm1, %edx ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm2 + pmovmskb %xmm2, %edx ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm3 + pmovmskb %xmm3, %edx ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm0 + pmovmskb %xmm0, %edx ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm1 + pmovmskb %xmm1, %edx ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm2 + pmovmskb %xmm2, %edx ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + pcmpeqd (%rax), %xmm3 + pmovmskb %xmm3, %edx ++ addq $16, %rax + test %edx, %edx +- lea 16(%rax), %rax + jnz L(exit) + + and $-0x40, %rax +@@ -132,104 +132,100 @@ + pminub %xmm0, %xmm2 + pcmpeqd %xmm3, %xmm2 + pmovmskb %xmm2, %edx ++ addq $64, %rax + test %edx, %edx +- lea 64(%rax), %rax + jz L(aligned_64_loop) + + pcmpeqd -64(%rax), %xmm3 + pmovmskb %xmm3, %edx ++ addq $48, %rdi + test %edx, %edx +- lea 48(%rcx), %rcx + jnz L(exit) + + pcmpeqd %xmm1, %xmm3 + pmovmskb %xmm3, %edx ++ addq $-16, %rdi + test %edx, %edx +- lea -16(%rcx), %rcx + jnz L(exit) + + pcmpeqd -32(%rax), %xmm3 + pmovmskb %xmm3, %edx ++ addq $-16, %rdi + test %edx, %edx +- lea -16(%rcx), %rcx + jnz L(exit) + + pcmpeqd %xmm6, %xmm3 + pmovmskb %xmm3, %edx ++ addq $-16, %rdi + test %edx, %edx +- lea -16(%rcx), %rcx +- jnz L(exit) +- +- jmp L(aligned_64_loop) ++ jz L(aligned_64_loop) + + .p2align 4 + L(exit): +- sub %rcx, %rax ++ sub %rdi, %rax + shr $2, %rax + test %dl, %dl + jz L(exit_high) + +- mov %dl, %cl +- and $15, %cl ++ andl $15, %edx + jz L(exit_1) + ret + +- .p2align 4 ++ /* No align here. Naturally aligned % 16 == 1. */ + L(exit_high): +- mov %dh, %ch +- and $15, %ch ++ andl $(15 << 8), %edx + jz L(exit_3) + add $2, %rax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_1): + add $1, %rax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_3): + add $3, %rax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_tail0): +- xor %rax, %rax ++ xorl %eax, %eax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_tail1): +- mov $1, %rax ++ movl $1, %eax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_tail2): +- mov $2, %rax ++ movl $2, %eax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_tail3): +- mov $3, %rax ++ movl $3, %eax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_tail4): +- mov $4, %rax ++ movl $4, %eax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_tail5): +- mov $5, %rax ++ movl $5, %eax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_tail6): +- mov $6, %rax ++ movl $6, %eax + ret + +- .p2align 4 ++ .p2align 3 + L(exit_tail7): +- mov $7, %rax ++ movl $7, %eax + ret + + END (__wcslen) +diff -Nuar a/sysdeps/x86_64/wcsrchr.S b/sysdeps/x86_64/wcsrchr.S +--- a/sysdeps/x86_64/wcsrchr.S 2022-02-03 08:27:54.000000000 +0300 ++++ b/sysdeps/x86_64/wcsrchr.S 2025-10-20 21:02:22.000000000 +0300 +@@ -1,4 +1,4 @@ +-/* wcsrchr with SSSE3 ++/* wcsrchr optimized with SSE2. + Copyright (C) 2011-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + +@@ -16,266 +16,12 @@ + License along with the GNU C Library; if not, see + . */ + +-#include + +- .text +-ENTRY (wcsrchr) ++#define USE_AS_WCSRCHR 1 ++#define NO_PMINU 1 + +- movd %rsi, %xmm1 +- mov %rdi, %rcx +- punpckldq %xmm1, %xmm1 +- pxor %xmm2, %xmm2 +- punpckldq %xmm1, %xmm1 +- and $63, %rcx +- cmp $48, %rcx +- ja L(crosscache) ++#ifndef STRRCHR ++# define STRRCHR wcsrchr ++#endif + +- movdqu (%rdi), %xmm0 +- pcmpeqd %xmm0, %xmm2 +- pcmpeqd %xmm1, %xmm0 +- pmovmskb %xmm2, %rcx +- pmovmskb %xmm0, %rax +- add $16, %rdi +- +- test %rax, %rax +- jnz L(unaligned_match1) +- +- test %rcx, %rcx +- jnz L(return_null) +- +- and $-16, %rdi +- xor %r8, %r8 +- jmp L(loop) +- +- .p2align 4 +-L(unaligned_match1): +- test %rcx, %rcx +- jnz L(prolog_find_zero_1) +- +- mov %rax, %r8 +- mov %rdi, %rsi +- and $-16, %rdi +- jmp L(loop) +- +- .p2align 4 +-L(crosscache): +- and $15, %rcx +- and $-16, %rdi +- pxor %xmm3, %xmm3 +- movdqa (%rdi), %xmm0 +- pcmpeqd %xmm0, %xmm3 +- pcmpeqd %xmm1, %xmm0 +- pmovmskb %xmm3, %rdx +- pmovmskb %xmm0, %rax +- shr %cl, %rdx +- shr %cl, %rax +- add $16, %rdi +- +- test %rax, %rax +- jnz L(unaligned_match) +- +- test %rdx, %rdx +- jnz L(return_null) +- +- xor %r8, %r8 +- jmp L(loop) +- +- .p2align 4 +-L(unaligned_match): +- test %rdx, %rdx +- jnz L(prolog_find_zero) +- +- mov %rax, %r8 +- lea (%rdi, %rcx), %rsi +- +-/* Loop start on aligned string. */ +- .p2align 4 +-L(loop): +- movdqa (%rdi), %xmm0 +- pcmpeqd %xmm0, %xmm2 +- add $16, %rdi +- pcmpeqd %xmm1, %xmm0 +- pmovmskb %xmm2, %rcx +- pmovmskb %xmm0, %rax +- or %rax, %rcx +- jnz L(matches) +- +- movdqa (%rdi), %xmm3 +- pcmpeqd %xmm3, %xmm2 +- add $16, %rdi +- pcmpeqd %xmm1, %xmm3 +- pmovmskb %xmm2, %rcx +- pmovmskb %xmm3, %rax +- or %rax, %rcx +- jnz L(matches) +- +- movdqa (%rdi), %xmm4 +- pcmpeqd %xmm4, %xmm2 +- add $16, %rdi +- pcmpeqd %xmm1, %xmm4 +- pmovmskb %xmm2, %rcx +- pmovmskb %xmm4, %rax +- or %rax, %rcx +- jnz L(matches) +- +- movdqa (%rdi), %xmm5 +- pcmpeqd %xmm5, %xmm2 +- add $16, %rdi +- pcmpeqd %xmm1, %xmm5 +- pmovmskb %xmm2, %rcx +- pmovmskb %xmm5, %rax +- or %rax, %rcx +- jz L(loop) +- +- .p2align 4 +-L(matches): +- test %rax, %rax +- jnz L(match) +-L(return_value): +- test %r8, %r8 +- jz L(return_null) +- mov %r8, %rax +- mov %rsi, %rdi +- +- test $15 << 4, %ah +- jnz L(match_fourth_wchar) +- test %ah, %ah +- jnz L(match_third_wchar) +- test $15 << 4, %al +- jnz L(match_second_wchar) +- lea -16(%rdi), %rax +- ret +- +- .p2align 4 +-L(match): +- pmovmskb %xmm2, %rcx +- test %rcx, %rcx +- jnz L(find_zero) +- mov %rax, %r8 +- mov %rdi, %rsi +- jmp L(loop) +- +- .p2align 4 +-L(find_zero): +- test $15, %cl +- jnz L(find_zero_in_first_wchar) +- test %cl, %cl +- jnz L(find_zero_in_second_wchar) +- test $15, %ch +- jnz L(find_zero_in_third_wchar) +- +- and $1 << 13 - 1, %rax +- jz L(return_value) +- +- test $15 << 4, %ah +- jnz L(match_fourth_wchar) +- test %ah, %ah +- jnz L(match_third_wchar) +- test $15 << 4, %al +- jnz L(match_second_wchar) +- lea -16(%rdi), %rax +- ret +- +- .p2align 4 +-L(find_zero_in_first_wchar): +- test $1, %rax +- jz L(return_value) +- lea -16(%rdi), %rax +- ret +- +- .p2align 4 +-L(find_zero_in_second_wchar): +- and $1 << 5 - 1, %rax +- jz L(return_value) +- +- test $15 << 4, %al +- jnz L(match_second_wchar) +- lea -16(%rdi), %rax +- ret +- +- .p2align 4 +-L(find_zero_in_third_wchar): +- and $1 << 9 - 1, %rax +- jz L(return_value) +- +- test %ah, %ah +- jnz L(match_third_wchar) +- test $15 << 4, %al +- jnz L(match_second_wchar) +- lea -16(%rdi), %rax +- ret +- +- .p2align 4 +-L(prolog_find_zero): +- add %rcx, %rdi +- mov %rdx, %rcx +-L(prolog_find_zero_1): +- test $15, %cl +- jnz L(prolog_find_zero_in_first_wchar) +- test %cl, %cl +- jnz L(prolog_find_zero_in_second_wchar) +- test $15, %ch +- jnz L(prolog_find_zero_in_third_wchar) +- +- and $1 << 13 - 1, %rax +- jz L(return_null) +- +- test $15 << 4, %ah +- jnz L(match_fourth_wchar) +- test %ah, %ah +- jnz L(match_third_wchar) +- test $15 << 4, %al +- jnz L(match_second_wchar) +- lea -16(%rdi), %rax +- ret +- +- .p2align 4 +-L(prolog_find_zero_in_first_wchar): +- test $1, %rax +- jz L(return_null) +- lea -16(%rdi), %rax +- ret +- +- .p2align 4 +-L(prolog_find_zero_in_second_wchar): +- and $1 << 5 - 1, %rax +- jz L(return_null) +- +- test $15 << 4, %al +- jnz L(match_second_wchar) +- lea -16(%rdi), %rax +- ret +- +- .p2align 4 +-L(prolog_find_zero_in_third_wchar): +- and $1 << 9 - 1, %rax +- jz L(return_null) +- +- test %ah, %ah +- jnz L(match_third_wchar) +- test $15 << 4, %al +- jnz L(match_second_wchar) +- lea -16(%rdi), %rax +- ret +- +- .p2align 4 +-L(match_second_wchar): +- lea -12(%rdi), %rax +- ret +- +- .p2align 4 +-L(match_third_wchar): +- lea -8(%rdi), %rax +- ret +- +- .p2align 4 +-L(match_fourth_wchar): +- lea -4(%rdi), %rax +- ret +- +- .p2align 4 +-L(return_null): +- xor %rax, %rax +- ret +- +-END (wcsrchr) ++#include "../strrchr.S" +diff -Nuar a/sysdeps/x86_64/wmemcmp.S b/sysdeps/x86_64/wmemcmp.S +--- a/sysdeps/x86_64/wmemcmp.S 1970-01-01 02:00:00.000000000 +0200 ++++ b/sysdeps/x86_64/wmemcmp.S 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,23 @@ ++/* wmemcmp optimized with SSE2. ++ Copyright (C) 2022 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 ++ . */ ++ ++#define MEMCMP __wmemcmp ++#define USE_AS_WMEMCMP 1 ++#include "memcmp.S" ++ ++weak_alias (__wmemcmp, wmemcmp) +diff -Nuar a/time/Makefile b/time/Makefile +--- a/time/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/time/Makefile 2025-10-20 21:02:22.000000000 +0300 +@@ -50,7 +50,7 @@ + tst-clock tst-clock2 tst-clock_nanosleep tst-cpuclock1 \ + tst-adjtime tst-ctime tst-difftime tst-mktime4 tst-clock_settime \ + tst-settimeofday tst-itimer tst-gmtime tst-timegm \ +- tst-timespec_get tst-timespec_getres ++ tst-timespec_get tst-timespec_getres tst-strftime4 + + tests-time64 := \ + tst-adjtime-time64 \ +@@ -65,6 +65,7 @@ + tst-itimer-time64 \ + tst-mktime4-time64 \ + tst-settimeofday-time64 \ ++ tst-strftime4-time64 \ + tst-timegm-time64 \ + tst-timespec_get-time64 \ + tst-timespec_getres-time64 \ +diff -Nuar a/time/mktime.c b/time/mktime.c +--- a/time/mktime.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/time/mktime.c 2025-10-20 21:02:22.000000000 +0300 +@@ -429,8 +429,13 @@ + time with the right value, and use its UTC offset. + + Heuristic: probe the adjacent timestamps in both directions, +- looking for the desired isdst. This should work for all real +- time zone histories in the tz database. */ ++ looking for the desired isdst. If none is found within a ++ reasonable duration bound, assume a one-hour DST difference. ++ This should work for all real time zone histories in the tz ++ database. */ ++ ++ /* +1 if we wanted standard time but got DST, -1 if the reverse. */ ++ int dst_difference = (isdst == 0) - (tm.tm_isdst == 0); + + /* Distance between probes when looking for a DST boundary. In + tzdata2003a, the shortest period of DST is 601200 seconds +@@ -441,12 +446,14 @@ + periods when probing. */ + int stride = 601200; + +- /* The longest period of DST in tzdata2003a is 536454000 seconds +- (e.g., America/Jujuy starting 1946-10-01 01:00). The longest +- period of non-DST is much longer, but it makes no real sense +- to search for more than a year of non-DST, so use the DST +- max. */ +- int duration_max = 536454000; ++ /* In TZDB 2021e, the longest period of DST (or of non-DST), in ++ which the DST (or adjacent DST) difference is not one hour, ++ is 457243209 seconds: e.g., America/Cambridge_Bay with leap ++ seconds, starting 1965-10-31 00:00 in a switch from ++ double-daylight time (-05) to standard time (-07), and ++ continuing to 1980-04-27 02:00 in a switch from standard time ++ (-07) to daylight time (-06). */ ++ int duration_max = 457243209; + + /* Search in both directions, so the maximum distance is half + the duration; add the stride to avoid off-by-1 problems. */ +@@ -483,6 +490,11 @@ + } + } + ++ /* No unusual DST offset was found nearby. Assume one-hour DST. */ ++ t += 60 * 60 * dst_difference; ++ if (mktime_min <= t && t <= mktime_max && convert_time (convert, t, &tm)) ++ goto offset_found; ++ + __set_errno (EOVERFLOW); + return -1; + } +diff -Nuar a/time/strftime_l.c b/time/strftime_l.c +--- a/time/strftime_l.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/time/strftime_l.c 2025-10-20 21:02:22.000000000 +0300 +@@ -159,6 +159,10 @@ + #ifdef _LIBC + # define tzname __tzname + # define tzset __tzset ++ ++# define time_t __time64_t ++# define __gmtime_r(t, tp) __gmtime64_r (t, tp) ++# define mktime(tp) __mktime64 (tp) + #endif + + #if !HAVE_TM_GMTOFF +diff -Nuar a/time/strptime_l.c b/time/strptime_l.c +--- a/time/strptime_l.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/time/strptime_l.c 2025-10-20 21:02:22.000000000 +0300 +@@ -30,8 +30,10 @@ + #ifdef _LIBC + # define HAVE_LOCALTIME_R 0 + # include "../locale/localeinfo.h" +-#endif + ++# define time_t __time64_t ++# define __localtime_r(t, tp) __localtime64_r (t, tp) ++#endif + + #if ! HAVE_LOCALTIME_R && ! defined localtime_r + # ifdef _LIBC +diff -Nuar a/time/tst-strftime4.c b/time/tst-strftime4.c +--- a/time/tst-strftime4.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/time/tst-strftime4.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,52 @@ ++/* Test strftime and strptime after 2038-01-19 03:14:07 UTC (bug 30053). ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ TEST_VERIFY_EXIT (setenv ("TZ", "UTC0", 1) == 0); ++ tzset (); ++ if (sizeof (time_t) > 4) ++ { ++ time_t wrap = (time_t) 2147483648LL; ++ char buf[80]; ++ struct tm *tm = gmtime (&wrap); ++ TEST_VERIFY_EXIT (tm != NULL); ++ TEST_VERIFY_EXIT (strftime (buf, sizeof buf, "%s", tm) > 0); ++ puts (buf); ++ TEST_VERIFY (strcmp (buf, "2147483648") == 0); ++ ++ struct tm tm2; ++ char *p = strptime (buf, "%s", &tm2); ++ TEST_VERIFY_EXIT (p != NULL && *p == '\0'); ++ time_t t = mktime (&tm2); ++ printf ("%lld\n", (long long) t); ++ TEST_VERIFY (t == wrap); ++ } ++ else ++ FAIL_UNSUPPORTED ("32-bit time_t"); ++ return 0; ++} ++ ++#include +diff -Nuar a/time/tst-strftime4-time64.c b/time/tst-strftime4-time64.c +--- a/time/tst-strftime4-time64.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/time/tst-strftime4-time64.c 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1 @@ ++#include "tst-strftime4.c" +diff -Nuar a/time/tzfile.c b/time/tzfile.c +--- a/time/tzfile.c 2022-02-03 08:27:54.000000000 +0300 ++++ b/time/tzfile.c 2025-10-20 21:02:22.000000000 +0300 +@@ -32,7 +32,7 @@ + int __use_tzfile; + static dev_t tzfile_dev; + static ino64_t tzfile_ino; +-static time_t tzfile_mtime; ++static __time64_t tzfile_mtime; + + struct ttinfo + { +@@ -61,6 +61,10 @@ + static struct leap *leaps; + static char *tzspec; + ++/* Used to restore the daylight variable during time conversion, as if ++ tzset had been called. */ ++static int daylight_saved; ++ + #include + #include + +@@ -438,36 +442,35 @@ + if (__tzname[1] == NULL) + __tzname[1] = __tzname[0]; + ++ daylight_saved = 0; + if (num_transitions == 0) + /* Use the first rule (which should also be the only one). */ + rule_stdoff = rule_dstoff = types[0].offset; + else + { +- int stdoff_set = 0, dstoff_set = 0; +- rule_stdoff = rule_dstoff = 0; ++ rule_stdoff = 0; ++ ++ /* Search for the last rule with a standard time offset. This ++ will be used for the global timezone variable. */ + i = num_transitions - 1; + do +- { +- if (!stdoff_set && !types[type_idxs[i]].isdst) +- { +- stdoff_set = 1; +- rule_stdoff = types[type_idxs[i]].offset; +- } +- else if (!dstoff_set && types[type_idxs[i]].isdst) +- { +- dstoff_set = 1; +- rule_dstoff = types[type_idxs[i]].offset; +- } +- if (stdoff_set && dstoff_set) ++ if (!types[type_idxs[i]].isdst) ++ { ++ rule_stdoff = types[type_idxs[i]].offset; + break; +- } ++ } ++ else ++ daylight_saved = 1; + while (i-- > 0); + +- if (!dstoff_set) +- rule_dstoff = rule_stdoff; ++ /* Keep searching to see if there is a DST rule. This ++ information will be used to set the global daylight ++ variable. */ ++ while (i-- > 0 && !daylight_saved) ++ daylight_saved = types[type_idxs[i]].isdst; + } + +- __daylight = rule_stdoff != rule_dstoff; ++ __daylight = daylight_saved; + __timezone = -rule_stdoff; + + done: +@@ -731,7 +734,7 @@ + } + + struct ttinfo *info = &types[i]; +- __daylight = rule_stdoff != rule_dstoff; ++ __daylight = daylight_saved; + __timezone = -rule_stdoff; + + if (__tzname[0] == NULL) +diff -Nuar a/timezone/Makefile b/timezone/Makefile +--- a/timezone/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/timezone/Makefile 2025-10-20 21:02:22.000000000 +0300 +@@ -23,7 +23,7 @@ + include ../Makeconfig + + others := zdump zic +-tests := test-tz tst-timezone tst-tzset tst-bz28707 ++tests := test-tz tst-timezone tst-tzset tst-bz28707 tst-bz29951 + + generated-dirs += testdata + +@@ -86,11 +86,13 @@ + Europe/London) + $(objpfx)tst-tzset.out: $(addprefix $(testdata)/XT, 1 2 3 4) + $(objpfx)tst-bz28707.out: $(testdata)/XT5 ++$(objpfx)tst-bz29951.out: $(testdata)/XT6 + + test-tz-ENV = TZDIR=$(testdata) + tst-timezone-ENV = TZDIR=$(testdata) + tst-tzset-ENV = TZDIR=$(testdata) + tst-bz28707-ENV = TZDIR=$(testdata) ++tst-bz29951-ENV = TZDIR=$(testdata) + + # Note this must come second in the deps list for $(built-program-cmd) to work. + zic-deps = $(objpfx)zic $(leapseconds) yearistype +diff -Nuar a/timezone/testdata/XT6 b/timezone/testdata/XT6 +--- a/timezone/testdata/XT6 1970-01-01 02:00:00.000000000 +0200 ++++ b/timezone/testdata/XT6 2025-10-20 21:02:19.000000000 +0300 +@@ -0,0 +1,2 @@ ++TZif2 $ݻ#`xe/?pNB`p+*_`̯`z p` pJp!a~"Rp#D$4%%7`&@2N`3D6p45jPQTـRi \   LMTCESTCETEETTZif2 $ݻ#`xe/?pNB`p+*_`̯`z p` pJp!a~"Rp#D$4%%7`&@2N`3D6p45jPQTـRi \   LMTCESTCETEET ++EET-2 +diff -Nuar a/timezone/tst-bz29951.c b/timezone/tst-bz29951.c +--- a/timezone/tst-bz29951.c 1970-01-01 02:00:00.000000000 +0200 ++++ b/timezone/tst-bz29951.c 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,68 @@ ++/* Check that daylight is set if the last DST transition did not change offset. ++ Copyright (C) 2023 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++/* Set the specified time zone with error checking. */ ++static void ++set_timezone (const char *name) ++{ ++ TEST_VERIFY (setenv ("TZ", name, 1) == 0); ++ errno = 0; ++ tzset (); ++ TEST_COMPARE (errno, 0); ++} ++ ++static int ++do_test (void) ++{ ++ /* Test zone based on tz-2022g version of Africa/Tripoli. The last ++ DST transition coincided with a change in the standard time ++ offset, effectively making it a no-op. ++ ++ Africa/Tripoli Thu Oct 24 23:59:59 2013 UT ++ = Fri Oct 25 01:59:59 2013 CEST isdst=1 gmtoff=7200 ++ Africa/Tripoli Fri Oct 25 00:00:00 2013 UT ++ = Fri Oct 25 02:00:00 2013 EET isdst=0 gmtoff=7200 ++ */ ++ set_timezone ("XT6"); ++ TEST_VERIFY (daylight != 0); ++ TEST_COMPARE (timezone, -7200); ++ ++ /* Check that localtime re-initializes the two variables. */ ++ daylight = timezone = 17; ++ time_t t = 844034401; ++ struct tm *tm = localtime (&t); ++ TEST_VERIFY (daylight != 0); ++ TEST_COMPARE (timezone, -7200); ++ TEST_COMPARE (tm->tm_year, 96); ++ TEST_COMPARE (tm->tm_mon, 8); ++ TEST_COMPARE (tm->tm_mday, 29); ++ TEST_COMPARE (tm->tm_hour, 23); ++ TEST_COMPARE (tm->tm_min, 0); ++ TEST_COMPARE (tm->tm_sec, 1); ++ TEST_COMPARE (tm->tm_gmtoff, 3600); ++ TEST_COMPARE (tm->tm_isdst, 0); ++ ++ return 0; ++} ++ ++#include +diff -Nuar a/wcsmbs/bits/wchar2-decl.h b/wcsmbs/bits/wchar2-decl.h +--- a/wcsmbs/bits/wchar2-decl.h 1970-01-01 02:00:00.000000000 +0200 ++++ b/wcsmbs/bits/wchar2-decl.h 2025-10-20 21:02:22.000000000 +0300 +@@ -0,0 +1,124 @@ ++/* Checking macros for wchar functions. Declarations only. ++ Copyright (C) 2004-2022 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 ++ . */ ++ ++#ifndef _BITS_WCHAR2_DECL_H ++#define _BITS_WCHAR2_DECL_H 1 ++ ++#ifndef _WCHAR_H ++# error "Never include directly; use instead." ++#endif ++ ++ ++extern wchar_t *__wmemcpy_chk (wchar_t *__restrict __s1, ++ const wchar_t *__restrict __s2, size_t __n, ++ size_t __ns1) __THROW; ++extern wchar_t *__wmemmove_chk (wchar_t *__s1, const wchar_t *__s2, ++ size_t __n, size_t __ns1) __THROW; ++ ++ ++#ifdef __USE_GNU ++ ++extern wchar_t *__wmempcpy_chk (wchar_t *__restrict __s1, ++ const wchar_t *__restrict __s2, size_t __n, ++ size_t __ns1) __THROW; ++ ++#endif ++ ++ ++extern wchar_t *__wmemset_chk (wchar_t *__s, wchar_t __c, size_t __n, ++ size_t __ns) __THROW; ++extern wchar_t *__wcscpy_chk (wchar_t *__restrict __dest, ++ const wchar_t *__restrict __src, ++ size_t __n) __THROW; ++extern wchar_t *__wcpcpy_chk (wchar_t *__restrict __dest, ++ const wchar_t *__restrict __src, ++ size_t __destlen) __THROW; ++extern wchar_t *__wcsncpy_chk (wchar_t *__restrict __dest, ++ const wchar_t *__restrict __src, size_t __n, ++ size_t __destlen) __THROW; ++extern wchar_t *__wcpncpy_chk (wchar_t *__restrict __dest, ++ const wchar_t *__restrict __src, size_t __n, ++ size_t __destlen) __THROW; ++extern wchar_t *__wcscat_chk (wchar_t *__restrict __dest, ++ const wchar_t *__restrict __src, ++ size_t __destlen) __THROW; ++extern wchar_t *__wcsncat_chk (wchar_t *__restrict __dest, ++ const wchar_t *__restrict __src, ++ size_t __n, size_t __destlen) __THROW; ++extern int __swprintf_chk (wchar_t *__restrict __s, size_t __n, ++ int __flag, size_t __s_len, ++ const wchar_t *__restrict __format, ...) ++ __THROW /* __attribute__ ((__format__ (__wprintf__, 5, 6))) */; ++extern int __vswprintf_chk (wchar_t *__restrict __s, size_t __n, ++ int __flag, size_t __s_len, ++ const wchar_t *__restrict __format, ++ __gnuc_va_list __arg) ++ __THROW /* __attribute__ ((__format__ (__wprintf__, 5, 0))) */; ++ ++#if __USE_FORTIFY_LEVEL > 1 ++ ++extern int __fwprintf_chk (__FILE *__restrict __stream, int __flag, ++ const wchar_t *__restrict __format, ...); ++extern int __wprintf_chk (int __flag, const wchar_t *__restrict __format, ++ ...); ++extern int __vfwprintf_chk (__FILE *__restrict __stream, int __flag, ++ const wchar_t *__restrict __format, ++ __gnuc_va_list __ap); ++extern int __vwprintf_chk (int __flag, const wchar_t *__restrict __format, ++ __gnuc_va_list __ap); ++ ++#endif ++ ++extern wchar_t *__fgetws_chk (wchar_t *__restrict __s, size_t __size, int __n, ++ __FILE *__restrict __stream) __wur; ++ ++#ifdef __USE_GNU ++ ++extern wchar_t *__fgetws_unlocked_chk (wchar_t *__restrict __s, size_t __size, ++ int __n, __FILE *__restrict __stream) ++ __wur; ++ ++#endif ++ ++extern size_t __wcrtomb_chk (char *__restrict __s, wchar_t __wchar, ++ mbstate_t *__restrict __p, ++ size_t __buflen) __THROW __wur; ++extern size_t __mbsrtowcs_chk (wchar_t *__restrict __dst, ++ const char **__restrict __src, ++ size_t __len, mbstate_t *__restrict __ps, ++ size_t __dstlen) __THROW; ++extern size_t __wcsrtombs_chk (char *__restrict __dst, ++ const wchar_t **__restrict __src, ++ size_t __len, mbstate_t *__restrict __ps, ++ size_t __dstlen) __THROW; ++ ++#ifdef __USE_XOPEN2K8 ++ ++extern size_t __mbsnrtowcs_chk (wchar_t *__restrict __dst, ++ const char **__restrict __src, size_t __nmc, ++ size_t __len, mbstate_t *__restrict __ps, ++ size_t __dstlen) __THROW; ++extern size_t __wcsnrtombs_chk (char *__restrict __dst, ++ const wchar_t **__restrict __src, ++ size_t __nwc, size_t __len, ++ mbstate_t *__restrict __ps, size_t __dstlen) ++ __THROW; ++ ++#endif ++ ++#endif /* bits/wchar2-decl.h. */ +diff -Nuar a/wcsmbs/bits/wchar2.h b/wcsmbs/bits/wchar2.h +--- a/wcsmbs/bits/wchar2.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/wcsmbs/bits/wchar2.h 2025-10-20 21:02:22.000000000 +0300 +@@ -21,9 +21,6 @@ + #endif + + +-extern wchar_t *__wmemcpy_chk (wchar_t *__restrict __s1, +- const wchar_t *__restrict __s2, size_t __n, +- size_t __ns1) __THROW; + extern wchar_t *__REDIRECT_NTH (__wmemcpy_alias, + (wchar_t *__restrict __s1, + const wchar_t *__restrict __s2, size_t __n), +@@ -45,8 +42,6 @@ + } + + +-extern wchar_t *__wmemmove_chk (wchar_t *__s1, const wchar_t *__s2, +- size_t __n, size_t __ns1) __THROW; + extern wchar_t *__REDIRECT_NTH (__wmemmove_alias, (wchar_t *__s1, + const wchar_t *__s2, + size_t __n), wmemmove); +@@ -66,9 +61,6 @@ + + + #ifdef __USE_GNU +-extern wchar_t *__wmempcpy_chk (wchar_t *__restrict __s1, +- const wchar_t *__restrict __s2, size_t __n, +- size_t __ns1) __THROW; + extern wchar_t *__REDIRECT_NTH (__wmempcpy_alias, + (wchar_t *__restrict __s1, + const wchar_t *__restrict __s2, +@@ -91,8 +83,6 @@ + #endif + + +-extern wchar_t *__wmemset_chk (wchar_t *__s, wchar_t __c, size_t __n, +- size_t __ns) __THROW; + extern wchar_t *__REDIRECT_NTH (__wmemset_alias, (wchar_t *__s, wchar_t __c, + size_t __n), wmemset); + extern wchar_t *__REDIRECT_NTH (__wmemset_chk_warn, +@@ -110,9 +100,6 @@ + } + + +-extern wchar_t *__wcscpy_chk (wchar_t *__restrict __dest, +- const wchar_t *__restrict __src, +- size_t __n) __THROW; + extern wchar_t *__REDIRECT_NTH (__wcscpy_alias, + (wchar_t *__restrict __dest, + const wchar_t *__restrict __src), wcscpy); +@@ -120,16 +107,13 @@ + __fortify_function wchar_t * + __NTH (wcscpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src)) + { +- size_t sz = __glibc_objsize (__dest); +- if (sz != (size_t) -1) +- return __wcscpy_chk (__dest, __src, sz / sizeof (wchar_t)); ++ size_t __sz = __glibc_objsize (__dest); ++ if (__sz != (size_t) -1) ++ return __wcscpy_chk (__dest, __src, __sz / sizeof (wchar_t)); + return __wcscpy_alias (__dest, __src); + } + + +-extern wchar_t *__wcpcpy_chk (wchar_t *__restrict __dest, +- const wchar_t *__restrict __src, +- size_t __destlen) __THROW; + extern wchar_t *__REDIRECT_NTH (__wcpcpy_alias, + (wchar_t *__restrict __dest, + const wchar_t *__restrict __src), wcpcpy); +@@ -137,16 +121,13 @@ + __fortify_function wchar_t * + __NTH (wcpcpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src)) + { +- size_t sz = __glibc_objsize (__dest); +- if (sz != (size_t) -1) +- return __wcpcpy_chk (__dest, __src, sz / sizeof (wchar_t)); ++ size_t __sz = __glibc_objsize (__dest); ++ if (__sz != (size_t) -1) ++ return __wcpcpy_chk (__dest, __src, __sz / sizeof (wchar_t)); + return __wcpcpy_alias (__dest, __src); + } + + +-extern wchar_t *__wcsncpy_chk (wchar_t *__restrict __dest, +- const wchar_t *__restrict __src, size_t __n, +- size_t __destlen) __THROW; + extern wchar_t *__REDIRECT_NTH (__wcsncpy_alias, + (wchar_t *__restrict __dest, + const wchar_t *__restrict __src, +@@ -168,9 +149,6 @@ + } + + +-extern wchar_t *__wcpncpy_chk (wchar_t *__restrict __dest, +- const wchar_t *__restrict __src, size_t __n, +- size_t __destlen) __THROW; + extern wchar_t *__REDIRECT_NTH (__wcpncpy_alias, + (wchar_t *__restrict __dest, + const wchar_t *__restrict __src, +@@ -192,9 +170,6 @@ + } + + +-extern wchar_t *__wcscat_chk (wchar_t *__restrict __dest, +- const wchar_t *__restrict __src, +- size_t __destlen) __THROW; + extern wchar_t *__REDIRECT_NTH (__wcscat_alias, + (wchar_t *__restrict __dest, + const wchar_t *__restrict __src), wcscat); +@@ -202,16 +177,13 @@ + __fortify_function wchar_t * + __NTH (wcscat (wchar_t *__restrict __dest, const wchar_t *__restrict __src)) + { +- size_t sz = __glibc_objsize (__dest); +- if (sz != (size_t) -1) +- return __wcscat_chk (__dest, __src, sz / sizeof (wchar_t)); ++ size_t __sz = __glibc_objsize (__dest); ++ if (__sz != (size_t) -1) ++ return __wcscat_chk (__dest, __src, __sz / sizeof (wchar_t)); + return __wcscat_alias (__dest, __src); + } + + +-extern wchar_t *__wcsncat_chk (wchar_t *__restrict __dest, +- const wchar_t *__restrict __src, +- size_t __n, size_t __destlen) __THROW; + extern wchar_t *__REDIRECT_NTH (__wcsncat_alias, + (wchar_t *__restrict __dest, + const wchar_t *__restrict __src, +@@ -221,17 +193,13 @@ + __NTH (wcsncat (wchar_t *__restrict __dest, const wchar_t *__restrict __src, + size_t __n)) + { +- size_t sz = __glibc_objsize (__dest); +- if (sz != (size_t) -1) +- return __wcsncat_chk (__dest, __src, __n, sz / sizeof (wchar_t)); ++ size_t __sz = __glibc_objsize (__dest); ++ if (__sz != (size_t) -1) ++ return __wcsncat_chk (__dest, __src, __n, __sz / sizeof (wchar_t)); + return __wcsncat_alias (__dest, __src, __n); + } + + +-extern int __swprintf_chk (wchar_t *__restrict __s, size_t __n, +- int __flag, size_t __s_len, +- const wchar_t *__restrict __format, ...) +- __THROW /* __attribute__ ((__format__ (__wprintf__, 5, 6))) */; + + extern int __REDIRECT_NTH_LDBL (__swprintf_alias, + (wchar_t *__restrict __s, size_t __n, +@@ -243,10 +211,10 @@ + __NTH (swprintf (wchar_t *__restrict __s, size_t __n, + const wchar_t *__restrict __fmt, ...)) + { +- size_t sz = __glibc_objsize (__s); +- if (sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1) ++ size_t __sz = __glibc_objsize (__s); ++ if (__sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1) + return __swprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, +- sz / sizeof (wchar_t), __fmt, __va_arg_pack ()); ++ __sz / sizeof (wchar_t), __fmt, __va_arg_pack ()); + return __swprintf_alias (__s, __n, __fmt, __va_arg_pack ()); + } + #elif !defined __cplusplus +@@ -258,11 +226,6 @@ + : swprintf (s, n, __VA_ARGS__)) + #endif + +-extern int __vswprintf_chk (wchar_t *__restrict __s, size_t __n, +- int __flag, size_t __s_len, +- const wchar_t *__restrict __format, +- __gnuc_va_list __arg) +- __THROW /* __attribute__ ((__format__ (__wprintf__, 5, 0))) */; + + extern int __REDIRECT_NTH_LDBL (__vswprintf_alias, + (wchar_t *__restrict __s, size_t __n, +@@ -273,26 +236,16 @@ + __NTH (vswprintf (wchar_t *__restrict __s, size_t __n, + const wchar_t *__restrict __fmt, __gnuc_va_list __ap)) + { +- size_t sz = __glibc_objsize (__s); +- if (sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1) ++ size_t __sz = __glibc_objsize (__s); ++ if (__sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1) + return __vswprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, +- sz / sizeof (wchar_t), __fmt, __ap); ++ __sz / sizeof (wchar_t), __fmt, __ap); + return __vswprintf_alias (__s, __n, __fmt, __ap); + } + + + #if __USE_FORTIFY_LEVEL > 1 + +-extern int __fwprintf_chk (__FILE *__restrict __stream, int __flag, +- const wchar_t *__restrict __format, ...); +-extern int __wprintf_chk (int __flag, const wchar_t *__restrict __format, +- ...); +-extern int __vfwprintf_chk (__FILE *__restrict __stream, int __flag, +- const wchar_t *__restrict __format, +- __gnuc_va_list __ap); +-extern int __vwprintf_chk (int __flag, const wchar_t *__restrict __format, +- __gnuc_va_list __ap); +- + # ifdef __va_arg_pack + __fortify_function int + wprintf (const wchar_t *__restrict __fmt, ...) +@@ -328,8 +281,6 @@ + + #endif + +-extern wchar_t *__fgetws_chk (wchar_t *__restrict __s, size_t __size, int __n, +- __FILE *__restrict __stream) __wur; + extern wchar_t *__REDIRECT (__fgetws_alias, + (wchar_t *__restrict __s, int __n, + __FILE *__restrict __stream), fgetws) __wur; +@@ -342,18 +293,15 @@ + __fortify_function __wur wchar_t * + fgetws (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize (__s); +- if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), sz)) ++ size_t __sz = __glibc_objsize (__s); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), __sz)) + return __fgetws_alias (__s, __n, __stream); +- if (__glibc_unsafe_len (__n, sizeof (wchar_t), sz)) +- return __fgetws_chk_warn (__s, sz / sizeof (wchar_t), __n, __stream); +- return __fgetws_chk (__s, sz / sizeof (wchar_t), __n, __stream); ++ if (__glibc_unsafe_len (__n, sizeof (wchar_t), __sz)) ++ return __fgetws_chk_warn (__s, __sz / sizeof (wchar_t), __n, __stream); ++ return __fgetws_chk (__s, __sz / sizeof (wchar_t), __n, __stream); + } + + #ifdef __USE_GNU +-extern wchar_t *__fgetws_unlocked_chk (wchar_t *__restrict __s, size_t __size, +- int __n, __FILE *__restrict __stream) +- __wur; + extern wchar_t *__REDIRECT (__fgetws_unlocked_alias, + (wchar_t *__restrict __s, int __n, + __FILE *__restrict __stream), fgetws_unlocked) +@@ -368,20 +316,17 @@ + __fortify_function __wur wchar_t * + fgetws_unlocked (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream) + { +- size_t sz = __glibc_objsize (__s); +- if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), sz)) ++ size_t __sz = __glibc_objsize (__s); ++ if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), __sz)) + return __fgetws_unlocked_alias (__s, __n, __stream); +- if (__glibc_unsafe_len (__n, sizeof (wchar_t), sz)) +- return __fgetws_unlocked_chk_warn (__s, sz / sizeof (wchar_t), __n, ++ if (__glibc_unsafe_len (__n, sizeof (wchar_t), __sz)) ++ return __fgetws_unlocked_chk_warn (__s, __sz / sizeof (wchar_t), __n, + __stream); +- return __fgetws_unlocked_chk (__s, sz / sizeof (wchar_t), __n, __stream); ++ return __fgetws_unlocked_chk (__s, __sz / sizeof (wchar_t), __n, __stream); + } + #endif + + +-extern size_t __wcrtomb_chk (char *__restrict __s, wchar_t __wchar, +- mbstate_t *__restrict __p, +- size_t __buflen) __THROW __wur; + extern size_t __REDIRECT_NTH (__wcrtomb_alias, + (char *__restrict __s, wchar_t __wchar, + mbstate_t *__restrict __ps), wcrtomb) __wur; +@@ -404,10 +349,6 @@ + } + + +-extern size_t __mbsrtowcs_chk (wchar_t *__restrict __dst, +- const char **__restrict __src, +- size_t __len, mbstate_t *__restrict __ps, +- size_t __dstlen) __THROW; + extern size_t __REDIRECT_NTH (__mbsrtowcs_alias, + (wchar_t *__restrict __dst, + const char **__restrict __src, +@@ -431,10 +372,6 @@ + } + + +-extern size_t __wcsrtombs_chk (char *__restrict __dst, +- const wchar_t **__restrict __src, +- size_t __len, mbstate_t *__restrict __ps, +- size_t __dstlen) __THROW; + extern size_t __REDIRECT_NTH (__wcsrtombs_alias, + (char *__restrict __dst, + const wchar_t **__restrict __src, +@@ -458,10 +395,6 @@ + + + #ifdef __USE_XOPEN2K8 +-extern size_t __mbsnrtowcs_chk (wchar_t *__restrict __dst, +- const char **__restrict __src, size_t __nmc, +- size_t __len, mbstate_t *__restrict __ps, +- size_t __dstlen) __THROW; + extern size_t __REDIRECT_NTH (__mbsnrtowcs_alias, + (wchar_t *__restrict __dst, + const char **__restrict __src, size_t __nmc, +@@ -485,11 +418,6 @@ + } + + +-extern size_t __wcsnrtombs_chk (char *__restrict __dst, +- const wchar_t **__restrict __src, +- size_t __nwc, size_t __len, +- mbstate_t *__restrict __ps, size_t __dstlen) +- __THROW; + extern size_t __REDIRECT_NTH (__wcsnrtombs_alias, + (char *__restrict __dst, + const wchar_t **__restrict __src, +diff -Nuar a/wcsmbs/Makefile b/wcsmbs/Makefile +--- a/wcsmbs/Makefile 2022-02-03 08:27:54.000000000 +0300 ++++ b/wcsmbs/Makefile 2025-10-20 21:02:22.000000000 +0300 +@@ -22,8 +22,9 @@ + + include ../Makeconfig + +-headers := wchar.h bits/wchar.h bits/wchar2.h bits/wchar-ldbl.h uchar.h \ +- bits/types/__mbstate_t.h bits/types/mbstate_t.h bits/types/wint_t.h ++headers := wchar.h bits/wchar.h bits/wchar2.h bits/wchar2-decl.h \ ++ bits/wchar-ldbl.h uchar.h bits/types/__mbstate_t.h \ ++ bits/types/mbstate_t.h bits/types/wint_t.h + + routines := wcscat wcschr wcscmp wcscpy wcscspn wcsdup wcslen wcsncat \ + wcsncmp wcsncpy wcspbrk wcsrchr wcsspn wcstok wcsstr wmemchr \ +diff -Nuar a/wcsmbs/wchar.h b/wcsmbs/wchar.h +--- a/wcsmbs/wchar.h 2022-02-03 08:27:54.000000000 +0300 ++++ b/wcsmbs/wchar.h 2025-10-20 21:02:22.000000000 +0300 +@@ -864,14 +864,21 @@ + + /* Define some macros helping to catch buffer overflows. */ + #if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function +-# include ++/* Declare all functions from bits/wchar2-decl.h first. */ ++# include + #endif + +-#include ++/* The following headers provide asm redirections. These redirections must ++ appear before the first usage of these functions, e.g. in bits/wchar.h. */ + #if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 + # include + #endif + ++#if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function ++/* Now include the function definitions and redirects too. */ ++# include ++#endif ++ + __END_DECLS + + #endif /* wchar.h */ diff --git a/system/base/glibc/files/glibc32.txt b/system/base/glibc/files/glibc32.txt old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/host.conf b/system/base/glibc/files/host.conf old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/locale-gen.txt b/system/base/glibc/files/locale-gen.txt old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/locale-no-archive.patch b/system/base/glibc/files/locale-no-archive.patch old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/nscd.conf b/system/base/glibc/files/nscd.conf old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/nsswitch.conf b/system/base/glibc/files/nsswitch.conf old mode 100644 new mode 100755 diff --git a/system/base/glibc/files/pure64.patch b/system/base/glibc/files/pure64.patch old mode 100644 new mode 100755 diff --git a/system/base/glibc/pspec.xml b/system/base/glibc/pspec.xml old mode 100644 new mode 100755 index 56da5337..099d2d1f --- a/system/base/glibc/pspec.xml +++ b/system/base/glibc/pspec.xml @@ -23,7 +23,7 @@ - 0001-localedef-Update-LC_MONETARY-handling-Bug-28845.patch + glibc-4e5004-git.patch @@ -2089,6 +2089,13 @@ + + 2025-10-20 + 2.35 + Rebuild + Mustafa Cinasal + muscnsl@gmail.com + 2025-07-25 2.35 diff --git a/system/base/glibc/translations.xml b/system/base/glibc/translations.xml old mode 100644 new mode 100755