Skip to content

Commit d2c221b

Browse files
committed
[lldb][AArch64] Add Guarded Control Stack support for Linux core files
This allows you to read the same registers as you would for a live process. As the content of proc/pid/smaps is not included in the core file, we don't get the "ss" marker. The GCS region is stil in the list though.
1 parent e9e06be commit d2c221b

File tree

5 files changed

+54
-0
lines changed

5 files changed

+54
-0
lines changed

lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ RegisterContextCorePOSIX_arm64::Create(Thread &thread, const ArchSpec &arch,
6969
if (fpmr_data.GetByteSize() >= sizeof(uint64_t))
7070
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskFPMR);
7171

72+
DataExtractor gcs_data = getRegset(notes, arch.GetTriple(), AARCH64_GCS_Desc);
73+
struct __attribute__((packed)) gcs_regs {
74+
uint64_t features_enabled;
75+
uint64_t features_locked;
76+
uint64_t gcspr_e0;
77+
};
78+
if (gcs_data.GetByteSize() >= sizeof(gcs_regs))
79+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskGCS);
80+
7281
auto register_info_up =
7382
std::make_unique<RegisterInfoPOSIX_arm64>(arch, opt_regsets);
7483
return std::unique_ptr<RegisterContextCorePOSIX_arm64>(
@@ -136,6 +145,9 @@ RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
136145
if (m_register_info_up->IsFPMRPresent())
137146
m_fpmr_data = getRegset(notes, target_triple, AARCH64_FPMR_Desc);
138147

148+
if (m_register_info_up->IsGCSPresent())
149+
m_gcs_data = getRegset(notes, target_triple, AARCH64_GCS_Desc);
150+
139151
ConfigureRegisterContext();
140152
}
141153

@@ -330,6 +342,11 @@ bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info,
330342
assert(offset < m_mte_data.GetByteSize());
331343
value.SetFromMemoryData(*reg_info, m_mte_data.GetDataStart() + offset,
332344
reg_info->byte_size, lldb::eByteOrderLittle, error);
345+
} else if (IsGCS(reg)) {
346+
offset = reg_info->byte_offset - m_register_info_up->GetGCSOffset();
347+
assert(offset < m_gcs_data.GetByteSize());
348+
value.SetFromMemoryData(*reg_info, m_gcs_data.GetDataStart() + offset,
349+
reg_info->byte_size, lldb::eByteOrderLittle, error);
333350
} else if (IsSME(reg)) {
334351
// If you had SME in the process, active or otherwise, there will at least
335352
// be a ZA header. No header, no SME at all.

lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class RegisterContextCorePOSIX_arm64 : public RegisterContextPOSIX_arm64 {
6363
lldb_private::DataExtractor m_mte_data;
6464
lldb_private::DataExtractor m_zt_data;
6565
lldb_private::DataExtractor m_fpmr_data;
66+
lldb_private::DataExtractor m_gcs_data;
6667

6768
SVEState m_sve_state = SVEState::Unknown;
6869
uint16_t m_sve_vector_length = 0;

lldb/source/Plugins/Process/elf-core/RegisterUtilities.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ constexpr RegsetDesc AARCH64_FPMR_Desc[] = {
148148
{llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_FPMR},
149149
};
150150

151+
constexpr RegsetDesc AARCH64_GCS_Desc[] = {
152+
{llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_GCS},
153+
};
154+
151155
constexpr RegsetDesc PPC_VMX_Desc[] = {
152156
{llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX},
153157
{llvm::Triple::Linux, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX},

lldb/test/API/linux/aarch64/gcs/TestAArch64LinuxGCS.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,35 @@ def test_gcs_expression_enable_gcs(self):
374374
# consistent with the disabled -> enabled behaviour.
375375
enabled |= 1
376376
self.check_gcs_registers(enabled, locked, spr_el0)
377+
378+
@skipIfLLVMTargetMissing("AArch64")
379+
def test_gcs_core_file(self):
380+
# To re-generate the core file, build the test file and run it on a
381+
# machine with GCS enabled. Note that because the kernel decides where
382+
# the GCS is stored, the value of gcspr_el0 and which memory region it
383+
# points to may change between runs.
384+
385+
self.runCmd("target create --core corefile")
386+
387+
self.expect(
388+
"bt",
389+
substrs=["stop reason = SIGSEGV: control protection fault"],
390+
)
391+
392+
self.expect(
393+
"register read --all",
394+
substrs=[
395+
"Guarded Control Stack Registers:",
396+
"gcs_features_enabled = 0x0000000000000001",
397+
"gcs_features_locked = 0x0000000000000000",
398+
"gcspr_el0 = 0x0000ffffa83ffff0",
399+
],
400+
)
401+
402+
# Core files do not include /proc/pid/smaps, so we cannot see the
403+
# shadow stack "ss" flag. gcspr_el0 should at least point to some mapped
404+
# region.
405+
self.expect(
406+
"memory region $gcspr_el0",
407+
substrs=["[0x0000ffffa8000000-0x0000ffffa8400000) rw-"],
408+
)
24 KB
Binary file not shown.

0 commit comments

Comments
 (0)