zzheng updated this revision to Diff 287428. zzheng added a comment. Rebased & ping...
IMHO, the patch is in good shape. As we discussed in the bi-weekly meetings, RV32E only has 16 registers. Systems based on RV32E may have limited memory as well. Besides, LLVM does not have full support for RV32E yet. We can commit this patch as-is and change it later if RV32E needs SCS. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D84414/new/ https://reviews.llvm.org/D84414 Files: clang/lib/Driver/SanitizerArgs.cpp clang/lib/Driver/ToolChain.cpp clang/test/CodeGen/shadowcallstack-attr.c clang/test/Driver/sanitizer-ld.c llvm/lib/Target/RISCV/RISCVFrameLowering.cpp llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.cpp llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.h llvm/test/CodeGen/RISCV/shadowcallstack.ll
Index: llvm/test/CodeGen/RISCV/shadowcallstack.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/RISCV/shadowcallstack.ll @@ -0,0 +1,174 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=riscv32 -mattr=+reserve-x18 -verify-machineinstrs < %s \ +; RUN: | FileCheck %s --check-prefix=RV32 +; RUN: llc -mtriple=riscv64 -mattr=+reserve-x18 -verify-machineinstrs < %s \ +; RUN: | FileCheck %s --check-prefix=RV64 + +define void @f1() shadowcallstack { +; RV32-LABEL: f1: +; RV32: # %bb.0: +; RV32-NEXT: ret +; +; RV64-LABEL: f1: +; RV64: # %bb.0: +; RV64-NEXT: ret + ret void +} + +declare void @foo() + +define void @f2() shadowcallstack { +; RV32-LABEL: f2: +; RV32: # %bb.0: +; RV32-NEXT: tail foo +; +; RV64-LABEL: f2: +; RV64: # %bb.0: +; RV64-NEXT: tail foo + tail call void @foo() + ret void +} + +declare i32 @bar() + +define i32 @f3() shadowcallstack { +; RV32-LABEL: f3: +; RV32: # %bb.0: +; RV32-NEXT: sw ra, 0(s2) +; RV32-NEXT: addi s2, s2, 4 +; RV32-NEXT: addi sp, sp, -16 +; RV32-NEXT: .cfi_def_cfa_offset 16 +; RV32-NEXT: sw ra, 12(sp) +; RV32-NEXT: .cfi_offset ra, -4 +; RV32-NEXT: call bar +; RV32-NEXT: lw ra, 12(sp) +; RV32-NEXT: addi sp, sp, 16 +; RV32-NEXT: addi s2, s2, -4 +; RV32-NEXT: lw ra, 0(s2) +; RV32-NEXT: ret +; +; RV64-LABEL: f3: +; RV64: # %bb.0: +; RV64-NEXT: sd ra, 0(s2) +; RV64-NEXT: addi s2, s2, 8 +; RV64-NEXT: addi sp, sp, -16 +; RV64-NEXT: .cfi_def_cfa_offset 16 +; RV64-NEXT: sd ra, 8(sp) +; RV64-NEXT: .cfi_offset ra, -8 +; RV64-NEXT: call bar +; RV64-NEXT: ld ra, 8(sp) +; RV64-NEXT: addi sp, sp, 16 +; RV64-NEXT: addi s2, s2, -8 +; RV64-NEXT: ld ra, 0(s2) +; RV64-NEXT: ret + %res = call i32 @bar() + %res1 = add i32 %res, 1 + ret i32 %res +} + +define i32 @f4() shadowcallstack { +; RV32-LABEL: f4: +; RV32: # %bb.0: +; RV32-NEXT: sw ra, 0(s2) +; RV32-NEXT: addi s2, s2, 4 +; RV32-NEXT: addi sp, sp, -16 +; RV32-NEXT: .cfi_def_cfa_offset 16 +; RV32-NEXT: sw ra, 12(sp) +; RV32-NEXT: sw s0, 8(sp) +; RV32-NEXT: sw s1, 4(sp) +; RV32-NEXT: sw s3, 0(sp) +; RV32-NEXT: .cfi_offset ra, -4 +; RV32-NEXT: .cfi_offset s0, -8 +; RV32-NEXT: .cfi_offset s1, -12 +; RV32-NEXT: .cfi_offset s3, -16 +; RV32-NEXT: call bar +; RV32-NEXT: mv s3, a0 +; RV32-NEXT: call bar +; RV32-NEXT: mv s1, a0 +; RV32-NEXT: call bar +; RV32-NEXT: mv s0, a0 +; RV32-NEXT: call bar +; RV32-NEXT: add a1, s3, s1 +; RV32-NEXT: add a0, s0, a0 +; RV32-NEXT: add a0, a1, a0 +; RV32-NEXT: lw s3, 0(sp) +; RV32-NEXT: lw s1, 4(sp) +; RV32-NEXT: lw s0, 8(sp) +; RV32-NEXT: lw ra, 12(sp) +; RV32-NEXT: addi sp, sp, 16 +; RV32-NEXT: addi s2, s2, -4 +; RV32-NEXT: lw ra, 0(s2) +; RV32-NEXT: ret +; +; RV64-LABEL: f4: +; RV64: # %bb.0: +; RV64-NEXT: sd ra, 0(s2) +; RV64-NEXT: addi s2, s2, 8 +; RV64-NEXT: addi sp, sp, -32 +; RV64-NEXT: .cfi_def_cfa_offset 32 +; RV64-NEXT: sd ra, 24(sp) +; RV64-NEXT: sd s0, 16(sp) +; RV64-NEXT: sd s1, 8(sp) +; RV64-NEXT: sd s3, 0(sp) +; RV64-NEXT: .cfi_offset ra, -8 +; RV64-NEXT: .cfi_offset s0, -16 +; RV64-NEXT: .cfi_offset s1, -24 +; RV64-NEXT: .cfi_offset s3, -32 +; RV64-NEXT: call bar +; RV64-NEXT: mv s3, a0 +; RV64-NEXT: call bar +; RV64-NEXT: mv s1, a0 +; RV64-NEXT: call bar +; RV64-NEXT: mv s0, a0 +; RV64-NEXT: call bar +; RV64-NEXT: add a1, s3, s1 +; RV64-NEXT: add a0, s0, a0 +; RV64-NEXT: addw a0, a1, a0 +; RV64-NEXT: ld s3, 0(sp) +; RV64-NEXT: ld s1, 8(sp) +; RV64-NEXT: ld s0, 16(sp) +; RV64-NEXT: ld ra, 24(sp) +; RV64-NEXT: addi sp, sp, 32 +; RV64-NEXT: addi s2, s2, -8 +; RV64-NEXT: ld ra, 0(s2) +; RV64-NEXT: ret + %res1 = call i32 @bar() + %res2 = call i32 @bar() + %res3 = call i32 @bar() + %res4 = call i32 @bar() + %res12 = add i32 %res1, %res2 + %res34 = add i32 %res3, %res4 + %res1234 = add i32 %res12, %res34 + ret i32 %res1234 +} + +define i32 @f5() shadowcallstack nounwind { +; RV32-LABEL: f5: +; RV32: # %bb.0: +; RV32-NEXT: sw ra, 0(s2) +; RV32-NEXT: addi s2, s2, 4 +; RV32-NEXT: addi sp, sp, -16 +; RV32-NEXT: sw ra, 12(sp) +; RV32-NEXT: call bar +; RV32-NEXT: lw ra, 12(sp) +; RV32-NEXT: addi sp, sp, 16 +; RV32-NEXT: addi s2, s2, -4 +; RV32-NEXT: lw ra, 0(s2) +; RV32-NEXT: ret +; +; RV64-LABEL: f5: +; RV64: # %bb.0: +; RV64-NEXT: sd ra, 0(s2) +; RV64-NEXT: addi s2, s2, 8 +; RV64-NEXT: addi sp, sp, -16 +; RV64-NEXT: sd ra, 8(sp) +; RV64-NEXT: call bar +; RV64-NEXT: ld ra, 8(sp) +; RV64-NEXT: addi sp, sp, 16 +; RV64-NEXT: addi s2, s2, -8 +; RV64-NEXT: ld ra, 0(s2) +; RV64-NEXT: ret + %res = call i32 @bar() + %res1 = add i32 %res, 1 + ret i32 %res +} Index: llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.h =================================================================== --- llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.h +++ llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.h @@ -208,6 +208,9 @@ // Returns the register used to hold the stack pointer after realignment. Register getBPReg(); +// Returns the register holding shadow call stack pointer. +Register getSCSPReg(); + } // namespace RISCVABI namespace RISCVFeatures { Index: llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.cpp =================================================================== --- llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.cpp +++ llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.cpp @@ -67,6 +67,9 @@ // saved registers and X8 will be used as fp. So we choose X9 as bp. Register getBPReg() { return RISCV::X9; } +// Returns the register holding shadow call stack pointer. +Register getSCSPReg() { return RISCV::X18; } + } // namespace RISCVABI namespace RISCVFeatures { Index: llvm/lib/Target/RISCV/RISCVFrameLowering.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -23,6 +23,94 @@ using namespace llvm; +// For now we use x18, a.k.a s2, as pointer to shadow call stack. +// User should explicitly set -ffixed-x18 and not use x18 in their asm. +static void emitSCSPrologue(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) { + if (!MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)) + return; + + const auto &STI = MF.getSubtarget<RISCVSubtarget>(); + Register RAReg = STI.getRegisterInfo()->getRARegister(); + + // Do not save RA to SCS if it's not saved to regular stack, i.e. + // RA is not subject to overwritten. + std::vector<CalleeSavedInfo> &CSI = MF.getFrameInfo().getCalleeSavedInfo(); + if (std::none_of(CSI.begin(), CSI.end(), + [&](CalleeSavedInfo &CSR) { return CSR.getReg() == RAReg; })) + return; + + // Get shadow call stack pointer register. + Register SCSPReg = RISCVABI::getSCSPReg(); + + // Emit an error message and bail out. + if (!STI.isRegisterReservedByUser(SCSPReg)) { + MF.getFunction().getContext().diagnose(DiagnosticInfoUnsupported{ + MF.getFunction(), "x18 not reserved by user for Shadow Call Stack."}); + return; + } + + DebugLoc DL = MI != MBB.end() ? MI->getDebugLoc() : DebugLoc(); + + const RISCVInstrInfo *TII = STI.getInstrInfo(); + bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit); + int64_t SlotSize = STI.getXLen() / 8; + // Store return address to shadow call stack + // sw ra, 0(s2) + // addi s2, s2, 4 + BuildMI(MBB, MI, DL, TII->get(IsRV64 ? RISCV::SD : RISCV::SW)) + .addReg(RAReg) + .addReg(SCSPReg) + .addImm(0); + BuildMI(MBB, MI, DL, TII->get(RISCV::ADDI)) + .addReg(SCSPReg, RegState::Define) + .addReg(SCSPReg) + .addImm(SlotSize); +} + +static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) { + if (!MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack)) + return; + + const auto &STI = MF.getSubtarget<RISCVSubtarget>(); + Register RAReg = STI.getRegisterInfo()->getRARegister(); + + // Do not restore RA from SCS if it's not saved to regular stack, i.e. + // RA is not subject to overwritten. + std::vector<CalleeSavedInfo> &CSI = MF.getFrameInfo().getCalleeSavedInfo(); + if (std::none_of(CSI.begin(), CSI.end(), + [&](CalleeSavedInfo &CSR) { return CSR.getReg() == RAReg; })) + return; + + // Get shadow call stack pointer register. + Register SCSPReg = RISCVABI::getSCSPReg(); + + // Emit an error message and bail out. + if (!STI.isRegisterReservedByUser(SCSPReg)) { + MF.getFunction().getContext().diagnose(DiagnosticInfoUnsupported{ + MF.getFunction(), "x18 not reserved by user for Shadow Call Stack."}); + return; + } + + DebugLoc DL = MI != MBB.end() ? MI->getDebugLoc() : DebugLoc(); + + const RISCVInstrInfo *TII = STI.getInstrInfo(); + bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit); + int64_t SlotSize = STI.getXLen() / 8; + // Load return address from shadow call stack + // addi s2, s2, -4 + // lw ra, 0(s2) + BuildMI(MBB, MI, DL, TII->get(RISCV::ADDI)) + .addReg(SCSPReg, RegState::Define) + .addReg(SCSPReg) + .addImm(-SlotSize); + BuildMI(MBB, MI, DL, TII->get(IsRV64 ? RISCV::LD : RISCV::LW)) + .addReg(RAReg, RegState::Define) + .addReg(SCSPReg) + .addImm(0); +} + // Get the ID of the libcall used for spilling and restoring callee saved // registers. The ID is representative of the number of registers saved or // restored by the libcall, except it is zero-indexed - ID 0 corresponds to a @@ -222,6 +310,9 @@ Register SPReg = getSPReg(STI); Register BPReg = RISCVABI::getBPReg(); + // Emit prologue for shadow call stack. + emitSCSPrologue(MF, MBB, MBBI); + // Since spillCalleeSavedRegisters may have inserted a libcall, skip past // any instructions marked as FrameSetup while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup)) @@ -457,6 +548,9 @@ // Deallocate stack adjustReg(MBB, MBBI, DL, SPReg, SPReg, StackSize, MachineInstr::FrameDestroy); + + // Emit epilogue for shadow call stack. + emitSCSEpilogue(MF, MBB, MBBI); } int RISCVFrameLowering::getFrameIndexReference(const MachineFunction &MF, Index: clang/test/Driver/sanitizer-ld.c =================================================================== --- clang/test/Driver/sanitizer-ld.c +++ clang/test/Driver/sanitizer-ld.c @@ -615,6 +615,16 @@ // CHECK-SHADOWCALLSTACK-LINUX-AARCH64: '-fsanitize=shadow-call-stack' only allowed with '-ffixed-x18' // RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \ +// RUN: -target riscv32-unknown-elf -fuse-ld=ld \ +// RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-ELF-RISCV32 %s +// CHECK-SHADOWCALLSTACK-ELF-RISCV32: '-fsanitize=shadow-call-stack' only allowed with '-ffixed-x18' + +// RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \ +// RUN: -target riscv64-unknown-linux -fuse-ld=ld \ +// RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-LINUX-RISCV64 %s +// CHECK-SHADOWCALLSTACK-LINUX-RISCV64: '-fsanitize=shadow-call-stack' only allowed with '-ffixed-x18' + +// RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \ // RUN: -target aarch64-unknown-linux -fuse-ld=ld -ffixed-x18 \ // RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-LINUX-AARCH64-X18 %s // RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \ Index: clang/test/CodeGen/shadowcallstack-attr.c =================================================================== --- clang/test/CodeGen/shadowcallstack-attr.c +++ clang/test/CodeGen/shadowcallstack-attr.c @@ -1,9 +1,23 @@ -// RUN: %clang_cc1 -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=UNBLACKLISTED %s +// RUN: %clang_cc1 -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=UNBLOCKLISTED %s -// RUN: %clang_cc1 -D ATTR -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLACKLISTED %s +// RUN: %clang_cc1 -D ATTR -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLOCKLISTED %s // RUN: echo -e "[shadow-call-stack]\nfun:foo" > %t -// RUN: %clang_cc1 -fsanitize-blacklist=%t -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLACKLISTED %s +// RUN: %clang_cc1 -fsanitize-blacklist=%t -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLOCKLISTED %s + +// RUN: %clang_cc1 -triple riscv32-linux-gnu -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=UNBLOCKLISTED %s + +// RUN: %clang_cc1 -D ATTR -triple riscv32-linux-gnu -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLOCKLISTED %s + +// RUN: echo -e "[shadow-call-stack]\nfun:foo" > %t +// RUN: %clang_cc1 -fsanitize-blacklist=%t -triple riscv32-linux-gnu -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLOCKLISTED %s + +// RUN: %clang_cc1 -triple riscv64-linux-gnu -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=UNBLOCKLISTED %s + +// RUN: %clang_cc1 -D ATTR -triple riscv64-linux-gnu -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLOCKLISTED %s + +// RUN: echo -e "[shadow-call-stack]\nfun:foo" > %t +// RUN: %clang_cc1 -fsanitize-blacklist=%t -triple riscv64-linux-gnu -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLOCKLISTED %s #ifdef ATTR __attribute__((no_sanitize("shadow-call-stack"))) @@ -12,5 +26,5 @@ // CHECK: define i32 @foo(i32* %a) -// BLACKLISTED-NOT: attributes {{.*}}shadowcallstack{{.*}} -// UNBLACKLISTED: attributes {{.*}}shadowcallstack{{.*}} +// BLOCKLISTED-NOT: attributes {{.*}}shadowcallstack{{.*}} +// UNBLOCKLISTED: attributes {{.*}}shadowcallstack{{.*}} Index: clang/lib/Driver/ToolChain.cpp =================================================================== --- clang/lib/Driver/ToolChain.cpp +++ clang/lib/Driver/ToolChain.cpp @@ -1029,7 +1029,8 @@ getTriple().getArch() == llvm::Triple::arm || getTriple().isWasm() || getTriple().isAArch64()) Res |= SanitizerKind::CFIICall; - if (getTriple().getArch() == llvm::Triple::x86_64 || getTriple().isAArch64()) + if (getTriple().getArch() == llvm::Triple::x86_64 || + getTriple().isAArch64() || getTriple().isRISCV()) Res |= SanitizerKind::ShadowCallStack; if (getTriple().isAArch64()) Res |= SanitizerKind::MemTag; Index: clang/lib/Driver/SanitizerArgs.cpp =================================================================== --- clang/lib/Driver/SanitizerArgs.cpp +++ clang/lib/Driver/SanitizerArgs.cpp @@ -495,8 +495,10 @@ << lastArgumentForMask(D, Args, Kinds & NeedsLTO) << "-flto"; } - if ((Kinds & SanitizerKind::ShadowCallStack) && TC.getTriple().isAArch64() && - !llvm::AArch64::isX18ReservedByDefault(TC.getTriple()) && + if ((Kinds & SanitizerKind::ShadowCallStack) && + ((TC.getTriple().isAArch64() && + !llvm::AArch64::isX18ReservedByDefault(TC.getTriple())) || + TC.getTriple().isRISCV()) && !Args.hasArg(options::OPT_ffixed_x18)) { D.Diag(diag::err_drv_argument_only_allowed_with) << lastArgumentForMask(D, Args, Kinds & SanitizerKind::ShadowCallStack)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits