diff options
author | Luke Cheeseman <luke.cheeseman@arm.com> | 2018-12-14 11:30:12 +0000 |
---|---|---|
committer | Luke Cheeseman <luke.cheeseman@arm.com> | 2018-12-14 11:30:12 +0000 |
commit | e1a819e82de87bfd217d73ca74a39d10b46d0a71 (patch) | |
tree | d2d100a78ce79f0bffbe3fd90f02e544c6e754f0 | |
parent | 96408bb04a03b4ac961831c270fd9c996358ee03 (diff) | |
download | bcm5719-llvm-e1a819e82de87bfd217d73ca74a39d10b46d0a71.tar.gz bcm5719-llvm-e1a819e82de87bfd217d73ca74a39d10b46d0a71.zip |
[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
-rw-r--r-- | libunwind/include/libunwind.h | 5 | ||||
-rw-r--r-- | libunwind/src/DwarfInstructions.hpp | 21 | ||||
-rw-r--r-- | libunwind/src/DwarfParser.hpp | 8 | ||||
-rw-r--r-- | libunwind/src/Registers.hpp | 8 | ||||
-rw-r--r-- | libunwind/src/dwarf2.h | 5 |
5 files changed, 45 insertions, 2 deletions
diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h index 97d2b82290c..d824c131835 100644 --- a/libunwind/include/libunwind.h +++ b/libunwind/include/libunwind.h @@ -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, diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index 88e67198a37..2bf19c56332 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -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); diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp index b749b7776e1..68506a35053 100644 --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -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) { diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp index da30dd1adb0..5ca715f9ba3 100644 --- a/libunwind/src/Registers.hpp +++ b/libunwind/src/Registers.hpp @@ -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 diff --git a/libunwind/src/dwarf2.h b/libunwind/src/dwarf2.h index 0dcd2ca99ba..58525eea044 100644 --- a/libunwind/src/dwarf2.h +++ b/libunwind/src/dwarf2.h @@ -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 }; |