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 @@ -76,8 +76,10 @@ 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 @@ 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 @@ 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 @@ 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; 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 @@ -33,6 +33,17 @@ #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 @@ .Success()) opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSVE); + NativeProcessLinux &process = native_thread.GetProcess(); + + llvm::Optional 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 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(target_arch, opt_regsets); return std::make_unique( @@ -82,6 +105,9 @@ ::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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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. diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h @@ -51,6 +51,8 @@ Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; + NativeProcessLinux &GetProcess(); + private: // Interface for friend classes @@ -90,8 +92,6 @@ // Private interface void MaybeLogStateChange(lldb::StateType new_state); - NativeProcessLinux &GetProcess(); - void SetStopped(); // Member Variables diff --git a/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h b/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h --- a/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h +++ b/lldb/source/Plugins/Process/POSIX/NativeProcessELF.h @@ -21,6 +21,9 @@ class NativeProcessELF : public NativeProcessProtocol { using NativeProcessProtocol::NativeProcessProtocol; +public: + llvm::Optional GetAuxValue(enum AuxVector::EntryType type); + protected: template struct ELFLinkMap { T l_addr; @@ -30,8 +33,6 @@ T l_prev; }; - llvm::Optional GetAuxValue(enum AuxVector::EntryType type); - lldb::addr_t GetSharedLibraryInfoAddress() override; template 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 @@ -26,6 +26,8 @@ enum { eRegsetMaskDefault = 0, eRegsetMaskSVE = 1, + eRegsetMaskPAuth = 2, + eRegsetMaskMTE = 4, eRegsetMaskDynamic = ~1, }; @@ -94,6 +96,10 @@ 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 @@ 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> @@ -137,6 +147,9 @@ std::vector m_dynamic_reg_infos; std::vector m_dynamic_reg_sets; + + std::vector pauth_regnum_collection; + std::vector m_mte_regnum_collection; }; #endif 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 @@ -77,6 +77,8 @@ 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 @@ {"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 @@ 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 @@ 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 @@ 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::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; +} diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h --- a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h @@ -470,6 +470,13 @@ 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 @@ 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 @@ {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