mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-27 23:51:56 +00:00
[ARM] Implement setjmp BTI placement for PACBTI-M
This patch intends to guard indirect branches performed by longjmp by inserting BTI instructions after calls to setjmp. Calls with 'returns-twice' are lowered to a new pseudo-instruction named t2CALL_BTI that is later expanded to a bundle of {tBL,t2BTI}. This patch is part of a series that adds support for the PACBTI-M extension of the Armv8.1-M architecture, as detailed here: https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/armv8-1-m-pointer-authentication-and-branch-target-identification-extension The PACBTI-M specification can be found in the Armv8-M Architecture Reference Manual: https://developer.arm.com/documentation/ddi0553/latest The following people contributed to this patch: - Alexandros Lamprineas - Ties Stuij Reviewed By: labrinea Differential Revision: https://reviews.llvm.org/D112427
This commit is contained in:
parent
6b41eb7f26
commit
0fbb17458a
@ -3260,6 +3260,11 @@ Thread pointer access method (AArch32/AArch64 only)
|
||||
|
||||
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
|
||||
|
@ -3338,6 +3338,11 @@ def mno_fix_cortex_a53_835769 : Flag<["-"], "mno-fix-cortex-a53-835769">,
|
||||
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)">;
|
||||
|
@ -875,6 +875,8 @@ fp16_fml_fallthrough:
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
7
clang/test/Driver/arm-bti-return-twice.c
Normal file
7
clang/test/Driver/arm-bti-return-twice.c
Normal file
@ -0,0 +1,7 @@
|
||||
// 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"
|
@ -446,6 +446,11 @@ def FeaturePACBTI : SubtargetFeature<"pacbti", "HasPACBTI", "true",
|
||||
"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
|
||||
//
|
||||
|
@ -3073,6 +3073,22 @@ bool ARMExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
|
||||
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();
|
||||
|
@ -1658,6 +1658,7 @@ const char *ARMTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
||||
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)
|
||||
@ -2321,6 +2322,12 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
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"))
|
||||
@ -2726,7 +2733,9 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
// 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;
|
||||
|
@ -69,6 +69,7 @@ class VectorType;
|
||||
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).
|
||||
|
@ -5736,3 +5736,10 @@ def t2BTI : PACBTIHintSpaceNoOpsInst<"bti", 0b00001111>;
|
||||
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]>;
|
||||
|
@ -534,6 +534,10 @@ protected:
|
||||
/// 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;
|
||||
|
||||
@ -948,6 +952,8 @@ public:
|
||||
bool hardenSlsRetBr() const { return HardenSlsRetBr; }
|
||||
bool hardenSlsBlr() const { return HardenSlsBlr; }
|
||||
bool hardenSlsNoComdat() const { return HardenSlsNoComdat; }
|
||||
|
||||
bool getNoBTIAtReturnTwice() const { return NoBTIAtReturnTwice; }
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
50
llvm/test/CodeGen/ARM/setjmp-bti-basic.ll
Normal file
50
llvm/test/CodeGen/ARM/setjmp-bti-basic.ll
Normal file
@ -0,0 +1,50 @@
|
||||
; 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}
|
92
llvm/test/CodeGen/ARM/setjmp-bti-outliner.ll
Normal file
92
llvm/test/CodeGen/ARM/setjmp-bti-outliner.ll
Normal file
@ -0,0 +1,92 @@
|
||||
; 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}
|
Loading…
Reference in New Issue
Block a user