[libunwind] [NFC][PAC][libunwind] Check signing scheme flags integrity (PR #173878)
Daniil Kovalev via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 29 08:58:35 PST 2025
https://github.com/kovdan01 created https://github.com/llvm/llvm-project/pull/173878
Please pay no attention to this PR, its submitted to trigger build jobs and ensure that WIP work is not breaking platforms I can't test locally
>From b824caa6c6cb306e0e024fe9aa872a9a5e3f35bb Mon Sep 17 00:00:00 2001
From: Daniil Kovalev <dkovalev at accesssoftek.com>
Date: Mon, 8 Dec 2025 16:31:10 +0300
Subject: [PATCH 1/2] [PAC][libunwind][AArch64] Keep LR signed when stored in
context struct
There are two ways of return address signing: pac-ret (enabled via
`-mbranch-protection=pac-ret`) and ptrauth-returns (enabled as part of
Apple's arm64e or experimental pauthtest ABI on Linux).
Previously, signed LR was handled in libunwind as follows:
1. For pac-ret, the signed LR value was authenticated, and the resulting
unsigned LR value was stored in the context structure.
2. For ptrauth-returns (which is assumed to be a part of a full-fledged
PAuth ABI like arm64e or pauthtest), the signed LR value was
re-signed using the same key (IB) and address of the `__pc` field in
the context structure as a modifier.
This patch unifies the signed LR handling logic by keeping LR signed
for pac-ret similarly to ptrauth-returns. It makes LR substitution in
the context structure harder.
Note that LR signed state or signing scheme for pac-ret might differ between
stack frames. The behavior differs from ptrauth-returns, which has a fixed
signing scheme and is enabled as a part of bigger PAuth ABI which is either
present everywhere or not. In order to handle different signing schemes
across stack frames, new subfields `__state`, `__second_modifier` and
`__use_b_key` of the field `__ra_sign` in the context structure are used.
When stored in the context structure, the pointer is resigned with
maintaining original key and using the address of the `__pc` field in
the structure as a modifier.
---
libunwind/include/__libunwind_config.h | 4 +-
libunwind/include/libunwind.h | 6 +
libunwind/src/DwarfInstructions.hpp | 92 +++--------
libunwind/src/Registers.hpp | 207 +++++++++++++++++++++----
libunwind/src/UnwindCursor.hpp | 8 +-
libunwind/src/UnwindLevel1.c | 58 ++++++-
libunwind/src/UnwindRegistersRestore.S | 163 +++++++++++++++----
libunwind/src/UnwindRegistersSave.S | 104 ++++++++++---
libunwind/src/libunwind.cpp | 11 ++
9 files changed, 482 insertions(+), 171 deletions(-)
diff --git a/libunwind/include/__libunwind_config.h b/libunwind/include/__libunwind_config.h
index 980d11ef5d4f2..3327272c2a6bc 100644
--- a/libunwind/include/__libunwind_config.h
+++ b/libunwind/include/__libunwind_config.h
@@ -73,11 +73,11 @@
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC
# elif defined(__aarch64__)
# define _LIBUNWIND_TARGET_AARCH64 1
-#define _LIBUNWIND_CONTEXT_SIZE 67
+#define _LIBUNWIND_CONTEXT_SIZE 69
# if defined(__SEH__)
# define _LIBUNWIND_CURSOR_SIZE 164
# else
-#define _LIBUNWIND_CURSOR_SIZE 79
+#define _LIBUNWIND_CURSOR_SIZE 81
# endif
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64
# elif defined(__arm__)
diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h
index 56ca7110274a3..6a56548867642 100644
--- a/libunwind/include/libunwind.h
+++ b/libunwind/include/libunwind.h
@@ -642,6 +642,12 @@ enum {
// reserved block
UNW_AARCH64_RA_SIGN_STATE = 34,
+ // The following two registers are not real Dwarf registers and use numbers
+ // which are not occupied by real Dwarf registers according to
+ // https://github.com/ARM-software/abi-aa/blob/2025Q1/aadwarf64/aadwarf64.rst
+ UNW_AARCH64_RA_SIGN_SECOND_MODIFIER = 128,
+ UNW_AARCH64_RA_SIGN_USE_B_KEY = 129,
+
// FP/vector registers
UNW_AARCH64_V0 = 64,
UNW_AARCH64_V1 = 65,
diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp
index d2822e8be29ef..ad1555db3abd2 100644
--- a/libunwind/src/DwarfInstructions.hpp
+++ b/libunwind/src/DwarfInstructions.hpp
@@ -75,10 +75,8 @@ class DwarfInstructions {
__builtin_unreachable();
}
#if defined(_LIBUNWIND_TARGET_AARCH64)
- static bool isReturnAddressSigned(A &addressSpace, R registers, pint_t cfa,
- PrologInfo &prolog);
- static bool isReturnAddressSignedWithPC(A &addressSpace, R registers,
- pint_t cfa, PrologInfo &prolog);
+ static pint_t getRASignState(A &addressSpace, const R ®isters, pint_t cfa,
+ const PrologInfo &prolog);
#endif
};
@@ -176,34 +174,13 @@ v128 DwarfInstructions<A, R>::getSavedVectorRegister(
}
#if defined(_LIBUNWIND_TARGET_AARCH64)
template <typename A, typename R>
-bool DwarfInstructions<A, R>::isReturnAddressSigned(A &addressSpace,
- R registers, pint_t cfa,
- PrologInfo &prolog) {
- pint_t raSignState;
- auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE];
- if (regloc.location == CFI_Parser<A>::kRegisterUnused)
- raSignState = static_cast<pint_t>(regloc.value);
- else
- raSignState = getSavedRegister(addressSpace, registers, cfa, regloc);
-
- // Only bit[0] is meaningful.
- return raSignState & 0x01;
-}
-
-template <typename A, typename R>
-bool DwarfInstructions<A, R>::isReturnAddressSignedWithPC(A &addressSpace,
- R registers,
- pint_t cfa,
- PrologInfo &prolog) {
- pint_t raSignState;
+typename A::pint_t
+DwarfInstructions<A, R>::getRASignState(A &addressSpace, const R ®isters,
+ pint_t cfa, const PrologInfo &prolog) {
auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE];
if (regloc.location == CFI_Parser<A>::kRegisterUnused)
- raSignState = static_cast<pint_t>(regloc.value);
- else
- raSignState = getSavedRegister(addressSpace, registers, cfa, regloc);
-
- // Only bit[1] is meaningful.
- return raSignState & 0x02;
+ return static_cast<pint_t>(regloc.value);
+ return getSavedRegister(addressSpace, registers, cfa, regloc);
}
#endif
@@ -302,54 +279,25 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace,
isSignalFrame = cieInfo.isSignalFrame;
-#if defined(_LIBUNWIND_TARGET_AARCH64) && \
- !defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
- // There are two ways of return address signing: pac-ret (enabled via
- // -mbranch-protection=pac-ret) and ptrauth-returns (enabled as part of
- // Apple's arm64e or experimental pauthtest ABI on Linux). The code
- // below handles signed RA for pac-ret, while ptrauth-returns uses
- // different logic.
- // TODO: unify logic for both cases, see
- // https://github.com/llvm/llvm-project/issues/160110
- //
+#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 ((R::getArch() == REGISTERS_ARM64) &&
- isReturnAddressSigned(addressSpace, registers, cfa, prolog) &&
+ // using the v8.3 pointer authentication extensions. In order to
+ // store signed return address in the registers context structure, we
+ // need to save the signing scheme for this address.
+ pint_t raSignState = getRASignState(addressSpace, registers, cfa, prolog);
+ bool isReturnAddressSigned = (raSignState & 1);
+ if ((R::getArch() == REGISTERS_ARM64) && isReturnAddressSigned &&
returnAddress != 0) {
#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;
-
- // We use the hint versions of the authentication instructions below to
- // ensure they're assembled by the compiler even for targets with no
- // FEAT_PAuth/FEAT_PAuth_LR support.
- if (isReturnAddressSignedWithPC(addressSpace, registers, cfa, prolog)) {
- register unsigned long long x15 __asm("x15") =
- prolog.ptrAuthDiversifier;
- if (cieInfo.addressesSignedWithBKey) {
- asm("hint 0x27\n\t" // pacm
- "hint 0xe"
- : "+r"(x17)
- : "r"(x16), "r"(x15)); // autib1716
- } else {
- asm("hint 0x27\n\t" // pacm
- "hint 0xc"
- : "+r"(x17)
- : "r"(x16), "r"(x15)); // autia1716
- }
- } else {
- if (cieInfo.addressesSignedWithBKey)
- asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
- else
- asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
+ newRegisters.setRegister(UNW_AARCH64_RA_SIGN_STATE, raSignState);
+ if (newRegisters.isReturnAddressSignedWithPC()) {
+ newRegisters.setRegister(UNW_AARCH64_RA_SIGN_SECOND_MODIFIER,
+ prolog.ptrAuthDiversifier);
}
- returnAddress = x17;
+ newRegisters.setRegister(UNW_AARCH64_RA_SIGN_USE_B_KEY,
+ cieInfo.addressesSignedWithBKey ? 1 : 0);
#endif
}
#endif
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 45a2b0921ea3b..2245ccba61e4f 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1877,49 +1877,179 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
uint64_t getSP() const { return _registers.__sp; }
void setSP(uint64_t value) { _registers.__sp = value; }
+
uint64_t getIP() const {
uint64_t value = _registers.__pc;
-#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+
+ if (!isReturnAddressSigned())
+ return value;
+
+#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ abortCrossRASigning();
+#else
// Note the value of the PC was signed to its address in the register state
// but everyone else expects it to be sign by the SP, so convert on return.
- value = (uint64_t)ptrauth_auth_and_resign((void *)_registers.__pc,
- ptrauth_key_return_address,
- &_registers.__pc,
- ptrauth_key_return_address,
- getSP());
+ register uint64_t x17 __asm("x17") = value;
+ register uint64_t x16 __asm("x16") =
+ reinterpret_cast<uint64_t>(&_registers.__pc);
+ register uint64_t x14 __asm("x14") = getSP();
+ if (isReturnAddressSignedWithPC()) {
+ register uint64_t x15 __asm("x15") =
+ _registers.__ra_sign.__second_modifier;
+ if (isReturnAddressSignedWithBKey()) {
+ asm("hint 0xe \n\t" // autib1716
+ "mov x16, x14\n\t"
+ "hint 0x27 \n\t" // pacm
+ "hint 0xa " // pacib1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(x14));
+ } else {
+ asm("hint 0xc \n\t" // autia1716
+ "mov x16, x14\n\t"
+ "hint 0x27 \n\t" // pacm
+ "hint 0x8 " // pacia1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(x14));
+ }
+ } else {
+ if (isReturnAddressSignedWithBKey()) {
+ asm("hint 0xe \n\t" // autib1716
+ "mov x16, x14\n\t"
+ "hint 0xa " // pacib1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x14));
+ } else {
+ asm("hint 0xc \n\t" // autia1716
+ "mov x16, x14\n\t"
+ "hint 0x8 " // pacia1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x14));
+ }
+ }
+ return x17;
#endif
- return value;
}
+
void setIP(uint64_t value) {
-#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ if (!isReturnAddressSigned()) {
+ _registers.__pc = value;
+ return;
+ }
+
+#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ abortCrossRASigning();
+#else
// Note the value which was set should have been signed with the SP.
// We then resign with the slot we are being stored in to so that both SP
// and LR can't be spoofed at the same time.
- value = (uint64_t)ptrauth_auth_and_resign((void *)value,
- ptrauth_key_return_address,
- getSP(),
- ptrauth_key_return_address,
- &_registers.__pc);
+ register uint64_t x17 __asm("x17") = value;
+ register uint64_t x16 __asm("x16") = getSP();
+ register uint64_t x14 __asm("x14") =
+ reinterpret_cast<uint64_t>(&_registers.__pc);
+ if (isReturnAddressSignedWithPC()) {
+ register uint64_t x15 __asm("x15") =
+ _registers.__ra_sign.__second_modifier;
+ if (isReturnAddressSignedWithBKey()) {
+ asm("hint 0x27 \n\t" // pacm
+ "hint 0xe \n\t" // autib1716
+ "mov x16, x14\n\t"
+ "hint 0xa " // pacib1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(x14));
+ } else {
+ asm("hint 0x27 \n\t" // pacm
+ "hint 0xc \n\t" // autia1716
+ "mov x16, x14\n\t"
+ "hint 0x8 " // pacia1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(x14));
+ }
+ } else {
+ if (isReturnAddressSignedWithBKey()) {
+ asm("hint 0xe \n\t" // autib1716
+ "mov x16, x14\n\t"
+ "hint 0xa " // pacib1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x14));
+ } else {
+ asm("hint 0xc \n\t" // autia1716
+ "mov x16, x14\n\t"
+ "hint 0x8 " // pacia1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x14));
+ }
+ }
+ _registers.__pc = x17;
#endif
- _registers.__pc = value;
}
+
uint64_t getFP() const { return _registers.__fp; }
void setFP(uint64_t value) { _registers.__fp = value; }
-#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ // NOTE: For full-fledged PAuth ABIs like Apple's arm64e and Linux's
+ // pauthtest link_reg_t is __ptrauth-qualified. So, LR is re-signed with
+ // link_reg_t signing scheme after it is authenticated with this function.
+ // When just pac-ret is used, link_reg_t is not __ptrauth-qualified and LR
+ // remains unsigned after authentication.
+ // TODO: avoid exposing unsigned LR in absence of full-fledged PAuth ABI.
void
loadAndAuthenticateLinkRegister(reg_t inplaceAuthedLinkRegister,
link_reg_t *referenceAuthedLinkRegister) {
- // If we are in an arm64/arm64e frame, then the PC should have been signed
- // with the SP
- *referenceAuthedLinkRegister =
- (uint64_t)ptrauth_auth_data((void *)inplaceAuthedLinkRegister,
- ptrauth_key_return_address,
- _registers.__sp);
- }
+ if (!isReturnAddressSigned()) {
+ *referenceAuthedLinkRegister = inplaceAuthedLinkRegister;
+ return;
+ }
+
+#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ abortCrossRASigning();
+#else
+ register reg_t x17 __asm("x17") = inplaceAuthedLinkRegister;
+ register reg_t x16 __asm("x16") = getSP();
+ if (isReturnAddressSignedWithPC()) {
+ register reg_t x15 __asm("x15") = _registers.__ra_sign.__second_modifier;
+ if (isReturnAddressSignedWithBKey()) {
+ asm("hint 0x27\n\t" // pacm
+ "hint 0xe " // autib1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x15));
+ } else {
+ asm("hint 0x27\n\t" // pacm
+ "hint 0xc " // autia1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x15));
+ }
+ } else {
+ if (isReturnAddressSignedWithBKey()) {
+ asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
+ } else {
+ asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
+ }
+ }
+ *referenceAuthedLinkRegister = x17;
#endif
+ }
+
+ bool isReturnAddressSigned() const {
+ return _registers.__ra_sign.__state & 1;
+ }
+ bool isReturnAddressSignedWithPC() const {
+ return _registers.__ra_sign.__state & 2;
+ }
+ bool isReturnAddressSignedWithBKey() const {
+ return _registers.__ra_sign.__use_b_key;
+ }
private:
+#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ void abortCrossRASigning() const {
+ // We should never go here since non-null RA signed state is either set
+ // by architecture-specific __unw_getcontext or by stepWithDwarf which
+ // already contains a corresponding check and should have already
+ // emitted the UNW_ECROSSRASIGNING error.
+ _LIBUNWIND_ABORT("UNW_ECROSSRASIGNING");
+ }
+#endif
+
uint64_t lazyGetVG() const;
void zaDisable() const {
@@ -1945,7 +2075,12 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
uint64_t __lr = 0; // Link register x30
uint64_t __sp = 0; // Stack pointer x31
uint64_t __pc = 0; // Program counter
- uint64_t __ra_sign_state = 0; // RA sign state register
+ struct RASign {
+ uint64_t __state = 0; // RA sign state register
+ uint64_t __second_modifier = 0; // Additional modifier used for RA
+ // signing with FEAT_PAuth_LR
+ uint64_t __use_b_key = 0; // 0 for IA key, 1 for IB key
+ } __ra_sign;
};
struct Misc {
@@ -1971,14 +2106,13 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
static_assert((check_fit<Registers_arm64, unw_context_t>::does_fit),
"arm64 registers do not fit into unw_context_t");
memcpy(&_registers, registers, sizeof(_registers));
- static_assert(sizeof(GPRs) == 0x110,
- "expected VFP registers to be at offset 272");
+ static_assert(sizeof(GPRs) == 0x120,
+ "expected VFP registers to be at offset 288");
memcpy(_vectorHalfRegisters,
static_cast<const uint8_t *>(registers) + sizeof(GPRs),
sizeof(_vectorHalfRegisters));
_misc_registers.__vg = 0;
-#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
// We have to do some pointer authentication fixups after this copy,
// and as part of that we need to load the source pc without
// authenticating so that we maintain the signature for the resigning
@@ -1987,7 +2121,6 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
memmove(&pcRegister, ((uint8_t *)&_registers) + offsetof(GPRs, __pc),
sizeof(pcRegister));
setIP(pcRegister);
-#endif
}
inline Registers_arm64::Registers_arm64(const Registers_arm64 &other) {
@@ -2008,12 +2141,16 @@ inline bool Registers_arm64::validRegister(int regNum) const {
return true;
if (regNum == UNW_REG_SP)
return true;
+ if (regNum == UNW_AARCH64_RA_SIGN_STATE)
+ return true;
+ if (regNum == UNW_AARCH64_RA_SIGN_SECOND_MODIFIER)
+ return true;
+ if (regNum == UNW_AARCH64_RA_SIGN_USE_B_KEY)
+ return true;
if (regNum < 0)
return false;
if (regNum > 95)
return false;
- if (regNum == UNW_AARCH64_RA_SIGN_STATE)
- return true;
if (regNum == UNW_AARCH64_VG)
return true;
if ((regNum > 32) && (regNum < 64))
@@ -2041,7 +2178,11 @@ inline uint64_t Registers_arm64::getRegister(int regNum) const {
if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
return _registers.__sp;
if (regNum == UNW_AARCH64_RA_SIGN_STATE)
- return _registers.__ra_sign_state;
+ return _registers.__ra_sign.__state;
+ if (regNum == UNW_AARCH64_RA_SIGN_SECOND_MODIFIER)
+ return _registers.__ra_sign.__second_modifier;
+ if (regNum == UNW_AARCH64_RA_SIGN_USE_B_KEY)
+ return _registers.__ra_sign.__use_b_key;
if (regNum == UNW_AARCH64_FP)
return getFP();
if (regNum == UNW_AARCH64_LR)
@@ -2059,7 +2200,11 @@ inline void Registers_arm64::setRegister(int regNum, uint64_t value) {
else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
_registers.__sp = value;
else if (regNum == UNW_AARCH64_RA_SIGN_STATE)
- _registers.__ra_sign_state = value;
+ _registers.__ra_sign.__state = value;
+ else if (regNum == UNW_AARCH64_RA_SIGN_SECOND_MODIFIER)
+ _registers.__ra_sign.__second_modifier = value;
+ else if (regNum == UNW_AARCH64_RA_SIGN_USE_B_KEY)
+ _registers.__ra_sign.__use_b_key = value;
else if (regNum == UNW_AARCH64_FP)
setFP(value);
else if (regNum == UNW_AARCH64_LR)
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index afa0cae790377..60ef722ae08e5 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1065,7 +1065,7 @@ class UnwindCursor : public AbstractUnwindCursor{
const UnwindInfoSections §s,
uint32_t fdeSectionOffsetHint = 0);
int stepWithDwarfFDE(bool stage2) {
-#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+#if defined(_LIBUNWIND_TARGET_AARCH64)
typename R::reg_t rawPC = this->getReg(UNW_REG_IP);
typename R::link_reg_t pc;
_registers.loadAndAuthenticateLinkRegister(rawPC, &pc);
@@ -2735,7 +2735,8 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
#endif
typename R::link_reg_t pc;
-#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+#if defined(_LIBUNWIND_TARGET_AARCH64) && \
+ !(defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32))
_registers.loadAndAuthenticateLinkRegister(rawPC, &pc);
#else
pc = rawPC;
@@ -3305,7 +3306,8 @@ void UnwindCursor<A, R>::getInfo(unw_proc_info_t *info) {
template <typename A, typename R>
bool UnwindCursor<A, R>::getFunctionName(char *buf, size_t bufLen,
unw_word_t *offset) {
-#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+#if defined(_LIBUNWIND_TARGET_AARCH64) && \
+ !(defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32))
typename R::reg_t rawPC = this->getReg(UNW_REG_IP);
typename R::link_reg_t pc;
_registers.loadAndAuthenticateLinkRegister(rawPC, &pc);
diff --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c
index 7368b3cb80336..361bf6c3c1575 100644
--- a/libunwind/src/UnwindLevel1.c
+++ b/libunwind/src/UnwindLevel1.c
@@ -615,14 +615,58 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
unw_word_t result;
__unw_get_reg(cursor, UNW_REG_IP, &result);
-#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
- // If we are in an arm64e frame, then the PC should have been signed with the
- // sp
+#if defined(_LIBUNWIND_TARGET_AARCH64) && \
+ !(defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32))
{
- unw_word_t sp;
- __unw_get_reg(cursor, UNW_REG_SP, &sp);
- result = (unw_word_t)ptrauth_auth_data((void *)result,
- ptrauth_key_return_address, sp);
+ unw_word_t raSignState, raSignUseBKey;
+ __unw_get_reg(cursor, UNW_AARCH64_RA_SIGN_STATE, &raSignState);
+ __unw_get_reg(cursor, UNW_AARCH64_RA_SIGN_USE_B_KEY, &raSignUseBKey);
+
+ bool isReturnAddressSigned = (raSignState & 1);
+ bool isReturnAddressSignedWithPC = (raSignState & 2);
+
+ if (isReturnAddressSigned) {
+#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ // We should never go here since non-null RA signed state is either set
+ // by architecture-specific __unw_getcontext or by stepWithDwarf which
+ // already contains a corresponding check and should have already
+ // emitted the UNW_ECROSSRASIGNING error.
+ _LIBUNWIND_ABORT("UNW_ECROSSRASIGNING");
+#else
+ unw_word_t sp;
+ __unw_get_reg(cursor, UNW_REG_SP, &sp);
+
+ register uint64_t x17 __asm("x17") = result;
+ register uint64_t x16 __asm("x16") = sp;
+
+ if (isReturnAddressSignedWithPC) {
+ unw_word_t raSignSecondModifier;
+ __unw_get_reg(cursor, UNW_AARCH64_RA_SIGN_SECOND_MODIFIER,
+ &raSignSecondModifier);
+
+ register uint64_t x15 __asm("x15") = raSignSecondModifier;
+
+ if (raSignUseBKey) {
+ __asm__("hint 0x27\n\t" // pacm
+ "hint 0xe " // autib1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x15));
+ } else {
+ __asm__("hint 0x27\n\t" // pacm
+ "hint 0xc " // autia1716
+ : "+r"(x17)
+ : "r"(x16), "r"(x15));
+ }
+ } else {
+ if (raSignUseBKey) {
+ __asm__("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
+ } else {
+ __asm__("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
+ }
+ }
+ result = x17;
+#endif
+ }
}
#endif
diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index 76a80344034f7..a98b9cb85de3c 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -672,7 +672,8 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
ldp x8, x9, [x0, #0x040]
ldp x10,x11, [x0, #0x050]
ldp x12,x13, [x0, #0x060]
- ldp x14,x15, [x0, #0x070]
+ // x14 and x15 are used as scratch registers in this function
+ // and will be restored later.
// x16 and x17 were clobbered by the call into the unwinder, so no point in
// restoring them.
ldp x18,x19, [x0, #0x090]
@@ -683,42 +684,143 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
ldp x28,x29, [x0, #0x0E0]
#if defined(__ARM_FP) && __ARM_FP != 0
- ldp d0, d1, [x0, #0x110]
- ldp d2, d3, [x0, #0x120]
- ldp d4, d5, [x0, #0x130]
- ldp d6, d7, [x0, #0x140]
- ldp d8, d9, [x0, #0x150]
- ldp d10,d11, [x0, #0x160]
- ldp d12,d13, [x0, #0x170]
- ldp d14,d15, [x0, #0x180]
- ldp d16,d17, [x0, #0x190]
- ldp d18,d19, [x0, #0x1A0]
- ldp d20,d21, [x0, #0x1B0]
- ldp d22,d23, [x0, #0x1C0]
- ldp d24,d25, [x0, #0x1D0]
- ldp d26,d27, [x0, #0x1E0]
- ldp d28,d29, [x0, #0x1F0]
- ldr d30, [x0, #0x200]
- ldr d31, [x0, #0x208]
+ ldp d0, d1, [x0, #0x120]
+ ldp d2, d3, [x0, #0x130]
+ ldp d4, d5, [x0, #0x140]
+ ldp d6, d7, [x0, #0x150]
+ ldp d8, d9, [x0, #0x160]
+ ldp d10,d11, [x0, #0x170]
+ ldp d12,d13, [x0, #0x180]
+ ldp d14,d15, [x0, #0x190]
+ ldp d16,d17, [x0, #0x1A0]
+ ldp d18,d19, [x0, #0x1B0]
+ ldp d20,d21, [x0, #0x1C0]
+ ldp d22,d23, [x0, #0x1D0]
+ ldp d24,d25, [x0, #0x1E0]
+ ldp d26,d27, [x0, #0x1F0]
+ ldr d28, [x0, #0x200]
+ ldr d29, [x0, #0x208]
+ ldr d30, [x0, #0x210]
+ ldr d31, [x0, #0x218]
#endif
+
+ // The lr value stored in __pc might be signed with IA/IB key (as described
+ // by __ra_sign.__use_b_key field) and &__pc address as a modifier.
+ // If the lr value is signed, re-sign that with the corresponding key
+ // and initial signing scheme using __sp as a modifier and potentially
+ // involving a second modifier (as described by (__ra_sign.__state & 2)).
+
+ ldr x14, [x0, #0x0F8] // x14 = __sp
+ ldr x15, [x0, #0x110] // x15 = __ra_sign.__second_modifier
+ add x16, x0, #0x100 // x16 = &__pc
+ ldr x17, [x0, #0x100] // x17 = __pc
+
+ ldr x1, [x0, #0x108] // x1 = __ra_sign.__state
+ and x1, x1, #1
+ cbz x1, .Lresign_end
+
+ // lr needs resign
+ ldr x1, [x0, #0x108] // x1 = __ra_sign.__state
+ and x1, x1, #2
+ cbnz x1, .Lresign_with_pc
+
+ // lr needs resign without pc as a second modifier
+ ldr x1, [x0, #0x118] // x1 = __ra_sign.__use_b_key
+ cbnz x1, .Lresign_with_b_key
+
+ // lr needs resign with A key and without pc as a second modifier
+ hint 0xc // autia1716
+ mov x16, x14 // x16 = __sp
+ hint 0x8 // pacia1716
+ b .Lresign_end
+
+.Lresign_with_b_key:
+ // lr needs resign with B key and without pc as a second modifier
+ hint 0xe // autib1716
+ mov x16, x14 // x16 = __sp
+ hint 0xa // pacib1716
+ b .Lresign_end
+
+.Lresign_with_pc:
+ // lr needs resign with pc as a second modifier
+ ldr x1, [x0, #0x118] // x1 = __ra_sign.__use_b_key
+ cbnz x1, .Lresign_with_pc_b_key
+
+ // lr needs resign with A key and pc as a second modifier
+ hint 0xc // autia1716
+ mov x16, x14 // x16 = __sp
+ hint 0x27 // pacm
+ hint 0x8 // pacia1716
+ b .Lresign_end
+
+.Lresign_with_pc_b_key:
+ // lr needs resign with B key and pc as a second modifier
+ hint 0xe // autib1716
+ mov x16, x14 // x16 = __sp
+ hint 0x27 // pacm
+ hint 0xa // pacib1716
+ b .Lresign_end
+
+.Lresign_end:
+ mov lr, x17 // assign final lr value
+ ldp x14,x15, [x0, #0x070] // restore x14,x15
+ ldr x16, [x0, #0x110] // second modifier for auti{a|b}sp with PACM
+
// Finally, restore sp. This must be done after the last read from the
// context struct, because it is allocated on the stack, and an exception
// could clobber the de-allocated portion of the stack after sp has been
// restored.
+ ldr x17, [x0, #0x0F8] // load sp into scratch
- ldr x16, [x0, #0x0F8] // load sp into scratch
- ldr lr, [x0, #0x100] // restore pc into lr
+ ldr x1, [x0, #0x108] // x1 = __ra_sign.__state
+ and x1, x1, #1
+ cbz x1, .Lauth_end
-#if __has_feature(ptrauth_calls)
- // The LR is signed with its address inside the register state. Time
- // to resign to be a regular ROP protected signed pointer
- add x1, x0, #0x100
- autib lr, x1
- pacib lr, x16 // signed the scratch register for sp
-#endif
+ // lr is signed
+ ldr x1, [x0, #0x108] // x1 = __ra_sign.__state
+ and x1, x1, #2
+ cbnz x1, .Lauth_with_pc
+
+ // lr is signed without pc as a second modifier
+ ldr x1, [x0, #0x118] // x1 = __ra_sign.__use_b_key
+ cbnz x1, .Lauth_with_b_key
+
+ // lr is signed with A key and without pc as a second modifier
+ ldp x0, x1, [x0, #0x000] // restore x0,x1
+ mov sp,x17 // restore sp
+ hint 0x1d // autiasp
+ ret x30
+
+.Lauth_with_b_key:
+ // lr is signed with B key and without pc as a second modifier
+ ldp x0, x1, [x0, #0x000] // restore x0,x1
+ mov sp,x17 // restore sp
+ hint 0x1f // autibsp
+ ret x30
+.Lauth_with_pc:
+ // lr is signed with pc as a second modifier
+ ldr x1, [x0, #0x118] // x1 = __ra_sign.__use_b_key
+ cbnz x1, .Lauth_with_pc_b_key
+
+ // lr is signed with A key and pc as a second modifier
+ ldp x0, x1, [x0, #0x000] // restore x0,x1
+ mov sp,x17 // restore sp
+ hint 0x27 // pacm
+ hint 0x1d // autiasp
+ ret x30
+
+.Lauth_with_pc_b_key:
+ // lr is signed with B key and pc as a second modifier
+ ldp x0, x1, [x0, #0x000] // restore x0,x1
+ mov sp,x17 // restore sp
+ hint 0x27 // pacm
+ hint 0x1f // autibsp
+ ret x30
+
+.Lauth_end:
ldp x0, x1, [x0, #0x000] // restore x0,x1
- mov sp,x16 // restore sp
+ mov sp,x17 // restore sp
#if defined(__ARM_FEATURE_GCS_DEFAULT)
// If GCS is enabled we need to push the address we're returning to onto the
// GCS stack. We can't just return using br, as there won't be a BTI landing
@@ -729,12 +831,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
gcspushm x30
Lnogcs:
#endif
-
-#if __has_feature(ptrauth_calls)
- retab
-#else
ret x30 // jump to pc
-#endif
#elif defined(__arm__) && !defined(__APPLE__)
diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S
index f988fd461def1..252353b7861f7 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -769,6 +769,20 @@ LnoR2Fix:
#define __has_feature(__feature) 0
#endif
+#ifdef __ARM_FEATURE_PAC_DEFAULT
+# define UNW_ARM_FEATURE_PAC_DEFAULT __ARM_FEATURE_PAC_DEFAULT
+#else
+# if __has_feature(ptrauth_returns)
+# define UNW_ARM_FEATURE_PAC_DEFAULT 2
+# else
+# define UNW_ARM_FEATURE_PAC_DEFAULT 0
+# endif
+#endif
+
+#define UNW_RA_SIGNED ((UNW_ARM_FEATURE_PAC_DEFAULT & 3) != 0)
+#define UNW_RA_SIGNED_WITH_B_KEY ((UNW_ARM_FEATURE_PAC_DEFAULT & 2) != 0)
+#define UNW_RA_SIGNED_WITH_PC ((UNW_ARM_FEATURE_PAC_DEFAULT & 8) != 0)
+
//
// extern int __unw_getcontext(unw_context_t* thread_state)
//
@@ -778,8 +792,16 @@ LnoR2Fix:
.p2align 2
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
-#if __has_feature(ptrauth_calls)
- pacibsp
+#if UNW_RA_SIGNED
+# if UNW_RA_SIGNED_WITH_PC
+ hint 0x27 // pacm
+.L__unw_getcontext_start:
+# endif
+# if UNW_RA_SIGNED_WITH_B_KEY
+ hint 0x1b // pacibsp
+# else
+ hint 0x19 // paciasp
+# endif
#endif
stp x0, x1, [x0, #0x000]
@@ -801,33 +823,69 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
mov x1,sp
str x1, [x0, #0x0F8]
str x30, [x0, #0x100] // store return address as pc
+
+ // Fill subfields of __ra_sign field
+#if UNW_RA_SIGNED
+# if UNW_RA_SIGNED_WITH_PC
+ adrp x16, .L__unw_getcontext_start
+ add x16, x16, :lo12:.L__unw_getcontext_start
+ str x16, [x0, #0x110] // __second_modifier = x16
+# else
+ str xzr, [x0, #0x110] // __second_modifier = 0
+# endif
+
+ mov x16, #1
+ str x16, [x0, #0x108] // __state = 1
+
+# if UNW_RA_SIGNED_WITH_B_KEY
+ str x16, [x0, #0x118] // __use_b_key = 1
+# else
+ str xzr, [x0, #0x118] // __use_b_key = 0
+# endif
+
+ ldr x16, [x0, #0x080] // restore x16 after clobber
+#else
+ str xzr, [x0, #0x108] // __state = 0
+ str xzr, [x0, #0x110] // __second_modifier = 0
+ str xzr, [x0, #0x118] // __use_b_key = 0
+#endif
+
// skip cpsr
#if defined(__ARM_FP) && __ARM_FP != 0
- stp d0, d1, [x0, #0x110]
- stp d2, d3, [x0, #0x120]
- stp d4, d5, [x0, #0x130]
- stp d6, d7, [x0, #0x140]
- stp d8, d9, [x0, #0x150]
- stp d10,d11, [x0, #0x160]
- stp d12,d13, [x0, #0x170]
- stp d14,d15, [x0, #0x180]
- stp d16,d17, [x0, #0x190]
- stp d18,d19, [x0, #0x1A0]
- stp d20,d21, [x0, #0x1B0]
- stp d22,d23, [x0, #0x1C0]
- stp d24,d25, [x0, #0x1D0]
- stp d26,d27, [x0, #0x1E0]
- stp d28,d29, [x0, #0x1F0]
- str d30, [x0, #0x200]
- str d31, [x0, #0x208]
+ stp d0, d1, [x0, #0x120]
+ stp d2, d3, [x0, #0x130]
+ stp d4, d5, [x0, #0x140]
+ stp d6, d7, [x0, #0x150]
+ stp d8, d9, [x0, #0x160]
+ stp d10,d11, [x0, #0x170]
+ stp d12,d13, [x0, #0x180]
+ stp d14,d15, [x0, #0x190]
+ stp d16,d17, [x0, #0x1A0]
+ stp d18,d19, [x0, #0x1B0]
+ stp d20,d21, [x0, #0x1C0]
+ stp d22,d23, [x0, #0x1D0]
+ stp d24,d25, [x0, #0x1E0]
+ stp d26,d27, [x0, #0x1F0]
+ str d28, [x0, #0x200]
+ str d29, [x0, #0x208]
+ str d30, [x0, #0x210]
+ str d31, [x0, #0x218]
#endif
mov x0, #0 // return UNW_ESUCCESS
-#if __has_feature(ptrauth_calls)
- retab
-#else
- ret
+#if UNW_RA_SIGNED
+# if UNW_RA_SIGNED_WITH_PC
+ adrp x16, .L__unw_getcontext_start
+ add x16, x16, :lo12:.L__unw_getcontext_start
+ hint 0x27 // pacm
+# endif
+# if UNW_RA_SIGNED_WITH_B_KEY
+ hint 0x1f // autibsp
+# else
+ hint 0x1d // autiasp
+# endif
#endif
+ ret
//
// extern "C" int64_t __libunwind_Registers_arm64_za_disable()
diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index b3036396c379d..9fdeca0cdbe4b 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -162,6 +162,17 @@ _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
_LIBUNWIND_ABORT("Bad unwind through arm64e");
}
}
+#elif defined(_LIBUNWIND_TARGET_AARCH64) && \
+ !(defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32))
+ // We expect IP register value to be signed only for a full-fledged
+ // PAuth ABI such as Apple's arm64e or Linux's pauthtest. Otherwise,
+ // the value to be assigned to the IP register is an unsigned pointer,
+ // so we need to update RA sign info and mark the pointer as unsigned.
+ // This prevents attempts of unsigned pointer authentication in case
+ // if previously a signed RA was stored in the IP register field.
+ co->setReg(UNW_AARCH64_RA_SIGN_STATE, 0);
+ co->setReg(UNW_AARCH64_RA_SIGN_SECOND_MODIFIER, 0);
+ co->setReg(UNW_AARCH64_RA_SIGN_USE_B_KEY, 0);
#endif
// If the original call expects stack adjustment, perform this now.
>From a9430a2f4fdb449cd460702dc343853d5bb668da Mon Sep 17 00:00:00 2001
From: Daniil Kovalev <dkovalev at accesssoftek.com>
Date: Sun, 21 Dec 2025 23:47:33 +0300
Subject: [PATCH 2/2] Check signing scheme flags integrity
---
libunwind/include/libunwind.h | 10 +-
libunwind/src/DwarfInstructions.hpp | 25 +-
libunwind/src/DwarfParser.hpp | 2 +-
libunwind/src/Registers.hpp | 516 ++++++++++++++++++-------
libunwind/src/UnwindLevel1.c | 140 ++++---
libunwind/src/UnwindRegistersRestore.S | 190 +++++----
libunwind/src/UnwindRegistersSave.S | 69 ++--
libunwind/src/libunwind.cpp | 7 +-
8 files changed, 645 insertions(+), 314 deletions(-)
diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h
index 6a56548867642..d711b0cccbfcb 100644
--- a/libunwind/include/libunwind.h
+++ b/libunwind/include/libunwind.h
@@ -642,11 +642,13 @@ enum {
// reserved block
UNW_AARCH64_RA_SIGN_STATE = 34,
- // The following two registers are not real Dwarf registers and use numbers
- // which are not occupied by real Dwarf registers according to
+ // Warning: the two registers below are intended for internal libunwind use
+ // only and should not be used by external code. These are not real Dwarf
+ // registers and they use numbers which are not occupied by real Dwarf
+ // registers according to
// https://github.com/ARM-software/abi-aa/blob/2025Q1/aadwarf64/aadwarf64.rst
- UNW_AARCH64_RA_SIGN_SECOND_MODIFIER = 128,
- UNW_AARCH64_RA_SIGN_USE_B_KEY = 129,
+ UNW_AARCH64_RA_SIGNING_SCHEME_SECOND_MODIFIER = 128,
+ UNW_AARCH64_RA_SIGNING_SCHEME_FLAGS = 129,
// FP/vector registers
UNW_AARCH64_V0 = 64,
diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp
index ad1555db3abd2..2d5b50031c924 100644
--- a/libunwind/src/DwarfInstructions.hpp
+++ b/libunwind/src/DwarfInstructions.hpp
@@ -75,8 +75,8 @@ class DwarfInstructions {
__builtin_unreachable();
}
#if defined(_LIBUNWIND_TARGET_AARCH64)
- static pint_t getRASignState(A &addressSpace, const R ®isters, pint_t cfa,
- const PrologInfo &prolog);
+ static pint_t getRASignedState(A &addressSpace, const R ®isters,
+ pint_t cfa, const PrologInfo &prolog);
#endif
};
@@ -174,9 +174,8 @@ v128 DwarfInstructions<A, R>::getSavedVectorRegister(
}
#if defined(_LIBUNWIND_TARGET_AARCH64)
template <typename A, typename R>
-typename A::pint_t
-DwarfInstructions<A, R>::getRASignState(A &addressSpace, const R ®isters,
- pint_t cfa, const PrologInfo &prolog) {
+typename A::pint_t DwarfInstructions<A, R>::getRASignedState(
+ A &addressSpace, const R ®isters, pint_t cfa, const PrologInfo &prolog) {
auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE];
if (regloc.location == CFI_Parser<A>::kRegisterUnused)
return static_cast<pint_t>(regloc.value);
@@ -284,20 +283,16 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace,
// using the v8.3 pointer authentication extensions. In order to
// store signed return address in the registers context structure, we
// need to save the signing scheme for this address.
- pint_t raSignState = getRASignState(addressSpace, registers, cfa, prolog);
- bool isReturnAddressSigned = (raSignState & 1);
- if ((R::getArch() == REGISTERS_ARM64) && isReturnAddressSigned &&
+ pint_t raSignState =
+ getRASignedState(addressSpace, registers, cfa, prolog);
+ if ((R::getArch() == REGISTERS_ARM64) && raSignState != 0 &&
returnAddress != 0) {
#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
return UNW_ECROSSRASIGNING;
#else
- newRegisters.setRegister(UNW_AARCH64_RA_SIGN_STATE, raSignState);
- if (newRegisters.isReturnAddressSignedWithPC()) {
- newRegisters.setRegister(UNW_AARCH64_RA_SIGN_SECOND_MODIFIER,
- prolog.ptrAuthDiversifier);
- }
- newRegisters.setRegister(UNW_AARCH64_RA_SIGN_USE_B_KEY,
- cieInfo.addressesSignedWithBKey ? 1 : 0);
+ newRegisters.setRASigningScheme(
+ raSignState, cieInfo.addressesSignedWithBKey,
+ prolog.ptrAuthDiversifier);
#endif
}
#endif
diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp
index 2b04ae2831f9a..1b657f290b3ac 100644
--- a/libunwind/src/DwarfParser.hpp
+++ b/libunwind/src/DwarfParser.hpp
@@ -97,7 +97,7 @@ class CFI_Parser {
uint32_t spExtraArgSize;
RegisterLocation savedRegisters[kMaxRegisterNumber + 1];
#if defined(_LIBUNWIND_TARGET_AARCH64)
- pint_t ptrAuthDiversifier;
+ pint_t ptrAuthDiversifier = 0;
#endif
enum class InitializeTime { kLazy, kNormal };
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 2245ccba61e4f..69c057758c944 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1842,6 +1842,78 @@ extern "C" void *__libunwind_shstk_get_jump_target() {
}
#endif
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
+
+#define STRING_IMPL(x) #x
+#define STRING(x) STRING_IMPL(x)
+
+#define RUN_IF_PAUTH_FEATURE_PRESENT(scratchReg, code) \
+ "mrs " #scratchReg ", ID_AA64ISAR1_EL1" "\n\t" \
+ "lsr " #scratchReg ", " #scratchReg ", #24" "\n\t" \
+ "ands " #scratchReg ", " #scratchReg ", #255" "\n\t" \
+ "cbnz " #scratchReg ", .Lcheck_pac_code" STRING(__LINE__) "\n\t" \
+ "mrs " #scratchReg ", ID_AA64ISAR2_EL1" "\n\t" \
+ "lsr " #scratchReg ", " #scratchReg ", #8" "\n\t" \
+ "ands " #scratchReg ", " #scratchReg ", #15" "\n\t" \
+ "cbnz " #scratchReg ", .Lcheck_pac_code" STRING(__LINE__) "\n\t" \
+ "b .Lcheck_pac_end" STRING(__LINE__) "\n\t" \
+ ".Lcheck_pac_code" STRING(__LINE__) ":" "\n\t" \
+ code "\n\t" \
+ ".Lcheck_pac_end" STRING(__LINE__) ":" "\n\t"
+
+// The '0xc470 + KeyID' trap code is used by LLVM codegen to abort execution
+// on auth failure w/o FPAC. Mimic this behavior assuming GA key ID is 4.
+#define PACGA_TRAP "brk #0xc474\n\t"
+
+#define CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY(schemeReg, schemePacReg, \
+ modifierReg, scratchReg) \
+ RUN_IF_PAUTH_FEATURE_PRESENT( \
+ scratchReg, \
+ "pacga " #modifierReg ", " #schemeReg ", " #modifierReg "\n\t" \
+ "cmp " #modifierReg ", " #schemePacReg "\n\t" \
+ "b.eq .Lcheck_integrity_success_" STRING(__LINE__) "\n\t" \
+ PACGA_TRAP "\n\t" \
+ ".Lcheck_integrity_success_" STRING(__LINE__) ":" "\n\t" \
+ )
+
+#define CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI(schemeReg) \
+ "cmp " #schemeReg ", 5" "\n\t" \
+ "b.eq .Lcheck_integrity_for_pauthabi_success_" STRING(__LINE__) "\n\t" \
+ PACGA_TRAP "\n\t" \
+ ".Lcheck_integrity_for_pauthabi_success_" STRING(__LINE__) ":" "\n\t" \
+
+#define SIGNING_SCHEME_FLAGS_SWITCH(schemeReg, codeIf0, codeIf1, \
+ codeIf3, codeIf5, codeIf7) \
+ "cmp " #schemeReg ", #0" "\n\t" \
+ "b.ne .Lswitch_1_" STRING(__LINE__) "\n\t" \
+ codeIf0 "\n\t" \
+ "b .Lswitch_end_" STRING(__LINE__) "\n\t" \
+ ".Lswitch_1_" STRING(__LINE__) ":" "\n\t" \
+ "cmp " #schemeReg ", #1" "\n\t" \
+ "b.ne .Lswitch_3_" STRING(__LINE__) "\n\t" \
+ codeIf1 "\n\t" \
+ "b .Lswitch_end_" STRING(__LINE__) "\n\t" \
+ ".Lswitch_3_" STRING(__LINE__) ":" "\n\t" \
+ "cmp " #schemeReg ", #3" "\n\t" \
+ "b.ne .Lswitch_5_" STRING(__LINE__) "\n\t" \
+ codeIf3 "\n\t" \
+ "b .Lswitch_end_" STRING(__LINE__) "\n\t" \
+ ".Lswitch_5_" STRING(__LINE__) ":" "\n\t" \
+ "cmp " #schemeReg ", #5" "\n\t" \
+ "b.ne .Lswitch_7_" STRING(__LINE__) "\n\t" \
+ codeIf5 "\n\t" \
+ "b .Lswitch_end_" STRING(__LINE__) "\n\t" \
+ ".Lswitch_7_" STRING(__LINE__) ":" "\n\t" \
+ "cmp " #schemeReg ", #7" "\n\t" \
+ "b.ne .Lswitch_unexpected" STRING(__LINE__) "\n\t" \
+ codeIf7 "\n\t" \
+ "b .Lswitch_end_" STRING(__LINE__) "\n\t" \
+ ".Lswitch_unexpected" STRING(__LINE__) ":" "\n\t" \
+ PACGA_TRAP "\n\t" \
+ ".Lswitch_end_" STRING(__LINE__) ":" "\n\t"
+
+#endif
+
class _LIBUNWIND_HIDDEN Registers_arm64 {
public:
Registers_arm64() = default;
@@ -1878,108 +1950,112 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
uint64_t getSP() const { return _registers.__sp; }
void setSP(uint64_t value) { _registers.__sp = value; }
- uint64_t getIP() const {
+ uint64_t getIP() const {
uint64_t value = _registers.__pc;
- if (!isReturnAddressSigned())
- return value;
-
-#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
- abortCrossRASigning();
-#else
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
// Note the value of the PC was signed to its address in the register state
// but everyone else expects it to be sign by the SP, so convert on return.
register uint64_t x17 __asm("x17") = value;
register uint64_t x16 __asm("x16") =
reinterpret_cast<uint64_t>(&_registers.__pc);
- register uint64_t x14 __asm("x14") = getSP();
- if (isReturnAddressSignedWithPC()) {
- register uint64_t x15 __asm("x15") =
- _registers.__ra_sign.__second_modifier;
- if (isReturnAddressSignedWithBKey()) {
- asm("hint 0xe \n\t" // autib1716
- "mov x16, x14\n\t"
- "hint 0x27 \n\t" // pacm
- "hint 0xa " // pacib1716
- : "+r"(x17)
- : "r"(x16), "r"(x15), "r"(x14));
- } else {
- asm("hint 0xc \n\t" // autia1716
- "mov x16, x14\n\t"
- "hint 0x27 \n\t" // pacm
- "hint 0x8 " // pacia1716
- : "+r"(x17)
- : "r"(x16), "r"(x15), "r"(x14));
- }
- } else {
- if (isReturnAddressSignedWithBKey()) {
- asm("hint 0xe \n\t" // autib1716
- "mov x16, x14\n\t"
- "hint 0xa " // pacib1716
- : "+r"(x17)
- : "r"(x16), "r"(x14));
- } else {
- asm("hint 0xc \n\t" // autia1716
- "mov x16, x14\n\t"
- "hint 0x8 " // pacia1716
- : "+r"(x17)
- : "r"(x16), "r"(x14));
- }
- }
+ register uint64_t x15 __asm("x15") =
+ _registers.__ra_signing_scheme.__second_modifier;
+ register uint64_t x14 __asm("x14") = _registers.__sp;
+ register uint64_t x13 __asm("x13") =
+ reinterpret_cast<uint64_t>(&_registers.__ra_signing_scheme.__flags);
+ register uint64_t x12 __asm("x12") = _registers.__ra_signing_scheme.__flags;
+ register uint64_t x11 __asm("x11") =
+ _registers.__ra_signing_scheme.__flags_pac;
+
+ asm(CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY(
+ /*schemeReg=*/x12, /*schemePacReg=*/x11,
+ /*modifierReg=*/x13, /*scratchReg=*/x10)
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI(/*schemeReg=*/x12)
+#endif
+ SIGNING_SCHEME_FLAGS_SWITCH(
+ /*schemeReg=*/x12,
+ /*codeIf0=*/"",
+ /*codeIf1=*/"hint 0xc \n\t" // autia1716
+ "mov x16, x14\n\t"
+ "hint 0x8 \n\t", // pacia1716
+ /*codeIf3=*/"hint 0x27 \n\t" // pacm
+ "hint 0xc \n\t" // autia1716
+ "mov x16, x14\n\t"
+ "hint 0x27 \n\t" // pacm
+ "hint 0x8 \n\t", // pacia1716
+ /*codeIf5=*/"hint 0xe \n\t" // autib1716
+ "mov x16, x14\n\t"
+ "hint 0xa \n\t", // pacib1716
+ /*codeIf7=*/"hint 0x27 \n\t" // pacm
+ "hint 0xe \n\t" // autib1716
+ "mov x16, x14\n\t"
+ "hint 0x27 \n\t" // pacm
+ "hint 0xa \n\t" // pacib1716
+ )
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(x14), "r"(x13), "r"(x12), "r"(x11)
+ );
return x17;
+#else
+ if (_registers.__ra_signing_scheme.__flags != 0)
+ abortCrossRASigningSchemeing();
+ return value;
#endif
}
void setIP(uint64_t value) {
- if (!isReturnAddressSigned()) {
- _registers.__pc = value;
- return;
- }
-
-#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
- abortCrossRASigning();
-#else
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
// Note the value which was set should have been signed with the SP.
// We then resign with the slot we are being stored in to so that both SP
// and LR can't be spoofed at the same time.
register uint64_t x17 __asm("x17") = value;
- register uint64_t x16 __asm("x16") = getSP();
+ register uint64_t x16 __asm("x16") = _registers.__sp;
+ register uint64_t x15 __asm("x15") =
+ _registers.__ra_signing_scheme.__second_modifier;
register uint64_t x14 __asm("x14") =
reinterpret_cast<uint64_t>(&_registers.__pc);
- if (isReturnAddressSignedWithPC()) {
- register uint64_t x15 __asm("x15") =
- _registers.__ra_sign.__second_modifier;
- if (isReturnAddressSignedWithBKey()) {
- asm("hint 0x27 \n\t" // pacm
- "hint 0xe \n\t" // autib1716
- "mov x16, x14\n\t"
- "hint 0xa " // pacib1716
- : "+r"(x17)
- : "r"(x16), "r"(x15), "r"(x14));
- } else {
- asm("hint 0x27 \n\t" // pacm
- "hint 0xc \n\t" // autia1716
- "mov x16, x14\n\t"
- "hint 0x8 " // pacia1716
- : "+r"(x17)
- : "r"(x16), "r"(x15), "r"(x14));
- }
- } else {
- if (isReturnAddressSignedWithBKey()) {
- asm("hint 0xe \n\t" // autib1716
- "mov x16, x14\n\t"
- "hint 0xa " // pacib1716
- : "+r"(x17)
- : "r"(x16), "r"(x14));
- } else {
- asm("hint 0xc \n\t" // autia1716
- "mov x16, x14\n\t"
- "hint 0x8 " // pacia1716
- : "+r"(x17)
- : "r"(x16), "r"(x14));
- }
- }
+ register uint64_t x13 __asm("x13") =
+ reinterpret_cast<uint64_t>(&_registers.__ra_signing_scheme.__flags);
+ register uint64_t x12 __asm("x12") = _registers.__ra_signing_scheme.__flags;
+ register uint64_t x11 __asm("x11") =
+ _registers.__ra_signing_scheme.__flags_pac;
+
+ asm(CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY(
+ /*schemeReg=*/x12, /*schemePacReg=*/x11, /*modifierReg=*/x13,
+ /*scratchReg=*/x10)
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI(/*schemeReg=*/x12)
+#endif
+ SIGNING_SCHEME_FLAGS_SWITCH(
+ /*schemeReg=*/x12,
+ /*codeIf0=*/"",
+ /*codeIf1=*/"hint 0xc \n\t" // autia1716
+ "mov x16, x14\n\t"
+ "hint 0x8 \n\t", // pacia1716
+ /*codeIf3=*/"hint 0x27 \n\t" // pacm
+ "hint 0xc \n\t" // autia1716
+ "mov x16, x14\n\t"
+ "hint 0x27 \n\t" // pacm
+ "hint 0x8 \n\t", // pacia1716
+ /*codeIf5=*/"hint 0xe \n\t" // autib1716
+ "mov x16, x14\n\t"
+ "hint 0xa \n\t", // pacib1716
+ /*codeIf7=*/"hint 0x27 \n\t" // pacm
+ "hint 0xe \n\t" // autib1716
+ "mov x16, x14\n\t"
+ "hint 0x27 \n\t" // pacm
+ "hint 0xa \n\t" // pacib1716
+ )
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(x14), "r"(x13), "r"(x12), "r"(x11)
+ );
_registers.__pc = x17;
+#else
+ if (_registers.__ra_signing_scheme.__flags != 0)
+ abortCrossRASigningSchemeing();
+ _registers.__pc = value;
#endif
}
@@ -1995,60 +2071,172 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
void
loadAndAuthenticateLinkRegister(reg_t inplaceAuthedLinkRegister,
link_reg_t *referenceAuthedLinkRegister) {
- if (!isReturnAddressSigned()) {
- *referenceAuthedLinkRegister = inplaceAuthedLinkRegister;
- return;
- }
-
-#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
- abortCrossRASigning();
-#else
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
register reg_t x17 __asm("x17") = inplaceAuthedLinkRegister;
- register reg_t x16 __asm("x16") = getSP();
- if (isReturnAddressSignedWithPC()) {
- register reg_t x15 __asm("x15") = _registers.__ra_sign.__second_modifier;
- if (isReturnAddressSignedWithBKey()) {
- asm("hint 0x27\n\t" // pacm
- "hint 0xe " // autib1716
- : "+r"(x17)
- : "r"(x16), "r"(x15));
- } else {
- asm("hint 0x27\n\t" // pacm
- "hint 0xc " // autia1716
- : "+r"(x17)
- : "r"(x16), "r"(x15));
- }
- } else {
- if (isReturnAddressSignedWithBKey()) {
- asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
- } else {
- asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
- }
- }
+ register reg_t x16 __asm("x16") = _registers.__sp;
+ register uint64_t x15 __asm("x15") =
+ _registers.__ra_signing_scheme.__second_modifier;
+ register uint64_t x13 __asm("x13") =
+ reinterpret_cast<uint64_t>(&_registers.__ra_signing_scheme.__flags);
+ register uint64_t x12 __asm("x12") = _registers.__ra_signing_scheme.__flags;
+ register uint64_t x11 __asm("x11") =
+ _registers.__ra_signing_scheme.__flags_pac;
+
+ asm(CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY(
+ /*schemeReg=*/x12, /*schemePacReg=*/x11, /*modifierReg=*/x13,
+ /*scratchReg=*/x10)
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI(/*schemeReg=*/x12)
+#endif
+ SIGNING_SCHEME_FLAGS_SWITCH(
+ /*schemeReg=*/x12,
+ /*codeIf0=*/"",
+ /*codeIf1=*/"hint 0xc \n\t", // autia1716
+ /*codeIf3=*/"hint 0x27\n\t" // pacm
+ "hint 0xc \n\t", // autia1716
+ /*codeIf5=*/"hint 0xe \n\t", // autib1716
+ /*codeIf7=*/"hint 0x27\n\t" // pacm
+ "hint 0xe \n\t" // autib1716
+ )
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(x13), "r"(x12), "r"(x11)
+ );
*referenceAuthedLinkRegister = x17;
+#else
+ if (_registers.__ra_signing_scheme.__flags != 0)
+ abortCrossRASigningSchemeing();
+ *referenceAuthedLinkRegister = inplaceAuthedLinkRegister;
#endif
}
- bool isReturnAddressSigned() const {
- return _registers.__ra_sign.__state & 1;
- }
- bool isReturnAddressSignedWithPC() const {
- return _registers.__ra_sign.__state & 2;
- }
- bool isReturnAddressSignedWithBKey() const {
- return _registers.__ra_sign.__use_b_key;
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ void setRASigningScheme(uint64_t raSignState,
+ bool isRASigningSchemeedWithBKey,
+ uint64_t secondModifier) {
+ register uint64_t x16 __asm("x16") =
+ raSignState + (isRASigningSchemeedWithBKey ? 4 : 0);
+ register uint64_t x17 __asm("x17") =
+ reinterpret_cast<uint64_t>(&_registers.__ra_signing_scheme.__flags);
+ register uint64_t x15 __asm("x15") = secondModifier;
+
+ asm(
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI(/*schemeReg=*/x16)
+#endif
+ SIGNING_SCHEME_FLAGS_SWITCH(
+ /*schemeReg=*/x16,
+ /*codeIf0=*/"cbnz x15, .Lsetscheme_unexpected\n\t",
+ /*codeIf1=*/"cbnz x15, .Lsetscheme_unexpected\n\t",
+ /*codeIf3=*/"cbz x15, .Lsetscheme_unexpected\n\t",
+ /*codeIf5=*/"cbnz x15, .Lsetscheme_unexpected\n\t",
+ /*codeIf7=*/"cbz x15, .Lsetscheme_unexpected\n\t"
+ )
+ "b .Lsetscheme_ok \n\t"
+ ".Lsetscheme_unexpected:\n\t"
+ PACGA_TRAP "\n\t"
+ ".Lsetscheme_ok: \n\t"
+ "str x16, [x17, #0] \n\t"
+ RUN_IF_PAUTH_FEATURE_PRESENT(
+ /*scratchReg=*/x14,
+ "pacga x16, x16, x17 \n\t"
+ "str x16, [x17, #8]\n\t"
+ )
+ "str x15, [x17, #16]\n\t"
+ :
+ : "r"(x17), "r"(x16), "r"(x15)
+ );
}
+#endif // defined(_LIBUNWIND_IS_NATIVE_ONLY)
private:
#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
- void abortCrossRASigning() const {
+ void abortCrossRASigningSchemeing() const {
// We should never go here since non-null RA signed state is either set
// by architecture-specific __unw_getcontext or by stepWithDwarf which
// already contains a corresponding check and should have already
// emitted the UNW_ECROSSRASIGNING error.
_LIBUNWIND_ABORT("UNW_ECROSSRASIGNING");
}
+#else
+ void setZeroSigningScheme() {
+ register uint64_t x17 __asm("x17") =
+ reinterpret_cast<uint64_t>(&_registers.__ra_signing_scheme.__flags);
+
+ asm(// Ensure that second modifier is zero
+ "ldr x16, [x17, #16]\n\t"
+ "cbz .Lsetzero_ok \n\t"
+ PACGA_TRAP "\n\t"
+ ".Lsetzero_ok: \n\t"
+ // Assign zero to signing scheme flags field
+ "str xzr, [x17, #0] \n\t"
+ // Compute PAC for signing scheme flags field
+ RUN_IF_PAUTH_FEATURE_PRESENT(
+ /*scratchReg=*/x14,
+ "pacga x16, xzr, x17 \n\t"
+ "str x16, [x17, #8]\n\t"
+ )
+ :
+ : "r"(x17)
+ );
+ }
+
+ void recomputeSigningSchemeFlagsPAC(const uint8_t *oldModifier) {
+ register uint64_t x17 __asm("x17") =
+ reinterpret_cast<uint64_t>(oldModifier);
+ register uint64_t x16 __asm("x16") = _registers.__ra_signing_scheme.__flags;
+ register uint64_t x15 __asm("x15") =
+ _registers.__ra_signing_scheme.__flags_pac;
+ register uint64_t x14 __asm("x14") =
+ reinterpret_cast<uint64_t>(&_registers.__ra_signing_scheme.__flags);
+ asm(CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY(
+ /*schemeReg=*/x16, /*schemePacReg=*/x15,
+ /*modifierReg=*/x17, /*scratchReg=*/x13)
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI(/*schemeReg=*/x16)
+#endif
+ RUN_IF_PAUTH_FEATURE_PRESENT(
+ /*scratchReg=*/x13,
+ "pacga x16, x16, x14 \n\t"
+ "str x16, [x14, #8]\n\t"
+ )
+ :
+ : "r"(x17), "r"(x16), "r"(x15), "r"(x14)
+ );
+ }
+
+ // Is PAuth is available, returns signing scheme flags value with embedded
+ // PAC computed with GA key and SP modifier. The PAC computed by PACGA only
+ // occupies most-significant 32 bits of 64-bit value, which are not
+ // overlapping with bits occupied by valid flags values.
+ // If PAuth is not available, just returns signing scheme flags.
+ uint64_t getRASigningSchemeFlags() const {
+ register uint64_t x17 __asm("x17") =
+ reinterpret_cast<uint64_t>(&_registers.__ra_signing_scheme.__flags);
+ register uint64_t x16 __asm("x16") = _registers.__ra_signing_scheme.__flags;
+ register uint64_t x15 __asm("x15") =
+ _registers.__ra_signing_scheme.__flags_pac;
+ register uint64_t x14 __asm("x14") = _registers.__sp;
+
+ // TODO: reuse code from CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY macro.
+ asm(
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI(/*schemeReg=*/x16)
#endif
+ RUN_IF_PAUTH_FEATURE_PRESENT(
+ /*scratchReg=*/x13,
+ "pacga x17, x16, x17 \n\t"
+ "cmp x17, x15 \n\t"
+ "b.eq .Lget_ra_scheme_success\n\t"
+ PACGA_TRAP "\n\t"
+ ".Lget_ra_scheme_success: \n\t"
+ "pacga x14, x16, x14 \n\t"
+ "orr x16, x14, x16 \n\t"
+ )
+ : "+r"(x16)
+ : "r"(x17), "r"(x15), "r"(x14));
+ return x16;
+ }
+#endif // !defined(_LIBUNWIND_IS_NATIVE_ONLY)
uint64_t lazyGetVG() const;
@@ -2075,12 +2263,25 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
uint64_t __lr = 0; // Link register x30
uint64_t __sp = 0; // Stack pointer x31
uint64_t __pc = 0; // Program counter
- struct RASign {
- uint64_t __state = 0; // RA sign state register
- uint64_t __second_modifier = 0; // Additional modifier used for RA
- // signing with FEAT_PAuth_LR
- uint64_t __use_b_key = 0; // 0 for IA key, 1 for IB key
- } __ra_sign;
+ struct RASigningScheme {
+ // Bitmask for RA signing parameters:
+ // - bit 0: is RA signed;
+ // - bit 1: is RA signed with second modifier;
+ // - bit 2: is RA signed with B key.
+ // Valid values: 0, 1, 3, 5, 7. Note that it does not directly
+ // correspond to any physical or Dwarf registers.
+ uint64_t __flags = 0;
+
+ // Pointer authentication code for the `__flags` value computed with GA
+ // key (if `pacga` instruction is available) and address diversity. It is
+ // crucial to check integrity of `__flags` since the signing scheme is
+ // defined at runtime and attacker can substitute both the pointer and the
+ // signing scheme.
+ uint64_t __flags_pac = 0;
+
+ // Additional modifier used for RA signing with FEAT_PAuth_LR.
+ uint64_t __second_modifier = 0;
+ } __ra_signing_scheme;
};
struct Misc {
@@ -2120,6 +2321,10 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
uint64_t pcRegister = 0;
memmove(&pcRegister, ((uint8_t *)&_registers) + offsetof(GPRs, __pc),
sizeof(pcRegister));
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ recomputeSigningSchemeFlagsPAC(
+ (const uint8_t *)registers + offsetof(GPRs, __ra_signing_scheme.__flags));
+#endif
setIP(pcRegister);
}
@@ -2130,6 +2335,9 @@ inline Registers_arm64::Registers_arm64(const Registers_arm64 &other) {
inline Registers_arm64 &
Registers_arm64::operator=(const Registers_arm64 &other) {
memmove(static_cast<void *>(this), &other, sizeof(*this));
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ recomputeSigningSchemeFlagsPAC((const uint8_t *)&other._registers.__ra_signing_scheme.__flags);
+#endif
// We perform this step to ensure that we correctly authenticate and re-sign
// the pc after the bitwise copy.
setIP(other.getIP());
@@ -2141,12 +2349,16 @@ inline bool Registers_arm64::validRegister(int regNum) const {
return true;
if (regNum == UNW_REG_SP)
return true;
+ // UNW_AARCH64_RA_SIGN_STATE is a Dwarf pseudo-register and is not stored in
+ // the context struct.
if (regNum == UNW_AARCH64_RA_SIGN_STATE)
+ return false;
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ if (regNum == UNW_AARCH64_RA_SIGNING_SCHEME_SECOND_MODIFIER)
return true;
- if (regNum == UNW_AARCH64_RA_SIGN_SECOND_MODIFIER)
- return true;
- if (regNum == UNW_AARCH64_RA_SIGN_USE_B_KEY)
+ if (regNum == UNW_AARCH64_RA_SIGNING_SCHEME_FLAGS)
return true;
+#endif
if (regNum < 0)
return false;
if (regNum > 95)
@@ -2177,12 +2389,12 @@ inline uint64_t Registers_arm64::getRegister(int regNum) const {
return getIP();
if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
return _registers.__sp;
- if (regNum == UNW_AARCH64_RA_SIGN_STATE)
- return _registers.__ra_sign.__state;
- if (regNum == UNW_AARCH64_RA_SIGN_SECOND_MODIFIER)
- return _registers.__ra_sign.__second_modifier;
- if (regNum == UNW_AARCH64_RA_SIGN_USE_B_KEY)
- return _registers.__ra_sign.__use_b_key;
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
+ if (regNum == UNW_AARCH64_RA_SIGNING_SCHEME_SECOND_MODIFIER)
+ return _registers.__ra_signing_scheme.__second_modifier;
+ if (regNum == UNW_AARCH64_RA_SIGNING_SCHEME_FLAGS)
+ return getRASigningSchemeFlags();
+#endif
if (regNum == UNW_AARCH64_FP)
return getFP();
if (regNum == UNW_AARCH64_LR)
@@ -2199,12 +2411,21 @@ inline void Registers_arm64::setRegister(int regNum, uint64_t value) {
setIP(value);
else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
_registers.__sp = value;
- else if (regNum == UNW_AARCH64_RA_SIGN_STATE)
- _registers.__ra_sign.__state = value;
- else if (regNum == UNW_AARCH64_RA_SIGN_SECOND_MODIFIER)
- _registers.__ra_sign.__second_modifier = value;
- else if (regNum == UNW_AARCH64_RA_SIGN_USE_B_KEY)
- _registers.__ra_sign.__use_b_key = value;
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ else if (regNum == UNW_AARCH64_RA_SIGNING_SCHEME_SECOND_MODIFIER ||
+ regNum == UNW_AARCH64_RA_SIGNING_SCHEME_FLAGS)
+ _LIBUNWIND_ABORT("Cannot change signing scheme for PAuth-enabled ABI");
+#else
+ else if (regNum == UNW_AARCH64_RA_SIGNING_SCHEME_SECOND_MODIFIER) {
+ assert(value == 0 && "Expect value 0 when called from __unw_set_reg");
+ _registers.__ra_signing_scheme.__second_modifier = 0;
+ } else if (regNum == UNW_AARCH64_RA_SIGNING_SCHEME_FLAGS) {
+ assert(value == 0 && "Expect value 0 when called from __unw_set_reg");
+ setZeroSigningScheme();
+ }
+#endif
+#endif
else if (regNum == UNW_AARCH64_FP)
setFP(value);
else if (regNum == UNW_AARCH64_LR)
@@ -2387,6 +2608,17 @@ inline v128 Registers_arm64::getVectorRegister(int) const {
inline void Registers_arm64::setVectorRegister(int, v128) {
_LIBUNWIND_ABORT("no arm64 vector register support yet");
}
+
+#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
+#undef SIGNING_SCHEME_FLAGS_SWITCH
+#undef CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI
+#undef CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY
+#undef PACGA_TRAP
+#undef RUN_IF_PAUTH_FEATURE_PRESENT
+#undef STRING
+#undef STRING_IMPL
+#endif
+
#endif // _LIBUNWIND_TARGET_AARCH64
#if defined(_LIBUNWIND_TARGET_ARM)
diff --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c
index 361bf6c3c1575..8c7a5f20fcc4f 100644
--- a/libunwind/src/UnwindLevel1.c
+++ b/libunwind/src/UnwindLevel1.c
@@ -616,57 +616,101 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
__unw_get_reg(cursor, UNW_REG_IP, &result);
#if defined(_LIBUNWIND_TARGET_AARCH64) && \
+ defined(_LIBUNWIND_IS_NATIVE_ONLY) && \
!(defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32))
{
- unw_word_t raSignState, raSignUseBKey;
- __unw_get_reg(cursor, UNW_AARCH64_RA_SIGN_STATE, &raSignState);
- __unw_get_reg(cursor, UNW_AARCH64_RA_SIGN_USE_B_KEY, &raSignUseBKey);
-
- bool isReturnAddressSigned = (raSignState & 1);
- bool isReturnAddressSignedWithPC = (raSignState & 2);
-
- if (isReturnAddressSigned) {
-#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
- // We should never go here since non-null RA signed state is either set
- // by architecture-specific __unw_getcontext or by stepWithDwarf which
- // already contains a corresponding check and should have already
- // emitted the UNW_ECROSSRASIGNING error.
- _LIBUNWIND_ABORT("UNW_ECROSSRASIGNING");
-#else
- unw_word_t sp;
- __unw_get_reg(cursor, UNW_REG_SP, &sp);
-
- register uint64_t x17 __asm("x17") = result;
- register uint64_t x16 __asm("x16") = sp;
-
- if (isReturnAddressSignedWithPC) {
- unw_word_t raSignSecondModifier;
- __unw_get_reg(cursor, UNW_AARCH64_RA_SIGN_SECOND_MODIFIER,
- &raSignSecondModifier);
-
- register uint64_t x15 __asm("x15") = raSignSecondModifier;
-
- if (raSignUseBKey) {
- __asm__("hint 0x27\n\t" // pacm
- "hint 0xe " // autib1716
- : "+r"(x17)
- : "r"(x16), "r"(x15));
- } else {
- __asm__("hint 0x27\n\t" // pacm
- "hint 0xc " // autia1716
- : "+r"(x17)
- : "r"(x16), "r"(x15));
- }
- } else {
- if (raSignUseBKey) {
- __asm__("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
- } else {
- __asm__("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
- }
- }
- result = x17;
+ unw_word_t raSigningSchemeFlagsWithPAC;
+ __unw_get_reg(cursor, UNW_AARCH64_RA_SIGNING_SCHEME_FLAGS,
+ &raSigningSchemeFlagsWithPAC);
+ unw_word_t raSigningSchemeFlags =
+ raSigningSchemeFlagsWithPAC & 0x00000000ffffffffull;
+ unw_word_t raSigningSchemeFlagsPAC =
+ raSigningSchemeFlagsWithPAC & 0xffffffff00000000ull;
+
+ unw_word_t sp;
+ __unw_get_reg(cursor, UNW_REG_SP, &sp);
+
+ unw_word_t raSigningSchemeSecondModifier;
+ __unw_get_reg(cursor, UNW_AARCH64_RA_SIGNING_SCHEME_SECOND_MODIFIER,
+ &raSigningSchemeSecondModifier);
+
+ register uint64_t x17 __asm("x17") = result;
+ register uint64_t x16 __asm("x16") = sp;
+ register uint64_t x15 __asm("x15") = raSigningSchemeSecondModifier;
+ register uint64_t x14 __asm("x14") = raSigningSchemeFlags;
+ register uint64_t x13 __asm("x13") = raSigningSchemeFlagsPAC;
+
+ // TODO: make similar code from Registers.hpp reusable and adopt it here.
+ __asm__(
+ // Check if PAuth feature is available. See also
+ // RUN_IF_PAUTH_FEATURE_PRESENT in Registers.hpp
+ "mrs x12, ID_AA64ISAR1_EL1 \n\t"
+ "lsr x12, x12, #24 \n\t"
+ "ands x12, x12, #255 \n\t"
+ "cbnz x12, .Lcheck_pac \n\t"
+ "mrs x12, ID_AA64ISAR2_EL1 \n\t"
+ "lsr x12, x12, #8 \n\t"
+ "ands x12, x12, #15 \n\t"
+ "cbnz x12, .Lcheck_pac \n\t"
+ "b .Lno_pauth \n\t"
+ ".Lcheck_pac: \n\t"
+
+ // See also CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY in Registers.hpp.
+ "pacga x12, x14, x16 \n\t"
+ "cmp x12, x13 \n\t"
+ "b.eq .Lpacga_success\n\t"
+ "brk #0xc474 \n\t"
+ ".Lpacga_success: \n\t"
+ ".Lno_pauth: \n\t"
+
+ // See also CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI
+ // in Registers.hpp.
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ "cmp x14, 5 \n\t"
+ "b.eq .Lpauthabi_success\n\t"
+ "brk #0xc474 \n\t"
+ ".Lpauthabi_success: \n\t"
#endif
- }
+
+ // See also SIGNING_SCHEME_FLAGS_SWITCH in Registers.hpp.
+ "cmp x14, #0 \n\t"
+ "b.ne .Lswitch_1 \n\t"
+ "b .Lswitch_end\n\t"
+
+ ".Lswitch_1: \n\t"
+ "cmp x14, #1 \n\t"
+ "b.ne .Lswitch_3 \n\t"
+ "hint 0xc \n\t" // autia1716
+ "b .Lswitch_end\n\t"
+
+ ".Lswitch_3: \n\t"
+ "cmp x14, #3 \n\t"
+ "b.ne .Lswitch_5 \n\t"
+ "hint 0x27 \n\t" // pacm
+ "hint 0xc \n\t" // autia1716
+ "b .Lswitch_end\n\t"
+
+ ".Lswitch_5: \n\t"
+ "cmp x14, #5 \n\t"
+ "b.ne .Lswitch_7 \n\t"
+ "hint 0xe \n\t" // autib1716
+ "b .Lswitch_end\n\t"
+
+ ".Lswitch_7: \n\t"
+ "cmp x14, #7 \n\t"
+ "b.ne .Lswitch_unexpected\n\t"
+ "hint 0x27 \n\t" // pacm
+ "hint 0xe \n\t" // autib1716
+ "b .Lswitch_end\n\t"
+
+ ".Lswitch_unexpected:\n\t"
+ "brk #0xc474 \n\t"
+
+ ".Lswitch_end:\n\t"
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(x14), "r"(x13)
+ );
+ result = x17;
}
#endif
diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index a98b9cb85de3c..e7a212acc7c1f 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -704,64 +704,97 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
ldr d31, [x0, #0x218]
#endif
- // The lr value stored in __pc might be signed with IA/IB key (as described
- // by __ra_sign.__use_b_key field) and &__pc address as a modifier.
- // If the lr value is signed, re-sign that with the corresponding key
- // and initial signing scheme using __sp as a modifier and potentially
- // involving a second modifier (as described by (__ra_sign.__state & 2)).
+ // The lr value stored in __pc might be signed with IA/IB key, &__pc address
+ // as a modifier and an optional second modifier. If the lr value is signed,
+ // re-sign that with the corresponding key and initial signing scheme using
+ // __sp as a modifier and potentially involving a second modifier
+ // (as described by '__ra_signing_scheme.__flags & 2').
ldr x14, [x0, #0x0F8] // x14 = __sp
- ldr x15, [x0, #0x110] // x15 = __ra_sign.__second_modifier
+ ldr x15, [x0, #0x118] // x15 = __ra_signing_scheme.__second_modifier
+ ldr x1, [x0, #0x108] // x1 = __ra_signing_scheme.__flags
+ ldr x16, [x0, #0x110] // x16 = __ra_signing_scheme.__flags_pac
+
+ // Check if PAuth feature is available. See also
+ // RUN_IF_PAUTH_FEATURE_PRESENT in Registers.hpp
+ mrs x17, ID_AA64ISAR1_EL1
+ lsr x17, x17, #24
+ ands x17, x17, #255
+ cbnz x17, .Lcheck_pac
+ mrs x17, ID_AA64ISAR2_EL1
+ lsr x17, x17, #8
+ ands x17, x17, #15
+ cbnz x17, .Lcheck_pac
+ b .Lno_pauth
+ .Lcheck_pac:
+
+ // See also CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY in Registers.hpp.
+ add x17, x0, #0x108
+ pacga x17, x1, x17
+ cmp x17, x16
+ b.eq .Lpacga_success
+ brk #0xc474
+ .Lpacga_success:
+ .Lno_pauth:
+
+ // See also CHECK_SIGNING_SCHEME_FLAGS_INTEGRITY_FOR_PAUTHABI
+ // in Registers.hpp.
+#if __has_feature(ptrauth_returns)
+ cmp x1, #5
+ b.eq .Lpauthabi_success
+ brk #0xc474
+ .Lpauthabi_success:
+#endif
+
add x16, x0, #0x100 // x16 = &__pc
ldr x17, [x0, #0x100] // x17 = __pc
- ldr x1, [x0, #0x108] // x1 = __ra_sign.__state
- and x1, x1, #1
- cbz x1, .Lresign_end
-
- // lr needs resign
- ldr x1, [x0, #0x108] // x1 = __ra_sign.__state
- and x1, x1, #2
- cbnz x1, .Lresign_with_pc
-
- // lr needs resign without pc as a second modifier
- ldr x1, [x0, #0x118] // x1 = __ra_sign.__use_b_key
- cbnz x1, .Lresign_with_b_key
-
- // lr needs resign with A key and without pc as a second modifier
- hint 0xc // autia1716
- mov x16, x14 // x16 = __sp
- hint 0x8 // pacia1716
- b .Lresign_end
-
-.Lresign_with_b_key:
- // lr needs resign with B key and without pc as a second modifier
- hint 0xe // autib1716
- mov x16, x14 // x16 = __sp
- hint 0xa // pacib1716
- b .Lresign_end
-
-.Lresign_with_pc:
- // lr needs resign with pc as a second modifier
- ldr x1, [x0, #0x118] // x1 = __ra_sign.__use_b_key
- cbnz x1, .Lresign_with_pc_b_key
-
- // lr needs resign with A key and pc as a second modifier
- hint 0xc // autia1716
- mov x16, x14 // x16 = __sp
- hint 0x27 // pacm
- hint 0x8 // pacia1716
- b .Lresign_end
-
-.Lresign_with_pc_b_key:
- // lr needs resign with B key and pc as a second modifier
- hint 0xe // autib1716
- mov x16, x14 // x16 = __sp
- hint 0x27 // pacm
- hint 0xa // pacib1716
- b .Lresign_end
+ // See also SIGNING_SCHEME_FLAGS_SWITCH in Registers.hpp.
+ cmp x1, #0
+ b.ne .Lswitch_1716_1
+ b .Lswitch_1716_end
+
+ .Lswitch_1716_1:
+ cmp x1, #1
+ b.ne .Lswitch_1716_3
+ hint 0xc // autia1716
+ mov x16, x14
+ hint 0x8 // pacia1716
+ b .Lswitch_1716_end
+
+ .Lswitch_1716_3:
+ cmp x1, #3
+ b.ne .Lswitch_1716_5
+ hint 0x27 // pacm
+ hint 0xc // autia1716
+ mov x16, x14
+ hint 0x27 // pacm
+ hint 0x8 // pacia1716
+ b .Lswitch_1716_end
+
+ .Lswitch_1716_5:
+ cmp x1, #5
+ b.ne .Lswitch_1716_7
+ hint 0xe // autib1716
+ mov x16, x14
+ hint 0xa // pacib1716
+ b .Lswitch_1716_end
+
+ .Lswitch_1716_7:
+ cmp x1, #7
+ b.ne .Lswitch_1716_unexpected
+ hint 0x27 // pacm
+ hint 0xe // autib1716
+ mov x16, x14
+ hint 0x27 // pacm
+ hint 0xa // pacib1716
+ b .Lswitch_1716_end
+
+ .Lswitch_1716_unexpected:
+ brk #0xc474
+
+ .Lswitch_1716_end:
-.Lresign_end:
mov lr, x17 // assign final lr value
ldp x14,x15, [x0, #0x070] // restore x14,x15
ldr x16, [x0, #0x110] // second modifier for auti{a|b}sp with PACM
@@ -770,55 +803,56 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
// context struct, because it is allocated on the stack, and an exception
// could clobber the de-allocated portion of the stack after sp has been
// restored.
- ldr x17, [x0, #0x0F8] // load sp into scratch
- ldr x1, [x0, #0x108] // x1 = __ra_sign.__state
- and x1, x1, #1
- cbz x1, .Lauth_end
+ ldr x17, [x0, #0x0F8] // load sp into scratch
- // lr is signed
- ldr x1, [x0, #0x108] // x1 = __ra_sign.__state
- and x1, x1, #2
- cbnz x1, .Lauth_with_pc
+ // Integrity of the signing scheme stored in x1 was checked above and no
+ // manipulations on x1 were performed since then, so skip this check.
- // lr is signed without pc as a second modifier
- ldr x1, [x0, #0x118] // x1 = __ra_sign.__use_b_key
- cbnz x1, .Lauth_with_b_key
+ // See also SIGNING_SCHEME_FLAGS_SWITCH in Registers.hpp.
+ cmp x1, #0
+ b.ne .Lswitch_lrsp_1
+ b .Lswitch_lrsp_end
- // lr is signed with A key and without pc as a second modifier
+ .Lswitch_lrsp_1:
+ cmp x1, #1
+ b.ne .Lswitch_lrsp_3
ldp x0, x1, [x0, #0x000] // restore x0,x1
mov sp,x17 // restore sp
hint 0x1d // autiasp
ret x30
-.Lauth_with_b_key:
- // lr is signed with B key and without pc as a second modifier
+ .Lswitch_lrsp_3:
+ cmp x1, #3
+ b.ne .Lswitch_lrsp_5
ldp x0, x1, [x0, #0x000] // restore x0,x1
mov sp,x17 // restore sp
- hint 0x1f // autibsp
+ hint 0x27 // pacm
+ hint 0x1d // autiasp
ret x30
-.Lauth_with_pc:
- // lr is signed with pc as a second modifier
- ldr x1, [x0, #0x118] // x1 = __ra_sign.__use_b_key
- cbnz x1, .Lauth_with_pc_b_key
-
- // lr is signed with A key and pc as a second modifier
+ .Lswitch_lrsp_5:
+ cmp x1, #5
+ b.ne .Lswitch_lrsp_7
ldp x0, x1, [x0, #0x000] // restore x0,x1
mov sp,x17 // restore sp
- hint 0x27 // pacm
- hint 0x1d // autiasp
+ hint 0x1f // autibsp
ret x30
-.Lauth_with_pc_b_key:
- // lr is signed with B key and pc as a second modifier
+ .Lswitch_lrsp_7:
+ cmp x1, #7
+ b.ne .Lswitch_lrsp_unexpected
+
ldp x0, x1, [x0, #0x000] // restore x0,x1
mov sp,x17 // restore sp
hint 0x27 // pacm
hint 0x1f // autibsp
ret x30
-.Lauth_end:
+ .Lswitch_lrsp_unexpected:
+ brk #0xc474
+
+.Lswitch_lrsp_end:
ldp x0, x1, [x0, #0x000] // restore x0,x1
mov sp,x17 // restore sp
#if defined(__ARM_FEATURE_GCS_DEFAULT)
diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S
index 252353b7861f7..4c4942982f159 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -783,6 +783,24 @@ LnoR2Fix:
#define UNW_RA_SIGNED_WITH_B_KEY ((UNW_ARM_FEATURE_PAC_DEFAULT & 2) != 0)
#define UNW_RA_SIGNED_WITH_PC ((UNW_ARM_FEATURE_PAC_DEFAULT & 8) != 0)
+#if UNW_RA_SIGNED
+# if UNW_RA_SIGNED_WITH_PC
+# if UNW_RA_SIGNED_WITH_B_KEY
+# define UNW_RA_SIGNING_SCHEME 7
+# else
+# define UNW_RA_SIGNING_SCHEME 3
+# endif
+# else
+# if UNW_RA_SIGNED_WITH_B_KEY
+# define UNW_RA_SIGNING_SCHEME 5
+# else
+# define UNW_RA_SIGNING_SCHEME 1
+# endif
+# endif
+#else
+# define UNW_RA_SIGNING_SCHEME 0
+#endif
+
//
// extern int __unw_getcontext(unw_context_t* thread_state)
//
@@ -824,31 +842,38 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
str x1, [x0, #0x0F8]
str x30, [x0, #0x100] // store return address as pc
- // Fill subfields of __ra_sign field
-#if UNW_RA_SIGNED
-# if UNW_RA_SIGNED_WITH_PC
- adrp x16, .L__unw_getcontext_start
- add x16, x16, :lo12:.L__unw_getcontext_start
- str x16, [x0, #0x110] // __second_modifier = x16
-# else
- str xzr, [x0, #0x110] // __second_modifier = 0
-# endif
-
- mov x16, #1
- str x16, [x0, #0x108] // __state = 1
-
-# if UNW_RA_SIGNED_WITH_B_KEY
- str x16, [x0, #0x118] // __use_b_key = 1
+ // Fill subfields of __ra_signing_scheme field
+
+ mov x16, #UNW_RA_SIGNING_SCHEME
+ str x16, [x0, #0x108] // __flags
+
+ // Check if PAuth feature is available. See also
+ // RUN_IF_PAUTH_FEATURE_PRESENT in Registers.hpp
+ mrs x17, ID_AA64ISAR1_EL1
+ lsr x17, x17, #24
+ ands x17, x17, #255
+ cbnz x17, .Lcompute_pac
+ mrs x17, ID_AA64ISAR2_EL1
+ lsr x17, x17, #8
+ ands x17, x17, #15
+ cbnz x17, .Lcompute_pac
+ str xzr, [x0, #0x110] // __flags_pac
+ b .Lno_pauth
+ .Lcompute_pac:
+ add x17, x0, #0x108
+ pacga x17, x16, x17
+ str x17, [x0, #0x110] // __flags_pac
+ .Lno_pauth:
+
+#if UNW_RA_SIGNED_WITH_PC
+ adrp x17, .L__unw_getcontext_start
+ add x17, x17, :lo12:.L__unw_getcontext_start
# else
- str xzr, [x0, #0x118] // __use_b_key = 0
+ mov x17, xzr
# endif
+ str x17, [x0, #0x118] // __second_modifier = x17
- ldr x16, [x0, #0x080] // restore x16 after clobber
-#else
- str xzr, [x0, #0x108] // __state = 0
- str xzr, [x0, #0x110] // __second_modifier = 0
- str xzr, [x0, #0x118] // __use_b_key = 0
-#endif
+ ldp x16,x17, [x0, #0x080] // restore x16 and x17 after clobber
// skip cpsr
#if defined(__ARM_FP) && __ARM_FP != 0
diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index 9fdeca0cdbe4b..400a41fc677f5 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -162,7 +162,7 @@ _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
_LIBUNWIND_ABORT("Bad unwind through arm64e");
}
}
-#elif defined(_LIBUNWIND_TARGET_AARCH64) && \
+#elif defined(_LIBUNWIND_TARGET_AARCH64) && defined(_LIBUNWIND_IS_NATIVE_ONLY) && \
!(defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32))
// We expect IP register value to be signed only for a full-fledged
// PAuth ABI such as Apple's arm64e or Linux's pauthtest. Otherwise,
@@ -170,9 +170,8 @@ _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
// so we need to update RA sign info and mark the pointer as unsigned.
// This prevents attempts of unsigned pointer authentication in case
// if previously a signed RA was stored in the IP register field.
- co->setReg(UNW_AARCH64_RA_SIGN_STATE, 0);
- co->setReg(UNW_AARCH64_RA_SIGN_SECOND_MODIFIER, 0);
- co->setReg(UNW_AARCH64_RA_SIGN_USE_B_KEY, 0);
+ co->setReg(UNW_AARCH64_RA_SIGNING_SCHEME_SECOND_MODIFIER, 0);
+ co->setReg(UNW_AARCH64_RA_SIGNING_SCHEME_FLAGS, 0);
#endif
// If the original call expects stack adjustment, perform this now.
More information about the cfe-commits
mailing list