[libunwind] [libunwind][arm64e] Mach-O Support unwinding PAuth_LR frames (PR #202772)
Jon Roelofs via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 26 11:52:08 PDT 2026
https://github.com/jroelofs updated https://github.com/llvm/llvm-project/pull/202772
>From 25829ccdbe4d045170328a97ba55f23600403e27 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Tue, 9 Jun 2026 14:04:32 -0700
Subject: [PATCH 1/9] [libunwind][arm64e] Mach-O Support unwinding PAuth_LR
frames
Under PAuth_LR, compact unwind entries marked UNWIND_ARM64_MODE_FRAME_PAUTH_LR
are signed with the PC of the pacibsppc at the start of the range. The compiler
may emit those entries with UNWIND_IS_NOT_FUNCTION_START when the pacibsppc is
not the first instruction in the function; the unwinder must scan backwards past
such entries to find the actual function start (and its LSDA pointer).
When unwinding a PAuth_LR frame, authenticate and re-sign using setIPPAuthLR(),
which calls __builtin_ptrauth_auth_with_pc_and_resign when available, falling
back to inline asm (autib171615 + pacib) for compilers that lack the builtin.
To reduce the size of Compact Unwind entries for shrinkwrapped frames, new
offset bits encode the distance in multiples of 4 bytes from the range start to
the pacibsppc. The compiler emits paired FRAMELESS/FRAMELESS+PAUTH_LR entries
that the linker merges into a single FRAME_PAUTH_LR | (offset/4) << 12 encoding.
rdar://161907302&169746432&171118419
---
.../include/mach-o/compact_unwind_encoding.h | 13 ++-
libunwind/src/CompactUnwinder.hpp | 20 ++++-
libunwind/src/DwarfInstructions.hpp | 22 +++--
libunwind/src/Registers.hpp | 25 ++++++
libunwind/src/UnwindCursor.hpp | 83 ++++++++++++++-----
5 files changed, 131 insertions(+), 32 deletions(-)
diff --git a/libunwind/include/mach-o/compact_unwind_encoding.h b/libunwind/include/mach-o/compact_unwind_encoding.h
index c495b83c40b90..b6901cfb9055d 100644
--- a/libunwind/include/mach-o/compact_unwind_encoding.h
+++ b/libunwind/include/mach-o/compact_unwind_encoding.h
@@ -288,6 +288,10 @@ enum {
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,
@@ -323,8 +327,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 929f540b662b4..0daf76a373296 100644
--- a/libunwind/src/CompactUnwinder.hpp
+++ b/libunwind/src/CompactUnwinder.hpp
@@ -520,6 +520,7 @@ int CompactUnwinder_arm64<A>::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<A>::stepWithCompactEncodingFrameless(
template <typename A>
int CompactUnwinder_arm64<A>::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,22 @@ int CompactUnwinder_arm64<A>::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 165c4a99e9a92..62c4a71f87be3 100644
--- a/libunwind/src/DwarfInstructions.hpp
+++ b/libunwind/src/DwarfInstructions.hpp
@@ -302,16 +302,27 @@ int DwarfInstructions<A, R>::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 (isReturnAddressSignedWithPC(addressSpace, registers, cfa, prolog)) {
+ 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
@@ -353,6 +364,7 @@ int DwarfInstructions<A, R>::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 c88422a6d5f4f..abd0fca239a09 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1917,6 +1917,31 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
#endif
_registers.__pc = value;
}
+ __attribute__((target("pauth-lr")))
+ void setIPPAuthLR(uint64_t value, uint64_t signing_pc) {
+#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
+#if __has_builtin(__builtin_ptrauth_auth_with_pc_and_resign)
+ value = (uint64_t)ptrauth_auth_with_pc_and_resign(
+ (void *)value,
+ ptrauth_key_process_dependent_code,
+ (void *)getSP(),
+ (void *)signing_pc,
+ ptrauth_key_return_address,
+ &_registers.__pc);
+#else
+ register uint64_t x17 __asm("x17") = value;
+ register uint64_t x16 __asm("x16") = getSP();
+ 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 ff92c9e0a844a..468107f3ded49 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1869,6 +1869,7 @@ bool UnwindCursor<A, R>::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;
@@ -1908,21 +1909,32 @@ bool UnwindCursor<A, R>::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) {
+ 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);
+ "\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);
+ "\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) {
@@ -1956,7 +1968,7 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
high = mid;
}
}
- funcStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset
+ rangeStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset
+ sects.dso_base;
if (low < last)
funcEnd =
@@ -1964,11 +1976,40 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
+ 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) {
+ 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) {
@@ -1978,21 +2019,6 @@ bool UnwindCursor<A, R>::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",
@@ -2069,7 +2095,18 @@ bool UnwindCursor<A, R>::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
>From b4bc71c4aebd6233b6b702b2db4eeda894ddf7e9 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Tue, 9 Jun 2026 14:15:26 -0700
Subject: [PATCH 2/9] clang-format
---
.../include/mach-o/compact_unwind_encoding.h | 45 ++++++++--------
libunwind/src/CompactUnwinder.hpp | 1 -
libunwind/src/Registers.hpp | 15 +++---
libunwind/src/UnwindCursor.hpp | 52 +++++++++++--------
4 files changed, 57 insertions(+), 56 deletions(-)
diff --git a/libunwind/include/mach-o/compact_unwind_encoding.h b/libunwind/include/mach-o/compact_unwind_encoding.h
index b6901cfb9055d..029b781287e02 100644
--- a/libunwind/include/mach-o/compact_unwind_encoding.h
+++ b/libunwind/include/mach-o/compact_unwind_encoding.h
@@ -284,27 +284,27 @@ enum {
// 24-bits offset of DWARF FDE in __eh_frame section
//
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,
- UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
- UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
- UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
- UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
- UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
- UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
- UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
-
- UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
- UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+ 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,
+ UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
+ UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
+ UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
+ UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
+ UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
+ UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
+ UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
+
+ UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
+ UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
};
// For arm64 there are three modes for the compact unwind encoding:
// UNWIND_ARM64_MODE_FRAME:
@@ -335,9 +335,6 @@ enum {
// to the `pacibsppc`, with 0 indicating that the `pacibsppc` is the first
// instruction in the range.
-
-
-
////////////////////////////////////////////////////////////////////////////////
//
// Relocatable Object Files: __LD,__compact_unwind
diff --git a/libunwind/src/CompactUnwinder.hpp b/libunwind/src/CompactUnwinder.hpp
index 0daf76a373296..bc9c569a8ccb9 100644
--- a/libunwind/src/CompactUnwinder.hpp
+++ b/libunwind/src/CompactUnwinder.hpp
@@ -715,7 +715,6 @@ int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
registers.setIP(linkRegister);
}
-
return UNW_STEP_SUCCESS;
}
#endif // _LIBUNWIND_TARGET_AARCH64
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index abd0fca239a09..1dce396d111d3 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1917,17 +1917,13 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
#endif
_registers.__pc = value;
}
- __attribute__((target("pauth-lr")))
- void setIPPAuthLR(uint64_t value, uint64_t signing_pc) {
+ __attribute__((target("pauth-lr"))) void setIPPAuthLR(uint64_t value,
+ uint64_t signing_pc) {
#if defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
#if __has_builtin(__builtin_ptrauth_auth_with_pc_and_resign)
value = (uint64_t)ptrauth_auth_with_pc_and_resign(
- (void *)value,
- ptrauth_key_process_dependent_code,
- (void *)getSP(),
- (void *)signing_pc,
- ptrauth_key_return_address,
- &_registers.__pc);
+ (void *)value, ptrauth_key_process_dependent_code, (void *)getSP(),
+ (void *)signing_pc, ptrauth_key_return_address, &_registers.__pc);
#else
register uint64_t x17 __asm("x17") = value;
register uint64_t x16 __asm("x16") = getSP();
@@ -1935,7 +1931,8 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
uint64_t resignDisc = (uint64_t)&_registers.__pc;
asm("autib171615\n\t"
"pacib %0, %3"
- : "+r"(x17) : "r"(x16), "r"(x15), "r"(resignDisc));
+ : "+r"(x17)
+ : "r"(x16), "r"(x15), "r"(resignDisc));
value = x17;
#endif
#endif
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 468107f3ded49..9e39b6f8b6e7a 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1911,30 +1911,34 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
encoding = pageIndex.encoding(low);
rangeStart = pageIndex.functionOffset(low) + sects.dso_base;
- // If UNWIND_IS_NOT_FUNCTION_START is set, walk backwards to find the actual function start.
+ // 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) {
uint32_t backIndex = low;
do {
--backIndex;
- } while (backIndex > 0 && (pageIndex.encoding(backIndex) & UNWIND_IS_NOT_FUNCTION_START));
+ } 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, rangeStart=0x%llX, funcEnd=0x%llX\n",
- (uint64_t) pc, (uint64_t) funcStart, (uint64_t) rangeStart, (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, rangeStart=0x%llX, funcEnd=0x%llX\n",
- (uint64_t) pc, (uint64_t) funcStart, (uint64_t) rangeStart, (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) {
@@ -1968,8 +1972,8 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
high = mid;
}
}
- rangeStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset
- + sects.dso_base;
+ rangeStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset +
+ sects.dso_base;
if (low < last)
funcEnd =
pageIndex.functionOffset(low + 1) + firstLevelFunctionOffset
@@ -1985,31 +1989,35 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
sectionHeader.commonEncodingsArraySectionOffset() +
encIdx * sizeof(uint32_t));
} else {
- uint16_t pageEncIdx = encIdx - (uint16_t)sectionHeader.commonEncodingsArrayCount();
+ uint16_t pageEncIdx =
+ encIdx - (uint16_t)sectionHeader.commonEncodingsArrayCount();
return _addressSpace.get32(secondLevelAddr +
- pageHeader.encodingsPageOffset() +
- pageEncIdx * sizeof(uint32_t));
+ 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.
+ // 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) {
uint32_t backIndex = low;
do {
--backIndex;
- } while (backIndex > 0 && (encodingAtIndex(backIndex) & UNWIND_IS_NOT_FUNCTION_START));
- funcStart = pageIndex.functionOffset(backIndex) + firstLevelFunctionOffset
- + sects.dso_base;
+ } 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, rangeStart=0x%llX",
- (uint64_t) pc, (uint64_t) funcStart, (uint64_t) rangeStart);
+ (uint64_t)pc, (uint64_t)funcStart,
+ (uint64_t)rangeStart);
return false;
}
if (pc > funcEnd) {
@@ -2096,8 +2104,8 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
"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.
+ // 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;
>From 92f161ac8e9608b7756b06f1851ebe428b557320 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Thu, 11 Jun 2026 16:29:12 -0700
Subject: [PATCH 3/9] clang-format
---
libunwind/src/UnwindCursor.hpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 9e39b6f8b6e7a..8707d0df09300 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -2104,8 +2104,8 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
"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.
+ // 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;
>From 7449cc4be758b9f8ad8998cde78e1491679beaa5 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Fri, 12 Jun 2026 14:16:52 -0700
Subject: [PATCH 4/9] un-clang-format an enum
---
.../include/mach-o/compact_unwind_encoding.h | 42 +++++++++----------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/libunwind/include/mach-o/compact_unwind_encoding.h b/libunwind/include/mach-o/compact_unwind_encoding.h
index 029b781287e02..214acc0330d59 100644
--- a/libunwind/include/mach-o/compact_unwind_encoding.h
+++ b/libunwind/include/mach-o/compact_unwind_encoding.h
@@ -284,27 +284,27 @@ enum {
// 24-bits offset of DWARF FDE in __eh_frame section
//
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,
- UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
- UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
- UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
- UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
- UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
- UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
- UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
-
- UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
- UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+ 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,
+ UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
+ UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
+ UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
+ UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
+ UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
+ UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
+ UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
+
+ UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
+ UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
};
// For arm64 there are three modes for the compact unwind encoding:
// UNWIND_ARM64_MODE_FRAME:
>From f17e1540821efe359db1f75dd5d5137c1b3a75e2 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Fri, 12 Jun 2026 16:29:01 -0700
Subject: [PATCH 5/9] tell clang-format to go away
---
libunwind/include/mach-o/compact_unwind_encoding.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/libunwind/include/mach-o/compact_unwind_encoding.h b/libunwind/include/mach-o/compact_unwind_encoding.h
index 214acc0330d59..d1770864cf647 100644
--- a/libunwind/include/mach-o/compact_unwind_encoding.h
+++ b/libunwind/include/mach-o/compact_unwind_encoding.h
@@ -41,11 +41,13 @@ 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
@@ -67,6 +69,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 +87,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 +99,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:
@@ -202,6 +208,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 +226,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 +238,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:
@@ -283,6 +294,7 @@ 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,
@@ -306,6 +318,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
>From dfbfbaa341b8a4efae73018924f0efd64e9a87ae Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Mon, 15 Jun 2026 10:51:10 -0700
Subject: [PATCH 6/9] remove blank lines that clang-format insists on
---
libunwind/include/mach-o/compact_unwind_encoding.h | 8 --------
1 file changed, 8 deletions(-)
diff --git a/libunwind/include/mach-o/compact_unwind_encoding.h b/libunwind/include/mach-o/compact_unwind_encoding.h
index d1770864cf647..2bfa4dd7b13a1 100644
--- a/libunwind/include/mach-o/compact_unwind_encoding.h
+++ b/libunwind/include/mach-o/compact_unwind_encoding.h
@@ -39,7 +39,6 @@
//
typedef uint32_t compact_unwind_encoding_t;
-
// architecture independent bits
// clang-format off
enum {
@@ -49,9 +48,6 @@ enum {
};
// clang-format on
-
-
-
//
// x86
//
@@ -188,9 +184,6 @@ enum {
//}
//
-
-
-
//
// x86_64
//
@@ -278,7 +271,6 @@ enum {
// function.
//
-
// ARM64
//
// 1-bit: start
>From 425508c4d0291ddb3020c94203764479f31e5c9a Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Fri, 26 Jun 2026 11:22:31 -0700
Subject: [PATCH 7/9] has_builtin
---
libunwind/src/Registers.hpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 1dce396d111d3..6212736f5ee41 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1920,6 +1920,9 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
__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_buitlin(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_process_dependent_code, (void *)getSP(),
>From 3b065dfadee1dad0365352ba922a5bed4c27644d Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Fri, 26 Jun 2026 11:22:53 -0700
Subject: [PATCH 8/9] ptrauth_key_return_address for auth_with_pc
---
libunwind/src/Registers.hpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 6212736f5ee41..d6a71d653c57d 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1925,7 +1925,7 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
#endif
#if __has_builtin(__builtin_ptrauth_auth_with_pc_and_resign)
value = (uint64_t)ptrauth_auth_with_pc_and_resign(
- (void *)value, ptrauth_key_process_dependent_code, (void *)getSP(),
+ (void *)value, ptrauth_key_return_address, (void *)getSP(),
(void *)signing_pc, ptrauth_key_return_address, &_registers.__pc);
#else
register uint64_t x17 __asm("x17") = value;
>From a7d1aadbde73cbce8ae2e4daeea716e17c0071d2 Mon Sep 17 00:00:00 2001
From: Jon Roelofs <jonathan_roelofs at apple.com>
Date: Fri, 26 Jun 2026 11:39:04 -0700
Subject: [PATCH 9/9] drop unnecessary casts
---
libunwind/src/Registers.hpp | 4 ++--
libunwind/src/UnwindCursor.hpp | 6 ++++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index d6a71d653c57d..1ca341ee30102 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1925,8 +1925,8 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
#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, (void *)getSP(),
- (void *)signing_pc, ptrauth_key_return_address, &_registers.__pc);
+ (void *)value, ptrauth_key_return_address, getSP(),
+ signing_pc, ptrauth_key_return_address, &_registers.__pc);
#else
register uint64_t x17 __asm("x17") = value;
register uint64_t x16 __asm("x16") = getSP();
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 8707d0df09300..1384dcba392e2 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -1915,6 +1915,9 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
// 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;
@@ -2003,6 +2006,9 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(
// 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;
More information about the cfe-commits
mailing list