[PATCH] powerpc: Restore NULL check on _rtld_global_ro in INIT_ARCH [BZ #34503]

Michael Pfeifroth micpf@westermo.com
Tue Aug 11 13:28:24 GMT 2026


On 11/08/26 08:50, Adhemerval Zanella Netto wrote:
> The patch looks good, but maybe it would be good to add a regression testcase
> for that.  [...]

Thanks for the review.  v2 adds the test as suggested — ppc64 only as
you noted.

The module (tst-ppc64-ifunc-bind-now-mod.so) is linked with -z,now and
has no DT_NEEDED on libm; the main binary has DT_NEEDED on libm so modf
resolves correctly at runtime.  The test simply calls ifunc_ptr(2.5,
&ip) and checks the result: without the fix the process would segfault
before reaching the check.

-- >8 --
>From 0000000000000000000000000000000000000002 Mon Sep 17 00:00:00 2001
From: Michael Pfeifroth <micpf@westermo.com>
Date: Mon, 11 Aug 2026 14:30:00 +0200
Subject: [PATCH v2] powerpc: Restore NULL check on _rtld_global_ro in
 INIT_ARCH [BZ #34503]

Commit 21841f0d562f ("PowerPC: Influence cpu/arch hwcap features via
GLIBC_TUNABLES") changed the INIT_ARCH() macro used by powerpc32/power4
and (via a one-line include) powerpc64 multiarch IFUNC resolvers to
read hwcap and hwcap2 through a direct

    &GLRO(dl_powerpc_cpu_features)

reference, instead of the previous __GLRO() wrapper.  The __GLRO() macro
performs a volatile NULL check on _rtld_global_ro, which matters because
IFUNC resolvers can run before _rtld_global_ro has been relocated for the
current library.

This regression triggers when a shared library's IFUNC symbol from libm
is resolved via BIND_NOW (full RELRO) before libm's own GOT is relocated:
the resolver's INIT_ARCH() then dereferences a NULL _rtld_global_ro and
segfaults at the hwcap load.  The concrete failure seen was rsyslogd
crashing on startup on powerpc64 (e5500, BE) with

    rsyslogd -> librsyslog -> libfastjson -> modf() IFUNC in libm

when libfastjson lacked a DT_NEEDED on libm, so libm was relocated after
libfastjson's IFUNC resolvers ran.

Restore the __GLRO()-based access for both hwcap and hwcap2, matching
the pre-2.41 behaviour and how use_cached_memopt is already read in the
same macro.  This is a no-op once _rtld_global_ro is fully initialised
and simply reinstates the early-startup NULL guard.

Add a regression test (ppc64 only; ppc32 has additional early-startup
constraints that make the same test infeasible there).  The module is
linked with -z,now and intentionally has no DT_NEEDED on libm, so the
IFUNC resolver for modf() runs before libm is fully relocated.

Signed-off-by: Michael Pfeifroth <micpf@westermo.com>
---
 sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h      |  6 ++--
 sysdeps/powerpc/powerpc64/Makefile                          | 13 +++++++
 sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c   | 28 +++++++++++++++
 sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c       | 45 +++++++++++++++++++++++++
 4 files changed, 89 insertions(+), 3 deletions(-)
 create mode 100644 sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c
 create mode 100644 sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c

diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
index 9aab67a5..e681b452 100644
--- a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
+++ b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
@@ -36,9 +36,9 @@
 /* Get the hardware information post the tunables set, the macro checks
    it and fills the previous ones.  */
 #define INIT_ARCH() \
-  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);	\
-  unsigned long int hwcap = features->hwcap;				\
-  unsigned long int __attribute__((unused)) hwcap2 = features->hwcap2; \
+  unsigned long int hwcap = __GLRO(dl_powerpc_cpu_features.hwcap);	\
+  unsigned long int __attribute__((unused)) hwcap2 =			\
+    __GLRO(dl_powerpc_cpu_features.hwcap2);				\
   bool __attribute__((unused)) use_cached_memopt =		\
     __GLRO(dl_powerpc_cpu_features.use_cached_memopt);		\
   if (hwcap & PPC_FEATURE_ARCH_2_06)				\

diff --git a/sysdeps/powerpc/powerpc64/Makefile b/sysdeps/powerpc/powerpc64/Makefile
index 12345678..abcdefab 100644
--- a/sysdeps/powerpc/powerpc64/Makefile
+++ b/sysdeps/powerpc/powerpc64/Makefile
@@ -42,6 +42,19 @@ CFLAGS-rtld-strnlen.os = $(no-special-regs)
 ifeq ($(subdir),elf)
 # help gcc inline asm code from dl-machine.h
 +cflags += -finline-limit=2000
+
+# Regression test for BZ #34503: IFUNC resolver crash with BIND_NOW when
+# _rtld_global_ro has not been relocated yet in the calling library's GOT.
+# Restricted to ppc64; ppc32 has additional early-startup constraints.
+tests += tst-ppc64-ifunc-bind-now
+modules-names += tst-ppc64-ifunc-bind-now-mod
+tst-ppc64-ifunc-bind-now-mod.so-no-z-defs = yes
+LDFLAGS-tst-ppc64-ifunc-bind-now-mod.so = -Wl,-z,now
+$(objpfx)tst-ppc64-ifunc-bind-now: $(objpfx)tst-ppc64-ifunc-bind-now-mod.so
+LDLIBS-tst-ppc64-ifunc-bind-now = -Wl,--no-as-needed -lm \
+  $(objpfx)tst-ppc64-ifunc-bind-now-mod.so
 endif

diff --git a/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c b/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c
new file mode 100644
index 00000000..11111111
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now-mod.c
@@ -0,0 +1,28 @@
+/* Shared library module for tst-ppc64-ifunc-bind-now.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* Reference modf without a DT_NEEDED on libm.  This module is linked
+   with -z,now so its BIND_NOW IFUNC relocation for modf() runs as early
+   as possible during dynamic linking -- before _rtld_global_ro has been
+   relocated in this library's GOT context.  Tests that INIT_ARCH()
+   handles a NULL _rtld_global_ro gracefully (BZ #34503).  */
+
+extern double modf (double, double *);
+
+double (*volatile ifunc_ptr) (double, double *) = modf;

diff --git a/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c b/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c
new file mode 100644
index 00000000..22222222
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/tst-ppc64-ifunc-bind-now.c
@@ -0,0 +1,45 @@
+/* Regression test for BZ #34503: powerpc IFUNC resolver crash with BIND_NOW.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* Verify that calling an IFUNC function (modf from libm) via a function
+   pointer exported from a shared library that was linked with -z,now and
+   has no DT_NEEDED on libm does not crash.
+
+   Without the fix, INIT_ARCH() in the modf IFUNC resolver dereferences
+   _rtld_global_ro directly; if that pointer has not been relocated yet
+   in the module's GOT context, the dereference faults.  With the fix,
+   the __GLRO() wrapper returns 0 when _rtld_global_ro is NULL and the
+   resolver falls back to the default implementation.  */
+
+#include <math.h>
+#include <stdio.h>
+#include <support/check.h>
+
+/* Exported by tst-ppc64-ifunc-bind-now-mod.so, which is built with
+   -z,now and has no DT_NEEDED on libm.  */
+extern double (*volatile ifunc_ptr) (double, double *);
+
+static int
+do_test (void)
+{
+  double ip;
+  double frac = ifunc_ptr (2.5, &ip);
+  printf ("modf(2.5) = %g + %g (expect 0.5 + 2)\n", frac, ip);
+  TEST_VERIFY (frac == 0.5);
+  TEST_VERIFY (ip == 2.0);
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.34.1

On 8/10/26 12:41, Michael Pfeifroth wrote:
> Sorry, v1 was mangled by my mail client (tabs converted to spaces on
> paste from X11 primary selection).  Resending correctly.  No changes
> from v1 otherwise.
> 
> -- >8 --
> Commit 21841f0d562f ("PowerPC: Influence cpu/arch hwcap features via
> GLIBC_TUNABLES") changed the INIT_ARCH() macro used by powerpc32/power4
> and (via a one-line include) powerpc64 multiarch IFUNC resolvers to
> read hwcap and hwcap2 through a direct
> 
>     &GLRO(dl_powerpc_cpu_features)
> 
> reference, instead of the previous __GLRO() wrapper.  The __GLRO() macro
> performs a volatile NULL check on _rtld_global_ro, which matters because
> IFUNC resolvers can run before _rtld_global_ro has been relocated for the
> current library.
> 
> This regression triggers when a shared library's IFUNC symbol from libm
> is resolved via BIND_NOW (full RELRO) before libm's own GOT is relocated:
> the resolver's INIT_ARCH() then dereferences a NULL _rtld_global_ro and
> segfaults at the hwcap load.  The concrete failure seen was rsyslogd
> crashing on startup on powerpc64 (e5500, BE) with
> 
>     rsyslogd -> librsyslog -> libfastjson -> modf() IFUNC in libm
> 
> when libfastjson lacked a DT_NEEDED on libm, so libm was relocated after
> libfastjson's IFUNC resolvers ran.
> 
> Restore the __GLRO()-based access for both hwcap and hwcap2, matching
> the pre-2.41 behaviour and how use_cached_memopt is already read in the
> same macro.  This is a no-op once _rtld_global_ro is fully initialised
> and simply reinstates the early-startup NULL guard.
> 
> Signed-off-by: Michael Pfeifroth <micpf@westermo.com>
> ---
>  sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
> index 9aab67a5..e681b452 100644
> --- a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
> +++ b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
> @@ -36,9 +36,9 @@
>  /* Get the hardware information post the tunables set, the macro checks
>     it and fills the previous ones.  */
>  #define INIT_ARCH() \
> -  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);	\
> -  unsigned long int hwcap = features->hwcap;				\
> -  unsigned long int __attribute__((unused)) hwcap2 = features->hwcap2; \
> +  unsigned long int hwcap = __GLRO(dl_powerpc_cpu_features.hwcap);	\
> +  unsigned long int __attribute__((unused)) hwcap2 =			\
> +    __GLRO(dl_powerpc_cpu_features.hwcap2);				\
>    bool __attribute__((unused)) use_cached_memopt =		\
>      __GLRO(dl_powerpc_cpu_features.use_cached_memopt);		\
>    if (hwcap & PPC_FEATURE_ARCH_2_06)				\



More information about the Libc-alpha mailing list