diff --git a/libunwind/include/mach-o/compact_unwind_encoding.h b/libunwind/include/mach-o/compact_unwind_encoding.h
index c495b83c40b9..2bfa4dd7b13a 100644
--- a/libunwind/include/mach-o/compact_unwind_encoding.h
+++ b/libunwind/include/mach-o/compact_unwind_encoding.h
@@ -39,16 +39,14 @@
//
typedef uint32_t compact_unwind_encoding_t;
-
// architecture independent bits
+// clang-format off
enum {
UNWIND_IS_NOT_FUNCTION_START = 0x80000000,
UNWIND_HAS_LSDA = 0x40000000,
UNWIND_PERSONALITY_MASK = 0x30000000,
};
-
-
-
+// clang-format on
//
// x86
@@ -67,6 +65,7 @@ enum {
// 3-bits register count
// 10-bits register permutation
//
+// clang-format off
enum {
UNWIND_X86_MODE_MASK = 0x0F000000,
UNWIND_X86_MODE_EBP_FRAME = 0x01000000,
@@ -84,7 +83,9 @@ enum {
UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF,
};
+// clang-format on
+// clang-format off
enum {
UNWIND_X86_REG_NONE = 0,
UNWIND_X86_REG_EBX = 1,
@@ -94,6 +95,7 @@ enum {
UNWIND_X86_REG_ESI = 5,
UNWIND_X86_REG_EBP = 6,
};
+// clang-format on
//
// For x86 there are four modes for the compact unwind encoding:
@@ -182,9 +184,6 @@ enum {
//}
//
-
-
-
//
// x86_64
//
@@ -202,6 +201,7 @@ enum {
// 3-bits register count
// 10-bits register permutation
//
+// clang-format off
enum {
UNWIND_X86_64_MODE_MASK = 0x0F000000,
UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000,
@@ -219,7 +219,9 @@ enum {
UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
};
+// clang-format on
+// clang-format off
enum {
UNWIND_X86_64_REG_NONE = 0,
UNWIND_X86_64_REG_RBX = 1,
@@ -229,6 +231,8 @@ enum {
UNWIND_X86_64_REG_R15 = 5,
UNWIND_X86_64_REG_RBP = 6,
};
+// clang-format on
+
//
// For x86_64 there are four modes for the compact unwind encoding:
// UNWIND_X86_64_MODE_RBP_FRAME:
@@ -267,7 +271,6 @@ enum {
// function.
//
-
// ARM64
//
// 1-bit: start
@@ -283,11 +286,16 @@ enum {
// DWARF:
// 24-bits offset of DWARF FDE in __eh_frame section
//
+// clang-format off
enum {
UNWIND_ARM64_MODE_MASK = 0x0F000000,
UNWIND_ARM64_MODE_FRAMELESS = 0x02000000,
UNWIND_ARM64_MODE_DWARF = 0x03000000,
UNWIND_ARM64_MODE_FRAME = 0x04000000,
+ UNWIND_ARM64_MODE_FRAME_PAUTH_LR = 0x05000000,
+
+ UNWIND_ARM64_PAUTH_LR_OFFSET_MASK = 0x00FFF000,
+ UNWIND_ARM64_PAUTH_LR_OFFSET_SHIFT = 12,
UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001,
UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002,
@@ -302,6 +310,8 @@ enum {
UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
};
+// clang-format on
+//
// For arm64 there are three modes for the compact unwind encoding:
// UNWIND_ARM64_MODE_FRAME:
// This is a standard arm64 prolog where FP/LR are immediately pushed on the
@@ -323,11 +333,13 @@ enum {
// This mode is never used in object files. It is only generated by the
// linker in final linked images which have only DWARF unwind info for a
// function.
-//
-
-
-
-
+// For arm64e there is one more mode for compact unwind encoding:
+// UNWIND_ARM64_MODE_FRAME_PAUTH_LR:
+// An arm64e prologue, where LR is signed in the range with a `pacibsppc`.
+// The UNWIND_ARM64_PAUTH_LR_OFFSET_MASK bits specify an unsigned positive
+// offset (encoded as a multiple of 4 bytes) from the beginning of the range
+// to the `pacibsppc`, with 0 indicating that the `pacibsppc` is the first
+// instruction in the range.
////////////////////////////////////////////////////////////////////////////////
//
diff --git a/libunwind/src/CompactUnwinder.hpp b/libunwind/src/CompactUnwinder.hpp
index 929f540b662b..bc9c569a8ccb 100644
--- a/libunwind/src/CompactUnwinder.hpp
+++ b/libunwind/src/CompactUnwinder.hpp
@@ -520,6 +520,7 @@ int CompactUnwinder_arm64::stepWithCompactEncoding(
A &addressSpace, Registers_arm64 ®isters) {
switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
case UNWIND_ARM64_MODE_FRAME:
+ case UNWIND_ARM64_MODE_FRAME_PAUTH_LR:
return stepWithCompactEncodingFrame(compactEncoding, functionStart,
addressSpace, registers);
case UNWIND_ARM64_MODE_FRAMELESS:
@@ -619,7 +620,7 @@ int CompactUnwinder_arm64::stepWithCompactEncodingFrameless(
template
int CompactUnwinder_arm64::stepWithCompactEncodingFrame(
- compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
+ compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
Registers_arm64 ®isters) {
Registers_arm64::reg_t savedRegisterLoc = registers.getFP() - 8;
@@ -698,7 +699,21 @@ int CompactUnwinder_arm64::stepWithCompactEncodingFrame(
registers.setSP(fp + 16);
// pop return address into pc
- registers.setIP(addressSpace.get64(fp + 8));
+ Registers_arm64::reg_t linkRegister = addressSpace.get64(fp + 8);
+
+ // authenticate lr, if needed
+ if ((encoding & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_FRAME_PAUTH_LR) {
+ // Offset from the beginning of the range to the pacibsppc, in words.
+ uint32_t offset = (encoding & UNWIND_ARM64_PAUTH_LR_OFFSET_MASK) >>
+ UNWIND_ARM64_PAUTH_LR_OFFSET_SHIFT;
+
+ // Address of the pacibsppc
+ uint64_t pacibsppcAddr = functionStart + offset * 4;
+
+ registers.setIPPAuthLR(linkRegister, pacibsppcAddr);
+ } else {
+ registers.setIP(linkRegister);
+ }
return UNW_STEP_SUCCESS;
}
diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp
index be8bcf1c9c93..8868932d6821 100644
--- a/libunwind/src/DwarfInstructions.hpp
+++ b/libunwind/src/DwarfInstructions.hpp
@@ -300,16 +300,28 @@ int DwarfInstructions::stepWithDwarf(
isSignalFrame = cieInfo.isSignalFrame;
-#if defined(_LIBUNWIND_TARGET_AARCH64) && \
- !defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+#if defined(_LIBUNWIND_TARGET_AARCH64)
// 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.
+ // below handles signed RA for ptrauth-returns, while pac-ret uses pacm
+ // instructions from the hint space.
+ //
// TODO: unify logic for both cases, see
// https://github.com/llvm/llvm-project/issues/160110
- //
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+ if (getReturnAddressSignStatus(addressSpace, registers, cfa, prolog) ==
+ RASignedWithPC) {
+ newRegisters.setIPPAuthLR(returnAddress, prolog.ptrAuthDiversifier);
+ } else {
+ newRegisters.setIP(returnAddress);
+ }
+
+ // Simulate the step by replacing the register set with the new ones.
+ registers = newRegisters;
+
+ return UNW_STEP_SUCCESS;
+#else
// 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
@@ -352,6 +364,7 @@ int DwarfInstructions::stepWithDwarf(
#endif
}
#endif
+#endif
#if defined(_LIBUNWIND_IS_NATIVE_ONLY) && defined(_LIBUNWIND_TARGET_ARM) && \
defined(__ARM_FEATURE_PAUTH)
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index c88422a6d5f4..d79d836d6925 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1896,11 +1896,9 @@ public:
#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 signed 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());
+ value = (uint64_t)ptrauth_auth_and_resign(
+ (void *)_registers.__pc, ptrauth_key_return_address, &_registers.__pc,
+ ptrauth_key_return_address, _registers.__sp);
#endif
return value;
}
@@ -1909,14 +1907,37 @@ public:
// 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);
+ value = (uint64_t)ptrauth_auth_and_resign(
+ (void *)value, ptrauth_key_return_address, _registers.__sp,
+ ptrauth_key_return_address, &_registers.__pc);
#endif
_registers.__pc = value;
}
+ __attribute__((target("pauth-lr"))) void setIPPAuthLR(uint64_t value,
+ uint64_t signing_pc) {
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+#if __has_builtin(__builtin_ptrauth_auth_with_pc_and_resign)
+ value = (uint64_t)ptrauth_auth_with_pc_and_resign(
+ (void *)value, ptrauth_key_return_address, _registers.__sp, signing_pc,
+ ptrauth_key_return_address, &_registers.__pc);
+#else
+ register uint64_t x17 __asm("x17") = value;
+ register uint64_t x16 __asm("x16") = _registers.__sp;
+ register uint64_t x15 __asm("x15") = signing_pc;
+ uint64_t resignDisc = (uint64_t)&_registers.__pc;
+ asm("autib171615\n\t"
+ "pacib %0, %3"
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(resignDisc));
+ value = x17;
+#endif
+#endif
+ _registers.__pc = value;
+ }
+
uint64_t getFP() const { return _registers.__fp; }
void setFP(uint64_t value) { _registers.__fp = value; }
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 537dbd83f4f7..ad110359e26d 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1911,6 +1911,7 @@ bool UnwindCursor::getInfoFromCompactEncodingSection(
// do a binary search of second level page index
uint32_t encoding = 0;
pint_t funcStart = 0;
+ pint_t rangeStart = 0;
pint_t funcEnd = 0;
pint_t lsda = 0;
pint_t personality = 0;
@@ -1950,21 +1951,39 @@ bool UnwindCursor::getInfoFromCompactEncodingSection(
}
}
encoding = pageIndex.encoding(low);
- funcStart = pageIndex.functionOffset(low) + sects.dso_base;
+ rangeStart = pageIndex.functionOffset(low) + sects.dso_base;
+
+ // If UNWIND_IS_NOT_FUNCTION_START is set, walk backwards to find the actual
+ // function start.
+ funcStart = rangeStart;
+ if (encoding & UNWIND_IS_NOT_FUNCTION_START) {
+ assert(low != 0 &&
+ "UNWIND_IS_NOT_FUNCTION_START comact unwind must be preceded by a "
+ "~UNWIND_IS_NOT_FUNCTION_START entry for the same function");
+ uint32_t backIndex = low;
+ do {
+ --backIndex;
+ } while (backIndex > 0 &&
+ (pageIndex.encoding(backIndex) & UNWIND_IS_NOT_FUNCTION_START));
+ funcStart = pageIndex.functionOffset(backIndex) + sects.dso_base;
+ }
+
if (pc < funcStart) {
if (log)
- fprintf(
- stderr,
- "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n",
- (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd);
+ fprintf(stderr,
+ "\tpc not in table, pc=0x%llX, funcStart=0x%llX, "
+ "rangeStart=0x%llX, funcEnd=0x%llX\n",
+ (uint64_t)pc, (uint64_t)funcStart, (uint64_t)rangeStart,
+ (uint64_t)funcEnd);
return false;
}
if (pc > funcEnd) {
if (log)
- fprintf(
- stderr,
- "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n",
- (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd);
+ fprintf(stderr,
+ "\tpc not in table, pc=0x%llX, funcStart=0x%llX, "
+ "rangeStart=0x%llX, funcEnd=0x%llX\n",
+ (uint64_t)pc, (uint64_t)funcStart, (uint64_t)rangeStart,
+ (uint64_t)funcEnd);
return false;
}
} else if (pageKind == UNWIND_SECOND_LEVEL_COMPRESSED) {
@@ -1998,19 +2017,55 @@ bool UnwindCursor::getInfoFromCompactEncodingSection(
high = mid;
}
}
- funcStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset
- + sects.dso_base;
+ rangeStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset +
+ sects.dso_base;
if (low < last)
funcEnd =
pageIndex.functionOffset(low + 1) + firstLevelFunctionOffset
+ sects.dso_base;
else
funcEnd = firstLevelNextPageFunctionOffset + sects.dso_base;
+
+ auto encodingAtIndex = [&](uint32_t idx) -> uint32_t {
+ uint16_t encIdx = pageIndex.encodingIndex(idx);
+ if (encIdx < sectionHeader.commonEncodingsArrayCount()) {
+ return _addressSpace.get32(
+ sects.compact_unwind_section +
+ sectionHeader.commonEncodingsArraySectionOffset() +
+ encIdx * sizeof(uint32_t));
+ } else {
+ uint16_t pageEncIdx =
+ encIdx - (uint16_t)sectionHeader.commonEncodingsArrayCount();
+ return _addressSpace.get32(secondLevelAddr +
+ pageHeader.encodingsPageOffset() +
+ pageEncIdx * sizeof(uint32_t));
+ }
+ };
+
+ encoding = encodingAtIndex(low);
+
+ // If UNWIND_IS_NOT_FUNCTION_START is set, walk backwards to find the actual
+ // function start.
+ funcStart = rangeStart;
+ if (encoding & UNWIND_IS_NOT_FUNCTION_START) {
+ assert(low != 0 &&
+ "UNWIND_IS_NOT_FUNCTION_START comact unwind must be preceded by a "
+ "~UNWIND_IS_NOT_FUNCTION_START entry for the same function.");
+ uint32_t backIndex = low;
+ do {
+ --backIndex;
+ } while (backIndex > 0 &&
+ (encodingAtIndex(backIndex) & UNWIND_IS_NOT_FUNCTION_START));
+ funcStart = pageIndex.functionOffset(backIndex) +
+ firstLevelFunctionOffset + sects.dso_base;
+ }
+
if (pc < funcStart) {
_LIBUNWIND_DEBUG_LOG("malformed __unwind_info, pc=0x%llX "
"not in second level compressed unwind table. "
- "funcStart=0x%llX",
- (uint64_t) pc, (uint64_t) funcStart);
+ "funcStart=0x%llX, rangeStart=0x%llX",
+ (uint64_t)pc, (uint64_t)funcStart,
+ (uint64_t)rangeStart);
return false;
}
if (pc > funcEnd) {
@@ -2020,21 +2075,6 @@ bool UnwindCursor::getInfoFromCompactEncodingSection(
(uint64_t) pc, (uint64_t) funcEnd);
return false;
}
- uint16_t encodingIndex = pageIndex.encodingIndex(low);
- if (encodingIndex < sectionHeader.commonEncodingsArrayCount()) {
- // encoding is in common table in section header
- encoding = _addressSpace.get32(
- sects.compact_unwind_section +
- sectionHeader.commonEncodingsArraySectionOffset() +
- encodingIndex * sizeof(uint32_t));
- } else {
- // encoding is in page specific table
- uint16_t pageEncodingIndex =
- encodingIndex - (uint16_t)sectionHeader.commonEncodingsArrayCount();
- encoding = _addressSpace.get32(secondLevelAddr +
- pageHeader.encodingsPageOffset() +
- pageEncodingIndex * sizeof(uint32_t));
- }
} else {
_LIBUNWIND_DEBUG_LOG(
"malformed __unwind_info at 0x%0llX bad second level page",
@@ -2111,7 +2151,18 @@ bool UnwindCursor::getInfoFromCompactEncodingSection(
fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), "
"encoding=0x%08X, lsda=0x%08llX for funcStart=0x%llX\n",
(uint64_t) pc, encoding, (uint64_t) lsda, (uint64_t) funcStart);
+
+ // For ARM64 PAuth_LR frames, start_ip should be the pacibsppc address
+ // (rangeStart). For all other cases, start_ip should be the function start.
+#if defined(_LIBUNWIND_TARGET_AARCH64)
+ if ((encoding & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_FRAME_PAUTH_LR) {
+ _info.start_ip = rangeStart;
+ } else {
+ _info.start_ip = funcStart;
+ }
+#else
_info.start_ip = funcStart;
+#endif
_info.end_ip = funcEnd;
_info.lsda = lsda;
// We use memmove to copy the personality function as we have already manually