Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v3 2/3] This patch adds support to debug thread local variables defined in shared libraries in AIX.
@ 2026-09-08 13:32 Aditya Vidyadhar Kamath
  2026-09-08 15:20 ` Simon Marchi
  0 siblings, 1 reply; 3+ messages in thread
From: Aditya Vidyadhar Kamath @ 2026-09-08 13:32 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.

Module-id resolution for shared libraries uses two strategies:

1. R_TLSML (local-dynamic): there is exactly one R_TLSML reloc per
   module, and the loader always writes that module's own id into the
   corresponding TOC slot.  Read it from the inferior and return it.

2. R_TLSM (global-dynamic): each R_TLSM relocation in any loaded module's .loader
   section references the library that exports the named TLS symbol.
   Scan R_TLSM relocs across all loaded objfiles for each, resolve the
   loader symbol name and check whether the library of interest exports
   it.  If so, read the TOC slot value, that is the library's module-id.
   Matching is done by symbol name, not by symbol value, so two
   libraries that both define a TLS variable at within-module offset 0
   are distinguished correctly.

The module-id is cached in a per-objfile structure so the .loader scan
is paid only once per shared library.

Module-id 0 is a valid AIX loader assignment in initial-exec model and
is not treated as "not yet allocated".  The main executable is
distinguished from a module-id-0 library by returning the out-of-band
sentinel XCOFF_MODID_MAIN_EXE (UINT64_MAX) instead of 0.

the address computation is done using 3 cases, they are:

- XCOFF_MODID_MAIN_EXE: local-exec where the XCOFF symbol value is the
  signed TP-relative offset directly; address = tp + offset.

- module-id 0 is initial-execed shared library where the AIX loader merges all
  initial-exec modules into one contiguous TLS segment and adjusts each
  variable's TP-relative offset accordingly.  The link-time XCOFF symbol
  value does not necessarily equal the runtime offset.  The patch scans
  the library's R_TLS slots and matches by l_value (unique within one
  module) to find the slot the loader filled with the actual runtime
  TP-relative offset, then computes address = tp + runtime_offset.

- module-id n > 0 is global-dynamic where we iterate the per-thread thread vector:
  tls_base = thread_vector[n]; address = tls_base + offset.
---
 gdb/rs6000-aix-tdep.c | 455 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 438 insertions(+), 17 deletions(-)

diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c
index 76763f08e08..8b6a79f158d 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,28 @@ static struct rs6000_aix_reg_vrreg_offset rs6000_aix_vrreg_offset =
   560 /* vrsave_offset */
 };
 
+/* Sentinel returned by rs6000_aix_fetch_tls_load_module_address for the main
+   executable.  AIX module IDs are small non-negative integers, so UINT64_MAX
+   is safely outside the range the loader ever uses.  */
+#define XCOFF_MODID_MAIN_EXE ((CORE_ADDR) UINT64_MAX)
+
+/* Per-objfile cache for the AIX TLS module-id.
+
+   fetch_tls_load_module_address scans the .loader section once and caches
+   the result so subsequent TLS lookups in the same shared library are cheap.
+
+   mod_id == 0 is a real module-id (initial-exec); it does not mean
+   "not yet resolved".  resolved == true is the flag for that.  */
+
+struct aix_tls_objfile_data
+{
+  bool resolved = false;
+  uint64_t mod_id = 0;
+};
+
+static const registry<objfile>::key<aix_tls_objfile_data>
+  aix_tls_objfile_data_key;
+
 static int
 rs6000_aix_get_vrreg_offset (ppc_gdbarch_tdep *tdep,
   const struct rs6000_aix_reg_vrreg_offset *offsets,
@@ -1356,29 +1416,362 @@ rs6000_aix_core_xfer_shared_libraries_aix (struct gdbarch *gdbarch,
 				    offset, len, 0);
 }
 
-/* For AIX, use the rs6000_aix_fetch_tls_load_module_address gdbarch method.  */
+/* Helper to read an 8-byte unsigned slot from the inferior at RUNTIME_ADDR.
+   Returns the value, or throws TLS_GENERIC_ERROR on memory read failure.  */
+
+static uint64_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 (uint64_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 thread vector pointer lives at *tp:
+     thread_vec_ptr = *(uint64_t *) tp
+     tls_base = thread_vec_ptr[mod_id]   (each entry is one 8-byte pointer)
+   Throws TLS_NOT_ALLOCATED_YET_ERROR if the slot is not yet filled.  */
+
+static CORE_ADDR
+rs6000_aix_thread_vec_lookup (ULONGEST tp, uint64_t mod_id,
+			      const char *objfile_name_str)
+{
+  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_vec_ptr
+    = (CORE_ADDR) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG);
+
+  if (thread_vec_ptr == 0)
+    throw_error (TLS_NOT_ALLOCATED_YET_ERROR,
+		 _("TLS storage not yet allocated for \"%s\""),
+		 objfile_name_str);
+
+  CORE_ADDR entry_addr = thread_vec_ptr + mod_id * sizeof (uint64_t);
+  if (target_read_memory (entry_addr, buf, sizeof buf) != 0)
+    throw_error (TLS_GENERIC_ERROR,
+		 _("Cannot resolve TLS for \"%s\": "
+		 "failed to read thread_vector[%s] at %s"),
+		 objfile_name_str,
+		 pulongest (mod_id),
+		 core_addr_to_string (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;
+}
+
+/* Return true if OBJFILE exports a minimal symbol named NAME.  Used by
+   rs6000_aix_fetch_tls_load_module_address to match R_TLSM relocations
+   to their owning library by symbol name rather than by offset value.  */
+
+static bool
+rs6000_aix_objfile_exports_symbol (struct objfile *objfile, const char *name)
+{
+  for (minimal_symbol *msym : objfile->msymbols ())
+    if (strcmp (msym->linkage_name (), name) == 0)
+      return true;
+  return false;
+}
+
+/* Scan the .loader section of OBJFILE to determine the AIX TLS module-id
+   for that shared library.  The module-id is the index into the per-thread
+   thread vector that the AIX runtime fills at load time.
+
+   Two strategies are tried in order:
+
+   1. R_TLSML (local-dynamic): there is exactly one R_TLSML reloc per
+      module, and the loader always writes that module's own id into the
+      corresponding TOC slot.  If found, read the slot value from the
+      inferior and return it.
+
+   2. R_TLSM (global-dynamic): each R_TLSM reloc references the module
+      that *exports* the TLS symbol named in the accompanying loader
+      symbol table entry.  Scan every R_TLSM reloc across all loaded
+      objfiles; for each, check whether OBJFILE exports the referenced
+      symbol name.  If it does, the TOC slot value for that reloc is
+      OBJFILE's module-id.
+
+   Returns the module-id, or throws TLS_NOT_ALLOCATED_YET_ERROR if the
+   loader has not filled any relevant slot yet.  */
+
+static uint64_t
+rs6000_aix_find_module_id (struct objfile *obj)
+{
+  const char *oname = objfile_name (obj);
+  bfd *abfd = obj->obfd.get ();
+
+  /* First strategy: R_TLSML in the owning .so  */
+  {
+    asection *loader_sec = bfd_get_section_by_name (abfd, ".loader");
+    if (loader_sec != nullptr)
+      {
+	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))
+	  {
+	    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 = obj->data_section_offset ();
+
+	    const gdb_byte *rp = loader_buf.data () + reloc_start;
+	    for (size_t i = 0; i < ldhdr.l_nreloc; i++, rp += relsz)
+	      {
+		struct internal_ldrel ldrel;
+		bfd_xcoff_swap_ldrel_in (abfd, rp, &ldrel);
+		if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLSML)
+		  continue;
+
+		/* R_TLSML: the loader writes this module's own id here.
+		   mod_id == 0 is valid (initial-exec).  */
+		CORE_ADDR slot_addr = ldrel.l_vaddr + data_slide;
+		uint64_t mod_id = rs6000_aix_read_tls_slot (slot_addr, oname);
+		return mod_id;
+	      }
+	  }
+      }
+  }
+
+  /* Second strategy: R_TLSM scan across all objfiles  */
+  for (struct objfile &candidate : current_program_space->objfiles ())
+    {
+      if (candidate.obfd.get () == nullptr)
+	continue;
+
+      bfd *cabfd = candidate.obfd.get ();
+      asection *loader_sec = bfd_get_section_by_name (cabfd, ".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 (cabfd, loader_sec, loader_buf.data (),
+				     0, loader_size))
+	continue;
+
+      struct internal_ldhdr ldhdr;
+      bfd_xcoff_swap_ldhdr_in (cabfd, loader_buf.data (), &ldhdr);
+      bfd_vma sym_start   = bfd_xcoff_loader_symbol_offset (cabfd, &ldhdr);
+      bfd_size_type symsz = bfd_xcoff_ldsymsz (cabfd);
+      bfd_vma reloc_start = bfd_xcoff_loader_reloc_offset (cabfd, &ldhdr);
+      bfd_size_type relsz = bfd_xcoff_ldrelsz (cabfd);
+      CORE_ADDR data_slide = candidate.data_section_offset ();
+      /* String table immediately follows the loader symbol table.  */
+      const char *strtab
+	= (ldhdr.l_stoff < loader_size
+	   ? (const char *) loader_buf.data () + ldhdr.l_stoff
+	   : nullptr);
+
+      const gdb_byte *rp = loader_buf.data () + reloc_start;
+      for (size_t i = 0; i < ldhdr.l_nreloc; i++, rp += relsz)
+	{
+	  struct internal_ldrel ldrel;
+	  bfd_xcoff_swap_ldrel_in (cabfd, rp, &ldrel);
+	  if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLSM)
+	    continue;
+
+	  /* Indices 0..2 are the special .text/.data/.bss section refs.  */
+	  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 (cabfd,
+				   loader_buf.data () + ldsym_off, &ldsym);
+
+	  /* Names up to SYMNMLEN bytes are stored inline; longer ones
+	     are in the string table.  */
+	  char nambuf[SYMNMLEN + 1];
+	  const char *sym_name;
+	  if (ldsym._l._l_l._l_zeroes != 0)
+	    {
+	      memcpy (nambuf, ldsym._l._l_name, SYMNMLEN);
+	      nambuf[SYMNMLEN] = '\0';
+	      sym_name = nambuf;
+	    }
+	  else if (strtab != nullptr
+		   && ldsym._l._l_l._l_offset < ldhdr.l_stlen)
+	    sym_name = strtab + ldsym._l._l_l._l_offset;
+	  else
+	    continue;
+
+	  /* If OBJ exports this symbol then the loader wrote OBJ's
+	     module-id into this R_TLSM slot.  First match wins.  */
+	  if (!rs6000_aix_objfile_exports_symbol (obj, sym_name))
+	    continue;
+
+	  /* mod_id == 0 is valid (initial-exec); return it as-is.  */
+	  CORE_ADDR slot_addr = ldrel.l_vaddr + data_slide;
+	  uint64_t mod_id = rs6000_aix_read_tls_slot (slot_addr, oname);
+	  return mod_id;
+	}
+    }
+
+  throw_error (TLS_GENERIC_ERROR,
+	       _("Cannot resolve TLS for \"%s\": "
+		 "no R_TLSML or R_TLSM relocation found for this module"),
+	       oname);
+}
+
+/* Implement the fetch_tls_load_module_address gdbarch hook for AIX.
+
+   For the main executable return XCOFF_MODID_MAIN_EXE so that
+   get_thread_local_address can tell it apart from a shared library whose
+   module-id happens to be 0.  For shared libraries, scan the .loader
+   section for the module-id the AIX loader wrote into the R_TLSML or
+   R_TLSM slot, cache it per-objfile, and return it.  */
 
 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)
+  /* The main executable uses local-exec; no thread-vector lookup needed.  */
+  if (!(objfile->flags & OBJF_SHARED))
+    return XCOFF_MODID_MAIN_EXE;
+
+  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 cached result if available.  */
+  aix_tls_objfile_data &cache
+    = aix_tls_objfile_data_key.try_emplace (objfile);
+  if (cache.resolved)
+    return (CORE_ADDR) cache.mod_id;
+
+  /* Scan .loader section to determine the real module-id.  */
+  uint64_t mod_id = rs6000_aix_find_module_id (objfile);
+
+  cache.mod_id = mod_id;
+  cache.resolved = true;
+
+  return (CORE_ADDR) mod_id;
+}
+
+/* For a shared library loaded with module-id 0 (initial-exec model), the
+   AIX loader writes the actual runtime TP-relative offset into each R_TLS
+   slot.  This runtime value can differ from the link-time XCOFF symbol
+   value because the initial TLS segment merges multiple modules and may
+   shift each module's variables relative to their per-module link-time
+   positions.
+
+   Scan OBJFILE's .loader section for an R_TLS reloc whose loader-symbol
+   l_value matches STATIC_OFFSET (the XCOFF symbol value GDB has).  Within
+   one module those link-time values are unique, so the match pinpoints the
+   right slot.  If found, read the runtime value from the inferior's TOC
+   slot and store it in *RUNTIME_OFFSET.  Return true on success.  */
+
+static bool
+rs6000_aix_find_initial_exec_tls_offset (struct objfile *objfile,
+					 CORE_ADDR static_offset,
+					 int64_t *runtime_offset)
+{
+  bfd *abfd = objfile->obfd.get ();
+  if (abfd == nullptr)
+    return false;
+
+  asection *loader_sec = bfd_get_section_by_name (abfd, ".loader");
+  if (loader_sec == nullptr)
+    return false;
+
+  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))
+    return false;
+
+  struct internal_ldhdr ldhdr;
+  bfd_xcoff_swap_ldhdr_in (abfd, loader_buf.data (), &ldhdr);
+
+  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);
+  CORE_ADDR data_slide = objfile->data_section_offset ();
+
+  const gdb_byte *rp = loader_buf.data () + reloc_start;
+  for (size_t i = 0; i < ldhdr.l_nreloc; i++, rp += relsz)
+    {
+      struct internal_ldrel ldrel;
+      bfd_xcoff_swap_ldrel_in (abfd, rp, &ldrel);
+
+      /* R_TLSM and R_TLSML carry module-ids, not variable offsets.  */
+      if ((ldrel.l_rtype & 0xff) != XCOFF_R_TLS)
+	continue;
+
+      /* Indices 0..2 are the special .text/.data/.bss section refs.  */
+      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);
+
+      /* l_value holds the link-time TP-relative offset.  It is unique
+	 within a module, so matching against static_offset identifies
+	 exactly the right variable.  */
+      if ((CORE_ADDR)(int64_t) ldsym.l_value != static_offset)
+	continue;
+
+      /* Read the runtime value the loader wrote into this TOC slot.  */
+      CORE_ADDR slot_addr = ldrel.l_vaddr + data_slide;
+      gdb_byte buf[8];
+      if (target_read_memory (slot_addr, buf, sizeof buf) != 0)
+	return false;
+
+      *runtime_offset
+	= (int64_t) extract_unsigned_integer (buf, sizeof buf, BFD_ENDIAN_BIG);
+      return true;
+    }
+
+  return false;
 }
 
 /* Implement the get_thread_local_address gdbarch method for AIX.
 
-   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  */
+   On 64-bit AIX the thread pointer is in r13.
+
+   lm_addr is what fetch_tls_load_module_address returned:
+     XCOFF_MODID_MAIN_EXE  - main executable, local-exec.  The XCOFF symbol
+       value is the signed TP-relative offset directly: address = tp + offset.
+     0  - shared library with AIX module-id 0 (initial-exec).  The XCOFF
+       static symbol value is the within-module link-time offset; the loader
+       may have shifted it in the merged initial TLS segment.  Scan the R_TLS
+       slots to get the real runtime TP-relative offset.
+     n > 0  - shared library with global-dynamic model (module-id n).  Walk
+       the thread vector: address = thread_vector[n] + offset.  */
 
 static CORE_ADDR
 rs6000_aix_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid,
@@ -1401,12 +1794,40 @@ rs6000_aix_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid,
     throw_error (TLS_GENERIC_ERROR,
 		 _("Unable to fetch thread pointer for TLS lookup"));
 
-  /* local-exec: XCOFF symbol value is a signed TP-relative offset.  */
-  if (lm_addr == 0)
+  /* Local-exec (main executable): the XCOFF symbol value is the signed
+     TP-relative offset.  */
+  if (lm_addr == XCOFF_MODID_MAIN_EXE)
     return tp + (CORE_ADDR)(int64_t) offset;
 
-  throw_error (TLS_GENERIC_ERROR,
-	       _("TLS in shared libraries not yet supported on AIX"));
+  /* Initial-exec shared library (module-id 0): the link-time symbol value
+     may not be the runtime TP-relative offset because the initial TLS
+     segment can shift a library's variables.  Find the actual offset from
+     the R_TLS slot the loader filled in.  */
+  if (lm_addr == 0)
+    {
+      for (struct objfile &candidate : current_program_space->objfiles ())
+	{
+	  if (!(candidate.flags & OBJF_SHARED))
+	    continue;
+	  if (candidate.obfd.get () == nullptr)
+	    continue;
+
+	  int64_t runtime_tprel;
+	  if (rs6000_aix_find_initial_exec_tls_offset (&candidate,
+						       offset,
+						       &runtime_tprel))
+	    return tp + (CORE_ADDR) runtime_tprel;
+	}
+
+      /* No R_TLS slot found; fall back to the static offset.  */
+      return tp + (CORE_ADDR)(int64_t) offset;
+    }
+
+  /* Global-dynamic (module-id > 0): walk the thread vector.  */
+  uint64_t mod_id = (uint64_t) lm_addr;
+  CORE_ADDR tls_base = rs6000_aix_thread_vec_lookup (tp, mod_id,
+						     "<shared library>");
+  return tls_base + offset;
 }
 
 static void
-- 
2.51.2


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v3 2/3] This patch adds support to debug thread local variables defined in shared libraries in AIX.
  2026-09-08 13:32 [PATCH v3 2/3] This patch adds support to debug thread local variables defined in shared libraries in AIX Aditya Vidyadhar Kamath
@ 2026-09-08 15:20 ` Simon Marchi
  2026-09-10  9:19   ` Aditya Kamath
  0 siblings, 1 reply; 3+ messages in thread
From: Simon Marchi @ 2026-09-08 15:20 UTC (permalink / raw)
  To: Aditya Vidyadhar Kamath, ulrich.weigand, tom
  Cc: gdb-patches, Aditya.Kamath1, sangamesh.swamy

On 9/8/26 9:32 AM, Aditya Vidyadhar Kamath wrote:
> 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.
> 
> Module-id resolution for shared libraries uses two strategies:
> 
> 1. R_TLSML (local-dynamic): there is exactly one R_TLSML reloc per
>    module, and the loader always writes that module's own id into the
>    corresponding TOC slot.  Read it from the inferior and return it.
> 
> 2. R_TLSM (global-dynamic): each R_TLSM relocation in any loaded module's .loader
>    section references the library that exports the named TLS symbol.
>    Scan R_TLSM relocs across all loaded objfiles for each, resolve the
>    loader symbol name and check whether the library of interest exports
>    it.  If so, read the TOC slot value, that is the library's module-id.
>    Matching is done by symbol name, not by symbol value, so two
>    libraries that both define a TLS variable at within-module offset 0
>    are distinguished correctly.
> 
> The module-id is cached in a per-objfile structure so the .loader scan
> is paid only once per shared library.
> 
> Module-id 0 is a valid AIX loader assignment in initial-exec model and
> is not treated as "not yet allocated".  The main executable is
> distinguished from a module-id-0 library by returning the out-of-band
> sentinel XCOFF_MODID_MAIN_EXE (UINT64_MAX) instead of 0.
> 
> the address computation is done using 3 cases, they are:
> 
> - XCOFF_MODID_MAIN_EXE: local-exec where the XCOFF symbol value is the
>   signed TP-relative offset directly; address = tp + offset.
> 
> - module-id 0 is initial-execed shared library where the AIX loader merges all
>   initial-exec modules into one contiguous TLS segment and adjusts each
>   variable's TP-relative offset accordingly.  The link-time XCOFF symbol
>   value does not necessarily equal the runtime offset.  The patch scans
>   the library's R_TLS slots and matches by l_value (unique within one
>   module) to find the slot the loader filled with the actual runtime
>   TP-relative offset, then computes address = tp + runtime_offset.
> 
> - module-id n > 0 is global-dynamic where we iterate the per-thread thread vector:
>   tls_base = thread_vector[n]; address = tls_base + offset.

Please reword the commit subject to something like:

  Add support for TLS variables in shared libraries on AIX

Simon

^ permalink raw reply	[flat|nested] 3+ messages in thread

* RE: [PATCH v3 2/3] This patch adds support to debug thread local variables defined in shared libraries in AIX.
  2026-09-08 15:20 ` Simon Marchi
@ 2026-09-10  9:19   ` Aditya Kamath
  0 siblings, 0 replies; 3+ messages in thread
From: Aditya Kamath @ 2026-09-10  9:19 UTC (permalink / raw)
  To: Simon Marchi, Aditya Vidyadhar Kamath, Ulrich Weigand, tom
  Cc: gdb-patches, SANGAMESH MALLAYYA

[-- Attachment #1: Type: text/plain, Size: 230 bytes --]

Hi Simon and community members,

>Please reword the commit subject to something like:

>Add support for TLS variables in shared libraries on AIX

Sure sending v4 of this patch soon with the correction.

Regards,
Aditya.

[-- Attachment #2: Type: text/html, Size: 1143 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-10  9:19 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-08 13:32 [PATCH v3 2/3] This patch adds support to debug thread local variables defined in shared libraries in AIX Aditya Vidyadhar Kamath
2026-09-08 15:20 ` Simon Marchi
2026-09-10  9:19   ` Aditya Kamath

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox