[PATCH 2/2] powerpc: Fix stack buffer overflow in loader error messages (bug 34541)

Adhemerval Zanella adhemerval.zanella@linaro.org
Mon Sep 28 13:00:01 GMT 2026


Use _dl_exception_create_format instead string operation on a fixed
buffer, which allocates a message of the exact size.

The powerpc32 and powerpc64 _dl_reloc_overflow implementations
are consolidated and the powerpc32 message now says "reloc at" instead
of "relocation at" (as on powerpc64).

The new tst-reloc-overflow and tst-localentry (powerpc64le) tests load a
module that must be rejected because of an out of range R_PPC*_ADDR16
relocation, or because of a PLT call optimized with --plt-localentry
that resolves to a function with a non-zero local entry point.  Both use
a symbol name of more than 8 KiB, and check that dlerror reports it.
tst-localentry-mod is built with -mno-pcrel, since ld does not apply the
localentry optimization to PC-relative calls.

Checked on powerpc-linux-gnu, powerpc64-linux-gnu, and
powerpc64le-linux-gnu (also with --with-cpu=power10).

Co-authored-by: Florian Weimer <fweimer@redhat.com>
---
 sysdeps/powerpc/Makefile                      |  7 ++-
 sysdeps/powerpc/dl-reloc_overflow.c           | 40 +++++++++++++++++
 sysdeps/powerpc/ldsodefs.h                    |  8 ++++
 sysdeps/powerpc/powerpc32/dl-machine.c        | 25 -----------
 sysdeps/powerpc/powerpc32/dl-machine.h        |  6 ---
 sysdeps/powerpc/powerpc64/dl-machine.c        | 43 +++----------------
 sysdeps/powerpc/powerpc64/dl-machine.h        |  8 +---
 sysdeps/powerpc/powerpc64/le/Makefile         | 16 +++++++
 .../powerpc64/le/tst-localentry-callee-v0.S   | 36 ++++++++++++++++
 .../powerpc64/le/tst-localentry-callee.S      | 35 +++++++++++++++
 .../powerpc/powerpc64/le/tst-localentry-mod.c | 27 ++++++++++++
 .../powerpc64/le/tst-localentry-name.h        | 41 ++++++++++++++++++
 sysdeps/powerpc/powerpc64/le/tst-localentry.c | 40 +++++++++++++++++
 sysdeps/powerpc/tst-reloc-overflow-lib.c      | 21 +++++++++
 sysdeps/powerpc/tst-reloc-overflow-mod.S      | 29 +++++++++++++
 sysdeps/powerpc/tst-reloc-overflow-name.h     | 41 ++++++++++++++++++
 sysdeps/powerpc/tst-reloc-overflow.c          | 40 +++++++++++++++++
 17 files changed, 387 insertions(+), 76 deletions(-)
 create mode 100644 sysdeps/powerpc/dl-reloc_overflow.c
 create mode 100644 sysdeps/powerpc/powerpc64/le/tst-localentry-callee-v0.S
 create mode 100644 sysdeps/powerpc/powerpc64/le/tst-localentry-callee.S
 create mode 100644 sysdeps/powerpc/powerpc64/le/tst-localentry-mod.c
 create mode 100644 sysdeps/powerpc/powerpc64/le/tst-localentry-name.h
 create mode 100644 sysdeps/powerpc/powerpc64/le/tst-localentry.c
 create mode 100644 sysdeps/powerpc/tst-reloc-overflow-lib.c
 create mode 100644 sysdeps/powerpc/tst-reloc-overflow-mod.S
 create mode 100644 sysdeps/powerpc/tst-reloc-overflow-name.h
 create mode 100644 sysdeps/powerpc/tst-reloc-overflow.c

diff --git a/sysdeps/powerpc/Makefile b/sysdeps/powerpc/Makefile
index fadd45807fb..a69ceb07c90 100644
--- a/sysdeps/powerpc/Makefile
+++ b/sysdeps/powerpc/Makefile
@@ -4,7 +4,7 @@ endif
 
 ifeq ($(subdir),elf)
 # extra shared linker files to link into dl-allobjs.so and libc
-sysdep-dl-routines += dl-machine hwcapinfo
+sysdep-dl-routines += dl-machine dl-reloc_overflow hwcapinfo
 sysdep_routines += dl-machine hwcapinfo
 # extra shared linker files to link only into dl-allobjs.so
 sysdep-rtld-routines += dl-machine hwcapinfo
@@ -14,6 +14,11 @@ mod-tlsopt-powerpc.so-no-z-defs = yes
 tests += tst-tlsopt-powerpc
 $(objpfx)tst-tlsopt-powerpc: $(objpfx)mod-tlsopt-powerpc.so
 
+tests += tst-reloc-overflow
+modules-names += tst-reloc-overflow-mod tst-reloc-overflow-lib
+$(objpfx)tst-reloc-overflow.out: $(objpfx)tst-reloc-overflow-mod.so
+$(objpfx)tst-reloc-overflow-mod.so: $(objpfx)tst-reloc-overflow-lib.so
+
 tests-static += tst-cache-ppc-static
 tests-internal += tst-cache-ppc-static
 
diff --git a/sysdeps/powerpc/dl-reloc_overflow.c b/sysdeps/powerpc/dl-reloc_overflow.c
new file mode 100644
index 00000000000..ed4760dd987
--- /dev/null
+++ b/sysdeps/powerpc/dl-reloc_overflow.c
@@ -0,0 +1,40 @@
+/* Relocation overflow reporting for POWER.
+   Copyright (C) 1995-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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, see <https://www.gnu.org/licenses/>.  */
+
+#include <ldsodefs.h>
+
+void
+_dl_reloc_overflow (struct link_map *map, const char *name,
+		    ElfW(Addr) *const reloc_addr, const ElfW(Sym) *refsym)
+{
+  struct dl_exception exc;
+
+  if (refsym != NULL)
+    {
+      const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+      _dl_exception_create_format
+	(&exc, map->l_name, "%s reloc at 0x%lx for symbol `%s' out of range",
+	 name, (unsigned long int) reloc_addr, strtab + refsym->st_name);
+    }
+  else
+    _dl_exception_create_format
+      (&exc, map->l_name, "%s reloc at 0x%lx out of range",
+       name, (unsigned long int) reloc_addr);
+
+  _dl_signal_exception (0, &exc, NULL);
+}
diff --git a/sysdeps/powerpc/ldsodefs.h b/sysdeps/powerpc/ldsodefs.h
index 0803b9f8a2f..9fee2141c66 100644
--- a/sysdeps/powerpc/ldsodefs.h
+++ b/sysdeps/powerpc/ldsodefs.h
@@ -66,4 +66,12 @@ struct La_ppc64v2_retval;
 
 #include_next <ldsodefs.h>
 
+/* Signal an error for the relocation NAME at RELOC_ADDR in MAP, whose
+   resolved value does not fit the relocated field.  REFSYM is the
+   referenced symbol, or NULL.  */
+extern void _dl_reloc_overflow (struct link_map *map, const char *name,
+				ElfW(Addr) *const reloc_addr,
+				const ElfW(Sym) *refsym)
+     attribute_hidden __attribute__ ((__noreturn__));
+
 #endif
diff --git a/sysdeps/powerpc/powerpc32/dl-machine.c b/sysdeps/powerpc/powerpc32/dl-machine.c
index ebdfc63a47c..a67f4eac4fc 100644
--- a/sysdeps/powerpc/powerpc32/dl-machine.c
+++ b/sysdeps/powerpc/powerpc32/dl-machine.c
@@ -23,7 +23,6 @@
 #include <ldsodefs.h>
 #include <elf/dynamic-link.h>
 #include <dl-machine.h>
-#include <_itoa.h>
 
 /* Stuff for the PLT.  */
 #define PLT_INITIAL_ENTRY_WORDS 18
@@ -377,30 +376,6 @@ __elf_machine_fixup_plt (struct link_map *map,
   return finaladdr;
 }
 
-void
-_dl_reloc_overflow (struct link_map *map,
-		    const char *name,
-		    Elf32_Addr *const reloc_addr,
-		    const Elf32_Sym *refsym)
-{
-  char buffer[128];
-  char *t;
-  t = stpcpy (buffer, name);
-  t = stpcpy (t, " relocation at 0x00000000");
-  _itoa_word ((unsigned) reloc_addr, t, 16, 0);
-  if (refsym)
-    {
-      const char *strtab;
-
-      strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
-      t = stpcpy (t, " for symbol `");
-      t = stpcpy (t, strtab + refsym->st_name);
-      t = stpcpy (t, "'");
-    }
-  t = stpcpy (t, " out of range");
-  _dl_signal_error (0, map->l_name, NULL, buffer);
-}
-
 void
 __process_machine_rela (struct link_map *map,
 			const Elf32_Rela *reloc,
diff --git a/sysdeps/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h
index e42f6fd3990..d27e18b3b81 100644
--- a/sysdeps/powerpc/powerpc32/dl-machine.h
+++ b/sysdeps/powerpc/powerpc32/dl-machine.h
@@ -277,12 +277,6 @@ extern void __process_machine_rela (struct link_map *map,
 				    int rinfo, bool skip_ifunc)
   attribute_hidden;
 
-/* Call _dl_signal_error when a resolved value overflows a relocated area.  */
-extern void _dl_reloc_overflow (struct link_map *map,
-				const char *name,
-				Elf32_Addr *const reloc_addr,
-				const Elf32_Sym *refsym) attribute_hidden;
-
 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
    LOADADDR is the load address of the object; INFO is an array indexed
    by DT_* of the .dynamic section info.  */
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.c b/sysdeps/powerpc/powerpc64/dl-machine.c
index 29fd15c8c7d..ea454da436b 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.c
+++ b/sysdeps/powerpc/powerpc64/dl-machine.c
@@ -16,51 +16,18 @@
    License along with the GNU C Library; see the file COPYING.LIB.  If
    not, see <https://www.gnu.org/licenses/>.  */
 
-#include <string.h>
-#include <unistd.h>
 #include <ldsodefs.h>
-#include <_itoa.h>
 #include <dl-machine.h>
 
-void
-_dl_reloc_overflow (struct link_map *map,
-		    const char *name,
-		    Elf64_Addr *const reloc_addr,
-		    const Elf64_Sym *refsym)
-{
-  char buffer[1024];
-  char *t;
-  t = stpcpy (buffer, name);
-  /* Notice that _itoa_word() writes characters from the higher address to the
-     lower address, requiring the destination string to reserve all the
-     required size before the call.  */
-  t = stpcpy (t, " reloc at 0x0000000000000000");
-  _itoa_word ((unsigned long) reloc_addr, t, 16, 0);
-  if (refsym)
-    {
-      const char *strtab;
-
-      strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
-      t = stpcpy (t, " for symbol `");
-      t = stpcpy (t, strtab + refsym->st_name);
-      t = stpcpy (t, "'");
-    }
-  t = stpcpy (t, " out of range");
-  _dl_signal_error (0, map->l_name, NULL, buffer);
-}
-
 #if _CALL_ELF == 2
 void
 _dl_error_localentry (struct link_map *map, const Elf64_Sym *refsym)
 {
-  char buffer[1024];
-  char *t;
-  const char *strtab;
+  const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
 
-  strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
-  t = stpcpy (buffer, "expected localentry:0 `");
-  t = stpcpy (t, strtab + refsym->st_name);
-  t = stpcpy (t, "'");
-  _dl_signal_error (0, map->l_name, NULL, buffer);
+  struct dl_exception exc;
+  _dl_exception_create_format (&exc, map->l_name, "expected localentry:0 `%s'",
+			       strtab + refsym->st_name);
+  _dl_signal_exception (0, &exc, NULL);
 }
 #endif
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
index c6e82d229eb..dacc7e042d1 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.h
+++ b/sysdeps/powerpc/powerpc64/dl-machine.h
@@ -443,7 +443,8 @@ elf_machine_runtime_setup (struct link_map *map, struct r_scope_elem *scope[],
 
 #if _CALL_ELF == 2
 extern void attribute_hidden _dl_error_localentry (struct link_map *map,
-						   const Elf64_Sym *refsym);
+						   const Elf64_Sym *refsym)
+     __attribute__ ((__noreturn__));
 
 /* If the PLT entry resolves to a function in the same object, return
    the target function's local entry point offset if usable.  */
@@ -606,11 +607,6 @@ ppc_init_fake_thread_pointer (void)
 
 #define dont_expect(X) __builtin_expect ((X), 0)
 
-extern void attribute_hidden _dl_reloc_overflow (struct link_map *map,
-						 const char *name,
-						 Elf64_Addr *const reloc_addr,
-						 const Elf64_Sym *refsym);
-
 static inline void __attribute__ ((always_inline))
 elf_machine_rela_relative (Elf64_Addr l_addr, const Elf64_Rela *reloc,
 			   void *const reloc_addr_arg)
diff --git a/sysdeps/powerpc/powerpc64/le/Makefile b/sysdeps/powerpc/powerpc64/le/Makefile
index b77775cf955..5f6c8a0afdd 100644
--- a/sysdeps/powerpc/powerpc64/le/Makefile
+++ b/sysdeps/powerpc/powerpc64/le/Makefile
@@ -237,4 +237,20 @@ ifeq (no,$(build-hardcoded-path-in-tests))
 tests-container += tst-glibc-hwcaps-cache
 endif
 
+tests += tst-localentry
+modules-names += tst-localentry-mod tst-localentry-callee \
+		 tst-localentry-callee-v0
+
+# Both callees share a soname, so the module records a DT_NEEDED on
+# tst-localentry-callee.so while being linked against the v0 definition.
+LDFLAGS-tst-localentry-callee-v0.so += -Wl,-soname,tst-localentry-callee.so
+LDFLAGS-tst-localentry-mod.so += -Wl,--plt-localentry
+# ld only applies the localentry:0 optimization to TOC-based calls, so
+# PC-relative code (the default for power10) would not exercise the check.
+CFLAGS-tst-localentry-mod.c += -mno-pcrel
+
+$(objpfx)tst-localentry.out: $(objpfx)tst-localentry-mod.so \
+			     $(objpfx)tst-localentry-callee.so
+$(objpfx)tst-localentry-mod.so: $(objpfx)tst-localentry-callee-v0.so
+
 endif # $(subdir) == elf
diff --git a/sysdeps/powerpc/powerpc64/le/tst-localentry-callee-v0.S b/sysdeps/powerpc/powerpc64/le/tst-localentry-callee-v0.S
new file mode 100644
index 00000000000..cafd8284418
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/le/tst-localentry-callee-v0.S
@@ -0,0 +1,36 @@
+/* Callee with a local entry point of zero, used at link time.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-localentry-name.h"
+
+/* The modules required st_other to have localentry:0, because that is what
+   makes ld optimize the module's call under --plt-localentry.
+
+   This is written in assembly to assure the code generation decision
+   regardless of compiler flags (like stack protection) which change how
+   the leaf function returns the value.  */
+
+	.abiversion 2
+	.section	".text"
+	.globl	LOCALENTRY_SYMBOL
+	.type	LOCALENTRY_SYMBOL, @function
+LOCALENTRY_SYMBOL:
+	.localentry	LOCALENTRY_SYMBOL, 0
+	li	3, 42
+	blr
+	.size	LOCALENTRY_SYMBOL, .-LOCALENTRY_SYMBOL
diff --git a/sysdeps/powerpc/powerpc64/le/tst-localentry-callee.S b/sysdeps/powerpc/powerpc64/le/tst-localentry-callee.S
new file mode 100644
index 00000000000..a1b8f6ff4ee
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/le/tst-localentry-callee.S
@@ -0,0 +1,35 @@
+/* Callee with a non-zero local entry point, substituted at run time.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-localentry-name.h"
+
+/* The same symbol with a global entry sequence that sets up r2, so st_other
+   says localentry:8.  Resolving the module's optimized call to this
+   definition is the ABI violation ld.so has to reject.  */
+
+	.abiversion 2
+	.section	".text"
+	.globl	LOCALENTRY_SYMBOL
+	.type	LOCALENTRY_SYMBOL, @function
+LOCALENTRY_SYMBOL:
+0:	addis	2, 12, .TOC.-0b@ha
+	addi	2, 2, .TOC.-0b@l
+	.localentry	LOCALENTRY_SYMBOL, .-LOCALENTRY_SYMBOL
+	li	3, 43
+	blr
+	.size	LOCALENTRY_SYMBOL, .-LOCALENTRY_SYMBOL
diff --git a/sysdeps/powerpc/powerpc64/le/tst-localentry-mod.c b/sysdeps/powerpc/powerpc64/le/tst-localentry-mod.c
new file mode 100644
index 00000000000..497070f9b01
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/le/tst-localentry-mod.c
@@ -0,0 +1,27 @@
+/* Module whose PLT call was optimized for a localentry:0 callee.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-localentry-name.h"
+
+extern int LOCALENTRY_SYMBOL (void);
+
+int
+call_it (void)
+{
+  return LOCALENTRY_SYMBOL ();
+}
diff --git a/sysdeps/powerpc/powerpc64/le/tst-localentry-name.h b/sysdeps/powerpc/powerpc64/le/tst-localentry-name.h
new file mode 100644
index 00000000000..0587f914cc7
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/le/tst-localentry-name.h
@@ -0,0 +1,41 @@
+/* Overly long symbol name shared by the local entry point test.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _TST_LOCALENTRY_NAME_H
+#define _TST_LOCALENTRY_NAME_H 1
+
+#define CAT_(a, b) a##b
+#define CAT(a, b) CAT_(a, b)
+
+#define N16   xxxxxxxxxxxxxxxx
+#define N32   CAT (N16, N16)
+#define N64   CAT (N32, N32)
+#define N128  CAT (N64, N64)
+#define N256  CAT (N128, N128)
+#define N512  CAT (N256, N256)
+#define N1024 CAT (N512, N512)
+#define N2048 CAT (N1024, N1024)
+#define N4096 CAT (N2048, N2048)
+#define N8192 CAT (N4096, N4096)
+
+#define LOCALENTRY_SYMBOL CAT (tst_localentry_symbol_, N8192)
+
+#define STR_(x) #x
+#define STR(x) STR_(x)
+
+#endif
diff --git a/sysdeps/powerpc/powerpc64/le/tst-localentry.c b/sysdeps/powerpc/powerpc64/le/tst-localentry.c
new file mode 100644
index 00000000000..f3619ead263
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/le/tst-localentry.c
@@ -0,0 +1,40 @@
+/* Check the message for a local entry point mismatch (bug 34541).
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <string.h>
+#include <support/check.h>
+
+#include "tst-localentry-name.h"
+
+static int
+do_test (void)
+{
+  void *h = dlopen ("tst-localentry-mod.so", RTLD_NOW);
+  TEST_VERIFY (h == NULL);
+
+  const char *msg = dlerror ();
+  TEST_VERIFY_EXIT (msg != NULL);
+
+  TEST_VERIFY (strstr (msg, STR (LOCALENTRY_SYMBOL)) != NULL);
+  TEST_VERIFY (strstr (msg, "expected localentry:0") != NULL);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/powerpc/tst-reloc-overflow-lib.c b/sysdeps/powerpc/tst-reloc-overflow-lib.c
new file mode 100644
index 00000000000..217e5bb5911
--- /dev/null
+++ b/sysdeps/powerpc/tst-reloc-overflow-lib.c
@@ -0,0 +1,21 @@
+/* Definition of the symbol the relocation overflow test refers to.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-reloc-overflow-name.h"
+
+int OVERFLOW_SYMBOL = 42;
diff --git a/sysdeps/powerpc/tst-reloc-overflow-mod.S b/sysdeps/powerpc/tst-reloc-overflow-mod.S
new file mode 100644
index 00000000000..5595ac20c47
--- /dev/null
+++ b/sysdeps/powerpc/tst-reloc-overflow-mod.S
@@ -0,0 +1,29 @@
+/* Module with an out of range R_PPC*_ADDR16 relocation.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "tst-reloc-overflow-name.h"
+
+/* _dl_reloc_overflow is only reached for relocation types that store an
+   address into a field narrower than a pointer.  */
+
+	.data
+	.globl	refword
+	.type	refword, @object
+refword:
+	.short	OVERFLOW_SYMBOL
+	.size	refword, 2
diff --git a/sysdeps/powerpc/tst-reloc-overflow-name.h b/sysdeps/powerpc/tst-reloc-overflow-name.h
new file mode 100644
index 00000000000..7eb010f774c
--- /dev/null
+++ b/sysdeps/powerpc/tst-reloc-overflow-name.h
@@ -0,0 +1,41 @@
+/* Overly long symbol name shared by the relocation overflow test.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _TST_RELOC_OVERFLOW_NAME_H
+#define _TST_RELOC_OVERFLOW_NAME_H 1
+
+#define CAT_(a, b) a##b
+#define CAT(a, b) CAT_(a, b)
+
+#define N16   xxxxxxxxxxxxxxxx
+#define N32   CAT (N16, N16)
+#define N64   CAT (N32, N32)
+#define N128  CAT (N64, N64)
+#define N256  CAT (N128, N128)
+#define N512  CAT (N256, N256)
+#define N1024 CAT (N512, N512)
+#define N2048 CAT (N1024, N1024)
+#define N4096 CAT (N2048, N2048)
+#define N8192 CAT (N4096, N4096)
+
+#define OVERFLOW_SYMBOL CAT (tst_reloc_overflow_symbol_, N8192)
+
+#define STR_(x) #x
+#define STR(x) STR_(x)
+
+#endif
diff --git a/sysdeps/powerpc/tst-reloc-overflow.c b/sysdeps/powerpc/tst-reloc-overflow.c
new file mode 100644
index 00000000000..99d3289330f
--- /dev/null
+++ b/sysdeps/powerpc/tst-reloc-overflow.c
@@ -0,0 +1,40 @@
+/* Check the message for an out of range relocation (bug 34541).
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <string.h>
+#include <support/check.h>
+
+#include "tst-reloc-overflow-name.h"
+
+static int
+do_test (void)
+{
+  void *h = dlopen ("tst-reloc-overflow-mod.so", RTLD_NOW);
+  TEST_VERIFY (h == NULL);
+
+  const char *msg = dlerror ();
+  TEST_VERIFY_EXIT (msg != NULL);
+
+  TEST_VERIFY (strstr (msg, STR (OVERFLOW_SYMBOL)) != NULL);
+  TEST_VERIFY (strstr (msg, "out of range") != NULL);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.53.0



More information about the Libc-alpha mailing list