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. @@ -108,7 +109,13 @@ uint64_t m_mte_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; @@ -128,9 +135,9 @@ Status WriteMTEControl(); - Status ReadTLSTPIDR(); + Status ReadTLS(); - Status WriteTLSTPIDR(); + Status WriteTLS(); bool IsSVE(unsigned reg) const; bool IsPAuth(unsigned reg) const; @@ -147,7 +154,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(); } @@ -161,7 +168,7 @@ size_t GetMTEControlSize() { return sizeof(m_mte_ctrl_reg); } - size_t GetTLSTPIDRSize() { return sizeof(m_tls_tpidr_reg); } + size_t GetTLSBufferSize() { return m_tls_size; } llvm::Error ReadHardwareDebugInfo() 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; // 16 is just a maximum value, query hardware for actual watchpoint count m_max_hwp_supported = 16; @@ -148,7 +148,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; @@ -255,13 +259,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"); @@ -480,16 +484,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(); } return Status("Failed to write register value"); @@ -533,9 +537,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; @@ -558,7 +562,7 @@ if (GetRegisterInfo().IsMTEEnabled()) ::memcpy(dst, GetMTEControl(), GetMTEControlSize()); - ::memcpy(dst, GetTLSTPIDR(), GetTLSTPIDRSize()); + ::memcpy(dst, GetTLSBuffer(), GetTLSBufferSize()); return error; } @@ -845,7 +849,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(); @@ -979,38 +983,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); uint32_t ConfigureVectorLength(uint32_t sve_vq); 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)}; // Number of register sets provided by this context. enum { @@ -87,7 +89,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_register_sets_default = 2, k_num_register_sets = 3 @@ -193,8 +195,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. RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64( const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets) @@ -236,9 +237,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)); m_register_info_count = m_dynamic_reg_infos.size(); m_register_info_p = m_dynamic_reg_infos.data(); @@ -323,18 +324,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/TestAArch64LinuxTLSRegister.py b/lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py deleted file mode 100644 --- a/lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -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 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_registers/TestAArch64LinuxTLSRegisters.py b/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py @@ -0,0 +1,115 @@ +""" +Test lldb's ability to read and write the AArch64 TLS registers. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class AArch64LinuxTLSRegisters(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def setup(self, registers): + 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, + ) + + if "tpidr2" in registers: + self.runCmd("settings set target.run-args 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"], + ) + + def check_tls_reg(self, registers): + self.setup(registers) + + regs = self.thread().GetSelectedFrame().GetRegisters() + tls_regs = regs.GetFirstValueByName("Thread Local Storage Registers") + self.assertTrue(tls_regs.IsValid(), "No TLS registers found.") + + # Since we can't predict what the value will be, the program has set + # a target value for us to find. + initial_values = { + "tpidr": 0x1122334455667788, + "tpidr2": 0x8877665544332211, + } + + for register in registers: + tls_reg = tls_regs.GetChildMemberWithName(register) + self.assertTrue(tls_reg.IsValid(), "{} register not found.".format( + register)) + self.assertEqual(tls_reg.GetValueAsUnsigned(), initial_values[register]) + + set_values = { + "tpidr": 0x1111222233334444, + "tpidr2": 0x4444333322221111, + } + + # Set our own value(s) for the program to find. + for register in registers: + self.expect("register write {} 0x{:x}".format(register, + set_values[register])) + + self.expect("continue") + + self.expect( + "thread list", + STOPPED_DUE_TO_BREAKPOINT, + substrs=["stopped", "stop reason = breakpoint"], + ) + + for register in registers: + self.expect("p {}_was_set".format(register), substrs=["true"]) + + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + def test_tls_no_sme(self): + if self.isAArch64SME(): + self.skipTest("SME must not be present.") + + self.check_tls_reg(["tpidr"]) + + @skipUnlessArch("aarch64") + @skipUnlessPlatform(["linux"]) + def test_tls_sme(self): + if not self.isAArch64SME(): + self.skipTest("SME must present.") + + self.check_tls_reg(["tpidr", "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,60 @@ +#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[]) { + bool use_tpidr2 = argc > 1; + + uint64_t original_tpidr = get_tpidr(); + // Accessing this on a core without it produces SIGILL. Only do this if + // requested. + uint64_t original_tpidr2 = 0; + if (use_tpidr2) + original_tpidr2 = get_tpidr2(); + + uint64_t tpidr_pattern = 0x1122334455667788; + set_tpidr(tpidr_pattern); + + uint64_t tpidr2_pattern = 0x8877665544332211; + if (use_tpidr2) + set_tpidr2(tpidr2_pattern); + + // Set break point at this line. + // lldb will now set its own pattern(s) for us to find. + + uint64_t new_tpidr = get_tpidr(); + volatile bool tpidr_was_set = new_tpidr == 0x1111222233334444; + + uint64_t new_tpidr2 = 0; + volatile bool tpidr2_was_set = false; + if (use_tpidr2) { + new_tpidr2 = get_tpidr2(); + tpidr2_was_set = new_tpidr2 == 0x4444333322221111; + } + + set_tpidr(original_tpidr); + if (use_tpidr2) + set_tpidr2(original_tpidr2); + + return 0; // Set break point 2 at this line. +}