[PATCH] elf: Fix ld.so SIGSEGV in trace mode with unresolved TLS symbols (BZ 34532)
Adhemerval Zanella <[email protected]>
| Newsgroups | gmane.comp.lib.glibc.alpha |
|---|---|
| Message-ID | <[email protected]> |
On loongarch and hppa, running ld.so in trace mode on an object with
dynamic relocations against undefined TLS symbols crashes with SIGSEGV.
Add the same NULL guards used by all other targets.
Checked on loongarch64-linux-gnu-lp64d and hppa-linux-gnu.
---
elf/Makefile | 5 +++
elf/tst-trace-tls-mod.c | 41 +++++++++++++++++
elf/tst-trace-tls.c | 82 ++++++++++++++++++++++++++++++++++
sysdeps/hppa/dl-machine.h | 4 +-
sysdeps/loongarch/dl-machine.h | 14 ++++--
5 files changed, 141 insertions(+), 5 deletions(-)
create mode 100644 elf/tst-trace-tls-mod.c
create mode 100644 elf/tst-trace-tls.c
diff --git a/elf/Makefile b/elf/Makefile
index dbc6cfec7fe..d9abbad1cf7 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -529,6 +529,7 @@ tests += \
tst-tlsalign \
tst-tlsalign-extern \
tst-tlsgap \
+ tst-trace-tls \
tst-unique1 \
tst-unique2 \
tst-unwind-ctor \
@@ -1108,6 +1109,7 @@ modules-names += \
tst-tlsmod7 \
tst-tlsmod8 \
tst-tlsmod9 \
+ tst-trace-tls-mod \
tst-unique1mod1 \
tst-unique1mod2 \
tst-unique2mod1 \
@@ -3544,6 +3546,9 @@ $(objpfx)tst-nodeps2-mod.so: $(common-objpfx)libc.so \
$(objpfx)tst-nodeps2.out: \
$(objpfx)tst-nodeps1-mod.so $(objpfx)tst-nodeps2-mod.so
+$(objpfx)tst-trace-tls.out: $(objpfx)tst-trace-tls-mod.so $(objpfx)ld.so
+tst-trace-tls-mod.so-no-z-defs = yes
+
$(objpfx)tst-tlsgap: $(shared-thread-library)
$(objpfx)tst-tlsgap.out: \
$(objpfx)tst-tlsgap-mod0.so \
diff --git a/elf/tst-trace-tls-mod.c b/elf/tst-trace-tls-mod.c
new file mode 100644
index 00000000000..7b855fd814c
--- /dev/null
+++ b/elf/tst-trace-tls-mod.c
@@ -0,0 +1,41 @@
+/* Shared object with relocations against undefined TLS symbols.
+ 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/>. */
+
+extern __thread int missing_tls_gd
+ __attribute__ ((tls_model ("global-dynamic")));
+extern __thread int missing_tls_ie
+ __attribute__ ((tls_model ("initial-exec")));
+extern int missing_data;
+
+int
+access_tls_gd (void)
+{
+ return missing_tls_gd;
+}
+
+int
+access_tls_ie (void)
+{
+ return missing_tls_ie;
+}
+
+int
+access_data (void)
+{
+ return missing_data;
+}
diff --git a/elf/tst-trace-tls.c b/elf/tst-trace-tls.c
new file mode 100644
index 00000000000..2d541b1a519
--- /dev/null
+++ b/elf/tst-trace-tls.c
@@ -0,0 +1,82 @@
+/* Check that trace mode handles unresolved TLS symbols (bug XXXXX).
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/support.h>
+
+/* Check if unresolved TLS symbol in trace mode (LD_TRACE_LOADED_OBJECTS)
+ is correctly reported. */
+
+static char *mod;
+
+static struct support_capture_subprocess
+run_ldso (const char *env1, const char *env2)
+{
+ char *const argv[] =
+ { (char *) support_objdir_elf_ldso, (char *) mod, NULL };
+ char *const envp[] =
+ { (char *) "LD_TRACE_LOADED_OBJECTS=1", (char *) env1, (char *) env2,
+ NULL };
+
+ struct support_capture_subprocess result
+ = support_capture_subprogram (argv[0], argv, envp);
+ support_capture_subprocess_check (&result, "tst-trace-tls", 0,
+ sc_allow_stdout | sc_allow_stderr);
+ return result;
+}
+
+static void
+run_trace (const char *title, const char *env1, const char *env2,
+ bool check_undefined)
+{
+ printf ("info: checking %s\n", title);
+
+ struct support_capture_subprocess result = run_ldso (env1, env2);
+
+ if (check_undefined)
+ {
+ TEST_VERIFY (strstr (result.err.buffer,
+ "undefined symbol: missing_tls_gd") != NULL);
+ TEST_VERIFY (strstr (result.err.buffer,
+ "undefined symbol: missing_tls_ie") != NULL);
+ TEST_VERIFY (strstr (result.err.buffer,
+ "undefined symbol: missing_data") != NULL);
+ }
+
+ support_capture_subprocess_free (&result);
+}
+
+static int
+do_test (void)
+{
+ mod = xasprintf ("%s/elf/tst-trace-tls-mod.so", support_objdir_root);
+
+ run_trace ("ldd -d", "LD_WARN=yes", NULL, true);
+ run_trace ("ldd -r", "LD_WARN=yes", "LD_BIND_NOW=1", true);
+ run_trace ("ldd -u", "LD_DEBUG=unused", NULL, false);
+
+ free (mod);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/hppa/dl-machine.h b/sysdeps/hppa/dl-machine.h
index 9bdbd85b6b3..ba2e4123c6e 100644
--- a/sysdeps/hppa/dl-machine.h
+++ b/sysdeps/hppa/dl-machine.h
@@ -700,7 +700,9 @@ elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
#if !defined RTLD_BOOTSTRAP
case R_PARISC_TLS_DTPMOD32:
- value = sym_map->l_tls_modid;
+ /* The sym_map may be NULL in trace mode (LD_TRACE_LOADED_OBJECTS). */
+ if (sym_map != NULL)
+ value = sym_map->l_tls_modid;
break;
case R_PARISC_TLS_DTPOFF32:
diff --git a/sysdeps/loongarch/dl-machine.h b/sysdeps/loongarch/dl-machine.h
index ae295264ae9..1fb2635f92e 100644
--- a/sysdeps/loongarch/dl-machine.h
+++ b/sysdeps/loongarch/dl-machine.h
@@ -197,16 +197,22 @@ elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
#ifndef RTLD_BOOTSTRAP
case __WORDSIZE == 64 ? R_LARCH_TLS_DTPMOD64 : R_LARCH_TLS_DTPMOD32:
- *addr_field = sym_map->l_tls_modid;
+ /* The sym_map may be NULL in trace mode (LD_TRACE_LOADED_OBJECTS). */
+ if (sym_map != NULL)
+ *addr_field = sym_map->l_tls_modid;
break;
case __WORDSIZE == 64 ? R_LARCH_TLS_DTPREL64 : R_LARCH_TLS_DTPREL32:
- *addr_field = TLS_DTPREL_VALUE (sym) + reloc->r_addend;
+ if (sym != NULL)
+ *addr_field = TLS_DTPREL_VALUE (sym) + reloc->r_addend;
break;
case __WORDSIZE == 64 ? R_LARCH_TLS_TPREL64 : R_LARCH_TLS_TPREL32:
- CHECK_STATIC_TLS (map, sym_map, sym);
- *addr_field = TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend;
+ if (sym != NULL)
+ {
+ CHECK_STATIC_TLS (map, sym_map, sym);
+ *addr_field = TLS_TPREL_VALUE (sym_map, sym) + reloc->r_addend;
+ }
break;
case __WORDSIZE == 64 ? R_LARCH_TLS_DESC64 : R_LARCH_TLS_DESC32:
--
2.53.0