1
0
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:
Andrew Paverd 2019-10-28 13:22:19 +00:00 committed by David Chisnall
parent 2b03c39683
commit d090368b0c
66 changed files with 1447 additions and 29 deletions

View File

@ -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

View File

@ -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
----------------------

View File

@ -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
/// \{

View File

@ -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();

View File

@ -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; }

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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.

View File

@ -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&);

View File

@ -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;

View File

@ -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> {

View 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

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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 {}

View 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;
}

View File

@ -10,6 +10,7 @@ add_llvm_library(LLVMCodeGen
BuiltinGCs.cpp
CalcSpillWeights.cpp
CallingConvLower.cpp
CFGuardLongjmp.cpp
CFIInstrInserter.cpp
CodeGen.cpp
CodeGenPrepare.cpp

View File

@ -22,6 +22,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
initializeAtomicExpandPass(Registry);
initializeBranchFolderPassPass(Registry);
initializeBranchRelaxationPass(Registry);
initializeCFGuardLongjmpPass(Registry);
initializeCFIInstrInserterPass(Registry);
initializeCodeGenPreparePass(Registry);
initializeDeadMachineInstructionElimPass(Registry);

View File

@ -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;

View File

@ -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) {

View File

@ -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) {

View File

@ -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;

View File

@ -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 &&

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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))

View File

@ -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());

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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)>;

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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());
}

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {

View 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);
}

View File

@ -0,0 +1,9 @@
add_llvm_library(LLVMCFGuard
CFGuard.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms
DEPENDS
intrinsics_gen
)

View 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

View File

@ -8,3 +8,4 @@ add_subdirectory(Vectorize)
add_subdirectory(Hello)
add_subdirectory(ObjCARC)
add_subdirectory(Coroutines)
add_subdirectory(CFGuard)

View File

@ -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

View File

@ -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()

View File

@ -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

View 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}

View 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}

View 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}

View 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}

View File

@ -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 "}

View 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}

View 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}

View 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}

View 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}