[AArch64][libunwind] Unwinding support for return address signing
- Follow up to revision r342895 - gcc would not build libunwind with the earlier patch as the autia1716 instruction wasn't allowed to be assembled for pre armv8.3a targets - The autia1716 instruction lives in the hint space encodings so is a valid instruction for all armv8a targets - To work around this I have swapped out the autia1716 instruction for the hint instruction Differential Revision: https://reviews.llvm.org/D55700 llvm-svn: 349140
This commit is contained in:
parent
96408bb04a
commit
e1a819e82d
|
@ -57,6 +57,9 @@ enum {
|
|||
UNW_EINVAL = -6547, /* unsupported operation or bad value */
|
||||
UNW_EBADVERSION = -6548, /* unwind info has unsupported version */
|
||||
UNW_ENOINFO = -6549 /* no unwind info found */
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||
, UNW_ECROSSRASIGNING = -6550 /* cross unwind with return address signing */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct unw_context_t {
|
||||
|
@ -547,6 +550,8 @@ enum {
|
|||
UNW_ARM64_X31 = 31,
|
||||
UNW_ARM64_SP = 31,
|
||||
// reserved block
|
||||
UNW_ARM64_RA_SIGN_STATE = 34,
|
||||
// reserved block
|
||||
UNW_ARM64_D0 = 64,
|
||||
UNW_ARM64_D1 = 65,
|
||||
UNW_ARM64_D2 = 66,
|
||||
|
|
|
@ -198,6 +198,27 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
|
|||
// restoring SP means setting it to CFA.
|
||||
newRegisters.setSP(cfa);
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
// If the target is aarch64 then the return address may have been signed
|
||||
// using the v8.3 pointer authentication extensions. The original
|
||||
// return address needs to be authenticated before the return address is
|
||||
// restored. autia1716 is used instead of autia as autia1716 assembles
|
||||
// to a NOP on pre-v8.3a architectures.
|
||||
if (prolog.savedRegisters[UNW_ARM64_RA_SIGN_STATE].value) {
|
||||
#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||
return UNW_ECROSSRASIGNING;
|
||||
#else
|
||||
register unsigned long long x17 __asm("x17") = returnAddress;
|
||||
register unsigned long long x16 __asm("x16") = cfa;
|
||||
|
||||
// This is the autia1716 instruction. The hint instruction is used here
|
||||
// as gcc does not assemble autia1716 for pre armv8.3a targets.
|
||||
asm("hint 0xc": "+r"(x17): "r"(x16));
|
||||
returnAddress = x17;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// Return address is address after call site instruction, so setting IP to
|
||||
// that does simualates a return.
|
||||
newRegisters.setIP(returnAddress);
|
||||
|
|
|
@ -666,6 +666,14 @@ bool CFI_Parser<A>::parseInstructions(A &addressSpace, pint_t instructions,
|
|||
_LIBUNWIND_TRACE_DWARF(
|
||||
"DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset);
|
||||
break;
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
case DW_CFA_AARCH64_negate_ra_state:
|
||||
results->savedRegisters[UNW_ARM64_RA_SIGN_STATE].value ^= 0x1;
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n");
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
operand = opcode & 0x3F;
|
||||
switch (opcode & 0xC0) {
|
||||
|
|
|
@ -1786,7 +1786,7 @@ private:
|
|||
uint64_t __lr; // Link register x30
|
||||
uint64_t __sp; // Stack pointer x31
|
||||
uint64_t __pc; // Program counter
|
||||
uint64_t padding; // 16-byte align
|
||||
uint64_t __ra_sign_state; // RA sign state register
|
||||
};
|
||||
|
||||
GPRs _registers;
|
||||
|
@ -1822,6 +1822,8 @@ inline bool Registers_arm64::validRegister(int regNum) const {
|
|||
return false;
|
||||
if (regNum > 95)
|
||||
return false;
|
||||
if (regNum == UNW_ARM64_RA_SIGN_STATE)
|
||||
return true;
|
||||
if ((regNum > 31) && (regNum < 64))
|
||||
return false;
|
||||
return true;
|
||||
|
@ -1832,6 +1834,8 @@ inline uint64_t Registers_arm64::getRegister(int regNum) const {
|
|||
return _registers.__pc;
|
||||
if (regNum == UNW_REG_SP)
|
||||
return _registers.__sp;
|
||||
if (regNum == UNW_ARM64_RA_SIGN_STATE)
|
||||
return _registers.__ra_sign_state;
|
||||
if ((regNum >= 0) && (regNum < 32))
|
||||
return _registers.__x[regNum];
|
||||
_LIBUNWIND_ABORT("unsupported arm64 register");
|
||||
|
@ -1842,6 +1846,8 @@ inline void Registers_arm64::setRegister(int regNum, uint64_t value) {
|
|||
_registers.__pc = value;
|
||||
else if (regNum == UNW_REG_SP)
|
||||
_registers.__sp = value;
|
||||
else if (regNum == UNW_ARM64_RA_SIGN_STATE)
|
||||
_registers.__ra_sign_state = value;
|
||||
else if ((regNum >= 0) && (regNum < 32))
|
||||
_registers.__x[regNum] = value;
|
||||
else
|
||||
|
|
|
@ -49,7 +49,10 @@ enum {
|
|||
// GNU extensions
|
||||
DW_CFA_GNU_window_save = 0x2D,
|
||||
DW_CFA_GNU_args_size = 0x2E,
|
||||
DW_CFA_GNU_negative_offset_extended = 0x2F
|
||||
DW_CFA_GNU_negative_offset_extended = 0x2F,
|
||||
|
||||
// AARCH64 extensions
|
||||
DW_CFA_AARCH64_negate_ra_state = 0x2D
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue