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
