Index: include/llvm/CodeGen/CommandFlags.h =================================================================== --- include/llvm/CodeGen/CommandFlags.h +++ include/llvm/CodeGen/CommandFlags.h @@ -223,6 +223,28 @@ " functions"), cl::init(false)); +cl::opt +CFIType("cfi-type", + cl::desc("Choose the type of Control-Flow Integrity check to add"), + cl::init(CFIntegrity::Sub), + cl::values( + clEnumValN(CFIntegrity::Sub, "sub", + "Subtract the pointer from the table base, then mask."), + clEnumValN(CFIntegrity::Ror, "ror", + "Use rotate to check the offset from a table base."), + clEnumValN(CFIntegrity::Add, "add", + "Mask out the high bits and add to an aligned base."), + clEnumValEnd)); + +cl::opt +CFIEnforcing("cfi-enforcing", + cl::desc("Enforce CFI or pass a the violation to a funcion."), + cl::init(false)); + +cl::opt +CFIFuncName("cfi-func-name", cl::desc("The name of the CFI function to call"), + cl::init("")); + // Common utility function tightly tied to the options listed here. Initializes // a TargetOptions object with CodeGen flags and returns it. static inline TargetOptions InitTargetOptionsFromCodeGenFlags() { @@ -251,6 +273,9 @@ Options.MCOptions = InitMCTargetOptionsFromFlags(); Options.JTType = JTableType; Options.JumpTableAll = JumpTableAll; + Options.CFIType = CFIType; + Options.CFIEnforcing = CFIEnforcing; + Options.CFIFuncName = CFIFuncName; return Options; } Index: include/llvm/CodeGen/ForwardControlFlowIntegrity.h =================================================================== --- /dev/null +++ include/llvm/CodeGen/ForwardControlFlowIntegrity.h @@ -0,0 +1,115 @@ +//===-- ForwardControlFlowIntegrity.h: Forward-Edge CFI ---------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass instruments indirect calls with checks to ensure that these calls +// pass through the appropriate jump-instruction table generated by +// JumpInstrTables. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_FORWARDCONTROLFLOWINTEGRITY_H +#define LLVM_CODEGEN_FORWARDCONTROLFLOWINTEGRITY_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/Pass.h" +#include "llvm/Target/TargetOptions.h" + +#include + +namespace llvm { + +class AnalysisUsage; +class BasicBlock; +class Constant; +class Function; +class Instruction; +class Module; +class Value; + +/// ForwardControlFlowIntegrity uses the information from JumpInstrTableInfo to +/// prepend checks to indirect calls to make sure that these calls target valid +/// locations. +class ForwardControlFlowIntegrity : public ModulePass { +public: + static char ID; + + ForwardControlFlowIntegrity(); + ForwardControlFlowIntegrity(JumpTable::JumpTableType JTT, + CFIntegrity::CFIntegrityType CFIType, + bool CFIEnforcing, std::string CFIFuncName); + virtual ~ForwardControlFlowIntegrity(); + + /// Runs the CFI pass on a given module. This works best if the module in + /// question is the result of link-time optimization (see lib/LTO). + bool runOnModule(Module &M) override; + const char *getPassName() const override { + return "ForwardControlFlowIntegrity"; + } + void getAnalysisUsage(AnalysisUsage &AU) const override; + +private: + typedef DenseSet CallSet; + + /// A structure that is used to keep track of constant table information. + typedef struct CFIConstants { + Constant *StartValue; + Constant *MaskValue; + Constant *Size; + } CFIConstants; + + /// A map from function type to the base of the table for this type and a mask + /// for the table + typedef DenseMap CFITables; + + CallSet IndirectCalls; + + /// The type of jumptable implementation. + JumpTable::JumpTableType JTType; + + /// The type of CFI check to add before each indirect call. + CFIntegrity::CFIntegrityType CFIType; + + /// A value that controls whether or not CFI violations cause a halt. + bool CFIEnforcing; + + /// The name of the function to call in case of a CFI violation when + /// CFIEnforcing is false. There is a default function that ignores + /// violations. + std::string CFIFuncName; + + /// Adds checks to each indirect call site to make sure that it is calling a + /// function in our jump table. + void updateIndirectCalls(Module &M, CFITables &CFIT); + + /// Walks the instructions to find all the indirect calls. + void getIndirectCalls(Module &M); + + /// Adds a function that handles violations in non-enforcing mode + /// (!CFIEnforcing). The default warning function simply returns, since the + /// exact details of how to handle CFI violations depend on the application. + void addWarningFunction(Module &M); + + /// Rewrites a function pointer in a call/invoke instruction to force it into + /// a table. + void rewriteFunctionPointer(Module &M, Instruction *I, Value *FunPtr, + Constant *JumpTableStart, Constant *JumpTableMask, + Constant *JumpTableSize); + + /// Inserts a check and a call to a warning function at a given instruction + /// that must be an indirect call. + void insertWarning(Module &M, BasicBlock *Block, Instruction *I, + Value *FunPtr); +}; + +ModulePass * +createForwardControlFlowIntegrityPass(JumpTable::JumpTableType JTT, + CFIntegrity::CFIntegrityType CFIType, + bool CFIEnforcing, StringRef CFIFuncName); +} + +#endif // LLVM_CODEGEN_FORWARDCONTROLFLOWINTEGRITY_H Index: include/llvm/CodeGen/JumpInstrTables.h =================================================================== --- include/llvm/CodeGen/JumpInstrTables.h +++ include/llvm/CodeGen/JumpInstrTables.h @@ -64,6 +64,15 @@ /// Checks to see if there is already a table for the given FunctionType. bool hasTable(FunctionType *FunTy); + /// Maps the function into a subset of function types, depending on the + /// jump-instruction table style selected from JumpTableTypes in + /// JumpInstrTables.cpp. The choice of mapping determines the number of + /// jump-instruction tables generated by this pass. E.g., the simplest mapping + /// converts every function type into void f(); so, all functions end up in a + /// single table. + static FunctionType *transformType(JumpTable::JumpTableType JTT, + FunctionType *FunTy); + private: /// The metadata used while a jump table is being built struct TableMeta { @@ -76,14 +85,6 @@ typedef DenseMap JumpMap; - /// Maps the function into a subset of function types, depending on the - /// jump-instruction table style selected from JumpTableTypes in - /// JumpInstrTables.cpp. The choice of mapping determines the number of - /// jump-instruction tables generated by this pass. E.g., the simplest mapping - /// converts every function type into void f(); so, all functions end up in a - /// single table. - FunctionType *transformType(FunctionType *FunTy); - /// The current state of functions and jump entries in the table(s). JumpMap Metadata; Index: include/llvm/CodeGen/Passes.h =================================================================== --- include/llvm/CodeGen/Passes.h +++ include/llvm/CodeGen/Passes.h @@ -590,6 +590,10 @@ /// createJumpInstrTables - This pass creates jump-instruction tables. ModulePass *createJumpInstrTablesPass(); + + /// createForwardControlFlowIntegrityPass - This pass adds control-flow + /// integrity. + ModulePass *createForwardControlFlowIntegrityPass(); } // End llvm namespace #endif Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -89,6 +89,7 @@ void initializeCFGOnlyViewerPass(PassRegistry&); void initializeCFGPrinterPass(PassRegistry&); void initializeCFGSimplifyPassPass(PassRegistry&); +void initializeForwardControlFlowIntegrityPass(PassRegistry&); void initializeFlattenCFGPassPass(PassRegistry&); void initializeStructurizeCFGPass(PassRegistry&); void initializeCFGViewerPass(PassRegistry&); Index: include/llvm/Target/TargetOptions.h =================================================================== --- include/llvm/Target/TargetOptions.h +++ include/llvm/Target/TargetOptions.h @@ -50,6 +50,17 @@ }; } + namespace CFIntegrity { + enum CFIntegrityType { + None, // Don't add control-flow integrity checks. + Sub, // Use subtraction-based checks. + Ror, // Use rotation-based checks. + Add // Use addition-based checks. This depends on having + // sufficient alignment in the code and is usually not + // feasible. + }; + } + class TargetOptions { public: TargetOptions() @@ -66,7 +77,8 @@ DataSections(false), TrapUnreachable(false), TrapFuncName(""), FloatABIType(FloatABI::Default), AllowFPOpFusion(FPOpFusion::Standard), JTType(JumpTable::Single), - JumpTableAll(false) {} + JumpTableAll(false), CFIType(CFIntegrity::None), CFIEnforcing(false), + CFIFuncName("") {} /// PrintMachineCode - This flag is enabled when the -print-machineinstrs /// option is specified on the command line, and should enable debugging @@ -225,6 +237,20 @@ /// automatically applied to all address-taken functions. bool JumpTableAll; + /// CFIType - This flag specifies the type of control-flow integrity check + /// to add as a preamble to indirect calls. + CFIntegrity::CFIntegrityType CFIType; + + /// CFIEnforcing - This flags controls whether or not CFI violations cause + /// the program to halt. + bool CFIEnforcing; + + /// getCFIFuncName - If this returns a non-empty string, then this is the + /// name of the function that will be called for each CFI violation in + /// non-enforcing mode. + std::string CFIFuncName; + StringRef getCFIFuncName() const; + /// Machine level options. MCTargetOptions MCOptions; }; Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -898,6 +898,13 @@ bool IsThumb = (Arch == Triple::thumb || Arch == Triple::thumbeb); MCInst TrapInst; TM.getInstrInfo()->getTrap(TrapInst); + + // Emit the right section for these functions. If the tables are not empty, + // then we are guaranteed that the first entry of any table is also not + // empty. So, we can use it to get the right section for functions. + Function *BaseFun = JITI->getTables().begin()->second.begin()->first; + OutStreamer.SwitchSection( + getObjFileLowering().SectionForGlobal(BaseFun, *Mang, TM)); for (const auto &KV : JITI->getTables()) { uint64_t Count = 0; for (const auto &FunPair : KV.second) { Index: lib/CodeGen/CMakeLists.txt =================================================================== --- lib/CodeGen/CMakeLists.txt +++ lib/CodeGen/CMakeLists.txt @@ -19,6 +19,7 @@ ExecutionDepsFix.cpp ExpandISelPseudos.cpp ExpandPostRAPseudos.cpp + ForwardControlFlowIntegrity.cpp GCMetadata.cpp GCMetadataPrinter.cpp GCStrategy.cpp Index: lib/CodeGen/ForwardControlFlowIntegrity.cpp =================================================================== --- /dev/null +++ lib/CodeGen/ForwardControlFlowIntegrity.cpp @@ -0,0 +1,396 @@ +//===-- ForwardControlFlowIntegrity.cpp: Forward-Edge CFI -----------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief A pass that instruments code with fast checks for indirect calls and +/// hooks for a function to check violations. +/// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "cfi" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/JumpInstrTableInfo.h" +#include "llvm/Analysis/Verifier.h" +#include "llvm/CodeGen/ForwardControlFlowIntegrity.h" +#include "llvm/CodeGen/JumpInstrTables.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/IR/Type.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +using namespace llvm; + +STATISTIC(NumCFIIndirectCalls, + "Number of indirect call sites rewritten by the CFI pass"); + +char ForwardControlFlowIntegrity::ID = 0; +INITIALIZE_PASS_BEGIN(ForwardControlFlowIntegrity, "forward-cfi", + "Control-Flow Integrity", true, true) +INITIALIZE_PASS_DEPENDENCY(JumpInstrTableInfo); +INITIALIZE_PASS_DEPENDENCY(JumpInstrTables); +INITIALIZE_PASS_END(ForwardControlFlowIntegrity, "forward-cfi", + "Control-Flow Integrity", true, true) + +ModulePass *llvm::createForwardControlFlowIntegrityPass() { + return new ForwardControlFlowIntegrity(); +} + +ModulePass *llvm::createForwardControlFlowIntegrityPass( + JumpTable::JumpTableType JTT, CFIntegrity::CFIntegrityType CFIType, + bool CFIEnforcing, StringRef CFIFuncName) { + return new ForwardControlFlowIntegrity(JTT, CFIType, CFIEnforcing, + CFIFuncName); +} + +namespace { +// Checks to see if a given CallSite is making an indirect call, including +// cases where the indirect call is made through a bitcast. +bool isIndirectCall(CallSite &CS) { + if (CS.getCalledFunction()) + return false; + + // Check the value to see if it is merely a bitcast of a function. In + // this case, it will translate to a direct function call in the resulting + // assembly, so we won't treat it as an indirect call here. + const Value *V = CS.getCalledValue(); + if (const ConstantExpr *CE = dyn_cast(V)) { + return !(CE->isCast() && isa(CE->getOperand(0))); + } + + // Otherwise, since we know it's a call, it must be an indirect call + return true; +} + +const char cfi_failure_func_name[] = "__llvm_cfi_pointer_warning"; +const char cfi_func_name_prefix[] = "__llvm_cfi_function_"; +} // end anonymous namespace + +ForwardControlFlowIntegrity::ForwardControlFlowIntegrity() + : ModulePass(ID), IndirectCalls(), JTType(JumpTable::Single), + CFIType(CFIntegrity::None), CFIEnforcing(false), CFIFuncName("") { + initializeForwardControlFlowIntegrityPass(*PassRegistry::getPassRegistry()); +} + +ForwardControlFlowIntegrity::ForwardControlFlowIntegrity( + JumpTable::JumpTableType JTT, CFIntegrity::CFIntegrityType CFIType, + bool CFIEnforcing, std::string CFIFuncName) + : ModulePass(ID), IndirectCalls(), JTType(JTT), CFIType(CFIType), + CFIEnforcing(CFIEnforcing), CFIFuncName(CFIFuncName) { + initializeForwardControlFlowIntegrityPass(*PassRegistry::getPassRegistry()); +} + +ForwardControlFlowIntegrity::~ForwardControlFlowIntegrity() {} + +void ForwardControlFlowIntegrity::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + AU.addRequired(); +} + +void ForwardControlFlowIntegrity::getIndirectCalls(Module &M) { + // To get the indirect calls, we iterate over all functions and iterate over + // the list of basic blocks in each. We extract a total list of indirect calls + // before modifying any of them, since our modifications will modify the list + // of basic blocks. + for (Function &F : M) { + for (auto FI = F.begin(), FE = F.end(); FI != FE; ++FI) { + BasicBlock *BB = FI; + for (auto BI = BB->begin(), BE = BB->end(); BI != BE; ++BI) { + Instruction *I = BI; + + CallSite CS(I); + if (!(CS.isCall() && isIndirectCall(CS))) + continue; + + Value *CalledValue = CS.getCalledValue(); + + // Don't rewrite this instruction if the indirect call is actually just + // inline assembly, since our transformation will generate an invalid + // module in that case. + if (isa(CalledValue)) + continue; + + IndirectCalls.insert(I); + } + } + } +} + +void ForwardControlFlowIntegrity::updateIndirectCalls(Module &M, + CFITables &CFIT) { + Type *Int64Ty = Type::getInt64Ty(M.getContext()); + for (Instruction *I : IndirectCalls) { + CallSite CS(I); + Value *CalledValue = CS.getCalledValue(); + + // Get the function type for this call and look it up in the tables. + Type *VTy = CalledValue->getType(); + PointerType *PTy = dyn_cast(VTy); + if (!PTy) + continue; + + Type *EltTy = PTy->getElementType(); + FunctionType *FunTy = dyn_cast(EltTy); + if (!FunTy) + continue; + + FunctionType *TransformedTy = JumpInstrTables::transformType(JTType, FunTy); + ++NumCFIIndirectCalls; + Constant *JumpTableStart = NULL; + Constant *JumpTableMask = NULL; + Constant *JumpTableSize = NULL; + + // Some call sites have function types that don't correspond to any + // address-taken function in the module. This happens when function pointers + // are passed in from external code. + auto it = CFIT.find(TransformedTy); + if (it == CFIT.end()) { + // In this case, make sure that the function pointer will change by + // setting the mask and the start to be 0 so that the transformed + // function is 0. + JumpTableStart = ConstantInt::get(Int64Ty, 0); + JumpTableMask = ConstantInt::get(Int64Ty, 0); + JumpTableSize = ConstantInt::get(Int64Ty, 0); + } else { + JumpTableStart = it->second.StartValue; + JumpTableMask = it->second.MaskValue; + JumpTableSize = it->second.Size; + } + + rewriteFunctionPointer(M, I, CalledValue, JumpTableStart, JumpTableMask, + JumpTableSize); + } + + return; +} + +bool ForwardControlFlowIntegrity::runOnModule(Module &M) { + if (CFIType == CFIntegrity::None) + return false; + + JumpInstrTableInfo *JITI = &getAnalysis(); + Type *Int64Ty = Type::getInt64Ty(M.getContext()); + Type *VoidPtrTy = Type::getInt8PtrTy(M.getContext()); + + // Set up tables for control-flow integrity based on information about the + // jump-instruction tables. + CFITables CFIT; + for (const auto &KV : JITI->getTables()) { + uint64_t Size = static_cast(KV.second.size()); + uint64_t TableSize = NextPowerOf2(Size); + + // This computation assumes that each entry takes up 8 bytes. This should + // really get this information from the Target in some way. + int64_t MaskValue = ((TableSize << 3) - 1) & -8; + Constant *JumpTableMaskValue = ConstantInt::get(Int64Ty, MaskValue); + Constant *JumpTableSize = ConstantInt::get(Int64Ty, Size); + + // The base of the table is defined to be the first jumptable function in + // the table. + Function *First = KV.second.begin()->second; + Constant *JumpTableStartValue = ConstantExpr::getBitCast(First, VoidPtrTy); + CFIT[KV.first].StartValue = JumpTableStartValue; + CFIT[KV.first].MaskValue = JumpTableMaskValue; + CFIT[KV.first].Size = JumpTableSize; + } + + if (CFIT.empty()) + return false; + + getIndirectCalls(M); + + if (!CFIEnforcing) { + addWarningFunction(M); + } + + // Update the instructions with the check and the indirect jump through our + // table. + updateIndirectCalls(M, CFIT); + + return true; +} + +void ForwardControlFlowIntegrity::addWarningFunction(Module &M) { + Type *CharTy = Type::getInt8Ty(M.getContext()); + PointerType *CharPtrTy = PointerType::getUnqual(CharTy); + + // Get the type of the Warning Function: void (i8*, i8*), + // where the first argument is the name of the function in which the violation + // occurs, and the second is the function pointer that violates CFI. + SmallVector WarningFunArgs; + WarningFunArgs.push_back(CharPtrTy); + WarningFunArgs.push_back(CharPtrTy); + FunctionType *WarningFunTy = + FunctionType::get(Type::getVoidTy(M.getContext()), WarningFunArgs, false); + + if (!CFIFuncName.empty()) { + // If this function is not defined in the module, then it will have to be + // defined at link time. + Constant *FailureFun = M.getOrInsertFunction(CFIFuncName, WarningFunTy); + assert(FailureFun && "Could not get or insert the function specified by" + " -cfi-func-name"); + } else { + // The default warning function swallows the warning and lets the call + // continue, since there's no generic way for it to print out this + // information. + Function *WarningFun = M.getFunction(cfi_failure_func_name); + if (!WarningFun) { + WarningFun = Function::Create(WarningFunTy, GlobalValue::ExternalLinkage, + cfi_failure_func_name, &M); + } + + BasicBlock *Entry = + BasicBlock::Create(M.getContext(), "entry", WarningFun, 0); + ReturnInst::Create(M.getContext(), Entry); + } +} + +void ForwardControlFlowIntegrity::rewriteFunctionPointer( + Module &M, Instruction *I, Value *FunPtr, Constant *JumpTableStart, + Constant *JumpTableMask, Constant *JumpTableSize) { + IRBuilder<> TempBuilder(I); + + Type *OrigFunType = FunPtr->getType(); + + BasicBlock *CurBB = cast(I->getParent()); + Function *CurF = cast(CurBB->getParent()); + Type *Int64Ty = Type::getInt64Ty(M.getContext()); + + Value *TI = TempBuilder.CreatePtrToInt(FunPtr, Int64Ty); + Value *TStartInt = TempBuilder.CreatePtrToInt(JumpTableStart, Int64Ty); + + Value *NewFunPtr = NULL; + Value *Check = NULL; + assert(CFIType != CFIntegrity::None && "Can't apply null CFI checks"); + if (CFIType == CFIntegrity::Sub) { + // This is the subtract, mask, and add version. + // Subtract from the base. + Value *Sub = TempBuilder.CreateSub(TI, TStartInt); + + // Mask the difference to force this to be a table offset. + Value *And = TempBuilder.CreateAnd(Sub, JumpTableMask); + + // Add it back to the base. + Value *Result = TempBuilder.CreateAdd(And, TStartInt); + + // Convert it back into a function pointer that we can call. + NewFunPtr = TempBuilder.CreateIntToPtr(Result, OrigFunType); + } else if (CFIType == CFIntegrity::Ror) { + // This is the subtract and rotate version. + // Rotate right by the alignment value. The optimizer should recognize + // this sequence as a rotation. + // TODO: these values should come from the JumpInstrTableInfo. + Constant *RightShift = ConstantInt::get(Int64Ty, 3); + Constant *LeftShift = ConstantInt::get(Int64Ty, 64 - 3); + + // Subtract from the base. + Value *Sub = TempBuilder.CreateSub(TI, TStartInt); + + // Create the equivalent of a rotate-right instruction. + Value *Shr = TempBuilder.CreateLShr(Sub, RightShift); + Value *Shl = TempBuilder.CreateShl(Sub, LeftShift); + Value *Or = TempBuilder.CreateOr(Shr, Shl); + + // Perform unsigned comparison to check for inclusion in the table. + Check = TempBuilder.CreateICmpULT(Or, JumpTableSize); + NewFunPtr = FunPtr; + } else if (CFIType == CFIntegrity::Add) { + // This is the mask and add version. + // Mask the function pointer to turn it into an offset into the table. + Value *And = TempBuilder.CreateAnd(TI, JumpTableMask); + + // Then or this offset to the base and get the pointer value. + Value *Result = TempBuilder.CreateAdd(And, TStartInt); + + // Convert it back into a function pointer that we can call. + NewFunPtr = TempBuilder.CreateIntToPtr(Result, OrigFunType); + } + + if (!CFIEnforcing) { + // If a check hasn't been added (in the rotation version), then check to see + // if it's the same as the original function. This check determines whether + // or not we call the CFI failure function. + if (!Check) + Check = TempBuilder.CreateICmpEQ(NewFunPtr, FunPtr); + BasicBlock *InvalidPtrBlock = + BasicBlock::Create(M.getContext(), "invalid.ptr", CurF, 0); + BasicBlock *ContinuationBB = CurBB->splitBasicBlock(I); + + // Remove the unconditional branch that connects the two blocks. + TerminatorInst *TermInst = CurBB->getTerminator(); + TermInst->eraseFromParent(); + + // Add a conditional branch that depends on the Check above. + BranchInst::Create(ContinuationBB, InvalidPtrBlock, Check, CurBB); + + // Call the warning function for this pointer, then continue. + Instruction *BI = BranchInst::Create(ContinuationBB, InvalidPtrBlock); + insertWarning(M, InvalidPtrBlock, BI, FunPtr); + } else { + // Modify the instruction to call this value. + CallSite CS(I); + CS.setCalledFunction(NewFunPtr); + } +} + +void ForwardControlFlowIntegrity::insertWarning(Module &M, BasicBlock *Block, + Instruction *I, Value *FunPtr) { + Function *ParentFun = cast(Block->getParent()); + + // Get the function to call right before the instruction. + Function *WarningFun = NULL; + if (CFIFuncName.empty()) { + WarningFun = M.getFunction(cfi_failure_func_name); + } else { + WarningFun = M.getFunction(CFIFuncName); + } + + assert(WarningFun && "Could not find the CFI failure function"); + + // Look up or create a GlobalVariable + // __llvm_cfi_function_ParentName containing this name. + StringRef ParentName(ParentFun->getName()); + std::string GVName = + Twine(cfi_func_name_prefix).concat(ParentFun->getName()).str(); + GlobalVariable *ParentNameGV = M.getNamedGlobal(GVName); + if (!ParentNameGV) { + Type *CharTy = Type::getInt8Ty(M.getContext()); + ArrayType *ParentNameStringTy = + ArrayType::get(CharTy, ParentName.size() + 1); + + ParentNameGV = new GlobalVariable(M, ParentNameStringTy, true, + GlobalValue::PrivateLinkage, 0, ".str"); + Constant *ParentNameStrConst = + ConstantDataArray::getString(M.getContext(), ParentName, true); + ParentNameGV->setInitializer(ParentNameStrConst); + } + + Type *VoidPtrTy = Type::getInt8PtrTy(M.getContext()); + + IRBuilder<> WarningInserter(I); + Value *ParentNamePtr = WarningInserter.CreateBitCast(ParentNameGV, VoidPtrTy); + Value *FunVoidPtr = WarningInserter.CreateBitCast(FunPtr, VoidPtrTy); + WarningInserter.CreateCall2(WarningFun, ParentNamePtr, FunVoidPtr); +} Index: lib/CodeGen/JumpInstrTables.cpp =================================================================== --- lib/CodeGen/JumpInstrTables.cpp +++ lib/CodeGen/JumpInstrTables.cpp @@ -167,7 +167,7 @@ Function *JumpInstrTables::insertEntry(Module &M, Function *Target) { FunctionType *OrigFunTy = Target->getFunctionType(); - FunctionType *FunTy = transformType(OrigFunTy); + FunctionType *FunTy = transformType(JTType, OrigFunTy); JumpMap::iterator it = Metadata.find(FunTy); if (Metadata.end() == it) { @@ -195,11 +195,12 @@ } bool JumpInstrTables::hasTable(FunctionType *FunTy) { - FunctionType *TransTy = transformType(FunTy); + FunctionType *TransTy = transformType(JTType, FunTy); return Metadata.end() != Metadata.find(TransTy); } -FunctionType *JumpInstrTables::transformType(FunctionType *FunTy) { +FunctionType *JumpInstrTables::transformType(JumpTable::JumpTableType JTT, + FunctionType *FunTy) { // Returning nullptr forces all types into the same table, since all types map // to the same type Type *VoidPtrTy = Type::getInt8PtrTy(FunTy->getContext()); @@ -215,7 +216,7 @@ Type *Int32Ty = Type::getInt32Ty(FunTy->getContext()); FunctionType *VoidFnTy = FunctionType::get( Type::getVoidTy(FunTy->getContext()), EmptyParams, false); - switch (JTType) { + switch (JTT) { case JumpTable::Single: return FunctionType::get(RetTy, EmptyParams, false); Index: lib/CodeGen/LLVMTargetMachine.cpp =================================================================== --- lib/CodeGen/LLVMTargetMachine.cpp +++ lib/CodeGen/LLVMTargetMachine.cpp @@ -15,6 +15,7 @@ #include "llvm/Analysis/Passes.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/CodeGen/ForwardControlFlowIntegrity.h" #include "llvm/CodeGen/JumpInstrTables.h" #include "llvm/CodeGen/MachineFunctionAnalysis.h" #include "llvm/CodeGen/MachineModuleInfo.h" @@ -145,6 +146,9 @@ // JIT time, so we don't add them directly to addPassesToGenerateCode. PM.add(createJumpInstrTableInfoPass()); PM.add(createJumpInstrTablesPass(Options.JTType, Options.JumpTableAll)); + PM.add(createForwardControlFlowIntegrityPass(Options.JTType, Options.CFIType, + Options.CFIEnforcing, + Options.getCFIFuncName())); // Add common CodeGen passes. MCContext *Context = addPassesToGenerateCode(this, PM, DisableVerify, Index: lib/CodeGen/TargetOptionsImpl.cpp =================================================================== --- lib/CodeGen/TargetOptionsImpl.cpp +++ lib/CodeGen/TargetOptionsImpl.cpp @@ -51,3 +51,10 @@ StringRef TargetOptions::getTrapFunctionName() const { return TrapFuncName; } + +/// getCFIFuncName - If this returns a non-empty string, then it is the name of +/// the function that gets called on CFI violations in CFI non-enforcing mode +/// (!TargetOptions::CFIEnforcing). +StringRef TargetOptions::getCFIFuncName() const { + return CFIFuncName; +} Index: test/CodeGen/Generic/stop-after.ll =================================================================== --- test/CodeGen/Generic/stop-after.ll +++ test/CodeGen/Generic/stop-after.ll @@ -5,6 +5,6 @@ ; STOP: Loop Strength Reduction ; STOP-NEXT: Machine Function Analysis -; START: -machine-branch-prob -jump-instr-tables -gc-lowering +; START: -machine-branch-prob -jump-instr-tables -forward-cfi -gc-lowering ; START: FunctionPass Manager ; START-NEXT: Lower Garbage Collection Instructions Index: test/CodeGen/X86/cfi_enforcing.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/cfi_enforcing.ll @@ -0,0 +1,32 @@ +; RUN: llc -jump-table-all -cfi-enforcing <%s | FileCheck %s + +target triple = "x86_64-unknown-linux-gnu" +define void @indirect_fun() { + ret void +} + +define i32 @m(void ()* %fun) { + call void ()* %fun() +; CHECK: subl +; CHECK: andq $8, +; CHECK: leaq __llvm_jump_instr_table_0_1(%r +; CHECK-NOT: callq __llvm_cfi_pointer_warning +; CHECK: callq *%r + ret i32 0 +} + +define void ()* @get_fun() { + ret void ()* @indirect_fun +} + +define i32 @main(i32 %argc, i8** %argv) { + %f = call void ()* ()* @get_fun() + %a = call i32 @m(void ()* %f) + ret i32 %a +} +; XFAIL: win32 + +; CHECK: .globl __llvm_jump_instr_table_0_1 +; CHECK: .align 8 +; CHECK: __llvm_jump_instr_table_0_1: +; CHECK: jmp indirect_fun@PLT Index: test/CodeGen/X86/cfi_non_default_function.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/cfi_non_default_function.ll @@ -0,0 +1,28 @@ +; RUN: llc -jump-table-all -cfi-func-name=cfi_new_failure <%s | FileCheck %s + +target triple = "x86_64-unknown-linux-gnu" +define void @indirect_fun() { + ret void +} + +define i32 @m(void ()* %fun) { + call void ()* %fun() +; CHECK: callq cfi_new_failure + ret i32 0 +} + +define void ()* @get_fun() { + ret void ()* @indirect_fun +} + +define i32 @main(i32 %argc, i8** %argv) { + %f = call void ()* ()* @get_fun() + %a = call i32 @m(void ()* %f) + ret i32 %a +} +; XFAIL: win32 + +; CHECK: .globl __llvm_jump_instr_table_0_1 +; CHECK: .align 8 +; CHECK: __llvm_jump_instr_table_0_1: +; CHECK: jmp indirect_fun@PLT Index: test/CodeGen/X86/cfi_simple_indirect_call.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/cfi_simple_indirect_call.ll @@ -0,0 +1,45 @@ +; RUN: llc -jump-table-all -cfi-type=sub <%s | FileCheck --check-prefix=SUB %s +; RUN: llc -jump-table-all -cfi-type=add <%s | FileCheck --check-prefix=ADD %s +; RUN: llc -jump-table-all -cfi-type=ror <%s | FileCheck --check-prefix=ROR %s + +target triple = "x86_64-unknown-linux-gnu" + +define void @indirect_fun() { + ret void +} + +define i32 @m(void ()* %fun) { + call void ()* %fun() +; SUB: subl +; SUB: andq $8 +; SUB: leaq __llvm_jump_instr_table_0_1 +; SUB: callq __llvm_cfi_pointer_warning + +; ROR: subq +; ROR: rolq $61 +; ROR: testq +; ROR: callq __llvm_cfi_pointer_warning + +; ADD: andq $8 +; ADD: leaq __llvm_jump_instr_table_0_1 +; ADD: cmpq +; ADD: callq __llvm_cfi_pointer_warning +ret i32 0 +} + +define void ()* @get_fun() { + ret void ()* @indirect_fun +} + +define i32 @main(i32 %argc, i8** %argv) { + %f = call void ()* ()* @get_fun() + %a = call i32 @m(void ()* %f) + ret i32 %a +} +; XFAIL: win32 +; SUB: .text +; SUB: .globl __llvm_jump_instr_table_0_1 +; SUB: .align 8 +; SUB: .type __llvm_jump_instr_table_0_1,@function +; SUB:__llvm_jump_instr_table_0_1: +; SUB: jmp indirect_fun@PLT Index: test/CodeGen/X86/jump_table_alias.ll =================================================================== --- test/CodeGen/X86/jump_table_alias.ll +++ test/CodeGen/X86/jump_table_alias.ll @@ -26,7 +26,7 @@ ; because they both alias the same value. ; CHECK: .globl __llvm_jump_instr_table_0_1 -; CHECK: .align 8, 0x90 +; CHECK: .align 8 ; CHECK: .type __llvm_jump_instr_table_0_1,@function ; CHECK: __llvm_jump_instr_table_0_1: ; CHECK: jmp f@PLT Index: test/CodeGen/X86/jump_table_all.ll =================================================================== --- test/CodeGen/X86/jump_table_all.ll +++ test/CodeGen/X86/jump_table_all.ll @@ -26,7 +26,7 @@ ; This table should be created even without the jumptable annotation ; because llc is called with -jump-table-all, and @f has its address taken ; CHECK: .globl __llvm_jump_instr_table_0_1 -; CHECK: .align 8, 0x90 +; CHECK: .align 8 ; CHECK: .type __llvm_jump_instr_table_0_1,@function ; CHECK: __llvm_jump_instr_table_0_1: ; CHECK: jmp f@PLT Index: test/CodeGen/X86/jump_table_bitcast.ll =================================================================== --- test/CodeGen/X86/jump_table_bitcast.ll +++ test/CodeGen/X86/jump_table_bitcast.ll @@ -15,12 +15,12 @@ define i32 @main() { %g = alloca i32 (...)*, align 8 store i32 (...)* bitcast (i32 ()* @f to i32 (...)*), i32 (...)** %g, align 8 -; CHECK: movq $__llvm_jump_instr_table_0_[[ENTRY:1|2|3]], (%rsp) -; CHECK: movl $__llvm_jump_instr_table_0_[[ENTRY]], %ecx +; CHECK: movq $__llvm_jump_instr_table_0_[[ENTRY:1|2|3]], 8(%rsp) +; CHECK: movl $__llvm_jump_instr_table_0_[[ENTRY]], %1 = load i32 (...)** %g, align 8 %call = call i32 (...)* %1() call void (void ()*)* @h(void ()* bitcast (void (void ()*)* @h to void ()*)) -; CHECK: movl $__llvm_jump_instr_table_0_{{1|2|3}}, %edi +; CHECK: movl $__llvm_jump_instr_table_0_{{1|2|3}}, ; CHECK: callq h %a = call i32 (i32*)* bitcast (i32 (i8*)* @g to i32(i32*)*)(i32* null) @@ -29,17 +29,17 @@ } ; CHECK: .globl __llvm_jump_instr_table_0_1 -; CHECK: .align 8, 0x90 +; CHECK: .align 8 ; CHECK: .type __llvm_jump_instr_table_0_1,@function ; CHECK: __llvm_jump_instr_table_0_1: ; CHECK: jmp {{f|g|h}}@PLT ; CHECK: .globl __llvm_jump_instr_table_0_2 -; CHECK: .align 8, 0x90 +; CHECK: .align 8 ; CHECK: .type __llvm_jump_instr_table_0_2,@function ; CHECK: __llvm_jump_instr_table_0_2: ; CHECK: jmp {{f|g|h}}@PLT ; CHECK: .globl __llvm_jump_instr_table_0_3 -; CHECK: .align 8, 0x90 +; CHECK: .align 8 ; CHECK: .type __llvm_jump_instr_table_0_3,@function ; CHECK: __llvm_jump_instr_table_0_3: ; CHECK: jmp {{f|g|h}}@PLT Index: test/CodeGen/X86/jump_tables.ll =================================================================== --- test/CodeGen/X86/jump_tables.ll +++ test/CodeGen/X86/jump_tables.ll @@ -75,198 +75,198 @@ } ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_1 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_1,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_1: ; SINGLE-DAG: jmp indirect_fun_array@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_2 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_2,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_2: ; SINGLE-DAG: jmp indirect_fun_i32_2@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_3 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_3,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_3: ; SINGLE-DAG: jmp indirect_fun_vec_2@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_4 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_4,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_4: ; SINGLE-DAG: jmp indirect_fun_i32S_2@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_5 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_5,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_5: ; SINGLE-DAG: jmp indirect_fun_struct@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_6 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_6,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_6: ; SINGLE-DAG: jmp indirect_fun_i32_1@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_7 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_7,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_7: ; SINGLE-DAG: jmp indirect_fun_i32@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_8 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_8,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_8: ; SINGLE-DAG: jmp indirect_fun_fun@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_9 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_9,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_9: ; SINGLE-DAG: jmp indirect_fun_fun_ret@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_10 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_10,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_10: ; SINGLE-DAG: jmp indirect_fun@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_11 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_11,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_11: ; SINGLE-DAG: jmp indirect_fun_match@PLT ; SINGLE-DAG: .globl __llvm_jump_instr_table_0_12 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: .type __llvm_jump_instr_table_0_12,@function ; SINGLE-DAG: __llvm_jump_instr_table_0_12: ; SINGLE-DAG: jmp indirect_fun_vec@PLT -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: ud2 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: ud2 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: ud2 -; SINGLE-DAG: .align 8, 0x90 +; SINGLE-DAG: .align 8 ; SINGLE-DAG: ud2 ; ARITY-DAG: .globl __llvm_jump_instr_table_2_1 -; ARITY-DAG: .align 8, 0x90 +; ARITY-DAG: .align 8 ; ARITY-DAG: .type __llvm_jump_instr_table_2_1,@function ; ARITY-DAG: __llvm_jump_instr_table_2_1: ; ARITY-DAG: jmp indirect_fun{{.*}}@PLT -; ARITY-DAG: .align 8, 0x90 +; ARITY-DAG: .align 8 ; ARITY-DAG: ud2 ; ARITY-DAG: .globl __llvm_jump_instr_table_0_1 -; ARITY-DAG: .align 8, 0x90 +; ARITY-DAG: .align 8 ; ARITY-DAG: .type __llvm_jump_instr_table_0_1,@function ; ARITY-DAG: __llvm_jump_instr_table_0_1: ; ARITY-DAG: jmp indirect_fun{{.*}}@PLT ; ARITY-DAG: .globl __llvm_jump_instr_table_1_1 -; ARITY-DAG: .align 8, 0x90 +; ARITY-DAG: .align 8 ; ARITY-DAG: .type __llvm_jump_instr_table_1_1,@function ; ARITY-DAG: __llvm_jump_instr_table_1_1: ; ARITY-DAG: jmp indirect_fun{{.*}}@PLT ; SIMPL-DAG: .globl __llvm_jump_instr_table_2_1 -; SIMPL-DAG: .align 8, 0x90 +; SIMPL-DAG: .align 8 ; SIMPL-DAG: .type __llvm_jump_instr_table_2_1,@function ; SIMPL-DAG: __llvm_jump_instr_table_2_1: ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT -; SIMPL-DAG: .align 8, 0x90 +; SIMPL-DAG: .align 8 ; SIMPL-DAG: ud2 ; SIMPL-DAG: .globl __llvm_jump_instr_table_0_1 -; SIMPL-DAG: .align 8, 0x90 +; SIMPL-DAG: .align 8 ; SIMPL-DAG: .type __llvm_jump_instr_table_0_1,@function ; SIMPL-DAG: __llvm_jump_instr_table_0_1: ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT ; SIMPL-DAG: .globl __llvm_jump_instr_table_1_1 -; SIMPL-DAG: .align 8, 0x90 +; SIMPL-DAG: .align 8 ; SIMPL-DAG: .type __llvm_jump_instr_table_1_1,@function ; SIMPL-DAG: __llvm_jump_instr_table_1_1: ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT ; SIMPL-DAG: .globl __llvm_jump_instr_table_3_1 -; SIMPL-DAG: .align 8, 0x90 +; SIMPL-DAG: .align 8 ; SIMPL-DAG: .type __llvm_jump_instr_table_3_1,@function ; SIMPL-DAG: __llvm_jump_instr_table_3_1: ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT ; SIMPL-DAG: .globl __llvm_jump_instr_table_4_1 -; SIMPL-DAG: .align 8, 0x90 +; SIMPL-DAG: .align 8 ; SIMPL-DAG: .type __llvm_jump_instr_table_4_1,@function ; SIMPL-DAG: __llvm_jump_instr_table_4_1: ; SIMPL-DAG: jmp indirect_fun{{.*}}@PLT ; FULL-DAG: .globl __llvm_jump_instr_table_10_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_10_1,@function ; FULL-DAG:__llvm_jump_instr_table_10_1: ; FULL-DAG: jmp indirect_fun_i32_1@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_9_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_9_1,@function ; FULL-DAG:__llvm_jump_instr_table_9_1: ; FULL-DAG: jmp indirect_fun_i32_2@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_7_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_7_1,@function ; FULL-DAG:__llvm_jump_instr_table_7_1: ; FULL-DAG: jmp indirect_fun_i32S_2@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_3_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_3_1,@function ; FULL-DAG:__llvm_jump_instr_table_3_1: ; FULL-DAG: jmp indirect_fun_vec_2@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_2_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_2_1,@function ; FULL-DAG:__llvm_jump_instr_table_2_1: ; FULL-DAG: jmp indirect_fun@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_8_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_8_1,@function ; FULL-DAG:__llvm_jump_instr_table_8_1: ; FULL-DAG: jmp indirect_fun_i32@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_1_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_1_1,@function ; FULL-DAG:__llvm_jump_instr_table_1_1: ; FULL-DAG: jmp indirect_fun_array@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_0_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_0_1,@function ; FULL-DAG:__llvm_jump_instr_table_0_1: ; FULL-DAG: jmp indirect_fun_vec@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_6_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_6_1,@function ; FULL-DAG:__llvm_jump_instr_table_6_1: ; FULL-DAG: jmp indirect_fun_struct@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_5_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_5_1,@function ; FULL-DAG:__llvm_jump_instr_table_5_1: ; FULL-DAG: jmp indirect_fun_fun@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2 ; FULL-DAG: .globl __llvm_jump_instr_table_4_1 -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: .type __llvm_jump_instr_table_4_1,@function ; FULL-DAG:__llvm_jump_instr_table_4_1: ; FULL-DAG: jmp indirect_fun_fun_ret@PLT -; FULL-DAG: .align 8, 0x90 +; FULL-DAG: .align 8 ; FULL-DAG: ud2