Skip to content

[lldb][AArch64] Add Guarded Control Stack registers #123720

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,14 @@
#define NT_ARM_FPMR 0x40e /* Floating point mode register */
#endif

#ifndef NT_ARM_GCS
#define NT_ARM_GCS 0x410 /* Guarded Control Stack control registers */
#endif

#define HWCAP_PACA (1 << 30)

#define HWCAP_GCS (1UL << 32)

#define HWCAP2_MTE (1 << 18)

#define HWCAP2_FPMR (1UL << 48)
Expand Down Expand Up @@ -150,6 +156,8 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE);
if (*auxv_at_hwcap2 & HWCAP2_FPMR)
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskFPMR);
if (*auxv_at_hwcap & HWCAP_GCS)
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskGCS);
}

opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS);
Expand Down Expand Up @@ -193,6 +201,7 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
::memset(&m_pac_mask, 0, sizeof(m_pac_mask));
::memset(&m_tls_regs, 0, sizeof(m_tls_regs));
::memset(&m_sme_pseudo_regs, 0, sizeof(m_sme_pseudo_regs));
::memset(&m_gcs_regs, 0, sizeof(m_gcs_regs));
std::fill(m_zt_reg.begin(), m_zt_reg.end(), 0);

m_mte_ctrl_reg = 0;
Expand All @@ -213,6 +222,7 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
m_tls_is_valid = false;
m_zt_buffer_is_valid = false;
m_fpmr_is_valid = false;
m_gcs_is_valid = false;

// SME adds the tpidr2 register
m_tls_size = GetRegisterInfo().IsSSVEPresent() ? sizeof(m_tls_regs)
Expand Down Expand Up @@ -433,6 +443,14 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
offset = reg_info->byte_offset - GetRegisterInfo().GetFPMROffset();
assert(offset < GetFPMRBufferSize());
src = (uint8_t *)GetFPMRBuffer() + offset;
} else if (IsGCS(reg)) {
error = ReadGCS();
if (error.Fail())
return error;

offset = reg_info->byte_offset - GetRegisterInfo().GetGCSOffset();
assert(offset < GetGCSBufferSize());
src = (uint8_t *)GetGCSBuffer() + offset;
} else
return Status::FromErrorString(
"failed - register wasn't recognized to be a GPR or an FPR, "
Expand Down Expand Up @@ -657,6 +675,17 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);

return WriteFPMR();
} else if (IsGCS(reg)) {
error = ReadGCS();
if (error.Fail())
return error;

offset = reg_info->byte_offset - GetRegisterInfo().GetGCSOffset();
assert(offset < GetGCSBufferSize());
dst = (uint8_t *)GetGCSBuffer() + offset;
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);

return WriteGCS();
}

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

static uint8_t *AddRegisterSetType(uint8_t *dst,
Expand Down Expand Up @@ -759,6 +789,13 @@ NativeRegisterContextLinux_arm64::CacheAllRegisters(uint32_t &cached_size) {
return error;
}

if (GetRegisterInfo().IsGCSPresent()) {
cached_size += sizeof(RegisterSetType) + GetGCSBufferSize();
error = ReadGCS();
if (error.Fail())
return error;
}

// tpidr is always present but tpidr2 depends on SME.
cached_size += sizeof(RegisterSetType) + GetTLSBufferSize();
error = ReadTLS();
Expand Down Expand Up @@ -867,6 +904,11 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
GetFPMRBufferSize());
}

if (GetRegisterInfo().IsGCSPresent()) {
dst = AddSavedRegisters(dst, RegisterSetType::GCS, GetGCSBuffer(),
GetGCSBufferSize());
}

dst = AddSavedRegisters(dst, RegisterSetType::TLS, GetTLSBuffer(),
GetTLSBufferSize());

Expand Down Expand Up @@ -1020,6 +1062,11 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
GetFPMRBuffer(), &src, GetFPMRBufferSize(), m_fpmr_is_valid,
std::bind(&NativeRegisterContextLinux_arm64::WriteFPMR, this));
break;
case RegisterSetType::GCS:
error = RestoreRegisters(
GetGCSBuffer(), &src, GetGCSBufferSize(), m_gcs_is_valid,
std::bind(&NativeRegisterContextLinux_arm64::WriteGCS, this));
break;
}

if (error.Fail())
Expand Down Expand Up @@ -1067,6 +1114,10 @@ bool NativeRegisterContextLinux_arm64::IsFPMR(unsigned reg) const {
return GetRegisterInfo().IsFPMRReg(reg);
}

bool NativeRegisterContextLinux_arm64::IsGCS(unsigned reg) const {
return GetRegisterInfo().IsGCSReg(reg);
}

llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
if (!m_refresh_hwdebug_info) {
return llvm::Error::success();
Expand Down Expand Up @@ -1215,6 +1266,7 @@ void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
m_tls_is_valid = false;
m_zt_buffer_is_valid = false;
m_fpmr_is_valid = false;
m_gcs_is_valid = false;

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

Status NativeRegisterContextLinux_arm64::ReadGCS() {
Status error;

if (m_gcs_is_valid)
return error;

struct iovec ioVec;
ioVec.iov_base = GetGCSBuffer();
ioVec.iov_len = GetGCSBufferSize();

error = ReadRegisterSet(&ioVec, GetGCSBufferSize(), NT_ARM_GCS);

if (error.Success())
m_gcs_is_valid = true;

return error;
}

Status NativeRegisterContextLinux_arm64::WriteGCS() {
Status error;

error = ReadGCS();
if (error.Fail())
return error;

struct iovec ioVec;
ioVec.iov_base = GetGCSBuffer();
ioVec.iov_len = GetGCSBufferSize();

m_gcs_is_valid = false;

return WriteRegisterSet(&ioVec, GetGCSBufferSize(), NT_ARM_GCS);
}

Status NativeRegisterContextLinux_arm64::ReadZAHeader() {
Status error;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class NativeRegisterContextLinux_arm64
bool m_pac_mask_is_valid;
bool m_tls_is_valid;
size_t m_tls_size;
bool m_gcs_is_valid;

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

Expand Down Expand Up @@ -136,6 +137,12 @@ class NativeRegisterContextLinux_arm64

uint64_t m_fpmr_reg;

struct gcs_regs {
uint64_t features_enabled;
uint64_t features_locked;
uint64_t gcspr_e0;
} m_gcs_regs;

bool IsGPR(unsigned reg) const;

bool IsFPR(unsigned reg) const;
Expand Down Expand Up @@ -166,6 +173,10 @@ class NativeRegisterContextLinux_arm64

Status WriteZA();

Status ReadGCS();

Status WriteGCS();

// No WriteZAHeader because writing only the header will disable ZA.
// Instead use WriteZA and ensure you have the correct ZA buffer size set
// beforehand if you wish to disable it.
Expand All @@ -187,6 +198,7 @@ class NativeRegisterContextLinux_arm64
bool IsMTE(unsigned reg) const;
bool IsTLS(unsigned reg) const;
bool IsFPMR(unsigned reg) const;
bool IsGCS(unsigned reg) const;

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

Expand All @@ -212,6 +224,8 @@ class NativeRegisterContextLinux_arm64

void *GetFPMRBuffer() { return &m_fpmr_reg; }

void *GetGCSBuffer() { return &m_gcs_regs; }

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

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

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

size_t GetGCSBufferSize() { return sizeof(m_gcs_regs); }

llvm::Error ReadHardwareDebugInfo() override;

llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ bool RegisterContextPOSIX_arm64::IsFPMR(unsigned reg) const {
return m_register_info_up->IsFPMRReg(reg);
}

bool RegisterContextPOSIX_arm64::IsGCS(unsigned reg) const {
return m_register_info_up->IsGCSReg(reg);
}

RegisterContextPOSIX_arm64::RegisterContextPOSIX_arm64(
lldb_private::Thread &thread,
std::unique_ptr<RegisterInfoPOSIX_arm64> register_info)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class RegisterContextPOSIX_arm64 : public lldb_private::RegisterContext {
bool IsSME(unsigned reg) const;
bool IsMTE(unsigned reg) const;
bool IsFPMR(unsigned reg) const;
bool IsGCS(unsigned reg) const;

bool IsSVEZ(unsigned reg) const { return m_register_info_up->IsSVEZReg(reg); }
bool IsSVEP(unsigned reg) const { return m_register_info_up->IsSVEPReg(reg); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ static lldb_private::RegisterInfo g_register_infos_sme2[] = {
static lldb_private::RegisterInfo g_register_infos_fpmr[] = {
DEFINE_EXTENSION_REG(fpmr)};

static lldb_private::RegisterInfo g_register_infos_gcs[] = {
DEFINE_EXTENSION_REG(gcs_features_enabled),
DEFINE_EXTENSION_REG(gcs_features_locked), DEFINE_EXTENSION_REG(gcspr_el0)};

// Number of register sets provided by this context.
enum {
k_num_gpr_registers = gpr_w28 - gpr_x0 + 1,
Expand All @@ -109,6 +113,7 @@ enum {
// only for SME1 registers.
k_num_sme_register = 3,
k_num_fpmr_register = 1,
k_num_gcs_register = 3,
k_num_register_sets_default = 2,
k_num_register_sets = 3
};
Expand Down Expand Up @@ -221,6 +226,9 @@ static const lldb_private::RegisterSet g_reg_set_sme_arm64 = {
static const lldb_private::RegisterSet g_reg_set_fpmr_arm64 = {
"Floating Point Mode Register", "fpmr", k_num_fpmr_register, nullptr};

static const lldb_private::RegisterSet g_reg_set_gcs_arm64 = {
"Guarded Control Stack Registers", "gcs", k_num_gcs_register, nullptr};

RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets)
: lldb_private::RegisterInfoAndSetInterface(target_arch),
Expand Down Expand Up @@ -273,6 +281,9 @@ RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
if (m_opt_regsets.AllSet(eRegsetMaskFPMR))
AddRegSetFPMR();

if (m_opt_regsets.AllSet(eRegsetMaskGCS))
AddRegSetGCS();

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();
Expand Down Expand Up @@ -434,6 +445,24 @@ void RegisterInfoPOSIX_arm64::AddRegSetFPMR() {
m_dynamic_reg_sets.back().registers = m_fpmr_regnum_collection.data();
}

void RegisterInfoPOSIX_arm64::AddRegSetGCS() {
uint32_t gcs_regnum = m_dynamic_reg_infos.size();
for (uint32_t i = 0; i < k_num_gcs_register; i++) {
m_gcs_regnum_collection.push_back(gcs_regnum + i);
m_dynamic_reg_infos.push_back(g_register_infos_gcs[i]);
m_dynamic_reg_infos[gcs_regnum + i].byte_offset =
m_dynamic_reg_infos[gcs_regnum + i - 1].byte_offset +
m_dynamic_reg_infos[gcs_regnum + i - 1].byte_size;
m_dynamic_reg_infos[gcs_regnum + i].kinds[lldb::eRegisterKindLLDB] =
gcs_regnum + i;
}

m_per_regset_regnum_range[m_register_set_count] =
std::make_pair(gcs_regnum, m_dynamic_reg_infos.size());
m_dynamic_reg_sets.push_back(g_reg_set_gcs_arm64);
m_dynamic_reg_sets.back().registers = m_gcs_regnum_collection.data();
}

uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLengthSVE(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.
Expand Down Expand Up @@ -561,6 +590,10 @@ bool RegisterInfoPOSIX_arm64::IsFPMRReg(unsigned reg) const {
return llvm::is_contained(m_fpmr_regnum_collection, reg);
}

bool RegisterInfoPOSIX_arm64::IsGCSReg(unsigned reg) const {
return llvm::is_contained(m_gcs_regnum_collection, reg);
}

uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; }

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

uint32_t RegisterInfoPOSIX_arm64::GetFPMROffset() const {
return m_register_info_p[m_fpmr_regnum_collection[0]].byte_offset;
}
}

uint32_t RegisterInfoPOSIX_arm64::GetGCSOffset() const {
return m_register_info_p[m_gcs_regnum_collection[0]].byte_offset;
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class RegisterInfoPOSIX_arm64
eRegsetMaskZA = 32,
eRegsetMaskZT = 64,
eRegsetMaskFPMR = 128,
eRegsetMaskGCS = 256,
eRegsetMaskDynamic = ~1,
};

Expand Down Expand Up @@ -113,6 +114,8 @@ class RegisterInfoPOSIX_arm64

void AddRegSetFPMR();

void AddRegSetGCS();

uint32_t ConfigureVectorLengthSVE(uint32_t sve_vq);

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

bool IsSVEReg(unsigned reg) const;
bool IsSVEZReg(unsigned reg) const;
Expand All @@ -144,6 +148,7 @@ class RegisterInfoPOSIX_arm64
bool IsSMERegZA(unsigned reg) const;
bool IsSMERegZT(unsigned reg) const;
bool IsFPMRReg(unsigned reg) const;
bool IsGCSReg(unsigned reg) const;

uint32_t GetRegNumSVEZ0() const;
uint32_t GetRegNumSVEFFR() const;
Expand All @@ -156,6 +161,7 @@ class RegisterInfoPOSIX_arm64
uint32_t GetTLSOffset() const;
uint32_t GetSMEOffset() const;
uint32_t GetFPMROffset() const;
uint32_t GetGCSOffset() const;

private:
typedef std::map<uint32_t, std::vector<lldb_private::RegisterInfo>>
Expand Down Expand Up @@ -188,6 +194,7 @@ class RegisterInfoPOSIX_arm64
std::vector<uint32_t> m_tls_regnum_collection;
std::vector<uint32_t> m_sme_regnum_collection;
std::vector<uint32_t> m_fpmr_regnum_collection;
std::vector<uint32_t> m_gcs_regnum_collection;
};

#endif
Loading
Loading