Skip to content

Commit 1164b4e

Browse files
committed
[LLDB] Arm64/Linux Add MTE and Pointer Authentication registers
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
1 parent d6d3d21 commit 1164b4e

File tree

7 files changed

+251
-6
lines changed

7 files changed

+251
-6
lines changed

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@
3333
#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension */
3434
#endif
3535

36+
#ifndef NT_ARM_PAC_MASK
37+
#define NT_ARM_PAC_MASK 0x406 /* Pointer authentication code masks */
38+
#endif
39+
40+
#ifndef NT_ARM_TAGGED_ADDR_CTRL
41+
#define NT_ARM_TAGGED_ADDR_CTRL 0x409 /* Tagged address control register */
42+
#endif
43+
44+
#define HWCAP_PACA (1 << 30)
45+
#define HWCAP2_MTE (1 << 18)
46+
3647
#define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize())
3748

3849
using namespace lldb;
@@ -62,6 +73,18 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
6273
.Success())
6374
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskSVE);
6475

76+
NativeProcessLinux &process = native_thread.GetProcess();
77+
78+
llvm::Optional<uint64_t> auxv_at_hwcap =
79+
process.GetAuxValue(AuxVector::AUXV_AT_HWCAP);
80+
if (auxv_at_hwcap && (*auxv_at_hwcap & HWCAP_PACA))
81+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskPAuth);
82+
83+
llvm::Optional<uint64_t> auxv_at_hwcap2 =
84+
process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2);
85+
if (auxv_at_hwcap && (*auxv_at_hwcap2 & HWCAP2_MTE))
86+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE);
87+
6588
auto register_info_up =
6689
std::make_unique<RegisterInfoPOSIX_arm64>(target_arch, opt_regsets);
6790
return std::make_unique<NativeRegisterContextLinux_arm64>(
@@ -82,6 +105,9 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
82105
::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
83106
::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
84107
::memset(&m_sve_header, 0, sizeof(m_sve_header));
108+
::memset(&m_pac_mask, 0, sizeof(m_pac_mask));
109+
110+
m_mte_ctrl_reg = 0;
85111

86112
// 16 is just a maximum value, query hardware for actual watchpoint count
87113
m_max_hwp_supported = 16;
@@ -93,6 +119,8 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
93119
m_fpu_is_valid = false;
94120
m_sve_buffer_is_valid = false;
95121
m_sve_header_is_valid = false;
122+
m_pac_mask_is_valid = false;
123+
m_mte_ctrl_is_valid = false;
96124

97125
if (GetRegisterInfo().IsSVEEnabled())
98126
m_sve_state = SVEState::Unknown;
@@ -229,6 +257,22 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
229257
src = (uint8_t *)GetSVEBuffer() + offset;
230258
}
231259
}
260+
} else if (IsPAuth(reg)) {
261+
error = ReadPAuthMask();
262+
if (error.Fail())
263+
return error;
264+
265+
offset = reg_info->byte_offset - GetRegisterInfo().GetPAuthOffset();
266+
assert(offset < GetPACMaskSize());
267+
src = (uint8_t *)GetPACMask() + offset;
268+
} else if (IsMTE(reg)) {
269+
error = ReadMTEControl();
270+
if (error.Fail())
271+
return error;
272+
273+
offset = reg_info->byte_offset - GetRegisterInfo().GetMTEOffset();
274+
assert(offset < GetMTEControlSize());
275+
src = (uint8_t *)GetMTEControl() + offset;
232276
} else
233277
return Status("failed - register wasn't recognized to be a GPR or an FPR, "
234278
"write strategy unknown");
@@ -387,6 +431,17 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
387431
return WriteAllSVE();
388432
}
389433
}
434+
} else if (IsMTE(reg)) {
435+
error = ReadMTEControl();
436+
if (error.Fail())
437+
return error;
438+
439+
offset = reg_info->byte_offset - GetRegisterInfo().GetMTEOffset();
440+
assert(offset < GetMTEControlSize());
441+
dst = (uint8_t *)GetMTEControl() + offset;
442+
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
443+
444+
return WriteMTEControl();
390445
}
391446

392447
return Status("Failed to write register value");
@@ -475,6 +530,14 @@ bool NativeRegisterContextLinux_arm64::IsSVE(unsigned reg) const {
475530
return GetRegisterInfo().IsSVEReg(reg);
476531
}
477532

533+
bool NativeRegisterContextLinux_arm64::IsPAuth(unsigned reg) const {
534+
return GetRegisterInfo().IsPAuthReg(reg);
535+
}
536+
537+
bool NativeRegisterContextLinux_arm64::IsMTE(unsigned reg) const {
538+
return GetRegisterInfo().IsMTEReg(reg);
539+
}
540+
478541
llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
479542
if (!m_refresh_hwdebug_info) {
480543
return llvm::Error::success();
@@ -616,6 +679,8 @@ void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
616679
m_fpu_is_valid = false;
617680
m_sve_buffer_is_valid = false;
618681
m_sve_header_is_valid = false;
682+
m_pac_mask_is_valid = false;
683+
m_mte_ctrl_is_valid = false;
619684

620685
// Update SVE registers in case there is change in configuration.
621686
ConfigureRegisterContext();
@@ -633,7 +698,26 @@ Status NativeRegisterContextLinux_arm64::ReadSVEHeader() {
633698

634699
error = ReadRegisterSet(&ioVec, GetSVEHeaderSize(), NT_ARM_SVE);
635700

636-
m_sve_header_is_valid = true;
701+
if (error.Success())
702+
m_sve_header_is_valid = true;
703+
704+
return error;
705+
}
706+
707+
Status NativeRegisterContextLinux_arm64::ReadPAuthMask() {
708+
Status error;
709+
710+
if (m_pac_mask_is_valid)
711+
return error;
712+
713+
struct iovec ioVec;
714+
ioVec.iov_base = GetPACMask();
715+
ioVec.iov_len = GetPACMaskSize();
716+
717+
error = ReadRegisterSet(&ioVec, GetPACMaskSize(), NT_ARM_PAC_MASK);
718+
719+
if (error.Success())
720+
m_pac_mask_is_valid = true;
637721

638722
return error;
639723
}
@@ -693,6 +777,40 @@ Status NativeRegisterContextLinux_arm64::WriteAllSVE() {
693777
return WriteRegisterSet(&ioVec, GetSVEBufferSize(), NT_ARM_SVE);
694778
}
695779

780+
Status NativeRegisterContextLinux_arm64::ReadMTEControl() {
781+
Status error;
782+
783+
if (m_mte_ctrl_is_valid)
784+
return error;
785+
786+
struct iovec ioVec;
787+
ioVec.iov_base = GetMTEControl();
788+
ioVec.iov_len = GetMTEControlSize();
789+
790+
error = ReadRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL);
791+
792+
if (error.Success())
793+
m_mte_ctrl_is_valid = true;
794+
795+
return error;
796+
}
797+
798+
Status NativeRegisterContextLinux_arm64::WriteMTEControl() {
799+
Status error;
800+
801+
error = ReadMTEControl();
802+
if (error.Fail())
803+
return error;
804+
805+
struct iovec ioVec;
806+
ioVec.iov_base = GetMTEControl();
807+
ioVec.iov_len = GetMTEControlSize();
808+
809+
m_mte_ctrl_is_valid = false;
810+
811+
return WriteRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL);
812+
}
813+
696814
void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
697815
// ConfigureRegisterContext gets called from InvalidateAllRegisters
698816
// on every stop and configures SVE vector length.

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ class NativeRegisterContextLinux_arm64
7676
bool m_gpr_is_valid;
7777
bool m_fpu_is_valid;
7878
bool m_sve_buffer_is_valid;
79+
bool m_mte_ctrl_is_valid;
7980

8081
bool m_sve_header_is_valid;
82+
bool m_pac_mask_is_valid;
8183

8284
struct user_pt_regs m_gpr_arm64; // 64-bit general purpose registers.
8385

@@ -90,6 +92,15 @@ class NativeRegisterContextLinux_arm64
9092

9193
bool m_refresh_hwdebug_info;
9294

95+
struct user_pac_mask {
96+
uint64_t data_mask;
97+
uint64_t insn_mask;
98+
};
99+
100+
struct user_pac_mask m_pac_mask;
101+
102+
uint64_t m_mte_ctrl_reg;
103+
93104
bool IsGPR(unsigned reg) const;
94105

95106
bool IsFPR(unsigned reg) const;
@@ -102,20 +113,36 @@ class NativeRegisterContextLinux_arm64
102113

103114
Status WriteSVEHeader();
104115

116+
Status ReadPAuthMask();
117+
118+
Status ReadMTEControl();
119+
120+
Status WriteMTEControl();
121+
105122
bool IsSVE(unsigned reg) const;
123+
bool IsPAuth(unsigned reg) const;
124+
bool IsMTE(unsigned reg) const;
106125

107126
uint64_t GetSVERegVG() { return m_sve_header.vl / 8; }
108127

109128
void SetSVERegVG(uint64_t vg) { m_sve_header.vl = vg * 8; }
110129

111130
void *GetSVEHeader() { return &m_sve_header; }
112131

132+
void *GetPACMask() { return &m_pac_mask; }
133+
134+
void *GetMTEControl() { return &m_mte_ctrl_reg; }
135+
113136
void *GetSVEBuffer();
114137

115138
size_t GetSVEHeaderSize() { return sizeof(m_sve_header); }
116139

140+
size_t GetPACMaskSize() { return sizeof(m_pac_mask); }
141+
117142
size_t GetSVEBufferSize() { return m_sve_ptrace_payload.size(); }
118143

144+
size_t GetMTEControlSize() { return sizeof(m_mte_ctrl_reg); }
145+
119146
llvm::Error ReadHardwareDebugInfo() override;
120147

121148
llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;

lldb/source/Plugins/Process/Linux/NativeThreadLinux.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class NativeThreadLinux : public NativeThreadProtocol {
5151

5252
Status RemoveHardwareBreakpoint(lldb::addr_t addr) override;
5353

54+
NativeProcessLinux &GetProcess();
55+
5456
private:
5557
// Interface for friend classes
5658

@@ -90,8 +92,6 @@ class NativeThreadLinux : public NativeThreadProtocol {
9092
// Private interface
9193
void MaybeLogStateChange(lldb::StateType new_state);
9294

93-
NativeProcessLinux &GetProcess();
94-
9595
void SetStopped();
9696

9797
// Member Variables

lldb/source/Plugins/Process/POSIX/NativeProcessELF.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ namespace lldb_private {
2121
class NativeProcessELF : public NativeProcessProtocol {
2222
using NativeProcessProtocol::NativeProcessProtocol;
2323

24+
public:
25+
llvm::Optional<uint64_t> GetAuxValue(enum AuxVector::EntryType type);
26+
2427
protected:
2528
template <typename T> struct ELFLinkMap {
2629
T l_addr;
@@ -30,8 +33,6 @@ class NativeProcessELF : public NativeProcessProtocol {
3033
T l_prev;
3134
};
3235

33-
llvm::Optional<uint64_t> GetAuxValue(enum AuxVector::EntryType type);
34-
3536
lldb::addr_t GetSharedLibraryInfoAddress() override;
3637

3738
template <typename ELF_EHDR, typename ELF_PHDR, typename ELF_DYN>

lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ enum {
7777
k_num_gpr_registers = gpr_w28 - gpr_x0 + 1,
7878
k_num_fpr_registers = fpu_fpcr - fpu_v0 + 1,
7979
k_num_sve_registers = sve_ffr - sve_vg + 1,
80+
k_num_mte_register = 1,
81+
k_num_pauth_register = 2,
8082
k_num_register_sets_default = 2,
8183
k_num_register_sets = 3
8284
};
@@ -175,6 +177,12 @@ static const lldb_private::RegisterSet g_reg_sets_arm64[k_num_register_sets] = {
175177
{"Scalable Vector Extension Registers", "sve", k_num_sve_registers,
176178
g_sve_regnums_arm64}};
177179

180+
static const lldb_private::RegisterSet g_reg_set_pauth_arm64 = {
181+
"Pointer Authentication Registers", "pauth", k_num_pauth_register, NULL};
182+
183+
static const lldb_private::RegisterSet g_reg_set_mte_arm64 = {
184+
"MTE Control Register", "mte", k_num_mte_register, NULL};
185+
178186
RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
179187
const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets)
180188
: lldb_private::RegisterInfoAndSetInterface(target_arch),
@@ -209,6 +217,12 @@ RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
209217
llvm::copy(reg_infos_ref, std::back_inserter(m_dynamic_reg_infos));
210218
llvm::copy(reg_sets_ref, std::back_inserter(m_dynamic_reg_sets));
211219

220+
if (m_opt_regsets.AllSet(eRegsetMaskPAuth))
221+
AddRegSetPAuth();
222+
223+
if (m_opt_regsets.AllSet(eRegsetMaskMTE))
224+
AddRegSetMTE();
225+
212226
m_register_info_count = m_dynamic_reg_infos.size();
213227
m_register_info_p = m_dynamic_reg_infos.data();
214228
m_register_set_p = m_dynamic_reg_sets.data();
@@ -259,6 +273,39 @@ RegisterInfoPOSIX_arm64::GetRegisterSet(size_t set_index) const {
259273
return nullptr;
260274
}
261275

276+
void RegisterInfoPOSIX_arm64::AddRegSetPAuth() {
277+
uint32_t pa_regnum = m_dynamic_reg_infos.size();
278+
for (uint32_t i = 0; i < k_num_pauth_register; i++) {
279+
pauth_regnum_collection.push_back(pa_regnum + i);
280+
m_dynamic_reg_infos.push_back(g_register_infos_pauth[i]);
281+
m_dynamic_reg_infos[pa_regnum + i].byte_offset =
282+
m_dynamic_reg_infos[pa_regnum + i - 1].byte_offset +
283+
m_dynamic_reg_infos[pa_regnum + i - 1].byte_size;
284+
m_dynamic_reg_infos[pa_regnum + i].kinds[lldb::eRegisterKindLLDB] =
285+
pa_regnum + i;
286+
}
287+
288+
m_per_regset_regnum_range[m_register_set_count] =
289+
std::make_pair(pa_regnum, m_dynamic_reg_infos.size());
290+
m_dynamic_reg_sets.push_back(g_reg_set_pauth_arm64);
291+
m_dynamic_reg_sets.back().registers = pauth_regnum_collection.data();
292+
}
293+
294+
void RegisterInfoPOSIX_arm64::AddRegSetMTE() {
295+
uint32_t mte_regnum = m_dynamic_reg_infos.size();
296+
m_mte_regnum_collection.push_back(mte_regnum);
297+
m_dynamic_reg_infos.push_back(g_register_infos_mte[0]);
298+
m_dynamic_reg_infos[mte_regnum].byte_offset =
299+
m_dynamic_reg_infos[mte_regnum - 1].byte_offset +
300+
m_dynamic_reg_infos[mte_regnum - 1].byte_size;
301+
m_dynamic_reg_infos[mte_regnum].kinds[lldb::eRegisterKindLLDB] = mte_regnum;
302+
303+
m_per_regset_regnum_range[m_register_set_count] =
304+
std::make_pair(mte_regnum, mte_regnum + 1);
305+
m_dynamic_reg_sets.push_back(g_reg_set_mte_arm64);
306+
m_dynamic_reg_sets.back().registers = m_mte_regnum_collection.data();
307+
}
308+
262309
uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLength(uint32_t sve_vq) {
263310
// sve_vq contains SVE Quad vector length in context of AArch64 SVE.
264311
// SVE register infos if enabled cannot be disabled by selecting sve_vq = 0.
@@ -342,6 +389,18 @@ bool RegisterInfoPOSIX_arm64::IsSVERegVG(unsigned reg) const {
342389
return sve_vg == reg;
343390
}
344391

392+
bool RegisterInfoPOSIX_arm64::IsPAuthReg(unsigned reg) const {
393+
return std::find(pauth_regnum_collection.begin(),
394+
pauth_regnum_collection.end(),
395+
reg) != pauth_regnum_collection.end();
396+
}
397+
398+
bool RegisterInfoPOSIX_arm64::IsMTEReg(unsigned reg) const {
399+
return std::find(m_mte_regnum_collection.begin(),
400+
m_mte_regnum_collection.end(),
401+
reg) != m_mte_regnum_collection.end();
402+
}
403+
345404
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; }
346405

347406
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; }
@@ -351,3 +410,11 @@ uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPCR() const { return fpu_fpcr; }
351410
uint32_t RegisterInfoPOSIX_arm64::GetRegNumFPSR() const { return fpu_fpsr; }
352411

353412
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEVG() const { return sve_vg; }
413+
414+
uint32_t RegisterInfoPOSIX_arm64::GetPAuthOffset() const {
415+
return m_register_info_p[pauth_regnum_collection[0]].byte_offset;
416+
}
417+
418+
uint32_t RegisterInfoPOSIX_arm64::GetMTEOffset() const {
419+
return m_register_info_p[m_mte_regnum_collection[0]].byte_offset;
420+
}

0 commit comments

Comments
 (0)