[binutils-gdb] Implement native TLS support on Windows

Hannes Domani via Gdb-cvs <[email protected]> Sat, 23 May 2026 09:49:36 +0000 (GMT)
Newsgroups gmane.comp.gdb.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9d7734ef4fc74e448deb00824fc0504c0570c387

commit 9d7734ef4fc74e448deb00824fc0504c0570c387
Author: Hannes Domani <[email protected]>
Date:   Thu May 7 20:54:54 2026 +0200

    Implement native TLS support on Windows
    
    GCC 16 introduced native TLS variables on Windows, so this adds
    debugger support for them.
    
    The fetch_tls_load_module_address gdbarch method is used to get the
    address of _tls_index of the OBJFILE, which is then forwarded as LM_ADDR
    to windows_get_thread_local_address.
    
    The TLS slot for a module can be found in
    TIB->thread_local_storage[_tls_index].
    
    Approved-By: Tom Tromey <[email protected]>

Diff:
---
 gdb/NEWS           |  2 ++
 gdb/windows-tdep.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 75 insertions(+)

diff --git a/gdb/NEWS b/gdb/NEWS
index 48a80856aa1..7c8cf9af4c2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -81,6 +81,8 @@
 * The Windows native target now supports non-stop mode.  This feature
   requires Windows 10 or later.
 
+* Support for native Thread Local Storage (TLS) variables on Windows.
+
 * Configure changes
 
 ** --with-babeltrace has been removed.  The babeltrace library was
diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c
index 38ee1d7275e..78bf49b9c81 100644
--- a/gdb/windows-tdep.c
+++ b/gdb/windows-tdep.c
@@ -954,6 +954,73 @@ windows_init_abi_common (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_get_siginfo_type (gdbarch, windows_get_siginfo_type);
 }
 
+/* Implement the fetch_tls_load_module_address gdbarch method.
+   Usually this method returns an address representing a load module, but this
+   doesn't exist on Windows.  Instead it returns the address of the
+   "_tls_index" variable of OBJFILE, which is then forwarded as LM_ADDR
+   in windows_get_thread_local_address below.  */
+
+static CORE_ADDR
+windows_tls_index_address (struct objfile *objfile)
+{
+  bound_minimal_symbol minsym
+    = lookup_minimal_symbol_linkage ("_tls_index", objfile, false);
+  if (minsym.minsym == nullptr)
+    throw_error (TLS_GENERIC_ERROR, _("Cannot find address of _tls_index"));
+
+  return minsym.value_address ();
+}
+
+/* Implement the get_thread_local_address gdbarch method.
+   Each module (with TLS variables) gets its own TLS slot represented by
+   _tls_index, which can be found in TIB->thread_local_storage[_tls_index].
+   The address of _tls_index is provided by the LM_ADDR argument.  */
+
+static CORE_ADDR
+windows_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid,
+				  CORE_ADDR lm_addr, CORE_ADDR offset)
+{
+  int ptr_bytes;
+  int tls_offset; /* Offset of thread_local_storage in TIB.  */
+  if (gdbarch_ptr_bit (gdbarch) == 32)
+    {
+      ptr_bytes = 4;
+      tls_offset = 44;
+    }
+  else
+    {
+      ptr_bytes = 8;
+      tls_offset = 88;
+    }
+
+  gdb_byte buf[8];
+  if (target_read_memory (lm_addr, buf, 4))
+    throw_error (TLS_GENERIC_ERROR, _("Cannot read _tls_index"));
+
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  uint32_t tls_index = extract_unsigned_integer (buf, 4, byte_order);
+
+  CORE_ADDR tlb;
+  if (!target_get_tib_address (ptid, &tlb))
+    throw_error (TLS_GENERIC_ERROR, _("Cannot get tib address"));
+
+  if (target_read_memory (tlb + tls_offset, buf, ptr_bytes))
+    throw_error (TLS_GENERIC_ERROR, _("Cannot read thread_local_storage"));
+
+  CORE_ADDR tls_ptr = extract_unsigned_integer (buf, ptr_bytes, byte_order);
+  if (tls_ptr == 0)
+    throw_error (TLS_NOT_ALLOCATED_YET_ERROR, _("TLS not allocated yet"));
+
+  if (target_read_memory (tls_ptr + tls_index * ptr_bytes, buf, ptr_bytes))
+    throw_error (TLS_GENERIC_ERROR, _("Cannot read TLS slot"));
+
+  CORE_ADDR slot_ptr = extract_unsigned_integer (buf, ptr_bytes, byte_order);
+  if (slot_ptr == 0)
+    throw_error (TLS_NOT_ALLOCATED_YET_ERROR, _("TLS slot not allocated yet"));
+
+  return slot_ptr + offset;
+}
+
 /* See windows-tdep.h.  */
 void
 windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
@@ -963,6 +1030,12 @@ windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
   /* On Windows, "long"s are only 32bit.  */
   set_gdbarch_long_bit (gdbarch, 32);
+
+  /* Enable TLS support.  */
+  set_gdbarch_fetch_tls_load_module_address (gdbarch,
+					     windows_tls_index_address);
+  set_gdbarch_get_thread_local_address (gdbarch,
+					windows_get_thread_local_address);
 }
 
 /* See windows-tdep.h.  */