[llvm] 0f41778 - [AArch64] Support customizing stack protector guard
Nick Desaulniers via llvm-commits
llvm-commits at lists.llvm.org
Mon May 17 11:49:30 PDT 2021
Author: Nick Desaulniers
Date: 2021-05-17T11:49:22-07:00
New Revision: 0f417789192e74f9d2fad0f6aee4efc394257176
URL: https://github.com/llvm/llvm-project/commit/0f417789192e74f9d2fad0f6aee4efc394257176
DIFF: https://github.com/llvm/llvm-project/commit/0f417789192e74f9d2fad0f6aee4efc394257176.diff
LOG: [AArch64] Support customizing stack protector guard
Follow up to D88631 but for aarch64; the Linux kernel uses the command
line flags:
1. -mstack-protector-guard=sysreg
2. -mstack-protector-guard-reg=sp_el0
3. -mstack-protector-guard-offset=0
to use the system register sp_el0 for the stack canary, enabling the
kernel to have a unique stack canary per task (like a thread, but not
limited to userspace as the kernel can preempt itself).
Address pr/47341 for aarch64.
Fixes: https://github.com/ClangBuiltLinux/linux/issues/289
Signed-off-by: Nick Desaulniers <ndesaulniers at google.com>
Reviewed By: xiangzhangllvm, DavidSpickett, dmgreen
Differential Revision: https://reviews.llvm.org/D100919
Added:
llvm/test/CodeGen/AArch64/stack-guard-sysreg.ll
Modified:
clang/include/clang/Basic/CodeGenOptions.h
clang/include/clang/Driver/Options.td
clang/lib/CodeGen/BackendUtil.cpp
clang/lib/Driver/ToolChains/Clang.cpp
clang/test/Driver/stack-protector-guard.c
llvm/include/llvm/Target/TargetOptions.h
llvm/lib/CodeGen/CommandFlags.cpp
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 901013033ba6a..c77c34a8a57d6 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -364,8 +364,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// 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/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 303b5b95e76d7..31239d29ddf1c 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3410,7 +3410,7 @@ def mstack_protector_guard_EQ : Joined<["-"], "mstack-protector-guard=">, Group<
MarshallingInfoString<CodeGenOpts<"StackProtectorGuard">>;
def mstack_protector_guard_offset_EQ : Joined<["-"], "mstack-protector-guard-offset=">, Group<m_Group>, Flags<[CC1Option]>,
HelpText<"Use the given offset for addressing the stack-protector guard">,
- MarshallingInfoInt<CodeGenOpts<"StackProtectorGuardOffset">, "INT_MAX">;
+ MarshallingInfoInt<CodeGenOpts<"StackProtectorGuardOffset">, "INT_MAX", "int">;
def mstack_protector_guard_reg_EQ : Joined<["-"], "mstack-protector-guard-reg=">, Group<m_Group>, Flags<[CC1Option]>,
HelpText<"Use the given reg for addressing the stack-protector guard">,
MarshallingInfoString<CodeGenOpts<"StackProtectorGuardReg">, [{"none"}]>;
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 837ffd0950753..989210ca53907 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -559,10 +559,11 @@ static bool initTargetOptions(DiagnosticsEngine &Diags,
Options.UniqueBasicBlockSectionNames =
CodeGenOpts.UniqueBasicBlockSectionNames;
Options.StackProtectorGuard =
- llvm::StringSwitch<llvm::StackProtectorGuards>(CodeGenOpts
- .StackProtectorGuard)
+ llvm::StringSwitch<llvm::StackProtectorGuards>(
+ 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
index fe0f57d4c4791..7d4dfdc46492d 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3102,25 +3102,28 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
}
}
- // 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 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
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 @@ static void RenderSSPOptions(const Driver &D, const ToolChain &TC,
<< 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
index cccf0d3088206..3df8f2a3092d3 100644
--- 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
index e992534c26e43..61781b5a208d4 100644
--- a/llvm/include/llvm/Target/TargetOptions.h
+++ b/llvm/include/llvm/Target/TargetOptions.h
@@ -73,11 +73,7 @@ namespace llvm {
None // Do not use Basic Block Sections.
};
- enum class StackProtectorGuards {
- None,
- TLS,
- Global
- };
+ enum class StackProtectorGuards { None, TLS, Global, SysReg };
enum class EABI {
Unknown,
@@ -335,7 +331,7 @@ namespace llvm {
/// 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
index b8f1ca154f6c6..593a3303e10c3 100644
--- a/llvm/lib/CodeGen/CommandFlags.cpp
+++ b/llvm/lib/CodeGen/CommandFlags.cpp
@@ -508,6 +508,8 @@ codegen::getStackProtectorGuardMode(llvm::TargetOptions &Options) {
return StackProtectorGuards::TLS;
if (getStackProtectorGuard() == "global")
return StackProtectorGuards::Global;
+ if (getStackProtectorGuard() == "sysreg")
+ return StackProtectorGuards::SysReg;
if (getStackProtectorGuard() != "none") {
ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
MemoryBuffer::getFile(getStackProtectorGuard());
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
index 52b53bfa50115..6fc4775200a8a 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -1904,6 +1904,68 @@ bool AArch64InstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
}
Register Reg = MI.getOperand(0).getReg();
+ TargetOptions Options = MI.getParent()->getParent()->getTarget().Options;
+ if (Options.StackProtectorGuard == StackProtectorGuards::SysReg) {
+ const AArch64SysReg::SysReg *SrcReg =
+ AArch64SysReg::lookupSysRegByName(Options.StackProtectorGuardReg);
+ if (!SrcReg)
+ report_fatal_error("Unknown SysReg for Stack Protector Guard Register");
+
+ // mrs xN, sysreg
+ BuildMI(MBB, MI, DL, get(AArch64::MRS))
+ .addDef(Reg, RegState::Renamable)
+ .addImm(SrcReg->Encoding);
+ int Offset = Options.StackProtectorGuardOffset;
+ if (Offset >= 0 && Offset <= 32760 && Offset % 8 == 0) {
+ // ldr xN, [xN, #offset]
+ BuildMI(MBB, MI, DL, get(AArch64::LDRXui))
+ .addDef(Reg)
+ .addUse(Reg, RegState::Kill)
+ .addImm(Offset / 8);
+ } else if (Offset >= -256 && Offset <= 255) {
+ // ldur xN, [xN, #offset]
+ BuildMI(MBB, MI, DL, get(AArch64::LDURXi))
+ .addDef(Reg)
+ .addUse(Reg, RegState::Kill)
+ .addImm(Offset);
+ } else if (Offset >= -4095 && Offset <= 4095) {
+ if (Offset > 0) {
+ // add xN, xN, #offset
+ BuildMI(MBB, MI, DL, get(AArch64::ADDXri))
+ .addDef(Reg)
+ .addUse(Reg, RegState::Kill)
+ .addImm(Offset)
+ .addImm(0);
+ } else {
+ // sub xN, xN, #offset
+ BuildMI(MBB, MI, DL, get(AArch64::SUBXri))
+ .addDef(Reg)
+ .addUse(Reg, RegState::Kill)
+ .addImm(-Offset)
+ .addImm(0);
+ }
+ // ldr xN, [xN]
+ BuildMI(MBB, MI, DL, get(AArch64::LDRXui))
+ .addDef(Reg)
+ .addUse(Reg, RegState::Kill)
+ .addImm(0);
+ } else {
+ // Cases that are larger than +/- 4095 and not a multiple of 8, or larger
+ // than 23760.
+ // It might be nice to use AArch64::MOVi32imm here, which would get
+ // expanded in PreSched2 after PostRA, but our lone scratch Reg already
+ // contains the MRS result. findScratchNonCalleeSaveRegister() in
+ // AArch64FrameLowering might help us find such a scratch register
+ // though. If we failed to find a scratch register, we could emit a
+ // stream of add instructions to build up the immediate. Or, we could try
+ // to insert a AArch64::MOVi32imm before register allocation so that we
+ // didn't need to scavenge for a scratch register.
+ report_fatal_error("Unable to encode Stack Protector Guard Offset");
+ }
+ MBB.erase(MI);
+ return true;
+ }
+
const GlobalValue *GV =
cast<GlobalValue>((*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
index 0000000000000..4e5d1425a70d2
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/stack-guard-sysreg.ll
@@ -0,0 +1,105 @@
+; 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
+; RUN: llc %s --stack-protector-guard=sysreg \
+; RUN: --stack-protector-guard-reg=sp_el0 \
+; RUN: --stack-protector-guard-offset=257 -verify-machineinstrs -o - | \
+; RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-257-OFFSET %s
+; RUN: llc %s --stack-protector-guard=sysreg \
+; RUN: --stack-protector-guard-reg=sp_el0 \
+; RUN: --stack-protector-guard-offset=-257 -verify-machineinstrs -o - | \
+; RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-MINUS-257-OFFSET %s
+
+; XFAIL
+; RUN: not --crash llc %s --stack-protector-guard=sysreg \
+; RUN: --stack-protector-guard-reg=sp_el0 \
+; RUN: --stack-protector-guard-offset=32761 -o - 2>&1 | \
+; RUN: FileCheck --check-prefix=CHECK-BAD-OFFSET %s
+; RUN: not --crash llc %s --stack-protector-guard=sysreg \
+; RUN: --stack-protector-guard-reg=sp_el0 \
+; RUN: --stack-protector-guard-offset=-4096 -o - 2>&1 | \
+; RUN: FileCheck --check-prefix=CHECK-BAD-OFFSET %s
+; RUN: not --crash llc %s --stack-protector-guard=sysreg \
+; RUN: --stack-protector-guard-reg=sp_el0 \
+; RUN: --stack-protector-guard-offset=4097 -o - 2>&1 | \
+; RUN: FileCheck --check-prefix=CHECK-BAD-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: ldur x8, [x8, #-8]
+; CHECK-NPOT-OFFSET: ldur x8, [x8, #1]
+; CHECK-NPOT-NEG-OFFSET: ldur x8, [x8, #-1]
+; CHECK-257-OFFSET: add x8, x8, #257
+; CHECK-257-OFFSET-NEXT: ldr x8, [x8]
+; CHECK-MINUS-257-OFFSET: sub x8, x8, #257
+; CHECK-MINUS-257-OFFSET-NEXT: ldr x8, [x8]
+; 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: ldur x9, [x9, #-8]
+; CHECK-NPOT-OFFSET: ldur x9, [x9, #1]
+; CHECK-NPOT-NEG-OFFSET: ldur x9, [x9, #-1]
+; CHECK-257-OFFSET: add x9, x9, #257
+; CHECK-257-OFFSET-NEXT: ldr x9, [x9]
+; CHECK-MINUS-257-OFFSET: sub x9, x9, #257
+; CHECK-MINUS-257-OFFSET-NEXT: ldr x9, [x9]
+; 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 void @baz(i32*)
+
+attributes #0 = { sspstrong }
+
+; CHECK-BAD-OFFSET: LLVM ERROR: Unable to encode Stack Protector Guard Offset
More information about the llvm-commits
mailing list