Index: llvm/trunk/lib/Target/X86/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Target/X86/CMakeLists.txt +++ llvm/trunk/lib/Target/X86/CMakeLists.txt @@ -23,6 +23,7 @@ set(sources X86AsmPrinter.cpp X86CallFrameOptimization.cpp + X86CallingConv.cpp X86CallLowering.cpp X86CmovConversion.cpp X86DomainReassignment.cpp @@ -36,6 +37,7 @@ X86InstructionSelector.cpp X86ISelDAGToDAG.cpp X86ISelLowering.cpp + X86IndirectBranchTracking.cpp X86InterleavedAccess.cpp X86InstrFMA3Info.cpp X86InstrInfo.cpp @@ -57,7 +59,6 @@ X86VZeroUpper.cpp X86WinAllocaExpander.cpp X86WinEHState.cpp - X86CallingConv.cpp ) add_llvm_target(X86CodeGen ${sources}) Index: llvm/trunk/lib/Target/X86/X86.h =================================================================== --- llvm/trunk/lib/Target/X86/X86.h +++ llvm/trunk/lib/Target/X86/X86.h @@ -49,6 +49,10 @@ /// 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(); Index: llvm/trunk/lib/Target/X86/X86IndirectBranchTracking.cpp =================================================================== --- llvm/trunk/lib/Target/X86/X86IndirectBranchTracking.cpp +++ llvm/trunk/lib/Target/X86/X86IndirectBranchTracking.cpp @@ -0,0 +1,163 @@ +//===---- 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 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(); + + // 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; +} Index: llvm/trunk/lib/Target/X86/X86InstrSystem.td =================================================================== --- llvm/trunk/lib/Target/X86/X86InstrSystem.td +++ llvm/trunk/lib/Target/X86/X86InstrSystem.td @@ -536,6 +536,11 @@ } // 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 { Index: llvm/trunk/lib/Target/X86/X86TargetMachine.cpp =================================================================== --- llvm/trunk/lib/Target/X86/X86TargetMachine.cpp +++ llvm/trunk/lib/Target/X86/X86TargetMachine.cpp @@ -426,6 +426,8 @@ if (getOptLevel() != CodeGenOpt::None) addPass(new X86ExecutionDepsFix()); + addPass(createX86IndirectBranchTrackingPass()); + if (UseVZeroUpper) addPass(createX86IssueVZeroUpperPass()); Index: llvm/trunk/test/CodeGen/X86/O0-pipeline.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/O0-pipeline.ll +++ llvm/trunk/test/CodeGen/X86/O0-pipeline.ll @@ -48,6 +48,7 @@ ; 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 Index: llvm/trunk/test/CodeGen/X86/indirect-branch-tracking.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/indirect-branch-tracking.ll +++ llvm/trunk/test/CodeGen/X86/indirect-branch-tracking.ll @@ -0,0 +1,200 @@ +; 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) ; [#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 +} Index: llvm/trunk/test/MC/X86/cet-encoding.s =================================================================== --- llvm/trunk/test/MC/X86/cet-encoding.s +++ llvm/trunk/test/MC/X86/cet-encoding.s @@ -1,4 +1,4 @@ -// 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] @@ -167,3 +167,11 @@ // 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