This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG1c466477ad46: [RISCV] Support Shadow Call Stack (authored by
zzheng).
Changed prior to commit:
https://reviews.llvm.org/D84414?vs=292378&id=292651#toc
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: lw ra, -4(s2)
+; RV32-NEXT: addi s2, s2, -4
+; 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: ld ra, -8(s2)
+; RV64-NEXT: addi s2, s2, -8
+; 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: lw ra, -4(s2)
+; RV32-NEXT: addi s2, s2, -4
+; 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: ld ra, -8(s2)
+; RV64-NEXT: addi s2, s2, -8
+; 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: lw ra, -4(s2)
+; RV32-NEXT: addi s2, s2, -4
+; 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: ld ra, -8(s2)
+; RV64-NEXT: addi s2, s2, -8
+; 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,105 @@
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,
+ const DebugLoc &DL) {
+ if (!MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack))
+ return;
+
+ const auto &STI = MF.getSubtarget<RISCVSubtarget>();
+ Register RAReg = STI.getRegisterInfo()->getRARegister();
+
+ // Do not save RA to the SCS if it's not saved to the regular stack,
+ // i.e. RA is not at risk of being overwritten.
+ std::vector<CalleeSavedInfo> &CSI = MF.getFrameInfo().getCalleeSavedInfo();
+ if (std::none_of(CSI.begin(), CSI.end(),
+ [&](CalleeSavedInfo &CSR) { return CSR.getReg() == RAReg; }))
+ return;
+
+ Register SCSPReg = RISCVABI::getSCSPReg();
+
+ auto &Ctx = MF.getFunction().getContext();
+ if (!STI.isRegisterReservedByUser(SCSPReg)) {
+ Ctx.diagnose(DiagnosticInfoUnsupported{
+ MF.getFunction(), "x18 not reserved by user for Shadow Call Stack."});
+ return;
+ }
+
+ const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+ if (RVFI->useSaveRestoreLibCalls(MF)) {
+ Ctx.diagnose(DiagnosticInfoUnsupported{
+ MF.getFunction(),
+ "Shadow Call Stack cannot be combined with Save/Restore LibCalls."});
+ return;
+ }
+
+ const RISCVInstrInfo *TII = STI.getInstrInfo();
+ bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit);
+ int64_t SlotSize = STI.getXLen() / 8;
+ // Store return address to shadow call stack
+ // s[w|d] ra, 0(s2)
+ // addi s2, s2, [4|8]
+ 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,
+ const DebugLoc &DL) {
+ if (!MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack))
+ return;
+
+ const auto &STI = MF.getSubtarget<RISCVSubtarget>();
+ Register RAReg = STI.getRegisterInfo()->getRARegister();
+
+ // See emitSCSPrologue() above.
+ std::vector<CalleeSavedInfo> &CSI = MF.getFrameInfo().getCalleeSavedInfo();
+ if (std::none_of(CSI.begin(), CSI.end(),
+ [&](CalleeSavedInfo &CSR) { return CSR.getReg() == RAReg; }))
+ return;
+
+ Register SCSPReg = RISCVABI::getSCSPReg();
+
+ auto &Ctx = MF.getFunction().getContext();
+ if (!STI.isRegisterReservedByUser(SCSPReg)) {
+ Ctx.diagnose(DiagnosticInfoUnsupported{
+ MF.getFunction(), "x18 not reserved by user for Shadow Call Stack."});
+ return;
+ }
+
+ const auto *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+ if (RVFI->useSaveRestoreLibCalls(MF)) {
+ Ctx.diagnose(DiagnosticInfoUnsupported{
+ MF.getFunction(),
+ "Shadow Call Stack cannot be combined with Save/Restore LibCalls."});
+ return;
+ }
+
+ const RISCVInstrInfo *TII = STI.getInstrInfo();
+ bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit);
+ int64_t SlotSize = STI.getXLen() / 8;
+ // Load return address from shadow call stack
+ // l[w|d] ra, -[4|8](s2)
+ // addi s2, s2, -[4|8]
+ BuildMI(MBB, MI, DL, TII->get(IsRV64 ? RISCV::LD : RISCV::LW))
+ .addReg(RAReg, RegState::Define)
+ .addReg(SCSPReg)
+ .addImm(-SlotSize);
+ BuildMI(MBB, MI, DL, TII->get(RISCV::ADDI))
+ .addReg(SCSPReg, RegState::Define)
+ .addReg(SCSPReg)
+ .addImm(-SlotSize);
+}
+
// 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,15 +321,18 @@
Register SPReg = getSPReg(STI);
Register BPReg = RISCVABI::getBPReg();
+ // Debug location must be unknown since the first debug location is used
+ // to determine the end of the prologue.
+ DebugLoc DL;
+
+ // Emit prologue for shadow call stack.
+ emitSCSPrologue(MF, MBB, MBBI, DL);
+
// Since spillCalleeSavedRegisters may have inserted a libcall, skip past
// any instructions marked as FrameSetup
while (MBBI != MBB.end() && MBBI->getFlag(MachineInstr::FrameSetup))
++MBBI;
- // Debug location must be unknown since the first debug location is used
- // to determine the end of the prologue.
- DebugLoc DL;
-
// Determine the correct frame layout
determineFrameLayout(MF);
@@ -457,6 +559,9 @@
// Deallocate stack
adjustReg(MBB, MBBI, DL, SPReg, SPReg, StackSize, MachineInstr::FrameDestroy);
+
+ // Emit epilogue for shadow call stack.
+ emitSCSEpilogue(MF, MBB, MBBI, DL);
}
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
@@ -614,6 +614,16 @@
// RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-LINUX-AARCH64 %s
// 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
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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits