mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-28 04:33:05 +00:00
Remove the Forward Control Flow Integrity pass and its dependencies.
This work is currently being rethought along different lines and if this work is needed it can be resurrected out of svn. Remove it for now as no current work in ongoing on it and it's unused. Verified with the authors before removal. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@230780 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
b4d6a560c1
commit
930da21265
@ -159,10 +159,6 @@ namespace llvm {
|
||||
//
|
||||
FunctionPass *createMemDepPrinter();
|
||||
|
||||
// createJumpInstrTableInfoPass - This creates a pass that stores information
|
||||
// about the jump tables created by JumpInstrTables
|
||||
ImmutablePass *createJumpInstrTableInfoPass();
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
//
|
||||
// createMemDerefPrinter - This pass collects memory dereferenceability
|
||||
|
@ -1,122 +0,0 @@
|
||||
//===-- 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/SmallVector.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
#include <string>
|
||||
|
||||
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 CFIType,
|
||||
bool CFIEnforcing, std::string CFIFuncName);
|
||||
~ForwardControlFlowIntegrity() override;
|
||||
|
||||
/// 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 "Forward Control-Flow Integrity";
|
||||
}
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
||||
|
||||
private:
|
||||
typedef SmallVector<Instruction *, 64> CallSet;
|
||||
|
||||
/// A structure that is used to keep track of constant table information.
|
||||
struct CFIConstants {
|
||||
Constant *StartValue;
|
||||
Constant *MaskValue;
|
||||
Constant *Size;
|
||||
};
|
||||
|
||||
/// A map from function type to the base of the table for this type and a mask
|
||||
/// for the table
|
||||
typedef DenseMap<FunctionType *, CFIConstants> CFITables;
|
||||
|
||||
CallSet IndirectCalls;
|
||||
|
||||
/// The type of jumptable implementation.
|
||||
JumpTable::JumpTableType JTType;
|
||||
|
||||
/// The type of CFI check to add before each indirect call.
|
||||
CFIntegrity 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;
|
||||
|
||||
/// The alignment of each entry in the table, from JumpInstrTableInfo. The
|
||||
/// JumpInstrTableInfo class always makes this a power of two.
|
||||
uint64_t ByteAlignment;
|
||||
|
||||
/// The base-2 logarithm of ByteAlignment, needed for some of the transforms
|
||||
/// (like CFIntegrity::Ror)
|
||||
unsigned LogByteAlignment;
|
||||
|
||||
/// 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 CFIType,
|
||||
bool CFIEnforcing, StringRef CFIFuncName);
|
||||
}
|
||||
|
||||
#endif // LLVM_CODEGEN_FORWARDCONTROLFLOWINTEGRITY_H
|
@ -1,105 +0,0 @@
|
||||
//===-- JumpInstrTables.h: Jump-Instruction Tables --------------*- C++ -*-===//
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief An implementation of tables consisting of jump instructions
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CODEGEN_JUMPINSTRTABLES_H
|
||||
#define LLVM_CODEGEN_JUMPINSTRTABLES_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
|
||||
namespace llvm {
|
||||
class Constant;
|
||||
class Function;
|
||||
class FunctionType;
|
||||
class JumpInstrTableInfo;
|
||||
class Module;
|
||||
|
||||
/// A class to manage a set of jump tables indexed on function type. It looks at
|
||||
/// each function in the module to find all the functions that have the
|
||||
/// jumptable attribute set. For each such function, it creates a new
|
||||
/// jump-instruction-table function and stores the mapping in the ImmutablePass
|
||||
/// JumpInstrTableInfo.
|
||||
///
|
||||
/// These special functions get lowered in AsmPrinter to assembly of the form:
|
||||
/// \verbatim
|
||||
/// .globl f
|
||||
/// .type f,@function
|
||||
/// .align 8,0x90
|
||||
/// f:
|
||||
/// jmp f_orig@PLT
|
||||
/// \endverbatim
|
||||
///
|
||||
/// Support for an architecture depends on three functions in TargetInstrInfo:
|
||||
/// getUnconditionalBranch, getTrap, and getJumpInstrTableEntryBound. AsmPrinter
|
||||
/// uses these to generate the appropriate instructions for the jump statement
|
||||
/// (an unconditional branch) and for padding to make the table have a size that
|
||||
/// is a power of two. This padding uses a trap instruction to ensure that calls
|
||||
/// to this area halt the program. The default implementations of these
|
||||
/// functions call llvm_unreachable, except for getJumpInstrTableEntryBound,
|
||||
/// which returns 0 by default.
|
||||
class JumpInstrTables : public ModulePass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
JumpInstrTables();
|
||||
JumpInstrTables(JumpTable::JumpTableType JTT);
|
||||
virtual ~JumpInstrTables();
|
||||
bool runOnModule(Module &M) override;
|
||||
const char *getPassName() const override { return "Jump-Instruction Tables"; }
|
||||
void getAnalysisUsage(AnalysisUsage &AU) const override;
|
||||
|
||||
/// Creates a jump-instruction table function for the Target and adds it to
|
||||
/// the tables.
|
||||
Function *insertEntry(Module &M, Function *Target);
|
||||
|
||||
/// 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 {
|
||||
/// The number of this table
|
||||
unsigned TableNum;
|
||||
|
||||
/// The current number of jump entries in the table.
|
||||
unsigned Count;
|
||||
};
|
||||
|
||||
typedef DenseMap<FunctionType *, struct TableMeta> JumpMap;
|
||||
|
||||
/// The current state of functions and jump entries in the table(s).
|
||||
JumpMap Metadata;
|
||||
|
||||
/// The ImmutablePass that stores information about the generated tables.
|
||||
JumpInstrTableInfo *JITI;
|
||||
|
||||
/// The total number of tables.
|
||||
unsigned TableCount;
|
||||
|
||||
/// The type of tables to build.
|
||||
JumpTable::JumpTableType JTType;
|
||||
};
|
||||
|
||||
/// Creates a JumpInstrTables pass for the given type of jump table.
|
||||
ModulePass *createJumpInstrTablesPass(JumpTable::JumpTableType JTT);
|
||||
}
|
||||
|
||||
#endif /* LLVM_CODEGEN_JUMPINSTRTABLES_H */
|
@ -150,8 +150,6 @@ void initializeInstCountPass(PassRegistry&);
|
||||
void initializeInstNamerPass(PassRegistry&);
|
||||
void initializeInternalizePassPass(PassRegistry&);
|
||||
void initializeIntervalPartitionPass(PassRegistry&);
|
||||
void initializeJumpInstrTableInfoPass(PassRegistry&);
|
||||
void initializeJumpInstrTablesPass(PassRegistry&);
|
||||
void initializeJumpThreadingPass(PassRegistry&);
|
||||
void initializeLCSSAPass(PassRegistry&);
|
||||
void initializeLICMPass(PassRegistry&);
|
||||
|
@ -91,8 +91,6 @@ namespace {
|
||||
(void) llvm::createIndVarSimplifyPass();
|
||||
(void) llvm::createInstructionCombiningPass();
|
||||
(void) llvm::createInternalizePass();
|
||||
(void) llvm::createJumpInstrTableInfoPass();
|
||||
(void) llvm::createJumpInstrTablesPass();
|
||||
(void) llvm::createLCSSAPass();
|
||||
(void) llvm::createLICMPass();
|
||||
(void) llvm::createLazyValueInfoPass();
|
||||
|
@ -49,7 +49,6 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
|
||||
initializeIVUsersPass(Registry);
|
||||
initializeInstCountPass(Registry);
|
||||
initializeIntervalPartitionPass(Registry);
|
||||
initializeJumpInstrTableInfoPass(Registry);
|
||||
initializeLazyValueInfoPass(Registry);
|
||||
initializeLibCallAliasAnalysisPass(Registry);
|
||||
initializeLintPass(Registry);
|
||||
|
@ -27,7 +27,6 @@ add_llvm_library(LLVMAnalysis
|
||||
InstructionSimplify.cpp
|
||||
Interval.cpp
|
||||
IntervalPartition.cpp
|
||||
JumpInstrTableInfo.cpp
|
||||
LazyCallGraph.cpp
|
||||
LazyValueInfo.cpp
|
||||
LibCallAliasAnalysis.cpp
|
||||
|
@ -1,55 +0,0 @@
|
||||
//===-- JumpInstrTableInfo.cpp: Info for Jump-Instruction Tables ----------===//
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Information about jump-instruction tables that have been created by
|
||||
/// JumpInstrTables pass.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DEBUG_TYPE "jiti"
|
||||
|
||||
#include "llvm/Analysis/JumpInstrTableInfo.h"
|
||||
#include "llvm/Analysis/Passes.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
INITIALIZE_PASS(JumpInstrTableInfo, "jump-instr-table-info",
|
||||
"Jump-Instruction Table Info", true, true)
|
||||
char JumpInstrTableInfo::ID = 0;
|
||||
|
||||
ImmutablePass *llvm::createJumpInstrTableInfoPass() {
|
||||
return new JumpInstrTableInfo();
|
||||
}
|
||||
|
||||
ModulePass *llvm::createJumpInstrTableInfoPass(unsigned Bound) {
|
||||
// This cast is always safe, since Bound is always in a subset of uint64_t.
|
||||
uint64_t B = static_cast<uint64_t>(Bound);
|
||||
return new JumpInstrTableInfo(B);
|
||||
}
|
||||
|
||||
JumpInstrTableInfo::JumpInstrTableInfo(uint64_t ByteAlign)
|
||||
: ImmutablePass(ID), Tables(), ByteAlignment(ByteAlign) {
|
||||
if (!llvm::isPowerOf2_64(ByteAlign)) {
|
||||
// Note that we don't explicitly handle overflow here, since we handle the 0
|
||||
// case explicitly when a caller actually tries to create jumptable entries,
|
||||
// and this is the return value on overflow.
|
||||
ByteAlignment = llvm::NextPowerOf2(ByteAlign);
|
||||
}
|
||||
|
||||
initializeJumpInstrTableInfoPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
JumpInstrTableInfo::~JumpInstrTableInfo() {}
|
||||
|
||||
void JumpInstrTableInfo::insertEntry(FunctionType *TableFunTy, Function *Target,
|
||||
Function *Jump) {
|
||||
Tables[TableFunTy].push_back(JumpPair(Target, Jump));
|
||||
}
|
@ -1018,59 +1018,6 @@ bool AsmPrinter::doFinalization(Module &M) {
|
||||
EmitVisibility(Name, V, false);
|
||||
}
|
||||
|
||||
// Get information about jump-instruction tables to print.
|
||||
JumpInstrTableInfo *JITI = getAnalysisIfAvailable<JumpInstrTableInfo>();
|
||||
|
||||
if (JITI && !JITI->getTables().empty()) {
|
||||
// Since we're at the module level we can't use a function specific
|
||||
// MCSubtargetInfo - instead create one with the module defaults.
|
||||
std::unique_ptr<MCSubtargetInfo> STI(TM.getTarget().createMCSubtargetInfo(
|
||||
TM.getTargetTriple(), TM.getTargetCPU(), TM.getTargetFeatureString()));
|
||||
unsigned Arch = Triple(getTargetTriple()).getArch();
|
||||
bool IsThumb = (Arch == Triple::thumb || Arch == Triple::thumbeb);
|
||||
const TargetInstrInfo *TII = TM.getSubtargetImpl()->getInstrInfo();
|
||||
MCInst TrapInst;
|
||||
TII->getTrap(TrapInst);
|
||||
unsigned LogAlignment = llvm::Log2_64(JITI->entryByteAlignment());
|
||||
|
||||
// Emit the right section for these functions.
|
||||
OutStreamer.SwitchSection(OutContext.getObjectFileInfo()->getTextSection());
|
||||
for (const auto &KV : JITI->getTables()) {
|
||||
uint64_t Count = 0;
|
||||
for (const auto &FunPair : KV.second) {
|
||||
// Emit the function labels to make this be a function entry point.
|
||||
MCSymbol *FunSym =
|
||||
OutContext.GetOrCreateSymbol(FunPair.second->getName());
|
||||
EmitAlignment(LogAlignment);
|
||||
if (IsThumb)
|
||||
OutStreamer.EmitThumbFunc(FunSym);
|
||||
if (MAI->hasDotTypeDotSizeDirective())
|
||||
OutStreamer.EmitSymbolAttribute(FunSym, MCSA_ELF_TypeFunction);
|
||||
OutStreamer.EmitLabel(FunSym);
|
||||
|
||||
// Emit the jump instruction to transfer control to the original
|
||||
// function.
|
||||
MCInst JumpToFun;
|
||||
MCSymbol *TargetSymbol =
|
||||
OutContext.GetOrCreateSymbol(FunPair.first->getName());
|
||||
const MCSymbolRefExpr *TargetSymRef =
|
||||
MCSymbolRefExpr::Create(TargetSymbol, MCSymbolRefExpr::VK_PLT,
|
||||
OutContext);
|
||||
TII->getUnconditionalBranch(JumpToFun, TargetSymRef);
|
||||
OutStreamer.EmitInstruction(JumpToFun, *STI);
|
||||
++Count;
|
||||
}
|
||||
|
||||
// Emit enough padding instructions to fill up to the next power of two.
|
||||
uint64_t Remaining = NextPowerOf2(Count) - Count;
|
||||
for (uint64_t C = 0; C < Remaining; ++C) {
|
||||
EmitAlignment(LogAlignment);
|
||||
OutStreamer.EmitInstruction(TrapInst, *STI);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Emit module flags.
|
||||
SmallVector<Module::ModuleFlagEntry, 8> ModuleFlags;
|
||||
M.getModuleFlagsMetadata(ModuleFlags);
|
||||
|
@ -19,7 +19,6 @@ add_llvm_library(LLVMCodeGen
|
||||
ExecutionDepsFix.cpp
|
||||
ExpandISelPseudos.cpp
|
||||
ExpandPostRAPseudos.cpp
|
||||
ForwardControlFlowIntegrity.cpp
|
||||
GCMetadata.cpp
|
||||
GCMetadataPrinter.cpp
|
||||
GCRootLowering.cpp
|
||||
@ -29,7 +28,6 @@ add_llvm_library(LLVMCodeGen
|
||||
InlineSpiller.cpp
|
||||
InterferenceCache.cpp
|
||||
IntrinsicLowering.cpp
|
||||
JumpInstrTables.cpp
|
||||
LLVMTargetMachine.cpp
|
||||
LatencyPriorityQueue.cpp
|
||||
LexicalScopes.cpp
|
||||
|
@ -1,374 +0,0 @@
|
||||
//===-- 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/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/IRBuilder.h"
|
||||
#include "llvm/IR/InlineAsm.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Operator.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
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 CFIType, bool CFIEnforcing,
|
||||
StringRef CFIFuncName) {
|
||||
return new ForwardControlFlowIntegrity(JTT, CFIType, CFIEnforcing,
|
||||
CFIFuncName);
|
||||
}
|
||||
|
||||
// Checks to see if a given CallSite is making an indirect call, including
|
||||
// cases where the indirect call is made through a bitcast.
|
||||
static 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<ConstantExpr>(V)) {
|
||||
return !(CE->isCast() && isa<Function>(CE->getOperand(0)));
|
||||
}
|
||||
|
||||
// Otherwise, since we know it's a call, it must be an indirect call
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char cfi_failure_func_name[] = "__llvm_cfi_pointer_warning";
|
||||
|
||||
ForwardControlFlowIntegrity::ForwardControlFlowIntegrity()
|
||||
: ModulePass(ID), IndirectCalls(), JTType(JumpTable::Single),
|
||||
CFIType(CFIntegrity::Sub), CFIEnforcing(false), CFIFuncName("") {
|
||||
initializeForwardControlFlowIntegrityPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
ForwardControlFlowIntegrity::ForwardControlFlowIntegrity(
|
||||
JumpTable::JumpTableType JTT, CFIntegrity 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<JumpInstrTableInfo>();
|
||||
AU.addRequired<JumpInstrTables>();
|
||||
}
|
||||
|
||||
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 (BasicBlock &BB : F) {
|
||||
for (Instruction &I : BB) {
|
||||
CallSite CS(&I);
|
||||
if (!(CS && 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<InlineAsm>(CalledValue))
|
||||
continue;
|
||||
|
||||
IndirectCalls.push_back(&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<PointerType>(VTy);
|
||||
Type *EltTy = PTy->getElementType();
|
||||
FunctionType *FunTy = dyn_cast<FunctionType>(EltTy);
|
||||
FunctionType *TransformedTy = JumpInstrTables::transformType(JTType, FunTy);
|
||||
++NumCFIIndirectCalls;
|
||||
Constant *JumpTableStart = nullptr;
|
||||
Constant *JumpTableMask = nullptr;
|
||||
Constant *JumpTableSize = nullptr;
|
||||
|
||||
// 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) {
|
||||
JumpInstrTableInfo *JITI = &getAnalysis<JumpInstrTableInfo>();
|
||||
Type *Int64Ty = Type::getInt64Ty(M.getContext());
|
||||
Type *VoidPtrTy = Type::getInt8PtrTy(M.getContext());
|
||||
|
||||
// JumpInstrTableInfo stores information about the alignment of each entry.
|
||||
// The alignment returned by JumpInstrTableInfo is alignment in bytes, not
|
||||
// in the exponent.
|
||||
ByteAlignment = JITI->entryByteAlignment();
|
||||
LogByteAlignment = llvm::Log2_64(ByteAlignment);
|
||||
|
||||
// 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<uint64_t>(KV.second.size());
|
||||
uint64_t TableSize = NextPowerOf2(Size);
|
||||
|
||||
int64_t MaskValue = ((TableSize << LogByteAlignment) - 1) & -ByteAlignment;
|
||||
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) {
|
||||
PointerType *CharPtrTy = Type::getInt8PtrTy(M.getContext());
|
||||
|
||||
// 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<Type *, 2> WarningFunArgs;
|
||||
WarningFunArgs.push_back(CharPtrTy);
|
||||
WarningFunArgs.push_back(CharPtrTy);
|
||||
FunctionType *WarningFunTy =
|
||||
FunctionType::get(Type::getVoidTy(M.getContext()), WarningFunArgs, false);
|
||||
|
||||
if (!CFIFuncName.empty()) {
|
||||
Constant *FailureFun = M.getOrInsertFunction(CFIFuncName, WarningFunTy);
|
||||
if (!FailureFun)
|
||||
report_fatal_error("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::LinkOnceAnyLinkage,
|
||||
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<BasicBlock>(I->getParent());
|
||||
Function *CurF = cast<Function>(CurBB->getParent());
|
||||
Type *Int64Ty = Type::getInt64Ty(M.getContext());
|
||||
|
||||
Value *TI = TempBuilder.CreatePtrToInt(FunPtr, Int64Ty);
|
||||
Value *TStartInt = TempBuilder.CreatePtrToInt(JumpTableStart, Int64Ty);
|
||||
|
||||
Value *NewFunPtr = nullptr;
|
||||
Value *Check = nullptr;
|
||||
switch (CFIType) {
|
||||
case 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);
|
||||
break;
|
||||
}
|
||||
case CFIntegrity::Ror: {
|
||||
// This is the subtract and rotate version.
|
||||
// Rotate right by the alignment value. The optimizer should recognize
|
||||
// this sequence as a rotation.
|
||||
|
||||
// This cast is safe, since unsigned is always a subset of uint64_t.
|
||||
uint64_t LogByteAlignment64 = static_cast<uint64_t>(LogByteAlignment);
|
||||
Constant *RightShift = ConstantInt::get(Int64Ty, LogByteAlignment64);
|
||||
Constant *LeftShift = ConstantInt::get(Int64Ty, 64 - LogByteAlignment64);
|
||||
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
case 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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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<Function>(Block->getParent());
|
||||
|
||||
// Get the function to call right before the instruction.
|
||||
Function *WarningFun = nullptr;
|
||||
if (CFIFuncName.empty()) {
|
||||
WarningFun = M.getFunction(cfi_failure_func_name);
|
||||
} else {
|
||||
WarningFun = M.getFunction(CFIFuncName);
|
||||
}
|
||||
|
||||
assert(WarningFun && "Could not find the CFI failure function");
|
||||
|
||||
Type *VoidPtrTy = Type::getInt8PtrTy(M.getContext());
|
||||
|
||||
IRBuilder<> WarningInserter(I);
|
||||
// Create a mergeable GlobalVariable containing the name of the function.
|
||||
Value *ParentNameGV =
|
||||
WarningInserter.CreateGlobalString(ParentFun->getName());
|
||||
Value *ParentNamePtr = WarningInserter.CreateBitCast(ParentNameGV, VoidPtrTy);
|
||||
Value *FunVoidPtr = WarningInserter.CreateBitCast(FunPtr, VoidPtrTy);
|
||||
WarningInserter.CreateCall2(WarningFun, ParentNamePtr, FunVoidPtr);
|
||||
}
|
@ -1,296 +0,0 @@
|
||||
//===-- JumpInstrTables.cpp: Jump-Instruction Tables ----------------------===//
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief An implementation of jump-instruction tables.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DEBUG_TYPE "jt"
|
||||
|
||||
#include "llvm/CodeGen/JumpInstrTables.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Analysis/JumpInstrTableInfo.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/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Operator.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
char JumpInstrTables::ID = 0;
|
||||
|
||||
INITIALIZE_PASS_BEGIN(JumpInstrTables, "jump-instr-tables",
|
||||
"Jump-Instruction Tables", true, true)
|
||||
INITIALIZE_PASS_DEPENDENCY(JumpInstrTableInfo);
|
||||
INITIALIZE_PASS_END(JumpInstrTables, "jump-instr-tables",
|
||||
"Jump-Instruction Tables", true, true)
|
||||
|
||||
STATISTIC(NumJumpTables, "Number of indirect call tables generated");
|
||||
STATISTIC(NumFuncsInJumpTables, "Number of functions in the jump tables");
|
||||
|
||||
ModulePass *llvm::createJumpInstrTablesPass() {
|
||||
// The default implementation uses a single table for all functions.
|
||||
return new JumpInstrTables(JumpTable::Single);
|
||||
}
|
||||
|
||||
ModulePass *llvm::createJumpInstrTablesPass(JumpTable::JumpTableType JTT) {
|
||||
return new JumpInstrTables(JTT);
|
||||
}
|
||||
|
||||
namespace {
|
||||
static const char jump_func_prefix[] = "__llvm_jump_instr_table_";
|
||||
static const char jump_section_prefix[] = ".jump.instr.table.text.";
|
||||
|
||||
// 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<ConstantExpr>(V)) {
|
||||
return !(CE->isCast() && isa<Function>(CE->getOperand(0)));
|
||||
}
|
||||
|
||||
// Otherwise, since we know it's a call, it must be an indirect call
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replaces Functions and GlobalAliases with a different Value.
|
||||
bool replaceGlobalValueIndirectUse(GlobalValue *GV, Value *V, Use *U) {
|
||||
User *Us = U->getUser();
|
||||
if (!Us)
|
||||
return false;
|
||||
if (Instruction *I = dyn_cast<Instruction>(Us)) {
|
||||
CallSite CS(I);
|
||||
|
||||
// Don't do the replacement if this use is a direct call to this function.
|
||||
// If the use is not the called value, then replace it.
|
||||
if (CS && (isIndirectCall(CS) || CS.isCallee(U))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
U->set(V);
|
||||
} else if (Constant *C = dyn_cast<Constant>(Us)) {
|
||||
// Don't replace calls to bitcasts of function symbols, since they get
|
||||
// translated to direct calls.
|
||||
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(Us)) {
|
||||
if (CE->getOpcode() == Instruction::BitCast) {
|
||||
// This bitcast must have exactly one user.
|
||||
if (CE->user_begin() != CE->user_end()) {
|
||||
User *ParentUs = *CE->user_begin();
|
||||
if (CallInst *CI = dyn_cast<CallInst>(ParentUs)) {
|
||||
CallSite CS(CI);
|
||||
Use &CEU = *CE->use_begin();
|
||||
if (CS.isCallee(&CEU)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GlobalAlias doesn't support replaceUsesOfWithOnConstant. And the verifier
|
||||
// requires alias to point to a defined function. So, GlobalAlias is handled
|
||||
// as a separate case in runOnModule.
|
||||
if (!isa<GlobalAlias>(C))
|
||||
C->replaceUsesOfWithOnConstant(GV, V, U);
|
||||
} else {
|
||||
llvm_unreachable("The Use of a Function symbol is neither an instruction "
|
||||
"nor a constant");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replaces all replaceable address-taken uses of GV with a pointer to a
|
||||
// jump-instruction table entry.
|
||||
void replaceValueWithFunction(GlobalValue *GV, Function *F) {
|
||||
// Go through all uses of this function and replace the uses of GV with the
|
||||
// jump-table version of the function. Get the uses as a vector before
|
||||
// replacing them, since replacing them changes the use list and invalidates
|
||||
// the iterator otherwise.
|
||||
for (Value::use_iterator I = GV->use_begin(), E = GV->use_end(); I != E;) {
|
||||
Use &U = *I++;
|
||||
|
||||
// Replacement of constants replaces all instances in the constant. So, some
|
||||
// uses might have already been handled by the time we reach them here.
|
||||
if (U.get() == GV)
|
||||
replaceGlobalValueIndirectUse(GV, F, &U);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
JumpInstrTables::JumpInstrTables()
|
||||
: ModulePass(ID), Metadata(), JITI(nullptr), TableCount(0),
|
||||
JTType(JumpTable::Single) {
|
||||
initializeJumpInstrTablesPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
JumpInstrTables::JumpInstrTables(JumpTable::JumpTableType JTT)
|
||||
: ModulePass(ID), Metadata(), JITI(nullptr), TableCount(0), JTType(JTT) {
|
||||
initializeJumpInstrTablesPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
JumpInstrTables::~JumpInstrTables() {}
|
||||
|
||||
void JumpInstrTables::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<JumpInstrTableInfo>();
|
||||
}
|
||||
|
||||
Function *JumpInstrTables::insertEntry(Module &M, Function *Target) {
|
||||
FunctionType *OrigFunTy = Target->getFunctionType();
|
||||
FunctionType *FunTy = transformType(JTType, OrigFunTy);
|
||||
|
||||
JumpMap::iterator it = Metadata.find(FunTy);
|
||||
if (Metadata.end() == it) {
|
||||
struct TableMeta Meta;
|
||||
Meta.TableNum = TableCount;
|
||||
Meta.Count = 0;
|
||||
Metadata[FunTy] = Meta;
|
||||
it = Metadata.find(FunTy);
|
||||
++NumJumpTables;
|
||||
++TableCount;
|
||||
}
|
||||
|
||||
it->second.Count++;
|
||||
|
||||
std::string NewName(jump_func_prefix);
|
||||
NewName += (Twine(it->second.TableNum) + "_" + Twine(it->second.Count)).str();
|
||||
Function *JumpFun =
|
||||
Function::Create(OrigFunTy, GlobalValue::ExternalLinkage, NewName, &M);
|
||||
// The section for this table
|
||||
JumpFun->setSection((jump_section_prefix + Twine(it->second.TableNum)).str());
|
||||
JITI->insertEntry(FunTy, Target, JumpFun);
|
||||
|
||||
++NumFuncsInJumpTables;
|
||||
return JumpFun;
|
||||
}
|
||||
|
||||
bool JumpInstrTables::hasTable(FunctionType *FunTy) {
|
||||
FunctionType *TransTy = transformType(JTType, FunTy);
|
||||
return Metadata.end() != Metadata.find(TransTy);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
// Ignore the return type.
|
||||
Type *RetTy = VoidPtrTy;
|
||||
bool IsVarArg = FunTy->isVarArg();
|
||||
std::vector<Type *> ParamTys(FunTy->getNumParams());
|
||||
FunctionType::param_iterator PI, PE;
|
||||
int i = 0;
|
||||
|
||||
std::vector<Type *> EmptyParams;
|
||||
Type *Int32Ty = Type::getInt32Ty(FunTy->getContext());
|
||||
FunctionType *VoidFnTy = FunctionType::get(
|
||||
Type::getVoidTy(FunTy->getContext()), EmptyParams, false);
|
||||
switch (JTT) {
|
||||
case JumpTable::Single:
|
||||
|
||||
return FunctionType::get(RetTy, EmptyParams, false);
|
||||
case JumpTable::Arity:
|
||||
// Transform all types to void* so that all functions with the same arity
|
||||
// end up in the same table.
|
||||
for (PI = FunTy->param_begin(), PE = FunTy->param_end(); PI != PE;
|
||||
PI++, i++) {
|
||||
ParamTys[i] = VoidPtrTy;
|
||||
}
|
||||
|
||||
return FunctionType::get(RetTy, ParamTys, IsVarArg);
|
||||
case JumpTable::Simplified:
|
||||
// Project all parameters types to one of 3 types: composite, integer, and
|
||||
// function, matching the three subclasses of Type.
|
||||
for (PI = FunTy->param_begin(), PE = FunTy->param_end(); PI != PE;
|
||||
++PI, ++i) {
|
||||
assert((isa<IntegerType>(*PI) || isa<FunctionType>(*PI) ||
|
||||
isa<CompositeType>(*PI)) &&
|
||||
"This type is not an Integer or a Composite or a Function");
|
||||
if (isa<CompositeType>(*PI)) {
|
||||
ParamTys[i] = VoidPtrTy;
|
||||
} else if (isa<FunctionType>(*PI)) {
|
||||
ParamTys[i] = VoidFnTy;
|
||||
} else if (isa<IntegerType>(*PI)) {
|
||||
ParamTys[i] = Int32Ty;
|
||||
}
|
||||
}
|
||||
|
||||
return FunctionType::get(RetTy, ParamTys, IsVarArg);
|
||||
case JumpTable::Full:
|
||||
// Don't transform this type at all.
|
||||
return FunTy;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool JumpInstrTables::runOnModule(Module &M) {
|
||||
JITI = &getAnalysis<JumpInstrTableInfo>();
|
||||
|
||||
// Get the set of jumptable-annotated functions that have their address taken.
|
||||
DenseMap<Function *, Function *> Functions;
|
||||
for (Function &F : M) {
|
||||
if (F.hasFnAttribute(Attribute::JumpTable) && F.hasAddressTaken()) {
|
||||
assert(F.hasUnnamedAddr() &&
|
||||
"Attribute 'jumptable' requires 'unnamed_addr'");
|
||||
Functions[&F] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the jump-table functions.
|
||||
for (auto &KV : Functions) {
|
||||
Function *F = KV.first;
|
||||
KV.second = insertEntry(M, F);
|
||||
}
|
||||
|
||||
// GlobalAlias is a special case, because the target of an alias statement
|
||||
// must be a defined function. So, instead of replacing a given function in
|
||||
// the alias, we replace all uses of aliases that target jumptable functions.
|
||||
// Note that there's no need to create these functions, since only aliases
|
||||
// that target known jumptable functions are replaced, and there's no way to
|
||||
// put the jumptable annotation on a global alias.
|
||||
DenseMap<GlobalAlias *, Function *> Aliases;
|
||||
for (GlobalAlias &GA : M.aliases()) {
|
||||
Constant *Aliasee = GA.getAliasee();
|
||||
if (Function *F = dyn_cast<Function>(Aliasee)) {
|
||||
auto it = Functions.find(F);
|
||||
if (it != Functions.end()) {
|
||||
Aliases[&GA] = it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace each address taken function with its jump-instruction table entry.
|
||||
for (auto &KV : Functions)
|
||||
replaceValueWithFunction(KV.first, KV.second);
|
||||
|
||||
for (auto &KV : Aliases)
|
||||
replaceValueWithFunction(KV.first, KV.second);
|
||||
|
||||
return !Functions.empty();
|
||||
}
|
@ -12,12 +12,9 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
#include "llvm/Analysis/JumpInstrTableInfo.h"
|
||||
#include "llvm/Analysis/Passes.h"
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
#include "llvm/CodeGen/BasicTTIImpl.h"
|
||||
#include "llvm/CodeGen/ForwardControlFlowIntegrity.h"
|
||||
#include "llvm/CodeGen/JumpInstrTables.h"
|
||||
#include "llvm/CodeGen/MachineFunctionAnalysis.h"
|
||||
#include "llvm/CodeGen/MachineModuleInfo.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
@ -145,16 +142,6 @@ bool LLVMTargetMachine::addPassesToEmitFile(PassManagerBase &PM,
|
||||
bool DisableVerify,
|
||||
AnalysisID StartAfter,
|
||||
AnalysisID StopAfter) {
|
||||
// Passes to handle jumptable function annotations. These can't be handled at
|
||||
// JIT time, so we don't add them directly to addPassesToGenerateCode.
|
||||
PM.add(createJumpInstrTableInfoPass(
|
||||
getSubtargetImpl()->getInstrInfo()->getJumpInstrTableEntryBound()));
|
||||
PM.add(createJumpInstrTablesPass(Options.JTType));
|
||||
if (Options.FCFI)
|
||||
PM.add(createForwardControlFlowIntegrityPass(
|
||||
Options.JTType, Options.CFIType, Options.CFIEnforcing,
|
||||
Options.getCFIFuncName()));
|
||||
|
||||
// Add common CodeGen passes.
|
||||
MCContext *Context = addPassesToGenerateCode(this, PM, DisableVerify,
|
||||
StartAfter, StopAfter);
|
||||
|
@ -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 -gc-lowering
|
||||
; START: FunctionPass Manager
|
||||
; START-NEXT: Lower Garbage Collection Instructions
|
||||
|
@ -1,34 +0,0 @@
|
||||
; RUN: llc -mtriple=i386-unknown-linux-gnu -fcfi -cfi-enforcing <%s | FileCheck --check-prefix=X86 %s
|
||||
; RUN: llc -mtriple=x86_64-unknown-linux-gnu -fcfi -cfi-enforcing <%s | FileCheck --check-prefix=X86-64 %s
|
||||
|
||||
define void @indirect_fun() unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @m(void ()* %fun) {
|
||||
call void ()* %fun()
|
||||
; CHECK: subl
|
||||
; X86-64: andq $8,
|
||||
; X86-64: leaq __llvm_jump_instr_table_0_1({{%[a-z0-9]+}}), [[REG:%[a-z0-9]+]]
|
||||
; X86-64-NOT: callq __llvm_cfi_pointer_warning
|
||||
; X86-64: callq *[[REG]]
|
||||
; X86: andl $8,
|
||||
; X86: leal __llvm_jump_instr_table_0_1({{%[a-z0-9]+}}), [[REG:%[a-z0-9]+]]
|
||||
; X86-NOT: calll __llvm_cfi_pointer_warning
|
||||
; X86: calll *[[REG]]
|
||||
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
|
||||
}
|
||||
|
||||
; CHECK: .align 8
|
||||
; CHECK: __llvm_jump_instr_table_0_1:
|
||||
; CHECK: jmp indirect_fun@PLT
|
@ -1,35 +0,0 @@
|
||||
; RUN: llc <%s -fcfi -cfi-type=sub | FileCheck %s
|
||||
; ModuleID = 'test.cc'
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
declare i32 @__gxx_personality_v0(...)
|
||||
|
||||
@_ZTIPKc = external constant i8*
|
||||
@_ZTIi = external constant i8*
|
||||
|
||||
define void @f() unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
@a = global void ()* @f
|
||||
|
||||
; Make sure invoke gets targeted as well as regular calls
|
||||
define void @_Z3foov(void ()* %f) uwtable ssp {
|
||||
; CHECK-LABEL: _Z3foov:
|
||||
entry:
|
||||
invoke void %f()
|
||||
to label %try.cont unwind label %lpad
|
||||
; CHECK: callq __llvm_cfi_pointer_warning
|
||||
; CHECK: callq *%rbx
|
||||
|
||||
lpad:
|
||||
%0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)
|
||||
catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
filter [1 x i8*] [i8* bitcast (i8** @_ZTIPKc to i8*)]
|
||||
ret void
|
||||
|
||||
try.cont:
|
||||
ret void
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
; RUN: llc -fcfi -cfi-func-name=cfi_new_failure <%s | FileCheck %s
|
||||
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
define void @indirect_fun() unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @m(void ()* %fun) {
|
||||
; CHECK-LABEL: @m
|
||||
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
|
||||
}
|
||||
|
||||
; CHECK: .align 8
|
||||
; CHECK: __llvm_jump_instr_table_0_1:
|
||||
; CHECK: jmp indirect_fun@PLT
|
@ -1,43 +0,0 @@
|
||||
; RUN: llc -fcfi -cfi-type=sub <%s | FileCheck --check-prefix=SUB %s
|
||||
; RUN: llc -fcfi -cfi-type=add <%s | FileCheck --check-prefix=ADD %s
|
||||
; RUN: llc -fcfi -cfi-type=ror <%s | FileCheck --check-prefix=ROR %s
|
||||
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define void @indirect_fun() unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @m(void ()* %fun) {
|
||||
call void ()* %fun()
|
||||
; SUB: subl
|
||||
; SUB: andq $8
|
||||
; SUB-LABEL: leaq __llvm_jump_instr_table_0_1
|
||||
; SUB-LABEL: callq __llvm_cfi_pointer_warning
|
||||
|
||||
; ROR: subq
|
||||
; ROR: rolq $61
|
||||
; ROR: testq
|
||||
; ROR-LABEL: callq __llvm_cfi_pointer_warning
|
||||
|
||||
; ADD: andq $8
|
||||
; ADD-LABEL: leaq __llvm_jump_instr_table_0_1
|
||||
; ADD: cmpq
|
||||
; ADD-LABEL: 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
|
||||
}
|
||||
; SUB: .text
|
||||
; SUB: .align 8
|
||||
; SUB-LABEL: .type __llvm_jump_instr_table_0_1,@function
|
||||
; SUB-LABEL:__llvm_jump_instr_table_0_1:
|
||||
; SUB-LABEL: jmp indirect_fun@PLT
|
@ -1,32 +0,0 @@
|
||||
; RUN: llc <%s -jump-table-type=single | FileCheck %s
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
define i32 @f() unnamed_addr jumptable {
|
||||
entry:
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
@i = internal alias i32 ()* @f
|
||||
@j = alias i32 ()* @f
|
||||
|
||||
define i32 @main(i32 %argc, i8** %argv) {
|
||||
%temp = alloca i32 ()*, align 8
|
||||
store i32 ()* @i, i32()** %temp, align 8
|
||||
; CHECK: movq $__llvm_jump_instr_table_0_1
|
||||
%1 = load i32 ()** %temp, align 8
|
||||
; CHECK: movl $__llvm_jump_instr_table_0_1
|
||||
%2 = call i32 ()* %1()
|
||||
%3 = call i32 ()* @i()
|
||||
; CHECK: callq i
|
||||
%4 = call i32 ()* @j()
|
||||
; CHECK: callq j
|
||||
ret i32 %3
|
||||
}
|
||||
|
||||
; There should only be one table, even though there are two GlobalAliases,
|
||||
; because they both alias the same value.
|
||||
|
||||
; CHECK: .align 8, 0x90
|
||||
; CHECK: .type __llvm_jump_instr_table_0_1,@function
|
||||
; CHECK: __llvm_jump_instr_table_0_1:
|
||||
; CHECK: jmp f@PLT
|
||||
|
@ -1,29 +0,0 @@
|
||||
; RUN: llc -filetype=obj <%s -jump-table-type=single -o %t1
|
||||
; RUN: llvm-objdump -triple=x86_64-unknown-linux-gnu -d %t1 | FileCheck %s
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
define i32 @f() unnamed_addr jumptable {
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define i32 @g(i8* %a) unnamed_addr jumptable {
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define void @h(void ()* %func) unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @main() {
|
||||
%g = alloca i32 (...)*, align 8
|
||||
store i32 (...)* bitcast (i32 ()* @f to i32 (...)*), i32 (...)** %g, align 8
|
||||
%1 = load i32 (...)** %g, align 8
|
||||
%call = call i32 (...)* %1()
|
||||
call void (void ()*)* @h(void ()* bitcast (void (void ()*)* @h to void ()*))
|
||||
%a = call i32 (i32*)* bitcast (i32 (i8*)* @g to i32(i32*)*)(i32* null)
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
; Make sure that the padding from getJumpInstrTableEntryBound is right.
|
||||
; CHECK: __llvm_jump_instr_table_0_1:
|
||||
; CHECK-NEXT: e9 00 00 00 00 jmp 0
|
||||
; CHECK-NEXT: 0f 1f 00 nopl (%rax)
|
@ -1,43 +0,0 @@
|
||||
; RUN: llc <%s -jump-table-type=single | FileCheck %s
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
define i32 @f() unnamed_addr jumptable {
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define i32 @g(i8* %a) unnamed_addr jumptable {
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define void @h(void ()* %func) unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
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]],
|
||||
; 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}},
|
||||
; CHECK: callq h
|
||||
|
||||
%a = call i32 (i32*)* bitcast (i32 (i8*)* @g to i32(i32*)*)(i32* null)
|
||||
; CHECK: callq g
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
; CHECK: .align 8, 0x90
|
||||
; CHECK: .type __llvm_jump_instr_table_0_1,@function
|
||||
; CHECK: __llvm_jump_instr_table_0_1:
|
||||
; CHECK: jmp {{f|g|h}}@PLT
|
||||
; CHECK: .align 8, 0x90
|
||||
; CHECK: .type __llvm_jump_instr_table_0_2,@function
|
||||
; CHECK: __llvm_jump_instr_table_0_2:
|
||||
; CHECK: jmp {{f|g|h}}@PLT
|
||||
; CHECK: .align 8, 0x90
|
||||
; CHECK: .type __llvm_jump_instr_table_0_3,@function
|
||||
; CHECK: __llvm_jump_instr_table_0_3:
|
||||
; CHECK: jmp {{f|g|h}}@PLT
|
||||
|
@ -1,255 +0,0 @@
|
||||
; RUN: llc <%s -jump-table-type=single | FileCheck --check-prefix=SINGLE %s
|
||||
; RUN: llc <%s -jump-table-type=arity | FileCheck --check-prefix=ARITY %s
|
||||
; RUN: llc <%s -jump-table-type=simplified | FileCheck --check-prefix=SIMPL %s
|
||||
; RUN: llc <%s -jump-table-type=full | FileCheck --check-prefix=FULL %s
|
||||
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
%struct.fun_struct = type { i32 (...)* }
|
||||
|
||||
@a = global [12 x i32 () *] [ i32 ()* bitcast (void ()* @indirect_fun to i32 ()*),
|
||||
i32 ()* bitcast (void ()* @indirect_fun_match to i32 ()*),
|
||||
i32 ()* bitcast (i32 ()* @indirect_fun_i32 to i32 ()*),
|
||||
i32 ()* bitcast (i32 (i32)* @indirect_fun_i32_1 to i32 ()*),
|
||||
i32 ()* bitcast (i32 (i32, i32)* @indirect_fun_i32_2 to i32 ()*),
|
||||
i32 ()* bitcast (i32* (i32*, i32)* @indirect_fun_i32S_2 to i32 ()*),
|
||||
i32 ()* bitcast (void (%struct.fun_struct)* @indirect_fun_struct to i32 ()*),
|
||||
i32 ()* bitcast (void (i32 (...)*, i32)* @indirect_fun_fun to i32 ()*),
|
||||
i32 ()* bitcast (i32 (i32 (...)*, i32)* @indirect_fun_fun_ret to i32 ()*),
|
||||
i32 ()* bitcast (void ([19 x i8])* @indirect_fun_array to i32 ()*),
|
||||
i32 ()* bitcast (void (<3 x i32>)* @indirect_fun_vec to i32 ()*),
|
||||
i32 ()* bitcast (void (<4 x float>)* @indirect_fun_vec_2 to i32 ()*)
|
||||
]
|
||||
|
||||
define void @indirect_fun() unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @indirect_fun_match() unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @indirect_fun_i32() unnamed_addr jumptable {
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define i32 @indirect_fun_i32_1(i32 %a) unnamed_addr jumptable {
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
define i32 @indirect_fun_i32_2(i32 %a, i32 %b) unnamed_addr jumptable {
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
define i32* @indirect_fun_i32S_2(i32* %a, i32 %b) unnamed_addr jumptable {
|
||||
ret i32* %a
|
||||
}
|
||||
|
||||
define void @indirect_fun_struct(%struct.fun_struct %fs) unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @indirect_fun_fun(i32 (...)* %fun, i32 %a) unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @indirect_fun_fun_ret(i32 (...)* %fun, i32 %a) unnamed_addr jumptable {
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
define void @indirect_fun_array([19 x i8] %a) unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @indirect_fun_vec(<3 x i32> %a) unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @indirect_fun_vec_2(<4 x float> %a) unnamed_addr jumptable {
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @m(void ()* %fun) {
|
||||
call void ()* %fun()
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define void ()* @get_fun() {
|
||||
ret void ()* @indirect_fun
|
||||
; SINGLE: movl $__llvm_jump_instr_table_0_
|
||||
; ARITY: movl $__llvm_jump_instr_table_
|
||||
; SIMPL: movl $__llvm_jump_instr_table_
|
||||
; FULL: movl $__llvm_jump_instr_table_
|
||||
}
|
||||
|
||||
define i32 @main(i32 %argc, i8** %argv) {
|
||||
%f = call void ()* ()* @get_fun()
|
||||
%a = call i32 @m(void ()* %f)
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
; SINGLE-DAG: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; SINGLE-DAG: .align 8, 0x90
|
||||
; SINGLE-DAG: ud2
|
||||
; SINGLE-DAG: .align 8, 0x90
|
||||
; SINGLE-DAG: ud2
|
||||
; SINGLE-DAG: .align 8, 0x90
|
||||
; SINGLE-DAG: ud2
|
||||
|
||||
|
||||
; ARITY-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; ARITY-DAG: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; SIMPL-DAG: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; FULL-DAG: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
||||
; FULL-DAG: .align 8, 0x90
|
||||
; 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: ud2
|
@ -1,23 +0,0 @@
|
||||
; RUN: llvm-as <%s >%t1
|
||||
; RUN: llvm-lto -o %t2 %t1 -jump-table-type=arity
|
||||
; RUN: llvm-nm %t2 | FileCheck %s
|
||||
|
||||
; CHECK: t __llvm_jump_instr_table_0_1
|
||||
; CHECK: t __llvm_jump_instr_table_1_1
|
||||
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define i32 @g(i32 %a) unnamed_addr jumptable {
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
define i32 @f() unnamed_addr jumptable {
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define i32 @main() {
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
@llvm.used = appending global [2 x i8*] [i8* bitcast (i32(i32)* @g to i8*),
|
||||
i8* bitcast (i32()* @f to i8*)]
|
Loading…
Reference in New Issue
Block a user