[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