Skip to content

Commit a19da87

Browse files
committed
[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
1 parent 48b67dc commit a19da87

File tree

9 files changed

+179
-31
lines changed

9 files changed

+179
-31
lines changed

clang/include/clang/Basic/DiagnosticCommonKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ def err_target_unsupported_unaligned : Error<
298298
"the %0 sub-architecture does not support unaligned accesses">;
299299
def err_target_unsupported_execute_only : Error<
300300
"execute only is not supported for the %0 sub-architecture">;
301+
def err_target_unsupported_tp_hard : Error<
302+
"hardware TLS register is not supported for the %0 sub-architecture">;
301303
def err_target_unsupported_mcmse : Error<
302304
"-mcmse is not supported for %0">;
303305
def err_opt_not_valid_with_opt : Error<

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,4 +592,7 @@ def err_cc1_round_trip_ok_then_fail : Error<
592592
"generated arguments parse failed in round-trip">;
593593
def err_cc1_round_trip_mismatch : Error<
594594
"generated arguments do not match in round-trip">;
595+
596+
def err_drv_ssp_missing_offset_argument : Error<
597+
"'%0' is used without '-mstack-protector-guard-offset', and there is no default">;
595598
}

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3162,14 +3162,44 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
31623162
const std::string &TripleStr = EffectiveTriple.getTriple();
31633163
if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_EQ)) {
31643164
StringRef Value = A->getValue();
3165-
if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64())
3165+
if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() &&
3166+
!EffectiveTriple.isARM() && !EffectiveTriple.isThumb())
31663167
D.Diag(diag::err_drv_unsupported_opt_for_target)
31673168
<< A->getAsString(Args) << TripleStr;
3168-
if (EffectiveTriple.isX86() && Value != "tls" && Value != "global") {
3169+
if ((EffectiveTriple.isX86() || EffectiveTriple.isARM() ||
3170+
EffectiveTriple.isThumb()) &&
3171+
Value != "tls" && Value != "global") {
31693172
D.Diag(diag::err_drv_invalid_value_with_suggestion)
31703173
<< A->getOption().getName() << Value << "tls global";
31713174
return;
31723175
}
3176+
if ((EffectiveTriple.isARM() || EffectiveTriple.isThumb()) &&
3177+
Value == "tls") {
3178+
if (!Args.hasArg(options::OPT_mstack_protector_guard_offset_EQ)) {
3179+
D.Diag(diag::err_drv_ssp_missing_offset_argument)
3180+
<< A->getAsString(Args);
3181+
return;
3182+
}
3183+
// Check whether the target subarch supports the hardware TLS register
3184+
if (arm::getARMSubArchVersionNumber(EffectiveTriple) < 7 &&
3185+
llvm::ARM::parseArch(EffectiveTriple.getArchName()) !=
3186+
llvm::ARM::ArchKind::ARMV6T2) {
3187+
D.Diag(diag::err_target_unsupported_tp_hard)
3188+
<< EffectiveTriple.getArchName();
3189+
return;
3190+
}
3191+
// Check whether the user asked for something other than -mtp=cp15
3192+
if (Arg *A = Args.getLastArg(options::OPT_mtp_mode_EQ)) {
3193+
StringRef Value = A->getValue();
3194+
if (Value != "cp15") {
3195+
D.Diag(diag::err_drv_argument_not_allowed_with)
3196+
<< A->getAsString(Args) << "-mstack-protector-guard=tls";
3197+
return;
3198+
}
3199+
}
3200+
CmdArgs.push_back("-target-feature");
3201+
CmdArgs.push_back("+read-tp-hard");
3202+
}
31733203
if (EffectiveTriple.isAArch64() && Value != "sysreg" && Value != "global") {
31743204
D.Diag(diag::err_drv_invalid_value_with_suggestion)
31753205
<< A->getOption().getName() << Value << "sysreg global";
@@ -3180,14 +3210,21 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
31803210

31813211
if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_offset_EQ)) {
31823212
StringRef Value = A->getValue();
3183-
if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64())
3213+
if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64() &&
3214+
!EffectiveTriple.isARM() && !EffectiveTriple.isThumb())
31843215
D.Diag(diag::err_drv_unsupported_opt_for_target)
31853216
<< A->getAsString(Args) << TripleStr;
31863217
int Offset;
31873218
if (Value.getAsInteger(10, Offset)) {
31883219
D.Diag(diag::err_drv_invalid_value) << A->getOption().getName() << Value;
31893220
return;
31903221
}
3222+
if ((EffectiveTriple.isARM() || EffectiveTriple.isThumb()) &&
3223+
(Offset < 0 || Offset > 0xfffff)) {
3224+
D.Diag(diag::err_drv_invalid_int_value)
3225+
<< A->getOption().getName() << Value;
3226+
return;
3227+
}
31913228
A->render(Args, CmdArgs);
31923229
}
31933230

clang/test/Driver/stack-protector-guard.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
// RUN: FileCheck -check-prefix=CHECK-GS %s
1616

1717
// Invalid arch
18-
// RUN: not %clang -target arm-eabi-c -mstack-protector-guard=tls %s 2>&1 | \
18+
// RUN: not %clang -target powerpc64le-linux-gnu -mstack-protector-guard=tls %s 2>&1 | \
1919
// RUN: FileCheck -check-prefix=INVALID-ARCH %s
2020
// INVALID-ARCH: unsupported option '-mstack-protector-guard=tls' for target
2121

2222
// RUN: not %clang -target powerpc64le-linux-gnu -mstack-protector-guard-reg=fs %s 2>&1 | \
2323
// RUN: FileCheck -check-prefix=INVALID-ARCH2 %s
2424
// INVALID-ARCH2: unsupported option '-mstack-protector-guard-reg=fs' for target
2525

26-
// RUN: not %clang -target arm-linux-gnueabi -mstack-protector-guard-offset=10 %s 2>&1 | \
26+
// RUN: not %clang -target powerpc64le-linux-gnu -mstack-protector-guard-offset=10 %s 2>&1 | \
2727
// RUN: FileCheck -check-prefix=INVALID-ARCH3 %s
2828
// INVALID-ARCH3: unsupported option '-mstack-protector-guard-offset=10' for target
2929

@@ -37,6 +37,26 @@
3737
// CHECK-GS: "-cc1" {{.*}}"-mstack-protector-guard-reg=gs"
3838
// INVALID-REG: error: invalid value {{.*}} in 'mstack-protector-guard-reg=', expected one of: fs gs
3939

40+
// RUN: not %clang -target arm-eabi-c -mstack-protector-guard=tls %s 2>&1 | \
41+
// RUN: FileCheck -check-prefix=MISSING-OFFSET %s
42+
// MISSING-OFFSET: error: '-mstack-protector-guard=tls' is used without '-mstack-protector-guard-offset', and there is no default
43+
44+
// RUN: not %clang -target arm-eabi-c -mstack-protector-guard-offset=1048576 %s 2>&1 | \
45+
// RUN: FileCheck -check-prefix=INVALID-OFFSET %s
46+
// INVALID-OFFSET: invalid integral value '1048576' in 'mstack-protector-guard-offset='
47+
48+
// RUN: not %clang -target arm-eabi-c -mstack-protector-guard=sysreg %s 2>&1 | \
49+
// RUN: FileCheck -check-prefix=INVALID-VALUE2 %s
50+
// INVALID-VALUE2: error: invalid value 'sysreg' in 'mstack-protector-guard=', expected one of: tls global
51+
52+
// RUN: not %clang -target thumbv6-eabi-c -mthumb -mstack-protector-guard=tls -mstack-protector-guard-offset=0 %s 2>&1 | \
53+
// RUN: FileCheck -check-prefix=INVALID-ARCH4 %s
54+
// INVALID-ARCH4: error: hardware TLS register is not supported for the thumbv6 sub-architecture
55+
56+
// RUN: not %clang -target thumbv7-eabi-c -mtp=soft -mstack-protector-guard=tls -mstack-protector-guard-offset=0 %s 2>&1 | \
57+
// RUN: FileCheck -check-prefix=INVALID-TP %s
58+
// INVALID-TP: error: invalid argument '-mtp=soft' not allowed with '-mstack-protector-guard=tls'
59+
4060
// RUN: %clang -### -target x86_64-unknown-unknown -mstack-protector-guard-offset=30 %s 2>&1 | \
4161
// RUN: FileCheck -check-prefix=CHECK-OFFSET %s
4262

llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4890,40 +4890,70 @@ void ARMBaseInstrInfo::expandLoadStackGuardBase(MachineBasicBlock::iterator MI,
48904890
MachineBasicBlock &MBB = *MI->getParent();
48914891
DebugLoc DL = MI->getDebugLoc();
48924892
Register Reg = MI->getOperand(0).getReg();
4893-
const GlobalValue *GV =
4894-
cast<GlobalValue>((*MI->memoperands_begin())->getValue());
4895-
bool IsIndirect = Subtarget.isGVIndirectSymbol(GV);
48964893
MachineInstrBuilder MIB;
4894+
unsigned int Offset = 0;
48974895

4898-
unsigned TargetFlags = ARMII::MO_NO_FLAG;
4899-
if (Subtarget.isTargetMachO()) {
4900-
TargetFlags |= ARMII::MO_NONLAZY;
4901-
} else if (Subtarget.isTargetCOFF()) {
4902-
if (GV->hasDLLImportStorageClass())
4903-
TargetFlags |= ARMII::MO_DLLIMPORT;
4904-
else if (IsIndirect)
4905-
TargetFlags |= ARMII::MO_COFFSTUB;
4906-
} else if (Subtarget.isGVInGOT(GV)) {
4907-
TargetFlags |= ARMII::MO_GOT;
4908-
}
4896+
if (LoadImmOpc == ARM::MRC || LoadImmOpc == ARM::t2MRC) {
4897+
assert(Subtarget.isReadTPHard() &&
4898+
"TLS stack protector requires hardware TLS register");
49094899

4910-
BuildMI(MBB, MI, DL, get(LoadImmOpc), Reg)
4911-
.addGlobalAddress(GV, 0, TargetFlags);
4900+
BuildMI(MBB, MI, DL, get(LoadImmOpc), Reg)
4901+
.addImm(15)
4902+
.addImm(0)
4903+
.addImm(13)
4904+
.addImm(0)
4905+
.addImm(3)
4906+
.add(predOps(ARMCC::AL));
49124907

4913-
if (IsIndirect) {
4914-
MIB = BuildMI(MBB, MI, DL, get(LoadOpc), Reg);
4915-
MIB.addReg(Reg, RegState::Kill).addImm(0);
4916-
auto Flags = MachineMemOperand::MOLoad |
4917-
MachineMemOperand::MODereferenceable |
4918-
MachineMemOperand::MOInvariant;
4919-
MachineMemOperand *MMO = MBB.getParent()->getMachineMemOperand(
4920-
MachinePointerInfo::getGOT(*MBB.getParent()), Flags, 4, Align(4));
4921-
MIB.addMemOperand(MMO).add(predOps(ARMCC::AL));
4908+
Module &M = *MBB.getParent()->getFunction().getParent();
4909+
Offset = M.getStackProtectorGuardOffset();
4910+
if (Offset & ~0xfffU) {
4911+
// The offset won't fit in the LDR's 12-bit immediate field, so emit an
4912+
// extra ADD to cover the delta. This gives us a guaranteed 8 additional
4913+
// bits, resulting in a range of 0 to +1 MiB for the guard offset.
4914+
unsigned AddOpc = (LoadImmOpc == ARM::MRC) ? ARM::ADDri : ARM::t2ADDri;
4915+
BuildMI(MBB, MI, DL, get(AddOpc), Reg)
4916+
.addReg(Reg, RegState::Kill)
4917+
.addImm(Offset & ~0xfffU)
4918+
.add(predOps(ARMCC::AL))
4919+
.addReg(0);
4920+
Offset &= 0xfffU;
4921+
}
4922+
} else {
4923+
const GlobalValue *GV =
4924+
cast<GlobalValue>((*MI->memoperands_begin())->getValue());
4925+
bool IsIndirect = Subtarget.isGVIndirectSymbol(GV);
4926+
4927+
unsigned TargetFlags = ARMII::MO_NO_FLAG;
4928+
if (Subtarget.isTargetMachO()) {
4929+
TargetFlags |= ARMII::MO_NONLAZY;
4930+
} else if (Subtarget.isTargetCOFF()) {
4931+
if (GV->hasDLLImportStorageClass())
4932+
TargetFlags |= ARMII::MO_DLLIMPORT;
4933+
else if (IsIndirect)
4934+
TargetFlags |= ARMII::MO_COFFSTUB;
4935+
} else if (Subtarget.isGVInGOT(GV)) {
4936+
TargetFlags |= ARMII::MO_GOT;
4937+
}
4938+
4939+
BuildMI(MBB, MI, DL, get(LoadImmOpc), Reg)
4940+
.addGlobalAddress(GV, 0, TargetFlags);
4941+
4942+
if (IsIndirect) {
4943+
MIB = BuildMI(MBB, MI, DL, get(LoadOpc), Reg);
4944+
MIB.addReg(Reg, RegState::Kill).addImm(0);
4945+
auto Flags = MachineMemOperand::MOLoad |
4946+
MachineMemOperand::MODereferenceable |
4947+
MachineMemOperand::MOInvariant;
4948+
MachineMemOperand *MMO = MBB.getParent()->getMachineMemOperand(
4949+
MachinePointerInfo::getGOT(*MBB.getParent()), Flags, 4, Align(4));
4950+
MIB.addMemOperand(MMO).add(predOps(ARMCC::AL));
4951+
}
49224952
}
49234953

49244954
MIB = BuildMI(MBB, MI, DL, get(LoadOpc), Reg);
49254955
MIB.addReg(Reg, RegState::Kill)
4926-
.addImm(0)
4956+
.addImm(Offset)
49274957
.cloneMemRefs(*MI)
49284958
.add(predOps(ARMCC::AL));
49294959
}

llvm/lib/Target/ARM/ARMInstrInfo.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ void ARMInstrInfo::expandLoadStackGuard(MachineBasicBlock::iterator MI) const {
9595
MachineFunction &MF = *MI->getParent()->getParent();
9696
const ARMSubtarget &Subtarget = MF.getSubtarget<ARMSubtarget>();
9797
const TargetMachine &TM = MF.getTarget();
98+
Module &M = *MF.getFunction().getParent();
99+
100+
if (M.getStackProtectorGuard() == "tls") {
101+
expandLoadStackGuardBase(MI, ARM::MRC, ARM::LDRi12);
102+
return;
103+
}
98104

99105
const GlobalValue *GV =
100106
cast<GlobalValue>((*MI->memoperands_begin())->getValue());

llvm/lib/Target/ARM/Thumb1InstrInfo.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ void Thumb1InstrInfo::expandLoadStackGuard(
135135
MachineBasicBlock::iterator MI) const {
136136
MachineFunction &MF = *MI->getParent()->getParent();
137137
const TargetMachine &TM = MF.getTarget();
138+
Module &M = *MF.getFunction().getParent();
139+
140+
assert(M.getStackProtectorGuard() != "tls" &&
141+
"TLS stack protector not supported for Thumb1 targets");
142+
138143
if (TM.isPositionIndependent())
139144
expandLoadStackGuardBase(MI, ARM::tLDRLIT_ga_pcrel, ARM::tLDRi);
140145
else

llvm/lib/Target/ARM/Thumb2InstrInfo.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,13 @@ loadRegFromStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
250250
void Thumb2InstrInfo::expandLoadStackGuard(
251251
MachineBasicBlock::iterator MI) const {
252252
MachineFunction &MF = *MI->getParent()->getParent();
253+
Module &M = *MF.getFunction().getParent();
254+
255+
if (M.getStackProtectorGuard() == "tls") {
256+
expandLoadStackGuardBase(MI, ARM::t2MRC, ARM::t2LDRi12);
257+
return;
258+
}
259+
253260
const GlobalValue *GV =
254261
cast<GlobalValue>((*MI->memoperands_begin())->getValue());
255262

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
; RUN: split-file %s %t
2+
; RUN: cat %t/main.ll %t/a.ll > %t/a2.ll
3+
; RUN: cat %t/main.ll %t/b.ll > %t/b2.ll
4+
; RUN: llc %t/a2.ll -mtriple=armv7-unknown-linux-gnueabihf -mattr=+read-tp-hard -o - | \
5+
; RUN: FileCheck --check-prefixes=CHECK,CHECK-SMALL %s
6+
; RUN: llc %t/a2.ll -mtriple=thumbv7-unknown-linux-gnueabihf -mattr=+read-tp-hard -o - | \
7+
; RUN: FileCheck --check-prefixes=CHECK,CHECK-SMALL %s
8+
; RUN: llc %t/b2.ll -mtriple=armv7-unknown-linux-gnueabihf -mattr=+read-tp-hard -o - | \
9+
; RUN: FileCheck --check-prefixes=CHECK,CHECK-LARGE %s
10+
; RUN: llc %t/b2.ll -mtriple=thumbv7-unknown-linux-gnueabihf -mattr=+read-tp-hard -o - | \
11+
; RUN: FileCheck --check-prefixes=CHECK,CHECK-LARGE %s
12+
13+
;--- main.ll
14+
declare void @baz(i32*)
15+
16+
define void @foo(i64 %t) sspstrong {
17+
%vla = alloca i32, i64 %t, align 4
18+
call void @baz(i32* nonnull %vla)
19+
ret void
20+
}
21+
!llvm.module.flags = !{!1, !2}
22+
!1 = !{i32 2, !"stack-protector-guard", !"tls"}
23+
24+
;--- a.ll
25+
!2 = !{i32 2, !"stack-protector-guard-offset", i32 1296}
26+
27+
;--- b.ll
28+
!2 = !{i32 2, !"stack-protector-guard-offset", i32 4296}
29+
30+
; CHECK: mrc p15, #0, [[REG1:r[0-9]+]], c13, c0, #3
31+
; CHECK-SMALL-NEXT: ldr{{(\.w)?}} [[REG1]], {{\[}}[[REG1]], #1296]
32+
; CHECK-LARGE-NEXT: add{{(\.w)?}} [[REG1]], [[REG1]], #4096
33+
; CHECK-LARGE-NEXT: ldr{{(\.w)?}} [[REG1]], {{\[}}[[REG1]], #200]
34+
; CHECK: bl baz
35+
; CHECK: mrc p15, #0, [[REG2:r[0-9]+]], c13, c0, #3
36+
; CHECK-SMALL-NEXT: ldr{{(\.w)?}} [[REG2]], {{\[}}[[REG2]], #1296]
37+
; CHECK-LARGE-NEXT: add{{(\.w)?}} [[REG2]], [[REG2]], #4096
38+
; CHECK-LARGE-NEXT: ldr{{(\.w)?}} [[REG2]], {{\[}}[[REG2]], #200]

0 commit comments

Comments
 (0)