DavidSpickett updated this revision to Diff 532589. DavidSpickett added a comment. This revision is now accepted and ready to land.
Change test so that we're reading with assembly and writing with lldb and vice versa. Instead of trying to get clever and actually move the thread storage about. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D152516/new/ https://reviews.llvm.org/D152516 Files: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h lldb/test/API/linux/aarch64/tls_register/Makefile lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py lldb/test/API/linux/aarch64/tls_register/main.c
Index: lldb/test/API/linux/aarch64/tls_register/main.c =================================================================== --- /dev/null +++ lldb/test/API/linux/aarch64/tls_register/main.c @@ -0,0 +1,24 @@ +#include <stdbool.h> +#include <stdint.h> + +int main() { + // Save tpidr to restore later. + uint64_t tpidr = 0; + __asm__ volatile("mrs %0, tpidr_el0" : "=r"(tpidr)); + + // Set a pattern for lldb to find. + uint64_t pattern = 0x1122334455667788; + __asm__ volatile("msr tpidr_el0, %0" ::"r"(pattern)); + + // Set break point at this line. + // lldb will now set its own pattern for us to find. + + uint64_t new_tpidr = pattern; + __asm__ volatile("mrs %0, tpidr_el0" : "=r"(new_tpidr)); + volatile bool tpidr_was_set = new_tpidr == 0x8877665544332211; + + // Restore the original. + __asm__ volatile("msr tpidr_el0, %0" ::"r"(tpidr)); + + return 0; // Set break point 2 at this line. +} Index: lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py =================================================================== --- /dev/null +++ lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py @@ -0,0 +1,66 @@ +""" +Test lldb's ability to read and write the AArch64 TLS register tpidr. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class AArch64LinuxTLSRegister(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + def test_tls(self): + self.build() + self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line( + self, + "main.c", + line_number("main.c", "// Set break point at this line."), + num_expected_locations=1, + ) + + lldbutil.run_break_set_by_file_and_line( + self, + "main.c", + line_number("main.c", "// Set break point 2 at this line."), + num_expected_locations=1, + ) + + self.runCmd("run", RUN_SUCCEEDED) + + if self.process().GetState() == lldb.eStateExited: + self.fail("Test program failed to run.") + + self.expect( + "thread list", + STOPPED_DUE_TO_BREAKPOINT, + substrs=["stopped", "stop reason = breakpoint"], + ) + + # Since we can't predict what the value will be, the program has set + # a target value for us to find. + + regs = self.thread().GetSelectedFrame().GetRegisters() + tls_regs = regs.GetFirstValueByName("Thread Local Storage Registers") + self.assertTrue(tls_regs.IsValid(), "No TLS registers found.") + tpidr = tls_regs.GetChildMemberWithName("tpidr") + self.assertTrue(tpidr.IsValid(), "No tpidr register found.") + + self.assertEqual(tpidr.GetValueAsUnsigned(), 0x1122334455667788) + + # Set our own value for the program to find. + self.expect("register write tpidr 0x{:x}".format(0x8877665544332211)) + self.expect("continue") + + self.expect( + "thread list", + STOPPED_DUE_TO_BREAKPOINT, + substrs=["stopped", "stop reason = breakpoint"], + ) + + self.expect("p tpidr_was_set", substrs=["true"]) \ No newline at end of file Index: lldb/test/API/linux/aarch64/tls_register/Makefile =================================================================== --- /dev/null +++ lldb/test/API/linux/aarch64/tls_register/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules Index: lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h =================================================================== --- lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h +++ lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h @@ -28,6 +28,7 @@ eRegsetMaskSVE = 1, eRegsetMaskPAuth = 2, eRegsetMaskMTE = 4, + eRegsetMaskTLS = 8, eRegsetMaskDynamic = ~1, }; @@ -102,6 +103,8 @@ void AddRegSetMTE(); + void AddRegSetTLS(); + uint32_t ConfigureVectorLength(uint32_t sve_vq); bool VectorSizeIsValid(uint32_t vq) { @@ -121,6 +124,7 @@ bool IsSVERegVG(unsigned reg) const; bool IsPAuthReg(unsigned reg) const; bool IsMTEReg(unsigned reg) const; + bool IsTLSReg(unsigned reg) const; uint32_t GetRegNumSVEZ0() const; uint32_t GetRegNumSVEFFR() const; @@ -129,6 +133,7 @@ uint32_t GetRegNumSVEVG() const; uint32_t GetPAuthOffset() const; uint32_t GetMTEOffset() const; + uint32_t GetTLSOffset() const; private: typedef std::map<uint32_t, std::vector<lldb_private::RegisterInfo>> @@ -155,6 +160,7 @@ std::vector<uint32_t> pauth_regnum_collection; std::vector<uint32_t> m_mte_regnum_collection; + std::vector<uint32_t> m_tls_regnum_collection; }; #endif Index: lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp =================================================================== --- lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp +++ lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp @@ -78,12 +78,16 @@ static lldb_private::RegisterInfo g_register_infos_mte[] = { DEFINE_EXTENSION_REG(mte_ctrl)}; +static lldb_private::RegisterInfo g_register_infos_tls[] = { + DEFINE_EXTENSION_REG(tpidr)}; + // Number of register sets provided by this context. enum { k_num_gpr_registers = gpr_w28 - gpr_x0 + 1, k_num_fpr_registers = fpu_fpcr - fpu_v0 + 1, k_num_sve_registers = sve_ffr - sve_vg + 1, k_num_mte_register = 1, + k_num_tls_register = 1, k_num_pauth_register = 2, k_num_register_sets_default = 2, k_num_register_sets = 3 @@ -189,6 +193,9 @@ static const lldb_private::RegisterSet g_reg_set_mte_arm64 = { "MTE Control Register", "mte", k_num_mte_register, nullptr}; +static const lldb_private::RegisterSet g_reg_set_tls_arm64 = { + "Thread Local Storage Registers", "tls", k_num_tls_register, nullptr}; + RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64( const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets) : lldb_private::RegisterInfoAndSetInterface(target_arch), @@ -229,6 +236,10 @@ if (m_opt_regsets.AllSet(eRegsetMaskMTE)) AddRegSetMTE(); + // tpidr is always present, but in future there will be others so this is + // done as a dynamic set. + AddRegSetTLS(); + m_register_info_count = m_dynamic_reg_infos.size(); m_register_info_p = m_dynamic_reg_infos.data(); m_register_set_p = m_dynamic_reg_sets.data(); @@ -312,6 +323,21 @@ m_dynamic_reg_sets.back().registers = m_mte_regnum_collection.data(); } +void RegisterInfoPOSIX_arm64::AddRegSetTLS() { + uint32_t tls_regnum = m_dynamic_reg_infos.size(); + m_tls_regnum_collection.push_back(tls_regnum); + m_dynamic_reg_infos.push_back(g_register_infos_tls[0]); + m_dynamic_reg_infos[tls_regnum].byte_offset = + m_dynamic_reg_infos[tls_regnum - 1].byte_offset + + m_dynamic_reg_infos[tls_regnum - 1].byte_size; + m_dynamic_reg_infos[tls_regnum].kinds[lldb::eRegisterKindLLDB] = tls_regnum; + + m_per_regset_regnum_range[m_register_set_count] = + std::make_pair(tls_regnum, tls_regnum + 1); + m_dynamic_reg_sets.push_back(g_reg_set_tls_arm64); + m_dynamic_reg_sets.back().registers = m_tls_regnum_collection.data(); +} + uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLength(uint32_t sve_vq) { // sve_vq contains SVE Quad vector length in context of AArch64 SVE. // SVE register infos if enabled cannot be disabled by selecting sve_vq = 0. @@ -403,6 +429,10 @@ return llvm::is_contained(m_mte_regnum_collection, reg); } +bool RegisterInfoPOSIX_arm64::IsTLSReg(unsigned reg) const { + return llvm::is_contained(m_tls_regnum_collection, reg); +} + uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; } uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; } @@ -420,3 +450,7 @@ uint32_t RegisterInfoPOSIX_arm64::GetMTEOffset() const { return m_register_info_p[m_mte_regnum_collection[0]].byte_offset; } + +uint32_t RegisterInfoPOSIX_arm64::GetTLSOffset() const { + return m_register_info_p[m_tls_regnum_collection[0]].byte_offset; +} Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -83,6 +83,7 @@ bool m_fpu_is_valid; bool m_sve_buffer_is_valid; bool m_mte_ctrl_is_valid; + bool m_tls_tpidr_is_valid; bool m_sve_header_is_valid; bool m_pac_mask_is_valid; @@ -107,6 +108,8 @@ uint64_t m_mte_ctrl_reg; + uint64_t m_tls_tpidr_reg; + bool IsGPR(unsigned reg) const; bool IsFPR(unsigned reg) const; @@ -125,9 +128,14 @@ Status WriteMTEControl(); + Status ReadTLSTPIDR(); + + Status WriteTLSTPIDR(); + bool IsSVE(unsigned reg) const; bool IsPAuth(unsigned reg) const; bool IsMTE(unsigned reg) const; + bool IsTLS(unsigned reg) const; uint64_t GetSVERegVG() { return m_sve_header.vl / 8; } @@ -139,6 +147,8 @@ void *GetMTEControl() { return &m_mte_ctrl_reg; } + void *GetTLSTPIDR() { return &m_tls_tpidr_reg; } + void *GetSVEBuffer() { return m_sve_ptrace_payload.data(); }; size_t GetSVEHeaderSize() { return sizeof(m_sve_header); } @@ -149,6 +159,8 @@ size_t GetMTEControlSize() { return sizeof(m_mte_ctrl_reg); } + size_t GetTLSTPIDRSize() { return sizeof(m_tls_tpidr_reg); } + llvm::Error ReadHardwareDebugInfo() override; llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; Index: lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -86,6 +86,8 @@ if (auxv_at_hwcap2 && (*auxv_at_hwcap2 & HWCAP2_MTE)) opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE); + opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS); + auto register_info_up = std::make_unique<RegisterInfoPOSIX_arm64>(target_arch, opt_regsets); return std::make_unique<NativeRegisterContextLinux_arm64>( @@ -116,6 +118,7 @@ ::memset(&m_pac_mask, 0, sizeof(m_pac_mask)); m_mte_ctrl_reg = 0; + m_tls_tpidr_reg = 0; // 16 is just a maximum value, query hardware for actual watchpoint count m_max_hwp_supported = 16; @@ -129,6 +132,7 @@ m_sve_header_is_valid = false; m_pac_mask_is_valid = false; m_mte_ctrl_is_valid = false; + m_tls_tpidr_is_valid = false; if (GetRegisterInfo().IsSVEEnabled()) m_sve_state = SVEState::Unknown; @@ -232,8 +236,15 @@ assert(offset < GetSVEBufferSize()); src = (uint8_t *)GetSVEBuffer() + offset; } - } else if (IsSVE(reg)) { + } else if (IsTLS(reg)) { + error = ReadTLSTPIDR(); + if (error.Fail()) + return error; + offset = reg_info->byte_offset - GetRegisterInfo().GetTLSOffset(); + assert(offset < GetTLSTPIDRSize()); + src = (uint8_t *)GetTLSTPIDR() + offset; + } else if (IsSVE(reg)) { if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown) return Status("SVE disabled or not supported"); @@ -450,6 +461,17 @@ ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); return WriteMTEControl(); + } else if (IsTLS(reg)) { + error = ReadTLSTPIDR(); + if (error.Fail()) + return error; + + offset = reg_info->byte_offset - GetRegisterInfo().GetTLSOffset(); + assert(offset < GetTLSTPIDRSize()); + dst = (uint8_t *)GetTLSTPIDR() + offset; + ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); + + return WriteTLSTPIDR(); } return Status("Failed to write register value"); @@ -490,6 +512,12 @@ return error; } + // tpidr is always present but there will be more in future. + reg_data_byte_size += GetTLSTPIDRSize(); + error = ReadTLSTPIDR(); + if (error.Fail()) + return error; + data_sp.reset(new DataBufferHeap(reg_data_byte_size, 0)); uint8_t *dst = data_sp->GetBytes(); @@ -507,6 +535,8 @@ if (GetRegisterInfo().IsMTEEnabled()) ::memcpy(dst, GetMTEControl(), GetMTEControlSize()); + ::memcpy(dst, GetTLSTPIDR(), GetTLSTPIDRSize()); + return error; } @@ -641,6 +671,10 @@ return GetRegisterInfo().IsMTEReg(reg); } +bool NativeRegisterContextLinux_arm64::IsTLS(unsigned reg) const { + return GetRegisterInfo().IsTLSReg(reg); +} + llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() { if (!m_refresh_hwdebug_info) { return llvm::Error::success(); @@ -784,6 +818,7 @@ m_sve_header_is_valid = false; m_pac_mask_is_valid = false; m_mte_ctrl_is_valid = false; + m_tls_tpidr_is_valid = false; // Update SVE registers in case there is change in configuration. ConfigureRegisterContext(); @@ -914,6 +949,40 @@ return WriteRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL); } +Status NativeRegisterContextLinux_arm64::ReadTLSTPIDR() { + Status error; + + if (m_tls_tpidr_is_valid) + return error; + + struct iovec ioVec; + ioVec.iov_base = GetTLSTPIDR(); + ioVec.iov_len = GetTLSTPIDRSize(); + + error = ReadRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS); + + if (error.Success()) + m_tls_tpidr_is_valid = true; + + return error; +} + +Status NativeRegisterContextLinux_arm64::WriteTLSTPIDR() { + Status error; + + error = ReadTLSTPIDR(); + if (error.Fail()) + return error; + + struct iovec ioVec; + ioVec.iov_base = GetTLSTPIDR(); + ioVec.iov_len = GetTLSTPIDRSize(); + + m_tls_tpidr_is_valid = false; + + return WriteRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS); +} + void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() { // ConfigureRegisterContext gets called from InvalidateAllRegisters // on every stop and configures SVE vector length.
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits