Skip to content

Commit 98681d5

Browse files
committed
[PAuthLR] Add support for FEAT_PAuth_LR to libunwind
This introduces support for unwinding programs where return addresses have been signed using FEAT_PAuth_Lr, where the value of PC is used as a diversifier (-mbranch-protection=pac-ret+pc). A new vendor specific call frame instruction is added, named `DW_CFA_AARCH64_negate_ra_state_with_pc`, to instruct the unwinder tocapture the value of PC at the point of signing and update bit 1 of the existing `RA_SIGN_STATE` pseudo-register to flag the need to use it for authentication. See ARM-software/abi-aa#245 for the ABI change. Authored-by: pratlucas <[email protected]>
1 parent bec839d commit 98681d5

File tree

3 files changed

+64
-13
lines changed

3 files changed

+64
-13
lines changed

libunwind/src/DwarfInstructions.hpp

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,10 @@ class DwarfInstructions {
7474
__builtin_unreachable();
7575
}
7676
#if defined(_LIBUNWIND_TARGET_AARCH64)
77-
static bool getRA_SIGN_STATE(A &addressSpace, R registers, pint_t cfa,
78-
PrologInfo &prolog);
77+
static bool isReturnAddressSigned(A &addressSpace, R registers, pint_t cfa,
78+
PrologInfo &prolog);
79+
static bool isReturnAddressSignedWithPC(A &addressSpace, R registers,
80+
pint_t cfa, PrologInfo &prolog);
7981
#endif
8082
};
8183

@@ -173,8 +175,9 @@ v128 DwarfInstructions<A, R>::getSavedVectorRegister(
173175
}
174176
#if defined(_LIBUNWIND_TARGET_AARCH64)
175177
template <typename A, typename R>
176-
bool DwarfInstructions<A, R>::getRA_SIGN_STATE(A &addressSpace, R registers,
177-
pint_t cfa, PrologInfo &prolog) {
178+
bool DwarfInstructions<A, R>::isReturnAddressSigned(A &addressSpace,
179+
R registers, pint_t cfa,
180+
PrologInfo &prolog) {
178181
pint_t raSignState;
179182
auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE];
180183
if (regloc.location == CFI_Parser<A>::kRegisterUnused)
@@ -185,6 +188,22 @@ bool DwarfInstructions<A, R>::getRA_SIGN_STATE(A &addressSpace, R registers,
185188
// Only bit[0] is meaningful.
186189
return raSignState & 0x01;
187190
}
191+
192+
template <typename A, typename R>
193+
bool DwarfInstructions<A, R>::isReturnAddressSignedWithPC(A &addressSpace,
194+
R registers,
195+
pint_t cfa,
196+
PrologInfo &prolog) {
197+
pint_t raSignState;
198+
auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE];
199+
if (regloc.location == CFI_Parser<A>::kRegisterUnused)
200+
raSignState = static_cast<pint_t>(regloc.value);
201+
else
202+
raSignState = getSavedRegister(addressSpace, registers, cfa, regloc);
203+
204+
// Only bit[1] is meaningful.
205+
return raSignState & 0x02;
206+
}
188207
#endif
189208

190209
template <typename A, typename R>
@@ -288,21 +307,32 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
288307
// restored. autia1716 is used instead of autia as autia1716 assembles
289308
// to a NOP on pre-v8.3a architectures.
290309
if ((R::getArch() == REGISTERS_ARM64) &&
291-
getRA_SIGN_STATE(addressSpace, registers, cfa, prolog) &&
310+
isReturnAddressSigned(addressSpace, registers, cfa, prolog) &&
292311
returnAddress != 0) {
293312
#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
294313
return UNW_ECROSSRASIGNING;
295314
#else
296315
register unsigned long long x17 __asm("x17") = returnAddress;
297316
register unsigned long long x16 __asm("x16") = cfa;
298317

299-
// These are the autia1716/autib1716 instructions. The hint instructions
300-
// are used here as gcc does not assemble autia1716/autib1716 for pre
301-
// armv8.3a targets.
302-
if (cieInfo.addressesSignedWithBKey)
303-
asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
304-
else
305-
asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
318+
// We use the hint versions of the authentication instructions below to
319+
// ensure they're assembled by the compiler even for targets with no
320+
// FEAT_PAuth/FEAT_PAuth_LR support.
321+
if(isReturnAddressSignedWithPC(addressSpace, registers, cfa, prolog)) {
322+
register unsigned long long x15 __asm("x15") = prolog.ptrAuthDiversifier;
323+
if(cieInfo.addressesSignedWithBKey) {
324+
asm("hint 0x27\n\t" // pacm
325+
"hint 0xe" : "+r"(x17) : "r"(x16), "r"(x15)); // autib1716
326+
} else {
327+
asm("hint 0x27\n\t" // pacm
328+
"hint 0xc" : "+r"(x17) : "r"(x16), "r"(x15)); // autia1716
329+
}
330+
} else {
331+
if (cieInfo.addressesSignedWithBKey)
332+
asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
333+
else
334+
asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
335+
}
306336
returnAddress = x17;
307337
#endif
308338
}

libunwind/src/DwarfParser.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class CFI_Parser {
9191
int64_t cfaExpression; // CFA = expression
9292
uint32_t spExtraArgSize;
9393
RegisterLocation savedRegisters[kMaxRegisterNumber + 1];
94+
#if defined(_LIBUNWIND_TARGET_AARCH64)
95+
pint_t ptrAuthDiversifier;
96+
#endif
9497
enum class InitializeTime { kLazy, kNormal };
9598

9699
// When saving registers, this data structure is lazily initialized.
@@ -799,6 +802,23 @@ bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
799802
}
800803
break;
801804

805+
#if defined(_LIBUNWIND_TARGET_AARCH64)
806+
case DW_CFA_AARCH64_negate_ra_state_with_pc: {
807+
int64_t value =
808+
results->savedRegisters[UNW_AARCH64_RA_SIGN_STATE].value ^ 0x3;
809+
results->setRegisterValue(UNW_AARCH64_RA_SIGN_STATE, value,
810+
initialState);
811+
// When calucating the value of the PC, it is assumed that the CFI instruction
812+
// is placed before the signing instruction, however it is placed after. Because
813+
// of this, we need to take into account the CFI instruction is one instruction
814+
// call later than expected, and reduce the PC value by 4 bytes to compensate.
815+
results->ptrAuthDiversifier = fdeInfo.pcStart + codeOffset - 0x4;
816+
_LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state_with_pc(pc=0x%" PRIx64 ")\n",
817+
static_cast<uint64_t>(results->ptrAuthDiversifier));
818+
}
819+
break;
820+
#endif
821+
802822
#else
803823
(void)arch;
804824
#endif

libunwind/src/dwarf2.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ enum {
5151
DW_CFA_GNU_negative_offset_extended = 0x2F,
5252

5353
// AARCH64 extensions
54-
DW_CFA_AARCH64_negate_ra_state = 0x2D
54+
DW_CFA_AARCH64_negate_ra_state_with_pc = 0x2C,
55+
DW_CFA_AARCH64_negate_ra_state = 0x2D
5556
};
5657

5758

0 commit comments

Comments
 (0)