diff --git a/nixos/modules/services/desktop-managers/gnome.nix b/nixos/modules/services/desktop-managers/gnome.nix index bfad0efc7f654..4fddcf9903cb8 100644 --- a/nixos/modules/services/desktop-managers/gnome.nix +++ b/nixos/modules/services/desktop-managers/gnome.nix @@ -390,6 +390,10 @@ in systemd.packages = [ pkgs.gnome-session pkgs.gnome-shell + ] + ++ removeExcluded [ + pkgs.xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/ + pkgs.xdg-user-dirs-gtk # Used to create the default bookmarks ]; services.udev.packages = [ diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix index fe0bde3176462..53f8db7a808c1 100644 --- a/nixos/tests/systemd-boot.nix +++ b/nixos/tests/systemd-boot.nix @@ -158,7 +158,8 @@ in } ); - basicXbootldr = runTest ( + # TODO: Fix on aarch64-linux + basicXbootldr = runTestOn [ "x86_64-linux" ] ( { lib, ... }: { name = "systemd-boot-xbootldr"; @@ -282,7 +283,8 @@ in } ); - update = runTest ( + # TODO: Fix on aarch64-linux + update = runTestOn [ "x86_64-linux" ] ( { lib, ... }: { name = "systemd-boot-update"; @@ -457,7 +459,8 @@ in } ); - entryFilenameXbootldr = runTest ( + # TODO: Fix on aarch64-linux + entryFilenameXbootldr = runTestOn [ "x86_64-linux" ] ( { lib, ... }: { name = "systemd-boot-entry-filename-xbootldr"; diff --git a/pkgs/applications/audio/mpg123/default.nix b/pkgs/applications/audio/mpg123/default.nix index fb95111cbfe5f..e526288734e1b 100644 --- a/pkgs/applications/audio/mpg123/default.nix +++ b/pkgs/applications/audio/mpg123/default.nix @@ -21,11 +21,11 @@ assert withConplay -> !libOnly; stdenv.mkDerivation rec { pname = "${lib.optionalString libOnly "lib"}mpg123"; - version = "1.33.3"; + version = "1.33.4"; src = fetchurl { url = "mirror://sourceforge/mpg123/mpg123-${version}.tar.bz2"; - hash = "sha256-agxkct0VbiE8IGj0ARXru3OXjC2HPma64qJQ4tIZjSY="; + hash = "sha256-OujJ/4Cpe/wOIuifvNdGh+yk/B2zFbEmB/J/ActaR9k="; }; outputs = [ diff --git a/pkgs/applications/graphics/ImageMagick/default.nix b/pkgs/applications/graphics/ImageMagick/default.nix index 05c4735524daa..33dd080943a1a 100644 --- a/pkgs/applications/graphics/ImageMagick/default.nix +++ b/pkgs/applications/graphics/ImageMagick/default.nix @@ -85,13 +85,13 @@ in stdenv.mkDerivation (finalAttrs: { pname = "imagemagick"; - version = "7.1.2-10"; + version = "7.1.2-13"; src = fetchFromGitHub { owner = "ImageMagick"; repo = "ImageMagick"; tag = finalAttrs.version; - hash = "sha256-96lhd0B4yV2s/zVazKrqAcDZvn+yIiXxp8fqyKSfxLc="; + hash = "sha256-meADRjoV1c48laD35TuWAwuE95L90agROuuKBd++Kn8="; }; outputs = [ diff --git a/pkgs/by-name/am/amazon-ssm-agent/package.nix b/pkgs/by-name/am/amazon-ssm-agent/package.nix index 2bcdd3b50dc18..225676459aa20 100644 --- a/pkgs/by-name/am/amazon-ssm-agent/package.nix +++ b/pkgs/by-name/am/amazon-ssm-agent/package.nix @@ -107,10 +107,6 @@ buildGoModule rec { ''; preBuild = '' - # Note: if this step fails, please patch the code to fix it! Please only skip - # tests if it is not feasible for the test to pass in a sandbox. - make quick-integtest - make pre-release make pre-build ''; @@ -143,11 +139,7 @@ buildGoModule rec { runHook postInstall ''; - checkFlags = [ - # Skip time dependent/flaky test - "-skip=TestSendStreamDataMessageWithStreamDataSequenceNumberMutexLocked" - "-skip=TestParallelAccessOfQueue" - ]; + doCheck = false; postFixup = '' wrapProgram $out/bin/amazon-ssm-agent \ diff --git a/pkgs/by-name/bi/bind/package.nix b/pkgs/by-name/bi/bind/package.nix index 68b0f8f4f81fd..c49ae029d7fa6 100644 --- a/pkgs/by-name/bi/bind/package.nix +++ b/pkgs/by-name/bi/bind/package.nix @@ -29,11 +29,11 @@ stdenv.mkDerivation (finalAttrs: { pname = "bind"; - version = "9.20.15"; + version = "9.20.18"; src = fetchurl { url = "https://downloads.isc.org/isc/bind9/${finalAttrs.version}/bind-${finalAttrs.version}.tar.xz"; - hash = "sha256-1is4+uSLqD/KYYERLQxxAY2LDyzihdx53GoDZ3Isyrs="; + hash = "sha256-38VGyZCsRRVSnNRcTdmVhisYrootDLKSCOiJal0yUzE="; }; outputs = [ diff --git a/pkgs/by-name/cu/curlMinimal/package.nix b/pkgs/by-name/cu/curlMinimal/package.nix index f64a4c9de767f..48cd59798c61b 100644 --- a/pkgs/by-name/cu/curlMinimal/package.nix +++ b/pkgs/by-name/cu/curlMinimal/package.nix @@ -86,7 +86,7 @@ assert stdenv.mkDerivation (finalAttrs: { pname = "curl"; - version = "8.17.0"; + version = "8.18.0"; src = fetchurl { urls = [ @@ -95,7 +95,7 @@ stdenv.mkDerivation (finalAttrs: { builtins.replaceStrings [ "." ] [ "_" ] finalAttrs.version }/curl-${finalAttrs.version}.tar.xz" ]; - hash = "sha256-lV9ucprWs1ZiYOj+9oYg52ujwxrPChhSRBahhaz3eZI="; + hash = "sha256-QN95Fm50qiAUk2XhHuTHmKRq1Xw05PaP0TEA4smpGUY="; }; # this could be accomplished by updateAutotoolsGnuConfigScriptsHook, but that causes infinite recursion diff --git a/pkgs/by-name/fo/fontforge/package.nix b/pkgs/by-name/fo/fontforge/package.nix index d929d95d6ea4a..7b60bd47b28e9 100644 --- a/pkgs/by-name/fo/fontforge/package.nix +++ b/pkgs/by-name/fo/fontforge/package.nix @@ -1,6 +1,7 @@ { stdenv, fetchFromGitHub, + fetchpatch, lib, replaceVars, cmake, @@ -48,6 +49,26 @@ stdenv.mkDerivation (finalAttrs: { patches = [ # Provide a Nix-controlled location for the initial `sys.path` entry. (replaceVars ./set-python-sys-path.patch { python = "${py}/${py.sitePackages}"; }) + (fetchpatch { + name = "CVE-2025-15279_1.patch"; + url = "https://github.com/fontforge/fontforge/commit/7d67700cf8888e0bb37b453ad54ed932c8587073.patch"; + hash = "sha256-AqixWSgMc75qkgO30nWnI9NKLRtVwCDR+uSEiwMtFKg="; + }) + (fetchpatch { + name = "CVE-2025-15279_2.patch"; + url = "https://github.com/fontforge/fontforge/commit/720ea95020c964202928afd2e93b0f5fac11027e.patch"; + hash = "sha256-DsP2fDTZlTtg8MXcnsuGQ4PFPOVp56Jm95gq877PLlE="; + }) + (fetchpatch { + name = "CVE-2025-15275.patch"; + url = "https://github.com/fontforge/fontforge/commit/7195402701ace7783753ef9424153eff48c9af44.patch"; + hash = "sha256-NHgKUvHF389z7PRqaDj3IWLSLijlSw0F3UYcMjLxKvE="; + }) + (fetchpatch { + name = "CVE-2025-15269.patch"; + url = "https://github.com/fontforge/fontforge/commit/6aea6db5da332d8ac94e3501bb83c1b21f52074d.patch"; + hash = "sha256-3KsWSXVRpPJbytVmzjExCGw6IaCgcrKwqQGRKpQAOiY="; + }) ]; # use $SOURCE_DATE_EPOCH instead of non-deterministic timestamps diff --git a/pkgs/by-name/ha/harfbuzz/package.nix b/pkgs/by-name/ha/harfbuzz/package.nix index d172d5922dd92..fa40f88b14fed 100644 --- a/pkgs/by-name/ha/harfbuzz/package.nix +++ b/pkgs/by-name/ha/harfbuzz/package.nix @@ -2,6 +2,7 @@ lib, stdenv, fetchurl, + fetchpatch, pkg-config, glib, freetype, @@ -41,6 +42,15 @@ stdenv.mkDerivation (finalAttrs: { hash = "sha256-5cgbf24LEC37AAz6QkU4uOiWq3ii9Lil7IyuYqtDNp4="; }; + patches = [ + (fetchpatch { + # https://github.com/harfbuzz/harfbuzz/security/advisories/GHSA-xvjr-f2r9-c7ww + name = "CVE-2026-22693.patch"; + url = "https://github.com/harfbuzz/harfbuzz/commit/1265ff8d990284f04d8768f35b0e20ae5f60daae.patch"; + hash = "sha256-mdgIhp1ndPSfzplBRB7s+BN2T5Z9dEYZ0bAmSDCUPSE="; + }) + ]; + postPatch = '' patchShebangs src/*.py test '' diff --git a/pkgs/by-name/in/inetutils/package.nix b/pkgs/by-name/in/inetutils/package.nix index 05cdabb47d26c..ebbcb629359a3 100644 --- a/pkgs/by-name/in/inetutils/package.nix +++ b/pkgs/by-name/in/inetutils/package.nix @@ -2,6 +2,7 @@ stdenv, lib, fetchurl, + fetchpatch, ncurses, perl, help2man, @@ -12,11 +13,11 @@ stdenv.mkDerivation rec { pname = "inetutils"; - version = "2.6"; + version = "2.7"; src = fetchurl { - url = "mirror://gnu/${pname}/${pname}-${version}.tar.xz"; - hash = "sha256-aL7b/q9z99hr4qfZm8+9QJPYKfUncIk5Ga4XTAsjV8o="; + url = "mirror://gnu/${pname}/${pname}-${version}.tar.gz"; + hash = "sha256-oVa+HN48XA/+/CYhgNk2mmBIQIeQeqVUxieH0vQOwIY="; }; outputs = [ @@ -29,6 +30,17 @@ stdenv.mkDerivation rec { ./inetutils-1_9-PATH_PROCNET_DEV.patch ./tests-libls.sh.patch + + (fetchpatch { + name = "CVE-2026-24061_1.patch"; + url = "https://codeberg.org/inetutils/inetutils/commit/fd702c02497b2f398e739e3119bed0b23dd7aa7b.patch"; + hash = "sha256-d/FdQyLD0gYr+erFqKDr8Okf04DFXknFaN03ls2aonQ="; + }) + (fetchpatch { + name = "CVE-2026-24061_2.patch"; + url = "https://codeberg.org/inetutils/inetutils/commit/ccba9f748aa8d50a38d7748e2e60362edd6a32cc.patch"; + hash = "sha256-ws+ed5vb7kVMHEbqK7yj6FUT355pTv2RZEYuXs5M7Io="; + }) ]; strictDeps = true; diff --git a/pkgs/by-name/kn/knot-resolver_6/package.nix b/pkgs/by-name/kn/knot-resolver_6/package.nix index d767d3b7223ab..14021cc706795 100644 --- a/pkgs/by-name/kn/knot-resolver_6/package.nix +++ b/pkgs/by-name/kn/knot-resolver_6/package.nix @@ -15,13 +15,15 @@ libuv, gnutls, lmdb, + # optionals, in principle jemalloc, systemdMinimal, libcap_ng, dns-root-data, - nghttp2, # optionals, in principle + nghttp2, + ngtcp2-gnutls, fstrm, - protobufc, # more optionals + protobufc, # test-only deps. cmocka, which, @@ -34,11 +36,11 @@ let # TODO: we could cut the `let` short here, but it would de-indent everything. unwrapped = stdenv.mkDerivation (finalAttrs: { pname = "knot-resolver_6"; - version = "6.1.0"; + version = "6.2.0"; src = fetchurl { url = "https://secure.nic.cz/files/knot-resolver/knot-resolver-${finalAttrs.version}.tar.xz"; - hash = "sha256-eSHfdQcobZBXS79a5mSopTeAXOQLX6ixX10NM+LEONA="; + hash = "sha256-tEYzvIQxgMC8fHfPexX+VxJDrpkrTdt0r97kz6gDcBs="; }; outputs = [ @@ -92,6 +94,7 @@ let ++ [ jemalloc nghttp2 + ngtcp2-gnutls # dnstap support fstrm protobufc diff --git a/pkgs/by-name/li/libcamera/package.nix b/pkgs/by-name/li/libcamera/package.nix index 5633a74ec866c..97d3893925eb2 100644 --- a/pkgs/by-name/li/libcamera/package.nix +++ b/pkgs/by-name/li/libcamera/package.nix @@ -27,12 +27,12 @@ stdenv.mkDerivation rec { pname = "libcamera"; - version = "0.5.2"; + version = "0.6.0"; src = fetchgit { url = "https://git.libcamera.org/libcamera/libcamera.git"; rev = "v${version}"; - hash = "sha256-nr1LmnedZMGBWLf2i5uw4E/OMeXObEKgjuO+PUx/GDY="; + hash = "sha256-zGcbzL1Q2hUaj/s9NjBlp7hVjmSFb0GF8CnCoDS82Tw="; }; outputs = [ @@ -112,6 +112,7 @@ stdenv.mkDerivation rec { "-Dv4l2=true" (lib.mesonEnable "tracing" withTracing) (lib.mesonEnable "qcam" withQcam) + "-Dlibunwind=disabled" "-Dlc-compliance=disabled" # tries unconditionally to download gtest when enabled # Avoid blanket -Werror to evade build failures on less # tested compilers. diff --git a/pkgs/by-name/li/libcdio/package.nix b/pkgs/by-name/li/libcdio/package.nix index 483868b4ea99c..216b6c889f15c 100644 --- a/pkgs/by-name/li/libcdio/package.nix +++ b/pkgs/by-name/li/libcdio/package.nix @@ -16,13 +16,13 @@ stdenv.mkDerivation (finalAttrs: { pname = "libcdio"; - version = "2.2.0"; + version = "2.3.0"; src = fetchFromGitHub { owner = "libcdio"; repo = "libcdio"; tag = finalAttrs.version; - hash = "sha256-izjZk2kz9PkLm9+INUdl1e7jMz3nUsQKdplKI9Io+CM="; + hash = "sha256-NZj6sMIhBORh2ZBs/WGI4BYri1REog4ovUug1t5p8Y8="; }; env = lib.optionalAttrs stdenv.hostPlatform.is32bit { diff --git a/pkgs/by-name/li/libheif/package.nix b/pkgs/by-name/li/libheif/package.nix index c69af0dda7cd9..f3c4804c1bbd7 100644 --- a/pkgs/by-name/li/libheif/package.nix +++ b/pkgs/by-name/li/libheif/package.nix @@ -2,6 +2,7 @@ lib, stdenv, fetchFromGitHub, + fetchpatch, cmake, pkg-config, dav1d, @@ -22,7 +23,7 @@ vips, }: -stdenv.mkDerivation rec { +stdenv.mkDerivation (finalAttrs: { pname = "libheif"; version = "1.20.2"; @@ -37,10 +38,19 @@ stdenv.mkDerivation rec { src = fetchFromGitHub { owner = "strukturag"; repo = "libheif"; - rev = "v${version}"; + rev = "v${finalAttrs.version}"; hash = "sha256-PVfdX3/Oe3DXpYU5WMnCSi2p9X4fPszq2X3uuyh8RVU="; }; + patches = [ + # CVE-2025-68431 (https://github.com/strukturag/libheif/security/advisories/GHSA-j87x-4gmq-cqfq) + (fetchpatch { + name = "001-fix-wrong-copy-width-in-overlay-images.patch"; + url = "https://github.com/strukturag/libheif/commit/b8c12a7b70f46c9516711a988483bed377b78d46.patch"; + hash = "sha256-PzGfcbdWAPdfExbSrPQwpk4v++TcNCXOhtwhgLGM13c="; + }) + ]; + nativeBuildInputs = [ pkg-config cmake @@ -90,4 +100,4 @@ stdenv.mkDerivation rec { platforms = lib.platforms.unix; maintainers = with lib.maintainers; [ kuflierl ]; }; -} +}) diff --git a/pkgs/by-name/li/libpcap/package.nix b/pkgs/by-name/li/libpcap/package.nix index 57304a6e3951b..55573a77c7e2c 100644 --- a/pkgs/by-name/li/libpcap/package.nix +++ b/pkgs/by-name/li/libpcap/package.nix @@ -26,13 +26,13 @@ stdenv.mkDerivation rec { pname = "libpcap"; - version = "1.10.5"; + version = "1.10.6"; __structuredAttrs = true; src = fetchurl { url = "https://www.tcpdump.org/release/${pname}-${version}.tar.gz"; - hash = "sha256-N87ZChmjAqfzLkWCJKAMNlwReQXCzTWsVEtogKgUiPA="; + hash = "sha256-hy3REzf+GrAq2dT+4EfJ2iRNaVxt3zTi67cz79Ttiqk="; }; outputs = [ diff --git a/pkgs/by-name/li/libsodium/package.nix b/pkgs/by-name/li/libsodium/package.nix index 734c84088d338..383ee5a05a084 100644 --- a/pkgs/by-name/li/libsodium/package.nix +++ b/pkgs/by-name/li/libsodium/package.nix @@ -1,18 +1,21 @@ { lib, stdenv, - fetchurl, autoreconfHook, + fetchFromGitHub, testers, + unstableGitUpdater, }: stdenv.mkDerivation (finalAttrs: { pname = "libsodium"; - version = "1.0.20"; + version = "1.0.20-unstable-2025-12-31"; - src = fetchurl { - url = "https://download.libsodium.org/libsodium/releases/libsodium-${finalAttrs.version}.tar.gz"; - hash = "sha256-67Ze9spDkzPCu0GgwZkFhyiNoH9sf9B8s6GMwY0wzhk="; + src = fetchFromGitHub { + owner = "jedisct1"; + repo = "libsodium"; + rev = "d44593f1a5b7d31b5122689605b489577356f7e8"; + hash = "sha256-6DC9JJLUwAoZHrYdCIiqOtAlWOEczec2r2FmaBbsa/Q="; }; outputs = [ @@ -37,13 +40,22 @@ stdenv.mkDerivation (finalAttrs: { doCheck = true; - passthru.tests.pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage; + passthru = { + tests.pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage; + updateScript = unstableGitUpdater { + branch = "stable"; + tagConverter = "cut -d - -f 1"; + }; + }; meta = { description = "Modern and easy-to-use crypto library"; homepage = "https://doc.libsodium.org/"; license = lib.licenses.isc; - maintainers = with lib.maintainers; [ raskin ]; + maintainers = with lib.maintainers; [ + mdaniels5757 + raskin + ]; pkgConfigModules = [ "libsodium" ]; platforms = lib.platforms.all; }; diff --git a/pkgs/by-name/li/libtasn1/package.nix b/pkgs/by-name/li/libtasn1/package.nix index c8e506d2bd532..7de5f9bc2b742 100644 --- a/pkgs/by-name/li/libtasn1/package.nix +++ b/pkgs/by-name/li/libtasn1/package.nix @@ -11,13 +11,13 @@ qemu, }: -stdenv.mkDerivation rec { +stdenv.mkDerivation (finalAttrs: { pname = "libtasn1"; - version = "4.20.0"; + version = "4.21.0"; src = fetchurl { - url = "mirror://gnu/libtasn1/libtasn1-${version}.tar.gz"; - sha256 = "sha256-kuDjvUwC1K7udgNrLd2D8McyukzaXLcdWDJysjWHp2w="; + url = "mirror://gnu/libtasn1/libtasn1-${finalAttrs.version}.tar.gz"; + hash = "sha256-HYpESiI8xUZCQHdzRuEl3lHY5qvwuLrHQqyEYJFn3Ic="; }; outputs = [ @@ -50,6 +50,6 @@ stdenv.mkDerivation rec { ''; license = lib.licenses.lgpl2Plus; platforms = lib.platforms.all; - changelog = "https://gitlab.com/gnutls/libtasn1/-/blob/v${version}/NEWS"; + changelog = "https://gitlab.com/gnutls/libtasn1/-/blob/v${finalAttrs.version}/NEWS.md"; }; -} +}) diff --git a/pkgs/by-name/pu/publicsuffix-list/package.nix b/pkgs/by-name/pu/publicsuffix-list/package.nix index 09df1b471010c..4d53a001b9972 100644 --- a/pkgs/by-name/pu/publicsuffix-list/package.nix +++ b/pkgs/by-name/pu/publicsuffix-list/package.nix @@ -7,13 +7,13 @@ stdenvNoCC.mkDerivation { pname = "publicsuffix-list"; - version = "0-unstable-2025-10-08"; + version = "0-unstable-2025-12-28"; src = fetchFromGitHub { owner = "publicsuffix"; repo = "list"; - rev = "ee7dec4a99602baaf51879dd8469b6642881a494"; - hash = "sha256-IlR3dICad9EZeizI3V0A1YCQZiV/xg2GxtmTLG4EASU="; + rev = "1ef6d3bc102c85d12e92be54ec0dad8ee990dd5f"; + hash = "sha256-rQdum6XLgfXwzpKTneakFmC80tOmlPFrZ8C7dfEnlSo="; }; dontBuild = true; diff --git a/pkgs/by-name/sd/sdl3/package.nix b/pkgs/by-name/sd/sdl3/package.nix index 48ef594995ad4..67416de996919 100644 --- a/pkgs/by-name/sd/sdl3/package.nix +++ b/pkgs/by-name/sd/sdl3/package.nix @@ -88,6 +88,20 @@ stdenv.mkDerivation (finalAttrs: { --replace-fail '"zenity"' '"${lib.getExe zenity}"' substituteInPlace src/dialog/unix/SDL_zenitydialog.c \ --replace-fail '"zenity"' '"${lib.getExe zenity}"' + '' + # https://github.com/libsdl-org/SDL/issues/14805 + + '' + substituteInPlace src/video/x11/SDL_x11vulkan.c \ + src/video/wayland/SDL_waylandvulkan.c \ + src/video/offscreen/SDL_offscreenvulkan.c \ + src/video/kmsdrm/SDL_kmsdrmvulkan.c \ + src/video/vivante/SDL_vivantevulkan.c \ + src/video/android/SDL_androidvulkan.c \ + --replace-fail 'libvulkan.so' '${lib.getLib vulkan-loader}/lib/libvulkan.so' + '' + + lib.optionalString x11Support '' + substituteInPlace src/video/x11/SDL_x11vulkan.c \ + --replace-fail 'libX11-xcb.so' '${lib.getLib xorg.libX11}/lib/libX11-xcb.so' ''; strictDeps = true; diff --git a/pkgs/by-name/tz/tzdata/package.nix b/pkgs/by-name/tz/tzdata/package.nix index 773419da35cbb..146b46dc230d2 100644 --- a/pkgs/by-name/tz/tzdata/package.nix +++ b/pkgs/by-name/tz/tzdata/package.nix @@ -8,16 +8,16 @@ stdenv.mkDerivation (finalAttrs: { pname = "tzdata"; - version = "2025b"; + version = "2025c"; srcs = [ (fetchurl { url = "https://data.iana.org/time-zones/releases/tzdata${finalAttrs.version}.tar.gz"; - hash = "sha256-EYEEEzRfx4BQF+J+qfpIhf10zWGykRcRrQOPXSjXFHQ="; + hash = "sha256-SqeeTv/uU/xAKf/l9uvpeTcoLrzfOG1dLakc6EFC+Vc="; }) (fetchurl { url = "https://data.iana.org/time-zones/releases/tzcode${finalAttrs.version}.tar.gz"; - hash = "sha256-Bfj+2zUl7nDUnIfT+ueKig265P6HqlZcZc2plIrhNew="; + hash = "sha256-aX6+ZiVESu9QgPWOSdA0JLu1Lgi/SD0921rPEMvRV0A="; }) ]; diff --git a/pkgs/by-name/xd/xdg-user-dirs-gtk/package.nix b/pkgs/by-name/xd/xdg-user-dirs-gtk/package.nix index 383b36d71b905..b13967cb78883 100644 --- a/pkgs/by-name/xd/xdg-user-dirs-gtk/package.nix +++ b/pkgs/by-name/xd/xdg-user-dirs-gtk/package.nix @@ -13,11 +13,11 @@ stdenv.mkDerivation (finalAttrs: { pname = "xdg-user-dirs-gtk"; - version = "0.14"; + version = "0.16"; src = fetchurl { url = "mirror://gnome/sources/xdg-user-dirs-gtk/${lib.versions.majorMinor finalAttrs.version}/xdg-user-dirs-gtk-${finalAttrs.version}.tar.xz"; - hash = "sha256-U3++FCskc27XiU5KAfaf11jLbHpnejgoeVKdIX9KKHM="; + hash = "sha256-voJd5/iRddterQzTdE0nYRCccJQyUahwkiWRYwJVIKk="; }; nativeBuildInputs = [ diff --git a/pkgs/by-name/xd/xdg-user-dirs/gettext-0.25.patch b/pkgs/by-name/xd/xdg-user-dirs/gettext-0.25.patch deleted file mode 100644 index f292da934d593..0000000000000 --- a/pkgs/by-name/xd/xdg-user-dirs/gettext-0.25.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/configure.ac b/configure.ac -index dc389db..45a35df 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -2,6 +2,7 @@ AC_INIT(xdg-user-dirs-update.c) - - AM_INIT_AUTOMAKE(xdg-user-dirs, 0.18) - AM_CONFIG_HEADER(config.h) -+AC_CONFIG_MACRO_DIRS([m4]) - - AM_SANITY_CHECK - AM_MAINTAINER_MODE -@@ -20,6 +21,7 @@ GETTEXT_PACKAGE=xdg-user-dirs - AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [The gettext domain name]) - AC_SUBST(GETTEXT_PACKAGE) - -+AM_GNU_GETTEXT_REQUIRE_VERSION([0.14.4]) - AM_GNU_GETTEXT([external]) - - dnl ========================================================================== diff --git a/pkgs/by-name/xd/xdg-user-dirs/package.nix b/pkgs/by-name/xd/xdg-user-dirs/package.nix index 1a77506ccf0a4..c0423a044442b 100644 --- a/pkgs/by-name/xd/xdg-user-dirs/package.nix +++ b/pkgs/by-name/xd/xdg-user-dirs/package.nix @@ -1,42 +1,41 @@ { lib, stdenv, - autoreconfHook, + meson, + ninja, fetchurl, libxslt, docbook_xsl, docbook_xml_dtd_43, gettext, - makeWrapper, + makeBinaryWrapper, + libiconv, + libintl, }: stdenv.mkDerivation (finalAttrs: { pname = "xdg-user-dirs"; - version = "0.18"; + version = "0.19"; src = fetchurl { - url = "https://user-dirs.freedesktop.org/releases/xdg-user-dirs-${finalAttrs.version}.tar.gz"; - hash = "sha256-7G8G10lc26N6cyA5+bXhV4vLKWV2/eDaQO2y9SIg3zw="; + url = "https://user-dirs.freedesktop.org/releases/xdg-user-dirs-${finalAttrs.version}.tar.xz"; + hash = "sha256-6S3rkpwQ1LKTKTl6+KJYUQEkf35hd6xvHSjoITDtjBk="; }; - patches = [ - # https://gitlab.freedesktop.org/xdg/xdg-user-dirs/-/merge_requests/16 - ./gettext-0.25.patch - ]; - - postPatch = '' - substituteInPlace Makefile.am \ - --replace-fail 'libraries = $(LIBINTL)' 'libraries = $(LIBICONV) $(LIBINTL)' - ''; - nativeBuildInputs = [ - autoreconfHook - makeWrapper + meson + ninja + makeBinaryWrapper libxslt docbook_xsl docbook_xml_dtd_43 - ] - ++ lib.optionals stdenv.hostPlatform.isDarwin [ gettext ]; + gettext + ]; + + buildInputs = [ + libiconv + libintl + ]; NIX_LDFLAGS = if stdenv.isDarwin then "-liconv" else null; @@ -44,6 +43,9 @@ stdenv.mkDerivation (finalAttrs: { # fallback values need to be last wrapProgram "$out/bin/xdg-user-dirs-update" \ --suffix XDG_CONFIG_DIRS : "$out/etc/xdg" + + substituteInPlace "$out/lib/systemd/user/xdg-user-dirs.service" \ + --replace-fail "/usr/bin/xdg-user-dirs-update" "$out/bin/xdg-user-dirs-update" ''; meta = { diff --git a/pkgs/development/compilers/go/1.25.nix b/pkgs/development/compilers/go/1.25.nix index acc8569de85c7..7e76017801425 100644 --- a/pkgs/development/compilers/go/1.25.nix +++ b/pkgs/development/compilers/go/1.25.nix @@ -25,11 +25,11 @@ let in stdenv.mkDerivation (finalAttrs: { pname = "go"; - version = "1.25.5"; + version = "1.25.6"; src = fetchurl { url = "https://go.dev/dl/go${finalAttrs.version}.src.tar.gz"; - hash = "sha256-IqX9CpHvzSihsFNxBrmVmygEth9Zw3WLUejlQpwalU8="; + hash = "sha256-WMv3ceRNdt5vVtGeM7d9dFoeSJNAkih15GWFuXXCsFk="; }; strictDeps = true; diff --git a/pkgs/development/compilers/llvm/common/llvm/default.nix b/pkgs/development/compilers/llvm/common/llvm/default.nix index 1e22dbe246037..79bc113d414fd 100644 --- a/pkgs/development/compilers/llvm/common/llvm/default.nix +++ b/pkgs/development/compilers/llvm/common/llvm/default.nix @@ -299,17 +299,10 @@ stdenv.mkDerivation ( # This was fixed upstream in LLVM 21 with # 88f041f3e05e26617856cc096d2e2864dfaa1c7b, but it’s too # painful to backport all the way. - lib.optionalString - ( - lib.versionOlder release_version "21" - || - # Rebuild avoidance; TODO: clean up on `staging`. - stdenv.hostPlatform.isx86 - ) - '' - substituteInPlace unittests/TargetParser/Host.cpp \ - --replace-fail "getMacOSHostVersion" "DISABLED_getMacOSHostVersion" - '' + lib.optionalString (lib.versionOlder release_version "21") '' + substituteInPlace unittests/TargetParser/Host.cpp \ + --replace-fail "getMacOSHostVersion" "DISABLED_getMacOSHostVersion" + '' + # This test fails with a `dysmutil` crash; have not yet dug into what's # going on here (TODO(@rrbutani)). diff --git a/pkgs/development/compilers/openjdk/21/source.json b/pkgs/development/compilers/openjdk/21/source.json index a43dec0d90665..5edd47f2b5762 100644 --- a/pkgs/development/compilers/openjdk/21/source.json +++ b/pkgs/development/compilers/openjdk/21/source.json @@ -1,6 +1,6 @@ { - "hash": "sha256-VYTitbgKgd9co9LXVnneaaKjMwAXFmscYp9McJV01Jg=", + "hash": "sha256-xaaYt63uvoB5zfwUxHjNR3HiBPzFTNc4xyQfuTC/7VU=", "owner": "openjdk", "repo": "jdk21u", - "rev": "refs/tags/jdk-21.0.9+10" + "rev": "refs/tags/jdk-21.0.10+7" } diff --git a/pkgs/development/interpreters/tcl/generic.nix b/pkgs/development/interpreters/tcl/generic.nix index 7ecc8250b9c24..9f2e781124e54 100644 --- a/pkgs/development/interpreters/tcl/generic.nix +++ b/pkgs/development/interpreters/tcl/generic.nix @@ -90,6 +90,18 @@ let ] ++ lib.optional stdenv.hostPlatform.is64bit "--enable-64bit"; + buildFlags = lib.optionals stdenv.hostPlatform.isStatic [ + # Don't use the default Make target for static, + # since it builds shared libraries for bundled packages. + "binaries" + "libraries" + "doc" + ]; + + makeFlags = lib.optionals stdenv.hostPlatform.isStatic [ + "INSTALL_PACKAGE_TARGETS=" + ]; + enableParallelBuilding = true; postInstall = diff --git a/pkgs/development/libraries/glibc/2.40-master.patch b/pkgs/development/libraries/glibc/2.40-master.patch index 3384de16dc3b4..664c2935d536f 100644 --- a/pkgs/development/libraries/glibc/2.40-master.patch +++ b/pkgs/development/libraries/glibc/2.40-master.patch @@ -43308,3 +43308,13890 @@ index 0000000000..3c091d8c44 +} + +#include + +commit 95c9b7351cc9e5dc8dafffd8ad9ffb968782410a +Author: Wilco Dijkstra +Date: Fri Jun 27 14:10:55 2025 +0000 + + AArch64: Avoid memset ifunc in cpu-features.c [BZ #33112] + + During early startup memcpy or memset must not be called since many targets + use ifuncs for them which won't be initialized yet. Security hardening may + use -ftrivial-auto-var-init=zero which inserts calls to memset. Redirect + memset to memset_generic by including dl-symbol-redir-ifunc.h in cpu-features.c. + This fixes BZ #33112. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 681a24ae4d0cb8ed92de98b4da660308840b09ba) + +diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c +index c0b047bc0d..0ad55a0c7f 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c ++++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #define DCZID_DZP_MASK (1 << 4) + #define DCZID_BS_MASK (0xf) + +commit 2f0c3a5287a79d363c4bc8b028c6136b0f8b14cb +Author: Pierre Blanchard +Date: Tue Mar 18 17:07:31 2025 +0000 + + AArch64: Optimize algorithm in users of SVE expf helper + + Polynomial order was unnecessarily high, unlocking multiple + optimizations. + Max error for new SVE expf is 0.88 +0.5ULP. + Max error for new SVE coshf is 2.56 +0.5ULP. + Performance improvement on Neoverse V1: expf (30%), coshf (26%). + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit cf56eb28fa277d9dbb301654682ca89f71c30a48) + +diff --git a/sysdeps/aarch64/fpu/coshf_sve.c b/sysdeps/aarch64/fpu/coshf_sve.c +index 7ad6efa0fc..508c0790ee 100644 +--- a/sysdeps/aarch64/fpu/coshf_sve.c ++++ b/sysdeps/aarch64/fpu/coshf_sve.c +@@ -39,9 +39,9 @@ special_case (svfloat32_t x, svfloat32_t half_e, svfloat32_t half_over_e, + } + + /* Single-precision vector cosh, using vector expf. +- Maximum error is 2.77 ULP: +- _ZGVsMxv_coshf(-0x1.5b38f4p+1) got 0x1.e45946p+2 +- want 0x1.e4594cp+2. */ ++ Maximum error is 2.56 +0.5 ULP: ++ _ZGVsMxv_coshf(-0x1.5b40f4p+1) got 0x1.e47748p+2 ++ want 0x1.e4774ep+2. */ + svfloat32_t SV_NAME_F1 (cosh) (svfloat32_t x, svbool_t pg) + { + const struct data *d = ptr_barrier (&data); +diff --git a/sysdeps/aarch64/fpu/expf_sve.c b/sysdeps/aarch64/fpu/expf_sve.c +index da93e01b87..aee86a2033 100644 +--- a/sysdeps/aarch64/fpu/expf_sve.c ++++ b/sysdeps/aarch64/fpu/expf_sve.c +@@ -40,9 +40,9 @@ special_case (svfloat32_t x, svbool_t special, const struct sv_expf_data *d) + } + + /* Optimised single-precision SVE exp function. +- Worst-case error is 1.04 ulp: +- SV_NAME_F1 (exp)(0x1.a8eda4p+1) got 0x1.ba74bcp+4 +- want 0x1.ba74bap+4. */ ++ Worst-case error is 0.88 +0.50 ULP: ++ _ZGVsMxv_expf(-0x1.bba276p-6) got 0x1.f25288p-1 ++ want 0x1.f2528ap-1. */ + svfloat32_t SV_NAME_F1 (exp) (svfloat32_t x, const svbool_t pg) + { + const struct data *d = ptr_barrier (&data); +diff --git a/sysdeps/aarch64/fpu/sv_expf_inline.h b/sysdeps/aarch64/fpu/sv_expf_inline.h +index 75781fb4dd..01fbb4d4c0 100644 +--- a/sysdeps/aarch64/fpu/sv_expf_inline.h ++++ b/sysdeps/aarch64/fpu/sv_expf_inline.h +@@ -24,50 +24,40 @@ + + struct sv_expf_data + { +- float c1, c3, inv_ln2; +- float ln2_lo, c0, c2, c4; +- float ln2_hi, shift; ++ float ln2_hi, ln2_lo, c1, null; ++ float inv_ln2, shift; + }; + +-/* Coefficients copied from the polynomial in AdvSIMD variant, reversed for +- compatibility with polynomial helpers. Shift is 1.5*2^17 + 127. */ ++/* Shift is 1.5*2^17 + 127. */ + #define SV_EXPF_DATA \ + { \ +- /* Coefficients copied from the polynomial in AdvSIMD variant. */ \ +- .c0 = 0x1.ffffecp-1f, .c1 = 0x1.fffdb6p-2f, .c2 = 0x1.555e66p-3f, \ +- .c3 = 0x1.573e2ep-5f, .c4 = 0x1.0e4020p-7f, .inv_ln2 = 0x1.715476p+0f, \ +- .ln2_hi = 0x1.62e4p-1f, .ln2_lo = 0x1.7f7d1cp-20f, \ +- .shift = 0x1.803f8p17f, \ ++ .c1 = 0.5f, .inv_ln2 = 0x1.715476p+0f, .ln2_hi = 0x1.62e4p-1f, \ ++ .ln2_lo = 0x1.7f7d1cp-20f, .shift = 0x1.803f8p17f, \ + } + +-#define C(i) sv_f32 (d->poly[i]) +- + static inline svfloat32_t + expf_inline (svfloat32_t x, const svbool_t pg, const struct sv_expf_data *d) + { + /* exp(x) = 2^n (1 + poly(r)), with 1 + poly(r) in [1/sqrt(2),sqrt(2)] + x = ln2*n + r, with r in [-ln2/2, ln2/2]. */ + +- svfloat32_t lane_consts = svld1rq (svptrue_b32 (), &d->ln2_lo); ++ svfloat32_t lane_consts = svld1rq (svptrue_b32 (), &d->ln2_hi); + + /* n = round(x/(ln2/N)). */ + svfloat32_t z = svmad_x (pg, sv_f32 (d->inv_ln2), x, d->shift); + svfloat32_t n = svsub_x (pg, z, d->shift); + + /* r = x - n*ln2/N. */ +- svfloat32_t r = svmsb_x (pg, sv_f32 (d->ln2_hi), n, x); ++ svfloat32_t r = x; + r = svmls_lane (r, n, lane_consts, 0); ++ r = svmls_lane (r, n, lane_consts, 1); + + /* scale = 2^(n/N). */ + svfloat32_t scale = svexpa (svreinterpret_u32 (z)); + +- /* poly(r) = exp(r) - 1 ~= C0 r + C1 r^2 + C2 r^3 + C3 r^4 + C4 r^5. */ +- svfloat32_t p12 = svmla_lane (sv_f32 (d->c1), r, lane_consts, 2); +- svfloat32_t p34 = svmla_lane (sv_f32 (d->c3), r, lane_consts, 3); ++ /* poly(r) = exp(r) - 1 ~= r + 0.5 r^2. */ + svfloat32_t r2 = svmul_x (svptrue_b32 (), r, r); +- svfloat32_t p14 = svmla_x (pg, p12, p34, r2); +- svfloat32_t p0 = svmul_lane (r, lane_consts, 1); +- svfloat32_t poly = svmla_x (pg, p0, r2, p14); ++ svfloat32_t poly = svmla_lane (r, r2, lane_consts, 2); + + return svmla_x (pg, scale, scale, poly); + } + +commit bfd0490911b8abb3c43d90a169a5fe3ff8b66c5b +Author: Dylan Fleming +Date: Mon May 19 11:36:51 2025 +0000 + + AArch64: Optimize inverse trig functions + + Improve performance of Inverse trig functions by altering how coefficients are + loaded. + + Performance improvement on Neoverse V1: + SVE acos 14% + AdvSIMD acos 6% + + AdvSIMD asin 6% + SVE asin 5% + AdvSIMD asinf 2% + + AdvSIMD atanf 22% + SVE atanf 20% + SVE atan 11% + AdvSIMD atan 5% + + SVE atan2 7% + SVE atan2f 4% + AdvSIMD atan2f 3% + AdvSIMD atan2 2% + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit 1e84509e0041c0a83997aba602a585bb3b8285f0) + +diff --git a/sysdeps/aarch64/fpu/acos_advsimd.c b/sysdeps/aarch64/fpu/acos_advsimd.c +index 0a86c9823a..aaf874f4ea 100644 +--- a/sysdeps/aarch64/fpu/acos_advsimd.c ++++ b/sysdeps/aarch64/fpu/acos_advsimd.c +@@ -18,24 +18,23 @@ + . */ + + #include "v_math.h" +-#include "poly_advsimd_f64.h" + + static const struct data + { +- float64x2_t poly[12]; +- float64x2_t pi, pi_over_2; ++ double c1, c3, c5, c7, c9, c11; ++ float64x2_t c0, c2, c4, c6, c8, c10; + uint64x2_t abs_mask; ++ float64x2_t pi, pi_over_2; + } data = { + /* Polynomial approximation of (asin(sqrt(x)) - sqrt(x)) / (x * sqrt(x)) + on [ 0x1p-106, 0x1p-2 ], relative error: 0x1.c3d8e169p-57. */ +- .poly = { V2 (0x1.555555555554ep-3), V2 (0x1.3333333337233p-4), +- V2 (0x1.6db6db67f6d9fp-5), V2 (0x1.f1c71fbd29fbbp-6), +- V2 (0x1.6e8b264d467d6p-6), V2 (0x1.1c5997c357e9dp-6), +- V2 (0x1.c86a22cd9389dp-7), V2 (0x1.856073c22ebbep-7), +- V2 (0x1.fd1151acb6bedp-8), V2 (0x1.087182f799c1dp-6), +- V2 (-0x1.6602748120927p-7), V2 (0x1.cfa0dd1f9478p-6), }, +- .pi = V2 (0x1.921fb54442d18p+1), +- .pi_over_2 = V2 (0x1.921fb54442d18p+0), ++ .c0 = V2 (0x1.555555555554ep-3), .c1 = 0x1.3333333337233p-4, ++ .c2 = V2 (0x1.6db6db67f6d9fp-5), .c3 = 0x1.f1c71fbd29fbbp-6, ++ .c4 = V2 (0x1.6e8b264d467d6p-6), .c5 = 0x1.1c5997c357e9dp-6, ++ .c6 = V2 (0x1.c86a22cd9389dp-7), .c7 = 0x1.856073c22ebbep-7, ++ .c8 = V2 (0x1.fd1151acb6bedp-8), .c9 = 0x1.087182f799c1dp-6, ++ .c10 = V2 (-0x1.6602748120927p-7), .c11 = 0x1.cfa0dd1f9478p-6, ++ .pi = V2 (0x1.921fb54442d18p+1), .pi_over_2 = V2 (0x1.921fb54442d18p+0), + .abs_mask = V2 (0x7fffffffffffffff), + }; + +@@ -63,7 +62,7 @@ special_case (float64x2_t x, float64x2_t y, uint64x2_t special) + + acos(x) ~ pi/2 - (x + x^3 P(x^2)). + +- The largest observed error in this region is 1.18 ulps, ++ The largest observed error in this region is 1.18 ulp: + _ZGVnN2v_acos (0x1.fbab0a7c460f6p-2) got 0x1.0d54d1985c068p+0 + want 0x1.0d54d1985c069p+0. + +@@ -71,9 +70,9 @@ special_case (float64x2_t x, float64x2_t y, uint64x2_t special) + + acos(x) = y + y * z * P(z), with z = (1-x)/2 and y = sqrt(z). + +- The largest observed error in this region is 1.52 ulps, +- _ZGVnN2v_acos (0x1.23d362722f591p-1) got 0x1.edbbedf8a7d6ep-1 +- want 0x1.edbbedf8a7d6cp-1. */ ++ The largest observed error in this region is 1.50 ulp: ++ _ZGVnN2v_acos (0x1.252a2cf3fb9acp-1) got 0x1.ec1a46aa82901p-1 ++ want 0x1.ec1a46aa829p-1. */ + float64x2_t VPCS_ATTR V_NAME_D1 (acos) (float64x2_t x) + { + const struct data *d = ptr_barrier (&data); +@@ -99,13 +98,32 @@ float64x2_t VPCS_ATTR V_NAME_D1 (acos) (float64x2_t x) + float64x2_t z = vbslq_f64 (a_le_half, ax, vsqrtq_f64 (z2)); + + /* Use a single polynomial approximation P for both intervals. */ ++ float64x2_t z3 = vmulq_f64 (z2, z); + float64x2_t z4 = vmulq_f64 (z2, z2); + float64x2_t z8 = vmulq_f64 (z4, z4); +- float64x2_t z16 = vmulq_f64 (z8, z8); +- float64x2_t p = v_estrin_11_f64 (z2, z4, z8, z16, d->poly); + +- /* Finalize polynomial: z + z * z2 * P(z2). */ +- p = vfmaq_f64 (z, vmulq_f64 (z, z2), p); ++ /* Order-11 Estrin. */ ++ float64x2_t c13 = vld1q_f64 (&d->c1); ++ float64x2_t c57 = vld1q_f64 (&d->c5); ++ float64x2_t c911 = vld1q_f64 (&d->c9); ++ ++ float64x2_t p01 = vfmaq_laneq_f64 (d->c0, z2, c13, 0); ++ float64x2_t p23 = vfmaq_laneq_f64 (d->c2, z2, c13, 1); ++ float64x2_t p03 = vfmaq_f64 (p01, z4, p23); ++ ++ float64x2_t p45 = vfmaq_laneq_f64 (d->c4, z2, c57, 0); ++ float64x2_t p67 = vfmaq_laneq_f64 (d->c6, z2, c57, 1); ++ float64x2_t p47 = vfmaq_f64 (p45, z4, p67); ++ ++ float64x2_t p89 = vfmaq_laneq_f64 (d->c8, z2, c911, 0); ++ float64x2_t p1011 = vfmaq_laneq_f64 (d->c10, z2, c911, 1); ++ float64x2_t p811 = vfmaq_f64 (p89, z4, p1011); ++ ++ float64x2_t p411 = vfmaq_f64 (p47, z8, p811); ++ float64x2_t p = vfmaq_f64 (p03, z8, p411); ++ ++ /* Finalize polynomial: z + z3 * P(z2). */ ++ p = vfmaq_f64 (z, z3, p); + + /* acos(|x|) = pi/2 - sign(x) * Q(|x|), for |x| < 0.5 + = 2 Q(|x|) , for 0.5 < x < 1.0 +diff --git a/sysdeps/aarch64/fpu/acos_sve.c b/sysdeps/aarch64/fpu/acos_sve.c +index 99dbfac3e6..870d8062cf 100644 +--- a/sysdeps/aarch64/fpu/acos_sve.c ++++ b/sysdeps/aarch64/fpu/acos_sve.c +@@ -18,20 +18,21 @@ + . */ + + #include "sv_math.h" +-#include "poly_sve_f64.h" + + static const struct data + { +- float64_t poly[12]; +- float64_t pi, pi_over_2; ++ float64_t c1, c3, c5, c7, c9, c11; ++ float64_t c0, c2, c4, c6, c8, c10; ++ float64_t pi_over_2; + } data = { + /* Polynomial approximation of (asin(sqrt(x)) - sqrt(x)) / (x * sqrt(x)) + on [ 0x1p-106, 0x1p-2 ], relative error: 0x1.c3d8e169p-57. */ +- .poly = { 0x1.555555555554ep-3, 0x1.3333333337233p-4, 0x1.6db6db67f6d9fp-5, +- 0x1.f1c71fbd29fbbp-6, 0x1.6e8b264d467d6p-6, 0x1.1c5997c357e9dp-6, +- 0x1.c86a22cd9389dp-7, 0x1.856073c22ebbep-7, 0x1.fd1151acb6bedp-8, +- 0x1.087182f799c1dp-6, -0x1.6602748120927p-7, 0x1.cfa0dd1f9478p-6, }, +- .pi = 0x1.921fb54442d18p+1, ++ .c0 = 0x1.555555555554ep-3, .c1 = 0x1.3333333337233p-4, ++ .c2 = 0x1.6db6db67f6d9fp-5, .c3 = 0x1.f1c71fbd29fbbp-6, ++ .c4 = 0x1.6e8b264d467d6p-6, .c5 = 0x1.1c5997c357e9dp-6, ++ .c6 = 0x1.c86a22cd9389dp-7, .c7 = 0x1.856073c22ebbep-7, ++ .c8 = 0x1.fd1151acb6bedp-8, .c9 = 0x1.087182f799c1dp-6, ++ .c10 = -0x1.6602748120927p-7, .c11 = 0x1.cfa0dd1f9478p-6, + .pi_over_2 = 0x1.921fb54442d18p+0, + }; + +@@ -42,20 +43,21 @@ static const struct data + + acos(x) ~ pi/2 - (x + x^3 P(x^2)). + +- The largest observed error in this region is 1.18 ulps, +- _ZGVsMxv_acos (0x1.fbc5fe28ee9e3p-2) got 0x1.0d4d0f55667f6p+0 +- want 0x1.0d4d0f55667f7p+0. ++ The largest observed error in this region is 1.18 ulp: ++ _ZGVsMxv_acos (0x1.fbb7c9079b429p-2) got 0x1.0d51266607582p+0 ++ want 0x1.0d51266607583p+0. + + For |x| in [0.5, 1.0], use same approximation with a change of variable + + acos(x) = y + y * z * P(z), with z = (1-x)/2 and y = sqrt(z). + +- The largest observed error in this region is 1.52 ulps, +- _ZGVsMxv_acos (0x1.24024271a500ap-1) got 0x1.ed82df4243f0dp-1 +- want 0x1.ed82df4243f0bp-1. */ ++ The largest observed error in this region is 1.50 ulp: ++ _ZGVsMxv_acos (0x1.252a2cf3fb9acp-1) got 0x1.ec1a46aa82901p-1 ++ want 0x1.ec1a46aa829p-1. */ + svfloat64_t SV_NAME_D1 (acos) (svfloat64_t x, const svbool_t pg) + { + const struct data *d = ptr_barrier (&data); ++ svbool_t ptrue = svptrue_b64 (); + + svuint64_t sign = svand_x (pg, svreinterpret_u64 (x), 0x8000000000000000); + svfloat64_t ax = svabs_x (pg, x); +@@ -70,24 +72,41 @@ svfloat64_t SV_NAME_D1 (acos) (svfloat64_t x, const svbool_t pg) + svfloat64_t z = svsqrt_m (ax, a_gt_half, z2); + + /* Use a single polynomial approximation P for both intervals. */ +- svfloat64_t z4 = svmul_x (pg, z2, z2); +- svfloat64_t z8 = svmul_x (pg, z4, z4); +- svfloat64_t z16 = svmul_x (pg, z8, z8); +- svfloat64_t p = sv_estrin_11_f64_x (pg, z2, z4, z8, z16, d->poly); ++ svfloat64_t z3 = svmul_x (ptrue, z2, z); ++ svfloat64_t z4 = svmul_x (ptrue, z2, z2); ++ svfloat64_t z8 = svmul_x (ptrue, z4, z4); ++ ++ svfloat64_t c13 = svld1rq (ptrue, &d->c1); ++ svfloat64_t c57 = svld1rq (ptrue, &d->c5); ++ svfloat64_t c911 = svld1rq (ptrue, &d->c9); ++ ++ svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), z2, c13, 0); ++ svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), z2, c13, 1); ++ svfloat64_t p03 = svmla_x (pg, p01, z4, p23); ++ ++ svfloat64_t p45 = svmla_lane (sv_f64 (d->c4), z2, c57, 0); ++ svfloat64_t p67 = svmla_lane (sv_f64 (d->c6), z2, c57, 1); ++ svfloat64_t p47 = svmla_x (pg, p45, z4, p67); ++ ++ svfloat64_t p89 = svmla_lane (sv_f64 (d->c8), z2, c911, 0); ++ svfloat64_t p1011 = svmla_lane (sv_f64 (d->c10), z2, c911, 1); ++ svfloat64_t p811 = svmla_x (pg, p89, z4, p1011); ++ ++ svfloat64_t p411 = svmla_x (pg, p47, z8, p811); ++ svfloat64_t p = svmad_x (pg, p411, z8, p03); + + /* Finalize polynomial: z + z * z2 * P(z2). */ +- p = svmla_x (pg, z, svmul_x (pg, z, z2), p); ++ p = svmad_x (pg, p, z3, z); + + /* acos(|x|) = pi/2 - sign(x) * Q(|x|), for |x| < 0.5 + = 2 Q(|x|) , for 0.5 < x < 1.0 + = pi - 2 Q(|x|) , for -1.0 < x < -0.5. */ +- svfloat64_t y +- = svreinterpret_f64 (svorr_x (pg, svreinterpret_u64 (p), sign)); +- +- svbool_t is_neg = svcmplt (pg, x, 0.0); +- svfloat64_t off = svdup_f64_z (is_neg, d->pi); +- svfloat64_t mul = svsel (a_gt_half, sv_f64 (2.0), sv_f64 (-1.0)); +- svfloat64_t add = svsel (a_gt_half, off, sv_f64 (d->pi_over_2)); +- +- return svmla_x (pg, add, mul, y); ++ svfloat64_t mul = svreinterpret_f64 ( ++ svlsl_m (a_gt_half, svreinterpret_u64 (sv_f64 (1.0)), 10)); ++ mul = svreinterpret_f64 (sveor_x (ptrue, svreinterpret_u64 (mul), sign)); ++ svfloat64_t add = svreinterpret_f64 ( ++ svorr_x (ptrue, sign, svreinterpret_u64 (sv_f64 (d->pi_over_2)))); ++ add = svsub_m (a_gt_half, sv_f64 (d->pi_over_2), add); ++ ++ return svmsb_x (pg, p, mul, add); + } +diff --git a/sysdeps/aarch64/fpu/asin_advsimd.c b/sysdeps/aarch64/fpu/asin_advsimd.c +index 2de6eff407..f4884227a5 100644 +--- a/sysdeps/aarch64/fpu/asin_advsimd.c ++++ b/sysdeps/aarch64/fpu/asin_advsimd.c +@@ -18,24 +18,23 @@ + . */ + + #include "v_math.h" +-#include "poly_advsimd_f64.h" + + static const struct data + { +- float64x2_t poly[12]; ++ float64x2_t c0, c2, c4, c6, c8, c10; + float64x2_t pi_over_2; + uint64x2_t abs_mask; ++ double c1, c3, c5, c7, c9, c11; + } data = { + /* Polynomial approximation of (asin(sqrt(x)) - sqrt(x)) / (x * sqrt(x)) + on [ 0x1p-106, 0x1p-2 ], relative error: 0x1.c3d8e169p-57. */ +- .poly = { V2 (0x1.555555555554ep-3), V2 (0x1.3333333337233p-4), +- V2 (0x1.6db6db67f6d9fp-5), V2 (0x1.f1c71fbd29fbbp-6), +- V2 (0x1.6e8b264d467d6p-6), V2 (0x1.1c5997c357e9dp-6), +- V2 (0x1.c86a22cd9389dp-7), V2 (0x1.856073c22ebbep-7), +- V2 (0x1.fd1151acb6bedp-8), V2 (0x1.087182f799c1dp-6), +- V2 (-0x1.6602748120927p-7), V2 (0x1.cfa0dd1f9478p-6), }, +- .pi_over_2 = V2 (0x1.921fb54442d18p+0), +- .abs_mask = V2 (0x7fffffffffffffff), ++ .c0 = V2 (0x1.555555555554ep-3), .c1 = 0x1.3333333337233p-4, ++ .c2 = V2 (0x1.6db6db67f6d9fp-5), .c3 = 0x1.f1c71fbd29fbbp-6, ++ .c4 = V2 (0x1.6e8b264d467d6p-6), .c5 = 0x1.1c5997c357e9dp-6, ++ .c6 = V2 (0x1.c86a22cd9389dp-7), .c7 = 0x1.856073c22ebbep-7, ++ .c8 = V2 (0x1.fd1151acb6bedp-8), .c9 = 0x1.087182f799c1dp-6, ++ .c10 = V2 (-0x1.6602748120927p-7), .c11 = 0x1.cfa0dd1f9478p-6, ++ .pi_over_2 = V2 (0x1.921fb54442d18p+0), .abs_mask = V2 (0x7fffffffffffffff), + }; + + #define AllMask v_u64 (0xffffffffffffffff) +@@ -68,8 +67,8 @@ special_case (float64x2_t x, float64x2_t y, uint64x2_t special) + asin(x) = pi/2 - (y + y * z * P(z)), with z = (1-x)/2 and y = sqrt(z). + + The largest observed error in this region is 2.69 ulps, +- _ZGVnN2v_asin (0x1.044ac9819f573p-1) got 0x1.110d7e85fdd5p-1 +- want 0x1.110d7e85fdd53p-1. */ ++ _ZGVnN2v_asin (0x1.044e8cefee301p-1) got 0x1.1111dd54ddf96p-1 ++ want 0x1.1111dd54ddf99p-1. */ + float64x2_t VPCS_ATTR V_NAME_D1 (asin) (float64x2_t x) + { + const struct data *d = ptr_barrier (&data); +@@ -86,7 +85,7 @@ float64x2_t VPCS_ATTR V_NAME_D1 (asin) (float64x2_t x) + return special_case (x, x, AllMask); + #endif + +- uint64x2_t a_lt_half = vcltq_f64 (ax, v_f64 (0.5)); ++ uint64x2_t a_lt_half = vcaltq_f64 (x, v_f64 (0.5)); + + /* Evaluate polynomial Q(x) = y + y * z * P(z) with + z = x ^ 2 and y = |x| , if |x| < 0.5 +@@ -99,7 +98,26 @@ float64x2_t VPCS_ATTR V_NAME_D1 (asin) (float64x2_t x) + float64x2_t z4 = vmulq_f64 (z2, z2); + float64x2_t z8 = vmulq_f64 (z4, z4); + float64x2_t z16 = vmulq_f64 (z8, z8); +- float64x2_t p = v_estrin_11_f64 (z2, z4, z8, z16, d->poly); ++ ++ /* order-11 estrin. */ ++ float64x2_t c13 = vld1q_f64 (&d->c1); ++ float64x2_t c57 = vld1q_f64 (&d->c5); ++ float64x2_t c911 = vld1q_f64 (&d->c9); ++ ++ float64x2_t p01 = vfmaq_laneq_f64 (d->c0, z2, c13, 0); ++ float64x2_t p23 = vfmaq_laneq_f64 (d->c2, z2, c13, 1); ++ float64x2_t p03 = vfmaq_f64 (p01, z4, p23); ++ ++ float64x2_t p45 = vfmaq_laneq_f64 (d->c4, z2, c57, 0); ++ float64x2_t p67 = vfmaq_laneq_f64 (d->c6, z2, c57, 1); ++ float64x2_t p47 = vfmaq_f64 (p45, z4, p67); ++ ++ float64x2_t p89 = vfmaq_laneq_f64 (d->c8, z2, c911, 0); ++ float64x2_t p1011 = vfmaq_laneq_f64 (d->c10, z2, c911, 1); ++ float64x2_t p811 = vfmaq_f64 (p89, z4, p1011); ++ ++ float64x2_t p07 = vfmaq_f64 (p03, z8, p47); ++ float64x2_t p = vfmaq_f64 (p07, z16, p811); + + /* Finalize polynomial: z + z * z2 * P(z2). */ + p = vfmaq_f64 (z, vmulq_f64 (z, z2), p); +diff --git a/sysdeps/aarch64/fpu/asin_sve.c b/sysdeps/aarch64/fpu/asin_sve.c +index 9daa382e34..acbf1b3bdd 100644 +--- a/sysdeps/aarch64/fpu/asin_sve.c ++++ b/sysdeps/aarch64/fpu/asin_sve.c +@@ -18,45 +18,43 @@ + . */ + + #include "sv_math.h" +-#include "poly_sve_f64.h" + + static const struct data + { +- float64_t poly[12]; +- float64_t pi_over_2f; ++ float64_t c1, c3, c5, c7, c9, c11; ++ float64_t c0, c2, c4, c6, c8, c10; ++ float64_t pi_over_2; + } data = { + /* Polynomial approximation of (asin(sqrt(x)) - sqrt(x)) / (x * sqrt(x)) + on [ 0x1p-106, 0x1p-2 ], relative error: 0x1.c3d8e169p-57. */ +- .poly = { 0x1.555555555554ep-3, 0x1.3333333337233p-4, +- 0x1.6db6db67f6d9fp-5, 0x1.f1c71fbd29fbbp-6, +- 0x1.6e8b264d467d6p-6, 0x1.1c5997c357e9dp-6, +- 0x1.c86a22cd9389dp-7, 0x1.856073c22ebbep-7, +- 0x1.fd1151acb6bedp-8, 0x1.087182f799c1dp-6, +- -0x1.6602748120927p-7, 0x1.cfa0dd1f9478p-6, }, +- .pi_over_2f = 0x1.921fb54442d18p+0, ++ .c0 = 0x1.555555555554ep-3, .c1 = 0x1.3333333337233p-4, ++ .c2 = 0x1.6db6db67f6d9fp-5, .c3 = 0x1.f1c71fbd29fbbp-6, ++ .c4 = 0x1.6e8b264d467d6p-6, .c5 = 0x1.1c5997c357e9dp-6, ++ .c6 = 0x1.c86a22cd9389dp-7, .c7 = 0x1.856073c22ebbep-7, ++ .c8 = 0x1.fd1151acb6bedp-8, .c9 = 0x1.087182f799c1dp-6, ++ .c10 = -0x1.6602748120927p-7, .c11 = 0x1.cfa0dd1f9478p-6, ++ .pi_over_2 = 0x1.921fb54442d18p+0, + }; + +-#define P(i) sv_f64 (d->poly[i]) +- + /* Double-precision SVE implementation of vector asin(x). + + For |x| in [0, 0.5], use an order 11 polynomial P such that the final + approximation is an odd polynomial: asin(x) ~ x + x^3 P(x^2). + +- The largest observed error in this region is 0.52 ulps, +- _ZGVsMxv_asin(0x1.d95ae04998b6cp-2) got 0x1.ec13757305f27p-2 +- want 0x1.ec13757305f26p-2. +- +- For |x| in [0.5, 1.0], use same approximation with a change of variable ++ The largest observed error in this region is 0.98 ulp: ++ _ZGVsMxv_asin (0x1.d98f6a748ed8ap-2) got 0x1.ec4eb661a73d3p-2 ++ want 0x1.ec4eb661a73d2p-2. + +- asin(x) = pi/2 - (y + y * z * P(z)), with z = (1-x)/2 and y = sqrt(z). ++ For |x| in [0.5, 1.0], use same approximation with a change of variable: ++ asin(x) = pi/2 - (y + y * z * P(z)), with z = (1-x)/2 and y = sqrt(z). + +- The largest observed error in this region is 2.69 ulps, +- _ZGVsMxv_asin(0x1.044ac9819f573p-1) got 0x1.110d7e85fdd5p-1 +- want 0x1.110d7e85fdd53p-1. */ ++ The largest observed error in this region is 2.66 ulp: ++ _ZGVsMxv_asin (0x1.04024f6e2a2fbp-1) got 0x1.10b9586f087a8p-1 ++ want 0x1.10b9586f087abp-1. */ + svfloat64_t SV_NAME_D1 (asin) (svfloat64_t x, const svbool_t pg) + { + const struct data *d = ptr_barrier (&data); ++ svbool_t ptrue = svptrue_b64 (); + + svuint64_t sign = svand_x (pg, svreinterpret_u64 (x), 0x8000000000000000); + svfloat64_t ax = svabs_x (pg, x); +@@ -70,17 +68,37 @@ svfloat64_t SV_NAME_D1 (asin) (svfloat64_t x, const svbool_t pg) + svfloat64_t z = svsqrt_m (ax, a_ge_half, z2); + + /* Use a single polynomial approximation P for both intervals. */ ++ svfloat64_t z3 = svmul_x (pg, z2, z); + svfloat64_t z4 = svmul_x (pg, z2, z2); + svfloat64_t z8 = svmul_x (pg, z4, z4); +- svfloat64_t z16 = svmul_x (pg, z8, z8); +- svfloat64_t p = sv_estrin_11_f64_x (pg, z2, z4, z8, z16, d->poly); ++ ++ svfloat64_t c13 = svld1rq (ptrue, &d->c1); ++ svfloat64_t c57 = svld1rq (ptrue, &d->c5); ++ svfloat64_t c911 = svld1rq (ptrue, &d->c9); ++ ++ /* Order-11 Estrin scheme. */ ++ svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), z2, c13, 0); ++ svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), z2, c13, 1); ++ svfloat64_t p03 = svmla_x (pg, p01, z4, p23); ++ ++ svfloat64_t p45 = svmla_lane (sv_f64 (d->c4), z2, c57, 0); ++ svfloat64_t p67 = svmla_lane (sv_f64 (d->c6), z2, c57, 1); ++ svfloat64_t p47 = svmla_x (pg, p45, z4, p67); ++ ++ svfloat64_t p89 = svmla_lane (sv_f64 (d->c8), z2, c911, 0); ++ svfloat64_t p1011 = svmla_lane (sv_f64 (d->c10), z2, c911, 1); ++ svfloat64_t p811 = svmla_x (pg, p89, z4, p1011); ++ ++ svfloat64_t p411 = svmla_x (pg, p47, z8, p811); ++ svfloat64_t p = svmla_x (pg, p03, z8, p411); ++ + /* Finalize polynomial: z + z * z2 * P(z2). */ +- p = svmla_x (pg, z, svmul_x (pg, z, z2), p); ++ p = svmla_x (pg, z, z3, p); + +- /* asin(|x|) = Q(|x|) , for |x| < 0.5 +- = pi/2 - 2 Q(|x|), for |x| >= 0.5. */ +- svfloat64_t y = svmad_m (a_ge_half, p, sv_f64 (-2.0), d->pi_over_2f); ++ /* asin(|x|) = Q(|x|), for |x| < 0.5 ++ = pi/2 - 2 Q(|x|), for |x| >= 0.5. */ ++ svfloat64_t y = svmad_m (a_ge_half, p, sv_f64 (-2.0), d->pi_over_2); + +- /* Copy sign. */ ++ /* Reinsert the sign from the argument. */ + return svreinterpret_f64 (svorr_x (pg, svreinterpret_u64 (y), sign)); + } +diff --git a/sysdeps/aarch64/fpu/asinf_advsimd.c b/sysdeps/aarch64/fpu/asinf_advsimd.c +index 59d870ca36..88437f85ab 100644 +--- a/sysdeps/aarch64/fpu/asinf_advsimd.c ++++ b/sysdeps/aarch64/fpu/asinf_advsimd.c +@@ -18,22 +18,21 @@ + . */ + + #include "v_math.h" +-#include "poly_advsimd_f32.h" + + static const struct data + { +- float32x4_t poly[5]; ++ float32x4_t c0, c2, c4; ++ float c1, c3; + float32x4_t pi_over_2f; + } data = { + /* Polynomial approximation of (asin(sqrt(x)) - sqrt(x)) / (x * sqrt(x)) on + [ 0x1p-24 0x1p-2 ] order = 4 rel error: 0x1.00a23bbp-29 . */ +- .poly = { V4 (0x1.55555ep-3), V4 (0x1.33261ap-4), V4 (0x1.70d7dcp-5), +- V4 (0x1.b059dp-6), V4 (0x1.3af7d8p-5) }, +- .pi_over_2f = V4 (0x1.921fb6p+0f), ++ .c0 = V4 (0x1.55555ep-3f), .c1 = 0x1.33261ap-4f, ++ .c2 = V4 (0x1.70d7dcp-5f), .c3 = 0x1.b059dp-6f, ++ .c4 = V4 (0x1.3af7d8p-5f), .pi_over_2f = V4 (0x1.921fb6p+0f), + }; + + #define AbsMask 0x7fffffff +-#define Half 0x3f000000 + #define One 0x3f800000 + #define Small 0x39800000 /* 2^-12. */ + +@@ -47,11 +46,8 @@ special_case (float32x4_t x, float32x4_t y, uint32x4_t special) + + /* Single-precision implementation of vector asin(x). + +- For |x| < Small, approximate asin(x) by x. Small = 2^-12 for correct +- rounding. If WANT_SIMD_EXCEPT = 0, Small = 0 and we proceed with the +- following approximation. + +- For |x| in [Small, 0.5], use order 4 polynomial P such that the final ++ For |x| <0.5, use order 4 polynomial P such that the final + approximation is an odd polynomial: asin(x) ~ x + x^3 P(x^2). + + The largest observed error in this region is 0.83 ulps, +@@ -80,24 +76,31 @@ float32x4_t VPCS_ATTR NOINLINE V_NAME_F1 (asin) (float32x4_t x) + #endif + + float32x4_t ax = vreinterpretq_f32_u32 (ia); +- uint32x4_t a_lt_half = vcltq_u32 (ia, v_u32 (Half)); ++ uint32x4_t a_lt_half = vcaltq_f32 (x, v_f32 (0.5f)); + + /* Evaluate polynomial Q(x) = y + y * z * P(z) with + z = x ^ 2 and y = |x| , if |x| < 0.5 + z = (1 - |x|) / 2 and y = sqrt(z), if |x| >= 0.5. */ + float32x4_t z2 = vbslq_f32 (a_lt_half, vmulq_f32 (x, x), +- vfmsq_n_f32 (v_f32 (0.5), ax, 0.5)); ++ vfmsq_n_f32 (v_f32 (0.5f), ax, 0.5f)); + float32x4_t z = vbslq_f32 (a_lt_half, ax, vsqrtq_f32 (z2)); + + /* Use a single polynomial approximation P for both intervals. */ +- float32x4_t p = v_horner_4_f32 (z2, d->poly); ++ ++ /* PW Horner 3 evaluation scheme. */ ++ float32x4_t z4 = vmulq_f32 (z2, z2); ++ float32x4_t c13 = vld1q_f32 (&d->c1); ++ float32x4_t p01 = vfmaq_laneq_f32 (d->c0, z2, c13, 0); ++ float32x4_t p23 = vfmaq_laneq_f32 (d->c2, z2, c13, 1); ++ float32x4_t p = vfmaq_f32 (p23, d->c4, z4); ++ p = vfmaq_f32 (p01, p, z4); + /* Finalize polynomial: z + z * z2 * P(z2). */ + p = vfmaq_f32 (z, vmulq_f32 (z, z2), p); + + /* asin(|x|) = Q(|x|) , for |x| < 0.5 + = pi/2 - 2 Q(|x|), for |x| >= 0.5. */ + float32x4_t y +- = vbslq_f32 (a_lt_half, p, vfmsq_n_f32 (d->pi_over_2f, p, 2.0)); ++ = vbslq_f32 (a_lt_half, p, vfmsq_n_f32 (d->pi_over_2f, p, 2.0f)); + + /* Copy sign. */ + return vbslq_f32 (v_u32 (AbsMask), y, x); +diff --git a/sysdeps/aarch64/fpu/atan2_advsimd.c b/sysdeps/aarch64/fpu/atan2_advsimd.c +index 1a8f02109f..1e5d20602e 100644 +--- a/sysdeps/aarch64/fpu/atan2_advsimd.c ++++ b/sysdeps/aarch64/fpu/atan2_advsimd.c +@@ -19,40 +19,38 @@ + + #include "math_config.h" + #include "v_math.h" +-#include "poly_advsimd_f64.h" + + static const struct data + { ++ double c1, c3, c5, c7, c9, c11, c13, c15, c17, c19; + float64x2_t c0, c2, c4, c6, c8, c10, c12, c14, c16, c18; + float64x2_t pi_over_2; +- double c1, c3, c5, c7, c9, c11, c13, c15, c17, c19; +- uint64x2_t zeroinfnan, minustwo; ++ uint64x2_t zeroinfnan; + } data = { +- /* Coefficients of polynomial P such that atan(x)~x+x*P(x^2) on +- [2**-1022, 1.0]. */ +- .c0 = V2 (-0x1.5555555555555p-2), +- .c1 = 0x1.99999999996c1p-3, +- .c2 = V2 (-0x1.2492492478f88p-3), +- .c3 = 0x1.c71c71bc3951cp-4, +- .c4 = V2 (-0x1.745d160a7e368p-4), +- .c5 = 0x1.3b139b6a88ba1p-4, +- .c6 = V2 (-0x1.11100ee084227p-4), +- .c7 = 0x1.e1d0f9696f63bp-5, +- .c8 = V2 (-0x1.aebfe7b418581p-5), +- .c9 = 0x1.842dbe9b0d916p-5, +- .c10 = V2 (-0x1.5d30140ae5e99p-5), +- .c11 = 0x1.338e31eb2fbbcp-5, +- .c12 = V2 (-0x1.00e6eece7de8p-5), +- .c13 = 0x1.860897b29e5efp-6, +- .c14 = V2 (-0x1.0051381722a59p-6), +- .c15 = 0x1.14e9dc19a4a4ep-7, +- .c16 = V2 (-0x1.d0062b42fe3bfp-9), +- .c17 = 0x1.17739e210171ap-10, +- .c18 = V2 (-0x1.ab24da7be7402p-13), +- .c19 = 0x1.358851160a528p-16, ++ /* Coefficients of polynomial P such that ++ atan(x)~x+x*P(x^2) on [2^-1022, 1.0]. */ ++ .c0 = V2 (-0x1.555555555552ap-2), ++ .c1 = 0x1.9999999995aebp-3, ++ .c2 = V2 (-0x1.24924923923f6p-3), ++ .c3 = 0x1.c71c7184288a2p-4, ++ .c4 = V2 (-0x1.745d11fb3d32bp-4), ++ .c5 = 0x1.3b136a18051b9p-4, ++ .c6 = V2 (-0x1.110e6d985f496p-4), ++ .c7 = 0x1.e1bcf7f08801dp-5, ++ .c8 = V2 (-0x1.ae644e28058c3p-5), ++ .c9 = 0x1.82eeb1fed85c6p-5, ++ .c10 = V2 (-0x1.59d7f901566cbp-5), ++ .c11 = 0x1.2c982855ab069p-5, ++ .c12 = V2 (-0x1.eb49592998177p-6), ++ .c13 = 0x1.69d8b396e3d38p-6, ++ .c14 = V2 (-0x1.ca980345c4204p-7), ++ .c15 = 0x1.dc050eafde0b3p-8, ++ .c16 = V2 (-0x1.7ea70755b8eccp-9), ++ .c17 = 0x1.ba3da3de903e8p-11, ++ .c18 = V2 (-0x1.44a4b059b6f67p-13), ++ .c19 = 0x1.c4a45029e5a91p-17, + .pi_over_2 = V2 (0x1.921fb54442d18p+0), + .zeroinfnan = V2 (2 * 0x7ff0000000000000ul - 1), +- .minustwo = V2 (0xc000000000000000), + }; + + #define SignMask v_u64 (0x8000000000000000) +@@ -77,10 +75,9 @@ zeroinfnan (uint64x2_t i, const struct data *d) + } + + /* Fast implementation of vector atan2. +- Maximum observed error is 2.8 ulps: +- _ZGVnN2vv_atan2 (0x1.9651a429a859ap+5, 0x1.953075f4ee26p+5) +- got 0x1.92d628ab678ccp-1 +- want 0x1.92d628ab678cfp-1. */ ++ Maximum observed error is 1.97 ulps: ++ _ZGVnN2vv_atan2 (0x1.42337dba73768p+5, 0x1.422d748cd3e29p+5) ++ got 0x1.9224810264efcp-1 want 0x1.9224810264efep-1. */ + float64x2_t VPCS_ATTR V_NAME_D2 (atan2) (float64x2_t y, float64x2_t x) + { + const struct data *d = ptr_barrier (&data); +@@ -101,26 +98,29 @@ float64x2_t VPCS_ATTR V_NAME_D2 (atan2) (float64x2_t y, float64x2_t x) + uint64x2_t pred_xlt0 = vcltzq_f64 (x); + uint64x2_t pred_aygtax = vcagtq_f64 (y, x); + +- /* Set up z for call to atan. */ +- float64x2_t n = vbslq_f64 (pred_aygtax, vnegq_f64 (ax), ay); +- float64x2_t q = vbslq_f64 (pred_aygtax, ay, ax); +- float64x2_t z = vdivq_f64 (n, q); +- +- /* Work out the correct shift. */ +- float64x2_t shift +- = vreinterpretq_f64_u64 (vandq_u64 (pred_xlt0, d->minustwo)); +- shift = vbslq_f64 (pred_aygtax, vaddq_f64 (shift, v_f64 (1.0)), shift); +- shift = vmulq_f64 (shift, d->pi_over_2); +- +- /* Calculate the polynomial approximation. +- Use split Estrin scheme for P(z^2) with deg(P)=19. Use split instead of +- full scheme to avoid underflow in x^16. +- The order 19 polynomial P approximates +- (atan(sqrt(x))-sqrt(x))/x^(3/2). */ ++ /* Set up z for evaluation of atan. */ ++ float64x2_t num = vbslq_f64 (pred_aygtax, vnegq_f64 (ax), ay); ++ float64x2_t den = vbslq_f64 (pred_aygtax, ay, ax); ++ float64x2_t z = vdivq_f64 (num, den); ++ ++ /* Work out the correct shift for atan2: ++ Multiplication by pi is done later. ++ -pi when x < 0 and ax < ay ++ -pi/2 when x < 0 and ax > ay ++ 0 when x >= 0 and ax < ay ++ pi/2 when x >= 0 and ax > ay. */ ++ float64x2_t shift = vreinterpretq_f64_u64 ( ++ vandq_u64 (pred_xlt0, vreinterpretq_u64_f64 (v_f64 (-2.0)))); ++ float64x2_t shift2 = vreinterpretq_f64_u64 ( ++ vandq_u64 (pred_aygtax, vreinterpretq_u64_f64 (v_f64 (1.0)))); ++ shift = vaddq_f64 (shift, shift2); ++ ++ /* Calculate the polynomial approximation. */ + float64x2_t z2 = vmulq_f64 (z, z); +- float64x2_t x2 = vmulq_f64 (z2, z2); +- float64x2_t x4 = vmulq_f64 (x2, x2); +- float64x2_t x8 = vmulq_f64 (x4, x4); ++ float64x2_t z3 = vmulq_f64 (z2, z); ++ float64x2_t z4 = vmulq_f64 (z2, z2); ++ float64x2_t z8 = vmulq_f64 (z4, z4); ++ float64x2_t z16 = vmulq_f64 (z8, z8); + + float64x2_t c13 = vld1q_f64 (&d->c1); + float64x2_t c57 = vld1q_f64 (&d->c5); +@@ -128,45 +128,43 @@ float64x2_t VPCS_ATTR V_NAME_D2 (atan2) (float64x2_t y, float64x2_t x) + float64x2_t c1315 = vld1q_f64 (&d->c13); + float64x2_t c1719 = vld1q_f64 (&d->c17); + +- /* estrin_7. */ ++ /* Order-7 Estrin. */ + float64x2_t p01 = vfmaq_laneq_f64 (d->c0, z2, c13, 0); + float64x2_t p23 = vfmaq_laneq_f64 (d->c2, z2, c13, 1); +- float64x2_t p03 = vfmaq_f64 (p01, x2, p23); ++ float64x2_t p03 = vfmaq_f64 (p01, z4, p23); + + float64x2_t p45 = vfmaq_laneq_f64 (d->c4, z2, c57, 0); + float64x2_t p67 = vfmaq_laneq_f64 (d->c6, z2, c57, 1); +- float64x2_t p47 = vfmaq_f64 (p45, x2, p67); ++ float64x2_t p47 = vfmaq_f64 (p45, z4, p67); + +- float64x2_t p07 = vfmaq_f64 (p03, x4, p47); ++ float64x2_t p07 = vfmaq_f64 (p03, z8, p47); + +- /* estrin_11. */ ++ /* Order-11 Estrin. */ + float64x2_t p89 = vfmaq_laneq_f64 (d->c8, z2, c911, 0); + float64x2_t p1011 = vfmaq_laneq_f64 (d->c10, z2, c911, 1); +- float64x2_t p811 = vfmaq_f64 (p89, x2, p1011); ++ float64x2_t p811 = vfmaq_f64 (p89, z4, p1011); + + float64x2_t p1213 = vfmaq_laneq_f64 (d->c12, z2, c1315, 0); + float64x2_t p1415 = vfmaq_laneq_f64 (d->c14, z2, c1315, 1); +- float64x2_t p1215 = vfmaq_f64 (p1213, x2, p1415); ++ float64x2_t p1215 = vfmaq_f64 (p1213, z4, p1415); + + float64x2_t p1617 = vfmaq_laneq_f64 (d->c16, z2, c1719, 0); + float64x2_t p1819 = vfmaq_laneq_f64 (d->c18, z2, c1719, 1); +- float64x2_t p1619 = vfmaq_f64 (p1617, x2, p1819); ++ float64x2_t p1619 = vfmaq_f64 (p1617, z4, p1819); + +- float64x2_t p815 = vfmaq_f64 (p811, x4, p1215); +- float64x2_t p819 = vfmaq_f64 (p815, x8, p1619); ++ float64x2_t p815 = vfmaq_f64 (p811, z8, p1215); ++ float64x2_t p819 = vfmaq_f64 (p815, z16, p1619); + +- float64x2_t ret = vfmaq_f64 (p07, p819, x8); ++ float64x2_t poly = vfmaq_f64 (p07, p819, z16); + + /* Finalize. y = shift + z + z^3 * P(z^2). */ +- ret = vfmaq_f64 (z, ret, vmulq_f64 (z2, z)); +- ret = vaddq_f64 (ret, shift); ++ float64x2_t ret = vfmaq_f64 (z, shift, d->pi_over_2); ++ ret = vfmaq_f64 (ret, z3, poly); + + if (__glibc_unlikely (v_any_u64 (special_cases))) + return special_case (y, x, ret, sign_xy, special_cases); + + /* Account for the sign of x and y. */ +- ret = vreinterpretq_f64_u64 ( ++ return vreinterpretq_f64_u64 ( + veorq_u64 (vreinterpretq_u64_f64 (ret), sign_xy)); +- +- return ret; + } +diff --git a/sysdeps/aarch64/fpu/atan2_sve.c b/sysdeps/aarch64/fpu/atan2_sve.c +index ed9f683436..0337ea57b7 100644 +--- a/sysdeps/aarch64/fpu/atan2_sve.c ++++ b/sysdeps/aarch64/fpu/atan2_sve.c +@@ -19,25 +19,25 @@ + + #include "math_config.h" + #include "sv_math.h" +-#include "poly_sve_f64.h" + + static const struct data + { +- float64_t poly[20]; +- float64_t pi_over_2; ++ float64_t c0, c2, c4, c6, c8, c10, c12, c14, c16, c18; ++ float64_t c1, c3, c5, c7, c9, c11, c13, c15, c17, c19; + } data = { + /* Coefficients of polynomial P such that atan(x)~x+x*P(x^2) on + [2**-1022, 1.0]. */ +- .poly = { -0x1.5555555555555p-2, 0x1.99999999996c1p-3, -0x1.2492492478f88p-3, +- 0x1.c71c71bc3951cp-4, -0x1.745d160a7e368p-4, 0x1.3b139b6a88ba1p-4, +- -0x1.11100ee084227p-4, 0x1.e1d0f9696f63bp-5, -0x1.aebfe7b418581p-5, +- 0x1.842dbe9b0d916p-5, -0x1.5d30140ae5e99p-5, 0x1.338e31eb2fbbcp-5, +- -0x1.00e6eece7de8p-5, 0x1.860897b29e5efp-6, -0x1.0051381722a59p-6, +- 0x1.14e9dc19a4a4ep-7, -0x1.d0062b42fe3bfp-9, 0x1.17739e210171ap-10, +- -0x1.ab24da7be7402p-13, 0x1.358851160a528p-16, }, +- .pi_over_2 = 0x1.921fb54442d18p+0, ++ .c0 = -0x1.555555555552ap-2, .c1 = 0x1.9999999995aebp-3, ++ .c2 = -0x1.24924923923f6p-3, .c3 = 0x1.c71c7184288a2p-4, ++ .c4 = -0x1.745d11fb3d32bp-4, .c5 = 0x1.3b136a18051b9p-4, ++ .c6 = -0x1.110e6d985f496p-4, .c7 = 0x1.e1bcf7f08801dp-5, ++ .c8 = -0x1.ae644e28058c3p-5, .c9 = 0x1.82eeb1fed85c6p-5, ++ .c10 = -0x1.59d7f901566cbp-5, .c11 = 0x1.2c982855ab069p-5, ++ .c12 = -0x1.eb49592998177p-6, .c13 = 0x1.69d8b396e3d38p-6, ++ .c14 = -0x1.ca980345c4204p-7, .c15 = 0x1.dc050eafde0b3p-8, ++ .c16 = -0x1.7ea70755b8eccp-9, .c17 = 0x1.ba3da3de903e8p-11, ++ .c18 = -0x1.44a4b059b6f67p-13, .c19 = 0x1.c4a45029e5a91p-17, + }; +- + /* Special cases i.e. 0, infinity, nan (fall back to scalar calls). */ + static svfloat64_t NOINLINE + special_case (svfloat64_t y, svfloat64_t x, svfloat64_t ret, +@@ -56,15 +56,17 @@ zeroinfnan (svuint64_t i, const svbool_t pg) + } + + /* Fast implementation of SVE atan2. Errors are greatest when y and +- x are reasonably close together. The greatest observed error is 2.28 ULP: +- _ZGVsMxvv_atan2 (-0x1.5915b1498e82fp+732, 0x1.54d11ef838826p+732) +- got -0x1.954f42f1fa841p-1 want -0x1.954f42f1fa843p-1. */ +-svfloat64_t SV_NAME_D2 (atan2) (svfloat64_t y, svfloat64_t x, const svbool_t pg) ++ x are reasonably close together. The greatest observed error is 1.94 ULP: ++ _ZGVsMxvv_atan2 (0x1.8a4bf7167228ap+5, 0x1.84971226bb57bp+5) ++ got 0x1.95db19dfef9ccp-1 want 0x1.95db19dfef9cep-1. */ ++svfloat64_t SV_NAME_D2 (atan2) (svfloat64_t y, svfloat64_t x, ++ const svbool_t pg) + { +- const struct data *data_ptr = ptr_barrier (&data); ++ const struct data *d = ptr_barrier (&data); + + svuint64_t ix = svreinterpret_u64 (x); + svuint64_t iy = svreinterpret_u64 (y); ++ svbool_t ptrue = svptrue_b64 (); + + svbool_t cmp_x = zeroinfnan (ix, pg); + svbool_t cmp_y = zeroinfnan (iy, pg); +@@ -81,32 +83,67 @@ svfloat64_t SV_NAME_D2 (atan2) (svfloat64_t y, svfloat64_t x, const svbool_t pg) + + svbool_t pred_aygtax = svcmpgt (pg, ay, ax); + +- /* Set up z for call to atan. */ +- svfloat64_t n = svsel (pred_aygtax, svneg_x (pg, ax), ay); +- svfloat64_t d = svsel (pred_aygtax, ay, ax); +- svfloat64_t z = svdiv_x (pg, n, d); +- +- /* Work out the correct shift. */ ++ /* Set up z for evaluation of atan. */ ++ svfloat64_t num = svsel (pred_aygtax, svneg_x (pg, ax), ay); ++ svfloat64_t den = svsel (pred_aygtax, ay, ax); ++ svfloat64_t z = svdiv_x (pg, num, den); ++ ++ /* Work out the correct shift for atan2: ++ Multiplication by pi is done later. ++ -pi when x < 0 and ax < ay ++ -pi/2 when x < 0 and ax > ay ++ 0 when x >= 0 and ax < ay ++ pi/2 when x >= 0 and ax > ay. */ + svfloat64_t shift = svreinterpret_f64 (svlsr_x (pg, sign_x, 1)); ++ svfloat64_t shift_mul = svreinterpret_f64 ( ++ svorr_x (pg, sign_x, svreinterpret_u64 (sv_f64 (0x1.921fb54442d18p+0)))); + shift = svsel (pred_aygtax, sv_f64 (1.0), shift); +- shift = svreinterpret_f64 (svorr_x (pg, sign_x, svreinterpret_u64 (shift))); +- shift = svmul_x (pg, shift, data_ptr->pi_over_2); ++ shift = svmla_x (pg, z, shift, shift_mul); + + /* Use split Estrin scheme for P(z^2) with deg(P)=19. */ + svfloat64_t z2 = svmul_x (pg, z, z); +- svfloat64_t x2 = svmul_x (pg, z2, z2); +- svfloat64_t x4 = svmul_x (pg, x2, x2); +- svfloat64_t x8 = svmul_x (pg, x4, x4); ++ svfloat64_t z3 = svmul_x (pg, z2, z); ++ svfloat64_t z4 = svmul_x (pg, z2, z2); ++ svfloat64_t z8 = svmul_x (pg, z4, z4); ++ svfloat64_t z16 = svmul_x (pg, z8, z8); + +- svfloat64_t ret = svmla_x ( +- pg, sv_estrin_7_f64_x (pg, z2, x2, x4, data_ptr->poly), +- sv_estrin_11_f64_x (pg, z2, x2, x4, x8, data_ptr->poly + 8), x8); ++ /* Order-7 Estrin. */ ++ svfloat64_t c13 = svld1rq (ptrue, &d->c1); ++ svfloat64_t c57 = svld1rq (ptrue, &d->c5); + +- /* y = shift + z + z^3 * P(z^2). */ +- svfloat64_t z3 = svmul_x (pg, z2, z); +- ret = svmla_x (pg, z, z3, ret); ++ svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), z2, c13, 0); ++ svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), z2, c13, 1); ++ svfloat64_t p45 = svmla_lane (sv_f64 (d->c4), z2, c57, 0); ++ svfloat64_t p67 = svmla_lane (sv_f64 (d->c6), z2, c57, 1); ++ ++ svfloat64_t p03 = svmla_x (pg, p01, z4, p23); ++ svfloat64_t p47 = svmla_x (pg, p45, z4, p67); ++ svfloat64_t p07 = svmla_x (pg, p03, z8, p47); ++ ++ /* Order-11 Estrin. */ ++ svfloat64_t c911 = svld1rq (ptrue, &d->c9); ++ svfloat64_t c1315 = svld1rq (ptrue, &d->c13); ++ svfloat64_t c1719 = svld1rq (ptrue, &d->c17); + +- ret = svadd_m (pg, ret, shift); ++ svfloat64_t p89 = svmla_lane (sv_f64 (d->c8), z2, c911, 0); ++ svfloat64_t p1011 = svmla_lane (sv_f64 (d->c10), z2, c911, 1); ++ svfloat64_t p811 = svmla_x (pg, p89, z4, p1011); ++ ++ svfloat64_t p1213 = svmla_lane (sv_f64 (d->c12), z2, c1315, 0); ++ svfloat64_t p1415 = svmla_lane (sv_f64 (d->c14), z2, c1315, 1); ++ svfloat64_t p1215 = svmla_x (pg, p1213, z4, p1415); ++ ++ svfloat64_t p1617 = svmla_lane (sv_f64 (d->c16), z2, c1719, 0); ++ svfloat64_t p1819 = svmla_lane (sv_f64 (d->c18), z2, c1719, 1); ++ svfloat64_t p1619 = svmla_x (pg, p1617, z4, p1819); ++ ++ svfloat64_t p815 = svmla_x (pg, p811, z8, p1215); ++ svfloat64_t p819 = svmla_x (pg, p815, z16, p1619); ++ ++ svfloat64_t poly = svmla_x (pg, p07, z16, p819); ++ ++ /* y = shift + z + z^3 * P(z^2). */ ++ svfloat64_t ret = svmla_x (pg, shift, z3, poly); + + /* Account for the sign of x and y. */ + if (__glibc_unlikely (svptest_any (pg, cmp_xy))) +diff --git a/sysdeps/aarch64/fpu/atan2f_advsimd.c b/sysdeps/aarch64/fpu/atan2f_advsimd.c +index 88daacd76c..23a825e63b 100644 +--- a/sysdeps/aarch64/fpu/atan2f_advsimd.c ++++ b/sysdeps/aarch64/fpu/atan2f_advsimd.c +@@ -18,22 +18,22 @@ + . */ + + #include "v_math.h" +-#include "poly_advsimd_f32.h" + + static const struct data + { +- float32x4_t c0, pi_over_2, c4, c6, c2; ++ float32x4_t c0, c4, c6, c2; + float c1, c3, c5, c7; + uint32x4_t comp_const; ++ float32x4_t pi; + } data = { + /* Coefficients of polynomial P such that atan(x)~x+x*P(x^2) on + [2**-128, 1.0]. + Generated using fpminimax between FLT_MIN and 1. */ +- .c0 = V4 (-0x1.55555p-2f), .c1 = 0x1.99935ep-3f, +- .c2 = V4 (-0x1.24051ep-3f), .c3 = 0x1.bd7368p-4f, +- .c4 = V4 (-0x1.491f0ep-4f), .c5 = 0x1.93a2c0p-5f, +- .c6 = V4 (-0x1.4c3c60p-6f), .c7 = 0x1.01fd88p-8f, +- .pi_over_2 = V4 (0x1.921fb6p+0f), .comp_const = V4 (2 * 0x7f800000lu - 1), ++ .c0 = V4 (-0x1.5554dcp-2), .c1 = 0x1.9978ecp-3, ++ .c2 = V4 (-0x1.230a94p-3), .c3 = 0x1.b4debp-4, ++ .c4 = V4 (-0x1.3550dap-4), .c5 = 0x1.61eebp-5, ++ .c6 = V4 (-0x1.0c17d4p-6), .c7 = 0x1.7ea694p-9, ++ .pi = V4 (0x1.921fb6p+1f), .comp_const = V4 (2 * 0x7f800000lu - 1), + }; + + #define SignMask v_u32 (0x80000000) +@@ -54,13 +54,13 @@ static inline uint32x4_t + zeroinfnan (uint32x4_t i, const struct data *d) + { + /* 2 * i - 1 >= 2 * 0x7f800000lu - 1. */ +- return vcgeq_u32 (vsubq_u32 (vmulq_n_u32 (i, 2), v_u32 (1)), d->comp_const); ++ return vcgeq_u32 (vsubq_u32 (vshlq_n_u32 (i, 1), v_u32 (1)), d->comp_const); + } + + /* Fast implementation of vector atan2f. Maximum observed error is +- 2.95 ULP in [0x1.9300d6p+6 0x1.93c0c6p+6] x [0x1.8c2dbp+6 0x1.8cea6p+6]: +- _ZGVnN4vv_atan2f (0x1.93836cp+6, 0x1.8cae1p+6) got 0x1.967f06p-1 +- want 0x1.967f00p-1. */ ++ 2.13 ULP in [0x1.9300d6p+6 0x1.93c0c6p+6] x [0x1.8c2dbp+6 0x1.8cea6p+6]: ++ _ZGVnN4vv_atan2f (0x1.14a9d4p-87, 0x1.0eb886p-87) got 0x1.97aea2p-1 ++ want 0x1.97ae9ep-1. */ + float32x4_t VPCS_ATTR NOINLINE V_NAME_F2 (atan2) (float32x4_t y, float32x4_t x) + { + const struct data *d = ptr_barrier (&data); +@@ -81,28 +81,31 @@ float32x4_t VPCS_ATTR NOINLINE V_NAME_F2 (atan2) (float32x4_t y, float32x4_t x) + uint32x4_t pred_xlt0 = vcltzq_f32 (x); + uint32x4_t pred_aygtax = vcgtq_f32 (ay, ax); + +- /* Set up z for call to atanf. */ +- float32x4_t n = vbslq_f32 (pred_aygtax, vnegq_f32 (ax), ay); +- float32x4_t q = vbslq_f32 (pred_aygtax, ay, ax); +- float32x4_t z = vdivq_f32 (n, q); +- +- /* Work out the correct shift. */ ++ /* Set up z for evaluation of atanf. */ ++ float32x4_t num = vbslq_f32 (pred_aygtax, vnegq_f32 (ax), ay); ++ float32x4_t den = vbslq_f32 (pred_aygtax, ay, ax); ++ float32x4_t z = vdivq_f32 (num, den); ++ ++ /* Work out the correct shift for atan2: ++ Multiplication by pi is done later. ++ -pi when x < 0 and ax < ay ++ -pi/2 when x < 0 and ax > ay ++ 0 when x >= 0 and ax < ay ++ pi/2 when x >= 0 and ax > ay. */ + float32x4_t shift = vreinterpretq_f32_u32 ( +- vandq_u32 (pred_xlt0, vreinterpretq_u32_f32 (v_f32 (-2.0f)))); +- shift = vbslq_f32 (pred_aygtax, vaddq_f32 (shift, v_f32 (1.0f)), shift); +- shift = vmulq_f32 (shift, d->pi_over_2); +- +- /* Calculate the polynomial approximation. +- Use 2-level Estrin scheme for P(z^2) with deg(P)=7. However, +- a standard implementation using z8 creates spurious underflow +- in the very last fma (when z^8 is small enough). +- Therefore, we split the last fma into a mul and an fma. +- Horner and single-level Estrin have higher errors that exceed +- threshold. */ ++ vandq_u32 (pred_xlt0, vreinterpretq_u32_f32 (v_f32 (-1.0f)))); ++ float32x4_t shift2 = vreinterpretq_f32_u32 ( ++ vandq_u32 (pred_aygtax, vreinterpretq_u32_f32 (v_f32 (0.5f)))); ++ shift = vaddq_f32 (shift, shift2); ++ ++ /* Calculate the polynomial approximation. */ + float32x4_t z2 = vmulq_f32 (z, z); ++ float32x4_t z3 = vmulq_f32 (z2, z); + float32x4_t z4 = vmulq_f32 (z2, z2); ++ float32x4_t z8 = vmulq_f32 (z4, z4); + + float32x4_t c1357 = vld1q_f32 (&d->c1); ++ + float32x4_t p01 = vfmaq_laneq_f32 (d->c0, z2, c1357, 0); + float32x4_t p23 = vfmaq_laneq_f32 (d->c2, z2, c1357, 1); + float32x4_t p45 = vfmaq_laneq_f32 (d->c4, z2, c1357, 2); +@@ -110,10 +113,11 @@ float32x4_t VPCS_ATTR NOINLINE V_NAME_F2 (atan2) (float32x4_t y, float32x4_t x) + float32x4_t p03 = vfmaq_f32 (p01, z4, p23); + float32x4_t p47 = vfmaq_f32 (p45, z4, p67); + +- float32x4_t ret = vfmaq_f32 (p03, z4, vmulq_f32 (z4, p47)); ++ float32x4_t poly = vfmaq_f32 (p03, z8, p47); + + /* y = shift + z * P(z^2). */ +- ret = vaddq_f32 (vfmaq_f32 (z, ret, vmulq_f32 (z2, z)), shift); ++ float32x4_t ret = vfmaq_f32 (z, shift, d->pi); ++ ret = vfmaq_f32 (ret, z3, poly); + + if (__glibc_unlikely (v_any_u32 (special_cases))) + { +diff --git a/sysdeps/aarch64/fpu/atan2f_sve.c b/sysdeps/aarch64/fpu/atan2f_sve.c +index 9ea197147c..f7f6d40ca6 100644 +--- a/sysdeps/aarch64/fpu/atan2f_sve.c ++++ b/sysdeps/aarch64/fpu/atan2f_sve.c +@@ -18,18 +18,18 @@ + . */ + + #include "sv_math.h" +-#include "poly_sve_f32.h" + + static const struct data + { +- float32_t poly[8]; ++ float32_t c0, c2, c4, c6; ++ float32_t c1, c3, c5, c7; + float32_t pi_over_2; + } data = { + /* Coefficients of polynomial P such that atan(x)~x+x*P(x^2) on + [2**-128, 1.0]. */ +- .poly = { -0x1.55555p-2f, 0x1.99935ep-3f, -0x1.24051ep-3f, 0x1.bd7368p-4f, +- -0x1.491f0ep-4f, 0x1.93a2c0p-5f, -0x1.4c3c60p-6f, 0x1.01fd88p-8f }, +- .pi_over_2 = 0x1.921fb6p+0f, ++ .c0 = -0x1.5554dcp-2, .c1 = 0x1.9978ecp-3, .c2 = -0x1.230a94p-3, ++ .c3 = 0x1.b4debp-4, .c4 = -0x1.3550dap-4, .c5 = 0x1.61eebp-5, ++ .c6 = -0x1.0c17d4p-6, .c7 = 0x1.7ea694p-9, .pi_over_2 = 0x1.921fb6p+0f, + }; + + /* Special cases i.e. 0, infinity, nan (fall back to scalar calls). */ +@@ -51,12 +51,14 @@ zeroinfnan (svuint32_t i, const svbool_t pg) + + /* Fast implementation of SVE atan2f based on atan(x) ~ shift + z + z^3 * + P(z^2) with reduction to [0,1] using z=1/x and shift = pi/2. Maximum +- observed error is 2.95 ULP: +- _ZGVsMxvv_atan2f (0x1.93836cp+6, 0x1.8cae1p+6) got 0x1.967f06p-1 +- want 0x1.967f00p-1. */ +-svfloat32_t SV_NAME_F2 (atan2) (svfloat32_t y, svfloat32_t x, const svbool_t pg) ++ observed error is 2.21 ULP: ++ _ZGVnN4vv_atan2f (0x1.a04aa8p+6, 0x1.9a274p+6) got 0x1.95ed3ap-1 ++ want 0x1.95ed36p-1. */ ++svfloat32_t SV_NAME_F2 (atan2) (svfloat32_t y, svfloat32_t x, ++ const svbool_t pg) + { +- const struct data *data_ptr = ptr_barrier (&data); ++ const struct data *d = ptr_barrier (&data); ++ svbool_t ptrue = svptrue_b32 (); + + svuint32_t ix = svreinterpret_u32 (x); + svuint32_t iy = svreinterpret_u32 (y); +@@ -76,29 +78,42 @@ svfloat32_t SV_NAME_F2 (atan2) (svfloat32_t y, svfloat32_t x, const svbool_t pg) + + svbool_t pred_aygtax = svcmpgt (pg, ay, ax); + +- /* Set up z for call to atan. */ +- svfloat32_t n = svsel (pred_aygtax, svneg_x (pg, ax), ay); +- svfloat32_t d = svsel (pred_aygtax, ay, ax); +- svfloat32_t z = svdiv_x (pg, n, d); +- +- /* Work out the correct shift. */ ++ /* Set up z for evaluation of atanf. */ ++ svfloat32_t num = svsel (pred_aygtax, svneg_x (pg, ax), ay); ++ svfloat32_t den = svsel (pred_aygtax, ay, ax); ++ svfloat32_t z = svdiv_x (ptrue, num, den); ++ ++ /* Work out the correct shift for atan2: ++ Multiplication by pi is done later. ++ -pi when x < 0 and ax < ay ++ -pi/2 when x < 0 and ax > ay ++ 0 when x >= 0 and ax < ay ++ pi/2 when x >= 0 and ax > ay. */ + svfloat32_t shift = svreinterpret_f32 (svlsr_x (pg, sign_x, 1)); + shift = svsel (pred_aygtax, sv_f32 (1.0), shift); + shift = svreinterpret_f32 (svorr_x (pg, sign_x, svreinterpret_u32 (shift))); +- shift = svmul_x (pg, shift, sv_f32 (data_ptr->pi_over_2)); + + /* Use pure Estrin scheme for P(z^2) with deg(P)=7. */ +- svfloat32_t z2 = svmul_x (pg, z, z); ++ svfloat32_t z2 = svmul_x (ptrue, z, z); ++ svfloat32_t z3 = svmul_x (pg, z2, z); + svfloat32_t z4 = svmul_x (pg, z2, z2); + svfloat32_t z8 = svmul_x (pg, z4, z4); + +- svfloat32_t ret = sv_estrin_7_f32_x (pg, z2, z4, z8, data_ptr->poly); ++ svfloat32_t odd_coeffs = svld1rq (ptrue, &d->c1); + +- /* ret = shift + z + z^3 * P(z^2). */ +- svfloat32_t z3 = svmul_x (pg, z2, z); +- ret = svmla_x (pg, z, z3, ret); ++ svfloat32_t p01 = svmla_lane (sv_f32 (d->c0), z2, odd_coeffs, 0); ++ svfloat32_t p23 = svmla_lane (sv_f32 (d->c2), z2, odd_coeffs, 1); ++ svfloat32_t p45 = svmla_lane (sv_f32 (d->c4), z2, odd_coeffs, 2); ++ svfloat32_t p67 = svmla_lane (sv_f32 (d->c6), z2, odd_coeffs, 3); + +- ret = svadd_m (pg, ret, shift); ++ svfloat32_t p03 = svmla_x (pg, p01, z4, p23); ++ svfloat32_t p47 = svmla_x (pg, p45, z4, p67); ++ ++ svfloat32_t poly = svmla_x (pg, p03, z8, p47); ++ ++ /* ret = shift + z + z^3 * P(z^2). */ ++ svfloat32_t ret = svmla_x (pg, z, shift, sv_f32 (d->pi_over_2)); ++ ret = svmla_x (pg, ret, z3, poly); + + /* Account for the sign of x and y. */ + +diff --git a/sysdeps/aarch64/fpu/atan_advsimd.c b/sysdeps/aarch64/fpu/atan_advsimd.c +index 14f1809796..f835dae828 100644 +--- a/sysdeps/aarch64/fpu/atan_advsimd.c ++++ b/sysdeps/aarch64/fpu/atan_advsimd.c +@@ -18,7 +18,6 @@ + . */ + + #include "v_math.h" +-#include "poly_advsimd_f64.h" + + static const struct data + { +@@ -28,16 +27,16 @@ static const struct data + } data = { + /* Coefficients of polynomial P such that atan(x)~x+x*P(x^2) on + [2**-1022, 1.0]. */ +- .c0 = V2 (-0x1.5555555555555p-2), .c1 = 0x1.99999999996c1p-3, +- .c2 = V2 (-0x1.2492492478f88p-3), .c3 = 0x1.c71c71bc3951cp-4, +- .c4 = V2 (-0x1.745d160a7e368p-4), .c5 = 0x1.3b139b6a88ba1p-4, +- .c6 = V2 (-0x1.11100ee084227p-4), .c7 = 0x1.e1d0f9696f63bp-5, +- .c8 = V2 (-0x1.aebfe7b418581p-5), .c9 = 0x1.842dbe9b0d916p-5, +- .c10 = V2 (-0x1.5d30140ae5e99p-5), .c11 = 0x1.338e31eb2fbbcp-5, +- .c12 = V2 (-0x1.00e6eece7de8p-5), .c13 = 0x1.860897b29e5efp-6, +- .c14 = V2 (-0x1.0051381722a59p-6), .c15 = 0x1.14e9dc19a4a4ep-7, +- .c16 = V2 (-0x1.d0062b42fe3bfp-9), .c17 = 0x1.17739e210171ap-10, +- .c18 = V2 (-0x1.ab24da7be7402p-13), .c19 = 0x1.358851160a528p-16, ++ .c0 = V2 (-0x1.555555555552ap-2), .c1 = 0x1.9999999995aebp-3, ++ .c2 = V2 (-0x1.24924923923f6p-3), .c3 = 0x1.c71c7184288a2p-4, ++ .c4 = V2 (-0x1.745d11fb3d32bp-4), .c5 = 0x1.3b136a18051b9p-4, ++ .c6 = V2 (-0x1.110e6d985f496p-4), .c7 = 0x1.e1bcf7f08801dp-5, ++ .c8 = V2 (-0x1.ae644e28058c3p-5), .c9 = 0x1.82eeb1fed85c6p-5, ++ .c10 = V2 (-0x1.59d7f901566cbp-5), .c11 = 0x1.2c982855ab069p-5, ++ .c12 = V2 (-0x1.eb49592998177p-6), .c13 = 0x1.69d8b396e3d38p-6, ++ .c14 = V2 (-0x1.ca980345c4204p-7), .c15 = 0x1.dc050eafde0b3p-8, ++ .c16 = V2 (-0x1.7ea70755b8eccp-9), .c17 = 0x1.ba3da3de903e8p-11, ++ .c18 = V2 (-0x1.44a4b059b6f67p-13), .c19 = 0x1.c4a45029e5a91p-17, + .pi_over_2 = V2 (0x1.921fb54442d18p+0), + }; + +@@ -47,9 +46,9 @@ static const struct data + + /* Fast implementation of vector atan. + Based on atan(x) ~ shift + z + z^3 * P(z^2) with reduction to [0,1] using +- z=1/x and shift = pi/2. Maximum observed error is 2.27 ulps: +- _ZGVnN2v_atan (0x1.0005af27c23e9p+0) got 0x1.9225645bdd7c1p-1 +- want 0x1.9225645bdd7c3p-1. */ ++ z=1/x and shift = pi/2. Maximum observed error is 2.45 ulps: ++ _ZGVnN2v_atan (0x1.0008d737eb3e6p+0) got 0x1.92288c551a4c1p-1 ++ want 0x1.92288c551a4c3p-1. */ + float64x2_t VPCS_ATTR V_NAME_D1 (atan) (float64x2_t x) + { + const struct data *d = ptr_barrier (&data); +@@ -78,59 +77,53 @@ float64x2_t VPCS_ATTR V_NAME_D1 (atan) (float64x2_t x) + y := arctan(x) for x < 1 + y := pi/2 + arctan(-1/x) for x > 1 + Hence, use z=-1/a if x>=1, otherwise z=a. */ +- uint64x2_t red = vcagtq_f64 (x, v_f64 (1.0)); ++ uint64x2_t red = vcagtq_f64 (x, v_f64 (-1.0)); + /* Avoid dependency in abs(x) in division (and comparison). */ +- float64x2_t z = vbslq_f64 (red, vdivq_f64 (v_f64 (1.0), x), x); ++ float64x2_t z = vbslq_f64 (red, vdivq_f64 (v_f64 (-1.0), x), x); ++ + float64x2_t shift = vreinterpretq_f64_u64 ( + vandq_u64 (red, vreinterpretq_u64_f64 (d->pi_over_2))); +- /* Use absolute value only when needed (odd powers of z). */ +- float64x2_t az = vbslq_f64 ( +- SignMask, vreinterpretq_f64_u64 (vandq_u64 (SignMask, red)), z); +- +- /* Calculate the polynomial approximation. +- Use split Estrin scheme for P(z^2) with deg(P)=19. Use split instead of +- full scheme to avoid underflow in x^16. +- The order 19 polynomial P approximates +- (atan(sqrt(x))-sqrt(x))/x^(3/2). */ ++ ++ /* Reinsert sign bit from argument into the shift value. */ ++ shift = vreinterpretq_f64_u64 ( ++ veorq_u64 (vreinterpretq_u64_f64 (shift), sign)); ++ ++ /* Calculate polynomial approximation P(z^2) with deg(P)=19. */ + float64x2_t z2 = vmulq_f64 (z, z); +- float64x2_t x2 = vmulq_f64 (z2, z2); +- float64x2_t x4 = vmulq_f64 (x2, x2); +- float64x2_t x8 = vmulq_f64 (x4, x4); ++ float64x2_t z4 = vmulq_f64 (z2, z2); ++ float64x2_t z8 = vmulq_f64 (z4, z4); ++ float64x2_t z16 = vmulq_f64 (z8, z8); + +- /* estrin_7. */ ++ /* Order-7 Estrin. */ + float64x2_t p01 = vfmaq_laneq_f64 (d->c0, z2, c13, 0); + float64x2_t p23 = vfmaq_laneq_f64 (d->c2, z2, c13, 1); +- float64x2_t p03 = vfmaq_f64 (p01, x2, p23); ++ float64x2_t p03 = vfmaq_f64 (p01, z4, p23); + + float64x2_t p45 = vfmaq_laneq_f64 (d->c4, z2, c57, 0); + float64x2_t p67 = vfmaq_laneq_f64 (d->c6, z2, c57, 1); +- float64x2_t p47 = vfmaq_f64 (p45, x2, p67); ++ float64x2_t p47 = vfmaq_f64 (p45, z4, p67); + +- float64x2_t p07 = vfmaq_f64 (p03, x4, p47); ++ float64x2_t p07 = vfmaq_f64 (p03, z8, p47); + +- /* estrin_11. */ ++ /* Order-11 Estrin. */ + float64x2_t p89 = vfmaq_laneq_f64 (d->c8, z2, c911, 0); + float64x2_t p1011 = vfmaq_laneq_f64 (d->c10, z2, c911, 1); +- float64x2_t p811 = vfmaq_f64 (p89, x2, p1011); ++ float64x2_t p811 = vfmaq_f64 (p89, z4, p1011); + + float64x2_t p1213 = vfmaq_laneq_f64 (d->c12, z2, c1315, 0); + float64x2_t p1415 = vfmaq_laneq_f64 (d->c14, z2, c1315, 1); +- float64x2_t p1215 = vfmaq_f64 (p1213, x2, p1415); ++ float64x2_t p1215 = vfmaq_f64 (p1213, z4, p1415); + + float64x2_t p1617 = vfmaq_laneq_f64 (d->c16, z2, c1719, 0); + float64x2_t p1819 = vfmaq_laneq_f64 (d->c18, z2, c1719, 1); +- float64x2_t p1619 = vfmaq_f64 (p1617, x2, p1819); ++ float64x2_t p1619 = vfmaq_f64 (p1617, z4, p1819); + +- float64x2_t p815 = vfmaq_f64 (p811, x4, p1215); +- float64x2_t p819 = vfmaq_f64 (p815, x8, p1619); ++ float64x2_t p815 = vfmaq_f64 (p811, z8, p1215); ++ float64x2_t p819 = vfmaq_f64 (p815, z16, p1619); + +- float64x2_t y = vfmaq_f64 (p07, p819, x8); ++ float64x2_t y = vfmaq_f64 (p07, p819, z16); + + /* Finalize. y = shift + z + z^3 * P(z^2). */ +- y = vfmaq_f64 (az, y, vmulq_f64 (z2, az)); +- y = vaddq_f64 (y, shift); +- +- /* y = atan(x) if x>0, -atan(-x) otherwise. */ +- y = vreinterpretq_f64_u64 (veorq_u64 (vreinterpretq_u64_f64 (y), sign)); +- return y; ++ y = vfmsq_f64 (v_f64 (-1.0), z2, y); ++ return vfmsq_f64 (shift, z, y); + } +diff --git a/sysdeps/aarch64/fpu/atan_sve.c b/sysdeps/aarch64/fpu/atan_sve.c +index fa1630304c..046c04fbb1 100644 +--- a/sysdeps/aarch64/fpu/atan_sve.c ++++ b/sysdeps/aarch64/fpu/atan_sve.c +@@ -18,23 +18,26 @@ + . */ + + #include "sv_math.h" +-#include "poly_sve_f64.h" + + static const struct data + { +- float64_t poly[20]; +- float64_t pi_over_2; ++ float64_t c0, c2, c4, c6, c8, c10, c12, c14, c16, c18; ++ float64_t c1, c3, c5, c7, c9, c11, c13, c15, c17, c19; ++ float64_t shift_val, neg_one; + } data = { + /* Coefficients of polynomial P such that atan(x)~x+x*P(x^2) on + [2**-1022, 1.0]. */ +- .poly = { -0x1.5555555555555p-2, 0x1.99999999996c1p-3, -0x1.2492492478f88p-3, +- 0x1.c71c71bc3951cp-4, -0x1.745d160a7e368p-4, 0x1.3b139b6a88ba1p-4, +- -0x1.11100ee084227p-4, 0x1.e1d0f9696f63bp-5, -0x1.aebfe7b418581p-5, +- 0x1.842dbe9b0d916p-5, -0x1.5d30140ae5e99p-5, 0x1.338e31eb2fbbcp-5, +- -0x1.00e6eece7de8p-5, 0x1.860897b29e5efp-6, -0x1.0051381722a59p-6, +- 0x1.14e9dc19a4a4ep-7, -0x1.d0062b42fe3bfp-9, 0x1.17739e210171ap-10, +- -0x1.ab24da7be7402p-13, 0x1.358851160a528p-16, }, +- .pi_over_2 = 0x1.921fb54442d18p+0, ++ .c0 = -0x1.555555555552ap-2, .c1 = 0x1.9999999995aebp-3, ++ .c2 = -0x1.24924923923f6p-3, .c3 = 0x1.c71c7184288a2p-4, ++ .c4 = -0x1.745d11fb3d32bp-4, .c5 = 0x1.3b136a18051b9p-4, ++ .c6 = -0x1.110e6d985f496p-4, .c7 = 0x1.e1bcf7f08801dp-5, ++ .c8 = -0x1.ae644e28058c3p-5, .c9 = 0x1.82eeb1fed85c6p-5, ++ .c10 = -0x1.59d7f901566cbp-5, .c11 = 0x1.2c982855ab069p-5, ++ .c12 = -0x1.eb49592998177p-6, .c13 = 0x1.69d8b396e3d38p-6, ++ .c14 = -0x1.ca980345c4204p-7, .c15 = 0x1.dc050eafde0b3p-8, ++ .c16 = -0x1.7ea70755b8eccp-9, .c17 = 0x1.ba3da3de903e8p-11, ++ .c18 = -0x1.44a4b059b6f67p-13, .c19 = 0x1.c4a45029e5a91p-17, ++ .shift_val = 0x1.490fdaa22168cp+1, .neg_one = -1, + }; + + /* Useful constants. */ +@@ -43,15 +46,14 @@ static const struct data + /* Fast implementation of SVE atan. + Based on atan(x) ~ shift + z + z^3 * P(z^2) with reduction to [0,1] using + z=1/x and shift = pi/2. Largest errors are close to 1. The maximum observed +- error is 2.27 ulps: +- _ZGVsMxv_atan (0x1.0005af27c23e9p+0) got 0x1.9225645bdd7c1p-1 +- want 0x1.9225645bdd7c3p-1. */ ++ error is 2.08 ulps: ++ _ZGVsMxv_atan (0x1.000a7c56975e8p+0) got 0x1.922a3163e15c2p-1 ++ want 0x1.922a3163e15c4p-1. */ + svfloat64_t SV_NAME_D1 (atan) (svfloat64_t x, const svbool_t pg) + { + const struct data *d = ptr_barrier (&data); + +- /* No need to trigger special case. Small cases, infs and nans +- are supported by our approximation technique. */ ++ svbool_t ptrue = svptrue_b64 (); + svuint64_t ix = svreinterpret_u64 (x); + svuint64_t sign = svand_x (pg, ix, SignMask); + +@@ -59,32 +61,60 @@ svfloat64_t SV_NAME_D1 (atan) (svfloat64_t x, const svbool_t pg) + y := arctan(x) for x < 1 + y := pi/2 + arctan(-1/x) for x > 1 + Hence, use z=-1/a if x>=1, otherwise z=a. */ +- svbool_t red = svacgt (pg, x, 1.0); +- /* Avoid dependency in abs(x) in division (and comparison). */ +- svfloat64_t z = svsel (red, svdivr_x (pg, x, 1.0), x); +- /* Use absolute value only when needed (odd powers of z). */ +- svfloat64_t az = svabs_x (pg, z); +- az = svneg_m (az, red, az); ++ svbool_t red = svacgt (pg, x, d->neg_one); ++ svfloat64_t z = svsel (red, svdiv_x (pg, sv_f64 (d->neg_one), x), x); ++ ++ /* Reuse of -1.0f to reduce constant loads, ++ We need a shift value of 1/2, which is created via -1 + (1 + 1/2). */ ++ svfloat64_t shift ++ = svadd_z (red, sv_f64 (d->neg_one), sv_f64 (d->shift_val)); ++ ++ /* Reinserts the sign bit of the argument to handle the case of x < -1. */ ++ shift = svreinterpret_f64 (sveor_x (pg, svreinterpret_u64 (shift), sign)); + + /* Use split Estrin scheme for P(z^2) with deg(P)=19. */ +- svfloat64_t z2 = svmul_x (pg, z, z); +- svfloat64_t x2 = svmul_x (pg, z2, z2); +- svfloat64_t x4 = svmul_x (pg, x2, x2); +- svfloat64_t x8 = svmul_x (pg, x4, x4); ++ svfloat64_t z2 = svmul_x (ptrue, z, z); ++ svfloat64_t z4 = svmul_x (ptrue, z2, z2); ++ svfloat64_t z8 = svmul_x (ptrue, z4, z4); ++ svfloat64_t z16 = svmul_x (ptrue, z8, z8); + +- svfloat64_t y +- = svmla_x (pg, sv_estrin_7_f64_x (pg, z2, x2, x4, d->poly), +- sv_estrin_11_f64_x (pg, z2, x2, x4, x8, d->poly + 8), x8); ++ /* Order-7 Estrin. */ ++ svfloat64_t c13 = svld1rq (ptrue, &d->c1); ++ svfloat64_t c57 = svld1rq (ptrue, &d->c5); + +- /* y = shift + z + z^3 * P(z^2). */ +- svfloat64_t z3 = svmul_x (pg, z2, az); +- y = svmla_x (pg, az, z3, y); ++ svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), z2, c13, 0); ++ svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), z2, c13, 1); ++ svfloat64_t p45 = svmla_lane (sv_f64 (d->c4), z2, c57, 0); ++ svfloat64_t p67 = svmla_lane (sv_f64 (d->c6), z2, c57, 1); ++ ++ svfloat64_t p03 = svmla_x (pg, p01, z4, p23); ++ svfloat64_t p47 = svmla_x (pg, p45, z4, p67); ++ svfloat64_t p07 = svmla_x (pg, p03, z8, p47); ++ ++ /* Order-11 Estrin. */ ++ svfloat64_t c911 = svld1rq (ptrue, &d->c9); ++ svfloat64_t c1315 = svld1rq (ptrue, &d->c13); ++ svfloat64_t c1719 = svld1rq (ptrue, &d->c17); + +- /* Apply shift as indicated by `red` predicate. */ +- y = svadd_m (red, y, d->pi_over_2); ++ svfloat64_t p89 = svmla_lane (sv_f64 (d->c8), z2, c911, 0); ++ svfloat64_t p1011 = svmla_lane (sv_f64 (d->c10), z2, c911, 1); ++ svfloat64_t p811 = svmla_x (pg, p89, z4, p1011); + +- /* y = atan(x) if x>0, -atan(-x) otherwise. */ +- y = svreinterpret_f64 (sveor_x (pg, svreinterpret_u64 (y), sign)); ++ svfloat64_t p1213 = svmla_lane (sv_f64 (d->c12), z2, c1315, 0); ++ svfloat64_t p1415 = svmla_lane (sv_f64 (d->c14), z2, c1315, 1); ++ svfloat64_t p1215 = svmla_x (pg, p1213, z4, p1415); + +- return y; ++ svfloat64_t p1617 = svmla_lane (sv_f64 (d->c16), z2, c1719, 0); ++ svfloat64_t p1819 = svmla_lane (sv_f64 (d->c18), z2, c1719, 1); ++ svfloat64_t p1619 = svmla_x (pg, p1617, z4, p1819); ++ ++ svfloat64_t p815 = svmla_x (pg, p811, z8, p1215); ++ svfloat64_t p819 = svmla_x (pg, p815, z16, p1619); ++ ++ svfloat64_t y = svmla_x (pg, p07, z16, p819); ++ ++ /* y = shift + z + z^3 * P(z^2). */ ++ shift = svadd_m (red, z, shift); ++ y = svmul_x (pg, z2, y); ++ return svmla_x (pg, shift, z, y); + } +diff --git a/sysdeps/aarch64/fpu/atanf_advsimd.c b/sysdeps/aarch64/fpu/atanf_advsimd.c +index d015cc70ad..e72074ec62 100644 +--- a/sysdeps/aarch64/fpu/atanf_advsimd.c ++++ b/sysdeps/aarch64/fpu/atanf_advsimd.c +@@ -22,26 +22,35 @@ + + static const struct data + { ++ uint32x4_t sign_mask, pi_over_2; ++ float32x4_t neg_one; ++#if WANT_SIMD_EXCEPT + float32x4_t poly[8]; +- float32x4_t pi_over_2; ++} data = { ++ .poly = { V4 (-0x1.5554dcp-2), V4 (0x1.9978ecp-3), V4 (-0x1.230a94p-3), ++ V4 (0x1.b4debp-4), V4 (-0x1.3550dap-4), V4 (0x1.61eebp-5), ++ V4 (-0x1.0c17d4p-6), V4 (0x1.7ea694p-9) }, ++#else ++ float32x4_t c0, c2, c4, c6; ++ float c1, c3, c5, c7; + } data = { + /* Coefficients of polynomial P such that atan(x)~x+x*P(x^2) on + [2**-128, 1.0]. + Generated using fpminimax between FLT_MIN and 1. */ +- .poly = { V4 (-0x1.55555p-2f), V4 (0x1.99935ep-3f), V4 (-0x1.24051ep-3f), +- V4 (0x1.bd7368p-4f), V4 (-0x1.491f0ep-4f), V4 (0x1.93a2c0p-5f), +- V4 (-0x1.4c3c60p-6f), V4 (0x1.01fd88p-8f) }, +- .pi_over_2 = V4 (0x1.921fb6p+0f), ++ .c0 = V4 (-0x1.5554dcp-2), .c1 = 0x1.9978ecp-3, ++ .c2 = V4 (-0x1.230a94p-3), .c3 = 0x1.b4debp-4, ++ .c4 = V4 (-0x1.3550dap-4), .c5 = 0x1.61eebp-5, ++ .c6 = V4 (-0x1.0c17d4p-6), .c7 = 0x1.7ea694p-9, ++#endif ++ .pi_over_2 = V4 (0x3fc90fdb), ++ .neg_one = V4 (-1.0f), ++ .sign_mask = V4 (0x80000000), + }; + +-#define SignMask v_u32 (0x80000000) +- +-#define P(i) d->poly[i] +- ++#if WANT_SIMD_EXCEPT + #define TinyBound 0x30800000 /* asuint(0x1p-30). */ + #define BigBound 0x4e800000 /* asuint(0x1p30). */ + +-#if WANT_SIMD_EXCEPT + static float32x4_t VPCS_ATTR NOINLINE + special_case (float32x4_t x, float32x4_t y, uint32x4_t special) + { +@@ -51,19 +60,20 @@ special_case (float32x4_t x, float32x4_t y, uint32x4_t special) + + /* Fast implementation of vector atanf based on + atan(x) ~ shift + z + z^3 * P(z^2) with reduction to [0,1] +- using z=-1/x and shift = pi/2. Maximum observed error is 2.9ulps: +- _ZGVnN4v_atanf (0x1.0468f6p+0) got 0x1.967f06p-1 want 0x1.967fp-1. */ ++ using z=-1/x and shift = pi/2. Maximum observed error is 2.02 ulps: ++ _ZGVnN4v_atanf (0x1.03d4cep+0) got 0x1.95ed3ap-1 ++ want 0x1.95ed36p-1. */ + float32x4_t VPCS_ATTR NOINLINE V_NAME_F1 (atan) (float32x4_t x) + { + const struct data *d = ptr_barrier (&data); + +- /* Small cases, infs and nans are supported by our approximation technique, +- but do not set fenv flags correctly. Only trigger special case if we need +- fenv. */ + uint32x4_t ix = vreinterpretq_u32_f32 (x); +- uint32x4_t sign = vandq_u32 (ix, SignMask); ++ uint32x4_t sign = vandq_u32 (ix, d->sign_mask); + + #if WANT_SIMD_EXCEPT ++ /* Small cases, infs and nans are supported by our approximation technique, ++ but do not set fenv flags correctly. Only trigger special case if we need ++ fenv. */ + uint32x4_t ia = vandq_u32 (ix, v_u32 (0x7ff00000)); + uint32x4_t special = vcgtq_u32 (vsubq_u32 (ia, v_u32 (TinyBound)), + v_u32 (BigBound - TinyBound)); +@@ -71,41 +81,52 @@ float32x4_t VPCS_ATTR NOINLINE V_NAME_F1 (atan) (float32x4_t x) + if (__glibc_unlikely (v_any_u32 (special))) + return special_case (x, x, v_u32 (-1)); + #endif +- + /* Argument reduction: +- y := arctan(x) for x < 1 +- y := pi/2 + arctan(-1/x) for x > 1 +- Hence, use z=-1/a if x>=1, otherwise z=a. */ +- uint32x4_t red = vcagtq_f32 (x, v_f32 (1.0)); +- /* Avoid dependency in abs(x) in division (and comparison). */ +- float32x4_t z = vbslq_f32 (red, vdivq_f32 (v_f32 (1.0f), x), x); ++ y := arctan(x) for |x| < 1 ++ y := arctan(-1/x) + pi/2 for x > +1 ++ y := arctan(-1/x) - pi/2 for x < -1 ++ Hence, use z=-1/a if x>=|-1|, otherwise z=a. */ ++ uint32x4_t red = vcagtq_f32 (x, d->neg_one); ++ ++ float32x4_t z = vbslq_f32 (red, vdivq_f32 (d->neg_one, x), x); ++ ++ /* Shift is calculated as +-pi/2 or 0, depending on the argument case. */ + float32x4_t shift = vreinterpretq_f32_u32 ( +- vandq_u32 (red, vreinterpretq_u32_f32 (d->pi_over_2))); +- /* Use absolute value only when needed (odd powers of z). */ +- float32x4_t az = vbslq_f32 ( +- SignMask, vreinterpretq_f32_u32 (vandq_u32 (SignMask, red)), z); ++ vandq_u32 (red, veorq_u32 (d->pi_over_2, sign))); ++ ++ float32x4_t z2 = vmulq_f32 (z, z); ++ float32x4_t z3 = vmulq_f32 (z, z2); ++ float32x4_t z4 = vmulq_f32 (z2, z2); ++#if WANT_SIMD_EXCEPT + + /* Calculate the polynomial approximation. + Use 2-level Estrin scheme for P(z^2) with deg(P)=7. However, + a standard implementation using z8 creates spurious underflow + in the very last fma (when z^8 is small enough). +- Therefore, we split the last fma into a mul and an fma. +- Horner and single-level Estrin have higher errors that exceed +- threshold. */ +- float32x4_t z2 = vmulq_f32 (z, z); +- float32x4_t z4 = vmulq_f32 (z2, z2); +- ++ Therefore, we split the last fma into a mul and an fma. */ + float32x4_t y = vfmaq_f32 ( + v_pairwise_poly_3_f32 (z2, z4, d->poly), z4, + vmulq_f32 (z4, v_pairwise_poly_3_f32 (z2, z4, d->poly + 4))); + +- /* y = shift + z * P(z^2). */ +- y = vaddq_f32 (vfmaq_f32 (az, y, vmulq_f32 (z2, az)), shift); ++#else ++ float32x4_t z8 = vmulq_f32 (z4, z4); ++ ++ /* Uses an Estrin scheme for polynomial approximation. */ ++ float32x4_t odd_coeffs = vld1q_f32 (&d->c1); ++ ++ float32x4_t p01 = vfmaq_laneq_f32 (d->c0, z2, odd_coeffs, 0); ++ float32x4_t p23 = vfmaq_laneq_f32 (d->c2, z2, odd_coeffs, 1); ++ float32x4_t p45 = vfmaq_laneq_f32 (d->c4, z2, odd_coeffs, 2); ++ float32x4_t p67 = vfmaq_laneq_f32 (d->c6, z2, odd_coeffs, 3); + +- /* y = atan(x) if x>0, -atan(-x) otherwise. */ +- y = vreinterpretq_f32_u32 (veorq_u32 (vreinterpretq_u32_f32 (y), sign)); ++ float32x4_t p03 = vfmaq_f32 (p01, z4, p23); ++ float32x4_t p47 = vfmaq_f32 (p45, z4, p67); + +- return y; ++ float32x4_t y = vfmaq_f32 (p03, z8, p47); ++#endif ++ ++ /* y = shift + z * P(z^2). */ ++ return vfmaq_f32 (vaddq_f32 (shift, z), z3, y); + } + libmvec_hidden_def (V_NAME_F1 (atan)) + HALF_WIDTH_ALIAS_F1 (atan) +diff --git a/sysdeps/aarch64/fpu/atanf_sve.c b/sysdeps/aarch64/fpu/atanf_sve.c +index 7b54094586..0f5a054743 100644 +--- a/sysdeps/aarch64/fpu/atanf_sve.c ++++ b/sysdeps/aarch64/fpu/atanf_sve.c +@@ -18,18 +18,26 @@ + . */ + + #include "sv_math.h" +-#include "poly_sve_f32.h" + + static const struct data + { +- float32_t poly[8]; +- float32_t pi_over_2; ++ float32_t c1, c3, c5, c7; ++ float32_t c0, c2, c4, c6; ++ float32_t shift_val, neg_one; + } data = { + /* Coefficients of polynomial P such that atan(x)~x+x*P(x^2) on + [2**-128, 1.0]. */ +- .poly = { -0x1.55555p-2f, 0x1.99935ep-3f, -0x1.24051ep-3f, 0x1.bd7368p-4f, +- -0x1.491f0ep-4f, 0x1.93a2c0p-5f, -0x1.4c3c60p-6f, 0x1.01fd88p-8f }, +- .pi_over_2 = 0x1.921fb6p+0f, ++ .c0 = -0x1.5554dcp-2, ++ .c1 = 0x1.9978ecp-3, ++ .c2 = -0x1.230a94p-3, ++ .c3 = 0x1.b4debp-4, ++ .c4 = -0x1.3550dap-4, ++ .c5 = 0x1.61eebp-5, ++ .c6 = -0x1.0c17d4p-6, ++ .c7 = 0x1.7ea694p-9, ++ /* pi/2, used as a shift value after reduction. */ ++ .shift_val = 0x1.921fb54442d18p+0, ++ .neg_one = -1.0f, + }; + + #define SignMask (0x80000000) +@@ -37,43 +45,49 @@ static const struct data + /* Fast implementation of SVE atanf based on + atan(x) ~ shift + z + z^3 * P(z^2) with reduction to [0,1] using + z=-1/x and shift = pi/2. +- Largest observed error is 2.9 ULP, close to +/-1.0: +- _ZGVsMxv_atanf (0x1.0468f6p+0) got -0x1.967f06p-1 +- want -0x1.967fp-1. */ ++ Largest observed error is 2.12 ULP: ++ _ZGVsMxv_atanf (0x1.03d4cep+0) got 0x1.95ed3ap-1 ++ want 0x1.95ed36p-1. */ + svfloat32_t SV_NAME_F1 (atan) (svfloat32_t x, const svbool_t pg) + { + const struct data *d = ptr_barrier (&data); ++ svbool_t ptrue = svptrue_b32 (); + + /* No need to trigger special case. Small cases, infs and nans + are supported by our approximation technique. */ + svuint32_t ix = svreinterpret_u32 (x); +- svuint32_t sign = svand_x (pg, ix, SignMask); ++ svuint32_t sign = svand_x (ptrue, ix, SignMask); + + /* Argument reduction: + y := arctan(x) for x < 1 +- y := pi/2 + arctan(-1/x) for x > 1 +- Hence, use z=-1/a if x>=1, otherwise z=a. */ +- svbool_t red = svacgt (pg, x, 1.0f); +- /* Avoid dependency in abs(x) in division (and comparison). */ +- svfloat32_t z = svsel (red, svdiv_x (pg, sv_f32 (1.0f), x), x); +- /* Use absolute value only when needed (odd powers of z). */ +- svfloat32_t az = svabs_x (pg, z); +- az = svneg_m (az, red, az); +- +- /* Use split Estrin scheme for P(z^2) with deg(P)=7. */ +- svfloat32_t z2 = svmul_x (pg, z, z); +- svfloat32_t z4 = svmul_x (pg, z2, z2); +- svfloat32_t z8 = svmul_x (pg, z4, z4); +- +- svfloat32_t y = sv_estrin_7_f32_x (pg, z2, z4, z8, d->poly); +- +- /* y = shift + z + z^3 * P(z^2). */ +- svfloat32_t z3 = svmul_x (pg, z2, az); +- y = svmla_x (pg, az, z3, y); +- +- /* Apply shift as indicated by 'red' predicate. */ +- y = svadd_m (red, y, sv_f32 (d->pi_over_2)); +- +- /* y = atan(x) if x>0, -atan(-x) otherwise. */ +- return svreinterpret_f32 (sveor_x (pg, svreinterpret_u32 (y), sign)); ++ y := arctan(-1/x) + pi/2 for x > +1 ++ y := arctan(-1/x) - pi/2 for x < -1 ++ Hence, use z=-1/a if |x|>=|-1|, otherwise z=a. */ ++ svbool_t red = svacgt (pg, x, d->neg_one); ++ svfloat32_t z = svsel (red, svdiv_x (pg, sv_f32 (d->neg_one), x), x); ++ ++ /* Reinserts the sign bit of the argument to handle the case of x < -1. */ ++ svfloat32_t shift = svreinterpret_f32 ( ++ sveor_x (red, svreinterpret_u32 (sv_f32 (d->shift_val)), sign)); ++ ++ svfloat32_t z2 = svmul_x (ptrue, z, z); ++ svfloat32_t z3 = svmul_x (ptrue, z2, z); ++ svfloat32_t z4 = svmul_x (ptrue, z2, z2); ++ svfloat32_t z8 = svmul_x (ptrue, z4, z4); ++ ++ svfloat32_t odd_coeffs = svld1rq (ptrue, &d->c1); ++ ++ svfloat32_t p01 = svmla_lane (sv_f32 (d->c0), z2, odd_coeffs, 0); ++ svfloat32_t p23 = svmla_lane (sv_f32 (d->c2), z2, odd_coeffs, 1); ++ svfloat32_t p45 = svmla_lane (sv_f32 (d->c4), z2, odd_coeffs, 2); ++ svfloat32_t p67 = svmla_lane (sv_f32 (d->c6), z2, odd_coeffs, 3); ++ ++ svfloat32_t p03 = svmla_x (pg, p01, z4, p23); ++ svfloat32_t p47 = svmla_x (pg, p45, z4, p67); ++ ++ svfloat32_t y = svmla_x (pg, p03, z8, p47); ++ ++ /* shift + z + z^3 * P(z^2). */ ++ shift = svadd_m (red, z, shift); ++ return svmla_x (pg, shift, z3, y); + } + +commit 0348dd8b981ef892accd4a2a7c43e179eee91480 +Author: Luna Lamb +Date: Thu May 29 15:22:51 2025 +0000 + + AArch64: Improve codegen in SVE log1p + + Improves memory access, reformat evaluation scheme to pack coefficients. + 5% improvement in throughput microbenchmark on Neoverse V1. + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit da196e6134ede64728006518352d75b6c3902fec) + +diff --git a/sysdeps/aarch64/fpu/log1p_sve.c b/sysdeps/aarch64/fpu/log1p_sve.c +index 04f7e5720e..5251f3c075 100644 +--- a/sysdeps/aarch64/fpu/log1p_sve.c ++++ b/sysdeps/aarch64/fpu/log1p_sve.c +@@ -22,19 +22,33 @@ + + static const struct data + { +- double poly[19]; ++ float64_t c0, c2, c4, c6, c8, c10, c12, c14, c16; ++ float64_t c1, c3, c5, c7, c9, c11, c13, c15, c17, c18; + double ln2_hi, ln2_lo; + uint64_t hfrt2_top, onemhfrt2_top, inf, mone; + } data = { + /* Generated using Remez in [ sqrt(2)/2 - 1, sqrt(2) - 1]. Order 20 +- polynomial, however first 2 coefficients are 0 and 1 so are not stored. */ +- .poly = { -0x1.ffffffffffffbp-2, 0x1.55555555551a9p-2, -0x1.00000000008e3p-2, +- 0x1.9999999a32797p-3, -0x1.555555552fecfp-3, 0x1.249248e071e5ap-3, +- -0x1.ffffff8bf8482p-4, 0x1.c71c8f07da57ap-4, -0x1.9999ca4ccb617p-4, +- 0x1.7459ad2e1dfa3p-4, -0x1.554d2680a3ff2p-4, 0x1.3b4c54d487455p-4, +- -0x1.2548a9ffe80e6p-4, 0x1.0f389a24b2e07p-4, -0x1.eee4db15db335p-5, +- 0x1.e95b494d4a5ddp-5, -0x1.15fdf07cb7c73p-4, 0x1.0310b70800fcfp-4, +- -0x1.cfa7385bdb37ep-6, }, ++ polynomial, however first 2 coefficients are 0 and 1 so are not ++ stored. */ ++ .c0 = -0x1.ffffffffffffbp-2, ++ .c1 = 0x1.55555555551a9p-2, ++ .c2 = -0x1.00000000008e3p-2, ++ .c3 = 0x1.9999999a32797p-3, ++ .c4 = -0x1.555555552fecfp-3, ++ .c5 = 0x1.249248e071e5ap-3, ++ .c6 = -0x1.ffffff8bf8482p-4, ++ .c7 = 0x1.c71c8f07da57ap-4, ++ .c8 = -0x1.9999ca4ccb617p-4, ++ .c9 = 0x1.7459ad2e1dfa3p-4, ++ .c10 = -0x1.554d2680a3ff2p-4, ++ .c11 = 0x1.3b4c54d487455p-4, ++ .c12 = -0x1.2548a9ffe80e6p-4, ++ .c13 = 0x1.0f389a24b2e07p-4, ++ .c14 = -0x1.eee4db15db335p-5, ++ .c15 = 0x1.e95b494d4a5ddp-5, ++ .c16 = -0x1.15fdf07cb7c73p-4, ++ .c17 = 0x1.0310b70800fcfp-4, ++ .c18 = -0x1.cfa7385bdb37ep-6, + .ln2_hi = 0x1.62e42fefa3800p-1, + .ln2_lo = 0x1.ef35793c76730p-45, + /* top32(asuint64(sqrt(2)/2)) << 32. */ +@@ -49,7 +63,7 @@ static const struct data + #define BottomMask 0xffffffff + + static svfloat64_t NOINLINE +-special_case (svbool_t special, svfloat64_t x, svfloat64_t y) ++special_case (svfloat64_t x, svfloat64_t y, svbool_t special) + { + return sv_call_f64 (log1p, x, y, special); + } +@@ -91,8 +105,9 @@ svfloat64_t SV_NAME_D1 (log1p) (svfloat64_t x, svbool_t pg) + /* Reduce x to f in [sqrt(2)/2, sqrt(2)]. */ + svuint64_t utop + = svadd_x (pg, svand_x (pg, u, 0x000fffff00000000), d->hfrt2_top); +- svuint64_t u_red = svorr_x (pg, utop, svand_x (pg, mi, BottomMask)); +- svfloat64_t f = svsub_x (pg, svreinterpret_f64 (u_red), 1); ++ svuint64_t u_red ++ = svorr_x (pg, utop, svand_x (svptrue_b64 (), mi, BottomMask)); ++ svfloat64_t f = svsub_x (svptrue_b64 (), svreinterpret_f64 (u_red), 1); + + /* Correction term c/m. */ + svfloat64_t cm = svdiv_x (pg, svsub_x (pg, x, svsub_x (pg, m, 1)), m); +@@ -103,16 +118,47 @@ svfloat64_t SV_NAME_D1 (log1p) (svfloat64_t x, svbool_t pg) + Hence approximation has the form f + f^2 * P(f) + where P(x) = C0 + C1*x + C2x^2 + ... + Assembling this all correctly is dealt with at the final step. */ +- svfloat64_t f2 = svmul_x (pg, f, f), f4 = svmul_x (pg, f2, f2), +- f8 = svmul_x (pg, f4, f4), f16 = svmul_x (pg, f8, f8); +- svfloat64_t p = sv_estrin_18_f64_x (pg, f, f2, f4, f8, f16, d->poly); ++ svfloat64_t f2 = svmul_x (svptrue_b64 (), f, f), ++ f4 = svmul_x (svptrue_b64 (), f2, f2), ++ f8 = svmul_x (svptrue_b64 (), f4, f4), ++ f16 = svmul_x (svptrue_b64 (), f8, f8); ++ ++ svfloat64_t c13 = svld1rq (svptrue_b64 (), &d->c1); ++ svfloat64_t c57 = svld1rq (svptrue_b64 (), &d->c5); ++ svfloat64_t c911 = svld1rq (svptrue_b64 (), &d->c9); ++ svfloat64_t c1315 = svld1rq (svptrue_b64 (), &d->c13); ++ svfloat64_t c1718 = svld1rq (svptrue_b64 (), &d->c17); ++ ++ /* Order-18 Estrin scheme. */ ++ svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), f, c13, 0); ++ svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), f, c13, 1); ++ svfloat64_t p45 = svmla_lane (sv_f64 (d->c4), f, c57, 0); ++ svfloat64_t p67 = svmla_lane (sv_f64 (d->c6), f, c57, 1); ++ ++ svfloat64_t p03 = svmla_x (pg, p01, f2, p23); ++ svfloat64_t p47 = svmla_x (pg, p45, f2, p67); ++ svfloat64_t p07 = svmla_x (pg, p03, f4, p47); ++ ++ svfloat64_t p89 = svmla_lane (sv_f64 (d->c8), f, c911, 0); ++ svfloat64_t p1011 = svmla_lane (sv_f64 (d->c10), f, c911, 1); ++ svfloat64_t p1213 = svmla_lane (sv_f64 (d->c12), f, c1315, 0); ++ svfloat64_t p1415 = svmla_lane (sv_f64 (d->c14), f, c1315, 1); ++ ++ svfloat64_t p811 = svmla_x (pg, p89, f2, p1011); ++ svfloat64_t p1215 = svmla_x (pg, p1213, f2, p1415); ++ svfloat64_t p815 = svmla_x (pg, p811, f4, p1215); ++ ++ svfloat64_t p015 = svmla_x (pg, p07, f8, p815); ++ svfloat64_t p1617 = svmla_lane (sv_f64 (d->c16), f, c1718, 0); ++ svfloat64_t p1618 = svmla_lane (p1617, f2, c1718, 1); ++ svfloat64_t p = svmla_x (pg, p015, f16, p1618); + + svfloat64_t ylo = svmla_x (pg, cm, k, d->ln2_lo); + svfloat64_t yhi = svmla_x (pg, f, k, d->ln2_hi); +- svfloat64_t y = svmla_x (pg, svadd_x (pg, ylo, yhi), f2, p); + + if (__glibc_unlikely (svptest_any (pg, special))) +- return special_case (special, x, y); +- +- return y; ++ return special_case ( ++ x, svmla_x (svptrue_b64 (), svadd_x (svptrue_b64 (), ylo, yhi), f2, p), ++ special); ++ return svmla_x (svptrue_b64 (), svadd_x (svptrue_b64 (), ylo, yhi), f2, p); + } + +commit 01ab94cf780dffce9529374229bd2d80baf9c26f +Author: Dylan Fleming +Date: Wed Jun 18 16:17:12 2025 +0000 + + AArch64: Optimize SVE exp functions + + Improve performance of SVE exps by making better use + of the SVE FEXPA instruction. + + Performance improvement on Neoverse V1: + exp2_sve: 21% + exp2f_sve: 24% + exp10f_sve: 23% + expm1_sve: 25% + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit 1e3d1ddf977ecd653de8d0d10eb083d80ac21cf3) + +diff --git a/sysdeps/aarch64/fpu/exp10f_sve.c b/sysdeps/aarch64/fpu/exp10f_sve.c +index 8aa3fa9c43..0a4c264506 100644 +--- a/sysdeps/aarch64/fpu/exp10f_sve.c ++++ b/sysdeps/aarch64/fpu/exp10f_sve.c +@@ -19,26 +19,19 @@ + + #include "sv_math.h" + +-/* For x < -Thres, the result is subnormal and not handled correctly by +- FEXPA. */ +-#define Thres 37.9 ++/* For x < -Thres (-log10(2^126)), the result is subnormal and not handled ++ correctly by FEXPA. */ ++#define Thres 0x1.2f702p+5 + + static const struct data + { +- float log2_10_lo, c0, c2, c4; +- float c1, c3, log10_2; +- float shift, log2_10_hi, thres; ++ float log10_2, log2_10_hi, log2_10_lo, c1; ++ float c0, shift, thres; + } data = { + /* Coefficients generated using Remez algorithm with minimisation of relative +- error. +- rel error: 0x1.89dafa3p-24 +- abs error: 0x1.167d55p-23 in [-log10(2)/2, log10(2)/2] +- maxerr: 0.52 +0.5 ulp. */ +- .c0 = 0x1.26bb16p+1f, +- .c1 = 0x1.5350d2p+1f, +- .c2 = 0x1.04744ap+1f, +- .c3 = 0x1.2d8176p+0f, +- .c4 = 0x1.12b41ap-1f, ++ error. */ ++ .c0 = 0x1.26bb62p1, ++ .c1 = 0x1.53524cp1, + /* 1.5*2^17 + 127, a shift value suitable for FEXPA. */ + .shift = 0x1.803f8p17f, + .log10_2 = 0x1.a934fp+1, +@@ -53,28 +46,23 @@ sv_exp10f_inline (svfloat32_t x, const svbool_t pg, const struct data *d) + /* exp10(x) = 2^(n/N) * 10^r = 2^n * (1 + poly (r)), + with poly(r) in [1/sqrt(2), sqrt(2)] and + x = r + n * log10(2) / N, with r in [-log10(2)/2N, log10(2)/2N]. */ +- +- svfloat32_t lane_consts = svld1rq (svptrue_b32 (), &d->log2_10_lo); ++ svfloat32_t lane_consts = svld1rq (svptrue_b32 (), &d->log10_2); + + /* n = round(x/(log10(2)/N)). */ + svfloat32_t shift = sv_f32 (d->shift); +- svfloat32_t z = svmad_x (pg, sv_f32 (d->log10_2), x, shift); +- svfloat32_t n = svsub_x (svptrue_b32 (), z, shift); ++ svfloat32_t z = svmla_lane (shift, x, lane_consts, 0); ++ svfloat32_t n = svsub_x (pg, z, shift); + + /* r = x - n*log10(2)/N. */ +- svfloat32_t r = svmsb_x (pg, sv_f32 (d->log2_10_hi), n, x); +- r = svmls_lane (r, n, lane_consts, 0); ++ svfloat32_t r = x; ++ r = svmls_lane (r, n, lane_consts, 1); ++ r = svmls_lane (r, n, lane_consts, 2); + + svfloat32_t scale = svexpa (svreinterpret_u32 (z)); + + /* Polynomial evaluation: poly(r) ~ exp10(r)-1. */ +- svfloat32_t p12 = svmla_lane (sv_f32 (d->c1), r, lane_consts, 2); +- svfloat32_t p34 = svmla_lane (sv_f32 (d->c3), r, lane_consts, 3); +- svfloat32_t r2 = svmul_x (svptrue_b32 (), r, r); +- svfloat32_t p14 = svmla_x (pg, p12, p34, r2); +- svfloat32_t p0 = svmul_lane (r, lane_consts, 1); +- svfloat32_t poly = svmla_x (pg, p0, r2, p14); +- ++ svfloat32_t poly = svmla_lane (sv_f32 (d->c0), r, lane_consts, 3); ++ poly = svmul_x (pg, poly, r); + return svmla_x (pg, scale, scale, poly); + } + +@@ -85,11 +73,10 @@ special_case (svfloat32_t x, svbool_t special, const struct data *d) + special); + } + +-/* Single-precision SVE exp10f routine. Implements the same algorithm +- as AdvSIMD exp10f. +- Worst case error is 1.02 ULPs. +- _ZGVsMxv_exp10f(-0x1.040488p-4) got 0x1.ba5f9ep-1 +- want 0x1.ba5f9cp-1. */ ++/* Single-precision SVE exp10f routine. Based on the FEXPA instruction. ++ Worst case error is 1.10 ULP. ++ _ZGVsMxv_exp10f (0x1.cc76dep+3) got 0x1.be0172p+47 ++ want 0x1.be017p+47. */ + svfloat32_t SV_NAME_F1 (exp10) (svfloat32_t x, const svbool_t pg) + { + const struct data *d = ptr_barrier (&data); +diff --git a/sysdeps/aarch64/fpu/exp2_sve.c b/sysdeps/aarch64/fpu/exp2_sve.c +index 5dfb77cdbc..ed11423e45 100644 +--- a/sysdeps/aarch64/fpu/exp2_sve.c ++++ b/sysdeps/aarch64/fpu/exp2_sve.c +@@ -19,23 +19,21 @@ + + #include "sv_math.h" + +-#define N (1 << V_EXP_TABLE_BITS) +- + #define BigBound 1022 + #define UOFlowBound 1280 + + static const struct data + { +- double c0, c2; +- double c1, c3; ++ double c2, c4; ++ double c0, c1, c3; + double shift, big_bound, uoflow_bound; + } data = { + /* Coefficients are computed using Remez algorithm with + minimisation of the absolute error. */ +- .c0 = 0x1.62e42fefa3686p-1, .c1 = 0x1.ebfbdff82c241p-3, +- .c2 = 0x1.c6b09b16de99ap-5, .c3 = 0x1.3b2abf5571ad8p-7, +- .shift = 0x1.8p52 / N, .uoflow_bound = UOFlowBound, +- .big_bound = BigBound, ++ .c0 = 0x1.62e42fefa39efp-1, .c1 = 0x1.ebfbdff82a31bp-3, ++ .c2 = 0x1.c6b08d706c8a5p-5, .c3 = 0x1.3b2ad2ff7d2f3p-7, ++ .c4 = 0x1.5d8761184beb3p-10, .shift = 0x1.800000000ffc0p+46, ++ .uoflow_bound = UOFlowBound, .big_bound = BigBound, + }; + + #define SpecialOffset 0x6000000000000000 /* 0x1p513. */ +@@ -64,50 +62,52 @@ special_case (svbool_t pg, svfloat64_t s, svfloat64_t y, svfloat64_t n, + svadd_x (pg, svsub_x (pg, svreinterpret_u64 (s), SpecialBias2), b)); + + /* |n| > 1280 => 2^(n) overflows. */ +- svbool_t p_cmp = svacgt (pg, n, d->uoflow_bound); ++ svbool_t p_cmp = svacle (pg, n, d->uoflow_bound); + + svfloat64_t r1 = svmul_x (svptrue_b64 (), s1, s1); + svfloat64_t r2 = svmla_x (pg, s2, s2, y); + svfloat64_t r0 = svmul_x (svptrue_b64 (), r2, s1); + +- return svsel (p_cmp, r1, r0); ++ return svsel (p_cmp, r0, r1); + } + + /* Fast vector implementation of exp2. +- Maximum measured error is 1.65 ulp. +- _ZGVsMxv_exp2(-0x1.4c264ab5b559bp-6) got 0x1.f8db0d4df721fp-1 +- want 0x1.f8db0d4df721dp-1. */ ++ Maximum measured error is 0.52 + 0.5 ulp. ++ _ZGVsMxv_exp2 (0x1.3b72ad5b701bfp-1) got 0x1.8861641b49e08p+0 ++ want 0x1.8861641b49e07p+0. */ + svfloat64_t SV_NAME_D1 (exp2) (svfloat64_t x, svbool_t pg) + { + const struct data *d = ptr_barrier (&data); +- svbool_t no_big_scale = svacle (pg, x, d->big_bound); +- svbool_t special = svnot_z (pg, no_big_scale); +- +- /* Reduce x to k/N + r, where k is integer and r in [-1/2N, 1/2N]. */ +- svfloat64_t shift = sv_f64 (d->shift); +- svfloat64_t kd = svadd_x (pg, x, shift); +- svuint64_t ki = svreinterpret_u64 (kd); +- /* kd = k/N. */ +- kd = svsub_x (pg, kd, shift); +- svfloat64_t r = svsub_x (pg, x, kd); +- +- /* scale ~= 2^(k/N). */ +- svuint64_t idx = svand_x (pg, ki, N - 1); +- svuint64_t sbits = svld1_gather_index (pg, __v_exp_data, idx); +- /* This is only a valid scale when -1023*N < k < 1024*N. */ +- svuint64_t top = svlsl_x (pg, ki, 52 - V_EXP_TABLE_BITS); +- svfloat64_t scale = svreinterpret_f64 (svadd_x (pg, sbits, top)); +- +- svfloat64_t c13 = svld1rq (svptrue_b64 (), &d->c1); +- /* Approximate exp2(r) using polynomial. */ +- /* y = exp2(r) - 1 ~= C0 r + C1 r^2 + C2 r^3 + C3 r^4. */ ++ svbool_t special = svacge (pg, x, d->big_bound); ++ ++ svfloat64_t z = svadd_x (svptrue_b64 (), x, d->shift); ++ svfloat64_t n = svsub_x (svptrue_b64 (), z, d->shift); ++ svfloat64_t r = svsub_x (svptrue_b64 (), x, n); ++ ++ svfloat64_t scale = svexpa (svreinterpret_u64 (z)); ++ + svfloat64_t r2 = svmul_x (svptrue_b64 (), r, r); +- svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), r, c13, 0); +- svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), r, c13, 1); +- svfloat64_t p = svmla_x (pg, p01, p23, r2); ++ svfloat64_t c24 = svld1rq (svptrue_b64 (), &d->c2); ++ ++ /* Approximate exp2(r) using polynomial. */ ++ /* y = exp2(r) - 1 ~= r * (C0 + C1 r + C2 r^2 + C3 r^3 + C4 r^4). */ ++ svfloat64_t p12 = svmla_lane (sv_f64 (d->c1), r, c24, 0); ++ svfloat64_t p34 = svmla_lane (sv_f64 (d->c3), r, c24, 1); ++ svfloat64_t p = svmla_x (pg, p12, p34, r2); ++ p = svmad_x (pg, p, r, d->c0); + svfloat64_t y = svmul_x (svptrue_b64 (), r, p); ++ + /* Assemble exp2(x) = exp2(r) * scale. */ + if (__glibc_unlikely (svptest_any (pg, special))) +- return special_case (pg, scale, y, kd, d); ++ { ++ /* FEXPA zeroes the sign bit, however the sign is meaningful to the ++ special case function so needs to be copied. ++ e = sign bit of u << 46. */ ++ svuint64_t e = svand_x (pg, svlsl_x (pg, svreinterpret_u64 (z), 46), ++ 0x8000000000000000); ++ scale = svreinterpret_f64 (svadd_x (pg, e, svreinterpret_u64 (scale))); ++ return special_case (pg, scale, y, n, d); ++ } ++ + return svmla_x (pg, scale, scale, y); + } +diff --git a/sysdeps/aarch64/fpu/exp2f_sve.c b/sysdeps/aarch64/fpu/exp2f_sve.c +index c6216bed9e..cf01820288 100644 +--- a/sysdeps/aarch64/fpu/exp2f_sve.c ++++ b/sysdeps/aarch64/fpu/exp2f_sve.c +@@ -18,21 +18,17 @@ + . */ + + #include "sv_math.h" +-#include "poly_sve_f32.h" + + #define Thres 0x1.5d5e2ap+6f + + static const struct data + { +- float c0, c2, c4, c1, c3; +- float shift, thres; ++ float c0, c1, shift, thres; + } data = { +- /* Coefficients copied from the polynomial in AdvSIMD variant. */ +- .c0 = 0x1.62e422p-1f, +- .c1 = 0x1.ebf9bcp-3f, +- .c2 = 0x1.c6bd32p-5f, +- .c3 = 0x1.3ce9e4p-7f, +- .c4 = 0x1.59977ap-10f, ++ /* Coefficients generated using Remez algorithm with minimisation of relative ++ error. */ ++ .c0 = 0x1.62e485p-1, ++ .c1 = 0x1.ebfbe0p-3, + /* 1.5*2^17 + 127. */ + .shift = 0x1.803f8p17f, + /* Roughly 87.3. For x < -Thres, the result is subnormal and not handled +@@ -51,16 +47,8 @@ sv_exp2f_inline (svfloat32_t x, const svbool_t pg, const struct data *d) + + svfloat32_t scale = svexpa (svreinterpret_u32 (z)); + +- /* Polynomial evaluation: poly(r) ~ exp2(r)-1. +- Evaluate polynomial use hybrid scheme - offset ESTRIN by 1 for +- coefficients 1 to 4, and apply most significant coefficient directly. */ +- svfloat32_t even_coeffs = svld1rq (svptrue_b32 (), &d->c0); +- svfloat32_t r2 = svmul_x (svptrue_b32 (), r, r); +- svfloat32_t p12 = svmla_lane (sv_f32 (d->c1), r, even_coeffs, 1); +- svfloat32_t p34 = svmla_lane (sv_f32 (d->c3), r, even_coeffs, 2); +- svfloat32_t p14 = svmla_x (pg, p12, r2, p34); +- svfloat32_t p0 = svmul_lane (r, even_coeffs, 0); +- svfloat32_t poly = svmla_x (pg, p0, r2, p14); ++ svfloat32_t poly = svmla_x (pg, sv_f32 (d->c0), r, sv_f32 (d->c1)); ++ poly = svmul_x (svptrue_b32 (), poly, r); + + return svmla_x (pg, scale, scale, poly); + } +@@ -72,11 +60,10 @@ special_case (svfloat32_t x, svbool_t special, const struct data *d) + special); + } + +-/* Single-precision SVE exp2f routine. Implements the same algorithm +- as AdvSIMD exp2f. +- Worst case error is 1.04 ULPs. +- _ZGVsMxv_exp2f(-0x1.af994ap-3) got 0x1.ba6a66p-1 +- want 0x1.ba6a64p-1. */ ++/* Single-precision SVE exp2f routine, based on the FEXPA instruction. ++ Worst case error is 1.09 ULPs. ++ _ZGVsMxv_exp2f (0x1.9a2a94p-1) got 0x1.be1054p+0 ++ want 0x1.be1052p+0. */ + svfloat32_t SV_NAME_F1 (exp2) (svfloat32_t x, const svbool_t pg) + { + const struct data *d = ptr_barrier (&data); +diff --git a/sysdeps/aarch64/fpu/expm1_sve.c b/sysdeps/aarch64/fpu/expm1_sve.c +index c933cf9c0e..4c35e0341d 100644 +--- a/sysdeps/aarch64/fpu/expm1_sve.c ++++ b/sysdeps/aarch64/fpu/expm1_sve.c +@@ -18,82 +18,164 @@ + . */ + + #include "sv_math.h" +-#include "poly_sve_f64.h" + +-#define SpecialBound 0x1.62b7d369a5aa9p+9 +-#define ExponentBias 0x3ff0000000000000 ++#define FexpaBound 0x1.4cb5ecef28adap-3 /* 15*ln2/64. */ ++#define SpecialBound 0x1.628c2855bfaddp+9 /* ln(2^(1023 + 1/128)). */ + + static const struct data + { +- double poly[11]; +- double shift, inv_ln2, special_bound; +- /* To be loaded in one quad-word. */ ++ double c2, c4; ++ double inv_ln2; + double ln2_hi, ln2_lo; ++ double c0, c1, c3; ++ double shift, thres; ++ uint64_t expm1_data[32]; + } data = { +- /* Generated using fpminimax. */ +- .poly = { 0x1p-1, 0x1.5555555555559p-3, 0x1.555555555554bp-5, +- 0x1.111111110f663p-7, 0x1.6c16c16c1b5f3p-10, 0x1.a01a01affa35dp-13, +- 0x1.a01a018b4ecbbp-16, 0x1.71ddf82db5bb4p-19, 0x1.27e517fc0d54bp-22, +- 0x1.af5eedae67435p-26, 0x1.1f143d060a28ap-29, }, +- +- .special_bound = SpecialBound, +- .inv_ln2 = 0x1.71547652b82fep0, +- .ln2_hi = 0x1.62e42fefa39efp-1, +- .ln2_lo = 0x1.abc9e3b39803fp-56, +- .shift = 0x1.8p52, ++ /* Table emulating FEXPA - 1, for values of FEXPA close to 1. ++ The table holds values of 2^(i/64) - 1, computed in arbitrary precision. ++ The first half of the table stores values associated to i from 0 to 15. ++ The second half of the table stores values associated to i from 0 to -15. */ ++ .expm1_data = { ++ 0x0000000000000000, 0x3f864d1f3bc03077, 0x3f966c34c5615d0f, 0x3fa0e8a30eb37901, ++ 0x3fa6ab0d9f3121ec, 0x3fac7d865a7a3440, 0x3fb1301d0125b50a, 0x3fb429aaea92ddfb, ++ 0x3fb72b83c7d517ae, 0x3fba35beb6fcb754, 0x3fbd4873168b9aa8, 0x3fc031dc431466b2, ++ 0x3fc1c3d373ab11c3, 0x3fc35a2b2f13e6e9, 0x3fc4f4efa8fef709, 0x3fc6942d3720185a, ++ 0x0000000000000000, 0xbfc331751ec3a814, 0xbfc20224341286e4, 0xbfc0cf85bed0f8b7, ++ 0xbfbf332113d56b1f, 0xbfbcc0768d4175a6, 0xbfba46f918837cb7, 0xbfb7c695afc3b424, ++ 0xbfb53f391822dbc7, 0xbfb2b0cfe1266bd4, 0xbfb01b466423250a, 0xbfaafd11874c009e, ++ 0xbfa5b505d5b6f268, 0xbfa05e4119ea5d89, 0xbf95f134923757f3, 0xbf860f9f985bc9f4, ++ }, ++ ++ /* Generated using Remez, in [-log(2)/128, log(2)/128]. */ ++ .c0 = 0x1p-1, ++ .c1 = 0x1.55555555548f9p-3, ++ .c2 = 0x1.5555555554c22p-5, ++ .c3 = 0x1.111123aaa2fb2p-7, ++ .c4 = 0x1.6c16d77d98e5bp-10, ++ .ln2_hi = 0x1.62e42fefa3800p-1, ++ .ln2_lo = 0x1.ef35793c76730p-45, ++ .inv_ln2 = 0x1.71547652b82fep+0, ++ .shift = 0x1.800000000ffc0p+46, /* 1.5*2^46+1023. */ ++ .thres = SpecialBound, + }; + +-static svfloat64_t NOINLINE +-special_case (svfloat64_t x, svfloat64_t y, svbool_t pg) ++#define SpecialOffset 0x6000000000000000 /* 0x1p513. */ ++/* SpecialBias1 + SpecialBias1 = asuint(1.0). */ ++#define SpecialBias1 0x7000000000000000 /* 0x1p769. */ ++#define SpecialBias2 0x3010000000000000 /* 0x1p-254. */ ++ ++static NOINLINE svfloat64_t ++special_case (svbool_t pg, svfloat64_t y, svfloat64_t s, svfloat64_t p, ++ svfloat64_t n) + { +- return sv_call_f64 (expm1, x, y, pg); ++ /* s=2^n may overflow, break it up into s=s1*s2, ++ such that exp = s + s*y can be computed as s1*(s2+s2*y) ++ and s1*s1 overflows only if n>0. */ ++ ++ /* If n<=0 then set b to 0x6, 0 otherwise. */ ++ svbool_t p_sign = svcmple (pg, n, 0.0); /* n <= 0. */ ++ svuint64_t b ++ = svdup_u64_z (p_sign, SpecialOffset); /* Inactive lanes set to 0. */ ++ ++ /* Set s1 to generate overflow depending on sign of exponent n, ++ ie. s1 = 0x70...0 - b. */ ++ svfloat64_t s1 = svreinterpret_f64 (svsubr_x (pg, b, SpecialBias1)); ++ /* Offset s to avoid overflow in final result if n is below threshold. ++ ie. s2 = as_u64 (s) - 0x3010...0 + b. */ ++ svfloat64_t s2 = svreinterpret_f64 ( ++ svadd_x (pg, svsub_x (pg, svreinterpret_u64 (s), SpecialBias2), b)); ++ ++ /* |n| > 1280 => 2^(n) overflows. */ ++ svbool_t p_cmp = svacgt (pg, n, 1280.0); ++ ++ svfloat64_t r1 = svmul_x (svptrue_b64 (), s1, s1); ++ svfloat64_t r2 = svmla_x (pg, s2, s2, p); ++ svfloat64_t r0 = svmul_x (svptrue_b64 (), r2, s1); ++ ++ svbool_t is_safe = svacle (pg, n, 1023); /* Only correct special lanes. */ ++ return svsel (is_safe, y, svsub_x (pg, svsel (p_cmp, r1, r0), 1.0)); + } + +-/* Double-precision vector exp(x) - 1 function. +- The maximum error observed error is 2.18 ULP: +- _ZGVsMxv_expm1(0x1.634ba0c237d7bp-2) got 0x1.a8b9ea8d66e22p-2 +- want 0x1.a8b9ea8d66e2p-2. */ ++/* FEXPA based SVE expm1 algorithm. ++ Maximum measured error is 2.81 + 0.5 ULP: ++ _ZGVsMxv_expm1 (0x1.974060e619bfp-3) got 0x1.c290e5858bb53p-3 ++ want 0x1.c290e5858bb5p-3. */ + svfloat64_t SV_NAME_D1 (expm1) (svfloat64_t x, svbool_t pg) + { + const struct data *d = ptr_barrier (&data); + +- /* Large, Nan/Inf. */ +- svbool_t special = svnot_z (pg, svaclt (pg, x, d->special_bound)); +- +- /* Reduce argument to smaller range: +- Let i = round(x / ln2) +- and f = x - i * ln2, then f is in [-ln2/2, ln2/2]. +- exp(x) - 1 = 2^i * (expm1(f) + 1) - 1 +- where 2^i is exact because i is an integer. */ +- svfloat64_t shift = sv_f64 (d->shift); +- svfloat64_t n = svsub_x (pg, svmla_x (pg, shift, x, d->inv_ln2), shift); +- svint64_t i = svcvt_s64_x (pg, n); +- svfloat64_t ln2 = svld1rq (svptrue_b64 (), &d->ln2_hi); +- svfloat64_t f = svmls_lane (x, n, ln2, 0); +- f = svmls_lane (f, n, ln2, 1); +- +- /* Approximate expm1(f) using polynomial. +- Taylor expansion for expm1(x) has the form: +- x + ax^2 + bx^3 + cx^4 .... +- So we calculate the polynomial P(f) = a + bf + cf^2 + ... +- and assemble the approximation expm1(f) ~= f + f^2 * P(f). */ +- svfloat64_t f2 = svmul_x (pg, f, f); +- svfloat64_t f4 = svmul_x (pg, f2, f2); +- svfloat64_t f8 = svmul_x (pg, f4, f4); +- svfloat64_t p +- = svmla_x (pg, f, f2, sv_estrin_10_f64_x (pg, f, f2, f4, f8, d->poly)); +- +- /* Assemble the result. +- expm1(x) ~= 2^i * (p + 1) - 1 +- Let t = 2^i. */ +- svint64_t u = svadd_x (pg, svlsl_x (pg, i, 52), ExponentBias); +- svfloat64_t t = svreinterpret_f64 (u); +- +- /* expm1(x) ~= p * t + (t - 1). */ +- svfloat64_t y = svmla_x (pg, svsub_x (pg, t, 1), p, t); ++ svbool_t special = svacgt (pg, x, d->thres); + +- if (__glibc_unlikely (svptest_any (pg, special))) +- return special_case (x, y, special); ++ svfloat64_t z = svmla_x (pg, sv_f64 (d->shift), x, d->inv_ln2); ++ svuint64_t u = svreinterpret_u64 (z); ++ svfloat64_t n = svsub_x (pg, z, d->shift); + ++ /* r = x - n * ln2, r is in [-ln2/128, ln2/128]. */ ++ svfloat64_t ln2 = svld1rq (svptrue_b64 (), &d->ln2_hi); ++ svfloat64_t r = x; ++ r = svmls_lane (r, n, ln2, 0); ++ r = svmls_lane (r, n, ln2, 1); ++ ++ /* y = exp(r) - 1 ~= r + C0 r^2 + C1 r^3 + C2 r^4 + C3 r^5 + C4 r^6. */ ++ svfloat64_t r2 = svmul_x (svptrue_b64 (), r, r); ++ svfloat64_t c24 = svld1rq (svptrue_b64 (), &d->c2); ++ ++ svfloat64_t p; ++ svfloat64_t c12 = svmla_lane (sv_f64 (d->c1), r, c24, 0); ++ svfloat64_t c34 = svmla_lane (sv_f64 (d->c3), r, c24, 1); ++ p = svmad_x (pg, c34, r2, c12); ++ p = svmad_x (pg, p, r, sv_f64 (d->c0)); ++ p = svmad_x (pg, p, r2, r); ++ ++ svfloat64_t scale = svexpa (u); ++ svfloat64_t scalem1 = svsub_x (pg, scale, sv_f64 (1.0)); ++ ++ /* We want to construct expm1(x) = (scale - 1) + scale * poly. ++ However, for values of scale close to 1, scale-1 causes large ULP errors ++ due to cancellation. ++ ++ This can be circumvented by using a small lookup for scale-1 ++ when our input is below a certain bound, otherwise we can use FEXPA. ++ ++ This bound is based upon the table size: ++ Bound = (TableSize-1/64) * ln2. ++ The current bound is based upon a table size of 16. */ ++ svbool_t is_small = svaclt (pg, x, FexpaBound); ++ ++ if (svptest_any (pg, is_small)) ++ { ++ /* Index via the input of FEXPA, but we only care about the lower 4 bits. ++ */ ++ svuint64_t base_idx = svand_x (pg, u, 0xf); ++ ++ /* We can use the sign of x as a fifth bit to account for the asymmetry ++ of e^x around 0. */ ++ svuint64_t signBit ++ = svlsl_x (pg, svlsr_x (pg, svreinterpret_u64 (x), 63), 4); ++ svuint64_t idx = svorr_x (pg, base_idx, signBit); ++ ++ /* Lookup values for scale - 1 for small x. */ ++ svfloat64_t lookup = svreinterpret_f64 ( ++ svld1_gather_index (is_small, d->expm1_data, idx)); ++ ++ /* Select the appropriate scale - 1 value based on x. */ ++ scalem1 = svsel (is_small, lookup, scalem1); ++ } ++ ++ svfloat64_t y = svmla_x (pg, scalem1, scale, p); ++ ++ /* FEXPA returns nan for large inputs so we special case those. */ ++ if (__glibc_unlikely (svptest_any (pg, special))) ++ { ++ /* FEXPA zeroes the sign bit, however the sign is meaningful to the ++ special case function so needs to be copied. ++ e = sign bit of u << 46. */ ++ svuint64_t e = svand_x (pg, svlsl_x (pg, u, 46), 0x8000000000000000); ++ /* Copy sign to s. */ ++ scale = svreinterpret_f64 (svadd_x (pg, e, svreinterpret_u64 (scale))); ++ return special_case (pg, y, scale, p, n); ++ } ++ ++ /* return expm1 = (scale - 1) + (scale * poly). */ + return y; + } + +commit a79fbf10e762ae2cb615d57fd10446e0db95c50f +Author: Dylan Fleming +Date: Wed Jun 18 16:19:22 2025 +0000 + + AArch64: Optimise SVE FP64 Hyperbolics + + Reworke SVE FP64 hyperbolics to use the SVE FEXPA + instruction. + + Also update the special case handelling for large + inputs to be entirely vectorised. + + Performance improvements on Neoverse V1: + + cosh_sve: 19% for |x| < 709, 5x otherwise + sinh_sve: 24% for |x| < 709, 5.9x otherwise + tanh_sve: 12% for |x| < 19, 9x otherwise + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit dee22d2a81ab59afc165fb6dcb45d723f13582a0) + +diff --git a/sysdeps/aarch64/fpu/cosh_sve.c b/sysdeps/aarch64/fpu/cosh_sve.c +index e375dd8a34..3561893ae6 100644 +--- a/sysdeps/aarch64/fpu/cosh_sve.c ++++ b/sysdeps/aarch64/fpu/cosh_sve.c +@@ -21,71 +21,99 @@ + + static const struct data + { +- float64_t poly[3]; +- float64_t inv_ln2, ln2_hi, ln2_lo, shift, thres; ++ double c0, c2; ++ double c1, c3; ++ float64_t inv_ln2, ln2_hi, ln2_lo, shift; + uint64_t special_bound; + } data = { +- .poly = { 0x1.fffffffffffd4p-2, 0x1.5555571d6b68cp-3, +- 0x1.5555576a59599p-5, }, +- +- .inv_ln2 = 0x1.71547652b82fep8, /* N/ln2. */ +- /* -ln2/N. */ +- .ln2_hi = -0x1.62e42fefa39efp-9, +- .ln2_lo = -0x1.abc9e3b39803f3p-64, +- .shift = 0x1.8p+52, +- .thres = 704.0, +- +- /* 0x1.6p9, above which exp overflows. */ +- .special_bound = 0x4086000000000000, ++ /* Generated using Remez, in [-log(2)/128, log(2)/128]. */ ++ .c0 = 0x1.fffffffffdbcdp-2, ++ .c1 = 0x1.555555555444cp-3, ++ .c2 = 0x1.555573c6a9f7dp-5, ++ .c3 = 0x1.1111266d28935p-7, ++ .ln2_hi = 0x1.62e42fefa3800p-1, ++ .ln2_lo = 0x1.ef35793c76730p-45, ++ /* 1/ln2. */ ++ .inv_ln2 = 0x1.71547652b82fep+0, ++ .shift = 0x1.800000000ff80p+46, /* 1.5*2^46+1022. */ ++ ++ /* asuint(ln(2^(1024 - 1/128))), the value above which exp overflows. */ ++ .special_bound = 0x40862e37e7d8ba72, + }; + +-static svfloat64_t NOINLINE +-special_case (svfloat64_t x, svbool_t pg, svfloat64_t t, svbool_t special) +-{ +- svfloat64_t half_t = svmul_x (svptrue_b64 (), t, 0.5); +- svfloat64_t half_over_t = svdivr_x (pg, t, 0.5); +- svfloat64_t y = svadd_x (pg, half_t, half_over_t); +- return sv_call_f64 (cosh, x, y, special); +-} +- +-/* Helper for approximating exp(x). Copied from sv_exp_tail, with no +- special-case handling or tail. */ ++/* Helper for approximating exp(x)/2. ++ Functionally identical to FEXPA exp(x), but an adjustment in ++ the shift value which leads to a reduction in the exponent of scale by 1, ++ thus halving the result at no cost. */ + static inline svfloat64_t +-exp_inline (svfloat64_t x, const svbool_t pg, const struct data *d) ++exp_over_two_inline (const svbool_t pg, svfloat64_t x, const struct data *d) + { + /* Calculate exp(x). */ + svfloat64_t z = svmla_x (pg, sv_f64 (d->shift), x, d->inv_ln2); ++ svuint64_t u = svreinterpret_u64 (z); + svfloat64_t n = svsub_x (pg, z, d->shift); + +- svfloat64_t r = svmla_x (pg, x, n, d->ln2_hi); +- r = svmla_x (pg, r, n, d->ln2_lo); ++ svfloat64_t c13 = svld1rq (svptrue_b64 (), &d->c1); ++ svfloat64_t ln2 = svld1rq (svptrue_b64 (), &d->ln2_hi); + +- svuint64_t u = svreinterpret_u64 (z); +- svuint64_t e = svlsl_x (pg, u, 52 - V_EXP_TAIL_TABLE_BITS); +- svuint64_t i = svand_x (svptrue_b64 (), u, 0xff); ++ svfloat64_t r = x; ++ r = svmls_lane (r, n, ln2, 0); ++ r = svmls_lane (r, n, ln2, 1); + +- svfloat64_t y = svmla_x (pg, sv_f64 (d->poly[1]), r, d->poly[2]); +- y = svmla_x (pg, sv_f64 (d->poly[0]), r, y); +- y = svmla_x (pg, sv_f64 (1.0), r, y); +- y = svmul_x (svptrue_b64 (), r, y); ++ svfloat64_t r2 = svmul_x (svptrue_b64 (), r, r); ++ svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), r, c13, 0); ++ svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), r, c13, 1); ++ svfloat64_t p04 = svmla_x (pg, p01, p23, r2); ++ svfloat64_t p = svmla_x (pg, r, p04, r2); + +- /* s = 2^(n/N). */ +- u = svld1_gather_index (pg, __v_exp_tail_data, i); +- svfloat64_t s = svreinterpret_f64 (svadd_x (pg, u, e)); ++ svfloat64_t scale = svexpa (u); + +- return svmla_x (pg, s, s, y); ++ return svmla_x (pg, scale, scale, p); ++} ++ ++/* Vectorised special case to handle values past where exp_inline overflows. ++ Halves the input value and uses the identity exp(x) = exp(x/2)^2 to double ++ the valid range of inputs, and returns inf for anything past that. */ ++static svfloat64_t NOINLINE ++special_case (svbool_t pg, svbool_t special, svfloat64_t ax, svfloat64_t t, ++ const struct data *d) ++{ ++ /* Finish fast path to compute values for non-special cases. */ ++ svfloat64_t inv_twoexp = svdivr_x (pg, t, 0.25); ++ svfloat64_t y = svadd_x (pg, t, inv_twoexp); ++ ++ /* Halves input value, and then check if any cases ++ are still going to overflow. */ ++ ax = svmul_x (special, ax, 0.5); ++ svbool_t is_safe ++ = svcmplt (special, svreinterpret_u64 (ax), d->special_bound); ++ ++ /* Computes exp(x/2), and sets any overflowing lanes to inf. */ ++ svfloat64_t half_exp = exp_over_two_inline (special, ax, d); ++ half_exp = svsel (is_safe, half_exp, sv_f64 (INFINITY)); ++ ++ /* Construct special case cosh(x) = (exp(x/2)^2)/2. */ ++ svfloat64_t exp = svmul_x (svptrue_b64 (), half_exp, 2); ++ svfloat64_t special_y = svmul_x (special, exp, half_exp); ++ ++ /* Select correct return values for special and non-special cases. */ ++ special_y = svsel (special, special_y, y); ++ ++ /* Ensure an input of nan is correctly propagated. */ ++ svbool_t is_nan ++ = svcmpgt (special, svreinterpret_u64 (ax), sv_u64 (0x7ff0000000000000)); ++ return svsel (is_nan, ax, svsel (special, special_y, y)); + } + + /* Approximation for SVE double-precision cosh(x) using exp_inline. + cosh(x) = (exp(x) + exp(-x)) / 2. +- The greatest observed error is in the scalar fall-back region, so is the +- same as the scalar routine, 1.93 ULP: +- _ZGVsMxv_cosh (0x1.628ad45039d2fp+9) got 0x1.fd774e958236dp+1021 +- want 0x1.fd774e958236fp+1021. +- +- The greatest observed error in the non-special region is 1.54 ULP: +- _ZGVsMxv_cosh (0x1.ba5651dd4486bp+2) got 0x1.f5e2bb8d5c98fp+8 +- want 0x1.f5e2bb8d5c991p+8. */ ++ The greatest observed error in special case region is 2.66 + 0.5 ULP: ++ _ZGVsMxv_cosh (0x1.633b532ffbc1ap+9) got 0x1.f9b2d3d22399ep+1023 ++ want 0x1.f9b2d3d22399bp+1023 ++ ++ The greatest observed error in the non-special region is 1.01 + 0.5 ULP: ++ _ZGVsMxv_cosh (0x1.998ecbb3c1f81p+1) got 0x1.890b225657f84p+3 ++ want 0x1.890b225657f82p+3. */ + svfloat64_t SV_NAME_D1 (cosh) (svfloat64_t x, const svbool_t pg) + { + const struct data *d = ptr_barrier (&data); +@@ -94,14 +122,13 @@ svfloat64_t SV_NAME_D1 (cosh) (svfloat64_t x, const svbool_t pg) + svbool_t special = svcmpgt (pg, svreinterpret_u64 (ax), d->special_bound); + + /* Up to the point that exp overflows, we can use it to calculate cosh by +- exp(|x|) / 2 + 1 / (2 * exp(|x|)). */ +- svfloat64_t t = exp_inline (ax, pg, d); ++ (exp(|x|)/2 + 1) / (2 * exp(|x|)). */ ++ svfloat64_t half_exp = exp_over_two_inline (pg, ax, d); + +- /* Fall back to scalar for any special cases. */ ++ /* Falls back to entirely standalone vectorized special case. */ + if (__glibc_unlikely (svptest_any (pg, special))) +- return special_case (x, pg, t, special); ++ return special_case (pg, special, ax, half_exp, d); + +- svfloat64_t half_t = svmul_x (svptrue_b64 (), t, 0.5); +- svfloat64_t half_over_t = svdivr_x (pg, t, 0.5); +- return svadd_x (pg, half_t, half_over_t); ++ svfloat64_t inv_twoexp = svdivr_x (pg, half_exp, 0.25); ++ return svadd_x (pg, half_exp, inv_twoexp); + } +diff --git a/sysdeps/aarch64/fpu/sinh_sve.c b/sysdeps/aarch64/fpu/sinh_sve.c +index df5f6c8c06..ac7b306018 100644 +--- a/sysdeps/aarch64/fpu/sinh_sve.c ++++ b/sysdeps/aarch64/fpu/sinh_sve.c +@@ -18,90 +18,153 @@ + . */ + + #include "sv_math.h" +-#include "poly_sve_f64.h" + + static const struct data + { +- float64_t poly[11]; +- float64_t inv_ln2, m_ln2_hi, m_ln2_lo, shift; + uint64_t halff; +- int64_t onef; +- uint64_t large_bound; ++ double c2, c4; ++ double inv_ln2; ++ double ln2_hi, ln2_lo; ++ double c0, c1, c3; ++ double shift, special_bound, bound; ++ uint64_t expm1_data[20]; + } data = { +- /* Generated using Remez, deg=12 in [-log(2)/2, log(2)/2]. */ +- .poly = { 0x1p-1, 0x1.5555555555559p-3, 0x1.555555555554bp-5, +- 0x1.111111110f663p-7, 0x1.6c16c16c1b5f3p-10, +- 0x1.a01a01affa35dp-13, 0x1.a01a018b4ecbbp-16, +- 0x1.71ddf82db5bb4p-19, 0x1.27e517fc0d54bp-22, +- 0x1.af5eedae67435p-26, 0x1.1f143d060a28ap-29, }, +- +- .inv_ln2 = 0x1.71547652b82fep0, +- .m_ln2_hi = -0x1.62e42fefa39efp-1, +- .m_ln2_lo = -0x1.abc9e3b39803fp-56, +- .shift = 0x1.8p52, +- ++ /* Table lookup of 2^(i/64) - 1, for values of i from 0..19. */ ++ .expm1_data = { ++ 0x0000000000000000, 0x3f864d1f3bc03077, 0x3f966c34c5615d0f, 0x3fa0e8a30eb37901, ++ 0x3fa6ab0d9f3121ec, 0x3fac7d865a7a3440, 0x3fb1301d0125b50a, 0x3fb429aaea92ddfb, ++ 0x3fb72b83c7d517ae, 0x3fba35beb6fcb754, 0x3fbd4873168b9aa8, 0x3fc031dc431466b2, ++ 0x3fc1c3d373ab11c3, 0x3fc35a2b2f13e6e9, 0x3fc4f4efa8fef709, 0x3fc6942d3720185a, ++ 0x3fc837f0518db8a9, 0x3fc9e0459320b7fa, 0x3fcb8d39b9d54e55, 0x3fcd3ed9a72cffb7, ++ }, ++ ++ /* Generated using Remez, in [-log(2)/128, log(2)/128]. */ ++ .c0 = 0x1p-1, ++ .c1 = 0x1.55555555548f9p-3, ++ .c2 = 0x1.5555555554c22p-5, ++ .c3 = 0x1.111123aaa2fb2p-7, ++ .c4 = 0x1.6c16d77d98e5bp-10, ++ .ln2_hi = 0x1.62e42fefa3800p-1, ++ .ln2_lo = 0x1.ef35793c76730p-45, ++ .inv_ln2 = 0x1.71547652b82fep+0, ++ .shift = 0x1.800000000ffc0p+46, /* 1.5*2^46+1023. */ + .halff = 0x3fe0000000000000, +- .onef = 0x3ff0000000000000, +- /* 2^9. expm1 helper overflows for large input. */ +- .large_bound = 0x4080000000000000, ++ .special_bound = 0x1.62e37e7d8ba72p+9, /* ln(2^(1024 - 1/128)). */ ++ .bound = 0x1.a56ef8ec924ccp-3 /* 19*ln2/64. */ + }; + ++/* A specialised FEXPA expm1 that is only valid for positive inputs and ++ has no special cases. Based off the full FEXPA expm1 implementated for ++ _ZGVsMxv_expm1, with a slightly modified file to keep sinh under 3.5ULP. */ + static inline svfloat64_t +-expm1_inline (svfloat64_t x, svbool_t pg) ++expm1_inline (svbool_t pg, svfloat64_t x) + { + const struct data *d = ptr_barrier (&data); + +- /* Reduce argument: +- exp(x) - 1 = 2^i * (expm1(f) + 1) - 1 +- where i = round(x / ln2) +- and f = x - i * ln2 (f in [-ln2/2, ln2/2]). */ +- svfloat64_t j +- = svsub_x (pg, svmla_x (pg, sv_f64 (d->shift), x, d->inv_ln2), d->shift); +- svint64_t i = svcvt_s64_x (pg, j); +- svfloat64_t f = svmla_x (pg, x, j, d->m_ln2_hi); +- f = svmla_x (pg, f, j, d->m_ln2_lo); +- /* Approximate expm1(f) using polynomial. */ +- svfloat64_t f2 = svmul_x (pg, f, f); +- svfloat64_t f4 = svmul_x (pg, f2, f2); +- svfloat64_t f8 = svmul_x (pg, f4, f4); +- svfloat64_t p +- = svmla_x (pg, f, f2, sv_estrin_10_f64_x (pg, f, f2, f4, f8, d->poly)); +- /* t = 2^i. */ +- svfloat64_t t = svscale_x (pg, sv_f64 (1), i); +- /* expm1(x) ~= p * t + (t - 1). */ +- return svmla_x (pg, svsub_x (pg, t, 1.0), p, t); ++ svfloat64_t z = svmla_x (pg, sv_f64 (d->shift), x, d->inv_ln2); ++ svuint64_t u = svreinterpret_u64 (z); ++ svfloat64_t n = svsub_x (pg, z, d->shift); ++ ++ svfloat64_t ln2 = svld1rq (svptrue_b64 (), &d->ln2_hi); ++ svfloat64_t c24 = svld1rq (svptrue_b64 (), &d->c2); ++ ++ svfloat64_t r = x; ++ r = svmls_lane (r, n, ln2, 0); ++ r = svmls_lane (r, n, ln2, 1); ++ ++ svfloat64_t r2 = svmul_x (svptrue_b64 (), r, r); ++ ++ svfloat64_t p; ++ svfloat64_t c12 = svmla_lane (sv_f64 (d->c1), r, c24, 0); ++ svfloat64_t c34 = svmla_lane (sv_f64 (d->c3), r, c24, 1); ++ p = svmad_x (pg, c34, r2, c12); ++ p = svmad_x (pg, p, r, sv_f64 (d->c0)); ++ p = svmad_x (pg, p, r2, r); ++ ++ svfloat64_t scale = svexpa (u); ++ ++ /* We want to construct expm1(x) = (scale - 1) + scale * poly. ++ However, for values of scale close to 1, scale-1 causes large ULP errors ++ due to cancellation. ++ ++ This can be circumvented by using a small lookup for scale-1 ++ when our input is below a certain bound, otherwise we can use FEXPA. */ ++ svbool_t is_small = svaclt (pg, x, d->bound); ++ ++ /* Index via the input of FEXPA, but we only care about the lower 5 bits. */ ++ svuint64_t base_idx = svand_x (pg, u, 0x1f); ++ ++ /* Compute scale - 1 from FEXPA, and lookup values where this fails. */ ++ svfloat64_t scalem1_estimate = svsub_x (pg, scale, sv_f64 (1.0)); ++ svuint64_t scalem1_lookup ++ = svld1_gather_index (is_small, d->expm1_data, base_idx); ++ ++ /* Select the appropriate scale - 1 value based on x. */ ++ svfloat64_t scalem1 ++ = svsel (is_small, svreinterpret_f64 (scalem1_lookup), scalem1_estimate); ++ ++ /* return expm1 = scale - 1 + (scale * poly). */ ++ return svmla_x (pg, scalem1, scale, p); + } + ++/* Vectorised special case to handle values past where exp_inline overflows. ++ Halves the input value and uses the identity exp(x) = exp(x/2)^2 to double ++ the valid range of inputs, and returns inf for anything past that. */ + static svfloat64_t NOINLINE +-special_case (svfloat64_t x, svbool_t pg) ++special_case (svbool_t pg, svbool_t special, svfloat64_t ax, ++ svfloat64_t halfsign, const struct data *d) + { +- return sv_call_f64 (sinh, x, x, pg); ++ /* Halves input value, and then check if any cases ++ are still going to overflow. */ ++ ax = svmul_x (special, ax, 0.5); ++ svbool_t is_safe = svaclt (special, ax, d->special_bound); ++ ++ svfloat64_t t = expm1_inline (pg, ax); ++ ++ /* Finish fastpass to compute values for non-special cases. */ ++ svfloat64_t y = svadd_x (pg, t, svdiv_x (pg, t, svadd_x (pg, t, 1.0))); ++ y = svmul_x (pg, y, halfsign); ++ ++ /* Computes special lane, and set remaining overflow lanes to inf. */ ++ svfloat64_t half_special_y = svmul_x (svptrue_b64 (), t, halfsign); ++ svfloat64_t special_y = svmul_x (svptrue_b64 (), half_special_y, t); ++ ++ svuint64_t signed_inf ++ = svorr_x (svptrue_b64 (), svreinterpret_u64 (halfsign), ++ sv_u64 (0x7ff0000000000000)); ++ special_y = svsel (is_safe, special_y, svreinterpret_f64 (signed_inf)); ++ ++ /* Join resulting vectors together and return. */ ++ return svsel (special, special_y, y); + } + +-/* Approximation for SVE double-precision sinh(x) using expm1. +- sinh(x) = (exp(x) - exp(-x)) / 2. +- The greatest observed error is 2.57 ULP: +- _ZGVsMxv_sinh (0x1.a008538399931p-2) got 0x1.ab929fc64bd66p-2 +- want 0x1.ab929fc64bd63p-2. */ ++/* Approximation for SVE double-precision sinh(x) using FEXPA expm1. ++ Uses sinh(x) = e^2x - 1 / 2e^x, rewritten for accuracy. ++ The greatest observed error in the non-special region is 2.63 + 0.5 ULP: ++ _ZGVsMxv_sinh (0x1.b5e0e13ba88aep-2) got 0x1.c3587faf97b0cp-2 ++ want 0x1.c3587faf97b09p-2 ++ ++ The greatest observed error in the special region is 2.65 + 0.5 ULP: ++ _ZGVsMxv_sinh (0x1.633ce847dab1ap+9) got 0x1.fffd30eea0066p+1023 ++ want 0x1.fffd30eea0063p+1023. */ + svfloat64_t SV_NAME_D1 (sinh) (svfloat64_t x, svbool_t pg) + { + const struct data *d = ptr_barrier (&data); + ++ svbool_t special = svacge (pg, x, d->special_bound); + svfloat64_t ax = svabs_x (pg, x); + svuint64_t sign + = sveor_x (pg, svreinterpret_u64 (x), svreinterpret_u64 (ax)); + svfloat64_t halfsign = svreinterpret_f64 (svorr_x (pg, sign, d->halff)); + +- svbool_t special = svcmpge (pg, svreinterpret_u64 (ax), d->large_bound); +- + /* Fall back to scalar variant for all lanes if any are special. */ + if (__glibc_unlikely (svptest_any (pg, special))) +- return special_case (x, pg); ++ return special_case (pg, special, ax, halfsign, d); + + /* Up to the point that expm1 overflows, we can use it to calculate sinh + using a slight rearrangement of the definition of sinh. This allows us to + retain acceptable accuracy for very small inputs. */ +- svfloat64_t t = expm1_inline (ax, pg); ++ svfloat64_t t = expm1_inline (pg, ax); + t = svadd_x (pg, t, svdiv_x (pg, t, svadd_x (pg, t, 1.0))); + return svmul_x (pg, t, halfsign); + } +diff --git a/sysdeps/aarch64/fpu/tanh_sve.c b/sysdeps/aarch64/fpu/tanh_sve.c +index d25e011cea..805669845d 100644 +--- a/sysdeps/aarch64/fpu/tanh_sve.c ++++ b/sysdeps/aarch64/fpu/tanh_sve.c +@@ -18,83 +18,117 @@ + . */ + + #include "sv_math.h" +-#include "poly_sve_f64.h" + + static const struct data + { +- float64_t poly[11]; +- float64_t inv_ln2, ln2_hi, ln2_lo, shift; +- uint64_t thresh, tiny_bound; ++ double ln2_hi, ln2_lo; ++ double c2, c4; ++ double c0, c1, c3; ++ double two_over_ln2, shift; ++ uint64_t tiny_bound; ++ double large_bound, fexpa_bound; ++ uint64_t e2xm1_data[20]; + } data = { +- /* Generated using Remez, deg=12 in [-log(2)/2, log(2)/2]. */ +- .poly = { 0x1p-1, 0x1.5555555555559p-3, 0x1.555555555554bp-5, +- 0x1.111111110f663p-7, 0x1.6c16c16c1b5f3p-10, +- 0x1.a01a01affa35dp-13, 0x1.a01a018b4ecbbp-16, +- 0x1.71ddf82db5bb4p-19, 0x1.27e517fc0d54bp-22, +- 0x1.af5eedae67435p-26, 0x1.1f143d060a28ap-29, }, +- +- .inv_ln2 = 0x1.71547652b82fep0, +- .ln2_hi = -0x1.62e42fefa39efp-1, +- .ln2_lo = -0x1.abc9e3b39803fp-56, +- .shift = 0x1.8p52, +- ++ /* Generated using Remez, in [-log(2)/128, log(2)/128]. */ ++ .c0 = 0x1p-1, ++ .c1 = 0x1.55555555548f9p-3, ++ .c2 = 0x1.5555555554c22p-5, ++ .c3 = 0x1.111123aaa2fb2p-7, ++ .c4 = 0x1.6c16d77d98e5bp-10, ++ .ln2_hi = 0x1.62e42fefa3800p-1, ++ .ln2_lo = 0x1.ef35793c76730p-45, ++ .two_over_ln2 = 0x1.71547652b82fep+1, ++ .shift = 0x1.800000000ffc0p+46, /* 1.5*2^46+1023. */ + .tiny_bound = 0x3e40000000000000, /* asuint64 (0x1p-27). */ +- /* asuint64(0x1.241bf835f9d5fp+4) - asuint64(tiny_bound). */ +- .thresh = 0x01f241bf835f9d5f, ++ .large_bound = 0x1.30fc1931f09cap+4, /* arctanh(1 - 2^-54). */ ++ .fexpa_bound = 0x1.a56ef8ec924ccp-4, /* 19/64 * ln2/2. */ ++ /* Table lookup of 2^(i/64) - 1, for values of i from 0..19. */ ++ .e2xm1_data = { ++ 0x0000000000000000, 0x3f864d1f3bc03077, 0x3f966c34c5615d0f, 0x3fa0e8a30eb37901, ++ 0x3fa6ab0d9f3121ec, 0x3fac7d865a7a3440, 0x3fb1301d0125b50a, 0x3fb429aaea92ddfb, ++ 0x3fb72b83c7d517ae, 0x3fba35beb6fcb754, 0x3fbd4873168b9aa8, 0x3fc031dc431466b2, ++ 0x3fc1c3d373ab11c3, 0x3fc35a2b2f13e6e9, 0x3fc4f4efa8fef709, 0x3fc6942d3720185a, ++ 0x3fc837f0518db8a9, 0x3fc9e0459320b7fa, 0x3fcb8d39b9d54e55, 0x3fcd3ed9a72cffb7, ++ }, + }; + ++/* An expm1 inspired, FEXPA based helper function that returns an ++ accurate estimate for e^2x - 1. With no special case or support for ++ negative inputs of x. */ + static inline svfloat64_t +-expm1_inline (svfloat64_t x, const svbool_t pg, const struct data *d) +-{ +- /* Helper routine for calculating exp(x) - 1. Vector port of the helper from +- the scalar variant of tanh. */ +- +- /* Reduce argument: f in [-ln2/2, ln2/2], i is exact. */ +- svfloat64_t j +- = svsub_x (pg, svmla_x (pg, sv_f64 (d->shift), x, d->inv_ln2), d->shift); +- svint64_t i = svcvt_s64_x (pg, j); +- svfloat64_t f = svmla_x (pg, x, j, d->ln2_hi); +- f = svmla_x (pg, f, j, d->ln2_lo); +- +- /* Approximate expm1(f) using polynomial. */ +- svfloat64_t f2 = svmul_x (pg, f, f); +- svfloat64_t f4 = svmul_x (pg, f2, f2); +- svfloat64_t p = svmla_x ( +- pg, f, f2, +- sv_estrin_10_f64_x (pg, f, f2, f4, svmul_x (pg, f4, f4), d->poly)); +- +- /* t = 2 ^ i. */ +- svfloat64_t t = svscale_x (pg, sv_f64 (1), i); +- /* expm1(x) = p * t + (t - 1). */ +- return svmla_x (pg, svsub_x (pg, t, 1), p, t); +-} +- +-static svfloat64_t NOINLINE +-special_case (svfloat64_t x, svfloat64_t y, svbool_t special) ++e2xm1_inline (const svbool_t pg, svfloat64_t x, const struct data *d) + { +- return sv_call_f64 (tanh, x, y, special); ++ svfloat64_t z = svmla_x (pg, sv_f64 (d->shift), x, d->two_over_ln2); ++ svuint64_t u = svreinterpret_u64 (z); ++ svfloat64_t n = svsub_x (pg, z, d->shift); ++ ++ /* r = x - n * ln2/2, r is in [-ln2/(2N), ln2/(2N)]. */ ++ svfloat64_t ln2 = svld1rq (svptrue_b64 (), &d->ln2_hi); ++ svfloat64_t r = svadd_x (pg, x, x); ++ r = svmls_lane (r, n, ln2, 0); ++ r = svmls_lane (r, n, ln2, 1); ++ ++ /* y = exp(r) - 1 ~= r + C0 r^2 + C1 r^3 + C2 r^4 + C3 r^5 + C4 r^6. */ ++ svfloat64_t r2 = svmul_x (svptrue_b64 (), r, r); ++ svfloat64_t c24 = svld1rq (svptrue_b64 (), &d->c2); ++ ++ svfloat64_t p; ++ svfloat64_t c12 = svmla_lane (sv_f64 (d->c1), r, c24, 0); ++ svfloat64_t c34 = svmla_lane (sv_f64 (d->c3), r, c24, 1); ++ p = svmad_x (pg, c34, r2, c12); ++ p = svmad_x (pg, p, r, sv_f64 (d->c0)); ++ p = svmad_x (pg, p, r2, r); ++ ++ svfloat64_t scale = svexpa (u); ++ ++ /* We want to construct e2xm1(x) = (scale - 1) + scale * poly. ++ However, for values of scale close to 1, scale-1 causes large ULP errors ++ due to cancellation. ++ ++ This can be circumvented by using a small lookup for scale-1 ++ when our input is below a certain bound, otherwise we can use FEXPA. */ ++ svbool_t is_small = svaclt (pg, x, d->fexpa_bound); ++ ++ /* Index via the input of FEXPA, but we only care about the lower 5 bits. */ ++ svuint64_t base_idx = svand_x (pg, u, 0x1f); ++ ++ /* Compute scale - 1 from FEXPA, and lookup values where this fails. */ ++ svfloat64_t scalem1_estimate = svsub_x (pg, scale, sv_f64 (1.0)); ++ svuint64_t scalem1_lookup ++ = svld1_gather_index (is_small, d->e2xm1_data, base_idx); ++ ++ /* Select the appropriate scale - 1 value based on x. */ ++ svfloat64_t scalem1 ++ = svsel (is_small, svreinterpret_f64 (scalem1_lookup), scalem1_estimate); ++ return svmla_x (pg, scalem1, scale, p); + } + +-/* SVE approximation for double-precision tanh(x), using a simplified +- version of expm1. The greatest observed error is 2.77 ULP: +- _ZGVsMxv_tanh(-0x1.c4a4ca0f9f3b7p-3) got -0x1.bd6a21a163627p-3 +- want -0x1.bd6a21a163624p-3. */ ++/* SVE approximation for double-precision tanh(x), using a modified version of ++ FEXPA expm1 to calculate e^2x - 1. ++ The greatest observed error is 2.79 + 0.5 ULP: ++ _ZGVsMxv_tanh (0x1.fff868eb3c223p-9) got 0x1.fff7be486cae6p-9 ++ want 0x1.fff7be486cae9p-9. */ + svfloat64_t SV_NAME_D1 (tanh) (svfloat64_t x, svbool_t pg) + { + const struct data *d = ptr_barrier (&data); + +- svuint64_t ia = svreinterpret_u64 (svabs_x (pg, x)); ++ svbool_t large = svacge (pg, x, d->large_bound); + +- /* Trigger special-cases for tiny, boring and infinity/NaN. */ +- svbool_t special = svcmpgt (pg, svsub_x (pg, ia, d->tiny_bound), d->thresh); ++ /* We can use tanh(x) = (e^2x - 1) / (e^2x + 1) to approximate tanh. ++ As an additional optimisation, we can ensure more accurate values of e^x ++ by only using positive inputs. So we calculate tanh(|x|), and restore the ++ sign of the input before returning. */ ++ svfloat64_t ax = svabs_x (pg, x); ++ svuint64_t sign_bit ++ = sveor_x (pg, svreinterpret_u64 (x), svreinterpret_u64 (ax)); + +- svfloat64_t u = svadd_x (pg, x, x); ++ svfloat64_t p = e2xm1_inline (pg, ax, d); ++ svfloat64_t q = svadd_x (pg, p, 2); + +- /* tanh(x) = (e^2x - 1) / (e^2x + 1). */ +- svfloat64_t q = expm1_inline (u, pg, d); +- svfloat64_t qp2 = svadd_x (pg, q, 2); ++ /* For sufficiently high inputs, the result of tanh(|x|) is 1 when correctly ++ rounded, at this point we can return 1 directly, with sign correction. ++ This will also act as a guard against our approximation overflowing. */ ++ svfloat64_t y = svsel (large, sv_f64 (1.0), svdiv_x (pg, p, q)); + +- if (__glibc_unlikely (svptest_any (pg, special))) +- return special_case (x, svdiv_x (pg, q, qp2), special); +- return svdiv_x (pg, q, qp2); ++ return svreinterpret_f64 (svorr_x (pg, sign_bit, svreinterpret_u64 (y))); + } + +commit 3e5dfac7cc569117b6998425a259561890c893cd +Author: Luna Lamb +Date: Wed Jun 18 16:12:19 2025 +0000 + + AArch64: Improve codegen SVE log1p helper + + Improve codegen by packing coefficients. + 4% and 2% improvement in throughput microbenchmark on Neoverse V1, for acosh + and atanh respectively. + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit 6849c5b791edd216f2ec3fdbe4d138bc69b9b333) + +diff --git a/sysdeps/aarch64/fpu/acosh_sve.c b/sysdeps/aarch64/fpu/acosh_sve.c +index 3e4faaa5ca..78ebcffbb5 100644 +--- a/sysdeps/aarch64/fpu/acosh_sve.c ++++ b/sysdeps/aarch64/fpu/acosh_sve.c +@@ -30,10 +30,10 @@ special_case (svfloat64_t x, svfloat64_t y, svbool_t special) + } + + /* SVE approximation for double-precision acosh, based on log1p. +- The largest observed error is 3.19 ULP in the region where the ++ The largest observed error is 3.14 ULP in the region where the + argument to log1p falls in the k=0 interval, i.e. x close to 1: +- SV_NAME_D1 (acosh)(0x1.1e4388d4ca821p+0) got 0x1.ed23399f5137p-2 +- want 0x1.ed23399f51373p-2. */ ++ SV_NAME_D1 (acosh)(0x1.1e80ed12f0ad1p+0) got 0x1.ef0cee7c33ce1p-2 ++ want 0x1.ef0cee7c33ce4p-2. */ + svfloat64_t SV_NAME_D1 (acosh) (svfloat64_t x, const svbool_t pg) + { + /* (ix - One) >= (BigBound - One). */ +diff --git a/sysdeps/aarch64/fpu/atanh_sve.c b/sysdeps/aarch64/fpu/atanh_sve.c +index 7a52728d70..a4803e5c13 100644 +--- a/sysdeps/aarch64/fpu/atanh_sve.c ++++ b/sysdeps/aarch64/fpu/atanh_sve.c +@@ -30,7 +30,7 @@ special_case (svfloat64_t x, svfloat64_t y, svbool_t special) + } + + /* SVE approximation for double-precision atanh, based on log1p. +- The greatest observed error is 2.81 ULP: ++ The greatest observed error is 3.3 ULP: + _ZGVsMxv_atanh(0x1.ffae6288b601p-6) got 0x1.ffd8ff31b5019p-6 + want 0x1.ffd8ff31b501cp-6. */ + svfloat64_t SV_NAME_D1 (atanh) (svfloat64_t x, const svbool_t pg) +@@ -42,7 +42,6 @@ svfloat64_t SV_NAME_D1 (atanh) (svfloat64_t x, const svbool_t pg) + svfloat64_t halfsign = svreinterpret_f64 (svorr_x (pg, sign, Half)); + + /* It is special if iax >= 1. */ +-// svbool_t special = svcmpge (pg, iax, One); + svbool_t special = svacge (pg, x, 1.0); + + /* Computation is performed based on the following sequence of equality: +diff --git a/sysdeps/aarch64/fpu/sv_log1p_inline.h b/sysdeps/aarch64/fpu/sv_log1p_inline.h +index da019674f9..a9ecd75d19 100644 +--- a/sysdeps/aarch64/fpu/sv_log1p_inline.h ++++ b/sysdeps/aarch64/fpu/sv_log1p_inline.h +@@ -21,11 +21,12 @@ + #define AARCH64_FPU_SV_LOG1P_INLINE_H + + #include "sv_math.h" +-#include "poly_sve_f64.h" + + static const struct sv_log1p_data + { +- double poly[19], ln2[2]; ++ double c0, c2, c4, c6, c8, c10, c12, c14, c16; ++ double c1, c3, c5, c7, c9, c11, c13, c15, c17, c18; ++ double ln2_lo, ln2_hi; + uint64_t hf_rt2_top; + uint64_t one_m_hf_rt2_top; + uint32_t bottom_mask; +@@ -33,15 +34,30 @@ static const struct sv_log1p_data + } sv_log1p_data = { + /* Coefficients generated using Remez, deg=20, in [sqrt(2)/2-1, sqrt(2)-1]. + */ +- .poly = { -0x1.ffffffffffffbp-2, 0x1.55555555551a9p-2, -0x1.00000000008e3p-2, +- 0x1.9999999a32797p-3, -0x1.555555552fecfp-3, 0x1.249248e071e5ap-3, +- -0x1.ffffff8bf8482p-4, 0x1.c71c8f07da57ap-4, -0x1.9999ca4ccb617p-4, +- 0x1.7459ad2e1dfa3p-4, -0x1.554d2680a3ff2p-4, 0x1.3b4c54d487455p-4, +- -0x1.2548a9ffe80e6p-4, 0x1.0f389a24b2e07p-4, -0x1.eee4db15db335p-5, +- 0x1.e95b494d4a5ddp-5, -0x1.15fdf07cb7c73p-4, 0x1.0310b70800fcfp-4, +- -0x1.cfa7385bdb37ep-6 }, +- .ln2 = { 0x1.62e42fefa3800p-1, 0x1.ef35793c76730p-45 }, ++ .c0 = -0x1.ffffffffffffbp-2, ++ .c1 = 0x1.55555555551a9p-2, ++ .c2 = -0x1.00000000008e3p-2, ++ .c3 = 0x1.9999999a32797p-3, ++ .c4 = -0x1.555555552fecfp-3, ++ .c5 = 0x1.249248e071e5ap-3, ++ .c6 = -0x1.ffffff8bf8482p-4, ++ .c7 = 0x1.c71c8f07da57ap-4, ++ .c8 = -0x1.9999ca4ccb617p-4, ++ .c9 = 0x1.7459ad2e1dfa3p-4, ++ .c10 = -0x1.554d2680a3ff2p-4, ++ .c11 = 0x1.3b4c54d487455p-4, ++ .c12 = -0x1.2548a9ffe80e6p-4, ++ .c13 = 0x1.0f389a24b2e07p-4, ++ .c14 = -0x1.eee4db15db335p-5, ++ .c15 = 0x1.e95b494d4a5ddp-5, ++ .c16 = -0x1.15fdf07cb7c73p-4, ++ .c17 = 0x1.0310b70800fcfp-4, ++ .c18 = -0x1.cfa7385bdb37ep-6, ++ .ln2_lo = 0x1.62e42fefa3800p-1, ++ .ln2_hi = 0x1.ef35793c76730p-45, ++ /* top32(asuint64(sqrt(2)/2)) << 32. */ + .hf_rt2_top = 0x3fe6a09e00000000, ++ /* (top32(asuint64(1)) - top32(asuint64(sqrt(2)/2))) << 32. */ + .one_m_hf_rt2_top = 0x00095f6200000000, + .bottom_mask = 0xffffffff, + .one_top = 0x3ff +@@ -51,14 +67,14 @@ static inline svfloat64_t + sv_log1p_inline (svfloat64_t x, const svbool_t pg) + { + /* Helper for calculating log(x + 1). Adapted from v_log1p_inline.h, which +- differs from v_log1p_2u5.c by: ++ differs from advsimd/log1p.c by: + - No special-case handling - this should be dealt with by the caller. + - Pairwise Horner polynomial evaluation for improved accuracy. + - Optionally simulate the shortcut for k=0, used in the scalar routine, + using svsel, for improved accuracy when the argument to log1p is close + to 0. This feature is enabled by defining WANT_SV_LOG1P_K0_SHORTCUT as 1 + in the source of the caller before including this file. +- See sv_log1p_2u1.c for details of the algorithm. */ ++ See sve/log1p.c for details of the algorithm. */ + const struct sv_log1p_data *d = ptr_barrier (&sv_log1p_data); + svfloat64_t m = svadd_x (pg, x, 1); + svuint64_t mi = svreinterpret_u64 (m); +@@ -79,7 +95,7 @@ sv_log1p_inline (svfloat64_t x, const svbool_t pg) + svfloat64_t cm; + + #ifndef WANT_SV_LOG1P_K0_SHORTCUT +-#error \ ++#error \ + "Cannot use sv_log1p_inline.h without specifying whether you need the k0 shortcut for greater accuracy close to 0" + #elif WANT_SV_LOG1P_K0_SHORTCUT + /* Shortcut if k is 0 - set correction term to 0 and f to x. The result is +@@ -96,14 +112,46 @@ sv_log1p_inline (svfloat64_t x, const svbool_t pg) + #endif + + /* Approximate log1p(f) on the reduced input using a polynomial. */ +- svfloat64_t f2 = svmul_x (pg, f, f); +- svfloat64_t p = sv_pw_horner_18_f64_x (pg, f, f2, d->poly); ++ svfloat64_t f2 = svmul_x (svptrue_b64 (), f, f), ++ f4 = svmul_x (svptrue_b64 (), f2, f2), ++ f8 = svmul_x (svptrue_b64 (), f4, f4), ++ f16 = svmul_x (svptrue_b64 (), f8, f8); ++ ++ svfloat64_t c13 = svld1rq (svptrue_b64 (), &d->c1); ++ svfloat64_t c57 = svld1rq (svptrue_b64 (), &d->c5); ++ svfloat64_t c911 = svld1rq (svptrue_b64 (), &d->c9); ++ svfloat64_t c1315 = svld1rq (svptrue_b64 (), &d->c13); ++ svfloat64_t c1718 = svld1rq (svptrue_b64 (), &d->c17); ++ ++ /* Order-18 Estrin scheme. */ ++ svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), f, c13, 0); ++ svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), f, c13, 1); ++ svfloat64_t p45 = svmla_lane (sv_f64 (d->c4), f, c57, 0); ++ svfloat64_t p67 = svmla_lane (sv_f64 (d->c6), f, c57, 1); ++ ++ svfloat64_t p03 = svmla_x (pg, p01, f2, p23); ++ svfloat64_t p47 = svmla_x (pg, p45, f2, p67); ++ svfloat64_t p07 = svmla_x (pg, p03, f4, p47); ++ ++ svfloat64_t p89 = svmla_lane (sv_f64 (d->c8), f, c911, 0); ++ svfloat64_t p1011 = svmla_lane (sv_f64 (d->c10), f, c911, 1); ++ svfloat64_t p1213 = svmla_lane (sv_f64 (d->c12), f, c1315, 0); ++ svfloat64_t p1415 = svmla_lane (sv_f64 (d->c14), f, c1315, 1); ++ ++ svfloat64_t p811 = svmla_x (pg, p89, f2, p1011); ++ svfloat64_t p1215 = svmla_x (pg, p1213, f2, p1415); ++ svfloat64_t p815 = svmla_x (pg, p811, f4, p1215); ++ ++ svfloat64_t p015 = svmla_x (pg, p07, f8, p815); ++ svfloat64_t p1617 = svmla_lane (sv_f64 (d->c16), f, c1718, 0); ++ svfloat64_t p1618 = svmla_lane (p1617, f2, c1718, 1); ++ svfloat64_t p = svmla_x (pg, p015, f16, p1618); + + /* Assemble log1p(x) = k * log2 + log1p(f) + c/m. */ +- svfloat64_t ylo = svmla_x (pg, cm, k, d->ln2[0]); +- svfloat64_t yhi = svmla_x (pg, f, k, d->ln2[1]); ++ svfloat64_t ln2_lo_hi = svld1rq (svptrue_b64 (), &d->ln2_lo); ++ svfloat64_t ylo = svmla_lane (cm, k, ln2_lo_hi, 0); ++ svfloat64_t yhi = svmla_lane (f, k, ln2_lo_hi, 1); + +- return svmla_x (pg, svadd_x (pg, ylo, yhi), f2, p); ++ return svmad_x (pg, p, f2, svadd_x (pg, ylo, yhi)); + } +- + #endif + +commit e8ac8a9844ba6ef92d49354c103c1320fcfc0087 +Author: Florian Weimer +Date: Sat Feb 1 12:37:58 2025 +0100 + + elf: Do not add a copy of _dl_find_object to libc.so + + This reduces code size and dependencies on ld.so internals from + libc.so. + + Fixes commit f4c142bb9fe6b02c0af8cfca8a920091e2dba44b + ("arm: Use _dl_find_object on __gnu_Unwind_Find_exidx (BZ 31405)"). + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 96429bcc91a14f71b177ddc5e716de3069060f2c) + +diff --git a/elf/Makefile b/elf/Makefile +index afd4eb6fdd..5abf61758d 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -34,7 +34,6 @@ routines = \ + dl-addr \ + dl-addr-obj \ + dl-early_allocate \ +- dl-find_object \ + dl-iteratephdr \ + dl-libc \ + dl-origin \ +@@ -61,6 +60,7 @@ dl-routines = \ + dl-deps \ + dl-exception \ + dl-execstack \ ++ dl-find_object \ + dl-fini \ + dl-init \ + dl-load \ +diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c +index ae18b438d3..2e41fd064a 100644 +--- a/elf/dl-find_object.c ++++ b/elf/dl-find_object.c +@@ -356,7 +356,7 @@ _dlfo_lookup (uintptr_t pc, struct dl_find_object_internal *first1, size_t size) + } + + int +-__dl_find_object (void *pc1, struct dl_find_object *result) ++_dl_find_object (void *pc1, struct dl_find_object *result) + { + uintptr_t pc = (uintptr_t) pc1; + +@@ -463,8 +463,7 @@ __dl_find_object (void *pc1, struct dl_find_object *result) + return -1; + } /* Transaction retry loop. */ + } +-hidden_def (__dl_find_object) +-weak_alias (__dl_find_object, _dl_find_object) ++rtld_hidden_def (_dl_find_object) + + /* _dlfo_process_initial is called twice. First to compute the array + sizes from the initial loaded mappings. Second to fill in the +diff --git a/include/dlfcn.h b/include/dlfcn.h +index f49ee1b0c9..a44420fa37 100644 +--- a/include/dlfcn.h ++++ b/include/dlfcn.h +@@ -4,8 +4,7 @@ + #include /* For ElfW. */ + #include + +-extern __typeof (_dl_find_object) __dl_find_object; +-hidden_proto (__dl_find_object) ++rtld_hidden_proto (_dl_find_object) + + /* Internally used flag. */ + #define __RTLD_DLOPEN 0x80000000 +diff --git a/sysdeps/arm/find_exidx.c b/sysdeps/arm/find_exidx.c +index a924d59b9f..4257c26838 100644 +--- a/sysdeps/arm/find_exidx.c ++++ b/sysdeps/arm/find_exidx.c +@@ -15,6 +15,7 @@ + License along with the GNU C Library. If not, see + . */ + ++#include + #include + + /* Find the exception index table containing PC. */ +@@ -23,7 +24,7 @@ _Unwind_Ptr + __gnu_Unwind_Find_exidx (_Unwind_Ptr pc, int * pcount) + { + struct dl_find_object data; +- if (__dl_find_object ((void *) pc, &data) < 0) ++ if (GLRO(dl_find_object) ((void *) pc, &data) < 0) + return 0; + *pcount = data.dlfo_eh_count; + return (_Unwind_Ptr) data.dlfo_eh_frame; + +commit 0e1fea8879c82769540a3c6bdc14c6a49798a2f7 +Author: Florian Weimer +Date: Fri Aug 1 10:20:23 2025 +0200 + + elf: Extract rtld_setup_phdr function from dl_main + + Remove historic binutils reference from comment and update + how this data is used by applications. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 2cac9559e06044ba520e785c151fbbd25011865f) + +diff --git a/elf/rtld.c b/elf/rtld.c +index 09b9c9993b..6f50aeeafc 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -1284,6 +1284,37 @@ rtld_setup_main_map (struct link_map *main_map) + 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. */ ++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; ++ ++ ++ /* 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 +@@ -1750,33 +1781,7 @@ dl_main (const ElfW(Phdr) *phdr, + ++GL(dl_ns)[LM_ID_BASE]._ns_nloaded; + ++GL(dl_load_adds); + +- /* 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. */ +- +- /* 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) + +commit 2193f42655a9687ce66362905add03a4cfbc580e +Author: Florian Weimer +Date: Fri Aug 1 12:19:49 2025 +0200 + + elf: Handle ld.so with LOAD segment gaps in _dl_find_object (bug 31943) + + Detect if ld.so not contiguous and handle that case in _dl_find_object. + Set l_find_object_processed even for initially loaded link maps, + otherwise dlopen of an initially loaded object adds it to + _dlfo_loaded_mappings (where maps are expected to be contiguous), + in addition to _dlfo_nodelete_mappings. + + Test elf/tst-link-map-contiguous-ldso iterates over the loader + image, reading every word to make sure memory is actually mapped. + It only does that if the l_contiguous flag is set for the link map. + Otherwise, it finds gaps with mmap and checks that _dl_find_object + does not return the ld.so mapping for them. + + The test elf/tst-link-map-contiguous-main does the same thing for + the libc.so shared object. This only works if the kernel loaded + the main program because the glibc dynamic loader may fill + the gaps with PROT_NONE mappings in some cases, making it contiguous, + but accesses to individual words may still fault. + + Test elf/tst-link-map-contiguous-libc is again slightly different + because the dynamic loader always fills the gaps with PROT_NONE + mappings, so a different form of probing has to be used. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 20681be149b9eb1b6c1f4246bf4bd801221c86cd) + +diff --git a/NEWS b/NEWS +index 253b07ae99..1b3fd48158 100644 +--- a/NEWS ++++ b/NEWS +@@ -14,6 +14,7 @@ The following bugs are resolved with this release: + [31394] clone on sparc might fail with -EFAULT for no valid reason + [31717] elf: Avoid re-initializing already allocated TLS in dlopen + [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 + [31968] mremap implementation in C does not handle arguments correctly + [32026] strerror/strsignal TLS not handled correctly for secondary namespaces + [32052] Name space violation in fortify wrappers +diff --git a/elf/Makefile b/elf/Makefile +index 5abf61758d..b0b8c3aaf6 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -515,6 +515,8 @@ tests-internal += \ + 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 \ +@@ -526,6 +528,10 @@ tests-internal += \ + 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 \ +diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c +index 2e41fd064a..f258665a71 100644 +--- a/elf/dl-find_object.c ++++ b/elf/dl-find_object.c +@@ -465,6 +465,37 @@ _dl_find_object (void *pc1, struct dl_find_object *result) + } + 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 @@ _dlfo_process_initial (void) + + 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 @@ _dlfo_process_initial (void) + /* 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) + { +@@ -764,7 +785,6 @@ _dl_find_object_update_1 (struct link_map **loaded, size_t count) + /* 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; + } + +diff --git a/elf/dl-find_object.h b/elf/dl-find_object.h +index 0915065be0..8894c6657c 100644 +--- a/elf/dl-find_object.h ++++ b/elf/dl-find_object.h +@@ -87,7 +87,7 @@ _dl_find_object_to_external (struct dl_find_object_internal *internal, + } + + /* 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 @@ _dl_find_object_from_map (struct link_map *l, + 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 --git a/elf/rtld.c b/elf/rtld.c +index 6f50aeeafc..e577006708 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -1286,7 +1286,7 @@ rtld_setup_main_map (struct link_map *main_map) + + /* Set up the program header information for the dynamic linker + itself. It can be accessed via _r_debug and dl_iterate_phdr +- callbacks. */ ++ callbacks, and it is used by _dl_find_object. */ + static void + rtld_setup_phdr (void) + { +@@ -1304,6 +1304,29 @@ rtld_setup_phdr (void) + 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) +diff --git a/elf/tst-link-map-contiguous-ldso.c b/elf/tst-link-map-contiguous-ldso.c +new file mode 100644 +index 0000000000..04de808bb2 +--- /dev/null ++++ b/elf/tst-link-map-contiguous-ldso.c +@@ -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 --git a/elf/tst-link-map-contiguous-libc.c b/elf/tst-link-map-contiguous-libc.c +new file mode 100644 +index 0000000000..eb5728c765 +--- /dev/null ++++ b/elf/tst-link-map-contiguous-libc.c +@@ -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 --git a/elf/tst-link-map-contiguous-main.c b/elf/tst-link-map-contiguous-main.c +new file mode 100644 +index 0000000000..2d1a054f0f +--- /dev/null ++++ b/elf/tst-link-map-contiguous-main.c +@@ -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 + +commit ff17e7180af38356750cf829224c2b494af03502 +Author: Florian Weimer +Date: Fri May 16 19:53:09 2025 +0200 + + Remove + + Use __thread variables directly instead. The macros do not save any + typing. It seems unlikely that a future port will lack __thread + variable support. + + Some of the __libc_tsd_* variables are referenced from assembler + files, so keep their names. Previously, included + , which in turn included , so a few direct includes + of are now required. + + Reviewed-by: Frédéric Bérat + (cherry picked from commit 10a66a8e421b09682b774c795ef1da402235dddc) + +diff --git a/ctype/ctype-info.c b/ctype/ctype-info.c +index 9032547567..71d1c8e3b4 100644 +--- a/ctype/ctype-info.c ++++ b/ctype/ctype-info.c +@@ -19,20 +19,20 @@ + #include + #include + +-__libc_tsd_define (, const uint16_t *, CTYPE_B) +-__libc_tsd_define (, const int32_t *, CTYPE_TOLOWER) +-__libc_tsd_define (, const int32_t *, CTYPE_TOUPPER) ++__thread const uint16_t * __libc_tsd_CTYPE_B; ++__thread const int32_t * __libc_tsd_CTYPE_TOLOWER; ++__thread const int32_t * __libc_tsd_CTYPE_TOUPPER; + + + void + __ctype_init (void) + { +- const uint16_t **bp = __libc_tsd_address (const uint16_t *, CTYPE_B); +- *bp = (const uint16_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_CLASS) + 128; +- const int32_t **up = __libc_tsd_address (const int32_t *, CTYPE_TOUPPER); +- *up = ((int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOUPPER) + 128); +- const int32_t **lp = __libc_tsd_address (const int32_t *, CTYPE_TOLOWER); +- *lp = ((int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOLOWER) + 128); ++ __libc_tsd_CTYPE_B ++ = ((const uint16_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_CLASS)) + 128; ++ __libc_tsd_CTYPE_TOUPPER ++ = ((const int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOUPPER)) + 128; ++ __libc_tsd_CTYPE_TOLOWER = ++ ((const int32_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_TOLOWER)) + 128; + } + libc_hidden_def (__ctype_init) + +diff --git a/include/ctype.h b/include/ctype.h +index 493a6f80ce..e993adc86d 100644 +--- a/include/ctype.h ++++ b/include/ctype.h +@@ -24,33 +24,32 @@ libc_hidden_proto (toupper) + NL_CURRENT_INDIRECT. */ + + # include "../locale/localeinfo.h" +-# include + + # ifndef CTYPE_EXTERN_INLINE /* Used by ctype/ctype-info.c, which see. */ + # define CTYPE_EXTERN_INLINE extern inline + # endif + +-__libc_tsd_define (extern, const uint16_t *, CTYPE_B) +-__libc_tsd_define (extern, const int32_t *, CTYPE_TOUPPER) +-__libc_tsd_define (extern, const int32_t *, CTYPE_TOLOWER) ++extern __thread const uint16_t * __libc_tsd_CTYPE_B; ++extern __thread const int32_t * __libc_tsd_CTYPE_TOUPPER; ++extern __thread const int32_t * __libc_tsd_CTYPE_TOLOWER; + + + CTYPE_EXTERN_INLINE const uint16_t ** __attribute__ ((const)) + __ctype_b_loc (void) + { +- return __libc_tsd_address (const uint16_t *, CTYPE_B); ++ return &__libc_tsd_CTYPE_B; + } + + CTYPE_EXTERN_INLINE const int32_t ** __attribute__ ((const)) + __ctype_toupper_loc (void) + { +- return __libc_tsd_address (const int32_t *, CTYPE_TOUPPER); ++ return &__libc_tsd_CTYPE_TOUPPER; + } + + CTYPE_EXTERN_INLINE const int32_t ** __attribute__ ((const)) + __ctype_tolower_loc (void) + { +- return __libc_tsd_address (const int32_t *, CTYPE_TOLOWER); ++ return &__libc_tsd_CTYPE_TOLOWER; + } + + # ifndef __NO_CTYPE +diff --git a/include/rpc/rpc.h b/include/rpc/rpc.h +index f5cee6caef..936ea3cebb 100644 +--- a/include/rpc/rpc.h ++++ b/include/rpc/rpc.h +@@ -3,8 +3,6 @@ + + # ifndef _ISOMAC + +-#include +- + /* Now define the internal interfaces. */ + extern unsigned long _create_xid (void); + +@@ -47,7 +45,7 @@ extern void __rpc_thread_key_cleanup (void) attribute_hidden; + + extern void __rpc_thread_destroy (void) attribute_hidden; + +-__libc_tsd_define (extern, struct rpc_thread_variables *, RPC_VARS) ++extern __thread struct rpc_thread_variables *__libc_tsd_RPC_VARS; + + #define RPC_THREAD_VARIABLE(x) (__rpc_thread_variables()->x) + +diff --git a/locale/lc-ctype.c b/locale/lc-ctype.c +index c77ec51cb8..70556acaf0 100644 +--- a/locale/lc-ctype.c ++++ b/locale/lc-ctype.c +@@ -64,12 +64,9 @@ _nl_postload_ctype (void) + in fact using the global locale. */ + if (_NL_CURRENT_LOCALE == &_nl_global_locale) + { +- __libc_tsd_set (const uint16_t *, CTYPE_B, +- (void *) _nl_global_locale.__ctype_b); +- __libc_tsd_set (const int32_t *, CTYPE_TOUPPER, +- (void *) _nl_global_locale.__ctype_toupper); +- __libc_tsd_set (const int32_t *, CTYPE_TOLOWER, +- (void *) _nl_global_locale.__ctype_tolower); ++ __libc_tsd_CTYPE_B = _nl_global_locale.__ctype_b; ++ __libc_tsd_CTYPE_TOUPPER = _nl_global_locale.__ctype_toupper; ++ __libc_tsd_CTYPE_TOLOWER = _nl_global_locale.__ctype_tolower; + } + + #include +diff --git a/locale/localeinfo.h b/locale/localeinfo.h +index ed698faef1..bc8e92e4dc 100644 +--- a/locale/localeinfo.h ++++ b/locale/localeinfo.h +@@ -236,10 +236,8 @@ extern struct __locale_struct _nl_global_locale attribute_hidden; + + /* This fetches the thread-local locale_t pointer, either one set with + uselocale or &_nl_global_locale. */ +-#define _NL_CURRENT_LOCALE (__libc_tsd_get (locale_t, LOCALE)) +-#include +-__libc_tsd_define (extern, locale_t, LOCALE) +- ++#define _NL_CURRENT_LOCALE __libc_tsd_LOCALE ++extern __thread locale_t __libc_tsd_LOCALE; + + /* For static linking it is desireable to avoid always linking in the code + and data for every category when we can tell at link time that they are +diff --git a/locale/uselocale.c b/locale/uselocale.c +index 8136caf61b..0b247a77d5 100644 +--- a/locale/uselocale.c ++++ b/locale/uselocale.c +@@ -34,7 +34,7 @@ __uselocale (locale_t newloc) + { + const locale_t locobj + = newloc == LC_GLOBAL_LOCALE ? &_nl_global_locale : newloc; +- __libc_tsd_set (locale_t, LOCALE, locobj); ++ __libc_tsd_LOCALE = locobj; + + #ifdef NL_CURRENT_INDIRECT + /* Now we must update all the per-category thread-local variables to +@@ -62,11 +62,9 @@ __uselocale (locale_t newloc) + #endif + + /* Update the special tsd cache of some locale data. */ +- __libc_tsd_set (const uint16_t *, CTYPE_B, (void *) locobj->__ctype_b); +- __libc_tsd_set (const int32_t *, CTYPE_TOLOWER, +- (void *) locobj->__ctype_tolower); +- __libc_tsd_set (const int32_t *, CTYPE_TOUPPER, +- (void *) locobj->__ctype_toupper); ++ __libc_tsd_CTYPE_B = locobj->__ctype_b; ++ __libc_tsd_CTYPE_TOLOWER = locobj->__ctype_tolower; ++ __libc_tsd_CTYPE_TOUPPER = locobj->__ctype_toupper; + } + + return oldloc == &_nl_global_locale ? LC_GLOBAL_LOCALE : oldloc; +diff --git a/stdio-common/printf-parsemb.c b/stdio-common/printf-parsemb.c +index ab9fafb5ec..8db18f11b3 100644 +--- a/stdio-common/printf-parsemb.c ++++ b/stdio-common/printf-parsemb.c +@@ -17,6 +17,7 @@ + . */ + + #include ++#include + #include + #include + #include +diff --git a/string/strerror.c b/string/strerror.c +index 107d9d39c2..efa4e903ea 100644 +--- a/string/strerror.c ++++ b/string/strerror.c +@@ -21,5 +21,5 @@ + char * + strerror (int errnum) + { +- return __strerror_l (errnum, __libc_tsd_get (locale_t, LOCALE)); ++ return __strerror_l (errnum, __libc_tsd_LOCALE); + } +diff --git a/sunrpc/rpc_thread.c b/sunrpc/rpc_thread.c +index a04b7ec47f..e20f0a6230 100644 +--- a/sunrpc/rpc_thread.c ++++ b/sunrpc/rpc_thread.c +@@ -3,7 +3,6 @@ + #include + + #include +-#include + #include + #include + +diff --git a/sysdeps/generic/libc-tsd.h b/sysdeps/generic/libc-tsd.h +deleted file mode 100644 +index ac0e99e14b..0000000000 +--- a/sysdeps/generic/libc-tsd.h ++++ /dev/null +@@ -1,60 +0,0 @@ +-/* libc-internal interface for thread-specific data. Stub or TLS version. +- Copyright (C) 1998-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 +- . */ +- +-#ifndef _GENERIC_LIBC_TSD_H +-#define _GENERIC_LIBC_TSD_H 1 +- +-/* This file defines the following macros for accessing a small fixed +- set of thread-specific `void *' data used only internally by libc. +- +- __libc_tsd_define(CLASS, TYPE, KEY) -- Define or declare a datum with TYPE +- for KEY. CLASS can be `static' for +- keys used in only one source file, +- empty for global definitions, or +- `extern' for global declarations. +- __libc_tsd_address(TYPE, KEY) -- Return the `TYPE *' pointing to +- the current thread's datum for KEY. +- __libc_tsd_get(TYPE, KEY) -- Return the `TYPE' datum for KEY. +- __libc_tsd_set(TYPE, KEY, VALUE) -- Set the datum for KEY to VALUE. +- +- The set of available KEY's will usually be provided as an enum, +- and contains (at least): +- _LIBC_TSD_KEY_MALLOC +- _LIBC_TSD_KEY_DL_ERROR +- _LIBC_TSD_KEY_RPC_VARS +- All uses must be the literal _LIBC_TSD_* name in the __libc_tsd_* macros. +- Some implementations may not provide any enum at all and instead +- using string pasting in the macros. */ +- +-#include +- +-/* When full support for __thread variables is available, this interface is +- just a trivial wrapper for it. Without TLS, this is the generic/stub +- implementation for wholly single-threaded systems. +- +- We don't define an enum for the possible key values, because the KEYs +- translate directly into variables by macro magic. */ +- +-#define __libc_tsd_define(CLASS, TYPE, KEY) \ +- CLASS __thread TYPE __libc_tsd_##KEY attribute_tls_model_ie; +- +-#define __libc_tsd_address(TYPE, KEY) (&__libc_tsd_##KEY) +-#define __libc_tsd_get(TYPE, KEY) (__libc_tsd_##KEY) +-#define __libc_tsd_set(TYPE, KEY, VALUE) (__libc_tsd_##KEY = (VALUE)) +- +-#endif /* libc-tsd.h */ +diff --git a/time/strftime_l.c b/time/strftime_l.c +index 77adec9050..066c839c2f 100644 +--- a/time/strftime_l.c ++++ b/time/strftime_l.c +@@ -40,6 +40,7 @@ + #endif + + #include ++#include + #include /* Some systems define `time_t' here. */ + + #ifdef TIME_WITH_SYS_TIME + +commit a0cc6a7e1cb07fdf476241fbc84c67d01fb8efad +Author: Florian Weimer +Date: Fri May 16 19:53:09 2025 +0200 + + Use proper extern declaration for _nl_C_LC_CTYPE_{class,toupper,tolower} + + The existing initializers already contain explicit casts. Keep them + due to int/uint32_t mismatch. + + Reviewed-by: Frédéric Bérat + (cherry picked from commit e0c0f856f58ceb68800a964c36c15c606e7a8c4c) + +diff --git a/ctype/ctype-info.c b/ctype/ctype-info.c +index 71d1c8e3b4..94e312d91f 100644 +--- a/ctype/ctype-info.c ++++ b/ctype/ctype-info.c +@@ -41,10 +41,7 @@ libc_hidden_def (__ctype_init) + #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3) + + /* Defined in locale/C-ctype.c. */ +-extern const char _nl_C_LC_CTYPE_class[] attribute_hidden; + extern const char _nl_C_LC_CTYPE_class32[] attribute_hidden; +-extern const char _nl_C_LC_CTYPE_toupper[] attribute_hidden; +-extern const char _nl_C_LC_CTYPE_tolower[] attribute_hidden; + extern const char _nl_C_LC_CTYPE_class_upper[] attribute_hidden; + extern const char _nl_C_LC_CTYPE_class_lower[] attribute_hidden; + extern const char _nl_C_LC_CTYPE_class_alpha[] attribute_hidden; +diff --git a/include/ctype.h b/include/ctype.h +index e993adc86d..ae078a63d3 100644 +--- a/include/ctype.h ++++ b/include/ctype.h +@@ -63,6 +63,11 @@ __ctype_tolower_loc (void) + # define __isdigit_l(c, l) ({ int __c = (c); __c >= '0' && __c <= '9'; }) + # endif /* Not __NO_CTYPE. */ + ++/* For use in initializers. */ ++extern const char _nl_C_LC_CTYPE_class[] attribute_hidden; ++extern const uint32_t _nl_C_LC_CTYPE_toupper[] attribute_hidden; ++extern const uint32_t _nl_C_LC_CTYPE_tolower[] attribute_hidden; ++ + # endif /* IS_IN (libc). */ + #endif /* Not _ISOMAC. */ + +diff --git a/locale/xlocale.c b/locale/xlocale.c +index f2b9d03303..d11c1cbf8c 100644 +--- a/locale/xlocale.c ++++ b/locale/xlocale.c +@@ -18,18 +18,13 @@ + + #include + #include "localeinfo.h" ++#include + + #define DEFINE_CATEGORY(category, category_name, items, a) \ + extern struct __locale_data _nl_C_##category; + #include "categories.def" + #undef DEFINE_CATEGORY + +-/* Defined in locale/C-ctype.c. */ +-extern const char _nl_C_LC_CTYPE_class[] attribute_hidden; +-extern const char _nl_C_LC_CTYPE_toupper[] attribute_hidden; +-extern const char _nl_C_LC_CTYPE_tolower[] attribute_hidden; +- +- + const struct __locale_struct _nl_C_locobj attribute_hidden = + { + .__locales = + +commit 434e7e144a3825f910a5051af5f02d2e2198d4d9 +Author: Florian Weimer +Date: Fri May 16 19:53:09 2025 +0200 + + ctype: Fallback initialization of TLS using relocations (bug 19341, bug 32483) + + This ensures that the ctype data pointers in TLS are valid + in secondary namespaces even without initialization via + __ctype_init. + + Reviewed-by: Frédéric Bérat + (cherry picked from commit 2745db8dd3ec31045acd761b612516490085bc20) + +diff --git a/NEWS b/NEWS +index 1b3fd48158..c9982aabed 100644 +--- a/NEWS ++++ b/NEWS +@@ -9,6 +9,7 @@ Version 2.40.1 + + The following bugs are resolved with this release: + ++ [19341] ctype: Fallback initialization of TLS using relocations + [27821] ungetc: Fix backup buffer leak on program exit + [30081] resolv: Do not wait for non-existing second DNS response after error + [31394] clone on sparc might fail with -EFAULT for no valid reason +@@ -23,6 +24,7 @@ The following bugs are resolved with this release: + [32231] elf: Change ldconfig auxcache magic number + [32245] glibc -Wstringop-overflow= build failure on hppa + [32470] x86: Avoid integer truncation with large cache sizes ++ [32483] ctype macros segfault in multithreaded programs with multiple libc.so + [32810] Crash on x86-64 if XSAVEC disable via tunable + [32987] elf: Fix subprocess status handling for tst-dlopen-sgid + [33185] Fix double-free after allocation failure in regcomp +diff --git a/ctype/Makefile b/ctype/Makefile +index 3e09938bd1..b7cd5f2282 100644 +--- a/ctype/Makefile ++++ b/ctype/Makefile +@@ -36,6 +36,23 @@ aux := ctype-info + + tests := \ + test_ctype \ ++ tst-ctype-tls-dlmopen \ ++ tst-ctype-tls-dlopen-static \ + # tests + ++tests-static := \ ++ tst-ctype-tls-dlopen-static \ ++ # tests-static ++ ++modules-names := \ ++ tst-ctype-tls-mod \ ++ # modules-names ++ + include ../Rules ++ ++$(objpfx)tst-ctype-tls-dlmopen: $(shared-thread-library) ++$(objpfx)tst-ctype-tls-dlmopen.out: $(objpfx)tst-ctype-tls-mod.so ++$(objpfx)tst-ctype-tls-dlopen-static: $(static-thread-library) ++$(objpfx)tst-ctype-tls-dlopen-static.out: $(objpfx)tst-ctype-tls-mod.so ++tst-ctype-tls-dlopen-static-ENV = \ ++ LD_LIBRARY_PATH=$(ld-library-path):$(common-objpfx):$(common-objpfx)elf +diff --git a/ctype/ctype-info.c b/ctype/ctype-info.c +index 94e312d91f..621bd4239c 100644 +--- a/ctype/ctype-info.c ++++ b/ctype/ctype-info.c +@@ -19,9 +19,17 @@ + #include + #include + +-__thread const uint16_t * __libc_tsd_CTYPE_B; +-__thread const int32_t * __libc_tsd_CTYPE_TOLOWER; +-__thread const int32_t * __libc_tsd_CTYPE_TOUPPER; ++/* Fallback initialization using relocations. See the _nl_C_locobj ++ initializers in locale/xlocale.c. Usually, this is overwritten by ++ __ctype_init before user code runs, but this does not happen for ++ threads in secondary namespaces. With the initializers, secondary ++ namespaces at least get locale data from the C locale. */ ++__thread const uint16_t * __libc_tsd_CTYPE_B ++ = (const uint16_t *) _nl_C_LC_CTYPE_class + 128; ++__thread const int32_t * __libc_tsd_CTYPE_TOLOWER ++ = (const int32_t *) _nl_C_LC_CTYPE_tolower + 128; ++__thread const int32_t * __libc_tsd_CTYPE_TOUPPER ++ = (const int32_t *) _nl_C_LC_CTYPE_toupper + 128; + + + void +diff --git a/ctype/tst-ctype-tls-dlmopen.c b/ctype/tst-ctype-tls-dlmopen.c +new file mode 100644 +index 0000000000..f7eeb65551 +--- /dev/null ++++ b/ctype/tst-ctype-tls-dlmopen.c +@@ -0,0 +1,2 @@ ++#define DO_STATIC_TEST 0 ++#include "tst-ctype-tls-skeleton.c" +diff --git a/ctype/tst-ctype-tls-dlopen-static.c b/ctype/tst-ctype-tls-dlopen-static.c +new file mode 100644 +index 0000000000..c2c09c362c +--- /dev/null ++++ b/ctype/tst-ctype-tls-dlopen-static.c +@@ -0,0 +1,2 @@ ++#define DO_STATIC_TEST 1 ++#include "tst-ctype-tls-skeleton.c" +diff --git a/ctype/tst-ctype-tls-mod.c b/ctype/tst-ctype-tls-mod.c +new file mode 100644 +index 0000000000..52cbb9dcb6 +--- /dev/null ++++ b/ctype/tst-ctype-tls-mod.c +@@ -0,0 +1,37 @@ ++/* Wrappers for macros in a secondary namespace. ++ 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 ++ ++int ++my_isalpha (int ch) ++{ ++ return isalpha (ch); ++} ++ ++int ++my_toupper (int ch) ++{ ++ return toupper (ch); ++} ++ ++int ++my_tolower (int ch) ++{ ++ return tolower (ch); ++} +diff --git a/ctype/tst-ctype-tls-skeleton.c b/ctype/tst-ctype-tls-skeleton.c +new file mode 100644 +index 0000000000..8c53e35899 +--- /dev/null ++++ b/ctype/tst-ctype-tls-skeleton.c +@@ -0,0 +1,67 @@ ++/* Test that in a secondary namespace works. ++ 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 ++ . */ ++ ++/* Before this file is included, define DO_STATIC_TEST to 0 or 1. ++ With 0, dlmopen is used for the test. With 1, dlopen is used. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int (*my_isalpha) (int); ++static int (*my_toupper) (int); ++static int (*my_tolower) (int); ++ ++static void * ++checks (void *ignore) ++{ ++ TEST_VERIFY (my_isalpha ('a')); ++ TEST_VERIFY (!my_isalpha ('0')); ++ TEST_COMPARE (my_toupper ('a'), 'A'); ++ TEST_COMPARE (my_toupper ('A'), 'A'); ++ TEST_COMPARE (my_tolower ('a'), 'a'); ++ TEST_COMPARE (my_tolower ('A'), 'a'); ++ return NULL; ++} ++ ++static int ++do_test (void) ++{ ++ char *dso = xasprintf ("%s/ctype/tst-ctype-tls-mod.so", support_objdir_root); ++#if DO_STATIC_TEST ++ void *handle = xdlopen (dso, RTLD_LAZY); ++#else ++ void *handle = xdlmopen (LM_ID_NEWLM, dso, RTLD_LAZY); ++#endif ++ my_isalpha = xdlsym (handle, "my_isalpha"); ++ my_toupper = xdlsym (handle, "my_toupper"); ++ my_tolower = xdlsym (handle, "my_tolower"); ++ ++ checks (NULL); ++ xpthread_join (xpthread_create (NULL, checks, NULL)); ++ ++ xdlclose (handle); ++ free (dso); ++ ++ return 0; ++} ++ ++#include + +commit 1b305e7e53bd850542c9dccfcc17ff31cf605b62 +Author: Jens Remus +Date: Fri Jul 25 15:40:03 2025 +0200 + + Use TLS initial-exec model for __libc_tsd_CTYPE_* thread variables [BZ #33234] + + Commit 10a66a8e421b ("Remove ") removed the TLS initial-exec + (IE) model attribute from the __libc_tsd_CTYPE_* thread variable declarations + and definitions. Commit a894f04d8776 ("Optimize __libc_tsd_* thread + variable access") restored it on declarations. + + Restore the TLS initial-exec model attribute on __libc_tsd_CTYPE_* thread + variable definitions. + + This resolves test tst-locale1 failure on s390 32-bit, when using a + GNU linker without the fix from GNU binutils commit aefebe82dc89 + ("IBM zSystems: Fix offset relative to static TLS"). + + Reviewed-by: Florian Weimer + (cherry picked from commit e5363e6f460c2d58809bf10fc96d70fd1ef8b5b2) + +diff --git a/NEWS b/NEWS +index c9982aabed..acb5179088 100644 +--- a/NEWS ++++ b/NEWS +@@ -28,6 +28,7 @@ The following bugs are resolved with this release: + [32810] Crash on x86-64 if XSAVEC disable via tunable + [32987] elf: Fix subprocess status handling for tst-dlopen-sgid + [33185] Fix double-free after allocation failure in regcomp ++ [33234] Use TLS initial-exec model for __libc_tsd_CTYPE_* thread variables + + Version 2.40 + +diff --git a/ctype/ctype-info.c b/ctype/ctype-info.c +index 621bd4239c..b6cdf7eb66 100644 +--- a/ctype/ctype-info.c ++++ b/ctype/ctype-info.c +@@ -24,11 +24,11 @@ + __ctype_init before user code runs, but this does not happen for + threads in secondary namespaces. With the initializers, secondary + namespaces at least get locale data from the C locale. */ +-__thread const uint16_t * __libc_tsd_CTYPE_B ++__thread const uint16_t * __libc_tsd_CTYPE_B attribute_tls_model_ie + = (const uint16_t *) _nl_C_LC_CTYPE_class + 128; +-__thread const int32_t * __libc_tsd_CTYPE_TOLOWER ++__thread const int32_t * __libc_tsd_CTYPE_TOLOWER attribute_tls_model_ie + = (const int32_t *) _nl_C_LC_CTYPE_tolower + 128; +-__thread const int32_t * __libc_tsd_CTYPE_TOUPPER ++__thread const int32_t * __libc_tsd_CTYPE_TOUPPER attribute_tls_model_ie + = (const int32_t *) _nl_C_LC_CTYPE_toupper + 128; + + + +commit 666939940bfb6fc3c36b1039afcc3341a6c4ca0d +Author: Joe Ramsay +Date: Fri Jan 3 19:13:36 2025 +0000 + + math: Remove no-mathvec flag + + More routines are to follow, some of which hit many failures in the + current testsuite due to wrong sign of zero (mathvec routines are not + required to get this right). Instead of disabling a large number of + tests, change the failure condition such that, for vector routines, + tests pass as long as computed == expected == 0.0, regardless of sign. + + Affected tests (vector tests for expm1, log1p, sin, tan and tanh) all + still pass. + + (cherry picked from commit 939e770e0196ebd763cacc602421b76d62df0798) + +diff --git a/math/auto-libm-test-in b/math/auto-libm-test-in +index 8f0c3d87bb..7100171346 100644 +--- a/math/auto-libm-test-in ++++ b/math/auto-libm-test-in +@@ -5555,7 +5555,7 @@ exp2m1 -0x1p-1021 + exp2m1 -0x1p-16381 + + expm1 0 +-expm1 -0 no-mathvec ++expm1 -0 + expm1 1 + expm1 0.75 + expm1 2 +@@ -5620,7 +5620,7 @@ expm1 -0x1p-100 + expm1 0x1p-600 + expm1 -0x1p-600 + expm1 0x1p-10000 +-expm1 -0x1p-10000 no-mathvec ++expm1 -0x1p-10000 + expm1 0xe.4152ac57cd1ea7ap-60 + expm1 0x6.660247486aed8p-4 + expm1 0x6.289a78p-4 +@@ -6835,7 +6835,7 @@ log10p1 -0x6.3fef3067427e43dfcde9e48f74bcp-4 + log10p1 0x6.af53d00fd2845d4772260ef5adc4p-4 + + log1p 0 +-log1p -0 no-mathvec ++log1p -0 + log1p e-1 + log1p -0.25 + log1p -0.875 +@@ -7627,7 +7627,7 @@ pow 0x1.7ac7cp+5 23 + pow -0x1.7ac7cp+5 23 + + sin 0 +-sin -0 no-mathvec ++sin -0 + sin pi/6 + sin -pi/6 + sin pi/2 +@@ -7964,7 +7964,7 @@ sqrt min + sqrt min_subnorm + + tan 0 +-tan -0 no-mathvec ++tan -0 + tan pi/4 + tan pi/2 + tan -pi/2 +@@ -8056,7 +8056,7 @@ tan min_subnorm + tan -min_subnorm + + tanh 0 +-tanh -0 no-mathvec ++tanh -0 + tanh 0.75 + tanh -0.75 + tanh 1.0 +diff --git a/math/auto-libm-test-out-expm1 b/math/auto-libm-test-out-expm1 +index 91da41b7f6..8483455801 100644 +--- a/math/auto-libm-test-out-expm1 ++++ b/math/auto-libm-test-out-expm1 +@@ -23,31 +23,31 @@ expm1 0 + = expm1 tonearest ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = expm1 towardzero ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = expm1 upward ibm128 0x0p+0 : 0x0p+0 : inexact-ok +-expm1 -0 no-mathvec +-= expm1 downward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok ++expm1 -0 ++= expm1 downward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok + expm1 1 + = expm1 downward binary32 0x1p+0 : 0x1.b7e15p+0 : inexact-ok + = expm1 tonearest binary32 0x1p+0 : 0x1.b7e152p+0 : inexact-ok +@@ -1880,87 +1880,87 @@ expm1 0x1p-10000 + = expm1 tonearest binary128 0x1p-10000 : 0x1p-10000 : inexact-ok + = expm1 towardzero binary128 0x1p-10000 : 0x1p-10000 : inexact-ok + = expm1 upward binary128 0x1p-10000 : 0x1.0000000000000000000000000001p-10000 : inexact-ok +-expm1 -0x1p-10000 no-mathvec +-= expm1 downward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 tonearest ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 towardzero ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 upward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= expm1 downward binary32 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok underflow errno-erange-ok +-= expm1 tonearest binary32 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok underflow errno-erange-ok +-= expm1 towardzero binary32 -0x8p-152 : -0x0p+0 : no-mathvec inexact-ok underflow errno-erange-ok +-= expm1 upward binary32 -0x8p-152 : -0x0p+0 : no-mathvec inexact-ok underflow errno-erange-ok +-= expm1 downward binary64 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 tonearest binary64 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 towardzero binary64 -0x8p-152 : -0x7.ffffffffffffcp-152 : no-mathvec inexact-ok +-= expm1 upward binary64 -0x8p-152 : -0x7.ffffffffffffcp-152 : no-mathvec inexact-ok +-= expm1 downward intel96 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 tonearest intel96 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 towardzero intel96 -0x8p-152 : -0x7.fffffffffffffff8p-152 : no-mathvec inexact-ok +-= expm1 upward intel96 -0x8p-152 : -0x7.fffffffffffffff8p-152 : no-mathvec inexact-ok +-= expm1 downward m68k96 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 tonearest m68k96 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 towardzero m68k96 -0x8p-152 : -0x7.fffffffffffffff8p-152 : no-mathvec inexact-ok +-= expm1 upward m68k96 -0x8p-152 : -0x7.fffffffffffffff8p-152 : no-mathvec inexact-ok +-= expm1 downward binary128 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 tonearest binary128 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 towardzero binary128 -0x8p-152 : -0x7.fffffffffffffffffffffffffffcp-152 : no-mathvec inexact-ok +-= expm1 upward binary128 -0x8p-152 : -0x7.fffffffffffffffffffffffffffcp-152 : no-mathvec inexact-ok +-= expm1 downward ibm128 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 tonearest ibm128 -0x8p-152 : -0x8p-152 : no-mathvec inexact-ok +-= expm1 towardzero ibm128 -0x8p-152 : -0x7.fffffffffffffffffffffffffep-152 : no-mathvec inexact-ok +-= expm1 upward ibm128 -0x8p-152 : -0x7.fffffffffffffffffffffffffep-152 : no-mathvec inexact-ok +-= expm1 downward binary64 -0x4p-1076 : -0x4p-1076 : no-mathvec inexact-ok underflow errno-erange-ok +-= expm1 tonearest binary64 -0x4p-1076 : -0x4p-1076 : no-mathvec inexact-ok underflow errno-erange-ok +-= expm1 towardzero binary64 -0x4p-1076 : -0x0p+0 : no-mathvec inexact-ok underflow errno-erange-ok +-= expm1 upward binary64 -0x4p-1076 : -0x0p+0 : no-mathvec inexact-ok underflow errno-erange-ok +-= expm1 downward intel96 -0x4p-1076 : -0x4p-1076 : no-mathvec inexact-ok +-= expm1 tonearest intel96 -0x4p-1076 : -0x4p-1076 : no-mathvec inexact-ok +-= expm1 towardzero intel96 -0x4p-1076 : -0x3.fffffffffffffffcp-1076 : no-mathvec inexact-ok +-= expm1 upward intel96 -0x4p-1076 : -0x3.fffffffffffffffcp-1076 : no-mathvec inexact-ok +-= expm1 downward m68k96 -0x4p-1076 : -0x4p-1076 : no-mathvec inexact-ok +-= expm1 tonearest m68k96 -0x4p-1076 : -0x4p-1076 : no-mathvec inexact-ok +-= expm1 towardzero m68k96 -0x4p-1076 : -0x3.fffffffffffffffcp-1076 : no-mathvec inexact-ok +-= expm1 upward m68k96 -0x4p-1076 : -0x3.fffffffffffffffcp-1076 : no-mathvec inexact-ok +-= expm1 downward binary128 -0x4p-1076 : -0x4p-1076 : no-mathvec inexact-ok +-= expm1 tonearest binary128 -0x4p-1076 : -0x4p-1076 : no-mathvec inexact-ok +-= expm1 towardzero binary128 -0x4p-1076 : -0x3.fffffffffffffffffffffffffffep-1076 : no-mathvec inexact-ok +-= expm1 upward binary128 -0x4p-1076 : -0x3.fffffffffffffffffffffffffffep-1076 : no-mathvec inexact-ok +-= expm1 downward ibm128 -0x4p-1076 : -0x4p-1076 : no-mathvec xfail:ibm128-libgcc inexact-ok underflow errno-erange-ok +-= expm1 tonearest ibm128 -0x4p-1076 : -0x4p-1076 : no-mathvec inexact-ok underflow errno-erange-ok +-= expm1 towardzero ibm128 -0x4p-1076 : -0x0p+0 : no-mathvec xfail:ibm128-libgcc inexact-ok underflow errno-erange-ok +-= expm1 upward ibm128 -0x4p-1076 : -0x0p+0 : no-mathvec xfail:ibm128-libgcc inexact-ok underflow errno-erange-ok +-= expm1 downward intel96 -0x1p-10000 : -0x1p-10000 : no-mathvec inexact-ok +-= expm1 tonearest intel96 -0x1p-10000 : -0x1p-10000 : no-mathvec inexact-ok +-= expm1 towardzero intel96 -0x1p-10000 : -0xf.fffffffffffffffp-10004 : no-mathvec inexact-ok +-= expm1 upward intel96 -0x1p-10000 : -0xf.fffffffffffffffp-10004 : no-mathvec inexact-ok +-= expm1 downward m68k96 -0x1p-10000 : -0x1p-10000 : no-mathvec inexact-ok +-= expm1 tonearest m68k96 -0x1p-10000 : -0x1p-10000 : no-mathvec inexact-ok +-= expm1 towardzero m68k96 -0x1p-10000 : -0xf.fffffffffffffffp-10004 : no-mathvec inexact-ok +-= expm1 upward m68k96 -0x1p-10000 : -0xf.fffffffffffffffp-10004 : no-mathvec inexact-ok +-= expm1 downward binary128 -0x1p-10000 : -0x1p-10000 : no-mathvec inexact-ok +-= expm1 tonearest binary128 -0x1p-10000 : -0x1p-10000 : no-mathvec inexact-ok +-= expm1 towardzero binary128 -0x1p-10000 : -0xf.fffffffffffffffffffffffffff8p-10004 : no-mathvec inexact-ok +-= expm1 upward binary128 -0x1p-10000 : -0xf.fffffffffffffffffffffffffff8p-10004 : no-mathvec inexact-ok ++expm1 -0x1p-10000 ++= expm1 downward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 tonearest ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 towardzero ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 upward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= expm1 downward binary32 -0x8p-152 : -0x8p-152 : inexact-ok underflow errno-erange-ok ++= expm1 tonearest binary32 -0x8p-152 : -0x8p-152 : inexact-ok underflow errno-erange-ok ++= expm1 towardzero binary32 -0x8p-152 : -0x0p+0 : inexact-ok underflow errno-erange-ok ++= expm1 upward binary32 -0x8p-152 : -0x0p+0 : inexact-ok underflow errno-erange-ok ++= expm1 downward binary64 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 tonearest binary64 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 towardzero binary64 -0x8p-152 : -0x7.ffffffffffffcp-152 : inexact-ok ++= expm1 upward binary64 -0x8p-152 : -0x7.ffffffffffffcp-152 : inexact-ok ++= expm1 downward intel96 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 tonearest intel96 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 towardzero intel96 -0x8p-152 : -0x7.fffffffffffffff8p-152 : inexact-ok ++= expm1 upward intel96 -0x8p-152 : -0x7.fffffffffffffff8p-152 : inexact-ok ++= expm1 downward m68k96 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 tonearest m68k96 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 towardzero m68k96 -0x8p-152 : -0x7.fffffffffffffff8p-152 : inexact-ok ++= expm1 upward m68k96 -0x8p-152 : -0x7.fffffffffffffff8p-152 : inexact-ok ++= expm1 downward binary128 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 tonearest binary128 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 towardzero binary128 -0x8p-152 : -0x7.fffffffffffffffffffffffffffcp-152 : inexact-ok ++= expm1 upward binary128 -0x8p-152 : -0x7.fffffffffffffffffffffffffffcp-152 : inexact-ok ++= expm1 downward ibm128 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 tonearest ibm128 -0x8p-152 : -0x8p-152 : inexact-ok ++= expm1 towardzero ibm128 -0x8p-152 : -0x7.fffffffffffffffffffffffffep-152 : inexact-ok ++= expm1 upward ibm128 -0x8p-152 : -0x7.fffffffffffffffffffffffffep-152 : inexact-ok ++= expm1 downward binary64 -0x4p-1076 : -0x4p-1076 : inexact-ok underflow errno-erange-ok ++= expm1 tonearest binary64 -0x4p-1076 : -0x4p-1076 : inexact-ok underflow errno-erange-ok ++= expm1 towardzero binary64 -0x4p-1076 : -0x0p+0 : inexact-ok underflow errno-erange-ok ++= expm1 upward binary64 -0x4p-1076 : -0x0p+0 : inexact-ok underflow errno-erange-ok ++= expm1 downward intel96 -0x4p-1076 : -0x4p-1076 : inexact-ok ++= expm1 tonearest intel96 -0x4p-1076 : -0x4p-1076 : inexact-ok ++= expm1 towardzero intel96 -0x4p-1076 : -0x3.fffffffffffffffcp-1076 : inexact-ok ++= expm1 upward intel96 -0x4p-1076 : -0x3.fffffffffffffffcp-1076 : inexact-ok ++= expm1 downward m68k96 -0x4p-1076 : -0x4p-1076 : inexact-ok ++= expm1 tonearest m68k96 -0x4p-1076 : -0x4p-1076 : inexact-ok ++= expm1 towardzero m68k96 -0x4p-1076 : -0x3.fffffffffffffffcp-1076 : inexact-ok ++= expm1 upward m68k96 -0x4p-1076 : -0x3.fffffffffffffffcp-1076 : inexact-ok ++= expm1 downward binary128 -0x4p-1076 : -0x4p-1076 : inexact-ok ++= expm1 tonearest binary128 -0x4p-1076 : -0x4p-1076 : inexact-ok ++= expm1 towardzero binary128 -0x4p-1076 : -0x3.fffffffffffffffffffffffffffep-1076 : inexact-ok ++= expm1 upward binary128 -0x4p-1076 : -0x3.fffffffffffffffffffffffffffep-1076 : inexact-ok ++= expm1 downward ibm128 -0x4p-1076 : -0x4p-1076 : xfail:ibm128-libgcc inexact-ok underflow errno-erange-ok ++= expm1 tonearest ibm128 -0x4p-1076 : -0x4p-1076 : inexact-ok underflow errno-erange-ok ++= expm1 towardzero ibm128 -0x4p-1076 : -0x0p+0 : xfail:ibm128-libgcc inexact-ok underflow errno-erange-ok ++= expm1 upward ibm128 -0x4p-1076 : -0x0p+0 : xfail:ibm128-libgcc inexact-ok underflow errno-erange-ok ++= expm1 downward intel96 -0x1p-10000 : -0x1p-10000 : inexact-ok ++= expm1 tonearest intel96 -0x1p-10000 : -0x1p-10000 : inexact-ok ++= expm1 towardzero intel96 -0x1p-10000 : -0xf.fffffffffffffffp-10004 : inexact-ok ++= expm1 upward intel96 -0x1p-10000 : -0xf.fffffffffffffffp-10004 : inexact-ok ++= expm1 downward m68k96 -0x1p-10000 : -0x1p-10000 : inexact-ok ++= expm1 tonearest m68k96 -0x1p-10000 : -0x1p-10000 : inexact-ok ++= expm1 towardzero m68k96 -0x1p-10000 : -0xf.fffffffffffffffp-10004 : inexact-ok ++= expm1 upward m68k96 -0x1p-10000 : -0xf.fffffffffffffffp-10004 : inexact-ok ++= expm1 downward binary128 -0x1p-10000 : -0x1p-10000 : inexact-ok ++= expm1 tonearest binary128 -0x1p-10000 : -0x1p-10000 : inexact-ok ++= expm1 towardzero binary128 -0x1p-10000 : -0xf.fffffffffffffffffffffffffff8p-10004 : inexact-ok ++= expm1 upward binary128 -0x1p-10000 : -0xf.fffffffffffffffffffffffffff8p-10004 : inexact-ok + expm1 0xe.4152ac57cd1ea7ap-60 + = expm1 downward binary32 0xe.4152bp-60 : 0xe.4152bp-60 : inexact-ok + = expm1 tonearest binary32 0xe.4152bp-60 : 0xe.4152bp-60 : inexact-ok +diff --git a/math/auto-libm-test-out-log1p b/math/auto-libm-test-out-log1p +index f83241f51a..f7d3b35e6d 100644 +--- a/math/auto-libm-test-out-log1p ++++ b/math/auto-libm-test-out-log1p +@@ -23,31 +23,31 @@ log1p 0 + = log1p tonearest ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = log1p towardzero ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = log1p upward ibm128 0x0p+0 : 0x0p+0 : inexact-ok +-log1p -0 no-mathvec +-= log1p downward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p tonearest binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p towardzero binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p upward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p downward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p tonearest binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p towardzero binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p upward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p downward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p tonearest intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p towardzero intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p upward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p downward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p tonearest m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p towardzero m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p upward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p downward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p tonearest binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p towardzero binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p upward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p downward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p tonearest ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p towardzero ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= log1p upward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok ++log1p -0 ++= log1p downward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p tonearest binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p towardzero binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p upward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p downward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p tonearest binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p towardzero binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p upward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p downward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p tonearest intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p towardzero intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p upward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p downward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p tonearest m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p towardzero m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p upward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p downward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p tonearest binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p towardzero binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p upward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p downward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p tonearest ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p towardzero ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= log1p upward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok + log1p e-1 + = log1p downward binary32 0x1.b7e152p+0 : 0x1p+0 : inexact-ok + = log1p tonearest binary32 0x1.b7e152p+0 : 0x1p+0 : inexact-ok +diff --git a/math/auto-libm-test-out-sin b/math/auto-libm-test-out-sin +index e1f6845283..f1d21b179c 100644 +--- a/math/auto-libm-test-out-sin ++++ b/math/auto-libm-test-out-sin +@@ -23,31 +23,31 @@ sin 0 + = sin tonearest ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = sin towardzero ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = sin upward ibm128 0x0p+0 : 0x0p+0 : inexact-ok +-sin -0 no-mathvec +-= sin downward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin tonearest binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin towardzero binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin upward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin downward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin tonearest binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin towardzero binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin upward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin downward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin tonearest intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin towardzero intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin upward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin downward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin tonearest m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin towardzero m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin upward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin downward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin tonearest binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin towardzero binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin upward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin downward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin tonearest ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin towardzero ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= sin upward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok ++sin -0 ++= sin downward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin tonearest binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin towardzero binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin upward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin downward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin tonearest binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin towardzero binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin upward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin downward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin tonearest intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin towardzero intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin upward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin downward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin tonearest m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin towardzero m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin upward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin downward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin tonearest binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin towardzero binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin upward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin downward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin tonearest ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin towardzero ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= sin upward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok + sin pi/6 + = sin downward binary32 0x8.60a92p-4 : 0x8p-4 : inexact-ok + = sin tonearest binary32 0x8.60a92p-4 : 0x8p-4 : inexact-ok +diff --git a/math/auto-libm-test-out-tan b/math/auto-libm-test-out-tan +index f46fdc7ec6..7d00d03e1d 100644 +--- a/math/auto-libm-test-out-tan ++++ b/math/auto-libm-test-out-tan +@@ -23,31 +23,31 @@ tan 0 + = tan tonearest ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = tan towardzero ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = tan upward ibm128 0x0p+0 : 0x0p+0 : inexact-ok +-tan -0 no-mathvec +-= tan downward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan tonearest binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan towardzero binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan upward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan downward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan tonearest binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan towardzero binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan upward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan downward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan tonearest intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan towardzero intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan upward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan downward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan tonearest m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan towardzero m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan upward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan downward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan tonearest binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan towardzero binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan upward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan downward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan tonearest ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan towardzero ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tan upward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok ++tan -0 ++= tan downward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan tonearest binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan towardzero binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan upward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan downward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan tonearest binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan towardzero binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan upward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan downward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan tonearest intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan towardzero intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan upward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan downward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan tonearest m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan towardzero m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan upward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan downward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan tonearest binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan towardzero binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan upward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan downward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan tonearest ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan towardzero ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tan upward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok + tan pi/4 + = tan downward binary32 0xc.90fdbp-4 : 0x1p+0 : inexact-ok + = tan tonearest binary32 0xc.90fdbp-4 : 0x1p+0 : inexact-ok +diff --git a/math/auto-libm-test-out-tanh b/math/auto-libm-test-out-tanh +index 19ce2e7b93..8b9427c917 100644 +--- a/math/auto-libm-test-out-tanh ++++ b/math/auto-libm-test-out-tanh +@@ -23,31 +23,31 @@ tanh 0 + = tanh tonearest ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = tanh towardzero ibm128 0x0p+0 : 0x0p+0 : inexact-ok + = tanh upward ibm128 0x0p+0 : 0x0p+0 : inexact-ok +-tanh -0 no-mathvec +-= tanh downward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh tonearest binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh towardzero binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh upward binary32 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh downward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh tonearest binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh towardzero binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh upward binary64 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh downward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh tonearest intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh towardzero intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh upward intel96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh downward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh tonearest m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh towardzero m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh upward m68k96 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh downward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh tonearest binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh towardzero binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh upward binary128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh downward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh tonearest ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh towardzero ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok +-= tanh upward ibm128 -0x0p+0 : -0x0p+0 : no-mathvec inexact-ok ++tanh -0 ++= tanh downward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh tonearest binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh towardzero binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh upward binary32 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh downward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh tonearest binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh towardzero binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh upward binary64 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh downward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh tonearest intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh towardzero intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh upward intel96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh downward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh tonearest m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh towardzero m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh upward m68k96 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh downward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh tonearest binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh towardzero binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh upward binary128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh downward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh tonearest ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh towardzero ibm128 -0x0p+0 : -0x0p+0 : inexact-ok ++= tanh upward ibm128 -0x0p+0 : -0x0p+0 : inexact-ok + tanh 0.75 + = tanh downward binary32 0xcp-4 : 0xa.2991fp-4 : inexact-ok + = tanh tonearest binary32 0xcp-4 : 0xa.2991fp-4 : inexact-ok +diff --git a/math/gen-auto-libm-tests.c b/math/gen-auto-libm-tests.c +index 0b1e307fae..ff8a22b90f 100644 +--- a/math/gen-auto-libm-tests.c ++++ b/math/gen-auto-libm-tests.c +@@ -96,8 +96,7 @@ + zero and infinite results should be ignored; "xfail" indicates the + test is disabled as expected to produce incorrect results, + "xfail-rounding" indicates the test is disabled only in rounding +- modes other than round-to-nearest; "no-mathvec" indicates the test +- is disabled in vector math libraries. Otherwise, test flags are of ++ modes other than round-to-nearest. Otherwise, test flags are of + the form "spurious-" and "missing-", for any + exception ("overflow", "underflow", "inexact", "invalid", + "divbyzero"), "spurious-errno" and "missing-errno", to indicate +@@ -353,7 +352,6 @@ typedef enum + flag_missing_overflow, + flag_missing_underflow, + flag_missing_errno, +- flag_no_mathvec, + num_input_flag_types, + flag_first_flag = 0, + flag_spurious_first = flag_spurious_divbyzero, +@@ -379,7 +377,6 @@ static const char *const input_flags[num_input_flag_types] = + "missing-overflow", + "missing-underflow", + "missing-errno", +- "no-mathvec", + }; + + /* An input flag, possibly conditional. */ +@@ -2056,7 +2053,6 @@ output_for_one_input_case (FILE *fp, const char *filename, test_function *tf, + { + case flag_ignore_zero_inf_sign: + case flag_xfail: +- case flag_no_mathvec: + if (fprintf (fp, " %s%s", + input_flags[it->flags[i].type], + (it->flags[i].cond +diff --git a/math/gen-libm-test.py b/math/gen-libm-test.py +index 397dbd3259..6e8bb56437 100755 +--- a/math/gen-libm-test.py ++++ b/math/gen-libm-test.py +@@ -93,8 +93,7 @@ BEAUTIFY_MAP = {'minus_zero': '-0', + + # Flags in auto-libm-test-out that map directly to C flags. + FLAGS_SIMPLE = {'ignore-zero-inf-sign': 'IGNORE_ZERO_INF_SIGN', +- 'xfail': 'XFAIL_TEST', +- 'no-mathvec': 'NO_TEST_MATHVEC'} ++ 'xfail': 'XFAIL_TEST'} + + # Exceptions in auto-libm-test-out, and their corresponding C flags + # for being required, OK or required to be absent. +diff --git a/math/libm-test-support.c b/math/libm-test-support.c +index 0796f9d495..3fecd87064 100644 +--- a/math/libm-test-support.c ++++ b/math/libm-test-support.c +@@ -776,7 +776,7 @@ check_float_internal (const char *test_name, FLOAT computed, FLOAT expected, + ulps = ULPDIFF (computed, expected); + set_max_error (ulps, curr_max_error); + print_diff = 1; +- if ((exceptions & IGNORE_ZERO_INF_SIGN) == 0 ++ if (((exceptions & IGNORE_ZERO_INF_SIGN) == 0) && !flag_test_mathvec + && computed == 0.0 && expected == 0.0 + && signbit(computed) != signbit (expected)) + ok = 0; + +commit 6916013ce98feaa6f86380d79f545aae10aecb10 +Author: H.J. Lu +Date: Mon Jul 28 12:18:22 2025 -0700 + + x86-64: Add GLIBC_ABI_GNU2_TLS version [BZ #33129] + + Programs and shared libraries compiled with -mtls-dialect=gnu2 may fail + silently at run-time against glibc without the GNU2 TLS run-time fix + for: + + https://sourceware.org/bugzilla/show_bug.cgi?id=31372 + + Add GLIBC_ABI_GNU2_TLS version to indicate that glibc has the working + GNU2 TLS run-time. Linker can add the GLIBC_ABI_GNU2_TLS version to + binaries which depend on the working GNU2 TLS run-time: + + https://sourceware.org/bugzilla/show_bug.cgi?id=33130 + + so that such programs and shared libraries will fail to load and run at + run-time against libc.so without the GLIBC_ABI_GNU2_TLS version, instead + of fail silently at random. + + This fixes BZ #33129. + + Signed-off-by: H.J. Lu + Reviewed-by: Sam James + (cherry picked from commit 9df8fa397d515dc86ff5565f6c45625e672d539e) + +diff --git a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile +index ce949dba27..a54c9be4bf 100644 +--- a/sysdeps/x86_64/Makefile ++++ b/sysdeps/x86_64/Makefile +@@ -209,6 +209,15 @@ LDFLAGS-tst-plt-rewrite2 = -Wl,-z,now + LDFLAGS-tst-plt-rewritemod2.so = -Wl,-z,now,-z,undefs + tst-plt-rewrite2-ENV = GLIBC_TUNABLES=glibc.cpu.plt_rewrite=2 + $(objpfx)tst-plt-rewrite2: $(objpfx)tst-plt-rewritemod2.so ++ ++tests-special += $(objpfx)check-gnu2-tls.out ++ ++$(objpfx)check-gnu2-tls.out: $(common-objpfx)libc.so ++ LC_ALL=C $(READELF) -V -W $< \ ++ | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ ++ | grep GLIBC_ABI_GNU2_TLS > $@; \ ++ $(evaluate-test) ++generated += check-gnu2-tls.out + endif + + test-internal-extras += tst-gnu2-tls2mod1 +diff --git a/sysdeps/x86_64/Versions b/sysdeps/x86_64/Versions +index e94758b236..a63c11bcb2 100644 +--- a/sysdeps/x86_64/Versions ++++ b/sysdeps/x86_64/Versions +@@ -5,6 +5,11 @@ libc { + GLIBC_2.13 { + __fentry__; + } ++ GLIBC_ABI_GNU2_TLS { ++ # 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 { + +commit 62e2c1d21fdf74f6146977ba97ace2efc61a3c18 +Author: H.J. Lu +Date: Thu Aug 14 07:03:20 2025 -0700 + + x86-64: Add GLIBC_ABI_DT_X86_64_PLT [BZ #33212] + + When the linker -z mark-plt option is used to add DT_X86_64_PLT, + DT_X86_64_PLTSZ and DT_X86_64_PLTENT, the r_addend field of the + R_X86_64_JUMP_SLOT relocation stores the offset of the indirect + branch instruction. However, glibc versions without the commit: + + commit f8587a61892cbafd98ce599131bf4f103466f084 + Author: H.J. Lu + Date: Fri May 20 19:21:48 2022 -0700 + + x86-64: Ignore r_addend for R_X86_64_GLOB_DAT/R_X86_64_JUMP_SLOT + + According to x86-64 psABI, r_addend should be ignored for R_X86_64_GLOB_DAT + and R_X86_64_JUMP_SLOT. Since linkers always set their r_addends to 0, we + can ignore their r_addends. + + Reviewed-by: Fangrui Song + + won't ignore the r_addend value in the R_X86_64_JUMP_SLOT relocation. + Such programs and shared libraries will fail at run-time randomly. + + Add GLIBC_ABI_DT_X86_64_PLT version to indicate that glibc is compatible + with DT_X86_64_PLT. + + The linker can add the glibc GLIBC_ABI_DT_X86_64_PLT version dependency + whenever -z mark-plt is passed to the linker. The resulting programs and + shared libraries will fail to load at run-time against libc.so without the + GLIBC_ABI_DT_X86_64_PLT version, instead of fail randomly. + + This fixes BZ #33212. + + Signed-off-by: H.J. Lu + Reviewed-by: Sam James + (cherry picked from commit 399384e0c8193e31aea014220ccfa24300ae5938) + +diff --git a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile +index a54c9be4bf..e4895bd730 100644 +--- a/sysdeps/x86_64/Makefile ++++ b/sysdeps/x86_64/Makefile +@@ -210,6 +210,15 @@ LDFLAGS-tst-plt-rewritemod2.so = -Wl,-z,now,-z,undefs + tst-plt-rewrite2-ENV = GLIBC_TUNABLES=glibc.cpu.plt_rewrite=2 + $(objpfx)tst-plt-rewrite2: $(objpfx)tst-plt-rewritemod2.so + ++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 ++ + tests-special += $(objpfx)check-gnu2-tls.out + + $(objpfx)check-gnu2-tls.out: $(common-objpfx)libc.so +diff --git a/sysdeps/x86_64/Versions b/sysdeps/x86_64/Versions +index a63c11bcb2..0a759029e5 100644 +--- a/sysdeps/x86_64/Versions ++++ b/sysdeps/x86_64/Versions +@@ -10,6 +10,11 @@ libc { + # by scripts/versions.awk. + __placeholder_only_for_empty_version_map; + } ++ 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 { + +commit e08360c48482ef8836030af6814c31881e1c4a4c +Author: Florian Weimer +Date: Fri Oct 25 16:50:10 2024 +0200 + + elf: Run constructors on cyclic recursive dlopen (bug 31986) + + This is conceptually similar to the reported bug, but does not + depend on auditing. The fix is simple: just complete execution + of the constructors. This exposed the fact that the link map + for statically linked executables does not have l_init_called + set, even though constructors have run. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 9897ced8e78db5d813166a7ccccfd5a42c69ef20) + +diff --git a/elf/Makefile b/elf/Makefile +index b0b8c3aaf6..83b5ceff9c 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -416,6 +416,7 @@ tests += \ + tst-dlmopen1 \ + tst-dlmopen3 \ + tst-dlmopen4 \ ++ tst-dlopen-recurse \ + tst-dlopen-self \ + tst-dlopen-tlsmodid \ + tst-dlopen-tlsreinit1 \ +@@ -866,6 +867,8 @@ modules-names += \ + tst-dlmopen-twice-mod1 \ + tst-dlmopen-twice-mod2 \ + tst-dlmopen1mod \ ++ tst-dlopen-recursemod1 \ ++ tst-dlopen-recursemod2 \ + tst-dlopen-sgid-mod \ + tst-dlopen-tlsreinitmod1 \ + tst-dlopen-tlsreinitmod2 \ +@@ -3163,3 +3166,6 @@ $(objpfx)tst-dlopen-tlsreinit4.out: $(objpfx)tst-auditmod1.so + tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + + $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so ++ ++$(objpfx)tst-dlopen-recurse.out: $(objpfx)tst-dlopen-recursemod1.so ++$(objpfx)tst-dlopen-recursemod1.so: $(objpfx)tst-dlopen-recursemod2.so +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 8556e7bd2f..5139d276e0 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -601,6 +601,14 @@ dl_open_worker_begin (void *a) + = _dl_debug_update (args->nsid)->r_state; + assert (r_state == RT_CONSISTENT); + ++ /* Do not return without calling the (supposedly new) map's ++ constructor. This case occurs if a dependency of a directly ++ opened map has a constructor that calls dlopen again on the ++ initially opened map. The new map is initialized last, so ++ checking only it is enough. */ ++ if (!new->l_init_called) ++ _dl_catch_exception (NULL, call_dl_init, args); ++ + return; + } + +diff --git a/elf/dl-support.c b/elf/dl-support.c +index 451932dd03..94e8197c63 100644 +--- a/elf/dl-support.c ++++ b/elf/dl-support.c +@@ -99,6 +99,7 @@ static struct link_map _dl_main_map = + .l_used = 1, + .l_tls_offset = NO_TLS_OFFSET, + .l_serial = 1, ++ .l_init_called = 1, + }; + + /* Namespace information. */ +diff --git a/elf/tst-dlopen-recurse.c b/elf/tst-dlopen-recurse.c +new file mode 100644 +index 0000000000..c7fb379d37 +--- /dev/null ++++ b/elf/tst-dlopen-recurse.c +@@ -0,0 +1,34 @@ ++/* Test that recursive dlopen runs constructors before return (bug 31986). ++ 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) ++{ ++ void *handle = xdlopen ("tst-dlopen-recursemod1.so", RTLD_NOW); ++ int *status = dlsym (handle, "recursemod1_status"); ++ printf ("info: recursemod1_status == %d (from main)\n", *status); ++ TEST_COMPARE (*status, 2); ++ xdlclose (handle); ++ return 0; ++} ++ ++#include +diff --git a/elf/tst-dlopen-recursemod1.c b/elf/tst-dlopen-recursemod1.c +new file mode 100644 +index 0000000000..5e0cc0eb8c +--- /dev/null ++++ b/elf/tst-dlopen-recursemod1.c +@@ -0,0 +1,50 @@ ++/* Directly opened test module that gets recursively opened again. ++ 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 ++ ++int recursemod1_status; ++ ++/* Force linking against st-dlopen-recursemod2.so. Also allows ++ checking for relocation. */ ++extern int recursemod2_status; ++int *force_recursemod2_reference = &recursemod2_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ ++recursemod1_status; ++ printf ("info: tst-dlopen-recursemod1.so constructor called (status %d)\n", ++ recursemod1_status); ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ /* The recursemod1_status variable was incremented in the ++ tst-dlopen-recursemod2.so constructor. */ ++ printf ("info: tst-dlopen-recursemod1.so destructor called (status %d)\n", ++ recursemod1_status); ++ if (recursemod1_status != 2) ++ { ++ puts ("error: recursemod1_status == 2 expected"); ++ exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-recursemod2.c b/elf/tst-dlopen-recursemod2.c +new file mode 100644 +index 0000000000..edd2f2526b +--- /dev/null ++++ b/elf/tst-dlopen-recursemod2.c +@@ -0,0 +1,66 @@ ++/* Indirectly opened module that recursively opens the directly opened module. ++ 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 ++ ++int recursemod2_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ ++recursemod2_status; ++ printf ("info: tst-dlopen-recursemod2.so constructor called (status %d)\n", ++ recursemod2_status); ++ void *handle = dlopen ("tst-dlopen-recursemod1.so", RTLD_NOW); ++ if (handle == NULL) ++ { ++ printf ("error: dlopen: %s\n", dlerror ()); ++ exit (1); ++ } ++ int *status = dlsym (handle, "recursemod1_status"); ++ if (status == NULL) ++ { ++ printf ("error: dlsym: %s\n", dlerror ()); ++ exit (1); ++ } ++ printf ("info: recursemod1_status == %d\n", *status); ++ if (*status != 1) ++ { ++ puts ("error: recursemod1_status == 1 expected"); ++ exit (1); ++ } ++ ++*status; ++ printf ("info: recursemod1_status == %d\n", *status); ++ ++ int **mod2_status = dlsym (handle, "force_recursemod2_reference"); ++ if (mod2_status == NULL || *mod2_status != &recursemod2_status) ++ { ++ puts ("error: invalid recursemod2_status address in" ++ " tst-dlopen-recursemod1.so"); ++ exit (1); ++ } ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ printf ("info: tst-dlopen-recursemod2.so destructor called (status %d)\n", ++ recursemod2_status); ++} + +commit aeb63faf58ac8466f7b4e6d1101a5ffe1c4a426e +Author: Florian Weimer +Date: Fri Oct 25 16:50:10 2024 +0200 + + elf: Signal LA_ACT_CONSISTENT to auditors after RT_CONSISTENT switch + + Auditors can call into the dynamic loader again if + LA_ACT_CONSISTENT, and those recursive calls could observe + r_state != RT_CONSISTENT. + + We should consider failing dlopen/dlmopen/dlclose if + r_state != RT_CONSISTENT. The dynamic linker is probably not + in a state in which it can handle reentrant calls. This + needs further investigation. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit e096b7a1896886eb7dd2732ccbf1184b0eec9a63) + +diff --git a/elf/dl-close.c b/elf/dl-close.c +index 88226245eb..b6f4daac79 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -723,6 +723,11 @@ _dl_close_worker (struct link_map *map, bool force) + /* TLS is cleaned up for the unloaded modules. */ + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); + ++ /* Notify the debugger those objects are finalized and gone. */ ++ r->r_state = RT_CONSISTENT; ++ _dl_debug_state (); ++ LIBC_PROBE (unmap_complete, 2, nsid, r); ++ + #ifdef SHARED + /* Auditing checkpoint: we have deleted all objects. Also, do not notify + auditors of the cleanup of a failed audit module loading attempt. */ +@@ -735,11 +740,6 @@ _dl_close_worker (struct link_map *map, bool force) + --GL(dl_nns); + while (GL(dl_ns)[GL(dl_nns) - 1]._ns_loaded == NULL); + +- /* Notify the debugger those objects are finalized and gone. */ +- r->r_state = RT_CONSISTENT; +- _dl_debug_state (); +- LIBC_PROBE (unmap_complete, 2, nsid, r); +- + /* Recheck if we need to retry, release the lock. */ + out: + if (dl_close_state == rerun) +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 5139d276e0..5a30a57ee1 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -639,17 +639,17 @@ dl_open_worker_begin (void *a) + #endif + } + +-#ifdef SHARED +- /* Auditing checkpoint: we have added all objects. */ +- _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT); +-#endif +- + /* Notify the debugger all new objects are now ready to go. */ + struct r_debug *r = _dl_debug_update (args->nsid); + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + LIBC_PROBE (map_complete, 3, args->nsid, r, new); + ++#ifdef SHARED ++ /* Auditing checkpoint: we have added all objects. */ ++ _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT); ++#endif ++ + _dl_open_check (new); + + /* Print scope information. */ +diff --git a/elf/rtld.c b/elf/rtld.c +index e577006708..e012a597b4 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2417,9 +2417,6 @@ dl_main (const ElfW(Phdr) *phdr, + _dl_relocate_object might need to call `mprotect' for DT_TEXTREL. */ + _dl_sysdep_start_cleanup (); + +- /* Auditing checkpoint: we have added all objects. */ +- _dl_audit_activity_nsid (LM_ID_BASE, LA_ACT_CONSISTENT); +- + /* Notify the debugger all new objects are now ready to go. We must re-get + the address since by now the variable might be in another object. */ + r = _dl_debug_update (LM_ID_BASE); +@@ -2427,6 +2424,9 @@ dl_main (const ElfW(Phdr) *phdr, + _dl_debug_state (); + LIBC_PROBE (init_complete, 2, LM_ID_BASE, r); + ++ /* Auditing checkpoint: we have added all objects. */ ++ _dl_audit_activity_nsid (LM_ID_BASE, LA_ACT_CONSISTENT); ++ + #if defined USE_LDCONFIG && !defined MAP_COPY + /* We must munmap() the cache file. */ + _dl_unload_cache (); + +commit 91473c4eeab513ea2c502b987e0778a7591d847a +Author: Florian Weimer +Date: Fri Oct 25 16:50:10 2024 +0200 + + elf: Signal RT_CONSISTENT after relocation processing in dlopen (bug 31986) + + Previously, a la_activity audit event was generated before + relocation processing completed. This does did not match what + happened during initial startup in elf/rtld.c (towards the end + of dl_main). It also caused various problems if an auditor + tried to open the same shared object again using dlmopen: + If it was the directly loaded object, it had a search scope + associated with it, so the early exit in dl_open_worker_begin + was taken even though the object was unrelocated. This caused + the r_state == RT_CONSISTENT assert to fail. Avoidance of the + assert also depends on reversing the order of r_state update + and auditor event (already implemented in a previous commit). + + At the later point, args->map can be NULL due to failure, + so use the assigned namespace ID instead if that is available. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 43db5e2c0672cae7edea7c9685b22317eae25471) + +diff --git a/elf/Makefile b/elf/Makefile +index 83b5ceff9c..80a290074d 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -416,6 +416,7 @@ tests += \ + tst-dlmopen1 \ + tst-dlmopen3 \ + tst-dlmopen4 \ ++ tst-dlopen-auditdup \ + tst-dlopen-recurse \ + tst-dlopen-self \ + tst-dlopen-tlsmodid \ +@@ -867,6 +868,8 @@ modules-names += \ + tst-dlmopen-twice-mod1 \ + tst-dlmopen-twice-mod2 \ + tst-dlmopen1mod \ ++ tst-dlopen-auditdup-auditmod \ ++ tst-dlopen-auditdupmod \ + tst-dlopen-recursemod1 \ + tst-dlopen-recursemod2 \ + tst-dlopen-sgid-mod \ +@@ -3169,3 +3172,6 @@ $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so + + $(objpfx)tst-dlopen-recurse.out: $(objpfx)tst-dlopen-recursemod1.so + $(objpfx)tst-dlopen-recursemod1.so: $(objpfx)tst-dlopen-recursemod2.so ++tst-dlopen-auditdup-ENV = LD_AUDIT=$(objpfx)tst-dlopen-auditdup-auditmod.so ++$(objpfx)tst-dlopen-auditdup.out: \ ++ $(objpfx)tst-dlopen-auditdupmod.so $(objpfx)tst-dlopen-auditdup-auditmod.so +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 5a30a57ee1..88e8ad8d3a 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -576,6 +576,14 @@ dl_open_worker_begin (void *a) + _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", + new->l_name, new->l_ns, new->l_direct_opencount); + ++#ifdef SHARED ++ /* No relocation processing on this execution path. But ++ relocation has not been performed for static ++ position-dependent executables, so disable the assert for ++ static linking. */ ++ assert (new->l_relocated); ++#endif ++ + /* If the user requested the object to be in the global + namespace but it is not so far, prepare to add it now. This + can raise an exception to do a malloc failure. */ +@@ -597,10 +605,6 @@ dl_open_worker_begin (void *a) + if ((mode & RTLD_GLOBAL) && new->l_global == 0) + add_to_global_update (new); + +- const int r_state __attribute__ ((unused)) +- = _dl_debug_update (args->nsid)->r_state; +- assert (r_state == RT_CONSISTENT); +- + /* Do not return without calling the (supposedly new) map's + constructor. This case occurs if a dependency of a directly + opened map has a constructor that calls dlopen again on the +@@ -639,17 +643,6 @@ dl_open_worker_begin (void *a) + #endif + } + +- /* Notify the debugger all new objects are now ready to go. */ +- struct r_debug *r = _dl_debug_update (args->nsid); +- r->r_state = RT_CONSISTENT; +- _dl_debug_state (); +- LIBC_PROBE (map_complete, 3, args->nsid, r, new); +- +-#ifdef SHARED +- /* Auditing checkpoint: we have added all objects. */ +- _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT); +-#endif +- + _dl_open_check (new); + + /* Print scope information. */ +@@ -696,6 +689,7 @@ dl_open_worker_begin (void *a) + created dlmopen namespaces. Do not do this for static dlopen + because libc has relocations against ld.so, which may not have + been relocated at this point. */ ++ struct r_debug *r = _dl_debug_update (args->nsid); + #ifdef SHARED + if (GL(dl_ns)[args->nsid].libc_map != NULL) + _dl_open_relocate_one_object (args, r, GL(dl_ns)[args->nsid].libc_map, +@@ -787,6 +781,26 @@ dl_open_worker (void *a) + + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); + ++ /* Auditing checkpoint and debugger signalling. Do this even on ++ error, so that dlopen exists with consistent state. */ ++ if (args->nsid >= 0 || args->map != NULL) ++ { ++ Lmid_t nsid = args->map != NULL ? args->map->l_ns : args->nsid; ++ struct r_debug *r = _dl_debug_update (nsid); ++#ifdef SHARED ++ bool was_not_consistent = r->r_state != RT_CONSISTENT; ++#endif ++ r->r_state = RT_CONSISTENT; ++ _dl_debug_state (); ++ LIBC_PROBE (map_complete, 3, nsid, r, new); ++ ++#ifdef SHARED ++ if (was_not_consistent) ++ /* Avoid redudant/recursive signalling. */ ++ _dl_audit_activity_nsid (nsid, LA_ACT_CONSISTENT); ++#endif ++ } ++ + if (__glibc_unlikely (ex.errstring != NULL)) + /* Reraise the error. */ + _dl_signal_exception (err, &ex, NULL); +diff --git a/elf/tst-dlopen-auditdup-auditmod.c b/elf/tst-dlopen-auditdup-auditmod.c +new file mode 100644 +index 0000000000..9b67295e94 +--- /dev/null ++++ b/elf/tst-dlopen-auditdup-auditmod.c +@@ -0,0 +1,100 @@ ++/* Auditor that opens again an object that just has been opened. ++ 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 ++ ++unsigned int ++la_version (unsigned int v) ++{ ++ return LAV_CURRENT; ++} ++ ++static bool trigger_on_la_activity; ++ ++unsigned int ++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie) ++{ ++ printf ("info: la_objopen: \"%s\"\n", map->l_name); ++ if (strstr (map->l_name, "/tst-dlopen-auditdupmod.so") != NULL) ++ trigger_on_la_activity = true; ++ return 0; ++} ++ ++void ++la_activity (uintptr_t *cookie, unsigned int flag) ++{ ++ static unsigned int calls; ++ ++calls; ++ printf ("info: la_activity: call %u (flag %u)\n", calls, flag); ++ fflush (stdout); ++ if (trigger_on_la_activity) ++ { ++ /* Avoid triggering on the dlmopen call below. */ ++ static bool recursion; ++ if (recursion) ++ return; ++ recursion = true; ++ ++ puts ("info: about to dlmopen tst-dlopen-auditdupmod.so"); ++ fflush (stdout); ++ void *handle = dlmopen (LM_ID_BASE, "tst-dlopen-auditdupmod.so", ++ RTLD_NOW); ++ if (handle == NULL) ++ { ++ printf ("error: dlmopen: %s\n", dlerror ()); ++ fflush (stdout); ++ _exit (1); ++ } ++ ++ /* Check that the constructor has run. */ ++ int *status = dlsym (handle, "auditdupmod_status"); ++ if (status == NULL) ++ { ++ printf ("error: dlsym: %s\n", dlerror ()); ++ fflush (stdout); ++ _exit (1); ++ } ++ printf ("info: auditdupmod_status == %d\n", *status); ++ if (*status != 1) ++ { ++ puts ("error: auditdupmod_status == 1 expected"); ++ fflush (stdout); ++ _exit (1); ++ } ++ /* Checked in the destructor and the main program. */ ++ ++*status; ++ printf ("info: auditdupmod_status == %d\n", *status); ++ ++ /* Check that the module has been relocated. */ ++ int **status_address = dlsym (handle, "auditdupmod_status_address"); ++ if (status_address == NULL || *status_address != status) ++ { ++ puts ("error: invalid auditdupmod_status address in" ++ " tst-dlopen-auditdupmod.so"); ++ fflush (stdout); ++ _exit (1); ++ } ++ ++ fflush (stdout); ++ } ++} +diff --git a/elf/tst-dlopen-auditdup.c b/elf/tst-dlopen-auditdup.c +new file mode 100644 +index 0000000000..d022c58ae3 +--- /dev/null ++++ b/elf/tst-dlopen-auditdup.c +@@ -0,0 +1,36 @@ ++/* Test that recursive dlopen from auditor works (bug 31986). ++ 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) ++{ ++ puts ("info: about to dlopen tst-dlopen-auditdupmod.so"); ++ fflush (stdout); ++ void *handle = xdlopen ("tst-dlopen-auditdupmod.so", RTLD_NOW); ++ int *status = xdlsym (handle, "auditdupmod_status"); ++ printf ("info: auditdupmod_status == %d (from main)\n", *status); ++ TEST_COMPARE (*status, 2); ++ xdlclose (handle); ++ return 0; ++} ++ ++#include +diff --git a/elf/tst-dlopen-auditdupmod.c b/elf/tst-dlopen-auditdupmod.c +new file mode 100644 +index 0000000000..59b7e21daa +--- /dev/null ++++ b/elf/tst-dlopen-auditdupmod.c +@@ -0,0 +1,48 @@ ++/* Directly opened test module that gets reopened from the auditor. ++ 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 ++ ++int auditdupmod_status; ++ ++/* Used to check for successful relocation processing. */ ++int *auditdupmod_status_address = &auditdupmod_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ ++auditdupmod_status; ++ printf ("info: tst-dlopen-auditdupmod.so constructor called (status %d)\n", ++ auditdupmod_status); ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ /* The tst-dlopen-auditdup-auditmod.so auditor incremented ++ auditdupmod_status. */ ++ printf ("info: tst-dlopen-auditdupmod.so destructor called (status %d)\n", ++ auditdupmod_status); ++ if (auditdupmod_status != 2) ++ { ++ puts ("error: auditdupmod_status == 2 expected"); ++ exit (1); ++ } ++} + +commit d9bd6f386912ef1dbc0434d7cc03eae5ded86181 +Author: Florian Weimer +Date: Fri Oct 25 17:41:53 2024 +0200 + + elf: Fix map_complete Systemtap probe in dl_open_worker + + The refactoring did not take the change of variable into account. + Fixes commit 43db5e2c0672cae7edea7c9685b22317eae25471 + ("elf: Signal RT_CONSISTENT after relocation processing in dlopen + (bug 31986)"). + + (cherry picked from commit ac73067cb7a328bf106ecd041c020fc61be7e087) + +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 88e8ad8d3a..bd15f5f6a4 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -792,7 +792,7 @@ dl_open_worker (void *a) + #endif + r->r_state = RT_CONSISTENT; + _dl_debug_state (); +- LIBC_PROBE (map_complete, 3, nsid, r, new); ++ LIBC_PROBE (map_complete, 3, nsid, r, args->map); + + #ifdef SHARED + if (was_not_consistent) + +commit 23f95986fca6bc23538050332af297cda4805e45 +Author: Florian Weimer +Date: Mon Oct 28 14:45:30 2024 +0100 + + Revert "elf: Run constructors on cyclic recursive dlopen (bug 31986)" + + This reverts commit 9897ced8e78db5d813166a7ccccfd5a42c69ef20. + + Adjust the test expectations in elf/tst-dlopen-auditdup-auditmod.c + accordingly. + + (cherry picked from commit 95129e6b8fabdaa8cd8a4a5cc20be0f4cb0ba59f) + +diff --git a/elf/Makefile b/elf/Makefile +index 80a290074d..0d018be60f 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -417,7 +417,6 @@ tests += \ + tst-dlmopen3 \ + tst-dlmopen4 \ + tst-dlopen-auditdup \ +- tst-dlopen-recurse \ + tst-dlopen-self \ + tst-dlopen-tlsmodid \ + tst-dlopen-tlsreinit1 \ +@@ -870,8 +869,6 @@ modules-names += \ + tst-dlmopen1mod \ + tst-dlopen-auditdup-auditmod \ + tst-dlopen-auditdupmod \ +- tst-dlopen-recursemod1 \ +- tst-dlopen-recursemod2 \ + tst-dlopen-sgid-mod \ + tst-dlopen-tlsreinitmod1 \ + tst-dlopen-tlsreinitmod2 \ +@@ -3170,8 +3167,6 @@ tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + + $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so + +-$(objpfx)tst-dlopen-recurse.out: $(objpfx)tst-dlopen-recursemod1.so +-$(objpfx)tst-dlopen-recursemod1.so: $(objpfx)tst-dlopen-recursemod2.so + tst-dlopen-auditdup-ENV = LD_AUDIT=$(objpfx)tst-dlopen-auditdup-auditmod.so + $(objpfx)tst-dlopen-auditdup.out: \ + $(objpfx)tst-dlopen-auditdupmod.so $(objpfx)tst-dlopen-auditdup-auditmod.so +diff --git a/elf/dl-open.c b/elf/dl-open.c +index bd15f5f6a4..b00f283c42 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -605,14 +605,6 @@ dl_open_worker_begin (void *a) + if ((mode & RTLD_GLOBAL) && new->l_global == 0) + add_to_global_update (new); + +- /* Do not return without calling the (supposedly new) map's +- constructor. This case occurs if a dependency of a directly +- opened map has a constructor that calls dlopen again on the +- initially opened map. The new map is initialized last, so +- checking only it is enough. */ +- if (!new->l_init_called) +- _dl_catch_exception (NULL, call_dl_init, args); +- + return; + } + +diff --git a/elf/dl-support.c b/elf/dl-support.c +index 94e8197c63..451932dd03 100644 +--- a/elf/dl-support.c ++++ b/elf/dl-support.c +@@ -99,7 +99,6 @@ static struct link_map _dl_main_map = + .l_used = 1, + .l_tls_offset = NO_TLS_OFFSET, + .l_serial = 1, +- .l_init_called = 1, + }; + + /* Namespace information. */ +diff --git a/elf/tst-dlopen-auditdup-auditmod.c b/elf/tst-dlopen-auditdup-auditmod.c +index 9b67295e94..270a595ec4 100644 +--- a/elf/tst-dlopen-auditdup-auditmod.c ++++ b/elf/tst-dlopen-auditdup-auditmod.c +@@ -66,7 +66,11 @@ la_activity (uintptr_t *cookie, unsigned int flag) + _exit (1); + } + +- /* Check that the constructor has run. */ ++ /* Check that the constructor has not run. Running the ++ constructor would require constructing its dependencies, but ++ the constructor call that triggered this auditing activity ++ has not completed, and constructors among the dependencies ++ may not be able to deal with that. */ + int *status = dlsym (handle, "auditdupmod_status"); + if (status == NULL) + { +@@ -75,9 +79,9 @@ la_activity (uintptr_t *cookie, unsigned int flag) + _exit (1); + } + printf ("info: auditdupmod_status == %d\n", *status); +- if (*status != 1) ++ if (*status != 0) + { +- puts ("error: auditdupmod_status == 1 expected"); ++ puts ("error: auditdupmod_status == 0 expected"); + fflush (stdout); + _exit (1); + } +diff --git a/elf/tst-dlopen-recurse.c b/elf/tst-dlopen-recurse.c +deleted file mode 100644 +index c7fb379d37..0000000000 +--- a/elf/tst-dlopen-recurse.c ++++ /dev/null +@@ -1,34 +0,0 @@ +-/* Test that recursive dlopen runs constructors before return (bug 31986). +- 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) +-{ +- void *handle = xdlopen ("tst-dlopen-recursemod1.so", RTLD_NOW); +- int *status = dlsym (handle, "recursemod1_status"); +- printf ("info: recursemod1_status == %d (from main)\n", *status); +- TEST_COMPARE (*status, 2); +- xdlclose (handle); +- return 0; +-} +- +-#include +diff --git a/elf/tst-dlopen-recursemod1.c b/elf/tst-dlopen-recursemod1.c +deleted file mode 100644 +index 5e0cc0eb8c..0000000000 +--- a/elf/tst-dlopen-recursemod1.c ++++ /dev/null +@@ -1,50 +0,0 @@ +-/* Directly opened test module that gets recursively opened again. +- 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 +- +-int recursemod1_status; +- +-/* Force linking against st-dlopen-recursemod2.so. Also allows +- checking for relocation. */ +-extern int recursemod2_status; +-int *force_recursemod2_reference = &recursemod2_status; +- +-static void __attribute__ ((constructor)) +-init (void) +-{ +- ++recursemod1_status; +- printf ("info: tst-dlopen-recursemod1.so constructor called (status %d)\n", +- recursemod1_status); +-} +- +-static void __attribute__ ((destructor)) +-fini (void) +-{ +- /* The recursemod1_status variable was incremented in the +- tst-dlopen-recursemod2.so constructor. */ +- printf ("info: tst-dlopen-recursemod1.so destructor called (status %d)\n", +- recursemod1_status); +- if (recursemod1_status != 2) +- { +- puts ("error: recursemod1_status == 2 expected"); +- exit (1); +- } +-} +diff --git a/elf/tst-dlopen-recursemod2.c b/elf/tst-dlopen-recursemod2.c +deleted file mode 100644 +index edd2f2526b..0000000000 +--- a/elf/tst-dlopen-recursemod2.c ++++ /dev/null +@@ -1,66 +0,0 @@ +-/* Indirectly opened module that recursively opens the directly opened module. +- 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 +- +-int recursemod2_status; +- +-static void __attribute__ ((constructor)) +-init (void) +-{ +- ++recursemod2_status; +- printf ("info: tst-dlopen-recursemod2.so constructor called (status %d)\n", +- recursemod2_status); +- void *handle = dlopen ("tst-dlopen-recursemod1.so", RTLD_NOW); +- if (handle == NULL) +- { +- printf ("error: dlopen: %s\n", dlerror ()); +- exit (1); +- } +- int *status = dlsym (handle, "recursemod1_status"); +- if (status == NULL) +- { +- printf ("error: dlsym: %s\n", dlerror ()); +- exit (1); +- } +- printf ("info: recursemod1_status == %d\n", *status); +- if (*status != 1) +- { +- puts ("error: recursemod1_status == 1 expected"); +- exit (1); +- } +- ++*status; +- printf ("info: recursemod1_status == %d\n", *status); +- +- int **mod2_status = dlsym (handle, "force_recursemod2_reference"); +- if (mod2_status == NULL || *mod2_status != &recursemod2_status) +- { +- puts ("error: invalid recursemod2_status address in" +- " tst-dlopen-recursemod1.so"); +- exit (1); +- } +-} +- +-static void __attribute__ ((destructor)) +-fini (void) +-{ +- printf ("info: tst-dlopen-recursemod2.so destructor called (status %d)\n", +- recursemod2_status); +-} + +commit 812918bbde0e44d2cca303084b16a43f6a10190c +Author: Florian Weimer +Date: Wed Nov 6 10:33:44 2024 +0100 + + elf: rtld_multiple_ref is always true + + For a long time, libc.so.6 has dependend on ld.so, which + means that there is a reference to ld.so in all processes, + and rtld_multiple_ref is always true. In fact, if + rtld_multiple_ref were false, some of the ld.so setup code + would not run. + + Reviewed-by: DJ Delorie + (cherry picked from commit 8f8dd904c4a2207699bb666f30acceb5209c8d3f) + +diff --git a/elf/rtld.c b/elf/rtld.c +index e012a597b4..32c1b86697 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2011,43 +2011,37 @@ dl_main (const ElfW(Phdr) *phdr, + if (main_map->l_searchlist.r_list[i] == &GL(dl_rtld_map)) + break; + +- bool rtld_multiple_ref = false; +- if (__glibc_likely (i < main_map->l_searchlist.r_nlist)) +- { +- /* Some DT_NEEDED entry referred to the interpreter object itself, so +- put it back in the list of visible objects. We insert it into the +- chain in symbol search order because gdb uses the chain's order as +- its symbol search order. */ +- rtld_multiple_ref = true; ++ /* Insert the link map for the dynamic loader into the chain in ++ symbol search order because gdb uses the chain's order as its ++ symbol search order. */ + +- GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1]; +- if (__glibc_likely (state.mode == rtld_mode_normal)) +- { +- GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist +- ? main_map->l_searchlist.r_list[i + 1] +- : NULL); ++ GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1]; ++ if (__glibc_likely (state.mode == rtld_mode_normal)) ++ { ++ GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist ++ ? main_map->l_searchlist.r_list[i + 1] ++ : NULL); + #ifdef NEED_DL_SYSINFO_DSO +- if (GLRO(dl_sysinfo_map) != NULL +- && GL(dl_rtld_map).l_prev->l_next == GLRO(dl_sysinfo_map) +- && GL(dl_rtld_map).l_next != GLRO(dl_sysinfo_map)) +- GL(dl_rtld_map).l_prev = GLRO(dl_sysinfo_map); ++ if (GLRO(dl_sysinfo_map) != NULL ++ && GL(dl_rtld_map).l_prev->l_next == GLRO(dl_sysinfo_map) ++ && GL(dl_rtld_map).l_next != GLRO(dl_sysinfo_map)) ++ GL(dl_rtld_map).l_prev = GLRO(dl_sysinfo_map); + #endif +- } +- else +- /* In trace mode there might be an invisible object (which we +- could not find) after the previous one in the search list. +- In this case it doesn't matter much where we put the +- interpreter object, so we just initialize the list pointer so +- that the assertion below holds. */ +- GL(dl_rtld_map).l_next = GL(dl_rtld_map).l_prev->l_next; +- +- assert (GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next); +- GL(dl_rtld_map).l_prev->l_next = &GL(dl_rtld_map); +- if (GL(dl_rtld_map).l_next != NULL) +- { +- assert (GL(dl_rtld_map).l_next->l_prev == GL(dl_rtld_map).l_prev); +- GL(dl_rtld_map).l_next->l_prev = &GL(dl_rtld_map); +- } ++ } ++ else ++ /* In trace mode there might be an invisible object (which we ++ could not find) after the previous one in the search list. ++ In this case it doesn't matter much where we put the ++ interpreter object, so we just initialize the list pointer so ++ that the assertion below holds. */ ++ GL(dl_rtld_map).l_next = GL(dl_rtld_map).l_prev->l_next; ++ ++ assert (GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next); ++ GL(dl_rtld_map).l_prev->l_next = &GL(dl_rtld_map); ++ if (GL(dl_rtld_map).l_next != NULL) ++ { ++ assert (GL(dl_rtld_map).l_next->l_prev == GL(dl_rtld_map).l_prev); ++ GL(dl_rtld_map).l_next->l_prev = &GL(dl_rtld_map); + } + + /* Now let us see whether all libraries are available in the +@@ -2375,35 +2369,33 @@ dl_main (const ElfW(Phdr) *phdr, + /* Make sure no new search directories have been added. */ + assert (GLRO(dl_init_all_dirs) == GL(dl_all_dirs)); + +- if (rtld_multiple_ref) +- { +- /* There was an explicit ref to the dynamic linker as a shared lib. +- Re-relocate ourselves with user-controlled symbol definitions. ++ /* Re-relocate ourselves with user-controlled symbol definitions. + +- We must do this after TLS initialization in case after this +- re-relocation, we might call a user-supplied function +- (e.g. calloc from _dl_relocate_object) that uses TLS data. */ ++ We must do this after TLS initialization in case after this ++ re-relocation, we might call a user-supplied function ++ (e.g. calloc from _dl_relocate_object) that uses TLS data. */ + +- /* Set up the object lookup structures. */ +- _dl_find_object_init (); ++ /* Set up the object lookup structures. */ ++ _dl_find_object_init (); + +- /* The malloc implementation has been relocated, so resolving +- its symbols (and potentially calling IFUNC resolvers) is safe +- at this point. */ +- __rtld_malloc_init_real (main_map); ++ /* The malloc implementation has been relocated, so resolving ++ its symbols (and potentially calling IFUNC resolvers) is safe ++ at this point. */ ++ __rtld_malloc_init_real (main_map); + +- /* Likewise for the locking implementation. */ +- __rtld_mutex_init (); ++ /* Likewise for the locking implementation. */ ++ __rtld_mutex_init (); + +- RTLD_TIMING_VAR (start); +- rtld_timer_start (&start); ++ { ++ RTLD_TIMING_VAR (start); ++ rtld_timer_start (&start); + +- /* Mark the link map as not yet relocated again. */ +- GL(dl_rtld_map).l_relocated = 0; +- _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0); ++ /* Mark the link map as not yet relocated again. */ ++ GL(dl_rtld_map).l_relocated = 0; ++ _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0); + +- rtld_timer_accum (&relocate_time, start); +- } ++ rtld_timer_accum (&relocate_time, start); ++ } + + /* Relocation is complete. Perform early libc initialization. This + is the initial libc, even if audit modules have been loaded with + +commit 3b03a301e7452dbacd92fc1558cd50b9bbab7e78 +Author: Florian Weimer +Date: Wed Nov 6 10:33:44 2024 +0100 + + elf: Do not define consider_profiling, consider_symbind as macros + + This avoids surprises when refactoring the code if these identifiers + are re-used later in the file. + + Reviewed-by: DJ Delorie + (cherry picked from commit a79642204537dec8a1e1c58d1e0a074b3c624f46) + +diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c +index 4bf7aec88b..b2c1627ceb 100644 +--- a/elf/dl-reloc.c ++++ b/elf/dl-reloc.c +@@ -220,8 +220,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], + int lazy = reloc_mode & RTLD_LAZY; + int skip_ifunc = reloc_mode & __RTLD_NOIFUNC; + +-#ifdef SHARED + bool consider_symbind = false; ++#ifdef SHARED + /* If we are auditing, install the same handlers we need for profiling. */ + if ((reloc_mode & __RTLD_AUDIT) == 0) + { +@@ -240,9 +240,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], + } + #elif defined PROF + /* Never use dynamic linker profiling for gprof profiling code. */ +-# define consider_profiling 0 +-#else +-# define consider_symbind 0 ++ consider_profiling = 0; + #endif + + /* If DT_BIND_NOW is set relocate all references in this object. We +@@ -300,7 +298,6 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], + + ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc); + +-#ifndef PROF + if ((consider_profiling || consider_symbind) + && l->l_info[DT_PLTRELSZ] != NULL) + { +@@ -321,7 +318,6 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], + _dl_fatal_printf (errstring, RTLD_PROGNAME, l->l_name); + } + } +-#endif + } + + /* Mark the object so we know this work has been done. */ + +commit 7fd2dac6d6604822597dafb9d1b3e2c6a11f8422 +Author: Florian Weimer +Date: Wed Nov 6 10:33:44 2024 +0100 + + elf: Introduce _dl_relocate_object_no_relro + + And make _dl_protect_relro apply RELRO conditionally. + + Reviewed-by: DJ Delorie + (cherry picked from commit f2326c2ec0a0a8db7bc7f4db8cce3002768fc3b6) + +diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c +index b2c1627ceb..76d14830dd 100644 +--- a/elf/dl-reloc.c ++++ b/elf/dl-reloc.c +@@ -202,12 +202,9 @@ resolve_map (lookup_t l, struct r_scope_elem *scope[], const ElfW(Sym) **ref, + #include "dynamic-link.h" + + void +-_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], +- int reloc_mode, int consider_profiling) ++_dl_relocate_object_no_relro (struct link_map *l, struct r_scope_elem *scope[], ++ int reloc_mode, int consider_profiling) + { +- if (l->l_relocated) +- return; +- + struct textrels + { + caddr_t start; +@@ -338,17 +335,24 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], + + textrels = textrels->next; + } +- +- /* In case we can protect the data now that the relocations are +- done, do it. */ +- if (l->l_relro_size != 0) +- _dl_protect_relro (l); + } + ++void ++_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], ++ int reloc_mode, int consider_profiling) ++{ ++ if (l->l_relocated) ++ return; ++ _dl_relocate_object_no_relro (l, scope, reloc_mode, consider_profiling); ++ _dl_protect_relro (l); ++} + + void + _dl_protect_relro (struct link_map *l) + { ++ if (l->l_relro_size == 0) ++ return; ++ + ElfW(Addr) start = ALIGN_DOWN((l->l_addr + + l->l_relro_addr), + GLRO(dl_pagesize)); +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 259ce2e7d6..91447a5e77 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -1014,6 +1014,13 @@ extern void _dl_relocate_object (struct link_map *map, + int reloc_mode, int consider_profiling) + attribute_hidden; + ++/* Perform relocation, but do not apply RELRO. Does not check ++ L->relocated. Otherwise the same as _dl_relocate_object. */ ++void _dl_relocate_object_no_relro (struct link_map *map, ++ struct r_scope_elem *scope[], ++ int reloc_mode, int consider_profiling) ++ attribute_hidden; ++ + /* Protect PT_GNU_RELRO area. */ + extern void _dl_protect_relro (struct link_map *map) attribute_hidden; + + +commit 630be04e0430f044d32f45c912b4f5d9cdf57007 +Author: Florian Weimer +Date: Wed Nov 6 10:33:44 2024 +0100 + + elf: Switch to main malloc after final ld.so self-relocation + + Before commit ee1ada1bdb8074de6e1bdc956ab19aef7b6a7872 + ("elf: Rework exception handling in the dynamic loader + [BZ #25486]"), the previous order called the main calloc + to allocate a shadow GOT/PLT array for auditing support. + This happened before libc.so.6 ELF constructors were run, so + a user malloc could run without libc.so.6 having been + initialized fully. One observable effect was that + environ was NULL at this point. + + It does not seem to be possible at present to trigger such + an allocation, but it seems more robust to delay switching + to main malloc after ld.so self-relocation is complete. + The elf/tst-rtld-no-malloc-audit test case fails with a + 2.34-era glibc that does not have this fix. + + Reviewed-by: DJ Delorie + (cherry picked from commit c1560f3f75c0e892b5522c16f91b4e303f677094) + +diff --git a/elf/Makefile b/elf/Makefile +index 0d018be60f..fed2ff15f9 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -455,6 +455,9 @@ tests += \ + tst-recursive-tls \ + tst-relsort1 \ + tst-ro-dynamic \ ++ tst-rtld-no-malloc \ ++ tst-rtld-no-malloc-audit \ ++ tst-rtld-no-malloc-preload \ + tst-rtld-run-static \ + tst-single_threaded \ + tst-single_threaded-pthread \ +@@ -3170,3 +3173,9 @@ $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so + tst-dlopen-auditdup-ENV = LD_AUDIT=$(objpfx)tst-dlopen-auditdup-auditmod.so + $(objpfx)tst-dlopen-auditdup.out: \ + $(objpfx)tst-dlopen-auditdupmod.so $(objpfx)tst-dlopen-auditdup-auditmod.so ++ ++# Reuse an audit module which provides ample debug logging. ++tst-rtld-no-malloc-audit-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so ++ ++# Any shared object should do. ++tst-rtld-no-malloc-preload-ENV = LD_PRELOAD=$(objpfx)tst-auditmod1.so +diff --git a/elf/dl-support.c b/elf/dl-support.c +index 451932dd03..ee590edf93 100644 +--- a/elf/dl-support.c ++++ b/elf/dl-support.c +@@ -338,8 +338,7 @@ _dl_non_dynamic_init (void) + call_function_static_weak (_dl_find_object_init); + + /* Setup relro on the binary itself. */ +- if (_dl_main_map.l_relro_size != 0) +- _dl_protect_relro (&_dl_main_map); ++ _dl_protect_relro (&_dl_main_map); + } + + #ifdef DL_SYSINFO_IMPLEMENTATION +diff --git a/elf/rtld.c b/elf/rtld.c +index 32c1b86697..4adfc67260 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2369,30 +2369,27 @@ dl_main (const ElfW(Phdr) *phdr, + /* Make sure no new search directories have been added. */ + assert (GLRO(dl_init_all_dirs) == GL(dl_all_dirs)); + +- /* Re-relocate ourselves with user-controlled symbol definitions. +- +- We must do this after TLS initialization in case after this +- re-relocation, we might call a user-supplied function +- (e.g. calloc from _dl_relocate_object) that uses TLS data. */ +- + /* Set up the object lookup structures. */ + _dl_find_object_init (); + +- /* The malloc implementation has been relocated, so resolving +- its symbols (and potentially calling IFUNC resolvers) is safe +- at this point. */ +- __rtld_malloc_init_real (main_map); +- + /* Likewise for the locking implementation. */ + __rtld_mutex_init (); + ++ /* Re-relocate ourselves with user-controlled symbol definitions. */ ++ + { + RTLD_TIMING_VAR (start); + rtld_timer_start (&start); + +- /* Mark the link map as not yet relocated again. */ +- GL(dl_rtld_map).l_relocated = 0; +- _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0); ++ _dl_relocate_object_no_relro (&GL(dl_rtld_map), main_map->l_scope, 0, 0); ++ ++ /* The malloc implementation has been relocated, so resolving ++ its symbols (and potentially calling IFUNC resolvers) is safe ++ at this point. */ ++ __rtld_malloc_init_real (main_map); ++ ++ if (GL(dl_rtld_map).l_relro_size != 0) ++ _dl_protect_relro (&GL(dl_rtld_map)); + + rtld_timer_accum (&relocate_time, start); + } +diff --git a/elf/tst-rtld-no-malloc-audit.c b/elf/tst-rtld-no-malloc-audit.c +new file mode 100644 +index 0000000000..a028377ad1 +--- /dev/null ++++ b/elf/tst-rtld-no-malloc-audit.c +@@ -0,0 +1 @@ ++#include "tst-rtld-no-malloc.c" +diff --git a/elf/tst-rtld-no-malloc-preload.c b/elf/tst-rtld-no-malloc-preload.c +new file mode 100644 +index 0000000000..a028377ad1 +--- /dev/null ++++ b/elf/tst-rtld-no-malloc-preload.c +@@ -0,0 +1 @@ ++#include "tst-rtld-no-malloc.c" +diff --git a/elf/tst-rtld-no-malloc.c b/elf/tst-rtld-no-malloc.c +new file mode 100644 +index 0000000000..5f24d4bd72 +--- /dev/null ++++ b/elf/tst-rtld-no-malloc.c +@@ -0,0 +1,76 @@ ++/* Test that program loading does not call malloc. ++ 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 ++ ++static void ++print (const char *s) ++{ ++ const char *end = s + strlen (s); ++ while (s < end) ++ { ++ ssize_t ret = write (STDOUT_FILENO, s, end - s); ++ if (ret <= 0) ++ _exit (2); ++ s += ret; ++ } ++} ++ ++static void __attribute__ ((noreturn)) ++unexpected_call (const char *function) ++{ ++ print ("error: unexpected call to "); ++ print (function); ++ print ("\n"); ++ _exit (1); ++} ++ ++/* These are the malloc functions implement in elf/dl-minimal.c. */ ++ ++void ++free (void *ignored) ++{ ++ unexpected_call ("free"); ++} ++ ++void * ++calloc (size_t ignored1, size_t ignored2) ++{ ++ unexpected_call ("calloc"); ++} ++ ++void * ++malloc (size_t ignored) ++{ ++ unexpected_call ("malloc"); ++} ++ ++void * ++realloc (void *ignored1, size_t ignored2) ++{ ++ unexpected_call ("realloc"); ++} ++ ++int ++main (void) ++{ ++ /* Do not use the test wrapper, to avoid spurious malloc calls from it. */ ++ return 0; ++} + +commit f86839fc158e571ad99774fdeaaa2ad12d9109c1 +Author: Florian Weimer +Date: Tue Sep 3 17:52:47 2024 +0200 + + elf: Update DSO list, write audit log to elf/tst-audit23.out + + After commit 1d5024f4f052c12e404d42d3b5bfe9c3e9fd27c4 + ("support: Build with exceptions and asynchronous unwind tables + [BZ #30587]"), libgcc_s is expected to show up in the DSO + list on 32-bit Arm. Do not update max_objs because vdso is not + tracked (and which is the reason why the test currently passes + even with libgcc_s present). + + Also write the log output from the auditor to standard output, + for easier test debugging. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 4a50fdf8b2c1106b50cd9056b4c6f3a72cdeed5f) + +diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c +index d2640fe8b2..895dab08ea 100644 +--- a/elf/tst-audit23.c ++++ b/elf/tst-audit23.c +@@ -85,13 +85,28 @@ do_test (int argc, char *argv[]) + = support_capture_subprogram (spargv[0], spargv, NULL); + support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr); + ++ { ++ FILE *fp = fmemopen (result.err.buffer, result.err.length, "r"); ++ TEST_VERIFY (fp != NULL); ++ unsigned int line = 0; ++ char *buffer = NULL; ++ size_t buffer_length = 0; ++ puts ("info: *** audit log start ***"); ++ while (xgetline (&buffer, &buffer_length, fp)) ++ printf ("%6u\t%s", ++line, buffer); ++ puts ("info: *** audit log end ***"); ++ free (buffer); ++ xfclose (fp); ++ } ++ + /* The expected la_objopen/la_objclose: + 1. executable + 2. loader + 3. libc.so +- 4. tst-audit23mod.so +- 5. libc.so (LM_ID_NEWLM). +- 6. vdso (optional and ignored). */ ++ 4. libgcc_s.so (one some architectures, for libsupport) ++ 5. tst-audit23mod.so ++ 6. libc.so (LM_ID_NEWLM). ++ vdso (optional and ignored). */ + enum { max_objs = 6 }; + struct la_obj_t + { +@@ -115,8 +130,10 @@ do_test (int argc, char *argv[]) + TEST_VERIFY (out != NULL); + char *buffer = NULL; + size_t buffer_length = 0; ++ unsigned int line = 0; + while (xgetline (&buffer, &buffer_length, out)) + { ++ ++line; + if (startswith (buffer, "la_activity: ")) + { + uintptr_t cookie; +@@ -174,8 +191,8 @@ do_test (int argc, char *argv[]) + if (is_vdso (lname)) + continue; + if (nobjs == max_objs) +- FAIL_EXIT1 ("non expected la_objopen: %s %"PRIxPTR" %ld", +- lname, laddr, lmid); ++ FAIL_EXIT1 ("(line %u) non expected la_objopen: %s %"PRIxPTR" %ld", ++ line, lname, laddr, lmid); + objs[nobjs].lname = lname; + objs[nobjs].laddr = laddr; + objs[nobjs].lmid = lmid; + +commit 341233a2507209872e5a6fe80efbd16c40c3c2cd +Author: Florian Weimer +Date: Fri Nov 29 15:36:40 2024 +0100 + + elf: Add the endswith function to + + And include for a definition of bool. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit a20bc2f6233a726c7df8eaa332b6e498bd59321f) + +diff --git a/elf/endswith.h b/elf/endswith.h +index c6430c48be..3954e57f8e 100644 +--- a/elf/endswith.h ++++ b/elf/endswith.h +@@ -17,6 +17,7 @@ + #ifndef _ENDSWITH_H + #define _ENDSWITH_H + ++#include + #include + + /* Return true if the N bytes at NAME end with with the characters in +@@ -30,4 +31,11 @@ endswithn (const char *name, size_t n, const char *suffix) + strlen (suffix)) == 0); + } + ++/* Same as endswithn, but uses the entire SUBJECT for matching. */ ++static inline bool ++endswith (const char *subject, const char *suffix) ++{ ++ return endswithn (subject, strlen (subject), suffix); ++} ++ + #endif /* _ENDSWITH_H */ + +commit c10543d15b0e41057c8436ce1c88f6b92db47a07 +Author: Florian Weimer +Date: Fri Aug 9 15:31:18 2024 +0200 + + elf: Signal la_objopen for the proxy link map in dlmopen (bug 31985) + + Previously, the ld.so link map was silently added to the namespace. + This change produces an auditing event for it. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 8f36b1469677afe37168f9af1b77402d7a70c673) + +diff --git a/elf/dl-load.c b/elf/dl-load.c +index 8a89b71016..54c9c4d779 100644 +--- a/elf/dl-load.c ++++ b/elf/dl-load.c +@@ -918,6 +918,37 @@ _dl_process_pt_gnu_property (struct link_map *l, int fd, const ElfW(Phdr) *ph) + } + } + ++static void ++_dl_notify_new_object (int mode, Lmid_t nsid, struct link_map *l) ++{ ++ /* Signal that we are going to add new objects. */ ++ struct r_debug *r = _dl_debug_update (nsid); ++ if (r->r_state == RT_CONSISTENT) ++ { ++#ifdef SHARED ++ /* Auditing checkpoint: we are going to add new objects. Since this ++ is called after _dl_add_to_namespace_list the namespace is guaranteed ++ to not be empty. */ ++ if ((mode & __RTLD_AUDIT) == 0) ++ _dl_audit_activity_nsid (nsid, LA_ACT_ADD); ++#endif ++ ++ /* Notify the debugger we have added some objects. We need to ++ call _dl_debug_initialize in a static program in case dynamic ++ linking has not been used before. */ ++ r->r_state = RT_ADD; ++ _dl_debug_state (); ++ LIBC_PROBE (map_start, 2, nsid, r); ++ } ++ else ++ assert (r->r_state == RT_ADD); ++ ++#ifdef SHARED ++ /* Auditing checkpoint: we have a new object. */ ++ if (!GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing) ++ _dl_audit_objopen (l, nsid); ++#endif ++} + + /* Map in the shared object NAME, actually located in REALNAME, and already + opened on FD. */ +@@ -1018,6 +1049,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, + /* Add the map for the mirrored object to the object list. */ + _dl_add_to_namespace_list (l, nsid); + ++ _dl_notify_new_object (mode, nsid, l); ++ + return l; + } + #endif +@@ -1442,33 +1475,7 @@ cannot enable executable stack as shared object requires"); + if (mode & __RTLD_SPROF) + return l; + +- /* Signal that we are going to add new objects. */ +- struct r_debug *r = _dl_debug_update (nsid); +- if (r->r_state == RT_CONSISTENT) +- { +-#ifdef SHARED +- /* Auditing checkpoint: we are going to add new objects. Since this +- is called after _dl_add_to_namespace_list the namespace is guaranteed +- to not be empty. */ +- if ((mode & __RTLD_AUDIT) == 0) +- _dl_audit_activity_nsid (nsid, LA_ACT_ADD); +-#endif +- +- /* Notify the debugger we have added some objects. We need to +- call _dl_debug_initialize in a static program in case dynamic +- linking has not been used before. */ +- r->r_state = RT_ADD; +- _dl_debug_state (); +- LIBC_PROBE (map_start, 2, nsid, r); +- } +- else +- assert (r->r_state == RT_ADD); +- +-#ifdef SHARED +- /* Auditing checkpoint: we have a new object. */ +- if (!GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing) +- _dl_audit_objopen (l, nsid); +-#endif ++ _dl_notify_new_object (mode, nsid, l); + + return l; + } +diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c +index 895dab08ea..c8c6553b8f 100644 +--- a/elf/tst-audit23.c ++++ b/elf/tst-audit23.c +@@ -17,6 +17,7 @@ + . */ + + #include ++#include + #include + #include + #include +@@ -106,8 +107,9 @@ do_test (int argc, char *argv[]) + 4. libgcc_s.so (one some architectures, for libsupport) + 5. tst-audit23mod.so + 6. libc.so (LM_ID_NEWLM). ++ 7. loader (proxy link map in new namespace) + vdso (optional and ignored). */ +- enum { max_objs = 6 }; ++ enum { max_objs = 7 }; + struct la_obj_t + { + char *lname; +@@ -236,7 +238,9 @@ do_test (int argc, char *argv[]) + + for (size_t i = 0; i < nobjs; i++) + { +- TEST_COMPARE (objs[i].closed, true); ++ /* This subtest currently does not pass because of bug 32065. */ ++ if (! (endswith (objs[i].lname, LD_SO) && objs[i].lmid != LM_ID_BASE)) ++ TEST_COMPARE (objs[i].closed, true); + free (objs[i].lname); + } + + +commit 196fce69506d0b3aede8e333ed3a5e777a645352 +Author: Florian Weimer +Date: Fri Aug 9 16:06:40 2024 +0200 + + elf: Call la_objclose for proxy link maps in _dl_fini (bug 32065) + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit c4b160744cb39eca20dc36b39c7fa6e10352706c) + +diff --git a/elf/dl-fini.c b/elf/dl-fini.c +index db996270de..a1a4c25829 100644 +--- a/elf/dl-fini.c ++++ b/elf/dl-fini.c +@@ -69,6 +69,7 @@ _dl_fini (void) + + unsigned int i; + struct link_map *l; ++ struct link_map *proxy_link_map = NULL; + assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL); + for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next) + /* Do not handle ld.so in secondary namespaces. */ +@@ -84,6 +85,11 @@ _dl_fini (void) + are not dlclose()ed from underneath us. */ + ++l->l_direct_opencount; + } ++ else ++ /* Used below to call la_objclose for the ld.so proxy ++ link map. */ ++ proxy_link_map = l; ++ + assert (ns != LM_ID_BASE || i == nloaded); + assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1); + unsigned int nmaps = i; +@@ -122,6 +128,9 @@ _dl_fini (void) + --l->l_direct_opencount; + } + ++ if (proxy_link_map != NULL) ++ _dl_audit_objclose (proxy_link_map); ++ + #ifdef SHARED + _dl_audit_activity_nsid (ns, LA_ACT_CONSISTENT); + #endif +diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c +index c8c6553b8f..583c45f38a 100644 +--- a/elf/tst-audit23.c ++++ b/elf/tst-audit23.c +@@ -236,13 +236,26 @@ do_test (int argc, char *argv[]) + } + } + ++ Lmid_t lmid_other = LM_ID_NEWLM; ++ unsigned int other_namespace_count = 0; + for (size_t i = 0; i < nobjs; i++) + { +- /* This subtest currently does not pass because of bug 32065. */ +- if (! (endswith (objs[i].lname, LD_SO) && objs[i].lmid != LM_ID_BASE)) +- TEST_COMPARE (objs[i].closed, true); ++ if (objs[i].lmid != LM_ID_BASE) ++ { ++ if (lmid_other == LM_ID_NEWLM) ++ lmid_other = objs[i].lmid; ++ TEST_COMPARE (objs[i].lmid, lmid_other); ++ ++other_namespace_count; ++ if (!(endswith (objs[i].lname, "/" LIBC_SO) ++ || endswith (objs[i].lname, "/" LD_SO))) ++ FAIL ("unexpected object in secondary namespace: %s", ++ objs[i].lname); ++ } ++ TEST_COMPARE (objs[i].closed, true); + free (objs[i].lname); + } ++ /* Both libc.so and ld.so should be present. */ ++ TEST_COMPARE (other_namespace_count, 2); + + /* la_activity(LA_ACT_CONSISTENT) should be the last callback received. + Since only one link map may be not-CONSISTENT at a time, this also + +commit bab46c3dbddab9cbdae493da3006694e31e946a5 +Author: Florian Weimer +Date: Tue Sep 3 17:57:46 2024 +0200 + + elf: Reorder audit events in dlcose to match _dl_fini (bug 32066) + + This was discovered after extending elf/tst-audit23 to cover + dlclose of the dlmopen namespace. + + Auditors already experience the new order during process + shutdown (_dl_fini), so no LAV_CURRENT bump or backwards + compatibility code seems necessary. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 495b96e064da605630a23092d1e484ade4bdc093) + +diff --git a/elf/dl-close.c b/elf/dl-close.c +index b6f4daac79..4c963097f4 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -264,6 +264,12 @@ _dl_close_worker (struct link_map *map, bool force) + _dl_catch_exception (NULL, _dl_call_fini, imap); + + #ifdef SHARED ++ /* Auditing checkpoint: we will start deleting objects. ++ This is supposed to happen before la_objclose (see _dl_fini), ++ but only once per non-recursive dlclose call. */ ++ if (!unload_any) ++ _dl_audit_activity_nsid (nsid, LA_ACT_DELETE); ++ + /* Auditing checkpoint: we remove an object. */ + _dl_audit_objclose (imap); + #endif +@@ -424,12 +430,8 @@ _dl_close_worker (struct link_map *map, bool force) + if (!unload_any) + goto out; + +-#ifdef SHARED +- /* Auditing checkpoint: we will start deleting objects. */ +- _dl_audit_activity_nsid (nsid, LA_ACT_DELETE); +-#endif +- +- /* Notify the debugger we are about to remove some loaded objects. */ ++ /* Notify the debugger we are about to remove some loaded objects. ++ LA_ACT_DELETE has already been signalled above for !unload_any. */ + struct r_debug *r = _dl_debug_update (nsid); + r->r_state = RT_DELETE; + _dl_debug_state (); +diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c +index 583c45f38a..7786a74e64 100644 +--- a/elf/tst-audit23.c ++++ b/elf/tst-audit23.c +@@ -31,16 +31,21 @@ + #include + #include + #include ++#include + + static int restart; ++static int do_dlclose; + #define CMDLINE_OPTIONS \ +- { "restart", no_argument, &restart, 1 }, ++ { "restart", no_argument, &restart, 1 }, \ ++ { "dlclose", no_argument, &do_dlclose, 1 }, \ + + static int + handle_restart (void) + { + xdlopen ("tst-audit23mod.so", RTLD_NOW); +- xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW); ++ void *handle = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW); ++ if (do_dlclose) ++ xdlclose (handle); + + return 0; + } +@@ -60,8 +65,8 @@ is_vdso (const char *str) + || startswith (str, "linux-vdso"); + } + +-static int +-do_test (int argc, char *argv[]) ++static void ++do_one_test (int argc, char *argv[], bool pass_dlclose_flag) + { + /* We must have either: + - One or four parameters left if called initially: +@@ -69,16 +74,15 @@ do_test (int argc, char *argv[]) + + "--library-path" optional + + the library path optional + + the application name */ +- if (restart) +- return handle_restart (); +- +- char *spargv[9]; ++ char *spargv[10]; + TEST_VERIFY_EXIT (((argc - 1) + 3) < array_length (spargv)); + int i = 0; + for (; i < argc - 1; i++) + spargv[i] = argv[i + 1]; + spargv[i++] = (char *) "--direct"; + spargv[i++] = (char *) "--restart"; ++ if (pass_dlclose_flag) ++ spargv[i++] = (char *) "--dlclose"; + spargv[i] = NULL; + + setenv ("LD_AUDIT", "tst-auditmod23.so", 0); +@@ -146,8 +150,14 @@ do_test (int argc, char *argv[]) + + /* The cookie identifies the object at the head of the link map, + so we only add a new namespace if it changes from the previous +- one. This works since dlmopen is the last in the test body. */ +- if (cookie != last_act_cookie && last_act_cookie != -1) ++ one. This works since dlmopen is the last in the test body. ++ ++ Currently, this does not work as expected because there ++ is no head link map if a namespace is completely deleted. ++ No LA_ACT_CONSISTENT event is generated in that case. ++ See the comment in _dl_audit_activity_nsid and bug 32068. */ ++ if (cookie != last_act_cookie && last_act_cookie != -1 ++ && !pass_dlclose_flag) + TEST_COMPARE (last_act, LA_ACT_CONSISTENT); + + if (this_act == LA_ACT_ADD && acts[nacts] != cookie) +@@ -265,7 +275,16 @@ do_test (int argc, char *argv[]) + + free (buffer); + xfclose (out); ++} ++ ++static int ++do_test (int argc, char *argv[]) ++{ ++ if (restart) ++ return handle_restart (); + ++ do_one_test (argc, argv, false); ++ do_one_test (argc, argv, true); + return 0; + } + + +commit 46adca40b8e656a5c33321c4afbb0602f85d2689 +Author: Florian Weimer +Date: Fri Mar 7 17:37:50 2025 +0100 + + elf: Fix handling of symbol versions which hash to zero (bug 29190) + + This was found through code inspection. No application impact is + known. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 46d31980943d8be2f421c1e3276b265c7552636e) + +diff --git a/elf/Makefile b/elf/Makefile +index fed2ff15f9..08b3e8514e 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -489,6 +489,7 @@ tests += \ + tst-unique2 \ + tst-unwind-ctor \ + tst-unwind-main \ ++ tst-version-hash-zero \ + unload3 \ + unload4 \ + unload5 \ +@@ -987,6 +988,9 @@ modules-names += \ + tst-unique2mod1 \ + tst-unique2mod2 \ + tst-unwind-ctor-lib \ ++ tst-version-hash-zero-linkmod \ ++ tst-version-hash-zero-mod \ ++ tst-version-hash-zero-refmod \ + unload2dep \ + unload2mod \ + unload3mod1 \ +@@ -3179,3 +3183,20 @@ tst-rtld-no-malloc-audit-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + + # Any shared object should do. + tst-rtld-no-malloc-preload-ENV = LD_PRELOAD=$(objpfx)tst-auditmod1.so ++ ++$(objpfx)tst-version-hash-zero.out: \ ++ $(objpfx)tst-version-hash-zero-mod.so \ ++ $(objpfx)tst-version-hash-zero-refmod.so ++LDFLAGS-tst-version-hash-zero-mod.so = \ ++ -Wl,--version-script=tst-version-hash-zero-mod.map ++# The run-time test module tst-version-hash-zero-refmod.so is linked ++# to a stub module, tst-version-hash-zero-linkmod.so, to produce an ++# expected relocation error. ++$(objpfx)tst-version-hash-zero-refmod.so: \ ++ $(objpfx)tst-version-hash-zero-linkmod.so ++LDFLAGS-tst-version-hash-zero-linkmod.so = \ ++ -Wl,--version-script=tst-version-hash-zero-linkmod.map \ ++ -Wl,--soname=tst-version-hash-zero-mod.so ++$(objpfx)tst-version-hash-zero-refmod.so: \ ++ $(objpfx)tst-version-hash-zero-linkmod.so ++tst-version-hash-zero-refmod.so-no-z-defs = yes +diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c +index 19ad2a25c5..7a70f1df2d 100644 +--- a/elf/dl-lookup.c ++++ b/elf/dl-lookup.c +@@ -113,12 +113,22 @@ check_match (const char *const undef_name, + /* We can match the version information or use the + default one if it is not hidden. */ + ElfW(Half) ndx = verstab[symidx] & 0x7fff; +- if ((map->l_versions[ndx].hash != version->hash +- || strcmp (map->l_versions[ndx].name, version->name)) +- && (version->hidden || map->l_versions[ndx].hash +- || (verstab[symidx] & 0x8000))) +- /* It's not the version we want. */ +- return NULL; ++ if (map->l_versions[ndx].hash == version->hash ++ && strcmp (map->l_versions[ndx].name, version->name) == 0) ++ /* This is an exact version match. Return the symbol below. */ ++ ; ++ else ++ { ++ if (!version->hidden ++ && map->l_versions[ndx].name[0] == '\0' ++ && (verstab[symidx] & 0x8000) == 0 ++ && (*num_versions)++ == 0) ++ /* This is the global default version. Store it as a ++ fallback match. */ ++ *versioned_sym = sym; ++ ++ return NULL; ++ } + } + } + else +diff --git a/elf/dl-version.c b/elf/dl-version.c +index 8966d612cc..708b1c94ea 100644 +--- a/elf/dl-version.c ++++ b/elf/dl-version.c +@@ -357,6 +357,13 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) + ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next); + } + } ++ ++ /* The empty string has ELF hash zero. This avoids a NULL check ++ before the version string comparison in check_match in ++ dl-lookup.c. */ ++ for (unsigned int i = 0; i < map->l_nversions; ++i) ++ if (map->l_versions[i].name == NULL) ++ map->l_versions[i].name = ""; + } + + /* When there is a DT_VERNEED entry with libc.so on DT_NEEDED, issue +diff --git a/elf/tst-version-hash-zero-linkmod.c b/elf/tst-version-hash-zero-linkmod.c +new file mode 100644 +index 0000000000..15e2506d01 +--- /dev/null ++++ b/elf/tst-version-hash-zero-linkmod.c +@@ -0,0 +1,22 @@ ++/* Stub module for linking tst-version-hash-zero-refmod.so. ++ 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; see the file COPYING.LIB. If ++ not, see . */ ++ ++/* The version script assigns a different symbol version for the stub ++ module. Loading the module with the incorrect version is expected ++ to fail. */ ++#include "tst-version-hash-zero-mod.c" +diff --git a/elf/tst-version-hash-zero-linkmod.map b/elf/tst-version-hash-zero-linkmod.map +new file mode 100644 +index 0000000000..2dba7c22d7 +--- /dev/null ++++ b/elf/tst-version-hash-zero-linkmod.map +@@ -0,0 +1,7 @@ ++Base { ++ local: *; ++}; ++ ++OTHER_VERSION { ++ global: global_variable; ++} Base; +diff --git a/elf/tst-version-hash-zero-mod.c b/elf/tst-version-hash-zero-mod.c +new file mode 100644 +index 0000000000..ac6b0dc4a5 +--- /dev/null ++++ b/elf/tst-version-hash-zero-mod.c +@@ -0,0 +1,20 @@ ++/* Test module with a zero version symbol hash. ++ 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; see the file COPYING.LIB. If ++ not, see . */ ++ ++/* The symbol version is assigned by version script. */ ++int global_variable; +diff --git a/elf/tst-version-hash-zero-mod.map b/elf/tst-version-hash-zero-mod.map +new file mode 100644 +index 0000000000..41eaff7914 +--- /dev/null ++++ b/elf/tst-version-hash-zero-mod.map +@@ -0,0 +1,13 @@ ++Base { ++ local: *; ++}; ++ ++/* Define the version so that tst-version-hash-zero-refmod.so passes ++ the initial symbol version check. */ ++OTHER_VERSION { ++} Base; ++ ++/* This version string hashes to zero. */ ++PPPPPPPPPPPP { ++ global: global_variable; ++} Base; +diff --git a/elf/tst-version-hash-zero-refmod.c b/elf/tst-version-hash-zero-refmod.c +new file mode 100644 +index 0000000000..cd8b3dcef5 +--- /dev/null ++++ b/elf/tst-version-hash-zero-refmod.c +@@ -0,0 +1,23 @@ ++/* Test module that triggers a relocation failure in tst-version-hash-zero. ++ 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; see the file COPYING.LIB. If ++ not, see . */ ++ ++/* This is bound to global_variable@@OTHER_VERSION via ++ tst-version-hash-zero-linkmod.so, but at run time, only ++ global_variable@PPPPPPPPPPPP exists. */ ++extern int global_variable; ++int *pointer_variable = &global_variable; +diff --git a/elf/tst-version-hash-zero.c b/elf/tst-version-hash-zero.c +new file mode 100644 +index 0000000000..66a0db4f51 +--- /dev/null ++++ b/elf/tst-version-hash-zero.c +@@ -0,0 +1,56 @@ ++/* Symbols with version hash zero should not match any version (bug 29190). ++ 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; see the file COPYING.LIB. If ++ not, see . */ ++ ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ void *handle = xdlopen ("tst-version-hash-zero-mod.so", RTLD_NOW); ++ ++ /* This used to crash because some struct r_found_version entries ++ with hash zero did not have valid version strings. */ ++ TEST_VERIFY (xdlvsym (handle, "global_variable", "PPPPPPPPPPPP") != NULL); ++ ++ /* Consistency check. */ ++ TEST_VERIFY (xdlsym (handle, "global_variable") ++ == xdlvsym (handle, "global_variable", "PPPPPPPPPPPP")); ++ ++ /* This symbol version is supposed to be missing. */ ++ TEST_VERIFY (dlvsym (handle, "global_variable", "OTHER_VERSION") == NULL); ++ ++ /* tst-version-hash-zero-refmod.so references ++ global_variable@@OTHER_VERSION and is expected to fail to load. ++ dlvsym sets the hidden flag during lookup. Relocation does not, ++ so this exercises a different failure case. */ ++ TEST_VERIFY_EXIT (dlopen ("tst-version-hash-zero-refmod.so", RTLD_NOW) ++ == NULL); ++ const char *message = dlerror (); ++ if (strstr (message, ++ ": undefined symbol: global_variable, version OTHER_VERSION") ++ == NULL) ++ FAIL_EXIT1 ("unexpected dlopen failure: %s", message); ++ ++ xdlclose (handle); ++ return 0; ++} ++ ++#include + +commit cadc0a84503b6d0f6d5bb88b2888475c5dc1d426 +Author: Florian Weimer +Date: Tue Jan 7 09:18:07 2025 +0100 + + elf: Second ld.so relocation only if libc.so has been loaded + + Commit 8f8dd904c4a2207699bb666f30acceb5209c8d3f (“elf: + rtld_multiple_ref is always true”) removed some code that happened + to enable compatibility with programs that do not link against + libc.so. Such programs cannot call dlopen or any dynamic linker + functions (except __tls_get_addr), so this is not really useful. + Still ld.so should not crash with a null-pointer dereference + or undefined symbol reference in these cases. + + In the main relocation loop, call _dl_relocate_object unconditionally + because it already checks if the object has been relocated. + + If libc.so was loaded, self-relocate ld.so against it and call + __rtld_mutex_init and __rtld_malloc_init_real to activate the full + implementations. Those are available only if libc.so is there, + so skip these initialization steps if libc.so is absent. Without + libc.so, the global scope can be completely empty. This can cause + ld.so self-relocation to fail because if it uses symbol-based + relocations, which is why the second ld.so self-relocation is not + performed if libc.so is missing. + + The previous concern regarding GOT updates through self-relocation + no longer applies because function pointers are updated + explicitly through __rtld_mutex_init and __rtld_malloc_init_real, + and not through relocation. However, the second ld.so self-relocation + is still delayed, in case there are other symbols being used. + + Fixes commit 8f8dd904c4a2207699bb666f30acceb5209c8d3f (“elf: + rtld_multiple_ref is always true”). + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 706209867f1ba89c458033408d419e92d8055f58) + +diff --git a/elf/Makefile b/elf/Makefile +index 08b3e8514e..1d21bfadc7 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -3200,3 +3200,20 @@ LDFLAGS-tst-version-hash-zero-linkmod.so = \ + $(objpfx)tst-version-hash-zero-refmod.so: \ + $(objpfx)tst-version-hash-zero-linkmod.so + tst-version-hash-zero-refmod.so-no-z-defs = yes ++ ++# These rules link and run the special elf/tst-nolink-libc-* tests if ++# a port adds them to the tests variables. Neither test variant is ++# linked against libc.so, but tst-nolink-libc-1 is linked against ++# ld.so. The test is always run directly, not under the dynamic ++# linker. ++CFLAGS-tst-nolink-libc.c += $(no-stack-protector) ++$(objpfx)tst-nolink-libc-1: $(objpfx)tst-nolink-libc.o $(objpfx)ld.so ++ $(LINK.o) -nostdlib -nostartfiles -o $@ $< \ ++ -Wl,--dynamic-linker=$(objpfx)ld.so,--no-as-needed $(objpfx)ld.so ++$(objpfx)tst-nolink-libc-1.out: $(objpfx)tst-nolink-libc-1 $(objpfx)ld.so ++ $< > $@ 2>&1; $(evaluate-test) ++$(objpfx)tst-nolink-libc-2: $(objpfx)tst-nolink-libc.o ++ $(LINK.o) -nostdlib -nostartfiles -o $@ $< \ ++ -Wl,--dynamic-linker=$(objpfx)ld.so ++$(objpfx)tst-nolink-libc-2.out: $(objpfx)tst-nolink-libc-2 $(objpfx)ld.so ++ $< > $@ 2>&1; $(evaluate-test) +diff --git a/elf/rtld.c b/elf/rtld.c +index 4adfc67260..ee015c58c6 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2291,25 +2291,25 @@ dl_main (const ElfW(Phdr) *phdr, + + _rtld_main_check (main_map, _dl_argv[0]); + +- /* Now we have all the objects loaded. Relocate them all except for +- the dynamic linker itself. We do this in reverse order so that copy +- relocs of earlier objects overwrite the data written by later +- objects. We do not re-relocate the dynamic linker itself in this +- loop because that could result in the GOT entries for functions we +- call being changed, and that would break us. It is safe to relocate +- the dynamic linker out of order because it has no copy relocations. +- Likewise for libc, which is relocated early to ensure that IFUNC +- resolvers in libc work. */ ++ /* Now we have all the objects loaded. */ + + int consider_profiling = GLRO(dl_profile) != NULL; + + /* If we are profiling we also must do lazy reloaction. */ + GLRO(dl_lazy) |= consider_profiling; + ++ /* If libc.so has been loaded, relocate it early, after the dynamic ++ loader itself. The initial self-relocation of ld.so should be ++ sufficient for IFUNC resolvers in libc.so. */ + if (GL(dl_ns)[LM_ID_BASE].libc_map != NULL) +- _dl_relocate_object (GL(dl_ns)[LM_ID_BASE].libc_map, +- GL(dl_ns)[LM_ID_BASE].libc_map->l_scope, +- GLRO(dl_lazy) ? RTLD_LAZY : 0, consider_profiling); ++ { ++ RTLD_TIMING_VAR (start); ++ rtld_timer_start (&start); ++ _dl_relocate_object (GL(dl_ns)[LM_ID_BASE].libc_map, ++ GL(dl_ns)[LM_ID_BASE].libc_map->l_scope, ++ GLRO(dl_lazy) ? RTLD_LAZY : 0, consider_profiling); ++ rtld_timer_accum (&relocate_time, start); ++ } + + RTLD_TIMING_VAR (start); + rtld_timer_start (&start); +@@ -2332,9 +2332,8 @@ dl_main (const ElfW(Phdr) *phdr, + /* Also allocated with the fake malloc(). */ + l->l_free_initfini = 0; + +- if (l != &GL(dl_rtld_map)) +- _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0, +- consider_profiling); ++ _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0, ++ consider_profiling); + + /* Add object to slot information data if necessasy. */ + if (l->l_tls_blocksize != 0 && __rtld_tls_init_tp_called) +@@ -2372,27 +2371,22 @@ dl_main (const ElfW(Phdr) *phdr, + /* Set up the object lookup structures. */ + _dl_find_object_init (); + +- /* Likewise for the locking implementation. */ +- __rtld_mutex_init (); +- +- /* Re-relocate ourselves with user-controlled symbol definitions. */ +- +- { +- RTLD_TIMING_VAR (start); +- rtld_timer_start (&start); +- +- _dl_relocate_object_no_relro (&GL(dl_rtld_map), main_map->l_scope, 0, 0); +- +- /* The malloc implementation has been relocated, so resolving +- its symbols (and potentially calling IFUNC resolvers) is safe +- at this point. */ +- __rtld_malloc_init_real (main_map); ++ /* If libc.so was loaded, relocate ld.so against it. Complete ld.so ++ initialization with mutex symbols from libc.so and malloc symbols ++ from the global scope. */ ++ if (GL(dl_ns)[LM_ID_BASE].libc_map != NULL) ++ { ++ RTLD_TIMING_VAR (start); ++ rtld_timer_start (&start); ++ _dl_relocate_object_no_relro (&GL(dl_rtld_map), main_map->l_scope, 0, 0); ++ rtld_timer_accum (&relocate_time, start); + +- if (GL(dl_rtld_map).l_relro_size != 0) +- _dl_protect_relro (&GL(dl_rtld_map)); ++ __rtld_mutex_init (); ++ __rtld_malloc_init_real (main_map); ++ } + +- rtld_timer_accum (&relocate_time, start); +- } ++ /* All ld.so initialization is complete. Apply RELRO. */ ++ _dl_protect_relro (&GL(dl_rtld_map)); + + /* Relocation is complete. Perform early libc initialization. This + is the initial libc, even if audit modules have been loaded with +diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile +index 34890ef69a..b7ff9c5293 100644 +--- a/sysdeps/unix/sysv/linux/Makefile ++++ b/sysdeps/unix/sysv/linux/Makefile +@@ -639,7 +639,15 @@ install-bin += \ + # install-bin + + $(objpfx)pldd: $(objpfx)xmalloc.o ++ ++test-internal-extras += tst-nolink-libc ++ifeq ($(run-built-tests),yes) ++tests-special += \ ++ $(objpfx)tst-nolink-libc-1.out \ ++ $(objpfx)tst-nolink-libc-2.out \ ++ # tests-special + endif ++endif # $(subdir) == elf + + ifeq ($(subdir),rt) + CFLAGS-mq_send.c += -fexceptions +diff --git a/sysdeps/unix/sysv/linux/arm/Makefile b/sysdeps/unix/sysv/linux/arm/Makefile +index a73c897f43..e73ce4f811 100644 +--- a/sysdeps/unix/sysv/linux/arm/Makefile ++++ b/sysdeps/unix/sysv/linux/arm/Makefile +@@ -1,5 +1,8 @@ + ifeq ($(subdir),elf) + sysdep-rtld-routines += aeabi_read_tp libc-do-syscall ++# The test uses INTERNAL_SYSCALL_CALL. In thumb mode, this uses ++# an undefined reference to __libc_do_syscall. ++CFLAGS-tst-nolink-libc.c += -marm + endif + + ifeq ($(subdir),misc) +diff --git a/sysdeps/unix/sysv/linux/tst-nolink-libc.c b/sysdeps/unix/sysv/linux/tst-nolink-libc.c +new file mode 100644 +index 0000000000..817f37784b +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/tst-nolink-libc.c +@@ -0,0 +1,25 @@ ++/* Test program not linked against libc.so and not using any glibc functions. ++ 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 ++ ++void ++_start (void) ++{ ++ INTERNAL_SYSCALL_CALL (exit_group, 0); ++} + +commit 5177ccdbcb24406a93dff1c7b21160bb62e3a240 +Author: Florian Weimer +Date: Tue Mar 11 15:30:52 2025 +0100 + + elf: Test dlopen (NULL, RTLD_LAZY) from an ELF constructor + + This call must not complete initialization of all shared objects + in the global scope because the ELF constructor which makes the call + likely has not finished initialization. Calling more constructors + at this point would expose those to a partially constructed + dependency. + + This completes the revert of commit 9897ced8e78db5d813166a7ccccfd5a + ("elf: Run constructors on cyclic recursive dlopen (bug 31986)"). + + (cherry picked from commit d604f9c500570e80febfcc6a52b63a002b466f35) + +diff --git a/elf/Makefile b/elf/Makefile +index 1d21bfadc7..bf1a1ce05a 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -417,6 +417,7 @@ tests += \ + tst-dlmopen3 \ + tst-dlmopen4 \ + tst-dlopen-auditdup \ ++ tst-dlopen-constructor-null \ + tst-dlopen-self \ + tst-dlopen-tlsmodid \ + tst-dlopen-tlsreinit1 \ +@@ -873,6 +874,8 @@ modules-names += \ + tst-dlmopen1mod \ + tst-dlopen-auditdup-auditmod \ + tst-dlopen-auditdupmod \ ++ tst-dlopen-constructor-null-mod1 \ ++ tst-dlopen-constructor-null-mod2 \ + tst-dlopen-sgid-mod \ + tst-dlopen-tlsreinitmod1 \ + tst-dlopen-tlsreinitmod2 \ +@@ -3217,3 +3220,9 @@ $(objpfx)tst-nolink-libc-2: $(objpfx)tst-nolink-libc.o + -Wl,--dynamic-linker=$(objpfx)ld.so + $(objpfx)tst-nolink-libc-2.out: $(objpfx)tst-nolink-libc-2 $(objpfx)ld.so + $< > $@ 2>&1; $(evaluate-test) ++ ++$(objpfx)tst-dlopen-constructor-null: \ ++ $(objpfx)tst-dlopen-constructor-null-mod1.so \ ++ $(objpfx)tst-dlopen-constructor-null-mod2.so ++$(objpfx)tst-dlopen-constructor-null-mod2.so: \ ++ $(objpfx)tst-dlopen-constructor-null-mod1.so +diff --git a/elf/dl-open.c b/elf/dl-open.c +index b00f283c42..80f084d5c8 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -605,6 +605,16 @@ dl_open_worker_begin (void *a) + if ((mode & RTLD_GLOBAL) && new->l_global == 0) + add_to_global_update (new); + ++ /* It is not possible to run the ELF constructor for the new ++ link map if it has not executed yet: If this dlopen call came ++ from an ELF constructor that has not put that object into a ++ consistent state, completing initialization for the entire ++ scope will expose objects that have this partially ++ constructed object among its dependencies to this ++ inconsistent state. This could happen even with a benign ++ dlopen (NULL, RTLD_LAZY) call from a constructor of an ++ initially loaded shared object. */ ++ + return; + } + +diff --git a/elf/tst-dlopen-constructor-null-mod1.c b/elf/tst-dlopen-constructor-null-mod1.c +new file mode 100644 +index 0000000000..70a7a0ad46 +--- /dev/null ++++ b/elf/tst-dlopen-constructor-null-mod1.c +@@ -0,0 +1,55 @@ ++/* Module calling dlopen (NULL, RTLD_LAZY) to obtain the global scope. ++ 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 ++ ++int mod1_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ puts ("info: tst-dlopen-constructor-null-mod1.so constructor"); ++ ++ void *handle = dlopen (NULL, RTLD_LAZY); ++ if (handle == NULL) ++ { ++ printf ("error: %s\n", dlerror ()); ++ exit (1); ++ } ++ puts ("info: dlopen returned"); ++ if (dlsym (handle, "malloc") != malloc) ++ { ++ puts ("error: dlsym did not produce expected result"); ++ exit (1); ++ } ++ dlclose (handle); ++ ++ /* Check that the second module's constructor has not executed. */ ++ if (getenv ("mod2_status") != NULL) ++ { ++ printf ("error: mod2_status environment variable set: %s\n", ++ getenv ("mod2_status")); ++ exit (1); ++ } ++ ++ /* Communicate to the second module that the constructor executed. */ ++ mod1_status = 1; ++} +diff --git a/elf/tst-dlopen-constructor-null-mod2.c b/elf/tst-dlopen-constructor-null-mod2.c +new file mode 100644 +index 0000000000..d6e945beae +--- /dev/null ++++ b/elf/tst-dlopen-constructor-null-mod2.c +@@ -0,0 +1,37 @@ ++/* Module whose constructor should not be invoked by dlopen (NULL, RTLD_LAZY). ++ 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 ++ ++extern int mod1_status; ++int mod2_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ printf ("info: tst-dlopen-constructor-null-mod2.so constructor" ++ " (mod1_status=%d)", mod1_status); ++ if (!(mod1_status == 1 && mod2_status == 0)) ++ { ++ puts ("error: mod1_status == 1 && mod2_status == 0 expected"); ++ exit (1); ++ } ++ setenv ("mod2_status", "constructed", 1); ++ mod2_status = 1; ++} +diff --git a/elf/tst-dlopen-constructor-null.c b/elf/tst-dlopen-constructor-null.c +new file mode 100644 +index 0000000000..db90643325 +--- /dev/null ++++ b/elf/tst-dlopen-constructor-null.c +@@ -0,0 +1,38 @@ ++/* Verify that dlopen (NULL, RTLD_LAZY) does not complete initialization. ++ 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 ++ . */ ++ ++/* This test mimics what the glvndSetupPthreads function in libglvnd ++ does. */ ++ ++#include ++#include ++ ++/* Defined and initialized in the shared objects. */ ++extern int mod1_status; ++extern int mod2_status; ++ ++static int ++do_test (void) ++{ ++ TEST_COMPARE (mod1_status, 1); ++ TEST_COMPARE (mod2_status, 1); ++ TEST_COMPARE_STRING (getenv ("mod2_status"), "constructed"); ++ return 0; ++} ++ ++#include + +commit 1eba4921c7762a94b032ba70d5d22eb2963ee440 +Author: Florian Weimer +Date: Fri Jul 4 21:46:05 2025 +0200 + + elf: Introduce separate _r_debug_array variable + + It replaces the ns_debug member of the namespaces. Previously, + the base namespace had an unused ns_debug member. + + This change also fixes a concurrency issue: Now _dl_debug_initialize + only updates r_next of the previous namespace's r_debug after the new + r_debug is initialized, so that only the initialized version is + observed. (Client code accessing _r_debug will benefit from load + dependency tracking in CPUs even without explicit barriers.) + + Reviewed-by: H.J. Lu + (cherry picked from commit 7278d11f3a0cd528188c719bab75575b0aea2c6e) + +diff --git a/elf/dl-debug.c b/elf/dl-debug.c +index ef56de7a29..5c49fa847e 100644 +--- a/elf/dl-debug.c ++++ b/elf/dl-debug.c +@@ -30,17 +30,37 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr) + && VERIFY_MEMBER (l_prev)) + ? 1 : -1]; + ++#ifdef SHARED ++/* r_debug structs for secondary namespaces. The first namespace is ++ handled separately because its r_debug structure must overlap with ++ the public _r_debug symbol, so the first array element corresponds ++ to LM_ID_BASE + 1. See elf/dl-debug-symbols.S. */ ++struct r_debug_extended _r_debug_array[DL_NNS - 1]; ++ ++/* Return the r_debug object for the namespace NS. */ ++static inline struct r_debug_extended * ++get_rdebug (Lmid_t ns) ++{ ++ if (ns == LM_ID_BASE) ++ return &_r_debug_extended; ++ else ++ return &_r_debug_array[ns - 1]; ++} ++#else /* !SHARED */ ++static inline struct r_debug_extended * ++get_rdebug (Lmid_t ns) ++{ ++ return &_r_debug_extended; /* There is just one namespace. */ ++} ++#endif /* !SHARED */ ++ + /* Update the `r_map' member and return the address of `struct r_debug' + of the namespace NS. */ + + struct r_debug * + _dl_debug_update (Lmid_t ns) + { +- struct r_debug_extended *r; +- if (ns == LM_ID_BASE) +- r = &_r_debug_extended; +- else +- r = &GL(dl_ns)[ns]._ns_debug; ++ struct r_debug_extended *r = get_rdebug (ns); + if (r->base.r_map == NULL) + atomic_store_release (&r->base.r_map, + (void *) GL(dl_ns)[ns]._ns_loaded); +@@ -54,34 +74,7 @@ _dl_debug_update (Lmid_t ns) + struct r_debug * + _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + { +- struct r_debug_extended *r, **pp = NULL; +- +- if (ns == LM_ID_BASE) +- { +- r = &_r_debug_extended; +- /* Initialize r_version to 1. */ +- if (_r_debug_extended.base.r_version == 0) +- _r_debug_extended.base.r_version = 1; +- } +- else if (DL_NNS > 1) +- { +- r = &GL(dl_ns)[ns]._ns_debug; +- if (r->base.r_brk == 0) +- { +- /* Add the new namespace to the linked list. After a namespace +- is initialized, r_brk becomes non-zero. A namespace becomes +- empty (r_map == NULL) when it is unused. But it is never +- removed from the linked list. */ +- struct r_debug_extended *p; +- for (pp = &_r_debug_extended.r_next; +- (p = *pp) != NULL; +- pp = &p->r_next) +- ; +- +- r->base.r_version = 2; +- } +- } +- ++ struct r_debug_extended *r = get_rdebug (ns); + if (r->base.r_brk == 0) + { + /* Tell the debugger where to find the map of loaded objects. +@@ -89,20 +82,36 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + only once. */ + r->base.r_ldbase = ldbase ?: _r_debug_extended.base.r_ldbase; + r->base.r_brk = (ElfW(Addr)) &_dl_debug_state; +- r->r_next = NULL; ++ ++#ifdef SHARED ++ /* Add the new namespace to the linked list. This assumes that ++ namespaces are allocated in increasing order. After a ++ namespace is initialized, r_brk becomes non-zero. A ++ namespace becomes empty (r_map == NULL) when it is unused. ++ But it is never removed from the linked list. */ ++ ++ if (ns != LM_ID_BASE) ++ { ++ r->base.r_version = 2; ++ if (ns - 1 == LM_ID_BASE) ++ { ++ atomic_store_release (&_r_debug_extended.r_next, r); ++ /* Now there are multiple namespaces. */ ++ atomic_store_release (&_r_debug_extended.base.r_version, 2); ++ } ++ else ++ /* Update r_debug_extended of the previous namespace. */ ++ atomic_store_release (&_r_debug_array[ns - 2].r_next, r); ++ } ++ else ++#endif /* SHARED */ ++ r->base.r_version = 1; + } + + if (r->base.r_map == NULL) + atomic_store_release (&r->base.r_map, + (void *) GL(dl_ns)[ns]._ns_loaded); + +- if (pp != NULL) +- { +- atomic_store_release (pp, r); +- /* Bump r_version to 2 for the new namespace. */ +- atomic_store_release (&_r_debug_extended.base.r_version, 2); +- } +- + return &r->base; + } + +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 91447a5e77..8b9ae37830 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -350,8 +350,6 @@ struct rtld_global + size_t n_elements; + void (*free) (void *); + } _ns_unique_sym_table; +- /* Keep track of changes to each namespace' list. */ +- struct r_debug_extended _ns_debug; + } _dl_ns[DL_NNS]; + /* One higher than index of last used namespace. */ + EXTERN size_t _dl_nns; + +commit 7d649d1c990441181c903bd2f99251cbf1552180 +Author: Florian Weimer +Date: Fri Jul 4 21:46:16 2025 +0200 + + elf: Introduce _dl_debug_change_state + + It combines updating r_state with the debugger notification. + + The second change to _dl_open introduces an additional debugger + notification for dlmopen, but debuggers are expected to ignore it. + + Reviewed-by: H.J. Lu + (cherry picked from commit 8329939a37f483a16013dd8af8303cbcb86d92cb) + +diff --git a/elf/dl-close.c b/elf/dl-close.c +index 4c963097f4..fb27a1231c 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -433,8 +433,7 @@ _dl_close_worker (struct link_map *map, bool force) + /* Notify the debugger we are about to remove some loaded objects. + LA_ACT_DELETE has already been signalled above for !unload_any. */ + struct r_debug *r = _dl_debug_update (nsid); +- r->r_state = RT_DELETE; +- _dl_debug_state (); ++ _dl_debug_change_state (r, RT_DELETE); + LIBC_PROBE (unmap_start, 2, nsid, r); + + if (unload_global) +@@ -726,8 +725,7 @@ _dl_close_worker (struct link_map *map, bool force) + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); + + /* Notify the debugger those objects are finalized and gone. */ +- r->r_state = RT_CONSISTENT; +- _dl_debug_state (); ++ _dl_debug_change_state (r, RT_CONSISTENT); + LIBC_PROBE (unmap_complete, 2, nsid, r); + + #ifdef SHARED +diff --git a/elf/dl-debug.c b/elf/dl-debug.c +index 5c49fa847e..b3777ffc13 100644 +--- a/elf/dl-debug.c ++++ b/elf/dl-debug.c +@@ -67,6 +67,13 @@ _dl_debug_update (Lmid_t ns) + return &r->base; + } + ++void ++_dl_debug_change_state (struct r_debug *r, int state) ++{ ++ atomic_store_release (&r->r_state, state); ++ _dl_debug_state (); ++} ++ + /* Initialize _r_debug_extended for the namespace NS. LDBASE is the + run-time load address of the dynamic linker, to be put in + _r_debug_extended.r_ldbase. Return the address of _r_debug. */ +diff --git a/elf/dl-load.c b/elf/dl-load.c +index 54c9c4d779..23b7e6a0c6 100644 +--- a/elf/dl-load.c ++++ b/elf/dl-load.c +@@ -936,8 +936,7 @@ _dl_notify_new_object (int mode, Lmid_t nsid, struct link_map *l) + /* Notify the debugger we have added some objects. We need to + call _dl_debug_initialize in a static program in case dynamic + linking has not been used before. */ +- r->r_state = RT_ADD; +- _dl_debug_state (); ++ _dl_debug_change_state (r, RT_ADD); + LIBC_PROBE (map_start, 2, nsid, r); + } + else +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 80f084d5c8..6f6d3ddbf9 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -792,8 +792,7 @@ dl_open_worker (void *a) + #ifdef SHARED + bool was_not_consistent = r->r_state != RT_CONSISTENT; + #endif +- r->r_state = RT_CONSISTENT; +- _dl_debug_state (); ++ _dl_debug_change_state (r, RT_CONSISTENT); + LIBC_PROBE (map_complete, 3, nsid, r, args->map); + + #ifdef SHARED +@@ -871,7 +870,7 @@ no more namespaces available for dlmopen()")); + } + + GL(dl_ns)[nsid].libc_map = NULL; +- _dl_debug_update (nsid)->r_state = RT_CONSISTENT; ++ _dl_debug_change_state (_dl_debug_update (nsid), RT_CONSISTENT); + } + /* Never allow loading a DSO in a namespace which is empty. Such + direct placements is only causing problems. Also don't allow +diff --git a/elf/rtld.c b/elf/rtld.c +index ee015c58c6..e1b67f8ca6 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -1851,8 +1851,7 @@ dl_main (const ElfW(Phdr) *phdr, + elf_setup_debug_entry (main_map, r); + + /* We start adding objects. */ +- r->r_state = RT_ADD; +- _dl_debug_state (); ++ _dl_debug_change_state (r, RT_ADD); + LIBC_PROBE (init_start, 2, LM_ID_BASE, r); + + /* Auditing checkpoint: we are ready to signal that the initial map +@@ -2403,8 +2402,7 @@ dl_main (const ElfW(Phdr) *phdr, + /* Notify the debugger all new objects are now ready to go. We must re-get + the address since by now the variable might be in another object. */ + r = _dl_debug_update (LM_ID_BASE); +- r->r_state = RT_CONSISTENT; +- _dl_debug_state (); ++ _dl_debug_change_state (r, RT_CONSISTENT); + LIBC_PROBE (init_complete, 2, LM_ID_BASE, r); + + /* Auditing checkpoint: we have added all objects. */ +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 8b9ae37830..017406e7fa 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -1067,8 +1067,14 @@ extern void _dl_debug_state (void); + rtld_hidden_proto (_dl_debug_state) + + /* Initialize `struct r_debug_extended' for the namespace NS. LDBASE +- is the run-time load address of the dynamic linker, to be put in the +- `r_ldbase' member. Return the address of the structure. */ ++ is the run-time load address of the dynamic linker, to be put in ++ the `r_ldbase' member. ++ ++ Return the address of the r_debug structure for the namespace. ++ This is not merely a convenience or optimization, but it is ++ necessary for the LIBC_PROBE Systemtap/debugger probes to work ++ reliably: direct variable access can create probes that tools ++ cannot consume. */ + extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + attribute_hidden; + +@@ -1076,6 +1082,10 @@ extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + of the namespace NS. */ + extern struct r_debug *_dl_debug_update (Lmid_t ns) attribute_hidden; + ++/* Update R->r_state to STATE and notify the debugger by calling ++ _dl_debug_state. */ ++void _dl_debug_change_state (struct r_debug *r, int state) attribute_hidden; ++ + /* Initialize the basic data structure for the search paths. SOURCE + is either "LD_LIBRARY_PATH" or "--library-path". + GLIBC_HWCAPS_PREPEND adds additional glibc-hwcaps subdirectories to + +commit fbc5e95f83adb1d1fe6a518117b7aeba58c3b92e +Author: Florian Weimer +Date: Fri Jul 4 21:46:30 2025 +0200 + + elf: Restore support for _r_debug interpositions and copy relocations + + The changes in commit a93d9e03a31ec14405cb3a09aa95413b67067380 + ("Extend struct r_debug to support multiple namespaces [BZ #15971]") + break the dyninst dynamic instrumentation tool. It brings its + own definition of _r_debug (rather than a declaration). + + Furthermore, it turns out it is rather hard to use the proposed + handshake for accessing _r_debug via DT_DEBUG. If applications want + to access _r_debug, they can do so directly if the relevant code has + been built as PIC. To protect against harm from accidental copy + relocations due to linker relaxations, this commit restores copy + relocation support by adjusting both copies if interposition or + copy relocations are in play. Therefore, it is possible to + use a hidden reference in ld.so to access _r_debug. + + Only perform the copy relocation initialization if libc has been + loaded. Otherwise, the ld.so search scope can be empty, and the + lookup of the _r_debug symbol mail fail. + + Reviewed-by: H.J. Lu + (cherry picked from commit ea85e7d55087075376a29261e722e4fae14ecbe7) + +diff --git a/elf/Makefile b/elf/Makefile +index bf1a1ce05a..b0894c6430 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -416,6 +416,8 @@ tests += \ + tst-dlmopen1 \ + tst-dlmopen3 \ + tst-dlmopen4 \ ++ tst-dlmopen4-nonpic \ ++ tst-dlmopen4-pic \ + tst-dlopen-auditdup \ + tst-dlopen-constructor-null \ + tst-dlopen-self \ +@@ -2080,6 +2082,13 @@ $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so + + $(objpfx)tst-dlmopen4.out: $(objpfx)tst-dlmopen1mod.so + ++CFLAGS-tst-dlmopen4-pic.c += -fPIC ++$(objpfx)tst-dlmopen4-pic.out: $(objpfx)tst-dlmopen1mod.so ++ ++CFLAGS-tst-dlmopen4-nonpic.c += -fno-pie ++tst-dlmopen4-nonpic-no-pie = yes ++$(objpfx)tst-dlmopen4-nonpic.out: $(objpfx)tst-dlmopen1mod.so ++ + $(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so + tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + +diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S +index 4e35adef5d..33f0fc77de 100644 +--- a/elf/dl-debug-symbols.S ++++ b/elf/dl-debug-symbols.S +@@ -38,3 +38,4 @@ + _r_debug: + _r_debug_extended: + .zero R_DEBUG_EXTENDED_SIZE ++rtld_hidden_def (_r_debug) +diff --git a/elf/dl-debug.c b/elf/dl-debug.c +index b3777ffc13..8b51332309 100644 +--- a/elf/dl-debug.c ++++ b/elf/dl-debug.c +@@ -16,6 +16,7 @@ + License along with the GNU C Library; if not, see + . */ + ++#include + #include + + +@@ -37,6 +38,37 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr) + to LM_ID_BASE + 1. See elf/dl-debug-symbols.S. */ + struct r_debug_extended _r_debug_array[DL_NNS - 1]; + ++/* If not null, pointer to the _r_debug in the main executable. */ ++static struct r_debug *_r_debug_main; ++ ++void ++_dl_debug_post_relocate (struct link_map *main_map) ++{ ++ /* Perform a full symbol search in all objects, to maintain ++ compatibility if interposed _r_debug definitions. The lookup ++ cannot fail because there is a definition in ld.so, and this ++ function is only called if the ld.so search scope is not empty. */ ++ const ElfW(Sym) *sym = NULL; ++ lookup_t result =_dl_lookup_symbol_x ("_r_debug", main_map, &sym, ++ main_map->l_scope, NULL, 0, 0, NULL); ++ if (sym->st_size >= sizeof (struct r_debug)) ++ { ++ struct r_debug *main_r_debug = DL_SYMBOL_ADDRESS (result, sym); ++ if (main_r_debug != &_r_debug_extended.base) ++ { ++ /* The extended version of the struct is not available in ++ the main executable because a copy relocation has been ++ used. r_map etc. have already been copied as part of the ++ copy relocation processing. */ ++ main_r_debug->r_version = 1; ++ ++ /* Record that dual updates of the initial link map are ++ required. */ ++ _r_debug_main = main_r_debug; ++ } ++ } ++} ++ + /* Return the r_debug object for the namespace NS. */ + static inline struct r_debug_extended * + get_rdebug (Lmid_t ns) +@@ -71,6 +103,11 @@ void + _dl_debug_change_state (struct r_debug *r, int state) + { + atomic_store_release (&r->r_state, state); ++#ifdef SHARED ++ if (r == &_r_debug_extended.base && _r_debug_main != NULL) ++ /* Update the copy-relocation of _r_debug. */ ++ atomic_store_release (&_r_debug_main->r_state, state); ++#endif + _dl_debug_state (); + } + +@@ -103,7 +140,9 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + if (ns - 1 == LM_ID_BASE) + { + atomic_store_release (&_r_debug_extended.r_next, r); +- /* Now there are multiple namespaces. */ ++ /* Now there are multiple namespaces. Note that this ++ deliberately does not update the copy in the main ++ executable (if it exists). */ + atomic_store_release (&_r_debug_extended.base.r_version, 2); + } + else +@@ -116,8 +155,15 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + } + + if (r->base.r_map == NULL) +- atomic_store_release (&r->base.r_map, +- (void *) GL(dl_ns)[ns]._ns_loaded); ++ { ++ struct link_map_public *l = (void *) GL(dl_ns)[ns]._ns_loaded; ++ atomic_store_release (&r->base.r_map, l); ++#ifdef SHARED ++ if (ns == LM_ID_BASE && _r_debug_main != NULL) ++ /* Update the copy-relocation of _r_debug. */ ++ atomic_store_release (&_r_debug_main->r_map, l); ++#endif ++ } + + return &r->base; + } +diff --git a/elf/rtld.c b/elf/rtld.c +index e1b67f8ca6..dc65b6e404 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2382,6 +2382,9 @@ dl_main (const ElfW(Phdr) *phdr, + + __rtld_mutex_init (); + __rtld_malloc_init_real (main_map); ++ ++ /* Update copy-relocated _r_debug if necessary. */ ++ _dl_debug_post_relocate (main_map); + } + + /* All ld.so initialization is complete. Apply RELRO. */ +diff --git a/elf/tst-dlmopen4-nonpic.c b/elf/tst-dlmopen4-nonpic.c +new file mode 100644 +index 0000000000..ad4e409953 +--- /dev/null ++++ b/elf/tst-dlmopen4-nonpic.c +@@ -0,0 +1,2 @@ ++#define BUILD_FOR_NONPIC ++#include "tst-dlmopen4.c" +diff --git a/elf/tst-dlmopen4-pic.c b/elf/tst-dlmopen4-pic.c +new file mode 100644 +index 0000000000..919fa85c25 +--- /dev/null ++++ b/elf/tst-dlmopen4-pic.c +@@ -0,0 +1,2 @@ ++#define BUILD_FOR_PIC ++#include "tst-dlmopen4.c" +diff --git a/elf/tst-dlmopen4.c b/elf/tst-dlmopen4.c +index b1c5502621..9e053fbc59 100644 +--- a/elf/tst-dlmopen4.c ++++ b/elf/tst-dlmopen4.c +@@ -46,6 +46,15 @@ do_test (void) + TEST_COMPARE (debug->base.r_version, 1); + TEST_VERIFY_EXIT (debug->r_next == NULL); + ++#ifdef BUILD_FOR_PIC ++ /* In a PIC build, using _r_debug directly should give us the same ++ object. */ ++ TEST_VERIFY (&_r_debug == &debug->base); ++#endif ++#ifdef BUILD_FOR_NONPIC ++ TEST_COMPARE (_r_debug.r_version, 1); ++#endif ++ + void *h = xdlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", + RTLD_LAZY); + +@@ -57,6 +66,19 @@ do_test (void) + const char *name = basename (debug->r_next->base.r_map->l_name); + TEST_COMPARE_STRING (name, "tst-dlmopen1mod.so"); + ++#ifdef BUILD_FOR_NONPIC ++ /* If a copy relocation is used, it must be at version 1. */ ++ if (&_r_debug != &debug->base) ++ { ++ TEST_COMPARE (_r_debug.r_version, 1); ++ TEST_COMPARE ((uintptr_t) _r_debug.r_map, ++ (uintptr_t) debug->base.r_map); ++ TEST_COMPARE (_r_debug.r_brk, debug->base.r_brk); ++ TEST_COMPARE (_r_debug.r_state, debug->base.r_state); ++ TEST_COMPARE (_r_debug.r_ldbase, debug->base.r_ldbase); ++ } ++#endif ++ + xdlclose (h); + + return 0; +diff --git a/include/link.h b/include/link.h +index 5ed445d5a6..7ca305f780 100644 +--- a/include/link.h ++++ b/include/link.h +@@ -365,6 +365,8 @@ struct auditstate + dynamic linker. */ + extern struct r_debug_extended _r_debug_extended attribute_hidden; + ++rtld_hidden_proto (_r_debug) ++ + #if __ELF_NATIVE_CLASS == 32 + # define symbind symbind32 + # define LA_SYMBIND "la_symbind32" +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 017406e7fa..043abd3697 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -1078,6 +1078,10 @@ rtld_hidden_proto (_dl_debug_state) + extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + attribute_hidden; + ++/* This is called after relocation processing to handle a potential ++ copy relocation for _r_debug. */ ++void _dl_debug_post_relocate (struct link_map *main_map) attribute_hidden; ++ + /* Update the `r_map' member and return the address of `struct r_debug' + of the namespace NS. */ + extern struct r_debug *_dl_debug_update (Lmid_t ns) attribute_hidden; + +commit 87a6ccf0b41b7a11db27ab050bb145dffd1182c0 +Author: Florian Weimer +Date: Mon Jul 28 14:16:52 2025 +0200 + + elf: Compile _dl_debug_state separately (bug 33224) + + This ensures that the compiler will not inline it, so that + debuggers which do not use the Systemtap probes can reliably + set a breakpoint on it. + + Reviewed-by: Andreas K. Huettel + Tested-by: Andreas K. Huettel + (cherry picked from commit 620f0730f311635cd0e175a3ae4d0fc700c76366) + +diff --git a/elf/Makefile b/elf/Makefile +index b0894c6430..ab2da6c4c2 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -57,6 +57,7 @@ dl-routines = \ + dl-close \ + dl-debug \ + dl-debug-symbols \ ++ dl-debug_state \ + dl-deps \ + dl-exception \ + dl-execstack \ +diff --git a/elf/dl-debug.c b/elf/dl-debug.c +index 8b51332309..df36d61dcb 100644 +--- a/elf/dl-debug.c ++++ b/elf/dl-debug.c +@@ -167,14 +167,3 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + + return &r->base; + } +- +- +-/* This function exists solely to have a breakpoint set on it by the +- debugger. The debugger is supposed to find this function's address by +- examining the r_brk member of struct r_debug, but GDB 4.15 in fact looks +- for this particular symbol name in the PT_INTERP file. */ +-void +-_dl_debug_state (void) +-{ +-} +-rtld_hidden_def (_dl_debug_state) +diff --git a/elf/dl-debug_state.c b/elf/dl-debug_state.c +new file mode 100644 +index 0000000000..40c134a49e +--- /dev/null ++++ b/elf/dl-debug_state.c +@@ -0,0 +1,30 @@ ++/* Debugger hook called after dynamic linker updates. ++ Copyright (C) 1996-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 ++ ++/* This function exists solely to have a breakpoint set on it by the ++ debugger. The debugger is supposed to find this function's address by ++ examining the r_brk member of struct r_debug, but GDB 4.15 in fact looks ++ for this particular symbol name in the PT_INTERP file. Therefore, ++ this function must not be inlined. */ ++void ++_dl_debug_state (void) ++{ ++} ++rtld_hidden_def (_dl_debug_state) + +commit 3533310dd3373a095c8f84835910217c0924563c +Author: Florian Weimer +Date: Mon Aug 18 13:52:02 2025 +0200 + + elf: Preserve _rtld_global layout for the release branch + + Backporting commit 7d649d1c990441181c903bd2f99251cbf1552180 + ("elf: Introduce _dl_debug_change_state") removed the + _ns_debug member. Keep it to preseve struct layout. + +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 043abd3697..b4c6e6d2ca 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -350,6 +350,8 @@ struct rtld_global + size_t n_elements; + void (*free) (void *); + } _ns_unique_sym_table; ++ /* Keep track of changes to each namespace' list. */ ++ struct r_debug_extended _ns_debug_unused; + } _dl_ns[DL_NNS]; + /* One higher than index of last used namespace. */ + EXTERN size_t _dl_nns; + +commit c789121e026208baf0f61009150cd0feac34bf66 +Author: mengqinggang +Date: Fri Jan 3 15:49:43 2025 +0800 + + LoongArch: Regenerate preconfigure. [bug 32521] + + Add "mtls_descriptor=desc" to preconfigure.ac and regenerate preconfigure. + Fix failure: elf/tst-gnu2-tls2. + + Reported-by: Joseph S. Myers + Reported-by: Andreas K. Huettel + (cherry picked from commit d4cdb601df0a125550341f85d7011314e4746308) + +diff --git a/sysdeps/loongarch/preconfigure.ac b/sysdeps/loongarch/preconfigure.ac +index 67e4357013..df07dbf41f 100644 +--- a/sysdeps/loongarch/preconfigure.ac ++++ b/sysdeps/loongarch/preconfigure.ac +@@ -41,6 +41,7 @@ loongarch*) + AC_DEFINE_UNQUOTED([LOONGARCH_ABI_FRLEN], [$abi_flen]) + + base_machine=loongarch ++ mtls_descriptor=desc + ;; + esac + + +commit 836c9f0e0b36910814900d50868f9eb7152df82d +Author: H.J. Lu +Date: Mon Jun 9 05:22:10 2025 +0800 + + i386: Update ___tls_get_addr to preserve vector registers + + Compiler generates the following instruction sequence for dynamic TLS + access: + + leal tls_var@tlsgd(,%ebx,1), %eax + call ___tls_get_addr@PLT + + CALL instruction is transparent to compiler which assumes all registers, + except for EFLAGS, AX, CX, and DX, are unchanged after CALL. But + ___tls_get_addr is a normal function which doesn't preserve any vector + registers. + + 1. Rename the generic __tls_get_addr function to ___tls_get_addr_internal. + 2. Change ___tls_get_addr to a wrapper function with implementations for + FNSAVE, FXSAVE, XSAVE and XSAVEC to save and restore all vector registers. + 3. dl-tlsdesc-dynamic.h has: + + _dl_tlsdesc_dynamic: + /* Like all TLS resolvers, preserve call-clobbered registers. + We need two scratch regs anyway. */ + subl $32, %esp + cfi_adjust_cfa_offset (32) + + It is wrong to use + + movl %ebx, -28(%esp) + movl %esp, %ebx + cfi_def_cfa_register(%ebx) + ... + mov %ebx, %esp + cfi_def_cfa_register(%esp) + movl -28(%esp), %ebx + + to preserve EBX on stack. Fix it with: + + movl %ebx, 28(%esp) + movl %esp, %ebx + cfi_def_cfa_register(%ebx) + ... + mov %ebx, %esp + cfi_def_cfa_register(%esp) + movl 28(%esp), %ebx + + 4. Update _dl_tlsdesc_dynamic to call ___tls_get_addr_internal directly. + 5. Add have-test-mtls-traditional to compile tst-tls23-mod.c with + traditional TLS variant to verify the fix. + 6. Define DL_RUNTIME_RESOLVE_REALIGN_STACK in sysdeps/x86/sysdep.h. + + This fixes BZ #32996. + + Co-Authored-By: Adhemerval Zanella + Signed-off-by: H.J. Lu + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 848f0e46f03f22404ed9a8aabf3fd5ce8809a1be) + +diff --git a/configure b/configure +index 1d543548cd..77e1601250 100755 +--- a/configure ++++ b/configure +@@ -4901,6 +4901,9 @@ with_fp_cond=1 + # A preconfigure script may define another name to TLS descriptor variant + mtls_descriptor=gnu2 + ++# A preconfigure script may define another name to traditional TLS variant ++mtls_traditional=gnu ++ + if frags=`ls -d $srcdir/sysdeps/*/preconfigure 2> /dev/null` + then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sysdeps preconfigure fragments" >&5 +@@ -7223,6 +7226,39 @@ printf "%s\n" "$libc_cv_mtls_descriptor" >&6; } + config_vars="$config_vars + have-mtls-descriptor = $libc_cv_mtls_descriptor" + ++{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for traditional tls support" >&5 ++printf %s "checking for traditional tls support... " >&6; } ++if test ${libc_cv_test_mtls_traditional+y} ++then : ++ printf %s "(cached) " >&6 ++else case e in #( ++ e) cat > conftest.c <&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; } ++then ++ libc_cv_test_mtls_traditional=$mtls_traditional ++else ++ libc_cv_test_mtls_traditional=no ++fi ++rm -f conftest* ;; ++esac ++fi ++{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_test_mtls_traditional" >&5 ++printf "%s\n" "$libc_cv_test_mtls_traditional" >&6; } ++config_vars="$config_vars ++have-test-mtls-traditional = $libc_cv_test_mtls_traditional" ++ + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if -Wno-ignored-attributes is required for aliases" >&5 + printf %s "checking if -Wno-ignored-attributes is required for aliases... " >&6; } + if test ${libc_cv_wno_ignored_attributes+y} +diff --git a/configure.ac b/configure.ac +index 9cbc0bf68f..eb4f26d592 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -469,6 +469,9 @@ with_fp_cond=1 + # A preconfigure script may define another name to TLS descriptor variant + mtls_descriptor=gnu2 + ++# A preconfigure script may define another name to traditional TLS variant ++mtls_traditional=gnu ++ + dnl Let sysdeps/*/preconfigure act here. + LIBC_PRECONFIGURE([$srcdir], [for sysdeps]) + +@@ -1334,6 +1337,28 @@ rm -f conftest*]) + AC_SUBST(libc_cv_mtls_descriptor) + LIBC_CONFIG_VAR([have-mtls-descriptor], [$libc_cv_mtls_descriptor]) + ++dnl Check if CC supports traditional tls. ++AC_CACHE_CHECK([for traditional tls support], ++ libc_cv_test_mtls_traditional, ++[dnl ++cat > conftest.c <&AS_MESSAGE_LOG_FD]) ++then ++ libc_cv_test_mtls_traditional=$mtls_traditional ++else ++ libc_cv_test_mtls_traditional=no ++fi ++rm -f conftest*]) ++LIBC_CONFIG_VAR([have-test-mtls-traditional], ++ [$libc_cv_test_mtls_traditional]) ++ + dnl clang emits an warning for a double alias redirection, to warn the + dnl original symbol is sed even when weak definition overrides it. + dnl It is a usual pattern for weak_alias, where multiple alias point to +diff --git a/elf/Makefile b/elf/Makefile +index ab2da6c4c2..0303e08557 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -486,6 +486,7 @@ tests += \ + tst-tls19 \ + tst-tls20 \ + tst-tls21 \ ++ tst-tls23 \ + tst-tlsalign \ + tst-tlsalign-extern \ + tst-tlsgap \ +@@ -964,6 +965,7 @@ modules-names += \ + tst-tls19mod3 \ + tst-tls20mod-bad \ + tst-tls21mod \ ++ tst-tls23-mod \ + tst-tlsalign-lib \ + tst-tlsgap-mod0 \ + tst-tlsgap-mod1 \ +@@ -3197,6 +3199,13 @@ tst-rtld-no-malloc-audit-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + # Any shared object should do. + tst-rtld-no-malloc-preload-ENV = LD_PRELOAD=$(objpfx)tst-auditmod1.so + ++$(objpfx)tst-tls23: $(shared-thread-library) ++$(objpfx)tst-tls23.out: $(objpfx)tst-tls23-mod.so ++ ++ifneq (no,$(have-test-mtls-traditional)) ++CFLAGS-tst-tls23-mod.c += -mtls-dialect=$(have-test-mtls-traditional) ++endif ++ + $(objpfx)tst-version-hash-zero.out: \ + $(objpfx)tst-version-hash-zero-mod.so \ + $(objpfx)tst-version-hash-zero-refmod.so +diff --git a/elf/tst-tls23-mod.c b/elf/tst-tls23-mod.c +new file mode 100644 +index 0000000000..3ee4c70e40 +--- /dev/null ++++ b/elf/tst-tls23-mod.c +@@ -0,0 +1,32 @@ ++/* DSO used by tst-tls23. ++ 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 ++ ++__thread struct tls tls_var0 __attribute__ ((visibility ("hidden"))); ++ ++struct tls * ++apply_tls (struct tls *p) ++{ ++ INIT_TLS_CALL (); ++ BEFORE_TLS_CALL (); ++ tls_var0 = *p; ++ struct tls *ret = &tls_var0; ++ AFTER_TLS_CALL (); ++ return ret; ++} +diff --git a/elf/tst-tls23.c b/elf/tst-tls23.c +new file mode 100644 +index 0000000000..afe594c067 +--- /dev/null ++++ b/elf/tst-tls23.c +@@ -0,0 +1,106 @@ ++/* Test that __tls_get_addr preserves caller-saved registers. ++ 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 ++ ++#ifndef IS_SUPPORTED ++# define IS_SUPPORTED() true ++#endif ++ ++/* An architecture can define it to clobber caller-saved registers in ++ malloc below to verify that __tls_get_addr won't change caller-saved ++ registers. */ ++#ifndef PREPARE_MALLOC ++# define PREPARE_MALLOC() ++#endif ++ ++extern void * __libc_malloc (size_t); ++ ++size_t malloc_counter = 0; ++ ++void * ++malloc (size_t n) ++{ ++ PREPARE_MALLOC (); ++ malloc_counter++; ++ return __libc_malloc (n); ++} ++ ++static void *mod; ++static const char *modname = "tst-tls23-mod.so"; ++ ++static void ++open_mod (void) ++{ ++ mod = xdlopen (modname, RTLD_LAZY); ++ printf ("open %s\n", modname); ++} ++ ++static void ++close_mod (void) ++{ ++ xdlclose (mod); ++ mod = NULL; ++ printf ("close %s\n", modname); ++} ++ ++static void ++access_mod (const char *sym) ++{ ++ struct tls var = { -4, -4, -4, -4 }; ++ struct tls *(*f) (struct tls *) = xdlsym (mod, sym); ++ /* Check that our malloc is called. */ ++ malloc_counter = 0; ++ struct tls *p = f (&var); ++ TEST_VERIFY (malloc_counter != 0); ++ printf ("access %s: %s() = %p\n", modname, sym, p); ++ TEST_VERIFY_EXIT (memcmp (p, &var, sizeof (var)) == 0); ++ ++(p->a); ++} ++ ++static void * ++start (void *arg) ++{ ++ access_mod ("apply_tls"); ++ return arg; ++} ++ ++static int ++do_test (void) ++{ ++ if (!IS_SUPPORTED ()) ++ return EXIT_UNSUPPORTED; ++ ++ open_mod (); ++ pthread_t t = xpthread_create (NULL, start, NULL); ++ xpthread_join (t); ++ close_mod (); ++ ++ return 0; ++} ++ ++#include +diff --git a/sysdeps/x86_64/dl-trampoline-save.h b/elf/tst-tls23.h +similarity index 52% +rename from sysdeps/x86_64/dl-trampoline-save.h +rename to elf/tst-tls23.h +index 84eac4a8ac..d0e734569c 100644 +--- a/sysdeps/x86_64/dl-trampoline-save.h ++++ b/elf/tst-tls23.h +@@ -1,5 +1,5 @@ +-/* x86-64 PLT trampoline register save macros. +- Copyright (C) 2024 Free Software Foundation, Inc. ++/* Test that __tls_get_addr preserves caller-saved registers. ++ 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 +@@ -16,19 +16,25 @@ + License along with the GNU C Library; if not, see + . */ + +-#ifndef DL_STACK_ALIGNMENT +-/* Due to GCC bug: ++#include + +- https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066 ++struct tls ++{ ++ int64_t a, b, c, d; ++}; + +- __tls_get_addr may be called with 8-byte stack alignment. Although +- this bug has been fixed in GCC 4.9.4, 5.3 and 6, we can't assume +- that stack will be always aligned at 16 bytes. */ +-# define DL_STACK_ALIGNMENT 8 ++extern struct tls *apply_tls (struct tls *); ++ ++/* An architecture can define them to verify that caller-saved registers ++ aren't changed by __tls_get_addr. */ ++#ifndef INIT_TLS_CALL ++# define INIT_TLS_CALL() ++#endif ++ ++#ifndef BEFORE_TLS_CALL ++# define BEFORE_TLS_CALL() + #endif + +-/* True if _dl_runtime_resolve should align stack for STATE_SAVE or align +- stack to 16 bytes before calling _dl_fixup. */ +-#define DL_RUNTIME_RESOLVE_REALIGN_STACK \ +- (STATE_SAVE_ALIGNMENT > DL_STACK_ALIGNMENT \ +- || 16 > DL_STACK_ALIGNMENT) ++#ifndef AFTER_TLS_CALL ++# define AFTER_TLS_CALL() ++#endif +diff --git a/sysdeps/aarch64/preconfigure b/sysdeps/aarch64/preconfigure +index 19657b627b..e1b772c586 100644 +--- a/sysdeps/aarch64/preconfigure ++++ b/sysdeps/aarch64/preconfigure +@@ -3,5 +3,6 @@ aarch64*) + base_machine=aarch64 + machine=aarch64 + mtls_descriptor=desc ++ mtls_traditional=trad + ;; + esac +diff --git a/sysdeps/i386/Makefile b/sysdeps/i386/Makefile +index a2e8c0b128..ee6470d78e 100644 +--- a/sysdeps/i386/Makefile ++++ b/sysdeps/i386/Makefile +@@ -30,7 +30,9 @@ stack-align-test-flags += -malign-double + endif + + ifeq ($(subdir),elf) +-sysdep-dl-routines += tlsdesc dl-tlsdesc ++sysdep-dl-routines += \ ++ dl-tls-get-addr \ ++# sysdep-dl-routines + + tests += tst-audit3 + modules-names += tst-auditmod3a tst-auditmod3b +diff --git a/sysdeps/i386/dl-tls-get-addr.c b/sysdeps/i386/dl-tls-get-addr.c +new file mode 100644 +index 0000000000..c97e5c57be +--- /dev/null ++++ b/sysdeps/i386/dl-tls-get-addr.c +@@ -0,0 +1,68 @@ ++/* Ifunc selector for ___tls_get_addr. ++ 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 ++ . */ ++ ++#ifdef SHARED ++# define ___tls_get_addr __redirect____tls_get_addr ++# include ++# undef ___tls_get_addr ++# undef __tls_get_addr ++ ++# define SYMBOL_NAME ___tls_get_addr ++# include ++ ++extern __typeof (REDIRECT_NAME) OPTIMIZE (fnsave) attribute_hidden; ++extern __typeof (REDIRECT_NAME) OPTIMIZE (fxsave) attribute_hidden; ++extern __typeof (REDIRECT_NAME) OPTIMIZE (xsave) attribute_hidden; ++extern __typeof (REDIRECT_NAME) OPTIMIZE (xsavec) attribute_hidden; ++ ++static inline void * ++IFUNC_SELECTOR (void) ++{ ++ const struct cpu_features* cpu_features = __get_cpu_features (); ++ ++ if (cpu_features->xsave_state_size != 0) ++ { ++ if (CPU_FEATURE_USABLE_P (cpu_features, XSAVEC)) ++ return OPTIMIZE (xsavec); ++ else ++ return OPTIMIZE (xsave); ++ } ++ else if (CPU_FEATURE_USABLE_P (cpu_features, FXSR)) ++ return OPTIMIZE (fxsave); ++ return OPTIMIZE (fnsave); ++} ++ ++libc_ifunc_redirected (__redirect____tls_get_addr, ___tls_get_addr, ++ IFUNC_SELECTOR ()); ++ ++/* The special thing about the x86 TLS ABI is that we have two ++ variants of the __tls_get_addr function with different calling ++ conventions. The GNU version, which we are mostly concerned here, ++ takes the parameter in a register. The name is changed by adding ++ an additional underscore at the beginning. The Sun version uses ++ the normal calling convention. */ ++ ++rtld_hidden_proto (___tls_get_addr) ++rtld_hidden_def (___tls_get_addr) ++ ++void * ++__tls_get_addr (tls_index *ti) ++{ ++ return ___tls_get_addr (ti); ++} ++#endif +diff --git a/sysdeps/i386/dl-tls.h b/sysdeps/i386/dl-tls.h +index f17286703d..2380ec1bd4 100644 +--- a/sysdeps/i386/dl-tls.h ++++ b/sysdeps/i386/dl-tls.h +@@ -29,33 +29,13 @@ typedef struct dl_tls_index + /* This is the prototype for the GNU version. */ + extern void *___tls_get_addr (tls_index *ti) + __attribute__ ((__regparm__ (1))); +-extern void *___tls_get_addr_internal (tls_index *ti) +- __attribute__ ((__regparm__ (1))) attribute_hidden; +- + # if IS_IN (rtld) +-/* The special thing about the x86 TLS ABI is that we have two +- variants of the __tls_get_addr function with different calling +- conventions. The GNU version, which we are mostly concerned here, +- takes the parameter in a register. The name is changed by adding +- an additional underscore at the beginning. The Sun version uses +- the normal calling convention. */ +-void * +-__tls_get_addr (tls_index *ti) +-{ +- return ___tls_get_addr_internal (ti); +-} +- +- + /* Prepare using the definition of __tls_get_addr in the generic + version of this file. */ +-# define __tls_get_addr __attribute__ ((__regparm__ (1))) ___tls_get_addr +-strong_alias (___tls_get_addr, ___tls_get_addr_internal) +-rtld_hidden_proto (___tls_get_addr) +-rtld_hidden_def (___tls_get_addr) +-#else +- ++# define __tls_get_addr \ ++ __attribute__ ((__regparm__ (1))) ___tls_get_addr_internal ++# else + /* Users should get the better interface. */ +-# define __tls_get_addr ___tls_get_addr +- ++# define __tls_get_addr ___tls_get_addr + # endif + #endif +diff --git a/sysdeps/i386/dl-tlsdesc-dynamic.h b/sysdeps/i386/dl-tlsdesc-dynamic.h +index 3627028577..8a5952421e 100644 +--- a/sysdeps/i386/dl-tlsdesc-dynamic.h ++++ b/sysdeps/i386/dl-tlsdesc-dynamic.h +@@ -16,34 +16,6 @@ + License along with the GNU C Library; if not, see + . */ + +-#undef REGISTER_SAVE_AREA +- +-#if !defined USE_FNSAVE && (STATE_SAVE_ALIGNMENT % 16) != 0 +-# error STATE_SAVE_ALIGNMENT must be multiple of 16 +-#endif +- +-#if DL_RUNTIME_RESOLVE_REALIGN_STACK +-# ifdef USE_FNSAVE +-# error USE_FNSAVE shouldn't be defined +-# endif +-# ifdef USE_FXSAVE +-/* Use fxsave to save all registers. */ +-# define REGISTER_SAVE_AREA 512 +-# endif +-#else +-# ifdef USE_FNSAVE +-/* Use fnsave to save x87 FPU stack registers. */ +-# define REGISTER_SAVE_AREA 108 +-# else +-# ifndef USE_FXSAVE +-# error USE_FXSAVE must be defined +-# endif +-/* Use fxsave to save all registers. Add 12 bytes to align the stack +- to 16 bytes. */ +-# define REGISTER_SAVE_AREA (512 + 12) +-# endif +-#endif +- + .hidden _dl_tlsdesc_dynamic + .global _dl_tlsdesc_dynamic + .type _dl_tlsdesc_dynamic,@function +@@ -104,85 +76,7 @@ _dl_tlsdesc_dynamic: + ret + .p2align 4,,7 + 2: +- cfi_adjust_cfa_offset (32) +-#if DL_RUNTIME_RESOLVE_REALIGN_STACK +- movl %ebx, -28(%esp) +- movl %esp, %ebx +- cfi_def_cfa_register(%ebx) +- and $-STATE_SAVE_ALIGNMENT, %esp +-#endif +-#ifdef REGISTER_SAVE_AREA +- subl $REGISTER_SAVE_AREA, %esp +-# if !DL_RUNTIME_RESOLVE_REALIGN_STACK +- cfi_adjust_cfa_offset(REGISTER_SAVE_AREA) +-# endif +-#else +-# if !DL_RUNTIME_RESOLVE_REALIGN_STACK +-# error DL_RUNTIME_RESOLVE_REALIGN_STACK must be true +-# endif +- /* Allocate stack space of the required size to save the state. */ +- LOAD_PIC_REG (cx) +- subl RTLD_GLOBAL_RO_DL_X86_CPU_FEATURES_OFFSET+XSAVE_STATE_SIZE_OFFSET+_rtld_local_ro@GOTOFF(%ecx), %esp +-#endif +-#ifdef USE_FNSAVE +- fnsave (%esp) +-#elif defined USE_FXSAVE +- fxsave (%esp) +-#else +- /* Save the argument for ___tls_get_addr in EAX. */ +- movl %eax, %ecx +- movl $TLSDESC_CALL_STATE_SAVE_MASK, %eax +- xorl %edx, %edx +- /* Clear the XSAVE Header. */ +-# ifdef USE_XSAVE +- movl %edx, (512)(%esp) +- movl %edx, (512 + 4 * 1)(%esp) +- movl %edx, (512 + 4 * 2)(%esp) +- movl %edx, (512 + 4 * 3)(%esp) +-# endif +- movl %edx, (512 + 4 * 4)(%esp) +- movl %edx, (512 + 4 * 5)(%esp) +- movl %edx, (512 + 4 * 6)(%esp) +- movl %edx, (512 + 4 * 7)(%esp) +- movl %edx, (512 + 4 * 8)(%esp) +- movl %edx, (512 + 4 * 9)(%esp) +- movl %edx, (512 + 4 * 10)(%esp) +- movl %edx, (512 + 4 * 11)(%esp) +- movl %edx, (512 + 4 * 12)(%esp) +- movl %edx, (512 + 4 * 13)(%esp) +- movl %edx, (512 + 4 * 14)(%esp) +- movl %edx, (512 + 4 * 15)(%esp) +-# ifdef USE_XSAVE +- xsave (%esp) +-# else +- xsavec (%esp) +-# endif +- /* Restore the argument for ___tls_get_addr in EAX. */ +- movl %ecx, %eax +-#endif +- call HIDDEN_JUMPTARGET (___tls_get_addr) +- /* Get register content back. */ +-#ifdef USE_FNSAVE +- frstor (%esp) +-#elif defined USE_FXSAVE +- fxrstor (%esp) +-#else +- /* Save and retore ___tls_get_addr return value stored in EAX. */ +- movl %eax, %ecx +- movl $TLSDESC_CALL_STATE_SAVE_MASK, %eax +- xorl %edx, %edx +- xrstor (%esp) +- movl %ecx, %eax +-#endif +-#if DL_RUNTIME_RESOLVE_REALIGN_STACK +- mov %ebx, %esp +- cfi_def_cfa_register(%esp) +- movl -28(%esp), %ebx +- cfi_restore(%ebx) +-#else +- addl $REGISTER_SAVE_AREA, %esp +- cfi_adjust_cfa_offset(-REGISTER_SAVE_AREA) +-#endif ++#include "tls-get-addr-wrapper.h" + jmp 1b + cfi_endproc + .size _dl_tlsdesc_dynamic, .-_dl_tlsdesc_dynamic +diff --git a/sysdeps/i386/dl-tlsdesc.S b/sysdeps/i386/dl-tlsdesc.S +index f002feee56..dec7049911 100644 +--- a/sysdeps/i386/dl-tlsdesc.S ++++ b/sysdeps/i386/dl-tlsdesc.S +@@ -22,23 +22,6 @@ + #include + #include "tlsdesc.h" + +-#ifndef DL_STACK_ALIGNMENT +-/* Due to GCC bug: +- +- https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066 +- +- __tls_get_addr may be called with 4-byte stack alignment. Although +- this bug has been fixed in GCC 4.9.4, 5.3 and 6, we can't assume +- that stack will be always aligned at 16 bytes. */ +-# define DL_STACK_ALIGNMENT 4 +-#endif +- +-/* True if _dl_tlsdesc_dynamic should align stack for STATE_SAVE or align +- stack to MINIMUM_ALIGNMENT bytes before calling ___tls_get_addr. */ +-#define DL_RUNTIME_RESOLVE_REALIGN_STACK \ +- (STATE_SAVE_ALIGNMENT > DL_STACK_ALIGNMENT \ +- || MINIMUM_ALIGNMENT > DL_STACK_ALIGNMENT) +- + .text + + /* This function is used to compute the TP offset for symbols in +diff --git a/sysdeps/i386/tls-get-addr-wrapper.h b/sysdeps/i386/tls-get-addr-wrapper.h +new file mode 100644 +index 0000000000..0708e5ad1d +--- /dev/null ++++ b/sysdeps/i386/tls-get-addr-wrapper.h +@@ -0,0 +1,127 @@ ++/* Wrapper of i386 ___tls_get_addr to save and restore vector registers. ++ 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 ++ . */ ++ ++#undef REGISTER_SAVE_AREA ++ ++#if !defined USE_FNSAVE && (STATE_SAVE_ALIGNMENT % 16) != 0 ++# error STATE_SAVE_ALIGNMENT must be multiple of 16 ++#endif ++ ++#if DL_RUNTIME_RESOLVE_REALIGN_STACK ++# ifdef USE_FNSAVE ++# error USE_FNSAVE shouldn't be defined ++# endif ++# ifdef USE_FXSAVE ++/* Use fxsave to save all registers. */ ++# define REGISTER_SAVE_AREA 512 ++# endif ++#else ++# ifdef USE_FNSAVE ++/* Use fnsave to save x87 FPU stack registers. */ ++# define REGISTER_SAVE_AREA 108 ++# else ++# ifndef USE_FXSAVE ++# error USE_FXSAVE must be defined ++# endif ++/* Use fxsave to save all registers. Add 12 bytes to align the stack ++ to 16 bytes. */ ++# define REGISTER_SAVE_AREA (512 + 12) ++# endif ++#endif ++ ++#if DL_RUNTIME_RESOLVE_REALIGN_STACK ++ movl %ebx, 28(%esp) ++ movl %esp, %ebx ++ cfi_def_cfa_register(%ebx) ++ and $-STATE_SAVE_ALIGNMENT, %esp ++#endif ++#ifdef REGISTER_SAVE_AREA ++ subl $REGISTER_SAVE_AREA, %esp ++# if !DL_RUNTIME_RESOLVE_REALIGN_STACK ++ cfi_adjust_cfa_offset(REGISTER_SAVE_AREA) ++# endif ++#else ++# if !DL_RUNTIME_RESOLVE_REALIGN_STACK ++# error DL_RUNTIME_RESOLVE_REALIGN_STACK must be true ++# endif ++ /* Allocate stack space of the required size to save the state. */ ++ LOAD_PIC_REG (cx) ++ subl RTLD_GLOBAL_RO_DL_X86_CPU_FEATURES_OFFSET \ ++ +XSAVE_STATE_SIZE_OFFSET+_rtld_local_ro@GOTOFF(%ecx), %esp ++#endif ++#ifdef USE_FNSAVE ++ fnsave (%esp) ++#elif defined USE_FXSAVE ++ fxsave (%esp) ++#else ++ /* Save the argument for ___tls_get_addr in EAX. */ ++ movl %eax, %ecx ++ movl $TLSDESC_CALL_STATE_SAVE_MASK, %eax ++ xorl %edx, %edx ++ /* Clear the XSAVE Header. */ ++# ifdef USE_XSAVE ++ movl %edx, (512)(%esp) ++ movl %edx, (512 + 4 * 1)(%esp) ++ movl %edx, (512 + 4 * 2)(%esp) ++ movl %edx, (512 + 4 * 3)(%esp) ++# endif ++ movl %edx, (512 + 4 * 4)(%esp) ++ movl %edx, (512 + 4 * 5)(%esp) ++ movl %edx, (512 + 4 * 6)(%esp) ++ movl %edx, (512 + 4 * 7)(%esp) ++ movl %edx, (512 + 4 * 8)(%esp) ++ movl %edx, (512 + 4 * 9)(%esp) ++ movl %edx, (512 + 4 * 10)(%esp) ++ movl %edx, (512 + 4 * 11)(%esp) ++ movl %edx, (512 + 4 * 12)(%esp) ++ movl %edx, (512 + 4 * 13)(%esp) ++ movl %edx, (512 + 4 * 14)(%esp) ++ movl %edx, (512 + 4 * 15)(%esp) ++# ifdef USE_XSAVE ++ xsave (%esp) ++# else ++ xsavec (%esp) ++# endif ++ /* Restore the argument for ___tls_get_addr in EAX. */ ++ movl %ecx, %eax ++#endif ++ call ___tls_get_addr_internal ++ /* Get register content back. */ ++#ifdef USE_FNSAVE ++ frstor (%esp) ++#elif defined USE_FXSAVE ++ fxrstor (%esp) ++#else ++ /* Save and retore ___tls_get_addr return value stored in EAX. */ ++ movl %eax, %ecx ++ movl $TLSDESC_CALL_STATE_SAVE_MASK, %eax ++ xorl %edx, %edx ++ xrstor (%esp) ++ movl %ecx, %eax ++#endif ++#if DL_RUNTIME_RESOLVE_REALIGN_STACK ++ mov %ebx, %esp ++ cfi_def_cfa_register(%esp) ++ movl 28(%esp), %ebx ++ cfi_restore(%ebx) ++#else ++ addl $REGISTER_SAVE_AREA, %esp ++ cfi_adjust_cfa_offset(-REGISTER_SAVE_AREA) ++#endif ++ ++#undef STATE_SAVE_ALIGNMENT +diff --git a/sysdeps/i386/tls_get_addr.S b/sysdeps/i386/tls_get_addr.S +new file mode 100644 +index 0000000000..7d143d8a23 +--- /dev/null ++++ b/sysdeps/i386/tls_get_addr.S +@@ -0,0 +1,57 @@ ++/* Thread-local storage handling in the ELF dynamic linker. i386 version. ++ 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 ++ ++ .text ++#ifdef SHARED ++# define USE_FNSAVE ++# define MINIMUM_ALIGNMENT 4 ++# define STATE_SAVE_ALIGNMENT 4 ++# define ___tls_get_addr _____tls_get_addr_fnsave ++# include "tls_get_addr.h" ++# undef ___tls_get_addr ++# undef MINIMUM_ALIGNMENT ++# undef USE_FNSAVE ++ ++# define MINIMUM_ALIGNMENT 16 ++ ++# define USE_FXSAVE ++# define STATE_SAVE_ALIGNMENT 16 ++# define ___tls_get_addr _____tls_get_addr_fxsave ++# include "tls_get_addr.h" ++# undef ___tls_get_addr ++# undef USE_FXSAVE ++ ++# define USE_XSAVE ++# define STATE_SAVE_ALIGNMENT 64 ++# define ___tls_get_addr _____tls_get_addr_xsave ++# include "tls_get_addr.h" ++# undef ___tls_get_addr ++# undef USE_XSAVE ++ ++# define USE_XSAVEC ++# define STATE_SAVE_ALIGNMENT 64 ++# define ___tls_get_addr _____tls_get_addr_xsavec ++# include "tls_get_addr.h" ++# undef ___tls_get_addr ++# undef USE_XSAVEC ++#endif /* SHARED */ +diff --git a/sysdeps/i386/tls_get_addr.h b/sysdeps/i386/tls_get_addr.h +new file mode 100644 +index 0000000000..1825798724 +--- /dev/null ++++ b/sysdeps/i386/tls_get_addr.h +@@ -0,0 +1,42 @@ ++/* Thread-local storage handling in the ELF dynamic linker. i386 version. ++ 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 ++ . */ ++ ++ .hidden ___tls_get_addr ++ .global ___tls_get_addr ++ .type ___tls_get_addr,@function ++ ++ /* This function is a wrapper of ___tls_get_addr_internal to ++ preserve caller-saved vector registers. */ ++ ++ cfi_startproc ++ .align 16 ++___tls_get_addr: ++ /* Like all TLS resolvers, preserve call-clobbered registers. ++ We need two scratch regs anyway. */ ++ subl $32, %esp ++ cfi_adjust_cfa_offset (32) ++ movl %ecx, 20(%esp) ++ movl %edx, 24(%esp) ++#include "tls-get-addr-wrapper.h" ++ movl 20(%esp), %ecx ++ movl 24(%esp), %edx ++ addl $32, %esp ++ cfi_adjust_cfa_offset (-32) ++ ret ++ cfi_endproc ++ .size ___tls_get_addr, .-___tls_get_addr +diff --git a/sysdeps/loongarch/preconfigure b/sysdeps/loongarch/preconfigure +index 0d1e9ed8df..6726ab8302 100644 +--- a/sysdeps/loongarch/preconfigure ++++ b/sysdeps/loongarch/preconfigure +@@ -44,6 +44,7 @@ loongarch*) + + base_machine=loongarch + mtls_descriptor=desc ++ mtls_traditional=trad + ;; + esac + +diff --git a/sysdeps/loongarch/preconfigure.ac b/sysdeps/loongarch/preconfigure.ac +index df07dbf41f..56402261df 100644 +--- a/sysdeps/loongarch/preconfigure.ac ++++ b/sysdeps/loongarch/preconfigure.ac +@@ -42,6 +42,7 @@ loongarch*) + + base_machine=loongarch + mtls_descriptor=desc ++ mtls_traditional=trad + ;; + esac + +diff --git a/sysdeps/powerpc/Makefile b/sysdeps/powerpc/Makefile +index 5e6cb07ce6..5cdb64f29b 100644 +--- a/sysdeps/powerpc/Makefile ++++ b/sysdeps/powerpc/Makefile +@@ -28,6 +28,11 @@ tst-cache-ppc-static-dlopen-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(c + $(objpfx)tst-cache-ppc-static-dlopen.out: $(objpfx)mod-cache-ppc.so + + $(objpfx)tst-cache-ppc: $(objpfx)mod-cache-ppc.so ++ ++# The test checks if the __tls_get_addr does not clobber caller-saved ++# register, so disable the powerpc specific optimization to force a ++# __tls_get_addr call. ++LDFLAGS-tst-tls23-mod.so = -Wl,--no-tls-get-addr-optimize + endif + + ifneq (no,$(multi-arch)) +diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile +index 01b0192ddf..f64cee3cd9 100644 +--- a/sysdeps/x86/Makefile ++++ b/sysdeps/x86/Makefile +@@ -4,7 +4,13 @@ endif + + ifeq ($(subdir),elf) + sysdep_routines += get-cpuid-feature-leaf +-sysdep-dl-routines += dl-get-cpu-features ++sysdep-dl-routines += \ ++ dl-get-cpu-features \ ++ dl-tlsdesc \ ++ tls_get_addr \ ++ tlsdesc \ ++# sysdep-dl-routines ++ + sysdep_headers += \ + bits/platform/features.h \ + bits/platform/x86.h \ +@@ -113,6 +119,14 @@ $(objpfx)tst-gnu2-tls2-x86-noxsavexsavec.out: \ + $(objpfx)tst-gnu2-tls2mod0.so \ + $(objpfx)tst-gnu2-tls2mod1.so \ + $(objpfx)tst-gnu2-tls2mod2.so ++ ++CFLAGS-tst-tls23.c += -msse2 ++CFLAGS-tst-tls23-mod.c += -msse2 -mtune=haswell ++ ++LDFLAGS-tst-tls23 += -rdynamic ++tst-tls23-mod.so-no-z-defs = yes ++ ++$(objpfx)tst-tls23-mod.so: $(libsupport) + endif + + ifeq ($(subdir),math) +diff --git a/sysdeps/x86/sysdep.h b/sysdeps/x86/sysdep.h +index 1d6cabd816..d5f5ec0ecc 100644 +--- a/sysdeps/x86/sysdep.h ++++ b/sysdeps/x86/sysdep.h +@@ -183,6 +183,29 @@ + + #define atom_text_section .section ".text.atom", "ax" + ++#ifndef DL_STACK_ALIGNMENT ++/* Due to GCC bug: ++ ++ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066 ++ ++ __tls_get_addr may be called with 8-byte/4-byte stack alignment. ++ Although this bug has been fixed in GCC 4.9.4, 5.3 and 6, we can't ++ assume that stack will be always aligned at 16 bytes. */ ++# ifdef __x86_64__ ++# define DL_STACK_ALIGNMENT 8 ++# define MINIMUM_ALIGNMENT 16 ++# else ++# define DL_STACK_ALIGNMENT 4 ++# endif ++#endif ++ ++/* True if _dl_runtime_resolve/_dl_tlsdesc_dynamic should align stack for ++ STATE_SAVE or align stack to MINIMUM_ALIGNMENT bytes before calling ++ _dl_fixup/__tls_get_addr. */ ++#define DL_RUNTIME_RESOLVE_REALIGN_STACK \ ++ (STATE_SAVE_ALIGNMENT > DL_STACK_ALIGNMENT \ ++ || MINIMUM_ALIGNMENT > DL_STACK_ALIGNMENT) ++ + #endif /* __ASSEMBLER__ */ + + #endif /* _X86_SYSDEP_H */ +diff --git a/sysdeps/x86/tst-tls23.c b/sysdeps/x86/tst-tls23.c +new file mode 100644 +index 0000000000..6130d91cf8 +--- /dev/null ++++ b/sysdeps/x86/tst-tls23.c +@@ -0,0 +1,22 @@ ++#ifndef __x86_64__ ++#include ++ ++#define IS_SUPPORTED() CPU_FEATURE_ACTIVE (SSE2) ++#endif ++ ++/* Set XMM0...XMM7 to all 1s. */ ++#define PREPARE_MALLOC() \ ++{ \ ++ asm volatile ("pcmpeqd %%xmm0, %%xmm0" : : : "xmm0" ); \ ++ asm volatile ("pcmpeqd %%xmm1, %%xmm1" : : : "xmm1" ); \ ++ asm volatile ("pcmpeqd %%xmm2, %%xmm2" : : : "xmm2" ); \ ++ asm volatile ("pcmpeqd %%xmm3, %%xmm3" : : : "xmm3" ); \ ++ asm volatile ("pcmpeqd %%xmm4, %%xmm4" : : : "xmm4" ); \ ++ asm volatile ("pcmpeqd %%xmm5, %%xmm5" : : : "xmm5" ); \ ++ asm volatile ("pcmpeqd %%xmm6, %%xmm6" : : : "xmm6" ); \ ++ asm volatile ("pcmpeqd %%xmm7, %%xmm7" : : : "xmm7" ); \ ++} ++ ++#include ++ ++v2di v1, v2, v3; +diff --git a/sysdeps/x86/tst-tls23.h b/sysdeps/x86/tst-tls23.h +new file mode 100644 +index 0000000000..21cee4ca07 +--- /dev/null ++++ b/sysdeps/x86/tst-tls23.h +@@ -0,0 +1,35 @@ ++/* Test that __tls_get_addr preserves XMM registers. ++ 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 ++ ++typedef long long v2di __attribute__((vector_size(16))); ++extern v2di v1, v2, v3; ++ ++#define BEFORE_TLS_CALL() \ ++ v1 = __extension__(v2di){0, 0}; \ ++ v2 = __extension__(v2di){0, 0}; ++ ++#define AFTER_TLS_CALL() \ ++ v3 = __extension__(v2di){0, 0}; \ ++ asm volatile ("" : "+x" (v3)); \ ++ union { v2di x; long long a[2]; } u; \ ++ u.x = v3; \ ++ TEST_VERIFY_EXIT (u.a[0] == 0 && u.a[1] == 0); ++ ++#include +diff --git a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile +index e4895bd730..5ca6e5d034 100644 +--- a/sysdeps/x86_64/Makefile ++++ b/sysdeps/x86_64/Makefile +@@ -41,9 +41,6 @@ ifeq ($(subdir),elf) + CFLAGS-.os += $(if $(filter $(@F),$(patsubst %,%.os,$(all-rtld-routines))),\ + -mno-mmx) + +-sysdep-dl-routines += tlsdesc dl-tlsdesc tls_get_addr +- +-tests += ifuncmain8 + modules-names += ifuncmod8 + + $(objpfx)ifuncmain8: $(objpfx)ifuncmod8.so +diff --git a/sysdeps/x86_64/dl-tlsdesc.S b/sysdeps/x86_64/dl-tlsdesc.S +index 057a10862a..586079291a 100644 +--- a/sysdeps/x86_64/dl-tlsdesc.S ++++ b/sysdeps/x86_64/dl-tlsdesc.S +@@ -22,7 +22,6 @@ + #include + #include + #include "tlsdesc.h" +-#include "dl-trampoline-save.h" + + /* Area on stack to save and restore registers used for parameter + passing when calling _dl_tlsdesc_dynamic. */ +diff --git a/sysdeps/x86_64/dl-trampoline.S b/sysdeps/x86_64/dl-trampoline.S +index 87c5137837..4c11fcf032 100644 +--- a/sysdeps/x86_64/dl-trampoline.S ++++ b/sysdeps/x86_64/dl-trampoline.S +@@ -22,7 +22,6 @@ + #include + #include + #include +-#include "dl-trampoline-save.h" + + /* Area on stack to save and restore registers used for parameter + passing when calling _dl_fixup. */ + +commit 340df50b37c39bb50e39cf74060c6e395fed8eea +Author: Florian Weimer +Date: Mon Nov 25 17:32:54 2024 +0100 + + debug: Wire up tst-longjmp_chk3 + + The test was added in commit ac8cc9e300a002228eb7e660df3e7b333d9a7414 + without all the required Makefile scaffolding. Tweak the test + so that it actually builds (including with dynamic SIGSTKSZ). + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 4b7cfcc3fbfab55a1bbb32a2da69c048060739d6) + +diff --git a/debug/Makefile b/debug/Makefile +index 3903cc97a3..76c311d284 100644 +--- a/debug/Makefile ++++ b/debug/Makefile +@@ -287,6 +287,7 @@ tests = \ + tst-fortify-wide \ + tst-longjmp_chk \ + tst-longjmp_chk2 \ ++ tst-longjmp_chk3 \ + tst-realpath-chk \ + tst-sprintf-fortify-rdonly \ + tst-sprintf-fortify-unchecked \ +diff --git a/debug/tst-longjmp_chk3.c b/debug/tst-longjmp_chk3.c +index 9ff9977207..7bf1646b35 100644 +--- a/debug/tst-longjmp_chk3.c ++++ b/debug/tst-longjmp_chk3.c +@@ -18,9 +18,12 @@ + + #include + #include ++#include + #include + +-static char buf[SIGSTKSZ * 4]; ++#include ++ ++static char *buf; + static jmp_buf jb; + + static void +@@ -49,8 +52,10 @@ do_test (void) + set_fortify_handler (handler); + + /* Create a valid signal stack and enable it. */ ++ size_t bufsize = SIGSTKSZ * 4; ++ buf = xmalloc (bufsize); + ss.ss_sp = buf; +- ss.ss_size = sizeof (buf); ++ ss.ss_size = bufsize; + ss.ss_flags = 0; + if (sigaltstack (&ss, NULL) < 0) + { +@@ -65,8 +70,8 @@ do_test (void) + + /* Shrink the signal stack so the jmpbuf is now invalid. + We adjust the start & end to handle stacks that grow up & down. */ +- ss.ss_sp = buf + sizeof (buf) / 2; +- ss.ss_size = sizeof (buf) / 4; ++ ss.ss_sp = buf + bufsize / 2; ++ ss.ss_size = bufsize / 4; + if (sigaltstack (&ss, NULL) < 0) + { + printf ("second sigaltstack failed: %m\n"); + +commit 3e335fad3567b1634c1bb525b8defad86e488594 +Author: Florian Weimer +Date: Tue Nov 26 19:26:13 2024 +0100 + + debug: Fix tst-longjmp_chk3 build failure on Hurd + + Explicitly include for _exit and getpid. + + (cherry picked from commit 4836a9af89f1b4d482e6c72ff67e36226d36434c) + +diff --git a/debug/tst-longjmp_chk3.c b/debug/tst-longjmp_chk3.c +index 7bf1646b35..9b9db3b9e9 100644 +--- a/debug/tst-longjmp_chk3.c ++++ b/debug/tst-longjmp_chk3.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include + + +commit d67776356dc0ed6986e5f924cf1ac166f68324b7 +Author: H.J. Lu +Date: Mon Jul 28 12:16:11 2025 -0700 + + i386: Add GLIBC_ABI_GNU_TLS version [BZ #33221] + + On i386, programs and shared libraries with __thread usage may fail + silently at run-time against glibc without the TLS run-time fix for: + + https://sourceware.org/bugzilla/show_bug.cgi?id=32996 + + Add GLIBC_ABI_GNU_TLS version to indicate that glibc has the working + GNU TLS run-time. Linker can add the GLIBC_ABI_GNU_TLS version to + binaries which depend on the working TLS run-time so that such programs + and shared libraries will fail to load and run at run-time against + libc.so without the GLIBC_ABI_GNU_TLS version, instead of fail silently + at random. + + This fixes BZ #33221. + + Signed-off-by: H.J. Lu + Reviewed-by: Sam James + (cherry picked from commit ed1b7a5a489ab555a27fad9c101ebe2e1c1ba881) + +diff --git a/sysdeps/i386/Makefile b/sysdeps/i386/Makefile +index ee6470d78e..c0c017b899 100644 +--- a/sysdeps/i386/Makefile ++++ b/sysdeps/i386/Makefile +@@ -60,6 +60,15 @@ $(objpfx)tst-ld-sse-use.out: ../sysdeps/i386/tst-ld-sse-use.sh $(objpfx)ld.so + @echo "Checking ld.so for SSE register use. This will take a few seconds..." + $(BASH) $< $(objpfx) '$(NM)' '$(OBJDUMP)' '$(READELF)' > $@; \ + $(evaluate-test) ++ ++tests-special += $(objpfx)check-gnu-tls.out ++ ++$(objpfx)check-gnu-tls.out: $(common-objpfx)libc.so ++ LC_ALL=C $(READELF) -V -W $< \ ++ | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ ++ | grep GLIBC_ABI_GNU_TLS > $@; \ ++ $(evaluate-test) ++generated += check-gnu-tls.out + else + CFLAGS-.os += $(if $(filter rtld-%.os,$(@F)), $(rtld-CFLAGS)) + endif +diff --git a/sysdeps/i386/Versions b/sysdeps/i386/Versions +index 36e23b466a..9c84c8ef04 100644 +--- a/sysdeps/i386/Versions ++++ b/sysdeps/i386/Versions +@@ -28,6 +28,11 @@ libc { + GLIBC_2.13 { + __fentry__; + } ++ GLIBC_ABI_GNU_TLS { ++ # 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 { + +commit d6f67d835c8814b77efbc005977fd754873b8784 +Author: H.J. Lu +Date: Mon Aug 18 09:06:48 2025 -0700 + + i386: Also add GLIBC_ABI_GNU2_TLS version [BZ #33129] + + Since the GNU2 TLS run-time bug: + + https://sourceware.org/bugzilla/show_bug.cgi?id=31372 + + affects both i386 and x86-64, also add GLIBC_ABI_GNU2_TLS version to i386 + to indicate the working GNU2 TLS run-time. For x86-64, the additional + GNU2 TLS run-time bug fix is needed for + + https://sourceware.org/bugzilla/show_bug.cgi?id=31501 + + Signed-off-by: H.J. Lu + Reviewed-by: Sam James + (cherry picked from commit bd4628f3f18ac312408782eea450429c6f044860) + +diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile +index f64cee3cd9..c814060e08 100644 +--- a/sysdeps/x86/Makefile ++++ b/sysdeps/x86/Makefile +@@ -127,6 +127,15 @@ LDFLAGS-tst-tls23 += -rdynamic + tst-tls23-mod.so-no-z-defs = yes + + $(objpfx)tst-tls23-mod.so: $(libsupport) ++ ++tests-special += $(objpfx)check-gnu2-tls.out ++ ++$(objpfx)check-gnu2-tls.out: $(common-objpfx)libc.so ++ LC_ALL=C $(READELF) -V -W $< \ ++ | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ ++ | grep GLIBC_ABI_GNU2_TLS > $@; \ ++ $(evaluate-test) ++generated += check-gnu2-tls.out + endif + + ifeq ($(subdir),math) +diff --git a/sysdeps/x86/Versions b/sysdeps/x86/Versions +index 4b10c4b5d7..e8dcfccbe4 100644 +--- a/sysdeps/x86/Versions ++++ b/sysdeps/x86/Versions +@@ -7,4 +7,9 @@ libc { + GLIBC_2.33 { + __x86_get_cpuid_feature_leaf; + } ++ GLIBC_ABI_GNU2_TLS { ++ # This symbol is used only for empty version map and will be removed ++ # by scripts/versions.awk. ++ __placeholder_only_for_empty_version_map; ++ } + } +diff --git a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile +index 5ca6e5d034..27fd7ddea2 100644 +--- a/sysdeps/x86_64/Makefile ++++ b/sysdeps/x86_64/Makefile +@@ -215,15 +215,6 @@ $(objpfx)check-dt-x86-64-plt.out: $(common-objpfx)libc.so + | grep GLIBC_ABI_DT_X86_64_PLT > $@; \ + $(evaluate-test) + generated += check-dt-x86-64-plt.out +- +-tests-special += $(objpfx)check-gnu2-tls.out +- +-$(objpfx)check-gnu2-tls.out: $(common-objpfx)libc.so +- LC_ALL=C $(READELF) -V -W $< \ +- | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ +- | grep GLIBC_ABI_GNU2_TLS > $@; \ +- $(evaluate-test) +-generated += check-gnu2-tls.out + endif + + test-internal-extras += tst-gnu2-tls2mod1 +diff --git a/sysdeps/x86_64/Versions b/sysdeps/x86_64/Versions +index 0a759029e5..6a989ad3b3 100644 +--- a/sysdeps/x86_64/Versions ++++ b/sysdeps/x86_64/Versions +@@ -5,11 +5,6 @@ libc { + GLIBC_2.13 { + __fentry__; + } +- GLIBC_ABI_GNU2_TLS { +- # This symbol is used only for empty version map and will be removed +- # by scripts/versions.awk. +- __placeholder_only_for_empty_version_map; +- } + GLIBC_ABI_DT_X86_64_PLT { + # This symbol is used only for empty version map and will be removed + # by scripts/versions.awk. + +commit f7c0193b61357cb4b52c5805dd8b58718e63ea3b +Author: Florian Weimer +Date: Fri May 16 19:53:09 2025 +0200 + + Optimize __libc_tsd_* thread variable access + + These variables are not exported, and libc.so TLS is initial-exec + anyway. Declare these variables as hidden and use the initial-exec + TLS model. + + Reviewed-by: Frédéric Bérat + (cherry picked from commit a894f04d877653bea1639fc9a4adf73bd9347bf4) + +diff --git a/include/ctype.h b/include/ctype.h +index ae078a63d3..a15e5b6678 100644 +--- a/include/ctype.h ++++ b/include/ctype.h +@@ -29,9 +29,12 @@ libc_hidden_proto (toupper) + # define CTYPE_EXTERN_INLINE extern inline + # endif + +-extern __thread const uint16_t * __libc_tsd_CTYPE_B; +-extern __thread const int32_t * __libc_tsd_CTYPE_TOUPPER; +-extern __thread const int32_t * __libc_tsd_CTYPE_TOLOWER; ++extern __thread const uint16_t * __libc_tsd_CTYPE_B ++ attribute_hidden attribute_tls_model_ie; ++extern __thread const int32_t * __libc_tsd_CTYPE_TOUPPER ++ attribute_hidden attribute_tls_model_ie; ++extern __thread const int32_t * __libc_tsd_CTYPE_TOLOWER ++ attribute_hidden attribute_tls_model_ie; + + + CTYPE_EXTERN_INLINE const uint16_t ** __attribute__ ((const)) +diff --git a/include/rpc/rpc.h b/include/rpc/rpc.h +index 936ea3cebb..ba967833ad 100644 +--- a/include/rpc/rpc.h ++++ b/include/rpc/rpc.h +@@ -45,7 +45,8 @@ extern void __rpc_thread_key_cleanup (void) attribute_hidden; + + extern void __rpc_thread_destroy (void) attribute_hidden; + +-extern __thread struct rpc_thread_variables *__libc_tsd_RPC_VARS; ++extern __thread struct rpc_thread_variables *__libc_tsd_RPC_VARS ++ attribute_hidden attribute_tls_model_ie; + + #define RPC_THREAD_VARIABLE(x) (__rpc_thread_variables()->x) + +diff --git a/locale/localeinfo.h b/locale/localeinfo.h +index bc8e92e4dc..c3249d3715 100644 +--- a/locale/localeinfo.h ++++ b/locale/localeinfo.h +@@ -237,7 +237,8 @@ extern struct __locale_struct _nl_global_locale attribute_hidden; + /* This fetches the thread-local locale_t pointer, either one set with + uselocale or &_nl_global_locale. */ + #define _NL_CURRENT_LOCALE __libc_tsd_LOCALE +-extern __thread locale_t __libc_tsd_LOCALE; ++extern __thread locale_t __libc_tsd_LOCALE ++ attribute_hidden attribute_tls_model_ie; + + /* For static linking it is desireable to avoid always linking in the code + and data for every category when we can tell at link time that they are + +commit e20181e5106af0a0e228167d98cd8faf5064e23e +Author: Joseph Myers +Date: Fri Nov 8 01:53:48 2024 +0000 + + Avoid uninitialized result in sem_open when file does not exist + + A static analyzer apparently reported an uninitialized use of the + variable result in sem_open in the case where the file is required to + exist but does not exist. + + The report appears to be correct; set result to SEM_FAILED in that + case, and add a test for it. + + Note: the test passes for me even without the sem_open fix, I guess + because result happens to get value SEM_FAILED (i.e. 0) when + uninitialized. + + Tested for x86_64. + + (cherry picked from commit f745d78e2628cd5b13ca119ae0c0e21d08ad1906) + +diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile +index 04ea56559e..8039a211ec 100644 +--- a/sysdeps/pthread/Makefile ++++ b/sysdeps/pthread/Makefile +@@ -254,6 +254,7 @@ tests += \ + tst-sem14 \ + tst-sem15 \ + tst-sem16 \ ++ tst-sem17 \ + tst-setuid3 \ + tst-signal1 \ + tst-signal2 \ +diff --git a/sysdeps/pthread/sem_open.c b/sysdeps/pthread/sem_open.c +index e41236157a..dab734191a 100644 +--- a/sysdeps/pthread/sem_open.c ++++ b/sysdeps/pthread/sem_open.c +@@ -76,6 +76,7 @@ __sem_open (const char *name, int oflag, ...) + goto try_create; + + /* Return. errno is already set. */ ++ result = SEM_FAILED; + } + else + /* Check whether we already have this semaphore mapped and +diff --git a/sysdeps/pthread/tst-sem17.c b/sysdeps/pthread/tst-sem17.c +new file mode 100644 +index 0000000000..c3f05d196f +--- /dev/null ++++ b/sysdeps/pthread/tst-sem17.c +@@ -0,0 +1,35 @@ ++/* Test sem_open with missing file. ++ 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 ++ ++int ++do_test (void) ++{ ++ sem_unlink ("/glibc-tst-sem17"); ++ errno = 0; ++ sem_t *s = sem_open ("/glibc-tst-sem17", 0); ++ TEST_VERIFY (s == SEM_FAILED); ++ TEST_COMPARE (errno, ENOENT); ++ return 0; ++} ++ ++#include + +commit f03a78820ecda8795bd0ba094a70040c21fb38a6 +Author: Joseph Myers +Date: Fri Nov 8 17:08:09 2024 +0000 + + Rename new tst-sem17 test to tst-sem18 + + As noted by Adhemerval, we already have a tst-sem17 in nptl. + + Tested for x86_64. + + (cherry picked from commit c7dcf594f4c52fa7e2cc76918c8aa9abb98e9625) + +diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile +index 8039a211ec..830d9cb878 100644 +--- a/sysdeps/pthread/Makefile ++++ b/sysdeps/pthread/Makefile +@@ -254,7 +254,7 @@ tests += \ + tst-sem14 \ + tst-sem15 \ + tst-sem16 \ +- tst-sem17 \ ++ tst-sem18 \ + tst-setuid3 \ + tst-signal1 \ + tst-signal2 \ +diff --git a/sysdeps/pthread/tst-sem17.c b/sysdeps/pthread/tst-sem18.c +similarity index 92% +rename from sysdeps/pthread/tst-sem17.c +rename to sysdeps/pthread/tst-sem18.c +index c3f05d196f..1be207bcbe 100644 +--- a/sysdeps/pthread/tst-sem17.c ++++ b/sysdeps/pthread/tst-sem18.c +@@ -24,9 +24,9 @@ + int + do_test (void) + { +- sem_unlink ("/glibc-tst-sem17"); ++ sem_unlink ("/glibc-tst-sem18"); + errno = 0; +- sem_t *s = sem_open ("/glibc-tst-sem17", 0); ++ sem_t *s = sem_open ("/glibc-tst-sem18", 0); + TEST_VERIFY (s == SEM_FAILED); + TEST_COMPARE (errno, ENOENT); + return 0; + +commit 2966a6a3a0fd49eb4e3c9b05bc26977a1df98c99 +Author: Pierre Blanchard +Date: Wed Aug 20 17:41:50 2025 +0000 + + AArch64: Fix SVE powf routine [BZ #33299] + + Fix a bug in predicate logic introduced in last change. + A slight performance improvement from relying on all true + predicates during conversion from single to double. + This fixes BZ #33299. + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit aac077645a645bba0d67f3250e82017c539d0f4b) + +diff --git a/sysdeps/aarch64/fpu/powf_sve.c b/sysdeps/aarch64/fpu/powf_sve.c +index 08d7019a18..33bba96054 100644 +--- a/sysdeps/aarch64/fpu/powf_sve.c ++++ b/sysdeps/aarch64/fpu/powf_sve.c +@@ -223,15 +223,15 @@ sv_powf_core (const svbool_t pg, svuint32_t i, svuint32_t iz, svint32_t k, + const svbool_t ptrue = svptrue_b64 (); + + /* Unpack and promote input vectors (pg, y, z, i, k and sign_bias) into two +- * in order to perform core computation in double precision. */ ++ in order to perform core computation in double precision. */ + const svbool_t pg_lo = svunpklo (pg); + const svbool_t pg_hi = svunpkhi (pg); +- svfloat64_t y_lo +- = svcvt_f64_x (pg, svreinterpret_f32 (svunpklo (svreinterpret_u32 (y)))); +- svfloat64_t y_hi +- = svcvt_f64_x (pg, svreinterpret_f32 (svunpkhi (svreinterpret_u32 (y)))); +- svfloat64_t z_lo = svcvt_f64_x (pg, svreinterpret_f32 (svunpklo (iz))); +- svfloat64_t z_hi = svcvt_f64_x (pg, svreinterpret_f32 (svunpkhi (iz))); ++ svfloat64_t y_lo = svcvt_f64_x ( ++ ptrue, svreinterpret_f32 (svunpklo (svreinterpret_u32 (y)))); ++ svfloat64_t y_hi = svcvt_f64_x ( ++ ptrue, svreinterpret_f32 (svunpkhi (svreinterpret_u32 (y)))); ++ svfloat64_t z_lo = svcvt_f64_x (ptrue, svreinterpret_f32 (svunpklo (iz))); ++ svfloat64_t z_hi = svcvt_f64_x (ptrue, svreinterpret_f32 (svunpkhi (iz))); + svuint64_t i_lo = svunpklo (i); + svuint64_t i_hi = svunpkhi (i); + svint64_t k_lo = svunpklo (k); +@@ -312,7 +312,7 @@ svfloat32_t SV_NAME_F2 (pow) (svfloat32_t x, svfloat32_t y, const svbool_t pg) + (23 - V_POWF_EXP2_TABLE_BITS)); + + /* Compute core in extended precision and return intermediate ylogx results +- * to handle cases of underflow and underflow in exp. */ ++ to handle cases of underflow and overflow in exp. */ + svfloat32_t ylogx; + svfloat32_t ret + = sv_powf_core (yint_or_xpos, i, iz, k, y, sign_bias, &ylogx, d); + +commit 60795200d4deac38e2c9a49ba9a5ab7b2a96b242 +Author: Florian Weimer +Date: Fri Sep 12 21:33:34 2025 +0200 + + nss: Group merge does not react to ERANGE during merge (bug 33361) + + The break statement in CHECK_MERGE is expected to exit the surrounding + while loop, not the do-while loop with in the macro. Remove the + do-while loop from the macro. It is not needed to turn the macro + expansion into a single statement due to the way CHECK_MERGE is used + (and the statement expression would cover this anyway). + + Reviewed-by: Collin Funk + (cherry picked from commit 0fceed254559836b57ee05188deac649bc505d05) + +diff --git a/NEWS b/NEWS +index acb5179088..0756d27503 100644 +--- a/NEWS ++++ b/NEWS +@@ -29,6 +29,7 @@ The following bugs are resolved with this release: + [32987] elf: Fix subprocess status handling for tst-dlopen-sgid + [33185] Fix double-free after allocation failure in regcomp + [33234] Use TLS initial-exec model for __libc_tsd_CTYPE_* thread variables ++ [33361] nss: Group merge does not react to ERANGE during merge + + Version 2.40 + +diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c +index fe7d5b7d0e..3a15b1a4ae 100644 +--- a/nss/getXXbyYY_r.c ++++ b/nss/getXXbyYY_r.c +@@ -157,19 +157,15 @@ __merge_einval (LOOKUP_TYPE *a, + + #define CHECK_MERGE(err, status) \ + ({ \ +- do \ ++ if (err) \ + { \ +- if (err) \ +- { \ +- __set_errno (err); \ +- if (err == ERANGE) \ +- status = NSS_STATUS_TRYAGAIN; \ +- else \ +- status = NSS_STATUS_UNAVAIL; \ +- break; \ +- } \ ++ __set_errno (err); \ ++ if (err == ERANGE) \ ++ status = NSS_STATUS_TRYAGAIN; \ ++ else \ ++ status = NSS_STATUS_UNAVAIL; \ ++ break; \ + } \ +- while (0); \ + }) + + /* Type of the lookup function we need here. */ + +commit 43ea89efc014cb9783a09318c6e2e2a7b7bb1d7f +Author: Sunil K Pandey +Date: Mon Oct 6 18:13:04 2025 -0700 + + x86: Detect Intel Wildcat Lake Processor + + Detect Intel Wildcat Lake Processor and tune it similar to Intel Panther + Lake. https://cdrdv2.intel.com/v1/dl/getContent/671368 Section 1.2. + + Reviewed-by: H.J. Lu + (cherry picked from commit f8dd52901b72805a831d5a4cb7d971e4a3c9970b) + +diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c +index 52a2f03bdd..49ec19f5ee 100644 +--- a/sysdeps/x86/cpu-features.c ++++ b/sysdeps/x86/cpu-features.c +@@ -543,6 +543,7 @@ enum intel_microarch + INTEL_BIGCORE_PANTHERLAKE, + INTEL_BIGCORE_GRANITERAPIDS, + INTEL_BIGCORE_DIAMONDRAPIDS, ++ INTEL_BIGCORE_WILDCATLAKE, + + /* Mixed (bigcore + atom SOC). */ + INTEL_MIXED_LAKEFIELD, +@@ -702,6 +703,8 @@ intel_get_fam6_microarch (unsigned int model, + return INTEL_BIGCORE_ARROWLAKE; + case 0xCC: + return INTEL_BIGCORE_PANTHERLAKE; ++ case 0xD5: ++ return INTEL_BIGCORE_WILDCATLAKE; + case 0xAD: + case 0xAE: + return INTEL_BIGCORE_GRANITERAPIDS; +@@ -934,6 +937,7 @@ disable_tsx: + case INTEL_BIGCORE_LUNARLAKE: + case INTEL_BIGCORE_ARROWLAKE: + case INTEL_BIGCORE_PANTHERLAKE: ++ case INTEL_BIGCORE_WILDCATLAKE: + case INTEL_BIGCORE_SAPPHIRERAPIDS: + case INTEL_BIGCORE_EMERALDRAPIDS: + case INTEL_BIGCORE_GRANITERAPIDS: + +commit c4458286b3daa728e24b8a4cf86289e7eb4a0839 +Author: Sunil K Pandey +Date: Wed Sep 24 09:38:17 2025 -0700 + + x86: Detect Intel Nova Lake Processor + + Detect Intel Nova Lake Processor and tune it similar to Intel Panther + Lake. https://cdrdv2.intel.com/v1/dl/getContent/671368 Section 1.2. + + Reviewed-by: H.J. Lu + (cherry picked from commit a114e29ddd530962d2b44aa9d89f1f6075abe7fa) + +diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c +index 49ec19f5ee..89041e6e05 100644 +--- a/sysdeps/x86/cpu-features.c ++++ b/sysdeps/x86/cpu-features.c +@@ -544,6 +544,7 @@ enum intel_microarch + INTEL_BIGCORE_GRANITERAPIDS, + INTEL_BIGCORE_DIAMONDRAPIDS, + INTEL_BIGCORE_WILDCATLAKE, ++ INTEL_BIGCORE_NOVALAKE, + + /* Mixed (bigcore + atom SOC). */ + INTEL_MIXED_LAKEFIELD, +@@ -821,6 +822,17 @@ disable_tsx: + break; + } + } ++ else if (family == 18) ++ switch (model) ++ { ++ case 0x01: ++ case 0x03: ++ microarch = INTEL_BIGCORE_NOVALAKE; ++ break; ++ ++ default: ++ break; ++ } + else if (family == 19) + switch (model) + { +@@ -938,6 +950,7 @@ disable_tsx: + case INTEL_BIGCORE_ARROWLAKE: + case INTEL_BIGCORE_PANTHERLAKE: + case INTEL_BIGCORE_WILDCATLAKE: ++ case INTEL_BIGCORE_NOVALAKE: + case INTEL_BIGCORE_SAPPHIRERAPIDS: + case INTEL_BIGCORE_EMERALDRAPIDS: + case INTEL_BIGCORE_GRANITERAPIDS: + +commit c0ae0450273c7346ec788bf6a933257d455ea820 +Author: Jiamei Xie +Date: Tue Oct 14 20:14:11 2025 +0800 + + x86: fix wmemset ifunc stray '!' (bug 33542) + + The ifunc selector for wmemset had a stray '!' in the + X86_ISA_CPU_FEATURES_ARCH_P(...) check: + + if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX2) + && X86_ISA_CPU_FEATURES_ARCH_P (cpu_features, + AVX_Fast_Unaligned_Load, !)) + + This effectively negated the predicate and caused the AVX2/AVX512 + paths to be skipped, making the dispatcher fall back to the SSE2 + implementation even on CPUs where AVX2/AVX512 are available. The + regression leads to noticeable throughput loss for wmemset. + + Remove the stray '!' so the AVX_Fast_Unaligned_Load capability is + tested as intended and the correct AVX2/EVEX variants are selected. + + Impact: + - On AVX2/AVX512-capable x86_64, wmemset no longer incorrectly + falls back to SSE2; perf now shows __wmemset_evex/avx2 variants. + + Testing: + - benchtests/bench-wmemset shows improved bandwidth across sizes. + - perf confirm the selected symbol is no longer SSE2. + + Signed-off-by: xiejiamei + Signed-off-by: Li jing + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 4d86b6cdd8132e0410347e07262239750f86dfb4) + +diff --git a/sysdeps/x86_64/multiarch/ifunc-wmemset.h b/sysdeps/x86_64/multiarch/ifunc-wmemset.h +index 6e20d9dd5a..727b69f95b 100644 +--- a/sysdeps/x86_64/multiarch/ifunc-wmemset.h ++++ b/sysdeps/x86_64/multiarch/ifunc-wmemset.h +@@ -35,7 +35,7 @@ IFUNC_SELECTOR (void) + + if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX2) + && X86_ISA_CPU_FEATURES_ARCH_P (cpu_features, +- AVX_Fast_Unaligned_Load, !)) ++ AVX_Fast_Unaligned_Load,)) + { + if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX512VL)) + { + +commit e8b9a4bcc2bb1f6c71f58d29dad51252e6336091 +Author: Florian Weimer +Date: Thu Nov 6 11:14:53 2025 +0100 + + aarch64: Do not link conform tests with -Wl,-z,force-bti (bug 33601) + + If the toolchain does not default to generate BTI markers in GCC, + the main program for conform runtime tests will not have the + BTI marker that -Wl,-z,force-bti requires. Without -Wl,-z,force-bti, + the link editor will not tell the dynamic linker to enable BTI, + and the missing BTI marker is harmless. + + Reviewed-by: Yury Khrustalev + (cherry picked from commit 75b6b263e928eaca01d836f6bb8b539346b6bb2d) + +diff --git a/NEWS b/NEWS +index 0756d27503..595998fc6e 100644 +--- a/NEWS ++++ b/NEWS +@@ -30,6 +30,7 @@ The following bugs are resolved with this release: + [33185] Fix double-free after allocation failure in regcomp + [33234] Use TLS initial-exec model for __libc_tsd_CTYPE_* thread variables + [33361] nss: Group merge does not react to ERANGE during merge ++ [33601] aarch64: Do not link conform tests with -Wl,-z,force-bti + + Version 2.40 + +diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile +index 141d7d9cc2..eb34f4541e 100644 +--- a/sysdeps/aarch64/Makefile ++++ b/sysdeps/aarch64/Makefile +@@ -2,11 +2,15 @@ long-double-fcts = yes + + ifeq (yes,$(aarch64-bti)) + # Mark linker output BTI compatible, it warns on non-BTI inputs. ++# Do not do this for conform tests because they may not be compiled ++# with the appropriate compiler flags. ++ifneq ($(subdir),conform) + sysdep-LDFLAGS += -Wl,-z,force-bti + # Make warnings fatal outside the test system. + LDFLAGS-lib.so += -Wl,--fatal-warnings + LDFLAGS-rtld += -Wl,-z,force-bti,--fatal-warnings +-endif ++endif # $(subdir) != conform ++endif # $(aarch64-bit) + + ifeq ($(subdir),elf) + sysdep-dl-routines += dl-bti + +commit cdc1665bb188319b5f16ee05c04de7f2ba580e27 +Author: Yury Khrustalev +Date: Mon Apr 28 15:12:04 2025 +0100 + + aarch64: Disable ZA state of SME in setjmp and sigsetjmp + + Due to the nature of the ZA state, setjmp() should clear it in the + same manner as it is already done by longjmp. + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit a7f6fd976c17b82dc198290b4ab7087f35855a0e) + (cherry picked from commit 1f57ffdf35334ab245544cbb88f3abf2e6c77c54) + +diff --git a/sysdeps/aarch64/setjmp.S b/sysdeps/aarch64/setjmp.S +index 43fdb1b2fb..e0c65cf035 100644 +--- a/sysdeps/aarch64/setjmp.S ++++ b/sysdeps/aarch64/setjmp.S +@@ -37,6 +37,29 @@ ENTRY (__sigsetjmp) + PTR_ARG (0) + + 1: ++ ++#if IS_IN(libc) ++ /* Disable ZA state of SME in libc.a and libc.so, but not in ld.so. */ ++# if HAVE_AARCH64_PAC_RET ++ PACIASP ++ cfi_window_save ++# endif ++ stp x29, x30, [sp, -16]! ++ cfi_adjust_cfa_offset (16) ++ cfi_rel_offset (x29, 0) ++ cfi_rel_offset (x30, 8) ++ mov x29, sp ++ bl __libc_arm_za_disable ++ ldp x29, x30, [sp], 16 ++ cfi_adjust_cfa_offset (-16) ++ cfi_restore (x29) ++ cfi_restore (x30) ++# if HAVE_AARCH64_PAC_RET ++ AUTIASP ++ cfi_window_save ++# endif ++#endif ++ + stp x19, x20, [x0, #JB_X19<<3] + stp x21, x22, [x0, #JB_X21<<3] + stp x23, x24, [x0, #JB_X23<<3] + +commit 51bcc73d95051e74512bb32fe17096b3db329cf3 +Author: Yury Khrustalev +Date: Tue Apr 29 13:44:11 2025 +0100 + + aarch64: update tests for SME + + Add test that checks that ZA state is disabled after setjmp and sigsetjmp + Update existing SME test that uses setjmp + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit 251f93262483b9c1184f5b72993d77a5d1c95f68) + (cherry picked from commit 97076e0cf14c635ef1d4ce1241e41c2c497533c8) + +diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile +index eb34f4541e..62ae97fab0 100644 +--- a/sysdeps/aarch64/Makefile ++++ b/sysdeps/aarch64/Makefile +@@ -74,7 +74,9 @@ sysdep_routines += \ + __arm_za_disable + + tests += \ +- tst-sme-jmp ++ tst-sme-jmp \ ++ tst-sme-za-state \ ++ # tests + endif + + ifeq ($(subdir),malloc) +diff --git a/sysdeps/aarch64/tst-sme-helper.h b/sysdeps/aarch64/tst-sme-helper.h +new file mode 100644 +index 0000000000..f049416c2b +--- /dev/null ++++ b/sysdeps/aarch64/tst-sme-helper.h +@@ -0,0 +1,97 @@ ++/* Utility functions for SME tests. ++ 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 ++ . */ ++ ++/* Streaming SVE vector register size. */ ++static unsigned long svl; ++ ++struct blk { ++ void *za_save_buffer; ++ uint16_t num_za_save_slices; ++ char __reserved[6]; ++}; ++ ++/* Read SVCR to get SM (bit0) and ZA (bit1) state. */ ++static unsigned long ++get_svcr (void) ++{ ++ register unsigned long x0 asm ("x0"); ++ asm volatile ( ++ ".inst 0xd53b4240 /* mrs x0, svcr */\n" ++ : "=r" (x0)); ++ return x0; ++} ++ ++/* Returns tpidr2. */ ++static void * ++get_tpidr2 (void) ++{ ++ register unsigned long x0 asm ("x0"); ++ asm volatile ( ++ ".inst 0xd53bd0a0 /* mrs x0, tpidr2_el0 */\n" ++ : "=r"(x0) :: "memory"); ++ return (void *) x0; ++} ++ ++/* Obtains current streaming SVE vector register size. */ ++static unsigned long ++get_svl (void) ++{ ++ register unsigned long x0 asm ("x0"); ++ asm volatile ( ++ ".inst 0x04bf5820 /* rdsvl x0, 1 */\n" ++ : "=r" (x0)); ++ return x0; ++} ++ ++/* PSTATE.ZA = 1, set ZA state to active. */ ++static void ++start_za (void) ++{ ++ asm volatile ( ++ ".inst 0xd503457f /* smstart za */"); ++} ++ ++/* Load data into ZA byte by byte from p. */ ++static void __attribute__ ((noinline)) ++load_za (const void *p) ++{ ++ register unsigned long x15 asm ("x15") = 0; ++ register unsigned long x16 asm ("x16") = (unsigned long)p; ++ register unsigned long x17 asm ("x17") = svl; ++ ++ asm volatile ( ++ ".inst 0xd503437f /* smstart sm */\n" ++ ".L_ldr_loop:\n" ++ ".inst 0xe1006200 /* ldr za[w15, 0], [x16] */\n" ++ "add w15, w15, 1\n" ++ ".inst 0x04305030 /* addvl x16, x16, 1 */\n" ++ "cmp w15, w17\n" ++ "bne .L_ldr_loop\n" ++ ".inst 0xd503427f /* smstop sm */\n" ++ : "+r"(x15), "+r"(x16), "+r"(x17)); ++} ++ ++/* Set tpidr2 to BLK. */ ++static void ++set_tpidr2 (struct blk *blk) ++{ ++ register unsigned long x0 asm ("x0") = (unsigned long)blk; ++ asm volatile ( ++ ".inst 0xd51bd0a0 /* msr tpidr2_el0, x0 */\n" ++ :: "r"(x0) : "memory"); ++} +diff --git a/sysdeps/aarch64/tst-sme-jmp.c b/sysdeps/aarch64/tst-sme-jmp.c +index 35769340e7..b9638c6a71 100644 +--- a/sysdeps/aarch64/tst-sme-jmp.c ++++ b/sysdeps/aarch64/tst-sme-jmp.c +@@ -27,87 +27,12 @@ + #include + #include + +-struct blk { +- void *za_save_buffer; +- uint16_t num_za_save_slices; +- char __reserved[6]; +-}; ++#include "tst-sme-helper.h" + +-static unsigned long svl; + static uint8_t *za_orig; + static uint8_t *za_dump; + static uint8_t *za_save; + +-static unsigned long +-get_svl (void) +-{ +- register unsigned long x0 asm ("x0"); +- asm volatile ( +- ".inst 0x04bf5820 /* rdsvl x0, 1 */\n" +- : "=r" (x0)); +- return x0; +-} +- +-/* PSTATE.ZA = 1, set ZA state to active. */ +-static void +-start_za (void) +-{ +- asm volatile ( +- ".inst 0xd503457f /* smstart za */"); +-} +- +-/* Read SVCR to get SM (bit0) and ZA (bit1) state. */ +-static unsigned long +-get_svcr (void) +-{ +- register unsigned long x0 asm ("x0"); +- asm volatile ( +- ".inst 0xd53b4240 /* mrs x0, svcr */\n" +- : "=r" (x0)); +- return x0; +-} +- +-/* Load data into ZA byte by byte from p. */ +-static void __attribute__ ((noinline)) +-load_za (const void *p) +-{ +- register unsigned long x15 asm ("x15") = 0; +- register unsigned long x16 asm ("x16") = (unsigned long)p; +- register unsigned long x17 asm ("x17") = svl; +- +- asm volatile ( +- ".inst 0xd503437f /* smstart sm */\n" +- ".L_ldr_loop:\n" +- ".inst 0xe1006200 /* ldr za[w15, 0], [x16] */\n" +- "add w15, w15, 1\n" +- ".inst 0x04305030 /* addvl x16, x16, 1 */\n" +- "cmp w15, w17\n" +- "bne .L_ldr_loop\n" +- ".inst 0xd503427f /* smstop sm */\n" +- : "+r"(x15), "+r"(x16), "+r"(x17)); +-} +- +-/* Set tpidr2 to BLK. */ +-static void +-set_tpidr2 (struct blk *blk) +-{ +- register unsigned long x0 asm ("x0") = (unsigned long)blk; +- asm volatile ( +- ".inst 0xd51bd0a0 /* msr tpidr2_el0, x0 */\n" +- :: "r"(x0) : "memory"); +-} +- +-/* Returns tpidr2. */ +-static void * +-get_tpidr2 (void) +-{ +- register unsigned long x0 asm ("x0"); +- asm volatile ( +- ".inst 0xd53bd0a0 /* mrs x0, tpidr2_el0 */\n" +- : "=r"(x0) :: "memory"); +- return (void *) x0; +-} +- + static void + print_data(const char *msg, void *p) + { +@@ -168,8 +93,8 @@ longjmp_test (void) + { + p = get_tpidr2 (); + printf ("before longjmp: tp2 = %p\n", p); +- if (p != &blk) +- FAIL_EXIT1 ("tpidr2 is clobbered"); ++ if (p != NULL) ++ FAIL_EXIT1 ("tpidr2 has not been reset to null"); + do_longjmp (env); + FAIL_EXIT1 ("longjmp returned"); + } +diff --git a/sysdeps/aarch64/tst-sme-za-state.c b/sysdeps/aarch64/tst-sme-za-state.c +new file mode 100644 +index 0000000000..63f6eebeb4 +--- /dev/null ++++ b/sysdeps/aarch64/tst-sme-za-state.c +@@ -0,0 +1,119 @@ ++/* Test for SME ZA state being cleared on setjmp and longjmp. ++ 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 "tst-sme-helper.h" ++ ++static uint8_t *state; ++ ++static void ++enable_sme_za_state (struct blk *ptr) ++{ ++ set_tpidr2 (ptr); ++ start_za (); ++ load_za (state); ++} ++ ++static void ++check_sme_za_state (const char msg[], bool clear) ++{ ++ unsigned long svcr = get_svcr (); ++ void *tpidr2 = get_tpidr2 (); ++ printf ("[%s]\n", msg); ++ printf ("svcr = %016lx\n", svcr); ++ printf ("tpidr2 = %016lx\n", (unsigned long)tpidr2); ++ if (clear) ++ { ++ TEST_VERIFY (svcr == 0); ++ TEST_VERIFY (tpidr2 == NULL); ++ } ++ else ++ { ++ TEST_VERIFY (svcr != 0); ++ TEST_VERIFY (tpidr2 != NULL); ++ } ++} ++ ++static void ++run (struct blk *ptr) ++{ ++ jmp_buf buf; ++ int ret; ++ ++ check_sme_za_state ("initial state", /* Clear. */ true); ++ ++ /* Enabled ZA state so that effect of disabling be observable. */ ++ enable_sme_za_state (ptr); ++ check_sme_za_state ("before setjmp", /* Clear. */ false); ++ ++ if ((ret = setjmp (buf)) == 0) ++ { ++ check_sme_za_state ("after setjmp", /* Clear. */ true); ++ ++ /* Enabled ZA state so that effect of disabling be observable. */ ++ enable_sme_za_state (ptr); ++ check_sme_za_state ("before longjmp", /* Clear. */ false); ++ ++ longjmp (buf, 42); ++ ++ /* Unreachable. */ ++ TEST_VERIFY (false); ++ __builtin_unreachable (); ++ } ++ ++ TEST_COMPARE (ret, 42); ++ check_sme_za_state ("after longjmp", /* Clear. */ true); ++} ++ ++static int ++do_test (void) ++{ ++ unsigned long hwcap2 = getauxval (AT_HWCAP2); ++ if ((hwcap2 & HWCAP2_SME) == 0) ++ return EXIT_UNSUPPORTED; ++ ++ /* Get current streaming SVE vector register size. */ ++ svl = get_svl (); ++ printf ("svl: %lu\n", svl); ++ TEST_VERIFY_EXIT (!(svl < 16 || svl % 16 != 0 || svl >= (1 << 16))); ++ ++ /* Initialise buffer for ZA state of SME. */ ++ state = xmalloc (svl * svl); ++ memset (state, 1, svl * svl); ++ struct blk blk = { ++ .za_save_buffer = state, ++ .num_za_save_slices = svl, ++ .__reserved = {0}, ++ }; ++ ++ run (&blk); ++ ++ free (state); ++ return 0; ++} ++ ++#include + +commit 7af8db46d2493521cb8e0c13907f601a61236ebf +Author: Yury Khrustalev +Date: Tue Oct 21 13:48:34 2025 +0100 + + aarch64: define macro for calling __libc_arm_za_disable + + A common sequence of instructions is used in several places + in assembly files, so define it in one place as an assembly + macro. + + Note that PAC instructions are not included in the new macro + because they are redundant given how we call the arm_za_disable + function (return address is not saved on stack, so no need to + sign it). + + (based on commits 6de12fc9ad56bc19fa6fcbd8ee502f29b5170d47 + and c0f0db2d59e0908057205b22b21dd9d626d780c1) + + Reviewed-by: Carlos O'Donell + (cherry picked from commit 1a0ee267147d002d66af29bcf3f5002d19b3c75a) + +diff --git a/sysdeps/aarch64/__longjmp.S b/sysdeps/aarch64/__longjmp.S +index 7b6add751e..84156ee6ef 100644 +--- a/sysdeps/aarch64/__longjmp.S ++++ b/sysdeps/aarch64/__longjmp.S +@@ -51,24 +51,7 @@ ENTRY (__longjmp) + + #if IS_IN(libc) + /* Disable ZA state of SME in libc.a and libc.so, but not in ld.so. */ +-# if HAVE_AARCH64_PAC_RET +- PACIASP +- cfi_window_save +-# endif +- stp x29, x30, [sp, -16]! +- cfi_adjust_cfa_offset (16) +- cfi_rel_offset (x29, 0) +- cfi_rel_offset (x30, 8) +- mov x29, sp +- bl __libc_arm_za_disable +- ldp x29, x30, [sp], 16 +- cfi_adjust_cfa_offset (-16) +- cfi_restore (x29) +- cfi_restore (x30) +-# if HAVE_AARCH64_PAC_RET +- AUTIASP +- cfi_window_save +-# endif ++ CALL_LIBC_ARM_ZA_DISABLE + #endif + + ldp x19, x20, [x0, #JB_X19<<3] +diff --git a/sysdeps/aarch64/setjmp.S b/sysdeps/aarch64/setjmp.S +index e0c65cf035..2fa0dc768d 100644 +--- a/sysdeps/aarch64/setjmp.S ++++ b/sysdeps/aarch64/setjmp.S +@@ -40,24 +40,7 @@ ENTRY (__sigsetjmp) + + #if IS_IN(libc) + /* Disable ZA state of SME in libc.a and libc.so, but not in ld.so. */ +-# if HAVE_AARCH64_PAC_RET +- PACIASP +- cfi_window_save +-# endif +- stp x29, x30, [sp, -16]! +- cfi_adjust_cfa_offset (16) +- cfi_rel_offset (x29, 0) +- cfi_rel_offset (x30, 8) +- mov x29, sp +- bl __libc_arm_za_disable +- ldp x29, x30, [sp], 16 +- cfi_adjust_cfa_offset (-16) +- cfi_restore (x29) +- cfi_restore (x30) +-# if HAVE_AARCH64_PAC_RET +- AUTIASP +- cfi_window_save +-# endif ++ CALL_LIBC_ARM_ZA_DISABLE + #endif + + stp x19, x20, [x0, #JB_X19<<3] +diff --git a/sysdeps/unix/sysv/linux/aarch64/setcontext.S b/sysdeps/unix/sysv/linux/aarch64/setcontext.S +index ba659438c5..1be9be2c27 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/setcontext.S ++++ b/sysdeps/unix/sysv/linux/aarch64/setcontext.S +@@ -49,25 +49,8 @@ ENTRY (__setcontext) + cbz x0, 1f + b C_SYMBOL_NAME (__syscall_error) + 1: +- /* Disable ZA of SME. */ +-#if HAVE_AARCH64_PAC_RET +- PACIASP +- cfi_window_save +-#endif +- stp x29, x30, [sp, -16]! +- cfi_adjust_cfa_offset (16) +- cfi_rel_offset (x29, 0) +- cfi_rel_offset (x30, 8) +- mov x29, sp +- bl __libc_arm_za_disable +- ldp x29, x30, [sp], 16 +- cfi_adjust_cfa_offset (-16) +- cfi_restore (x29) +- cfi_restore (x30) +-#if HAVE_AARCH64_PAC_RET +- AUTIASP +- cfi_window_save +-#endif ++ /* Clear ZA state of SME. */ ++ CALL_LIBC_ARM_ZA_DISABLE + /* Restore the general purpose registers. */ + mov x0, x9 + cfi_def_cfa (x0, 0) +diff --git a/sysdeps/unix/sysv/linux/aarch64/sysdep.h b/sysdeps/unix/sysv/linux/aarch64/sysdep.h +index bbbe35723c..966769a6d6 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/sysdep.h ++++ b/sysdeps/unix/sysv/linux/aarch64/sysdep.h +@@ -150,6 +150,18 @@ + mov x8, SYS_ify (syscall_name); \ + svc 0 + ++/* Clear ZA state of SME (ASM version). */ ++/* The __libc_arm_za_disable function has special calling convention ++ that allows to call it without stack manipulation and preserving ++ most of the registers. */ ++ .macro CALL_LIBC_ARM_ZA_DISABLE ++ mov x13, x30 ++ .cfi_register x30, x13 ++ bl __libc_arm_za_disable ++ mov x30, x13 ++ .cfi_register x13, x30 ++ .endm ++ + #else /* not __ASSEMBLER__ */ + + # ifdef __LP64__ + +commit 899ebf35691f01c357fc582b3e88db87accb5ee1 +Author: Yury Khrustalev +Date: Thu Sep 25 15:54:36 2025 +0100 + + aarch64: clear ZA state of SME before clone and clone3 syscalls + + This change adds a call to the __arm_za_disable() function immediately + before the SVC instruction inside clone() and clone3() wrappers. It also + adds a macro for inline clone() used in fork() and adds the same call to + the vfork implementation. This sets the ZA state of SME to "off" on return + from these functions (for both the child and the parent). + + The __arm_za_disable() function is described in [1] (8.1.3). Note that + the internal Glibc name for this function is __libc_arm_za_disable(). + + When this change was originally proposed [2,3], it generated a long + discussion where several questions and concerns were raised. Here we + will address these concerns and explain why this change is useful and, + in fact, necessary. + + In a nutshell, a C library that conforms to the AAPCS64 spec [1] (pertinent + to this change, mainly, the chapters 6.2 and 6.6), should have a call to the + __arm_za_disable() function in clone() and clone3() wrappers. The following + explains in detail why this is the case. + + When we consider using the __arm_za_disable() function inside the clone() + and clone3() libc wrappers, we talk about the C library subroutines clone() + and clone3() rather than the syscalls with similar names. In the current + version of Glibc, clone() is public and clone3() is private, but it being + private is not pertinent to this discussion. + + We will begin with stating that this change is NOT a bug fix for something + in the kernel. The requirement to call __arm_za_disable() does NOT come from + the kernel. It also is NOT needed to satisfy a contract between the kernel + and userspace. This is why it is not for the kernel documentation to describe + this requirement. This requirement is instead needed to satisfy a pure userspace + scheme outlined in [1] and to make sure that software that uses Glibc (or any + other C library that has correct handling of SME states (see below)) conforms + to [1] without having to unnecessarily become SME-aware thus losing portability. + + To recap (see [1] (6.2)), SME extension defines SME state which is part of + processor state. Part of this SME state is ZA state that is necessary to + manage ZA storage register in the context of the ZA lazy saving scheme [1] + (6.6). This scheme exists because it would be challenging to handle ZA + storage of SME in either callee-saved or caller-saved manner. + + There are 3 kinds of ZA state that are defined in terms of the PSTATE.ZA + bit and the TPIDR2_EL0 register (see [1] (6.6.3)): + + - "off":       PSTATE.ZA == 0 + - "active":    PSTATE.ZA == 1 TPIDR2_EL0 == null + - "dormant":   PSTATE.ZA == 1 TPIDR2_EL0 != null + + As [1] (6.7.2) outlines, every subroutine has exactly one SME-interface + depending on the permitted ZA-states on entry and on normal return from + a call to this subroutine. Callers of a subroutine must know and respect + the ZA-interface of the subroutines they are using. Using a subroutine + in a way that is not permitted by its ZA-interface is undefined behaviour. + + In particular, clone() and clone3() (the C library functions) have the + ZA-private interface. This means that the permitted ZA-states on entry + are "off" and "dormant" and that the permitted states on return are "off" + or "dormant" (but if and only if it was "dormant" on entry). + + This means that both functions in question should correctly handle both + "off" and "dormant" ZA-states on entry. The conforming states on return + are "off" and "dormant" (if inbound state was already "dormant"). + + This change ensures that the ZA-state on return is always "off". Note, + that, in the context of clone() and clone3(), "on return" means a point + when execution resumes at certain address after transferring from clone() + or clone3(). For the caller (we may refer to it as "parent") this is the + return address in the link register where the RET instruction jumps. For + the "child", this is the target branch address. + + So, the "off" state on return is permitted and conformant. Why can't we + retain the "dormant" state? In theory, we can, but we shouldn't, here is + why. + + Every subroutine with a private-ZA interface, including clone() and clone3(), + must comply with the lazy saving scheme [1] (6.7.2). This puts additional + responsibility on a subroutine if ZA-state on return is "dormant" because + this state has special meaning. The "caller" (that is the place in code + where execution is transferred to, so this include both "parent" and "child") + may check the ZA-state and use it as per the spec of the "dormant" state that + is outlined in [1] (6.6.6 and 6.6.7). + + Conforming to this would require more code inside of clone() and clone3() + which hardly is desirable. + + For the return to "parent" this could be achieved in theory, but given that + neither clone() nor clone3() are supposed to be used in the middle of an + SME operation, if wouldn't be useful. For the "return" to "child" this + would be particularly difficult to achieve given the complexity of these + functions and their interfaces. Most importantly, it would be illegal + and somewhat meaningless to allow a "child" to start execution in the + "dormant" ZA-state because the very essence of the "dormant" state implies + that there is a place to return and that there is some outer context that + we are allowed to interact with. + + To sum up, calling __arm_za_disable() to ensure the "off" ZA-state when the + execution resumes after a call to clone() or clone3() is correct and also + the most simple way to conform to [1]. + + Can there be situations when we can avoid calling __arm_za_disable()? + + Calling __arm_za_disable() implies certain (sufficiently small) overhead, + so one might rightly ponder avoiding making a call to this function when + we can afford not to. The most trivial cases like this (e.g. when the + calling thread doesn't have access to SME or to the TPIDR2_EL0 register) + are already handled by this function (see [1] (8.1.3 and 8.1.2)). Reasoning + about other possible use cases would require making code inside clone() and + clone3() more complicated and it would defeat the point of trying to make + an optimisation of not calling __arm_za_disable(). + + Why can't the kernel do this instead? + + The handling of SME state by the kernel is described in [4]. In short, + kernel must not impose a specific ZA-interface onto a userspace function. + Interaction with the kernel happens (among other thing) via system calls. + In Glibc many of the system calls (notably, including SYS_clone and + SYS_clone3) are used via wrappers, and the kernel has no control of them + and, moreover, it cannot dictate how these wrappers should behave because + it is simply outside of the kernel's remit. + + However, in certain cases, the kernel may ensure that a "child" doesn't + start in an incorrect state. This is what is done by the recent change + included in 6.16 kernel [5]. This is not enough to ensure that code that + uses clone() and clone3() function conforms to [1] when it runs on a + system that provides SME, hence this change. + + [1]: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst + [2]: https://inbox.sourceware.org/libc-alpha/20250522114828.2291047-1-yury.khrustalev@arm.com + [3]: https://inbox.sourceware.org/libc-alpha/20250609121407.3316070-1-yury.khrustalev@arm.com + [4]: https://www.kernel.org/doc/html/v6.16/arch/arm64/sme.html + [5]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=cde5c32db55740659fca6d56c09b88800d88fd29 + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 27effb3d50424fb9634be77a2acd614b0386ff25) + (cherry picked from commit 256030b9842a10b1f22851b1de0c119761417544) + (cherry picked from commit 889ae4bdbb4a6fbf37c2303da8cdae3d18880d9e) + +diff --git a/sysdeps/unix/sysv/linux/aarch64/clone.S b/sysdeps/unix/sysv/linux/aarch64/clone.S +index 0e7ee24e68..4fb9f02792 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/clone.S ++++ b/sysdeps/unix/sysv/linux/aarch64/clone.S +@@ -51,6 +51,9 @@ ENTRY(__clone) + and x1, x1, -16 + cbz x1, .Lsyscall_error + ++ /* Clear ZA state of SME. */ ++ CALL_LIBC_ARM_ZA_DISABLE ++ + /* Do the system call. */ + /* X0:flags, x1:newsp, x2:parenttidptr, x3:newtls, x4:childtid. */ + mov x0, x2 /* flags */ +diff --git a/sysdeps/unix/sysv/linux/aarch64/clone3.S b/sysdeps/unix/sysv/linux/aarch64/clone3.S +index e28aaa5083..17a40af916 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/clone3.S ++++ b/sysdeps/unix/sysv/linux/aarch64/clone3.S +@@ -50,6 +50,9 @@ ENTRY(__clone3) + cbz x10, .Lsyscall_error /* No NULL cl_args pointer. */ + cbz x2, .Lsyscall_error /* No NULL function pointer. */ + ++ /* Clear ZA state of SME. */ ++ CALL_LIBC_ARM_ZA_DISABLE ++ + /* Do the system call, the kernel expects: + x8: system call number + x0: cl_args +diff --git a/sysdeps/unix/sysv/linux/aarch64/sysdep.h b/sysdeps/unix/sysv/linux/aarch64/sysdep.h +index 966769a6d6..d1cf2de078 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/sysdep.h ++++ b/sysdeps/unix/sysv/linux/aarch64/sysdep.h +@@ -246,6 +246,31 @@ + #undef HAVE_INTERNAL_BRK_ADDR_SYMBOL + #define HAVE_INTERNAL_BRK_ADDR_SYMBOL 1 + ++/* Clear ZA state of SME (C version). */ ++/* The __libc_arm_za_disable function has special calling convention ++ that allows to call it without stack manipulation and preserving ++ most of the registers. */ ++#define CALL_LIBC_ARM_ZA_DISABLE() \ ++({ \ ++ unsigned long int __tmp; \ ++ asm volatile ( \ ++ " mov %0, x30\n" \ ++ " .cfi_register x30, %0\n" \ ++ " bl __libc_arm_za_disable\n" \ ++ " mov x30, %0\n" \ ++ " .cfi_register %0, x30\n" \ ++ : "=r" (__tmp) \ ++ : \ ++ : "x14", "x15", "x16", "x17", "x18", "memory" ); \ ++}) ++ ++/* Do clear ZA state of SME before making normal clone syscall. */ ++#define INLINE_CLONE_SYSCALL(a0, a1, a2, a3, a4) \ ++({ \ ++ CALL_LIBC_ARM_ZA_DISABLE (); \ ++ INLINE_SYSCALL_CALL (clone, a0, a1, a2, a3, a4); \ ++}) ++ + #endif /* __ASSEMBLER__ */ + + #endif /* linux/aarch64/sysdep.h */ +diff --git a/sysdeps/unix/sysv/linux/aarch64/vfork.S b/sysdeps/unix/sysv/linux/aarch64/vfork.S +index e71e492da3..65fa85ae89 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/vfork.S ++++ b/sysdeps/unix/sysv/linux/aarch64/vfork.S +@@ -27,6 +27,9 @@ + + ENTRY (__vfork) + ++ /* Clear ZA state of SME. */ ++ CALL_LIBC_ARM_ZA_DISABLE ++ + mov x0, #0x4111 /* CLONE_VM | CLONE_VFORK | SIGCHLD */ + mov x1, sp + DO_CALL (clone, 2) + +commit df3bcda9ac85eb720d27bbbc469cdc898cc9b02b +Author: Yury Khrustalev +Date: Fri Sep 26 10:03:45 2025 +0100 + + aarch64: tests for SME + + This commit adds tests for the following use cases relevant to handing of + the SME state: + + - fork() and vfork() + - clone() and clone3() + - signal handler + + While most cases are trivial, the case of clone3() is more complicated since + the clone3() symbol is not public in Glibc. + + To avoid having to check all possible ways clone3() may be called via other + public functions (e.g. vfork() or pthread_create()), we put together a test + that links directly with clone3.o. All the existing functions that have calls + to clone3() may not actually use it, in which case the outcome of such tests + would be unexpected. Having a direct call to the clone3() symbol in the test + allows to check precisely what we need to test: that the __arm_za_disable() + function is indeed called and has the desired effect. + + Linking to clone3.o also requires linking to __arm_za_disable.o that in + turn requires the _dl_hwcap2 hidden symbol which to provide in the test + and initialise it before using. + + Co-authored-by: Adhemerval Zanella Netto + Reviewed-by: Adhemerval Zanella + (cherry picked from commit ecb0fc2f0f839f36cd2a106283142c9df8ea8214) + (cherry picked from commit 71874f167aa5bb1538ff7e394beaacee28ebe65f) + (cherry picked from commit e4ffcf32b9213352917dcf7dc43adcaa0ff76503) + +diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile +index 62ae97fab0..f205884fe1 100644 +--- a/sysdeps/aarch64/Makefile ++++ b/sysdeps/aarch64/Makefile +@@ -75,8 +75,18 @@ sysdep_routines += \ + + tests += \ + tst-sme-jmp \ ++ tst-sme-signal \ + tst-sme-za-state \ + # tests ++tests-internal += \ ++ tst-sme-clone \ ++ tst-sme-clone3 \ ++ tst-sme-fork \ ++ tst-sme-vfork \ ++ # tests-internal ++ ++$(objpfx)tst-sme-clone3: $(objpfx)clone3.o $(objpfx)__arm_za_disable.o ++ + endif + + ifeq ($(subdir),malloc) +diff --git a/sysdeps/aarch64/tst-sme-clone.c b/sysdeps/aarch64/tst-sme-clone.c +new file mode 100644 +index 0000000000..7106ec7926 +--- /dev/null ++++ b/sysdeps/aarch64/tst-sme-clone.c +@@ -0,0 +1,53 @@ ++/* Test that ZA state of SME is cleared in both parent and child ++ when clone() syscall is used. ++ 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 "tst-sme-skeleton.c" ++ ++#include ++ ++static int ++fun (void * const arg) ++{ ++ printf ("in child: %s\n", (const char *)arg); ++ /* Check that ZA state of SME was disabled in child. */ ++ check_sme_za_state ("after clone in child", /* Clear. */ true); ++ return 0; ++} ++ ++static char __attribute__((aligned(16))) ++stack[1024 * 1024]; ++ ++static void ++run (struct blk *ptr) ++{ ++ char *syscall_name = (char *)"clone"; ++ printf ("in parent: before %s\n", syscall_name); ++ ++ /* Enabled ZA state so that effect of disabling be observable. */ ++ enable_sme_za_state (ptr); ++ check_sme_za_state ("before clone", /* Clear. */ false); ++ ++ pid_t pid = xclone (fun, syscall_name, stack, sizeof (stack), ++ CLONE_NEWUSER | CLONE_NEWNS | SIGCHLD); ++ ++ /* Check that ZA state of SME was disabled in parent. */ ++ check_sme_za_state ("after clone in parent", /* Clear. */ true); ++ ++ TEST_VERIFY (xwaitpid (pid, NULL, 0) == pid); ++} +diff --git a/sysdeps/aarch64/tst-sme-clone3.c b/sysdeps/aarch64/tst-sme-clone3.c +new file mode 100644 +index 0000000000..402b040cfd +--- /dev/null ++++ b/sysdeps/aarch64/tst-sme-clone3.c +@@ -0,0 +1,84 @@ ++/* Test that ZA state of SME is cleared in both parent and child ++ when clone3() syscall is used. ++ 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 "tst-sme-skeleton.c" ++ ++#include ++ ++#include ++#include ++#include ++ ++/* Since clone3 is not a public symbol, we link this test explicitly ++ with clone3.o and have to provide this declaration. */ ++int __clone3 (struct clone_args *cl_args, size_t size, ++ int (*func)(void *arg), void *arg); ++ ++static int ++fun (void * const arg) ++{ ++ printf ("in child: %s\n", (const char *)arg); ++ /* Check that ZA state of SME was disabled in child. */ ++ check_sme_za_state ("after clone3 in child", /* Clear. */ true); ++ return 0; ++} ++ ++static char __attribute__((aligned(16))) ++stack[1024 * 1024]; ++ ++/* Required by __arm_za_disable.o and provided by the startup code ++ as a hidden symbol. */ ++uint64_t _dl_hwcap2; ++ ++static void ++run (struct blk *ptr) ++{ ++ _dl_hwcap2 = getauxval (AT_HWCAP2); ++ ++ char *syscall_name = (char *)"clone3"; ++ struct clone_args args = { ++ .flags = CLONE_VM | CLONE_VFORK, ++ .exit_signal = SIGCHLD, ++ .stack = (uintptr_t) stack, ++ .stack_size = sizeof (stack), ++ }; ++ printf ("in parent: before %s\n", syscall_name); ++ ++ /* Enabled ZA state so that effect of disabling be observable. */ ++ enable_sme_za_state (ptr); ++ check_sme_za_state ("before clone3", /* Clear. */ false); ++ ++ pid_t pid = __clone3 (&args, sizeof (args), fun, syscall_name); ++ ++ /* Check that ZA state of SME was disabled in parent. */ ++ check_sme_za_state ("after clone3 in parent", /* Clear. */ true); ++ ++ printf ("%s child pid: %d\n", syscall_name, pid); ++ ++ xwaitpid (pid, NULL, 0); ++ printf ("in parent: after %s\n", syscall_name); ++} ++ ++/* Workaround to simplify linking with clone3.o. */ ++void __syscall_error(int code) ++{ ++ int err = -code; ++ fprintf (stderr, "syscall error %d (%s)\n", err, strerror (err)); ++ exit (err); ++} +diff --git a/sysdeps/aarch64/tst-sme-fork.c b/sysdeps/aarch64/tst-sme-fork.c +new file mode 100644 +index 0000000000..b003b08884 +--- /dev/null ++++ b/sysdeps/aarch64/tst-sme-fork.c +@@ -0,0 +1,43 @@ ++/* Test that ZA state of SME is cleared in both parent and child ++ when fork() function is used. ++ 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 "tst-sme-skeleton.c" ++ ++static void ++run (struct blk *blk) ++{ ++ /* Enabled ZA state so that effect of disabling be observable. */ ++ enable_sme_za_state (blk); ++ check_sme_za_state ("before fork", /* Clear. */ false); ++ fflush (stdout); ++ ++ pid_t pid = xfork (); ++ ++ if (pid == 0) ++ { ++ /* Check that ZA state of SME was disabled in child. */ ++ check_sme_za_state ("after fork in child", /* Clear. */ true); ++ exit (0); ++ } ++ ++ /* Check that ZA state of SME was disabled in parent. */ ++ check_sme_za_state ("after fork in parent", /* Clear. */ true); ++ ++ TEST_VERIFY (xwaitpid (pid, NULL, 0) == pid); ++} +diff --git a/sysdeps/aarch64/tst-sme-helper.h b/sysdeps/aarch64/tst-sme-helper.h +index f049416c2b..ab9c503e45 100644 +--- a/sysdeps/aarch64/tst-sme-helper.h ++++ b/sysdeps/aarch64/tst-sme-helper.h +@@ -16,9 +16,6 @@ + License along with the GNU C Library; if not, see + . */ + +-/* Streaming SVE vector register size. */ +-static unsigned long svl; +- + struct blk { + void *za_save_buffer; + uint16_t num_za_save_slices; +@@ -68,10 +65,10 @@ start_za (void) + + /* Load data into ZA byte by byte from p. */ + static void __attribute__ ((noinline)) +-load_za (const void *p) ++load_za (const void *buf, unsigned long svl) + { + register unsigned long x15 asm ("x15") = 0; +- register unsigned long x16 asm ("x16") = (unsigned long)p; ++ register unsigned long x16 asm ("x16") = (unsigned long)buf; + register unsigned long x17 asm ("x17") = svl; + + asm volatile ( +diff --git a/sysdeps/aarch64/tst-sme-jmp.c b/sysdeps/aarch64/tst-sme-jmp.c +index b9638c6a71..0c1d1a4ff8 100644 +--- a/sysdeps/aarch64/tst-sme-jmp.c ++++ b/sysdeps/aarch64/tst-sme-jmp.c +@@ -29,6 +29,9 @@ + + #include "tst-sme-helper.h" + ++/* Streaming SVE vector register size. */ ++static unsigned long svl; ++ + static uint8_t *za_orig; + static uint8_t *za_dump; + static uint8_t *za_save; +@@ -82,7 +85,7 @@ longjmp_test (void) + FAIL_EXIT1 ("svcr != 0: %lu", svcr); + set_tpidr2 (&blk); + start_za (); +- load_za (za_orig); ++ load_za (za_orig, svl); + + print_data ("za save space", za_save); + p = get_tpidr2 (); +@@ -131,7 +134,7 @@ setcontext_test (void) + FAIL_EXIT1 ("svcr != 0: %lu", svcr); + set_tpidr2 (&blk); + start_za (); +- load_za (za_orig); ++ load_za (za_orig, svl); + + print_data ("za save space", za_save); + p = get_tpidr2 (); +diff --git a/sysdeps/aarch64/tst-sme-signal.c b/sysdeps/aarch64/tst-sme-signal.c +new file mode 100644 +index 0000000000..b4b07bcc44 +--- /dev/null ++++ b/sysdeps/aarch64/tst-sme-signal.c +@@ -0,0 +1,115 @@ ++/* Test handling of SME state in a signal handler. ++ 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 "tst-sme-skeleton.c" ++ ++#include ++ ++static struct _aarch64_ctx * ++extension (void *p) ++{ ++ return p; ++} ++ ++#ifndef TPIDR2_MAGIC ++#define TPIDR2_MAGIC 0x54504902 ++#endif ++ ++#ifndef ZA_MAGIC ++#define ZA_MAGIC 0x54366345 ++#endif ++ ++#ifndef ZT_MAGIC ++#define ZT_MAGIC 0x5a544e01 ++#endif ++ ++#ifndef EXTRA_MAGIC ++#define EXTRA_MAGIC 0x45585401 ++#endif ++ ++/* We use a pipe to make sure that the final check of the SME state ++ happens after signal handler finished. */ ++static int pipefd[2]; ++ ++#define WRITE(msg) xwrite (1, msg, sizeof (msg)); ++ ++static void ++handler (int signo, siginfo_t *si, void *ctx) ++{ ++ TEST_VERIFY (signo == SIGUSR1); ++ WRITE ("in the handler\n"); ++ check_sme_za_state ("during signal", true /* State is clear. */); ++ ucontext_t *uc = ctx; ++ void *p = uc->uc_mcontext.__reserved; ++ unsigned int found = 0; ++ uint32_t m; ++ while ((m = extension (p)->magic)) ++ { ++ if (m == TPIDR2_MAGIC) ++ { ++ WRITE ("found TPIDR2_MAGIC\n"); ++ found += 1; ++ } ++ if (m == ZA_MAGIC) ++ { ++ WRITE ("found ZA_MAGIC\n"); ++ found += 1; ++ } ++ if (m == ZT_MAGIC) ++ { ++ WRITE ("found ZT_MAGIC\n"); ++ found += 1; ++ } ++ if (m == EXTRA_MAGIC) ++ { ++ WRITE ("found EXTRA_MAGIC\n"); ++ struct { struct _aarch64_ctx h; uint64_t data; } *e = p; ++ p = (char *)e->data; ++ continue; ++ } ++ p = (char *)p + extension (p)->size; ++ } ++ TEST_COMPARE (found, 3); ++ ++ /* Signal that the wait is over (see below). */ ++ char message = '\0'; ++ xwrite (pipefd[1], &message, 1); ++} ++ ++static void ++run (struct blk *blk) ++{ ++ xpipe (pipefd); ++ ++ struct sigaction sigact; ++ sigemptyset (&sigact.sa_mask); ++ sigact.sa_flags = 0; ++ sigact.sa_flags |= SA_SIGINFO; ++ sigact.sa_sigaction = handler; ++ xsigaction (SIGUSR1, &sigact, NULL); ++ ++ enable_sme_za_state (blk); ++ check_sme_za_state ("before signal", false /* State is not clear. */); ++ xraise (SIGUSR1); ++ ++ /* Wait for signal handler to complete. */ ++ char response; ++ xread (pipefd[0], &response, 1); ++ ++ check_sme_za_state ("after signal", false /* State is not clear. */); ++} +diff --git a/sysdeps/aarch64/tst-sme-skeleton.c b/sysdeps/aarch64/tst-sme-skeleton.c +new file mode 100644 +index 0000000000..ba84dda1cb +--- /dev/null ++++ b/sysdeps/aarch64/tst-sme-skeleton.c +@@ -0,0 +1,101 @@ ++/* Template for SME tests. ++ 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 "tst-sme-helper.h" ++ ++/* Streaming SVE vector register size. */ ++static unsigned long svl; ++ ++static uint8_t *state; ++ ++static void ++enable_sme_za_state (struct blk *blk) ++{ ++ start_za (); ++ set_tpidr2 (blk); ++ load_za (blk, svl); ++} ++ ++/* Check if SME state is disabled (when CLEAR is true) or ++ enabled (when CLEAR is false). */ ++static void ++check_sme_za_state (const char msg[], bool clear) ++{ ++ unsigned long svcr = get_svcr (); ++ void *tpidr2 = get_tpidr2 (); ++ printf ("[%s]\n", msg); ++ printf ("svcr = %016lx\n", svcr); ++ printf ("tpidr2 = %016lx\n", (unsigned long)tpidr2); ++ if (clear) ++ { ++ TEST_VERIFY (svcr == 0); ++ TEST_VERIFY (tpidr2 == NULL); ++ } ++ else ++ { ++ TEST_VERIFY (svcr != 0); ++ TEST_VERIFY (tpidr2 != NULL); ++ } ++} ++ ++/* Should be defined in actual test that includes this ++ skeleton file. */ ++static void ++run (struct blk *ptr); ++ ++static int ++do_test (void) ++{ ++ unsigned long hwcap2 = getauxval (AT_HWCAP2); ++ if ((hwcap2 & HWCAP2_SME) == 0) ++ return EXIT_UNSUPPORTED; ++ ++ /* Get current streaming SVE vector length in bytes. */ ++ svl = get_svl (); ++ printf ("svl: %lu\n", svl); ++ ++ TEST_VERIFY_EXIT (!(svl < 16 || svl % 16 != 0 || svl >= (1 << 16))); ++ ++ /* Initialise buffer for ZA state of SME. */ ++ state = xmalloc (svl * svl); ++ memset (state, 1, svl * svl); ++ struct blk blk = { ++ .za_save_buffer = state, ++ .num_za_save_slices = svl, ++ .__reserved = {0}, ++ }; ++ ++ run (&blk); ++ ++ free (state); ++ return 0; ++} ++ ++#include +diff --git a/sysdeps/aarch64/tst-sme-vfork.c b/sysdeps/aarch64/tst-sme-vfork.c +new file mode 100644 +index 0000000000..3feea065e5 +--- /dev/null ++++ b/sysdeps/aarch64/tst-sme-vfork.c +@@ -0,0 +1,43 @@ ++/* Test that ZA state of SME is cleared in both parent and child ++ when vfork() function is used. ++ 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 "tst-sme-skeleton.c" ++ ++static void ++run (struct blk *blk) ++{ ++ /* Enabled ZA state so that effect of disabling be observable. */ ++ enable_sme_za_state (blk); ++ check_sme_za_state ("before vfork", /* Clear. */ false); ++ fflush (stdout); ++ ++ pid_t pid = vfork (); ++ ++ if (pid == 0) ++ { ++ /* Check that ZA state of SME was disabled in child. */ ++ check_sme_za_state ("after vfork in child", /* Clear. */ true); ++ _exit (0); ++ } ++ ++ /* Check that ZA state of SME was disabled in parent. */ ++ check_sme_za_state ("after vfork in parent", /* Clear. */ true); ++ ++ TEST_VERIFY (xwaitpid (pid, NULL, 0) == pid); ++} +diff --git a/sysdeps/aarch64/tst-sme-za-state.c b/sysdeps/aarch64/tst-sme-za-state.c +index 63f6eebeb4..00118ef506 100644 +--- a/sysdeps/aarch64/tst-sme-za-state.c ++++ b/sysdeps/aarch64/tst-sme-za-state.c +@@ -16,47 +16,9 @@ + License along with the GNU C Library; if not, see + . */ + +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#include "tst-sme-helper.h" +- +-static uint8_t *state; +- +-static void +-enable_sme_za_state (struct blk *ptr) +-{ +- set_tpidr2 (ptr); +- start_za (); +- load_za (state); +-} ++#include "tst-sme-skeleton.c" + +-static void +-check_sme_za_state (const char msg[], bool clear) +-{ +- unsigned long svcr = get_svcr (); +- void *tpidr2 = get_tpidr2 (); +- printf ("[%s]\n", msg); +- printf ("svcr = %016lx\n", svcr); +- printf ("tpidr2 = %016lx\n", (unsigned long)tpidr2); +- if (clear) +- { +- TEST_VERIFY (svcr == 0); +- TEST_VERIFY (tpidr2 == NULL); +- } +- else +- { +- TEST_VERIFY (svcr != 0); +- TEST_VERIFY (tpidr2 != NULL); +- } +-} ++#include + + static void + run (struct blk *ptr) +@@ -88,32 +50,3 @@ run (struct blk *ptr) + TEST_COMPARE (ret, 42); + check_sme_za_state ("after longjmp", /* Clear. */ true); + } +- +-static int +-do_test (void) +-{ +- unsigned long hwcap2 = getauxval (AT_HWCAP2); +- if ((hwcap2 & HWCAP2_SME) == 0) +- return EXIT_UNSUPPORTED; +- +- /* Get current streaming SVE vector register size. */ +- svl = get_svl (); +- printf ("svl: %lu\n", svl); +- TEST_VERIFY_EXIT (!(svl < 16 || svl % 16 != 0 || svl >= (1 << 16))); +- +- /* Initialise buffer for ZA state of SME. */ +- state = xmalloc (svl * svl); +- memset (state, 1, svl * svl); +- struct blk blk = { +- .za_save_buffer = state, +- .num_za_save_slices = svl, +- .__reserved = {0}, +- }; +- +- run (&blk); +- +- free (state); +- return 0; +-} +- +-#include + +commit 1c0ad5ea63b2e5d39eb55f734b0e2bea7f766523 +Author: Yury Khrustalev +Date: Tue Oct 28 11:01:50 2025 +0000 + + aarch64: fix cfi directives around __libc_arm_za_disable + + Incorrect CFI directive corrupted call stack information + and prevented debuggers from correctly displaying call + stack information. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 2f77aec043f61e8533487850b11941a640ae2dea) + (cherry picked from commit de1fe81f471496366580ad728b8986a3424b2fd7) + (cherry picked from commit 5bf8ee7ad559fd60bedd3f5ec831d0b12b5000b8) + +diff --git a/sysdeps/unix/sysv/linux/aarch64/sysdep.h b/sysdeps/unix/sysv/linux/aarch64/sysdep.h +index d1cf2de078..dd98242dad 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/sysdep.h ++++ b/sysdeps/unix/sysv/linux/aarch64/sysdep.h +@@ -155,11 +155,12 @@ + that allows to call it without stack manipulation and preserving + most of the registers. */ + .macro CALL_LIBC_ARM_ZA_DISABLE ++ cfi_remember_state + mov x13, x30 +- .cfi_register x30, x13 ++ cfi_register(x30, x13) + bl __libc_arm_za_disable + mov x30, x13 +- .cfi_register x13, x30 ++ cfi_restore_state + .endm + + #else /* not __ASSEMBLER__ */ +@@ -254,11 +255,12 @@ + ({ \ + unsigned long int __tmp; \ + asm volatile ( \ ++ " .cfi_remember_state\n" \ + " mov %0, x30\n" \ +- " .cfi_register x30, %0\n" \ ++ " .cfi_register x30, %0\n" \ + " bl __libc_arm_za_disable\n" \ + " mov x30, %0\n" \ +- " .cfi_register %0, x30\n" \ ++ " .cfi_restore_state\n" \ + : "=r" (__tmp) \ + : \ + : "x14", "x15", "x16", "x17", "x18", "memory" ); \ + +commit a66680adf3b2266c177f94f3f63e4b182e6362fe +Author: Yury Khrustalev +Date: Tue Nov 11 11:40:25 2025 +0000 + + aarch64: fix includes in SME tests + + Use the correct include for the SIGCHLD macro: signal.h + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit a9c426bcca59a9e228c4fbe75e75154217ec4ada) + (cherry picked from commit 17c3eab387c3ceb6972e57888a89b1480793f81a) + (cherry picked from commit 215e9155ea06064342151d05446ae51da16e0f65) + +diff --git a/sysdeps/aarch64/tst-sme-clone.c b/sysdeps/aarch64/tst-sme-clone.c +index 7106ec7926..b6ad54fa37 100644 +--- a/sysdeps/aarch64/tst-sme-clone.c ++++ b/sysdeps/aarch64/tst-sme-clone.c +@@ -19,6 +19,7 @@ + + #include "tst-sme-skeleton.c" + ++#include + #include + + static int +diff --git a/sysdeps/aarch64/tst-sme-clone3.c b/sysdeps/aarch64/tst-sme-clone3.c +index 402b040cfd..f420d5984d 100644 +--- a/sysdeps/aarch64/tst-sme-clone3.c ++++ b/sysdeps/aarch64/tst-sme-clone3.c +@@ -22,7 +22,7 @@ + #include + + #include +-#include ++#include + #include + + /* Since clone3 is not a public symbol, we link this test explicitly + +commit 303363922cf027713ed887241cd9b59cf705fe11 +Author: Joe Ramsay +Date: Thu Nov 6 15:36:03 2025 +0000 + + AArch64: Optimise SVE scalar callbacks + + Instead of using SVE instructions to marshall special results into the + correct lane, just write the entire vector (and the predicate) to + memory, then use cheaper scalar operations. + + Geomean speedup of 16% in special intervals on Neoverse with GCC 14. + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit 5b82fb18827e962af9f080fdf3c1a69802783f67) + +diff --git a/sysdeps/aarch64/fpu/sv_math.h b/sysdeps/aarch64/fpu/sv_math.h +index 41a2013929..c2dae543a1 100644 +--- a/sysdeps/aarch64/fpu/sv_math.h ++++ b/sysdeps/aarch64/fpu/sv_math.h +@@ -24,11 +24,29 @@ + + #include "vecmath_config.h" + ++#if !defined(__ARM_FEATURE_SVE_BITS) || __ARM_FEATURE_SVE_BITS == 0 ++/* If not specified by -msve-vector-bits, assume maximum vector length. */ ++# define SVE_VECTOR_BYTES 256 ++#else ++# define SVE_VECTOR_BYTES (__ARM_FEATURE_SVE_BITS / 8) ++#endif ++#define SVE_NUM_FLTS (SVE_VECTOR_BYTES / sizeof (float)) ++#define SVE_NUM_DBLS (SVE_VECTOR_BYTES / sizeof (double)) ++/* Predicate is stored as one bit per byte of VL so requires VL / 64 bytes. */ ++#define SVE_NUM_PG_BYTES (SVE_VECTOR_BYTES / sizeof (uint64_t)) ++ + #define SV_NAME_F1(fun) _ZGVsMxv_##fun##f + #define SV_NAME_D1(fun) _ZGVsMxv_##fun + #define SV_NAME_F2(fun) _ZGVsMxvv_##fun##f + #define SV_NAME_D2(fun) _ZGVsMxvv_##fun + ++static inline void ++svstr_p (uint8_t *dst, svbool_t p) ++{ ++ /* Predicate STR does not currently have an intrinsic. */ ++ __asm__("str %0, [%x1]\n" : : "Upa"(p), "r"(dst) : "memory"); ++} ++ + /* Double precision. */ + static inline svint64_t + sv_s64 (int64_t x) +@@ -51,33 +69,35 @@ sv_f64 (double x) + static inline svfloat64_t + sv_call_f64 (double (*f) (double), svfloat64_t x, svfloat64_t y, svbool_t cmp) + { +- svbool_t p = svpfirst (cmp, svpfalse ()); +- while (svptest_any (cmp, p)) ++ double tmp[SVE_NUM_DBLS]; ++ uint8_t pg_bits[SVE_NUM_PG_BYTES]; ++ svstr_p (pg_bits, cmp); ++ svst1 (svptrue_b64 (), tmp, svsel (cmp, x, y)); ++ ++ for (int i = 0; i < svcntd (); i++) + { +- double elem = svclastb_n_f64 (p, 0, x); +- elem = (*f) (elem); +- svfloat64_t y2 = svdup_n_f64 (elem); +- y = svsel_f64 (p, y2, y); +- p = svpnext_b64 (cmp, p); ++ if (pg_bits[i] & 1) ++ tmp[i] = f (tmp[i]); + } +- return y; ++ return svld1 (svptrue_b64 (), tmp); + } + + static inline svfloat64_t + sv_call2_f64 (double (*f) (double, double), svfloat64_t x1, svfloat64_t x2, + svfloat64_t y, svbool_t cmp) + { +- svbool_t p = svpfirst (cmp, svpfalse ()); +- while (svptest_any (cmp, p)) ++ double tmp1[SVE_NUM_DBLS], tmp2[SVE_NUM_DBLS]; ++ uint8_t pg_bits[SVE_NUM_PG_BYTES]; ++ svstr_p (pg_bits, cmp); ++ svst1 (svptrue_b64 (), tmp1, svsel (cmp, x1, y)); ++ svst1 (cmp, tmp2, x2); ++ ++ for (int i = 0; i < svcntd (); i++) + { +- double elem1 = svclastb_n_f64 (p, 0, x1); +- double elem2 = svclastb_n_f64 (p, 0, x2); +- double ret = (*f) (elem1, elem2); +- svfloat64_t y2 = svdup_n_f64 (ret); +- y = svsel_f64 (p, y2, y); +- p = svpnext_b64 (cmp, p); ++ if (pg_bits[i] & 1) ++ tmp1[i] = f (tmp1[i], tmp2[i]); + } +- return y; ++ return svld1 (svptrue_b64 (), tmp1); + } + + static inline svuint64_t +@@ -109,33 +129,40 @@ sv_f32 (float x) + static inline svfloat32_t + sv_call_f32 (float (*f) (float), svfloat32_t x, svfloat32_t y, svbool_t cmp) + { +- svbool_t p = svpfirst (cmp, svpfalse ()); +- while (svptest_any (cmp, p)) ++ float tmp[SVE_NUM_FLTS]; ++ uint8_t pg_bits[SVE_NUM_PG_BYTES]; ++ svstr_p (pg_bits, cmp); ++ svst1 (svptrue_b32 (), tmp, svsel (cmp, x, y)); ++ ++ for (int i = 0; i < svcntd (); i++) + { +- float elem = svclastb_n_f32 (p, 0, x); +- elem = f (elem); +- svfloat32_t y2 = svdup_n_f32 (elem); +- y = svsel_f32 (p, y2, y); +- p = svpnext_b32 (cmp, p); ++ uint8_t p = pg_bits[i]; ++ if (p & 1) ++ tmp[i * 2] = f (tmp[i * 2]); ++ if (p & (1 << 4)) ++ tmp[i * 2 + 1] = f (tmp[i * 2 + 1]); + } +- return y; ++ return svld1 (svptrue_b32 (), tmp); + } + + static inline svfloat32_t + sv_call2_f32 (float (*f) (float, float), svfloat32_t x1, svfloat32_t x2, + svfloat32_t y, svbool_t cmp) + { +- svbool_t p = svpfirst (cmp, svpfalse ()); +- while (svptest_any (cmp, p)) ++ float tmp1[SVE_NUM_FLTS], tmp2[SVE_NUM_FLTS]; ++ uint8_t pg_bits[SVE_NUM_PG_BYTES]; ++ svstr_p (pg_bits, cmp); ++ svst1 (svptrue_b32 (), tmp1, svsel (cmp, x1, y)); ++ svst1 (cmp, tmp2, x2); ++ ++ for (int i = 0; i < svcntd (); i++) + { +- float elem1 = svclastb_n_f32 (p, 0, x1); +- float elem2 = svclastb_n_f32 (p, 0, x2); +- float ret = f (elem1, elem2); +- svfloat32_t y2 = svdup_n_f32 (ret); +- y = svsel_f32 (p, y2, y); +- p = svpnext_b32 (cmp, p); ++ uint8_t p = pg_bits[i]; ++ if (p & 1) ++ tmp1[i * 2] = f (tmp1[i * 2], tmp2[i * 2]); ++ if (p & (1 << 4)) ++ tmp1[i * 2 + 1] = f (tmp1[i * 2 + 1], tmp2[i * 2 + 1]); + } +- return y; ++ return svld1 (svptrue_b32 (), tmp1); + } +- + #endif + +commit b06fc96c20c30c28205725f3f60df4f5a7458bd3 +Author: Joe Ramsay +Date: Thu Nov 6 18:26:54 2025 +0000 + + AArch64: Fix instability in AdvSIMD tan + + Previously presence of special-cases in one lane could affect the + results in other lanes due to unconditional scalar fallback. The old + WANT_SIMD_EXCEPT option (which has never been enabled in libmvec) has + been removed from AOR, making it easier to spot and fix this. 4% + improvement in throughput with GCC 14 on Neoverse V1. This bug is + present as far back as 2.39 (where tan was first introduced). + + Reviewed-by: Wilco Dijkstra + (cherry picked from commit 6c22823da57aa5218f717f569c04c9573c0448c5) + +diff --git a/sysdeps/aarch64/fpu/tan_advsimd.c b/sysdeps/aarch64/fpu/tan_advsimd.c +index d56a102dd1..c6a5a17126 100644 +--- a/sysdeps/aarch64/fpu/tan_advsimd.c ++++ b/sysdeps/aarch64/fpu/tan_advsimd.c +@@ -25,9 +25,7 @@ static const struct data + float64x2_t poly[9]; + double half_pi[2]; + float64x2_t two_over_pi, shift; +-#if !WANT_SIMD_EXCEPT + float64x2_t range_val; +-#endif + } data = { + /* Coefficients generated using FPMinimax. */ + .poly = { V2 (0x1.5555555555556p-2), V2 (0x1.1111111110a63p-3), +@@ -38,20 +36,17 @@ static const struct data + .half_pi = { 0x1.921fb54442d18p0, 0x1.1a62633145c07p-54 }, + .two_over_pi = V2 (0x1.45f306dc9c883p-1), + .shift = V2 (0x1.8p52), +-#if !WANT_SIMD_EXCEPT + .range_val = V2 (0x1p23), +-#endif + }; + + #define RangeVal 0x4160000000000000 /* asuint64(0x1p23). */ + #define TinyBound 0x3e50000000000000 /* asuint64(2^-26). */ +-#define Thresh 0x310000000000000 /* RangeVal - TinyBound. */ + + /* Special cases (fall back to scalar calls). */ + static float64x2_t VPCS_ATTR NOINLINE +-special_case (float64x2_t x) ++special_case (float64x2_t x, float64x2_t n, float64x2_t d, uint64x2_t special) + { +- return v_call_f64 (tan, x, x, v_u64 (-1)); ++ return v_call_f64 (tan, x, vdivq_f64 (n, d), special); + } + + /* Vector approximation for double-precision tan. +@@ -65,14 +60,6 @@ float64x2_t VPCS_ATTR V_NAME_D1 (tan) (float64x2_t x) + very large inputs. Fall back to scalar routine for all lanes if any are + too large, or Inf/NaN. If fenv exceptions are expected, also fall back for + tiny input to avoid underflow. */ +-#if WANT_SIMD_EXCEPT +- uint64x2_t iax = vreinterpretq_u64_f64 (vabsq_f64 (x)); +- /* iax - tiny_bound > range_val - tiny_bound. */ +- uint64x2_t special +- = vcgtq_u64 (vsubq_u64 (iax, v_u64 (TinyBound)), v_u64 (Thresh)); +- if (__glibc_unlikely (v_any_u64 (special))) +- return special_case (x); +-#endif + + /* q = nearest integer to 2 * x / pi. */ + float64x2_t q +@@ -81,9 +68,8 @@ float64x2_t VPCS_ATTR V_NAME_D1 (tan) (float64x2_t x) + + /* Use q to reduce x to r in [-pi/4, pi/4], by: + r = x - q * pi/2, in extended precision. */ +- float64x2_t r = x; + float64x2_t half_pi = vld1q_f64 (dat->half_pi); +- r = vfmsq_laneq_f64 (r, q, half_pi, 0); ++ float64x2_t r = vfmsq_laneq_f64 (x, q, half_pi, 0); + r = vfmsq_laneq_f64 (r, q, half_pi, 1); + /* Further reduce r to [-pi/8, pi/8], to be reconstructed using double angle + formula. */ +@@ -114,12 +100,13 @@ float64x2_t VPCS_ATTR V_NAME_D1 (tan) (float64x2_t x) + + uint64x2_t no_recip = vtstq_u64 (vreinterpretq_u64_s64 (qi), v_u64 (1)); + +-#if !WANT_SIMD_EXCEPT + uint64x2_t special = vcageq_f64 (x, dat->range_val); ++ float64x2_t swap = vbslq_f64 (no_recip, n, vnegq_f64 (d)); ++ d = vbslq_f64 (no_recip, d, n); ++ n = swap; ++ + if (__glibc_unlikely (v_any_u64 (special))) +- return special_case (x); +-#endif ++ return special_case (x, n, d, special); + +- return vdivq_f64 (vbslq_f64 (no_recip, n, vnegq_f64 (d)), +- vbslq_f64 (no_recip, d, n)); ++ return vdivq_f64 (n, d); + } + +commit a07f8630bf8b99ebc7f1df829e4c46b84a3ff4cc +Author: Pierre Blanchard +Date: Tue Nov 18 15:09:05 2025 +0000 + + AArch64: Fix and improve SVE pow(f) special cases + + powf: + + Update scalar special case function to best use new interface. + + pow: + + Make specialcase NOINLINE to prevent str/ldr leaking in fast path. + Remove depency in sv_call2, as new callback impl is not a + performance gain. + Replace with vectorised specialcase since structure of scalar + routine is fairly simple. + + Throughput gain of about 5-10% on V1 for large values and 25% for subnormal `x`. + + Reviewed-by: Wilco Dijkstra  + (cherry picked from commit bb6519de1e6fe73d79bc71588ec4e5668907f080) + +diff --git a/sysdeps/aarch64/fpu/pow_sve.c b/sysdeps/aarch64/fpu/pow_sve.c +index 4242d22a49..a34db23594 100644 +--- a/sysdeps/aarch64/fpu/pow_sve.c ++++ b/sysdeps/aarch64/fpu/pow_sve.c +@@ -31,8 +31,8 @@ + The SVE algorithm drops the tail in the exp computation at the price of + a lower accuracy, slightly above 1ULP. + The SVE algorithm also drops the special treatement of small (< 2^-65) and +- large (> 2^63) finite values of |y|, as they only affect non-round to nearest +- modes. ++ large (> 2^63) finite values of |y|, as they only affect non-round to ++ nearest modes. + + Maximum measured error is 1.04 ULPs: + SV_NAME_D2 (pow) (0x1.3d2d45bc848acp+63, -0x1.a48a38b40cd43p-12) +@@ -156,42 +156,22 @@ sv_zeroinfnan (svbool_t pg, svuint64_t i) + a double. (int32_t)KI is the k used in the argument reduction and exponent + adjustment of scale, positive k here means the result may overflow and + negative k means the result may underflow. */ +-static inline double +-specialcase (double tmp, uint64_t sbits, uint64_t ki) +-{ +- double scale; +- if ((ki & 0x80000000) == 0) +- { +- /* k > 0, the exponent of scale might have overflowed by <= 460. */ +- sbits -= 1009ull << 52; +- scale = asdouble (sbits); +- return 0x1p1009 * (scale + scale * tmp); +- } +- /* k < 0, need special care in the subnormal range. */ +- sbits += 1022ull << 52; +- /* Note: sbits is signed scale. */ +- scale = asdouble (sbits); +- double y = scale + scale * tmp; +- return 0x1p-1022 * y; +-} +- +-/* Scalar fallback for special cases of SVE pow's exp. */ + static inline svfloat64_t +-sv_call_specialcase (svfloat64_t x1, svuint64_t u1, svuint64_t u2, +- svfloat64_t y, svbool_t cmp) ++specialcase (svfloat64_t tmp, svuint64_t sbits, svuint64_t ki, svbool_t cmp) + { +- svbool_t p = svpfirst (cmp, svpfalse ()); +- while (svptest_any (cmp, p)) +- { +- double sx1 = svclastb (p, 0, x1); +- uint64_t su1 = svclastb (p, 0, u1); +- uint64_t su2 = svclastb (p, 0, u2); +- double elem = specialcase (sx1, su1, su2); +- svfloat64_t y2 = sv_f64 (elem); +- y = svsel (p, y2, y); +- p = svpnext_b64 (cmp, p); +- } +- return y; ++ svbool_t p_pos = svcmpge_n_f64 (cmp, svreinterpret_f64_u64 (ki), 0.0); ++ ++ /* Scale up or down depending on sign of k. */ ++ svint64_t offset ++ = svsel_s64 (p_pos, sv_s64 (1009ull << 52), sv_s64 (-1022ull << 52)); ++ svfloat64_t factor ++ = svsel_f64 (p_pos, sv_f64 (0x1p1009), sv_f64 (0x1p-1022)); ++ ++ svuint64_t offset_sbits ++ = svsub_u64_x (cmp, sbits, svreinterpret_u64_s64 (offset)); ++ svfloat64_t scale = svreinterpret_f64_u64 (offset_sbits); ++ svfloat64_t res = svmad_f64_x (cmp, scale, tmp, scale); ++ return svmul_f64_x (cmp, res, factor); + } + + /* Compute y+TAIL = log(x) where the rounded result is y and TAIL has about +@@ -214,8 +194,8 @@ sv_log_inline (svbool_t pg, svuint64_t ix, svfloat64_t *tail, + + /* log(x) = k*Ln2 + log(c) + log1p(z/c-1). */ + /* SVE lookup requires 3 separate lookup tables, as opposed to scalar version +- that uses array of structures. We also do the lookup earlier in the code to +- make sure it finishes as early as possible. */ ++ that uses array of structures. We also do the lookup earlier in the code ++ to make sure it finishes as early as possible. */ + svfloat64_t invc = svld1_gather_index (pg, __v_pow_log_data.invc, i); + svfloat64_t logc = svld1_gather_index (pg, __v_pow_log_data.logc, i); + svfloat64_t logctail = svld1_gather_index (pg, __v_pow_log_data.logctail, i); +@@ -325,14 +305,14 @@ sv_exp_inline (svbool_t pg, svfloat64_t x, svfloat64_t xtail, + svbool_t oflow = svcmpge (pg, abstop, HugeExp); + oflow = svand_z (pg, uoflow, svbic_z (pg, oflow, uflow)); + +- /* For large |x| values (512 < |x| < 1024) scale * (1 + TMP) can overflow +- or underflow. */ ++ /* Handle underflow and overlow in scale. ++ For large |x| values (512 < |x| < 1024), scale * (1 + TMP) can ++ overflow or underflow. */ + svbool_t special = svbic_z (pg, uoflow, svorr_z (pg, uflow, oflow)); ++ if (__glibc_unlikely (svptest_any (pg, special))) ++ z = svsel (special, specialcase (tmp, sbits, ki, special), z); + +- /* Update result with special and large cases. */ +- z = sv_call_specialcase (tmp, sbits, ki, z, special); +- +- /* Handle underflow and overflow. */ ++ /* Handle underflow and overflow in exp. */ + svbool_t x_is_neg = svcmplt (pg, x, 0); + svuint64_t sign_mask + = svlsl_x (pg, sign_bias, 52 - V_POW_EXP_TABLE_BITS); +@@ -353,7 +333,7 @@ sv_exp_inline (svbool_t pg, svfloat64_t x, svfloat64_t xtail, + } + + static inline double +-pow_sc (double x, double y) ++pow_specialcase (double x, double y) + { + uint64_t ix = asuint64 (x); + uint64_t iy = asuint64 (y); +@@ -382,6 +362,14 @@ pow_sc (double x, double y) + return x; + } + ++/* Scalar fallback for special case routines with custom signature. */ ++static svfloat64_t NOINLINE ++sv_pow_specialcase (svfloat64_t x1, svfloat64_t x2, svfloat64_t y, ++ svbool_t cmp) ++{ ++ return sv_call2_f64 (pow_specialcase, x1, x2, y, cmp); ++} ++ + svfloat64_t SV_NAME_D2 (pow) (svfloat64_t x, svfloat64_t y, const svbool_t pg) + { + const struct data *d = ptr_barrier (&data); +@@ -444,7 +432,7 @@ svfloat64_t SV_NAME_D2 (pow) (svfloat64_t x, svfloat64_t y, const svbool_t pg) + + /* Cases of zero/inf/nan x or y. */ + if (__glibc_unlikely (svptest_any (svptrue_b64 (), special))) +- vz = sv_call2_f64 (pow_sc, x, y, vz, special); ++ vz = sv_pow_specialcase (x, y, vz, special); + + return vz; + } +diff --git a/sysdeps/aarch64/fpu/powf_sve.c b/sysdeps/aarch64/fpu/powf_sve.c +index 33bba96054..8f6f85dca9 100644 +--- a/sysdeps/aarch64/fpu/powf_sve.c ++++ b/sysdeps/aarch64/fpu/powf_sve.c +@@ -116,11 +116,10 @@ zeroinfnan (uint32_t ix) + preamble of scalar powf except that we do not update ix and sign_bias. This + is done in the preamble of the SVE powf. */ + static inline float +-powf_specialcase (float x, float y, float z) ++powf_specialcase (float x, float y) + { + uint32_t ix = asuint (x); + uint32_t iy = asuint (y); +- /* Either (x < 0x1p-126 or inf or nan) or (y is 0 or inf or nan). */ + if (__glibc_unlikely (zeroinfnan (iy))) + { + if (2 * iy == 0) +@@ -142,32 +141,15 @@ powf_specialcase (float x, float y, float z) + x2 = -x2; + return iy & 0x80000000 ? 1 / x2 : x2; + } +- /* We need a return here in case x<0 and y is integer, but all other tests +- need to be run. */ +- return z; ++ /* Return x for convenience, but make sure result is never used. */ ++ return x; + } + + /* Scalar fallback for special case routines with custom signature. */ + static svfloat32_t NOINLINE +-sv_call_powf_sc (svfloat32_t x1, svfloat32_t x2, svfloat32_t y) ++sv_call_powf_sc (svfloat32_t x1, svfloat32_t x2, svfloat32_t y, svbool_t cmp) + { +- /* Special cases of x or y: zero, inf and nan. */ +- svbool_t xspecial = sv_zeroinfnan (svptrue_b32 (), svreinterpret_u32 (x1)); +- svbool_t yspecial = sv_zeroinfnan (svptrue_b32 (), svreinterpret_u32 (x2)); +- svbool_t cmp = svorr_z (svptrue_b32 (), xspecial, yspecial); +- +- svbool_t p = svpfirst (cmp, svpfalse ()); +- while (svptest_any (cmp, p)) +- { +- float sx1 = svclastb (p, 0, x1); +- float sx2 = svclastb (p, 0, x2); +- float elem = svclastb (p, 0, y); +- elem = powf_specialcase (sx1, sx2, elem); +- svfloat32_t y2 = sv_f32 (elem); +- y = svsel (p, y2, y); +- p = svpnext_b32 (cmp, p); +- } +- return y; ++ return sv_call2_f32 (powf_specialcase, x1, x2, y, cmp); + } + + /* Compute core for half of the lanes in double precision. */ +@@ -330,7 +312,7 @@ svfloat32_t SV_NAME_F2 (pow) (svfloat32_t x, svfloat32_t y, const svbool_t pg) + ret = svsel (yint_or_xpos, ret, sv_f32 (__builtin_nanf (""))); + + if (__glibc_unlikely (svptest_any (cmp, cmp))) +- return sv_call_powf_sc (x, y, ret); ++ return sv_call_powf_sc (x, y, ret, cmp); + + return ret; + } + +commit a460dc7bd9dee24a2a305d87b8646ba683cd57de +Author: Sachin Monga +Date: Tue Oct 7 03:17:00 2025 -0500 + + ppc64le: Restore optimized strcmp for power10 + + This patch addresses the actual cause of CVE-2025-5702 + + The vector non-volatile registers are not used anymore for + 32 byte load and comparison operation + + Additionally, the assembler workaround used earlier for the + instruction lxvp is replaced with actual instruction. + + Signed-off-by: Sachin Monga + Co-authored-by: Paul Murphy + (cherry picked from commit 9a40b1cda519cc4f532acb6d020390829df3d81b) + +diff --git a/sysdeps/powerpc/powerpc64/le/power10/strcmp.S b/sysdeps/powerpc/powerpc64/le/power10/strcmp.S +new file mode 100644 +index 0000000000..0d4a53317c +--- /dev/null ++++ b/sysdeps/powerpc/powerpc64/le/power10/strcmp.S +@@ -0,0 +1,185 @@ ++/* Optimized strcmp implementation for PowerPC64/POWER10. ++ 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 ++ ++#ifndef STRCMP ++# define STRCMP strcmp ++#endif ++ ++/* Implements the function ++ int [r3] strcmp (const char *s1 [r3], const char *s2 [r4]). */ ++ ++ ++#define COMPARE_16(vreg1,vreg2,offset) \ ++ lxv vreg1+32,offset(r3); \ ++ lxv vreg2+32,offset(r4); \ ++ vcmpnezb. v7,vreg1,vreg2; \ ++ bne cr6,L(different); \ ++ ++#define COMPARE_32(vreg1,vreg2,offset,label1,label2) \ ++ lxvp vreg1+32,offset(r3); \ ++ lxvp vreg2+32,offset(r4); \ ++ vcmpnezb. v7,vreg1+1,vreg2+1; \ ++ bne cr6,L(label1); \ ++ vcmpnezb. v7,vreg1,vreg2; \ ++ bne cr6,L(label2); \ ++ ++#define TAIL(vreg1,vreg2) \ ++ vctzlsbb r6,v7; \ ++ vextubrx r5,r6,vreg1; \ ++ vextubrx r4,r6,vreg2; \ ++ subf r3,r4,r5; \ ++ blr; \ ++ ++#define CHECK_N_BYTES(reg1,reg2,len_reg) \ ++ sldi r0,len_reg,56; \ ++ lxvl 32+v4,reg1,r0; \ ++ lxvl 32+v5,reg2,r0; \ ++ add reg1,reg1,len_reg; \ ++ add reg2,reg2,len_reg; \ ++ vcmpnezb. v7,v4,v5; \ ++ vctzlsbb r6,v7; \ ++ cmpld cr7,r6,len_reg; \ ++ blt cr7,L(different); \ ++ ++ ++ .machine power10 ++ENTRY_TOCLESS (STRCMP, 4) ++ li r11,16 ++ /* eq bit of cr1 used as swap status flag to indicate if ++ source pointers were swapped. */ ++ crclr 4*cr1+eq ++ andi. r7,r3,15 ++ sub r7,r11,r7 /* r7(nalign1) = 16 - (str1 & 15). */ ++ andi. r9,r4,15 ++ sub r5,r11,r9 /* r5(nalign2) = 16 - (str2 & 15). */ ++ cmpld cr7,r7,r5 ++ beq cr7,L(same_aligned) ++ blt cr7,L(nalign1_min) ++ /* Swap r3 and r4, and r7 and r5 such that r3 and r7 hold the ++ pointer which is closer to the next 16B boundary so that only ++ one CHECK_N_BYTES is needed before entering the loop below. */ ++ mr r8,r4 ++ mr r4,r3 ++ mr r3,r8 ++ mr r12,r7 ++ mr r7,r5 ++ mr r5,r12 ++ crset 4*cr1+eq /* Set bit on swapping source pointers. */ ++ ++ .p2align 5 ++L(nalign1_min): ++ CHECK_N_BYTES(r3,r4,r7) ++ ++ .p2align 5 ++L(s1_aligned): ++ /* r9 and r5 is number of bytes to be read after and before ++ page boundary correspondingly. */ ++ sub r5,r5,r7 ++ subfic r9,r5,16 ++ /* Now let r7 hold the count of quadwords which can be ++ checked without crossing a page boundary. quadword offset is ++ (str2>>4)&0xFF. */ ++ rlwinm r7,r4,28,0xFF ++ /* Below check is required only for first iteration. For second ++ iteration and beyond, the new loop counter is always 255. */ ++ cmpldi r7,255 ++ beq L(L3) ++ /* Get the initial loop count by 255-((str2>>4)&0xFF). */ ++ subfic r11,r7,255 ++ ++ .p2align 5 ++L(L1): ++ mtctr r11 ++ ++ .p2align 5 ++L(L2): ++ COMPARE_16(v4,v5,0) /* Load 16B blocks using lxv. */ ++ addi r3,r3,16 ++ addi r4,r4,16 ++ bdnz L(L2) ++ /* Cross the page boundary of s2, carefully. */ ++ ++ .p2align 5 ++L(L3): ++ CHECK_N_BYTES(r3,r4,r5) ++ CHECK_N_BYTES(r3,r4,r9) ++ li r11,255 /* Load the new loop counter. */ ++ b L(L1) ++ ++ .p2align 5 ++L(same_aligned): ++ CHECK_N_BYTES(r3,r4,r7) ++ /* Align s1 to 32B and adjust s2 address. ++ Use lxvp only if both s1 and s2 are 32B aligned. */ ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ COMPARE_16(v4,v5,32) ++ COMPARE_16(v4,v5,48) ++ addi r3,r3,64 ++ addi r4,r4,64 ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ ++ clrldi r6,r3,59 ++ subfic r5,r6,32 ++ add r3,r3,r5 ++ add r4,r4,r5 ++ andi. r5,r4,0x1F ++ beq cr0,L(32B_aligned_loop) ++ ++ .p2align 5 ++L(16B_aligned_loop): ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ COMPARE_16(v4,v5,32) ++ COMPARE_16(v4,v5,48) ++ addi r3,r3,64 ++ addi r4,r4,64 ++ b L(16B_aligned_loop) ++ ++ /* Calculate and return the difference. */ ++L(different): ++ vctzlsbb r6,v7 ++ vextubrx r5,r6,v4 ++ vextubrx r4,r6,v5 ++ bt 4*cr1+eq,L(swapped) ++ subf r3,r4,r5 ++ blr ++ ++ /* If src pointers were swapped, then swap the ++ indices and calculate the return value. */ ++L(swapped): ++ subf r3,r5,r4 ++ blr ++ ++ .p2align 5 ++L(32B_aligned_loop): ++ COMPARE_32(v14,v16,0,tail1,tail2) ++ COMPARE_32(v14,v16,32,tail1,tail2) ++ COMPARE_32(v14,v16,64,tail1,tail2) ++ COMPARE_32(v14,v16,96,tail1,tail2) ++ addi r3,r3,128 ++ addi r4,r4,128 ++ b L(32B_aligned_loop) ++ ++L(tail1): TAIL(v15,v17) ++L(tail2): TAIL(v14,v16) ++ ++END (STRCMP) ++libc_hidden_builtin_def (strcmp) +diff --git a/sysdeps/powerpc/powerpc64/multiarch/Makefile b/sysdeps/powerpc/powerpc64/multiarch/Makefile +index 9f15f3207f..fa1107dfd9 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/Makefile ++++ b/sysdeps/powerpc/powerpc64/multiarch/Makefile +@@ -33,7 +33,8 @@ sysdep_routines += memcpy-power8-cached memcpy-power7 memcpy-a2 memcpy-power6 \ + ifneq (,$(filter %le,$(config-machine))) + sysdep_routines += memcmp-power10 memcpy-power10 memmove-power10 memset-power10 \ + rawmemchr-power9 rawmemchr-power10 \ +- strcmp-power9 strncmp-power9 strcpy-power9 stpcpy-power9 \ ++ strcmp-power9 strcmp-power10 strncmp-power9 \ ++ strcpy-power9 stpcpy-power9 \ + strlen-power9 strncpy-power9 stpncpy-power9 strlen-power10 + endif + CFLAGS-strncase-power7.c += -mcpu=power7 -funroll-loops +diff --git a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c +index 78443b7f34..9b3e617306 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c ++++ b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c +@@ -377,6 +377,10 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, + /* Support sysdeps/powerpc/powerpc64/multiarch/strcmp.c. */ + IFUNC_IMPL (i, name, strcmp, + #ifdef __LITTLE_ENDIAN__ ++ IFUNC_IMPL_ADD (array, i, strcmp, ++ (hwcap2 & PPC_FEATURE2_ARCH_3_1) ++ && (hwcap & PPC_FEATURE_HAS_VSX), ++ __strcmp_power10) + IFUNC_IMPL_ADD (array, i, strcmp, + hwcap2 & PPC_FEATURE2_ARCH_3_00 + && hwcap & PPC_FEATURE_HAS_ALTIVEC, +diff --git a/sysdeps/powerpc/powerpc64/multiarch/strcmp-power10.S b/sysdeps/powerpc/powerpc64/multiarch/strcmp-power10.S +new file mode 100644 +index 0000000000..a4ee7fb53c +--- /dev/null ++++ b/sysdeps/powerpc/powerpc64/multiarch/strcmp-power10.S +@@ -0,0 +1,26 @@ ++/* Optimized strcmp implementation for POWER10/PPC64. ++ 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 ++ . */ ++ ++#if defined __LITTLE_ENDIAN__ && IS_IN (libc) ++#define STRCMP __strcmp_power10 ++ ++#undef libc_hidden_builtin_def ++#define libc_hidden_builtin_def(name) ++ ++#include ++#endif /* __LITTLE_ENDIAN__ && IS_IN (libc) */ +diff --git a/sysdeps/powerpc/powerpc64/multiarch/strcmp.c b/sysdeps/powerpc/powerpc64/multiarch/strcmp.c +index 06b9b4090f..ff32496fab 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/strcmp.c ++++ b/sysdeps/powerpc/powerpc64/multiarch/strcmp.c +@@ -29,12 +29,16 @@ extern __typeof (strcmp) __strcmp_power7 attribute_hidden; + extern __typeof (strcmp) __strcmp_power8 attribute_hidden; + # ifdef __LITTLE_ENDIAN__ + extern __typeof (strcmp) __strcmp_power9 attribute_hidden; ++extern __typeof (strcmp) __strcmp_power10 attribute_hidden; + # endif + + # undef strcmp + + libc_ifunc_redirected (__redirect_strcmp, strcmp, + # ifdef __LITTLE_ENDIAN__ ++ (hwcap2 & PPC_FEATURE2_ARCH_3_1 ++ && hwcap & PPC_FEATURE_HAS_VSX) ++ ? __strcmp_power10 : + (hwcap2 & PPC_FEATURE2_ARCH_3_00 + && hwcap & PPC_FEATURE_HAS_ALTIVEC) + ? __strcmp_power9 : + +commit 4bfd05e2b8f69a1178272c60c17961a61e4e09b3 +Author: Sachin Monga +Date: Fri Nov 21 01:39:50 2025 -0500 + + ppc64le: Restore optimized strncmp for power10 + + This patch addresses the actual cause of CVE-2025-5745 + + The vector non-volatile registers are not used anymore for + 32 byte load and comparison operation + + Additionally, the assembler workaround used earlier for the + instruction lxvp is replaced with actual instruction. + + Signed-off-by: Sachin Monga + Co-authored-by: Paul Murphy + (cherry picked from commit 2ea943f7d487d6a4166658b32af7c5365889fc34) + +diff --git a/sysdeps/powerpc/powerpc64/le/power10/strncmp.S b/sysdeps/powerpc/powerpc64/le/power10/strncmp.S +new file mode 100644 +index 0000000000..6e09fcb7f2 +--- /dev/null ++++ b/sysdeps/powerpc/powerpc64/le/power10/strncmp.S +@@ -0,0 +1,252 @@ ++/* Optimized strncmp implementation for PowerPC64/POWER10. ++ 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 ++ ++/* Implements the function ++ ++ int [r3] strncmp (const char *s1 [r3], const char *s2 [r4], size_t [r5] n) ++ ++ The implementation uses unaligned doubleword access to avoid specialized ++ code paths depending of data alignment for first 32 bytes and uses ++ vectorised loops after that. */ ++ ++#ifndef STRNCMP ++# define STRNCMP strncmp ++#endif ++ ++#define COMPARE_16(vreg1,vreg2,offset) \ ++ lxv vreg1+32,offset(r3); \ ++ lxv vreg2+32,offset(r4); \ ++ vcmpnezb. v7,vreg1,vreg2; \ ++ bne cr6,L(different); \ ++ cmpldi cr7,r5,16; \ ++ ble cr7,L(ret0); \ ++ addi r5,r5,-16; ++ ++#define COMPARE_32(vreg1,vreg2,offset,label1,label2) \ ++ lxvp vreg1+32,offset(r3); \ ++ lxvp vreg2+32,offset(r4); \ ++ vcmpnezb. v7,vreg1+1,vreg2+1; \ ++ bne cr6,L(label1); \ ++ vcmpnezb. v7,vreg1,vreg2; \ ++ bne cr6,L(label2); \ ++ cmpldi cr7,r5,32; \ ++ ble cr7,L(ret0); \ ++ addi r5,r5,-32; ++ ++#define TAIL_FIRST_16B(vreg1,vreg2) \ ++ vctzlsbb r6,v7; \ ++ cmpld cr7,r5,r6; \ ++ ble cr7,L(ret0); \ ++ vextubrx r5,r6,vreg1; \ ++ vextubrx r4,r6,vreg2; \ ++ subf r3,r4,r5; \ ++ blr; ++ ++#define TAIL_SECOND_16B(vreg1,vreg2) \ ++ vctzlsbb r6,v7; \ ++ addi r0,r6,16; \ ++ cmpld cr7,r5,r0; \ ++ ble cr7,L(ret0); \ ++ vextubrx r5,r6,vreg1; \ ++ vextubrx r4,r6,vreg2; \ ++ subf r3,r4,r5; \ ++ blr; ++ ++#define CHECK_N_BYTES(reg1,reg2,len_reg) \ ++ sldi r6,len_reg,56; \ ++ lxvl 32+v4,reg1,r6; \ ++ lxvl 32+v5,reg2,r6; \ ++ add reg1,reg1,len_reg; \ ++ add reg2,reg2,len_reg; \ ++ vcmpnezb v7,v4,v5; \ ++ vctzlsbb r6,v7; \ ++ cmpld cr7,r6,len_reg; \ ++ blt cr7,L(different); \ ++ cmpld cr7,r5,len_reg; \ ++ ble cr7,L(ret0); \ ++ sub r5,r5,len_reg; \ ++ ++ .machine power10 ++ENTRY_TOCLESS (STRNCMP, 4) ++ /* Check if size is 0. */ ++ cmpdi cr0,r5,0 ++ beq cr0,L(ret0) ++ andi. r7,r3,4095 ++ andi. r8,r4,4095 ++ cmpldi cr0,r7,4096-16 ++ cmpldi cr1,r8,4096-16 ++ bgt cr0,L(crosses) ++ bgt cr1,L(crosses) ++ COMPARE_16(v4,v5,0) ++ addi r3,r3,16 ++ addi r4,r4,16 ++ ++L(crosses): ++ andi. r7,r3,15 ++ subfic r7,r7,16 /* r7(nalign1) = 16 - (str1 & 15). */ ++ andi. r9,r4,15 ++ subfic r8,r9,16 /* r8(nalign2) = 16 - (str2 & 15). */ ++ cmpld cr7,r7,r8 ++ beq cr7,L(same_aligned) ++ blt cr7,L(nalign1_min) ++ ++ /* nalign2 is minimum and s2 pointer is aligned. */ ++ CHECK_N_BYTES(r3,r4,r8) ++ /* Are we on the 64B hunk which crosses a page? */ ++ andi. r10,r3,63 /* Determine offset into 64B hunk. */ ++ andi. r8,r3,15 /* The offset into the 16B hunk. */ ++ neg r7,r3 ++ andi. r9,r7,15 /* Number of bytes after a 16B cross. */ ++ rlwinm. r7,r7,26,0x3F /* ((r4-4096))>>6&63. */ ++ beq L(compare_64_pagecross) ++ mtctr r7 ++ b L(compare_64B_unaligned) ++ ++ /* nalign1 is minimum and s1 pointer is aligned. */ ++L(nalign1_min): ++ CHECK_N_BYTES(r3,r4,r7) ++ /* Are we on the 64B hunk which crosses a page? */ ++ andi. r10,r4,63 /* Determine offset into 64B hunk. */ ++ andi. r8,r4,15 /* The offset into the 16B hunk. */ ++ neg r7,r4 ++ andi. r9,r7,15 /* Number of bytes after a 16B cross. */ ++ rlwinm. r7,r7,26,0x3F /* ((r4-4096))>>6&63. */ ++ beq L(compare_64_pagecross) ++ mtctr r7 ++ ++ .p2align 5 ++L(compare_64B_unaligned): ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ COMPARE_16(v4,v5,32) ++ COMPARE_16(v4,v5,48) ++ addi r3,r3,64 ++ addi r4,r4,64 ++ bdnz L(compare_64B_unaligned) ++ ++ /* Cross the page boundary of s2, carefully. Only for first ++ iteration we have to get the count of 64B blocks to be checked. ++ From second iteration and beyond, loop counter is always 63. */ ++L(compare_64_pagecross): ++ li r11, 63 ++ mtctr r11 ++ cmpldi r10,16 ++ ble L(cross_4) ++ cmpldi r10,32 ++ ble L(cross_3) ++ cmpldi r10,48 ++ ble L(cross_2) ++L(cross_1): ++ CHECK_N_BYTES(r3,r4,r9) ++ CHECK_N_BYTES(r3,r4,r8) ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ COMPARE_16(v4,v5,32) ++ addi r3,r3,48 ++ addi r4,r4,48 ++ b L(compare_64B_unaligned) ++L(cross_2): ++ COMPARE_16(v4,v5,0) ++ addi r3,r3,16 ++ addi r4,r4,16 ++ CHECK_N_BYTES(r3,r4,r9) ++ CHECK_N_BYTES(r3,r4,r8) ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ addi r3,r3,32 ++ addi r4,r4,32 ++ b L(compare_64B_unaligned) ++L(cross_3): ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ addi r3,r3,32 ++ addi r4,r4,32 ++ CHECK_N_BYTES(r3,r4,r9) ++ CHECK_N_BYTES(r3,r4,r8) ++ COMPARE_16(v4,v5,0) ++ addi r3,r3,16 ++ addi r4,r4,16 ++ b L(compare_64B_unaligned) ++L(cross_4): ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ COMPARE_16(v4,v5,32) ++ addi r3,r3,48 ++ addi r4,r4,48 ++ CHECK_N_BYTES(r3,r4,r9) ++ CHECK_N_BYTES(r3,r4,r8) ++ b L(compare_64B_unaligned) ++ ++L(same_aligned): ++ CHECK_N_BYTES(r3,r4,r7) ++ /* Align s1 to 32B and adjust s2 address. ++ Use lxvp only if both s1 and s2 are 32B aligned. */ ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ COMPARE_16(v4,v5,32) ++ COMPARE_16(v4,v5,48) ++ addi r3,r3,64 ++ addi r4,r4,64 ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ addi r5,r5,32 ++ ++ clrldi r6,r3,59 ++ subfic r7,r6,32 ++ add r3,r3,r7 ++ add r4,r4,r7 ++ subf r5,r7,r5 ++ andi. r7,r4,0x1F ++ beq cr0,L(32B_aligned_loop) ++ ++ .p2align 5 ++L(16B_aligned_loop): ++ COMPARE_16(v4,v5,0) ++ COMPARE_16(v4,v5,16) ++ COMPARE_16(v4,v5,32) ++ COMPARE_16(v4,v5,48) ++ addi r3,r3,64 ++ addi r4,r4,64 ++ b L(16B_aligned_loop) ++ ++ /* Calculate and return the difference. */ ++L(different): ++ TAIL_FIRST_16B(v4,v5) ++ ++ .p2align 5 ++L(32B_aligned_loop): ++ COMPARE_32(v14,v16,0,tail1,tail2) ++ COMPARE_32(v14,v16,32,tail1,tail2) ++ COMPARE_32(v14,v16,64,tail1,tail2) ++ COMPARE_32(v14,v16,96,tail1,tail2) ++ addi r3,r3,128 ++ addi r4,r4,128 ++ b L(32B_aligned_loop) ++ ++L(tail1): TAIL_FIRST_16B(v15,v17) ++L(tail2): TAIL_SECOND_16B(v14,v16) ++ ++ .p2align 5 ++L(ret0): ++ li r3,0 ++ blr ++ ++END(STRNCMP) ++libc_hidden_builtin_def(strncmp) +diff --git a/sysdeps/powerpc/powerpc64/multiarch/Makefile b/sysdeps/powerpc/powerpc64/multiarch/Makefile +index fa1107dfd9..9072b0d23a 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/Makefile ++++ b/sysdeps/powerpc/powerpc64/multiarch/Makefile +@@ -33,7 +33,7 @@ sysdep_routines += memcpy-power8-cached memcpy-power7 memcpy-a2 memcpy-power6 \ + ifneq (,$(filter %le,$(config-machine))) + sysdep_routines += memcmp-power10 memcpy-power10 memmove-power10 memset-power10 \ + rawmemchr-power9 rawmemchr-power10 \ +- strcmp-power9 strcmp-power10 strncmp-power9 \ ++ strcmp-power9 strcmp-power10 strncmp-power9 strncmp-power10 \ + strcpy-power9 stpcpy-power9 \ + strlen-power9 strncpy-power9 stpncpy-power9 strlen-power10 + endif +diff --git a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c +index 9b3e617306..14aa642675 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c ++++ b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c +@@ -164,6 +164,9 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array, + /* Support sysdeps/powerpc/powerpc64/multiarch/strncmp.c. */ + IFUNC_IMPL (i, name, strncmp, + #ifdef __LITTLE_ENDIAN__ ++ IFUNC_IMPL_ADD (array, i, strncmp, hwcap2 & PPC_FEATURE2_ARCH_3_1 ++ && hwcap & PPC_FEATURE_HAS_VSX, ++ __strncmp_power10) + IFUNC_IMPL_ADD (array, i, strncmp, hwcap2 & PPC_FEATURE2_ARCH_3_00 + && hwcap & PPC_FEATURE_HAS_ALTIVEC, + __strncmp_power9) +diff --git a/sysdeps/powerpc/powerpc64/multiarch/strncmp-power10.S b/sysdeps/powerpc/powerpc64/multiarch/strncmp-power10.S +new file mode 100644 +index 0000000000..bb25bc75b8 +--- /dev/null ++++ b/sysdeps/powerpc/powerpc64/multiarch/strncmp-power10.S +@@ -0,0 +1,25 @@ ++/* 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 ++ . */ ++ ++#if defined __LITTLE_ENDIAN__ && IS_IN (libc) ++#define STRNCMP __strncmp_power10 ++ ++#undef libc_hidden_builtin_def ++#define libc_hidden_builtin_def(name) ++ ++#include ++#endif +diff --git a/sysdeps/powerpc/powerpc64/multiarch/strncmp.c b/sysdeps/powerpc/powerpc64/multiarch/strncmp.c +index 6178f4a432..a5ed67f766 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/strncmp.c ++++ b/sysdeps/powerpc/powerpc64/multiarch/strncmp.c +@@ -29,6 +29,7 @@ extern __typeof (strncmp) __strncmp_ppc attribute_hidden; + extern __typeof (strncmp) __strncmp_power8 attribute_hidden; + # ifdef __LITTLE_ENDIAN__ + extern __typeof (strncmp) __strncmp_power9 attribute_hidden; ++extern __typeof (strncmp) __strncmp_power10 attribute_hidden; + # endif + # undef strncmp + +@@ -36,6 +37,9 @@ extern __typeof (strncmp) __strncmp_power9 attribute_hidden; + ifunc symbol properly. */ + libc_ifunc_redirected (__redirect_strncmp, strncmp, + # ifdef __LITTLE_ENDIAN__ ++ (hwcap2 & PPC_FEATURE2_ARCH_3_1 ++ && hwcap & PPC_FEATURE_HAS_VSX) ++ ? __strncmp_power10 : + (hwcap2 & PPC_FEATURE2_ARCH_3_00 + && hwcap & PPC_FEATURE_HAS_ALTIVEC) + ? __strncmp_power9 : + +commit 165497e8973f2bd960d8a3abcfa577720ee45bb4 +Author: Sachin Monga +Date: Thu Nov 27 10:58:19 2025 -0500 + + ppc64le: Power 10 rawmemchr clobbers v20 (bug #33091) + + Replace non-volatile(v20) by volatile(v17) + since v20 is not restored + + Reviewed-by: Peter Bergner + (cherry picked from commit b59799f14f97f697c3a36b4380bd4ce2fbe65f11) + +diff --git a/sysdeps/powerpc/powerpc64/le/power10/strlen.S b/sysdeps/powerpc/powerpc64/le/power10/strlen.S +index 0bd794540f..574a24b586 100644 +--- a/sysdeps/powerpc/powerpc64/le/power10/strlen.S ++++ b/sysdeps/powerpc/powerpc64/le/power10/strlen.S +@@ -31,7 +31,7 @@ + # define FUNCNAME RAWMEMCHR + # endif + # define MCOUNT_NARGS 2 +-# define VREG_ZERO v20 ++# define VREG_ZERO v17 + # define OFF_START_LOOP 256 + # define RAWMEMCHR_SUBTRACT_VECTORS \ + vsububm v4,v4,v18; \ + +commit 183f600c802108897352ea3be27d43b8e2db846c +Author: H.J. Lu +Date: Tue Dec 17 15:18:36 2024 +0800 + + getaddrinfo.c: Avoid uninitialized pointer access [BZ #32465] + + Add valid_decimal_value to check valid decimal value in a string to + avoid uninitialized endp in add_prefixlist and gaiconf_init as reported + by Clang 19: + + ./getaddrinfo.c:1884:11: error: variable 'endp' is used uninitialized whenever '||' condition is true [-Werror,-Wsometimes-uninitialized] + 1884 | && (cp == NULL + | ^~~~~~~~~~ + ./getaddrinfo.c:1887:11: note: uninitialized use occurs here + 1887 | && *endp == '\0' + | ^~~~ + ./getaddrinfo.c:1884:11: note: remove the '||' if its condition is always false + 1884 | && (cp == NULL + | ^~~~~~~~~~ + 1885 | || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX + | ~~ + ./getaddrinfo.c:1875:13: note: initialize the variable 'endp' to silence this warning + 1875 | char *endp; + | ^ + | = NULL + + This fixes BZ #32465. + + Signed-off-by: H.J. Lu + Reviewed-by: Sam James + (cherry picked from commit 33aeb88c5bc9a0c6b1bd7190a0ead7570972b719) + +diff --git a/nss/getaddrinfo.c b/nss/getaddrinfo.c +index 3ccd3905fa..542b362af3 100644 +--- a/nss/getaddrinfo.c ++++ b/nss/getaddrinfo.c +@@ -1865,6 +1865,22 @@ scopecmp (const void *p1, const void *p2) + return 1; + } + ++/* Return true if PTR points to a valid decimal value string and ++ store the value in *VALUE_P. Otherwise, return false. */ ++ ++static bool ++valid_decimal_value (const char *str, unsigned long int *value_p) ++{ ++ char *endp; ++ unsigned long int value = strtoul (str, &endp, 10); ++ if (str == endp ++ || *endp != '\0' ++ || (value == ULONG_MAX && errno == ERANGE)) ++ return false; ++ *value_p = value; ++ return true; ++} ++ + static bool + add_prefixlist (struct prefixlist **listp, size_t *lenp, bool *nullbitsp, + char *val1, char *val2, char **pos) +@@ -1872,7 +1888,6 @@ add_prefixlist (struct prefixlist **listp, size_t *lenp, bool *nullbitsp, + struct in6_addr prefix; + unsigned long int bits; + unsigned long int val; +- char *endp; + + bits = 128; + __set_errno (0); +@@ -1881,14 +1896,9 @@ add_prefixlist (struct prefixlist **listp, size_t *lenp, bool *nullbitsp, + *cp++ = '\0'; + *pos = cp; + if (inet_pton (AF_INET6, val1, &prefix) +- && (cp == NULL +- || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX +- || errno != ERANGE) +- && *endp == '\0' ++ && (cp == NULL || valid_decimal_value (cp, &bits)) + && bits <= 128 +- && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX +- || errno != ERANGE) +- && *endp == '\0' ++ && valid_decimal_value (val2, &val) + && val <= INT_MAX) + { + struct prefixlist *newp = malloc (sizeof (*newp)); +@@ -2031,7 +2041,6 @@ gaiconf_init (void) + struct in6_addr prefix; + unsigned long int bits; + unsigned long int val; +- char *endp; + + bits = 32; + __set_errno (0); +@@ -2042,15 +2051,10 @@ gaiconf_init (void) + { + bits = 128; + if (IN6_IS_ADDR_V4MAPPED (&prefix) +- && (cp == NULL +- || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX +- || errno != ERANGE) +- && *endp == '\0' ++ && (cp == NULL || valid_decimal_value (cp, &bits)) + && bits >= 96 + && bits <= 128 +- && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX +- || errno != ERANGE) +- && *endp == '\0' ++ && valid_decimal_value (val2, &val) + && val <= INT_MAX) + { + if (!add_scopelist (&scopelist, &nscopelist, +@@ -2064,14 +2068,9 @@ gaiconf_init (void) + } + } + else if (inet_pton (AF_INET, val1, &prefix.s6_addr32[3]) +- && (cp == NULL +- || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX +- || errno != ERANGE) +- && *endp == '\0' ++ && (cp == NULL || valid_decimal_value (cp, &bits)) + && bits <= 32 +- && ((val = strtoul (val2, &endp, 10)) != ULONG_MAX +- || errno != ERANGE) +- && *endp == '\0' ++ && valid_decimal_value (val2, &val) + && val <= INT_MAX) + { + if (!add_scopelist (&scopelist, &nscopelist, + +commit 0273c4d18ecbfe742439a45bb1df3fafaf1561c4 +Author: DJ Delorie +Date: Wed Oct 15 21:37:56 2025 -0400 + + sprof: check pread size and offset for overflow + + Add a bit of descriptive paranoia to the values we read from + the ELF headers and use to access data. + + Reviewed-by: Collin Funk + (cherry picked from commit 324084649b2da2f6840e3a1b84159a4e9a9e9a74) + +diff --git a/elf/sprof.c b/elf/sprof.c +index b19aca3292..0c687eab49 100644 +--- a/elf/sprof.c ++++ b/elf/sprof.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + + /* Get libc version number. */ + #include "../version.h" +@@ -410,6 +411,7 @@ load_shobj (const char *name) + int fd; + ElfW(Shdr) *shdr; + size_t pagesize = getpagesize (); ++ struct stat st; + + /* Since we use dlopen() we must be prepared to work around the sometimes + strange lookup rules for the shared objects. If we have a file foo.so +@@ -553,14 +555,39 @@ load_shobj (const char *name) + error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"), + map->l_name); + ++ if (fstat (fd, &st) < 0) ++ error (EXIT_FAILURE, errno, _("stat(%s) failure"), map->l_name); ++ ++ /* We're depending on data that's being read from the file, so be a ++ bit paranoid here and make sure the requests are reasonable - ++ i.e. both size and offset are nonnegative and smaller than the ++ file size, as well as the offset of the end of the data. PREAD ++ would have failed anyway, but this is more robust and explains ++ what happened better. Note that SZ must be unsigned and OFF may ++ be signed or unsigned. */ ++#define PCHECK(sz1,off1) { \ ++ size_t sz = sz1, end_off; \ ++ off_t off = off1; \ ++ if (sz > st.st_size \ ++ || off < 0 || off > st.st_size \ ++ || INT_ADD_WRAPV (sz, off, &end_off) \ ++ || end_off > st.st_size) \ ++ error (EXIT_FAILURE, ERANGE, \ ++ _("read outside of file extents %zu + %zd > %zu"), \ ++ sz, off, st.st_size); \ ++ } ++ + /* Map the section header. */ + size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr)); + shdr = (ElfW(Shdr) *) alloca (size); ++ PCHECK (size, ehdr->e_shoff); + if (pread (fd, shdr, size, ehdr->e_shoff) != size) + error (EXIT_FAILURE, errno, _("reading of section headers failed")); + + /* Get the section header string table. */ + char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size); ++ PCHECK (shdr[ehdr->e_shstrndx].sh_size, ++ shdr[ehdr->e_shstrndx].sh_offset); + if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size, + shdr[ehdr->e_shstrndx].sh_offset) + != shdr[ehdr->e_shstrndx].sh_size) +@@ -588,6 +615,7 @@ load_shobj (const char *name) + size_t size = debuglink_entry->sh_size; + char *debuginfo_fname = (char *) alloca (size + 1); + debuginfo_fname[size] = '\0'; ++ PCHECK (size, debuglink_entry->sh_offset); + if (pread (fd, debuginfo_fname, size, debuglink_entry->sh_offset) + != size) + { +@@ -641,21 +669,32 @@ load_shobj (const char *name) + if (fd2 != -1) + { + ElfW(Ehdr) ehdr2; ++ struct stat st; ++ ++ if (fstat (fd2, &st) < 0) ++ error (EXIT_FAILURE, errno, _("stat(%s) failure"), workbuf); + + /* Read the ELF header. */ ++ PCHECK (sizeof (ehdr2), 0); + if (pread (fd2, &ehdr2, sizeof (ehdr2), 0) != sizeof (ehdr2)) + error (EXIT_FAILURE, errno, + _("reading of ELF header failed")); + + /* Map the section header. */ +- size_t size = ehdr2.e_shnum * sizeof (ElfW(Shdr)); ++ size_t size; ++ if (INT_MULTIPLY_WRAPV (ehdr2.e_shnum, sizeof (ElfW(Shdr)), &size)) ++ error (EXIT_FAILURE, errno, _("too many section headers")); ++ + ElfW(Shdr) *shdr2 = (ElfW(Shdr) *) alloca (size); ++ PCHECK (size, ehdr2.e_shoff); + if (pread (fd2, shdr2, size, ehdr2.e_shoff) != size) + error (EXIT_FAILURE, errno, + _("reading of section headers failed")); + + /* Get the section header string table. */ + shstrtab = (char *) alloca (shdr2[ehdr2.e_shstrndx].sh_size); ++ PCHECK (shdr2[ehdr2.e_shstrndx].sh_size, ++ shdr2[ehdr2.e_shstrndx].sh_offset); + if (pread (fd2, shstrtab, shdr2[ehdr2.e_shstrndx].sh_size, + shdr2[ehdr2.e_shstrndx].sh_offset) + != shdr2[ehdr2.e_shstrndx].sh_size) + +commit 6a861d39464a8ff2a6a128fb54923f047f841927 +Author: Collin Funk +Date: Wed Oct 22 01:51:09 2025 -0700 + + sprof: fix -Wformat warnings on 32-bit hosts + + Reviewed-by: H.J. Lu + (cherry picked from commit 9681f645ba20fc3c18eb12ffebf94e3df1f888e3) + +diff --git a/elf/sprof.c b/elf/sprof.c +index 0c687eab49..1f5ab25ac3 100644 +--- a/elf/sprof.c ++++ b/elf/sprof.c +@@ -573,8 +573,8 @@ load_shobj (const char *name) + || INT_ADD_WRAPV (sz, off, &end_off) \ + || end_off > st.st_size) \ + error (EXIT_FAILURE, ERANGE, \ +- _("read outside of file extents %zu + %zd > %zu"), \ +- sz, off, st.st_size); \ ++ _("read outside of file extents %zu + %jd > %jd"), \ ++ sz, (intmax_t) off, (intmax_t) st.st_size); \ + } + + /* Map the section header. */ + +commit 9e2f52c5e08e605cf1aaa646a5398b7e5a04d4bb +Author: Florian Weimer +Date: Thu Nov 6 14:33:22 2025 +0100 + + support: Fix FILE * leak in check_for_unshare_hints in test-container + + The file opened via fopen is never closed. + + (cherry picked from commit 20a2a756089eacd7e7f4c02e381e82b5d0e40a2c) + +diff --git a/support/test-container.c b/support/test-container.c +index ebcc722da5..23fe31071c 100644 +--- a/support/test-container.c ++++ b/support/test-container.c +@@ -705,6 +705,7 @@ check_for_unshare_hints (int require_pidns) + + val = -1; /* Sentinel. */ + int cnt = fscanf (f, "%d", &val); ++ fclose (f); + if (cnt == 1 && val != files[i].bad_value) + continue; + + +commit 468ca594f835d98857f78c6d3a7154fdc30db214 +Author: Florian Weimer +Date: Thu Nov 6 14:49:21 2025 +0100 + + support: Exit on consistency check failure in resolv_response_add_name + + Using TEST_VERIFY (crname_target != crname) instructs some analysis + tools that crname_target == crname might hold. Under this assumption, + they report a use-after-free for crname_target->offset below, caused + by the previous free (crname). + + Reviewed-by: Collin Funk + (cherry picked from commit b64335ff111c071fde61aec1c1a8460afb3d16d4) + +diff --git a/support/resolv_test.c b/support/resolv_test.c +index f1613bd255..d4cc26b4aa 100644 +--- a/support/resolv_test.c ++++ b/support/resolv_test.c +@@ -326,7 +326,7 @@ resolv_response_add_name (struct resolv_response_builder *b, + crname_target = *ptr; + else + crname_target = NULL; +- TEST_VERIFY (crname_target != crname); ++ TEST_VERIFY_EXIT (crname_target != crname); + /* Not added to the tree. */ + free (crname); + } + +commit d861635092a5ad3499baf18b2ff955b778734a0e +Author: Sunil K Pandey +Date: Tue Dec 9 08:57:44 2025 -0800 + + nptl: Optimize trylock for high cache contention workloads (BZ #33704) + + Check lock availability before acquisition to reduce cache line + bouncing. Significantly improves trylock throughput on multi-core + systems under heavy contention. + + Tested on x86_64. + + Fixes BZ #33704. + + Co-authored-by: Alex M Wells + Reviewed-by: Wilco Dijkstra + (cherry picked from commit 63716823dbad9482e09972907ae98e9cb00f9b86) + +diff --git a/nptl/pthread_mutex_trylock.c b/nptl/pthread_mutex_trylock.c +index 720c103f3f..6cf47403dd 100644 +--- a/nptl/pthread_mutex_trylock.c ++++ b/nptl/pthread_mutex_trylock.c +@@ -48,7 +48,8 @@ ___pthread_mutex_trylock (pthread_mutex_t *mutex) + return 0; + } + +- if (lll_trylock (mutex->__data.__lock) == 0) ++ if (atomic_load_relaxed (&(mutex->__data.__lock)) == 0 ++ && lll_trylock (mutex->__data.__lock) == 0) + { + /* Record the ownership. */ + mutex->__data.__owner = id; +@@ -71,7 +72,10 @@ ___pthread_mutex_trylock (pthread_mutex_t *mutex) + /*FALL THROUGH*/ + case PTHREAD_MUTEX_ADAPTIVE_NP: + case PTHREAD_MUTEX_ERRORCHECK_NP: +- if (lll_trylock (mutex->__data.__lock) != 0) ++ /* Mutex type is already loaded, lock check overhead should ++ be minimal. */ ++ if (atomic_load_relaxed (&(mutex->__data.__lock)) != 0 ++ || lll_trylock (mutex->__data.__lock) != 0) + break; + + /* Record the ownership. */ + +commit bfc4dd9e526eacf3017dd8864ba0848e9d045dd4 +Author: Siddhesh Poyarekar +Date: Thu Jan 15 06:06:40 2026 -0500 + + memalign: reinstate alignment overflow check (CVE-2026-0861) + + The change to cap valid sizes to PTRDIFF_MAX inadvertently dropped the + overflow check for alignment in memalign functions, _mid_memalign and + _int_memalign. Reinstate the overflow check in _int_memalign, aligned + with the PTRDIFF_MAX change since that is directly responsible for the + CVE. The missing _mid_memalign check is not relevant (and does not have + a security impact) and may need a different approach to fully resolve, + so it has been omitted. + + CVE-Id: CVE-2026-0861 + Vulnerable-Commit: 9bf8e29ca136094f73f69f725f15c51facc97206 + Reported-by: Igor Morgenstern, Aisle Research + Fixes: BZ #33796 + Reviewed-by: Wilco Dijkstra + Signed-off-by: Siddhesh Poyarekar + (cherry picked from commit c9188d333717d3ceb7e3020011651f424f749f93) + +diff --git a/malloc/malloc.c b/malloc/malloc.c +index bcb6e5b83c..e39354595e 100644 +--- a/malloc/malloc.c ++++ b/malloc/malloc.c +@@ -5049,7 +5049,7 @@ _int_memalign (mstate av, size_t alignment, size_t bytes) + INTERNAL_SIZE_T size; + + nb = checked_request2size (bytes); +- if (nb == 0) ++ if (nb == 0 || alignment > PTRDIFF_MAX) + { + __set_errno (ENOMEM); + return NULL; +@@ -5065,7 +5065,10 @@ _int_memalign (mstate av, size_t alignment, size_t bytes) + we don't find anything in those bins, the common malloc code will + scan starting at 2x. */ + +- /* Call malloc with worst case padding to hit alignment. */ ++ /* Call malloc with worst case padding to hit alignment. ALIGNMENT is a ++ power of 2, so it tops out at (PTRDIFF_MAX >> 1) + 1, leaving plenty of ++ space to add MINSIZE and whatever checked_request2size adds to BYTES to ++ get NB. Consequently, total below also does not overflow. */ + m = (char *) (_int_malloc (av, nb + alignment + MINSIZE)); + + if (m == 0) +diff --git a/malloc/tst-malloc-too-large.c b/malloc/tst-malloc-too-large.c +index 2b91377e54..15b25cf01d 100644 +--- a/malloc/tst-malloc-too-large.c ++++ b/malloc/tst-malloc-too-large.c +@@ -152,7 +152,6 @@ test_large_allocations (size_t size) + } + + +-static long pagesize; + + /* This function tests the following aligned memory allocation functions + using several valid alignments and precedes each allocation test with a +@@ -171,8 +170,8 @@ test_large_aligned_allocations (size_t size) + + /* All aligned memory allocation functions expect an alignment that is a + power of 2. Given this, we test each of them with every valid +- alignment from 1 thru PAGESIZE. */ +- for (align = 1; align <= pagesize; align *= 2) ++ alignment for the type of ALIGN, i.e. until it wraps to 0. */ ++ for (align = 1; align > 0; align <<= 1) + { + test_setup (); + #if __GNUC_PREREQ (7, 0) +@@ -265,11 +264,6 @@ do_test (void) + DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than="); + #endif + +- /* Aligned memory allocation functions need to be tested up to alignment +- size equivalent to page size, which should be a power of 2. */ +- pagesize = sysconf (_SC_PAGESIZE); +- TEST_VERIFY_EXIT (powerof2 (pagesize)); +- + /* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e. + in the range (SIZE_MAX - 2^14, SIZE_MAX], fail. + + +commit 329c775788b2c9ff3da774ccf59fba7b6b8ff08e +Author: Carlos O'Donell +Date: Thu Jan 15 15:09:38 2026 -0500 + + resolv: Fix NSS DNS backend for getnetbyaddr (CVE-2026-0915) + + The default network value of zero for net was never tested for and + results in a DNS query constructed from uninitialized stack bytes. + The solution is to provide a default query for the case where net + is zero. + + Adding a test case for this was straight forward given the existence of + tst-resolv-network and if the test is added without the fix you observe + this failure: + + FAIL: resolv/tst-resolv-network + original exit status 1 + error: tst-resolv-network.c:174: invalid QNAME: \146\218\129\128 + error: 1 test failures + + With a random QNAME resulting from the use of uninitialized stack bytes. + + After the fix the test passes. + + Additionally verified using wireshark before and after to ensure + on-the-wire bytes for the DNS query were as expected. + + No regressions on x86_64. + + Reviewed-by: Florian Weimer + (cherry picked from commit e56ff82d5034ec66c6a78f517af6faa427f65b0b) + +diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c +index b32fd0fcab..71d67aa6f8 100644 +--- a/resolv/nss_dns/dns-network.c ++++ b/resolv/nss_dns/dns-network.c +@@ -207,6 +207,10 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result, + sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2], + net_bytes[1], net_bytes[0]); + break; ++ default: ++ /* Default network (net is originally zero). */ ++ strcpy (qbuf, "0.0.0.0.in-addr.arpa"); ++ break; + } + + net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024); +diff --git a/resolv/tst-resolv-network.c b/resolv/tst-resolv-network.c +index f40e6f926c..8d4f8badf3 100644 +--- a/resolv/tst-resolv-network.c ++++ b/resolv/tst-resolv-network.c +@@ -46,6 +46,9 @@ handle_code (const struct resolv_response_context *ctx, + { + switch (code) + { ++ case 0: ++ send_ptr (b, qname, qclass, qtype, "0.in-addr.arpa"); ++ break; + case 1: + send_ptr (b, qname, qclass, qtype, "1.in-addr.arpa"); + break; +@@ -265,6 +268,9 @@ do_test (void) + "error: TRY_AGAIN\n"); + + /* Lookup by address, success cases. */ ++ check_reverse (0, ++ "name: 0.in-addr.arpa\n" ++ "net: 0x00000000\n"); + check_reverse (1, + "name: 1.in-addr.arpa\n" + "net: 0x00000001\n"); + +commit 9fe8576664d43b87ca19401fb6a975e217e47623 +Author: Adhemerval Zanella +Date: Thu Jan 15 10:32:19 2026 -0300 + + posix: Reset wordexp_t fields with WRDE_REUSE (CVE-2025-15281 / BZ 33814) + + The wordexp fails to properly initialize the input wordexp_t when + WRDE_REUSE is used. The wordexp_t struct is properly freed, but + reuses the old wc_wordc value and updates the we_wordv in the + wrong position. A later wordfree will then call free with an + invalid pointer. + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + Reviewed-by: Carlos O'Donell + (cherry picked from commit 80cc58ea2de214f85b0a1d902a3b668ad2ecb302) + +diff --git a/NEWS b/NEWS +index 595998fc6e..dd7673282a 100644 +--- a/NEWS ++++ b/NEWS +@@ -31,6 +31,8 @@ The following bugs are resolved with this release: + [33234] Use TLS initial-exec model for __libc_tsd_CTYPE_* thread variables + [33361] nss: Group merge does not react to ERANGE during merge + [33601] aarch64: Do not link conform tests with -Wl,-z,force-bti ++ [33814] glob: wordexp with WRDE_REUSE and WRDE_APPEND may return ++ uninitialized memory + + Version 2.40 + +diff --git a/posix/Makefile b/posix/Makefile +index 830278a423..0cd5572297 100644 +--- a/posix/Makefile ++++ b/posix/Makefile +@@ -326,6 +326,7 @@ tests := \ + tst-wait4 \ + tst-waitid \ + tst-wordexp-nocmd \ ++ tst-wordexp-reuse \ + tstgetopt \ + # tests + +@@ -454,6 +455,8 @@ generated += \ + tst-rxspencer-no-utf8.mtrace \ + tst-vfork3-mem.out \ + tst-vfork3.mtrace \ ++ tst-wordexp-reuse-mem.out \ ++ tst-wordexp-reuse.mtrace \ + # generated + endif + endif +@@ -489,6 +492,7 @@ tests-special += \ + $(objpfx)tst-pcre-mem.out \ + $(objpfx)tst-rxspencer-no-utf8-mem.out \ + $(objpfx)tst-vfork3-mem.out \ ++ $(objpfx)tst-wordexp-reuse.out \ + # tests-special + endif + endif +@@ -772,3 +776,10 @@ $(objpfx)posix-conf-vars-def.h: $(..)scripts/gen-posix-conf-vars.awk \ + $(make-target-directory) + $(AWK) -f $(filter-out Makefile, $^) > $@.tmp + mv -f $@.tmp $@ ++ ++tst-wordexp-reuse-ENV += MALLOC_TRACE=$(objpfx)tst-wordexp-reuse.mtrace \ ++ LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so ++ ++$(objpfx)tst-wordexp-reuse-mem.out: $(objpfx)tst-wordexp-reuse.out ++ $(common-objpfx)malloc/mtrace $(objpfx)tst-wordexp-reuse.mtrace > $@; \ ++ $(evaluate-test) +diff --git a/posix/tst-wordexp-reuse.c b/posix/tst-wordexp-reuse.c +new file mode 100644 +index 0000000000..3926b9f557 +--- /dev/null ++++ b/posix/tst-wordexp-reuse.c +@@ -0,0 +1,89 @@ ++/* Test for wordexp with WRDE_REUSE flag. ++ Copyright (C) 2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++ ++#include ++ ++static int ++do_test (void) ++{ ++ mtrace (); ++ ++ { ++ wordexp_t p = { 0 }; ++ TEST_COMPARE (wordexp ("one", &p, 0), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[0], "one"); ++ TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[0], "two"); ++ wordfree (&p); ++ } ++ ++ { ++ wordexp_t p = { .we_offs = 2 }; ++ TEST_COMPARE (wordexp ("one", &p, 0), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[0], "one"); ++ TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE | WRDE_DOOFFS), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two"); ++ wordfree (&p); ++ } ++ ++ { ++ wordexp_t p = { 0 }; ++ TEST_COMPARE (wordexp ("one", &p, 0), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[0], "one"); ++ TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE | WRDE_APPEND), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[0], "two"); ++ wordfree (&p); ++ } ++ ++ { ++ wordexp_t p = { .we_offs = 2 }; ++ TEST_COMPARE (wordexp ("one", &p, WRDE_DOOFFS), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "one"); ++ TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE ++ | WRDE_DOOFFS), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two"); ++ wordfree (&p); ++ } ++ ++ { ++ wordexp_t p = { .we_offs = 2 }; ++ TEST_COMPARE (wordexp ("one", &p, WRDE_DOOFFS), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "one"); ++ TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE ++ | WRDE_DOOFFS | WRDE_APPEND), 0); ++ TEST_COMPARE (p.we_wordc, 1); ++ TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two"); ++ wordfree (&p); ++ } ++ ++ return 0; ++} ++ ++#include +diff --git a/posix/wordexp.c b/posix/wordexp.c +index a7362ef31b..4cd2364519 100644 +--- a/posix/wordexp.c ++++ b/posix/wordexp.c +@@ -2216,7 +2216,9 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags) + { + /* Minimal implementation of WRDE_REUSE for now */ + wordfree (pwordexp); ++ old_word.we_wordc = 0; + old_word.we_wordv = NULL; ++ pwordexp->we_wordc = 0; + } + + if ((flags & WRDE_APPEND) == 0) diff --git a/pkgs/development/libraries/glibc/common.nix b/pkgs/development/libraries/glibc/common.nix index 7e617c326996b..858806671d3c3 100644 --- a/pkgs/development/libraries/glibc/common.nix +++ b/pkgs/development/libraries/glibc/common.nix @@ -50,7 +50,7 @@ let version = "2.40"; - patchSuffix = "-66"; + patchSuffix = "-218"; sha256 = "sha256-GaiQF16SY9dI9ieZPeb0sa+c0h4D8IDkv7Oh+sECBaI="; in @@ -68,7 +68,7 @@ stdenv.mkDerivation ( /* No tarballs for stable upstream branch, only https://sourceware.org/git/glibc.git and using git would complicate bootstrapping. $ git fetch --all -p && git checkout origin/release/2.40/master && git describe - glibc-2.40-142-g2eb180377b + glibc-2.40-218-g9fe8576664 $ git show --minimal --reverse glibc-2.40.. ':!ADVISORIES' > 2.40-master.patch To compare the archive contents zdiff can be used. diff --git a/pkgs/development/libraries/libpng/default.nix b/pkgs/development/libraries/libpng/default.nix index 7af29dedd1ac2..e548f82bdbb8f 100644 --- a/pkgs/development/libraries/libpng/default.nix +++ b/pkgs/development/libraries/libpng/default.nix @@ -10,21 +10,21 @@ assert zlib != null; let - patchVersion = "1.6.50"; + patchVersion = "1.6.54"; patch_src = fetchurl { url = "mirror://sourceforge/libpng-apng/libpng-${patchVersion}-apng.patch.gz"; - hash = "sha256-aH3cDHyxKKPqWOFZtRKSUlN8J+3gwyqT8R8DEn8MAWU="; + hash = "sha256-Vjj8RQDq9IRXtNfJ5VP9gqsB13MEpwB32DNoQp1E8bQ="; }; whenPatched = lib.optionalString apngSupport; in stdenv.mkDerivation (finalAttrs: { pname = "libpng" + whenPatched "-apng"; - version = "1.6.52"; + version = "1.6.54"; src = fetchurl { url = "mirror://sourceforge/libpng/libpng-${finalAttrs.version}.tar.xz"; - hash = "sha256-Nr1yYijsk6O2wi/bSelKZ7FvL+mzm3i3y2V3KWZmHMw="; + hash = "sha256-AcnYowPJQewsURwUMSo7HTbO20Hi9RaMzaqF1TuIeAU="; }; postPatch = whenPatched "gunzip < ${patch_src} | patch -Np1" diff --git a/pkgs/development/libraries/libxml2/2.13-CVE-2026-0989.patch b/pkgs/development/libraries/libxml2/2.13-CVE-2026-0989.patch new file mode 100644 index 0000000000000..d3154c40d4614 --- /dev/null +++ b/pkgs/development/libraries/libxml2/2.13-CVE-2026-0989.patch @@ -0,0 +1,237 @@ +diff --git a/include/libxml/relaxng.h b/include/libxml/relaxng.h +index 079b7f12..78985e5e 100644 +--- a/include/libxml/relaxng.h ++++ b/include/libxml/relaxng.h +@@ -138,6 +138,10 @@ XMLPUBFUN int + xmlRelaxParserSetFlag (xmlRelaxNGParserCtxtPtr ctxt, + int flag); + ++XMLPUBFUN int ++ xmlRelaxParserSetIncLImit (xmlRelaxNGParserCtxt *ctxt, ++ int limit); ++ + XMLPUBFUN void + xmlRelaxNGFreeParserCtxt (xmlRelaxNGParserCtxtPtr ctxt); + XMLPUBFUN void +diff --git a/relaxng.c b/relaxng.c +index 24c3e510..eb0f2b36 100644 +--- a/relaxng.c ++++ b/relaxng.c +@@ -18,6 +18,8 @@ + + #ifdef LIBXML_SCHEMAS_ENABLED + ++#include ++#include + #include + #include + #include +@@ -44,6 +46,12 @@ + static const xmlChar *xmlRelaxNGNs = (const xmlChar *) + "http://relaxng.org/ns/structure/1.0"; + ++/* ++ * Default include limit, this can be override with RNG_INCLUDE_LIMIT ++ * env variable ++ */ ++static const int _xmlRelaxNGIncludeLimit = 1000; ++ + #define IS_RELAXNG(node, typ) \ + ((node != NULL) && (node->ns != NULL) && \ + (node->type == XML_ELEMENT_NODE) && \ +@@ -220,6 +228,7 @@ struct _xmlRelaxNGParserCtxt { + int incNr; /* Depth of the include parsing stack */ + int incMax; /* Max depth of the parsing stack */ + xmlRelaxNGIncludePtr *incTab; /* array of incs */ ++ int incLimit; /* Include limit, to avoid stack-overflow on parse */ + + int idref; /* requires idref checking */ + +@@ -1403,6 +1412,23 @@ xmlRelaxParserSetFlag(xmlRelaxNGParserCtxtPtr ctxt, int flags) + return(0); + } + ++/** ++ * Semi private function used to set the include recursion limit to a ++ * parser context. Set to 0 to use the default value. ++ * ++ * @param ctxt a RelaxNG parser context ++ * @param limit the new include depth limit ++ * @returns 0 if success and -1 in case of error ++ */ ++int ++xmlRelaxParserSetIncLImit(xmlRelaxNGParserCtxt *ctxt, int limit) ++{ ++ if (ctxt == NULL) return(-1); ++ if (limit < 0) return(-1); ++ ctxt->incLimit = limit; ++ return(0); ++} ++ + /************************************************************************ + * * + * Document functions * +@@ -1454,7 +1480,7 @@ xmlRelaxReadMemory(xmlRelaxNGParserCtxtPtr ctxt, const char *buf, int size) { + * + * Pushes a new include on top of the include stack + * +- * Returns 0 in case of error, the index in the stack otherwise ++ * Returns -1 in case of error, the index in the stack otherwise + */ + static int + xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt, +@@ -1468,9 +1494,15 @@ xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt, + sizeof(ctxt->incTab[0])); + if (ctxt->incTab == NULL) { + xmlRngPErrMemory(ctxt); +- return (0); ++ return (-1); + } + } ++ if (ctxt->incNr >= ctxt->incLimit) { ++ xmlRngPErr(ctxt, (xmlNodePtr)value->doc, XML_RNGP_PARSE_ERROR, ++ "xmlRelaxNG: inclusion recursion limit reached\n", NULL, NULL); ++ return(-1); ++ } ++ + if (ctxt->incNr >= ctxt->incMax) { + ctxt->incMax *= 2; + ctxt->incTab = +@@ -1479,7 +1511,7 @@ xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt, + sizeof(ctxt->incTab[0])); + if (ctxt->incTab == NULL) { + xmlRngPErrMemory(ctxt); +- return (0); ++ return (-1); + } + } + ctxt->incTab[ctxt->incNr] = value; +@@ -1649,7 +1681,9 @@ xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar * URL, + /* + * push it on the stack + */ +- xmlRelaxNGIncludePush(ctxt, ret); ++ if (xmlRelaxNGIncludePush(ctxt, ret) < 0) { ++ return (NULL); ++ } + + /* + * Some preprocessing of the document content, this include recursing +@@ -7373,11 +7407,32 @@ xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt) + xmlDocPtr doc; + xmlNodePtr root; + ++ const char *include_limit_env = getenv("RNG_INCLUDE_LIMIT"); ++ + xmlRelaxNGInitTypes(); + + if (ctxt == NULL) + return (NULL); + ++ if (ctxt->incLimit == 0) { ++ ctxt->incLimit = _xmlRelaxNGIncludeLimit; ++ if (include_limit_env != NULL) { ++ char *strEnd; ++ unsigned long val = 0; ++ errno = 0; ++ val = strtoul(include_limit_env, &strEnd, 10); ++ if (errno != 0 || *strEnd != 0 || val > INT_MAX) { ++ xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR, ++ "xmlRelaxNGParse: invalid RNG_INCLUDE_LIMIT %s\n", ++ (const xmlChar*)include_limit_env, ++ NULL); ++ return(NULL); ++ } ++ if (val) ++ ctxt->incLimit = val; ++ } ++ } ++ + /* + * First step is to parse the input document into an DOM/Infoset + */ +diff --git a/runtest.c b/runtest.c +index 26681a3c..a41897dd 100644 +--- a/runtest.c ++++ b/runtest.c +@@ -3529,6 +3529,70 @@ rngTest(const char *filename, + return(ret); + } + ++/** ++ * Parse an RNG schemas with a custom RNG_INCLUDE_LIMIT ++ * ++ * @param filename the schemas file ++ * @param result the file with expected result ++ * @param err the file with error messages ++ * @returns 0 in case of success, an error code otherwise ++ */ ++static int ++rngIncludeTest(const char *filename, ++ const char *resul ATTRIBUTE_UNUSED, ++ const char *errr ATTRIBUTE_UNUSED, ++ int options ATTRIBUTE_UNUSED) { ++ xmlRelaxNGParserCtxtPtr ctxt; ++ xmlRelaxNGPtr schemas; ++ int ret = 0; ++ ++ /* first compile the schemas if possible */ ++ ctxt = xmlRelaxNGNewParserCtxt(filename); ++ xmlRelaxNGSetParserStructuredErrors(ctxt, testStructuredErrorHandler, ++ NULL); ++ ++ /* Should work */ ++ schemas = xmlRelaxNGParse(ctxt); ++ if (schemas == NULL) { ++ testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n", ++ filename); ++ ret = -1; ++ goto done; ++ } ++ xmlRelaxNGFree(schemas); ++ xmlRelaxNGFreeParserCtxt(ctxt); ++ ++ ctxt = xmlRelaxNGNewParserCtxt(filename); ++ /* Should fail */ ++ xmlRelaxParserSetIncLImit(ctxt, 2); ++ xmlRelaxNGSetParserStructuredErrors(ctxt, testStructuredErrorHandler, ++ NULL); ++ schemas = xmlRelaxNGParse(ctxt); ++ if (schemas != NULL) { ++ ret = -1; ++ xmlRelaxNGFree(schemas); ++ } ++ xmlRelaxNGFreeParserCtxt(ctxt); ++ ++ ctxt = xmlRelaxNGNewParserCtxt(filename); ++ /* Should work */ ++ xmlRelaxParserSetIncLImit(ctxt, 3); ++ xmlRelaxNGSetParserStructuredErrors(ctxt, testStructuredErrorHandler, ++ NULL); ++ schemas = xmlRelaxNGParse(ctxt); ++ if (schemas == NULL) { ++ testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n", ++ filename); ++ ret = -1; ++ goto done; ++ } ++ xmlRelaxNGFree(schemas); ++ ++done: ++ xmlRelaxNGFreeParserCtxt(ctxt); ++ return(ret); ++} ++ + #ifdef LIBXML_READER_ENABLED + /** + * rngStreamTest: +@@ -4873,6 +4937,9 @@ testDesc testDescriptions[] = { + { "Relax-NG regression tests" , + rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL, + XML_PARSE_DTDATTR | XML_PARSE_NOENT }, ++ { "Relax-NG include limit tests" , ++ rngIncludeTest, "./test/relaxng/include/include-limit.rng", NULL, NULL, NULL, ++ 0 }, + #ifdef LIBXML_READER_ENABLED + { "Relax-NG streaming regression tests" , + rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL, diff --git a/pkgs/development/libraries/libxml2/2.13-CVE-2026-0992.patch b/pkgs/development/libraries/libxml2/2.13-CVE-2026-0992.patch new file mode 100644 index 0000000000000..95dde2b5017f6 --- /dev/null +++ b/pkgs/development/libraries/libxml2/2.13-CVE-2026-0992.patch @@ -0,0 +1,49 @@ +From f75abfcaa419a740a3191e56c60400f3ff18988d Mon Sep 17 00:00:00 2001 +From: Daniel Garcia Moreno +Date: Fri, 19 Dec 2025 11:02:18 +0100 +Subject: [PATCH] catalog: Ignore repeated nextCatalog entries + +This patch makes the catalog parsing to ignore repeated entries of +nextCatalog with the same value. + +Fix https://gitlab.gnome.org/GNOME/libxml2/-/issues/1019 +--- + catalog.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/catalog.c b/catalog.c +index 46b877e62..fa6d77ca1 100644 +--- a/catalog.c ++++ b/catalog.c +@@ -1223,9 +1223,27 @@ xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer, + BAD_CAST "delegateURI", BAD_CAST "uriStartString", + BAD_CAST "catalog", prefer, cgroup); + } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) { ++ xmlCatalogEntryPtr prev = parent->children; ++ + entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG, + BAD_CAST "nextCatalog", NULL, + BAD_CAST "catalog", prefer, cgroup); ++ /* Avoid duplication of nextCatalog */ ++ while (prev != NULL) { ++ if ((prev->type == XML_CATA_NEXT_CATALOG) && ++ (xmlStrEqual (prev->URL, entry->URL)) && ++ (xmlStrEqual (prev->value, entry->value)) && ++ (prev->prefer == entry->prefer) && ++ (prev->group == entry->group)) { ++ if (xmlDebugCatalogs) ++ fprintf(stderr, ++ "Ignoring repeated nextCatalog %s\n", entry->URL); ++ xmlFreeCatalogEntry(entry, NULL); ++ entry = NULL; ++ break; ++ } ++ prev = prev->next; ++ } + } + if (entry != NULL) { + if (parent != NULL) { +-- +GitLab + + diff --git a/pkgs/development/libraries/libxml2/CVE-2025-6021.patch b/pkgs/development/libraries/libxml2/CVE-2025-6021.patch deleted file mode 100644 index 7d20a17c70381..0000000000000 --- a/pkgs/development/libraries/libxml2/CVE-2025-6021.patch +++ /dev/null @@ -1,40 +0,0 @@ -diff --git a/tree.c b/tree.c -index f097cf87..4d966ec9 100644 ---- a/tree.c -+++ b/tree.c -@@ -47,6 +47,10 @@ - #include "private/error.h" - #include "private/tree.h" - -+#ifndef SIZE_MAX -+ #define SIZE_MAX ((size_t) -1) -+#endif -+ - int __xmlRegisterCallbacks = 0; - - /************************************************************************ -@@ -167,10 +168,10 @@ xmlGetParameterEntityFromDtd(const xmlDtd *dtd, const xmlChar *name) { - xmlChar * - xmlBuildQName(const xmlChar *ncname, const xmlChar *prefix, - xmlChar *memory, int len) { -- int lenn, lenp; -+ size_t lenn, lenp; - xmlChar *ret; - -- if (ncname == NULL) return(NULL); -+ if ((ncname == NULL) || (len < 0)) return(NULL); - if (prefix == NULL) return((xmlChar *) ncname); - - #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -@@ -181,8 +182,10 @@ xmlBuildQName(const xmlChar *ncname, const xmlChar *prefix, - - lenn = strlen((char *) ncname); - lenp = strlen((char *) prefix); -+ if (lenn >= SIZE_MAX - lenp - 1) -+ return(NULL); - -- if ((memory == NULL) || (len < lenn + lenp + 2)) { -+ if ((memory == NULL) || ((size_t) len < lenn + lenp + 2)) { - ret = (xmlChar *) xmlMallocAtomic(lenn + lenp + 2); - if (ret == NULL) - return(NULL); diff --git a/pkgs/development/libraries/libxml2/CVE-2025-6170.patch b/pkgs/development/libraries/libxml2/CVE-2025-6170.patch deleted file mode 100644 index b66f24e305e0d..0000000000000 --- a/pkgs/development/libraries/libxml2/CVE-2025-6170.patch +++ /dev/null @@ -1,112 +0,0 @@ -diff --git a/result/scripts/long_command b/result/scripts/long_command -new file mode 100644 -index 000000000..e6f00708b ---- /dev/null -+++ b/result/scripts/long_command -@@ -0,0 +1,8 @@ -+/ > b > b > Object is a Node Set : -+Set contains 1 nodes: -+1 ELEMENT a:c -+b > Unknown command This_is_a_really_long_command_string_designed_to_test_the_limits_of_the_memory_that_stores_the_comm -+b > b > Unknown command ess_currents_of_time_and_existence -+b > -+Navigating_the_labyrinthine_corridors_of_human_cognition_one_often_encounters_the_perplexing_paradox_that_the_more_we_delve_into_the_intricate_dance_of_neural_pathways_and_synaptic_firings_the_further_we_seem_to_stray_from_a_truly_holistic_understanding_of_consciousness_a_phenomenon_that_remains_as_elusive_as_a_moonbeam_caught_in_a_spiderweb_yet_undeniably_shapes_every_fleeting_thought_every_prof -+b > -\ No newline at end of file -diff --git a/debugXML.c b/debugXML.c -index ed56b0f8..aeeea3c0 100644 ---- a/debugXML.c -+++ b/debugXML.c -@@ -2780,6 +2780,10 @@ xmlShellPwd(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED, char *buffer, - return (0); - } - -+#define MAX_PROMPT_SIZE 500 -+#define MAX_ARG_SIZE 400 -+#define MAX_COMMAND_SIZE 100 -+ - /** - * xmlShell: - * @doc: the initial document -@@ -2795,10 +2795,10 @@ void - xmlShell(xmlDocPtr doc, const char *filename, xmlShellReadlineFunc input, - FILE * output) - { -- char prompt[500] = "/ > "; -+ char prompt[MAX_PROMPT_SIZE] = "/ > "; - char *cmdline = NULL, *cur; -- char command[100]; -- char arg[400]; -+ char command[MAX_COMMAND_SIZE]; -+ char arg[MAX_ARG_SIZE]; - int i; - xmlShellCtxtPtr ctxt; - xmlXPathObjectPtr list; -@@ -2856,7 +2856,8 @@ xmlShell(xmlDocPtr doc, const char *filename, xmlShellReadlineFunc input, - cur++; - i = 0; - while ((*cur != ' ') && (*cur != '\t') && -- (*cur != '\n') && (*cur != '\r')) { -+ (*cur != '\n') && (*cur != '\r') && -+ (i < (MAX_COMMAND_SIZE - 1))) { - if (*cur == 0) - break; - command[i++] = *cur++; -@@ -2871,7 +2872,7 @@ xmlShell(xmlDocPtr doc, const char *filename, xmlShellReadlineFunc input, - while ((*cur == ' ') || (*cur == '\t')) - cur++; - i = 0; -- while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) { -+ while ((*cur != '\n') && (*cur != '\r') && (*cur != 0) && (i < (MAX_ARG_SIZE-1))) { - if (*cur == 0) - break; - arg[i++] = *cur++; -diff --git a/xmllint.c b/xmllint.c -index c6273477..3d90272c 100644 ---- a/xmllint.c -+++ b/xmllint.c -@@ -724,6 +724,9 @@ xmlHTMLValidityWarning(void *ctx, const char *msg, ...) - ************************************************************************/ - #ifdef LIBXML_DEBUG_ENABLED - #ifdef LIBXML_XPATH_ENABLED -+ -+#define MAX_PROMPT_SIZE 500 -+ - /** - * xmlShellReadline: - * @prompt: the prompt value -@@ -754,9 +754,9 @@ xmlShellReadline(char *prompt) { - if (prompt != NULL) - fprintf(stdout, "%s", prompt); - fflush(stdout); -- if (!fgets(line_read, 500, stdin)) -+ if (!fgets(line_read, MAX_PROMPT_SIZE, stdin)) - return(NULL); -- line_read[500] = 0; -+ line_read[MAX_PROMPT_SIZE] = 0; - len = strlen(line_read); - ret = (char *) malloc(len + 1); - if (ret != NULL) { --- -diff --git a/test/scripts/long_command.script b/test/scripts/long_command.script -new file mode 100644 -index 000000000..00f6df09f ---- /dev/null -+++ b/test/scripts/long_command.script -@@ -0,0 +1,6 @@ -+cd a/b -+set -+xpath //*[namespace-uri()="foo"] -+This_is_a_really_long_command_string_designed_to_test_the_limits_of_the_memory_that_stores_the_command_please_dont_crash foo -+set Navigating_the_labyrinthine_corridors_of_human_cognition_one_often_encounters_the_perplexing_paradox_that_the_more_we_delve_into_the_intricate_dance_of_neural_pathways_and_synaptic_firings_the_further_we_seem_to_stray_from_a_truly_holistic_understanding_of_consciousness_a_phenomenon_that_remains_as_elusive_as_a_moonbeam_caught_in_a_spiderweb_yet_undeniably_shapes_every_fleeting_thought_every_profound_emotion_and_every_grand_aspiration_that_propels_our_species_ever_onward_through_the_relentless_currents_of_time_and_existence -+save - -diff --git a/test/scripts/long_command.xml b/test/scripts/long_command.xml -new file mode 100644 -index 000000000..1ba44016e ---- /dev/null -+++ b/test/scripts/long_command.xml -@@ -0,0 +1 @@ -+ --- -GitLab - diff --git a/pkgs/development/libraries/libxml2/default.nix b/pkgs/development/libraries/libxml2/default.nix index 41d2b1f953f0d..be187c33ffc4c 100644 --- a/pkgs/development/libraries/libxml2/default.nix +++ b/pkgs/development/libraries/libxml2/default.nix @@ -8,39 +8,35 @@ let packages = { libxml2_13 = callPackage ./common.nix { - version = "2.13.8"; + version = "2.13.9"; src = fetchFromGitLab { domain = "gitlab.gnome.org"; owner = "GNOME"; repo = "libxml2"; tag = "v${packages.libxml2_13.version}"; - hash = "sha256-acemyYs1yRSTSLH7YCGxnQzrEDm8YPTK4HtisC36LsY="; + hash = "sha256-1qrgoMu702MglErNH9N2eCWFqxQnNHepR13m53GJb58="; }; extraPatches = [ - # same as upstream patch but fixed conflict and added required import: - # https://gitlab.gnome.org/GNOME/libxml2/-/commit/acbbeef9f5dcdcc901c5f3fa14d583ef8cfd22f0.diff - ./CVE-2025-6021.patch - (fetchpatch { - name = "CVE-2025-49794-49796.patch"; - url = "https://gitlab.gnome.org/GNOME/libxml2/-/commit/f7ebc65f05bffded58d1e1b2138eb124c2e44f21.patch"; - hash = "sha256-p5Vc/lkakHKsxuFNnCQtFczjqFJBeLnCwIwv2GnrQco="; - }) - (fetchpatch { - name = "CVE-2025-49795.patch"; - url = "https://gitlab.gnome.org/GNOME/libxml2/-/commit/c24909ba2601848825b49a60f988222da3019667.patch"; - hash = "sha256-vICVSb+X89TTE4QY92/v/6fRk77Hy9vzEWWsADHqMlk="; - excludes = [ "runtest.c" ]; # tests were rewritten in C and are on schematron for 2.13.x, meaning this does not apply - }) - # same as upstream, fixed conflicts - # https://gitlab.gnome.org/GNOME/libxml2/-/commit/c340e419505cf4bf1d9ed7019a87cc00ec200434 - ./CVE-2025-6170.patch - # Unmerged ABI-breaking patch required to fix the following security issues: # - https://gitlab.gnome.org/GNOME/libxslt/-/issues/139 # - https://gitlab.gnome.org/GNOME/libxslt/-/issues/140 # See also https://gitlab.gnome.org/GNOME/libxml2/-/issues/906 # Source: https://github.com/chromium/chromium/blob/4fb4ae8ce3daa399c3d8ca67f2dfb9deffcc7007/third_party/libxml/chromium/xml-attr-extra.patch ./xml-attr-extra.patch + + (fetchpatch { + name = "CVE-2026-0990.patch"; + url = "https://gitlab.gnome.org/GNOME/libxml2/-/commit/1961208e958ca22f80a0b4e4c9d71cfa050aa982.patch"; + hash = "sha256-Df2WLCTsP/ItSzgnVkNjRpLKkBP4xUOXEfCUV9o/Yks="; + }) + + # Based on https://gitlab.gnome.org/GNOME/libxml2/-/commit/f75abfcaa419a740a3191e56c60400f3ff18988d + # Vendored, because there is no xmlCatalogPrintDebug in 2.13.9, use fprintf instead + ./2.13-CVE-2026-0992.patch + + # Based on https://gitlab.gnome.org/GNOME/libxml2/-/commit/19549c61590c1873468c53e0026a2fbffae428ef.patch + # There are only whitespace differences from upstream. + ./2.13-CVE-2026-0989.patch ]; freezeUpdateScript = true; extraMeta = { @@ -58,6 +54,23 @@ let tag = "v${packages.libxml2.version}"; hash = "sha256-FUfYMq5xT2i88JdIw9OtSofraUL3yjsyOVund+mfJKQ="; }; + extraPatches = [ + (fetchpatch { + name = "CVE-2026-0990.patch"; + url = "https://gitlab.gnome.org/GNOME/libxml2/-/commit/1961208e958ca22f80a0b4e4c9d71cfa050aa982.patch"; + hash = "sha256-Df2WLCTsP/ItSzgnVkNjRpLKkBP4xUOXEfCUV9o/Yks="; + }) + (fetchpatch { + name = "CVE-2026-0992.patch"; + url = "https://gitlab.gnome.org/GNOME/libxml2/-/commit/f75abfcaa419a740a3191e56c60400f3ff18988d.patch"; + hash = "sha256-an/PpmsncNwim/pe/zsDx9P6jbuCqvr4QSgTokyDvzw="; + }) + (fetchpatch { + name = "CVE-2026-0989.patch"; + url = "https://gitlab.gnome.org/GNOME/libxml2/-/commit/19549c61590c1873468c53e0026a2fbffae428ef.patch"; + hash = "sha256-YOF5cYdoFQuvsbcAezamglh/RKv8Zq6rm1nfo2/CLzo="; + }) + ]; extraMeta = { maintainers = with lib.maintainers; [ jtojnar diff --git a/pkgs/development/libraries/openssl/3.5/use-etc-ssl-certs-darwin.patch b/pkgs/development/libraries/openssl/3.5/use-etc-ssl-certs-darwin.patch deleted file mode 100644 index e8b07b4ae5990..0000000000000 --- a/pkgs/development/libraries/openssl/3.5/use-etc-ssl-certs-darwin.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/include/internal/common.h b/include/internal/common.h -index 15666f1..d91e25b 100644 ---- a/include/internal/common.h -+++ b/include/internal/common.h -@@ -83,7 +83,7 @@ __owur static ossl_inline int ossl_assert_int(int expr, const char *exprstr, - # ifndef OPENSSL_SYS_VMS - # define X509_CERT_AREA OPENSSLDIR - # define X509_CERT_DIR OPENSSLDIR "/certs" --# define X509_CERT_FILE OPENSSLDIR "/cert.pem" -+# define X509_CERT_FILE "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt" - # define X509_PRIVATE_DIR OPENSSLDIR "/private" - # define CTLOG_FILE OPENSSLDIR "/ct_log_list.cnf" - # else diff --git a/pkgs/development/libraries/openssl/3.5/use-etc-ssl-certs.patch b/pkgs/development/libraries/openssl/3.5/use-etc-ssl-certs.patch deleted file mode 100644 index 13a36fbcd0314..0000000000000 --- a/pkgs/development/libraries/openssl/3.5/use-etc-ssl-certs.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/include/internal/common.h b/include/internal/common.h -index 15666f1..d91e25b 100644 ---- a/include/internal/common.h -+++ b/include/internal/common.h -@@ -83,7 +83,7 @@ __owur static ossl_inline int ossl_assert_int(int expr, const char *exprstr, - # ifndef OPENSSL_SYS_VMS - # define X509_CERT_AREA OPENSSLDIR - # define X509_CERT_DIR OPENSSLDIR "/certs" --# define X509_CERT_FILE OPENSSLDIR "/cert.pem" -+# define X509_CERT_FILE "/etc/ssl/certs/ca-certificates.crt" - # define X509_PRIVATE_DIR OPENSSLDIR "/private" - # define CTLOG_FILE OPENSSLDIR "/ct_log_list.cnf" - # else diff --git a/pkgs/development/libraries/openssl/3.6/use-etc-ssl-certs-darwin.patch b/pkgs/development/libraries/openssl/3.6/use-etc-ssl-certs-darwin.patch new file mode 100644 index 0000000000000..bb2d048542cba --- /dev/null +++ b/pkgs/development/libraries/openssl/3.6/use-etc-ssl-certs-darwin.patch @@ -0,0 +1,13 @@ +diff --git a/include/internal/common.h b/include/internal/common.h +index 15666f1..d91e25b 100644 +--- a/include/internal/common.h ++++ b/include/internal/common.h +@@ -83,7 +83,7 @@ __owur static ossl_inline int ossl_assert_int(int expr, const char *exprstr, + #ifndef OPENSSL_SYS_VMS + #define X509_CERT_AREA OPENSSLDIR + #define X509_CERT_DIR OPENSSLDIR "/certs" +-#define X509_CERT_FILE OPENSSLDIR "/cert.pem" ++#define X509_CERT_FILE "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt" + #define X509_PRIVATE_DIR OPENSSLDIR "/private" + #define CTLOG_FILE OPENSSLDIR "/ct_log_list.cnf" + #else diff --git a/pkgs/development/libraries/openssl/3.6/use-etc-ssl-certs.patch b/pkgs/development/libraries/openssl/3.6/use-etc-ssl-certs.patch new file mode 100644 index 0000000000000..e0fb994161deb --- /dev/null +++ b/pkgs/development/libraries/openssl/3.6/use-etc-ssl-certs.patch @@ -0,0 +1,13 @@ +diff --git a/include/internal/common.h b/include/internal/common.h +index 15666f1..d91e25b 100644 +--- a/include/internal/common.h ++++ b/include/internal/common.h +@@ -83,7 +83,7 @@ __owur static ossl_inline int ossl_assert_int(int expr, const char *exprstr, + #ifndef OPENSSL_SYS_VMS + #define X509_CERT_AREA OPENSSLDIR + #define X509_CERT_DIR OPENSSLDIR "/certs" +-#define X509_CERT_FILE OPENSSLDIR "/cert.pem" ++#define X509_CERT_FILE "/etc/ssl/certs/ca-certificates.crt" + #define X509_PRIVATE_DIR OPENSSLDIR "/private" + #define CTLOG_FILE OPENSSLDIR "/ct_log_list.cnf" + #else diff --git a/pkgs/development/libraries/openssl/default.nix b/pkgs/development/libraries/openssl/default.nix index f26407dbe1b0e..09890abc48ff5 100644 --- a/pkgs/development/libraries/openssl/default.nix +++ b/pkgs/development/libraries/openssl/default.nix @@ -452,8 +452,8 @@ in }; openssl_3_6 = common { - version = "3.6.0"; - hash = "sha256-tqX0S362nj+jXb8VUkQFtEg3pIHUPYHa3d4/8h/LuOk="; + version = "3.6.1"; + hash = "sha256-sb/tzVson/Iq7ofJ1gD1FXZ+v0X3cWjLbWTyMfUYqC4="; patches = [ # Support for NIX_SSL_CERT_FILE, motivation: @@ -467,9 +467,9 @@ in # Look up SSL certificates in /etc rather than the immutable installation directory ( if stdenv.hostPlatform.isDarwin then - ./3.5/use-etc-ssl-certs-darwin.patch + ./3.6/use-etc-ssl-certs-darwin.patch else - ./3.5/use-etc-ssl-certs.patch + ./3.6/use-etc-ssl-certs.patch ) ] ++ lib.optionals stdenv.hostPlatform.isMinGW [ diff --git a/pkgs/development/python-modules/aiohttp/default.nix b/pkgs/development/python-modules/aiohttp/default.nix index 2706b731c80a8..a3beffd1a863d 100644 --- a/pkgs/development/python-modules/aiohttp/default.nix +++ b/pkgs/development/python-modules/aiohttp/default.nix @@ -51,14 +51,14 @@ buildPythonPackage rec { pname = "aiohttp"; - version = "3.13.2"; + version = "3.13.3"; pyproject = true; src = fetchFromGitHub { owner = "aio-libs"; repo = "aiohttp"; tag = "v${version}"; - hash = "sha256-LqYGrrWgSZazk0hjQvTFwqtU/PtMEaPi+m1Ya8Ds+pU="; + hash = "sha256-V+digrfigdA3hwd2xW47BACh3r07j6pGE8WFAGivZnA="; }; postPatch = '' diff --git a/pkgs/development/python-modules/bleach/default.nix b/pkgs/development/python-modules/bleach/default.nix index b9f3dd18cf7e6..4c7c9a8a55a70 100644 --- a/pkgs/development/python-modules/bleach/default.nix +++ b/pkgs/development/python-modules/bleach/default.nix @@ -29,6 +29,8 @@ buildPythonPackage rec { webencodings ]; + pythonRelaxDeps = [ "tinycss2" ]; + optional-dependencies = { css = [ tinycss2 ]; }; diff --git a/pkgs/development/python-modules/brotlicffi/default.nix b/pkgs/development/python-modules/brotlicffi/default.nix index 7f7171a6ebb99..618bffa999336 100644 --- a/pkgs/development/python-modules/brotlicffi/default.nix +++ b/pkgs/development/python-modules/brotlicffi/default.nix @@ -2,7 +2,6 @@ lib, fetchFromGitHub, buildPythonPackage, - pythonOlder, cffi, # overridden as pkgs.brotli brotli, @@ -13,15 +12,14 @@ buildPythonPackage rec { pname = "brotlicffi"; - version = "1.1.0.0"; + version = "1.2.0.0"; pyproject = true; - disabled = pythonOlder "3.7"; src = fetchFromGitHub { owner = "python-hyper"; repo = "brotlicffi"; - rev = "v${version}"; - sha256 = "sha256-oW4y1WBJ7+4XwNwwSSR0qUqN03cZYXUYQ6EAwce9dzI="; + tag = "v${version}"; + hash = "sha256-3/68qBfsFtH+7h3gPxUdkyHwG6qLbh+bVLrxzsb3bc4="; }; build-system = [ setuptools ]; @@ -49,6 +47,7 @@ buildPythonPackage rec { pythonImportsCheck = [ "brotlicffi" ]; meta = { + changelog = "https://github.com/python-hyper/brotlicffi/blob/${src.tag}/HISTORY.rst"; description = "Python CFFI bindings to the Brotli library"; homepage = "https://github.com/python-hyper/brotlicffi"; license = lib.licenses.mit; diff --git a/pkgs/development/python-modules/django/4.nix b/pkgs/development/python-modules/django/4.nix index 8438091346f9a..0688f35b9855c 100644 --- a/pkgs/development/python-modules/django/4.nix +++ b/pkgs/development/python-modules/django/4.nix @@ -45,16 +45,16 @@ buildPythonPackage rec { pname = "django"; - version = "4.2.27"; - format = "pyproject"; + version = "4.2.28"; + pyproject = true; disabled = pythonOlder "3.8"; src = fetchFromGitHub { owner = "django"; repo = "django"; - rev = "refs/tags/${version}"; - hash = "sha256-vdY85Ib2knRFLPmZZ6ojiD5R9diuvpVut1+nOVXSp0Y="; + tag = version; + hash = "sha256-dxlTonT8zFIFGFrrOW4GFKOy5b0chcESxt9i9xHO8h4="; }; patches = [ diff --git a/pkgs/development/python-modules/filelock/default.nix b/pkgs/development/python-modules/filelock/default.nix index 760286a19faf8..8b4baa9283689 100644 --- a/pkgs/development/python-modules/filelock/default.nix +++ b/pkgs/development/python-modules/filelock/default.nix @@ -12,12 +12,12 @@ buildPythonPackage rec { pname = "filelock"; - version = "3.20.1"; + version = "3.20.3"; pyproject = true; src = fetchPypi { inherit pname version; - hash = "sha256-uDYJSLNRuA9CCHjYUWUZoiBLB6783P0kkSpdMxJ/GIw="; + hash = "sha256-GMV+6RXH7GHP8Oz38Phpk2x8MBkbsM9AbxNBd40INOE="; }; build-system = [ diff --git a/pkgs/development/python-modules/jupyter-server/default.nix b/pkgs/development/python-modules/jupyter-server/default.nix index ae7d77d47edac..41f803ad4ab96 100644 --- a/pkgs/development/python-modules/jupyter-server/default.nix +++ b/pkgs/development/python-modules/jupyter-server/default.nix @@ -1,6 +1,5 @@ { lib, - pythonOlder, stdenv, buildPythonPackage, fetchPypi, @@ -35,14 +34,13 @@ buildPythonPackage rec { pname = "jupyter-server"; - version = "2.16.0"; + version = "2.17.0"; pyproject = true; - disabled = pythonOlder "3.9"; src = fetchPypi { pname = "jupyter_server"; inherit version; - hash = "sha256-ZdS0T98ty73+CqGs5KhC1Kr3RqK3sWgTTVqu01Yht/Y="; + hash = "sha256-w46omFZpZMiItHcq4e1Y7KhFkuiCUdLPxNFx+B9+mdU="; }; build-system = [ diff --git a/pkgs/development/python-modules/pillow/default.nix b/pkgs/development/python-modules/pillow/default.nix index 3fc623ce4eb9f..3b301afdc59b0 100644 --- a/pkgs/development/python-modules/pillow/default.nix +++ b/pkgs/development/python-modules/pillow/default.nix @@ -44,14 +44,14 @@ buildPythonPackage rec { pname = "pillow"; - version = "12.0.0"; + version = "12.1.0"; pyproject = true; src = fetchFromGitHub { owner = "python-pillow"; repo = "pillow"; tag = version; - hash = "sha256-58mjwHErEZPkkGBVZznkkMQN5Zo4ZBBiXnhqVp1F81g="; + hash = "sha256-QGtuxKpkx2FScQHj9lH4mhEAo6jE+NAR2sR5/zvHUuA="; }; build-system = [ diff --git a/pkgs/development/python-modules/pyasn1/default.nix b/pkgs/development/python-modules/pyasn1/default.nix index 727e107b15a5b..93c89ff6be20e 100644 --- a/pkgs/development/python-modules/pyasn1/default.nix +++ b/pkgs/development/python-modules/pyasn1/default.nix @@ -8,14 +8,14 @@ buildPythonPackage rec { pname = "pyasn1"; - version = "0.6.1"; + version = "0.6.2"; pyproject = true; disabled = pythonOlder "3.7"; src = fetchPypi { inherit pname version; - hash = "sha256-b1gNK92ENlOAgwrPRVUPJRFGn2c8tKWuOFejFwEosDQ="; + hash = "sha256-m1mislun5PgZfbdobAn7M+ZYuYM5+tuCbpUSYpAXgzs="; }; nativeBuildInputs = [ setuptools ]; diff --git a/pkgs/development/python-modules/pycdio/default.nix b/pkgs/development/python-modules/pycdio/default.nix index fe191d948abd8..c58f2ae568395 100644 --- a/pkgs/development/python-modules/pycdio/default.nix +++ b/pkgs/development/python-modules/pycdio/default.nix @@ -39,10 +39,21 @@ buildPythonPackage rec { libiconv ]; + postPatch = '' + substituteInPlace {data,test}/isofs-m1.cue \ + --replace-fail "ISOFS-M1.BIN" "isofs-m1.bin" + ''; + nativeCheckInputs = [ pytestCheckHook ]; enabledTestPaths = [ "test/test-*.py" ]; + disabledTests = [ + # Test are depending on image files that are not there + "test_bincue" + "test_cdda" + ]; + passthru.updateScript = nix-update-script { extraArgs = [ "--version=branch" ]; }; diff --git a/pkgs/development/python-modules/pynacl/default.nix b/pkgs/development/python-modules/pynacl/default.nix index 11a60e232336a..1606ab44db30f 100644 --- a/pkgs/development/python-modules/pynacl/default.nix +++ b/pkgs/development/python-modules/pynacl/default.nix @@ -14,7 +14,7 @@ buildPythonPackage rec { pname = "pynacl"; - version = "1.6.0"; + version = "1.6.2"; outputs = [ "out" "doc" @@ -27,7 +27,7 @@ buildPythonPackage rec { owner = "pyca"; repo = "pynacl"; tag = version; - hash = "sha256-7SDJB2bXn0IGJQi597yehs9epdfmS7slbQ97vFVUkEA="; + hash = "sha256-EzzJVRDgYQO6T8YIQjad/Eb9O+BXT4IpOpa48fpBPnc="; }; build-system = [ diff --git a/pkgs/development/python-modules/pypdf/default.nix b/pkgs/development/python-modules/pypdf/default.nix index 7bfb42ce7e3ee..b112a45a363ac 100644 --- a/pkgs/development/python-modules/pypdf/default.nix +++ b/pkgs/development/python-modules/pypdf/default.nix @@ -27,7 +27,7 @@ buildPythonPackage rec { pname = "pypdf"; - version = "6.1.0"; + version = "6.6.0"; pyproject = true; disabled = pythonOlder "3.8"; @@ -38,7 +38,7 @@ buildPythonPackage rec { tag = version; # fetch sample files used in tests fetchSubmodules = true; - hash = "sha256-qfLN6g2+3j35E4m9vGcWXL1BLiFdDZEFmxYgnknlW3M="; + hash = "sha256-C1ZFqqLFtxWOuLUT7KFSfWIE6a9xDPCFtOakzYP4NMY="; }; outputs = [ diff --git a/pkgs/development/python-modules/term-image/default.nix b/pkgs/development/python-modules/term-image/default.nix index 04f5070108d4b..c69ac82809031 100644 --- a/pkgs/development/python-modules/term-image/default.nix +++ b/pkgs/development/python-modules/term-image/default.nix @@ -21,6 +21,12 @@ buildPythonPackage rec { hash = "sha256-uA04KHKLXW0lx1y5brpCDARLac4/C8VmVinVMkEtTdM="; }; + # Override the overly strict `tool.pytest.ini_options.filterwarnings` + postPatch = '' + substituteInPlace pyproject.toml \ + --replace-fail '"error"' '#"error"' + ''; + build-system = [ setuptools ]; diff --git a/pkgs/development/python-modules/tinycss2/default.nix b/pkgs/development/python-modules/tinycss2/default.nix index 6a7129ad759e9..cc249b85ffde9 100644 --- a/pkgs/development/python-modules/tinycss2/default.nix +++ b/pkgs/development/python-modules/tinycss2/default.nix @@ -11,7 +11,7 @@ buildPythonPackage rec { pname = "tinycss2"; - version = "1.4.0"; + version = "1.5.1"; pyproject = true; disabled = pythonOlder "3.8"; @@ -22,7 +22,7 @@ buildPythonPackage rec { tag = "v${version}"; # for tests fetchSubmodules = true; - hash = "sha256-GVymUobWAE0YS65lti9dXRIIGpx0YkwF/vSb3y7cpYY="; + hash = "sha256-ZVmdHrqfF5fvBvHLaG2B4m1zek4wfEYArkntWzOqhfM="; }; build-system = [ flit-core ]; diff --git a/pkgs/development/python-modules/weasyprint/default.nix b/pkgs/development/python-modules/weasyprint/default.nix index 47c6a12e51fb5..74e63ac721029 100644 --- a/pkgs/development/python-modules/weasyprint/default.nix +++ b/pkgs/development/python-modules/weasyprint/default.nix @@ -32,7 +32,7 @@ buildPythonPackage rec { pname = "weasyprint"; - version = "66.0"; + version = "68.0"; pyproject = true; __darwinAllowLocalNetworking = true; @@ -41,7 +41,7 @@ buildPythonPackage rec { owner = "Kozea"; repo = "WeasyPrint"; tag = "v${version}"; - hash = "sha256-wmEDVEbikBpOQ5394IBPWQRjWZOLfMzEGxTtq4tt2Tw="; + hash = "sha256-kAJgSQz1RKrPwzO7I5xHXyXcXYJtvca9izjrAgTy3ek="; }; patches = [ @@ -98,6 +98,10 @@ buildPythonPackage rec { "test_visibility_3" "test_visibility_4" "test_woff_simple" + # AssertionError + "test_2d_transform" + # Reported upstream: https://github.com/Kozea/WeasyPrint/issues/2666 + "test_text_stroke" ]; FONTCONFIG_FILE = "${fontconfig.out}/etc/fonts/fonts.conf"; diff --git a/pkgs/development/python-modules/weasyprint/library-paths.patch b/pkgs/development/python-modules/weasyprint/library-paths.patch index dd2bbfe784e05..6053fbdc1ef0a 100644 --- a/pkgs/development/python-modules/weasyprint/library-paths.patch +++ b/pkgs/development/python-modules/weasyprint/library-paths.patch @@ -1,14 +1,14 @@ diff --git a/weasyprint/text/ffi.py b/weasyprint/text/ffi.py -index b5a9cf2..9380a79 100644 +index f57da3c..c50e41d 100644 --- a/weasyprint/text/ffi.py +++ b/weasyprint/text/ffi.py -@@ -444,25 +444,12 @@ if hasattr(os, 'add_dll_directory') and not hasattr(sys, 'frozen'): # pragma: n +@@ -473,25 +473,12 @@ if hasattr(os, 'add_dll_directory') and not hasattr(sys, 'frozen'): # pragma: n with suppress((OSError, FileNotFoundError)): os.add_dll_directory(dll_directory) -gobject = _dlopen( - ffi, 'libgobject-2.0-0', 'gobject-2.0-0', 'gobject-2.0', -- 'libgobject-2.0.so.0', 'libgobject-2.0.dylib', 'libgobject-2.0-0.dll') +- 'libgobject-2.0.so.0', 'libgobject-2.0.0.dylib', 'libgobject-2.0-0.dll') -pango = _dlopen( - ffi, 'libpango-1.0-0', 'pango-1.0-0', 'pango-1.0', 'libpango-1.0.so.0', - 'libpango-1.0.dylib', 'libpango-1.0-0.dll') @@ -25,12 +25,12 @@ index b5a9cf2..9380a79 100644 -pangoft2 = _dlopen( - ffi, 'libpangoft2-1.0-0', 'pangoft2-1.0-0', 'pangoft2-1.0', - 'libpangoft2-1.0.so.0', 'libpangoft2-1.0.dylib', 'libpangoft2-1.0-0.dll') -+gobject = _dlopen(ffi, "@gobject@") -+pango = _dlopen(ffi, "@pango@") -+harfbuzz = _dlopen(ffi, "@harfbuzz@") -+harfbuzz_subset = _dlopen(ffi, "@harfbuzz_subset@", allow_fail=True) -+fontconfig = _dlopen(ffi, "@fontconfig@") -+pangoft2 = _dlopen(ffi, "@pangoft2@") ++gobject = _dlopen(ffi, '@gobject@') ++pango = _dlopen(ffi, '@pango@') ++harfbuzz = _dlopen(ffi, '@harfbuzz@') ++harfbuzz_subset = _dlopen(ffi, '@harfbuzz_subset@', allow_fail=True) ++fontconfig = _dlopen(ffi, '@fontconfig@') ++pangoft2 = _dlopen(ffi, '@pangoft2@') gobject.g_type_init() diff --git a/pkgs/development/web/nodejs/v22.nix b/pkgs/development/web/nodejs/v22.nix index a9be2fdad4b36..cfc4f014fa1df 100644 --- a/pkgs/development/web/nodejs/v22.nix +++ b/pkgs/development/web/nodejs/v22.nix @@ -23,8 +23,8 @@ let in buildNodejs { inherit enableNpm; - version = "22.21.1"; - sha256 = "487d73fd4db00dc2420d659a8221b181a7937fbc5bc73f31c30b1680ad6ded6a"; + version = "22.22.0"; + sha256 = "4c138012bb5352f49822a8f3e6d1db71e00639d0c36d5b6756f91e4c6f30b683"; patches = ( if (stdenv.hostPlatform.emulatorAvailable buildPackages) then diff --git a/pkgs/os-specific/linux/systemd/0007-Change-usr-share-zoneinfo-to-etc-zoneinfo.patch b/pkgs/os-specific/linux/systemd/0007-Change-usr-share-zoneinfo-to-etc-zoneinfo.patch index 21589efdd8494..254a63c1a1072 100644 --- a/pkgs/os-specific/linux/systemd/0007-Change-usr-share-zoneinfo-to-etc-zoneinfo.patch +++ b/pkgs/os-specific/linux/systemd/0007-Change-usr-share-zoneinfo-to-etc-zoneinfo.patch @@ -70,7 +70,7 @@ index 55931a2546..2e00bd7539 100644 return r; /* Return EINVAL if not a symlink */ - const char *e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/"); -+ const char *e = PATH_STARTSWITH_SET(t, "/etc/zoneinfo/", "../etc/zoneinfo/"); ++ const char *e = PATH_STARTSWITH_SET(t, "/etc/zoneinfo/", "../etc/zoneinfo/", "zoneinfo/"); if (!e) return -EINVAL; if (!timezone_is_valid(e, LOG_DEBUG)) @@ -112,7 +112,7 @@ index 480a9c55c6..02c9ae6608 100644 path, - "../usr/share/zoneinfo/", - "/usr/share/zoneinfo/"); -+ "../etc/zoneinfo/", ++ "../etc/zoneinfo/", "zoneinfo/", + "/etc/zoneinfo/"); } diff --git a/pkgs/os-specific/linux/systemd/default.nix b/pkgs/os-specific/linux/systemd/default.nix index 49710f5123c56..c1dac94276505 100644 --- a/pkgs/os-specific/linux/systemd/default.nix +++ b/pkgs/os-specific/linux/systemd/default.nix @@ -207,13 +207,13 @@ let in stdenv.mkDerivation (finalAttrs: { inherit pname; - version = "258.2"; + version = "258.3"; src = fetchFromGitHub { owner = "systemd"; repo = "systemd"; rev = "v${finalAttrs.version}"; - hash = "sha256-1iWeuNefDOIEUSTzxzvt+jfcs6sSMPhxQfdwp0mqUjQ="; + hash = "sha256-wpg/0z7xrB8ysPaa/zNp1mz+yYRCGyXz0ODZcKapovM="; }; # On major changes, or when otherwise required, you *must* : @@ -956,7 +956,6 @@ stdenv.mkDerivation (finalAttrs: { systemd-initrd-luks-empty-passphrase systemd-initrd-luks-password systemd-initrd-luks-tpm2 - systemd-initrd-luks-unl0kr systemd-initrd-modprobe systemd-initrd-shutdown systemd-initrd-simple diff --git a/pkgs/servers/x11/xorg/default.nix b/pkgs/servers/x11/xorg/default.nix index c13e999194b3c..2d214e1dfb599 100644 --- a/pkgs/servers/x11/xorg/default.nix +++ b/pkgs/servers/x11/xorg/default.nix @@ -3051,11 +3051,11 @@ self: with self; { }: stdenv.mkDerivation (finalAttrs: { pname = "xorg-server"; - version = "21.1.20"; + version = "21.1.21"; builder = ./builder.sh; src = fetchurl { - url = "mirror://xorg/individual/xserver/xorg-server-21.1.20.tar.xz"; - sha256 = "sha256-dpW8YYJLOoG2utL3iwVADKAVAD3kAtGzIhFxBbcC6Tc="; + url = "mirror://xorg/individual/xserver/xorg-server-21.1.21.tar.xz"; + sha256 = "sha256-wMvlVFs/ZFuuYCS4MNHRFUqVY1BoOk5Ssv/1sPoatRk="; }; hardeningDisable = [ "bindnow" diff --git a/pkgs/tools/misc/grub/default.nix b/pkgs/tools/misc/grub/default.nix index 2cbdd44167842..51456e61b4c52 100644 --- a/pkgs/tools/misc/grub/default.nix +++ b/pkgs/tools/misc/grub/default.nix @@ -510,6 +510,47 @@ stdenv.mkDerivation rec { url = "https://git.savannah.gnu.org/cgit/grub.git/patch/?id=c448f511e74cb7c776b314fcb7943f98d3f22b6d"; hash = "sha256-64gMhCEW0aYHt46crX/qN/3Hj8MgvWLazgQlVXqe8LE="; }) + # https://lists.gnu.org/archive/html/grub-devel/2025-11/msg00155.html + (fetchpatch { + name = "1_commands_test_fix_error_in_recursion_depth_calculation.patch"; + url = "https://git.savannah.gnu.org/cgit/grub.git/patch/?id=cc9d621dd06bfa12eac511b37b4ceda5bd2f8246"; + hash = "sha256-GpLpqTKr2ke/YaxnZIO1Kh9wpde44h2mvwcODcAL/nk="; + }) + (fetchpatch { + name = "2_CVE-2025-54771.patch"; + url = "https://git.savannah.gnu.org/cgit/grub.git/patch/?id=c4fb4cbc941981894a00ba8e75d634a41967a27f"; + hash = "sha256-yWowlAMVXdfIyC+BiB00IZvTwIybvaPhxAyz0MPjQuY="; + }) + (fetchpatch { + name = "3_CVE-2025-54770.patch"; + url = "https://git.savannah.gnu.org/cgit/grub.git/patch/?id=10e58a14db20e17d1b6a39abe38df01fef98e29d"; + hash = "sha256-1ROc5n7sApw7aGr+y8gygFqVkifLdgOD3RPaW9b8aQQ="; + }) + (fetchpatch { + name = "4_CVE-2025-61662.patch"; + url = "https://git.savannah.gnu.org/cgit/grub.git/patch/?id=8ed78fd9f0852ab218cc1f991c38e5a229e43807"; + hash = "sha256-mG+vcZHbF4duY2YoYAzPBQRHfWvp5Fvgtm0XBk7JqqM="; + }) + (fetchpatch { + name = "5_CVE-2025-61663_CVE-2025-61664.patch"; + url = "https://git.savannah.gnu.org/cgit/grub.git/patch/?id=05d3698b8b03eccc49e53491bbd75dba15f40917"; + hash = "sha256-kgtXhZmAQpassEf8+RzqkghAzLrCcRoRMMnfunF/0J8="; + }) + (fetchpatch { + name = "6_tests_lib_functional_test_unregister_commands_on_module_unload.patch"; + url = "https://git.savannah.gnu.org/cgit/grub.git/patch/?id=9df1e693e70c5a274b6d60dc76efe2694b89c2fc"; + hash = "sha256-UzyYkpP7vivx2jzxi7BMP9h9OB2yraswrMW4g9UWsbI="; + }) + (fetchpatch { + name = "7_CVE-2025-61661.patch"; + url = "https://git.savannah.gnu.org/cgit/grub.git/patch/?id=549a9cc372fd0b96a4ccdfad0e12140476cc62a3"; + hash = "sha256-2mlDoVXY7Upwx4QBeAMOHUtoUlyx1MDDmabnrwK1gEY="; + }) + (fetchpatch { + name = "8_commands_usbtest_ensure_string_length_is_sufficient_in_usb_string_processing.patch"; + url = "https://git.savannah.gnu.org/cgit/grub.git/patch/?id=7debdce1e98907e65223a4b4c53a41345ac45e53"; + hash = "sha256-2ALvrmwxvpjQYjGNrQ0gyGotpk0kgmYlJXMF1xXrnEw="; + }) ]; postPatch = diff --git a/pkgs/tools/typesetting/tex/texlive/bin.nix b/pkgs/tools/typesetting/tex/texlive/bin.nix index 48b22d07c1447..7608cdd8e7134 100644 --- a/pkgs/tools/typesetting/tex/texlive/bin.nix +++ b/pkgs/tools/typesetting/tex/texlive/bin.nix @@ -346,6 +346,9 @@ rec { + '' rm "$out"/bin/{eptex,ptex,uptex} rm "${placeholder "ptex"}"/bin/{pbibtex,pdvitype,ppltotf,ptftopl} + '' + + lib.optionalString (stdenv.system == "aarch64-darwin") '' + # TMP: just cause a rebuild ''; passthru = { inherit version buildInputs; };