-
Notifications
You must be signed in to change notification settings - Fork 15.9k
[AArch64][PAC] Lower jump-tables using hardened pseudo. #97666
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -104,6 +104,8 @@ class AArch64AsmPrinter : public AsmPrinter { | |
|
|
||
| void LowerJumpTableDest(MCStreamer &OutStreamer, const MachineInstr &MI); | ||
|
|
||
| void LowerHardenedBRJumpTable(const MachineInstr &MI); | ||
|
|
||
| void LowerMOPS(MCStreamer &OutStreamer, const MachineInstr &MI); | ||
|
|
||
| void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM, | ||
|
|
@@ -1310,6 +1312,138 @@ void AArch64AsmPrinter::LowerJumpTableDest(llvm::MCStreamer &OutStreamer, | |
| .addImm(Size == 4 ? 0 : 2)); | ||
| } | ||
|
|
||
| void AArch64AsmPrinter::LowerHardenedBRJumpTable(const MachineInstr &MI) { | ||
| unsigned InstsEmitted = 0; | ||
|
|
||
| const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo(); | ||
| assert(MJTI && "Can't lower jump-table dispatch without JTI"); | ||
|
|
||
| const std::vector<MachineJumpTableEntry> &JTs = MJTI->getJumpTables(); | ||
| assert(!JTs.empty() && "Invalid JT index for jump-table dispatch"); | ||
|
|
||
| // Emit: | ||
| // mov x17, #<size of table> ; depending on table size, with MOVKs | ||
| // cmp x16, x17 ; or #imm if table size fits in 12-bit | ||
| // csel x16, x16, xzr, ls ; check for index overflow | ||
| // | ||
| // adrp x17, Ltable@PAGE ; materialize table address | ||
| // add x17, Ltable@PAGEOFF | ||
| // ldrsw x16, [x17, x16, lsl #2] ; load table entry | ||
| // | ||
| // Lanchor: | ||
| // adr x17, Lanchor ; compute target address | ||
| // add x16, x17, x16 | ||
| // br x16 ; branch to target | ||
|
|
||
| MachineOperand JTOp = MI.getOperand(0); | ||
|
|
||
| unsigned JTI = JTOp.getIndex(); | ||
| assert(!AArch64FI->getJumpTableEntryPCRelSymbol(JTI) && | ||
| "unsupported compressed jump table"); | ||
|
|
||
| const uint64_t NumTableEntries = JTs[JTI].MBBs.size(); | ||
|
|
||
| // cmp only supports a 12-bit immediate. If we need more, materialize the | ||
| // immediate, using x17 as a scratch register. | ||
| uint64_t MaxTableEntry = NumTableEntries - 1; | ||
| if (isUInt<12>(MaxTableEntry)) { | ||
| EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::SUBSXri) | ||
| .addReg(AArch64::XZR) | ||
| .addReg(AArch64::X16) | ||
| .addImm(MaxTableEntry) | ||
| .addImm(0)); | ||
| ++InstsEmitted; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: you probably want to create a helper lambda which will execute both Feel free to ignore.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, let's do that for the whole file someplace else |
||
| } else { | ||
| EmitToStreamer(*OutStreamer, | ||
| MCInstBuilder(AArch64::MOVZXi) | ||
| .addReg(AArch64::X17) | ||
| .addImm(static_cast<uint16_t>(MaxTableEntry)) | ||
| .addImm(0)); | ||
| ++InstsEmitted; | ||
| // It's sad that we have to manually materialize instructions, but we can't | ||
| // trivially reuse the main pseudo expansion logic. | ||
jroelofs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // A MOVK sequence is easy enough to generate and handles the general case. | ||
| for (int Offset = 16; Offset < 64; Offset += 16) { | ||
| if ((MaxTableEntry >> Offset) == 0) | ||
jroelofs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| break; | ||
| EmitToStreamer(*OutStreamer, | ||
| MCInstBuilder(AArch64::MOVKXi) | ||
| .addReg(AArch64::X17) | ||
| .addReg(AArch64::X17) | ||
| .addImm(static_cast<uint16_t>(MaxTableEntry >> Offset)) | ||
| .addImm(Offset)); | ||
| ++InstsEmitted; | ||
| } | ||
| EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::SUBSXrs) | ||
| .addReg(AArch64::XZR) | ||
| .addReg(AArch64::X16) | ||
| .addReg(AArch64::X17) | ||
| .addImm(0)); | ||
| ++InstsEmitted; | ||
| } | ||
|
|
||
| // This picks entry #0 on failure. | ||
| // We might want to trap instead. | ||
| EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::CSELXr) | ||
| .addReg(AArch64::X16) | ||
| .addReg(AArch64::X16) | ||
| .addReg(AArch64::XZR) | ||
| .addImm(AArch64CC::LS)); | ||
| ++InstsEmitted; | ||
|
|
||
| // Prepare the @PAGE/@PAGEOFF low/high operands. | ||
| MachineOperand JTMOHi(JTOp), JTMOLo(JTOp); | ||
| MCOperand JTMCHi, JTMCLo; | ||
|
|
||
| JTMOHi.setTargetFlags(AArch64II::MO_PAGE); | ||
| JTMOLo.setTargetFlags(AArch64II::MO_PAGEOFF | AArch64II::MO_NC); | ||
|
|
||
| MCInstLowering.lowerOperand(JTMOHi, JTMCHi); | ||
| MCInstLowering.lowerOperand(JTMOLo, JTMCLo); | ||
|
|
||
| EmitToStreamer( | ||
| *OutStreamer, | ||
| MCInstBuilder(AArch64::ADRP).addReg(AArch64::X17).addOperand(JTMCHi)); | ||
| ++InstsEmitted; | ||
|
|
||
| EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXri) | ||
| .addReg(AArch64::X17) | ||
| .addReg(AArch64::X17) | ||
| .addOperand(JTMCLo) | ||
| .addImm(0)); | ||
| ++InstsEmitted; | ||
|
|
||
| EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRSWroX) | ||
| .addReg(AArch64::X16) | ||
| .addReg(AArch64::X17) | ||
| .addReg(AArch64::X16) | ||
| .addImm(0) | ||
| .addImm(1)); | ||
| ++InstsEmitted; | ||
|
|
||
| MCSymbol *AdrLabel = MF->getContext().createTempSymbol(); | ||
| auto *AdrLabelE = MCSymbolRefExpr::create(AdrLabel, MF->getContext()); | ||
ahmedbougacha marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| AArch64FI->setJumpTableEntryInfo(JTI, 4, AdrLabel); | ||
|
|
||
| OutStreamer->emitLabel(AdrLabel); | ||
| EmitToStreamer( | ||
| *OutStreamer, | ||
| MCInstBuilder(AArch64::ADR).addReg(AArch64::X17).addExpr(AdrLabelE)); | ||
| ++InstsEmitted; | ||
|
|
||
| EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXrs) | ||
| .addReg(AArch64::X16) | ||
| .addReg(AArch64::X17) | ||
| .addReg(AArch64::X16) | ||
| .addImm(0)); | ||
| ++InstsEmitted; | ||
|
|
||
| EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::BR).addReg(AArch64::X16)); | ||
| ++InstsEmitted; | ||
|
|
||
| assert(STI->getInstrInfo()->getInstSizeInBytes(MI) >= InstsEmitted * 4); | ||
ahmedbougacha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| void AArch64AsmPrinter::LowerMOPS(llvm::MCStreamer &OutStreamer, | ||
| const llvm::MachineInstr &MI) { | ||
| unsigned Opcode = MI.getOpcode(); | ||
|
|
@@ -2177,6 +2311,10 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) { | |
| LowerJumpTableDest(*OutStreamer, *MI); | ||
| return; | ||
|
|
||
| case AArch64::BR_JumpTable: | ||
| LowerHardenedBRJumpTable(*MI); | ||
| return; | ||
|
|
||
| case AArch64::FMOVH0: | ||
| case AArch64::FMOVS0: | ||
| case AArch64::FMOVD0: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10678,6 +10678,21 @@ SDValue AArch64TargetLowering::LowerBR_JT(SDValue Op, | |
| auto *AFI = DAG.getMachineFunction().getInfo<AArch64FunctionInfo>(); | ||
| AFI->setJumpTableEntryInfo(JTI, 4, nullptr); | ||
|
|
||
| // With jump-table-hardening, we only expand the full jump table dispatch | ||
| // sequence later, to guarantee the integrity of the intermediate values. | ||
| if (DAG.getMachineFunction().getFunction().hasFnAttribute( | ||
| "jump-table-hardening") || | ||
| Subtarget->getTargetTriple().isArm64e()) { | ||
|
||
| assert(Subtarget->isTargetMachO() && | ||
|
||
| "hardened jump-table not yet supported on non-macho"); | ||
| SDValue X16Copy = DAG.getCopyToReg(DAG.getEntryNode(), DL, AArch64::X16, | ||
| Entry, SDValue()); | ||
| SDNode *B = DAG.getMachineNode(AArch64::BR_JumpTable, DL, MVT::Other, | ||
| DAG.getTargetJumpTable(JTI, MVT::i32), | ||
| X16Copy.getValue(0), X16Copy.getValue(1)); | ||
| return SDValue(B, 0); | ||
| } | ||
|
|
||
| SDNode *Dest = | ||
| DAG.getMachineNode(AArch64::JumpTableDest32, DL, MVT::i64, MVT::i64, JT, | ||
| Entry, DAG.getTargetJumpTable(JTI, MVT::i32)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3597,10 +3597,22 @@ bool AArch64InstructionSelector::selectBrJT(MachineInstr &I, | |
| unsigned JTI = I.getOperand(1).getIndex(); | ||
| Register Index = I.getOperand(2).getReg(); | ||
|
|
||
| MF->getInfo<AArch64FunctionInfo>()->setJumpTableEntryInfo(JTI, 4, nullptr); | ||
| if (MF->getFunction().hasFnAttribute("jump-table-hardening") || | ||
| STI.getTargetTriple().isArm64e()) { | ||
| if (TM.getCodeModel() != CodeModel::Small) | ||
jroelofs marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| report_fatal_error("Unsupported code-model for hardened jump-table"); | ||
|
|
||
| MIB.buildCopy({AArch64::X16}, I.getOperand(2).getReg()); | ||
| MIB.buildInstr(AArch64::BR_JumpTable) | ||
| .addJumpTableIndex(I.getOperand(1).getIndex()); | ||
| I.eraseFromParent(); | ||
| return true; | ||
| } | ||
|
|
||
| Register TargetReg = MRI.createVirtualRegister(&AArch64::GPR64RegClass); | ||
| Register ScratchReg = MRI.createVirtualRegister(&AArch64::GPR64spRegClass); | ||
|
|
||
| MF->getInfo<AArch64FunctionInfo>()->setJumpTableEntryInfo(JTI, 4, nullptr); | ||
| auto JumpTableInst = MIB.buildInstr(AArch64::JumpTableDest32, | ||
| {TargetReg, ScratchReg}, {JTAddr, Index}) | ||
| .addJumpTableIndex(JTI); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| ; RUN: llc -verify-machineinstrs -o - %s -mtriple=arm64-apple-ios -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 | FileCheck %s | ||
| ; RUN: llc -verify-machineinstrs -o - %s -mtriple=arm64-apple-ios -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 -code-model=large | FileCheck %s | ||
|
||
| ; RUN: llc -verify-machineinstrs -o - %s -mtriple=arm64-apple-ios -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 -global-isel -global-isel-abort=1 | FileCheck %s | ||
|
|
||
| ; CHECK-LABEL: test_jumptable: | ||
| ; CHECK: mov w[[INDEX:[0-9]+]], w0 | ||
| ; CHECK: cmp x[[INDEX]], #5 | ||
| ; CHECK: csel [[INDEX2:x[0-9]+]], x[[INDEX]], xzr, ls | ||
| ; CHECK-NEXT: adrp [[JTPAGE:x[0-9]+]], LJTI0_0@PAGE | ||
| ; CHECK-NEXT: add x[[JT:[0-9]+]], [[JTPAGE]], LJTI0_0@PAGEOFF | ||
| ; CHECK-NEXT: ldrsw [[OFFSET:x[0-9]+]], [x[[JT]], [[INDEX2]], lsl #2] | ||
| ; CHECK-NEXT: Ltmp0: | ||
| ; CHECK-NEXT: adr [[TABLE:x[0-9]+]], Ltmp0 | ||
| ; CHECK-NEXT: add [[DEST:x[0-9]+]], [[TABLE]], [[OFFSET]] | ||
| ; CHECK-NEXT: br [[DEST]] | ||
|
|
||
| define i32 @test_jumptable(i32 %in) "jump-table-hardening" { | ||
|
|
||
| switch i32 %in, label %def [ | ||
| i32 0, label %lbl1 | ||
| i32 1, label %lbl2 | ||
| i32 2, label %lbl3 | ||
| i32 4, label %lbl4 | ||
| i32 5, label %lbl5 | ||
| ] | ||
|
|
||
| def: | ||
| ret i32 0 | ||
|
|
||
| lbl1: | ||
| ret i32 1 | ||
|
|
||
| lbl2: | ||
| ret i32 2 | ||
|
|
||
| lbl3: | ||
| ret i32 4 | ||
|
|
||
| lbl4: | ||
| ret i32 8 | ||
|
|
||
| lbl5: | ||
| ret i32 10 | ||
|
|
||
| } | ||
|
|
||
| ; CHECK: LJTI0_0: | ||
| ; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; CHECK-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
Uh oh!
There was an error while loading. Please reload this page.