[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