From: Kevin Buettner <kevinb@redhat.com>
To: gdb-patches@sourceware.org
Cc: Kevin Buettner <kevinb@redhat.com>, Luis Machado <luis.machado@arm.com>
Subject: [PATCH v4 05/11] Internal TLS support for aarch64, x86_64, riscv, ppc64, and s390x
Date: Wed, 6 Nov 2024 12:21:41 -0700 [thread overview]
Message-ID: <20241106192517.206988-6-kevinb@redhat.com> (raw)
In-Reply-To: <20241106192517.206988-1-kevinb@redhat.com>
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
next prev parent reply other threads:[~2024-11-06 19:27 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-06 19:21 [PATCH v4 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
2024-11-06 19:21 ` [PATCH v4 01/11] Don't attempt to find TLS address when target has no registers Kevin Buettner
2024-12-19 21:36 ` Tom Tromey
2024-11-06 19:21 ` [PATCH v4 02/11] Allow TLS access to work in gdb.server/no-thread-db.exp Kevin Buettner
2024-11-06 19:21 ` [PATCH v4 03/11] Track and fetch TLS module ids for MUSL and GLIBC Kevin Buettner
2024-11-06 19:21 ` [PATCH v4 04/11] Implement internal TLS address lookup for Linux targets Kevin Buettner
2024-11-06 19:21 ` Kevin Buettner [this message]
2024-11-06 19:21 ` [PATCH v4 06/11] Internal, but disabled, TLS support for i386 Kevin Buettner
2024-11-06 19:21 ` [PATCH v4 07/11] Delete disabled i386 internal TLS support Kevin Buettner
2024-11-06 19:21 ` [PATCH v4 08/11] New test - gdb.base/tls-nothreads.exp Kevin Buettner
2024-11-06 19:21 ` [PATCH v4 09/11] New test - gdb.base/tls-multiobj.exp Kevin Buettner
2024-11-06 19:21 ` [PATCH v4 10/11] New test - gdb.base/tls-dlobj.exp Kevin Buettner
2024-11-06 19:21 ` [PATCH v4 11/11] Add TLS NEWS entry and document 'set force-internal-tls-address-lookup' command Kevin Buettner
2024-11-07 5:38 ` Eli Zaretskii
2024-11-27 17:36 ` [PATCH v4 00/11] GDB-internal TLS support for Linux targets Kevin Buettner
2024-11-27 18:59 ` Eli Zaretskii
2024-11-30 23:11 ` Kevin Buettner
2024-12-12 23:20 ` Kevin Buettner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241106192517.206988-6-kevinb@redhat.com \
--to=kevinb@redhat.com \
--cc=gdb-patches@sourceware.org \
--cc=luis.machado@arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox