Skip to content

Commit

Permalink
Setting to control addressable bits in high memory
Browse files Browse the repository at this point in the history
On AArch64, it is possible to have a program that accesses both low
(0x000...) and high (0xfff...) memory, and with pointer authentication,
you can have different numbers of bits used for pointer authentication
depending on whether the address is in high or low memory.

This adds a new target.process.highmem-virtual-addressable-bits
setting which the AArch64 Mac ABI plugin will use, when set, to
always set those unaddressable high bits for high memory addresses,
and will use the existing target.process.virtual-addressable-bits
setting for low memory addresses.

This patch does not change the existing behavior when only
target.process.virtual-addressable-bits is set.  In that case, the
value will apply to all addresses.

Not yet done is recognizing metadata in a live process connection
(gdb-remote qHostInfo) or a Mach-O corefile LC_NOTE to set the
correct number of addressing bits for both memory ranges.  That
will be a future change.

Differential Revision: https://reviews.llvm.org/D151292
rdar://109746900
  • Loading branch information
jasonmolenda committed Jun 1, 2023
1 parent bf8fe1c commit 21dfaf6
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 29 deletions.
17 changes: 17 additions & 0 deletions lldb/include/lldb/Target/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class ProcessProperties : public Properties {
FileSpec GetPythonOSPluginPath() const;
uint32_t GetVirtualAddressableBits() const;
void SetVirtualAddressableBits(uint32_t bits);
uint32_t GetHighmemVirtualAddressableBits() const;
void SetHighmemVirtualAddressableBits(uint32_t bits);
void SetPythonOSPluginPath(const FileSpec &file);
bool GetIgnoreBreakpointsInExpressions() const;
void SetIgnoreBreakpointsInExpressions(bool ignore);
Expand Down Expand Up @@ -1371,6 +1373,9 @@ class Process : public std::enable_shared_from_this<Process>,
lldb::addr_t GetCodeAddressMask();
lldb::addr_t GetDataAddressMask();

lldb::addr_t GetHighmemCodeAddressMask();
lldb::addr_t GetHighmemDataAddressMask();

void SetCodeAddressMask(lldb::addr_t code_address_mask) {
m_code_address_mask = code_address_mask;
}
Expand All @@ -1379,6 +1384,14 @@ class Process : public std::enable_shared_from_this<Process>,
m_data_address_mask = data_address_mask;
}

void SetHighmemCodeAddressMask(lldb::addr_t code_address_mask) {
m_highmem_code_address_mask = code_address_mask;
}

void SetHighmemDataAddressMask(lldb::addr_t data_address_mask) {
m_highmem_data_address_mask = data_address_mask;
}

/// Get the Modification ID of the process.
///
/// \return
Expand Down Expand Up @@ -3000,9 +3013,13 @@ void PruneThreadPlans();
/// Mask for code an data addresses. The default value (0) means no mask is
/// set. The bits set to 1 indicate bits that are NOT significant for
/// addressing.
/// The highmem versions are for targets where we may have different masks
/// for low memory versus high memory addresses.
/// @{
lldb::addr_t m_code_address_mask = 0;
lldb::addr_t m_data_address_mask = 0;
lldb::addr_t m_highmem_code_address_mask = 0;
lldb::addr_t m_highmem_data_address_mask = 0;
/// @}

bool m_clear_thread_plans_on_stop;
Expand Down
42 changes: 34 additions & 8 deletions lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,15 +814,41 @@ ValueObjectSP ABIMacOSX_arm64::GetReturnValueObjectImpl(
return return_valobj_sp;
}

lldb::addr_t ABIMacOSX_arm64::FixAddress(addr_t pc, addr_t mask) {
lldb::addr_t pac_sign_extension = 0x0080000000000000ULL;
// When no mask is specified, clear/set the top byte; preserve
// the low 55 bits (00..54) for addressing and bit 55 to indicate
// sign.
if (mask == 0) {
// ~((1ULL<<55)-1)
mask = 0xff80000000000000;
addr_t ABIMacOSX_arm64::FixCodeAddress(addr_t pc) {
addr_t pac_sign_extension = 0x0080000000000000ULL;
addr_t tbi_mask = 0xff80000000000000ULL;
addr_t mask = 0;

if (ProcessSP process_sp = GetProcessSP()) {
mask = process_sp->GetCodeAddressMask();
if (pc & pac_sign_extension) {
addr_t highmem_mask = process_sp->GetHighmemCodeAddressMask();
if (highmem_mask)
mask = highmem_mask;
}
}
if (mask == 0)
mask = tbi_mask;

return (pc & pac_sign_extension) ? pc | mask : pc & (~mask);
}

addr_t ABIMacOSX_arm64::FixDataAddress(addr_t pc) {
addr_t pac_sign_extension = 0x0080000000000000ULL;
addr_t tbi_mask = 0xff80000000000000ULL;
addr_t mask = 0;

if (ProcessSP process_sp = GetProcessSP()) {
mask = process_sp->GetDataAddressMask();
if (pc & pac_sign_extension) {
addr_t highmem_mask = process_sp->GetHighmemDataAddressMask();
if (highmem_mask)
mask = highmem_mask;
}
}
if (mask == 0)
mask = tbi_mask;

return (pc & pac_sign_extension) ? pc | mask : pc & (~mask);
}

Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ class ABIMacOSX_arm64 : public ABIAArch64 {
return true;
}

lldb::addr_t FixAddress(lldb::addr_t pc, lldb::addr_t mask) override;
lldb::addr_t FixCodeAddress(lldb::addr_t pc) override;
lldb::addr_t FixDataAddress(lldb::addr_t pc) override;

// Static Functions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1080,15 +1080,18 @@ void DynamicLoaderDarwinKernel::LoadKernelModuleIfNeeded() {
}
// If the kernel global with the T1Sz setting is available,
// update the target.process.virtual-addressable-bits to be correct.
// NB the xnu kernel always has T0Sz and T1Sz the same value. If
// it wasn't the same, we would need to set
// target.process.virtual-addressable-bits = T0Sz
// target.process.highmem-virtual-addressable-bits = T1Sz
symbol = m_kernel.GetModule()->FindFirstSymbolWithNameAndType(
arm64_T1Sz_value, eSymbolTypeData);
if (symbol) {
const uint32_t orig_bits_value = m_process->GetVirtualAddressableBits();
// Mark all bits as addressable so we don't strip any from our
// memory read below, with an incorrect default value.
// b55 is the sign extension bit with PAC, b56:63 are TBI,
// don't mark those as addressable.
m_process->SetVirtualAddressableBits(55);
const addr_t orig_code_mask = m_process->GetCodeAddressMask();
const addr_t orig_data_mask = m_process->GetDataAddressMask();

m_process->SetCodeAddressMask(0);
m_process->SetDataAddressMask(0);
Status error;
// gT1Sz is 8 bytes. We may run on a stripped kernel binary
// where we can't get the size accurately. Hardcode it.
Expand All @@ -1103,9 +1106,12 @@ void DynamicLoaderDarwinKernel::LoadKernelModuleIfNeeded() {
// T1Sz is 25, then 64-25 == 39, bits 0..38 are used for
// addressing, bits 39..63 are used for PAC/TBI or whatever.
uint32_t virt_addr_bits = 64 - sym_value;
m_process->SetVirtualAddressableBits(virt_addr_bits);
addr_t mask = ~((1ULL << virt_addr_bits) - 1);
m_process->SetCodeAddressMask(mask);
m_process->SetDataAddressMask(mask);
} else {
m_process->SetVirtualAddressableBits(orig_bits_value);
m_process->SetCodeAddressMask(orig_code_mask);
m_process->SetDataAddressMask(orig_data_mask);
}
}
} else {
Expand Down
42 changes: 30 additions & 12 deletions lldb/source/Target/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,18 @@ void ProcessProperties::SetVirtualAddressableBits(uint32_t bits) {
const uint32_t idx = ePropertyVirtualAddressableBits;
SetPropertyAtIndex(idx, static_cast<uint64_t>(bits));
}

uint32_t ProcessProperties::GetHighmemVirtualAddressableBits() const {
const uint32_t idx = ePropertyHighmemVirtualAddressableBits;
return GetPropertyAtIndexAs<uint64_t>(
idx, g_process_properties[idx].default_uint_value);
}

void ProcessProperties::SetHighmemVirtualAddressableBits(uint32_t bits) {
const uint32_t idx = ePropertyHighmemVirtualAddressableBits;
SetPropertyAtIndex(idx, static_cast<uint64_t>(bits));
}

void ProcessProperties::SetPythonOSPluginPath(const FileSpec &file) {
const uint32_t idx = ePropertyPythonOSPluginPath;
SetPropertyAtIndex(idx, file);
Expand Down Expand Up @@ -5651,25 +5663,31 @@ void Process::Flush() {
}

lldb::addr_t Process::GetCodeAddressMask() {
if (m_code_address_mask == 0) {
if (uint32_t number_of_addressable_bits = GetVirtualAddressableBits()) {
lldb::addr_t address_mask = ~((1ULL << number_of_addressable_bits) - 1);
SetCodeAddressMask(address_mask);
}
}
if (uint32_t num_bits_setting = GetVirtualAddressableBits())
return ~((1ULL << num_bits_setting) - 1);

return m_code_address_mask;
}

lldb::addr_t Process::GetDataAddressMask() {
if (m_data_address_mask == 0) {
if (uint32_t number_of_addressable_bits = GetVirtualAddressableBits()) {
lldb::addr_t address_mask = ~((1ULL << number_of_addressable_bits) - 1);
SetDataAddressMask(address_mask);
}
}
if (uint32_t num_bits_setting = GetVirtualAddressableBits())
return ~((1ULL << num_bits_setting) - 1);

return m_data_address_mask;
}

lldb::addr_t Process::GetHighmemCodeAddressMask() {
if (uint32_t num_bits_setting = GetHighmemVirtualAddressableBits())
return ~((1ULL << num_bits_setting) - 1);
return GetCodeAddressMask();
}

lldb::addr_t Process::GetHighmemDataAddressMask() {
if (uint32_t num_bits_setting = GetHighmemVirtualAddressableBits())
return ~((1ULL << num_bits_setting) - 1);
return GetDataAddressMask();
}

void Process::DidExec() {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::%s()", __FUNCTION__);
Expand Down
3 changes: 3 additions & 0 deletions lldb/source/Target/TargetProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ let Definition = "process" in {
def VirtualAddressableBits: Property<"virtual-addressable-bits", "UInt64">,
DefaultUnsignedValue<0>,
Desc<"The number of bits used for addressing. If the value is 39, then bits 0..38 are used for addressing. The default value of 0 means unspecified.">;
def HighmemVirtualAddressableBits: Property<"highmem-virtual-addressable-bits", "UInt64">,
DefaultUnsignedValue<0>,
Desc<"The number of bits used for addressing high memory, when it differs from low memory in the same Process. When this is non-zero, target.process.virtual-addressable-bits will be the value for low memory (0x000... addresses) and this setting will be the value for high memory (0xfff... addresses). When this is zero, target.process.virtual-addressable-bits applies to all addresses. It is very uncommon to use this setting.">;
def FollowForkMode: Property<"follow-fork-mode", "Enum">,
DefaultEnumValue<"eFollowParent">,
EnumValues<"OptionEnumValues(g_follow_fork_mode_values)">,
Expand Down

0 comments on commit 21dfaf6

Please sign in to comment.