[Lldb-commits] [lldb] 7e22921 - [lldb][AArch64] Add thread local storage tpidr register
David Spickett via lldb-commits
lldb-commits at lists.llvm.org
Mon Jun 19 04:52:04 PDT 2023
Author: David Spickett
Date: 2023-06-19T12:51:58+01:00
New Revision: 7e229217f4215b519b886e7881bae4da3742a7d2
URL: https://github.com/llvm/llvm-project/commit/7e229217f4215b519b886e7881bae4da3742a7d2
DIFF: https://github.com/llvm/llvm-project/commit/7e229217f4215b519b886e7881bae4da3742a7d2.diff
LOG: [lldb][AArch64] Add thread local storage tpidr register
This register is used as the pointer to the current thread
local storage block and is read from NT_ARM_TLS on Linux.
Though tpidr will be present on all AArch64 Linux, I am soon
going to add a second register tpidr2 to this set.
tpidr is only present when SME is implemented, therefore the
NT_ARM_TLS set will change size. This is why I've added this
as a dynamic register set to save changes later.
Reviewed By: omjavaid
Differential Revision: https://reviews.llvm.org/D152516
Added:
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
Modified:
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
Removed:
################################################################################
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
index 0ec152f0643a4..c9384a651e81b 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
@@ -86,6 +86,8 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
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 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
::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 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
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 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
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 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
::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 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
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 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
if (GetRegisterInfo().IsMTEEnabled())
::memcpy(dst, GetMTEControl(), GetMTEControlSize());
+ ::memcpy(dst, GetTLSTPIDR(), GetTLSTPIDRSize());
+
return error;
}
@@ -641,6 +671,10 @@ bool NativeRegisterContextLinux_arm64::IsMTE(unsigned reg) const {
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 @@ void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
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 @@ Status NativeRegisterContextLinux_arm64::WriteMTEControl() {
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.
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
index 14f669a3ce98f..2b5442578cd27 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
@@ -83,6 +83,7 @@ class NativeRegisterContextLinux_arm64
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 @@ class NativeRegisterContextLinux_arm64
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 @@ class NativeRegisterContextLinux_arm64
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 @@ class NativeRegisterContextLinux_arm64
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 @@ class NativeRegisterContextLinux_arm64
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;
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
index 497f0b4114dfb..af5bbda7bfcf5 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
@@ -78,12 +78,16 @@ static lldb_private::RegisterInfo g_register_infos_pauth[] = {
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_pauth_arm64 = {
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 @@ RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
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 @@ void RegisterInfoPOSIX_arm64::AddRegSetMTE() {
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 @@ bool RegisterInfoPOSIX_arm64::IsMTEReg(unsigned reg) const {
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::GetPAuthOffset() const {
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;
+}
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
index 4c52de99fde63..465d3f5b9f3bb 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
@@ -28,6 +28,7 @@ class RegisterInfoPOSIX_arm64
eRegsetMaskSVE = 1,
eRegsetMaskPAuth = 2,
eRegsetMaskMTE = 4,
+ eRegsetMaskTLS = 8,
eRegsetMaskDynamic = ~1,
};
@@ -102,6 +103,8 @@ class RegisterInfoPOSIX_arm64
void AddRegSetMTE();
+ void AddRegSetTLS();
+
uint32_t ConfigureVectorLength(uint32_t sve_vq);
bool VectorSizeIsValid(uint32_t vq) {
@@ -121,6 +124,7 @@ class RegisterInfoPOSIX_arm64
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 @@ class RegisterInfoPOSIX_arm64
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 @@ class RegisterInfoPOSIX_arm64
std::vector<uint32_t> pauth_regnum_collection;
std::vector<uint32_t> m_mte_regnum_collection;
+ std::vector<uint32_t> m_tls_regnum_collection;
};
#endif
diff --git a/lldb/test/API/linux/aarch64/tls_register/Makefile b/lldb/test/API/linux/aarch64/tls_register/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/tls_register/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py b/lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py
new file mode 100644
index 0000000000000..d4ab5d70cc764
--- /dev/null
+++ b/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
diff --git a/lldb/test/API/linux/aarch64/tls_register/main.c b/lldb/test/API/linux/aarch64/tls_register/main.c
new file mode 100644
index 0000000000000..7a5fd3f5fcbdf
--- /dev/null
+++ b/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.
+}
More information about the lldb-commits
mailing list