summaryrefslogtreecommitdiff
path: root/libunwind
diff options
context:
space:
mode:
authorLuke Cheeseman <luke.cheeseman@arm.com>2018-09-24 15:55:35 +0000
committerLuke Cheeseman <luke.cheeseman@arm.com>2018-09-24 15:55:35 +0000
commit3096464a75773ecfa4a34147f57a7dc177dd6aa3 (patch)
treeb36e1291ab2c246744a324a15483bc0d5b8331d9 /libunwind
parent58898a8566a462780711073bfceb165d84cc0f73 (diff)
[AArch64] Unwinding support for return address signing
- When return address signing is enabled, the LR may be signed on function entry - When an exception is thrown the return address is inspected used to unwind the call stack - Before this happens, the return address must be correctly authenticated to avoid causing an abort by dereferencing the signed pointer Differential Revision: https://reviews.llvm.org/D51432
Diffstat (limited to 'libunwind')
-rw-r--r--libunwind/include/libunwind.h5
-rw-r--r--libunwind/src/DwarfInstructions.hpp18
-rw-r--r--libunwind/src/DwarfParser.hpp8
-rw-r--r--libunwind/src/Registers.hpp9
-rw-r--r--libunwind/src/dwarf2.h5
5 files changed, 43 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..6ccd8607f5e 100644
--- a/libunwind/src/DwarfInstructions.hpp
+++ b/libunwind/src/DwarfInstructions.hpp
@@ -198,6 +198,24 @@ 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;
+ asm("autia1716": "+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 37ec84b8264..658482433af 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,8 +1834,11 @@ 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 +1847,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
};