[LLDB] Arm64/Linux Add MTE and Pointer Authentication registers
authorMuhammad Omair Javaid <omair.javaid@linaro.org>
Tue, 30 Mar 2021 10:39:38 +0000 (15:39 +0500)
committerMuhammad Omair Javaid <omair.javaid@linaro.org>
Tue, 30 Mar 2021 23:39:00 +0000 (04:39 +0500)
This patch adds two new dynamic register sets for AArch64 MTE and
Pointer Authentication features. These register sets are dynamic and
will only be available if underlying hardware support either of these
features. LLDB will pull in Aux vector information and create register
infos based on that information.

A follow up patch will add a test case to test these feature registers.

Reviewed By: labath, DavidSpickett

Differential Revision: https://reviews.llvm.org/D96460

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp
lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h
lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
lldb/source/Plugins/Process/POSIX/NativeProcessELF.h
lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h

index 09cf72c..50c299b 100644 (file)
 #define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension */
 #endif
 
+#ifndef NT_ARM_PAC_MASK
+#define NT_ARM_PAC_MASK 0x406 /* Pointer authentication code masks */
+#endif
+
+#ifndef NT_ARM_TAGGED_ADDR_CTRL
+#define NT_ARM_TAGGED_ADDR_CTRL 0x409 /* Tagged address control register */
+#endif
+
+#define HWCAP_PACA (1 << 30)
+#define HWCAP2_MTE (1 << 18)
+
 #define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize())
 
 using namespace lldb;
@@ -62,6 +73,18 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
             .Success())
       opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSVE);
 
+    NativeProcessLinux &process = native_thread.GetProcess();
+
+    llvm::Optional<uint64_t> auxv_at_hwcap =
+        process.GetAuxValue(AuxVector::AUXV_AT_HWCAP);
+    if (auxv_at_hwcap && (*auxv_at_hwcap & HWCAP_PACA))
+      opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskPAuth);
+
+    llvm::Optional<uint64_t> auxv_at_hwcap2 =
+        process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2);
+    if (auxv_at_hwcap && (*auxv_at_hwcap2 & HWCAP2_MTE))
+      opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE);
+
     auto register_info_up =
         std::make_unique<RegisterInfoPOSIX_arm64>(target_arch, opt_regsets);
     return std::make_unique<NativeRegisterContextLinux_arm64>(
@@ -82,6 +105,9 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
   ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
   ::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));
+
+  m_mte_ctrl_reg = 0;
 
   // 16 is just a maximum value, query hardware for actual watchpoint count
   m_max_hwp_supported = 16;
@@ -93,6 +119,8 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
   m_fpu_is_valid = false;
   m_sve_buffer_is_valid = false;
   m_sve_header_is_valid = false;
+  m_pac_mask_is_valid = false;
+  m_mte_ctrl_is_valid = false;
 
   if (GetRegisterInfo().IsSVEEnabled())
     m_sve_state = SVEState::Unknown;
@@ -229,6 +257,22 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
         src = (uint8_t *)GetSVEBuffer() + offset;
       }
     }
+  } else if (IsPAuth(reg)) {
+    error = ReadPAuthMask();
+    if (error.Fail())
+      return error;
+
+    offset = reg_info->byte_offset - GetRegisterInfo().GetPAuthOffset();
+    assert(offset < GetPACMaskSize());
+    src = (uint8_t *)GetPACMask() + offset;
+  } else if (IsMTE(reg)) {
+    error = ReadMTEControl();
+    if (error.Fail())
+      return error;
+
+    offset = reg_info->byte_offset - GetRegisterInfo().GetMTEOffset();
+    assert(offset < GetMTEControlSize());
+    src = (uint8_t *)GetMTEControl() + offset;
   } else
     return Status("failed - register wasn't recognized to be a GPR or an FPR, "
                   "write strategy unknown");
@@ -387,6 +431,17 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
         return WriteAllSVE();
       }
     }
+  } else if (IsMTE(reg)) {
+    error = ReadMTEControl();
+    if (error.Fail())
+      return error;
+
+    offset = reg_info->byte_offset - GetRegisterInfo().GetMTEOffset();
+    assert(offset < GetMTEControlSize());
+    dst = (uint8_t *)GetMTEControl() + offset;
+    ::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
+
+    return WriteMTEControl();
   }
 
   return Status("Failed to write register value");
@@ -475,6 +530,14 @@ bool NativeRegisterContextLinux_arm64::IsSVE(unsigned reg) const {
   return GetRegisterInfo().IsSVEReg(reg);
 }
 
+bool NativeRegisterContextLinux_arm64::IsPAuth(unsigned reg) const {
+  return GetRegisterInfo().IsPAuthReg(reg);
+}
+
+bool NativeRegisterContextLinux_arm64::IsMTE(unsigned reg) const {
+  return GetRegisterInfo().IsMTEReg(reg);
+}
+
 llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
   if (!m_refresh_hwdebug_info) {
     return llvm::Error::success();
@@ -616,6 +679,8 @@ void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
   m_fpu_is_valid = false;
   m_sve_buffer_is_valid = false;
   m_sve_header_is_valid = false;
+  m_pac_mask_is_valid = false;
+  m_mte_ctrl_is_valid = false;
 
   // Update SVE registers in case there is change in configuration.
   ConfigureRegisterContext();
@@ -633,7 +698,26 @@ Status NativeRegisterContextLinux_arm64::ReadSVEHeader() {
 
   error = ReadRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE);
 
-  m_sve_header_is_valid = true;
+  if (error.Success())
+    m_sve_header_is_valid = true;
+
+  return error;
+}
+
+Status NativeRegisterContextLinux_arm64::ReadPAuthMask() {
+  Status error;
+
+  if (m_pac_mask_is_valid)
+    return error;
+
+  struct iovec ioVec;
+  ioVec.iov_base = GetPACMask();
+  ioVec.iov_len = GetPACMaskSize();
+
+  error = ReadRegisterSet(&ioVec, GetPACMaskSize(), NT_ARM_PAC_MASK);
+
+  if (error.Success())
+    m_pac_mask_is_valid = true;
 
   return error;
 }
@@ -693,6 +777,40 @@ Status NativeRegisterContextLinux_arm64::WriteAllSVE() {
   return WriteRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE);
 }
 
+Status NativeRegisterContextLinux_arm64::ReadMTEControl() {
+  Status error;
+
+  if (m_mte_ctrl_is_valid)
+    return error;
+
+  struct iovec ioVec;
+  ioVec.iov_base = GetMTEControl();
+  ioVec.iov_len = GetMTEControlSize();
+
+  error = ReadRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL);
+
+  if (error.Success())
+    m_mte_ctrl_is_valid = true;
+
+  return error;
+}
+
+Status NativeRegisterContextLinux_arm64::WriteMTEControl() {
+  Status error;
+
+  error = ReadMTEControl();
+  if (error.Fail())
+    return error;
+
+  struct iovec ioVec;
+  ioVec.iov_base = GetMTEControl();
+  ioVec.iov_len = GetMTEControlSize();
+
+  m_mte_ctrl_is_valid = false;
+
+  return WriteRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL);
+}
+
 void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
   // ConfigureRegisterContext gets called from InvalidateAllRegisters
   // on every stop and configures SVE vector length.
index f0dd638..12c6c01 100644 (file)
@@ -76,8 +76,10 @@ private:
   bool m_gpr_is_valid;
   bool m_fpu_is_valid;
   bool m_sve_buffer_is_valid;
+  bool m_mte_ctrl_is_valid;
 
   bool m_sve_header_is_valid;
+  bool m_pac_mask_is_valid;
 
   struct user_pt_regs m_gpr_arm64; // 64-bit general purpose registers.
 
@@ -90,6 +92,15 @@ private:
 
   bool m_refresh_hwdebug_info;
 
+  struct user_pac_mask {
+    uint64_t data_mask;
+    uint64_t insn_mask;
+  };
+
+  struct user_pac_mask m_pac_mask;
+
+  uint64_t m_mte_ctrl_reg;
+
   bool IsGPR(unsigned reg) const;
 
   bool IsFPR(unsigned reg) const;
@@ -102,7 +113,15 @@ private:
 
   Status WriteSVEHeader();
 
+  Status ReadPAuthMask();
+
+  Status ReadMTEControl();
+
+  Status WriteMTEControl();
+
   bool IsSVE(unsigned reg) const;
+  bool IsPAuth(unsigned reg) const;
+  bool IsMTE(unsigned reg) const;
 
   uint64_t GetSVERegVG() { return m_sve_header.vl / 8; }
 
@@ -110,12 +129,20 @@ private:
 
   void *GetSVEHeader() { return &m_sve_header; }
 
+  void *GetPACMask() { return &m_pac_mask; }
+
+  void *GetMTEControl() { return &m_mte_ctrl_reg; }
+
   void *GetSVEBuffer();
 
   size_t GetSVEHeaderSize() { return sizeof(m_sve_header); }
 
+  size_t GetPACMaskSize() { return sizeof(m_pac_mask); }
+
   size_t GetSVEBufferSize() { return m_sve_ptrace_payload.size(); }
 
+  size_t GetMTEControlSize() { return sizeof(m_mte_ctrl_reg); }
+
   llvm::Error ReadHardwareDebugInfo() override;
 
   llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
index fd43c89..5cf473e 100644 (file)
@@ -51,6 +51,8 @@ public:
 
   Status RemoveHardwareBreakpoint(lldb::addr_t addr) override;
 
+  NativeProcessLinux &GetProcess();
+
 private:
   // Interface for friend classes
 
@@ -90,8 +92,6 @@ private:
   // Private interface
   void MaybeLogStateChange(lldb::StateType new_state);
 
-  NativeProcessLinux &GetProcess();
-
   void SetStopped();
 
   // Member Variables
index dcfa929..c014099 100644 (file)
@@ -21,6 +21,9 @@ namespace lldb_private {
 class NativeProcessELF : public NativeProcessProtocol {
   using NativeProcessProtocol::NativeProcessProtocol;
 
+public:
+  llvm::Optional<uint64_t> GetAuxValue(enum AuxVector::EntryType type);
+
 protected:
   template <typename T> struct ELFLinkMap {
     T l_addr;
@@ -30,8 +33,6 @@ protected:
     T l_prev;
   };
 
-  llvm::Optional<uint64_t> GetAuxValue(enum AuxVector::EntryType type);
-
   lldb::addr_t GetSharedLibraryInfoAddress() override;
 
   template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>
index f651a8a..4a29c99 100644 (file)
@@ -77,6 +77,8 @@ 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_pauth_register = 2,
   k_num_register_sets_default = 2,
   k_num_register_sets = 3
 };
@@ -175,6 +177,12 @@ static const lldb_private::RegisterSet g_reg_sets_arm64[k_num_register_sets] = {
     {"Scalable Vector Extension Registers", "sve", k_num_sve_registers,
      g_sve_regnums_arm64}};
 
+static const lldb_private::RegisterSet g_reg_set_pauth_arm64 = {
+    "Pointer Authentication Registers", "pauth", k_num_pauth_register, NULL};
+
+static const lldb_private::RegisterSet g_reg_set_mte_arm64 = {
+    "MTE Control Register", "mte", k_num_mte_register, NULL};
+
 RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
     const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets)
     : lldb_private::RegisterInfoAndSetInterface(target_arch),
@@ -209,6 +217,12 @@ RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
       llvm::copy(reg_infos_ref, std::back_inserter(m_dynamic_reg_infos));
       llvm::copy(reg_sets_ref, std::back_inserter(m_dynamic_reg_sets));
 
+      if (m_opt_regsets.AllSet(eRegsetMaskPAuth))
+        AddRegSetPAuth();
+
+      if (m_opt_regsets.AllSet(eRegsetMaskMTE))
+        AddRegSetMTE();
+
       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();
@@ -259,6 +273,39 @@ RegisterInfoPOSIX_arm64::GetRegisterSet(size_t set_index) const {
   return nullptr;
 }
 
+void RegisterInfoPOSIX_arm64::AddRegSetPAuth() {
+  uint32_t pa_regnum = m_dynamic_reg_infos.size();
+  for (uint32_t i = 0; i < k_num_pauth_register; i++) {
+    pauth_regnum_collection.push_back(pa_regnum + i);
+    m_dynamic_reg_infos.push_back(g_register_infos_pauth[i]);
+    m_dynamic_reg_infos[pa_regnum + i].byte_offset =
+        m_dynamic_reg_infos[pa_regnum + i - 1].byte_offset +
+        m_dynamic_reg_infos[pa_regnum + i - 1].byte_size;
+    m_dynamic_reg_infos[pa_regnum + i].kinds[lldb::eRegisterKindLLDB] =
+        pa_regnum + i;
+  }
+
+  m_per_regset_regnum_range[m_register_set_count] =
+      std::make_pair(pa_regnum, m_dynamic_reg_infos.size());
+  m_dynamic_reg_sets.push_back(g_reg_set_pauth_arm64);
+  m_dynamic_reg_sets.back().registers = pauth_regnum_collection.data();
+}
+
+void RegisterInfoPOSIX_arm64::AddRegSetMTE() {
+  uint32_t mte_regnum = m_dynamic_reg_infos.size();
+  m_mte_regnum_collection.push_back(mte_regnum);
+  m_dynamic_reg_infos.push_back(g_register_infos_mte[0]);
+  m_dynamic_reg_infos[mte_regnum].byte_offset =
+      m_dynamic_reg_infos[mte_regnum - 1].byte_offset +
+      m_dynamic_reg_infos[mte_regnum - 1].byte_size;
+  m_dynamic_reg_infos[mte_regnum].kinds[lldb::eRegisterKindLLDB] = mte_regnum;
+
+  m_per_regset_regnum_range[m_register_set_count] =
+      std::make_pair(mte_regnum, mte_regnum + 1);
+  m_dynamic_reg_sets.push_back(g_reg_set_mte_arm64);
+  m_dynamic_reg_sets.back().registers = m_mte_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.
@@ -342,6 +389,18 @@ bool RegisterInfoPOSIX_arm64::IsSVERegVG(unsigned reg) const {
   return sve_vg == reg;
 }
 
+bool RegisterInfoPOSIX_arm64::IsPAuthReg(unsigned reg) const {
+  return std::find(pauth_regnum_collection.begin(),
+                   pauth_regnum_collection.end(),
+                   reg) != pauth_regnum_collection.end();
+}
+
+bool RegisterInfoPOSIX_arm64::IsMTEReg(unsigned reg) const {
+  return std::find(m_mte_regnum_collection.begin(),
+                   m_mte_regnum_collection.end(),
+                   reg) != m_mte_regnum_collection.end();
+}
+
 uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; }
 
 uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; }
@@ -351,3 +410,11 @@ uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPCR() const { return fpu_fpcr; }
 uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPSR() const { return fpu_fpsr; }
 
 uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEVG() const { return sve_vg; }
+
+uint32_t RegisterInfoPOSIX_arm64::GetPAuthOffset() const {
+  return m_register_info_p[pauth_regnum_collection[0]].byte_offset;
+}
+
+uint32_t RegisterInfoPOSIX_arm64::GetMTEOffset() const {
+  return m_register_info_p[m_mte_regnum_collection[0]].byte_offset;
+}
index 955bc61..df6e44f 100644 (file)
@@ -26,6 +26,8 @@ public:
   enum {
     eRegsetMaskDefault = 0,
     eRegsetMaskSVE = 1,
+    eRegsetMaskPAuth = 2,
+    eRegsetMaskMTE = 4,
     eRegsetMaskDynamic = ~1,
   };
 
@@ -94,6 +96,10 @@ public:
 
   size_t GetRegisterSetFromRegisterIndex(uint32_t reg_index) const override;
 
+  void AddRegSetPAuth();
+
+  void AddRegSetMTE();
+
   uint32_t ConfigureVectorLength(uint32_t sve_vq);
 
   bool VectorSizeIsValid(uint32_t vq) {
@@ -108,12 +114,16 @@ public:
   bool IsSVEZReg(unsigned reg) const;
   bool IsSVEPReg(unsigned reg) const;
   bool IsSVERegVG(unsigned reg) const;
+  bool IsPAuthReg(unsigned reg) const;
+  bool IsMTEReg(unsigned reg) const;
 
   uint32_t GetRegNumSVEZ0() const;
   uint32_t GetRegNumSVEFFR() const;
   uint32_t GetRegNumFPCR() const;
   uint32_t GetRegNumFPSR() const;
   uint32_t GetRegNumSVEVG() const;
+  uint32_t GetPAuthOffset() const;
+  uint32_t GetMTEOffset() const;
 
 private:
   typedef std::map<uint32_t, std::vector<lldb_private::RegisterInfo>>
@@ -137,6 +147,9 @@ private:
 
   std::vector<lldb_private::RegisterInfo> m_dynamic_reg_infos;
   std::vector<lldb_private::RegisterSet> m_dynamic_reg_sets;
+
+  std::vector<uint32_t> pauth_regnum_collection;
+  std::vector<uint32_t> m_mte_regnum_collection;
 };
 
 #endif
index 4aee55e..95b1f8f 100644 (file)
@@ -470,6 +470,13 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM};
         LLDB_INVALID_REGNUM, lldb_kind                                         \
   }
 
+// Generates register kinds array for registers with only lldb kind
+#define KIND_ALL_INVALID                                                       \
+  {                                                                            \
+    LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,             \
+        LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM                               \
+  }
+
 // Generates register kinds array for vector registers
 #define GPR64_KIND(reg, generic_kind) MISC_KIND(reg, gpr, generic_kind)
 #define VREG_KIND(reg) MISC_KIND(reg, fpu, LLDB_INVALID_REGNUM)
@@ -526,6 +533,13 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM};
         nullptr, 0                                                             \
   }
 
+// Defines pointer authentication mask registers
+#define DEFINE_EXTENSION_REG(reg)                                              \
+  {                                                                            \
+    #reg, nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex,                \
+        KIND_ALL_INVALID, nullptr, nullptr, nullptr, 0                         \
+  }
+
 static lldb_private::RegisterInfo g_register_infos_arm64_le[] = {
     // DEFINE_GPR64(name, GENERIC KIND)
     DEFINE_GPR64(x0, LLDB_REGNUM_GENERIC_ARG1),
@@ -772,7 +786,12 @@ static lldb_private::RegisterInfo g_register_infos_arm64_le[] = {
     {DEFINE_DBG(wcr, 13)},
     {DEFINE_DBG(wcr, 14)},
     {DEFINE_DBG(wcr, 15)}
-    // clang-format on
 };
+// clang-format on
+static lldb_private::RegisterInfo g_register_infos_pauth[] = {
+    DEFINE_EXTENSION_REG(data_mask), DEFINE_EXTENSION_REG(code_mask)};
+
+static lldb_private::RegisterInfo g_register_infos_mte[] = {
+    DEFINE_EXTENSION_REG(mte_ctrl)};
 
 #endif // DECLARE_REGISTER_INFOS_ARM64_STRUCT