mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-23 22:00:10 +00:00
[RISCV] Implement KCFI operand bundle lowering
With `-fsanitize=kcfi` (Kernel Control-Flow Integrity), Clang emits "kcfi" operand bundles to indirect call instructions. Similarly to the target-specific lowering added in D119296, implement KCFI operand bundle lowering for RISC-V. This patch disables the generic KCFI pass for RISC-V in Clang, and adds the KCFI machine function pass in `RISCVPassConfig::addPreSched` to emit target-specific `KCFI_CHECK` pseudo instructions before calls that have KCFI operand bundles. The machine function pass also bundles the instructions to ensure we emit the checks immediately before the calls, which is not possible with the generic pass. `KCFI_CHECK` instructions are lowered in `RISCVAsmPrinter` to a contiguous code sequence that traps if the expected hash in the operand bundle doesn't match the hash before the target function address. This patch emits an `ebreak` instruction for error handling to match the Linux kernel's `BUG()` implementation. Just like for X86, we also emit trap locations to a `.kcfi_traps` section to support error handling, as we cannot embed additional information to the trap instruction itself. Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D148385
This commit is contained in:
parent
fd65b8da80
commit
62fa708ceb
@ -631,7 +631,7 @@ static void addKCFIPass(const Triple &TargetTriple, const LangOptions &LangOpts,
|
||||
PassBuilder &PB) {
|
||||
// If the back-end supports KCFI operand bundle lowering, skip KCFIPass.
|
||||
if (TargetTriple.getArch() == llvm::Triple::x86_64 ||
|
||||
TargetTriple.isAArch64(64))
|
||||
TargetTriple.isAArch64(64) || TargetTriple.isRISCV())
|
||||
return;
|
||||
|
||||
// Ensure we lower KCFI operand bundles with -O0.
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "RISCVMachineFunctionInfo.h"
|
||||
#include "RISCVTargetMachine.h"
|
||||
#include "TargetInfo/RISCVTargetInfo.h"
|
||||
#include "llvm/ADT/APInt.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
@ -72,6 +73,7 @@ public:
|
||||
typedef std::tuple<unsigned, uint32_t> HwasanMemaccessTuple;
|
||||
std::map<HwasanMemaccessTuple, MCSymbol *> HwasanMemaccessSymbols;
|
||||
void LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI);
|
||||
void LowerKCFI_CHECK(const MachineInstr &MI);
|
||||
void EmitHwasanMemaccessSymbols(Module &M);
|
||||
|
||||
// Wrapper needed for tblgenned pseudo lowering.
|
||||
@ -150,6 +152,9 @@ void RISCVAsmPrinter::emitInstruction(const MachineInstr *MI) {
|
||||
case RISCV::HWASAN_CHECK_MEMACCESS_SHORTGRANULES:
|
||||
LowerHWASAN_CHECK_MEMACCESS(*MI);
|
||||
return;
|
||||
case RISCV::KCFI_CHECK:
|
||||
LowerKCFI_CHECK(*MI);
|
||||
return;
|
||||
case RISCV::PseudoRVVInitUndefM1:
|
||||
case RISCV::PseudoRVVInitUndefM2:
|
||||
case RISCV::PseudoRVVInitUndefM4:
|
||||
@ -305,6 +310,92 @@ void RISCVAsmPrinter::LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI) {
|
||||
EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::PseudoCALL).addExpr(Expr));
|
||||
}
|
||||
|
||||
void RISCVAsmPrinter::LowerKCFI_CHECK(const MachineInstr &MI) {
|
||||
Register AddrReg = MI.getOperand(0).getReg();
|
||||
assert(std::next(MI.getIterator())->isCall() &&
|
||||
"KCFI_CHECK not followed by a call instruction");
|
||||
assert(std::next(MI.getIterator())->getOperand(0).getReg() == AddrReg &&
|
||||
"KCFI_CHECK call target doesn't match call operand");
|
||||
|
||||
// Temporary registers for comparing the hashes. If a register is used
|
||||
// for the call target, or reserved by the user, we can clobber another
|
||||
// temporary register as the check is immediately followed by the
|
||||
// call. The check defaults to X6/X7, but can fall back to X28-X31 if
|
||||
// needed.
|
||||
unsigned ScratchRegs[] = {RISCV::X6, RISCV::X7};
|
||||
unsigned NextReg = RISCV::X28;
|
||||
auto isRegAvailable = [&](unsigned Reg) {
|
||||
return Reg != AddrReg && !STI->isRegisterReservedByUser(Reg);
|
||||
};
|
||||
for (auto &Reg : ScratchRegs) {
|
||||
if (isRegAvailable(Reg))
|
||||
continue;
|
||||
while (!isRegAvailable(NextReg))
|
||||
++NextReg;
|
||||
Reg = NextReg++;
|
||||
if (Reg > RISCV::X31)
|
||||
report_fatal_error("Unable to find scratch registers for KCFI_CHECK");
|
||||
}
|
||||
|
||||
if (AddrReg == RISCV::X0) {
|
||||
// Checking X0 makes no sense. Instead of emitting a load, zero
|
||||
// ScratchRegs[0].
|
||||
EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::ADDI)
|
||||
.addReg(ScratchRegs[0])
|
||||
.addReg(RISCV::X0)
|
||||
.addImm(0));
|
||||
} else {
|
||||
// Adjust the offset for patchable-function-prefix. This assumes that
|
||||
// patchable-function-prefix is the same for all functions.
|
||||
int NopSize = STI->hasStdExtCOrZca() ? 2 : 4;
|
||||
int64_t PrefixNops = 0;
|
||||
(void)MI.getMF()
|
||||
->getFunction()
|
||||
.getFnAttribute("patchable-function-prefix")
|
||||
.getValueAsString()
|
||||
.getAsInteger(10, PrefixNops);
|
||||
|
||||
// Load the target function type hash.
|
||||
EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::LW)
|
||||
.addReg(ScratchRegs[0])
|
||||
.addReg(AddrReg)
|
||||
.addImm(-(PrefixNops * NopSize + 4)));
|
||||
}
|
||||
|
||||
// Load the expected 32-bit type hash.
|
||||
const int64_t Type = MI.getOperand(1).getImm();
|
||||
const int64_t Hi20 = ((Type + 0x800) >> 12) & 0xFFFFF;
|
||||
const int64_t Lo12 = SignExtend64<12>(Type);
|
||||
if (Hi20) {
|
||||
EmitToStreamer(
|
||||
*OutStreamer,
|
||||
MCInstBuilder(RISCV::LUI).addReg(ScratchRegs[1]).addImm(Hi20));
|
||||
}
|
||||
if (Lo12 || Hi20 == 0) {
|
||||
EmitToStreamer(*OutStreamer,
|
||||
MCInstBuilder((STI->hasFeature(RISCV::Feature64Bit) && Hi20)
|
||||
? RISCV::ADDIW
|
||||
: RISCV::ADDI)
|
||||
.addReg(ScratchRegs[1])
|
||||
.addReg(ScratchRegs[1])
|
||||
.addImm(Lo12));
|
||||
}
|
||||
|
||||
// Compare the hashes and trap if there's a mismatch.
|
||||
MCSymbol *Pass = OutContext.createTempSymbol();
|
||||
EmitToStreamer(*OutStreamer,
|
||||
MCInstBuilder(RISCV::BEQ)
|
||||
.addReg(ScratchRegs[0])
|
||||
.addReg(ScratchRegs[1])
|
||||
.addExpr(MCSymbolRefExpr::create(Pass, OutContext)));
|
||||
|
||||
MCSymbol *Trap = OutContext.createTempSymbol();
|
||||
OutStreamer->emitLabel(Trap);
|
||||
EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::EBREAK));
|
||||
emitKCFITrapEntry(*MI.getMF(), Trap);
|
||||
OutStreamer->emitLabel(Pass);
|
||||
}
|
||||
|
||||
void RISCVAsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
|
||||
if (HwasanMemaccessSymbols.empty())
|
||||
return;
|
||||
|
@ -15395,17 +15395,24 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI,
|
||||
if (Glue.getNode())
|
||||
Ops.push_back(Glue);
|
||||
|
||||
assert((!CLI.CFIType || CLI.CB->isIndirectCall()) &&
|
||||
"Unexpected CFI type for a direct call");
|
||||
|
||||
// Emit the call.
|
||||
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
|
||||
|
||||
if (IsTailCall) {
|
||||
MF.getFrameInfo().setHasTailCall();
|
||||
SDValue Ret = DAG.getNode(RISCVISD::TAIL, DL, NodeTys, Ops);
|
||||
if (CLI.CFIType)
|
||||
Ret.getNode()->setCFIType(CLI.CFIType->getZExtValue());
|
||||
DAG.addNoMergeSiteInfo(Ret.getNode(), CLI.NoMerge);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops);
|
||||
if (CLI.CFIType)
|
||||
Chain.getNode()->setCFIType(CLI.CFIType->getZExtValue());
|
||||
DAG.addNoMergeSiteInfo(Chain.getNode(), CLI.NoMerge);
|
||||
Glue = Chain.getValue(1);
|
||||
|
||||
@ -16864,6 +16871,24 @@ bool RISCVTargetLowering::lowerInterleavedStore(StoreInst *SI,
|
||||
return true;
|
||||
}
|
||||
|
||||
MachineInstr *
|
||||
RISCVTargetLowering::EmitKCFICheck(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::instr_iterator &MBBI,
|
||||
const TargetInstrInfo *TII) const {
|
||||
assert(MBBI->isCall() && MBBI->getCFIType() &&
|
||||
"Invalid call instruction for a KCFI check");
|
||||
assert(is_contained({RISCV::PseudoCALLIndirect, RISCV::PseudoTAILIndirect},
|
||||
MBBI->getOpcode()));
|
||||
|
||||
MachineOperand &Target = MBBI->getOperand(0);
|
||||
Target.setIsRenamable(false);
|
||||
|
||||
return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(RISCV::KCFI_CHECK))
|
||||
.addReg(Target.getReg())
|
||||
.addImm(MBBI->getCFIType())
|
||||
.getInstr();
|
||||
}
|
||||
|
||||
#define GET_REGISTER_MATCHER
|
||||
#include "RISCVGenAsmMatcher.inc"
|
||||
|
||||
|
@ -759,6 +759,12 @@ public:
|
||||
bool lowerInterleavedStore(StoreInst *SI, ShuffleVectorInst *SVI,
|
||||
unsigned Factor) const override;
|
||||
|
||||
bool supportKCFIBundles() const override { return true; }
|
||||
|
||||
MachineInstr *EmitKCFICheck(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::instr_iterator &MBBI,
|
||||
const TargetInstrInfo *TII) const override;
|
||||
|
||||
/// RISCVCCAssignFn - This target-specific function extends the default
|
||||
/// CCValAssign with additional information used to lower RISC-V calling
|
||||
/// conventions.
|
||||
|
@ -1265,6 +1265,9 @@ unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (Opcode == TargetOpcode::BUNDLE)
|
||||
return getInstBundleLength(MI);
|
||||
|
||||
if (MI.getParent() && MI.getParent()->getParent()) {
|
||||
if (isCompressibleInst(MI, STI))
|
||||
return 2;
|
||||
@ -1272,6 +1275,17 @@ unsigned RISCVInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
|
||||
return get(Opcode).getSize();
|
||||
}
|
||||
|
||||
unsigned RISCVInstrInfo::getInstBundleLength(const MachineInstr &MI) const {
|
||||
unsigned Size = 0;
|
||||
MachineBasicBlock::const_instr_iterator I = MI.getIterator();
|
||||
MachineBasicBlock::const_instr_iterator E = MI.getParent()->instr_end();
|
||||
while (++I != E && I->isInsideBundle()) {
|
||||
assert(!I->isBundle() && "No nested bundle!");
|
||||
Size += getInstSizeInBytes(*I);
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
bool RISCVInstrInfo::isAsCheapAsAMove(const MachineInstr &MI) const {
|
||||
const unsigned Opcode = MI.getOpcode();
|
||||
switch (Opcode) {
|
||||
|
@ -237,6 +237,9 @@ public:
|
||||
|
||||
protected:
|
||||
const RISCVSubtarget &STI;
|
||||
|
||||
private:
|
||||
unsigned getInstBundleLength(const MachineInstr &MI) const;
|
||||
};
|
||||
|
||||
namespace RISCV {
|
||||
|
@ -1887,6 +1887,13 @@ def HWASAN_CHECK_MEMACCESS_SHORTGRANULES
|
||||
[(int_hwasan_check_memaccess_shortgranules X5, GPRJALR:$ptr,
|
||||
(i32 timm:$accessinfo))]>;
|
||||
|
||||
// This gets lowered into a 20-byte instruction sequence (at most)
|
||||
let hasSideEffects = 0, mayLoad = 1, mayStore = 0,
|
||||
Defs = [ X6, X7, X28, X29, X30, X31 ], Size = 20 in {
|
||||
def KCFI_CHECK
|
||||
: Pseudo<(outs), (ins GPRJALR:$ptr, i32imm:$type), []>, Sched<[]>;
|
||||
}
|
||||
|
||||
/// Simple optimization
|
||||
def : Pat<(XLenVT (add GPR:$rs1, (AddiPair:$rs2))),
|
||||
(ADDI (ADDI GPR:$rs1, (AddiPairImmLarge AddiPair:$rs2)),
|
||||
|
@ -76,6 +76,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVTarget() {
|
||||
RegisterTargetMachine<RISCVTargetMachine> Y(getTheRISCV64Target());
|
||||
auto *PR = PassRegistry::getPassRegistry();
|
||||
initializeGlobalISel(*PR);
|
||||
initializeKCFIPass(*PR);
|
||||
initializeRISCVMakeCompressibleOptPass(*PR);
|
||||
initializeRISCVGatherScatterLoweringPass(*PR);
|
||||
initializeRISCVCodeGenPreparePass(*PR);
|
||||
@ -333,7 +334,10 @@ bool RISCVPassConfig::addGlobalInstructionSelect() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RISCVPassConfig::addPreSched2() {}
|
||||
void RISCVPassConfig::addPreSched2() {
|
||||
// Emit KCFI checks for indirect calls.
|
||||
addPass(createKCFIPass());
|
||||
}
|
||||
|
||||
void RISCVPassConfig::addPreEmitPass() {
|
||||
addPass(&BranchRelaxationPassID);
|
||||
@ -357,6 +361,11 @@ void RISCVPassConfig::addPreEmitPass2() {
|
||||
// possibility for other passes to break the requirements for forward
|
||||
// progress in the LR/SC block.
|
||||
addPass(createRISCVExpandAtomicPseudoPass());
|
||||
|
||||
// KCFI indirect call checks are lowered to a bundle.
|
||||
addPass(createUnpackMachineBundles([&](const MachineFunction &MF) {
|
||||
return MF.getFunction().getParent()->getModuleFlag("kcfi");
|
||||
}));
|
||||
}
|
||||
|
||||
void RISCVPassConfig::addMachineSSAOptimization() {
|
||||
|
@ -51,6 +51,7 @@
|
||||
; CHECK-NEXT: Machine Optimization Remark Emitter
|
||||
; CHECK-NEXT: Prologue/Epilogue Insertion & Frame Finalization
|
||||
; CHECK-NEXT: Post-RA pseudo instruction expansion pass
|
||||
; CHECK-NEXT: Insert KCFI indirect call checks
|
||||
; CHECK-NEXT: Analyze Machine Code For Garbage Collection
|
||||
; CHECK-NEXT: Insert fentry calls
|
||||
; CHECK-NEXT: Insert XRay ops
|
||||
@ -66,6 +67,7 @@
|
||||
; CHECK-NEXT: Stack Frame Layout Analysis
|
||||
; CHECK-NEXT: RISC-V pseudo instruction expansion pass
|
||||
; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass
|
||||
; CHECK-NEXT: Unpack machine instruction bundles
|
||||
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
|
||||
; CHECK-NEXT: Machine Optimization Remark Emitter
|
||||
; CHECK-NEXT: RISC-V Assembly Printer
|
||||
|
@ -155,6 +155,7 @@
|
||||
; CHECK-NEXT: Tail Duplication
|
||||
; CHECK-NEXT: Machine Copy Propagation Pass
|
||||
; CHECK-NEXT: Post-RA pseudo instruction expansion pass
|
||||
; CHECK-NEXT: Insert KCFI indirect call checks
|
||||
; CHECK-NEXT: MachineDominator Tree Construction
|
||||
; CHECK-NEXT: Machine Natural Loop Construction
|
||||
; CHECK-NEXT: Post RA top-down list latency scheduler
|
||||
@ -180,6 +181,7 @@
|
||||
; CHECK-NEXT: RISC-V Zcmp move merging pass
|
||||
; CHECK-NEXT: RISC-V pseudo instruction expansion pass
|
||||
; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass
|
||||
; CHECK-NEXT: Unpack machine instruction bundles
|
||||
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
|
||||
; CHECK-NEXT: Machine Optimization Remark Emitter
|
||||
; CHECK-NEXT: RISC-V Assembly Printer
|
||||
|
175
llvm/test/CodeGen/RISCV/kcfi-isel.mir
Normal file
175
llvm/test/CodeGen/RISCV/kcfi-isel.mir
Normal file
@ -0,0 +1,175 @@
|
||||
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 2
|
||||
# RUN: llc -mtriple=riscv64 -stop-after=finalize-isel -o - %s | FileCheck %s
|
||||
--- |
|
||||
; ModuleID = '<stdin>'
|
||||
source_filename = "<stdin>"
|
||||
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "riscv64"
|
||||
|
||||
define void @f1(ptr noundef %x) !kcfi_type !1 {
|
||||
call void %x() [ "kcfi"(i32 12345678) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @f2(ptr noundef %x) #0 {
|
||||
tail call void %x() [ "kcfi"(i32 12345678) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "patchable-function-entry"="2" }
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
|
||||
!0 = !{i32 4, !"kcfi", i32 1}
|
||||
!1 = !{i32 12345678}
|
||||
|
||||
...
|
||||
---
|
||||
name: f1
|
||||
alignment: 4
|
||||
exposesReturnsTwice: false
|
||||
legalized: false
|
||||
regBankSelected: false
|
||||
selected: false
|
||||
failedISel: false
|
||||
tracksRegLiveness: true
|
||||
hasWinCFI: false
|
||||
callsEHReturn: false
|
||||
callsUnwindInit: false
|
||||
hasEHCatchret: false
|
||||
hasEHScopes: false
|
||||
hasEHFunclets: false
|
||||
isOutlined: false
|
||||
debugInstrRef: false
|
||||
failsVerification: false
|
||||
tracksDebugUserValues: false
|
||||
registers:
|
||||
- { id: 0, class: gprjalr, preferred-register: '' }
|
||||
liveins:
|
||||
- { reg: '$x10', virtual-reg: '%0' }
|
||||
frameInfo:
|
||||
isFrameAddressTaken: false
|
||||
isReturnAddressTaken: false
|
||||
hasStackMap: false
|
||||
hasPatchPoint: false
|
||||
stackSize: 0
|
||||
offsetAdjustment: 0
|
||||
maxAlignment: 1
|
||||
adjustsStack: false
|
||||
hasCalls: true
|
||||
stackProtector: ''
|
||||
functionContext: ''
|
||||
maxCallFrameSize: 4294967295
|
||||
cvBytesOfCalleeSavedRegisters: 0
|
||||
hasOpaqueSPAdjustment: false
|
||||
hasVAStart: false
|
||||
hasMustTailInVarArgFunc: false
|
||||
hasTailCall: false
|
||||
localFrameSize: 0
|
||||
savePoint: ''
|
||||
restorePoint: ''
|
||||
fixedStack: []
|
||||
stack: []
|
||||
callSites: []
|
||||
debugValueSubstitutions: []
|
||||
constants: []
|
||||
machineFunctionInfo:
|
||||
varArgsFrameIndex: 0
|
||||
varArgsSaveSize: 0
|
||||
body: |
|
||||
bb.0 (%ir-block.0):
|
||||
liveins: $x10
|
||||
|
||||
; CHECK-LABEL: name: f1
|
||||
; CHECK: liveins: $x10, $x10, $x10
|
||||
; CHECK-NEXT: {{ $}}
|
||||
; CHECK-NEXT: [[COPY:%[0-9]+]]:gprjalr = COPY $x10
|
||||
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gprjalr = COPY $x10
|
||||
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprjalr = COPY $x10
|
||||
; CHECK-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
|
||||
; CHECK-NEXT: PseudoCALLIndirect [[COPY2]], csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2, cfi-type 12345678
|
||||
; CHECK-NEXT: ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
|
||||
; CHECK-NEXT: PseudoRET
|
||||
; CHECK-NEXT: {{ $}}
|
||||
; CHECK-NEXT: .1 (%ir-block.0):
|
||||
; CHECK-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
|
||||
; CHECK-NEXT: PseudoCALLIndirect [[COPY]], csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2, cfi-type 12345678
|
||||
; CHECK-NEXT: ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
|
||||
; CHECK-NEXT: PseudoRET
|
||||
%0:gprjalr = COPY $x10
|
||||
ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2
|
||||
PseudoCALLIndirect %0, csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2, cfi-type 12345678
|
||||
ADJCALLSTACKUP 0, 0, implicit-def dead $x2, implicit $x2
|
||||
PseudoRET
|
||||
|
||||
...
|
||||
---
|
||||
name: f2
|
||||
alignment: 4
|
||||
exposesReturnsTwice: false
|
||||
legalized: false
|
||||
regBankSelected: false
|
||||
selected: false
|
||||
failedISel: false
|
||||
tracksRegLiveness: true
|
||||
hasWinCFI: false
|
||||
callsEHReturn: false
|
||||
callsUnwindInit: false
|
||||
hasEHCatchret: false
|
||||
hasEHScopes: false
|
||||
hasEHFunclets: false
|
||||
isOutlined: false
|
||||
debugInstrRef: false
|
||||
failsVerification: false
|
||||
tracksDebugUserValues: false
|
||||
registers:
|
||||
- { id: 0, class: gprtc, preferred-register: '' }
|
||||
liveins:
|
||||
- { reg: '$x10', virtual-reg: '%0' }
|
||||
frameInfo:
|
||||
isFrameAddressTaken: false
|
||||
isReturnAddressTaken: false
|
||||
hasStackMap: false
|
||||
hasPatchPoint: false
|
||||
stackSize: 0
|
||||
offsetAdjustment: 0
|
||||
maxAlignment: 1
|
||||
adjustsStack: false
|
||||
hasCalls: false
|
||||
stackProtector: ''
|
||||
functionContext: ''
|
||||
maxCallFrameSize: 4294967295
|
||||
cvBytesOfCalleeSavedRegisters: 0
|
||||
hasOpaqueSPAdjustment: false
|
||||
hasVAStart: false
|
||||
hasMustTailInVarArgFunc: false
|
||||
hasTailCall: true
|
||||
localFrameSize: 0
|
||||
savePoint: ''
|
||||
restorePoint: ''
|
||||
fixedStack: []
|
||||
stack: []
|
||||
callSites: []
|
||||
debugValueSubstitutions: []
|
||||
constants: []
|
||||
machineFunctionInfo:
|
||||
varArgsFrameIndex: 0
|
||||
varArgsSaveSize: 0
|
||||
body: |
|
||||
bb.0 (%ir-block.0):
|
||||
liveins: $x10
|
||||
|
||||
; CHECK-LABEL: name: f2
|
||||
; CHECK: liveins: $x10, $x10, $x10
|
||||
; CHECK-NEXT: {{ $}}
|
||||
; CHECK-NEXT: [[COPY:%[0-9]+]]:gprtc = COPY $x10
|
||||
; CHECK-NEXT: [[COPY1:%[0-9]+]]:gprtc = COPY $x10
|
||||
; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprtc = COPY $x10
|
||||
; CHECK-NEXT: PseudoTAILIndirect [[COPY2]], implicit $x2, cfi-type 12345678
|
||||
; CHECK-NEXT: {{ $}}
|
||||
; CHECK-NEXT: .1 (%ir-block.0):
|
||||
; CHECK-NEXT: PseudoTAILIndirect [[COPY]], implicit $x2, cfi-type 12345678
|
||||
%0:gprtc = COPY $x10
|
||||
PseudoTAILIndirect %0, implicit $x2, cfi-type 12345678
|
||||
|
||||
...
|
54
llvm/test/CodeGen/RISCV/kcfi-patchable-function-prefix.ll
Normal file
54
llvm/test/CodeGen/RISCV/kcfi-patchable-function-prefix.ll
Normal file
@ -0,0 +1,54 @@
|
||||
; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,NOC
|
||||
; RUN: llc -mtriple=riscv64 -mattr=+c -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,C
|
||||
|
||||
; NOC: .p2align 2
|
||||
; C: .p2align 1
|
||||
; CHECK-NOT: nop
|
||||
; CHECK: .word 12345678
|
||||
; CHECK-LABEL: f1:
|
||||
define void @f1(ptr noundef %x) !kcfi_type !1 {
|
||||
; CHECK: lw t1, -4(a0)
|
||||
call void %x() [ "kcfi"(i32 12345678) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
; NOC: .p2align 2
|
||||
; C: .p2align 1
|
||||
; CHECK-NOT: .word
|
||||
; CHECK-NOT: nop
|
||||
; CHECK-LABEL: f2:
|
||||
define void @f2(ptr noundef %x) {
|
||||
; CHECK: lw t1, -4(a0)
|
||||
call void %x() [ "kcfi"(i32 12345678) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
; NOC: .p2align 2
|
||||
; C: .p2align 1
|
||||
; CHECK: .word 12345678
|
||||
; CHECK-COUNT-11: nop
|
||||
; CHECK-LABEL: f3:
|
||||
define void @f3(ptr noundef %x) #0 !kcfi_type !1 {
|
||||
; NOC: lw t1, -48(a0)
|
||||
; C: lw t1, -26(a0)
|
||||
call void %x() [ "kcfi"(i32 12345678) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
; NOC: .p2align 2
|
||||
; C: .p2align 1
|
||||
; CHECK-NOT: .word
|
||||
; CHECK-COUNT-11: nop
|
||||
; CHECK-LABEL: f4:
|
||||
define void @f4(ptr noundef %x) #0 {
|
||||
; NOC: lw t1, -48(a0)
|
||||
; C: lw t1, -26(a0)
|
||||
call void %x() [ "kcfi"(i32 12345678) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "patchable-function-prefix"="11" }
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 4, !"kcfi", i32 1}
|
||||
!1 = !{i32 12345678}
|
66
llvm/test/CodeGen/RISCV/kcfi.ll
Normal file
66
llvm/test/CodeGen/RISCV/kcfi.ll
Normal file
@ -0,0 +1,66 @@
|
||||
; RUN: llc -mtriple=riscv32 -verify-machineinstrs -riscv-no-aliases < %s \
|
||||
; RUN: | FileCheck %s --check-prefixes=CHECK,RV32
|
||||
; RUN: llc -mtriple=riscv64 -verify-machineinstrs -riscv-no-aliases < %s \
|
||||
; RUN: | FileCheck %s --check-prefixes=CHECK,RV64
|
||||
|
||||
; CHECK: .word 12345678
|
||||
define void @f1(ptr noundef %x) !kcfi_type !1 {
|
||||
; CHECK-LABEL: f1:
|
||||
; CHECK: # %bb.0:
|
||||
; CHECK: lw t1, -4(a0)
|
||||
; CHECK-NEXT: lui t2, 3014
|
||||
; RV32-NEXT: addi t2, t2, 334
|
||||
; RV64-NEXT: addiw t2, t2, 334
|
||||
; CHECK-NEXT: beq t1, t2, .Ltmp0
|
||||
; CHECK-NEXT: .Ltmp1:
|
||||
; CHECK-NEXT: ebreak
|
||||
; CHECK-NEXT: .section .kcfi_traps,"ao",@progbits,.text
|
||||
; CHECK-NEXT: .Ltmp2:
|
||||
; CHECK-NEXT: .word .Ltmp1-.Ltmp2
|
||||
; CHECK-NEXT: .text
|
||||
; CHECK-NEXT: .Ltmp0:
|
||||
; CHECK-NEXT: jalr ra, 0(a0)
|
||||
call void %x() [ "kcfi"(i32 12345678) ]
|
||||
; CHECK: lw t1, -4(s0)
|
||||
; CHECK-NEXT: addi t2, t2, 1234
|
||||
; CHECK-NEXT: beq t1, t2, .Ltmp3
|
||||
; CHECK-NEXT: .Ltmp4:
|
||||
; CHECK-NEXT: ebreak
|
||||
; CHECK-NEXT: .section .kcfi_traps,"ao",@progbits,.text
|
||||
; CHECK-NEXT: .Ltmp5:
|
||||
; CHECK-NEXT: .word .Ltmp4-.Ltmp5
|
||||
; CHECK-NEXT: .text
|
||||
; CHECK-NEXT: .Ltmp3:
|
||||
; CHECK-NEXT: jalr ra, 0(s0)
|
||||
call void %x() [ "kcfi"(i32 1234) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-NOT: .word:
|
||||
define void @f2(ptr noundef %x) #0 {
|
||||
; CHECK-LABEL: f2:
|
||||
; CHECK: # %bb.0:
|
||||
; CHECK-NEXT: addi zero, zero, 0
|
||||
; CHECK-NEXT: addi zero, zero, 0
|
||||
; CHECK-NEXT: lw t1, -4(a0)
|
||||
; CHECK-NEXT: lui t2, 3014
|
||||
; RV32-NEXT: addi t2, t2, 334
|
||||
; RV64-NEXT: addiw t2, t2, 334
|
||||
; CHECK-NEXT: beq t1, t2, .Ltmp6
|
||||
; CHECK-NEXT: .Ltmp7:
|
||||
; CHECK-NEXT: ebreak
|
||||
; CHECK-NEXT: .section .kcfi_traps,"ao",@progbits,.text
|
||||
; CHECK-NEXT: .Ltmp8:
|
||||
; CHECK-NEXT: .word .Ltmp7-.Ltmp8
|
||||
; CHECK-NEXT: .text
|
||||
; CHECK-NEXT: .Ltmp6:
|
||||
; CHECK-NEXT: jalr zero, 0(a0)
|
||||
tail call void %x() [ "kcfi"(i32 12345678) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "patchable-function-entry"="2" }
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 4, !"kcfi", i32 1}
|
||||
!1 = !{i32 12345678}
|
185
llvm/test/CodeGen/RISCV/kcfi.mir
Normal file
185
llvm/test/CodeGen/RISCV/kcfi.mir
Normal file
@ -0,0 +1,185 @@
|
||||
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 2
|
||||
# RUN: llc -mtriple=riscv64 -stop-after=kcfi -o - %s | FileCheck %s
|
||||
--- |
|
||||
; ModuleID = '<stdin>'
|
||||
source_filename = "<stdin>"
|
||||
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "riscv64"
|
||||
|
||||
define void @f1(ptr noundef %x) !kcfi_type !1 {
|
||||
call void %x() [ "kcfi"(i32 12345678) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @f2(ptr noundef %x) #0 {
|
||||
tail call void %x() [ "kcfi"(i32 12345678) ]
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { "patchable-function-entry"="2" }
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
|
||||
!0 = !{i32 4, !"kcfi", i32 1}
|
||||
!1 = !{i32 12345678}
|
||||
|
||||
...
|
||||
---
|
||||
name: f1
|
||||
alignment: 4
|
||||
exposesReturnsTwice: false
|
||||
legalized: false
|
||||
regBankSelected: false
|
||||
selected: false
|
||||
failedISel: false
|
||||
tracksRegLiveness: true
|
||||
hasWinCFI: false
|
||||
callsEHReturn: false
|
||||
callsUnwindInit: false
|
||||
hasEHCatchret: false
|
||||
hasEHScopes: false
|
||||
hasEHFunclets: false
|
||||
isOutlined: false
|
||||
debugInstrRef: false
|
||||
failsVerification: false
|
||||
tracksDebugUserValues: true
|
||||
registers: []
|
||||
liveins:
|
||||
- { reg: '$x10', virtual-reg: '' }
|
||||
frameInfo:
|
||||
isFrameAddressTaken: false
|
||||
isReturnAddressTaken: false
|
||||
hasStackMap: false
|
||||
hasPatchPoint: false
|
||||
stackSize: 16
|
||||
offsetAdjustment: 0
|
||||
maxAlignment: 8
|
||||
adjustsStack: true
|
||||
hasCalls: true
|
||||
stackProtector: ''
|
||||
functionContext: ''
|
||||
maxCallFrameSize: 0
|
||||
cvBytesOfCalleeSavedRegisters: 0
|
||||
hasOpaqueSPAdjustment: false
|
||||
hasVAStart: false
|
||||
hasMustTailInVarArgFunc: false
|
||||
hasTailCall: false
|
||||
localFrameSize: 0
|
||||
savePoint: ''
|
||||
restorePoint: ''
|
||||
fixedStack: []
|
||||
stack:
|
||||
- { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8,
|
||||
stack-id: default, callee-saved-register: '$x1', callee-saved-restored: true,
|
||||
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
|
||||
callSites: []
|
||||
debugValueSubstitutions: []
|
||||
constants: []
|
||||
machineFunctionInfo:
|
||||
varArgsFrameIndex: 0
|
||||
varArgsSaveSize: 0
|
||||
body: |
|
||||
bb.0 (%ir-block.0):
|
||||
liveins: $x10, $x1
|
||||
|
||||
; CHECK-LABEL: name: f1
|
||||
; CHECK: liveins: $x1, $x10
|
||||
; CHECK-NEXT: {{ $}}
|
||||
; CHECK-NEXT: $x2 = frame-setup ADDI $x2, -16
|
||||
; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
|
||||
; CHECK-NEXT: SD $x1, $x2, 8 :: (store (s64) into %stack.1)
|
||||
; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $x1, -8
|
||||
; CHECK-NEXT: $x2 = frame-setup ADDI $x2, -16
|
||||
; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16
|
||||
; CHECK-NEXT: SD $x1, $x2, 8 :: (store (s64) into %stack.0)
|
||||
; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $x1, -8
|
||||
; CHECK-NEXT: BUNDLE implicit-def dead $x6, implicit-def dead $x7, implicit-def dead $x28, implicit-def dead $x29, implicit-def dead $x30, implicit-def dead $x31, implicit-def dead $x1, implicit-def $x2, implicit $x10 {
|
||||
; CHECK-NEXT: KCFI_CHECK $x10, 12345678, implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31
|
||||
; CHECK-NEXT: PseudoCALLIndirect $x10, csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2
|
||||
; CHECK-NEXT: }
|
||||
; CHECK-NEXT: dead $x1 = LD $x2, 8 :: (load (s64) from %stack.0)
|
||||
; CHECK-NEXT: $x2 = frame-destroy ADDI $x2, 16
|
||||
; CHECK-NEXT: $x1 = LD $x2, 8 :: (load (s64) from %stack.1)
|
||||
; CHECK-NEXT: $x2 = frame-destroy ADDI $x2, 16
|
||||
; CHECK-NEXT: PseudoRET
|
||||
$x2 = frame-setup ADDI $x2, -16
|
||||
frame-setup CFI_INSTRUCTION def_cfa_offset 16
|
||||
SD killed $x1, $x2, 8 :: (store (s64) into %stack.0)
|
||||
frame-setup CFI_INSTRUCTION offset $x1, -8
|
||||
BUNDLE implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31, implicit-def dead $x1, implicit-def $x2, implicit killed $x10 {
|
||||
KCFI_CHECK $x10, 12345678, implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31
|
||||
PseudoCALLIndirect killed $x10, csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2
|
||||
}
|
||||
$x1 = LD $x2, 8 :: (load (s64) from %stack.0)
|
||||
$x2 = frame-destroy ADDI $x2, 16
|
||||
PseudoRET
|
||||
|
||||
...
|
||||
---
|
||||
name: f2
|
||||
alignment: 4
|
||||
exposesReturnsTwice: false
|
||||
legalized: false
|
||||
regBankSelected: false
|
||||
selected: false
|
||||
failedISel: false
|
||||
tracksRegLiveness: true
|
||||
hasWinCFI: false
|
||||
callsEHReturn: false
|
||||
callsUnwindInit: false
|
||||
hasEHCatchret: false
|
||||
hasEHScopes: false
|
||||
hasEHFunclets: false
|
||||
isOutlined: false
|
||||
debugInstrRef: false
|
||||
failsVerification: false
|
||||
tracksDebugUserValues: true
|
||||
registers: []
|
||||
liveins:
|
||||
- { reg: '$x10', virtual-reg: '' }
|
||||
frameInfo:
|
||||
isFrameAddressTaken: false
|
||||
isReturnAddressTaken: false
|
||||
hasStackMap: false
|
||||
hasPatchPoint: false
|
||||
stackSize: 0
|
||||
offsetAdjustment: 0
|
||||
maxAlignment: 1
|
||||
adjustsStack: false
|
||||
hasCalls: false
|
||||
stackProtector: ''
|
||||
functionContext: ''
|
||||
maxCallFrameSize: 0
|
||||
cvBytesOfCalleeSavedRegisters: 0
|
||||
hasOpaqueSPAdjustment: false
|
||||
hasVAStart: false
|
||||
hasMustTailInVarArgFunc: false
|
||||
hasTailCall: true
|
||||
localFrameSize: 0
|
||||
savePoint: ''
|
||||
restorePoint: ''
|
||||
fixedStack: []
|
||||
stack: []
|
||||
callSites: []
|
||||
debugValueSubstitutions: []
|
||||
constants: []
|
||||
machineFunctionInfo:
|
||||
varArgsFrameIndex: 0
|
||||
varArgsSaveSize: 0
|
||||
body: |
|
||||
bb.0 (%ir-block.0):
|
||||
liveins: $x10
|
||||
|
||||
; CHECK-LABEL: name: f2
|
||||
; CHECK: liveins: $x10
|
||||
; CHECK-NEXT: {{ $}}
|
||||
; CHECK-NEXT: BUNDLE implicit-def dead $x6, implicit-def dead $x7, implicit-def dead $x28, implicit-def dead $x29, implicit-def dead $x30, implicit-def dead $x31, implicit $x10, implicit $x2 {
|
||||
; CHECK-NEXT: KCFI_CHECK $x10, 12345678, implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31
|
||||
; CHECK-NEXT: PseudoTAILIndirect $x10, implicit $x2
|
||||
; CHECK-NEXT: }
|
||||
BUNDLE implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31, implicit killed $x10, implicit $x2 {
|
||||
KCFI_CHECK $x10, 12345678, implicit-def $x6, implicit-def $x7, implicit-def $x28, implicit-def $x29, implicit-def $x30, implicit-def $x31
|
||||
PseudoTAILIndirect killed $x10, implicit $x2
|
||||
}
|
||||
|
||||
...
|
Loading…
Reference in New Issue
Block a user