* [PATCH v2 2/2] This patch adds support to debug thread local variables defined in shared libraries in AIX.
@ 2026-09-03 8:56 Aditya Vidyadhar Kamath
2026-09-03 15:29 ` Ulrich Weigand
0 siblings, 1 reply; 3+ messages in thread
From: Aditya Vidyadhar Kamath @ 2026-09-03 8:56 UTC (permalink / raw)
To: ulrich.weigand, simon.marchi, tom
Cc: gdb-patches, Aditya.Kamath1, sangamesh.swamy, Aditya Vidyadhar Kamath
From: Aditya Vidyadhar Kamath <aditya.kamath1@ibm.com>
Sample debug output of this patch is as below
Thread 3 hit Breakpoint 1, thread_runner (arg=0x2) at tls_main.c:36
36 volatile int bp_here = 0; (void)bp_here;
$3 = 20
$4 = 40
thread 2: my_tls_var=20 lib_tls_var=40
[Thread 1 (tid 101646715) (id 1) exited]
[Thread 515 (tid 88015327) (id 3) exited]
[Inferior 1 (process 21758254) exited normally]
where lib_tls_var=40 is a variable from a thread library.
There are 4 models for TLS varialbles in AIX,
Local exec means the TLS variable is in main executable and defined there only.
Initial exec means the TLS variable is in a library and is linked in start up.
Global dynamic means TLS variable is in shared module and loaded when we dlopen
Local dynamic means TLS varialbe is in shared module it is local to the shared library code.
So local dynamic variables the compiler can group together for a shared look up.
This local-dymanic case is handled in rs6000_aix_compute_tls_address ()
For initial-exec (R_TLS_IE): read the runtime TP-relative offset from the
.loader section TOC slot written by the AIX loader, returning it with bit 0
set as a sentinel so get_thread_local_address() uses it directly without
re-adding the static symbol value.
For global-dynamic/local-dynamic (R_TLSM): scan the .loader section for the
R_TLSM relocation, read the 8-byte mod_id from the inferior's TOC, and
dereference it as a TP-relative pointer to the per-thread block, then add the
intra-module variable offset.
Local-exec (main executable) continues to use the existing tp + offset path.
This patch implements the same as mentioned above.
---
gdb/rs6000-aix-tdep.c | 509 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 498 insertions(+), 11 deletions(-)
diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c
index 76763f08e08..c4bc01c381e 100644
--- a/gdb/rs6000-aix-tdep.c
+++ b/gdb/rs6000-aix-tdep.c
@@ -40,6 +40,10 @@
#include "trad-frame.h"
#include "frame-unwind.h"
#include "inferior.h"
+#include "coff/internal.h"
+#include "libcoff.h"
+#include "coff/xcoff.h"
+#include "libxcoff.h"
/* If the kernel has to deliver a signal, it pushes a sigcontext
structure on the stack and then calls the signal handler, passing
@@ -69,6 +73,40 @@
/* Minimum possible text address in AIX. */
#define AIX_TEXT_SEGMENT_BASE 0x10000000
+/* XCOFF TLS relocation types (low byte of internal_ldrel.l_rtype).
+
+ The AIX loader fills TLS TOC slots as follows:
+
+ R_TLS (0x20, global-dynamic): the loader writes the signed TP-relative
+ offset for the variable. For multi-module programs this value is only
+ meaningful relative to the module's own TLS block base; to get the true
+ address the thread_vector must be consulted via __tls_get_addr.
+
+ R_TLS_IE (0x21, initial-exec): the loader writes the signed TP-relative
+ offset directly. address = tp + toc_val is always correct here.
+
+ R_TLS_LD (0x22, local-dynamic): the loader writes the module-relative
+ offset of the variable within the module's TLS block.
+
+ R_TLS_LE (0x23, local-exec): resolved statically by the linker; no
+ TOC slot is emitted at runtime.
+
+ R_TLSM (0x24): the loader writes the module-id for the module
+ that owns the variable. Used with R_TLS.
+
+ R_TLSML (0x25): the loader writes the module-id for the current module.
+ Used with R_TLS_LD one slot per module, shared by all
+ local-dynamic variables in that module.
+
+*/
+
+#define XCOFF_R_TLS 0x20
+#define XCOFF_R_TLS_IE 0x21
+#define XCOFF_R_TLS_LD 0x22
+#define XCOFF_R_TLS_LE 0x23
+#define XCOFF_R_TLSM 0x24
+#define XCOFF_R_TLSML 0x25
+
struct rs6000_aix_reg_vrreg_offset
{
int vr0_offset;
@@ -84,6 +122,27 @@ static struct rs6000_aix_reg_vrreg_offset rs6000_aix_vrreg_offset =
560 /* vrsave_offset */
};
+/* Description of a single TLS relocation found by scanning the .loader
+ section. Used as the return value of rs6000_aix_find_tls_reloc. */
+
+struct aix_tls_reloc_info
+{
+ /* The TLS model - XCOFF_R_TLS or XCOFF_R_TLS_IE or XCOFF_R_TLS_LD,
+ or XCOFF_R_TLS_LE. */
+ int rtype = -1;
+
+ /* Runtime address of the primary TOC slot. */
+ CORE_ADDR toc_addr = 0;
+
+ /* For global-dynamic: runtime address of the paired R_TLSM slot
+ that holds the module-id. 0 if not found. */
+ CORE_ADDR modid_toc_addr = 0;
+
+ /* For local-dynamic: runtime address of the R_TLSML slot that
+ holds the module-id for the whole module. 0 if not found. */
+ CORE_ADDR modid_ld_addr = 0;
+};
+
static int
rs6000_aix_get_vrreg_offset (ppc_gdbarch_tdep *tdep,
const struct rs6000_aix_reg_vrreg_offset *offsets,
@@ -1361,21 +1420,369 @@ rs6000_aix_core_xfer_shared_libraries_aix (struct gdbarch *gdbarch,
static CORE_ADDR
rs6000_aix_fetch_tls_load_module_address (struct objfile *objfile)
{
- /* TLS variables from shared libraries cannot be directly fetched
- via the thread pointer if they were loaded by dlopen(). */
- if (objfile->flags & OBJF_SHARED)
+ /* Main executable so lm_addr == 0. */
+ if (!(objfile->flags & OBJF_SHARED))
+ return 0;
+
+ /* Return the objfile pointer as the lm_addr token. The per-variable
+ TOC-slot lookup is done in rs6000_aix_get_thread_local_address where
+ the static symbol value is also available. */
+ if (objfile->obfd.get () == nullptr)
throw_error (TLS_GENERIC_ERROR,
- _("TLS lookup via thread pointer is not supported for "
- "shared library \"%s\"; full DTV-based lookup is not "
- "yet implemented for AIX"),
- objfile_name (objfile));
+ _("Cannot resolve TLS for \"%s\": no BFD"),
+ objfile_name (objfile));
- return 0;
+ return (CORE_ADDR)(uintptr_t) objfile;
+}
+
+/* Helper to read an 8-byte slot from the inferior at RUNTIME_ADDR.
+ Returns the value as a signed 64-bit integer, or throws TLS_GENERIC_ERROR
+ on a memory read failure. */
+
+static int64_t
+rs6000_aix_read_tls_slot (CORE_ADDR runtime_addr, const char *objfile_name_str)
+{
+ gdb_byte buf[8];
+ if (target_read_memory (runtime_addr, buf, sizeof buf) != 0)
+ throw_error (TLS_GENERIC_ERROR,
+ _("Cannot resolve TLS for \"%s\": "
+ "failed to read TLS TOC slot at %s from inferior"),
+ objfile_name_str,
+ core_addr_to_string (runtime_addr));
+ return (int64_t) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG);
+}
+
+/* Read the AIX thread entry for module MOD_ID and return the base address of
+ that module's TLS block. On 64-bit AIX the pointer is stored at the
+ address pointed to by the thread pointer (tp):
+ thread_ptr = *(uint64_t *) tp
+ tls_base = thread_ptr[mod_id]
+ Each entry is a single 8-byte pointer. Returns 0 if the slot has not
+ been allocated yet (throws TLS_NOT_ALLOCATED_YET_ERROR in that case). */
+
+static CORE_ADDR
+rs6000_aix_thread_vec_lookup (ULONGEST tp, uint64_t mod_id,
+ const char *objfile_name_str)
+{
+ /* The thread vector pointer lives at *tp on AIX 64-bit. */
+ gdb_byte buf[8];
+ if (target_read_memory ((CORE_ADDR) tp, buf, sizeof buf) != 0)
+ throw_error (TLS_GENERIC_ERROR,
+ _("Cannot resolve TLS for \"%s\": "
+ "failed to read thread vector pointer at tp=%s"),
+ objfile_name_str,
+ core_addr_to_string ((CORE_ADDR) tp));
+
+ CORE_ADDR thread_ptr
+ = (CORE_ADDR) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG);
+
+ if (thread_ptr == 0)
+ throw_error (TLS_NOT_ALLOCATED_YET_ERROR,
+ _("TLS storage not yet allocated for \"%s\""),
+ objfile_name_str);
+
+ /* thread_ptr[mod_id] is the TLS block base for this module. */
+ CORE_ADDR thread_entry_addr = thread_ptr + mod_id * sizeof (uint64_t);
+ if (target_read_memory (thread_entry_addr, buf, sizeof buf) != 0)
+ throw_error (TLS_GENERIC_ERROR,
+ _("Cannot resolve TLS for \"%s\": "
+ "failed to read thread_vec[%s] at %s"),
+ objfile_name_str,
+ pulongest (mod_id),
+ core_addr_to_string (thread_entry_addr));
+
+ CORE_ADDR tls_base
+ = (CORE_ADDR) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG);
+
+ if (tls_base == 0)
+ throw_error (TLS_NOT_ALLOCATED_YET_ERROR,
+ _("TLS storage not yet allocated for \"%s\" (thread vector[%s] == 0)"),
+ objfile_name_str, pulongest (mod_id));
+
+ return tls_base;
}
-/* Implement the get_thread_local_address gdbarch method for AIX.
+/* Scan the .loader section of ABFD for a TLS relocation whose loader symbol
+ value matches SYM_OFFSET. Returns an aix_tls_reloc_info describing what was
+ found, or one with rtype == -1 if no matching relocation exists.
+
+ For global-dynamic (R_TLS), also walks the reloc table looking for the
+ paired R_TLSM entry that has the same l_vaddr as the R_TLS slot minus
+ one pointer-width, which is how the AIX ABI lays out the two-word
+ {modid, offset} structure in the TOC.
+
+ For local-dynamic (R_TLS_LD), locates the corresponding R_TLSML reloc
+ which is always self-referencing -- l_symndx == the TOC entry's own csect
+ index in the same object. */
+
+static aix_tls_reloc_info
+rs6000_aix_find_tls_reloc (bfd *abfd,
+ CORE_ADDR data_slide,
+ const gdb::byte_vector &loader_buf,
+ CORE_ADDR sym_offset)
+{
+ aix_tls_reloc_info result;
+
+ struct internal_ldhdr ldhdr;
+ bfd_xcoff_swap_ldhdr_in (abfd, loader_buf.data (), &ldhdr);
+
+ bfd_size_type loader_size = loader_buf.size ();
+ bfd_vma sym_start = bfd_xcoff_loader_symbol_offset (abfd, &ldhdr);
+ bfd_size_type symsz = bfd_xcoff_ldsymsz (abfd);
+ bfd_vma reloc_start = bfd_xcoff_loader_reloc_offset (abfd, &ldhdr);
+ bfd_size_type relsz = bfd_xcoff_ldrelsz (abfd);
+
+ if (reloc_start + ldhdr.l_nreloc * relsz > loader_size)
+ return result;
+
+ /* Find the primary TLS reloc for this variable. */
+ const gdb_byte *reloc_ptr = loader_buf.data () + reloc_start;
+ for (size_t i = 0; i < ldhdr.l_nreloc; i++, reloc_ptr += relsz)
+ {
+ struct internal_ldrel ldrel;
+ bfd_xcoff_swap_ldrel_in (abfd, reloc_ptr, &ldrel);
+
+ int rtype = ldrel.l_rtype & 0xff;
+ if (rtype != XCOFF_R_TLS && rtype != XCOFF_R_TLS_IE
+ && rtype != XCOFF_R_TLS_LD)
+ continue;
+
+ /* l_symndx values 0..2 are section references; loader symbol indices
+ start at 3. */
+ if (ldrel.l_symndx < 3
+ || (bfd_vma)(ldrel.l_symndx - 3) >= ldhdr.l_nsyms)
+ continue;
+
+ bfd_vma ldsym_off = sym_start + (bfd_vma)(ldrel.l_symndx - 3) * symsz;
+ if (ldsym_off + symsz > loader_size)
+ continue;
+
+ struct internal_ldsym ldsym;
+ bfd_xcoff_swap_ldsym_in (abfd, loader_buf.data () + ldsym_off, &ldsym);
+
+
+ /* Match: the loader symbol's l_value equals the XCOFF static symbol
+ value that GDB received as 'sym_offset'. For R_TLS and R_TLS_IE
+ this is the TP-relative offset baked in at link time. For R_TLS_LD
+ this is the within-module variable offset. */
+ if ((CORE_ADDR)(int64_t) ldsym.l_value != sym_offset)
+ continue;
+
+ result.rtype = rtype;
+ result.toc_addr = ldrel.l_vaddr + data_slide;
+ break;
+ }
+
+ if (result.rtype == -1)
+ return result; /* No matching reloc found. */
+
+ /* Find the paired R_TLSM slot.
+ The AIX ABI places the two-word {modid, offset} GD descriptor in the
+ TOC at consecutive 8-byte slots. The R_TLSM reloc targets the first
+ word = modid and the R_TLS reloc targets the second word = offset.
+ So the R_TLSM slot lives at result.toc_addr - 8. We verify this by
+ scanning for an R_TLSM reloc with l_vaddr == result.toc_addr - 8. */
+ if (result.rtype == XCOFF_R_TLS)
+ {
+ bfd_vma expected_tlsm_vaddr = (result.toc_addr - data_slide) - 8;
+ reloc_ptr = loader_buf.data () + reloc_start;
+ for (size_t i = 0; i < ldhdr.l_nreloc; i++, reloc_ptr += relsz)
+ {
+ struct internal_ldrel ldrel;
+ bfd_xcoff_swap_ldrel_in (abfd, reloc_ptr, &ldrel);
+
+ if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLSM)
+ continue;
+ if (ldrel.l_vaddr == expected_tlsm_vaddr)
+ {
+ result.modid_toc_addr = ldrel.l_vaddr + data_slide;
+ break;
+ }
+ }
+ }
+
+ /* For local-dynamic is to find the R_TLSML reloc in the same
+ object. There is exactly one R_TLSML per module; it is self-targeting
+ i.e. the loader symbol it references is the TOC entry itself. */
+ if (result.rtype == XCOFF_R_TLS_LD)
+ {
+ reloc_ptr = loader_buf.data () + reloc_start;
+ for (size_t i = 0; i < ldhdr.l_nreloc; i++, reloc_ptr += relsz)
+ {
+ struct internal_ldrel ldrel;
+ bfd_xcoff_swap_ldrel_in (abfd, reloc_ptr, &ldrel);
+
+ if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLSML)
+ continue;
+
+ result.modid_ld_addr = ldrel.l_vaddr + data_slide;
+ break;
+ }
+ }
+
+ return result;
+}
- On 64-bit AIX the thread pointer (TP) is in R13. For variables in
+/* Cross-objfile fallback for the variable's owning objfile has no matching TLS
+ loader reloc (Ex: the variable is only accessed via __tls_get_addr from
+ code, not via a loader-section TOC slot). Search every other loaded objfile
+ for an R_TLS_IE or R_TLS reloc whose loader symbol l_value matches OFFSET.
+
+ On AIX with GCC, the main executable typically has R_TLS_IE relocs for
+ cross-module TLS variables, even when the defining .so has no matching
+ R_TLS / R_TLSM loader relocs for them. The R_TLS_IE TOC slot value is
+ the signed TP-relative offset, valid as: address = tp + toc_val.
+
+ Returns a filled aix_tls_reloc_info, or one with rtype==-1 if not found. */
+
+static aix_tls_reloc_info
+rs6000_aix_scan_all_objfiles (CORE_ADDR offset)
+{
+ for (objfile &candidate : current_program_space->objfiles ())
+ {
+ if (candidate.obfd.get () == nullptr)
+ continue;
+
+ bfd *abfd = candidate.obfd.get ();
+ asection *loader_sec = bfd_get_section_by_name (abfd, ".loader");
+ if (loader_sec == nullptr)
+ continue;
+
+ bfd_size_type loader_size = bfd_section_size (loader_sec);
+ gdb::byte_vector loader_buf (loader_size);
+ if (!bfd_get_section_contents (abfd, loader_sec, loader_buf.data (),
+ 0, loader_size))
+ continue;
+
+ aix_tls_reloc_info ri
+ = rs6000_aix_find_tls_reloc (abfd,
+ candidate.data_section_offset (),
+ loader_buf, offset);
+ if (ri.rtype != -1)
+ return ri;
+ }
+
+ aix_tls_reloc_info empty;
+ return empty;
+}
+
+/* This is a function for TLS resolution for a variable in OBJFILE when no loader reloc
+ was found in the owning .so or in any other loaded module.
+
+ On AIX the R_TLSM (module-id) slot is sometimes left as zero by the loader,
+ so above functions cannot be used. As a final fallback an attempt to
+ resolve via the runtime __tls_get_addr mechanism:
+
+ 1. Verify __tls_get_addr is present which confirms TLS-capable runtime.
+ 2. Find any R_TLSM or R_TLSML slot in the owning .so that is non-zero and
+ use its module-id to walk the thread vector.
+ 3. If all R_TLSM/R_TLSML slots are zero (loader did not fill them), throw
+ TLS_NOT_ALLOCATED_YET_ERROR. */
+
+static CORE_ADDR
+rs6000_aix_compute_tls_address (struct objfile *objfile,
+ ULONGEST tp,
+ CORE_ADDR offset)
+{
+ if (lookup_minimal_symbol (current_program_space, "__tls_get_addr").minsym
+ == nullptr)
+ throw_error (TLS_GENERIC_ERROR,
+ _("Cannot resolve TLS for \"%s\": "
+ "no loader relocation found and __tls_get_addr not "
+ "present in inferior"),
+ objfile_name (objfile));
+
+ bfd *abfd = objfile->obfd.get ();
+ asection *loader_sec = bfd_get_section_by_name (abfd, ".loader");
+ if (loader_sec == nullptr)
+ throw_error (TLS_GENERIC_ERROR,
+ _("Cannot resolve TLS for \"%s\": no .loader section"),
+ objfile_name (objfile));
+
+ bfd_size_type loader_size = bfd_section_size (loader_sec);
+ gdb::byte_vector loader_buf (loader_size);
+ if (!bfd_get_section_contents (abfd, loader_sec, loader_buf.data (),
+ 0, loader_size))
+ throw_error (TLS_GENERIC_ERROR,
+ _("Cannot resolve TLS for \"%s\": cannot read .loader"),
+ objfile_name (objfile));
+
+ struct internal_ldhdr ldhdr;
+ bfd_xcoff_swap_ldhdr_in (abfd, loader_buf.data (), &ldhdr);
+
+ bfd_vma reloc_start = bfd_xcoff_loader_reloc_offset (abfd, &ldhdr);
+ bfd_size_type relsz = bfd_xcoff_ldrelsz (abfd);
+ CORE_ADDR data_slide = objfile->data_section_offset ();
+
+ const gdb_byte *reloc_ptr = loader_buf.data () + reloc_start;
+ for (size_t i = 0; i < ldhdr.l_nreloc; i++, reloc_ptr += relsz)
+ {
+ struct internal_ldrel ldrel;
+ bfd_xcoff_swap_ldrel_in (abfd, reloc_ptr, &ldrel);
+ int rt = ldrel.l_rtype & 0xff;
+ if (rt != XCOFF_R_TLSM && rt != XCOFF_R_TLSML)
+ continue;
+
+ CORE_ADDR modid_addr = ldrel.l_vaddr + data_slide;
+ uint64_t mod_id
+ = (uint64_t) rs6000_aix_read_tls_slot (modid_addr,
+ objfile_name (objfile));
+ if (mod_id == 0)
+ continue; /* loader left this slot empty */
+
+ CORE_ADDR tls_base = rs6000_aix_thread_vec_lookup (tp, mod_id,
+ objfile_name (objfile));
+ return tls_base + offset;
+ }
+
+ /* Every R_TLSM/R_TLSML slot is zero -- TLS has not been initialised yet. */
+ throw_error (TLS_NOT_ALLOCATED_YET_ERROR,
+ _("TLS storage not yet allocated for \"%s\""),
+ objfile_name (objfile));
+}
+
+/* Scan the .loader section of OBJFILE and return TLS relocation information
+ for the variable with static symbol value SYM_OFFSET. Throws on hard
+ errors; returns an aix_tls_reloc_info with rtype == -1 if not found
+ (caller must decide what to do). */
+
+static aix_tls_reloc_info
+rs6000_aix_scan_loader (struct objfile *objfile, CORE_ADDR sym_offset)
+{
+ bfd *abfd = objfile->obfd.get ();
+
+ asection *loader_sec = bfd_get_section_by_name (abfd, ".loader");
+ if (loader_sec == nullptr)
+ throw_error (TLS_GENERIC_ERROR,
+ _("Cannot resolve TLS for \"%s\": no .loader section"),
+ objfile_name (objfile));
+
+ bfd_size_type loader_size = bfd_section_size (loader_sec);
+ gdb::byte_vector loader_buf (loader_size);
+ if (!bfd_get_section_contents (abfd, loader_sec, loader_buf.data (),
+ 0, loader_size))
+ throw_error (TLS_GENERIC_ERROR,
+ _("Cannot resolve TLS for \"%s\": cannot read .loader"),
+ objfile_name (objfile));
+
+ {
+ struct internal_ldhdr ldhdr;
+ bfd_xcoff_swap_ldhdr_in (abfd, loader_buf.data (), &ldhdr);
+ bfd_vma reloc_start = bfd_xcoff_loader_reloc_offset (abfd, &ldhdr);
+ bfd_size_type relsz = bfd_xcoff_ldrelsz (abfd);
+ if (reloc_start + ldhdr.l_nreloc * relsz > loader_size)
+ throw_error (TLS_GENERIC_ERROR,
+ _("Cannot resolve TLS for \"%s\": "
+ ".loader section truncated"),
+ objfile_name (objfile));
+ }
+
+ return rs6000_aix_find_tls_reloc (abfd,
+ objfile->data_section_offset (),
+ loader_buf, sym_offset);
+}
+
+/* On 64-bit AIX the thread pointer (TP) is in R13. For variables in
the main executable (lm_addr == 0) the XCOFF symbol value is a
signed TP-relative offset baked in at link time:
address = tp + (int64_t) offset */
@@ -1405,8 +1812,88 @@ rs6000_aix_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid,
if (lm_addr == 0)
return tp + (CORE_ADDR)(int64_t) offset;
+ /* Shared-library models: lm_addr encodes the objfile pointer. */
+ struct objfile *objfile = (struct objfile *)(uintptr_t) lm_addr;
+ aix_tls_reloc_info ri = rs6000_aix_scan_loader (objfile, offset);
+
+ if (ri.rtype == -1)
+ {
+ /* No loader reloc in the owning .so. Search every other objfile
+ (typically the main exe has R_TLS_IE for cross-module vars). */
+ ri = rs6000_aix_scan_all_objfiles (offset);
+ if (ri.rtype == -1)
+ return rs6000_aix_compute_tls_address (objfile, tp, offset);
+ }
+
+ if (ri.rtype == XCOFF_R_TLS_IE)
+ {
+ /* initial-exec: tp + signed TP-relative offset from TOC slot. */
+ int64_t tp_offset = rs6000_aix_read_tls_slot (ri.toc_addr,
+ objfile_name (objfile));
+ if (tp_offset == 0)
+ throw_error (TLS_NOT_ALLOCATED_YET_ERROR,
+ _("TLS storage not yet allocated for \"%s\""),
+ objfile_name (objfile));
+ return tp + (CORE_ADDR) tp_offset;
+ }
+
+ if (ri.rtype == XCOFF_R_TLS)
+ {
+ /* global-dynamic case only.
+ The AIX loader sometimes leaves R_TLSM as zero; in that case
+ TOC[R_TLS] itself holds the TP-relative offset (equivalent to
+ initial-exec), so use tp + toc_val as the fallback. */
+ if (ri.modid_toc_addr != 0)
+ {
+ uint64_t mod_id = (uint64_t) rs6000_aix_read_tls_slot (
+ ri.modid_toc_addr, objfile_name (objfile));
+ if (mod_id != 0)
+ {
+ int64_t var_offset = rs6000_aix_read_tls_slot (
+ ri.toc_addr, objfile_name (objfile));
+ CORE_ADDR tls_base = rs6000_aix_thread_vec_lookup (tp, mod_id,
+ objfile_name (objfile));
+ return tls_base + (CORE_ADDR) var_offset;
+ }
+ }
+
+ /* R_TLSM not found or zero: fall back to tp + TOC[R_TLS]. */
+ int64_t tp_offset = rs6000_aix_read_tls_slot (ri.toc_addr,
+ objfile_name (objfile));
+ if (tp_offset == 0)
+ throw_error (TLS_NOT_ALLOCATED_YET_ERROR,
+ _("TLS storage not yet allocated for \"%s\""),
+ objfile_name (objfile));
+ return tp + (CORE_ADDR) tp_offset;
+ }
+
+ if (ri.rtype == XCOFF_R_TLS_LD)
+ {
+ /* local-dynamic: TOC[R_TLSML] holds the module-id. */
+ if (ri.modid_ld_addr != 0)
+ {
+ uint64_t mod_id = (uint64_t) rs6000_aix_read_tls_slot (
+ ri.modid_ld_addr, objfile_name (objfile));
+ if (mod_id != 0)
+ {
+ int64_t var_offset = rs6000_aix_read_tls_slot (
+ ri.toc_addr, objfile_name (objfile));
+ CORE_ADDR tls_base = rs6000_aix_thread_vec_lookup (tp, mod_id,
+ objfile_name (objfile));
+ return tls_base + (CORE_ADDR) var_offset;
+ }
+ }
+
+ /* R_TLSML is zero: last resort. */
+ return rs6000_aix_compute_tls_address (objfile, tp, offset);
+ }
+
+ /* R_TLS_LE in a shared library should not occur; the linker rejects it. */
throw_error (TLS_GENERIC_ERROR,
- _("TLS in shared libraries not yet supported on AIX"));
+ _("Cannot resolve TLS for \"%s\": "
+ "unexpected TLS relocation type 0x%x in shared library"),
+ objfile_name (objfile), ri.rtype);
+
}
static void
--
2.51.2
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v2 2/2] This patch adds support to debug thread local variables defined in shared libraries in AIX.
2026-09-03 8:56 [PATCH v2 2/2] This patch adds support to debug thread local variables defined in shared libraries in AIX Aditya Vidyadhar Kamath
@ 2026-09-03 15:29 ` Ulrich Weigand
2026-09-08 13:30 ` Aditya Kamath
0 siblings, 1 reply; 3+ messages in thread
From: Ulrich Weigand @ 2026-09-03 15:29 UTC (permalink / raw)
To: akamath996, tom, simon.marchi
Cc: gdb-patches, SANGAMESH MALLAYYA, Aditya Kamath
Aditya Vidyadhar Kamath <akamath996@gmail.com> wrote:
>For global-dynamic/local-dynamic (R_TLSM): scan the .loader section
>for the R_TLSM relocation, read the 8-byte mod_id from the
>inferior's TOC, and dereference it as a TP-relative pointer to
>the per-thread block, then add the intra-module variable offset.
This is certainly more general than the original attempt, but ...
/* Match: the loader symbol's l_value equals the XCOFF static symbol
value that GDB received as 'sym_offset'. For R_TLS and R_TLS_IE
this is the TP-relative offset baked in at link time. For R_TLS_LD
this is the within-module variable offset. */
if ((CORE_ADDR)(int64_t) ldsym.l_value != sym_offset)
continue;
... I don't this logic can work. For the interesting case
(non-initial-exec modules), the sym_offset is *relative* to
the current module, which means that it does not at all
uniquely identify the symbol.
If you have two dynamic libraries, both defining a single
TLS symbol, they will both get offset 0. If you try to look
up a particular symbol in the relocation section using the
above logic, it will just look for offset 0 - but that could
randomly hit either of the two variables and therefore
result in the wrong module ID.
If you want to definitively identify the module ID of a
dynamic library from the relocations performed by the loader,
I think there's really only two options:
- if there is a R_TLSML in that library, it must resolve to
the current module ID (or 0 if it is actually initial-exec)
- otherwise, you need to scan all R_TLSM relocations in all
modules and check whether the *name* associated with the
relocation is found in the library you're interested in
(respecting search order); if yes, that's the module ID
for that library (or 0 for initial-exec)
That may be a bit inefficient, but I'm not really seeing
any better option here.
On the positive side, this means you can do the whole reloc
scanning fully in the fetch_tls_load_module_address hook
(since you only need the objfile - the offset doesn't help
you anything anyway), and simply return the module ID from
that hook. (That means you could even cache the module ID
in a per-objfile data structure to pay the price only once.)
Bye,
Ulrich
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v2 2/2] This patch adds support to debug thread local variables defined in shared libraries in AIX.
2026-09-03 15:29 ` Ulrich Weigand
@ 2026-09-08 13:30 ` Aditya Kamath
0 siblings, 0 replies; 3+ messages in thread
From: Aditya Kamath @ 2026-09-08 13:30 UTC (permalink / raw)
To: Ulrich Weigand, akamath996, tom, simon.marchi
Cc: gdb-patches, SANGAMESH MALLAYYA
[-- Attachment #1: Type: text/plain, Size: 2815 bytes --]
Hi Ulrich and community members,
Thank you for your feedback so far. I am sending a v3 patch of the same addressing your concerns soon.
>If you have two dynamic libraries, both defining a single
>TLS symbol, they will both get offset 0.
This is right. I get why you Said this. That is why I decided to have two strategies of which in strategy 2 of the new patch we match R_TLSM by symbol name, i.e. by reading the loader symbol name from the R_TLSM reloc entry and check whether the library we are looking for exports that name. Symbol names are unique under dynamic linker search-order rules, so the first match correctly identifies which library the slot belongs to.
>On the positive side, this means you can do the whole reloc
>scanning fully in the fetch_tls_load_module_address hook
>(since you only need the objfile - the offset doesn't help
>you anything anyway), and simply return the module ID from
>that hook.
rs6000_aix_fetch_tls_load_module_address () now does exactly this where it calls rs6000_aix_find_module_id(objfile) which scans the .loader section using only the objfile and returns the module-id. The offset plays no role in module-id discovery.
>(That means you could even cache the module ID
>in a per-objfile data structure to pay the price only once.)
The aix_tls_objfile_data struct attached via aix_tls_objfile_data_key implements exactly this. After the first call for a given shared library, cache.resolved is true and subsequent calls return cache.mod_id immediately without re-scanning.
Please see
+struct aix_tls_objfile_data
+{
+ bool resolved = false;
+ uint64_t mod_id = 0;
+};
One more thing I want to tell is testing in my LPAR with the test case patch, I found that the AIX loader assigns module-id 0 to initial-exec shared libraries, not just the main executable. The old code treated mod_id == 0 as "not yet allocated" and threw an error, which broke access to any variable in a library loaded as initial-exec. The v3 version of this patch removes those checks, and adds rs6000_aix_find_initial_exec_tls_offset to handle the fact that for initial-exec libraries the static XCOFF symbol value differs from the runtime TP-relative offset by the size of the initial TLS layout shift.
So these are the changes I made and am sending the v3 version of the patch to address the same.
So we have three patches:
v3-0001-Add-TLS-variable-debug-support-for-AIX-64-bit-XCO.patch - Already sent
v3-0002-This-patch-adds-support-to-debug-thread-local-var.patch - With corrected things to review
v2-0003-Add-test-cases-for-all-TLS-support-in-AIX.patch - with corrected commit message.
Kindly let me know what you think once I send them. Thanks for the guidance and review again.
Have a nice day ahead.
Thanks and regards,
Aditya.
[-- Attachment #2: Type: text/html, Size: 9184 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-08 13:31 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-03 8:56 [PATCH v2 2/2] This patch adds support to debug thread local variables defined in shared libraries in AIX Aditya Vidyadhar Kamath
2026-09-03 15:29 ` Ulrich Weigand
2026-09-08 13:30 ` Aditya Kamath
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox