Skip to content

Commit

Permalink
Add Windows Control Flow Guard checks (/guard:cf).
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ajpaverd authored and davidchisnall committed Oct 28, 2019
1 parent a233e7d commit d157a9b
Show file tree
Hide file tree
Showing 77 changed files with 1,514 additions and 62 deletions.
6 changes: 5 additions & 1 deletion clang/docs/ClangCommandLineReference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ Output path for the plist report

.. option:: -cfguard

Emit tables required for Windows Control Flow Guard.
Emit tables and checks for Windows Control Flow Guard.

.. option:: -cfguard-no-checks

Emit tables required for Windows Control Flow Guard without checks.

.. option:: -client\_name<arg>

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ CODEGENOPT(AssumeSaneOperatorNew , 1, 1) ///< implicit __attribute__((malloc)) o
CODEGENOPT(Autolink , 1, 1) ///< -fno-autolink
CODEGENOPT(ObjCAutoRefCountExceptions , 1, 0) ///< Whether ARC should be EH-safe.
CODEGENOPT(Backchain , 1, 0) ///< -mbackchain
CODEGENOPT(ControlFlowGuardNoChecks , 1, 0) ///< -cfguard-no-checks
CODEGENOPT(ControlFlowGuard , 1, 0) ///< -cfguard
CODEGENOPT(CoverageExtraChecksum, 1, 0) ///< Whether we need a second checksum for functions in GCNO files.
CODEGENOPT(CoverageNoFunctionNamesInData, 1, 0) ///< Do not include function names in GCDA files.
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/CC1Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,10 @@ def msign_return_address_key_EQ : Joined<["-"], "msign-return-address-key=">,
Values<"a_key,b_key">;
def mbranch_target_enforce : Flag<["-"], "mbranch-target-enforce">;
def fno_dllexport_inlines : Flag<["-"], "fno-dllexport-inlines">;
def cfguard_no_checks : Flag<["-"], "cfguard-no-checks">,
HelpText<"Emit Windows Control Flow Guard tables only (no checks)">;
def cfguard : Flag<["-"], "cfguard">,
HelpText<"Emit Windows Control Flow Guard tables and checks">;

//===----------------------------------------------------------------------===//
// Dependency Output Options
Expand Down
2 changes: 0 additions & 2 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,6 @@ def bind__at__load : Flag<["-"], "bind_at_load">;
def bundle__loader : Separate<["-"], "bundle_loader">;
def bundle : Flag<["-"], "bundle">;
def b : JoinedOrSeparate<["-"], "b">, Flags<[Unsupported]>;
def cfguard : Flag<["-"], "cfguard">, Flags<[CC1Option]>,
HelpText<"Emit tables required for Windows Control Flow Guard.">;
def cl_opt_disable : Flag<["-"], "cl-opt-disable">, Group<opencl_Group>, Flags<[CC1Option]>,
HelpText<"OpenCL only. This option disables all optimizations. By default optimizations are enabled.">;
def cl_strict_aliasing : Flag<["-"], "cl-strict-aliasing">, Group<opencl_Group>, Flags<[CC1Option]>,
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,11 @@ void CodeGenModule::Release() {
getModule().addModuleFlag(llvm::Module::Warning, "CodeViewGHash", 1);
}
if (CodeGenOpts.ControlFlowGuard) {
// We want function ID tables for Control Flow Guard.
getModule().addModuleFlag(llvm::Module::Warning, "cfguardtable", 1);
// Function ID tables and checks for Control Flow Guard (cfguard=2).
getModule().addModuleFlag(llvm::Module::Warning, "cfguard", 2);
} else if (CodeGenOpts.ControlFlowGuardNoChecks) {
// Function ID tables for Control Flow Guard (cfguard=1).
getModule().addModuleFlag(llvm::Module::Warning, "cfguard", 1);
}
if (CodeGenOpts.OptimizationLevel > 0 && CodeGenOpts.StrictVTablePointers) {
// We don't support LTO with 2 with different StrictVTablePointers
Expand Down
31 changes: 12 additions & 19 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5975,26 +5975,19 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
}

if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) {
SmallVector<StringRef, 1> SplitArgs;
StringRef(A->getValue()).split(SplitArgs, ",");
bool Instrument = false;
bool NoChecks = false;
for (StringRef Arg : SplitArgs) {
if (Arg.equals_lower("cf"))
Instrument = true;
else if (Arg.equals_lower("cf-"))
Instrument = false;
else if (Arg.equals_lower("nochecks"))
NoChecks = true;
else if (Arg.equals_lower("nochecks-"))
NoChecks = false;
else
D.Diag(diag::err_drv_invalid_value) << A->getSpelling() << Arg;
}
// Currently there's no support emitting CFG instrumentation; the flag only
// emits the table of address-taken functions.
if (Instrument || NoChecks)
StringRef GuardArgs = A->getValue();
// The only valid options are "cf", "cf,nochecks", and "cf-".
if (GuardArgs.equals_lower("cf")) {
// Emit CFG instrumentation and the table of address-taken functions.
CmdArgs.push_back("-cfguard");
} else if (GuardArgs.equals_lower("cf,nochecks")) {
// Emit only the table of address-taken functions.
CmdArgs.push_back("-cfguard-no-checks");
} else if (GuardArgs.equals_lower("cf-")) {
// Do nothing, but we might want to emit a security warning in future.
} else {
D.Diag(diag::err_drv_invalid_value) << A->getSpelling() << GuardArgs;
}
}
}

Expand Down
22 changes: 22 additions & 0 deletions clang/lib/Driver/ToolChains/MSVC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,17 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,

Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link);

// Control Flow Guard checks
if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) {
StringRef GuardArgs = A->getValue();
if (GuardArgs.equals_lower("cf") || GuardArgs.equals_lower("cf,nochecks")) {
// MSVC doesn't yet support the "nochecks" modifier.
CmdArgs.push_back("-guard:cf");
} else if (GuardArgs.equals_lower("cf-")) {
CmdArgs.push_back("-guard:cf-");
}
}

if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ,
options::OPT_fno_openmp, false)) {
CmdArgs.push_back("-nodefaultlib:vcomp.lib");
Expand Down Expand Up @@ -679,6 +690,17 @@ std::unique_ptr<Command> visualstudio::Compiler::GetCommand(
: "/Zc:threadSafeInit-");
}

// Control Flow Guard checks
if (Arg *A = Args.getLastArg(options::OPT__SLASH_guard)) {
StringRef GuardArgs = A->getValue();
if (GuardArgs.equals_lower("cf") || GuardArgs.equals_lower("cf,nochecks")) {
// MSVC doesn't yet support the "nochecks" modifier.
CmdArgs.push_back("/guard:cf");
} else if (GuardArgs.equals_lower("cf-")) {
CmdArgs.push_back("/guard:cf-");
}
}

// Pass through all unknown arguments so that the fallback command can see
// them too.
Args.AddAllArgs(CmdArgs, options::OPT_UNKNOWN);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier);

Opts.ControlFlowGuardNoChecks = Args.hasArg(OPT_cfguard_no_checks);
Opts.ControlFlowGuard = Args.hasArg(OPT_cfguard);

Opts.DisableGCov = Args.hasArg(OPT_test_coverage);
Expand Down
14 changes: 8 additions & 6 deletions clang/test/CodeGen/cfguardtable.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// RUN: %clang_cc1 -cfguard -emit-llvm %s -o - | FileCheck %s

void f() {}

// Check that the cfguardtable metadata flag gets set on the module.
// CHECK: !"cfguardtable", i32 1}
// RUN: %clang_cc1 -cfguard-no-checks -emit-llvm %s -o - | FileCheck %s -check-prefix=CFGUARDNOCHECKS
// RUN: %clang_cc1 -cfguard -emit-llvm %s -o - | FileCheck %s -check-prefix=CFGUARD

void f() {}

// Check that the cfguard metadata flag gets correctly set on the module.
// CFGUARDNOCHECKS: !"cfguard", i32 1}
// CFGUARD: !"cfguard", i32 2}
3 changes: 2 additions & 1 deletion clang/test/Driver/cl-fallback.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// command-line option, e.g. on Mac where %s is commonly under /Users.

// RUN: %clang_cl --target=i686-pc-win32 /fallback /Dfoo=bar /Ubaz /Ifoo /O0 /Ox /GR /GR- /GS /GS- /Gy /Gy- \
// RUN: /Gw /Gw- /LD /LDd /EHs /EHs- /Zl /MD /MDd /MTd /MT /FImyheader.h /Zi \
// RUN: /Gw /Gw- /LD /LDd /EHs /EHs- /Zl /MD /MDd /MTd /MT /guard:cf /guard:cf- /FImyheader.h /Zi \
// RUN: -garbage -moregarbage \
// RUN: -### -- %s 2>&1 \
// RUN: | FileCheck %s
Expand Down Expand Up @@ -33,6 +33,7 @@
// CHECK: "/EHs-"
// CHECK: "/Zl"
// CHECK: "/MT"
// CHECK: "/guard:cf-"
// CHECK: "-garbage"
// CHECK: "-moregarbage"
// CHECK: "/Tc" "{{.*cl-fallback.c}}"
Expand Down
9 changes: 7 additions & 2 deletions clang/test/Driver/cl-options.c
Original file line number Diff line number Diff line change
Expand Up @@ -597,9 +597,14 @@
// NOCFGUARD-NOT: -cfguard

// RUN: %clang_cl /guard:cf -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARD %s
// RUN: %clang_cl /guard:cf,nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARD %s
// RUN: %clang_cl /guard:nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARD %s
// CFGUARD: -cfguard
// CFGUARD-NOT: -cfguard-no-checks

// RUN: %clang_cl /guard:cf,nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDNOCHECKS %s
// CFGUARDNOCHECKS: -cfguard-no-checks

// RUN: %clang_cl /guard:nochecks -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDNOCHECKSINVALID %s
// CFGUARDNOCHECKSINVALID: invalid value 'nochecks' in '/guard:'

// RUN: %clang_cl /guard:foo -### -- %s 2>&1 | FileCheck -check-prefix=CFGUARDINVALID %s
// CFGUARDINVALID: invalid value 'foo' in '/guard:'
Expand Down
11 changes: 11 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions llvm/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------

Expand Down
15 changes: 15 additions & 0 deletions llvm/include/llvm/CodeGen/MachineFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// \{

Expand Down Expand Up @@ -830,6 +834,17 @@ class MachineFunction {

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

Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/CodeGen/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
8 changes: 6 additions & 2 deletions llvm/include/llvm/CodeGen/TargetCallingConv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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; }

Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/CodeGen/TargetLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,14 @@ class TargetLoweringBase {
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);

Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/IR/CallingConv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/IR/InstrTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/LLVMContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class LLVMContext {
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.
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/InitializePasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -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&);
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/MC/MCObjectFileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ class MCObjectFileInfo {
MCSection *XDataSection;
MCSection *SXDataSection;
MCSection *GFIDsSection;
MCSection *GLJMPSection;

public:
void InitMCObjectFileInfo(const Triple &TT, bool PIC, MCContext &ctx,
Expand Down Expand Up @@ -379,6 +380,7 @@ class MCObjectFileInfo {
MCSection *getXDataSection() const { return XDataSection; }
MCSection *getSXDataSection() const { return SXDataSection; }
MCSection *getGFIDsSection() const { return GFIDsSection; }
MCSection *getGLJMPSection() const { return GLJMPSection; }

MCSection *getEHFrameSection() {
return EHFrameSection;
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/Target/TargetCallingConv.td
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
26 changes: 26 additions & 0 deletions llvm/include/llvm/Transforms/CFGuard.h
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit d157a9b

Please sign in to comment.