Upstream debugserver arm64e support.

The changes are minor; primarily debugserver needs to go through
accessor functions/macros when changing pc/fp/sp/lr, and debugserver
needs to clear any existing pointer auth bits from values in two
cases.  debugserver can fetch the number of bits used for addressing
from a sysctl, and will include that in the qHostInfo reply.  Update
qHostInfo documentation to document it.
This commit is contained in:
Jason Molenda 2019-12-04 15:20:56 -08:00
parent 00e436f130
commit e11df58580
4 changed files with 124 additions and 4 deletions

View File

@ -790,6 +790,13 @@ distribution_id: optional. For linux, specifies distribution id (e.g. ubuntu, fe
osmajor: optional, specifies the major version number of the OS (e.g. for macOS 10.12.2, it would be 10)
osminor: optional, specifies the minor version number of the OS (e.g. for macOS 10.12.2, it would be 12)
ospatch: optional, specifies the patch level number of the OS (e.g. for macOS 10.12.2, it would be 2)
addressing_bits: optional, specifies how many bits in addresses are
significant for addressing, base 10. If bits 38..0
in a 64-bit pointer are significant for addressing,
then the value is 39. This is needed on e.g. Aarch64
v8.3 ABIs that use pointer authentication, so lldb
knows which bits to clear/set to get the actual
addresses.
//----------------------------------------------------------------------
// "qGDBServerVersion"

View File

@ -1722,6 +1722,8 @@ nub_bool_t DNBSetArchitecture(const char *arch) {
else if (strstr(arch, "arm64_32") == arch ||
strstr(arch, "aarch64_32") == arch)
return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64_32);
else if (strstr(arch, "arm64e") == arch)
return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);
else if (strstr(arch, "arm64") == arch || strstr(arch, "armv8") == arch ||
strstr(arch, "aarch64") == arch)
return DNBArchProtocol::SetArchitecture(CPU_TYPE_ARM64);

View File

@ -26,6 +26,10 @@
#include <inttypes.h>
#include <sys/sysctl.h>
#if __has_feature(ptrauth_calls)
#include <ptrauth.h>
#endif
// Break only in privileged or user mode
// (PAC bits in the DBGWVRn_EL1 watchpoint control register)
#define S_USER ((uint32_t)(2u << 1))
@ -93,7 +97,11 @@ uint32_t DNBArchMachARM64::GetCPUType() { return CPU_TYPE_ARM64; }
uint64_t DNBArchMachARM64::GetPC(uint64_t failValue) {
// Get program counter
if (GetGPRState(false) == KERN_SUCCESS)
#if defined(__LP64__)
return arm_thread_state64_get_pc(m_state.context.gpr);
#else
return m_state.context.gpr.__pc;
#endif
return failValue;
}
@ -101,7 +109,17 @@ kern_return_t DNBArchMachARM64::SetPC(uint64_t value) {
// Get program counter
kern_return_t err = GetGPRState(false);
if (err == KERN_SUCCESS) {
#if defined(__LP64__)
#if __has_feature(ptrauth_calls)
// The incoming value could be garbage. Strip it to avoid
// trapping when it gets resigned in the thread state.
value = (uint64_t) ptrauth_strip((void*) value, ptrauth_key_function_pointer);
value = (uint64_t) ptrauth_sign_unauthenticated((void*) value, ptrauth_key_function_pointer, 0);
#endif
arm_thread_state64_set_pc_fptr (m_state.context.gpr, (void*) value);
#else
m_state.context.gpr.__pc = value;
#endif
err = SetGPRState();
}
return err == KERN_SUCCESS;
@ -110,7 +128,11 @@ kern_return_t DNBArchMachARM64::SetPC(uint64_t value) {
uint64_t DNBArchMachARM64::GetSP(uint64_t failValue) {
// Get stack pointer
if (GetGPRState(false) == KERN_SUCCESS)
#if defined(__LP64__)
return arm_thread_state64_get_sp(m_state.context.gpr);
#else
return m_state.context.gpr.__sp;
#endif
return failValue;
}
@ -167,8 +189,15 @@ kern_return_t DNBArchMachARM64::GetGPRState(bool force) {
x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8], x[9], x[0], x[11],
x[12], x[13], x[14], x[15], x[16], x[17], x[18], x[19], x[20], x[21],
x[22], x[23], x[24], x[25], x[26], x[27], x[28],
#if defined(__LP64__)
(uint64_t) arm_thread_state64_get_fp (m_state.context.gpr),
(uint64_t) arm_thread_state64_get_lr (m_state.context.gpr),
(uint64_t) arm_thread_state64_get_sp (m_state.context.gpr),
(uint64_t) arm_thread_state64_get_pc (m_state.context.gpr),
#else
m_state.context.gpr.__fp, m_state.context.gpr.__lr,
m_state.context.gpr.__sp, m_state.context.gpr.__pc,
#endif
m_state.context.gpr.__cpsr);
}
m_state.SetError(set, Read, kret);
@ -564,12 +593,20 @@ kern_return_t DNBArchMachARM64::EnableHardwareSingleStep(bool enable) {
if (enable) {
DNBLogThreadedIf(LOG_STEP,
"%s: Setting MDSCR_EL1 Single Step bit at pc 0x%llx",
#if defined(__LP64__)
__FUNCTION__, (uint64_t)arm_thread_state64_get_pc (m_state.context.gpr));
#else
__FUNCTION__, (uint64_t)m_state.context.gpr.__pc);
#endif
m_state.dbg.__mdscr_el1 |= SS_ENABLE;
} else {
DNBLogThreadedIf(LOG_STEP,
"%s: Clearing MDSCR_EL1 Single Step bit at pc 0x%llx",
#if defined(__LP64__)
__FUNCTION__, (uint64_t)arm_thread_state64_get_pc (m_state.context.gpr));
#else
__FUNCTION__, (uint64_t)m_state.context.gpr.__pc);
#endif
m_state.dbg.__mdscr_el1 &= ~(SS_ENABLE);
}
@ -1409,10 +1446,28 @@ const DNBRegisterInfo DNBArchMachARM64::g_gpr_registers[] = {
DEFINE_GPR_IDX(26, x26, NULL, INVALID_NUB_REGNUM),
DEFINE_GPR_IDX(27, x27, NULL, INVALID_NUB_REGNUM),
DEFINE_GPR_IDX(28, x28, NULL, INVALID_NUB_REGNUM),
DEFINE_GPR_NAME(fp, "x29", GENERIC_REGNUM_FP),
DEFINE_GPR_NAME(lr, "x30", GENERIC_REGNUM_RA),
DEFINE_GPR_NAME(sp, "xsp", GENERIC_REGNUM_SP),
DEFINE_GPR_NAME(pc, NULL, GENERIC_REGNUM_PC),
// For the G/g packet we want to show where the offset into the regctx
// is for fp/lr/sp/pc, but we cannot directly access them on arm64e
// devices (and therefore can't offsetof() them)) - add the offset based
// on the last accessible register by hand for advertising the location
// in the regctx to lldb. We'll go through the accessor functions when
// we read/write them here.
{
e_regSetGPR, gpr_fp, "fp", "x29", Uint, Hex, 8, GPR_OFFSET_IDX(28) + 8,
dwarf_fp, dwarf_fp, GENERIC_REGNUM_FP, debugserver_gpr_fp, NULL, NULL
},
{
e_regSetGPR, gpr_lr, "lr", "x30", Uint, Hex, 8, GPR_OFFSET_IDX(28) + 16,
dwarf_lr, dwarf_lr, GENERIC_REGNUM_RA, debugserver_gpr_lr, NULL, NULL
},
{
e_regSetGPR, gpr_sp, "sp", "xsp", Uint, Hex, 8, GPR_OFFSET_IDX(28) + 24,
dwarf_sp, dwarf_sp, GENERIC_REGNUM_SP, debugserver_gpr_sp, NULL, NULL
},
{
e_regSetGPR, gpr_pc, "pc", NULL, Uint, Hex, 8, GPR_OFFSET_IDX(28) + 32,
dwarf_pc, dwarf_pc, GENERIC_REGNUM_PC, debugserver_gpr_pc, NULL, NULL
},
// in armv7 we specify that writing to the CPSR should invalidate r8-12, sp,
// lr.
@ -1769,7 +1824,20 @@ bool DNBArchMachARM64::GetRegisterValue(uint32_t set, uint32_t reg,
switch (set) {
case e_regSetGPR:
if (reg <= gpr_pc) {
#if defined(__LP64__)
if (reg == gpr_pc)
value->value.uint64 = arm_thread_state64_get_pc (m_state.context.gpr);
else if (reg == gpr_lr)
value->value.uint64 = arm_thread_state64_get_lr (m_state.context.gpr);
else if (reg == gpr_sp)
value->value.uint64 = arm_thread_state64_get_sp (m_state.context.gpr);
else if (reg == gpr_fp)
value->value.uint64 = arm_thread_state64_get_fp (m_state.context.gpr);
else
value->value.uint64 = m_state.context.gpr.__x[reg];
#else
value->value.uint64 = m_state.context.gpr.__x[reg];
#endif
return true;
} else if (reg == gpr_cpsr) {
value->value.uint32 = m_state.context.gpr.__cpsr;
@ -1859,7 +1927,27 @@ bool DNBArchMachARM64::SetRegisterValue(uint32_t set, uint32_t reg,
switch (set) {
case e_regSetGPR:
if (reg <= gpr_pc) {
#if defined(__LP64__)
uint64_t signed_value = value->value.uint64;
#if __has_feature(ptrauth_calls)
// The incoming value could be garbage. Strip it to avoid
// trapping when it gets resigned in the thread state.
signed_value = (uint64_t) ptrauth_strip((void*) signed_value, ptrauth_key_function_pointer);
signed_value = (uint64_t) ptrauth_sign_unauthenticated((void*) signed_value, ptrauth_key_function_pointer, 0);
#endif
if (reg == gpr_pc)
arm_thread_state64_set_pc_fptr (m_state.context.gpr, (void*) signed_value);
else if (reg == gpr_lr)
arm_thread_state64_set_lr_fptr (m_state.context.gpr, (void*) signed_value);
else if (reg == gpr_sp)
arm_thread_state64_set_sp (m_state.context.gpr, value->value.uint64);
else if (reg == gpr_fp)
arm_thread_state64_set_fp (m_state.context.gpr, value->value.uint64);
else
m_state.context.gpr.__x[reg] = value->value.uint64;
#else
m_state.context.gpr.__x[reg] = value->value.uint64;
#endif
success = true;
} else if (reg == gpr_cpsr) {
m_state.context.gpr.__cpsr = value->value.uint32;

View File

@ -4643,6 +4643,24 @@ static bool GetHostCPUType(uint32_t &cputype, uint32_t &cpusubtype,
return g_host_cputype != 0;
}
static bool GetAddressingBits(uint32_t &addressing_bits) {
static uint32_t g_addressing_bits = 0;
static bool g_tried_addressing_bits_syscall = false;
if (g_tried_addressing_bits_syscall == false) {
size_t len = sizeof (uint32_t);
if (::sysctlbyname("machdep.virtual_address_size",
&g_addressing_bits, &len, NULL, 0) != 0) {
g_addressing_bits = 0;
}
}
g_tried_addressing_bits_syscall = true;
addressing_bits = g_addressing_bits;
if (addressing_bits > 0)
return true;
else
return false;
}
rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) {
std::ostringstream strm;
@ -4655,6 +4673,11 @@ rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) {
strm << "cpusubtype:" << std::dec << cpusubtype << ';';
}
uint32_t addressing_bits = 0;
if (GetAddressingBits(addressing_bits)) {
strm << "addressing_bits:" << std::dec << addressing_bits << ';';
}
// The OS in the triple should be "ios" or "macosx" which doesn't match our
// "Darwin" which gets returned from "kern.ostype", so we need to hardcode
// this for now.