aboutsummaryrefslogtreecommitdiff
path: root/libunwind/src
diff options
context:
space:
mode:
Diffstat (limited to 'libunwind/src')
-rw-r--r--libunwind/src/AddressSpace.hpp27
-rw-r--r--libunwind/src/CompactUnwinder.hpp21
-rw-r--r--libunwind/src/DwarfInstructions.hpp29
-rw-r--r--libunwind/src/DwarfParser.hpp42
-rw-r--r--libunwind/src/Registers.hpp132
-rw-r--r--libunwind/src/UnwindCursor.hpp76
-rw-r--r--libunwind/src/UnwindLevel1.c38
-rw-r--r--libunwind/src/UnwindRegistersRestore.S23
-rw-r--r--libunwind/src/UnwindRegistersSave.S12
-rw-r--r--libunwind/src/libunwind.cpp48
10 files changed, 381 insertions, 67 deletions
diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp
index 5551c7d..63f9cb3 100644
--- a/libunwind/src/AddressSpace.hpp
+++ b/libunwind/src/AddressSpace.hpp
@@ -129,22 +129,27 @@ struct UnwindInfoSections {
defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) || \
defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
// No dso_base for SEH.
- uintptr_t dso_base;
+ uintptr_t __ptrauth_unwind_uis_dso_base
+ dso_base = 0;
#endif
#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
size_t text_segment_length;
#endif
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
- uintptr_t dwarf_section;
- size_t dwarf_section_length;
+ uintptr_t __ptrauth_unwind_uis_dwarf_section
+ dwarf_section = 0;
+ size_t __ptrauth_unwind_uis_dwarf_section_length
+ dwarf_section_length = 0;
#endif
#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
uintptr_t dwarf_index_section;
size_t dwarf_index_section_length;
#endif
#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
- uintptr_t compact_unwind_section;
- size_t compact_unwind_section_length;
+ uintptr_t __ptrauth_unwind_uis_compact_unwind_section
+ compact_unwind_section = 0;
+ size_t __ptrauth_unwind_uis_compact_unwind_section_length
+ compact_unwind_section_length = 0;
#endif
#if defined(_LIBUNWIND_ARM_EHABI)
uintptr_t arm_section;
@@ -196,7 +201,7 @@ public:
static int64_t getSLEB128(pint_t &addr, pint_t end);
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
- pint_t datarelBase = 0);
+ pint_t datarelBase = 0, pint_t *resultAddr = nullptr);
bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
unw_word_t *offset);
bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
@@ -269,7 +274,7 @@ inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) {
inline LocalAddressSpace::pint_t
LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
- pint_t datarelBase) {
+ pint_t datarelBase, pint_t *resultAddr) {
pint_t startAddr = addr;
const uint8_t *p = (uint8_t *)addr;
pint_t result;
@@ -353,8 +358,14 @@ LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
break;
}
- if (encoding & DW_EH_PE_indirect)
+ if (encoding & DW_EH_PE_indirect) {
+ if (resultAddr)
+ *resultAddr = result;
result = getP(result);
+ } else {
+ if (resultAddr)
+ *resultAddr = startAddr;
+ }
return result;
}
diff --git a/libunwind/src/CompactUnwinder.hpp b/libunwind/src/CompactUnwinder.hpp
index a7a8a15..cd2e0e3 100644
--- a/libunwind/src/CompactUnwinder.hpp
+++ b/libunwind/src/CompactUnwinder.hpp
@@ -601,11 +601,17 @@ int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
savedRegisterLoc -= 8;
}
+ // We load the link register prior to setting the new SP as the authentication
+ // schema for LR entangles the SP of the old frame into the diversifier.
+ Registers_arm64::reg_t linkRegister = registers.getRegister(UNW_AARCH64_LR);
+
// subtract stack size off of sp
registers.setSP(savedRegisterLoc);
- // set pc to be value in lr
- registers.setIP(registers.getRegister(UNW_AARCH64_LR));
+ // Set pc to be value in lr. This needs to be performed after the new SP has
+ // been set, as the PC authentication schema entangles the SP of the new
+ // frame.
+ registers.setIP(linkRegister);
return UNW_STEP_SUCCESS;
}
@@ -614,7 +620,7 @@ template <typename A>
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
Registers_arm64 &registers) {
- uint64_t savedRegisterLoc = registers.getFP() - 8;
+ Registers_arm64::reg_t savedRegisterLoc = registers.getFP() - 8;
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc));
@@ -680,11 +686,16 @@ int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
savedRegisterLoc -= 8;
}
- uint64_t fp = registers.getFP();
+ Registers_arm64::reg_t fp = registers.getFP();
+
// fp points to old fp
registers.setFP(addressSpace.get64(fp));
- // old sp is fp less saved fp and lr
+
+ // Old sp is fp less saved fp and lr. We need to set this prior to setting
+ // the lr as the pointer authentication schema for the lr incorporates the
+ // sp as part of the diversifier.
registers.setSP(fp + 16);
+
// pop return address into pc
registers.setIP(addressSpace.get64(fp + 8));
diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp
index e7be0d6..d2822e8 100644
--- a/libunwind/src/DwarfInstructions.hpp
+++ b/libunwind/src/DwarfInstructions.hpp
@@ -22,7 +22,6 @@
#include "dwarf2.h"
#include "libunwind_ext.h"
-
namespace libunwind {
@@ -34,8 +33,9 @@ public:
typedef typename A::pint_t pint_t;
typedef typename A::sint_t sint_t;
- static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart,
- R &registers, bool &isSignalFrame, bool stage2);
+ static int stepWithDwarf(A &addressSpace, const typename R::link_reg_t &pc,
+ pint_t fdeStart, R &registers, bool &isSignalFrame,
+ bool stage2);
private:
@@ -64,9 +64,10 @@ private:
static pint_t getCFA(A &addressSpace, const PrologInfo &prolog,
const R &registers) {
- if (prolog.cfaRegister != 0)
- return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) +
- prolog.cfaRegisterOffset);
+ if (prolog.cfaRegister != 0) {
+ uintptr_t cfaRegister = registers.getRegister((int)prolog.cfaRegister);
+ return (pint_t)(cfaRegister + prolog.cfaRegisterOffset);
+ }
if (prolog.cfaExpression != 0)
return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace,
registers, 0);
@@ -207,7 +208,8 @@ bool DwarfInstructions<A, R>::isReturnAddressSignedWithPC(A &addressSpace,
#endif
template <typename A, typename R>
-int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
+int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace,
+ const typename R::link_reg_t &pc,
pint_t fdeStart, R &registers,
bool &isSignalFrame, bool stage2) {
FDE_Info fdeInfo;
@@ -264,7 +266,7 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
// by a CFI directive later on.
newRegisters.setSP(cfa);
- pint_t returnAddress = 0;
+ typename R::reg_t returnAddress = 0;
constexpr int lastReg = R::lastDwarfRegNum();
static_assert(static_cast<int>(CFI_Parser<A>::kMaxRegisterNumber) >=
lastReg,
@@ -300,7 +302,16 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
isSignalFrame = cieInfo.isSignalFrame;
-#if defined(_LIBUNWIND_TARGET_AARCH64)
+#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 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
diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp
index 25250e0..dbd7d65 100644
--- a/libunwind/src/DwarfParser.hpp
+++ b/libunwind/src/DwarfParser.hpp
@@ -23,6 +23,10 @@
#include "config.h"
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+#include <ptrauth.h>
+#endif
+
namespace libunwind {
/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
@@ -33,6 +37,7 @@ template <typename A>
class CFI_Parser {
public:
typedef typename A::pint_t pint_t;
+ typedef pint_t __ptrauth_unwind_cie_info_personality personality_t;
/// Information encoded in a CIE (Common Information Entry)
struct CIE_Info {
@@ -43,7 +48,7 @@ public:
uint8_t lsdaEncoding;
uint8_t personalityEncoding;
uint8_t personalityOffsetInCIE;
- pint_t personality;
+ personality_t personality;
uint32_t codeAlignFactor;
int dataAlignFactor;
bool isSignalFrame;
@@ -369,6 +374,7 @@ const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie,
cieInfo->returnAddressRegister = (uint8_t)raReg;
// parse augmentation data based on augmentation string
const char *result = NULL;
+ pint_t resultAddr = 0;
if (addressSpace.get8(strStart) == 'z') {
// parse augmentation data length
addressSpace.getULEB128(p, cieContentEnd);
@@ -377,13 +383,41 @@ const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie,
case 'z':
cieInfo->fdesHaveAugmentationData = true;
break;
- case 'P':
+ case 'P': {
cieInfo->personalityEncoding = addressSpace.get8(p);
++p;
cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie);
- cieInfo->personality = addressSpace
- .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
+ pint_t personality = addressSpace.getEncodedP(
+ p, cieContentEnd, cieInfo->personalityEncoding,
+ /*datarelBase=*/0, &resultAddr);
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ if (personality) {
+ // The GOT for the personality function was signed address
+ // authenticated. Manually re-sign with the CIE_Info::personality
+ // schema. If we could guarantee the encoding of the personality we
+ // could avoid this by simply giving resultAddr the correct ptrauth
+ // schema and performing an assignment.
+#if defined(__arm64e__)
+ const auto oldDiscriminator = resultAddr;
+#else
+ const auto oldDiscriminator = ptrauth_blend_discriminator(
+ (void *)resultAddr, __ptrauth_unwind_pauthtest_personality_disc);
+#endif
+ const auto discriminator = ptrauth_blend_discriminator(
+ &cieInfo->personality,
+ __ptrauth_unwind_cie_info_personality_disc);
+ void *signedPtr = ptrauth_auth_and_resign(
+ (void *)personality, ptrauth_key_function_pointer,
+ oldDiscriminator, ptrauth_key_function_pointer, discriminator);
+ personality = (pint_t)signedPtr;
+ }
+#endif
+ // We use memmove to set the CIE personality as we have already
+ // re-signed the pointer to the correct schema.
+ memmove((void *)&cieInfo->personality, (void *)&personality,
+ sizeof(personality));
break;
+ }
case 'L':
cieInfo->lsdaEncoding = addressSpace.get8(p);
++p;
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 8b3055f..5a5b578 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -17,6 +17,7 @@
#include "config.h"
#include "libunwind.h"
+#include "libunwind_ext.h"
#include "shadow_stack_unwind.h"
namespace libunwind {
@@ -60,6 +61,9 @@ public:
Registers_x86();
Registers_x86(const void *registers);
+ typedef uint32_t reg_t;
+ typedef uint32_t link_reg_t;
+
bool validRegister(int num) const;
uint32_t getRegister(int num) const;
void setRegister(int num, uint32_t value);
@@ -278,6 +282,9 @@ public:
Registers_x86_64();
Registers_x86_64(const void *registers);
+ typedef uint64_t reg_t;
+ typedef uint64_t link_reg_t;
+
bool validRegister(int num) const;
uint64_t getRegister(int num) const;
void setRegister(int num, uint64_t value);
@@ -597,6 +604,9 @@ public:
Registers_ppc();
Registers_ppc(const void *registers);
+ typedef uint32_t reg_t;
+ typedef uint32_t link_reg_t;
+
bool validRegister(int num) const;
uint32_t getRegister(int num) const;
void setRegister(int num, uint32_t value);
@@ -1169,6 +1179,9 @@ public:
Registers_ppc64();
Registers_ppc64(const void *registers);
+ typedef uint64_t reg_t;
+ typedef uint64_t link_reg_t;
+
bool validRegister(int num) const;
uint64_t getRegister(int num) const;
void setRegister(int num, uint64_t value);
@@ -1826,6 +1839,11 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
public:
Registers_arm64();
Registers_arm64(const void *registers);
+ Registers_arm64(const Registers_arm64 &);
+ Registers_arm64 &operator=(const Registers_arm64 &);
+
+ typedef uint64_t reg_t;
+ typedef uint64_t __ptrauth_unwind_registers_arm64_link_reg link_reg_t;
bool validRegister(int num) const;
uint64_t getRegister(int num) const;
@@ -1845,10 +1863,47 @@ public:
uint64_t getSP() const { return _registers.__sp; }
void setSP(uint64_t value) { _registers.__sp = value; }
- uint64_t getIP() const { return _registers.__pc; }
- void setIP(uint64_t value) { _registers.__pc = value; }
- uint64_t getFP() const { return _registers.__fp; }
- void setFP(uint64_t value) { _registers.__fp = value; }
+ uint64_t getIP() const {
+ uint64_t value = _registers.__pc;
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ // 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());
+#endif
+ return value;
+ }
+ void setIP(uint64_t value) {
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ // 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);
+#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)
+ 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);
+ }
+#endif
private:
uint64_t lazyGetVG() const;
@@ -1889,11 +1944,35 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
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
+ // performed by setIP.
+ uint64_t pcRegister = 0;
+ memmove(&pcRegister, ((uint8_t *)&_registers) + offsetof(GPRs, __pc),
+ sizeof(pcRegister));
+ setIP(pcRegister);
+#endif
+}
+
+inline Registers_arm64::Registers_arm64(const Registers_arm64 &other) {
+ *this = other;
+}
+
+inline Registers_arm64 &
+Registers_arm64::operator=(const Registers_arm64 &other) {
+ memmove(static_cast<void *>(this), &other, sizeof(*this));
+ // We perform this step to ensure that we correctly authenticate and re-sign
+ // the pc after the bitwise copy.
+ setIP(other.getIP());
+ return *this;
}
inline Registers_arm64::Registers_arm64() {
- memset(&_registers, 0, sizeof(_registers));
- memset(&_vectorHalfRegisters, 0, sizeof(_vectorHalfRegisters));
+ memset(static_cast<void *>(this), 0, sizeof(*this));
}
inline bool Registers_arm64::validRegister(int regNum) const {
@@ -1930,13 +2009,13 @@ inline uint64_t Registers_arm64::lazyGetVG() const {
inline uint64_t Registers_arm64::getRegister(int regNum) const {
if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
- return _registers.__pc;
+ 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_FP)
- return _registers.__fp;
+ return getFP();
if (regNum == UNW_AARCH64_LR)
return _registers.__lr;
if (regNum == UNW_AARCH64_VG)
@@ -1948,13 +2027,13 @@ inline uint64_t Registers_arm64::getRegister(int regNum) const {
inline void Registers_arm64::setRegister(int regNum, uint64_t value) {
if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
- _registers.__pc = 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_FP)
- _registers.__fp = value;
+ setFP(value);
else if (regNum == UNW_AARCH64_LR)
_registers.__lr = value;
else if (regNum == UNW_AARCH64_VG)
@@ -2148,6 +2227,9 @@ public:
Registers_arm();
Registers_arm(const void *registers);
+ typedef uint32_t reg_t;
+ typedef uint32_t link_reg_t;
+
bool validRegister(int num) const;
uint32_t getRegister(int num) const;
void setRegister(int num, uint32_t value);
@@ -2653,6 +2735,9 @@ public:
Registers_or1k();
Registers_or1k(const void *registers);
+ typedef uint32_t reg_t;
+ typedef uint32_t link_reg_t;
+
bool validRegister(int num) const;
uint32_t getRegister(int num) const;
void setRegister(int num, uint32_t value);
@@ -2852,6 +2937,9 @@ public:
Registers_mips_o32();
Registers_mips_o32(const void *registers);
+ typedef uint32_t reg_t;
+ typedef uint32_t link_reg_t;
+
bool validRegister(int num) const;
uint32_t getRegister(int num) const;
void setRegister(int num, uint32_t value);
@@ -3187,6 +3275,9 @@ public:
Registers_mips_newabi();
Registers_mips_newabi(const void *registers);
+ typedef uint64_t reg_t;
+ typedef uint64_t link_reg_t;
+
bool validRegister(int num) const;
uint64_t getRegister(int num) const;
void setRegister(int num, uint64_t value);
@@ -3490,6 +3581,9 @@ public:
Registers_sparc();
Registers_sparc(const void *registers);
+ typedef uint32_t reg_t;
+ typedef uint32_t link_reg_t;
+
bool validRegister(int num) const;
uint32_t getRegister(int num) const;
void setRegister(int num, uint32_t value);
@@ -3676,6 +3770,9 @@ public:
Registers_sparc64() = default;
Registers_sparc64(const void *registers);
+ typedef uint64_t reg_t;
+ typedef uint64_t link_reg_t;
+
bool validRegister(int num) const;
uint64_t getRegister(int num) const;
void setRegister(int num, uint64_t value);
@@ -3861,6 +3958,9 @@ public:
Registers_hexagon();
Registers_hexagon(const void *registers);
+ typedef uint32_t reg_t;
+ typedef uint32_t link_reg_t;
+
bool validRegister(int num) const;
uint32_t getRegister(int num) const;
void setRegister(int num, uint32_t value);
@@ -4076,6 +4176,9 @@ public:
Registers_riscv();
Registers_riscv(const void *registers);
+ typedef ::libunwind::reg_t reg_t;
+ typedef ::libunwind::reg_t link_reg_t;
+
bool validRegister(int num) const;
reg_t getRegister(int num) const;
void setRegister(int num, reg_t value);
@@ -4373,6 +4476,9 @@ public:
Registers_ve();
Registers_ve(const void *registers);
+ typedef uint64_t reg_t;
+ typedef uint64_t link_reg_t;
+
bool validRegister(int num) const;
uint64_t getRegister(int num) const;
void setRegister(int num, uint64_t value);
@@ -4816,6 +4922,9 @@ public:
Registers_s390x();
Registers_s390x(const void *registers);
+ typedef uint64_t reg_t;
+ typedef uint64_t link_reg_t;
+
bool validRegister(int num) const;
uint64_t getRegister(int num) const;
void setRegister(int num, uint64_t value);
@@ -5104,6 +5213,9 @@ public:
Registers_loongarch();
Registers_loongarch(const void *registers);
+ typedef uint64_t reg_t;
+ typedef uint64_t link_reg_t;
+
bool validRegister(int num) const;
uint64_t getRegister(int num) const;
void setRegister(int num, uint64_t value);
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 9a1afd3..7ec5f9e 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1047,18 +1047,26 @@ private:
bool getInfoFromFdeCie(const typename CFI_Parser<A>::FDE_Info &fdeInfo,
const typename CFI_Parser<A>::CIE_Info &cieInfo,
pint_t pc, uintptr_t dso_base);
- bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections &sects,
- uint32_t fdeSectionOffsetHint=0);
+ bool getInfoFromDwarfSection(const typename R::link_reg_t &pc,
+ const UnwindInfoSections &sects,
+ uint32_t fdeSectionOffsetHint = 0);
int stepWithDwarfFDE(bool stage2) {
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ typename R::reg_t rawPC = this->getReg(UNW_REG_IP);
+ typename R::link_reg_t pc;
+ _registers.loadAndAuthenticateLinkRegister(rawPC, &pc);
+#else
+ typename R::link_reg_t pc = this->getReg(UNW_REG_IP);
+#endif
return DwarfInstructions<A, R>::stepWithDwarf(
- _addressSpace, (pint_t)this->getReg(UNW_REG_IP),
- (pint_t)_info.unwind_info, _registers, _isSignalFrame, stage2);
+ _addressSpace, pc, (pint_t)_info.unwind_info, _registers,
+ _isSignalFrame, stage2);
}
#endif
#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
- bool getInfoFromCompactEncodingSection(pint_t pc,
- const UnwindInfoSections &sects);
+ bool getInfoFromCompactEncodingSection(const typename R::link_reg_t &pc,
+ const UnwindInfoSections &sects);
int stepWithCompactEncoding(bool stage2 = false) {
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
if ( compactSaysUseDwarf() )
@@ -1359,13 +1367,13 @@ UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
"UnwindCursor<> does not fit in unw_cursor_t");
static_assert((alignof(UnwindCursor<A, R>) <= alignof(unw_cursor_t)),
"UnwindCursor<> requires more alignment than unw_cursor_t");
- memset(&_info, 0, sizeof(_info));
+ memset(static_cast<void *>(&_info), 0, sizeof(_info));
}
template <typename A, typename R>
UnwindCursor<A, R>::UnwindCursor(A &as, void *)
: _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) {
- memset(&_info, 0, sizeof(_info));
+ memset(static_cast<void *>(&_info), 0, sizeof(_info));
// FIXME
// fill in _registers from thread arg
}
@@ -1683,9 +1691,9 @@ bool UnwindCursor<A, R>::getInfoFromFdeCie(
}
template <typename A, typename R>
-bool UnwindCursor<A, R>::getInfoFromDwarfSection(pint_t pc,
- const UnwindInfoSections &sects,
- uint32_t fdeSectionOffsetHint) {
+bool UnwindCursor<A, R>::getInfoFromDwarfSection(
+ const typename R::link_reg_t &pc, const UnwindInfoSections &sects,
+ uint32_t fdeSectionOffsetHint) {
typename CFI_Parser<A>::FDE_Info fdeInfo;
typename CFI_Parser<A>::CIE_Info cieInfo;
bool foundFDE = false;
@@ -1743,8 +1751,8 @@ bool UnwindCursor<A, R>::getInfoFromDwarfSection(pint_t pc,
#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
template <typename A, typename R>
-bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(pint_t pc,
- const UnwindInfoSections &sects) {
+bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
+ const typename R::link_reg_t &pc, const UnwindInfoSections &sects) {
const bool log = false;
if (log)
fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n",
@@ -1975,6 +1983,16 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(pint_t pc,
personalityIndex * sizeof(uint32_t));
pint_t personalityPointer = sects.dso_base + (pint_t)personalityDelta;
personality = _addressSpace.getP(personalityPointer);
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ // The GOT for the personality function was signed address authenticated.
+ // Resign it as a regular function pointer.
+ const auto discriminator = ptrauth_blend_discriminator(
+ &_info.handler, __ptrauth_unwind_upi_handler_disc);
+ void *signedPtr = ptrauth_auth_and_resign(
+ (void *)personality, ptrauth_key_function_pointer, personalityPointer,
+ ptrauth_key_function_pointer, discriminator);
+ personality = (__typeof(personality))signedPtr;
+#endif
if (log)
fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), "
"personalityDelta=0x%08X, personality=0x%08llX\n",
@@ -1988,7 +2006,11 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(pint_t pc,
_info.start_ip = funcStart;
_info.end_ip = funcEnd;
_info.lsda = lsda;
- _info.handler = personality;
+ // We use memmove to copy the personality function as we have already manually
+ // re-signed the pointer, and assigning directly will attempt to incorrectly
+ // sign the already signed value.
+ memmove(reinterpret_cast<void *>(&_info.handler),
+ reinterpret_cast<void *>(&personality), sizeof(personality));
_info.gp = 0;
_info.flags = 0;
_info.format = encoding;
@@ -2641,11 +2663,19 @@ void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
_isSigReturn = false;
#endif
- pint_t pc = static_cast<pint_t>(this->getReg(UNW_REG_IP));
+ typename R::reg_t rawPC = this->getReg(UNW_REG_IP);
+
#if defined(_LIBUNWIND_ARM_EHABI)
// Remove the thumb bit so the IP represents the actual instruction address.
// This matches the behaviour of _Unwind_GetIP on arm.
- pc &= (pint_t)~0x1;
+ rawPC &= (pint_t)~0x1;
+#endif
+
+ typename R::link_reg_t pc;
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ _registers.loadAndAuthenticateLinkRegister(rawPC, &pc);
+#else
+ pc = rawPC;
#endif
// Exit early if at the top of the stack.
@@ -3189,16 +3219,22 @@ template <typename A, typename R> int UnwindCursor<A, R>::step(bool stage2) {
template <typename A, typename R>
void UnwindCursor<A, R>::getInfo(unw_proc_info_t *info) {
if (_unwindInfoMissing)
- memset(info, 0, sizeof(*info));
+ memset(static_cast<void *>(info), 0, sizeof(*info));
else
*info = _info;
}
template <typename A, typename R>
bool UnwindCursor<A, R>::getFunctionName(char *buf, size_t bufLen,
- unw_word_t *offset) {
- return _addressSpace.findFunctionName((pint_t)this->getReg(UNW_REG_IP),
- buf, bufLen, offset);
+ unw_word_t *offset) {
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ typename R::reg_t rawPC = this->getReg(UNW_REG_IP);
+ typename R::link_reg_t pc;
+ _registers.loadAndAuthenticateLinkRegister(rawPC, &pc);
+#else
+ typename R::link_reg_t pc = this->getReg(UNW_REG_IP);
+#endif
+ return _addressSpace.findFunctionName(pc, buf, bufLen, offset);
}
#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN)
diff --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c
index f3b451a..b0cd60d 100644
--- a/libunwind/src/UnwindLevel1.c
+++ b/libunwind/src/UnwindLevel1.c
@@ -90,6 +90,23 @@
} while (0)
#endif
+// We need this helper function as the semantics of casting between integers and
+// function pointers mean that we end up with a function pointer without the
+// correct signature. Instead we assign to an integer with a matching schema,
+// and then memmove the result into a variable of the correct type. This memmove
+// is possible as `_Unwind_Personality_Fn` is a standard function pointer, and
+// as such is not address diversified.
+static _Unwind_Personality_Fn get_handler_function(unw_proc_info_t *frameInfo) {
+ uintptr_t __unwind_ptrauth_restricted_intptr(ptrauth_key_function_pointer,
+ 0,
+ ptrauth_function_pointer_type_discriminator(_Unwind_Personality_Fn))
+ reauthenticatedIntegerHandler = frameInfo->handler;
+ _Unwind_Personality_Fn handler;
+ memmove(&handler, (void *)&reauthenticatedIntegerHandler,
+ sizeof(_Unwind_Personality_Fn));
+ return handler;
+}
+
static _Unwind_Reason_Code
unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) {
__unw_init_local(cursor, uc);
@@ -147,8 +164,7 @@ unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except
// If there is a personality routine, ask it if it will want to stop at
// this frame.
if (frameInfo.handler != 0) {
- _Unwind_Personality_Fn p =
- (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler);
+ _Unwind_Personality_Fn p = get_handler_function(&frameInfo);
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase1(ex_obj=%p): calling personality function %p",
(void *)exception_object, (void *)(uintptr_t)p);
@@ -276,8 +292,7 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor,
++framesWalked;
// If there is a personality routine, tell it we are unwinding.
if (frameInfo.handler != 0) {
- _Unwind_Personality_Fn p =
- (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler);
+ _Unwind_Personality_Fn p = get_handler_function(&frameInfo);
_Unwind_Action action = _UA_CLEANUP_PHASE;
if (sp == exception_object->private_2) {
// Tell personality this was the frame it marked in phase 1.
@@ -394,8 +409,7 @@ unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
++framesWalked;
// If there is a personality routine, tell it we are unwinding.
if (frameInfo.handler != 0) {
- _Unwind_Personality_Fn p =
- (_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler);
+ _Unwind_Personality_Fn p = get_handler_function(&frameInfo);
_LIBUNWIND_TRACE_UNWINDING(
"unwind_phase2_forced(ex_obj=%p): calling personality function %p",
(void *)exception_object, (void *)(uintptr_t)p);
@@ -597,6 +611,18 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
unw_cursor_t *cursor = (unw_cursor_t *)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
+ {
+ 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);
+ }
+#endif
+
_LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIxPTR,
(void *)context, result);
return (uintptr_t)result;
diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index 1077d80..1ab4c43 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -25,6 +25,8 @@
#if !defined(__USING_SJLJ_EXCEPTIONS__)
#if defined(__i386__)
+.att_syntax
+
DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_jumpto)
#
# extern "C" void __libunwind_Registers_x86_jumpto(Registers_x86 *);
@@ -69,6 +71,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_jumpto)
# skip gs
#elif defined(__x86_64__) && !defined(__arm64ec__)
+.att_syntax
DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_64_jumpto)
#
@@ -659,7 +662,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
ldp x24,x25, [x0, #0x0C0]
ldp x26,x27, [x0, #0x0D0]
ldp x28,x29, [x0, #0x0E0]
- ldr x30, [x0, #0x100] // restore pc into lr
+
#if defined(__ARM_FP) && __ARM_FP != 0
ldp d0, d1, [x0, #0x110]
ldp d2, d3, [x0, #0x120]
@@ -683,7 +686,18 @@ 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 x16, [x0, #0x0F8]
+
+ ldr x16, [x0, #0x0F8] // load sp into scratch
+ ldr lr, [x0, #0x100] // restore pc into lr
+
+#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
+
ldp x0, x1, [x0, #0x000] // restore x0,x1
mov sp,x16 // restore sp
#if defined(__ARM_FEATURE_GCS_DEFAULT)
@@ -696,7 +710,12 @@ 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 8bf99eb..31a177f 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -25,6 +25,7 @@
#if !defined(__USING_SJLJ_EXCEPTIONS__)
#if defined(__i386__)
+.att_syntax
#
# extern int __unw_getcontext(unw_context_t* thread_state)
@@ -109,6 +110,7 @@ DEFINE_LIBUNWIND_FUNCTION("#__unw_getcontext")
.text
#elif defined(__x86_64__)
+.att_syntax
#
# extern int __unw_getcontext(unw_context_t* thread_state)
@@ -769,6 +771,11 @@ LnoR2Fix:
//
.p2align 2
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
+
+#if __has_feature(ptrauth_calls)
+ pacibsp
+#endif
+
stp x0, x1, [x0, #0x000]
stp x2, x3, [x0, #0x010]
stp x4, x5, [x0, #0x020]
@@ -809,7 +816,12 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
str d31, [x0, #0x208]
#endif
mov x0, #0 // return UNW_ESUCCESS
+
+#if __has_feature(ptrauth_calls)
+ retab
+#else
ret
+#endif
#elif defined(__arm__) && !defined(__APPLE__)
diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index cf39ec5..951d87d 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -118,14 +118,52 @@ _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
typedef LocalAddressSpace::pint_t pint_t;
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
if (co->validReg(regNum)) {
- co->setReg(regNum, (pint_t)value);
// special case altering IP to re-find info (being called by personality
// function)
if (regNum == UNW_REG_IP) {
unw_proc_info_t info;
// First, get the FDE for the old location and then update it.
co->getInfo(&info);
- co->setInfoBasedOnIPRegister(false);
+
+ pint_t sp = (pint_t)co->getReg(UNW_REG_SP);
+
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ {
+ // It is only valid to set the IP within the current function. This is
+ // important for ptrauth, otherwise the IP cannot be correctly signed.
+ // The current signature of `value` is via the schema:
+ // __ptrauth(ptrauth_key_return_address, <<sp>>, 0)
+ // For this to be generally usable we manually re-sign it to the
+ // directly supported schema:
+ // __ptrauth(ptrauth_key_return_address, 1, 0)
+ unw_word_t
+ __unwind_ptrauth_restricted_intptr(ptrauth_key_return_address, 1,
+ 0) authenticated_value;
+ unw_word_t opaque_value = (uint64_t)ptrauth_auth_and_resign(
+ (void *)value, ptrauth_key_return_address, sp,
+ ptrauth_key_return_address, &authenticated_value);
+ memmove(reinterpret_cast<void *>(&authenticated_value),
+ reinterpret_cast<void *>(&opaque_value),
+ sizeof(authenticated_value));
+ if (authenticated_value < info.start_ip ||
+ authenticated_value > info.end_ip)
+ _LIBUNWIND_ABORT("PC vs frame info mismatch");
+
+ // PC should have been signed with the sp, so we verify that
+ // roundtripping does not fail.
+ pint_t pc = (pint_t)co->getReg(UNW_REG_IP);
+ if (ptrauth_auth_and_resign((void *)pc, ptrauth_key_return_address, sp,
+ ptrauth_key_return_address,
+ sp) != (void *)pc) {
+ _LIBUNWIND_LOG("Bad unwind through arm64e (0x%zX, 0x%zX)->0x%zX\n",
+ pc, sp,
+ (pint_t)ptrauth_auth_data(
+ (void *)pc, ptrauth_key_return_address, sp));
+ _LIBUNWIND_ABORT("Bad unwind through arm64e");
+ }
+ }
+#endif
+
// If the original call expects stack adjustment, perform this now.
// Normal frame unwinding would have included the offset already in the
// CFA computation.
@@ -133,7 +171,11 @@ _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
// this should actually be - info.gp. LLVM doesn't currently support
// any such platforms and Clang doesn't export a macro for them.
if (info.gp)
- co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + info.gp);
+ co->setReg(UNW_REG_SP, sp + info.gp);
+ co->setReg(UNW_REG_IP, value);
+ co->setInfoBasedOnIPRegister(false);
+ } else {
+ co->setReg(regNum, (pint_t)value);
}
return UNW_ESUCCESS;
}