[lldb][AArch64] Add HWCAP3 to register field detection (#145029)

This will be used to detect the presence of Arm's new Memory Tagging
store only checking feature. This commit just adds the plumbing to get
that value into the detection function.

FreeBSD has not allocated a number for HWCAP3 and already has AT_ARGV
defined as 29. So instead of attempting to read from FreeBSD processes,
I've explicitly passed 0. We don't want to be reading some other entry
accidentally.

If/when FreeBSD adds HWCAP3 we can handle it like we do for
AUXV_FREEBSD_AT_HWCAP.

No extra tests here, those will be coming with the next change for MTE
support.

(cherry picked from commit d26ca8b87266024546501051ccaf75cb3756aee3)
This commit is contained in:
David Spickett 2025-07-28 16:09:24 +01:00 committed by Tobias Hieta
parent 614544048c
commit 9ad630fcb7
7 changed files with 59 additions and 25 deletions

View File

@ -44,7 +44,8 @@ NativeRegisterContextFreeBSD::CreateHostNativeRegisterContextFreeBSD(
NativeProcessFreeBSD &process = native_thread.GetProcess();
g_register_flags_detector.DetectFields(
process.GetAuxValue(AuxVector::AUXV_FREEBSD_AT_HWCAP).value_or(0),
process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2).value_or(0));
process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2).value_or(0),
/*hwcap3=*/0);
}
return new NativeRegisterContextFreeBSD_arm64(target_arch, native_thread);

View File

@ -162,10 +162,13 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS);
std::optional<uint64_t> auxv_at_hwcap3 =
process.GetAuxValue(AuxVector::AUXV_AT_HWCAP3);
std::lock_guard<std::mutex> lock(g_register_flags_detector_mutex);
if (!g_register_flags_detector.HasDetected())
g_register_flags_detector.DetectFields(auxv_at_hwcap.value_or(0),
auxv_at_hwcap2.value_or(0));
auxv_at_hwcap2.value_or(0),
auxv_at_hwcap3.value_or(0));
auto register_info_up =
std::make_unique<RegisterInfoPOSIX_arm64>(target_arch, opt_regsets);

View File

@ -84,6 +84,7 @@ const char *AuxVector::GetEntryName(EntryType type) const {
case ENTRY_NAME(AUXV_AT_BASE_PLATFORM); break;
case ENTRY_NAME(AUXV_AT_RANDOM); break;
case ENTRY_NAME(AUXV_AT_HWCAP2); break;
case ENTRY_NAME(AUXV_AT_HWCAP3); break;
case ENTRY_NAME(AUXV_AT_EXECFN); break;
case ENTRY_NAME(AUXV_AT_SYSINFO); break;
case ENTRY_NAME(AUXV_AT_SYSINFO_EHDR); break;

View File

@ -57,6 +57,7 @@ public:
AUXV_AT_BASE_PLATFORM = 24, ///< String identifying real platforms.
AUXV_AT_RANDOM = 25, ///< Address of 16 random bytes.
AUXV_AT_HWCAP2 = 26, ///< Extension of AT_HWCAP.
AUXV_AT_HWCAP3 = 29, ///< Extension of AT_HWCAP.
AUXV_AT_EXECFN = 31, ///< Filename of executable.
AUXV_AT_SYSINFO = 32, ///< Pointer to the global system page used for system
/// calls and other nice things.

View File

@ -29,8 +29,10 @@
using namespace lldb_private;
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectFPMRFields(uint64_t hwcap, uint64_t hwcap2) {
Arm64RegisterFlagsDetector::DetectFPMRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap;
(void)hwcap3;
if (!(hwcap2 & HWCAP2_FPMR))
return {};
@ -53,8 +55,10 @@ Arm64RegisterFlagsDetector::DetectFPMRFields(uint64_t hwcap, uint64_t hwcap2) {
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectGCSFeatureFields(uint64_t hwcap,
uint64_t hwcap2) {
uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap2;
(void)hwcap3;
if (!(hwcap & HWCAP_GCS))
return {};
@ -67,8 +71,10 @@ Arm64RegisterFlagsDetector::DetectGCSFeatureFields(uint64_t hwcap,
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2) {
Arm64RegisterFlagsDetector::DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap;
(void)hwcap3;
if (!(hwcap2 & HWCAP2_SME))
return {};
@ -83,9 +89,10 @@ Arm64RegisterFlagsDetector::DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2) {
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectMTECtrlFields(uint64_t hwcap,
uint64_t hwcap2) {
Arm64RegisterFlagsDetector::DetectMTECtrlFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap;
(void)hwcap3;
if (!(hwcap2 & HWCAP2_MTE))
return {};
@ -103,7 +110,10 @@ Arm64RegisterFlagsDetector::DetectMTECtrlFields(uint64_t hwcap,
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectFPCRFields(uint64_t hwcap, uint64_t hwcap2) {
Arm64RegisterFlagsDetector::DetectFPCRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap3;
static const FieldEnum rmode_enum(
"rmode_enum", {{0, "RN"}, {1, "RP"}, {2, "RM"}, {3, "RZ"}});
@ -142,10 +152,12 @@ Arm64RegisterFlagsDetector::DetectFPCRFields(uint64_t hwcap, uint64_t hwcap2) {
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectFPSRFields(uint64_t hwcap, uint64_t hwcap2) {
Arm64RegisterFlagsDetector::DetectFPSRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
// fpsr's contents are constant.
(void)hwcap;
(void)hwcap2;
(void)hwcap3;
return {
// Bits 31-28 are N/Z/C/V, only used by AArch32.
@ -162,7 +174,10 @@ Arm64RegisterFlagsDetector::DetectFPSRFields(uint64_t hwcap, uint64_t hwcap2) {
}
Arm64RegisterFlagsDetector::Fields
Arm64RegisterFlagsDetector::DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2) {
Arm64RegisterFlagsDetector::DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
(void)hwcap3;
// The fields here are a combination of the Arm manual's SPSR_EL1,
// plus a few changes where Linux has decided not to make use of them at all,
// or at least not from userspace.
@ -207,9 +222,10 @@ Arm64RegisterFlagsDetector::DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2) {
return cpsr_fields;
}
void Arm64RegisterFlagsDetector::DetectFields(uint64_t hwcap, uint64_t hwcap2) {
void Arm64RegisterFlagsDetector::DetectFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3) {
for (auto &reg : m_registers)
reg.m_flags.SetFields(reg.m_detector(hwcap, hwcap2));
reg.m_flags.SetFields(reg.m_detector(hwcap, hwcap2, hwcap3));
m_has_detected = true;
}

View File

@ -40,7 +40,7 @@ public:
/// If called more than once, fields will be redetected each time from
/// scratch. If the target would not have this register at all, the list of
/// fields will be left empty.
void DetectFields(uint64_t hwcap, uint64_t hwcap2);
void DetectFields(uint64_t hwcap, uint64_t hwcap2, uint64_t hwcap3);
/// Add the field information of any registers named in this class,
/// to the relevant RegisterInfo instances. Note that this will be done
@ -53,15 +53,22 @@ public:
private:
using Fields = std::vector<RegisterFlags::Field>;
using DetectorFn = std::function<Fields(uint64_t, uint64_t)>;
using DetectorFn = std::function<Fields(uint64_t, uint64_t, uint64_t)>;
static Fields DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2);
static Fields DetectFPSRFields(uint64_t hwcap, uint64_t hwcap2);
static Fields DetectFPCRFields(uint64_t hwcap, uint64_t hwcap2);
static Fields DetectMTECtrlFields(uint64_t hwcap, uint64_t hwcap2);
static Fields DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2);
static Fields DetectFPMRFields(uint64_t hwcap, uint64_t hwcap2);
static Fields DetectGCSFeatureFields(uint64_t hwcap, uint64_t hwcap2);
static Fields DetectCPSRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3);
static Fields DetectFPSRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3);
static Fields DetectFPCRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3);
static Fields DetectMTECtrlFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3);
static Fields DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3);
static Fields DetectFPMRFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3);
static Fields DetectGCSFeatureFields(uint64_t hwcap, uint64_t hwcap2,
uint64_t hwcap3);
struct RegisterEntry {
RegisterEntry(llvm::StringRef name, unsigned size, DetectorFn detector)

View File

@ -96,14 +96,19 @@ RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
llvm::Triple::OSType os = process->GetArchitecture().GetTriple().getOS();
if ((os == llvm::Triple::Linux) || (os == llvm::Triple::FreeBSD)) {
AuxVector aux_vec(process->GetAuxvData());
std::optional<uint64_t> auxv_at_hwcap = aux_vec.GetAuxValue(
os == llvm::Triple::FreeBSD ? AuxVector::AUXV_FREEBSD_AT_HWCAP
: AuxVector::AUXV_AT_HWCAP);
bool is_freebsd = os == llvm::Triple::FreeBSD;
std::optional<uint64_t> auxv_at_hwcap =
aux_vec.GetAuxValue(is_freebsd ? AuxVector::AUXV_FREEBSD_AT_HWCAP
: AuxVector::AUXV_AT_HWCAP);
std::optional<uint64_t> auxv_at_hwcap2 =
aux_vec.GetAuxValue(AuxVector::AUXV_AT_HWCAP2);
std::optional<uint64_t> auxv_at_hwcap3 =
is_freebsd ? std::nullopt
: aux_vec.GetAuxValue(AuxVector::AUXV_AT_HWCAP3);
m_register_flags_detector.DetectFields(auxv_at_hwcap.value_or(0),
auxv_at_hwcap2.value_or(0));
auxv_at_hwcap2.value_or(0),
auxv_at_hwcap3.value_or(0));
m_register_flags_detector.UpdateRegisterInfo(GetRegisterInfo(),
GetRegisterCount());
}