|
19 | 19 | #include "RISCVMachineFunctionInfo.h" |
20 | 20 | #include "RISCVTargetMachine.h" |
21 | 21 | #include "TargetInfo/RISCVTargetInfo.h" |
| 22 | +#include "llvm/ADT/APInt.h" |
22 | 23 | #include "llvm/ADT/Statistic.h" |
23 | 24 | #include "llvm/BinaryFormat/ELF.h" |
24 | 25 | #include "llvm/CodeGen/AsmPrinter.h" |
@@ -72,6 +73,7 @@ class RISCVAsmPrinter : public AsmPrinter { |
72 | 73 | typedef std::tuple<unsigned, uint32_t> HwasanMemaccessTuple; |
73 | 74 | std::map<HwasanMemaccessTuple, MCSymbol *> HwasanMemaccessSymbols; |
74 | 75 | void LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI); |
| 76 | + void LowerKCFI_CHECK(const MachineInstr &MI); |
75 | 77 | void EmitHwasanMemaccessSymbols(Module &M); |
76 | 78 |
|
77 | 79 | // Wrapper needed for tblgenned pseudo lowering. |
@@ -150,6 +152,9 @@ void RISCVAsmPrinter::emitInstruction(const MachineInstr *MI) { |
150 | 152 | case RISCV::HWASAN_CHECK_MEMACCESS_SHORTGRANULES: |
151 | 153 | LowerHWASAN_CHECK_MEMACCESS(*MI); |
152 | 154 | return; |
| 155 | + case RISCV::KCFI_CHECK: |
| 156 | + LowerKCFI_CHECK(*MI); |
| 157 | + return; |
153 | 158 | case RISCV::PseudoRVVInitUndefM1: |
154 | 159 | case RISCV::PseudoRVVInitUndefM2: |
155 | 160 | case RISCV::PseudoRVVInitUndefM4: |
@@ -305,6 +310,92 @@ void RISCVAsmPrinter::LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI) { |
305 | 310 | EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::PseudoCALL).addExpr(Expr)); |
306 | 311 | } |
307 | 312 |
|
| 313 | +void RISCVAsmPrinter::LowerKCFI_CHECK(const MachineInstr &MI) { |
| 314 | + Register AddrReg = MI.getOperand(0).getReg(); |
| 315 | + assert(std::next(MI.getIterator())->isCall() && |
| 316 | + "KCFI_CHECK not followed by a call instruction"); |
| 317 | + assert(std::next(MI.getIterator())->getOperand(0).getReg() == AddrReg && |
| 318 | + "KCFI_CHECK call target doesn't match call operand"); |
| 319 | + |
| 320 | + // Temporary registers for comparing the hashes. If a register is used |
| 321 | + // for the call target, or reserved by the user, we can clobber another |
| 322 | + // temporary register as the check is immediately followed by the |
| 323 | + // call. The check defaults to X6/X7, but can fall back to X28-X31 if |
| 324 | + // needed. |
| 325 | + unsigned ScratchRegs[] = {RISCV::X6, RISCV::X7}; |
| 326 | + unsigned NextReg = RISCV::X28; |
| 327 | + auto isRegAvailable = [&](unsigned Reg) { |
| 328 | + return Reg != AddrReg && !STI->isRegisterReservedByUser(Reg); |
| 329 | + }; |
| 330 | + for (auto &Reg : ScratchRegs) { |
| 331 | + if (isRegAvailable(Reg)) |
| 332 | + continue; |
| 333 | + while (!isRegAvailable(NextReg)) |
| 334 | + ++NextReg; |
| 335 | + Reg = NextReg++; |
| 336 | + if (Reg > RISCV::X31) |
| 337 | + report_fatal_error("Unable to find scratch registers for KCFI_CHECK"); |
| 338 | + } |
| 339 | + |
| 340 | + if (AddrReg == RISCV::X0) { |
| 341 | + // Checking X0 makes no sense. Instead of emitting a load, zero |
| 342 | + // ScratchRegs[0]. |
| 343 | + EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::ADDI) |
| 344 | + .addReg(ScratchRegs[0]) |
| 345 | + .addReg(RISCV::X0) |
| 346 | + .addImm(0)); |
| 347 | + } else { |
| 348 | + // Adjust the offset for patchable-function-prefix. This assumes that |
| 349 | + // patchable-function-prefix is the same for all functions. |
| 350 | + int NopSize = STI->hasStdExtCOrZca() ? 2 : 4; |
| 351 | + int64_t PrefixNops = 0; |
| 352 | + (void)MI.getMF() |
| 353 | + ->getFunction() |
| 354 | + .getFnAttribute("patchable-function-prefix") |
| 355 | + .getValueAsString() |
| 356 | + .getAsInteger(10, PrefixNops); |
| 357 | + |
| 358 | + // Load the target function type hash. |
| 359 | + EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::LW) |
| 360 | + .addReg(ScratchRegs[0]) |
| 361 | + .addReg(AddrReg) |
| 362 | + .addImm(-(PrefixNops * NopSize + 4))); |
| 363 | + } |
| 364 | + |
| 365 | + // Load the expected 32-bit type hash. |
| 366 | + const int64_t Type = MI.getOperand(1).getImm(); |
| 367 | + const int64_t Hi20 = ((Type + 0x800) >> 12) & 0xFFFFF; |
| 368 | + const int64_t Lo12 = SignExtend64<12>(Type); |
| 369 | + if (Hi20) { |
| 370 | + EmitToStreamer( |
| 371 | + *OutStreamer, |
| 372 | + MCInstBuilder(RISCV::LUI).addReg(ScratchRegs[1]).addImm(Hi20)); |
| 373 | + } |
| 374 | + if (Lo12 || Hi20 == 0) { |
| 375 | + EmitToStreamer(*OutStreamer, |
| 376 | + MCInstBuilder((STI->hasFeature(RISCV::Feature64Bit) && Hi20) |
| 377 | + ? RISCV::ADDIW |
| 378 | + : RISCV::ADDI) |
| 379 | + .addReg(ScratchRegs[1]) |
| 380 | + .addReg(ScratchRegs[1]) |
| 381 | + .addImm(Lo12)); |
| 382 | + } |
| 383 | + |
| 384 | + // Compare the hashes and trap if there's a mismatch. |
| 385 | + MCSymbol *Pass = OutContext.createTempSymbol(); |
| 386 | + EmitToStreamer(*OutStreamer, |
| 387 | + MCInstBuilder(RISCV::BEQ) |
| 388 | + .addReg(ScratchRegs[0]) |
| 389 | + .addReg(ScratchRegs[1]) |
| 390 | + .addExpr(MCSymbolRefExpr::create(Pass, OutContext))); |
| 391 | + |
| 392 | + MCSymbol *Trap = OutContext.createTempSymbol(); |
| 393 | + OutStreamer->emitLabel(Trap); |
| 394 | + EmitToStreamer(*OutStreamer, MCInstBuilder(RISCV::EBREAK)); |
| 395 | + emitKCFITrapEntry(*MI.getMF(), Trap); |
| 396 | + OutStreamer->emitLabel(Pass); |
| 397 | +} |
| 398 | + |
308 | 399 | void RISCVAsmPrinter::EmitHwasanMemaccessSymbols(Module &M) { |
309 | 400 | if (HwasanMemaccessSymbols.empty()) |
310 | 401 | return; |
|
0 commit comments