https://github.com/dpaoliello updated https://github.com/llvm/llvm-project/pull/176276
>From 4e948b6e89cc52f300508e28f1f269a5657689f9 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 | 7 +++ clang/lib/Driver/ToolChains/Clang.cpp | 9 ++++ clang/test/CodeGen/cfguard-mechanism.c | 11 ++++ clang/test/Driver/cl-options.c | 6 +++ llvm/docs/LangRef.rst | 30 +++++++++++ llvm/include/llvm/Support/CodeGen.h | 7 +++ llvm/include/llvm/Transforms/CFGuard.h | 12 ++--- llvm/lib/Passes/PassBuilder.cpp | 20 ------- llvm/lib/Passes/PassRegistry.def | 5 +- .../AArch64/AArch64Arm64ECCallLowering.cpp | 16 ++++++ .../Target/AArch64/AArch64TargetMachine.cpp | 2 +- llvm/lib/Target/ARM/ARMTargetMachine.cpp | 2 +- llvm/lib/Target/X86/X86CallingConv.td | 4 +- llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp | 4 +- llvm/lib/Target/X86/X86RegisterInfo.cpp | 10 ++-- llvm/lib/Target/X86/X86TargetMachine.cpp | 6 +-- llvm/lib/Transforms/CFGuard/CFGuard.cpp | 52 ++++++++++--------- .../CodeGen/AArch64/cfguard-module-flag.ll | 46 ++++++++++++---- llvm/test/CodeGen/ARM/cfguard-module-flag.ll | 36 +++++++++---- llvm/test/CodeGen/X86/cfguard-module-flag.ll | 43 +++++++++++---- llvm/test/Linker/cfguard.ll | 39 ++++++++++++++ 24 files changed, 282 insertions(+), 102 deletions(-) create mode 100644 clang/test/CodeGen/cfguard-mechanism.c create mode 100644 llvm/test/Linker/cfguard.ll 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 8ef0d87faaeaf..0a3d5beca5e96 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 5ecb516c2cf68..65ac6fa06a880 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -2271,6 +2271,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,dispatch,check">, + NormalizedValues<["Automatic", "Dispatch", "Check"]>, + 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 " @@ -9411,6 +9419,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<"Use the dispatch mechanism to call the Control Flow Guard checker">; +def _SLASH_d2guardcfgdispatch_ : CLFlag<"d2guardcfgdispatch-">, + HelpText<"Use the check mechanism to call the Control Flow Guard checker">; 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 d50c9605a30b3..67c54ceb74d75 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1148,6 +1148,13 @@ void CodeGenModule::Release() { 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. getModule().addModuleFlag(llvm::Module::Warning, "ehcontguard", 1); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 0293b04217673..37fe39cd9a92c 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7457,6 +7457,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); @@ -8557,6 +8560,12 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType, A->claim(); } + // Control Flow Guard mechanism for Windows. + if (Args.hasArg(options::OPT__SLASH_d2guardcfgdispatch_)) + CmdArgs.push_back("-fwin-cfg-mechanism=check"); + else if (Args.hasArg(options::OPT__SLASH_d2guardcfgdispatch)) + CmdArgs.push_back("-fwin-cfg-mechanism=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..e3b3a6eae2d7f --- /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=dispatch -emit-llvm %s -o - | FileCheck %s -check-prefix=DISPATCH +// RUN: %clang_cc1 -fwin-cfg-mechanism=check -emit-llvm %s -o - | FileCheck %s -check-prefix=CHECK +// RUN: %clang -fwin-cfg-mechanism=dispatch -S -emit-llvm %s -o - | FileCheck %s -check-prefix=DISPATCH + +void f(void) {} + +// CHECK: !"cfguard-mechanism", i32 1} +// DISPATCH: !"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 9fd3ff6326dcc..54f92c98a3739 100644 --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -847,4 +847,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=dispatch + +// RUN: %clang_cl /d2guardcfgdispatch- /c -### -- %s 2>&1 | FileCheck %s --check-prefix=GUARDCFGDISPATCHNEG +// GUARDCFGDISPATCHNEG: -fwin-cfg-mechanism=check + void f(void) { } diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 9c3ffb396649b..bc776f7a1e1b7 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -9011,6 +9011,36 @@ For example: This will change the stack alignment to 8B. +Windows Control Flow Guard Metadata +----------------------------------- + +Controls what Control Flow Guard (CFG) checks are performed, how they are +performed, and what metadata is emitted. There are multiple flags that can be +used to control different aspects of CFG. Using two different values for the +same flag will raise a warning when linking. + +To pass this information to the backend, these options are encoded in module +flags metadata, using the following key-value pairs: + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Key + - Value + + * - cfguard + - * 0 --- CFG is completely disabled. + * 1 --- The CFG table is emitted, but no checks are performed. + * 2 --- The CFG table is emitted and checks are performed. + + * - cfguard-mechanism + - * 0 --- CFG uses the default mechanism for the architecture. + * 1 --- CFG uses the "check" mechanism. This will result in a separate + call to the checker function and then one to the target. + * 2 --- CFG uses the "dispatch" mechanism. This calls a dispatcher + function which both checks and then calls the target. + Embedded Objects Names Metadata =============================== diff --git a/llvm/include/llvm/Support/CodeGen.h b/llvm/include/llvm/Support/CodeGen.h index 65d262a087378..52f00c3258c0f 100644 --- a/llvm/include/llvm/Support/CodeGen.h +++ b/llvm/include/llvm/Support/CodeGen.h @@ -183,6 +183,13 @@ namespace llvm { Enabled = 2, }; + enum class ControlFlowGuardMechanism { + // Choose the mechanism automatically based on the target. + Automatic = 0, + Check = 1, + Dispatch = 2, + }; + } // namespace llvm #endif diff --git a/llvm/include/llvm/Transforms/CFGuard.h b/llvm/include/llvm/Transforms/CFGuard.h index df5385718becc..175d62851f363 100644 --- a/llvm/include/llvm/Transforms/CFGuard.h +++ b/llvm/include/llvm/Transforms/CFGuard.h @@ -24,18 +24,12 @@ class CFGuardPass : public PassInfoMixin<CFGuardPass> { public: enum class Mechanism { Check, Dispatch }; - CFGuardPass(Mechanism M = Mechanism::Check) : GuardMechanism(M) {} + CFGuardPass() {} LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM); - -private: - Mechanism GuardMechanism; }; -/// Insert Control FLow Guard checks on indirect function calls. -LLVM_ABI FunctionPass *createCFGuardCheckPass(); - -/// Insert Control FLow Guard dispatches on indirect function calls. -LLVM_ABI FunctionPass *createCFGuardDispatchPass(); +/// Insert Control Flow Guard checks on indirect function calls. +LLVM_ABI FunctionPass *createCFGuardPass(); LLVM_ABI bool isCFGuardCall(const CallBase *CB); LLVM_ABI bool isCFGuardFunction(const GlobalValue *GV); diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 45955426d66a0..0acf3a1e7146c 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -871,26 +871,6 @@ Expected<bool> parsePostOrderFunctionAttrsPassOptions(StringRef Params) { Params, "skip-non-recursive-function-attrs", "PostOrderFunctionAttrs"); } -Expected<CFGuardPass::Mechanism> parseCFGuardPassOptions(StringRef Params) { - if (Params.empty()) - return CFGuardPass::Mechanism::Check; - - auto [Param, RHS] = Params.split(';'); - if (!RHS.empty()) - return make_error<StringError>( - formatv("too many CFGuardPass parameters '{}'", Params).str(), - inconvertibleErrorCode()); - - if (Param == "check") - return CFGuardPass::Mechanism::Check; - if (Param == "dispatch") - return CFGuardPass::Mechanism::Dispatch; - - return make_error<StringError>( - formatv("invalid CFGuardPass mechanism: '{}'", Param).str(), - inconvertibleErrorCode()); -} - Expected<bool> parseEarlyCSEPassOptions(StringRef Params) { return PassBuilder::parseSinglePassOption(Params, "memssa", "EarlyCSE"); } 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 c27a693ceecc1..c8acd9a383574 100644 --- a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/IR/CallingConv.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instruction.h" @@ -797,6 +798,21 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) { // Check if this module has the cfguard flag and read its value. CFGuardModuleFlag = M->getControlFlowGuardMode(); + // Warn if the module flag requests an unsupported CFGuard mechanism. + if (CFGuardModuleFlag == ControlFlowGuardMode::Enabled) { + if (auto *CI = mdconst::dyn_extract_or_null<ConstantInt>( + Mod.getModuleFlag("cfguard-mechanism"))) { + auto MechanismOverride = + static_cast<ControlFlowGuardMechanism>(CI->getZExtValue()); + if (MechanismOverride != ControlFlowGuardMechanism::Automatic && + MechanismOverride != ControlFlowGuardMechanism::Check) + Mod.getContext().diagnose( + DiagnosticInfoGeneric("only the Check Control Flow Guard mechanism " + "is supported for Arm64EC", + DS_Warning)); + } + } + PtrTy = PointerType::getUnqual(M->getContext()); I64Ty = Type::getInt64Ty(M->getContext()); VoidTy = Type::getVoidTy(M->getContext()); diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp index 652844e9dc591..b7a2bf58a2c45 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -703,7 +703,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/X86CodeGenPassBuilder.cpp b/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp index 74256b13ecf6b..8950e44307305 100644 --- a/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp +++ b/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp @@ -81,9 +81,7 @@ void X86CodeGenPassBuilder::addIRPasses(PassManagerWrapper &PMW) const { // Add Control Flow Guard checks. const Triple &TT = TM.getTargetTriple(); if (TT.isOSWindows()) - addFunctionPass(CFGuardPass(TT.isX86_64() ? CFGuardPass::Mechanism::Dispatch - : CFGuardPass::Mechanism::Check), - PMW); + addFunctionPass(CFGuardPass(), PMW); if (TM.Options.JMCInstrument) { flushFPMsToMPM(PMW); 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 96afcbf21c78f..767b4395273a2 100644 --- a/llvm/lib/Target/X86/X86TargetMachine.cpp +++ b/llvm/lib/Target/X86/X86TargetMachine.cpp @@ -442,11 +442,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 28d0eddca7ce2..a9819a54d20e5 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 @@ -148,7 +135,6 @@ class CFGuardImpl { private: // Only add checks if the module has them enabled. ControlFlowGuardMode CFGuardModuleFlag = ControlFlowGuardMode::Disabled; - StringRef GuardFnName; 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); } @@ -239,12 +225,36 @@ bool CFGuardImpl::doInitialization(Module &M) { 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::Check: + GuardMechanism = Mechanism::Check; + break; + case ControlFlowGuardMechanism::Dispatch: + 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, @@ -294,7 +304,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(); @@ -303,13 +313,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::isCFGuardCall(const CallBase *CB) { return CB->getCallingConv() == CallingConv::CFGuard_Check || diff --git a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll index 317ad2c42a3e1..e47fa5b59c837 100644 --- a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll +++ b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll @@ -1,14 +1,19 @@ - -; 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/.normal:// %s | llc -mtriple=arm64ec-pc-windows-msvc 2>&1 | FileCheck %s --check-prefixes=CHECK,EC,NOECWARN +; RUN: sed -e s/.check:// %s | llc -mtriple=aarch64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.check:// %s | llc -mtriple=arm64ec-pc-windows-msvc 2>&1 | FileCheck %s --check-prefixes=CHECK,EC,NOECWARN +; RUN: sed -e s/.dispatch:// %s | llc -mtriple=aarch64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH +; RUN: sed -e s/.dispatch:// %s | llc -mtriple=arm64ec-pc-windows-msvc 2>&1 | FileCheck %s --check-prefixes=CHECK,EC,ECWARN ; 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() +; NOECWARN-NOT: warning: +; ECWARN: warning: only the Check Control Flow Guard mechanism is supported for Arm64EC + define void @func_in_module_without_cfguard() #0 { entry: %func_ptr = alloca ptr, align 8 @@ -18,9 +23,30 @@ 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 + + ; Arm64EC Always uses check + ; EC-SAME: __os_arm64x_check_icall_cfg + ; EC-NOT: _dispatch_icall_ } -!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} +;check: !llvm.module.flags = !{!1, !2} +;dispatch: !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..e17730fb3e777 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/.check:// %s | llc -mtriple=arm-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.dispatch:// %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} +;check: !llvm.module.flags = !{!1, !2} +;dispatch: !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..cb2ba290dba31 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/.check:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.check:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK +; RUN: sed -e s/.dispatch:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH +; RUN: sed -e s/.dispatch:// %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} +;check: !llvm.module.flags = !{!1, !2} +;dispatch: !llvm.module.flags = !{!1, !3} diff --git a/llvm/test/Linker/cfguard.ll b/llvm/test/Linker/cfguard.ll new file mode 100644 index 0000000000000..2ffdb6ed612ab --- /dev/null +++ b/llvm/test/Linker/cfguard.ll @@ -0,0 +1,39 @@ +; RUN: split-file %s %t +; RUN: llvm-link %t/base.ll %t/mode.ll 2>&1 | FileCheck --check-prefix=MODE %s +; RUN: llvm-link %t/base.ll %t/mechanism.ll 2>&1 | FileCheck --check-prefix=MECHANISM %s +; RUN: llvm-link %t/base.ll %t/same.ll + +; MODE: warning: linking module flags 'cfguard': IDs have conflicting values +; MECHANISM: warning: linking module flags 'cfguard-mechanism': IDs have conflicting values + +;--- base.ll +define void @foo() { + ret void +} +!llvm.module.flags = !{!0,!1} +!0 = !{i32 2, !"cfguard", i32 1} +!1 = !{i32 2, !"cfguard-mechanism", i32 1} + +;--- mode.ll +define void @bar() { + ret void +} +!llvm.module.flags = !{!0,!1} +!0 = !{i32 2, !"cfguard", i32 2} +!1 = !{i32 2, !"cfguard-mechanism", i32 1} + +;--- mechanism.ll +define void @bar() { + ret void +} +!llvm.module.flags = !{!0,!1} +!0 = !{i32 2, !"cfguard", i32 1} +!1 = !{i32 2, !"cfguard-mechanism", i32 2} + +;--- same.ll +define void @bar() { + ret void +} +!llvm.module.flags = !{!0,!1} +!0 = !{i32 2, !"cfguard", i32 1} +!1 = !{i32 2, !"cfguard-mechanism", i32 1} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
