diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -298,6 +298,8 @@ "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 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -592,4 +592,7 @@ "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 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3162,14 +3162,44 @@ 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 @@ 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 @@ 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 --- 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 --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -4890,40 +4890,70 @@ MachineBasicBlock &MBB = *MI->getParent(); DebugLoc DL = MI->getDebugLoc(); Register Reg = MI->getOperand(0).getReg(); - const GlobalValue *GV = - cast((*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((*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 --- a/llvm/lib/Target/ARM/ARMInstrInfo.cpp +++ b/llvm/lib/Target/ARM/ARMInstrInfo.cpp @@ -95,6 +95,12 @@ MachineFunction &MF = *MI->getParent()->getParent(); const ARMSubtarget &Subtarget = MF.getSubtarget(); 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((*MI->memoperands_begin())->getValue()); diff --git a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp --- a/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp +++ b/llvm/lib/Target/ARM/Thumb1InstrInfo.cpp @@ -135,6 +135,11 @@ 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 --- a/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp +++ b/llvm/lib/Target/ARM/Thumb2InstrInfo.cpp @@ -250,6 +250,13 @@ 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((*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 --- /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]