Skip to content

Commit

Permalink
[AArch64][Libunwind] Add Support for FEAT_PAuthLR DWARF Instruction (l…
Browse files Browse the repository at this point in the history
…lvm#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 llvm#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:
https://github.com/ARM-software/abi-aa/blob/853286c7ab66048e4b819682ce17f567b77a0291/aadwarf64/aadwarf64.rst#id23
  • Loading branch information
Stylie777 authored Oct 28, 2024
1 parent a4fd3db commit 86f76c3
Show file tree
Hide file tree
Showing 34 changed files with 733 additions and 241 deletions.
59 changes: 47 additions & 12 deletions libunwind/src/DwarfInstructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ class DwarfInstructions {
__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
};

Expand Down Expand Up @@ -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)
Expand All @@ -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>
Expand Down Expand Up @@ -288,21 +307,37 @@ 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;
#else
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
}
Expand Down
21 changes: 21 additions & 0 deletions libunwind/src/DwarfParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class CFI_Parser {
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.
Expand Down Expand Up @@ -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
Expand Down
58 changes: 29 additions & 29 deletions libunwind/src/dwarf2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/Dwarf.def
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,7 @@ HANDLE_DW_CFA(0x16, val_expression)
// Vendor extensions:
HANDLE_DW_CFA_PRED(0x1d, MIPS_advance_loc8, SELECT_MIPS64)
HANDLE_DW_CFA_PRED(0x2d, GNU_window_save, SELECT_SPARC)
HANDLE_DW_CFA_PRED(0x2c, AARCH64_negate_ra_state_with_pc, SELECT_AARCH64)
HANDLE_DW_CFA_PRED(0x2d, AARCH64_negate_ra_state, SELECT_AARCH64)
HANDLE_DW_CFA_PRED(0x2e, GNU_args_size, SELECT_X86)
// Heterogeneous Debugging Extension defined at
Expand Down
7 changes: 7 additions & 0 deletions llvm/include/llvm/MC/MCDwarf.h
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ class MCCFIInstruction {
OpRegister,
OpWindowSave,
OpNegateRAState,
OpNegateRAStateWithPC,
OpGnuArgsSize,
OpLabel,
};
Expand Down Expand Up @@ -642,6 +643,12 @@ class MCCFIInstruction {
return MCCFIInstruction(OpNegateRAState, L, 0, INT64_C(0), Loc);
}

/// .cfi_negate_ra_state_with_pc AArch64 negate RA state with PC.
static MCCFIInstruction createNegateRAStateWithPC(MCSymbol *L,
SMLoc Loc = {}) {
return MCCFIInstruction(OpNegateRAStateWithPC, L, 0, INT64_C(0), Loc);
}

/// .cfi_restore says that the rule for Register is now the same as it
/// was at the beginning of the function, after all initial instructions added
/// by .cfi_startproc were executed.
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/MC/MCStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,7 @@ class MCStreamer {
SMLoc Loc = {});
virtual void emitCFIWindowSave(SMLoc Loc = {});
virtual void emitCFINegateRAState(SMLoc Loc = {});
virtual void emitCFINegateRAStateWithPC(SMLoc Loc = {});
virtual void emitCFILabelDirective(SMLoc Loc, StringRef Name);

virtual void emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc = SMLoc());
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ void AsmPrinter::emitCFIInstruction(const MCCFIInstruction &Inst) const {
case MCCFIInstruction::OpNegateRAState:
OutStreamer->emitCFINegateRAState(Loc);
break;
case MCCFIInstruction::OpNegateRAStateWithPC:
OutStreamer->emitCFINegateRAStateWithPC(Loc);
break;
case MCCFIInstruction::OpSameValue:
OutStreamer->emitCFISameValue(Inst.getRegister(), Loc);
break;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/CFIInstrInserter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {
case MCCFIInstruction::OpEscape:
case MCCFIInstruction::OpWindowSave:
case MCCFIInstruction::OpNegateRAState:
case MCCFIInstruction::OpNegateRAStateWithPC:
case MCCFIInstruction::OpGnuArgsSize:
case MCCFIInstruction::OpLabel:
break;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/CodeGen/MIRParser/MILexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) {
.Case("window_save", MIToken::kw_cfi_window_save)
.Case("negate_ra_sign_state",
MIToken::kw_cfi_aarch64_negate_ra_sign_state)
.Case("negate_ra_sign_state_with_pc",
MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc)
.Case("blockaddress", MIToken::kw_blockaddress)
.Case("intrinsic", MIToken::kw_intrinsic)
.Case("target-index", MIToken::kw_target_index)
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/MIRParser/MILexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ struct MIToken {
kw_cfi_undefined,
kw_cfi_window_save,
kw_cfi_aarch64_negate_ra_sign_state,
kw_cfi_aarch64_negate_ra_sign_state_with_pc,
kw_blockaddress,
kw_intrinsic,
kw_target_index,
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/CodeGen/MIRParser/MIParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2576,6 +2576,10 @@ bool MIParser::parseCFIOperand(MachineOperand &Dest) {
case MIToken::kw_cfi_aarch64_negate_ra_sign_state:
CFIIndex = MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr));
break;
case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc:
CFIIndex =
MF.addFrameInst(MCCFIInstruction::createNegateRAStateWithPC(nullptr));
break;
case MIToken::kw_cfi_escape: {
std::string Values;
if (parseCFIEscapeValues(Values))
Expand Down Expand Up @@ -2931,6 +2935,7 @@ bool MIParser::parseMachineOperand(const unsigned OpCode, const unsigned OpIdx,
case MIToken::kw_cfi_undefined:
case MIToken::kw_cfi_window_save:
case MIToken::kw_cfi_aarch64_negate_ra_sign_state:
case MIToken::kw_cfi_aarch64_negate_ra_sign_state_with_pc:
return parseCFIOperand(Dest);
case MIToken::kw_blockaddress:
return parseBlockAddressOperand(Dest);
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/CodeGen/MachineOperand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,10 @@ static void printCFI(raw_ostream &OS, const MCCFIInstruction &CFI,
if (MCSymbol *Label = CFI.getLabel())
MachineOperand::printSymbol(OS, *Label);
break;
case MCCFIInstruction::OpNegateRAStateWithPC:
OS << "negate_ra_sign_state_with_pc ";
if (MCSymbol *Label = CFI.getLabel())
MachineOperand::printSymbol(OS, *Label);
default:
// TODO: Print the other CFI Operations.
OS << "<unserializable cfi directive>";
Expand Down
24 changes: 24 additions & 0 deletions llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset,
case DW_CFA_remember_state:
case DW_CFA_restore_state:
case DW_CFA_GNU_window_save:
case DW_CFA_AARCH64_negate_ra_state_with_pc:
// No operands
addInstruction(Opcode);
break;
Expand Down Expand Up @@ -666,6 +667,28 @@ Error UnwindTable::parseRows(const CFIProgram &CFIP, UnwindRow &Row,
}
break;

case dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc: {
constexpr uint32_t AArch64DWARFPAuthRaState = 34;
auto LRLoc = Row.getRegisterLocations().getRegisterLocation(
AArch64DWARFPAuthRaState);
if (LRLoc) {
if (LRLoc->getLocation() == UnwindLocation::Constant) {
// Toggle the constant value of bits[1:0] from 0 to 1 or 1 to 0.
LRLoc->setConstant(LRLoc->getConstant() ^ 0x3);
} else {
return createStringError(
errc::invalid_argument,
"%s encountered when existing rule for this register is not "
"a constant",
CFIP.callFrameString(Inst.Opcode).str().c_str());
}
} else {
Row.getRegisterLocations().setRegisterLocation(
AArch64DWARFPAuthRaState, UnwindLocation::createIsConstant(0x3));
}
break;
}

case dwarf::DW_CFA_undefined: {
llvm::Expected<uint64_t> RegNum = Inst.getOperandAsUnsigned(CFIP, 0);
if (!RegNum)
Expand Down Expand Up @@ -847,6 +870,7 @@ CFIProgram::getOperandTypes() {
DECLARE_OP0(DW_CFA_remember_state);
DECLARE_OP0(DW_CFA_restore_state);
DECLARE_OP0(DW_CFA_GNU_window_save);
DECLARE_OP0(DW_CFA_AARCH64_negate_ra_state_with_pc);
DECLARE_OP1(DW_CFA_GNU_args_size, OT_Offset);
DECLARE_OP0(DW_CFA_nop);

Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/MC/MCAsmStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ class MCAsmStreamer final : public MCStreamer {
SMLoc Loc) override;
void emitCFIWindowSave(SMLoc Loc) override;
void emitCFINegateRAState(SMLoc Loc) override;
void emitCFINegateRAStateWithPC(SMLoc Loc) override;
void emitCFIReturnColumn(int64_t Register) override;
void emitCFILabelDirective(SMLoc Loc, StringRef Name) override;

Expand Down Expand Up @@ -2145,6 +2146,12 @@ void MCAsmStreamer::emitCFINegateRAState(SMLoc Loc) {
EmitEOL();
}

void MCAsmStreamer::emitCFINegateRAStateWithPC(SMLoc Loc) {
MCStreamer::emitCFINegateRAStateWithPC(Loc);
OS << "\t.cfi_negate_ra_state_with_pc";
EmitEOL();
}

void MCAsmStreamer::emitCFIReturnColumn(int64_t Register) {
MCStreamer::emitCFIReturnColumn(Register);
OS << "\t.cfi_return_column ";
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/MC/MCDwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,10 @@ void FrameEmitterImpl::emitCFIInstruction(const MCCFIInstruction &Instr) {
Streamer.emitInt8(dwarf::DW_CFA_AARCH64_negate_ra_state);
return;

case MCCFIInstruction::OpNegateRAStateWithPC:
Streamer.emitInt8(dwarf::DW_CFA_AARCH64_negate_ra_state_with_pc);
return;

case MCCFIInstruction::OpUndefined: {
unsigned Reg = Instr.getRegister();
Streamer.emitInt8(dwarf::DW_CFA_undefined);
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/MC/MCStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,16 @@ void MCStreamer::emitCFINegateRAState(SMLoc Loc) {
CurFrame->Instructions.push_back(Instruction);
}

void MCStreamer::emitCFINegateRAStateWithPC(SMLoc Loc) {
MCSymbol *Label = emitCFILabel();
MCCFIInstruction Instruction =
MCCFIInstruction::createNegateRAStateWithPC(Label, Loc);
MCDwarfFrameInfo *CurFrame = getCurrentDwarfFrameInfo();
if (!CurFrame)
return;
CurFrame->Instructions.push_back(Instruction);
}

void MCStreamer::emitCFIReturnColumn(int64_t Register) {
MCDwarfFrameInfo *CurFrame = getCurrentDwarfFrameInfo();
if (!CurFrame)
Expand Down
Loading

0 comments on commit 86f76c3

Please sign in to comment.