[AArch64][Libunwind] Add Support for FEAT_PAuthLR DWARF Instruction (#112171)
As part of FEAT_PAuthLR, a new DWARF Frame Instruction was introduced,
`DW_CFA_AARCH64_negate_ra_state_with_pc`. This instructs Libunwind that
the PC has been used with the signing instruction. This change includes
three commits
- Libunwind support for the newly introduced DWARF Instruction
- CodeGen Support for the DWARF Instructions
- Reversing the changes made in #96377. Due to
`DW_CFA_AARCH64_negate_ra_state_with_pc`'s requirements to be placed
immediately after the signing instruction, this would mean the CFI
Instruction location was not consistent with the generated location when
not using FEAT_PAuthLR. The commit reverses the changes and makes the
location consistent across the different branch protection options.
While this does have a code size effect, this is a negligible one.
For the ABI information, see here:
853286c7ab/aadwarf64/aadwarf64.rst (id23)
This commit is contained in:
@@ -74,8 +74,10 @@ private:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
static bool getRA_SIGN_STATE(A &addressSpace, R registers, pint_t cfa,
|
||||
PrologInfo &prolog);
|
||||
static bool isReturnAddressSigned(A &addressSpace, R registers, pint_t cfa,
|
||||
PrologInfo &prolog);
|
||||
static bool isReturnAddressSignedWithPC(A &addressSpace, R registers,
|
||||
pint_t cfa, PrologInfo &prolog);
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -173,8 +175,9 @@ v128 DwarfInstructions<A, R>::getSavedVectorRegister(
|
||||
}
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
template <typename A, typename R>
|
||||
bool DwarfInstructions<A, R>::getRA_SIGN_STATE(A &addressSpace, R registers,
|
||||
pint_t cfa, PrologInfo &prolog) {
|
||||
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)
|
||||
@@ -185,6 +188,22 @@ bool DwarfInstructions<A, R>::getRA_SIGN_STATE(A &addressSpace, R registers,
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename A, typename R>
|
||||
@@ -288,7 +307,7 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
|
||||
// restored. autia1716 is used instead of autia as autia1716 assembles
|
||||
// to a NOP on pre-v8.3a architectures.
|
||||
if ((R::getArch() == REGISTERS_ARM64) &&
|
||||
getRA_SIGN_STATE(addressSpace, registers, cfa, prolog) &&
|
||||
isReturnAddressSigned(addressSpace, registers, cfa, prolog) &&
|
||||
returnAddress != 0) {
|
||||
#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||
return UNW_ECROSSRASIGNING;
|
||||
@@ -296,13 +315,29 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
|
||||
register unsigned long long x17 __asm("x17") = returnAddress;
|
||||
register unsigned long long x16 __asm("x16") = cfa;
|
||||
|
||||
// These are the autia1716/autib1716 instructions. The hint instructions
|
||||
// are used here as gcc does not assemble autia1716/autib1716 for pre
|
||||
// armv8.3a targets.
|
||||
if (cieInfo.addressesSignedWithBKey)
|
||||
asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
|
||||
else
|
||||
asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
|
||||
// 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
|
||||
}
|
||||
returnAddress = x17;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -91,6 +91,9 @@ public:
|
||||
int64_t cfaExpression; // CFA = expression
|
||||
uint32_t spExtraArgSize;
|
||||
RegisterLocation savedRegisters[kMaxRegisterNumber + 1];
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
pint_t ptrAuthDiversifier;
|
||||
#endif
|
||||
enum class InitializeTime { kLazy, kNormal };
|
||||
|
||||
// When saving registers, this data structure is lazily initialized.
|
||||
@@ -799,6 +802,24 @@ bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
|
||||
}
|
||||
break;
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
case DW_CFA_AARCH64_negate_ra_state_with_pc: {
|
||||
int64_t value =
|
||||
results->savedRegisters[UNW_AARCH64_RA_SIGN_STATE].value ^ 0x3;
|
||||
results->setRegisterValue(UNW_AARCH64_RA_SIGN_STATE, value,
|
||||
initialState);
|
||||
// When calculating the value of the PC, it is assumed that the CFI
|
||||
// instruction is placed before the signing instruction, however it is
|
||||
// placed after. Because of this, we need to take into account the CFI
|
||||
// instruction is one instruction call later than expected, and reduce
|
||||
// the PC value by 4 bytes to compensate.
|
||||
results->ptrAuthDiversifier = fdeInfo.pcStart + codeOffset - 0x4;
|
||||
_LIBUNWIND_TRACE_DWARF(
|
||||
"DW_CFA_AARCH64_negate_ra_state_with_pc(pc=0x%" PRIx64 ")\n",
|
||||
static_cast<uint64_t>(results->ptrAuthDiversifier));
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#else
|
||||
(void)arch;
|
||||
#endif
|
||||
|
||||
@@ -18,43 +18,43 @@
|
||||
|
||||
// DWARF unwind instructions
|
||||
enum {
|
||||
DW_CFA_nop = 0x0,
|
||||
DW_CFA_set_loc = 0x1,
|
||||
DW_CFA_advance_loc1 = 0x2,
|
||||
DW_CFA_advance_loc2 = 0x3,
|
||||
DW_CFA_advance_loc4 = 0x4,
|
||||
DW_CFA_offset_extended = 0x5,
|
||||
DW_CFA_restore_extended = 0x6,
|
||||
DW_CFA_undefined = 0x7,
|
||||
DW_CFA_same_value = 0x8,
|
||||
DW_CFA_register = 0x9,
|
||||
DW_CFA_remember_state = 0xA,
|
||||
DW_CFA_restore_state = 0xB,
|
||||
DW_CFA_def_cfa = 0xC,
|
||||
DW_CFA_def_cfa_register = 0xD,
|
||||
DW_CFA_def_cfa_offset = 0xE,
|
||||
DW_CFA_def_cfa_expression = 0xF,
|
||||
DW_CFA_expression = 0x10,
|
||||
DW_CFA_nop = 0x0,
|
||||
DW_CFA_set_loc = 0x1,
|
||||
DW_CFA_advance_loc1 = 0x2,
|
||||
DW_CFA_advance_loc2 = 0x3,
|
||||
DW_CFA_advance_loc4 = 0x4,
|
||||
DW_CFA_offset_extended = 0x5,
|
||||
DW_CFA_restore_extended = 0x6,
|
||||
DW_CFA_undefined = 0x7,
|
||||
DW_CFA_same_value = 0x8,
|
||||
DW_CFA_register = 0x9,
|
||||
DW_CFA_remember_state = 0xA,
|
||||
DW_CFA_restore_state = 0xB,
|
||||
DW_CFA_def_cfa = 0xC,
|
||||
DW_CFA_def_cfa_register = 0xD,
|
||||
DW_CFA_def_cfa_offset = 0xE,
|
||||
DW_CFA_def_cfa_expression = 0xF,
|
||||
DW_CFA_expression = 0x10,
|
||||
DW_CFA_offset_extended_sf = 0x11,
|
||||
DW_CFA_def_cfa_sf = 0x12,
|
||||
DW_CFA_def_cfa_offset_sf = 0x13,
|
||||
DW_CFA_val_offset = 0x14,
|
||||
DW_CFA_val_offset_sf = 0x15,
|
||||
DW_CFA_val_expression = 0x16,
|
||||
DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta
|
||||
DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register
|
||||
DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register
|
||||
DW_CFA_def_cfa_sf = 0x12,
|
||||
DW_CFA_def_cfa_offset_sf = 0x13,
|
||||
DW_CFA_val_offset = 0x14,
|
||||
DW_CFA_val_offset_sf = 0x15,
|
||||
DW_CFA_val_expression = 0x16,
|
||||
DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta
|
||||
DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register
|
||||
DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register
|
||||
|
||||
// GNU extensions
|
||||
DW_CFA_GNU_window_save = 0x2D,
|
||||
DW_CFA_GNU_args_size = 0x2E,
|
||||
DW_CFA_GNU_window_save = 0x2D,
|
||||
DW_CFA_GNU_args_size = 0x2E,
|
||||
DW_CFA_GNU_negative_offset_extended = 0x2F,
|
||||
|
||||
// AARCH64 extensions
|
||||
DW_CFA_AARCH64_negate_ra_state = 0x2D
|
||||
DW_CFA_AARCH64_negate_ra_state_with_pc = 0x2C,
|
||||
DW_CFA_AARCH64_negate_ra_state = 0x2D
|
||||
};
|
||||
|
||||
|
||||
// FSF exception handling Pointer-Encoding constants
|
||||
// Used in CFI augmentation by GCC
|
||||
enum {
|
||||
|
||||
Reference in New Issue
Block a user