Allow memory accesses to be unaligned (AArch32/AArch64 only)
+.. option:: -mno-bti-at-return-twice
+
+Do not add a BTI instruction after a setjmp or other return-twice construct (Arm
+only)
+
Hexagon
-------
.. option:: -mieee-rnd-near
def mmark_bti_property : Flag<["-"], "mmark-bti-property">,
Group<m_aarch64_Features_Group>,
HelpText<"Add .note.gnu.property with BTI to assembly files (AArch64 only)">;
+def mno_bti_at_return_twice : Flag<["-"], "mno-bti-at-return-twice">,
+ Group<m_arm_Features_Group>,
+ HelpText<"Do not add a BTI instruction after a setjmp or other"
+ " return-twice construct (Arm only)">;
+
foreach i = {1-31} in
def ffixed_x#i : Flag<["-"], "ffixed-x"#i>, Group<m_Group>,
HelpText<"Reserve the x"#i#" register (AArch64/RISC-V only)">;
}
}
+ if (Args.getLastArg(options::OPT_mno_bti_at_return_twice))
+ Features.push_back("+no-bti-at-return-twice");
}
std::string arm::getARMArch(StringRef Arch, const llvm::Triple &Triple) {
--- /dev/null
+// RUN: %clang -target arm-arm-none-eabi -march=armv8-m.main -mbranch-protection=bti \
+// RUN: -mno-bti-at-return-twice -### %s 2>&1 | FileCheck %s --check-prefix=FEAT
+// RUN: %clang -target arm-arm-none-eabi -march=armv8-m.main -mbranch-protection=bti \
+// RUN: -### %s 2>&1 | FileCheck %s --check-prefix=NOFEAT
+
+// FEAT: "+no-bti-at-return-twice"
+// NOFEAT-NOT: "+no-bti-at-return-twice"
"Enable Pointer Authentication and Branch "
"Target Identification">;
+def FeatureNoBTIAtReturnTwice : SubtargetFeature<"no-bti-at-return-twice",
+ "NoBTIAtReturnTwice", "true",
+ "Don't place a BTI instruction "
+ "after a return-twice">;
+
//===----------------------------------------------------------------------===//
// ARM architecture class
//
MI.eraseFromParent();
return true;
}
+ case ARM::t2CALL_BTI: {
+ MachineFunction &MF = *MI.getMF();
+ MachineInstrBuilder MIB =
+ BuildMI(MF, MI.getDebugLoc(), TII->get(ARM::tBL));
+ MIB.cloneMemRefs(MI);
+ for (unsigned i = 0; i < MI.getNumOperands(); ++i)
+ MIB.add(MI.getOperand(i));
+ if (MI.isCandidateForCallSiteEntry())
+ MF.moveCallSiteInfo(&MI, MIB.getInstr());
+ MIBundleBuilder Bundler(MBB, MI);
+ Bundler.append(MIB);
+ Bundler.append(BuildMI(MF, MI.getDebugLoc(), TII->get(ARM::t2BTI)));
+ finalizeBundle(MBB, Bundler.begin(), Bundler.end());
+ MI.eraseFromParent();
+ return true;
+ }
case ARM::LOADDUAL:
case ARM::STOREDUAL: {
Register PairReg = MI.getOperand(0).getReg();
MAKE_CASE(ARMISD::CALL_PRED)
MAKE_CASE(ARMISD::CALL_NOLINK)
MAKE_CASE(ARMISD::tSECALL)
+ MAKE_CASE(ARMISD::t2CALL_BTI)
MAKE_CASE(ARMISD::BRCOND)
MAKE_CASE(ARMISD::BR_JT)
MAKE_CASE(ARMISD::BR2_JT)
bool isCmseNSCall = false;
bool isSibCall = false;
bool PreferIndirect = false;
+ bool GuardWithBTI = false;
+
+ // Lower 'returns_twice' calls to a pseudo-instruction.
+ if (CLI.CB && CLI.CB->getAttributes().hasFnAttr(Attribute::ReturnsTwice) &&
+ !Subtarget->getNoBTIAtReturnTwice())
+ GuardWithBTI = AFI->branchTargetEnforcement();
// Determine whether this is a non-secure function call.
if (CLI.CB && CLI.CB->getAttributes().hasFnAttr("cmse_nonsecure_call"))
// FIXME: handle tail calls differently.
unsigned CallOpc;
if (Subtarget->isThumb()) {
- if (isCmseNSCall)
+ if (GuardWithBTI)
+ CallOpc = ARMISD::t2CALL_BTI;
+ else if (isCmseNSCall)
CallOpc = ARMISD::tSECALL;
else if ((!isDirect || isARMFunc) && !Subtarget->hasV5TOps())
CallOpc = ARMISD::CALL_NOLINK;
CALL_PRED, // Function call that's predicable.
CALL_NOLINK, // Function call with branch not branch-and-link.
tSECALL, // CMSE non-secure function call.
+ t2CALL_BTI, // Thumb function call followed by BTI instruction.
BRCOND, // Conditional branch.
BR_JT, // Jumptable branch.
BR2_JT, // Jumptable branch (2 level - jumptable entry is a jump).
def t2AUT : PACBTIHintSpaceUseInst<"aut", 0b00101101> {
let hasSideEffects = 1;
}
+
+def ARMt2CallBTI : SDNode<"ARMISD::t2CALL_BTI", SDT_ARMcall,
+ [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, SDNPVariadic]>;
+
+def t2CALL_BTI : PseudoInst<(outs), (ins pred:$p, thumb_bl_target:$func),
+ IIC_Br, [(ARMt2CallBTI tglobaladdr:$func)]>,
+ Requires<[IsThumb2]>, Sched<[WriteBrL]>;
/// Selected instruction itineraries (one entry per itinerary class.)
InstrItineraryData InstrItins;
+ /// NoBTIAtReturnTwice - Don't place a BTI instruction after
+ /// return-twice constructs (setjmp)
+ bool NoBTIAtReturnTwice = false;
+
/// Options passed via command line that could influence the target
const TargetOptions &Options;
bool hardenSlsRetBr() const { return HardenSlsRetBr; }
bool hardenSlsBlr() const { return HardenSlsBlr; }
bool hardenSlsNoComdat() const { return HardenSlsNoComdat; }
+
+ bool getNoBTIAtReturnTwice() const { return NoBTIAtReturnTwice; }
};
} // end namespace llvm
--- /dev/null
+; RUN: llc -mtriple=thumbv8.1m.main-arm-none-eabi < %s | FileCheck %s --check-prefix=BTI
+; RUN: llc -mtriple=thumbv8.1m.main-arm-none-eabi -mattr=+no-bti-at-return-twice < %s | \
+; RUN: FileCheck %s --check-prefix=NOBTI
+
+; C source
+; --------
+; jmp_buf buf;
+;
+; extern void bar(int x);
+;
+; int foo(int x) {
+; if (setjmp(buf))
+; x = 0;
+; else
+; bar(x);
+; return x;
+; }
+
+@buf = global [20 x i64] zeroinitializer, align 8
+
+define i32 @foo(i32 %x) {
+; BTI-LABEL: foo:
+; BTI: bl setjmp
+; BTI-NEXT: bti
+; NOBTI-LABEL: foo:
+; NOBTI: bl setjmp
+; NOBTI-NOT: bti
+
+entry:
+ %call = call i32 @setjmp(i64* getelementptr inbounds ([20 x i64], [20 x i64]* @buf, i32 0, i32 0)) #0
+ %tobool.not = icmp eq i32 %call, 0
+ br i1 %tobool.not, label %if.else, label %if.end
+
+if.else: ; preds = %entry
+ call void @bar(i32 %x)
+ br label %if.end
+
+if.end: ; preds = %entry, %if.else
+ %x.addr.0 = phi i32 [ %x, %if.else ], [ 0, %entry ]
+ ret i32 %x.addr.0
+}
+
+declare void @bar(i32)
+declare i32 @setjmp(i64*) #0
+
+attributes #0 = { returns_twice }
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 1, !"branch-target-enforcement", i32 1}
--- /dev/null
+; RUN: llc -mtriple=thumbv8.1m.main-arm-none-eabi -enable-machine-outliner < %s | \
+; RUN: FileCheck %s --check-prefix=BTI
+; RUN: llc -mtriple=thumbv8.1m.main-arm-none-eabi -enable-machine-outliner -mattr=+no-bti-at-return-twice < %s | FileCheck %s --check-prefix=NOBTI
+
+; C source
+; --------
+; jmp_buf buf;
+;
+; extern void h(int a, int b, int *c);
+;
+; int f(int a, int b, int c, int d) {
+; if (setjmp(buf) != 0)
+; return -1;
+; h(a, b, &a);
+; return 2 + a * (a + b) / (c + d);
+; }
+;
+; int g(int a, int b, int c, int d) {
+; if (setjmp(buf) != 0)
+; return -1;
+; h(a, b, &a);
+; return 1 + a * (a + b) / (c + d);
+; }
+
+@buf = global [20 x i64] zeroinitializer, align 8
+
+define i32 @f(i32 %a, i32 %b, i32 %c, i32 %d) {
+; BTI-LABEL: f:
+; BTI: bl OUTLINED_FUNCTION_0
+; BTI-NEXT: bti
+; NOBTI-LABEL: f:
+; NOBTI: bl OUTLINED_FUNCTION_0
+; NOBTI-NEXT: cbz r0, .LBB0_2
+entry:
+ %a.addr = alloca i32, align 4
+ store i32 %a, i32* %a.addr, align 4
+ %call = call i32 @setjmp(i64* getelementptr inbounds ([20 x i64], [20 x i64]* @buf, i32 0, i32 0)) #0
+ %cmp.not = icmp eq i32 %call, 0
+ br i1 %cmp.not, label %if.end, label %return
+
+if.end: ; preds = %entry
+ call void @h(i32 %a, i32 %b, i32* nonnull %a.addr)
+ %0 = load i32, i32* %a.addr, align 4
+ %add = add nsw i32 %0, %b
+ %mul = mul nsw i32 %add, %0
+ %add1 = add nsw i32 %d, %c
+ %div = sdiv i32 %mul, %add1
+ %add2 = add nsw i32 %div, 2
+ br label %return
+
+return: ; preds = %entry, %if.end
+ %retval.0 = phi i32 [ %add2, %if.end ], [ -1, %entry ]
+ ret i32 %retval.0
+}
+
+define i32 @g(i32 %a, i32 %b, i32 %c, i32 %d) {
+; BTI-LABEL: g:
+; BTI: bl OUTLINED_FUNCTION_0
+; BTI-NEXT: bti
+; NOBTI-LABEL: g:
+; NOBTI: bl OUTLINED_FUNCTION_0
+; NOBTI-NEXT: cbz r0, .LBB1_2
+entry:
+ %a.addr = alloca i32, align 4
+ store i32 %a, i32* %a.addr, align 4
+ %call = call i32 @setjmp(i64* getelementptr inbounds ([20 x i64], [20 x i64]* @buf, i32 0, i32 0)) #0
+ %cmp.not = icmp eq i32 %call, 0
+ br i1 %cmp.not, label %if.end, label %return
+
+if.end: ; preds = %entry
+ call void @h(i32 %a, i32 %b, i32* nonnull %a.addr)
+ %0 = load i32, i32* %a.addr, align 4
+ %add = add nsw i32 %0, %b
+ %mul = mul nsw i32 %add, %0
+ %add1 = add nsw i32 %d, %c
+ %div = sdiv i32 %mul, %add1
+ %add2 = add nsw i32 %div, 1
+ br label %return
+
+return: ; preds = %entry, %if.end
+ %retval.0 = phi i32 [ %add2, %if.end ], [ -1, %entry ]
+ ret i32 %retval.0
+}
+
+declare void @h(i32, i32, i32*)
+declare i32 @setjmp(i64*) #0
+
+attributes #0 = { returns_twice }
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 1, !"branch-target-enforcement", i32 1}