Skip to content

Commit 02c6002

Browse files
[lldb][AArch64] Add Guarded Control Stack registers (#123720)
The Guarded Control Stack extension implements a shadow stack and the Linux kernel provides access to 3 registers for it via ptrace. struct user_gcs { __u64 features_enabled; __u64 features_locked; __u64 gcspr_el0; }; This commit adds support for reading those from a live process. The first 2 are pseudo registers based on the real control register and the 3rd is a real register. This is the stack pointer for the guarded stack. I have added a "gcs_" prefix to the "features" registers so that they have a clear name when shown individually. Also this means they will tab complete from "gcs", and be next to gcspr_el0 in any sorted lists of registers. Guarded Control Stack Registers: gcs_features_enabled = 0x0000000000000000 gcs_features_locked = 0x0000000000000000 gcspr_el0 = 0x0000000000000000 Testing is more of the usual, where possible I'm writing a register then doing something in the program to confirm the value was actually sent to ptrace.
1 parent 6292a80 commit 02c6002

File tree

8 files changed

+323
-5
lines changed

8 files changed

+323
-5
lines changed

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

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,14 @@
6464
#define NT_ARM_FPMR 0x40e /* Floating point mode register */
6565
#endif
6666

67+
#ifndef NT_ARM_GCS
68+
#define NT_ARM_GCS 0x410 /* Guarded Control Stack control registers */
69+
#endif
70+
6771
#define HWCAP_PACA (1 << 30)
6872

73+
#define HWCAP_GCS (1UL << 32)
74+
6975
#define HWCAP2_MTE (1 << 18)
7076

7177
#define HWCAP2_FPMR (1UL << 48)
@@ -150,6 +156,8 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
150156
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE);
151157
if (*auxv_at_hwcap2 & HWCAP2_FPMR)
152158
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskFPMR);
159+
if (*auxv_at_hwcap & HWCAP_GCS)
160+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskGCS);
153161
}
154162

155163
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS);
@@ -193,6 +201,7 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
193201
::memset(&m_pac_mask, 0, sizeof(m_pac_mask));
194202
::memset(&m_tls_regs, 0, sizeof(m_tls_regs));
195203
::memset(&m_sme_pseudo_regs, 0, sizeof(m_sme_pseudo_regs));
204+
::memset(&m_gcs_regs, 0, sizeof(m_gcs_regs));
196205
std::fill(m_zt_reg.begin(), m_zt_reg.end(), 0);
197206

198207
m_mte_ctrl_reg = 0;
@@ -213,6 +222,7 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
213222
m_tls_is_valid = false;
214223
m_zt_buffer_is_valid = false;
215224
m_fpmr_is_valid = false;
225+
m_gcs_is_valid = false;
216226

217227
// SME adds the tpidr2 register
218228
m_tls_size = GetRegisterInfo().IsSSVEPresent() ? sizeof(m_tls_regs)
@@ -433,6 +443,14 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
433443
offset = reg_info->byte_offset - GetRegisterInfo().GetFPMROffset();
434444
assert(offset < GetFPMRBufferSize());
435445
src = (uint8_t *)GetFPMRBuffer() + offset;
446+
} else if (IsGCS(reg)) {
447+
error = ReadGCS();
448+
if (error.Fail())
449+
return error;
450+
451+
offset = reg_info->byte_offset - GetRegisterInfo().GetGCSOffset();
452+
assert(offset < GetGCSBufferSize());
453+
src = (uint8_t *)GetGCSBuffer() + offset;
436454
} else
437455
return Status::FromErrorString(
438456
"failed - register wasn't recognized to be a GPR or an FPR, "
@@ -657,6 +675,17 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
657675
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
658676

659677
return WriteFPMR();
678+
} else if (IsGCS(reg)) {
679+
error = ReadGCS();
680+
if (error.Fail())
681+
return error;
682+
683+
offset = reg_info->byte_offset - GetRegisterInfo().GetGCSOffset();
684+
assert(offset < GetGCSBufferSize());
685+
dst = (uint8_t *)GetGCSBuffer() + offset;
686+
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
687+
688+
return WriteGCS();
660689
}
661690

662691
return Status::FromErrorString("Failed to write register value");
@@ -672,6 +701,7 @@ enum RegisterSetType : uint32_t {
672701
SME, // ZA only, because SVCR and SVG are pseudo registers.
673702
SME2, // ZT only.
674703
FPMR,
704+
GCS, // Guarded Control Stack registers.
675705
};
676706

677707
static uint8_t *AddRegisterSetType(uint8_t *dst,
@@ -759,6 +789,13 @@ NativeRegisterContextLinux_arm64::CacheAllRegisters(uint32_t &cached_size) {
759789
return error;
760790
}
761791

792+
if (GetRegisterInfo().IsGCSPresent()) {
793+
cached_size += sizeof(RegisterSetType) + GetGCSBufferSize();
794+
error = ReadGCS();
795+
if (error.Fail())
796+
return error;
797+
}
798+
762799
// tpidr is always present but tpidr2 depends on SME.
763800
cached_size += sizeof(RegisterSetType) + GetTLSBufferSize();
764801
error = ReadTLS();
@@ -867,6 +904,11 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
867904
GetFPMRBufferSize());
868905
}
869906

907+
if (GetRegisterInfo().IsGCSPresent()) {
908+
dst = AddSavedRegisters(dst, RegisterSetType::GCS, GetGCSBuffer(),
909+
GetGCSBufferSize());
910+
}
911+
870912
dst = AddSavedRegisters(dst, RegisterSetType::TLS, GetTLSBuffer(),
871913
GetTLSBufferSize());
872914

@@ -1020,6 +1062,11 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
10201062
GetFPMRBuffer(), &src, GetFPMRBufferSize(), m_fpmr_is_valid,
10211063
std::bind(&NativeRegisterContextLinux_arm64::WriteFPMR, this));
10221064
break;
1065+
case RegisterSetType::GCS:
1066+
error = RestoreRegisters(
1067+
GetGCSBuffer(), &src, GetGCSBufferSize(), m_gcs_is_valid,
1068+
std::bind(&NativeRegisterContextLinux_arm64::WriteGCS, this));
1069+
break;
10231070
}
10241071

10251072
if (error.Fail())
@@ -1067,6 +1114,10 @@ bool NativeRegisterContextLinux_arm64::IsFPMR(unsigned reg) const {
10671114
return GetRegisterInfo().IsFPMRReg(reg);
10681115
}
10691116

1117+
bool NativeRegisterContextLinux_arm64::IsGCS(unsigned reg) const {
1118+
return GetRegisterInfo().IsGCSReg(reg);
1119+
}
1120+
10701121
llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
10711122
if (!m_refresh_hwdebug_info) {
10721123
return llvm::Error::success();
@@ -1215,6 +1266,7 @@ void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
12151266
m_tls_is_valid = false;
12161267
m_zt_buffer_is_valid = false;
12171268
m_fpmr_is_valid = false;
1269+
m_gcs_is_valid = false;
12181270

12191271
// Update SVE and ZA registers in case there is change in configuration.
12201272
ConfigureRegisterContext();
@@ -1400,6 +1452,40 @@ Status NativeRegisterContextLinux_arm64::WriteTLS() {
14001452
return WriteRegisterSet(&ioVec, GetTLSBufferSize(), NT_ARM_TLS);
14011453
}
14021454

1455+
Status NativeRegisterContextLinux_arm64::ReadGCS() {
1456+
Status error;
1457+
1458+
if (m_gcs_is_valid)
1459+
return error;
1460+
1461+
struct iovec ioVec;
1462+
ioVec.iov_base = GetGCSBuffer();
1463+
ioVec.iov_len = GetGCSBufferSize();
1464+
1465+
error = ReadRegisterSet(&ioVec, GetGCSBufferSize(), NT_ARM_GCS);
1466+
1467+
if (error.Success())
1468+
m_gcs_is_valid = true;
1469+
1470+
return error;
1471+
}
1472+
1473+
Status NativeRegisterContextLinux_arm64::WriteGCS() {
1474+
Status error;
1475+
1476+
error = ReadGCS();
1477+
if (error.Fail())
1478+
return error;
1479+
1480+
struct iovec ioVec;
1481+
ioVec.iov_base = GetGCSBuffer();
1482+
ioVec.iov_len = GetGCSBufferSize();
1483+
1484+
m_gcs_is_valid = false;
1485+
1486+
return WriteRegisterSet(&ioVec, GetGCSBufferSize(), NT_ARM_GCS);
1487+
}
1488+
14031489
Status NativeRegisterContextLinux_arm64::ReadZAHeader() {
14041490
Status error;
14051491

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class NativeRegisterContextLinux_arm64
9292
bool m_pac_mask_is_valid;
9393
bool m_tls_is_valid;
9494
size_t m_tls_size;
95+
bool m_gcs_is_valid;
9596

9697
struct user_pt_regs m_gpr_arm64; // 64-bit general purpose registers.
9798

@@ -136,6 +137,12 @@ class NativeRegisterContextLinux_arm64
136137

137138
uint64_t m_fpmr_reg;
138139

140+
struct gcs_regs {
141+
uint64_t features_enabled;
142+
uint64_t features_locked;
143+
uint64_t gcspr_e0;
144+
} m_gcs_regs;
145+
139146
bool IsGPR(unsigned reg) const;
140147

141148
bool IsFPR(unsigned reg) const;
@@ -166,6 +173,10 @@ class NativeRegisterContextLinux_arm64
166173

167174
Status WriteZA();
168175

176+
Status ReadGCS();
177+
178+
Status WriteGCS();
179+
169180
// No WriteZAHeader because writing only the header will disable ZA.
170181
// Instead use WriteZA and ensure you have the correct ZA buffer size set
171182
// beforehand if you wish to disable it.
@@ -187,6 +198,7 @@ class NativeRegisterContextLinux_arm64
187198
bool IsMTE(unsigned reg) const;
188199
bool IsTLS(unsigned reg) const;
189200
bool IsFPMR(unsigned reg) const;
201+
bool IsGCS(unsigned reg) const;
190202

191203
uint64_t GetSVERegVG() { return m_sve_header.vl / 8; }
192204

@@ -212,6 +224,8 @@ class NativeRegisterContextLinux_arm64
212224

213225
void *GetFPMRBuffer() { return &m_fpmr_reg; }
214226

227+
void *GetGCSBuffer() { return &m_gcs_regs; }
228+
215229
size_t GetSVEHeaderSize() { return sizeof(m_sve_header); }
216230

217231
size_t GetPACMaskSize() { return sizeof(m_pac_mask); }
@@ -234,6 +248,8 @@ class NativeRegisterContextLinux_arm64
234248

235249
size_t GetFPMRBufferSize() { return sizeof(m_fpmr_reg); }
236250

251+
size_t GetGCSBufferSize() { return sizeof(m_gcs_regs); }
252+
237253
llvm::Error ReadHardwareDebugInfo() override;
238254

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

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ bool RegisterContextPOSIX_arm64::IsFPMR(unsigned reg) const {
6363
return m_register_info_up->IsFPMRReg(reg);
6464
}
6565

66+
bool RegisterContextPOSIX_arm64::IsGCS(unsigned reg) const {
67+
return m_register_info_up->IsGCSReg(reg);
68+
}
69+
6670
RegisterContextPOSIX_arm64::RegisterContextPOSIX_arm64(
6771
lldb_private::Thread &thread,
6872
std::unique_ptr<RegisterInfoPOSIX_arm64> register_info)

lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class RegisterContextPOSIX_arm64 : public lldb_private::RegisterContext {
5959
bool IsSME(unsigned reg) const;
6060
bool IsMTE(unsigned reg) const;
6161
bool IsFPMR(unsigned reg) const;
62+
bool IsGCS(unsigned reg) const;
6263

6364
bool IsSVEZ(unsigned reg) const { return m_register_info_up->IsSVEZReg(reg); }
6465
bool IsSVEP(unsigned reg) const { return m_register_info_up->IsSVEPReg(reg); }

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

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ static lldb_private::RegisterInfo g_register_infos_sme2[] = {
9797
static lldb_private::RegisterInfo g_register_infos_fpmr[] = {
9898
DEFINE_EXTENSION_REG(fpmr)};
9999

100+
static lldb_private::RegisterInfo g_register_infos_gcs[] = {
101+
DEFINE_EXTENSION_REG(gcs_features_enabled),
102+
DEFINE_EXTENSION_REG(gcs_features_locked), DEFINE_EXTENSION_REG(gcspr_el0)};
103+
100104
// Number of register sets provided by this context.
101105
enum {
102106
k_num_gpr_registers = gpr_w28 - gpr_x0 + 1,
@@ -109,6 +113,7 @@ enum {
109113
// only for SME1 registers.
110114
k_num_sme_register = 3,
111115
k_num_fpmr_register = 1,
116+
k_num_gcs_register = 3,
112117
k_num_register_sets_default = 2,
113118
k_num_register_sets = 3
114119
};
@@ -221,6 +226,9 @@ static const lldb_private::RegisterSet g_reg_set_sme_arm64 = {
221226
static const lldb_private::RegisterSet g_reg_set_fpmr_arm64 = {
222227
"Floating Point Mode Register", "fpmr", k_num_fpmr_register, nullptr};
223228

229+
static const lldb_private::RegisterSet g_reg_set_gcs_arm64 = {
230+
"Guarded Control Stack Registers", "gcs", k_num_gcs_register, nullptr};
231+
224232
RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
225233
const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets)
226234
: lldb_private::RegisterInfoAndSetInterface(target_arch),
@@ -273,6 +281,9 @@ RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
273281
if (m_opt_regsets.AllSet(eRegsetMaskFPMR))
274282
AddRegSetFPMR();
275283

284+
if (m_opt_regsets.AllSet(eRegsetMaskGCS))
285+
AddRegSetGCS();
286+
276287
m_register_info_count = m_dynamic_reg_infos.size();
277288
m_register_info_p = m_dynamic_reg_infos.data();
278289
m_register_set_p = m_dynamic_reg_sets.data();
@@ -434,6 +445,24 @@ void RegisterInfoPOSIX_arm64::AddRegSetFPMR() {
434445
m_dynamic_reg_sets.back().registers = m_fpmr_regnum_collection.data();
435446
}
436447

448+
void RegisterInfoPOSIX_arm64::AddRegSetGCS() {
449+
uint32_t gcs_regnum = m_dynamic_reg_infos.size();
450+
for (uint32_t i = 0; i < k_num_gcs_register; i++) {
451+
m_gcs_regnum_collection.push_back(gcs_regnum + i);
452+
m_dynamic_reg_infos.push_back(g_register_infos_gcs[i]);
453+
m_dynamic_reg_infos[gcs_regnum + i].byte_offset =
454+
m_dynamic_reg_infos[gcs_regnum + i - 1].byte_offset +
455+
m_dynamic_reg_infos[gcs_regnum + i - 1].byte_size;
456+
m_dynamic_reg_infos[gcs_regnum + i].kinds[lldb::eRegisterKindLLDB] =
457+
gcs_regnum + i;
458+
}
459+
460+
m_per_regset_regnum_range[m_register_set_count] =
461+
std::make_pair(gcs_regnum, m_dynamic_reg_infos.size());
462+
m_dynamic_reg_sets.push_back(g_reg_set_gcs_arm64);
463+
m_dynamic_reg_sets.back().registers = m_gcs_regnum_collection.data();
464+
}
465+
437466
uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLengthSVE(uint32_t sve_vq) {
438467
// sve_vq contains SVE Quad vector length in context of AArch64 SVE.
439468
// SVE register infos if enabled cannot be disabled by selecting sve_vq = 0.
@@ -561,6 +590,10 @@ bool RegisterInfoPOSIX_arm64::IsFPMRReg(unsigned reg) const {
561590
return llvm::is_contained(m_fpmr_regnum_collection, reg);
562591
}
563592

593+
bool RegisterInfoPOSIX_arm64::IsGCSReg(unsigned reg) const {
594+
return llvm::is_contained(m_gcs_regnum_collection, reg);
595+
}
596+
564597
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; }
565598

566599
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; }
@@ -593,4 +626,8 @@ uint32_t RegisterInfoPOSIX_arm64::GetSMEOffset() const {
593626

594627
uint32_t RegisterInfoPOSIX_arm64::GetFPMROffset() const {
595628
return m_register_info_p[m_fpmr_regnum_collection[0]].byte_offset;
596-
}
629+
}
630+
631+
uint32_t RegisterInfoPOSIX_arm64::GetGCSOffset() const {
632+
return m_register_info_p[m_gcs_regnum_collection[0]].byte_offset;
633+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class RegisterInfoPOSIX_arm64
3333
eRegsetMaskZA = 32,
3434
eRegsetMaskZT = 64,
3535
eRegsetMaskFPMR = 128,
36+
eRegsetMaskGCS = 256,
3637
eRegsetMaskDynamic = ~1,
3738
};
3839

@@ -113,6 +114,8 @@ class RegisterInfoPOSIX_arm64
113114

114115
void AddRegSetFPMR();
115116

117+
void AddRegSetGCS();
118+
116119
uint32_t ConfigureVectorLengthSVE(uint32_t sve_vq);
117120

118121
void ConfigureVectorLengthZA(uint32_t za_vq);
@@ -132,6 +135,7 @@ class RegisterInfoPOSIX_arm64
132135
bool IsMTEPresent() const { return m_opt_regsets.AnySet(eRegsetMaskMTE); }
133136
bool IsTLSPresent() const { return m_opt_regsets.AnySet(eRegsetMaskTLS); }
134137
bool IsFPMRPresent() const { return m_opt_regsets.AnySet(eRegsetMaskFPMR); }
138+
bool IsGCSPresent() const { return m_opt_regsets.AnySet(eRegsetMaskGCS); }
135139

136140
bool IsSVEReg(unsigned reg) const;
137141
bool IsSVEZReg(unsigned reg) const;
@@ -144,6 +148,7 @@ class RegisterInfoPOSIX_arm64
144148
bool IsSMERegZA(unsigned reg) const;
145149
bool IsSMERegZT(unsigned reg) const;
146150
bool IsFPMRReg(unsigned reg) const;
151+
bool IsGCSReg(unsigned reg) const;
147152

148153
uint32_t GetRegNumSVEZ0() const;
149154
uint32_t GetRegNumSVEFFR() const;
@@ -156,6 +161,7 @@ class RegisterInfoPOSIX_arm64
156161
uint32_t GetTLSOffset() const;
157162
uint32_t GetSMEOffset() const;
158163
uint32_t GetFPMROffset() const;
164+
uint32_t GetGCSOffset() const;
159165

160166
private:
161167
typedef std::map<uint32_t, std::vector<lldb_private::RegisterInfo>>
@@ -188,6 +194,7 @@ class RegisterInfoPOSIX_arm64
188194
std::vector<uint32_t> m_tls_regnum_collection;
189195
std::vector<uint32_t> m_sme_regnum_collection;
190196
std::vector<uint32_t> m_fpmr_regnum_collection;
197+
std::vector<uint32_t> m_gcs_regnum_collection;
191198
};
192199

193200
#endif

0 commit comments

Comments
 (0)