diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -380,8 +380,10 @@ /// other styles we may implement in the future. std::string StackProtectorGuard; - /// The TLS base register when StackProtectorGuard is "tls". + /// The TLS base register when StackProtectorGuard is "tls", or register used + /// to store the stack canary for "sysreg". /// On x86 this can be "fs" or "gs". + /// On AArch64 this can only be "sp_el0". std::string StackProtectorGuardReg; /// Path to ignorelist file specifying which objects diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -555,10 +555,11 @@ Options.UniqueBasicBlockSectionNames = CodeGenOpts.UniqueBasicBlockSectionNames; Options.StackProtectorGuard = - llvm::StringSwitch(CodeGenOpts - .StackProtectorGuard) + llvm::StringSwitch( + CodeGenOpts.StackProtectorGuard) .Case("tls", llvm::StackProtectorGuards::TLS) .Case("global", llvm::StackProtectorGuards::Global) + .Case("sysreg", llvm::StackProtectorGuards::SysReg) .Default(llvm::StackProtectorGuards::None); Options.StackProtectorGuardOffset = CodeGenOpts.StackProtectorGuardOffset; Options.StackProtectorGuardReg = CodeGenOpts.StackProtectorGuardReg; 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 @@ -3102,25 +3102,28 @@ } } - // First support "tls" and "global" for X86 target. - // TODO: Support "sysreg" for AArch64. 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()) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; - if (Value != "tls" && Value != "global") { + if (EffectiveTriple.isX86() && Value != "tls" && Value != "global") { D.Diag(diag::err_drv_invalid_value_with_suggestion) << A->getOption().getName() << Value << "tls global"; return; } + if (EffectiveTriple.isAArch64() && Value != "sysreg" && Value != "global") { + D.Diag(diag::err_drv_invalid_value_with_suggestion) + << A->getOption().getName() << Value << "sysreg global"; + return; + } A->render(Args, CmdArgs); } if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_offset_EQ)) { StringRef Value = A->getValue(); - if (!EffectiveTriple.isX86()) + if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64()) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; int Offset; @@ -3133,7 +3136,7 @@ if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_reg_EQ)) { StringRef Value = A->getValue(); - if (!EffectiveTriple.isX86()) + if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64()) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; if (EffectiveTriple.isX86() && (Value != "fs" && Value != "gs")) { @@ -3141,6 +3144,10 @@ << A->getOption().getName() << Value << "fs gs"; return; } + if (EffectiveTriple.isAArch64() && Value != "sp_el0") { + D.Diag(diag::err_drv_invalid_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 @@ -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 aarch64-linux-gnu -mstack-protector-guard-offset=10 %s 2>&1 | \ +// RUN: not %clang -target arm-linux-gnueabi -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 @@ -41,3 +41,19 @@ // RUN: FileCheck -check-prefix=CHECK-OFFSET %s // CHECK-OFFSET: "-cc1" {{.*}}"-mstack-protector-guard-offset=30" + +// RUN: %clang -### -target aarch64-linux-gnu -mstack-protector-guard=sysreg \ +// RUN: -mstack-protector-guard-reg=sp_el0 \ +// RUN: -mstack-protector-guard-offset=0 %s 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-AARCH64 %s +// RUN: %clang -### -target aarch64-linux-gnu \ +// RUN: -mstack-protector-guard=tls %s 2>&1 | \ +// RUN: FileCheck -check-prefix=INVALID-VALUE-AARCH64 %s +// RUN: %clang -### -target aarch64-linux-gnu -mstack-protector-guard=sysreg \ +// RUN: -mstack-protector-guard-reg=foo \ +// RUN: -mstack-protector-guard-offset=0 %s 2>&1 | \ +// RUN: FileCheck -check-prefix=INVALID-REG-AARCH64 %s + +// CHECK-AARCH64: "-cc1" {{.*}}"-mstack-protector-guard=sysreg" "-mstack-protector-guard-offset=0" "-mstack-protector-guard-reg=sp_el0" +// INVALID-VALUE-AARCH64: error: invalid value 'tls' in 'mstack-protector-guard=', expected one of: sysreg global +// INVALID-REG-AARCH64: error: invalid value 'foo' in 'mstack-protector-guard-reg=' diff --git a/llvm/include/llvm/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h --- a/llvm/include/llvm/Target/TargetOptions.h +++ b/llvm/include/llvm/Target/TargetOptions.h @@ -73,11 +73,7 @@ None // Do not use Basic Block Sections. }; - enum class StackProtectorGuards { - None, - TLS, - Global - }; + enum class StackProtectorGuards { None, TLS, Global, SysReg }; enum class EABI { Unknown, @@ -330,7 +326,7 @@ /// Stack protector guard offset to use. int StackProtectorGuardOffset = INT_MAX; - /// Stack protector guard mode to use, e.g. tls, global. + /// Stack protector guard mode to use, e.g. tls, global, sysreg. StackProtectorGuards StackProtectorGuard = StackProtectorGuards::None; diff --git a/llvm/lib/CodeGen/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp --- a/llvm/lib/CodeGen/CommandFlags.cpp +++ b/llvm/lib/CodeGen/CommandFlags.cpp @@ -503,6 +503,8 @@ return StackProtectorGuards::TLS; if (getStackProtectorGuard() == "global") return StackProtectorGuards::Global; + if (getStackProtectorGuard() == "sysreg") + return StackProtectorGuards::SysReg; if (getStackProtectorGuard() != "none") { ErrorOr> MBOrErr = MemoryBuffer::getFile(getStackProtectorGuard()); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -1901,6 +1901,30 @@ } Register Reg = MI.getOperand(0).getReg(); + if (MI.getOpcode() == AArch64::LOAD_STACK_GUARD) { + TargetOptions Options = MI.getParent()->getParent()->getTarget().Options; + if (Options.StackProtectorGuard == StackProtectorGuards::SysReg) { + const AArch64SysReg::SysReg *SrcReg = + AArch64SysReg::lookupSysRegByName(Options.StackProtectorGuardReg); + assert(SrcReg && "Unable to encode SysReg"); + BuildMI(MBB, MI, DL, get(AArch64::MRS)) + .addDef(Reg, RegState::Renamable) + .addImm(SrcReg->Encoding); + if (Options.StackProtectorGuardOffset % 8 == 0) + BuildMI(MBB, MI, DL, get(AArch64::LDRXui)) + .addDef(Reg) + .addUse(Reg, RegState::Kill) + .addImm(Options.StackProtectorGuardOffset >> 3); + else + BuildMI(MBB, MI, DL, get(AArch64::LDURXi)) + .addDef(Reg) + .addUse(Reg, RegState::Kill) + .addImm(Options.StackProtectorGuardOffset); + MBB.erase(MI); + return true; + } + } + const GlobalValue *GV = cast((*MI.memoperands_begin())->getValue()); const TargetMachine &TM = MBB.getParent()->getTarget(); diff --git a/llvm/test/CodeGen/AArch64/stack-guard-sysreg.ll b/llvm/test/CodeGen/AArch64/stack-guard-sysreg.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/stack-guard-sysreg.ll @@ -0,0 +1,73 @@ +; RUN: llc %s --stack-protector-guard=sysreg \ +; RUN: --stack-protector-guard-reg=sp_el0 \ +; RUN: --stack-protector-guard-offset=0 -verify-machineinstrs -o - | \ +; RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NO-OFFSET %s +; RUN: llc %s --stack-protector-guard=sysreg \ +; RUN: --stack-protector-guard-reg=sp_el0 \ +; RUN: --stack-protector-guard-offset=8 -verify-machineinstrs -o - | \ +; RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-POSITIVE-OFFSET %s +; RUN: llc %s --stack-protector-guard=sysreg \ +; RUN: --stack-protector-guard-reg=sp_el0 \ +; RUN: --stack-protector-guard-offset=-8 -verify-machineinstrs -o - | \ +; RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NEGATIVE-OFFSET %s +; RUN: llc %s --stack-protector-guard=sysreg \ +; RUN: --stack-protector-guard-reg=sp_el0 \ +; RUN: --stack-protector-guard-offset=1 -verify-machineinstrs -o - | \ +; RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NPOT-OFFSET %s +; RUN: llc %s --stack-protector-guard=sysreg \ +; RUN: --stack-protector-guard-reg=sp_el0 \ +; RUN: --stack-protector-guard-offset=-1 -verify-machineinstrs -o - | \ +; RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NPOT-NEG-OFFSET %s + +target triple = "aarch64-unknown-linux-gnu" + +; Verify that we `mrs` from `SP_EL0` twice, rather than load from +; __stack_chk_guard. +define dso_local void @foo(i64 %t) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: stp x29, x30, [sp, #-16]! // 16-byte Folded Spill +; CHECK-NEXT: mov x29, sp +; CHECK-NEXT: sub sp, sp, #16 // =16 +; CHECK-NEXT: .cfi_def_cfa w29, 16 +; CHECK-NEXT: .cfi_offset w30, -8 +; CHECK-NEXT: .cfi_offset w29, -16 +; CHECK-NEXT: mrs x8, SP_EL0 +; CHECK-NO-OFFSET: ldr x8, [x8] +; CHECK-POSITIVE-OFFSET: ldr x8, [x8, #8] +; CHECK-NEGATIVE-OFFSET: ldr x8, [x8, #-8] +; CHECK-NPOT-OFFSET: ldur x8, [x8, #1] +; CHECK-NPOT-NEG-OFFSET: ldur x8, [x8, #-1] +; CHECK-NEXT: lsl x9, x0, #2 +; CHECK-NEXT: add x9, x9, #15 // =15 +; CHECK-NEXT: and x9, x9, #0xfffffffffffffff0 +; CHECK-NEXT: stur x8, [x29, #-8] +; CHECK-NEXT: mov x8, sp +; CHECK-NEXT: sub x0, x8, x9 +; CHECK-NEXT: mov sp, x0 +; CHECK-NEXT: bl baz +; CHECK-NEXT: ldur x8, [x29, #-8] +; CHECK-NEXT: mrs x9, SP_EL0 +; CHECK-NO-OFFSET: ldr x9, [x9] +; CHECK-POSITIVE-OFFSET: ldr x9, [x9, #8] +; CHECK-NEGATIVE-OFFSET: ldr x9, [x9, #-8] +; CHECK-NPOT-OFFSET: ldur x9, [x9, #1] +; CHECK-NPOT-NEG-OFFSET: ldur x9, [x9, #-1] +; CHECK-NEXT: cmp x9, x8 +; CHECK-NEXT: b.ne .LBB0_2 +; CHECK-NEXT: // %bb.1: // %entry +; CHECK-NEXT: mov sp, x29 +; CHECK-NEXT: ldp x29, x30, [sp], #16 // 16-byte Folded Reload +; CHECK-NEXT: ret +; CHECK-NEXT: .LBB0_2: // %entry +; CHECK-NEXT: bl __stack_chk_fail +; CHECK-NOT: __stack_chk_guard +entry: + %vla = alloca i32, i64 %t, align 4 + call void @baz(i32* nonnull %vla) + ret void +} + +declare dso_local void @baz(i32*) local_unnamed_addr + +attributes #0 = { sspstrong }