-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[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 all commits
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,139 @@ 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(); | ||
| const auto *AdrLabelE = MCSymbolRefExpr::create(AdrLabel, MF->getContext()); | ||
| 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; | ||
|
|
||
| (void)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 +2312,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,30 @@ SDValue AArch64TargetLowering::LowerBR_JT(SDValue Op, | |
| auto *AFI = DAG.getMachineFunction().getInfo<AArch64FunctionInfo>(); | ||
| AFI->setJumpTableEntryInfo(JTI, 4, nullptr); | ||
|
|
||
| // With aarch64-jump-table-hardening, we only expand the jump table dispatch | ||
| // sequence later, to guarantee the integrity of the intermediate values. | ||
| if (DAG.getMachineFunction().getFunction().hasFnAttribute( | ||
| "aarch64-jump-table-hardening")) { | ||
| CodeModel::Model CM = getTargetMachine().getCodeModel(); | ||
|
||
| if (Subtarget->isTargetMachO()) { | ||
|
||
| if (CM != CodeModel::Small && CM != CodeModel::Large) | ||
| report_fatal_error("Unsupported code-model for hardened jump-table"); | ||
| } else { | ||
| // Note that COFF support would likely also need JUMP_TABLE_DEBUG_INFO. | ||
| assert(Subtarget->isTargetELF() && | ||
| "jump table hardening only supported on MachO/ELF"); | ||
| if (CM != CodeModel::Small) | ||
| report_fatal_error("Unsupported code-model for hardened jump-table"); | ||
| } | ||
|
|
||
| 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 |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| ; RUN: rm -rf %t && split-file %s %t | ||
|
|
||
| ;--- err1.ll | ||
|
|
||
| ; RUN: not --crash llc %t/err1.ll -mtriple=aarch64-elf \ | ||
| ; RUN: -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 \ | ||
| ; RUN: -code-model=large \ | ||
| ; RUN: -o - -verify-machineinstrs 2>&1 | FileCheck %s --check-prefix=ERR1 | ||
|
|
||
| ; RUN: not --crash llc %t/err1.ll -mtriple=aarch64-elf \ | ||
| ; RUN: -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 \ | ||
| ; RUN: -global-isel -global-isel-abort=1 \ | ||
| ; RUN: -code-model=large \ | ||
| ; RUN: -o - -verify-machineinstrs 2>&1 | FileCheck %s --check-prefix=ERR1 | ||
|
|
||
| ; ERR1: LLVM ERROR: Unsupported code-model for hardened jump-table | ||
| define i32 @test_jumptable(i32 %in) "aarch64-jump-table-hardening" { | ||
|
|
||
| switch i32 %in, label %def [ | ||
| i32 0, label %lbl1 | ||
| i32 1, label %lbl2 | ||
| ] | ||
|
|
||
| def: | ||
| ret i32 0 | ||
|
|
||
| lbl1: | ||
| ret i32 1 | ||
|
|
||
| lbl2: | ||
| ret i32 2 | ||
| } | ||
|
|
||
| ;--- test.ll | ||
|
|
||
| ; RUN: llc %t/test.ll -mtriple=arm64-apple-darwin -aarch64-enable-collect-loh=0 \ | ||
| ; RUN: -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 \ | ||
| ; RUN: -o - -verify-machineinstrs | FileCheck %s --check-prefix=MACHO | ||
|
|
||
| ; RUN: llc %t/test.ll -mtriple=arm64-apple-darwin -aarch64-enable-collect-loh=0 \ | ||
| ; RUN: -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 \ | ||
| ; RUN: -global-isel -global-isel-abort=1 \ | ||
| ; RUN: -o - -verify-machineinstrs | FileCheck %s --check-prefix=MACHO | ||
|
|
||
| ; RUN: llc %t/test.ll -mtriple=arm64-apple-darwin -aarch64-enable-collect-loh=0 \ | ||
| ; RUN: -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 \ | ||
| ; RUN: -code-model=large \ | ||
| ; RUN: -o - -verify-machineinstrs | FileCheck %s --check-prefix=MACHO | ||
|
|
||
| ; RUN: llc %t/test.ll -mtriple=arm64-apple-darwin -aarch64-enable-collect-loh=0 \ | ||
| ; RUN: -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 \ | ||
| ; RUN: -global-isel -global-isel-abort=1 \ | ||
| ; RUN: -code-model=large \ | ||
| ; RUN: -o - -verify-machineinstrs | FileCheck %s --check-prefix=MACHO | ||
|
|
||
| ; RUN: llc %t/test.ll -mtriple=aarch64-elf \ | ||
| ; RUN: -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 \ | ||
| ; RUN: -o - -verify-machineinstrs | FileCheck %s --check-prefix=ELF | ||
|
|
||
| ; RUN: llc %t/test.ll -mtriple=aarch64-elf \ | ||
| ; RUN: -aarch64-min-jump-table-entries=1 -aarch64-enable-atomic-cfg-tidy=0 \ | ||
| ; RUN: -global-isel -global-isel-abort=1 \ | ||
| ; RUN: -o - -verify-machineinstrs | FileCheck %s --check-prefix=ELF | ||
|
|
||
| ; MACHO-LABEL: test_jumptable: | ||
| ; MACHO: mov w16, w0 | ||
| ; MACHO: cmp x16, #5 | ||
| ; MACHO: csel x16, x16, xzr, ls | ||
| ; MACHO-NEXT: adrp x17, LJTI0_0@PAGE | ||
| ; MACHO-NEXT: add x17, x17, LJTI0_0@PAGEOFF | ||
| ; MACHO-NEXT: ldrsw x16, [x17, x16, lsl #2] | ||
| ; MACHO-NEXT: Ltmp0: | ||
| ; MACHO-NEXT: adr x17, Ltmp0 | ||
| ; MACHO-NEXT: add x16, x17, x16 | ||
| ; MACHO-NEXT: br x16 | ||
|
|
||
| ; ELF-LABEL: test_jumptable: | ||
| ; ELF: mov w16, w0 | ||
| ; ELF: cmp x16, #5 | ||
| ; ELF: csel x16, x16, xzr, ls | ||
| ; ELF-NEXT: adrp x17, .LJTI0_0 | ||
| ; ELF-NEXT: add x17, x17, :lo12:.LJTI0_0 | ||
| ; ELF-NEXT: ldrsw x16, [x17, x16, lsl #2] | ||
| ; ELF-NEXT: .Ltmp0: | ||
| ; ELF-NEXT: adr x17, .Ltmp0 | ||
| ; ELF-NEXT: add x16, x17, x16 | ||
| ; ELF-NEXT: br x16 | ||
|
|
||
| define i32 @test_jumptable(i32 %in) "aarch64-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 | ||
|
|
||
| } | ||
|
|
||
| ; MACHO-LABEL: LJTI0_0: | ||
| ; MACHO-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; MACHO-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; MACHO-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; MACHO-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; MACHO-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
| ; MACHO-NEXT: .long LBB{{[0-9_]+}}-Ltmp0 | ||
|
|
||
| ; ELF-LABEL: .LJTI0_0: | ||
| ; ELF-NEXT: .word .LBB{{[0-9_]+}}-.Ltmp0 | ||
| ; ELF-NEXT: .word .LBB{{[0-9_]+}}-.Ltmp0 | ||
| ; ELF-NEXT: .word .LBB{{[0-9_]+}}-.Ltmp0 | ||
| ; ELF-NEXT: .word .LBB{{[0-9_]+}}-.Ltmp0 | ||
| ; ELF-NEXT: .word .LBB{{[0-9_]+}}-.Ltmp0 | ||
| ; ELF-NEXT: .word .LBB{{[0-9_]+}}-.Ltmp0 |
Uh oh!
There was an error while loading. Please reload this page.