set(sources
X86AsmPrinter.cpp
X86CallFrameOptimization.cpp
+ X86CallingConv.cpp
X86CallLowering.cpp
X86CmovConversion.cpp
X86DomainReassignment.cpp
X86InstructionSelector.cpp
X86ISelDAGToDAG.cpp
X86ISelLowering.cpp
+ X86IndirectBranchTracking.cpp
X86InterleavedAccess.cpp
X86InstrFMA3Info.cpp
X86InstrInfo.cpp
X86VZeroUpper.cpp
X86WinAllocaExpander.cpp
X86WinEHState.cpp
- X86CallingConv.cpp
)
add_llvm_target(X86CodeGen ${sources})
/// transition penalty between functions encoded with AVX and SSE.
FunctionPass *createX86IssueVZeroUpperPass();
+/// This pass inserts ENDBR instructions before indirect jump/call
+/// destinations as part of CET IBT mechanism.
+FunctionPass *createX86IndirectBranchTrackingPass();
+
/// Return a pass that pads short functions with NOOPs.
/// This will prevent a stall when returning on the Atom.
FunctionPass *createX86PadShortFunctions();
--- /dev/null
+//===---- X86IndirectBranchTracking.cpp - Enables CET IBT mechanism -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a pass that enables Indirect Branch Tracking (IBT) as part
+// of Control-Flow Enforcement Technology (CET).
+// The pass adds ENDBR (End Branch) machine instructions at the beginning of
+// each basic block or function that is referenced by an indrect jump/call
+// instruction.
+// The ENDBR instructions have a NOP encoding and as such are ignored in
+// targets that do not support CET IBT mechanism.
+//===----------------------------------------------------------------------===//
+
+#include "X86.h"
+#include "X86InstrInfo.h"
+#include "X86Subtarget.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineJumpTableInfo.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "x86-indirect-branch-tracking"
+
+static cl::opt<bool> IndirectBranchTracking(
+ "x86-indirect-branch-tracking", cl::init(false), cl::Hidden,
+ cl::desc("Enable X86 indirect branch tracking pass."));
+
+STATISTIC(NumEndBranchAdded, "Number of ENDBR instructions added");
+
+namespace {
+class X86IndirectBranchTrackingPass : public MachineFunctionPass {
+public:
+ X86IndirectBranchTrackingPass() : MachineFunctionPass(ID) {}
+
+ StringRef getPassName() const override {
+ return "X86 Indirect Branch Tracking";
+ }
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+private:
+ static char ID;
+
+ /// Machine instruction info used throughout the class.
+ const X86InstrInfo *TII;
+
+ /// Endbr opcode for the current machine function.
+ unsigned int EndbrOpcode;
+
+ /// The function looks for an indirect jump terminator in MBB predecessors.
+ ///
+ /// Jump tables are generated when lowering switch-case statements or
+ /// setjmp/longjump functions.
+ /// As a result only indirect jumps use jump tables.
+ /// The function verifies this assumption.
+ ///
+ /// \return true if the input \p MBB has a predecessor MBB with indirect
+ /// branch terminator or false otherwise.
+ bool verifyIndirectJump(const MachineBasicBlock *MBB) const;
+
+ /// Adds a new ENDBR instruction to the begining of the MBB.
+ /// The function will not add it if already exists.
+ /// It will add ENDBR32 or ENDBR64 opcode, depending on the target.
+ void addENDBR(MachineBasicBlock &MBB) const;
+};
+
+} // end anonymous namespace
+
+char X86IndirectBranchTrackingPass::ID = 0;
+
+FunctionPass *llvm::createX86IndirectBranchTrackingPass() {
+ return new X86IndirectBranchTrackingPass();
+}
+
+bool X86IndirectBranchTrackingPass::verifyIndirectJump(
+ const MachineBasicBlock *MBB) const {
+ for (auto &PredMBB : MBB->predecessors())
+ for (auto &TermI : PredMBB->terminators())
+ if (TermI.isIndirectBranch())
+ return true;
+
+ return false;
+}
+
+void X86IndirectBranchTrackingPass::addENDBR(MachineBasicBlock &MBB) const {
+ assert(TII && "Target instruction info was not initialized");
+ assert((X86::ENDBR64 == EndbrOpcode || X86::ENDBR32 == EndbrOpcode) &&
+ "Unexpected Endbr opcode");
+
+ auto MI = MBB.begin();
+ // If the MBB is empty or the first instruction is not ENDBR,
+ // add the ENDBR instruction to the beginning of the MBB.
+ if (MI == MBB.end() || EndbrOpcode != MI->getOpcode()) {
+ BuildMI(MBB, MI, MBB.findDebugLoc(MI), TII->get(EndbrOpcode));
+ NumEndBranchAdded++;
+ }
+}
+
+bool X86IndirectBranchTrackingPass::runOnMachineFunction(MachineFunction &MF) {
+ const X86Subtarget &SubTarget = MF.getSubtarget<X86Subtarget>();
+
+ // Make sure that the target supports ENDBR instruction.
+ if (!SubTarget.hasIBT())
+ return false;
+
+ // Check that the cf-protection-branch is enabled.
+ Metadata *isCFProtectionSupported =
+ MF.getMMI().getModule()->getModuleFlag("cf-protection-branch");
+ if (!isCFProtectionSupported && !IndirectBranchTracking)
+ return false;
+
+ // True if the current MF was changed and false otherwise.
+ bool Changed = false;
+
+ TII = SubTarget.getInstrInfo();
+ EndbrOpcode = SubTarget.is64Bit() ? X86::ENDBR64 : X86::ENDBR32;
+
+ // Non-internal function or function whose address was taken, can be
+ // invoked through indirect calls. Mark the first BB with ENDBR instruction.
+ // TODO: Do not add ENDBR instruction in case notrack attribute is used.
+ if (MF.getFunction().hasAddressTaken() ||
+ !MF.getFunction().hasLocalLinkage()) {
+ auto MBB = MF.begin();
+ addENDBR(*MBB);
+ Changed = true;
+ }
+
+ for (auto &MBB : MF) {
+ // Find all basic blocks that thier address was taken (for example
+ // in the case of indirect jump) and add ENDBR instruction.
+ if (MBB.hasAddressTaken()) {
+ addENDBR(MBB);
+ Changed = true;
+ }
+ }
+
+ // Adds ENDBR instructions to MBB destinations of the jump table.
+ // TODO: In case of more than 50 destinations, do not add ENDBR and
+ // instead add DS_PREFIX.
+ if (MachineJumpTableInfo *JTI = MF.getJumpTableInfo()) {
+ for (const auto &JT : JTI->getJumpTables()) {
+ for (auto *MBB : JT.MBBs) {
+ // This assert verifies the assumption that this MBB has an indirect
+ // jump terminator in one of its predecessor.
+ assert(verifyIndirectJump(MBB) &&
+ "The MBB is not the destination of an indirect jump");
+
+ addENDBR(*MBB);
+ Changed = true;
+ }
+ }
+ }
+
+ return Changed;
+}
} // Defs SSP
} // SchedRW && HasSHSTK
+let Predicates = [HasIBT] in {
+ def ENDBR64 : I<0x1E, MRM_FA, (outs), (ins), "endbr64", []>, XS;
+ def ENDBR32 : I<0x1E, MRM_FB, (outs), (ins), "endbr32", []>, XS;
+} // HasIBT
+
//===----------------------------------------------------------------------===//
// XSAVE instructions
let SchedRW = [WriteSystem] in {
if (getOptLevel() != CodeGenOpt::None)
addPass(new X86ExecutionDepsFix());
+ addPass(createX86IndirectBranchTrackingPass());
+
if (UseVZeroUpper)
addPass(createX86IssueVZeroUpperPass());
; CHECK-NEXT: Post-RA pseudo instruction expansion pass
; CHECK-NEXT: X86 pseudo instruction expansion pass
; CHECK-NEXT: Analyze Machine Code For Garbage Collection
+; CHECK-NEXT: X86 Indirect Branch Tracking
; CHECK-NEXT: X86 vzeroupper inserter
; CHECK-NEXT: Contiguously Lay Out Funclets
; CHECK-NEXT: StackMap Liveness Analysis
--- /dev/null
+; RUN: llc -mtriple=x86_64-unknown-unknown -mattr=+ibt -x86-indirect-branch-tracking < %s | FileCheck %s --check-prefix=ALL --check-prefix=X86_64
+; RUN: llc -mtriple=i386-unknown-unknown -mattr=+ibt -x86-indirect-branch-tracking < %s | FileCheck %s --check-prefix=ALL --check-prefix=X86
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Test1
+;; -----
+;; Checks ENDBR insertion in case of indirect branch IR instruction.
+;; Also since the function is not internal, make sure that endbr32/64 was
+;; added at the beginning of the function.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+define i8 @test1(){
+; ALL-LABEL: test1
+; X86_64: endbr64
+; X86: endbr32
+; ALL: jmp{{q|l}} *
+; ALL: .LBB0_1:
+; X86_64-NEXT: endbr64
+; X86-NEXT: endbr32
+; ALL: .LBB0_2:
+; X86_64-NEXT: endbr64
+; X86-NEXT: endbr32
+entry:
+ %0 = select i1 undef, i8* blockaddress(@test1, %bb), i8* blockaddress(@test1, %bb6) ; <i8*> [#uses=1]
+ indirectbr i8* %0, [label %bb, label %bb6]
+
+bb: ; preds = %entry
+ ret i8 1
+
+bb6: ; preds = %entry
+ ret i8 2
+}
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Test2
+;; -----
+;; Checks ENDBR insertion in case of switch case statement.
+;; Also since the function is not internal, ENDBR instruction should be
+;; added to its first basic block.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+define i32 @test2(i32 %a) {
+; ALL-LABEL: test2
+; X86_64: endbr64
+; X86: endbr32
+; ALL: jmp{{q|l}} *
+; ALL: .LBB1_2:
+; X86_64-NEXT: endbr64
+; X86-NEXT: endbr32
+; ALL: .LBB1_7:
+; X86_64-NOT: endbr64
+; X86-NOT: endbr32
+; ALL: .LBB1_3:
+; X86_64-NEXT: endbr64
+; X86-NEXT: endbr32
+; ALL: .LBB1_4:
+; X86_64-NEXT: endbr64
+; X86-NEXT: endbr32
+; ALL: .LBB1_5:
+; X86_64-NEXT: endbr64
+; X86-NEXT: endbr32
+; ALL: .LBB1_6:
+; X86_64-NEXT: endbr64
+; X86-NEXT: endbr32
+entry:
+ %retval = alloca i32, align 4
+ %a.addr = alloca i32, align 4
+ store i32 %a, i32* %a.addr, align 4
+ %0 = load i32, i32* %a.addr, align 4
+ switch i32 %0, label %sw.default [
+ i32 0, label %sw.bb
+ i32 1, label %sw.bb1
+ i32 2, label %sw.bb2
+ i32 3, label %sw.bb3
+ i32 4, label %sw.bb4
+ ]
+
+sw.bb: ; preds = %entry
+ store i32 5, i32* %retval, align 4
+ br label %return
+
+sw.bb1: ; preds = %entry
+ store i32 7, i32* %retval, align 4
+ br label %return
+
+sw.bb2: ; preds = %entry
+ store i32 2, i32* %retval, align 4
+ br label %return
+
+sw.bb3: ; preds = %entry
+ store i32 32, i32* %retval, align 4
+ br label %return
+
+sw.bb4: ; preds = %entry
+ store i32 73, i32* %retval, align 4
+ br label %return
+
+sw.default: ; preds = %entry
+ store i32 0, i32* %retval, align 4
+ br label %return
+
+return: ; preds = %sw.default, %sw.bb4, %sw.bb3, %sw.bb2, %sw.bb1, %sw.bb
+ %1 = load i32, i32* %retval, align 4
+ ret i32 %1
+}
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Test3
+;; -----
+;; Checks ENDBR insertion in case of indirect call instruction.
+;; The new instruction should be added to the called function (test6)
+;; although it is internal.
+;; Also since the function is not internal, ENDBR instruction should be
+;; added to its first basic block.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+define void @test3() {
+; ALL-LABEL: test3
+; X86_64: endbr64
+; X86: endbr32
+; ALL: call{{q|l}} *
+entry:
+ %f = alloca i32 (...)*, align 8
+ store i32 (...)* bitcast (i32 (i32)* @test6 to i32 (...)*), i32 (...)** %f, align 8
+ %0 = load i32 (...)*, i32 (...)** %f, align 8
+ %call = call i32 (...) %0()
+ ret void
+}
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Test4
+;; -----
+;; Checks ENDBR insertion in case of setjmp-like function calls.
+;; Also since the function is not internal, ENDBR instruction should be
+;; added to its first basic block.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+@buf = internal global [5 x i8*] zeroinitializer
+declare i8* @llvm.frameaddress(i32)
+declare i8* @llvm.stacksave()
+declare i32 @llvm.eh.sjlj.setjmp(i8*)
+
+define i32 @test4() {
+; ALL-LABEL: test4
+; X86_64: endbr64
+; X86: endbr32
+; ALL: .LBB3_3:
+; X86_64-NEXT: endbr64
+; X86-NEXT: endbr32
+ %fp = tail call i8* @llvm.frameaddress(i32 0)
+ store i8* %fp, i8** getelementptr inbounds ([5 x i8*], [5 x i8*]* @buf, i64 0, i64 0), align 16
+ %sp = tail call i8* @llvm.stacksave()
+ store i8* %sp, i8** getelementptr inbounds ([5 x i8*], [5 x i8*]* @buf, i64 0, i64 2), align 16
+ %r = tail call i32 @llvm.eh.sjlj.setjmp(i8* bitcast ([5 x i8*]* @buf to i8*))
+ ret i32 %r
+}
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Test5
+;; -----
+;; Checks ENDBR insertion in case of internal function.
+;; Since the function is internal and its address was not taken,
+;; make sure that endbr32/64 was not added at the beginning of the
+;; function.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+define internal i8 @test5(){
+; ALL-LABEL: test5
+; X86_64-NOT: endbr64
+; X86-NOT: endbr32
+ ret i8 1
+}
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Test6
+;; -----
+;; Checks ENDBR insertion in case of function that its was address taken.
+;; Since the function's address was taken by test3() and despite being
+;; internal, check for added endbr32/64 at the beginning of the function.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+define internal i32 @test6(i32 %a) {
+; ALL-LABEL: test6
+; X86_64: endbr64
+; X86: endbr32
+ ret i32 1
+}
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Test7
+;; -----
+;; Checks ENDBR insertion in case of non-intrenal function.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+define i32 @test7() {
+; ALL-LABEL: test7
+; X86_64: endbr64
+; X86: endbr32
+ ret i32 1
+}
-// RUN: llvm-mc -triple x86_64-unknown-unknown -mattr=+shstk --show-encoding %s | FileCheck %s
+// RUN: llvm-mc -triple x86_64-unknown-unknown -mattr=+shstk -mattr=+ibt --show-encoding %s | FileCheck %s
// CHECK: incsspd %r13d
// CHECK: # encoding: [0xf3,0x41,0x0f,0xae,0xed]
// CHECK: setssbsy
// CHECK: # encoding: [0xf3,0x0f,0x01,0xe8]
setssbsy
+
+// CHECK: endbr64
+// CHECK: # encoding: [0xf3,0x0f,0x1e,0xfa]
+ endbr64
+
+// CHECK: endbr32
+// CHECK: # encoding: [0xf3,0x0f,0x1e,0xfb]
+ endbr32