[llvm] a19da87 - [ARM] implement support for TLS register based stack protector
Ard Biesheuvel via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 9 09:23:22 PST 2021
Author: Ard Biesheuvel
Date: 2021-11-09T18:19:47+01:00
New Revision: a19da876ab93d54ebc20aadd12820f74220d2f50
URL: https://github.com/llvm/llvm-project/commit/a19da876ab93d54ebc20aadd12820f74220d2f50
DIFF: https://github.com/llvm/llvm-project/commit/a19da876ab93d54ebc20aadd12820f74220d2f50.diff
LOG: [ARM] implement support for TLS register based stack protector
Implement support for loading the stack canary from a memory location held in
the TLS register, with an optional offset applied. This is used by the Linux
kernel to implement per-task stack canaries, which is impossible on SMP systems
when using a global variable for the stack canary.
Reviewed By: nickdesaulniers
Differential Revision: https://reviews.llvm.org/D112768
Added:
llvm/test/CodeGen/ARM/stack-guard-tls.ll
Modified:
clang/include/clang/Basic/DiagnosticCommonKinds.td
clang/include/clang/Basic/DiagnosticDriverKinds.td
clang/lib/Driver/ToolChains/Clang.cpp
clang/test/Driver/stack-protector-guard.c
llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
llvm/lib/Target/ARM/ARMInstrInfo.cpp
llvm/lib/Target/ARM/Thumb1InstrInfo.cpp
llvm/lib/Target/ARM/Thumb2InstrInfo.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index 1ed1c8cd9a19f..fe4ac5ed6cb03 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -298,6 +298,8 @@ def err_target_unsupported_unaligned : Error<
"the %0 sub-architecture does not support unaligned accesses">;
def err_target_unsupported_execute_only : Error<
"execute only is not supported for the %0 sub-architecture">;
+def err_target_unsupported_tp_hard : Error<
+ "hardware TLS register is not supported for the %0 sub-architecture">;
def err_target_unsupported_mcmse : Error<
"-mcmse is not supported for %0">;
def err_opt_not_valid_with_opt : Error<
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index fa464952189ba..8e7c14dc1549b 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -592,4 +592,7 @@ def err_cc1_round_trip_ok_then_fail : Error<
"generated arguments parse failed in round-trip">;
def err_cc1_round_trip_mismatch : Error<
"generated arguments do not match in round-trip">;
+
+def err_drv_ssp_missing_offset_argument : Error<
+ "'%0' is used without '-mstack-protector-guard-offset', and there is no default">;
}
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index dc20ae05ed419..e8ad105a78290 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3162,14 +3162,44 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
const std::string &TripleStr = EffectiveTriple.getTriple();
if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_EQ)) {
StringRef Value = A->getValue();
- if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64())
+ if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() &&
+ !EffectiveTriple.isARM() && !EffectiveTriple.isThumb())
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getAsString(Args) << TripleStr;
- if (EffectiveTriple.isX86() && Value != "tls" && Value != "global") {
+ if ((EffectiveTriple.isX86() || EffectiveTriple.isARM() ||
+ EffectiveTriple.isThumb()) &&
+ Value != "tls" && Value != "global") {
D.Diag(diag::err_drv_invalid_value_with_suggestion)
<< A->getOption().getName() << Value << "tls global";
return;
}
+ if ((EffectiveTriple.isARM() || EffectiveTriple.isThumb()) &&
+ Value == "tls") {
+ if (!Args.hasArg(options::OPT_mstack_protector_guard_offset_EQ)) {
+ D.Diag(diag::err_drv_ssp_missing_offset_argument)
+ << A->getAsString(Args);
+ return;
+ }
+ // Check whether the target subarch supports the hardware TLS register
+ if (arm::getARMSubArchVersionNumber(EffectiveTriple) < 7 &&
+ llvm::ARM::parseArch(EffectiveTriple.getArchName()) !=
+ llvm::ARM::ArchKind::ARMV6T2) {
+ D.Diag(diag::err_target_unsupported_tp_hard)
+ << EffectiveTriple.getArchName();
+ return;
+ }
+ // Check whether the user asked for something other than -mtp=cp15
+ if (Arg *A = Args.getLastArg(options::OPT_mtp_mode_EQ)) {
+ StringRef Value = A->getValue();
+ if (Value != "cp15") {
+ D.Diag(diag::err_drv_argument_not_allowed_with)
+ << A->getAsString(Args) << "-mstack-protector-guard=tls";
+ return;
+ }
+ }
+ CmdArgs.push_back("-target-feature");
+ CmdArgs.push_back("+read-tp-hard");
+ }
if (EffectiveTriple.isAArch64() && Value != "sysreg" && Value != "global") {
D.Diag(diag::err_drv_invalid_value_with_suggestion)
<< A->getOption().getName() << Value << "sysreg global";
@@ -3180,7 +3210,8 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_offset_EQ)) {
StringRef Value = A->getValue();
- if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64())
+ if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() &&
+ !EffectiveTriple.isARM() && !EffectiveTriple.isThumb())
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getAsString(Args) << TripleStr;
int Offset;
@@ -3188,6 +3219,12 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
D.Diag(diag::err_drv_invalid_value) << A->getOption().getName() << Value;
return;
}
+ if ((EffectiveTriple.isARM() || EffectiveTriple.isThumb()) &&
+ (Offset < 0 || Offset > 0xfffff)) {
+ D.Diag(diag::err_drv_invalid_int_value)
+ << A->getOption().getName() << Value;
+ return;
+ }
A->render(Args, CmdArgs);
}
diff --git a/clang/test/Driver/stack-protector-guard.c b/clang/test/Driver/stack-protector-guard.c
index 3df8f2a3092d3..2c215b1995967 100644
--- a/clang/test/Driver/stack-protector-guard.c
+++ b/clang/test/Driver/stack-protector-guard.c
@@ -15,7 +15,7 @@
// RUN: FileCheck -check-prefix=CHECK-GS %s
// Invalid arch
-// RUN: not %clang -target arm-eabi-c -mstack-protector-guard=tls %s 2>&1 | \
+// RUN: not %clang -target powerpc64le-linux-gnu -mstack-protector-guard=tls %s 2>&1 | \
// RUN: FileCheck -check-prefix=INVALID-ARCH %s
// INVALID-ARCH: unsupported option '-mstack-protector-guard=tls' for target
@@ -23,7 +23,7 @@
// RUN: FileCheck -check-prefix=INVALID-ARCH2 %s
// INVALID-ARCH2: unsupported option '-mstack-protector-guard-reg=fs' for target
-// RUN: not %clang -target arm-linux-gnueabi -mstack-protector-guard-offset=10 %s 2>&1 | \
+// RUN: not %clang -target powerpc64le-linux-gnu -mstack-protector-guard-offset=10 %s 2>&1 | \
// RUN: FileCheck -check-prefix=INVALID-ARCH3 %s
// INVALID-ARCH3: unsupported option '-mstack-protector-guard-offset=10' for target
@@ -37,6 +37,26 @@
// CHECK-GS: "-cc1" {{.*}}"-mstack-protector-guard-reg=gs"
// INVALID-REG: error: invalid value {{.*}} in 'mstack-protector-guard-reg=', expected one of: fs gs
+// RUN: not %clang -target arm-eabi-c -mstack-protector-guard=tls %s 2>&1 | \
+// RUN: FileCheck -check-prefix=MISSING-OFFSET %s
+// MISSING-OFFSET: error: '-mstack-protector-guard=tls' is used without '-mstack-protector-guard-offset', and there is no default
+
+// RUN: not %clang -target arm-eabi-c -mstack-protector-guard-offset=1048576 %s 2>&1 | \
+// RUN: FileCheck -check-prefix=INVALID-OFFSET %s
+// INVALID-OFFSET: invalid integral value '1048576' in 'mstack-protector-guard-offset='
+
+// RUN: not %clang -target arm-eabi-c -mstack-protector-guard=sysreg %s 2>&1 | \
+// RUN: FileCheck -check-prefix=INVALID-VALUE2 %s
+// INVALID-VALUE2: error: invalid value 'sysreg' in 'mstack-protector-guard=', expected one of: tls global
+
+// RUN: not %clang -target thumbv6-eabi-c -mthumb -mstack-protector-guard=tls -mstack-protector-guard-offset=0 %s 2>&1 | \
+// RUN: FileCheck -check-prefix=INVALID-ARCH4 %s
+// INVALID-ARCH4: error: hardware TLS register is not supported for the thumbv6 sub-architecture
+
+// RUN: not %clang -target thumbv7-eabi-c -mtp=soft -mstack-protector-guard=tls -mstack-protector-guard-offset=0 %s 2>&1 | \
+// RUN: FileCheck -check-prefix=INVALID-TP %s
+// INVALID-TP: error: invalid argument '-mtp=soft' not allowed with '-mstack-protector-guard=tls'
+
// RUN: %clang -### -target x86_64-unknown-unknown -mstack-protector-guard-offset=30 %s 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-OFFSET %s
diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
index 6fed6483c7835..4a66b4fa060be 100644
--- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
@@ -4890,40 +4890,70 @@ void ARMBaseInstrInfo::expandLoadStackGuardBase(MachineBasicBlock::iterator MI,
MachineBasicBlock &MBB = *MI->getParent();
DebugLoc DL = MI->getDebugLoc();
Register Reg = MI->getOperand(0).getReg();
- const GlobalValue *GV =
- cast<GlobalValue>((*MI->memoperands_begin())->getValue());
- bool IsIndirect = Subtarget.isGVIndirectSymbol(GV);
MachineInstrBuilder MIB;
+ unsigned int Offset = 0;
- unsigned TargetFlags = ARMII::MO_NO_FLAG;
- if (Subtarget.isTargetMachO()) {
- TargetFlags |= ARMII::MO_NONLAZY;
- } else if (Subtarget.isTargetCOFF()) {
- if (GV->hasDLLImportStorageClass())
- TargetFlags |= ARMII::MO_DLLIMPORT;
- else if (IsIndirect)
- TargetFlags |= ARMII::MO_COFFSTUB;
- } else if (Subtarget.isGVInGOT(GV)) {
- TargetFlags |= ARMII::MO_GOT;
- }
+ if (LoadImmOpc == ARM::MRC || LoadImmOpc == ARM::t2MRC) {
+ assert(Subtarget.isReadTPHard() &&
+ "TLS stack protector requires hardware TLS register");
- BuildMI(MBB, MI, DL, get(LoadImmOpc), Reg)
- .addGlobalAddress(GV, 0, TargetFlags);
+ BuildMI(MBB, MI, DL, get(LoadImmOpc), Reg)
+ .addImm(15)
+ .addImm(0)
+ .addImm(13)
+ .addImm(0)
+ .addImm(3)
+ .add(predOps(ARMCC::AL));
- if (IsIndirect) {
- MIB = BuildMI(MBB, MI, DL, get(LoadOpc), Reg);
- MIB.addReg(Reg, RegState::Kill).addImm(0);
- auto Flags = MachineMemOperand::MOLoad |
- MachineMemOperand::MODereferenceable |
- MachineMemOperand::MOInvariant;
- MachineMemOperand *MMO = MBB.getParent()->getMachineMemOperand(
- MachinePointerInfo::getGOT(*MBB.getParent()), Flags, 4, Align(4));
- MIB.addMemOperand(MMO).add(predOps(ARMCC::AL));
+ Module &M = *MBB.getParent()->getFunction().getParent();
+ Offset = M.getStackProtectorGuardOffset();
+ if (Offset & ~0xfffU) {
+ // The offset won't fit in the LDR's 12-bit immediate field, so emit an
+ // extra ADD to cover the delta. This gives us a guaranteed 8 additional
+ // bits, resulting in a range of 0 to +1 MiB for the guard offset.
+ unsigned AddOpc = (LoadImmOpc == ARM::MRC) ? ARM::ADDri : ARM::t2ADDri;
+ BuildMI(MBB, MI, DL, get(AddOpc), Reg)
+ .addReg(Reg, RegState::Kill)
+ .addImm(Offset & ~0xfffU)
+ .add(predOps(ARMCC::AL))
+ .addReg(0);
+ Offset &= 0xfffU;
+ }
+ } else {
+ const GlobalValue *GV =
+ cast<GlobalValue>((*MI->memoperands_begin())->getValue());
+ bool IsIndirect = Subtarget.isGVIndirectSymbol(GV);
+
+ unsigned TargetFlags = ARMII::MO_NO_FLAG;
+ if (Subtarget.isTargetMachO()) {
+ TargetFlags |= ARMII::MO_NONLAZY;
+ } else if (Subtarget.isTargetCOFF()) {
+ if (GV->hasDLLImportStorageClass())
+ TargetFlags |= ARMII::MO_DLLIMPORT;
+ else if (IsIndirect)
+ TargetFlags |= ARMII::MO_COFFSTUB;
+ } else if (Subtarget.isGVInGOT(GV)) {
+ TargetFlags |= ARMII::MO_GOT;
+ }
+
+ BuildMI(MBB, MI, DL, get(LoadImmOpc), Reg)
+ .addGlobalAddress(GV, 0, TargetFlags);
+
+ if (IsIndirect) {
+ MIB = BuildMI(MBB, MI, DL, get(LoadOpc), Reg);
+ MIB.addReg(Reg, RegState::Kill).addImm(0);
+ auto Flags = MachineMemOperand::MOLoad |
+ MachineMemOperand::MODereferenceable |
+ MachineMemOperand::MOInvariant;
+ MachineMemOperand *MMO = MBB.getParent()->getMachineMemOperand(
+ MachinePointerInfo::getGOT(*MBB.getParent()), Flags, 4, Align(4));
+ MIB.addMemOperand(MMO).add(predOps(ARMCC::AL));
+ }
}
MIB = BuildMI(MBB, MI, DL, get(LoadOpc), Reg);
MIB.addReg(Reg, RegState::Kill)
- .addImm(0)
+ .addImm(Offset)
.cloneMemRefs(*MI)
.add(predOps(ARMCC::AL));
}
diff --git a/llvm/lib/Target/ARM/ARMInstrInfo.cpp b/llvm/lib/Target/ARM/ARMInstrInfo.cpp
index 106ba87107f85..5dee5e04af815 100644
--- a/llvm/lib/Target/ARM/ARMInstrInfo.cpp
+++ b/llvm/lib/Target/ARM/ARMInstrInfo.cpp
@@ -95,6 +95,12 @@ void ARMInstrInfo::expandLoadStackGuard(MachineBasicBlock::iterator MI) const {
MachineFunction &MF = *MI->getParent()->getParent();
const ARMSubtarget &Subtarget = MF.getSubtarget<ARMSubtarget>();
const TargetMachine &TM = MF.getTarget();
+ Module &M = *MF.getFunction().getParent();
+
+ if (M.getStackProtectorGuard() == "tls") {
+ expandLoadStackGuardBase(MI, ARM::MRC, ARM::LDRi12);
+ return;
+ }
const GlobalValue *GV =
cast<GlobalValue>((*MI->memoperands_begin())->getValue());
diff --git a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp
index cf5eb4b4c0f1e..b1d7b9a283e05 100644
--- a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp
+++ b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp
@@ -135,6 +135,11 @@ void Thumb1InstrInfo::expandLoadStackGuard(
MachineBasicBlock::iterator MI) const {
MachineFunction &MF = *MI->getParent()->getParent();
const TargetMachine &TM = MF.getTarget();
+ Module &M = *MF.getFunction().getParent();
+
+ assert(M.getStackProtectorGuard() != "tls" &&
+ "TLS stack protector not supported for Thumb1 targets");
+
if (TM.isPositionIndependent())
expandLoadStackGuardBase(MI, ARM::tLDRLIT_ga_pcrel, ARM::tLDRi);
else
diff --git a/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp b/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp
index e3b2f77b70495..bdb167a08e61d 100644
--- a/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp
+++ b/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp
@@ -250,6 +250,13 @@ loadRegFromStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
void Thumb2InstrInfo::expandLoadStackGuard(
MachineBasicBlock::iterator MI) const {
MachineFunction &MF = *MI->getParent()->getParent();
+ Module &M = *MF.getFunction().getParent();
+
+ if (M.getStackProtectorGuard() == "tls") {
+ expandLoadStackGuardBase(MI, ARM::t2MRC, ARM::t2LDRi12);
+ return;
+ }
+
const GlobalValue *GV =
cast<GlobalValue>((*MI->memoperands_begin())->getValue());
diff --git a/llvm/test/CodeGen/ARM/stack-guard-tls.ll b/llvm/test/CodeGen/ARM/stack-guard-tls.ll
new file mode 100644
index 0000000000000..a891aacbd2ae2
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/stack-guard-tls.ll
@@ -0,0 +1,38 @@
+; RUN: split-file %s %t
+; RUN: cat %t/main.ll %t/a.ll > %t/a2.ll
+; RUN: cat %t/main.ll %t/b.ll > %t/b2.ll
+; RUN: llc %t/a2.ll -mtriple=armv7-unknown-linux-gnueabihf -mattr=+read-tp-hard -o - | \
+; RUN: FileCheck --check-prefixes=CHECK,CHECK-SMALL %s
+; RUN: llc %t/a2.ll -mtriple=thumbv7-unknown-linux-gnueabihf -mattr=+read-tp-hard -o - | \
+; RUN: FileCheck --check-prefixes=CHECK,CHECK-SMALL %s
+; RUN: llc %t/b2.ll -mtriple=armv7-unknown-linux-gnueabihf -mattr=+read-tp-hard -o - | \
+; RUN: FileCheck --check-prefixes=CHECK,CHECK-LARGE %s
+; RUN: llc %t/b2.ll -mtriple=thumbv7-unknown-linux-gnueabihf -mattr=+read-tp-hard -o - | \
+; RUN: FileCheck --check-prefixes=CHECK,CHECK-LARGE %s
+
+;--- main.ll
+declare void @baz(i32*)
+
+define void @foo(i64 %t) sspstrong {
+ %vla = alloca i32, i64 %t, align 4
+ call void @baz(i32* nonnull %vla)
+ ret void
+}
+!llvm.module.flags = !{!1, !2}
+!1 = !{i32 2, !"stack-protector-guard", !"tls"}
+
+;--- a.ll
+!2 = !{i32 2, !"stack-protector-guard-offset", i32 1296}
+
+;--- b.ll
+!2 = !{i32 2, !"stack-protector-guard-offset", i32 4296}
+
+; CHECK: mrc p15, #0, [[REG1:r[0-9]+]], c13, c0, #3
+; CHECK-SMALL-NEXT: ldr{{(\.w)?}} [[REG1]], {{\[}}[[REG1]], #1296]
+; CHECK-LARGE-NEXT: add{{(\.w)?}} [[REG1]], [[REG1]], #4096
+; CHECK-LARGE-NEXT: ldr{{(\.w)?}} [[REG1]], {{\[}}[[REG1]], #200]
+; CHECK: bl baz
+; CHECK: mrc p15, #0, [[REG2:r[0-9]+]], c13, c0, #3
+; CHECK-SMALL-NEXT: ldr{{(\.w)?}} [[REG2]], {{\[}}[[REG2]], #1296]
+; CHECK-LARGE-NEXT: add{{(\.w)?}} [[REG2]], [[REG2]], #4096
+; CHECK-LARGE-NEXT: ldr{{(\.w)?}} [[REG2]], {{\[}}[[REG2]], #200]
More information about the llvm-commits
mailing list