[Lldb-commits] [lldb] [llvm] [lldb][RISCV] Implement access to TLS variables on RISC-V (PR #191410)

Georgiy Samoylov via lldb-commits lldb-commits at lists.llvm.org
Mon Apr 13 01:54:59 PDT 2026


https://github.com/sga-sc updated https://github.com/llvm/llvm-project/pull/191410

>From 3bc76ed52ffa3088ee93fd5bc09683e85a5acd9b Mon Sep 17 00:00:00 2001
From: Georgiy Samoylov <Ignitor21838 at gmail.com>
Date: Fri, 10 Apr 2026 15:55:51 +0300
Subject: [PATCH 1/3] [lldb][RISCV] Add TP constants for RISC-V

---
 lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp             | 3 ++-
 lldb/source/Plugins/Process/Utility/RegisterInfos_riscv64.h | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
index dce905f08aa5f..bf0e5a15ad790 100644
--- a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
+++ b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
@@ -91,7 +91,7 @@ static const std::array<RegisterInfo, 33> g_register_infos = {
      DEFINE_GENERIC_REGISTER_STUB(ra, nullptr, LLDB_REGNUM_GENERIC_RA),
      DEFINE_GENERIC_REGISTER_STUB(sp, nullptr, LLDB_REGNUM_GENERIC_SP),
      DEFINE_REGISTER_STUB(gp, nullptr),
-     DEFINE_REGISTER_STUB(tp, nullptr),
+     DEFINE_GENERIC_REGISTER_STUB(tp, nullptr, LLDB_REGNUM_GENERIC_TP),
      DEFINE_REGISTER_STUB(t0, nullptr),
      DEFINE_REGISTER_STUB(t1, nullptr),
      DEFINE_REGISTER_STUB(t2, nullptr),
@@ -820,6 +820,7 @@ static uint32_t GetGenericNum(llvm::StringRef name) {
       .Cases({"ra", "x1"}, LLDB_REGNUM_GENERIC_RA)
       .Cases({"sp", "x2"}, LLDB_REGNUM_GENERIC_SP)
       .Cases({"fp", "s0"}, LLDB_REGNUM_GENERIC_FP)
+      .Cases({"tp", "x4"}, LLDB_REGNUM_GENERIC_TP)
       .Case("a0", LLDB_REGNUM_GENERIC_ARG1)
       .Case("a1", LLDB_REGNUM_GENERIC_ARG2)
       .Case("a2", LLDB_REGNUM_GENERIC_ARG3)
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfos_riscv64.h b/lldb/source/Plugins/Process/Utility/RegisterInfos_riscv64.h
index 628ed3770bdc0..c9959168b1536 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfos_riscv64.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfos_riscv64.h
@@ -85,7 +85,7 @@ static lldb_private::RegisterInfo g_register_infos_riscv64_gpr[] = {
     DEFINE_GPR64_ALT(ra, x1, LLDB_REGNUM_GENERIC_RA),
     DEFINE_GPR64_ALT(sp, x2, LLDB_REGNUM_GENERIC_SP),
     DEFINE_GPR64_ALT(gp, x3, LLDB_INVALID_REGNUM),
-    DEFINE_GPR64_ALT(tp, x4, LLDB_INVALID_REGNUM),
+    DEFINE_GPR64_ALT(tp, x4, LLDB_REGNUM_GENERIC_TP),
     DEFINE_GPR64_ALT(t0, x5, LLDB_INVALID_REGNUM),
     DEFINE_GPR64_ALT(t1, x6, LLDB_INVALID_REGNUM),
     DEFINE_GPR64_ALT(t2, x7, LLDB_INVALID_REGNUM),

>From aba5099c73b8dbe651d24e757b16453c4e725721 Mon Sep 17 00:00:00 2001
From: Georgiy Samoylov <Ignitor21838 at gmail.com>
Date: Fri, 10 Apr 2026 15:56:11 +0300
Subject: [PATCH 2/3] [lldb][RISCV] Add helper for VMA search inside PT_TLS

---
 .../POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp     | 57 ++++++++++++++++++-
 1 file changed, 55 insertions(+), 2 deletions(-)

diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
index f80fb3b2d2655..5272eaac51f32 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
@@ -9,6 +9,7 @@
 // Main header include
 #include "DynamicLoaderPOSIXDYLD.h"
 
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
 #include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h"
 #include "lldb/Breakpoint/BreakpointLocation.h"
 #include "lldb/Core/Debugger.h"
@@ -27,6 +28,7 @@
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/Log.h"
 #include "lldb/Utility/ProcessInfo.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Support/ThreadPool.h"
 
 #include <memory>
@@ -814,6 +816,26 @@ addr_t DynamicLoaderPOSIXDYLD::GetEntryPoint() {
   return m_entry_point;
 }
 
+static lldb::addr_t GetPTTLSVAddr(const lldb::ModuleSP &module_sp) {
+  if (!module_sp)
+    return LLDB_INVALID_ADDRESS;
+
+  ObjectFile *objfile = module_sp->GetObjectFile();
+  if (!objfile)
+    return LLDB_INVALID_ADDRESS;
+
+  auto *elf_obj = llvm::dyn_cast<ObjectFileELF>(objfile);
+  if (!elf_obj)
+    return LLDB_INVALID_ADDRESS;
+
+  for (const auto &phdr : elf_obj->ProgramHeaders()) {
+    if (phdr.p_type == llvm::ELF::PT_TLS)
+      return phdr.p_vaddr;
+  }
+
+  return LLDB_INVALID_ADDRESS;
+}
+
 lldb::addr_t
 DynamicLoaderPOSIXDYLD::GetThreadLocalData(const lldb::ModuleSP module_sp,
                                            const lldb::ThreadSP thread,
@@ -894,6 +916,19 @@ DynamicLoaderPOSIXDYLD::GetThreadLocalData(const lldb::ModuleSP module_sp,
       dtv_ptr = tp;
     }
   }
+  const llvm::Triple &triple = module_sp->GetArchitecture().GetTriple();
+
+  // On RISC-V with glibc the TLS layout uses Variant I (TLS_DTV_AT_TP).
+  // The thread pointer (tp) points just past a tcbhead_t header which
+  // contains two pointers: { dtv, private }. This means the DTV pointer
+  // itself is located two pointer-sized slots before tp, so dtv_ptr must
+  // be computed as tp - 2 * sizeof(void*). See MaskRay, “All about
+  // thread-local storage” (RISC-V/glibc section) and glibc's RISC-V
+  // TLS port (__tls_get_addr / THREAD_DTV).
+  if (triple.isRISCV()) {
+    dtv_ptr = tp - 2 * triple.getArchPointerBitWidth() / 8;
+  }
+
   addr_t dtv = (dtv_ptr != LLDB_INVALID_ADDRESS) ? ReadPointer(dtv_ptr)
                                                  : LLDB_INVALID_ADDRESS;
   if (dtv == LLDB_INVALID_ADDRESS) {
@@ -915,8 +950,26 @@ DynamicLoaderPOSIXDYLD::GetThreadLocalData(const lldb::ModuleSP module_sp,
   if (tls_block == LLDB_INVALID_ADDRESS) {
     LLDB_LOGF(log, "GetThreadLocalData error: fail to read tls_block");
     return LLDB_INVALID_ADDRESS;
-  } else
-    return tls_block + tls_file_addr;
+  }
+
+  // DW_OP_GNU_push_tls_address gives us a value in tls_file_addr that can be
+  // either:
+  //   - a pure offset inside the TLS block (e.g. x86_64/glibc), or
+  //   - a virtual address inside the PT_TLS segment (e.g. RISC-V/glibc),
+  //     roughly PT_TLS.p_vaddr + TPOFF(sym).
+  // To handle both cases, we try to normalize it to a plain offset (tpoff)
+  // by subtracting PT_TLS.p_vaddr when available.
+  addr_t pt_tls_vaddr = GetPTTLSVAddr(module_sp);
+  addr_t tpoff = tls_file_addr;
+
+  // If the module has a PT_TLS segment and the DWARF value lies at or above
+  // its p_vaddr, treat tls_file_addr as a VMA within PT_TLS and convert it
+  // to an offset. Otherwise, keep the original value (it is already an offset
+  // on targets like x86_64).
+  if (pt_tls_vaddr != LLDB_INVALID_ADDRESS && tls_file_addr >= pt_tls_vaddr)
+    tpoff = tls_file_addr - pt_tls_vaddr;
+
+  return tls_block + tpoff;
 }
 
 void DynamicLoaderPOSIXDYLD::ResolveExecutableModule(

>From 556089fb07aef4d1dafa1048ed67a69255bde1cd Mon Sep 17 00:00:00 2001
From: Georgiy Samoylov <Ignitor21838 at gmail.com>
Date: Mon, 13 Apr 2026 09:48:38 +0300
Subject: [PATCH 3/3] Add release notes

---
 llvm/docs/ReleaseNotes.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index a47c5a48d51b8..dddc72e556b12 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -283,7 +283,7 @@ Changes to LLDB
 ### Linux
 
 * On Arm Linux, the `tpidruro` register can now be read. Writing to this register is not supported.
-* Thread local variables are now supported on Arm Linux if the program being debugged is using glibc.
+* Thread local variables are now supported on Arm and RISC-V Linux if the program being debugged is using glibc.
 * LLDB now supports AArch64 Linux systems that only have SME (as opposed to
   SVE and SME). See the AArch64 Linux [documentation](https://lldb.llvm.org/use/aarch64-linux.html#sme-only-systems)
   for more details.



More information about the lldb-commits mailing list