https://github.com/igorkudrin updated 
https://github.com/llvm/llvm-project/pull/181315

>From cb8be96868c4a6b0b8011b6a94c189fc25d738be Mon Sep 17 00:00:00 2001
From: Igor Kudrin <[email protected]>
Date: Wed, 11 Feb 2026 20:59:49 -0800
Subject: [PATCH 1/9] [lldb][ARM] Support thread local variables on ARM Linux

This implements reading the thread pointer register (`TPIDRURO`) on ARM
Linux and improves `DynamicLoaderPOSIXDYLD::GetThreadLocalData()` to
support the TLS memory layout where the thread pointer register points
directly to the DTV pointer.
---
 lldb/include/lldb/Host/linux/Ptrace.h         |  8 +++-
 .../POSIX-DYLD/DYLDRendezvous.cpp             |  5 ++-
 .../DynamicLoader/POSIX-DYLD/DYLDRendezvous.h |  3 +-
 .../POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp     | 38 ++++++++++++++---
 .../Linux/NativeRegisterContextLinux_arm.cpp  | 41 +++++++++++++++++--
 .../Linux/NativeRegisterContextLinux_arm.h    |  9 ++++
 .../Utility/RegisterContextPOSIX_arm.cpp      |  7 ++++
 .../Utility/RegisterContextPOSIX_arm.h        |  2 +
 .../Process/Utility/RegisterInfoPOSIX_arm.cpp | 30 ++++++++++----
 .../Process/Utility/RegisterInfoPOSIX_arm.h   |  6 ++-
 .../Process/Utility/RegisterInfos_arm.h       | 16 ++++++++
 .../API/lang/c/tls_globals/TestTlsGlobals.py  |  2 +-
 12 files changed, 145 insertions(+), 22 deletions(-)

diff --git a/lldb/include/lldb/Host/linux/Ptrace.h 
b/lldb/include/lldb/Host/linux/Ptrace.h
index aabd3fd4fc557..233ef962782c7 100644
--- a/lldb/include/lldb/Host/linux/Ptrace.h
+++ b/lldb/include/lldb/Host/linux/Ptrace.h
@@ -38,9 +38,15 @@ typedef int __ptrace_request;
 #ifndef PTRACE_SETREGSET
 #define PTRACE_SETREGSET 0x4205
 #endif
+
 #ifndef PTRACE_GET_THREAD_AREA
+#ifdef __arm__
+#define PTRACE_GET_THREAD_AREA 22
+#else
 #define PTRACE_GET_THREAD_AREA 25
-#endif
+#endif // __arm__
+#endif // PTRACE_GET_THREAD_AREA
+
 #ifndef PTRACE_ARCH_PRCTL
 #define PTRACE_ARCH_PRCTL 30
 #endif
diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp 
b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
index 1a9c4593b1b4f..f9c29b95185dc 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
@@ -714,7 +714,8 @@ bool DYLDRendezvous::FindMetadata(const char *name, 
PThreadField field,
     return false;
 
   Address address = list[0].symbol->GetAddress();
-  address.SetOffset(address.GetOffset() + field * sizeof(uint32_t));
+  int field_num = (field == eStructSize) ? 0 : field;
+  address.SetOffset(address.GetOffset() + field_num * sizeof(uint32_t));
 
   // Read from target memory as this allows us to try process memory and
   // fallback to reading from read only sections from the object files. Here we
@@ -737,6 +738,8 @@ const DYLDRendezvous::ThreadInfo 
&DYLDRendezvous::GetThreadInfo() {
   if (!m_thread_info.valid) {
     bool ok = true;
 
+    ok &= FindMetadata("_thread_db_sizeof_pthread", eStructSize,
+                       m_thread_info.pthread_size);
     ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
                        m_thread_info.dtv_offset);
     ok &=
diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h 
b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
index b8bdf78fbdfad..d13b8250cc314 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
@@ -133,6 +133,7 @@ class DYLDRendezvous {
   // the per-thread state.
   struct ThreadInfo {
     bool valid;             // whether we read valid metadata
+    uint32_t pthread_size;  // size of struct pthread
     uint32_t dtv_offset;    // offset of DTV pointer within pthread
     uint32_t dtv_slot_size; // size of one DTV slot
     uint32_t modid_offset;  // offset of module ID within link_map
@@ -345,7 +346,7 @@ class DYLDRendezvous {
   /// supplied by the runtime linker.
   bool TakeSnapshot(SOEntryList &entry_list);
 
-  enum PThreadField { eSize, eNElem, eOffset };
+  enum PThreadField { eSize, eNElem, eOffset, eStructSize };
 
   bool FindMetadata(const char *name, PThreadField field, uint32_t &value);
 
diff --git 
a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp 
b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
index 1d814f93484d8..270339dd74111 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
@@ -843,10 +843,11 @@ DynamicLoaderPOSIXDYLD::GetThreadLocalData(const 
lldb::ModuleSP module_sp,
   LLDB_LOGF(log,
             "GetThreadLocalData info: link_map=0x%" PRIx64
             ", thread info metadata: "
-            "modid_offset=0x%" PRIx32 ", dtv_offset=0x%" PRIx32
-            ", tls_offset=0x%" PRIx32 ", dtv_slot_size=%" PRIx32 "\n",
-            link_map, metadata.modid_offset, metadata.dtv_offset,
-            metadata.tls_offset, metadata.dtv_slot_size);
+            "modid_offset=0x%" PRIx32 ", pthread_size=0x%" PRIx32
+            ", dtv_offset=0x%" PRIx32 ", tls_offset=0x%" PRIx32
+            ", dtv_slot_size=%" PRIx32 "\n",
+            link_map, metadata.modid_offset, metadata.pthread_size,
+            metadata.dtv_offset, metadata.tls_offset, metadata.dtv_slot_size);
 
   // Get the thread pointer.
   addr_t tp = thread->GetThreadPointer();
@@ -865,8 +866,33 @@ DynamicLoaderPOSIXDYLD::GetThreadLocalData(const 
lldb::ModuleSP module_sp,
   }
 
   // Lookup the DTV structure for this thread.
-  addr_t dtv_ptr = tp + metadata.dtv_offset;
-  addr_t dtv = ReadPointer(dtv_ptr);
+  addr_t dtv_ptr = LLDB_INVALID_ADDRESS;
+  if (metadata.dtv_offset < metadata.pthread_size) {
+    // The DTV pointer field lies within `pthread`. This indicates that `libc`
+    // placed `tcbhead_t header`, which contains the `dtv` field, inside
+    // `pthread`, so, for this architecture, `TLS_TCB_AT_TP` is set to `1` and
+    // `TLS_DTV_AT_TP` is `0`. This corresponds to the "Variant II" memory
+    // layout described in Ulrich Drepper's ELF TLS document
+    // (https://akkadia.org/drepper/tls.pdf). The thread pointer points to the
+    // start of `pthread`, and the address of the `dtv` field can be calculated
+    // by adding its offset.
+    dtv_ptr = tp + metadata.dtv_offset;
+  } else if (metadata.dtv_offset == metadata.pthread_size) {
+    // The DTV pointer field is located right after `pthread`. This means that,
+    // for this architecture, `TLS_DTV_AT_TP` is set to `1` in `libc`, which 
may
+    // correspond to the "Version I" memory layout, in which the thread pointer
+    // points directly to the `dtv` field. However, for different 
architectures,
+    // the position of the `dtv` field relative to the thread pointer may vary,
+    // so the following calculations must be adjusted for each platform.
+    //
+    // On AArch64 and ARM, `tp` is known to point directly to `dtv`.
+    const llvm::Triple &triple = module_sp->GetArchitecture().GetTriple();
+    if (triple.isAArch64() || triple.isARM()) {
+      dtv_ptr = tp;
+    }
+  }
+  addr_t dtv = (dtv_ptr != LLDB_INVALID_ADDRESS) ? ReadPointer(dtv_ptr)
+                                                 : LLDB_INVALID_ADDRESS;
   if (dtv == LLDB_INVALID_ADDRESS) {
     LLDB_LOGF(log, "GetThreadLocalData error: fail to read dtv");
     return LLDB_INVALID_ADDRESS;
diff --git 
a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp 
b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
index c1bc6a3f036bf..70b957b0285fe 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
@@ -25,9 +25,10 @@
 
 #if defined(__arm64__) || defined(__aarch64__)
 #include "NativeRegisterContextLinux_arm64dbreg.h"
+#endif
+
 #include "lldb/Host/linux/Ptrace.h"
 #include <asm/ptrace.h>
-#endif
 
 #define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(m_fpr))
 
@@ -68,8 +69,9 @@ NativeRegisterContextLinux::DetermineArchitecture(lldb::tid_t 
tid) {
 
 NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm(
     const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
-    : NativeRegisterContextRegisterInfo(native_thread,
-                                        new 
RegisterInfoPOSIX_arm(target_arch)),
+    : NativeRegisterContextRegisterInfo(
+          native_thread,
+          new RegisterInfoPOSIX_arm(target_arch, /*has_tls_reg=*/true)),
       NativeRegisterContextLinux(native_thread) {
   assert(target_arch.GetMachine() == llvm::Triple::arm);
 
@@ -77,6 +79,7 @@ 
NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm(
   ::memset(&m_gpr_arm, 0, sizeof(m_gpr_arm));
   ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
   ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
+  m_tpidr = 0;
 
   // 16 is just a maximum value, query hardware for actual watchpoint count
   m_max_hwp_supported = 16;
@@ -120,6 +123,11 @@ NativeRegisterContextLinux_arm::ReadRegister(const 
RegisterInfo *reg_info,
     error = ReadFPR();
     if (error.Fail())
       return error;
+  } else if (IsTLS(reg)) {
+    error = ReadTLS();
+    if (error.Success())
+      reg_value.SetUInt32(m_tpidr);
+    return error;
   } else {
     uint32_t full_reg = reg;
     bool is_subreg = reg_info->invalidate_regs &&
@@ -199,6 +207,10 @@ NativeRegisterContextLinux_arm::WriteRegister(const 
RegisterInfo *reg_info,
     return WriteFPR();
   }
 
+  if (IsTLS(reg_index))
+    return Status::FromErrorString(
+        "writing to a thread pointer register is not implemented");
+
   return Status::FromErrorString(
       "failed - register wasn't recognized to be a GPR or an FPR, "
       "write strategy unknown");
@@ -283,6 +295,13 @@ bool NativeRegisterContextLinux_arm::IsFPR(unsigned reg) 
const {
   return false;
 }
 
+bool NativeRegisterContextLinux_arm::IsTLS(unsigned reg) const {
+  if (GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
+      RegisterInfoPOSIX_arm::TLSRegSet)
+    return true;
+  return false;
+}
+
 llvm::Error NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() {
   if (!m_refresh_hwdebug_info)
     return llvm::Error::success();
@@ -480,4 +499,20 @@ Status NativeRegisterContextLinux_arm::WriteFPR() {
 #endif // __arm__
 }
 
+Status NativeRegisterContextLinux_arm::ReadTLS() {
+#ifdef __arm__
+  return NativeProcessLinux::PtraceWrapper(PTRACE_GET_THREAD_AREA,
+                                           m_thread.GetID(), nullptr,
+                                           GetTLSBuffer(), GetTLSSize());
+#else  // __aarch64__
+  Status error;
+
+  struct iovec ioVec;
+  ioVec.iov_base = GetTLSBuffer();
+  ioVec.iov_len = GetTLSSize();
+
+  return ReadRegisterSet(&ioVec, GetTLSSize(), NT_ARM_TLS);
+#endif // __arm__
+}
+
 #endif // defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h 
b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
index cf36859b16ad4..2c292ad19b966 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
@@ -58,15 +58,22 @@ class NativeRegisterContextLinux_arm : public 
NativeRegisterContextLinux,
 
   Status WriteFPR() override;
 
+  Status ReadTLS();
+
   void *GetGPRBuffer() override { return &m_gpr_arm; }
 
   void *GetFPRBuffer() override { return &m_fpr; }
 
   size_t GetFPRSize() override { return sizeof(m_fpr); }
 
+  void *GetTLSBuffer() { return &m_tpidr; }
+
+  size_t GetTLSSize() { return sizeof(m_tpidr); }
+
 private:
   uint32_t m_gpr_arm[k_num_gpr_registers_arm];
   RegisterInfoPOSIX_arm::FPU m_fpr;
+  uint32_t m_tpidr;
 
   bool m_refresh_hwdebug_info;
 
@@ -74,6 +81,8 @@ class NativeRegisterContextLinux_arm : public 
NativeRegisterContextLinux,
 
   bool IsFPR(unsigned reg) const;
 
+  bool IsTLS(unsigned reg) const;
+
   llvm::Error ReadHardwareDebugInfo() override;
 
   llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp 
b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
index 684176bccdf0c..2d8f5a30fcd56 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
@@ -39,6 +39,13 @@ bool RegisterContextPOSIX_arm::IsFPR(unsigned reg) {
   return false;
 }
 
+bool RegisterContextPOSIX_arm::IsTLS(unsigned reg) {
+  if (m_register_info_up->GetRegisterSetFromRegisterIndex(reg) ==
+      RegisterInfoPOSIX_arm::TLSRegSet)
+    return true;
+  return false;
+}
+
 RegisterContextPOSIX_arm::RegisterContextPOSIX_arm(
     lldb_private::Thread &thread,
     std::unique_ptr<RegisterInfoPOSIX_arm> register_info)
diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h 
b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
index 099c37d46f496..ecd927e64cf70 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
@@ -51,6 +51,8 @@ class RegisterContextPOSIX_arm : public 
lldb_private::RegisterContext {
 
   bool IsFPR(unsigned reg);
 
+  bool IsTLS(unsigned reg);
+
   size_t GetFPUSize() { return sizeof(RegisterInfoPOSIX_arm::FPU); }
 
   virtual bool ReadGPR() = 0;
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp 
b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
index d47647422ae21..fbe85a3a92544 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
@@ -75,7 +75,9 @@ GetRegisterInfoCount(const lldb_private::ArchSpec 
&target_arch) {
 enum {
   k_num_gpr_registers = gpr_cpsr - gpr_r0 + 1,
   k_num_fpr_registers = fpu_q15 - fpu_s0 + 1,
-  k_num_register_sets = 2
+  k_num_tls_registers = 1,
+  k_num_register_sets_without_tls = 2,
+  k_num_register_sets_with_tls = 3
 };
 
 // arm general purpose registers.
@@ -142,18 +144,29 @@ static_assert(((sizeof g_fpu_regnums_arm / sizeof 
g_fpu_regnums_arm[0]) - 1) ==
                   k_num_fpr_registers,
               "g_fpu_regnums_arm has wrong number of register infos");
 
+// arm thread local storage registers.
+static const uint32_t g_tls_regnums_arm[] = {
+    tls_tpidruro,
+    LLDB_INVALID_REGNUM // register sets need to end with this flag
+};
+static_assert(((sizeof g_tls_regnums_arm / sizeof g_tls_regnums_arm[0]) - 1) ==
+                  k_num_tls_registers,
+              "g_tls_regnums_arm has wrong number of register infos");
+
 // Register sets for arm.
-static const RegisterSet g_reg_sets_arm[k_num_register_sets] = {
+static const RegisterSet g_reg_sets_arm[k_num_register_sets_with_tls] = {
     {"General Purpose Registers", "gpr", k_num_gpr_registers,
      g_gpr_regnums_arm},
-    {"Floating Point Registers", "fpu", k_num_fpr_registers,
-     g_fpu_regnums_arm}};
+    {"Floating Point Registers", "fpu", k_num_fpr_registers, 
g_fpu_regnums_arm},
+    {"Thread Local Storage Registers", "tls", k_num_tls_registers,
+     g_tls_regnums_arm}};
 
 RegisterInfoPOSIX_arm::RegisterInfoPOSIX_arm(
-    const lldb_private::ArchSpec &target_arch)
+    const lldb_private::ArchSpec &target_arch, bool has_tls_reg)
     : lldb_private::RegisterInfoAndSetInterface(target_arch),
       m_register_info_p(GetRegisterInfoPtr(target_arch)),
-      m_register_info_count(GetRegisterInfoCount(target_arch)) {}
+      m_register_info_count(GetRegisterInfoCount(target_arch)),
+      m_has_tls_reg(has_tls_reg) {}
 
 size_t RegisterInfoPOSIX_arm::GetGPRSize() const {
   return sizeof(struct RegisterInfoPOSIX_arm::GPR);
@@ -169,7 +182,8 @@ RegisterInfoPOSIX_arm::GetRegisterInfo() const {
 }
 
 size_t RegisterInfoPOSIX_arm::GetRegisterSetCount() const {
-  return k_num_register_sets;
+  return m_has_tls_reg ? k_num_register_sets_with_tls
+                       : k_num_register_sets_without_tls;
 }
 
 size_t RegisterInfoPOSIX_arm::GetRegisterSetFromRegisterIndex(
@@ -178,6 +192,8 @@ size_t 
RegisterInfoPOSIX_arm::GetRegisterSetFromRegisterIndex(
     return GPRegSet;
   if (reg_index <= fpu_q15)
     return FPRegSet;
+  if (reg_index == tls_tpidruro && m_has_tls_reg)
+    return TLSRegSet;
   return LLDB_INVALID_REGNUM;
 }
 
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h 
b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
index db155d757ca8c..327e9f746cab0 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
@@ -15,7 +15,7 @@
 
 class RegisterInfoPOSIX_arm : public lldb_private::RegisterInfoAndSetInterface 
{
 public:
-  enum { GPRegSet = 0, FPRegSet};
+  enum { GPRegSet = 0, FPRegSet, TLSRegSet };
 
   struct GPR {
     uint32_t r[16]; // R0-R15
@@ -47,7 +47,8 @@ class RegisterInfoPOSIX_arm : public 
lldb_private::RegisterInfoAndSetInterface {
     uint32_t wcr[16];
   };
 
-  RegisterInfoPOSIX_arm(const lldb_private::ArchSpec &target_arch);
+  RegisterInfoPOSIX_arm(const lldb_private::ArchSpec &target_arch,
+                        bool has_tls_reg = false);
 
   size_t GetGPRSize() const override;
 
@@ -67,6 +68,7 @@ class RegisterInfoPOSIX_arm : public 
lldb_private::RegisterInfoAndSetInterface {
 private:
   const lldb_private::RegisterInfo *m_register_info_p;
   uint32_t m_register_info_count;
+  bool m_has_tls_reg;
 };
 
 #endif // LLDB_SOURCE_PLUGINS_PROCESS_UTILITY_REGISTERINFOPOSIX_ARM_H
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h 
b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
index f535ca40c4030..afaada679cb79 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
@@ -146,6 +146,8 @@ enum {
   fpu_q14,
   fpu_q15,
 
+  tls_tpidruro,
+
   exc_exception,
   exc_fsr,
   exc_far,
@@ -682,6 +684,20 @@ static RegisterInfo g_register_infos_arm[] = {
     FPU_QREG(q14, 56),
     FPU_QREG(q15, 60),
 
+    {
+        "tpidruro",
+        nullptr,
+        4,
+        0,
+        eEncodingUint,
+        eFormatHex,
+        {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_TP,
+         LLDB_INVALID_REGNUM, tls_tpidruro},
+        nullptr,
+        nullptr,
+        nullptr,
+    },
+
     {
         "exception",
         nullptr,
diff --git a/lldb/test/API/lang/c/tls_globals/TestTlsGlobals.py 
b/lldb/test/API/lang/c/tls_globals/TestTlsGlobals.py
index ed696bca54ab4..28d5212cbc7dc 100644
--- a/lldb/test/API/lang/c/tls_globals/TestTlsGlobals.py
+++ b/lldb/test/API/lang/c/tls_globals/TestTlsGlobals.py
@@ -38,7 +38,7 @@ def setUp(self):
     # TLS works differently on Windows, this would need to be implemented
     # separately.
     @skipIfWindows
-    @skipIf(oslist=["linux"], archs=["arm$", "aarch64"])
+    @skipIf(oslist=["linux"], archs=["aarch64"])
     @skipIf(oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]))
     @expectedFailureIf(lldbplatformutil.xcode15LinkerBug())
     def test(self):

>From 16150f1ee19d0e5950da063fa56a6679b4ddff4f Mon Sep 17 00:00:00 2001
From: Igor Kudrin <[email protected]>
Date: Tue, 17 Feb 2026 17:36:17 -0800
Subject: [PATCH 2/9] fixup! Add comments

---
 lldb/include/lldb/Host/linux/Ptrace.h                    | 1 +
 .../Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp  | 2 ++
 .../Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h    | 9 ++++++++-
 .../Plugins/Process/Utility/RegisterInfoPOSIX_arm.h      | 2 ++
 4 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/lldb/include/lldb/Host/linux/Ptrace.h 
b/lldb/include/lldb/Host/linux/Ptrace.h
index 233ef962782c7..0a45516a45c7f 100644
--- a/lldb/include/lldb/Host/linux/Ptrace.h
+++ b/lldb/include/lldb/Host/linux/Ptrace.h
@@ -41,6 +41,7 @@ typedef int __ptrace_request;
 
 #ifndef PTRACE_GET_THREAD_AREA
 #ifdef __arm__
+// Arm has a different value, see arch/arm/include/uapi/asm/ptrace.h.
 #define PTRACE_GET_THREAD_AREA 22
 #else
 #define PTRACE_GET_THREAD_AREA 25
diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp 
b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
index f9c29b95185dc..98c753c2c2490 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
@@ -714,6 +714,8 @@ bool DYLDRendezvous::FindMetadata(const char *name, 
PThreadField field,
     return false;
 
   Address address = list[0].symbol->GetAddress();
+  // eSize, eNElem, and eOffset correspond to the fields of the DESC structure.
+  // eStructSize instructs to read a value generated by DB_STRUCT.
   int field_num = (field == eStructSize) ? 0 : field;
   address.SetOffset(address.GetOffset() + field_num * sizeof(uint32_t));
 
diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h 
b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
index d13b8250cc314..41eab64dcfc3b 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
@@ -346,7 +346,14 @@ class DYLDRendezvous {
   /// supplied by the runtime linker.
   bool TakeSnapshot(SOEntryList &entry_list);
 
-  enum PThreadField { eSize, eNElem, eOffset, eStructSize };
+  /// For the definitions of the metadata entries, see
+  /// <glibc>/nptl_db/(db_info.c, structs.def, thread_dbP.h).
+  enum PThreadField {
+    eSize,      // Size of an element of a field as defined by DESC, bits
+    eNElem,     // Number of elements in the field
+    eOffset,    // Offset of the field
+    eStructSize // Size of a type as defined by DB_STRUCT, bytes
+  };
 
   bool FindMetadata(const char *name, PThreadField field, uint32_t &value);
 
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h 
b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
index 327e9f746cab0..c6384f64bfff6 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
@@ -68,6 +68,8 @@ class RegisterInfoPOSIX_arm : public 
lldb_private::RegisterInfoAndSetInterface {
 private:
   const lldb_private::RegisterInfo *m_register_info_p;
   uint32_t m_register_info_count;
+  // Only provide information about the TLS register to users of this class 
that
+  // can handle it. Currently, only `NativeRegisterContextLinux_arm` reads it.
   bool m_has_tls_reg;
 };
 

>From c4b7acbd6c33846eeee63e1df24c81cad0476b11 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <[email protected]>
Date: Tue, 17 Feb 2026 17:38:34 -0800
Subject: [PATCH 3/9] fixup! Remove changes in RegisterContextPOSIX_arm

---
 .../Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp   | 7 -------
 .../Plugins/Process/Utility/RegisterContextPOSIX_arm.h     | 2 --
 2 files changed, 9 deletions(-)

diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp 
b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
index 2d8f5a30fcd56..684176bccdf0c 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.cpp
@@ -39,13 +39,6 @@ bool RegisterContextPOSIX_arm::IsFPR(unsigned reg) {
   return false;
 }
 
-bool RegisterContextPOSIX_arm::IsTLS(unsigned reg) {
-  if (m_register_info_up->GetRegisterSetFromRegisterIndex(reg) ==
-      RegisterInfoPOSIX_arm::TLSRegSet)
-    return true;
-  return false;
-}
-
 RegisterContextPOSIX_arm::RegisterContextPOSIX_arm(
     lldb_private::Thread &thread,
     std::unique_ptr<RegisterInfoPOSIX_arm> register_info)
diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h 
b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
index ecd927e64cf70..099c37d46f496 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm.h
@@ -51,8 +51,6 @@ class RegisterContextPOSIX_arm : public 
lldb_private::RegisterContext {
 
   bool IsFPR(unsigned reg);
 
-  bool IsTLS(unsigned reg);
-
   size_t GetFPUSize() { return sizeof(RegisterInfoPOSIX_arm::FPU); }
 
   virtual bool ReadGPR() = 0;

>From b259f0249fbf5c07531ba5263ba87bf542f1b32e Mon Sep 17 00:00:00 2001
From: Igor Kudrin <[email protected]>
Date: Tue, 17 Feb 2026 17:39:11 -0800
Subject: [PATCH 4/9] fixup! Simplify NativeRegisterContextLinux_arm::IsTLS()

---
 .../Process/Linux/NativeRegisterContextLinux_arm.cpp        | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git 
a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp 
b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
index 70b957b0285fe..21ba06c902d33 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
@@ -296,10 +296,8 @@ bool NativeRegisterContextLinux_arm::IsFPR(unsigned reg) 
const {
 }
 
 bool NativeRegisterContextLinux_arm::IsTLS(unsigned reg) const {
-  if (GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
-      RegisterInfoPOSIX_arm::TLSRegSet)
-    return true;
-  return false;
+  return GetRegisterInfo().GetRegisterSetFromRegisterIndex(reg) ==
+         RegisterInfoPOSIX_arm::TLSRegSet;
 }
 
 llvm::Error NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() {

>From 0a115d99c12c0fbbf3187e367f4acd8555d6cc50 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <[email protected]>
Date: Tue, 17 Feb 2026 17:40:13 -0800
Subject: [PATCH 5/9] fixup! "tpidruro" -> "tpidr"

---
 .../Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp       | 4 ++--
 lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h     | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp 
b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
index fbe85a3a92544..49a3629019003 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
@@ -146,7 +146,7 @@ static_assert(((sizeof g_fpu_regnums_arm / sizeof 
g_fpu_regnums_arm[0]) - 1) ==
 
 // arm thread local storage registers.
 static const uint32_t g_tls_regnums_arm[] = {
-    tls_tpidruro,
+    tls_tpidr,
     LLDB_INVALID_REGNUM // register sets need to end with this flag
 };
 static_assert(((sizeof g_tls_regnums_arm / sizeof g_tls_regnums_arm[0]) - 1) ==
@@ -192,7 +192,7 @@ size_t 
RegisterInfoPOSIX_arm::GetRegisterSetFromRegisterIndex(
     return GPRegSet;
   if (reg_index <= fpu_q15)
     return FPRegSet;
-  if (reg_index == tls_tpidruro && m_has_tls_reg)
+  if (reg_index == tls_tpidr && m_has_tls_reg)
     return TLSRegSet;
   return LLDB_INVALID_REGNUM;
 }
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h 
b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
index afaada679cb79..99efe9b6449a0 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
@@ -146,7 +146,7 @@ enum {
   fpu_q14,
   fpu_q15,
 
-  tls_tpidruro,
+  tls_tpidr,
 
   exc_exception,
   exc_fsr,
@@ -685,14 +685,14 @@ static RegisterInfo g_register_infos_arm[] = {
     FPU_QREG(q15, 60),
 
     {
-        "tpidruro",
+        "tpidr",
         nullptr,
         4,
         0,
         eEncodingUint,
         eFormatHex,
         {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_TP,
-         LLDB_INVALID_REGNUM, tls_tpidruro},
+         LLDB_INVALID_REGNUM, tls_tpidr},
         nullptr,
         nullptr,
         nullptr,

>From 1706e80502e78993ebacaae962e029bcc0d70a2b Mon Sep 17 00:00:00 2001
From: Igor Kudrin <[email protected]>
Date: Tue, 17 Feb 2026 19:38:51 -0800
Subject: [PATCH 6/9] fixup! fix comment: "Version I" -> "Variant I"

---
 .../Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git 
a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp 
b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
index 270339dd74111..3541d2f998c45 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
@@ -880,7 +880,7 @@ DynamicLoaderPOSIXDYLD::GetThreadLocalData(const 
lldb::ModuleSP module_sp,
   } else if (metadata.dtv_offset == metadata.pthread_size) {
     // The DTV pointer field is located right after `pthread`. This means that,
     // for this architecture, `TLS_DTV_AT_TP` is set to `1` in `libc`, which 
may
-    // correspond to the "Version I" memory layout, in which the thread pointer
+    // correspond to the "Variant I" memory layout, in which the thread pointer
     // points directly to the `dtv` field. However, for different 
architectures,
     // the position of the `dtv` field relative to the thread pointer may vary,
     // so the following calculations must be adjusted for each platform.

>From fa0f11993c520c47588c29dd5893c8eaffca92f0 Mon Sep 17 00:00:00 2001
From: Igor Kudrin <[email protected]>
Date: Wed, 18 Feb 2026 22:45:11 -0800
Subject: [PATCH 7/9] fixup! Revert "fixup! "tpidruro" -> "tpidr""

This reverts commit 0a115d99c12c0fbbf3187e367f4acd8555d6cc50.
---
 .../Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp       | 4 ++--
 lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h     | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp 
b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
index 49a3629019003..fbe85a3a92544 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
@@ -146,7 +146,7 @@ static_assert(((sizeof g_fpu_regnums_arm / sizeof 
g_fpu_regnums_arm[0]) - 1) ==
 
 // arm thread local storage registers.
 static const uint32_t g_tls_regnums_arm[] = {
-    tls_tpidr,
+    tls_tpidruro,
     LLDB_INVALID_REGNUM // register sets need to end with this flag
 };
 static_assert(((sizeof g_tls_regnums_arm / sizeof g_tls_regnums_arm[0]) - 1) ==
@@ -192,7 +192,7 @@ size_t 
RegisterInfoPOSIX_arm::GetRegisterSetFromRegisterIndex(
     return GPRegSet;
   if (reg_index <= fpu_q15)
     return FPRegSet;
-  if (reg_index == tls_tpidr && m_has_tls_reg)
+  if (reg_index == tls_tpidruro && m_has_tls_reg)
     return TLSRegSet;
   return LLDB_INVALID_REGNUM;
 }
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h 
b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
index 99efe9b6449a0..afaada679cb79 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
@@ -146,7 +146,7 @@ enum {
   fpu_q14,
   fpu_q15,
 
-  tls_tpidr,
+  tls_tpidruro,
 
   exc_exception,
   exc_fsr,
@@ -685,14 +685,14 @@ static RegisterInfo g_register_infos_arm[] = {
     FPU_QREG(q15, 60),
 
     {
-        "tpidr",
+        "tpidruro",
         nullptr,
         4,
         0,
         eEncodingUint,
         eFormatHex,
         {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_TP,
-         LLDB_INVALID_REGNUM, tls_tpidr},
+         LLDB_INVALID_REGNUM, tls_tpidruro},
         nullptr,
         nullptr,
         nullptr,

>From 8555dc49fd117b87588093696f67d432fc23242a Mon Sep 17 00:00:00 2001
From: Igor Kudrin <[email protected]>
Date: Wed, 18 Feb 2026 23:33:41 -0800
Subject: [PATCH 8/9] fixup! Add a distinct offset for tpidruro

---
 .../Linux/NativeRegisterContextLinux_arm.cpp       | 14 +++++++++++---
 .../Process/Linux/NativeRegisterContextLinux_arm.h |  6 +++---
 .../Process/Utility/RegisterInfoPOSIX_arm.cpp      |  8 +++++---
 .../Process/Utility/RegisterInfoPOSIX_arm.h        |  4 ++++
 .../Plugins/Process/Utility/RegisterInfos_arm.h    |  6 +++++-
 .../Common/arm/RegisterContextWindows_arm.cpp      |  1 +
 6 files changed, 29 insertions(+), 10 deletions(-)

diff --git 
a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp 
b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
index 21ba06c902d33..c83cea2bbf5bd 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
@@ -30,7 +30,7 @@
 #include "lldb/Host/linux/Ptrace.h"
 #include <asm/ptrace.h>
 
-#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(m_fpr))
+#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(m_fpr) + sizeof(m_tls))
 
 #ifndef PTRACE_GETVFPREGS
 #define PTRACE_GETVFPREGS 27
@@ -76,10 +76,10 @@ 
NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm(
   assert(target_arch.GetMachine() == llvm::Triple::arm);
 
   ::memset(&m_fpr, 0, sizeof(m_fpr));
+  ::memset(&m_tls, 0, sizeof(m_tls));
   ::memset(&m_gpr_arm, 0, sizeof(m_gpr_arm));
   ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
   ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
-  m_tpidr = 0;
 
   // 16 is just a maximum value, query hardware for actual watchpoint count
   m_max_hwp_supported = 16;
@@ -126,7 +126,7 @@ NativeRegisterContextLinux_arm::ReadRegister(const 
RegisterInfo *reg_info,
   } else if (IsTLS(reg)) {
     error = ReadTLS();
     if (error.Success())
-      reg_value.SetUInt32(m_tpidr);
+      reg_value.SetUInt32(m_tls.tpidruro);
     return error;
   } else {
     uint32_t full_reg = reg;
@@ -229,10 +229,16 @@ Status 
NativeRegisterContextLinux_arm::ReadAllRegisterValues(
   if (error.Fail())
     return error;
 
+  error = ReadTLS();
+  if (error.Fail())
+    return error;
+
   uint8_t *dst = data_sp->GetBytes();
   ::memcpy(dst, &m_gpr_arm, GetGPRSize());
   dst += GetGPRSize();
   ::memcpy(dst, &m_fpr, sizeof(m_fpr));
+  dst += sizeof(m_fpr);
+  ::memcpy(dst, &m_tls, sizeof(m_tls));
 
   return error;
 }
@@ -278,6 +284,8 @@ Status 
NativeRegisterContextLinux_arm::WriteAllRegisterValues(
   if (error.Fail())
     return error;
 
+  // Note: writing to a thread pointer register is not implemented.
+
   return error;
 }
 
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h 
b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
index 2c292ad19b966..3722f667b62c5 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
@@ -66,14 +66,14 @@ class NativeRegisterContextLinux_arm : public 
NativeRegisterContextLinux,
 
   size_t GetFPRSize() override { return sizeof(m_fpr); }
 
-  void *GetTLSBuffer() { return &m_tpidr; }
+  void *GetTLSBuffer() { return &m_tls; }
 
-  size_t GetTLSSize() { return sizeof(m_tpidr); }
+  size_t GetTLSSize() { return sizeof(m_tls); }
 
 private:
   uint32_t m_gpr_arm[k_num_gpr_registers_arm];
   RegisterInfoPOSIX_arm::FPU m_fpr;
-  uint32_t m_tpidr;
+  RegisterInfoPOSIX_arm::TLS m_tls;
 
   bool m_refresh_hwdebug_info;
 
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp 
b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
index fbe85a3a92544..021ea56325e77 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.cpp
@@ -24,13 +24,15 @@ using namespace lldb_private;
 #define FPSCR_OFFSET                                                           
\
   (LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm::FPU, fpscr) +                
\
    sizeof(RegisterInfoPOSIX_arm::GPR))
+#define TLS_OFFSET                                                             
\
+  (sizeof(RegisterInfoPOSIX_arm::GPR) + sizeof(RegisterInfoPOSIX_arm::FPU))
 #define EXC_OFFSET(idx)                                                        
\
-  ((idx)*4 + sizeof(RegisterInfoPOSIX_arm::GPR) +                              
\
-   sizeof(RegisterInfoPOSIX_arm::FPU))
+  ((idx) * 4 + sizeof(RegisterInfoPOSIX_arm::GPR) +                            
\
+   sizeof(RegisterInfoPOSIX_arm::FPU) + sizeof(RegisterInfoPOSIX_arm::TLS))
 #define DBG_OFFSET(reg)                                                        
\
   ((LLVM_EXTENSION offsetof(RegisterInfoPOSIX_arm::DBG, reg) +                 
\
     sizeof(RegisterInfoPOSIX_arm::GPR) + sizeof(RegisterInfoPOSIX_arm::FPU) +  
\
-    sizeof(RegisterInfoPOSIX_arm::EXC)))
+    sizeof(RegisterInfoPOSIX_arm::TLS) + sizeof(RegisterInfoPOSIX_arm::EXC)))
 
 #define DEFINE_DBG(reg, i)                                                     
\
   #reg, NULL, sizeof(((RegisterInfoPOSIX_arm::DBG *) NULL)->reg[i]),           
\
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h 
b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
index c6384f64bfff6..0695b0afd858e 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm.h
@@ -40,6 +40,10 @@ class RegisterInfoPOSIX_arm : public 
lldb_private::RegisterInfoAndSetInterface {
     uint32_t far; /* Virtual Fault Address */
   };
 
+  struct TLS {
+    uint32_t tpidruro;
+  };
+
   struct DBG {
     uint32_t bvr[16];
     uint32_t bcr[16];
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h 
b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
index afaada679cb79..6bcfe949bd478 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm.h
@@ -32,6 +32,10 @@ using namespace lldb_private;
 #error FPSCR_OFFSET must be defined before including this header file
 #endif
 
+#ifndef TLS_OFFSET
+#error TLS_OFFSET must be defined before including this header file
+#endif
+
 #ifndef EXC_OFFSET
 #error EXC_OFFSET_NAME must be defined before including this header file
 #endif
@@ -688,7 +692,7 @@ static RegisterInfo g_register_infos_arm[] = {
         "tpidruro",
         nullptr,
         4,
-        0,
+        TLS_OFFSET,
         eEncodingUint,
         eFormatHex,
         {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_TP,
diff --git 
a/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp 
b/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp
index bf970bd521c73..6f0efc257695d 100644
--- 
a/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp
+++ 
b/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp
@@ -25,6 +25,7 @@ using namespace lldb_private;
 #define GPR_OFFSET(idx) 0
 #define FPU_OFFSET(idx) 0
 #define FPSCR_OFFSET 0
+#define TLS_OFFSET 0
 #define EXC_OFFSET(reg) 0
 #define DBG_OFFSET_NAME(reg) 0
 

>From 74ca6254bc708ebdd20eb42e62c68a44578d31df Mon Sep 17 00:00:00 2001
From: Igor Kudrin <[email protected]>
Date: Thu, 19 Feb 2026 21:01:13 -0800
Subject: [PATCH 9/9] fixup! add a release note

---
 llvm/docs/ReleaseNotes.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 257d962ba8941..8858beaa8eecd 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -195,6 +195,10 @@ Changes to LLDB
 * The crashed thread is now automatically selected on start.
 * Threads are listed in incrmental order by pid then by tid.
 
+### Linux
+
+* Thread local variables are now supported on Arm Linux if the program being 
debugged is using glibc.
+
 Changes to BOLT
 ---------------
 

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to