DavidSpickett updated this revision to Diff 532156.
DavidSpickett added a comment.

Always add the TLS regset mask value. It worked on my QEMU setup because
it also had the other extensions, but an armv8-a machine didn't.


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,25 @@
+#include <stdbool.h>
+#include <stdint.h>
+
+struct __attribute__((__packed__)) TestStruct {
+  uint64_t one;
+  uint64_t two;
+};
+
+int main() {
+  static __thread struct TestStruct test_data;
+  test_data.one = 0xABABABABCDCDCDCD;
+#define TWO_VALUE 0xEFEFEFEF01010101
+  test_data.two = TWO_VALUE;
+
+  // Barrier to ensure the above writes are done first.
+  __asm__ volatile("" ::: "memory"); // Set break point at this line.
+
+  // Here lldb moves the TLS pointer 8 bytes forward.
+  // So this actually reads test_data.two instead of test_data.one.
+  volatile bool tls_has_moved = test_data.one == TWO_VALUE;
+
+  return 0; // Set break point 2 at this line.
+  // lldb will reset the thread pointer here so we don't potentially confuse
+  // libc.
+}
Index: lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py
===================================================================
--- /dev/null
+++ lldb/test/API/linux/aarch64/tls_register/TestAArch64LinuxTLSRegister.py
@@ -0,0 +1,77 @@
+"""
+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"],
+        )
+
+        # We can't know what the value should be since it moves about between
+        # runs. So we'll check that we can read it, and by modifying it, see
+        # changes in the program.
+
+        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.")
+        tpidr = tpidr.GetValueAsUnsigned()
+
+        # We should be able to find a known value in the TLS area it points to.
+        # test_data should be very soon after the header, 64 bytes seems to work fine.
+        self.expect("memory find -e 0xABABABABCDCDCDCD $tpidr $tpidr+64",
+                    substrs=["data found at location: 0x"])
+
+        # Now modify the TLS pointer so that anyone reading test_data.one actually
+        # reads test_data.two.
+        self.expect("register write tpidr 0x{:x}".format(tpidr+8))
+
+        # Let the program read test_data.one.
+        self.expect("continue")
+
+        self.expect(
+            "thread list",
+            STOPPED_DUE_TO_BREAKPOINT,
+            substrs=["stopped", "stop reason = breakpoint"],
+        )
+
+        self.expect("p tls_has_moved", substrs=["true"])
+
+        # Reset TLS so we don't potentially confuse the libc.
+        self.expect("register write tpidr 0x{:x}".format(tpidr))
\ 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

Reply via email to