Skip to content

[AArch64][Libunwind] Add Support for FEAT_PAuthLR DWARF Instruction #112171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading