diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h @@ -83,10 +83,11 @@ 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; + bool m_tls_is_valid; + size_t m_tls_size; struct user_pt_regs m_gpr_arm64; // 64-bit general purpose registers. @@ -109,7 +110,13 @@ uint64_t m_mte_ctrl_reg; uint64_t m_sme_ctrl_reg; - uint64_t m_tls_tpidr_reg; + struct tls_regs { + uint64_t tpidr_reg; + // Only valid when SME is present. + uint64_t tpidr2_reg; + }; + + struct tls_regs m_tls_regs; bool IsGPR(unsigned reg) const; @@ -129,9 +136,9 @@ Status WriteMTEControl(); - Status ReadTLSTPIDR(); + Status ReadTLS(); - Status WriteTLSTPIDR(); + Status WriteTLS(); // SVCR is a pseudo register and we do not allow writes to it. Status ReadSMEControl(); @@ -152,7 +159,7 @@ void *GetMTEControl() { return &m_mte_ctrl_reg; } - void *GetTLSTPIDR() { return &m_tls_tpidr_reg; } + void *GetTLSBuffer() { return &m_tls_regs; } void *GetSVEBuffer() { return m_sve_ptrace_payload.data(); } @@ -166,12 +173,12 @@ size_t GetMTEControlSize() { return sizeof(m_mte_ctrl_reg); } - size_t GetTLSTPIDRSize() { return sizeof(m_tls_tpidr_reg); } - void *GetSMEControl() { return &m_sme_ctrl_reg; } size_t GetSMEControlSize() { return sizeof(m_sme_ctrl_reg); } + size_t GetTLSBufferSize() { return m_tls_size; } + llvm::Error ReadHardwareDebugInfo() override; llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -132,9 +132,9 @@ ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); ::memset(&m_sve_header, 0, sizeof(m_sve_header)); ::memset(&m_pac_mask, 0, sizeof(m_pac_mask)); + ::memset(&m_tls_regs, 0, sizeof(m_tls_regs)); m_mte_ctrl_reg = 0; - m_tls_tpidr_reg = 0; m_sme_ctrl_reg = 0; // 16 is just a maximum value, query hardware for actual watchpoint count @@ -149,7 +149,11 @@ m_sve_header_is_valid = false; m_pac_mask_is_valid = false; m_mte_ctrl_is_valid = false; - m_tls_tpidr_is_valid = false; + m_tls_is_valid = false; + + // SME adds the tpidr2 register + m_tls_size = GetRegisterInfo().IsSSVEEnabled() ? sizeof(m_tls_regs) + : sizeof(m_tls_regs.tpidr_reg); if (GetRegisterInfo().IsSVEEnabled() || GetRegisterInfo().IsSSVEEnabled()) m_sve_state = SVEState::Unknown; @@ -256,13 +260,13 @@ src = (uint8_t *)GetSVEBuffer() + offset; } } else if (IsTLS(reg)) { - error = ReadTLSTPIDR(); + error = ReadTLS(); if (error.Fail()) return error; offset = reg_info->byte_offset - GetRegisterInfo().GetTLSOffset(); - assert(offset < GetTLSTPIDRSize()); - src = (uint8_t *)GetTLSTPIDR() + offset; + assert(offset < GetTLSBufferSize()); + src = (uint8_t *)GetTLSBuffer() + offset; } else if (IsSVE(reg)) { if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown) return Status("SVE disabled or not supported"); @@ -488,16 +492,16 @@ return WriteMTEControl(); } else if (IsTLS(reg)) { - error = ReadTLSTPIDR(); + error = ReadTLS(); if (error.Fail()) return error; offset = reg_info->byte_offset - GetRegisterInfo().GetTLSOffset(); - assert(offset < GetTLSTPIDRSize()); - dst = (uint8_t *)GetTLSTPIDR() + offset; + assert(offset < GetTLSBufferSize()); + dst = (uint8_t *)GetTLSBuffer() + offset; ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size); - return WriteTLSTPIDR(); + return WriteTLS(); } else if (IsSME(reg)) { return Status("Writing to SVCR is not supported."); } @@ -543,9 +547,9 @@ return error; } - // tpidr is always present but there will be more in future. - reg_data_byte_size += GetTLSTPIDRSize(); - error = ReadTLSTPIDR(); + // tpidr is always present but tpidr2 depends on SME. + reg_data_byte_size += GetTLSBufferSize(); + error = ReadTLS(); if (error.Fail()) return error; @@ -568,7 +572,7 @@ if (GetRegisterInfo().IsMTEEnabled()) ::memcpy(dst, GetMTEControl(), GetMTEControlSize()); - ::memcpy(dst, GetTLSTPIDR(), GetTLSTPIDRSize()); + ::memcpy(dst, GetTLSBuffer(), GetTLSBufferSize()); return error; } @@ -859,7 +863,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; + m_tls_is_valid = false; // Update SVE registers in case there is change in configuration. ConfigureRegisterContext(); @@ -1003,38 +1007,38 @@ return WriteRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL); } -Status NativeRegisterContextLinux_arm64::ReadTLSTPIDR() { +Status NativeRegisterContextLinux_arm64::ReadTLS() { Status error; - if (m_tls_tpidr_is_valid) + if (m_tls_is_valid) return error; struct iovec ioVec; - ioVec.iov_base = GetTLSTPIDR(); - ioVec.iov_len = GetTLSTPIDRSize(); + ioVec.iov_base = GetTLSBuffer(); + ioVec.iov_len = GetTLSBufferSize(); - error = ReadRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS); + error = ReadRegisterSet(&ioVec, GetTLSBufferSize(), NT_ARM_TLS); if (error.Success()) - m_tls_tpidr_is_valid = true; + m_tls_is_valid = true; return error; } -Status NativeRegisterContextLinux_arm64::WriteTLSTPIDR() { +Status NativeRegisterContextLinux_arm64::WriteTLS() { Status error; - error = ReadTLSTPIDR(); + error = ReadTLS(); if (error.Fail()) return error; struct iovec ioVec; - ioVec.iov_base = GetTLSTPIDR(); - ioVec.iov_len = GetTLSTPIDRSize(); + ioVec.iov_base = GetTLSBuffer(); + ioVec.iov_len = GetTLSBufferSize(); - m_tls_tpidr_is_valid = false; + m_tls_is_valid = false; - return WriteRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS); + return WriteRegisterSet(&ioVec, GetTLSBufferSize(), NT_ARM_TLS); } void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() { diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h @@ -104,7 +104,7 @@ void AddRegSetMTE(); - void AddRegSetTLS(); + void AddRegSetTLS(bool has_tpidr2); void AddRegSetSME(); diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp --- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp @@ -79,7 +79,9 @@ DEFINE_EXTENSION_REG(mte_ctrl)}; static lldb_private::RegisterInfo g_register_infos_tls[] = { - DEFINE_EXTENSION_REG(tpidr)}; + DEFINE_EXTENSION_REG(tpidr), + // Only present when SME is present + DEFINE_EXTENSION_REG(tpidr2)}; static lldb_private::RegisterInfo g_register_infos_sme[] = { DEFINE_EXTENSION_REG(svcr)}; @@ -90,7 +92,7 @@ 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, + // Number of TLS registers is dynamic so it is not listed here. k_num_pauth_register = 2, k_num_sme_register = 1, k_num_register_sets_default = 2, @@ -197,8 +199,7 @@ 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}; +// The size of the TLS set is dynamic, so not listed here. static const lldb_private::RegisterSet g_reg_set_sme_arm64 = { "Scalable Matrix Extension Registers", "sme", k_num_sme_register, nullptr}; @@ -243,9 +244,9 @@ 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(); + // The TLS set always contains tpidr but only has tpidr2 when SME is + // present. + AddRegSetTLS(m_opt_regsets.AllSet(eRegsetMaskSSVE)); if (m_opt_regsets.AllSet(eRegsetMaskSSVE)) AddRegSetSME(); @@ -333,18 +334,23 @@ m_dynamic_reg_sets.back().registers = m_mte_regnum_collection.data(); } -void RegisterInfoPOSIX_arm64::AddRegSetTLS() { +void RegisterInfoPOSIX_arm64::AddRegSetTLS(bool has_tpidr2) { 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; + uint32_t num_regs = has_tpidr2 ? 2 : 1; + for (uint32_t i = 0; i < num_regs; i++) { + m_tls_regnum_collection.push_back(tls_regnum + i); + m_dynamic_reg_infos.push_back(g_register_infos_tls[i]); + m_dynamic_reg_infos[tls_regnum + i].byte_offset = + m_dynamic_reg_infos[tls_regnum + i - 1].byte_offset + + m_dynamic_reg_infos[tls_regnum + i - 1].byte_size; + m_dynamic_reg_infos[tls_regnum + i].kinds[lldb::eRegisterKindLLDB] = + tls_regnum + i; + } 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); + std::make_pair(tls_regnum, m_dynamic_reg_infos.size()); + m_dynamic_reg_sets.push_back( + {"Thread Local Storage Registers", "tls", num_regs, nullptr}); m_dynamic_reg_sets.back().registers = m_tls_regnum_collection.data(); } diff --git a/lldb/test/API/linux/aarch64/tls_register/main.c b/lldb/test/API/linux/aarch64/tls_register/main.c deleted file mode 100644 --- a/lldb/test/API/linux/aarch64/tls_register/main.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include - -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. -} diff --git a/lldb/test/API/linux/aarch64/tls_register/Makefile b/lldb/test/API/linux/aarch64/tls_registers/Makefile rename from lldb/test/API/linux/aarch64/tls_register/Makefile rename to lldb/test/API/linux/aarch64/tls_registers/Makefile diff --git a/lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py b/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py rename from lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py rename to lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py --- a/lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py +++ b/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py @@ -1,5 +1,5 @@ """ -Test lldb's ability to read and write the AArch64 TLS register tpidr. +Test lldb's ability to read and write the AArch64 TLS registers. """ import lldb @@ -8,12 +8,10 @@ from lldbsuite.test import lldbutil -class AArch64LinuxTLSRegister(TestBase): +class AArch64LinuxTLSRegisters(TestBase): NO_DEBUG_INFO_TESTCASE = True - @skipUnlessArch("aarch64") - @skipUnlessPlatform(["linux"]) - def test_tls(self): + def setup(self, register_name): self.build() self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) @@ -31,6 +29,8 @@ num_expected_locations=1, ) + self.runCmd("settings set target.run-args {}".format( + {"tpidr": 1, "tpidr2": 2}[register_name])) self.runCmd("run", RUN_SUCCEEDED) if self.process().GetState() == lldb.eStateExited: @@ -42,19 +42,24 @@ substrs=["stopped", "stop reason = breakpoint"], ) + def check_tls_reg(self, register_name): + self.setup(register_name) + # 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.") + tls_reg = tls_regs.GetChildMemberWithName(register_name) + self.assertTrue(tls_reg.IsValid(), "{} register not found.".format( + register_name)) - self.assertEqual(tpidr.GetValueAsUnsigned(), 0x1122334455667788) + self.assertEqual(tls_reg.GetValueAsUnsigned(), 0x1122334455667788) # Set our own value for the program to find. - self.expect("register write tpidr 0x{:x}".format(0x8877665544332211)) + self.expect("register write {} 0x{:x}".format( + register_name, 0x8877665544332211)) self.expect("continue") self.expect( @@ -63,4 +68,32 @@ substrs=["stopped", "stop reason = breakpoint"], ) - self.expect("p tpidr_was_set", substrs=["true"]) \ No newline at end of file + self.expect("p reg_was_set", substrs=["true"]) + + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + def test_tpidr(self): + self.check_tls_reg("tpidr") + + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + def test_tpidr2(self): + if not self.isAArch64SME(): + self.skipTest("SME must present.") + + self.check_tls_reg("tpidr2") + + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + def test_tpidr2_no_sme(self): + if self.isAArch64SME(): + self.skipTest("SME must not be present.") + + self.setup("tpidr") + + regs = self.thread().GetSelectedFrame().GetRegisters() + tls_regs = regs.GetFirstValueByName("Thread Local Storage Registers") + self.assertTrue(tls_regs.IsValid(), "No TLS registers found.") + tls_reg = tls_regs.GetChildMemberWithName("tpidr2") + self.assertFalse(tls_reg.IsValid(), + "tpdir2 should not be present without SME") diff --git a/lldb/test/API/linux/aarch64/tls_registers/main.c b/lldb/test/API/linux/aarch64/tls_registers/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/linux/aarch64/tls_registers/main.c @@ -0,0 +1,57 @@ +#include +#include + +uint64_t get_tpidr(void) { + uint64_t tpidr = 0; + __asm__ volatile("mrs %0, tpidr_el0" : "=r"(tpidr)); + return tpidr; +} + +uint64_t get_tpidr2(void) { + uint64_t tpidr2 = 0; + // S3_3_C13_C0_5 means tpidr2, and will work with older tools. + __asm__ volatile("mrs %0, S3_3_C13_C0_5" : "=r"(tpidr2)); + return tpidr2; +} + +void set_tpidr(uint64_t value) { + __asm__ volatile("msr tpidr_el0, %0" ::"r"(value)); +} + +void set_tpidr2(uint64_t value) { + __asm__ volatile("msr S3_3_C13_C0_5, %0" ::"r"(value)); +} + +int main(int argc, char *argv[]) { + uint64_t (*getter)(void) = get_tpidr; + void (*setter)(uint64_t) = set_tpidr; + + // Expect just the register number. + if (argc != 2) + return 1; + + switch (argv[1][0]) { + case '1': + break; + case '2': + getter = get_tpidr2; + setter = set_tpidr2; + break; + default: + return 1; + } + + uint64_t original = getter(); + uint64_t pattern = 0x1122334455667788; + setter(pattern); + + // Set break point at this line. + // lldb will now set its own pattern for us to find. + + uint64_t new_value = getter(); + volatile bool reg_was_set = new_value == 0x8877665544332211; + + setter(original); + + return 0; // Set break point 2 at this line. +}