https://github.com/dpaoliello updated https://github.com/llvm/llvm-project/pull/176276
>From 310b9a12d91b2e77e9e82cc01b0b603fd7bd9e42 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello <[email protected]> Date: Thu, 15 Jan 2026 16:11:36 -0800 Subject: [PATCH] [win] Add a flag to control the Control Flow Guard mechanism on Windows --- clang/include/clang/Basic/CodeGenOptions.def | 4 ++ clang/include/clang/Basic/CodeGenOptions.h | 1 + clang/include/clang/Options/Options.td | 12 ++++ clang/lib/CodeGen/CodeGenModule.cpp | 15 ++++- clang/lib/Driver/ToolChains/Clang.cpp | 9 +++ clang/test/CodeGen/cfguard-mechanism.c | 11 ++++ clang/test/Driver/cl-options.c | 6 ++ llvm/include/llvm/IR/Module.h | 3 + llvm/include/llvm/Support/CodeGen.h | 17 +++++ llvm/include/llvm/Transforms/CFGuard.h | 17 ++--- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 4 +- llvm/lib/CodeGen/CFGuardLongjmp.cpp | 3 +- llvm/lib/IR/Module.cpp | 7 +++ llvm/lib/Passes/PassRegistry.def | 5 +- .../AArch64/AArch64Arm64ECCallLowering.cpp | 9 ++- .../Target/AArch64/AArch64TargetMachine.cpp | 2 +- llvm/lib/Target/ARM/ARMTargetMachine.cpp | 2 +- llvm/lib/Target/X86/X86CallingConv.td | 4 +- llvm/lib/Target/X86/X86RegisterInfo.cpp | 10 ++- llvm/lib/Target/X86/X86TargetMachine.cpp | 6 +- llvm/lib/Transforms/CFGuard/CFGuard.cpp | 63 ++++++++++--------- .../CodeGen/AArch64/cfguard-module-flag.ll | 36 ++++++++--- llvm/test/CodeGen/ARM/cfguard-module-flag.ll | 36 ++++++++--- llvm/test/CodeGen/X86/cfguard-module-flag.ll | 43 +++++++++---- 24 files changed, 227 insertions(+), 98 deletions(-) create mode 100644 clang/test/CodeGen/cfguard-mechanism.c diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index baf8b093c10e6..3dad7dd1d551f 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -505,6 +505,10 @@ CODEGENOPT(AllResourcesBound, 1, 0, Benign) ENUM_CODEGENOPT(WinX64EHUnwindV2, WinX64EHUnwindV2Mode, 2, WinX64EHUnwindV2Mode::Disabled, Benign) +/// Controls the mechanism used for Control Flow Guard (CFG) on Windows. +ENUM_CODEGENOPT(WinControlFlowGuardMechanism, ControlFlowGuardMechanism, + 2, ControlFlowGuardMechanism::Automatic, Benign) + /// FIXME: Make DebugOptions its own top-level .def file. #include "DebugOptions.def" diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index c60ca507ff917..365dd4fe2eac3 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -66,6 +66,7 @@ class CodeGenOptionsBase { using VectorLibrary = llvm::driver::VectorLibrary; using ZeroCallUsedRegsKind = llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind; using WinX64EHUnwindV2Mode = llvm::WinX64EHUnwindV2Mode; + using ControlFlowGuardMechanism = llvm::ControlFlowGuardMechanism; using DebugCompressionType = llvm::DebugCompressionType; using EmitDwarfUnwindType = llvm::EmitDwarfUnwindType; diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index d4dd71b9d1bea..b74cac8cb44f8 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -2257,6 +2257,14 @@ def winx64_eh_unwindv2 NormalizedValues<["Disabled", "BestEffort", "Required"]>, NormalizedValuesScope<"llvm::WinX64EHUnwindV2Mode">, MarshallingInfoEnum<CodeGenOpts<"WinX64EHUnwindV2">, "Disabled">; +def win_cfg_mechanism + : Joined<["-"], "fwin-cfg-mechanism=">, Group<f_Group>, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Sets the mechanism to use for Control Flow Guard on Windows">, + Values<"automatic,force-dispatch,force-check">, + NormalizedValues<["Automatic", "ForceDispatch", "ForceCheck"]>, + NormalizedValuesScope<"llvm::ControlFlowGuardMechanism">, + MarshallingInfoEnum<CodeGenOpts<"WinControlFlowGuardMechanism">, "Automatic">; def fexcess_precision_EQ : Joined<["-"], "fexcess-precision=">, Group<f_Group>, Visibility<[ClangOption, CLOption]>, HelpText<"Allows control over excess precision on targets where native " @@ -9382,6 +9390,10 @@ def _SLASH_d2epilogunwind : CLFlag<"d2epilogunwind">, HelpText<"Best effort generate unwind v2 (epilog) information for x64 Windows">; def _SLASH_d2epilogunwindrequirev2 : CLFlag<"d2epilogunwindrequirev2">, HelpText<"Require generation of unwind v2 (epilog) information for x64 Windows">; +def _SLASH_d2guardcfgdispatch : CLFlag<"d2guardcfgdispatch">, + HelpText<"Force Control Flow Guard to use the dispatch pattern">; +def _SLASH_d2guardcfgdispatch_ : CLFlag<"d2guardcfgdispatch-">, + HelpText<"Force Control Flow Guard to use the check pattern">; def _SLASH_EH : CLJoined<"EH">, HelpText<"Set exception handling model">; def _SLASH_EP : CLFlag<"EP">, HelpText<"Disable linemarker output and preprocess to stdout">; diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 614bca627e03c..99242afd1c2b8 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1123,10 +1123,21 @@ void CodeGenModule::Release() { } if (CodeGenOpts.ControlFlowGuard) { // Function ID tables and checks for Control Flow Guard (cfguard=2). - getModule().addModuleFlag(llvm::Module::Warning, "cfguard", 2); + getModule().addModuleFlag( + llvm::Module::Warning, "cfguard", + static_cast<unsigned>(llvm::ControlFlowGuardMode::Enabled)); } else if (CodeGenOpts.ControlFlowGuardNoChecks) { // Function ID tables for Control Flow Guard (cfguard=1). - getModule().addModuleFlag(llvm::Module::Warning, "cfguard", 1); + getModule().addModuleFlag( + llvm::Module::Warning, "cfguard", + static_cast<unsigned>(llvm::ControlFlowGuardMode::TableOnly)); + } + if (CodeGenOpts.getWinControlFlowGuardMechanism() != + llvm::ControlFlowGuardMechanism::Automatic) { + // Specify the Control Flow Guard mechanism to use on Windows. + getModule().addModuleFlag( + llvm::Module::Warning, "cfguard-mechanism", + static_cast<unsigned>(CodeGenOpts.getWinControlFlowGuardMechanism())); } if (CodeGenOpts.EHContGuard) { // Function ID tables for EH Continuation Guard. diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 4ca98600d6e93..706910d4bc02c 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7393,6 +7393,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Unwind v2 (epilog) information for x64 Windows. Args.AddLastArg(CmdArgs, options::OPT_winx64_eh_unwindv2); + // Control Flow Guard mechanism for Windows. + Args.AddLastArg(CmdArgs, options::OPT_win_cfg_mechanism); + // C++ "sane" operator new. Args.addOptOutFlag(CmdArgs, options::OPT_fassume_sane_operator_new, options::OPT_fno_assume_sane_operator_new); @@ -8522,6 +8525,12 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType, A->claim(); } + // Unwind v2 (epilog) information for x64 Windows. + if (Args.hasArg(options::OPT__SLASH_d2guardcfgdispatch_)) + CmdArgs.push_back("-fwin-cfg-mechanism=force-check"); + else if (Args.hasArg(options::OPT__SLASH_d2guardcfgdispatch)) + CmdArgs.push_back("-fwin-cfg-mechanism=force-dispatch"); + for (const auto &FuncOverride : Args.getAllArgValues(options::OPT__SLASH_funcoverride)) { CmdArgs.push_back(Args.MakeArgString( diff --git a/clang/test/CodeGen/cfguard-mechanism.c b/clang/test/CodeGen/cfguard-mechanism.c new file mode 100644 index 0000000000000..c6db8ff5135b1 --- /dev/null +++ b/clang/test/CodeGen/cfguard-mechanism.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s -check-prefix=AUTOMATIC +// RUN: %clang_cc1 -fwin-cfg-mechanism=automatic -emit-llvm %s -o - | FileCheck %s -check-prefix=AUTOMATIC +// RUN: %clang_cc1 -fwin-cfg-mechanism=force-dispatch -emit-llvm %s -o - | FileCheck %s -check-prefix=FORCEDISPATCH +// RUN: %clang_cc1 -fwin-cfg-mechanism=force-check -emit-llvm %s -o - | FileCheck %s -check-prefix=FORCECHECK +// RUN: %clang -fwin-cfg-mechanism=force-dispatch -S -emit-llvm %s -o - | FileCheck %s -check-prefix=FORCEDISPATCH + +void f(void) {} + +// FORCECHECK: !"cfguard-mechanism", i32 1} +// FORCEDISPATCH: !"cfguard-mechanism", i32 2} +// AUTOMATIC-NOT: "cfguard-mechanism" diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c index 1b1169b71554a..bd25184fb9240 100644 --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -845,4 +845,10 @@ // FUNCOVERRIDE: -loader-replaceable-function=override_me1 // FUNCOVERRIDE-SAME: -loader-replaceable-function=override_me2 +// RUN: %clang_cl /d2guardcfgdispatch /c -### -- %s 2>&1 | FileCheck %s --check-prefix=GUARDCFGDISPATCH +// GUARDCFGDISPATCH: -fwin-cfg-mechanism=force-dispatch + +// RUN: %clang_cl /d2guardcfgdispatch- /c -### -- %s 2>&1 | FileCheck %s --check-prefix=GUARDCFGDISPATCHNEG +// GUARDCFGDISPATCHNEG: -fwin-cfg-mechanism=force-check + void f(void) { } diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h index ac6c20b81d68c..7156a83c9f3cc 100644 --- a/llvm/include/llvm/IR/Module.h +++ b/llvm/include/llvm/IR/Module.h @@ -1056,6 +1056,9 @@ class LLVM_ABI Module { /// Get how unwind v2 (epilog) information should be generated for x64 /// Windows. WinX64EHUnwindV2Mode getWinX64EHUnwindV2Mode() const; + + /// Gets the Control Flow Guard mode. + ControlFlowGuardMode getControlFlowGuardMode() const; }; /// Given "llvm.used" or "llvm.compiler.used" as a global name, collect the diff --git a/llvm/include/llvm/Support/CodeGen.h b/llvm/include/llvm/Support/CodeGen.h index 848796ef574f8..9626782729f3c 100644 --- a/llvm/include/llvm/Support/CodeGen.h +++ b/llvm/include/llvm/Support/CodeGen.h @@ -173,6 +173,23 @@ namespace llvm { Required = 2, }; + enum class ControlFlowGuardMode { + // Don't enable Control Flow Guard. + Disabled = 0, + // Emit the Control Flow Guard tables in the binary, but don't emit any + // checks. + TableOnly = 1, + // Enable Control Flow Guard checks and emit the tables. + Enabled = 2, + }; + + enum class ControlFlowGuardMechanism { + // Choose the mechanism automatically based on the target. + Automatic = 0, + ForceCheck = 1, + ForceDispatch = 2, + }; + } // namespace llvm #endif diff --git a/llvm/include/llvm/Transforms/CFGuard.h b/llvm/include/llvm/Transforms/CFGuard.h index b81db8f487965..096fab033fccc 100644 --- a/llvm/include/llvm/Transforms/CFGuard.h +++ b/llvm/include/llvm/Transforms/CFGuard.h @@ -12,6 +12,7 @@ #define LLVM_TRANSFORMS_CFGUARD_H #include "llvm/IR/PassManager.h" +#include "llvm/Support/Compiler.h" namespace llvm { @@ -22,20 +23,14 @@ class CFGuardPass : public PassInfoMixin<CFGuardPass> { public: enum class Mechanism { Check, Dispatch }; - CFGuardPass(Mechanism M = Mechanism::Check) : GuardMechanism(M) {} - PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM); - -private: - Mechanism GuardMechanism; + CFGuardPass() {} + LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM); }; -/// Insert Control FLow Guard checks on indirect function calls. -FunctionPass *createCFGuardCheckPass(); - -/// Insert Control FLow Guard dispatches on indirect function calls. -FunctionPass *createCFGuardDispatchPass(); +/// Insert Control Flow Guard checks on indirect function calls. +LLVM_ABI FunctionPass *createCFGuardPass(); -bool isCFGuardFunction(const GlobalValue *GV); +LLVM_ABI bool isCFGuardFunction(const GlobalValue *GV); } // namespace llvm diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index d96294e06d579..5990c2a2ba26a 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -665,7 +665,7 @@ bool AsmPrinter::doInitialization(Module &M) { Handlers.push_back(std::unique_ptr<EHStreamer>(ES)); // Emit tables for any value of cfguard flag (i.e. cfguard=1 or cfguard=2). - if (mdconst::extract_or_null<ConstantInt>(M.getModuleFlag("cfguard"))) + if (M.getControlFlowGuardMode() != ControlFlowGuardMode::Disabled) EHHandlers.push_back(std::make_unique<WinCFGuard>(this)); for (auto &Handler : Handlers) @@ -5088,7 +5088,7 @@ void AsmPrinter::emitCOFFFeatureSymbol(Module &M) { Feat00Value |= COFF::Feat00Flags::SafeSEH; } - if (M.getModuleFlag("cfguard")) { + if (M.getControlFlowGuardMode() != ControlFlowGuardMode::Disabled) { // Object is CFG-aware. Feat00Value |= COFF::Feat00Flags::GuardCF; } diff --git a/llvm/lib/CodeGen/CFGuardLongjmp.cpp b/llvm/lib/CodeGen/CFGuardLongjmp.cpp index 04de011400568..639d0537c2cc1 100644 --- a/llvm/lib/CodeGen/CFGuardLongjmp.cpp +++ b/llvm/lib/CodeGen/CFGuardLongjmp.cpp @@ -62,7 +62,8 @@ FunctionPass *llvm::createCFGuardLongjmpPass() { return new CFGuardLongjmp(); } bool CFGuardLongjmp::runOnMachineFunction(MachineFunction &MF) { // Skip modules for which the cfguard flag is not set. - if (!MF.getFunction().getParent()->getModuleFlag("cfguard")) + if (MF.getFunction().getParent()->getControlFlowGuardMode() == + ControlFlowGuardMode::Disabled) return false; // Skip functions that do not have calls to _setjmp. diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp index 7360d0fa1f86a..11dc68e0e4751 100644 --- a/llvm/lib/IR/Module.cpp +++ b/llvm/lib/IR/Module.cpp @@ -940,3 +940,10 @@ WinX64EHUnwindV2Mode Module::getWinX64EHUnwindV2Mode() const { return static_cast<WinX64EHUnwindV2Mode>(CI->getZExtValue()); return WinX64EHUnwindV2Mode::Disabled; } + +ControlFlowGuardMode Module::getControlFlowGuardMode() const { + Metadata *MD = getModuleFlag("cfguard"); + if (auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(MD)) + return static_cast<ControlFlowGuardMode>(CI->getZExtValue()); + return ControlFlowGuardMode::Disabled; +} diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 2cfb5b2592601..02ada05519433 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -413,6 +413,7 @@ FUNCTION_PASS("bdce", BDCEPass()) FUNCTION_PASS("break-crit-edges", BreakCriticalEdgesPass()) FUNCTION_PASS("callbr-prepare", CallBrPreparePass()) FUNCTION_PASS("callsite-splitting", CallSiteSplittingPass()) +FUNCTION_PASS("cfguard", CFGuardPass()) FUNCTION_PASS("chr", ControlHeightReductionPass()) FUNCTION_PASS("codegenprepare", CodeGenPreparePass(*TM)) FUNCTION_PASS("complex-deinterleaving", ComplexDeinterleavingPass(*TM)) @@ -573,10 +574,6 @@ FUNCTION_PASS("wasm-eh-prepare", WasmEHPreparePass()) #ifndef FUNCTION_PASS_WITH_PARAMS #define FUNCTION_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS) #endif -FUNCTION_PASS_WITH_PARAMS( - "cfguard", "CFGuardPass", - [](CFGuardPass::Mechanism M) { return CFGuardPass(M); }, - parseCFGuardPassOptions, "check;dispatch") FUNCTION_PASS_WITH_PARAMS( "early-cse", "EarlyCSEPass", [](bool UseMemorySSA) { return EarlyCSEPass(UseMemorySSA); }, diff --git a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp index d0c4b1b9f83fd..c27a693ceecc1 100644 --- a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp @@ -75,7 +75,7 @@ class AArch64Arm64ECCallLowering : public ModulePass { bool runOnModule(Module &M) override; private: - int cfguard_module_flag = 0; + ControlFlowGuardMode CFGuardModuleFlag = ControlFlowGuardMode::Disabled; FunctionType *GuardFnType = nullptr; FunctionType *DispatchFnType = nullptr; Constant *GuardFnCFGlobal = nullptr; @@ -758,7 +758,8 @@ void AArch64Arm64ECCallLowering::lowerCall(CallBase *CB) { // Load the global symbol as a pointer to the check function. Value *GuardFn; - if (cfguard_module_flag == 2 && !CB->hasFnAttr("guard_nocf")) + if ((CFGuardModuleFlag == ControlFlowGuardMode::Enabled) && + !CB->hasFnAttr("guard_nocf")) GuardFn = GuardFnCFGlobal; else GuardFn = GuardFnGlobal; @@ -794,9 +795,7 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) { M = &Mod; // 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(); + CFGuardModuleFlag = M->getControlFlowGuardMode(); PtrTy = PointerType::getUnqual(M->getContext()); I64Ty = Type::getInt64Ty(M->getContext()); diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp index 3aba866458830..09795672da1cf 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -674,7 +674,7 @@ void AArch64PassConfig::addIRPasses() { if (TM->getTargetTriple().isWindowsArm64EC()) addPass(createAArch64Arm64ECCallLoweringPass()); else - addPass(createCFGuardCheckPass()); + addPass(createCFGuardPass()); } if (TM->Options.JMCInstrument) diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp index 590d4c70592f8..aadf8624c7c8c 100644 --- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp +++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp @@ -384,7 +384,7 @@ void ARMPassConfig::addIRPasses() { // Add Control Flow Guard checks. if (TM->getTargetTriple().isOSWindows()) - addPass(createCFGuardCheckPass()); + addPass(createCFGuardPass()); if (TM->Options.JMCInstrument) addPass(createJMCInstrumenterPass()); diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td index f020e0b55141c..7d24126f820f8 100644 --- a/llvm/lib/Target/X86/X86CallingConv.td +++ b/llvm/lib/Target/X86/X86CallingConv.td @@ -1217,8 +1217,10 @@ def CSR_Win32_CFGuard_Check_NoSSE : CalleeSavedRegs<(add CSR_32_RegCall_NoSSE, E def CSR_Win32_CFGuard_Check : CalleeSavedRegs<(add CSR_32_RegCall, ECX)>; def CSR_Win64_RegCall_NoSSE : CalleeSavedRegs<(add RBX, RBP, (sequence "R%u", 10, 15))>; -def CSR_Win64_RegCall : CalleeSavedRegs<(add CSR_Win64_RegCall_NoSSE, +def CSR_Win64_RegCall : CalleeSavedRegs<(add CSR_Win64_RegCall_NoSSE, (sequence "XMM%u", 8, 15))>; +def CSR_Win64_CFGuard_Check_NoSSE : CalleeSavedRegs<(add CSR_Win64_RegCall_NoSSE, RCX)>; +def CSR_Win64_CFGuard_Check : CalleeSavedRegs<(add CSR_Win64_RegCall, RCX)>; def CSR_SysV64_RegCall_NoSSE : CalleeSavedRegs<(add RBX, RBP, (sequence "R%u", 12, 15))>; def CSR_SysV64_RegCall : CalleeSavedRegs<(add CSR_SysV64_RegCall_NoSSE, diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp index 72f38133e21ff..635c06440c873 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -434,9 +434,13 @@ X86RegisterInfo::getCallPreservedMask(const MachineFunction &MF, 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); + if (Is64Bit) { + return (HasSSE ? CSR_Win64_CFGuard_Check_RegMask + : CSR_Win64_CFGuard_Check_NoSSE_RegMask); + } else { + return (HasSSE ? CSR_Win32_CFGuard_Check_RegMask + : CSR_Win32_CFGuard_Check_NoSSE_RegMask); + } case CallingConv::Cold: if (Is64Bit) return CSR_64_MostRegs_RegMask; diff --git a/llvm/lib/Target/X86/X86TargetMachine.cpp b/llvm/lib/Target/X86/X86TargetMachine.cpp index d5907d55998b5..bf9ec458acf27 100644 --- a/llvm/lib/Target/X86/X86TargetMachine.cpp +++ b/llvm/lib/Target/X86/X86TargetMachine.cpp @@ -441,11 +441,7 @@ void X86PassConfig::addIRPasses() { // Add Control Flow Guard checks. const Triple &TT = TM->getTargetTriple(); if (TT.isOSWindows()) { - if (TT.isX86_64()) { - addPass(createCFGuardDispatchPass()); - } else { - addPass(createCFGuardCheckPass()); - } + addPass(createCFGuardPass()); } if (TM->Options.JMCInstrument) diff --git a/llvm/lib/Transforms/CFGuard/CFGuard.cpp b/llvm/lib/Transforms/CFGuard/CFGuard.cpp index 46456706d46a1..b648b4f862c65 100644 --- a/llvm/lib/Transforms/CFGuard/CFGuard.cpp +++ b/llvm/lib/Transforms/CFGuard/CFGuard.cpp @@ -38,24 +38,11 @@ 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 Mechanism::Dispatch -/// mechanism. X86, ARM, and AArch64 targets use the Mechanism::Check machanism. +/// address-taken function. class CFGuardImpl { public: using Mechanism = CFGuardPass::Mechanism; - CFGuardImpl(Mechanism M) : GuardMechanism(M) { - // Get or insert the guard check or dispatch global symbols. - switch (GuardMechanism) { - case Mechanism::Check: - GuardFnName = GuardCheckFunctionName; - break; - case Mechanism::Dispatch: - GuardFnName = GuardDispatchFunctionName; - break; - } - } - /// 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 @@ -147,8 +134,7 @@ class CFGuardImpl { private: // Only add checks if the module has the cfguard=2 flag. - int CFGuardModuleFlag = 0; - StringRef GuardFnName; + ControlFlowGuardMode CFGuardModuleFlag = ControlFlowGuardMode::Disabled; Mechanism GuardMechanism = Mechanism::Check; FunctionType *GuardFnType = nullptr; PointerType *GuardFnPtrType = nullptr; @@ -162,7 +148,7 @@ class CFGuard : public FunctionPass { static char ID; // Default constructor required for the INITIALIZE_PASS macro. - CFGuard(CFGuardImpl::Mechanism M) : FunctionPass(ID), Impl(M) {} + CFGuard() : FunctionPass(ID) {} bool doInitialization(Module &M) override { return Impl.doInitialization(M); } bool runOnFunction(Function &F) override { return Impl.runOnFunction(F); } @@ -233,20 +219,42 @@ void CFGuardImpl::insertCFGuardDispatch(CallBase *CB) { bool CFGuardImpl::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"))) - CFGuardModuleFlag = MD->getZExtValue(); + CFGuardModuleFlag = M.getControlFlowGuardMode(); // Skip modules for which CFGuard checks have been disabled. - if (CFGuardModuleFlag != 2) + if (CFGuardModuleFlag != ControlFlowGuardMode::Enabled) return false; + // Determine the guard mechanism to use. + ControlFlowGuardMechanism MechanismOverride = + ControlFlowGuardMechanism::Automatic; + if (auto *CI = mdconst::dyn_extract_or_null<ConstantInt>( + M.getModuleFlag("cfguard-mechanism"))) + MechanismOverride = + static_cast<ControlFlowGuardMechanism>(CI->getZExtValue()); + switch (MechanismOverride) { + case ControlFlowGuardMechanism::ForceCheck: + GuardMechanism = Mechanism::Check; + break; + case ControlFlowGuardMechanism::ForceDispatch: + GuardMechanism = Mechanism::Dispatch; + break; + default: + // X86_64 uses dispatch; all other architectures use check. + GuardMechanism = + M.getTargetTriple().isX86_64() ? Mechanism::Dispatch : Mechanism::Check; + break; + } + // Set up prototypes for the guard check and dispatch functions. GuardFnType = FunctionType::get(Type::getVoidTy(M.getContext()), {PointerType::getUnqual(M.getContext())}, false); GuardFnPtrType = PointerType::get(M.getContext(), 0); + StringRef GuardFnName = GuardMechanism == Mechanism::Check + ? GuardCheckFunctionName + : GuardDispatchFunctionName; GuardFnGlobal = M.getOrInsertGlobal(GuardFnName, GuardFnPtrType, [&] { auto *Var = new GlobalVariable(M, GuardFnPtrType, false, GlobalVariable::ExternalLinkage, nullptr, @@ -259,8 +267,7 @@ bool CFGuardImpl::doInitialization(Module &M) { } bool CFGuardImpl::runOnFunction(Function &F) { - // Skip modules for which CFGuard checks have been disabled. - if (CFGuardModuleFlag != 2) + if (CFGuardModuleFlag != ControlFlowGuardMode::Enabled) return false; SmallVector<CallBase *, 8> IndirectCalls; @@ -296,7 +303,7 @@ bool CFGuardImpl::runOnFunction(Function &F) { } PreservedAnalyses CFGuardPass::run(Function &F, FunctionAnalysisManager &FAM) { - CFGuardImpl Impl(GuardMechanism); + CFGuardImpl Impl; bool Changed = Impl.doInitialization(*F.getParent()); Changed |= Impl.runOnFunction(F); return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); @@ -305,13 +312,7 @@ PreservedAnalyses CFGuardPass::run(Function &F, FunctionAnalysisManager &FAM) { char CFGuard::ID = 0; INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false) -FunctionPass *llvm::createCFGuardCheckPass() { - return new CFGuard(CFGuardPass::Mechanism::Check); -} - -FunctionPass *llvm::createCFGuardDispatchPass() { - return new CFGuard(CFGuardPass::Mechanism::Dispatch); -} +FunctionPass *llvm::createCFGuardPass() { return new CFGuard(); } bool llvm::isCFGuardFunction(const GlobalValue *GV) { if (GV->getLinkage() != GlobalValue::ExternalLinkage) diff --git a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll index 317ad2c42a3e1..ae81b33512d2d 100644 --- a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll +++ b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll @@ -1,12 +1,11 @@ - -; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s -; RUN: llc < %s -mtriple=aarch64-w64-windows-gnu | FileCheck %s +; RUN: sed -e s/.tableonly:// %s | llc -mtriple=aarch64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,TABLEONLY +; RUN: sed -e s/.tableonly:// %s | llc -mtriple=aarch64-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,TABLEONLY +; RUN: sed -e s/.normal:// %s | llc -mtriple=aarch64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.normal:// %s | llc -mtriple=aarch64-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.forcecheck:// %s | llc -mtriple=aarch64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.forcedispatch:// %s | llc -mtriple=aarch64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH ; 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 { @@ -18,9 +17,26 @@ entry: call void %0() ret void - ; CHECK-NOT: __guard_check_icall_fptr - ; CHECK-NOT: __guard_dispatch_icall_fptr + ; CHECK: adrp + + ; USECHECK-SAME: __guard_check_icall_fptr + ; USECHECK-NOT: __guard_dispatch_icall_fptr + + ; USEDISPATCH-SAME: __guard_dispatch_icall_fptr + ; USEDISPATCH-NOT: __guard_check_icall_fptr + + ; TABLEONLY-SAME: target_func + ; TABLEONLY-NOT: __guard_dispatch_icall_fptr + ; TABLEONLY-NOT: __guard_check_icall_fptr } -!llvm.module.flags = !{!0} +; CHECK: .section .gfids$y,"dr" + !0 = !{i32 2, !"cfguard", i32 1} +!1 = !{i32 2, !"cfguard", i32 2} +!2 = !{i32 2, !"cfguard-mechanism", i32 1} +!3 = !{i32 2, !"cfguard-mechanism", i32 2} +;tableonly: !llvm.module.flags = !{!0} +;normal: !llvm.module.flags = !{!1} +;forcecheck: !llvm.module.flags = !{!1, !2} +;forcedispatch: !llvm.module.flags = !{!1, !3} diff --git a/llvm/test/CodeGen/ARM/cfguard-module-flag.ll b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll index bb3c04a54caff..e345cb4505a26 100644 --- a/llvm/test/CodeGen/ARM/cfguard-module-flag.ll +++ b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll @@ -1,12 +1,11 @@ - -; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s -; RUN: llc < %s -mtriple=arm-w64-windows-gnu | FileCheck %s +; RUN: sed -e s/.tableonly:// %s | llc -mtriple=arm-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,TABLEONLY +; RUN: sed -e s/.tableonly:// %s | llc -mtriple=arm-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,TABLEONLY +; RUN: sed -e s/.normal:// %s | llc -mtriple=arm-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.normal:// %s | llc -mtriple=arm-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.forcecheck:// %s | llc -mtriple=arm-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.forcedispatch:// %s | llc -mtriple=arm-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH ; 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 { @@ -18,10 +17,27 @@ entry: call void %0() ret void - ; CHECK-NOT: __guard_check_icall_fptr - ; CHECK-NOT: __guard_dispatch_icall_fptr + ; CHECK: movw + + ; USECHECK-SAME: __guard_check_icall_fptr + ; USECHECK-NOT: __guard_dispatch_icall_fptr + + ; USEDISPATCH-SAME: __guard_dispatch_icall_fptr + ; USEDISPATCH-NOT: __guard_check_icall_fptr + + ; TABLEONLY-SAME: target_func + ; TABLEONLY-NOT: __guard_dispatch_icall_fptr + ; TABLEONLY-NOT: __guard_check_icall_fptr } attributes #0 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="all" "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" "use-soft-float"="false"} -!llvm.module.flags = !{!0} +; CHECK: .section .gfids$y,"dr" + !0 = !{i32 2, !"cfguard", i32 1} +!1 = !{i32 2, !"cfguard", i32 2} +!2 = !{i32 2, !"cfguard-mechanism", i32 1} +!3 = !{i32 2, !"cfguard-mechanism", i32 2} +;tableonly: !llvm.module.flags = !{!0} +;normal: !llvm.module.flags = !{!1} +;forcecheck: !llvm.module.flags = !{!1, !2} +;forcedispatch: !llvm.module.flags = !{!1, !3} diff --git a/llvm/test/CodeGen/X86/cfguard-module-flag.ll b/llvm/test/CodeGen/X86/cfguard-module-flag.ll index f69eccfc3d282..e369a7d0a7dda 100644 --- a/llvm/test/CodeGen/X86/cfguard-module-flag.ll +++ b/llvm/test/CodeGen/X86/cfguard-module-flag.ll @@ -1,13 +1,17 @@ -; RUN: llc < %s -mtriple=i686-pc-windows-msvc | FileCheck %s -check-prefix=X86 -; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s -check-prefix=X64 -; RUN: llc < %s -mtriple=i686-w64-windows-gnu | FileCheck %s -check-prefix=X86 -; RUN: llc < %s -mtriple=x86_64-w64-windows-gnu | FileCheck %s -check-prefix=X64 +; RUN: sed -e s/.tableonly:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,TABLEONLY +; RUN: sed -e s/.tableonly:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,TABLEONLY +; RUN: sed -e s/.tableonly:// %s | llc -mtriple=i686-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,TABLEONLY +; RUN: sed -e s/.tableonly:// %s | llc -mtriple=x86_64-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,TABLEONLY +; RUN: sed -e s/.normal:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.normal:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH +; RUN: sed -e s/.normal:// %s | llc -mtriple=i686-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.normal:// %s | llc -mtriple=x86_64-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,USEDISPATCH +; RUN: sed -e s/.forcecheck:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.forcecheck:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.forcedispatch:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH +; RUN: sed -e s/.forcedispatch:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH ; 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 { @@ -19,9 +23,26 @@ entry: call void %0() ret void - ; X86-NOT: __guard_check_icall_fptr - ; X64-NOT: __guard_dispatch_icall_fptr + ; CHECK: call + + ; USECHECK-SAME: __guard_check_icall_fptr + ; USECHECK-NOT: __guard_dispatch_icall_fptr + + ; USEDISPATCH-SAME: __guard_dispatch_icall_fptr + ; USEDISPATCH-NOT: __guard_check_icall_fptr + + ; TABLEONLY-SAME: *% + ; TABLEONLY-NOT: __guard_dispatch_icall_fptr + ; TABLEONLY-NOT: __guard_check_icall_fptr } -!llvm.module.flags = !{!0} +; CHECK: .section .gfids$y,"dr" + !0 = !{i32 2, !"cfguard", i32 1} +!1 = !{i32 2, !"cfguard", i32 2} +!2 = !{i32 2, !"cfguard-mechanism", i32 1} +!3 = !{i32 2, !"cfguard-mechanism", i32 2} +;tableonly: !llvm.module.flags = !{!0} +;normal: !llvm.module.flags = !{!1} +;forcecheck: !llvm.module.flags = !{!1, !2} +;forcedispatch: !llvm.module.flags = !{!1, !3} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
