* [PATCH v3 01/11] Don't attempt to find TLS address when target has no registers
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 02/11] Allow TLS access to work in gdb.server/no-thread-db.exp Kevin Buettner
` (9 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
This commit fixes two bugs, one of which is Bug 25807 - that one is
fixed by the change in findvar.c. I found it while testing on aarch64;
it turned a KFAIL for gdb.threads/tls.exp: print a_thread_local
into a FAIL due to a GDB internal error. In addition to the existing
test just noted, I've also added a test to the new test case
gdb.base/tls-nothreads.exp. It'll be tested, using different
scenarios, up to 8 times:
PASS: gdb.base/tls-nothreads.exp: default: force_internal_tls=false: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: default: force_internal_tls=true: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: static: force_internal_tls=false: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: static: force_internal_tls=true: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads: force_internal_tls=false: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads: force_internal_tls=true: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads-static: force_internal_tls=false: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads-static: force_internal_tls=true: after exit: print tls_tbss_1
There is a related problem that's fixed by the minsyms.c change. It
can be observed when debugging a program without debugging symbols
when the program is not executing. I've written a new test for this,
but it's included in the new test case gdb.base/tls-nothreads.exp,
found later in this series. Depending on the target, it'll be run
up to 8 times for different targets. E.g., on aarch64, I'm seeing
these PASSes, all of which test this change:
PASS: gdb.base/tls-nothreads.exp: default: force_internal_tls=false: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: default: force_internal_tls=true: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: static: force_internal_tls=false: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: static: force_internal_tls=true: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads: force_internal_tls=false: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads: force_internal_tls=true: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads-static: force_internal_tls=false: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads-static: force_internal_tls=true: stripped: after exit: print (int) tls_tbss_1
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=25807
---
gdb/findvar.c | 7 ++++++-
gdb/minsyms.c | 8 +++++++-
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/gdb/findvar.c b/gdb/findvar.c
index f7760aa61ca..a530e679c8a 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -485,7 +485,12 @@ language_defn::read_var_value (struct symbol *var,
/* Determine address of TLS variable. */
if (obj_section
&& (obj_section->the_bfd_section->flags & SEC_THREAD_LOCAL) != 0)
- addr = target_translate_tls_address (obj_section->objfile, addr);
+ {
+ if (!target_has_registers ())
+ error (_("Cannot read `%s' without registers"),
+ var->print_name ());
+ addr = target_translate_tls_address (obj_section->objfile, addr);
+ }
}
break;
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index 33eb9072e5f..a904bd96045 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -1684,7 +1684,13 @@ find_minsym_type_and_address (minimal_symbol *msymbol,
{
/* Skip translation if caller does not need the address. */
if (address_p != NULL)
- *address_p = target_translate_tls_address (objfile, addr);
+ {
+ if (!target_has_registers ())
+ error (_(
+ "Cannot determine address of TLS symbol `%s' without registers"),
+ bound_msym.minsym->print_name ());
+ *address_p = target_translate_tls_address (objfile, addr);
+ }
return builtin_type (objfile)->nodebug_tls_symbol;
}
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 02/11] Allow TLS access to work in gdb.server/no-thread-db.exp
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 01/11] Don't attempt to find TLS address when target has no registers Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 03/11] Track and fetch TLS module ids for MUSL and GLIBC Kevin Buettner
` (8 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
The patches later in the series add GDB-internal TLS support for
certain targets. This commit updates the "print foo" test in
gdb.server/no-thread-db.exp to accept either a TLS failure (when
libthread_db isn't available) or printing the correct answer, which
will occur when GDB's internal TLS address resolution can be used.
I'm making this change prior to the commits which actually add
the GDB-internal TLS support in order to avoid tripping regression
testers.
---
gdb/testsuite/gdb.server/no-thread-db.exp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/gdb/testsuite/gdb.server/no-thread-db.exp b/gdb/testsuite/gdb.server/no-thread-db.exp
index 656fad1a560..baad81b0f8e 100644
--- a/gdb/testsuite/gdb.server/no-thread-db.exp
+++ b/gdb/testsuite/gdb.server/no-thread-db.exp
@@ -57,6 +57,8 @@ gdb_breakpoint ${srcfile}:[gdb_get_line_number "after tls assignment"]
gdb_continue_to_breakpoint "after tls assignment"
# Printing a tls variable should fail gracefully without a libthread_db.
+# Alternately, the correct answer might be printed due GDB's internal
+# TLS support for some targets.
set re_exec "\[^\r\n\]*[file tail $binfile]"
gdb_test "print foo" \
- "Cannot find thread-local storage for Thread \[^,\]+, executable file $re_exec:\[\r\n\]+Remote target failed to process qGetTLSAddr request"
+ "= 1|(?:Cannot find thread-local storage for Thread \[^,\]+, executable file $re_exec:\[\r\n\]+Remote target failed to process qGetTLSAddr request)"
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 03/11] Track and fetch TLS module ids for MUSL and GLIBC
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 01/11] Don't attempt to find TLS address when target has no registers Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 02/11] Allow TLS access to work in gdb.server/no-thread-db.exp Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 04/11] Implement internal TLS address lookup for Linux targets Kevin Buettner
` (7 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
This commit adds, to solib-svr4.h and solib-svr4.c, functions
glibc_link_map_to_tls_module_id and musl_link_map_to_tls_module_id for
use with callers in linux-tdep.c (which is not in this commit). It
adds a number of helper functions for implementing link map to module
id support.
It also renames existing function 'find_program_interpreter' to
'svr4_find_program_interpreter' and makes it visible to other source
files within GDB. It will be used in the libc sniffing code in
linux-tdep.c in a later commit in this series. The libc sniffer is
needed in order to know which link map to module id function to call
as the method for determining module ids differs between libc /
dynamic linker implementations. These details are discussed in
comments in the patch.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=24548
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31563
---
gdb/solib-svr4.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++-
gdb/solib-svr4.h | 12 +++
2 files changed, 211 insertions(+), 4 deletions(-)
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 7999a8e6c7e..b331aac6122 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -405,6 +405,9 @@ struct svr4_info
The special entry zero is reserved for a linear list to support
gdbstubs that do not support namespaces. */
std::map<CORE_ADDR, std::vector<svr4_so>> solib_lists;
+
+ bool glibc_tls_slots_inited = false;
+ std::vector<CORE_ADDR> glibc_tls_slots;
};
/* Per-program-space data key. */
@@ -586,10 +589,10 @@ read_program_header (int type, int *p_arch_size, CORE_ADDR *base_addr)
return buf;
}
+/* See solib-svr4.h. */
-/* Return program interpreter string. */
-static std::optional<gdb::byte_vector>
-find_program_interpreter (void)
+std::optional<gdb::byte_vector>
+svr4_find_program_interpreter ()
{
/* If we have a current exec_bfd, use its section table. */
if (current_program_space->exec_bfd ()
@@ -1516,6 +1519,194 @@ svr4_fetch_objfile_link_map (struct objfile *objfile)
return 0;
}
+/* Return true if bfd section BFD_SECT is a thread local section
+ (i.e. either named ".tdata" or ".tbss"), and false otherwise. */
+
+static bool
+is_thread_local_section (struct bfd_section *bfd_sect)
+{
+ return ((strcmp (bfd_sect->name, ".tdata") == 0
+ || strcmp (bfd_sect->name, ".tbss") == 0)
+ && bfd_sect->size != 0);
+}
+
+/* Return true if objfile OBJF contains a thread local section, and
+ false otherwise. */
+
+static bool
+has_thread_local_section (const objfile *objf)
+{
+ for (obj_section *objsec : objf->sections ())
+ if (is_thread_local_section (objsec->the_bfd_section))
+ return true;
+ return false;
+}
+
+/* Return true if solib SO contains a thread local section, and false
+ otherwise. */
+
+static bool
+has_thread_local_section (const solib &so)
+{
+ for (const target_section &p : so.sections)
+ if (is_thread_local_section (p.the_bfd_section))
+ return true;
+ return false;
+}
+
+/* For the MUSL C library, given link map address LM_ADDR, return the
+ corresponding TLS module id, or 0 if not found.
+
+ Background: Unlike the mechanism used by glibc (see below), the
+ scheme used by the MUSL C library is pretty simple. If the
+ executable contains TLS variables it gets module id 1. Otherwise,
+ the first shared object loaded which contains TLS variables is
+ assigned to module id 1. TLS-containing shared objects are then
+ assigned consecutive module ids, based on the order that they are
+ loaded. When unloaded via dlclose, module ids are reassigned as if
+ that module had never been loaded. */
+
+int
+musl_link_map_to_tls_module_id (CORE_ADDR lm_addr)
+{
+ /* When lm_addr is zero, the program is statically linked. Any TLS
+ variables will be in module id 1. */
+ if (lm_addr == 0)
+ return 1;
+
+ int mod_id = 0;
+ if (has_thread_local_section (current_program_space->symfile_object_file))
+ mod_id++;
+
+ struct svr4_info *info = get_svr4_info (current_program_space);
+
+ /* Cause svr4_current_sos() to be run if it hasn't been already. */
+ if (info->main_lm_addr == 0)
+ solib_add (NULL, 0, auto_solib_add);
+
+ /* Handle case where lm_addr corresponds to the main program.
+ Return value is either 0, when there are no TLS variables, or 1,
+ when there are. */
+ if (lm_addr == info->main_lm_addr)
+ return mod_id;
+
+ /* Iterate through the shared objects, possibly incrementing the
+ module id, and returning mod_id should a match be found. */
+ for (const solib &so : current_program_space->solibs ())
+ {
+ if (has_thread_local_section (so))
+ mod_id++;
+
+ auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
+ if (li->lm_addr == lm_addr)
+ return mod_id;
+ }
+ return 0;
+}
+
+/* For GLIBC, given link map address LM_ADDR, return the corresponding TLS
+ module id, or 0 if not found. */
+
+int
+glibc_link_map_to_tls_module_id (CORE_ADDR lm_addr)
+{
+ /* When lm_addr is zero, the program is statically linked. Any TLS
+ variables will be in module id 1. */
+ if (lm_addr == 0)
+ return 1;
+
+ /* Look up lm_addr in the TLS slot data structure. */
+ struct svr4_info *info = get_svr4_info (current_program_space);
+ auto it = std::find (info->glibc_tls_slots.begin (),
+ info->glibc_tls_slots.end (),
+ lm_addr);
+ if (it == info->glibc_tls_slots.end ())
+ return 0;
+ else
+ return 1 + it - info->glibc_tls_slots.begin ();
+}
+
+/* Conditionally, based on whether the shared object, SO, contains TLS
+ variables, assign a link map address to a TLS module id slot. This
+ code is GLIBC-specific and may only work for specific GLIBC
+ versions. That said, it is known to work for (at least) GLIBC
+ versions 2.27 thru 2.40.
+
+ Background: In order to implement internal TLS address lookup
+ code, it is necessary to find the module id that has been
+ associated with a specific link map address. In GLIBC, the TLS
+ module id is stored in struct link_map, in the member
+ 'l_tls_modid'. While the first several members of struct link_map
+ are part of the SVR4 ABI, the offset to l_tls_modid definitely is
+ not. Therefore, since we don't know the offset to l_tls_modid, we
+ cannot simply look it up - which is a shame, because things would
+ be so much more easy and obviously accurate, if we could access
+ l_tls_modid.
+
+ GLIBC has a concept of TLS module id slots. These slots are
+ allocated consecutively as shared objects containing TLS variables
+ are loaded. When unloaded (e.g. via dlclose()), the corresponding
+ slot is marked as unused, but may be used again when later loading
+ a shared object.
+
+ The functions tls_maybe_fill_slot and tls_maybe_erase_slot are
+ associated with the observers 'solib_loaded' and 'solib_unloaded'.
+ They (attempt to) track use of TLS module id slots in the same way
+ that GLIBC does, which will hopefully provide an accurate module id
+ when asked to provide it via glibc_link_map_to_tls_module_id(),
+ above. */
+
+static void
+tls_maybe_fill_slot (solib &so)
+{
+ struct svr4_info *info = get_svr4_info (current_program_space);
+ if (!info->glibc_tls_slots_inited)
+ {
+ /* Cause svr4_current_sos() to be run if it hasn't been already. */
+ if (info->main_lm_addr == 0)
+ svr4_current_sos_direct (info);
+
+ /* Quit early when main_lm_addr is still 0. */
+ if (info->main_lm_addr == 0)
+ return;
+
+ /* Also quit early when symfile_object_file is not yet known. */
+ if (current_program_space->symfile_object_file == nullptr)
+ return;
+
+ if (has_thread_local_section (current_program_space->symfile_object_file))
+ info->glibc_tls_slots.push_back (info->main_lm_addr);
+ info->glibc_tls_slots_inited = true;
+ }
+
+ if (has_thread_local_section (so))
+ {
+ auto it = std::find (info->glibc_tls_slots.begin (),
+ info->glibc_tls_slots.end (),
+ 0);
+ auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
+ if (it == info->glibc_tls_slots.end ())
+ info->glibc_tls_slots.push_back (li->lm_addr);
+ else
+ *it = li->lm_addr;
+ }
+}
+
+/* Remove a link map address from the TLS module slot data structure.
+ As noted above, this code is GLIBC-specific. */
+
+static void
+tls_maybe_erase_slot (program_space *pspace, const solib &so)
+{
+ struct svr4_info *info = get_svr4_info (pspace);
+ auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
+ auto it = std::find (info->glibc_tls_slots.begin (),
+ info->glibc_tls_slots.end (),
+ li->lm_addr);
+ if (it != info->glibc_tls_slots.end ())
+ *it = 0;
+}
+
/* On some systems, the only way to recognize the link map entry for
the main executable file is by looking at its name. Return
non-zero iff SONAME matches one of the known main executable names. */
@@ -2305,7 +2496,7 @@ enable_break (struct svr4_info *info, int from_tty)
/* Find the program interpreter; if not found, warn the user and drop
into the old breakpoint at symbol code. */
std::optional<gdb::byte_vector> interp_name_holder
- = find_program_interpreter ();
+ = svr4_find_program_interpreter ();
if (interp_name_holder)
{
const char *interp_name = (const char *) interp_name_holder->data ();
@@ -3382,4 +3573,8 @@ _initialize_svr4_solib ()
{
gdb::observers::free_objfile.attach (svr4_free_objfile_observer,
"solib-svr4");
+
+ /* Set up observers for tracking GLIBC TLS module id slots. */
+ gdb::observers::solib_loaded.attach (tls_maybe_fill_slot, "solib-svr4");
+ gdb::observers::solib_unloaded.attach (tls_maybe_erase_slot, "solib-svr4");
}
diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h
index 579fe6d9843..6e25441f221 100644
--- a/gdb/solib-svr4.h
+++ b/gdb/solib-svr4.h
@@ -112,4 +112,16 @@ extern struct link_map_offsets *svr4_lp64_fetch_link_map_offsets (void);
SVR4 run time loader. */
int svr4_in_dynsym_resolve_code (CORE_ADDR pc);
+/* For the MUSL C library, given link map address LM_ADDR, return the
+ corresponding TLS module id, or 0 if not found. */
+int musl_link_map_to_tls_module_id (CORE_ADDR lm_addr);
+
+/* For GLIBC, given link map address LM_ADDR, return the corresponding TLS
+ module id, or 0 if not found. */
+int glibc_link_map_to_tls_module_id (CORE_ADDR lm_addr);
+
+/* Return program interpreter string. */
+
+std::optional<gdb::byte_vector> svr4_find_program_interpreter ();
+
#endif /* solib-svr4.h */
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 04/11] Implement internal TLS address lookup for Linux targets
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
` (2 preceding siblings ...)
2024-10-25 2:26 ` [PATCH v3 03/11] Track and fetch TLS module ids for MUSL and GLIBC Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 05/11] Internal TLS support for aarch64, x86_64, riscv, ppc64, and s390x Kevin Buettner
` (6 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
This commit adds non-architecture-specific support for internal TLS
address lookup for Linux targets. By "internal", I mean support which
does not rely on libthread_db. Knowledge of how to traverse TLS data
structures is contained in this commit along with the next commit
containing architecture specific knowledge regarding TLS offsets,
registers, and such.
The new function 'linux_get_thread_local_address' is a gdbarch method.
It should be passed as an argument to
set_gdbarch_get_thread_local_address in architecture specific
<arch>-linux-tdep.c files which wish to offer internal TLS support.
The architecture specific tdep files need to define a get_tls_dtv_addr
method - as the name suggests, it needs to return the address of the
DTV (dynamic thread vector) via architecture specific means. This
usually entails fetching the thread pointer via a register or registers
assigned to this purpose, and then using that value to locate the
address of the DTV from within the TCB (thread control block).
Additionally, some architectures also need to provide a DTP offset,
which is used by the MUSL C library to adjust the value obtained
from a DTV entry to that of the start of the TLS block for a particular
thread. This is provided, when necessary, by a get_tls_dtp_offset
method.
Both methods, get_tls_dtv_addr and get_tls_dtp_offset, are registered
with data structures maintained by linux-tdep.c via the new function
linux_register_tls_methods(). Thus, for example, on RISC-V,
riscv_linux_init_abi() will make the following two calls, the first
for registering the internal get_thread_local_address gdbarch method
and the second for registering riscv-specific methods for obtaining
the DTV address and DTP offset:
set_gdbarch_get_thread_local_address (gdbarch,
linux_get_thread_local_address);
linux_register_tls_methods (info, gdbarch, riscv_linux_get_tls_dtv_addr,
riscv_linux_get_tls_dtp_offset);
Internal TLS support is provided for two C libraries, GLIBC, and MUSL.
Details for accessing the various TLS data structures differ between
these libraries. As a consequence, linux-tdep.h defines a new enum,
linux_libc, with values linux_libc_unknown, linux_libc_musl, and
linux_libc_glibc. A new static function libc_tls_sniffer uses
heuristics to (try to) decide whether a program was linked against
GLIBC or MUSL. Working out what the heuristics should be, especially
for statically linked binaries, turned out to be harder than I thought
it would be.
A new maintenance setting, force-internal-tls-address-lookup, has been
added, which, when set to 'on', will (as the name suggests) force the
internal TLS lookup mechanisms to be used. Otherwise, if thread_db
support is available (via the thread stratum), that will be preferred
since it should be more accurate. I expect that this setting will be
mostly used by test cases in the GDB test suite. The new test cases
that are part of this series all use it, with iterations using both
'on' and 'off' for all of the tests therein.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=24548
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31563
---
gdb/linux-tdep.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++
gdb/linux-tdep.h | 36 ++++++++
2 files changed, 247 insertions(+)
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 65ec221ef48..42ff1b3f2a1 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -48,6 +48,11 @@
#include <ctype.h>
#include <unordered_map>
+/* When true, force internal TLS address lookup instead of lookup via
+ the thread stratum. */
+
+static bool force_internal_tls_address_lookup = false;
+
/* This enum represents the values that the user can choose when
informing the Linux kernel about which memory mappings will be
dumped in a corefile. They are described in the file
@@ -198,6 +203,15 @@ struct linux_gdbarch_data
{
struct type *siginfo_type = nullptr;
int num_disp_step_buffers = 0;
+
+ /* Method for looking up TLS DTV. */
+ get_tls_dtv_addr_ftype *get_tls_dtv_addr = nullptr;
+
+ /* Method for looking up the TLS DTP offset. */
+ get_tls_dtp_offset_ftype *get_tls_dtp_offset = nullptr;
+
+ /* Cached libc value for TLS lookup purposes. */
+ enum linux_libc libc = linux_libc_unknown;
};
static const registry<gdbarch>::key<linux_gdbarch_data>
@@ -2744,6 +2758,172 @@ show_dump_excluded_mappings (struct ui_file *file, int from_tty,
" flag is %s.\n"), value);
}
+/* For TLS lookup purposes, use heuristics to decide whether program
+ was linked against MUSL or GLIBC. */
+
+static enum linux_libc
+libc_tls_sniffer (struct gdbarch *gdbarch)
+{
+ /* Check for cached libc value. */
+ linux_gdbarch_data *gdbarch_data = get_linux_gdbarch_data (gdbarch);
+ if (gdbarch_data->libc != linux_libc_unknown)
+ return gdbarch_data->libc;
+
+ linux_libc libc = linux_libc_unknown;
+
+ /* Fetch the program interpreter. */
+ std::optional<gdb::byte_vector> interp_name_holder
+ = svr4_find_program_interpreter ();
+ if (interp_name_holder)
+ {
+ /* A dynamically linked program linked against MUSL will have a
+ "ld-musl-" in its interpreter name. (Two examples of MUSL
+ interpreter names are "/lib/ld-musl-x86_64.so.1" and
+ "lib/ld-musl-aarch64.so.1".) If it's not found, assume GLIBC. */
+ const char *interp_name = (const char *) interp_name_holder->data ();
+ if (strstr (interp_name, "/ld-musl-") != nullptr)
+ libc = linux_libc_musl;
+ else
+ libc = linux_libc_glibc;
+ gdbarch_data->libc = libc;
+ return libc;
+ }
+
+ /* If there is no interpreter name, it's statically linked. For
+ programs with TLS data, a program statically linked against MUSL
+ will have the symbols 'main_tls' and 'builtin_tls'. If both of
+ these are present, assume that it was statically linked against
+ MUSL, otherwise assume GLIBC. */
+ if (lookup_minimal_symbol (current_program_space, "main_tls").minsym
+ != nullptr
+ && lookup_minimal_symbol (current_program_space, "builtin_tls").minsym
+ != nullptr)
+ libc = linux_libc_musl;
+ else
+ libc = linux_libc_glibc;
+ gdbarch_data->libc = libc;
+ return libc;
+}
+
+/* Implement gdbarch method, get_thread_local_address, for architectures
+ which provide a method for determining the DTV and possibly the DTP
+ offset. */
+
+CORE_ADDR
+linux_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid,
+ CORE_ADDR lm_addr, CORE_ADDR offset)
+{
+ linux_gdbarch_data *gdbarch_data = get_linux_gdbarch_data (gdbarch);
+
+ /* Use the target's get_thread_local_address method when:
+
+ - No method has been provided for finding the TLS DTV.
+
+ or
+
+ - The thread stratum has been pushed (at some point) onto the
+ target stack, except when 'force_internal_tls_address_lookup'
+ has been set.
+
+ The idea here is to prefer use of of the target's thread_stratum
+ method since it should be more accurate. */
+ if (gdbarch_data->get_tls_dtv_addr == nullptr
+ || (find_target_at (thread_stratum) != nullptr
+ && !force_internal_tls_address_lookup))
+ {
+ struct target_ops *target = current_inferior ()->top_target ();
+ return target->get_thread_local_address (ptid, lm_addr, offset);
+ }
+ else
+ {
+ /* Details, found below, regarding TLS layout is for the GNU C
+ library (glibc) and the MUSL C library (musl), circa 2024.
+ While some of this layout is defined by the TLS ABI, some of
+ it, such as how/where to find the DTV pointer in the TCB, is
+ not. A good source of ABI info for some architectures can be
+ found in "ELF Handling For Thread-Local Storage" by Ulrich
+ Drepper. That document is worth consulting even for
+ architectures not described there, since the general approach
+ and terminology is used regardless.
+
+ Some architectures, such as aarch64, are not described in
+ that document, so some details had to ferreted out using the
+ glibc source code. Likewise, the MUSL source code was
+ consulted for details which differ from GLIBC. */
+ enum linux_libc libc = libc_tls_sniffer (gdbarch);
+ int mod_id;
+ if (libc == linux_libc_glibc)
+ mod_id = glibc_link_map_to_tls_module_id (lm_addr);
+ else /* Assume MUSL. */
+ mod_id = musl_link_map_to_tls_module_id (lm_addr);
+ if (mod_id == 0)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to determine TLS module id"));
+
+ /* Use the architecture specific DTV fetcher to obtain the DTV. */
+ CORE_ADDR dtv_addr = gdbarch_data->get_tls_dtv_addr (gdbarch, ptid, libc);
+
+ /* In GLIBC, The DTV (dynamic thread vector) is an array of
+ structs consisting of two fields, the first of which is a
+ pointer to the TLS block of interest. (The second field is a
+ pointer that assists with memory management, but that's not
+ of interest here.) Also, the 0th entry is the generation
+ number, but although it's a single scalar, the 0th entry is
+ padded to be the same size as all the rest. Thus each
+ element of the DTV array is two pointers in size.
+
+ In MUSL, the DTV is simply an array of pointers. The 0th
+ entry is still the generation number, but contains no padding
+ aside from that which is needed to make it pointer sized. */
+ int m; /* Multiplier, for size of DTV entry. */
+ switch (libc)
+ {
+ case linux_libc_glibc:
+ m = 2;
+ break;
+ default:
+ m = 1;
+ break;
+ }
+
+ /* Obtain TLS block address. Module ids start at 1, so there's
+ no need to adjust it to skip over the 0th entry of the DTV,
+ which is the generation number. */
+ CORE_ADDR dtv_elem_addr
+ = dtv_addr + mod_id * m * (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ if (target_read_memory (dtv_elem_addr, buf.data (), buf.size ()) != 0)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch TLS block address"));
+ const struct builtin_type *builtin = builtin_type (gdbarch);
+ CORE_ADDR tls_block_addr = gdbarch_pointer_to_address
+ (gdbarch, builtin->builtin_data_ptr,
+ buf.data ());
+
+ /* When the TLS block addr is 0 or -1, this usually indicates that
+ the TLS storage hasn't been allocated yet. (In GLIBC, some
+ architectures use 0 while others use -1.) */
+ if (tls_block_addr == 0 || tls_block_addr == (CORE_ADDR) -1)
+ throw_error (TLS_NOT_ALLOCATED_YET_ERROR, _("TLS not allocated yet"));
+
+ /* MUSL (and perhaps other C libraries, though not GLIBC) have
+ TLS implementations for some architectures which, for some
+ reason, have DTV entries which must be negatively offset by
+ DTP_OFFSET in order to obtain the TLS block address.
+ DTP_OFFSET is a constant in the MUSL sources - these offsets,
+ when they're non-zero, seem to be either 0x800 or 0x8000,
+ and are present for riscv[32/64], powerpc[32/64], m68k, and
+ mips.
+
+ Use the architecture specific get_tls_dtp_offset method, if
+ present, to obtain this offset. */
+ ULONGEST dtp_offset
+ = gdbarch_data->get_tls_dtp_offset == nullptr
+ ? 0
+ : gdbarch_data->get_tls_dtp_offset (gdbarch, ptid, libc);
+
+ return tls_block_addr - dtp_offset + offset;
+ }
+}
+
/* To be called from the various GDB_OSABI_LINUX handlers for the
various GNU/Linux architectures and machine types.
@@ -2787,6 +2967,20 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
}
+/* See linux-tdep.h. */
+
+void
+linux_register_tls_methods (struct gdbarch_info info, struct gdbarch *gdbarch,
+ get_tls_dtv_addr_ftype *get_tls_dtv_addr,
+ get_tls_dtp_offset_ftype *get_tls_dtp_offset)
+{
+ gdb_assert (get_tls_dtv_addr != nullptr);
+
+ linux_gdbarch_data *gdbarch_data = get_linux_gdbarch_data (gdbarch);
+ gdbarch_data->get_tls_dtv_addr = get_tls_dtv_addr;
+ gdbarch_data->get_tls_dtp_offset = get_tls_dtp_offset;
+}
+
void _initialize_linux_tdep ();
void
_initialize_linux_tdep ()
@@ -2822,6 +3016,23 @@ VM_DONTDUMP flag (\"dd\" in /proc/PID/smaps) when generating the corefile. For\
more information about this file, refer to the manpage of proc(5) and core(5)."),
NULL, show_dump_excluded_mappings,
&setlist, &showlist);
+
+ add_setshow_boolean_cmd ("force-internal-tls-address-lookup", class_obscure,
+ &force_internal_tls_address_lookup, _("\
+Set to force internal TLS address lookup."), _("\
+Show whether GDB is forced to use internal TLS address lookup."), _("\
+When resolving addresses for TLS (Thread Local Storage) variables,\n\
+GDB will attempt to use facilities provided by the thread library (i.e.\n\
+libthread_db). If those facilities aren't available, GDB will fall\n\
+back to using some internal (to GDB), but possibly less accurate\n\
+mechanisms to resolve the addresses for TLS variables. When this flag\n\
+is set, GDB will force use of the fall-back TLS resolution mechanisms.\n\
+This flag is used by some GDB tests to ensure that the internal fallback\n\
+code is exercised and working as expected. The default is to not force\n\
+the internal fall-back mechanisms to be used."),
+ NULL, NULL,
+ &maintenance_set_cmdlist,
+ &maintenance_show_cmdlist);
}
/* Fetch (and possibly build) an appropriate `link_map_offsets' for
diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
index 66b03cabaa6..83f1538f033 100644
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -88,6 +88,35 @@ extern void linux_displaced_step_restore_all_in_ptid (inferior *parent_inf,
extern void linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
int num_disp_step_buffers);
+/* C library variants for TLS lookup. */
+
+enum linux_libc
+{
+ linux_libc_unknown,
+ linux_libc_musl,
+ linux_libc_glibc
+};
+
+/* Function type for "get_tls_dtv_addr" method. */
+
+typedef CORE_ADDR (get_tls_dtv_addr_ftype) (struct gdbarch *gdbarch,
+ ptid_t ptid,
+ enum linux_libc libc);
+
+/* Function type for "get_tls_dtp_offset" method. */
+
+typedef CORE_ADDR (get_tls_dtp_offset_ftype) (struct gdbarch *gdbarch,
+ ptid_t ptid,
+ enum linux_libc libc);
+
+/* Register architecture specific methods for fetching the TLS DTV
+ and TLS DTP, used by linux_get_thread_local_address. */
+
+extern void linux_register_tls_methods
+ (struct gdbarch_info info, struct gdbarch *gdbarch,
+ get_tls_dtv_addr_ftype *get_tls_dtv_addr,
+ get_tls_dtp_offset_ftype *get_tls_dtp_offset = nullptr);
+
extern int linux_is_uclinux (void);
/* Fetch the AT_HWCAP entry from auxv data AUXV. Use TARGET and GDBARCH to
@@ -117,4 +146,11 @@ extern CORE_ADDR linux_get_hwcap2 ();
extern struct link_map_offsets *linux_ilp32_fetch_link_map_offsets ();
extern struct link_map_offsets *linux_lp64_fetch_link_map_offsets ();
+/* Used as a gdbarch method for get_thread_local_address when the tdep
+ file also defines a suitable method for obtaining the TLS DTV.
+ See linux_init_abi(), above. */
+CORE_ADDR
+linux_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid,
+ CORE_ADDR lm_addr, CORE_ADDR offset);
+
#endif /* linux-tdep.h */
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 05/11] Internal TLS support for aarch64, x86_64, riscv, ppc64, and s390x
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
` (3 preceding siblings ...)
2024-10-25 2:26 ` [PATCH v3 04/11] Implement internal TLS address lookup for Linux targets Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 06/11] Internal, but disabled, TLS support for i386 Kevin Buettner
` (5 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner, Luis Machado
For each architecture, aarch64, x86_64, riscv, ppc64, and s390x,
this commit defines a suitable 'get_tls_dtv_addr' method and,
when necessary, a 'get_tls_dtp_offset' method.
It also registers linux_get_thread_local_address, defined in
linux-tdep.c (in an earlier commit), as the get_thread_local_address
gdbarch method. It also registers its architecture specific code
using linux_register_tls_methods().
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=24548
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31563
Reviewed-By: Luis Machado <luis.machado@arm.com>
---
gdb/aarch64-linux-tdep.c | 55 ++++++++++++++++++++++++++++
gdb/amd64-linux-tdep.c | 37 +++++++++++++++++++
gdb/ppc-linux-tdep.c | 62 ++++++++++++++++++++++++++++++++
gdb/riscv-linux-tdep.c | 78 ++++++++++++++++++++++++++++++++++++++++
gdb/s390-linux-tdep.c | 43 ++++++++++++++++++++++
5 files changed, 275 insertions(+)
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index c608a84bc71..89423a233f2 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -35,6 +35,7 @@
#include "target/target.h"
#include "expop.h"
#include "auxv.h"
+#include "inferior.h"
#include "regcache.h"
#include "regset.h"
@@ -2693,6 +2694,57 @@ aarch64_use_target_description_from_corefile_notes (gdbarch *gdbarch,
return true;
}
+/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID.
+ Throw a suitable TLS error if something goes wrong. */
+
+static CORE_ADDR
+aarch64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
+ linux_libc libc)
+{
+ /* On aarch64, the thread pointer is found in the TPIDR register.
+ Note that this is the first register in the TLS feature - see
+ features/aarch64-tls.c - and it will always be present. */
+ regcache *regcache
+ = get_thread_arch_regcache (current_inferior (), ptid, gdbarch);
+ aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
+ target_fetch_registers (regcache, tdep->tls_regnum_base);
+ ULONGEST thr_ptr;
+ if (regcache->cooked_read (tdep->tls_regnum_base, &thr_ptr) != REG_VALID)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer"));
+
+ CORE_ADDR dtv_ptr_addr;
+ switch (libc)
+ {
+ case linux_libc_musl:
+ /* MUSL: The DTV pointer is found at the very end of the pthread
+ struct which is located *before* the thread pointer. I.e.
+ the thread pointer will be just beyond the end of the struct,
+ so the address of the DTV pointer is found one pointer-size
+ before the thread pointer. */
+ dtv_ptr_addr = thr_ptr - (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ break;
+ case linux_libc_glibc:
+ /* GLIBC: The thread pointer (tpidr) points at the TCB (thread control
+ block). On aarch64, this struct (tcbhead_t) is defined to
+ contain two pointers. The first is a pointer to the DTV and
+ the second is a pointer to private data. So the DTV pointer
+ address is the same as the thread pointer. */
+ dtv_ptr_addr = thr_ptr;
+ break;
+ default:
+ throw_error (TLS_GENERIC_ERROR, _("Unknown aarch64 C library"));
+ break;
+ }
+ gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address"));
+
+ const struct builtin_type *builtin = builtin_type (gdbarch);
+ CORE_ADDR dtv_addr = gdbarch_pointer_to_address
+ (gdbarch, builtin->builtin_data_ptr, buf.data ());
+ return dtv_addr;
+}
+
static void
aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
@@ -2714,6 +2766,9 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
+ set_gdbarch_get_thread_local_address (gdbarch,
+ linux_get_thread_local_address);
+ linux_register_tls_methods (info, gdbarch, aarch64_linux_get_tls_dtv_addr);
/* Shared library handling. */
set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 77de8211d86..7512448d987 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -34,6 +34,7 @@
#include "i386-linux-tdep.h"
#include "linux-tdep.h"
#include "gdbsupport/x86-xstate.h"
+#include "inferior.h"
#include "amd64-tdep.h"
#include "solib-svr4.h"
@@ -1767,6 +1768,39 @@ amd64_dtrace_parse_probe_argument (struct gdbarch *gdbarch,
}
}
+/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID.
+ Throw a suitable TLS error if something goes wrong. */
+
+static CORE_ADDR
+amd64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
+ enum linux_libc libc)
+{
+ /* On x86-64, the thread pointer is found in the fsbase register. */
+ regcache *regcache
+ = get_thread_arch_regcache (current_inferior (), ptid, gdbarch);
+ target_fetch_registers (regcache, AMD64_FSBASE_REGNUM);
+ ULONGEST fsbase;
+ if (regcache->cooked_read (AMD64_FSBASE_REGNUM, &fsbase) != REG_VALID)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer"));
+
+ /* The thread pointer (fsbase) points at the TCB (thread control
+ block). The first two members of this struct are both pointers,
+ where the first will be a pointer to the TCB (i.e. it points at
+ itself) and the second will be a pointer to the DTV (dynamic
+ thread vector). There are many other fields too, but the one
+ we care about here is the DTV pointer. Compute the address
+ of the DTV pointer, fetch it, and convert it to an address. */
+ CORE_ADDR dtv_ptr_addr = fsbase + gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
+ gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address"));
+
+ const struct builtin_type *builtin = builtin_type (gdbarch);
+ CORE_ADDR dtv_addr = gdbarch_pointer_to_address
+ (gdbarch, builtin->builtin_data_ptr, buf.data ());
+ return dtv_addr;
+}
+
static void
amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch,
int num_disp_step_buffers)
@@ -1797,6 +1831,9 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch,
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
+ set_gdbarch_get_thread_local_address (gdbarch,
+ linux_get_thread_local_address);
+ linux_register_tls_methods (info, gdbarch, amd64_linux_get_tls_dtv_addr);
/* GNU/Linux uses SVR4-style shared libraries. */
set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index 8a5eea765c2..42a4bc71263 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -2070,6 +2070,63 @@ ppc64_linux_gcc_target_options (struct gdbarch *gdbarch)
return "";
}
+/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID.
+ Throw a suitable TLS error if something goes wrong. */
+
+static CORE_ADDR
+ppc64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
+ enum linux_libc libc)
+{
+ /* On ppc64, the thread pointer is found in r13. Fetch this
+ register. */
+ regcache *regcache
+ = get_thread_arch_regcache (current_inferior (), ptid, gdbarch);
+ int thread_pointer_regnum = PPC_R0_REGNUM + 13;
+ target_fetch_registers (regcache, thread_pointer_regnum);
+ ULONGEST thr_ptr;
+ if (regcache->cooked_read (thread_pointer_regnum, &thr_ptr) != REG_VALID)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer"));
+
+ /* The thread pointer (r13) is an address that is 0x7000 ahead of
+ the *end* of the TCB (thread control block). The field
+ holding the DTV address is at the very end of the TCB.
+ Therefore, the DTV pointer address can be found by
+ subtracting (0x7000+8) from the thread pointer. Compute the
+ address of the DTV pointer, fetch it, and convert it to an
+ address. */
+ CORE_ADDR dtv_ptr_addr = thr_ptr - 0x7000 - 8;
+ gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address"));
+
+ const struct builtin_type *builtin = builtin_type (gdbarch);
+ CORE_ADDR dtv_addr = gdbarch_pointer_to_address
+ (gdbarch, builtin->builtin_data_ptr, buf.data ());
+ return dtv_addr;
+}
+
+/* For internal TLS lookup, return the DTP offset, which is the offset
+ to subtract from a DTV entry, in order to obtain the address of the
+ TLS block. */
+
+static ULONGEST
+ppc_linux_get_tls_dtp_offset (struct gdbarch *gdbarch, ptid_t ptid,
+ linux_libc libc)
+{
+ if (libc == linux_libc_musl)
+ {
+ /* This value is DTP_OFFSET, which represents the value to
+ subtract from the DTV entry. For PPC, it can be found in
+ MUSL's arch/powerpc64/pthread_arch.h and
+ arch/powerpc32/pthread_arch.h. (Both values are the same.)
+ It represents the value to subtract from the DTV entry, once
+ it has been fetched from the DTV array. */
+ return 0x8000;
+ }
+ else
+ return 0;
+}
+
static displaced_step_prepare_status
ppc_linux_displaced_step_prepare (gdbarch *arch, thread_info *thread,
CORE_ADDR &displaced_pc)
@@ -2283,6 +2340,11 @@ ppc_linux_init_abi (struct gdbarch_info info,
set_gdbarch_gnu_triplet_regexp (gdbarch, ppc64_gnu_triplet_regexp);
/* Set GCC target options. */
set_gdbarch_gcc_target_options (gdbarch, ppc64_linux_gcc_target_options);
+ /* Internal thread local address support. */
+ set_gdbarch_get_thread_local_address (gdbarch,
+ linux_get_thread_local_address);
+ linux_register_tls_methods (info, gdbarch, ppc64_linux_get_tls_dtv_addr,
+ ppc_linux_get_tls_dtp_offset);
}
set_gdbarch_core_read_description (gdbarch, ppc_linux_core_read_description);
diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
index ff478cf4c28..31d90a32ce4 100644
--- a/gdb/riscv-linux-tdep.c
+++ b/gdb/riscv-linux-tdep.c
@@ -25,6 +25,7 @@
#include "tramp-frame.h"
#include "trad-frame.h"
#include "gdbarch.h"
+#include "inferior.h"
/* The following value is derived from __NR_rt_sigreturn in
<include/uapi/asm-generic/unistd.h> from the Linux source tree. */
@@ -173,6 +174,79 @@ riscv_linux_syscall_next_pc (const frame_info_ptr &frame)
return pc + 4 /* Length of the ECALL insn. */;
}
+/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID.
+ Throw a suitable TLS error if something goes wrong. */
+
+static CORE_ADDR
+riscv_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
+ linux_libc libc)
+{
+ /* On RISC-V, the thread pointer is found in TP. */
+ regcache *regcache
+ = get_thread_arch_regcache (current_inferior (), ptid, gdbarch);
+ int thread_pointer_regnum = RISCV_TP_REGNUM;
+ target_fetch_registers (regcache, thread_pointer_regnum);
+ ULONGEST thr_ptr;
+ if (regcache->cooked_read (thread_pointer_regnum, &thr_ptr) != REG_VALID)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer"));
+
+ CORE_ADDR dtv_ptr_addr;
+ switch (libc)
+ {
+ case linux_libc_musl:
+ /* MUSL: The DTV pointer is found at the very end of the pthread
+ struct which is located *before* the thread pointer. I.e.
+ the thread pointer will be just beyond the end of the struct,
+ so the address of the DTV pointer is found one pointer-size
+ before the thread pointer. */
+ dtv_ptr_addr
+ = thr_ptr - (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ break;
+ case linux_libc_glibc:
+ /* GLIBC: The thread pointer (TP) points just beyond the end of
+ the TCB (thread control block). On RISC-V, this struct
+ (tcbhead_t) is defined to contain two pointers. The first is
+ a pointer to the DTV and the second is a pointer to private
+ data. So the DTV pointer address is 16 bytes (i.e. the size of
+ two pointers) before thread pointer. */
+
+ dtv_ptr_addr
+ = thr_ptr - 2 * (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ break;
+ default:
+ throw_error (TLS_GENERIC_ERROR, _("Unknown RISC-V C library"));
+ break;
+ }
+
+ gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address"));
+
+ const struct builtin_type *builtin = builtin_type (gdbarch);
+ CORE_ADDR dtv_addr = gdbarch_pointer_to_address
+ (gdbarch, builtin->builtin_data_ptr, buf.data ());
+ return dtv_addr;
+}
+
+/* For internal TLS lookup, return the DTP offset, which is the offset
+ to subtract from a DTV entry, in order to obtain the address of the
+ TLS block. */
+
+static ULONGEST
+riscv_linux_get_tls_dtp_offset (struct gdbarch *gdbarch, ptid_t ptid,
+ linux_libc libc)
+{
+ if (libc == linux_libc_musl)
+ {
+ /* This value is DTP_OFFSET in MUSL's arch/riscv64/pthread_arch.h.
+ It represents the value to subtract from the DTV entry, once
+ it has been loaded. */
+ return 0x800;
+ }
+ else
+ return 0;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -198,6 +272,10 @@ riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
+ set_gdbarch_get_thread_local_address (gdbarch,
+ linux_get_thread_local_address);
+ linux_register_tls_methods (info, gdbarch, riscv_linux_get_tls_dtv_addr,
+ riscv_linux_get_tls_dtp_offset);
set_gdbarch_iterate_over_regset_sections
(gdbarch, riscv_linux_iterate_over_regset_sections);
diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c
index bc1db550d2e..17055d9f7b5 100644
--- a/gdb/s390-linux-tdep.c
+++ b/gdb/s390-linux-tdep.c
@@ -40,6 +40,7 @@
#include "target.h"
#include "trad-frame.h"
#include "xml-syscall.h"
+#include "inferior.h"
#include "features/s390-linux32v1.c"
#include "features/s390-linux32v2.c"
@@ -1123,6 +1124,45 @@ s390_init_linux_record_tdep (struct linux_record_tdep *record_tdep,
record_tdep->ioctl_FIOQSIZE = 0x545e;
}
+/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID.
+ Throw a suitable TLS error if something goes wrong. */
+
+static CORE_ADDR
+s390_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
+ enum linux_libc libc)
+{
+ /* On S390, the thread pointer is found in two registers A0 and A1
+ (or, using gdb naming, acr0 and acr1) A0 contains the top 32
+ bits of the address and A1 contains the bottom 32 bits. */
+ regcache *regcache
+ = get_thread_arch_regcache (current_inferior (), ptid, gdbarch);
+ target_fetch_registers (regcache, S390_A0_REGNUM);
+ target_fetch_registers (regcache, S390_A1_REGNUM);
+ ULONGEST thr_ptr_lo, thr_ptr_hi, thr_ptr;
+ if (regcache->cooked_read (S390_A0_REGNUM, &thr_ptr_hi) != REG_VALID
+ || regcache->cooked_read (S390_A1_REGNUM, &thr_ptr_lo) != REG_VALID)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer"));
+ thr_ptr = (thr_ptr_hi << 32) + thr_ptr_lo;
+
+ /* The thread pointer points at the TCB (thread control block). The
+ first two members of this struct are both pointers, where the
+ first will be a pointer to the TCB (i.e. it points at itself)
+ and the second will be a pointer to the DTV (dynamic thread
+ vector). There are many other fields too, but the one we care
+ about here is the DTV pointer. Compute the address of the DTV
+ pointer, fetch it, and convert it to an address. */
+ CORE_ADDR dtv_ptr_addr
+ = thr_ptr + gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
+ gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT);
+ if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address"));
+
+ const struct builtin_type *builtin = builtin_type (gdbarch);
+ CORE_ADDR dtv_addr = gdbarch_pointer_to_address
+ (gdbarch, builtin->builtin_data_ptr, buf.data ());
+ return dtv_addr;
+}
+
/* Initialize OSABI common for GNU/Linux on 31- and 64-bit systems. */
static void
@@ -1151,6 +1191,9 @@ s390_linux_init_abi_any (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
+ set_gdbarch_get_thread_local_address (gdbarch,
+ linux_get_thread_local_address);
+ linux_register_tls_methods (info, gdbarch, s390_linux_get_tls_dtv_addr);
/* Support reverse debugging. */
set_gdbarch_process_record_signal (gdbarch, s390_linux_record_signal);
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 06/11] Internal, but disabled, TLS support for i386
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
` (4 preceding siblings ...)
2024-10-25 2:26 ` [PATCH v3 05/11] Internal TLS support for aarch64, x86_64, riscv, ppc64, and s390x Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 07/11] Delete disabled i386 internal TLS support Kevin Buettner
` (4 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
This commit shows how internal TLS address lookup support could
be implemented for the i386 target.
Unfortunately, it doesn't work due to I386_GSBASE_REGNUM being
unavailable for Linux targets. I looked at trying to access the
gsbase register via PTRACE_GET_THREAD_AREA, but did not understand
it well enough to finish it. Since the i386 target is much less
important than it used to be, I gave up working on it.
I don't want to leave this disabled code in our sources, so I
will delete it in the next commit, however, this commit will be
in our git repo, so it'll be available for someone with sufficient
interest in the i386 target to look at.
---
gdb/i386-linux-tdep.c | 44 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 9dec83ac753..8e9ffe984a3 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -753,6 +753,45 @@ i386_linux_displaced_step_copy_insn (struct gdbarch *gdbarch,
return closure_;
}
+#if 0
+/* Disabled because fetching I386_GSBASE_REGNUM causes an internal
+ error. */
+
+/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID.
+ Throw a suitable TLS error if something goes wrong. */
+
+static CORE_ADDR
+i386_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
+ enum linux_libc libc)
+{
+ /* On i386, the thread pointer is found in the gsbase register. */
+ regcache *regcache
+ = get_thread_arch_regcache (current_inferior (), ptid, gdbarch);
+ target_fetch_registers (regcache, I386_GSBASE_REGNUM);
+ ULONGEST gsbase;
+ if (regcache->cooked_read (I386_GSBASE_REGNUM, &gsbase) != REG_VALID)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer"));
+
+ /* The thread pointer (gsbase) points at the TCB (thread control
+ block). The first two members of this struct are both pointers,
+ where the first will be a pointer to the TCB (i.e. it points at
+ itself) and the second will be a pointer to the DTV (dynamic
+ thread vector). There are many other fields too, but the one
+ we care about here is the DTV pointer. Compute the address
+ of the DTV pointer, fetch it, and convert it to an address. */
+ CORE_ADDR dtv_ptr_addr
+ = gsbase + gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
+ gdb_byte buf[gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT];
+ if (target_read_memory (dtv_ptr_addr, buf, sizeof buf) != 0)
+ throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address"));
+
+ const struct builtin_type *builtin = builtin_type (gdbarch);
+ CORE_ADDR dtv_addr = gdbarch_pointer_to_address
+ (gdbarch, builtin->builtin_data_ptr, buf);
+ return dtv_addr;
+}
+#endif
+
static void
i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
@@ -988,6 +1027,11 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
+#if 0
+ set_gdbarch_get_thread_local_address (gdbarch,
+ linux_get_thread_local_address);
+ linux_register_tls_methods (info, gdbarch, i386_linux_get_tls_dtv_addr);
+#endif
/* Core file support. */
set_gdbarch_iterate_over_regset_sections
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 07/11] Delete disabled i386 internal TLS support
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
` (5 preceding siblings ...)
2024-10-25 2:26 ` [PATCH v3 06/11] Internal, but disabled, TLS support for i386 Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 08/11] New test - gdb.base/tls-nothreads.exp Kevin Buettner
` (3 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
As mentioned in the previous commit, this commit deletes the disabled
code which could be used to implement internal TLS support for the
i386 target.
---
gdb/i386-linux-tdep.c | 44 -------------------------------------------
1 file changed, 44 deletions(-)
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 8e9ffe984a3..9dec83ac753 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -753,45 +753,6 @@ i386_linux_displaced_step_copy_insn (struct gdbarch *gdbarch,
return closure_;
}
-#if 0
-/* Disabled because fetching I386_GSBASE_REGNUM causes an internal
- error. */
-
-/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID.
- Throw a suitable TLS error if something goes wrong. */
-
-static CORE_ADDR
-i386_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
- enum linux_libc libc)
-{
- /* On i386, the thread pointer is found in the gsbase register. */
- regcache *regcache
- = get_thread_arch_regcache (current_inferior (), ptid, gdbarch);
- target_fetch_registers (regcache, I386_GSBASE_REGNUM);
- ULONGEST gsbase;
- if (regcache->cooked_read (I386_GSBASE_REGNUM, &gsbase) != REG_VALID)
- throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer"));
-
- /* The thread pointer (gsbase) points at the TCB (thread control
- block). The first two members of this struct are both pointers,
- where the first will be a pointer to the TCB (i.e. it points at
- itself) and the second will be a pointer to the DTV (dynamic
- thread vector). There are many other fields too, but the one
- we care about here is the DTV pointer. Compute the address
- of the DTV pointer, fetch it, and convert it to an address. */
- CORE_ADDR dtv_ptr_addr
- = gsbase + gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
- gdb_byte buf[gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT];
- if (target_read_memory (dtv_ptr_addr, buf, sizeof buf) != 0)
- throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address"));
-
- const struct builtin_type *builtin = builtin_type (gdbarch);
- CORE_ADDR dtv_addr = gdbarch_pointer_to_address
- (gdbarch, builtin->builtin_data_ptr, buf);
- return dtv_addr;
-}
-#endif
-
static void
i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
@@ -1027,11 +988,6 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Enable TLS support. */
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
-#if 0
- set_gdbarch_get_thread_local_address (gdbarch,
- linux_get_thread_local_address);
- linux_register_tls_methods (info, gdbarch, i386_linux_get_tls_dtv_addr);
-#endif
/* Core file support. */
set_gdbarch_iterate_over_regset_sections
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 08/11] New test - gdb.base/tls-nothreads.exp
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
` (6 preceding siblings ...)
2024-10-25 2:26 ` [PATCH v3 07/11] Delete disabled i386 internal TLS support Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 09/11] New test - gdb.base/tls-multiobj.exp Kevin Buettner
` (2 subsequent siblings)
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
This commit introduces a new test, gdb.base/tls-nothreads.exp.
It has a test case, a C file, which has several TLS variables in the
main program, which, once compiled and linked, should end up (in ELF
files) in .tdata and .tbss. The test compiles the program in a number
of different ways, making sure that each variable is accessible
regardless of how it was compiled.
Note that some of the compilation scenarios end up with a statically
linked executable. Prior to this series of commits, accessing TLS
variables from a statically linked program on Linux did not work.
For certain targets (x86_64, aarch64, s390x, riscv, and ppc64),
all on Linux, support has been added to GDB for accessing thread
local storage in statically linked executables. This test is
important for testing those build scenarios.
But it's also important to make sure that GDB's internal TLS support
works for other scenarios too. In order to accomplish that, the
tests are also run in a mode which forces the internal support to
be used.
It also adds a new file, gdb.base/tls-common.exp.tcl, which includes
some common definitions used by the three new TLS tests, including
the one added by this commit. In particular, it sets a TCL variable,
'internal_tls_linux_targets' which list the targets mentioned earlier.
This means that as internal TLS support is added for other targets,
the target should be listed in just one file as opposed to three
(or more if other tests using tls-common.exp.tcl are added).
---
gdb/testsuite/gdb.base/tls-common.exp.tcl | 50 +++++
gdb/testsuite/gdb.base/tls-nothreads.c | 57 +++++
gdb/testsuite/gdb.base/tls-nothreads.exp | 248 ++++++++++++++++++++++
3 files changed, 355 insertions(+)
create mode 100644 gdb/testsuite/gdb.base/tls-common.exp.tcl
create mode 100644 gdb/testsuite/gdb.base/tls-nothreads.c
create mode 100644 gdb/testsuite/gdb.base/tls-nothreads.exp
diff --git a/gdb/testsuite/gdb.base/tls-common.exp.tcl b/gdb/testsuite/gdb.base/tls-common.exp.tcl
new file mode 100644
index 00000000000..7aa7f466aa1
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-common.exp.tcl
@@ -0,0 +1,50 @@
+# Copyright 2024 Free Software Foundation, Inc.
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# Require statement, variables and procs used by tls-nothreads.exp,
+# tls-multiobj.exp, and tls-dlobj.exp.
+
+# The tests listed above are known to work for the targets listed on
+# the 'require' line, below.
+#
+# At the moment, only the Linux target is listed, but, ideally, these
+# tests should be run on other targets too. E.g, testing on FreeBSD
+# shows many failures which should be addressed in some fashion before
+# enabling it for that target.
+
+require {is_any_target "*-*-linux*"}
+
+# These are the targets which have support for internal TLS lookup:
+
+set internal_tls_linux_targets {"x86_64-*-linux*" "aarch64-*-linux*"
+ "riscv*-*-linux*" "powerpc64*-*-linux*"
+ "s390x*-*-linux*"}
+
+# The "maint set force-internal-tls-address-lookup" command is only
+# available for certain Linux architectures. Don't attempt to force
+# use of internal TLS support for architectures which don't support
+# it.
+
+if [is_any_target {*}$internal_tls_linux_targets] {
+ set internal_tls_iters { false true }
+} else {
+ set internal_tls_iters { false }
+}
+
+# Set up a kfail with message KFAIL_MSG when KFAIL_COND holds, then
+# issue gdb_test with command CMD and regular expression RE.
+
+proc gdb_test_with_kfail {cmd re kfail_cond kfail_msg} {
+ if [uplevel 1 [list expr $kfail_cond]] {
+ setup_kfail $kfail_msg *-*-*
+ }
+ gdb_test $cmd $re
+}
diff --git a/gdb/testsuite/gdb.base/tls-nothreads.c b/gdb/testsuite/gdb.base/tls-nothreads.c
new file mode 100644
index 00000000000..b3aaa3383e7
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-nothreads.c
@@ -0,0 +1,57 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+__thread int tls_tbss_1;
+__thread int tls_tbss_2;
+__thread int tls_tbss_3;
+
+__thread int tls_tdata_1 = 21;
+__thread int tls_tdata_2 = 22;
+__thread int tls_tdata_3 = 23;
+
+volatile int data;
+
+void
+use_it (int a)
+{
+ data = a;
+}
+
+int
+main (int argc, char **argv)
+{
+ use_it (-1);
+
+ tls_tbss_1 = 24; /* main-breakpoint-1 */
+ tls_tbss_2 = 25;
+ tls_tbss_3 = 26;
+
+ tls_tdata_1 = 42;
+ tls_tdata_2 = 43;
+ tls_tdata_3 = 44;
+
+ use_it (tls_tbss_1);
+ use_it (tls_tbss_2);
+ use_it (tls_tbss_3);
+ use_it (tls_tdata_1);
+ use_it (tls_tdata_2);
+ use_it (tls_tdata_3);
+
+ use_it (100); /* main-breakpoint-2 */
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.base/tls-nothreads.exp b/gdb/testsuite/gdb.base/tls-nothreads.exp
new file mode 100644
index 00000000000..d235d1a2ab5
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-nothreads.exp
@@ -0,0 +1,248 @@
+# Copyright 2024 Free Software Foundation, Inc.
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# Using different compilation/linking scenarios, attempt to access
+# thread-local variables in a non-threaded program. Also test that
+# GDB internal TLS lookup works correctly.
+
+source $srcdir/$subdir/tls-common.exp.tcl
+
+standard_testfile
+
+proc do_tests {force_internal_tls {do_kfail_tls_access 0}} {
+ clean_restart $::binfile
+ if ![runto_main] {
+ return
+ }
+
+ if $force_internal_tls {
+ gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+ }
+
+ if { $do_kfail_tls_access && [istarget "*-*-linux*"] } {
+ # Turn off do_kfail_tls_access when libthread_db is loaded.
+ # This can happen for the default case when testing x86_64
+ # w/ -m32 using glibc versions 2.34 or newer.
+ gdb_test_multiple "maint check libthread-db" "Check for loaded libthread_db" {
+ -re -wrap "libthread_db integrity checks passed." {
+ set do_kfail_tls_access 0
+ pass $gdb_test_name
+ }
+ -re -wrap "No libthread_db loaded" {
+ pass $gdb_test_name
+ }
+ }
+ # Also turn off do_kfail_tls_access when connected to a
+ # gdbserver and we observe that accessing a TLS variable
+ # works.
+ if [target_is_gdbserver] {
+ gdb_test_multiple "print tls_tbss_1" "Check TLS accessibility when connected to a gdbserver" {
+ -re -wrap "= 0" {
+ set do_kfail_tls_access 0
+ pass $gdb_test_name
+ }
+ -re -wrap "Remote target failed to process qGetTLSAddr request" {
+ pass $gdb_test_name
+ }
+ }
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"]
+ gdb_continue_to_breakpoint "main-breakpoint-1"
+
+ set t $do_kfail_tls_access
+ set m "tls not available"
+ with_test_prefix "before assignments" {
+ gdb_test_with_kfail "print tls_tbss_1" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_tbss_2" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_tbss_3" ".* = 0" $t $m
+
+ gdb_test_with_kfail "print tls_tdata_1" ".* = 21" $t $m
+ gdb_test_with_kfail "print tls_tdata_2" ".* = 22" $t $m
+ gdb_test_with_kfail "print tls_tdata_3" ".* = 23" $t $m
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"]
+ gdb_continue_to_breakpoint "main-breakpoint-2"
+
+ with_test_prefix "after assignments" {
+ gdb_test_with_kfail "print tls_tbss_1" ".* = 24" $t $m
+ gdb_test_with_kfail "print tls_tbss_2" ".* = 25" $t $m
+ gdb_test_with_kfail "print tls_tbss_3" ".* = 26" $t $m
+
+ gdb_test_with_kfail "print tls_tdata_1" ".* = 42" $t $m
+ gdb_test_with_kfail "print tls_tdata_2" ".* = 43" $t $m
+ gdb_test_with_kfail "print tls_tdata_3" ".* = 44" $t $m
+ }
+
+ # Make a core file now, but save testing using it until the end
+ # in case core files are not supported.
+ set corefile ${::binfile}.core
+ set core_supported 0
+ if { ![is_remote host] } {
+ set core_supported [gdb_gcore_cmd $corefile "save corefile"]
+ }
+
+ # Now continue to end and see what happens when attempting to
+ # access a TLS variable when the program is no longer running.
+ gdb_continue_to_end
+ with_test_prefix "after exit" {
+ gdb_test "print tls_tbss_1" \
+ "Cannot read `tls_tbss_1' without registers"
+ }
+
+ with_test_prefix "stripped" {
+ set binfile_stripped "${::binfile}.stripped"
+ set objcopy [gdb_find_objcopy]
+ set cmd "$objcopy --strip-debug ${::binfile} $binfile_stripped"
+ if ![catch "exec $cmd" cmd_output] {
+ clean_restart $binfile_stripped
+ if ![runto_main] {
+ return
+ }
+
+ if $force_internal_tls {
+ gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+ }
+
+ # While there are no debug (e.g. DWARF) symbols, there
+ # are minimal symbols, so we should be able to place a
+ # breakpoint in use_it and continue to it. Continuing
+ # twice should put us past the assignments, at which point
+ # we can see if the TLS variables are still accessible.
+ gdb_test "break use_it" "Breakpoint 2 at $::hex"
+ gdb_test "continue" "Breakpoint 2, $::hex in use_it.*"
+ gdb_test "continue" "Breakpoint 2, $::hex in use_it.*" "continue 2"
+
+ # Note that a cast has been added in order to avoid the
+ # "...has unknown type; cast it to its declared type"
+ # problem.
+ gdb_test_with_kfail "print (int) tls_tbss_1" ".* = 24" $t $m
+ gdb_test_with_kfail "print (int) tls_tbss_2" ".* = 25" $t $m
+ gdb_test_with_kfail "print (int) tls_tbss_3" ".* = 26" $t $m
+
+ gdb_test_with_kfail "print (int) tls_tdata_1" ".* = 42" $t $m
+ gdb_test_with_kfail "print (int) tls_tdata_2" ".* = 43" $t $m
+ gdb_test_with_kfail "print (int) tls_tdata_3" ".* = 44" $t $m
+
+ # Get rid of the "use_it" breakpoint
+ gdb_test_no_output "del 2"
+
+ # Continue to program exit
+ gdb_continue_to_end
+
+ # TLS variables should not be accessible after program exit
+ # (This case initially caused GDB to crash during development
+ # of GDB-internal TLS lookup support.)
+ with_test_prefix "after exit" {
+ gdb_test "print (int) tls_tbss_1" \
+ "Cannot determine address of TLS symbol `tls_tbss_1' without registers"
+ }
+ }
+ }
+
+ # Finish test early if no core file was made.
+ if !$core_supported {
+ return
+ }
+
+ clean_restart $::binfile
+
+ set core_loaded [gdb_core_cmd $corefile "load corefile"]
+ if { $core_loaded == -1 } {
+ return
+ }
+
+ with_test_prefix "core file" {
+ if $force_internal_tls {
+ gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+ }
+
+ gdb_test_with_kfail "print tls_tbss_1" ".* = 24" $t $m
+ gdb_test_with_kfail "print tls_tbss_2" ".* = 25" $t $m
+ gdb_test_with_kfail "print tls_tbss_3" ".* = 26" $t $m
+
+ gdb_test_with_kfail "print tls_tdata_1" ".* = 42" $t $m
+ gdb_test_with_kfail "print tls_tdata_2" ".* = 43" $t $m
+ gdb_test_with_kfail "print tls_tdata_3" ".* = 44" $t $m
+ }
+}
+
+# Certain linux target architectures implement support for internal
+# TLS lookup which is used when thread stratum support (via
+# libthread_db) is missing or when the linux-only GDB maintenance
+# setting 'force-internal-tls-address-lookup' is 'on'. Thus for some
+# of the testing scenarios, such as statically linked executables,
+# this internal support will be used. Set 'do_kfail_tls_access' to 1
+# for those architectures which don't implement internal TLS support.
+if {[istarget *-*-linux*]
+ && ![is_any_target {*}$internal_tls_linux_targets]} {
+ set do_kfail_tls_access 1
+} elseif {[istarget *-*-linux*] && [is_x86_like_target]} {
+ # This covers the case of x86_64 with -m32:
+ set do_kfail_tls_access 1
+} else {
+ set do_kfail_tls_access 0
+}
+
+set binprefix $binfile
+
+with_test_prefix "default" {
+ set binfile $binprefix-default
+ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+ untested "failed to compile"
+ } else {
+ foreach_with_prefix force_internal_tls $internal_tls_iters {
+ # Depending on glibc version, it might not be appropriate
+ # for do_kfail_tls_access to be set here. That will be
+ # handled in 'do_tests', disabling it if necessary.
+ #
+ # Specifically, glibc versions 2.34 and later have the
+ # thread library (and libthread_db availability) in
+ # programs not linked against libpthread.so
+ do_tests $force_internal_tls $do_kfail_tls_access
+ }
+ }
+}
+
+with_test_prefix "static" {
+ set binfile $binprefix-static
+ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } {
+ untested "failed to compile"
+ } else {
+ foreach_with_prefix force_internal_tls $internal_tls_iters {
+ do_tests $force_internal_tls $do_kfail_tls_access
+ }
+ }
+}
+
+with_test_prefix "pthreads" {
+ set binfile $binprefix-pthreads
+ if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+ untested "failed to compile"
+ } else {
+ foreach_with_prefix force_internal_tls $internal_tls_iters {
+ do_tests $force_internal_tls
+ }
+ }
+}
+
+with_test_prefix "pthreads-static" {
+ set binfile $binprefix-pthreads-static
+ if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } {
+ untested "failed to compile"
+ } else {
+ foreach_with_prefix force_internal_tls $internal_tls_iters {
+ do_tests $force_internal_tls $do_kfail_tls_access
+ }
+ }
+}
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 09/11] New test - gdb.base/tls-multiobj.exp
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
` (7 preceding siblings ...)
2024-10-25 2:26 ` [PATCH v3 08/11] New test - gdb.base/tls-nothreads.exp Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 10/11] New test - gdb.base/tls-dlobj.exp Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 11/11] Add TLS NEWS entry and document 'set force-internal-tls-address-lookup' command Kevin Buettner
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
This test exercises GDB's internal TLS support when both the main
program and several shared libraries have TLS variables. It also
tests existing (non-internal) TLS support too.
It tests using two compilation scenarios, "default", in which
libpthread is not linked with the program and libraries as well
as one which does use libpthread.
It tests link map address to module id mapping code in GDB
in addition to the ability of GDB to traverse TLS data structures
with several libraries in play.
---
gdb/testsuite/gdb.base/tls-multiobj.c | 89 +++++++++
gdb/testsuite/gdb.base/tls-multiobj.exp | 230 ++++++++++++++++++++++++
gdb/testsuite/gdb.base/tls-multiobj1.c | 26 +++
gdb/testsuite/gdb.base/tls-multiobj2.c | 26 +++
gdb/testsuite/gdb.base/tls-multiobj3.c | 26 +++
5 files changed, 397 insertions(+)
create mode 100644 gdb/testsuite/gdb.base/tls-multiobj.c
create mode 100644 gdb/testsuite/gdb.base/tls-multiobj.exp
create mode 100644 gdb/testsuite/gdb.base/tls-multiobj1.c
create mode 100644 gdb/testsuite/gdb.base/tls-multiobj2.c
create mode 100644 gdb/testsuite/gdb.base/tls-multiobj3.c
diff --git a/gdb/testsuite/gdb.base/tls-multiobj.c b/gdb/testsuite/gdb.base/tls-multiobj.c
new file mode 100644
index 00000000000..10e67da54d8
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-multiobj.c
@@ -0,0 +1,89 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+__thread int tls_main_tbss_1;
+__thread int tls_main_tbss_2;
+__thread int tls_main_tdata_1 = 96;
+__thread int tls_main_tdata_2 = 97;
+
+extern __thread int tls_lib1_tbss_1;
+extern __thread int tls_lib1_tbss_2;
+extern __thread int tls_lib1_tdata_1;
+extern __thread int tls_lib1_tdata_2;
+
+extern __thread int tls_lib2_tbss_1;
+extern __thread int tls_lib2_tbss_2;
+extern __thread int tls_lib2_tdata_1;
+extern __thread int tls_lib2_tdata_2;
+
+extern __thread int tls_lib3_tbss_1;
+extern __thread int tls_lib3_tbss_2;
+extern __thread int tls_lib3_tdata_1;
+extern __thread int tls_lib3_tdata_2;
+
+extern void lib1_func ();
+extern void lib2_func ();
+extern void lib3_func ();
+
+volatile int data;
+
+void
+use_it (int a)
+{
+ data = a;
+}
+
+int
+main (int argc, char **argv)
+{
+ use_it (-1);
+
+ tls_main_tbss_1 = 51; /* main-breakpoint-1 */
+ tls_main_tbss_2 = 52;
+ tls_main_tdata_1 = 53;
+ tls_main_tdata_2 = 54;
+
+ tls_lib1_tbss_1 = 151;
+ tls_lib1_tbss_2 = 152;
+ tls_lib1_tdata_1 = 153;
+ tls_lib1_tdata_2 = 154;
+
+ tls_lib2_tbss_1 = 251;
+ tls_lib2_tbss_2 = 252;
+ tls_lib2_tdata_1 = 253;
+ tls_lib2_tdata_2 = 254;
+
+ tls_lib3_tbss_1 = 351;
+ tls_lib3_tbss_2 = 352;
+ tls_lib3_tdata_1 = 353;
+ tls_lib3_tdata_2 = 354;
+
+ lib1_func ();
+ lib2_func ();
+ lib3_func ();
+
+ /* Attempt to keep variables in the main program from being optimized
+ away. */
+ use_it (tls_main_tbss_1);
+ use_it (tls_main_tbss_2);
+ use_it (tls_main_tdata_1);
+ use_it (tls_main_tdata_2);
+
+ use_it (100); /* main-breakpoint-2 */
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.base/tls-multiobj.exp b/gdb/testsuite/gdb.base/tls-multiobj.exp
new file mode 100644
index 00000000000..a78db1460e9
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-multiobj.exp
@@ -0,0 +1,230 @@
+# Copyright 2024 Free Software Foundation, Inc.
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# Using different compilation/linking scenarios, attempt to access
+# thread-local variables in a non-threaded program using multiple
+# shared objects.
+
+source $srcdir/$subdir/tls-common.exp.tcl
+
+standard_testfile
+
+set lib1src "${srcdir}/${subdir}/${testfile}1.c"
+set lib2src "${srcdir}/${subdir}/${testfile}2.c"
+set lib3src "${srcdir}/${subdir}/${testfile}3.c"
+
+set lib1obj [standard_output_file "${testfile}1-lib.so"]
+set lib2obj [standard_output_file "${testfile}2-lib.so"]
+set lib3obj [standard_output_file "${testfile}3-lib.so"]
+
+proc do_tests {force_internal_tls {do_kfail_tls_access 0}} {
+ clean_restart $::binfile
+ if ![runto_main] {
+ return
+ }
+
+ if $force_internal_tls {
+ gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+ }
+
+ if { $do_kfail_tls_access && [istarget "*-*-linux*"] } {
+ # Turn off do_kfail_tls_access when libthread_db is loaded.
+ # This can happen for the default case when testing x86_64
+ # w/ -m32 using glibc versions 2.34 or newer.
+ gdb_test_multiple "maint check libthread-db" "Check for loaded libthread_db" {
+ -re -wrap "libthread_db integrity checks passed." {
+ set do_kfail_tls_access 0
+ pass $gdb_test_name
+ }
+ -re -wrap "No libthread_db loaded" {
+ pass $gdb_test_name
+ }
+ }
+ # Also turn off do_kfail_tls_access when connected to a
+ # gdbserver and we observe that accessing a TLS variable
+ # works.
+ if [target_is_gdbserver] {
+ gdb_test_multiple "print tls_main_tbss_1" \
+ "Check TLS accessibility when connected to a gdbserver" {
+ -re -wrap "= 0" {
+ set do_kfail_tls_access 0
+ pass $gdb_test_name
+ }
+ -re -wrap "Remote target failed to process qGetTLSAddr request" {
+ pass $gdb_test_name
+ }
+ }
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"]
+ gdb_continue_to_breakpoint "main-breakpoint-1"
+
+ set t $do_kfail_tls_access
+ set m "tls not available"
+ with_test_prefix "before assignments" {
+ gdb_test_with_kfail "print tls_main_tbss_1" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_main_tbss_2" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_main_tdata_1" ".* = 96" $t $m
+ gdb_test_with_kfail "print tls_main_tdata_2" ".* = 97" $t $m
+
+ gdb_test_with_kfail "print tls_lib1_tbss_1" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_lib1_tbss_2" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_lib1_tdata_1" ".* = 196" $t $m
+ gdb_test_with_kfail "print tls_lib1_tdata_2" ".* = 197" $t $m
+
+ gdb_test_with_kfail "print tls_lib2_tbss_1" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_lib2_tbss_2" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_lib2_tdata_1" ".* = 296" $t $m
+ gdb_test_with_kfail "print tls_lib2_tdata_2" ".* = 297" $t $m
+
+ gdb_test_with_kfail "print tls_lib3_tbss_1" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_lib3_tbss_2" ".* = 0" $t $m
+ gdb_test_with_kfail "print tls_lib3_tdata_1" ".* = 396" $t $m
+ gdb_test_with_kfail "print tls_lib3_tdata_2" ".* = 397" $t $m
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"]
+ gdb_continue_to_breakpoint "main-breakpoint-2"
+
+ with_test_prefix "after assignments" {
+ gdb_test_with_kfail "print tls_main_tbss_1" ".* = 51" $t $m
+ gdb_test_with_kfail "print tls_main_tbss_2" ".* = 52" $t $m
+ gdb_test_with_kfail "print tls_main_tdata_1" ".* = 53" $t $m
+ gdb_test_with_kfail "print tls_main_tdata_2" ".* = 54" $t $m
+
+ gdb_test_with_kfail "print tls_lib1_tbss_1" ".* = 151" $t $m
+ gdb_test_with_kfail "print tls_lib1_tbss_2" ".* = 152" $t $m
+ gdb_test_with_kfail "print tls_lib1_tdata_1" ".* = 153" $t $m
+ gdb_test_with_kfail "print tls_lib1_tdata_2" ".* = 154" $t $m
+
+ gdb_test_with_kfail "print tls_lib2_tbss_1" ".* = 251" $t $m
+ gdb_test_with_kfail "print tls_lib2_tbss_2" ".* = 252" $t $m
+ gdb_test_with_kfail "print tls_lib2_tdata_1" ".* = 253" $t $m
+ gdb_test_with_kfail "print tls_lib2_tdata_2" ".* = 254" $t $m
+
+ gdb_test_with_kfail "print tls_lib3_tbss_1" ".* = 351" $t $m
+ gdb_test_with_kfail "print tls_lib3_tbss_2" ".* = 352" $t $m
+ gdb_test_with_kfail "print tls_lib3_tdata_1" ".* = 353" $t $m
+ gdb_test_with_kfail "print tls_lib3_tdata_2" ".* = 354" $t $m
+ }
+
+ set corefile ${::binfile}.core
+ set core_supported 0
+ if { ![is_remote host] } {
+ set core_supported [gdb_gcore_cmd $corefile "save corefile"]
+ }
+
+ # Finish test early if no core file was made.
+ if !$core_supported {
+ return
+ }
+
+ clean_restart $::binfile
+
+ set core_loaded [gdb_core_cmd $corefile "load corefile"]
+ if { $core_loaded == -1 } {
+ return
+ }
+
+ with_test_prefix "core file" {
+ if $force_internal_tls {
+ gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+ }
+
+ gdb_test_with_kfail "print tls_main_tbss_1" ".* = 51" $t $m
+ gdb_test_with_kfail "print tls_main_tbss_2" ".* = 52" $t $m
+ gdb_test_with_kfail "print tls_main_tdata_1" ".* = 53" $t $m
+ gdb_test_with_kfail "print tls_main_tdata_2" ".* = 54" $t $m
+
+ gdb_test_with_kfail "print tls_lib1_tbss_1" ".* = 151" $t $m
+ gdb_test_with_kfail "print tls_lib1_tbss_2" ".* = 152" $t $m
+ gdb_test_with_kfail "print tls_lib1_tdata_1" ".* = 153" $t $m
+ gdb_test_with_kfail "print tls_lib1_tdata_2" ".* = 154" $t $m
+
+ gdb_test_with_kfail "print tls_lib2_tbss_1" ".* = 251" $t $m
+ gdb_test_with_kfail "print tls_lib2_tbss_2" ".* = 252" $t $m
+ gdb_test_with_kfail "print tls_lib2_tdata_1" ".* = 253" $t $m
+ gdb_test_with_kfail "print tls_lib2_tdata_2" ".* = 254" $t $m
+
+ gdb_test_with_kfail "print tls_lib3_tbss_1" ".* = 351" $t $m
+ gdb_test_with_kfail "print tls_lib3_tbss_2" ".* = 352" $t $m
+ gdb_test_with_kfail "print tls_lib3_tdata_1" ".* = 353" $t $m
+ gdb_test_with_kfail "print tls_lib3_tdata_2" ".* = 354" $t $m
+ }
+}
+
+if { [gdb_compile_shlib $lib1src $lib1obj {debug}] != "" } {
+ untested "failed to compile shared object"
+ return -1
+}
+if { [gdb_compile_shlib $lib2src $lib2obj {debug}] != "" } {
+ untested "failed to compile shared object"
+ return -1
+}
+if { [gdb_compile_shlib $lib3src $lib3obj {debug}] != "" } {
+ untested "failed to compile shared object"
+ return -1
+}
+
+# Certain linux target architectures implement support for internal
+# TLS lookup which is used when thread stratum support (via
+# libthread_db) is missing or when the linux-only GDB maintenance
+# setting 'force-internal-tls-address-lookup' is 'on'. Thus for some
+# of the testing scenarios, such as statically linked executables,
+# this internal support will be used. Set 'do_kfail_tls_access' to 1
+# for those architectures which don't implement internal tls support.
+if {[istarget *-*-linux*]
+ && ![is_any_target {*}$internal_tls_linux_targets]} {
+ set do_kfail_tls_access 1
+} elseif {[istarget *-*-linux*] && [is_x86_like_target]} {
+ # This covers the case of x86_64 with -m32:
+ set do_kfail_tls_access 1
+} else {
+ set do_kfail_tls_access 0
+}
+
+set binprefix $binfile
+
+with_test_prefix "default" {
+ set binfile $binprefix-default
+ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \
+ [list debug shlib=${lib1obj} \
+ shlib=${lib2obj} \
+ shlib=${lib3obj}]] != "" } {
+ untested "failed to compile"
+ } else {
+ foreach_with_prefix force_internal_tls $internal_tls_iters {
+ # Depending on glibc version, it might not be appropriate
+ # for do_kfail_tls_access to be set here. That will be
+ # handled in 'do_tests', disabling it if necessary.
+ #
+ # Specifically, glibc versions 2.34 and later have the
+ # thread library (and libthread_db availability) in
+ # programs not linked against libpthread.so
+ do_tests $force_internal_tls $do_kfail_tls_access
+ }
+ }
+}
+
+with_test_prefix "pthreads" {
+ set binfile $binprefix-pthreads
+ if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \
+ [list debug shlib=${lib1obj} \
+ shlib=${lib2obj} \
+ shlib=${lib3obj}]] != "" } {
+ untested "failed to compile"
+ } else {
+ foreach_with_prefix force_internal_tls $internal_tls_iters {
+ do_tests $force_internal_tls
+ }
+ }
+}
diff --git a/gdb/testsuite/gdb.base/tls-multiobj1.c b/gdb/testsuite/gdb.base/tls-multiobj1.c
new file mode 100644
index 00000000000..86e72228b1b
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-multiobj1.c
@@ -0,0 +1,26 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+__thread int tls_lib1_tbss_1;
+__thread int tls_lib1_tbss_2;
+__thread int tls_lib1_tdata_1 = 196;
+__thread int tls_lib1_tdata_2 = 197;
+
+void
+lib1_func ()
+{
+}
diff --git a/gdb/testsuite/gdb.base/tls-multiobj2.c b/gdb/testsuite/gdb.base/tls-multiobj2.c
new file mode 100644
index 00000000000..cea07092a5d
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-multiobj2.c
@@ -0,0 +1,26 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+__thread int tls_lib2_tbss_1;
+__thread int tls_lib2_tbss_2;
+__thread int tls_lib2_tdata_1 = 296;
+__thread int tls_lib2_tdata_2 = 297;
+
+void
+lib2_func ()
+{
+}
diff --git a/gdb/testsuite/gdb.base/tls-multiobj3.c b/gdb/testsuite/gdb.base/tls-multiobj3.c
new file mode 100644
index 00000000000..bb0f2395fbf
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-multiobj3.c
@@ -0,0 +1,26 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+__thread int tls_lib3_tbss_1;
+__thread int tls_lib3_tbss_2;
+__thread int tls_lib3_tdata_1 = 396;
+__thread int tls_lib3_tdata_2 = 397;
+
+void
+lib3_func ()
+{
+}
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 10/11] New test - gdb.base/tls-dlobj.exp
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
` (8 preceding siblings ...)
2024-10-25 2:26 ` [PATCH v3 09/11] New test - gdb.base/tls-multiobj.exp Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 2:26 ` [PATCH v3 11/11] Add TLS NEWS entry and document 'set force-internal-tls-address-lookup' command Kevin Buettner
10 siblings, 0 replies; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
This test exercises musl_link_map_to_tls_module_id() and
glibc_link_map_to_tls_module_id(), both of which are in solib-svr4.c.
Prior to writing this test, I had only written what is now named
'musl_link_map_to_tls_module_id' and it worked for both GLIBC and
MUSL. Once I wrote this new test, tls-dlobj.exp, there were a number
of tests which didn't work with GLIBC. This led me to write a
GLIBC-specific link map to module id function, i.e,
'glibc_link_map_to_tls_module_id'.
It only has one compilation scenario, in which the pthread(s) library
is used - as noted in a comment, it became too much of a hassle to try
to KFAIL things, though it certainly could have been done in much the
same was as was done in gdb.base/multiobj.exp. It didn't seem that
important to do so, however, since I believe that the other tests
have adequate coverage for different compilation scenarios.
---
gdb/testsuite/gdb.base/tls-dlobj-lib.c | 87 ++++++
gdb/testsuite/gdb.base/tls-dlobj.c | 311 ++++++++++++++++++++
gdb/testsuite/gdb.base/tls-dlobj.exp | 378 +++++++++++++++++++++++++
3 files changed, 776 insertions(+)
create mode 100644 gdb/testsuite/gdb.base/tls-dlobj-lib.c
create mode 100644 gdb/testsuite/gdb.base/tls-dlobj.c
create mode 100644 gdb/testsuite/gdb.base/tls-dlobj.exp
diff --git a/gdb/testsuite/gdb.base/tls-dlobj-lib.c b/gdb/testsuite/gdb.base/tls-dlobj-lib.c
new file mode 100644
index 00000000000..3f8ebc4f6a7
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-dlobj-lib.c
@@ -0,0 +1,87 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* This program needs to be compiled with preprocessor symbol set to
+ a small integer, e.g. "gcc -DN=1 ..." With N defined, the CONCAT2
+ and CONCAT3 macros will construct suitable names for the global
+ variables and functions. */
+
+#define CONCAT2(a,b) CONCAT2_(a,b)
+#define CONCAT2_(a,b) a ## b
+
+#define CONCAT3(a,b,c) CONCAT3_(a,b,c)
+#define CONCAT3_(a,b,c) a ## b ## c
+
+/* For N=1, this ends up being...
+ __thread int tls_lib1_tbss_1;
+ __thread int tls_lib1_tbss_2;
+ __thread int tls_lib1_tdata_1 = 196;
+ __thread int tls_lib1_tdata_2 = 197; */
+
+__thread int CONCAT3(tls_lib, N, _tbss_1);
+__thread int CONCAT3(tls_lib, N, _tbss_2);
+__thread int CONCAT3(tls_lib, N, _tdata_1) = CONCAT2(N, 96);
+__thread int CONCAT3(tls_lib, N, _tdata_2) = CONCAT2(N, 97);
+
+/* Substituting for N, define function:
+
+ int get_tls_libN_var (int which) . */
+
+int
+CONCAT3(get_tls_lib, N, _var) (int which)
+{
+ switch (which)
+ {
+ case 0:
+ return -1;
+ case 1:
+ return CONCAT3(tls_lib, N, _tbss_1);
+ case 2:
+ return CONCAT3(tls_lib, N, _tbss_2);
+ case 3:
+ return CONCAT3(tls_lib, N, _tdata_1);
+ case 4:
+ return CONCAT3(tls_lib, N, _tdata_2);
+ }
+ return -1;
+}
+
+/* Substituting for N, define function:
+
+ void set_tls_libN_var (int which, int val) . */
+
+void
+CONCAT3(set_tls_lib, N, _var) (int which, int val)
+{
+ switch (which)
+ {
+ case 0:
+ break;
+ case 1:
+ CONCAT3(tls_lib, N, _tbss_1) = val;
+ break;
+ case 2:
+ CONCAT3(tls_lib, N, _tbss_2) = val;
+ break;
+ case 3:
+ CONCAT3(tls_lib, N, _tdata_1) = val;
+ break;
+ case 4:
+ CONCAT3(tls_lib, N, _tdata_2) = val;
+ break;
+ }
+}
diff --git a/gdb/testsuite/gdb.base/tls-dlobj.c b/gdb/testsuite/gdb.base/tls-dlobj.c
new file mode 100644
index 00000000000..322bdda0393
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-dlobj.c
@@ -0,0 +1,311 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef void (*setter_ftype) (int which, int val);
+
+__thread int tls_main_tbss_1;
+__thread int tls_main_tbss_2;
+__thread int tls_main_tdata_1 = 96;
+__thread int tls_main_tdata_2 = 97;
+
+extern void set_tls_lib10_var (int which, int val);
+extern void set_tls_lib11_var (int which, int val);
+
+volatile int data;
+
+static void
+set_tls_main_var (int which, int val)
+{
+ switch (which)
+ {
+ case 1:
+ tls_main_tbss_1 = val;
+ break;
+ case 2:
+ tls_main_tbss_2 = val;
+ break;
+ case 3:
+ tls_main_tdata_1 = val;
+ break;
+ case 4:
+ tls_main_tdata_2 = val;
+ break;
+ }
+}
+
+void
+use_it (int a)
+{
+ data = a;
+}
+
+static void *
+load_dso (char *dso_name, int n, setter_ftype *setterp)
+{
+ char buf[80];
+ void *sym;
+ void *handle = dlopen (dso_name, RTLD_NOW | RTLD_GLOBAL);
+ if (handle == NULL)
+ {
+ fprintf (stderr, "dlopen of DSO '%s' failed: %s\n", dso_name, dlerror ());
+ exit (1);
+ }
+ sprintf (buf, "set_tls_lib%d_var", n);
+ sym = dlsym (handle, buf);
+ assert (sym != NULL);
+ *setterp = sym;
+
+ /* Some libc implementations (for some architectures) refuse to
+ initialize TLS data structures (specifically, the DTV) without
+ first calling dlsym on one of the TLS symbols. */
+ sprintf (buf, "tls_lib%d_tdata_1", n);
+ assert (dlsym (handle, buf) != NULL);
+
+ return handle;
+}
+
+int
+main (int argc, char **argv)
+{
+ int i, status;
+ setter_ftype s0, s1, s2, s3, s4, s10, s11;
+ void *h1 = load_dso (OBJ1, 1, &s1);
+ void *h2 = load_dso (OBJ2, 2, &s2);
+ void *h3 = load_dso (OBJ3, 3, &s3);
+ void *h4 = load_dso (OBJ4, 4, &s4);
+ s0 = set_tls_main_var;
+ s10 = set_tls_lib10_var;
+ s11 = set_tls_lib11_var;
+
+ use_it (0); /* main-breakpoint-1 */
+
+ /* Set TLS variables in main program and all libraries. */
+ for (i = 1; i <= 4; i++)
+ s0 (i, 10 + i);
+ for (i = 1; i <= 4; i++)
+ s1 (i, 110 + i);
+ for (i = 1; i <= 4; i++)
+ s2 (i, 210 + i);
+ for (i = 1; i <= 4; i++)
+ s3 (i, 310 + i);
+ for (i = 1; i <= 4; i++)
+ s4 (i, 410 + i);
+ for (i = 1; i <= 4; i++)
+ s10 (i, 1010 + i);
+ for (i = 1; i <= 4; i++)
+ s11 (i, 1110 + i);
+
+ use_it (0); /* main-breakpoint-2 */
+
+ /* Unload lib2 and lib3. */
+ status = dlclose (h2);
+ assert (status == 0);
+ status = dlclose (h3);
+ assert (status == 0);
+
+ /* Set TLS variables in main program and in libraries which are still
+ loaded. */
+ for (i = 1; i <= 4; i++)
+ s0 (i, 20 + i);
+ for (i = 1; i <= 4; i++)
+ s1 (i, 120 + i);
+ for (i = 1; i <= 4; i++)
+ s4 (i, 420 + i);
+ for (i = 1; i <= 4; i++)
+ s10 (i, 1020 + i);
+ for (i = 1; i <= 4; i++)
+ s11 (i, 1120 + i);
+
+ use_it (0); /* main-breakpoint-3 */
+
+ /* Load lib3. */
+ h3 = load_dso (OBJ3, 3, &s3);
+
+ /* Set TLS vars again; currently, only lib2 is not loaded. */
+ for (i = 1; i <= 4; i++)
+ s0 (i, 30 + i);
+ for (i = 1; i <= 4; i++)
+ s1 (i, 130 + i);
+ for (i = 1; i <= 4; i++)
+ s3 (i, 330 + i);
+ for (i = 1; i <= 4; i++)
+ s4 (i, 430 + i);
+ for (i = 1; i <= 4; i++)
+ s10 (i, 1030 + i);
+ for (i = 1; i <= 4; i++)
+ s11 (i, 1130 + i);
+
+ use_it (0); /* main-breakpoint-4 */
+
+ /* Unload lib1 and lib4; load lib2. */
+ status = dlclose (h1);
+ assert (status == 0);
+ status = dlclose (h4);
+ assert (status == 0);
+ h2 = load_dso (OBJ2, 2, &s2);
+
+ /* Set TLS vars; currently, lib2 and lib3 are loaded,
+ lib1 and lib4 are not. */
+ for (i = 1; i <= 4; i++)
+ s0 (i, 40 + i);
+ for (i = 1; i <= 4; i++)
+ s2 (i, 240 + i);
+ for (i = 1; i <= 4; i++)
+ s3 (i, 340 + i);
+ for (i = 1; i <= 4; i++)
+ s10 (i, 1040 + i);
+ for (i = 1; i <= 4; i++)
+ s11 (i, 1140 + i);
+
+ use_it (0); /* main-breakpoint-5 */
+
+ /* Load lib4 and lib1. Unload lib2. */
+ h4 = load_dso (OBJ4, 4, &s4);
+ h1 = load_dso (OBJ1, 1, &s1);
+ status = dlclose (h2);
+ assert (status == 0);
+
+ /* Set TLS vars; currently, lib1, lib3, and lib4 are loaded;
+ lib2 is not loaded. */
+ for (i = 1; i <= 4; i++)
+ s0 (i, 50 + i);
+ for (i = 1; i <= 4; i++)
+ s1 (i, 150 + i);
+ for (i = 1; i <= 4; i++)
+ s3 (i, 350 + i);
+ for (i = 1; i <= 4; i++)
+ s4 (i, 450 + i);
+ for (i = 1; i <= 4; i++)
+ s10 (i, 1050 + i);
+ for (i = 1; i <= 4; i++)
+ s11 (i, 1150 + i);
+
+ use_it (0); /* main-breakpoint-6 */
+
+ /* Load lib2, unload lib1, lib3, and lib4; then load lib3 again. */
+ h2 = load_dso (OBJ2, 2, &s2);
+ status = dlclose (h1);
+ assert (status == 0);
+ status = dlclose (h3);
+ assert (status == 0);
+ status = dlclose (h4);
+ assert (status == 0);
+ h3 = load_dso (OBJ3, 3, &s3);
+
+ /* Set TLS vars; currently, lib2 and lib3 are loaded;
+ lib1 and lib4 are not loaded. */
+ for (i = 1; i <= 4; i++)
+ s0 (i, 60 + i);
+ for (i = 1; i <= 4; i++)
+ s2 (i, 260 + i);
+ for (i = 1; i <= 4; i++)
+ s3 (i, 360 + i);
+ for (i = 1; i <= 4; i++)
+ s10 (i, 1060 + i);
+ for (i = 1; i <= 4; i++)
+ s11 (i, 1160 + i);
+
+ use_it (0); /* main-breakpoint-7 */
+
+ /* Unload lib3 and lib2, then (re)load lib4, lib3, lib2, and lib1,
+ in that order. */
+ status = dlclose (h3);
+ assert (status == 0);
+ status = dlclose (h2);
+ assert (status == 0);
+ h4 = load_dso (OBJ4, 4, &s4);
+ h3 = load_dso (OBJ3, 3, &s3);
+ h2 = load_dso (OBJ2, 2, &s2);
+ h1 = load_dso (OBJ1, 1, &s1);
+
+ /* Set TLS vars; currently, lib1, lib2, lib3, and lib4 are all
+ loaded. */
+ for (i = 1; i <= 4; i++)
+ s0 (i, 70 + i);
+ for (i = 1; i <= 4; i++)
+ s1 (i, 170 + i);
+ for (i = 1; i <= 4; i++)
+ s2 (i, 270 + i);
+ for (i = 1; i <= 4; i++)
+ s3 (i, 370 + i);
+ for (i = 1; i <= 4; i++)
+ s4 (i, 470 + i);
+ for (i = 1; i <= 4; i++)
+ s10 (i, 1070 + i);
+ for (i = 1; i <= 4; i++)
+ s11 (i, 1170 + i);
+
+ use_it (0); /* main-breakpoint-8 */
+
+ /* Unload lib3, lib1, and lib4. */
+ status = dlclose (h3);
+ assert (status == 0);
+ status = dlclose (h1);
+ assert (status == 0);
+ status = dlclose (h4);
+ assert (status == 0);
+
+ /* Set TLS vars; currently, lib2 is loaded; lib1, lib3, and lib4 are
+ not. */
+ for (i = 1; i <= 4; i++)
+ s0 (i, 80 + i);
+ for (i = 1; i <= 4; i++)
+ s2 (i, 280 + i);
+ for (i = 1; i <= 4; i++)
+ s10 (i, 1080 + i);
+ for (i = 1; i <= 4; i++)
+ s11 (i, 1180 + i);
+
+ use_it (0); /* main-breakpoint-9 */
+
+ /* Load lib3, unload lib2, load lib4. */
+ h3 = load_dso (OBJ3, 3, &s3);
+ status = dlclose (h2);
+ assert (status == 0);
+ h4 = load_dso (OBJ4, 4, &s4);
+
+ /* Set TLS vars; currently, lib3 and lib4 are loaded; lib1 and lib2
+ are not. */
+ for (i = 1; i <= 4; i++)
+ s0 (i, 90 + i);
+ for (i = 1; i <= 4; i++)
+ s3 (i, 390 + i);
+ for (i = 1; i <= 4; i++)
+ s4 (i, 490 + i);
+ for (i = 1; i <= 4; i++)
+ s10 (i, 1090 + i);
+ for (i = 1; i <= 4; i++)
+ s11 (i, 1190 + i);
+
+ use_it (0); /* main-breakpoint-10 */
+
+ /* Attempt to keep variables in the main program from being optimized
+ away. */
+ use_it (tls_main_tbss_1);
+ use_it (tls_main_tbss_2);
+ use_it (tls_main_tdata_1);
+ use_it (tls_main_tdata_2);
+
+ use_it (100); /* main-breakpoint-last */
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.base/tls-dlobj.exp b/gdb/testsuite/gdb.base/tls-dlobj.exp
new file mode 100644
index 00000000000..02f2ff81219
--- /dev/null
+++ b/gdb/testsuite/gdb.base/tls-dlobj.exp
@@ -0,0 +1,378 @@
+# Copyright 2024 Free Software Foundation, Inc.
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# Test that the GDB-internal TLS link map to module id mapping code
+# works correctly when debugging a program which is linked against
+# shared objects and which also loads and unloads other shared objects
+# in different orders. For targets which have GDB-internal TLS
+# support, it'll check both GDB-internal TLS support as well as that
+# provided by a helper library such as libthread_db.
+
+source $srcdir/$subdir/tls-common.exp.tcl
+
+require allow_shlib_tests
+
+standard_testfile
+
+set libsrc "${srcdir}/${subdir}/${testfile}-lib.c"
+
+# These will be dlopen'd:
+set lib1obj [standard_output_file "${testfile}1-lib.so"]
+set lib2obj [standard_output_file "${testfile}2-lib.so"]
+set lib3obj [standard_output_file "${testfile}3-lib.so"]
+set lib4obj [standard_output_file "${testfile}4-lib.so"]
+
+# These will be dynamically linked with the main program:
+set lib10obj [standard_output_file "${testfile}10-lib.so"]
+set lib11obj [standard_output_file "${testfile}11-lib.so"]
+
+# Due to problems with some versions of glibc, we expect some tests to
+# fail due to TLS storage not being allocated/initialized. Test
+# command CMD using regular expression RE, and use XFAIL instead of
+# FAIL when the relevant RE is matched and COND is true when evaluated
+# in the upper level.
+
+proc gdb_test_with_xfail { cmd re cond} {
+ gdb_test_multiple $cmd $cmd {
+ -re -wrap $re {
+ pass $gdb_test_name
+ }
+ -re -wrap "The inferior has not yet allocated storage for thread-local variables.*" {
+ if [ uplevel 1 [list expr $cond]] {
+ xfail $gdb_test_name
+ } else {
+ fail $gdb_test_name
+ }
+ }
+ }
+}
+
+proc do_tests {force_internal_tls} {
+ clean_restart $::binfile
+ if ![runto_main] {
+ return
+ }
+
+ if $force_internal_tls {
+ gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"]
+ gdb_continue_to_breakpoint "main-breakpoint-1"
+
+ with_test_prefix "before assignments" {
+ gdb_test "print tls_main_tbss_1" ".* = 0"
+ gdb_test "print tls_main_tbss_2" ".* = 0"
+ gdb_test "print tls_main_tdata_1" ".* = 96"
+ gdb_test "print tls_main_tdata_2" ".* = 97"
+
+ # For these tests, where we're attempting to access TLS vars
+ # in a dlopen'd library, but before assignment to any of the
+ # vars, so it could happen that storage hasn't been allocated
+ # yet. But it might also work. (When testing against MUSL,
+ # things just work; GLIBC ends to produce the TLS error.) So
+ # accept either the right answer or a TLS error message.
+
+ set tlserr "The inferior has not yet allocated storage for thread-local variables.*"
+ foreach n {1 2 3 4} {
+ gdb_test "print tls_lib${n}_tbss_1" \
+ "0|${tlserr}"
+ gdb_test "print tls_lib${n}_tbss_2" \
+ "0|${tlserr}"
+ gdb_test "print tls_lib${n}_tdata_1" \
+ "96|${tlserr}"
+ gdb_test "print tls_lib${n}_tdata_2" \
+ "97|${tlserr}"
+ }
+ foreach n {10 11} {
+ gdb_test "print tls_lib${n}_tbss_1" ".* = 0"
+ gdb_test "print tls_lib${n}_tbss_2" ".* = 0"
+ gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}96"
+ gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}97"
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"]
+ gdb_continue_to_breakpoint "main-breakpoint-2"
+
+ with_test_prefix "at main-breakpoint-2" {
+ gdb_test "print tls_main_tbss_1" ".* = 11"
+ gdb_test "print tls_main_tbss_2" ".* = 12"
+ gdb_test "print tls_main_tdata_1" ".* = 13"
+ gdb_test "print tls_main_tdata_2" ".* = 14"
+
+ foreach n {1 2 3 4 10 11} {
+ gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}11"
+ gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}12"
+ gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}13"
+ gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}14"
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-3"]
+ gdb_continue_to_breakpoint "main-breakpoint-3"
+
+ # At this point lib2 and lib3 have been unloaded. Also, TLS vars
+ # in remaining libraries have been changed.
+
+ with_test_prefix "at main-breakpoint-3" {
+ gdb_test "print tls_main_tbss_1" ".* = 21"
+ gdb_test "print tls_main_tbss_2" ".* = 22"
+ gdb_test "print tls_main_tdata_1" ".* = 23"
+ gdb_test "print tls_main_tdata_2" ".* = 24"
+
+ foreach n {1 4 10 11} {
+ gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}21"
+ gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}22"
+ gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}23"
+ gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}24"
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-4"]
+ gdb_continue_to_breakpoint "main-breakpoint-4"
+
+ # lib3 has been loaded again; lib2 is the only one not loaded.
+
+ with_test_prefix "at main-breakpoint-4" {
+ gdb_test "print tls_main_tbss_1" ".* = 31"
+ gdb_test "print tls_main_tbss_2" ".* = 32"
+ gdb_test "print tls_main_tdata_1" ".* = 33"
+ gdb_test "print tls_main_tdata_2" ".* = 34"
+
+ set cond { $n == 3 }
+ foreach n {1 3 4 10 11} {
+ gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}31" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}32" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}33" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}34" $cond
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-5"]
+ gdb_continue_to_breakpoint "main-breakpoint-5"
+
+ # lib2 and lib3 are loaded; lib1 and lib4 are not.
+
+ with_test_prefix "at main-breakpoint-5" {
+ gdb_test "print tls_main_tbss_1" ".* = 41"
+ gdb_test "print tls_main_tbss_2" ".* = 42"
+ gdb_test "print tls_main_tdata_1" ".* = 43"
+ gdb_test "print tls_main_tdata_2" ".* = 44"
+
+ set cond { $n == 2 || $n == 3 }
+ foreach n {2 3 10 11} {
+ gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}41" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}42" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}43" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}44" $cond
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-6"]
+ gdb_continue_to_breakpoint "main-breakpoint-6"
+
+ # lib1, lib3 and lib4 are loaded; lib2 is not loaded.
+
+ with_test_prefix "at main-breakpoint-6" {
+ gdb_test "print tls_main_tbss_1" ".* = 51"
+ gdb_test "print tls_main_tbss_2" ".* = 52"
+ gdb_test "print tls_main_tdata_1" ".* = 53"
+ gdb_test "print tls_main_tdata_2" ".* = 54"
+
+ set cond { $n == 1 || $n == 3 || $n == 4}
+ foreach n {1 3 4 10 11} {
+ gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}51" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}52" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}53" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}54" $cond
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-7"]
+ gdb_continue_to_breakpoint "main-breakpoint-7"
+
+ # lib2 and lib3 are loaded; lib1 and lib4 are not.
+
+ with_test_prefix "at main-breakpoint-7" {
+ gdb_test "print tls_main_tbss_1" ".* = 61"
+ gdb_test "print tls_main_tbss_2" ".* = 62"
+ gdb_test "print tls_main_tdata_1" ".* = 63"
+ gdb_test "print tls_main_tdata_2" ".* = 64"
+
+ set cond { $n == 2 || $n == 3 }
+ foreach n {2 3 10 11} {
+ gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}61" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}62" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}63" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}64" $cond
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-8"]
+ gdb_continue_to_breakpoint "main-breakpoint-8"
+
+ # lib1, lib2, lib3, and lib4 are all loaded.
+
+ with_test_prefix "at main-breakpoint-8" {
+ gdb_test "print tls_main_tbss_1" ".* = 71"
+ gdb_test "print tls_main_tbss_2" ".* = 72"
+ gdb_test "print tls_main_tdata_1" ".* = 73"
+ gdb_test "print tls_main_tdata_2" ".* = 74"
+
+ foreach n {1 2 3 4 10 11} {
+ gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}71"
+ gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}72"
+ gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}73"
+ gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}74"
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-9"]
+ gdb_continue_to_breakpoint "main-breakpoint-9"
+
+ # lib2 is loaded; lib1, lib3, and lib4 are not.
+
+ with_test_prefix "at main-breakpoint-9" {
+ gdb_test "print tls_main_tbss_1" ".* = 81"
+ gdb_test "print tls_main_tbss_2" ".* = 82"
+ gdb_test "print tls_main_tdata_1" ".* = 83"
+ gdb_test "print tls_main_tdata_2" ".* = 84"
+
+ foreach n {2 10 11} {
+ gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}81"
+ gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}82"
+ gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}83"
+ gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}84"
+ }
+ }
+
+ gdb_breakpoint [gdb_get_line_number "main-breakpoint-10"]
+ gdb_continue_to_breakpoint "main-breakpoint-10"
+
+ # lib3 and lib4 are loaded; lib1 and lib2 are not.
+
+ with_test_prefix "at main-breakpoint-10" {
+ gdb_test "print tls_main_tbss_1" ".* = 91"
+ gdb_test "print tls_main_tbss_2" ".* = 92"
+ gdb_test "print tls_main_tdata_1" ".* = 93"
+ gdb_test "print tls_main_tdata_2" ".* = 94"
+
+ set cond { $n == 3 || $n == 4 }
+ foreach n {3 4 10 11} {
+ gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}91" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}92" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}93" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}94" $cond
+ }
+ }
+
+ # gdb_interact
+
+ set corefile ${::binfile}.core
+ set core_supported 0
+ if { ![is_remote host] } {
+ set core_supported [gdb_gcore_cmd $corefile "save corefile"]
+ }
+
+ # Finish test early if no core file was made.
+ if !$core_supported {
+ return
+ }
+
+ clean_restart $::binfile
+
+ set core_loaded [gdb_core_cmd $corefile "load corefile"]
+ if { $core_loaded == -1 } {
+ return
+ }
+
+ with_test_prefix "core file" {
+ if $force_internal_tls {
+ gdb_test_no_output "maint set force-internal-tls-address-lookup on"
+ }
+
+ gdb_test "print tls_main_tbss_1" ".* = 91"
+ gdb_test "print tls_main_tbss_2" ".* = 92"
+ gdb_test "print tls_main_tdata_1" ".* = 93"
+ gdb_test "print tls_main_tdata_2" ".* = 94"
+
+ set cond { $n == 3 || $n == 4 }
+ foreach n {3 4 10 11} {
+ gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}91" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}92" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}93" $cond
+ gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}94" $cond
+ }
+ }
+}
+
+# Build shared objects for dlopen:
+if { [gdb_compile_shlib $libsrc $lib1obj [list debug additional_flags=-DN=1]] != "" } {
+ untested "failed to compile shared object"
+ return -1
+}
+if { [gdb_compile_shlib $libsrc $lib2obj [list debug additional_flags=-DN=2]] != "" } {
+ untested "failed to compile shared object"
+ return -1
+}
+if { [gdb_compile_shlib $libsrc $lib3obj [list debug additional_flags=-DN=3]] != "" } {
+ untested "failed to compile shared object"
+ return -1
+}
+if { [gdb_compile_shlib $libsrc $lib4obj [list debug additional_flags=-DN=4]] != "" } {
+ untested "failed to compile shared object"
+ return -1
+}
+
+# Build shared objects to link against main program:
+if { [gdb_compile_shlib $libsrc $lib10obj [list debug additional_flags=-DN=10]] != "" } {
+ untested "failed to compile shared object"
+ return -1
+}
+if { [gdb_compile_shlib $libsrc $lib11obj [list debug additional_flags=-DN=11]] != "" } {
+ untested "failed to compile shared object"
+ return -1
+}
+
+# Use gdb_compile_pthreads to build and link the main program for
+# testing. It's also possible to run the tests using plain old
+# gdb_compile, but this adds complexity with setting up additional
+# KFAILs. (When run using GLIBC versions earlier than 2.34, a program
+# that's not dynamically linked against libpthread will lack a working
+# libthread_db, and, therefore, won't be able to access thread local
+# storage without GDB-internal TLS support. Additional complications
+# arise from when testing on x86_64 with -m32, which tends to work
+# okay on GLIBC 2.34 and newer, but not older versions. It gets messy
+# to properly sort out all of these cases.)
+#
+# This test was originally written to do it both ways, i.e. with both
+# both gdb_compile and gdb_compile_pthreads, but the point of this
+# test is to check that the link map address to TLS module id mapping
+# code works correctly in programs which use lots of dlopen and
+# dlclose calls in various orders - and that can be done using just
+# gdb_compile_pthreads.
+
+if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \
+ [list debug shlib_load \
+ shlib=${lib10obj} \
+ shlib=${lib11obj} \
+ additional_flags=-DOBJ1=\"${lib1obj}\" \
+ additional_flags=-DOBJ2=\"${lib2obj}\" \
+ additional_flags=-DOBJ3=\"${lib3obj}\" \
+ additional_flags=-DOBJ4=\"${lib4obj}\" \
+ ]] != "" } {
+ untested "failed to compile"
+} else {
+ foreach_with_prefix force_internal_tls $internal_tls_iters {
+ do_tests $force_internal_tls
+ }
+}
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 11/11] Add TLS NEWS entry and document 'set force-internal-tls-address-lookup' command
2024-10-25 2:26 [PATCH v3 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
` (9 preceding siblings ...)
2024-10-25 2:26 ` [PATCH v3 10/11] New test - gdb.base/tls-dlobj.exp Kevin Buettner
@ 2024-10-25 2:26 ` Kevin Buettner
2024-10-25 7:34 ` Eli Zaretskii
10 siblings, 1 reply; 13+ messages in thread
From: Kevin Buettner @ 2024-10-25 2:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Kevin Buettner
---
gdb/NEWS | 20 ++++++++++++++++++
gdb/doc/gdb.texinfo | 50 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+)
diff --git a/gdb/NEWS b/gdb/NEWS
index 42b8a88fd8a..ef417202e80 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -65,6 +65,26 @@
the return value from the latest "stepOut" command, when
appropriate.
+* GDB-internal Thread Local Storage (TLS) support
+
+ ** Linux targets for the x86_64, aarch64, ppc64, s390x, and riscv
+ architectures now have GDB-internal support for TLS address
+ lookup in addition to that traditionally provided by the
+ libthread_db library. This internal support works for programs
+ linked against either the GLIBC or MUSL C libraries. For
+ programs linked against MUSL, this new internal support provides
+ new debug functionality, allowing access to TLS variables, due to
+ the fact that MUSL does not implement the libthread_db library.
+ Internal TLS support is also useful in cross-debugging
+ situations, debugging statically linked binaries, and debugging
+ programs linked against GLIBC 2.33 and earlier, but which are not
+ linked against libpthread.
+
+ ** The command 'maint set force-internal-tls-address-lookup on' may
+ be used to force the internal TLS lookup mechanisms to be used.
+ Otherwise, TLS lookup via libthread_db will still be preferred,
+ when available.
+
* New commands
set style line-number foreground COLOR
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index eb2aff974bb..adb70f0d579 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -4084,6 +4084,56 @@ When @samp{on} @value{GDBN} will print additional messages when
threads are created and deleted.
@end table
+@cindex thread local storage
+@cindex @acronym{TLS}
+For some debugging targets, @value{GDBN} has support for accessing
+variables that reside in Thread Local Storage (@acronym{TLS}).
+@acronym{TLS} variables are similar to global variables, except that
+each thread has its own copy of the variable. While often used in
+multi-threaded programs, @acronym{TLS} variables can also be used in
+programs without threads. The C library variable @var{errno} is,
+perhaps, the most prominent example of a @acronym{TLS} variable that
+is frequently used in non-threaded programs. For targets where
+@value{GDBN} does not have good @acronym{TLS} support, printing or
+changing the value of @var{errno} might not be directly possible.
+
+@sc{gnu}/Linux and FreeBSD targets have support for accessing
+@acronym{TLS} variables. On @sc{gnu}/Linux, the helper library,
+@code{libthread_db}, is used to help resolve the addresses of
+@acronym{TLS} variables. Some FreeBSD and some @sc{gnu}/Linux targets
+also have @value{GDBN}-internal @acronym{TLS} resolution code.
+@sc{gnu}/Linux targets will attempt to use the @acronym{TLS} address
+lookup functionality provided by @code{libthread_db}, but will fall
+back to using its internal @acronym{TLS} support when
+@code{libthread_db} is not available. This can happen in
+cross-debugging scenarios or when debugging programs that are linked
+in such a way that @code{libthread_db} support is unavailable - this
+includes statically linked programs, linking against @acronym{GLIBC}
+versions earlier than 2.34, but not with @code{libpthread}, and use of
+other (non-@acronym{GLIBC}) C libraries.
+
+@table @code
+@kindex maint set force-internal-tls-address-lookup
+@kindex maint show force-internal-tls-address-lookup
+@cindex internal @acronym{TLS} address lookup
+@item maint set force-internal-tls-address-lookup
+@itemx maint show force-internal-tls-address-lookup
+Turns on or off forced use of @value{GDBN}-internal @acronym{TLS}
+address lookup code. Use @code{on} to enable and @code{off} to
+disable. The default for this setting is @code{off}.
+
+When disabled, @value{GDBN} will attempt to use a helper
+@code{libthread_db} library if possible, but will fall back to use of
+its own internal @acronym{TLS} address lookup mechanisms if necessary.
+
+When enabled, @value{GDBN} will only use @value{GDBN}'s internal
+@acronym{TLS} address lookup mechanisms, if they exist.
+
+This command is only available for @sc{gnu}/Linux targets. Its
+primary use is for testing - certain tests in the @value{GDBN} test
+suite use this command to force use of internal TLS address lookup.
+@end table
+
@node Forks
@section Debugging Forks
--
2.46.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v3 11/11] Add TLS NEWS entry and document 'set force-internal-tls-address-lookup' command
2024-10-25 2:26 ` [PATCH v3 11/11] Add TLS NEWS entry and document 'set force-internal-tls-address-lookup' command Kevin Buettner
@ 2024-10-25 7:34 ` Eli Zaretskii
0 siblings, 0 replies; 13+ messages in thread
From: Eli Zaretskii @ 2024-10-25 7:34 UTC (permalink / raw)
To: Kevin Buettner; +Cc: gdb-patches, kevinb
> From: Kevin Buettner <kevinb@redhat.com>
> Cc: Kevin Buettner <kevinb@redhat.com>
> Date: Thu, 24 Oct 2024 19:26:29 -0700
>
> ---
> gdb/NEWS | 20 ++++++++++++++++++
> gdb/doc/gdb.texinfo | 50 +++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 70 insertions(+)
Thanks, this is okay, provided you fix the following nit:
> +cross-debugging scenarios or when debugging programs that are linked
> +in such a way that @code{libthread_db} support is unavailable - this
^^^
Please use "--", not a single dash, as the result will look better in
print.
> +This command is only available for @sc{gnu}/Linux targets. Its
> +primary use is for testing - certain tests in the @value{GDBN} test
Likewise.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
^ permalink raw reply [flat|nested] 13+ messages in thread