mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 04:02:41 +01:00
Add Windows Control Flow Guard checks (/guard:cf).
Summary: A new function pass (Transforms/CFGuard/CFGuard.cpp) inserts CFGuard checks on indirect function calls, using either the check mechanism (X86, ARM, AArch64) or or the dispatch mechanism (X86-64). The check mechanism requires a new calling convention for the supported targets. The dispatch mechanism adds the target as an operand bundle, which is processed by SelectionDAG. Another pass (CodeGen/CFGuardLongjmp.cpp) identifies and emits valid longjmp targets, as required by /guard:cf. This feature is enabled using the `cfguard` CC1 option. Reviewers: thakis, rnk, theraven, pcc Subscribers: ychen, hans, metalcanine, dmajor, tomrittervg, alex, mehdi_amini, mgorny, javed.absar, kristof.beyls, hiraditya, steven_wu, dexonsmith, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D65761
This commit is contained in:
parent
2b03c39683
commit
d090368b0c
@ -444,6 +444,17 @@ added in the future:
|
||||
the GHC or the HiPE convention is used. <CodeGenerator.html#id80>`_ This
|
||||
calling convention does not support varargs and requires the prototype of
|
||||
all callees to exactly match the prototype of the function definition.
|
||||
"``cfguard_checkcc``" - Windows Control Flow Guard (Check mechanism)
|
||||
This calling convention is used for the Control Flow Guard check function,
|
||||
calls to which can be inserted before indirect calls to check that the call
|
||||
target is a valid function address. The check function has no return value,
|
||||
but it will trigger an OS-level error if the address is not a valid target.
|
||||
The set of registers preserved by the check function, and the register
|
||||
containing the target address are architecture-specific.
|
||||
|
||||
- On X86 the target address is passed in ECX.
|
||||
- On ARM the target address is passed in R0.
|
||||
- On AArch64 the target address is passed in X15.
|
||||
"``cc <n>``" - Numbered convention
|
||||
Any calling convention may be specified by number, allowing
|
||||
target-specific calling conventions to be used. Target specific
|
||||
|
@ -83,6 +83,11 @@ Non-comprehensive list of changes in this release
|
||||
``bcmp`` pattern, and convert it into a call to ``bcmp`` (or ``memcmp``)
|
||||
function.
|
||||
|
||||
* Windows Control Flow Guard: the ``-cfguard`` option now emits CFG checks on
|
||||
indirect function calls. The previous behavior is still available with the
|
||||
``-cfguard-nochecks`` option. Note that this feature should always be used
|
||||
with optimizations enabled.
|
||||
|
||||
Changes to the LLVM IR
|
||||
----------------------
|
||||
|
||||
|
@ -304,6 +304,10 @@ class MachineFunction {
|
||||
/// by debug and exception handling consumers.
|
||||
std::vector<MCCFIInstruction> FrameInstructions;
|
||||
|
||||
/// List of basic blocks immediately following calls to _setjmp. Used to
|
||||
/// construct a table of valid longjmp targets for Windows Control Flow Guard.
|
||||
std::vector<MCSymbol *> LongjmpTargets;
|
||||
|
||||
/// \name Exception Handling
|
||||
/// \{
|
||||
|
||||
@ -830,6 +834,17 @@ public:
|
||||
|
||||
LLVM_NODISCARD unsigned addFrameInst(const MCCFIInstruction &Inst);
|
||||
|
||||
/// Returns a reference to a list of symbols immediately following calls to
|
||||
/// _setjmp in the function. Used to construct the longjmp target table used
|
||||
/// by Windows Control Flow Guard.
|
||||
const std::vector<MCSymbol *> &getLongjmpTargets() const {
|
||||
return LongjmpTargets;
|
||||
}
|
||||
|
||||
/// Add the specified symbol to the list of valid longjmp targets for Windows
|
||||
/// Control Flow Guard.
|
||||
void addLongjmpTarget(MCSymbol *Target) { LongjmpTargets.push_back(Target); }
|
||||
|
||||
/// \name Exception Handling
|
||||
/// \{
|
||||
|
||||
|
@ -451,6 +451,10 @@ namespace llvm {
|
||||
/// Creates CFI Instruction Inserter pass. \see CFIInstrInserter.cpp
|
||||
FunctionPass *createCFIInstrInserter();
|
||||
|
||||
/// Creates CFGuard longjmp target identification pass.
|
||||
/// \see CFGuardLongjmp.cpp
|
||||
FunctionPass *createCFGuardLongjmpPass();
|
||||
|
||||
/// Create Hardware Loop pass. \see HardwareLoops.cpp
|
||||
FunctionPass *createHardwareLoopsPass();
|
||||
|
||||
|
@ -38,6 +38,7 @@ namespace ISD {
|
||||
unsigned IsSplitEnd : 1; ///< Last part of a split
|
||||
unsigned IsSwiftSelf : 1; ///< Swift self parameter
|
||||
unsigned IsSwiftError : 1; ///< Swift error parameter
|
||||
unsigned IsCFGuardTarget : 1; ///< Control Flow Guard target
|
||||
unsigned IsHva : 1; ///< HVA field for
|
||||
unsigned IsHvaStart : 1; ///< HVA structure start
|
||||
unsigned IsSecArgPass : 1; ///< Second argument
|
||||
@ -56,8 +57,8 @@ namespace ISD {
|
||||
ArgFlagsTy()
|
||||
: IsZExt(0), IsSExt(0), IsInReg(0), IsSRet(0), IsByVal(0), IsNest(0),
|
||||
IsReturned(0), IsSplit(0), IsInAlloca(0), IsSplitEnd(0),
|
||||
IsSwiftSelf(0), IsSwiftError(0), IsHva(0), IsHvaStart(0),
|
||||
IsSecArgPass(0), ByValAlign(0), OrigAlign(0),
|
||||
IsSwiftSelf(0), IsSwiftError(0), IsCFGuardTarget(0), IsHva(0),
|
||||
IsHvaStart(0), IsSecArgPass(0), ByValAlign(0), OrigAlign(0),
|
||||
IsInConsecutiveRegsLast(0), IsInConsecutiveRegs(0),
|
||||
IsCopyElisionCandidate(0), IsPointer(0), ByValSize(0),
|
||||
PointerAddrSpace(0) {
|
||||
@ -88,6 +89,9 @@ namespace ISD {
|
||||
bool isSwiftError() const { return IsSwiftError; }
|
||||
void setSwiftError() { IsSwiftError = 1; }
|
||||
|
||||
bool isCFGuardTarget() const { return IsCFGuardTarget; }
|
||||
void setCFGuardTarget() { IsCFGuardTarget = 1; }
|
||||
|
||||
bool isHva() const { return IsHva; }
|
||||
void setHva() { IsHva = 1; }
|
||||
|
||||
|
@ -188,13 +188,14 @@ public:
|
||||
bool IsReturned : 1;
|
||||
bool IsSwiftSelf : 1;
|
||||
bool IsSwiftError : 1;
|
||||
bool IsCFGuardTarget : 1;
|
||||
uint16_t Alignment = 0;
|
||||
Type *ByValType = nullptr;
|
||||
|
||||
ArgListEntry()
|
||||
: IsSExt(false), IsZExt(false), IsInReg(false), IsSRet(false),
|
||||
IsNest(false), IsByVal(false), IsInAlloca(false), IsReturned(false),
|
||||
IsSwiftSelf(false), IsSwiftError(false) {}
|
||||
IsSwiftSelf(false), IsSwiftError(false), IsCFGuardTarget(false) {}
|
||||
|
||||
void setAttributes(const CallBase *Call, unsigned ArgIdx);
|
||||
|
||||
|
@ -80,6 +80,12 @@ namespace CallingConv {
|
||||
/// be performed.
|
||||
Tail = 18,
|
||||
|
||||
/// Special calling convention on Windows for calling the Control
|
||||
/// Guard Check ICall funtion. The function takes exactly one argument
|
||||
/// (address of the target function) passed in the first argument register,
|
||||
/// and has no return value. All register values are preserved.
|
||||
CFGuard_Check = 19,
|
||||
|
||||
// Target - This is the start of the target-specific calling conventions,
|
||||
// e.g. fastcall and thiscall on X86.
|
||||
FirstTargetCC = 64,
|
||||
|
@ -1039,6 +1039,11 @@ struct OperandBundleUse {
|
||||
return getTagID() == LLVMContext::OB_funclet;
|
||||
}
|
||||
|
||||
/// Return true if this is a "cfguardtarget" operand bundle.
|
||||
bool isCFGuardTargetOperandBundle() const {
|
||||
return getTagID() == LLVMContext::OB_cfguardtarget;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Pointer to an entry in LLVMContextImpl::getOrInsertBundleTag.
|
||||
StringMapEntry<uint32_t> *Tag;
|
||||
|
@ -85,6 +85,7 @@ public:
|
||||
OB_deopt = 0, // "deopt"
|
||||
OB_funclet = 1, // "funclet"
|
||||
OB_gc_transition = 2, // "gc-transition"
|
||||
OB_cfguardtarget = 3, // "cfguardtarget"
|
||||
};
|
||||
|
||||
/// getMDKindID - Return a unique non-zero ID for the specified metadata kind.
|
||||
|
@ -91,6 +91,8 @@ void initializeCFGOnlyPrinterLegacyPassPass(PassRegistry&);
|
||||
void initializeCFGOnlyViewerLegacyPassPass(PassRegistry&);
|
||||
void initializeCFGPrinterLegacyPassPass(PassRegistry&);
|
||||
void initializeCFGSimplifyPassPass(PassRegistry&);
|
||||
void initializeCFGuardPass(PassRegistry&);
|
||||
void initializeCFGuardLongjmpPass(PassRegistry&);
|
||||
void initializeCFGViewerLegacyPassPass(PassRegistry&);
|
||||
void initializeCFIInstrInserterPass(PassRegistry&);
|
||||
void initializeCFLAndersAAWrapperPassPass(PassRegistry&);
|
||||
|
@ -211,6 +211,7 @@ protected:
|
||||
MCSection *XDataSection;
|
||||
MCSection *SXDataSection;
|
||||
MCSection *GFIDsSection;
|
||||
MCSection *GLJMPSection;
|
||||
|
||||
public:
|
||||
void InitMCObjectFileInfo(const Triple &TT, bool PIC, MCContext &ctx,
|
||||
@ -379,6 +380,7 @@ public:
|
||||
MCSection *getXDataSection() const { return XDataSection; }
|
||||
MCSection *getSXDataSection() const { return SXDataSection; }
|
||||
MCSection *getGFIDsSection() const { return GFIDsSection; }
|
||||
MCSection *getGLJMPSection() const { return GLJMPSection; }
|
||||
|
||||
MCSection *getEHFrameSection() {
|
||||
return EHFrameSection;
|
||||
|
@ -51,6 +51,11 @@ class CCIfSwiftSelf<CCAction A> : CCIf<"ArgFlags.isSwiftSelf()", A> {
|
||||
class CCIfSwiftError<CCAction A> : CCIf<"ArgFlags.isSwiftError()", A> {
|
||||
}
|
||||
|
||||
/// CCIfCFGuardTarget - If the current argument has cfguardtarget parameter
|
||||
/// attribute, apply Action A.
|
||||
class CCIfCFGuardTarget<CCAction A> : CCIf<"ArgFlags.isCFGuardTarget()", A> {
|
||||
}
|
||||
|
||||
/// CCIfConsecutiveRegs - If the current argument has InConsecutiveRegs
|
||||
/// parameter attribute, apply Action A.
|
||||
class CCIfConsecutiveRegs<CCAction A> : CCIf<"ArgFlags.isInConsecutiveRegs()", A> {
|
||||
|
26
include/llvm/Transforms/CFGuard.h
Normal file
26
include/llvm/Transforms/CFGuard.h
Normal file
@ -0,0 +1,26 @@
|
||||
//===-- CFGuard.h - CFGuard Transformations ---------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
// Windows Control Flow Guard passes (/guard:cf).
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_TRANSFORMS_CFGUARD_H
|
||||
#define LLVM_TRANSFORMS_CFGUARD_H
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class FunctionPass;
|
||||
|
||||
/// Insert Control FLow Guard checks on indirect function calls.
|
||||
FunctionPass *createCFGuardCheckPass();
|
||||
|
||||
/// Insert Control FLow Guard dispatches on indirect function calls.
|
||||
FunctionPass *createCFGuardDispatchPass();
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
@ -585,6 +585,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
||||
KEYWORD(ccc);
|
||||
KEYWORD(fastcc);
|
||||
KEYWORD(coldcc);
|
||||
KEYWORD(cfguard_checkcc);
|
||||
KEYWORD(x86_stdcallcc);
|
||||
KEYWORD(x86_fastcallcc);
|
||||
KEYWORD(x86_thiscallcc);
|
||||
|
@ -1921,6 +1921,7 @@ void LLParser::ParseOptionalDLLStorageClass(unsigned &Res) {
|
||||
/// ::= 'fastcc'
|
||||
/// ::= 'intel_ocl_bicc'
|
||||
/// ::= 'coldcc'
|
||||
/// ::= 'cfguard_checkcc'
|
||||
/// ::= 'x86_stdcallcc'
|
||||
/// ::= 'x86_fastcallcc'
|
||||
/// ::= 'x86_thiscallcc'
|
||||
@ -1965,6 +1966,7 @@ bool LLParser::ParseOptionalCallingConv(unsigned &CC) {
|
||||
case lltok::kw_ccc: CC = CallingConv::C; break;
|
||||
case lltok::kw_fastcc: CC = CallingConv::Fast; break;
|
||||
case lltok::kw_coldcc: CC = CallingConv::Cold; break;
|
||||
case lltok::kw_cfguard_checkcc: CC = CallingConv::CFGuard_Check; break;
|
||||
case lltok::kw_x86_stdcallcc: CC = CallingConv::X86_StdCall; break;
|
||||
case lltok::kw_x86_fastcallcc: CC = CallingConv::X86_FastCall; break;
|
||||
case lltok::kw_x86_regcallcc: CC = CallingConv::X86_RegCall; break;
|
||||
|
@ -132,6 +132,7 @@ enum Kind {
|
||||
kw_fastcc,
|
||||
kw_coldcc,
|
||||
kw_intel_ocl_bicc,
|
||||
kw_cfguard_checkcc,
|
||||
kw_x86_stdcallcc,
|
||||
kw_x86_fastcallcc,
|
||||
kw_x86_thiscallcc,
|
||||
|
@ -139,7 +139,7 @@ static const char *const DbgTimerDescription = "Debug Info Emission";
|
||||
static const char *const EHTimerName = "write_exception";
|
||||
static const char *const EHTimerDescription = "DWARF Exception Writer";
|
||||
static const char *const CFGuardName = "Control Flow Guard";
|
||||
static const char *const CFGuardDescription = "Control Flow Guard Tables";
|
||||
static const char *const CFGuardDescription = "Control Flow Guard";
|
||||
static const char *const CodeViewLineTablesGroupName = "linetables";
|
||||
static const char *const CodeViewLineTablesGroupDescription =
|
||||
"CodeView Line Tables";
|
||||
@ -381,12 +381,12 @@ bool AsmPrinter::doInitialization(Module &M) {
|
||||
EHTimerDescription, DWARFGroupName,
|
||||
DWARFGroupDescription);
|
||||
|
||||
// Emit tables for any value of cfguard flag (i.e. cfguard=1 or cfguard=2).
|
||||
if (mdconst::extract_or_null<ConstantInt>(
|
||||
MMI->getModule()->getModuleFlag("cfguardtable")))
|
||||
MMI->getModule()->getModuleFlag("cfguard")))
|
||||
Handlers.emplace_back(std::make_unique<WinCFGuard>(this), CFGuardName,
|
||||
CFGuardDescription, DWARFGroupName,
|
||||
DWARFGroupDescription);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,8 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains support for writing Win64 exception info into asm files.
|
||||
// This file contains support for writing the metadata for Windows Control Flow
|
||||
// Guard, including address-taken functions, and valid longjmp targets.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -29,16 +30,33 @@ WinCFGuard::WinCFGuard(AsmPrinter *A) : AsmPrinterHandler(), Asm(A) {}
|
||||
|
||||
WinCFGuard::~WinCFGuard() {}
|
||||
|
||||
void WinCFGuard::endFunction(const MachineFunction *MF) {
|
||||
|
||||
// Skip functions without any longjmp targets.
|
||||
if (MF->getLongjmpTargets().empty())
|
||||
return;
|
||||
|
||||
// Copy the function's longjmp targets to a module-level list.
|
||||
LongjmpTargets.insert(LongjmpTargets.end(), MF->getLongjmpTargets().begin(),
|
||||
MF->getLongjmpTargets().end());
|
||||
}
|
||||
|
||||
void WinCFGuard::endModule() {
|
||||
const Module *M = Asm->MMI->getModule();
|
||||
std::vector<const Function *> Functions;
|
||||
for (const Function &F : *M)
|
||||
if (F.hasAddressTaken())
|
||||
Functions.push_back(&F);
|
||||
if (Functions.empty())
|
||||
if (Functions.empty() && LongjmpTargets.empty())
|
||||
return;
|
||||
auto &OS = *Asm->OutStreamer;
|
||||
OS.SwitchSection(Asm->OutContext.getObjectFileInfo()->getGFIDsSection());
|
||||
for (const Function *F : Functions)
|
||||
OS.EmitCOFFSymbolIndex(Asm->getSymbol(F));
|
||||
|
||||
// Emit the symbol index of each longjmp target.
|
||||
OS.SwitchSection(Asm->OutContext.getObjectFileInfo()->getGLJMPSection());
|
||||
for (const MCSymbol *S : LongjmpTargets) {
|
||||
OS.EmitCOFFSymbolIndex(S);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,8 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains support for writing windows exception info into asm files.
|
||||
// This file contains support for writing the metadata for Windows Control Flow
|
||||
// Guard, including address-taken functions, and valid longjmp targets.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -15,12 +16,14 @@
|
||||
|
||||
#include "llvm/CodeGen/AsmPrinterHandler.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class LLVM_LIBRARY_VISIBILITY WinCFGuard : public AsmPrinterHandler {
|
||||
/// Target of directive emission.
|
||||
AsmPrinter *Asm;
|
||||
std::vector<const MCSymbol *> LongjmpTargets;
|
||||
|
||||
public:
|
||||
WinCFGuard(AsmPrinter *A);
|
||||
@ -28,7 +31,7 @@ public:
|
||||
|
||||
void setSymbolSize(const MCSymbol *Sym, uint64_t Size) override {}
|
||||
|
||||
/// Emit the Control Flow Guard function ID table
|
||||
/// Emit the Control Flow Guard function ID table.
|
||||
void endModule() override;
|
||||
|
||||
/// Gather pre-function debug information.
|
||||
@ -39,7 +42,7 @@ public:
|
||||
/// Gather post-function debug information.
|
||||
/// Please note that some AsmPrinter implementations may not call
|
||||
/// beginFunction at all.
|
||||
void endFunction(const MachineFunction *MF) override {}
|
||||
void endFunction(const MachineFunction *MF) override;
|
||||
|
||||
/// Process beginning of an instruction.
|
||||
void beginInstruction(const MachineInstr *MI) override {}
|
||||
|
119
lib/CodeGen/CFGuardLongjmp.cpp
Normal file
119
lib/CodeGen/CFGuardLongjmp.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
//===-- CFGuardLongjmp.cpp - Longjmp symbols for CFGuard --------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file contains a machine function pass to insert a symbol after each
|
||||
/// call to _setjmp and store this in the MachineFunction's LongjmpTargets
|
||||
/// vector. This will be used to emit the table of valid longjmp targets used
|
||||
/// by Control Flow Guard.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/CodeGen/MachineBasicBlock.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/CodeGen/MachineInstr.h"
|
||||
#include "llvm/CodeGen/MachineModuleInfo.h"
|
||||
#include "llvm/CodeGen/MachineOperand.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "cfguard-longjmp"
|
||||
|
||||
STATISTIC(CFGuardLongjmpTargets,
|
||||
"Number of Control Flow Guard longjmp targets");
|
||||
|
||||
namespace {
|
||||
|
||||
/// MachineFunction pass to insert a symbol after each call to _setjmp and store
|
||||
/// this in the MachineFunction's LongjmpTargets vector.
|
||||
class CFGuardLongjmp : public MachineFunctionPass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
CFGuardLongjmp() : MachineFunctionPass(ID) {
|
||||
initializeCFGuardLongjmpPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
|
||||
StringRef getPassName() const override {
|
||||
return "Control Flow Guard longjmp targets";
|
||||
}
|
||||
|
||||
bool runOnMachineFunction(MachineFunction &MF) override;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
char CFGuardLongjmp::ID = 0;
|
||||
|
||||
INITIALIZE_PASS(CFGuardLongjmp, "CFGuardLongjmp",
|
||||
"Insert symbols at valid longjmp targets for /guard:cf", false,
|
||||
false)
|
||||
FunctionPass *llvm::createCFGuardLongjmpPass() { return new CFGuardLongjmp(); }
|
||||
|
||||
bool CFGuardLongjmp::runOnMachineFunction(MachineFunction &MF) {
|
||||
|
||||
// Skip modules for which the cfguard flag is not set.
|
||||
if (!MF.getMMI().getModule()->getModuleFlag("cfguard"))
|
||||
return false;
|
||||
|
||||
// Skip functions that do not have calls to _setjmp.
|
||||
if (!MF.getFunction().callsFunctionThatReturnsTwice())
|
||||
return false;
|
||||
|
||||
SmallVector<MachineInstr *, 8> SetjmpCalls;
|
||||
|
||||
// Iterate over all instructions in the function and add calls to functions
|
||||
// that return twice to the list of targets.
|
||||
for (MachineBasicBlock &MBB : MF) {
|
||||
for (MachineInstr &MI : MBB) {
|
||||
|
||||
// Skip instructions that are not calls.
|
||||
if (!MI.isCall() || MI.getNumOperands() < 1)
|
||||
continue;
|
||||
|
||||
// Iterate over operands to find calls to global functions.
|
||||
for (MachineOperand &MO : MI.operands()) {
|
||||
if (!MO.isGlobal())
|
||||
continue;
|
||||
|
||||
auto *F = dyn_cast<Function>(MO.getGlobal());
|
||||
if (!F)
|
||||
continue;
|
||||
|
||||
// If the instruction calls a function that returns twice, add
|
||||
// it to the list of targets.
|
||||
if (F->hasFnAttribute(Attribute::ReturnsTwice)) {
|
||||
SetjmpCalls.push_back(&MI);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SetjmpCalls.empty())
|
||||
return false;
|
||||
|
||||
unsigned SetjmpNum = 0;
|
||||
|
||||
// For each possible target, create a new symbol and insert it immediately
|
||||
// after the call to setjmp. Add this symbol to the MachineFunction's list
|
||||
// of longjmp targets.
|
||||
for (MachineInstr *Setjmp : SetjmpCalls) {
|
||||
SmallString<128> SymbolName;
|
||||
raw_svector_ostream(SymbolName) << "$cfgsj_" << MF.getName() << SetjmpNum++;
|
||||
MCSymbol *SjSymbol = MF.getContext().getOrCreateSymbol(SymbolName);
|
||||
|
||||
Setjmp->setPostInstrSymbol(MF, SjSymbol);
|
||||
MF.addLongjmpTarget(SjSymbol);
|
||||
CFGuardLongjmpTargets++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -10,6 +10,7 @@ add_llvm_library(LLVMCodeGen
|
||||
BuiltinGCs.cpp
|
||||
CalcSpillWeights.cpp
|
||||
CallingConvLower.cpp
|
||||
CFGuardLongjmp.cpp
|
||||
CFIInstrInserter.cpp
|
||||
CodeGen.cpp
|
||||
CodeGenPrepare.cpp
|
||||
|
@ -22,6 +22,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
|
||||
initializeAtomicExpandPass(Registry);
|
||||
initializeBranchFolderPassPass(Registry);
|
||||
initializeBranchRelaxationPass(Registry);
|
||||
initializeCFGuardLongjmpPass(Registry);
|
||||
initializeCFIInstrInserterPass(Registry);
|
||||
initializeCodeGenPreparePass(Registry);
|
||||
initializeDeadMachineInstructionElimPass(Registry);
|
||||
|
@ -1590,6 +1590,10 @@ bool IRTranslator::translateCall(const User &U, MachineIRBuilder &MIRBuilder) {
|
||||
if (F && F->hasDLLImportStorageClass())
|
||||
return false;
|
||||
|
||||
// FIXME: support control flow guard targets.
|
||||
if (CI.countOperandBundlesOfType(LLVMContext::OB_cfguardtarget))
|
||||
return false;
|
||||
|
||||
if (CI.isInlineAsm())
|
||||
return translateInlineAsm(CI, MIRBuilder);
|
||||
|
||||
@ -1683,6 +1687,10 @@ bool IRTranslator::translateInvoke(const User &U,
|
||||
if (I.countOperandBundlesOfType(LLVMContext::OB_deopt))
|
||||
return false;
|
||||
|
||||
// FIXME: support control flow guard targets.
|
||||
if (I.countOperandBundlesOfType(LLVMContext::OB_cfguardtarget))
|
||||
return false;
|
||||
|
||||
// FIXME: support Windows exception handling.
|
||||
if (!isa<LandingPadInst>(EHPadBB->front()))
|
||||
return false;
|
||||
|
@ -1190,6 +1190,8 @@ bool FastISel::lowerCallTo(CallLoweringInfo &CLI) {
|
||||
Flags.setSwiftSelf();
|
||||
if (Arg.IsSwiftError)
|
||||
Flags.setSwiftError();
|
||||
if (Arg.IsCFGuardTarget)
|
||||
Flags.setCFGuardTarget();
|
||||
if (Arg.IsByVal)
|
||||
Flags.setByVal();
|
||||
if (Arg.IsInAlloca) {
|
||||
|
@ -2746,8 +2746,9 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
|
||||
|
||||
// Deopt bundles are lowered in LowerCallSiteWithDeoptBundle, and we don't
|
||||
// have to do anything here to lower funclet bundles.
|
||||
assert(!I.hasOperandBundlesOtherThan(
|
||||
{LLVMContext::OB_deopt, LLVMContext::OB_funclet}) &&
|
||||
assert(!I.hasOperandBundlesOtherThan({LLVMContext::OB_deopt,
|
||||
LLVMContext::OB_funclet,
|
||||
LLVMContext::OB_cfguardtarget}) &&
|
||||
"Cannot lower invokes with arbitrary operand bundles yet!");
|
||||
|
||||
const Value *Callee(I.getCalledValue());
|
||||
@ -7145,6 +7146,18 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee,
|
||||
isTailCall = false;
|
||||
}
|
||||
|
||||
// If call site has a cfguardtarget operand bundle, create and add an
|
||||
// additional ArgListEntry.
|
||||
if (auto Bundle = CS.getOperandBundle(LLVMContext::OB_cfguardtarget)) {
|
||||
TargetLowering::ArgListEntry Entry;
|
||||
Value *V = Bundle->Inputs[0];
|
||||
SDValue ArgNode = getValue(V);
|
||||
Entry.Node = ArgNode;
|
||||
Entry.Ty = V->getType();
|
||||
Entry.IsCFGuardTarget = true;
|
||||
Args.push_back(Entry);
|
||||
}
|
||||
|
||||
// Check if target-independent constraints permit a tail call here.
|
||||
// Target-dependent constraints are checked within TLI->LowerCallTo.
|
||||
if (isTailCall && !isInTailCallPosition(CS, DAG.getTarget()))
|
||||
@ -7686,8 +7699,10 @@ void SelectionDAGBuilder::visitCall(const CallInst &I) {
|
||||
|
||||
// Deopt bundles are lowered in LowerCallSiteWithDeoptBundle, and we don't
|
||||
// have to do anything here to lower funclet bundles.
|
||||
assert(!I.hasOperandBundlesOtherThan(
|
||||
{LLVMContext::OB_deopt, LLVMContext::OB_funclet}) &&
|
||||
// CFGuardTarget bundles are lowered in LowerCallTo.
|
||||
assert(!I.hasOperandBundlesOtherThan({LLVMContext::OB_deopt,
|
||||
LLVMContext::OB_funclet,
|
||||
LLVMContext::OB_cfguardtarget}) &&
|
||||
"Cannot lower calls with arbitrary operand bundles!");
|
||||
|
||||
SDValue Callee = getValue(I.getCalledValue());
|
||||
@ -9030,6 +9045,7 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const {
|
||||
Entry.IsReturned = false;
|
||||
Entry.IsSwiftSelf = false;
|
||||
Entry.IsSwiftError = false;
|
||||
Entry.IsCFGuardTarget = false;
|
||||
Entry.Alignment = Align;
|
||||
CLI.getArgs().insert(CLI.getArgs().begin(), Entry);
|
||||
CLI.NumFixedArgs += 1;
|
||||
@ -9142,6 +9158,8 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const {
|
||||
Flags.setSwiftSelf();
|
||||
if (Args[i].IsSwiftError)
|
||||
Flags.setSwiftError();
|
||||
if (Args[i].IsCFGuardTarget)
|
||||
Flags.setCFGuardTarget();
|
||||
if (Args[i].IsByVal)
|
||||
Flags.setByVal();
|
||||
if (Args[i].IsInAlloca) {
|
||||
|
@ -353,6 +353,7 @@ static void PrintCallingConv(unsigned cc, raw_ostream &Out) {
|
||||
case CallingConv::CXX_FAST_TLS: Out << "cxx_fast_tlscc"; break;
|
||||
case CallingConv::GHC: Out << "ghccc"; break;
|
||||
case CallingConv::Tail: Out << "tailcc"; break;
|
||||
case CallingConv::CFGuard_Check: Out << "cfguard_checkcc"; break;
|
||||
case CallingConv::X86_StdCall: Out << "x86_stdcallcc"; break;
|
||||
case CallingConv::X86_FastCall: Out << "x86_fastcallcc"; break;
|
||||
case CallingConv::X86_ThisCall: Out << "x86_thiscallcc"; break;
|
||||
|
@ -62,6 +62,11 @@ LLVMContext::LLVMContext() : pImpl(new LLVMContextImpl(*this)) {
|
||||
"gc-transition operand bundle id drifted!");
|
||||
(void)GCTransitionEntry;
|
||||
|
||||
auto *CFGuardTargetEntry = pImpl->getOrInsertBundleTag("cfguardtarget");
|
||||
assert(CFGuardTargetEntry->second == LLVMContext::OB_cfguardtarget &&
|
||||
"cfguardtarget operand bundle id drifted!");
|
||||
(void)CFGuardTargetEntry;
|
||||
|
||||
SyncScope::ID SingleThreadSSID =
|
||||
pImpl->getOrInsertSyncScopeID("singlethread");
|
||||
assert(SingleThreadSSID == SyncScope::SingleThread &&
|
||||
|
@ -2975,10 +2975,10 @@ void Verifier::visitCallBase(CallBase &Call) {
|
||||
if (Intrinsic::ID ID = (Intrinsic::ID)F->getIntrinsicID())
|
||||
visitIntrinsicCall(ID, Call);
|
||||
|
||||
// Verify that a callsite has at most one "deopt", at most one "funclet" and
|
||||
// at most one "gc-transition" operand bundle.
|
||||
// Verify that a callsite has at most one "deopt", at most one "funclet", at
|
||||
// most one "gc-transition", and at most one "cfguardtarget" operand bundle.
|
||||
bool FoundDeoptBundle = false, FoundFuncletBundle = false,
|
||||
FoundGCTransitionBundle = false;
|
||||
FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false;
|
||||
for (unsigned i = 0, e = Call.getNumOperandBundles(); i < e; ++i) {
|
||||
OperandBundleUse BU = Call.getOperandBundleAt(i);
|
||||
uint32_t Tag = BU.getTagID();
|
||||
@ -2997,6 +2997,12 @@ void Verifier::visitCallBase(CallBase &Call) {
|
||||
Assert(isa<FuncletPadInst>(BU.Inputs.front()),
|
||||
"Funclet bundle operands should correspond to a FuncletPadInst",
|
||||
Call);
|
||||
} else if (Tag == LLVMContext::OB_cfguardtarget) {
|
||||
Assert(!FoundCFGuardTargetBundle,
|
||||
"Multiple CFGuardTarget operand bundles", Call);
|
||||
FoundCFGuardTargetBundle = true;
|
||||
Assert(BU.Inputs.size() == 1,
|
||||
"Expected exactly one cfguardtarget bundle operand", Call);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -715,6 +715,11 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) {
|
||||
COFF::IMAGE_SCN_MEM_READ,
|
||||
SectionKind::getMetadata());
|
||||
|
||||
GLJMPSection = Ctx->getCOFFSection(".gljmp$y",
|
||||
COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
|
||||
COFF::IMAGE_SCN_MEM_READ,
|
||||
SectionKind::getMetadata());
|
||||
|
||||
TLSDataSection = Ctx->getCOFFSection(
|
||||
".tls$", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ |
|
||||
COFF::IMAGE_SCN_MEM_WRITE,
|
||||
|
@ -31,6 +31,9 @@ bool CC_AArch64_DarwinPCS_ILP32_VarArg(unsigned ValNo, MVT ValVT, MVT LocVT,
|
||||
bool CC_AArch64_Win64_VarArg(unsigned ValNo, MVT ValVT, MVT LocVT,
|
||||
CCValAssign::LocInfo LocInfo,
|
||||
ISD::ArgFlagsTy ArgFlags, CCState &State);
|
||||
bool CC_AArch64_Win64_CFGuard_Check(unsigned ValNo, MVT ValVT, MVT LocVT,
|
||||
CCValAssign::LocInfo LocInfo,
|
||||
ISD::ArgFlagsTy ArgFlags, CCState &State);
|
||||
bool CC_AArch64_WebKit_JS(unsigned ValNo, MVT ValVT, MVT LocVT,
|
||||
CCValAssign::LocInfo LocInfo,
|
||||
ISD::ArgFlagsTy ArgFlags, CCState &State);
|
||||
|
@ -170,6 +170,13 @@ def CC_AArch64_Win64_VarArg : CallingConv<[
|
||||
CCDelegateTo<CC_AArch64_AAPCS>
|
||||
]>;
|
||||
|
||||
// Windows Control Flow Guard checks take a single argument (the target function
|
||||
// address) and have no return value.
|
||||
let Entry = 1 in
|
||||
def CC_AArch64_Win64_CFGuard_Check : CallingConv<[
|
||||
CCIfType<[i64], CCAssignToReg<[X15]>>
|
||||
]>;
|
||||
|
||||
|
||||
// Darwin uses a calling convention which differs in only two ways
|
||||
// from the standard one at this level:
|
||||
@ -384,6 +391,12 @@ def CSR_Win_AArch64_AAPCS : CalleeSavedRegs<(add X19, X20, X21, X22, X23, X24,
|
||||
D8, D9, D10, D11,
|
||||
D12, D13, D14, D15)>;
|
||||
|
||||
// The Control Flow Guard check call uses a custom calling convention that also
|
||||
// preserves X0-X8 and Q0-Q7.
|
||||
def CSR_Win_AArch64_CFGuard_Check : CalleeSavedRegs<(add CSR_Win_AArch64_AAPCS,
|
||||
(sequence "X%u", 0, 8),
|
||||
(sequence "Q%u", 0, 7))>;
|
||||
|
||||
// AArch64 PCS for vector functions (VPCS)
|
||||
// must (additionally) preserve full Q8-Q23 registers
|
||||
def CSR_AArch64_AAVPCS : CalleeSavedRegs<(add X19, X20, X21, X22, X23, X24,
|
||||
|
@ -348,6 +348,8 @@ CCAssignFn *AArch64FastISel::CCAssignFnForCall(CallingConv::ID CC) const {
|
||||
return CC_AArch64_WebKit_JS;
|
||||
if (CC == CallingConv::GHC)
|
||||
return CC_AArch64_GHC;
|
||||
if (CC == CallingConv::CFGuard_Check)
|
||||
return CC_AArch64_Win64_CFGuard_Check;
|
||||
return Subtarget->isTargetDarwin() ? CC_AArch64_DarwinPCS : CC_AArch64_AAPCS;
|
||||
}
|
||||
|
||||
|
@ -3118,8 +3118,10 @@ CCAssignFn *AArch64TargetLowering::CCAssignFnForCall(CallingConv::ID CC,
|
||||
: CC_AArch64_DarwinPCS_VarArg;
|
||||
case CallingConv::Win64:
|
||||
return IsVarArg ? CC_AArch64_Win64_VarArg : CC_AArch64_AAPCS;
|
||||
case CallingConv::AArch64_VectorCall:
|
||||
return CC_AArch64_AAPCS;
|
||||
case CallingConv::CFGuard_Check:
|
||||
return CC_AArch64_Win64_CFGuard_Check;
|
||||
case CallingConv::AArch64_VectorCall:
|
||||
return CC_AArch64_AAPCS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,8 @@ AArch64RegisterInfo::AArch64RegisterInfo(const Triple &TT)
|
||||
const MCPhysReg *
|
||||
AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
|
||||
assert(MF && "Invalid MachineFunction pointer.");
|
||||
if (MF->getFunction().getCallingConv() == CallingConv::CFGuard_Check)
|
||||
return CSR_Win_AArch64_CFGuard_Check_SaveList;
|
||||
if (MF->getSubtarget<AArch64Subtarget>().isTargetWindows())
|
||||
return CSR_Win_AArch64_AAPCS_SaveList;
|
||||
if (MF->getFunction().getCallingConv() == CallingConv::GHC)
|
||||
@ -124,6 +126,8 @@ AArch64RegisterInfo::getCallPreservedMask(const MachineFunction &MF,
|
||||
return SCS ? CSR_AArch64_AAVPCS_SCS_RegMask : CSR_AArch64_AAVPCS_RegMask;
|
||||
if (CC == CallingConv::AArch64_SVE_VectorCall)
|
||||
return CSR_AArch64_SVE_AAPCS_RegMask;
|
||||
if (CC == CallingConv::CFGuard_Check)
|
||||
return CSR_Win_AArch64_CFGuard_Check_RegMask;
|
||||
if (MF.getSubtarget<AArch64Subtarget>().getTargetLowering()
|
||||
->supportSwiftError() &&
|
||||
MF.getFunction().getAttributes().hasAttrSomewhere(Attribute::SwiftError))
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Target/TargetLoweringObjectFile.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
#include "llvm/Transforms/CFGuard.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -459,6 +460,10 @@ void AArch64PassConfig::addIRPasses() {
|
||||
|
||||
addPass(createAArch64StackTaggingPass(/* MergeInit = */ TM->getOptLevel() !=
|
||||
CodeGenOpt::None));
|
||||
|
||||
// Add Control Flow Guard checks.
|
||||
if (TM->getTargetTriple().isOSWindows())
|
||||
addPass(createCFGuardCheckPass());
|
||||
}
|
||||
|
||||
// Pass Pipeline Configuration
|
||||
@ -617,6 +622,10 @@ void AArch64PassConfig::addPreEmitPass() {
|
||||
if (EnableBranchTargets)
|
||||
addPass(createAArch64BranchTargetsPass());
|
||||
|
||||
// Identify valid longjmp targets for Windows Control Flow Guard.
|
||||
if (TM->getTargetTriple().isOSWindows())
|
||||
addPass(createCFGuardLongjmpPass());
|
||||
|
||||
if (TM->getOptLevel() != CodeGenOpt::None && EnableCompressJumpTables)
|
||||
addPass(createAArch64CompressJumpTablesPass());
|
||||
|
||||
|
@ -30,5 +30,5 @@ has_jit = 1
|
||||
type = Library
|
||||
name = AArch64CodeGen
|
||||
parent = AArch64
|
||||
required_libraries = AArch64Desc AArch64Info AArch64Utils Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target TransformUtils GlobalISel
|
||||
required_libraries = AArch64Desc AArch64Info AArch64Utils Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target TransformUtils GlobalISel CFGuard
|
||||
add_to_library_groups = AArch64
|
||||
|
@ -75,6 +75,8 @@ ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
|
||||
// GHC set of callee saved regs is empty as all those regs are
|
||||
// used for passing STG regs around
|
||||
return CSR_NoRegs_SaveList;
|
||||
} else if (F.getCallingConv() == CallingConv::CFGuard_Check) {
|
||||
return CSR_Win_AAPCS_CFGuard_Check_SaveList;
|
||||
} else if (F.hasFnAttribute("interrupt")) {
|
||||
if (STI.isMClass()) {
|
||||
// M-class CPUs have hardware which saves the registers needed to allow a
|
||||
@ -123,7 +125,8 @@ ARMBaseRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
|
||||
if (CC == CallingConv::GHC)
|
||||
// This is academic because all GHC calls are (supposed to be) tail calls
|
||||
return CSR_NoRegs_RegMask;
|
||||
|
||||
if (CC == CallingConv::CFGuard_Check)
|
||||
return CSR_Win_AAPCS_CFGuard_Check_RegMask;
|
||||
if (STI.getTargetLowering()->supportSwiftError() &&
|
||||
MF.getFunction().getAttributes().hasAttrSomewhere(Attribute::SwiftError))
|
||||
return STI.isTargetDarwin() ? CSR_iOS_SwiftError_RegMask
|
||||
|
@ -32,6 +32,9 @@ bool CC_ARM_APCS_GHC(unsigned ValNo, MVT ValVT, MVT LocVT,
|
||||
bool FastCC_ARM_APCS(unsigned ValNo, MVT ValVT, MVT LocVT,
|
||||
CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags,
|
||||
CCState &State);
|
||||
bool CC_ARM_Win32_CFGuard_Check(unsigned ValNo, MVT ValVT, MVT LocVT,
|
||||
CCValAssign::LocInfo LocInfo,
|
||||
ISD::ArgFlagsTy ArgFlags, CCState &State);
|
||||
bool RetCC_ARM_AAPCS(unsigned ValNo, MVT ValVT, MVT LocVT,
|
||||
CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags,
|
||||
CCState &State);
|
||||
|
@ -246,6 +246,16 @@ def RetCC_ARM_AAPCS_VFP : CallingConv<[
|
||||
CCDelegateTo<RetCC_ARM_AAPCS_Common>
|
||||
]>;
|
||||
|
||||
|
||||
// Windows Control Flow Guard checks take a single argument (the target function
|
||||
// address) and have no return value.
|
||||
let Entry = 1 in
|
||||
def CC_ARM_Win32_CFGuard_Check : CallingConv<[
|
||||
CCIfType<[i32], CCAssignToReg<[R0]>>
|
||||
]>;
|
||||
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Callee-saved register lists.
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -256,6 +266,11 @@ def CSR_FPRegs : CalleeSavedRegs<(add (sequence "D%u", 0, 31))>;
|
||||
def CSR_AAPCS : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, R6, R5, R4,
|
||||
(sequence "D%u", 15, 8))>;
|
||||
|
||||
// The Windows Control Flow Guard Check function preserves the same registers as
|
||||
// AAPCS, and also preserves all floating point registers.
|
||||
def CSR_Win_AAPCS_CFGuard_Check : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7,
|
||||
R6, R5, R4, (sequence "D%u", 15, 0))>;
|
||||
|
||||
// R8 is used to pass swifterror, remove it from CSR.
|
||||
def CSR_AAPCS_SwiftError : CalleeSavedRegs<(sub CSR_AAPCS, R8)>;
|
||||
|
||||
|
@ -1879,6 +1879,8 @@ CCAssignFn *ARMFastISel::CCAssignFnForCall(CallingConv::ID CC,
|
||||
report_fatal_error("Can't return in GHC call convention");
|
||||
else
|
||||
return CC_ARM_APCS_GHC;
|
||||
case CallingConv::CFGuard_Check:
|
||||
return (Return ? RetCC_ARM_AAPCS : CC_ARM_Win32_CFGuard_Check);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1855,6 +1855,7 @@ ARMTargetLowering::getEffectiveCallingConv(CallingConv::ID CC,
|
||||
case CallingConv::ARM_AAPCS:
|
||||
case CallingConv::ARM_APCS:
|
||||
case CallingConv::GHC:
|
||||
case CallingConv::CFGuard_Check:
|
||||
return CC;
|
||||
case CallingConv::PreserveMost:
|
||||
return CallingConv::PreserveMost;
|
||||
@ -1914,6 +1915,8 @@ CCAssignFn *ARMTargetLowering::CCAssignFnForNode(CallingConv::ID CC,
|
||||
return (Return ? RetCC_ARM_APCS : CC_ARM_APCS_GHC);
|
||||
case CallingConv::PreserveMost:
|
||||
return (Return ? RetCC_ARM_AAPCS : CC_ARM_AAPCS);
|
||||
case CallingConv::CFGuard_Check:
|
||||
return (Return ? RetCC_ARM_AAPCS : CC_ARM_Win32_CFGuard_Check);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Target/TargetLoweringObjectFile.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
#include "llvm/Transforms/CFGuard.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
@ -420,6 +421,10 @@ void ARMPassConfig::addIRPasses() {
|
||||
// Match interleaved memory accesses to ldN/stN intrinsics.
|
||||
if (TM->getOptLevel() != CodeGenOpt::None)
|
||||
addPass(createInterleavedAccessPass());
|
||||
|
||||
// Add Control Flow Guard checks.
|
||||
if (TM->getTargetTriple().isOSWindows())
|
||||
addPass(createCFGuardCheckPass());
|
||||
}
|
||||
|
||||
void ARMPassConfig::addCodeGenPrepare() {
|
||||
@ -534,4 +539,8 @@ void ARMPassConfig::addPreEmitPass() {
|
||||
|
||||
addPass(createARMConstantIslandPass());
|
||||
addPass(createARMLowOverheadLoopsPass());
|
||||
|
||||
// Identify valid longjmp targets for Windows Control Flow Guard.
|
||||
if (TM->getTargetTriple().isOSWindows())
|
||||
addPass(createCFGuardLongjmpPass());
|
||||
}
|
||||
|
@ -30,5 +30,5 @@ has_jit = 1
|
||||
type = Library
|
||||
name = ARMCodeGen
|
||||
parent = ARM
|
||||
required_libraries = ARMDesc ARMInfo Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target GlobalISel ARMUtils TransformUtils
|
||||
required_libraries = ARMDesc ARMInfo Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target GlobalISel ARMUtils TransformUtils CFGuard
|
||||
add_to_library_groups = ARM
|
||||
|
@ -30,5 +30,5 @@ has_jit = 1
|
||||
type = Library
|
||||
name = X86CodeGen
|
||||
parent = X86
|
||||
required_libraries = Analysis AsmPrinter CodeGen Core MC SelectionDAG Support Target X86Desc X86Info X86Utils GlobalISel ProfileData
|
||||
required_libraries = Analysis AsmPrinter CodeGen Core MC SelectionDAG Support Target X86Desc X86Info X86Utils GlobalISel ProfileData CFGuard
|
||||
add_to_library_groups = X86
|
||||
|
@ -614,7 +614,7 @@ void X86AsmPrinter::EmitStartOfAsmFile(Module &M) {
|
||||
Feat00Flags |= 1;
|
||||
}
|
||||
|
||||
if (M.getModuleFlag("cfguardtable"))
|
||||
if (M.getModuleFlag("cfguard"))
|
||||
Feat00Flags |= 0x800; // Object is CFG-aware.
|
||||
|
||||
OutStreamer->EmitSymbolAttribute(S, MCSA_Global);
|
||||
|
@ -434,6 +434,7 @@ def RetCC_X86_32 : CallingConv<[
|
||||
// If FastCC, use RetCC_X86_32_Fast.
|
||||
CCIfCC<"CallingConv::Fast", CCDelegateTo<RetCC_X86_32_Fast>>,
|
||||
CCIfCC<"CallingConv::Tail", CCDelegateTo<RetCC_X86_32_Fast>>,
|
||||
// CFGuard_Check never returns a value so does not need a RetCC.
|
||||
// If HiPE, use RetCC_X86_32_HiPE.
|
||||
CCIfCC<"CallingConv::HiPE", CCDelegateTo<RetCC_X86_32_HiPE>>,
|
||||
CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<RetCC_X86_32_VectorCall>>,
|
||||
@ -606,6 +607,9 @@ def CC_X86_Win64_C : CallingConv<[
|
||||
// A SwiftError is passed in R12.
|
||||
CCIfSwiftError<CCIfType<[i64], CCAssignToReg<[R12]>>>,
|
||||
|
||||
// The 'CFGuardTarget' parameter, if any, is passed in RAX.
|
||||
CCIfCFGuardTarget<CCAssignToReg<[RAX]>>,
|
||||
|
||||
// 128 bit vectors are passed by pointer
|
||||
CCIfType<[v16i8, v8i16, v4i32, v2i64, v4f32, v2f64], CCPassIndirect<i64>>,
|
||||
|
||||
@ -936,6 +940,12 @@ def CC_X86_32_FastCC : CallingConv<[
|
||||
CCDelegateTo<CC_X86_32_Common>
|
||||
]>;
|
||||
|
||||
def CC_X86_Win32_CFGuard_Check : CallingConv<[
|
||||
// The CFGuard check call takes exactly one integer argument
|
||||
// (i.e. the target function address), which is passed in ECX.
|
||||
CCIfType<[i32], CCAssignToReg<[ECX]>>
|
||||
]>;
|
||||
|
||||
def CC_X86_32_GHC : CallingConv<[
|
||||
// Promote i8/i16 arguments to i32.
|
||||
CCIfType<[i8, i16], CCPromoteToType<i32>>,
|
||||
@ -1000,6 +1010,7 @@ def CC_X86_32 : CallingConv<[
|
||||
CCIfCC<"CallingConv::X86_FastCall", CCDelegateTo<CC_X86_32_FastCall>>,
|
||||
CCIfCC<"CallingConv::X86_VectorCall", CCDelegateTo<CC_X86_Win32_VectorCall>>,
|
||||
CCIfCC<"CallingConv::X86_ThisCall", CCDelegateTo<CC_X86_32_ThisCall>>,
|
||||
CCIfCC<"CallingConv::CFGuard_Check", CCDelegateTo<CC_X86_Win32_CFGuard_Check>>,
|
||||
CCIfCC<"CallingConv::Fast", CCDelegateTo<CC_X86_32_FastCC>>,
|
||||
CCIfCC<"CallingConv::Tail", CCDelegateTo<CC_X86_32_FastCC>>,
|
||||
CCIfCC<"CallingConv::GHC", CCDelegateTo<CC_X86_32_GHC>>,
|
||||
@ -1136,7 +1147,9 @@ def CSR_64_HHVM : CalleeSavedRegs<(add R12)>;
|
||||
// Register calling convention preserves few GPR and XMM8-15
|
||||
def CSR_32_RegCall_NoSSE : CalleeSavedRegs<(add ESI, EDI, EBX, EBP, ESP)>;
|
||||
def CSR_32_RegCall : CalleeSavedRegs<(add CSR_32_RegCall_NoSSE,
|
||||
(sequence "XMM%u", 4, 7))>;
|
||||
(sequence "XMM%u", 4, 7))>;
|
||||
def CSR_Win32_CFGuard_Check_NoSSE : CalleeSavedRegs<(add CSR_32_RegCall_NoSSE, ECX)>;
|
||||
def CSR_Win32_CFGuard_Check : CalleeSavedRegs<(add CSR_32_RegCall, ECX)>;
|
||||
def CSR_Win64_RegCall_NoSSE : CalleeSavedRegs<(add RBX, RBP, RSP,
|
||||
(sequence "R%u", 10, 15))>;
|
||||
def CSR_Win64_RegCall : CalleeSavedRegs<(add CSR_Win64_RegCall_NoSSE,
|
||||
|
@ -3218,6 +3218,7 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) {
|
||||
case CallingConv::X86_ThisCall:
|
||||
case CallingConv::Win64:
|
||||
case CallingConv::X86_64_SysV:
|
||||
case CallingConv::CFGuard_Check:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -341,6 +341,10 @@ X86RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
|
||||
return (HasSSE ? CSR_32_RegCall_SaveList :
|
||||
CSR_32_RegCall_NoSSE_SaveList);
|
||||
}
|
||||
case CallingConv::CFGuard_Check:
|
||||
assert(!Is64Bit && "CFGuard check mechanism only used on 32-bit X86");
|
||||
return (HasSSE ? CSR_Win32_CFGuard_Check_SaveList
|
||||
: CSR_Win32_CFGuard_Check_NoSSE_SaveList);
|
||||
case CallingConv::Cold:
|
||||
if (Is64Bit)
|
||||
return CSR_64_MostRegs_SaveList;
|
||||
@ -455,6 +459,10 @@ X86RegisterInfo::getCallPreservedMask(const MachineFunction &MF,
|
||||
return (HasSSE ? CSR_32_RegCall_RegMask :
|
||||
CSR_32_RegCall_NoSSE_RegMask);
|
||||
}
|
||||
case CallingConv::CFGuard_Check:
|
||||
assert(!Is64Bit && "CFGuard check mechanism only used on 32-bit X86");
|
||||
return (HasSSE ? CSR_Win32_CFGuard_Check_RegMask
|
||||
: CSR_Win32_CFGuard_Check_NoSSE_RegMask);
|
||||
case CallingConv::Cold:
|
||||
if (Is64Bit)
|
||||
return CSR_64_MostRegs_RegMask;
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Target/TargetLoweringObjectFile.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
#include "llvm/Transforms/CFGuard.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@ -414,6 +415,16 @@ void X86PassConfig::addIRPasses() {
|
||||
// thunk. These will be a no-op unless a function subtarget has the retpoline
|
||||
// feature enabled.
|
||||
addPass(createIndirectBrExpandPass());
|
||||
|
||||
// Add Control Flow Guard checks.
|
||||
const Triple &TT = TM->getTargetTriple();
|
||||
if (TT.isOSWindows()) {
|
||||
if (TT.getArch() == Triple::x86_64) {
|
||||
addPass(createCFGuardDispatchPass());
|
||||
} else {
|
||||
addPass(createCFGuardCheckPass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool X86PassConfig::addInstSelector() {
|
||||
@ -530,6 +541,9 @@ void X86PassConfig::addPreEmitPass2() {
|
||||
(!TT.isOSWindows() ||
|
||||
MAI->getExceptionHandlingType() == ExceptionHandling::DwarfCFI))
|
||||
addPass(createCFIInstrInserter());
|
||||
// Identify valid longjmp targets for Windows Control Flow Guard.
|
||||
if (TT.isOSWindows())
|
||||
addPass(createCFGuardLongjmpPass());
|
||||
}
|
||||
|
||||
std::unique_ptr<CSEConfigBase> X86PassConfig::getCSEConfig() const {
|
||||
|
307
lib/Transforms/CFGuard/CFGuard.cpp
Normal file
307
lib/Transforms/CFGuard/CFGuard.cpp
Normal file
@ -0,0 +1,307 @@
|
||||
//===-- CFGuard.cpp - Control Flow Guard checks -----------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file contains the IR transform to add Microsoft's Control Flow Guard
|
||||
/// checks on Windows targets.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Transforms/CFGuard.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/IR/CallingConv.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/Instruction.h"
|
||||
#include "llvm/InitializePasses.h"
|
||||
#include "llvm/Pass.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
using OperandBundleDef = OperandBundleDefT<Value *>;
|
||||
|
||||
#define DEBUG_TYPE "cfguard"
|
||||
|
||||
STATISTIC(CFGuardCounter, "Number of Control Flow Guard checks added");
|
||||
|
||||
namespace {
|
||||
|
||||
/// Adds Control Flow Guard (CFG) checks on indirect function calls/invokes.
|
||||
/// These checks ensure that the target address corresponds to the start of an
|
||||
/// address-taken function. X86_64 targets use the CF_Dispatch mechanism. X86,
|
||||
/// ARM, and AArch64 targets use the CF_Check machanism.
|
||||
class CFGuard : public FunctionPass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
enum Mechanism { CF_Check, CF_Dispatch };
|
||||
|
||||
// Default constructor required for the INITIALIZE_PASS macro.
|
||||
CFGuard() : FunctionPass(ID) {
|
||||
initializeCFGuardPass(*PassRegistry::getPassRegistry());
|
||||
// By default, use the guard check mechanism.
|
||||
GuardMechanism = CF_Check;
|
||||
}
|
||||
|
||||
// Recommended constructor used to specify the type of guard mechanism.
|
||||
CFGuard(Mechanism Var) : FunctionPass(ID) {
|
||||
initializeCFGuardPass(*PassRegistry::getPassRegistry());
|
||||
GuardMechanism = Var;
|
||||
}
|
||||
|
||||
/// Inserts a Control Flow Guard (CFG) check on an indirect call using the CFG
|
||||
/// check mechanism. When the image is loaded, the loader puts the appropriate
|
||||
/// guard check function pointer in the __guard_check_icall_fptr global
|
||||
/// symbol. This checks that the target address is a valid address-taken
|
||||
/// function. The address of the target function is passed to the guard check
|
||||
/// function in an architecture-specific register (e.g. ECX on 32-bit X86,
|
||||
/// X15 on Aarch64, and R0 on ARM). The guard check function has no return
|
||||
/// value (if the target is invalid, the guard check funtion will raise an
|
||||
/// error).
|
||||
///
|
||||
/// For example, the following LLVM IR:
|
||||
/// \code
|
||||
/// %func_ptr = alloca i32 ()*, align 8
|
||||
/// store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
/// %0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
/// %1 = call i32 %0()
|
||||
/// \endcode
|
||||
///
|
||||
/// is transformed to:
|
||||
/// \code
|
||||
/// %func_ptr = alloca i32 ()*, align 8
|
||||
/// store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
/// %0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
/// %1 = load void (i8*)*, void (i8*)** @__guard_check_icall_fptr
|
||||
/// %2 = bitcast i32 ()* %0 to i8*
|
||||
/// call cfguard_checkcc void %1(i8* %2)
|
||||
/// %3 = call i32 %0()
|
||||
/// \endcode
|
||||
///
|
||||
/// For example, the following X86 assembly code:
|
||||
/// \code
|
||||
/// movl $_target_func, %eax
|
||||
/// calll *%eax
|
||||
/// \endcode
|
||||
///
|
||||
/// is transformed to:
|
||||
/// \code
|
||||
/// movl $_target_func, %ecx
|
||||
/// calll *___guard_check_icall_fptr
|
||||
/// calll *%ecx
|
||||
/// \endcode
|
||||
///
|
||||
/// \param CB indirect call to instrument.
|
||||
void insertCFGuardCheck(CallBase *CB);
|
||||
|
||||
/// Inserts a Control Flow Guard (CFG) check on an indirect call using the CFG
|
||||
/// dispatch mechanism. When the image is loaded, the loader puts the
|
||||
/// appropriate guard check function pointer in the
|
||||
/// __guard_dispatch_icall_fptr global symbol. This checks that the target
|
||||
/// address is a valid address-taken function and, if so, tail calls the
|
||||
/// target. The target address is passed in an architecture-specific register
|
||||
/// (e.g. RAX on X86_64), with all other arguments for the target function
|
||||
/// passed as usual.
|
||||
///
|
||||
/// For example, the following LLVM IR:
|
||||
/// \code
|
||||
/// %func_ptr = alloca i32 ()*, align 8
|
||||
/// store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
/// %0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
/// %1 = call i32 %0()
|
||||
/// \endcode
|
||||
///
|
||||
/// is transformed to:
|
||||
/// \code
|
||||
/// %func_ptr = alloca i32 ()*, align 8
|
||||
/// store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
/// %0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
/// %1 = load i32 ()*, i32 ()** @__guard_dispatch_icall_fptr
|
||||
/// %2 = call i32 %1() [ "cfguardtarget"(i32 ()* %0) ]
|
||||
/// \endcode
|
||||
///
|
||||
/// For example, the following X86_64 assembly code:
|
||||
/// \code
|
||||
/// leaq target_func(%rip), %rax
|
||||
/// callq *%rax
|
||||
/// \endcode
|
||||
///
|
||||
/// is transformed to:
|
||||
/// \code
|
||||
/// leaq target_func(%rip), %rax
|
||||
/// callq *__guard_dispatch_icall_fptr(%rip)
|
||||
/// \endcode
|
||||
///
|
||||
/// \param CB indirect call to instrument.
|
||||
void insertCFGuardDispatch(CallBase *CB);
|
||||
|
||||
bool doInitialization(Module &M) override;
|
||||
bool runOnFunction(Function &F) override;
|
||||
|
||||
private:
|
||||
// Only add checks if the module has the cfguard=2 flag.
|
||||
int cfguard_module_flag = 0;
|
||||
Mechanism GuardMechanism = CF_Check;
|
||||
FunctionType *GuardFnType = nullptr;
|
||||
PointerType *GuardFnPtrType = nullptr;
|
||||
Constant *GuardFnGlobal = nullptr;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
void CFGuard::insertCFGuardCheck(CallBase *CB) {
|
||||
|
||||
assert(Triple(CB->getModule()->getTargetTriple()).isOSWindows() &&
|
||||
"Only applicable for Windows targets");
|
||||
assert(CB->isIndirectCall() &&
|
||||
"Control Flow Guard checks can only be added to indirect calls");
|
||||
|
||||
IRBuilder<> B(CB);
|
||||
Value *CalledOperand = CB->getCalledOperand();
|
||||
|
||||
// Load the global symbol as a pointer to the check function.
|
||||
LoadInst *GuardCheckLoad = B.CreateLoad(GuardFnPtrType, GuardFnGlobal);
|
||||
|
||||
// Create new call instruction. The CFGuard check should always be a call,
|
||||
// even if the original CallBase is an Invoke or CallBr instruction.
|
||||
CallInst *GuardCheck =
|
||||
B.CreateCall(GuardFnType, GuardCheckLoad,
|
||||
{B.CreateBitCast(CalledOperand, B.getInt8PtrTy())});
|
||||
|
||||
// Ensure that the first argument is passed in the correct register
|
||||
// (e.g. ECX on 32-bit X86 targets).
|
||||
GuardCheck->setCallingConv(CallingConv::CFGuard_Check);
|
||||
}
|
||||
|
||||
void CFGuard::insertCFGuardDispatch(CallBase *CB) {
|
||||
|
||||
assert(Triple(CB->getModule()->getTargetTriple()).isOSWindows() &&
|
||||
"Only applicable for Windows targets");
|
||||
assert(CB->isIndirectCall() &&
|
||||
"Control Flow Guard checks can only be added to indirect calls");
|
||||
|
||||
IRBuilder<> B(CB);
|
||||
Value *CalledOperand = CB->getCalledOperand();
|
||||
Type *CalledOperandType = CalledOperand->getType();
|
||||
|
||||
// Cast the guard dispatch global to the type of the called operand.
|
||||
PointerType *PTy = PointerType::get(CalledOperandType, 0);
|
||||
if (GuardFnGlobal->getType() != PTy)
|
||||
GuardFnGlobal = ConstantExpr::getBitCast(GuardFnGlobal, PTy);
|
||||
|
||||
// Load the global as a pointer to a function of the same type.
|
||||
LoadInst *GuardDispatchLoad = B.CreateLoad(CalledOperandType, GuardFnGlobal);
|
||||
|
||||
// Add the original call target as a cfguardtarget operand bundle.
|
||||
SmallVector<llvm::OperandBundleDef, 1> Bundles;
|
||||
CB->getOperandBundlesAsDefs(Bundles);
|
||||
Bundles.emplace_back("cfguardtarget", CalledOperand);
|
||||
|
||||
// Create a copy of the call/invoke instruction and add the new bundle.
|
||||
CallBase *NewCB;
|
||||
if (CallInst *CI = dyn_cast<CallInst>(CB)) {
|
||||
NewCB = CallInst::Create(CI, Bundles, CB);
|
||||
} else {
|
||||
assert(isa<InvokeInst>(CB) && "Unknown indirect call type");
|
||||
InvokeInst *II = cast<InvokeInst>(CB);
|
||||
NewCB = llvm::InvokeInst::Create(II, Bundles, CB);
|
||||
}
|
||||
|
||||
// Change the target of the call to be the guard dispatch function.
|
||||
NewCB->setCalledOperand(GuardDispatchLoad);
|
||||
|
||||
// Replace the original call/invoke with the new instruction.
|
||||
CB->replaceAllUsesWith(NewCB);
|
||||
|
||||
// Delete the original call/invoke.
|
||||
CB->eraseFromParent();
|
||||
}
|
||||
|
||||
bool CFGuard::doInitialization(Module &M) {
|
||||
|
||||
// Check if this module has the cfguard flag and read its value.
|
||||
if (auto *MD =
|
||||
mdconst::extract_or_null<ConstantInt>(M.getModuleFlag("cfguard")))
|
||||
cfguard_module_flag = MD->getZExtValue();
|
||||
|
||||
// Skip modules for which CFGuard checks have been disabled.
|
||||
if (cfguard_module_flag != 2)
|
||||
return false;
|
||||
|
||||
// Set up prototypes for the guard check and dispatch functions.
|
||||
GuardFnType = FunctionType::get(Type::getVoidTy(M.getContext()),
|
||||
{Type::getInt8PtrTy(M.getContext())}, false);
|
||||
GuardFnPtrType = PointerType::get(GuardFnType, 0);
|
||||
|
||||
// Get or insert the guard check or dispatch global symbols.
|
||||
if (GuardMechanism == CF_Check) {
|
||||
GuardFnGlobal =
|
||||
M.getOrInsertGlobal("__guard_check_icall_fptr", GuardFnPtrType);
|
||||
} else {
|
||||
assert(GuardMechanism == CF_Dispatch && "Invalid CFGuard mechanism");
|
||||
GuardFnGlobal =
|
||||
M.getOrInsertGlobal("__guard_dispatch_icall_fptr", GuardFnPtrType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CFGuard::runOnFunction(Function &F) {
|
||||
|
||||
// Skip modules and functions for which CFGuard checks have been disabled.
|
||||
if (cfguard_module_flag != 2 || F.hasFnAttribute(Attribute::NoCfCheck))
|
||||
return false;
|
||||
|
||||
SmallVector<CallBase *, 8> IndirectCalls;
|
||||
|
||||
// Iterate over the instructions to find all indirect call/invoke/callbr
|
||||
// instructions. Make a separate list of pointers to indirect
|
||||
// call/invoke/callbr instructions because the original instructions will be
|
||||
// deleted as the checks are added.
|
||||
for (BasicBlock &BB : F.getBasicBlockList()) {
|
||||
for (Instruction &I : BB.getInstList()) {
|
||||
auto *CB = dyn_cast<CallBase>(&I);
|
||||
if (CB && CB->isIndirectCall()) {
|
||||
IndirectCalls.push_back(CB);
|
||||
CFGuardCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no checks are needed, return early and add this attribute to indicate
|
||||
// that subsequent CFGuard passes can skip this function.
|
||||
if (IndirectCalls.empty()) {
|
||||
F.addFnAttr(Attribute::NoCfCheck);
|
||||
return false;
|
||||
}
|
||||
|
||||
// For each indirect call/invoke, add the appropriate dispatch or check.
|
||||
if (GuardMechanism == CF_Dispatch) {
|
||||
for (CallBase *CB : IndirectCalls) {
|
||||
insertCFGuardDispatch(CB);
|
||||
}
|
||||
} else {
|
||||
for (CallBase *CB : IndirectCalls) {
|
||||
insertCFGuardCheck(CB);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char CFGuard::ID = 0;
|
||||
INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false)
|
||||
|
||||
FunctionPass *llvm::createCFGuardCheckPass() {
|
||||
return new CFGuard(CFGuard::CF_Check);
|
||||
}
|
||||
|
||||
FunctionPass *llvm::createCFGuardDispatchPass() {
|
||||
return new CFGuard(CFGuard::CF_Dispatch);
|
||||
}
|
9
lib/Transforms/CFGuard/CMakeLists.txt
Normal file
9
lib/Transforms/CFGuard/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
add_llvm_library(LLVMCFGuard
|
||||
CFGuard.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms
|
||||
|
||||
DEPENDS
|
||||
intrinsics_gen
|
||||
)
|
21
lib/Transforms/CFGuard/LLVMBuild.txt
Normal file
21
lib/Transforms/CFGuard/LLVMBuild.txt
Normal file
@ -0,0 +1,21 @@
|
||||
;===- ./lib/Transforms/CFGuard/LLVMBuild.txt -------------------*- Conf -*--===;
|
||||
;
|
||||
; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
; See https://llvm.org/LICENSE.txt for license information.
|
||||
; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
;
|
||||
; This is an LLVMBuild description file for the components in this subdirectory.
|
||||
;
|
||||
; For more information on the LLVMBuild system, please see:
|
||||
;
|
||||
; http://llvm.org/docs/LLVMBuild.html
|
||||
;
|
||||
;===------------------------------------------------------------------------===;
|
||||
|
||||
[component_0]
|
||||
type = Library
|
||||
name = CFGuard
|
||||
parent = Transforms
|
||||
required_libraries = Core Support
|
@ -8,3 +8,4 @@ add_subdirectory(Vectorize)
|
||||
add_subdirectory(Hello)
|
||||
add_subdirectory(ObjCARC)
|
||||
add_subdirectory(Coroutines)
|
||||
add_subdirectory(CFGuard)
|
||||
|
@ -15,7 +15,7 @@
|
||||
;===------------------------------------------------------------------------===;
|
||||
|
||||
[common]
|
||||
subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC
|
||||
subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC CFGuard
|
||||
|
||||
[component_0]
|
||||
type = Group
|
||||
|
@ -26,6 +26,9 @@ declare spir_func void @spir_func()
|
||||
declare intel_ocl_bicc void @intel_ocl_bicc()
|
||||
; CHECK: declare intel_ocl_bicc void @intel_ocl_bicc
|
||||
|
||||
declare cfguard_checkcc void @cfguard_checkcc()
|
||||
; CHECK: declare cfguard_checkcc void @cfguard_checkcc
|
||||
|
||||
declare x86_stdcallcc void @x86_stdcallcc()
|
||||
; CHECK: declare x86_stdcallcc void @x86_stdcallcc
|
||||
|
||||
@ -95,6 +98,12 @@ define void @call_intel_ocl_bicc() {
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @call_cfguard_checkcc() {
|
||||
; CHECK: call cfguard_checkcc void @cfguard_checkcc
|
||||
call cfguard_checkcc void @cfguard_checkcc()
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @call_x86_stdcallcc() {
|
||||
; CHECK: call x86_stdcallcc void @x86_stdcallcc
|
||||
call x86_stdcallcc void @x86_stdcallcc()
|
||||
|
Binary file not shown.
@ -6,6 +6,7 @@
|
||||
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
|
||||
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
|
||||
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
|
||||
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
|
||||
; CHECK-NEXT: </OPERAND_BUNDLE_TAGS_BLOCK
|
||||
|
||||
; CHECK: <FUNCTION_BLOCK
|
||||
|
147
test/CodeGen/AArch64/cfguard-checks.ll
Normal file
147
test/CodeGen/AArch64/cfguard-checks.ll
Normal file
@ -0,0 +1,147 @@
|
||||
; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s
|
||||
; Control Flow Guard is currently only available on Windows
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added when required.
|
||||
|
||||
|
||||
declare i32 @target_func()
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are not added to functions with nocf_checks attribute.
|
||||
define i32 @func_nocf_checks() #0 {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = call i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; CHECK-LABEL: func_nocf_checks
|
||||
; CHECK: adrp x8, target_func
|
||||
; CHECK: add x8, x8, target_func
|
||||
; CHECK-NOT: __guard_check_icall_fptr
|
||||
; CHECK: blr x8
|
||||
}
|
||||
attributes #0 = { nocf_check }
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are added even at -O0.
|
||||
define i32 @func_optnone_cf() #1 {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = call i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; The call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; CHECK-LABEL: func_optnone_cf
|
||||
; CHECK: adrp x8, __guard_check_icall_fptr
|
||||
; CHECK: add x9, x8, __guard_check_icall_fptr
|
||||
; CHECK: adrp x8, target_func
|
||||
; CHECK: add x8, x8, target_func
|
||||
; CHECK: ldr x9, [x9]
|
||||
; CHECK: mov x15, x8
|
||||
; CHECK: blr x9
|
||||
; CHECK-NEXT: blr x8
|
||||
}
|
||||
attributes #1 = { noinline optnone }
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added in optimized code (common case).
|
||||
define i32 @func_cf() {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = call i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; The call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; CHECK-LABEL: func_cf
|
||||
; CHECK: adrp x8, __guard_check_icall_fptr
|
||||
; CHECK: ldr x9, [x8, __guard_check_icall_fptr]
|
||||
; CHECK: adrp x8, target_func
|
||||
; CHECK: add x8, x8, target_func
|
||||
; CHECK: mov x15, x8
|
||||
; CHECK: blr x9
|
||||
; CHECK-NEXT: blr x8
|
||||
}
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added on invoke instructions.
|
||||
define i32 @func_cf_invoke() personality i8* bitcast (void ()* @h to i8*) {
|
||||
entry:
|
||||
%0 = alloca i32, align 4
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%1 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%2 = invoke i32 %1()
|
||||
to label %invoke.cont unwind label %lpad
|
||||
invoke.cont: ; preds = %entry
|
||||
ret i32 %2
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%tmp = landingpad { i8*, i32 }
|
||||
catch i8* null
|
||||
ret i32 -1
|
||||
|
||||
; The call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; CHECK-LABEL: func_cf_invoke
|
||||
; CHECK: adrp x8, __guard_check_icall_fptr
|
||||
; CHECK: ldr x9, [x8, __guard_check_icall_fptr]
|
||||
; CHECK: adrp x8, target_func
|
||||
; CHECK: add x8, x8, target_func
|
||||
; CHECK: mov x15, x8
|
||||
; CHECK: blr x9
|
||||
; CHECK-NEXT: .Ltmp0:
|
||||
; CHECK-NEXT: blr x8
|
||||
; CHECK: ; %invoke.cont
|
||||
; CHECK: ; %lpad
|
||||
}
|
||||
|
||||
declare void @h()
|
||||
|
||||
|
||||
; Test that longjmp targets have public labels and are included in the .gljmp section.
|
||||
%struct._SETJMP_FLOAT128 = type { [2 x i64] }
|
||||
@buf1 = internal global [16 x %struct._SETJMP_FLOAT128] zeroinitializer, align 16
|
||||
|
||||
define i32 @func_cf_setjmp() {
|
||||
%1 = alloca i32, align 4
|
||||
%2 = alloca i32, align 4
|
||||
store i32 0, i32* %1, align 4
|
||||
store i32 -1, i32* %2, align 4
|
||||
%3 = call i8* @llvm.frameaddress(i32 0)
|
||||
%4 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %3) #2
|
||||
|
||||
; CHECK-LABEL: func_cf_setjmp
|
||||
; CHECK: bl _setjmp
|
||||
; CHECK-NEXT: $cfgsj_func_cf_setjmp0:
|
||||
|
||||
%5 = call i8* @llvm.frameaddress(i32 0)
|
||||
%6 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %5) #3
|
||||
|
||||
; CHECK: bl _setjmp
|
||||
; CHECK-NEXT: $cfgsj_func_cf_setjmp1:
|
||||
|
||||
store i32 1, i32* %2, align 4
|
||||
%7 = load i32, i32* %2, align 4
|
||||
ret i32 %7
|
||||
|
||||
; CHECK: .section .gljmp$y,"dr"
|
||||
; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp0
|
||||
; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp1
|
||||
}
|
||||
|
||||
declare i8* @llvm.frameaddress(i32)
|
||||
|
||||
; Function Attrs: returns_twice
|
||||
declare dso_local i32 @_setjmp(i8*, i8*) #2
|
||||
|
||||
attributes #2 = { returns_twice }
|
||||
attributes #3 = { returns_twice }
|
||||
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 2, !"cfguard", i32 2}
|
25
test/CodeGen/AArch64/cfguard-module-flag.ll
Normal file
25
test/CodeGen/AArch64/cfguard-module-flag.ll
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s
|
||||
; Control Flow Guard is currently only available on Windows
|
||||
|
||||
; Test that Control Flow Guard checks are not added in modules with the
|
||||
; cfguard=1 flag (emit tables but no checks).
|
||||
|
||||
|
||||
declare void @target_func()
|
||||
|
||||
define void @func_in_module_without_cfguard() #0 {
|
||||
entry:
|
||||
%func_ptr = alloca void ()*, align 8
|
||||
store void ()* @target_func, void ()** %func_ptr, align 8
|
||||
%0 = load void ()*, void ()** %func_ptr, align 8
|
||||
|
||||
call void %0()
|
||||
ret void
|
||||
|
||||
; CHECK-NOT: __guard_check_icall_fptr
|
||||
; CHECK-NOT: __guard_dispatch_icall_fptr
|
||||
}
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 2, !"cfguard", i32 1}
|
151
test/CodeGen/ARM/cfguard-checks.ll
Normal file
151
test/CodeGen/ARM/cfguard-checks.ll
Normal file
@ -0,0 +1,151 @@
|
||||
; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s
|
||||
; Control Flow Guard is currently only available on Windows
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added when required.
|
||||
|
||||
|
||||
declare i32 @target_func()
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are not added to functions with nocf_checks attribute.
|
||||
define i32 @func_nocf_checks() #0 {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = call arm_aapcs_vfpcc i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; CHECK-LABEL: func_nocf_checks
|
||||
; CHECK: movw r0, :lower16:target_func
|
||||
; CHECK: movt r0, :upper16:target_func
|
||||
; CHECK-NOT: __guard_check_icall_fptr
|
||||
; CHECK: blx r0
|
||||
}
|
||||
attributes #0 = { nocf_check "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"}
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are added even at -O0.
|
||||
define i32 @func_optnone_cf() #1 {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = call i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; The call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; CHECK-LABEL: func_optnone_cf
|
||||
; CHECK: movw r0, :lower16:target_func
|
||||
; CHECK: movt r0, :upper16:target_func
|
||||
; CHECK: str r0, [sp]
|
||||
; CHECK: ldr r4, [sp]
|
||||
; CHECK: movw r0, :lower16:__guard_check_icall_fptr
|
||||
; CHECK: movt r0, :upper16:__guard_check_icall_fptr
|
||||
; CHECK: ldr r1, [r0]
|
||||
; CHECK: mov r0, r4
|
||||
; CHECK: blx r1
|
||||
; CHECK-NEXT: blx r4
|
||||
}
|
||||
attributes #1 = { noinline optnone "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"}
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added in optimized code (common case).
|
||||
define i32 @func_cf() #2 {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = call i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; The call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; CHECK-LABEL: func_cf
|
||||
; CHECK: movw r0, :lower16:__guard_check_icall_fptr
|
||||
; CHECK: movt r0, :upper16:__guard_check_icall_fptr
|
||||
; CHECK: ldr r1, [r0]
|
||||
; CHECK: movw r4, :lower16:target_func
|
||||
; CHECK: movt r4, :upper16:target_func
|
||||
; CHECK: mov r0, r4
|
||||
; CHECK: blx r1
|
||||
; CHECK-NEXT: blx r4
|
||||
}
|
||||
attributes #2 = { "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3"}
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added on invoke instructions.
|
||||
define i32 @func_cf_invoke() #2 personality i8* bitcast (void ()* @h to i8*) {
|
||||
entry:
|
||||
%0 = alloca i32, align 4
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%1 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%2 = invoke i32 %1()
|
||||
to label %invoke.cont unwind label %lpad
|
||||
invoke.cont: ; preds = %entry
|
||||
ret i32 %2
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%tmp = landingpad { i8*, i32 }
|
||||
catch i8* null
|
||||
ret i32 -1
|
||||
|
||||
; The call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; CHECK-LABEL: func_cf_invoke
|
||||
; CHECK: movw r0, :lower16:__guard_check_icall_fptr
|
||||
; CHECK: movt r0, :upper16:__guard_check_icall_fptr
|
||||
; CHECK: ldr r1, [r0]
|
||||
; CHECK: movw r4, :lower16:target_func
|
||||
; CHECK: movt r4, :upper16:target_func
|
||||
; CHECK: mov r0, r4
|
||||
; CHECK: blx r1
|
||||
; CHECK-NEXT: $Mtmp0:
|
||||
; CHECK-NEXT: blx r4
|
||||
; CHECK: ; %invoke.cont
|
||||
; CHECK: ; %lpad
|
||||
}
|
||||
|
||||
declare void @h()
|
||||
|
||||
|
||||
; Test that longjmp targets have public labels and are included in the .gljmp section.
|
||||
%struct._SETJMP_FLOAT128 = type { [2 x i64] }
|
||||
@buf1 = internal global [16 x %struct._SETJMP_FLOAT128] zeroinitializer, align 16
|
||||
|
||||
define i32 @func_cf_setjmp() #2 {
|
||||
%1 = alloca i32, align 4
|
||||
%2 = alloca i32, align 4
|
||||
store i32 0, i32* %1, align 4
|
||||
store i32 -1, i32* %2, align 4
|
||||
%3 = call i8* @llvm.frameaddress(i32 0)
|
||||
%4 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %3) #3
|
||||
|
||||
; CHECK-LABEL: func_cf_setjmp
|
||||
; CHECK: bl _setjmp
|
||||
; CHECK-NEXT: $cfgsj_func_cf_setjmp0:
|
||||
|
||||
%5 = call i8* @llvm.frameaddress(i32 0)
|
||||
%6 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %5) #3
|
||||
|
||||
; CHECK: bl _setjmp
|
||||
; CHECK-NEXT: $cfgsj_func_cf_setjmp1:
|
||||
|
||||
store i32 1, i32* %2, align 4
|
||||
%7 = load i32, i32* %2, align 4
|
||||
ret i32 %7
|
||||
|
||||
; CHECK: .section .gljmp$y,"dr"
|
||||
; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp0
|
||||
; CHECK-NEXT: .symidx $cfgsj_func_cf_setjmp1
|
||||
}
|
||||
|
||||
declare i8* @llvm.frameaddress(i32)
|
||||
|
||||
; Function Attrs: returns_twice
|
||||
declare dso_local i32 @_setjmp(i8*, i8*) #3
|
||||
|
||||
attributes #3 = { returns_twice }
|
||||
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 2, !"cfguard", i32 2}
|
26
test/CodeGen/ARM/cfguard-module-flag.ll
Normal file
26
test/CodeGen/ARM/cfguard-module-flag.ll
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s
|
||||
; Control Flow Guard is currently only available on Windows
|
||||
|
||||
; Test that Control Flow Guard checks are not added in modules with the
|
||||
; cfguard=1 flag (emit tables but no checks).
|
||||
|
||||
|
||||
declare void @target_func()
|
||||
|
||||
define void @func_in_module_without_cfguard() #0 {
|
||||
entry:
|
||||
%func_ptr = alloca void ()*, align 8
|
||||
store void ()* @target_func, void ()** %func_ptr, align 8
|
||||
%0 = load void ()*, void ()** %func_ptr, align 8
|
||||
|
||||
call void %0()
|
||||
ret void
|
||||
|
||||
; CHECK-NOT: __guard_check_icall_fptr
|
||||
; CHECK-NOT: __guard_dispatch_icall_fptr
|
||||
}
|
||||
attributes #0 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" "target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3" "unsafe-fp-math"="false" "use-soft-float"="false"}
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 2, !"cfguard", i32 1}
|
@ -1,4 +1,5 @@
|
||||
; RUN: llc < %s | FileCheck %s
|
||||
; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s
|
||||
; Control Flow Guard is currently only available on Windows
|
||||
|
||||
; CHECK: .set @feat.00, 2048
|
||||
|
||||
@ -159,6 +160,6 @@ attributes #2 = { nounwind }
|
||||
!llvm.module.flags = !{!0, !1}
|
||||
!llvm.ident = !{!2}
|
||||
|
||||
!0 = !{i32 2, !"cfguardtable", i32 1}
|
||||
!0 = !{i32 2, !"cfguard", i32 1}
|
||||
!1 = !{i32 1, !"wchar_size", i32 2}
|
||||
!2 = !{!"clang version 6.0.0 "}
|
||||
|
231
test/CodeGen/X86/cfguard-checks.ll
Normal file
231
test/CodeGen/X86/cfguard-checks.ll
Normal file
@ -0,0 +1,231 @@
|
||||
; RUN: llc < %s -mtriple=i686-pc-windows-msvc | FileCheck %s -check-prefix=X32
|
||||
; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s -check-prefix=X64
|
||||
; Control Flow Guard is currently only available on Windows
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added when required.
|
||||
|
||||
|
||||
declare i32 @target_func()
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are not added to functions with nocf_checks attribute.
|
||||
define i32 @func_nocf_checks() #0 {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = call i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; X32-LABEL: func_nocf_checks
|
||||
; X32: movl $_target_func, %eax
|
||||
; X32-NOT: __guard_check_icall_fptr
|
||||
; X32: calll *%eax
|
||||
|
||||
; X64-LABEL: func_nocf_checks
|
||||
; X64: leaq target_func(%rip), %rax
|
||||
; X64-NOT: __guard_dispatch_icall_fptr
|
||||
; X64: callq *%rax
|
||||
}
|
||||
attributes #0 = { nocf_check }
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are added even at -O0.
|
||||
; FIXME Ideally these checks should be added as a single call instruction, as in the optimized case.
|
||||
define i32 @func_optnone_cf() #1 {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = call i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; X32-LABEL: func_optnone_cf
|
||||
; X32: leal _target_func, %eax
|
||||
; X32: movl %eax, (%esp)
|
||||
; X32: movl (%esp), %ecx
|
||||
; X32: movl ___guard_check_icall_fptr, %eax
|
||||
; X32: calll *%eax
|
||||
; X32-NEXT: calll *%ecx
|
||||
|
||||
; On x86_64, __guard_dispatch_icall_fptr tail calls the function, so there should be only one call instruction.
|
||||
; X64-LABEL: func_optnone_cf
|
||||
; X64: leaq target_func(%rip), %rax
|
||||
; X64: movq __guard_dispatch_icall_fptr(%rip), %rcx
|
||||
; X64: callq *%rcx
|
||||
; X64-NOT: callq
|
||||
}
|
||||
attributes #1 = { noinline optnone }
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added in optimized code (common case).
|
||||
define i32 @func_cf() {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = call i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; X32-LABEL: func_cf
|
||||
; X32: movl $_target_func, %esi
|
||||
; X32: movl $_target_func, %ecx
|
||||
; X32: calll *___guard_check_icall_fptr
|
||||
; X32-NEXT: calll *%esi
|
||||
|
||||
; On x86_64, __guard_dispatch_icall_fptr tail calls the function, so there should be only one call instruction.
|
||||
; X64-LABEL: func_cf
|
||||
; X64: leaq target_func(%rip), %rax
|
||||
; X64: callq *__guard_dispatch_icall_fptr(%rip)
|
||||
; X64-NOT: callq
|
||||
}
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added on invoke instructions.
|
||||
define i32 @func_cf_invoke() personality i8* bitcast (void ()* @h to i8*) {
|
||||
entry:
|
||||
%0 = alloca i32, align 4
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%1 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%2 = invoke i32 %1()
|
||||
to label %invoke.cont unwind label %lpad
|
||||
invoke.cont: ; preds = %entry
|
||||
ret i32 %2
|
||||
|
||||
lpad: ; preds = %entry
|
||||
%tmp = landingpad { i8*, i32 }
|
||||
catch i8* null
|
||||
ret i32 -1
|
||||
|
||||
; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; X32-LABEL: func_cf_invoke
|
||||
; X32: movl $_target_func, %esi
|
||||
; X32: movl $_target_func, %ecx
|
||||
; X32: calll *___guard_check_icall_fptr
|
||||
; X32-NEXT: calll *%esi
|
||||
; X32: # %invoke.cont
|
||||
; X32: # %lpad
|
||||
|
||||
; On x86_64, __guard_dispatch_icall_fptr tail calls the function, so there should be only one call instruction.
|
||||
; X64-LABEL: func_cf_invoke
|
||||
; X64: leaq target_func(%rip), %rax
|
||||
; X64: callq *__guard_dispatch_icall_fptr(%rip)
|
||||
; X64-NOT: callq
|
||||
; X64: # %invoke.cont
|
||||
; X64: # %lpad
|
||||
}
|
||||
|
||||
declare void @h()
|
||||
|
||||
|
||||
; Test that Control Flow Guard preserves floating point arguments.
|
||||
declare double @target_func_doubles(double, double, double, double)
|
||||
|
||||
define double @func_cf_doubles() {
|
||||
entry:
|
||||
%func_ptr = alloca double (double, double, double, double)*, align 8
|
||||
store double (double, double, double, double)* @target_func_doubles, double (double, double, double, double)** %func_ptr, align 8
|
||||
%0 = load double (double, double, double, double)*, double (double, double, double, double)** %func_ptr, align 8
|
||||
%1 = call double %0(double 1.000000e+00, double 2.000000e+00, double 3.000000e+00, double 4.000000e+00)
|
||||
ret double %1
|
||||
|
||||
; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; X32-LABEL: func_cf_doubles
|
||||
; X32: movl $_target_func_doubles, %esi
|
||||
; X32: movl $_target_func_doubles, %ecx
|
||||
; X32: calll *___guard_check_icall_fptr
|
||||
; X32: calll *%esi
|
||||
|
||||
|
||||
; On x86_64, __guard_dispatch_icall_fptr tail calls the function, so there should be only one call instruction.
|
||||
; X64-LABEL: func_cf_doubles
|
||||
; X64: leaq target_func_doubles(%rip), %rax
|
||||
; X64: movsd __real@3ff0000000000000(%rip), %xmm0
|
||||
; X64: movsd __real@4000000000000000(%rip), %xmm1
|
||||
; X64: movsd __real@4008000000000000(%rip), %xmm2
|
||||
; X64: movsd __real@4010000000000000(%rip), %xmm3
|
||||
; X64: callq *__guard_dispatch_icall_fptr(%rip)
|
||||
; X64-NOT: callq
|
||||
}
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added for tail calls.
|
||||
define i32 @func_cf_tail() {
|
||||
entry:
|
||||
%func_ptr = alloca i32 ()*, align 8
|
||||
store i32 ()* @target_func, i32 ()** %func_ptr, align 8
|
||||
%0 = load i32 ()*, i32 ()** %func_ptr, align 8
|
||||
%1 = musttail call i32 %0()
|
||||
ret i32 %1
|
||||
|
||||
; On i686, the call to __guard_check_icall_fptr should come immediately before the call to the target function.
|
||||
; X32-LABEL: func_cf_tail
|
||||
; X32: movl $_target_func, %ecx
|
||||
; X32: calll *___guard_check_icall_fptr
|
||||
; X32: movl $_target_func, %eax
|
||||
; X32: jmpl *%eax # TAILCALL
|
||||
; X32-NOT: calll
|
||||
|
||||
; X64-LABEL: func_cf_tail
|
||||
; X64: leaq target_func(%rip), %rax
|
||||
; X64: movq __guard_dispatch_icall_fptr(%rip), %rcx
|
||||
; X64: rex64 jmpq *%rcx # TAILCALL
|
||||
; X64-NOT: callq
|
||||
}
|
||||
|
||||
|
||||
; Test that longjmp targets have public labels and are included in the .gljmp section.
|
||||
%struct._SETJMP_FLOAT128 = type { [2 x i64] }
|
||||
@buf1 = internal global [16 x %struct._SETJMP_FLOAT128] zeroinitializer, align 16
|
||||
|
||||
define i32 @func_cf_setjmp() {
|
||||
%1 = alloca i32, align 4
|
||||
%2 = alloca i32, align 4
|
||||
store i32 0, i32* %1, align 4
|
||||
store i32 -1, i32* %2, align 4
|
||||
%3 = call i8* @llvm.frameaddress(i32 0)
|
||||
%4 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %3) #2
|
||||
|
||||
; X32-LABEL: func_cf_setjmp
|
||||
; X32: calll __setjmp
|
||||
; X32-NEXT: $cfgsj_func_cf_setjmp0:
|
||||
|
||||
; X64-LABEL: func_cf_setjmp
|
||||
; X64: callq _setjmp
|
||||
; X64-NEXT: $cfgsj_func_cf_setjmp0:
|
||||
|
||||
%5 = call i8* @llvm.frameaddress(i32 0)
|
||||
%6 = call i32 @_setjmp(i8* bitcast ([16 x %struct._SETJMP_FLOAT128]* @buf1 to i8*), i8* %5) #2
|
||||
|
||||
; X32: calll __setjmp
|
||||
; X32-NEXT: $cfgsj_func_cf_setjmp1:
|
||||
|
||||
; X64: callq _setjmp
|
||||
; X64-NEXT: $cfgsj_func_cf_setjmp1:
|
||||
|
||||
store i32 1, i32* %2, align 4
|
||||
%7 = load i32, i32* %2, align 4
|
||||
ret i32 %7
|
||||
|
||||
; X32: .section .gljmp$y,"dr"
|
||||
; X32-NEXT: .symidx $cfgsj_func_cf_setjmp0
|
||||
; X32-NEXT: .symidx $cfgsj_func_cf_setjmp1
|
||||
|
||||
; X64: .section .gljmp$y,"dr"
|
||||
; X64-NEXT: .symidx $cfgsj_func_cf_setjmp0
|
||||
; X64-NEXT: .symidx $cfgsj_func_cf_setjmp1
|
||||
}
|
||||
|
||||
declare i8* @llvm.frameaddress(i32)
|
||||
|
||||
; Function Attrs: returns_twice
|
||||
declare dso_local i32 @_setjmp(i8*, i8*) #2
|
||||
|
||||
attributes #2 = { returns_twice }
|
||||
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 2, !"cfguard", i32 2}
|
26
test/CodeGen/X86/cfguard-module-flag.ll
Normal file
26
test/CodeGen/X86/cfguard-module-flag.ll
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
; RUN: llc < %s -mtriple=i686-pc-windows-msvc | FileCheck %s -check-prefix=X32
|
||||
; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s -check-prefix=X64
|
||||
; Control Flow Guard is currently only available on Windows
|
||||
|
||||
; Test that Control Flow Guard checks are not added in modules with the
|
||||
; cfguard=1 flag (emit tables but no checks).
|
||||
|
||||
|
||||
declare void @target_func()
|
||||
|
||||
define void @func_in_module_without_cfguard() #0 {
|
||||
entry:
|
||||
%func_ptr = alloca void ()*, align 8
|
||||
store void ()* @target_func, void ()** %func_ptr, align 8
|
||||
%0 = load void ()*, void ()** %func_ptr, align 8
|
||||
|
||||
call void %0()
|
||||
ret void
|
||||
|
||||
; X32-NOT: __guard_check_icall_fptr
|
||||
; X64-NOT: __guard_dispatch_icall_fptr
|
||||
}
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 2, !"cfguard", i32 1}
|
38
test/CodeGen/X86/cfguard-x86-64-vectorcall.ll
Normal file
38
test/CodeGen/X86/cfguard-x86-64-vectorcall.ll
Normal file
@ -0,0 +1,38 @@
|
||||
; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s -check-prefix=X64
|
||||
; Control Flow Guard is currently only available on Windows
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added for x86_64 vector calls.
|
||||
define void @func_cf_vector_x64(void (%struct.HVA)* %0, %struct.HVA* %1) #0 {
|
||||
entry:
|
||||
%2 = alloca %struct.HVA, align 8
|
||||
%3 = bitcast %struct.HVA* %2 to i8*
|
||||
%4 = bitcast %struct.HVA* %1 to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %3, i8* align 8 %4, i64 32, i1 false)
|
||||
%5 = load %struct.HVA, %struct.HVA* %2, align 8
|
||||
call x86_vectorcallcc void %0(%struct.HVA inreg %5)
|
||||
ret void
|
||||
|
||||
; X64-LABEL: func_cf_vector_x64
|
||||
; X64: movq %rcx, %rax
|
||||
; X64: movups (%rdx), %xmm0
|
||||
; X64: movups 16(%rdx), %xmm1
|
||||
; X64: movaps %xmm0, 32(%rsp)
|
||||
; X64: movaps %xmm1, 48(%rsp)
|
||||
; X64: movsd 32(%rsp), %xmm0 # xmm0 = mem[0],zero
|
||||
; X64: movsd 40(%rsp), %xmm1 # xmm1 = mem[0],zero
|
||||
; X64: movsd 48(%rsp), %xmm2 # xmm2 = mem[0],zero
|
||||
; X64: movsd 56(%rsp), %xmm3 # xmm3 = mem[0],zero
|
||||
; X64: callq *__guard_dispatch_icall_fptr(%rip)
|
||||
; X64-NOT: callq
|
||||
}
|
||||
attributes #0 = { "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" }
|
||||
|
||||
%struct.HVA = type { double, double, double, double }
|
||||
|
||||
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1 immarg) #1
|
||||
attributes #1 = { argmemonly nounwind willreturn }
|
||||
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 2, !"cfguard", i32 2}
|
43
test/CodeGen/X86/cfguard-x86-vectorcall.ll
Normal file
43
test/CodeGen/X86/cfguard-x86-vectorcall.ll
Normal file
@ -0,0 +1,43 @@
|
||||
; RUN: llc < %s -mtriple=i686-pc-windows-msvc | FileCheck %s -check-prefix=X32
|
||||
; Control Flow Guard is currently only available on Windows
|
||||
|
||||
|
||||
; Test that Control Flow Guard checks are correctly added for x86 vector calls.
|
||||
define void @func_cf_vector_x86(void (%struct.HVA)* %0, %struct.HVA* %1) #0 {
|
||||
entry:
|
||||
%2 = alloca %struct.HVA, align 8
|
||||
%3 = bitcast %struct.HVA* %2 to i8*
|
||||
%4 = bitcast %struct.HVA* %1 to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %3, i8* align 8 %4, i32 32, i1 false)
|
||||
%5 = load %struct.HVA, %struct.HVA* %2, align 8
|
||||
call x86_vectorcallcc void %0(%struct.HVA inreg %5)
|
||||
ret void
|
||||
|
||||
; X32-LABEL: func_cf_vector_x86
|
||||
; X32: movl 12(%ebp), %eax
|
||||
; X32: movl 8(%ebp), %ecx
|
||||
; X32: movsd 24(%eax), %xmm4 # xmm4 = mem[0],zero
|
||||
; X32: movsd %xmm4, 24(%esp)
|
||||
; X32: movsd 16(%eax), %xmm5 # xmm5 = mem[0],zero
|
||||
; X32: movsd %xmm5, 16(%esp)
|
||||
; X32: movsd (%eax), %xmm6 # xmm6 = mem[0],zero
|
||||
; X32: movsd 8(%eax), %xmm7 # xmm7 = mem[0],zero
|
||||
; X32: movsd %xmm7, 8(%esp)
|
||||
; X32: movsd %xmm6, (%esp)
|
||||
; X32: calll *___guard_check_icall_fptr
|
||||
; X32: movaps %xmm6, %xmm0
|
||||
; X32: movaps %xmm7, %xmm1
|
||||
; X32: movaps %xmm5, %xmm2
|
||||
; X32: movaps %xmm4, %xmm3
|
||||
; X32: calll *%ecx
|
||||
}
|
||||
attributes #0 = { "target-cpu"="pentium4" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" }
|
||||
|
||||
%struct.HVA = type { double, double, double, double }
|
||||
|
||||
declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture writeonly, i8* nocapture readonly, i32, i1 immarg) #1
|
||||
attributes #1 = { argmemonly nounwind willreturn }
|
||||
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
!0 = !{i32 2, !"cfguard", i32 2}
|
Loading…
Reference in New Issue
Block a user