From d157a9bc8ba1085cc4808c6941412322a7fd884e Mon Sep 17 00:00:00 2001 From: Andrew Paverd <andrew.paverd@microsoft.com> Date: Mon, 28 Oct 2019 13:22:19 +0000 Subject: [PATCH] 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 --- clang/docs/ClangCommandLineReference.rst | 6 +- clang/include/clang/Basic/CodeGenOptions.def | 1 + clang/include/clang/Driver/CC1Options.td | 4 + clang/include/clang/Driver/Options.td | 2 - clang/lib/CodeGen/CodeGenModule.cpp | 7 +- clang/lib/Driver/ToolChains/Clang.cpp | 31 +- clang/lib/Driver/ToolChains/MSVC.cpp | 22 ++ clang/lib/Frontend/CompilerInvocation.cpp | 1 + clang/test/CodeGen/cfguardtable.c | 14 +- clang/test/Driver/cl-fallback.c | 3 +- clang/test/Driver/cl-options.c | 9 +- llvm/docs/LangRef.rst | 11 + llvm/docs/ReleaseNotes.rst | 5 + llvm/include/llvm/CodeGen/MachineFunction.h | 15 + llvm/include/llvm/CodeGen/Passes.h | 4 + llvm/include/llvm/CodeGen/TargetCallingConv.h | 8 +- llvm/include/llvm/CodeGen/TargetLowering.h | 3 +- llvm/include/llvm/IR/CallingConv.h | 6 + llvm/include/llvm/IR/InstrTypes.h | 5 + llvm/include/llvm/IR/LLVMContext.h | 1 + llvm/include/llvm/InitializePasses.h | 2 + llvm/include/llvm/MC/MCObjectFileInfo.h | 2 + llvm/include/llvm/Target/TargetCallingConv.td | 5 + llvm/include/llvm/Transforms/CFGuard.h | 26 ++ llvm/lib/AsmParser/LLLexer.cpp | 1 + llvm/lib/AsmParser/LLParser.cpp | 2 + llvm/lib/AsmParser/LLToken.h | 1 + llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 6 +- llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp | 22 +- llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h | 9 +- llvm/lib/CodeGen/CFGuardLongjmp.cpp | 119 +++++++ llvm/lib/CodeGen/CMakeLists.txt | 1 + llvm/lib/CodeGen/CodeGen.cpp | 1 + llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 8 + llvm/lib/CodeGen/SelectionDAG/FastISel.cpp | 2 + .../SelectionDAG/SelectionDAGBuilder.cpp | 26 +- llvm/lib/IR/AsmWriter.cpp | 1 + llvm/lib/IR/LLVMContext.cpp | 5 + llvm/lib/IR/Verifier.cpp | 12 +- llvm/lib/MC/MCObjectFileInfo.cpp | 5 + .../Target/AArch64/AArch64CallingConvention.h | 3 + .../AArch64/AArch64CallingConvention.td | 13 + llvm/lib/Target/AArch64/AArch64FastISel.cpp | 2 + .../Target/AArch64/AArch64ISelLowering.cpp | 6 +- .../Target/AArch64/AArch64RegisterInfo.cpp | 4 + .../Target/AArch64/AArch64TargetMachine.cpp | 9 + llvm/lib/Target/AArch64/LLVMBuild.txt | 2 +- llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp | 5 +- llvm/lib/Target/ARM/ARMCallingConv.h | 3 + llvm/lib/Target/ARM/ARMCallingConv.td | 15 + llvm/lib/Target/ARM/ARMFastISel.cpp | 2 + llvm/lib/Target/ARM/ARMISelLowering.cpp | 3 + llvm/lib/Target/ARM/ARMTargetMachine.cpp | 9 + llvm/lib/Target/ARM/LLVMBuild.txt | 2 +- llvm/lib/Target/X86/LLVMBuild.txt | 2 +- llvm/lib/Target/X86/X86AsmPrinter.cpp | 2 +- llvm/lib/Target/X86/X86CallingConv.td | 15 +- llvm/lib/Target/X86/X86FastISel.cpp | 1 + llvm/lib/Target/X86/X86RegisterInfo.cpp | 8 + llvm/lib/Target/X86/X86TargetMachine.cpp | 14 + llvm/lib/Transforms/CFGuard/CFGuard.cpp | 307 ++++++++++++++++++ llvm/lib/Transforms/CFGuard/CMakeLists.txt | 9 + llvm/lib/Transforms/CFGuard/LLVMBuild.txt | 21 ++ llvm/lib/Transforms/CMakeLists.txt | 1 + llvm/lib/Transforms/LLVMBuild.txt | 2 +- llvm/test/Bitcode/calling-conventions.3.2.ll | 9 + .../Bitcode/calling-conventions.3.2.ll.bc | Bin 1236 -> 2568 bytes .../Bitcode/operand-bundles-bc-analyzer.ll | 1 + llvm/test/CodeGen/AArch64/cfguard-checks.ll | 147 +++++++++ .../CodeGen/AArch64/cfguard-module-flag.ll | 25 ++ llvm/test/CodeGen/ARM/cfguard-checks.ll | 151 +++++++++ llvm/test/CodeGen/ARM/cfguard-module-flag.ll | 26 ++ llvm/test/CodeGen/WinCFGuard/cfguard.ll | 5 +- llvm/test/CodeGen/X86/cfguard-checks.ll | 231 +++++++++++++ llvm/test/CodeGen/X86/cfguard-module-flag.ll | 26 ++ .../CodeGen/X86/cfguard-x86-64-vectorcall.ll | 38 +++ .../CodeGen/X86/cfguard-x86-vectorcall.ll | 43 +++ 77 files changed, 1514 insertions(+), 62 deletions(-) create mode 100644 llvm/include/llvm/Transforms/CFGuard.h create mode 100644 llvm/lib/CodeGen/CFGuardLongjmp.cpp create mode 100644 llvm/lib/Transforms/CFGuard/CFGuard.cpp create mode 100644 llvm/lib/Transforms/CFGuard/CMakeLists.txt create mode 100644 llvm/lib/Transforms/CFGuard/LLVMBuild.txt create mode 100644 llvm/test/CodeGen/AArch64/cfguard-checks.ll create mode 100644 llvm/test/CodeGen/AArch64/cfguard-module-flag.ll create mode 100644 llvm/test/CodeGen/ARM/cfguard-checks.ll create mode 100644 llvm/test/CodeGen/ARM/cfguard-module-flag.ll create mode 100644 llvm/test/CodeGen/X86/cfguard-checks.ll create mode 100644 llvm/test/CodeGen/X86/cfguard-module-flag.ll create mode 100644 llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll create mode 100644 llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst index 5f6bb9829f5a..e8d561fae956 100644 --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -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> diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index d2266cc2d613..f8d94e352f28 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -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. diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 72019aa2a01a..1d550eb15ea8 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -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 diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 1c3d7f77707e..2401a31ceb9a 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -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]>, diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 75708d6e4966..480a33f27285 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -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 diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index ae12465d3f8b..c60dc76ae1bf 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -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; + } } } diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp index 1d31844bfcc8..4e143f6a5d3f 100644 --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -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"); @@ -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); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index f6e6f71b2805..f197a67e7a38 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -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); diff --git a/clang/test/CodeGen/cfguardtable.c b/clang/test/CodeGen/cfguardtable.c index 964929261297..05284f6d23a6 100644 --- a/clang/test/CodeGen/cfguardtable.c +++ b/clang/test/CodeGen/cfguardtable.c @@ -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} diff --git a/clang/test/Driver/cl-fallback.c b/clang/test/Driver/cl-fallback.c index 2e5698ce9f9f..a840883255fc 100644 --- a/clang/test/Driver/cl-fallback.c +++ b/clang/test/Driver/cl-fallback.c @@ -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 @@ -33,6 +33,7 @@ // CHECK: "/EHs-" // CHECK: "/Zl" // CHECK: "/MT" +// CHECK: "/guard:cf-" // CHECK: "-garbage" // CHECK: "-moregarbage" // CHECK: "/Tc" "{{.*cl-fallback.c}}" diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c index 354ed998f6f5..b82d69b6430a 100644 --- a/clang/test/Driver/cl-options.c +++ b/clang/test/Driver/cl-options.c @@ -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:' diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index c3f401126e16..7c3db50425d2 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -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 diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index 796b0524e0f2..131a08a4d4de 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -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 ---------------------- diff --git a/llvm/include/llvm/CodeGen/MachineFunction.h b/llvm/include/llvm/CodeGen/MachineFunction.h index 3a3176e51c51..6456e9277347 100644 --- a/llvm/include/llvm/CodeGen/MachineFunction.h +++ b/llvm/include/llvm/CodeGen/MachineFunction.h @@ -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 /// \{ diff --git a/llvm/include/llvm/CodeGen/Passes.h b/llvm/include/llvm/CodeGen/Passes.h index 1e765ce51e4a..5915cb3b2c73 100644 --- a/llvm/include/llvm/CodeGen/Passes.h +++ b/llvm/include/llvm/CodeGen/Passes.h @@ -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(); diff --git a/llvm/include/llvm/CodeGen/TargetCallingConv.h b/llvm/include/llvm/CodeGen/TargetCallingConv.h index db3d1175afee..f515050efadb 100644 --- a/llvm/include/llvm/CodeGen/TargetCallingConv.h +++ b/llvm/include/llvm/CodeGen/TargetCallingConv.h @@ -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; } diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index a58fca7e73f5..b9bf19474ca9 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -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); diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h index c1c979c2e2ab..d0906de3ea4e 100644 --- a/llvm/include/llvm/IR/CallingConv.h +++ b/llvm/include/llvm/IR/CallingConv.h @@ -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, diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index 7fb94e9d8c22..faf58cf19014 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -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; diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h index 91bd57dc5ac0..ea272740ba9d 100644 --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -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. diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h index 49f69340c828..b8108339abbf 100644 --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -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&); diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h index abc87bf27748..a28ddd7fa45f 100644 --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -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; diff --git a/llvm/include/llvm/Target/TargetCallingConv.td b/llvm/include/llvm/Target/TargetCallingConv.td index 7b1973cc3828..d5f3931c3d5d 100644 --- a/llvm/include/llvm/Target/TargetCallingConv.td +++ b/llvm/include/llvm/Target/TargetCallingConv.td @@ -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> { diff --git a/llvm/include/llvm/Transforms/CFGuard.h b/llvm/include/llvm/Transforms/CFGuard.h new file mode 100644 index 000000000000..86fcbc3c13e8 --- /dev/null +++ b/llvm/include/llvm/Transforms/CFGuard.h @@ -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 diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 5292b0e62744..819f8a0712db 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -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); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 664ef8d48446..41e1d0bd889a 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -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; diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h index f49feb2dc14d..2ebfb9da2579 100644 --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -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, diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 73c53d6c4af5..3692a03c268e 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -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; } diff --git a/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp b/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp index 290be81c6baa..f071a2583e5a 100644 --- a/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.cpp @@ -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); + } } diff --git a/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h b/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h index def0a59ab007..494a153b05ba 100644 --- a/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h +++ b/llvm/lib/CodeGen/AsmPrinter/WinCFGuard.h @@ -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 {} diff --git a/llvm/lib/CodeGen/CFGuardLongjmp.cpp b/llvm/lib/CodeGen/CFGuardLongjmp.cpp new file mode 100644 index 000000000000..42ad22b6cfab --- /dev/null +++ b/llvm/lib/CodeGen/CFGuardLongjmp.cpp @@ -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; +} diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index 50b469d6d936..1d561c3c3e81 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -10,6 +10,7 @@ add_llvm_library(LLVMCodeGen BuiltinGCs.cpp CalcSpillWeights.cpp CallingConvLower.cpp + CFGuardLongjmp.cpp CFIInstrInserter.cpp CodeGen.cpp CodeGenPrepare.cpp diff --git a/llvm/lib/CodeGen/CodeGen.cpp b/llvm/lib/CodeGen/CodeGen.cpp index ad9525f927e8..85696ccc482a 100644 --- a/llvm/lib/CodeGen/CodeGen.cpp +++ b/llvm/lib/CodeGen/CodeGen.cpp @@ -22,6 +22,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) { initializeAtomicExpandPass(Registry); initializeBranchFolderPassPass(Registry); initializeBranchRelaxationPass(Registry); + initializeCFGuardLongjmpPass(Registry); initializeCFIInstrInserterPass(Registry); initializeCodeGenPreparePass(Registry); initializeDeadMachineInstructionElimPass(Registry); diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp index 45cef4aca888..6e2d22057c1c 100644 --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -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; diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp index 6d7260d7aee5..4586a20562ec 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -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) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 51f5b1998bb7..e8e2bb49c9eb 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -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) { diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index b0c26e0ecaf5..f811c842cf56 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -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; diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp index 5e8772186a2a..cb13b27aa50f 100644 --- a/llvm/lib/IR/LLVMContext.cpp +++ b/llvm/lib/IR/LLVMContext.cpp @@ -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 && diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index b17fc433ed74..497dc394d960 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -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); } } diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index 70c0409ece7a..1a9ceb07dd5a 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -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, diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.h b/llvm/lib/Target/AArch64/AArch64CallingConvention.h index 5a55d090d7c8..59939e0684ed 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.h +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.h @@ -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); diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.td b/llvm/lib/Target/AArch64/AArch64CallingConvention.td index bccbbd4591ed..3c4121b1185e 100644 --- a/llvm/lib/Target/AArch64/AArch64CallingConvention.td +++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.td @@ -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, diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp index 277a3052f1e5..98410c2e747e 100644 --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -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; } diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index aa9e26c879a3..8e3a524ed2cb 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -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; } } diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp index de176088595d..918e89fd8868 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -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)) diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp index b3ed96e815be..0ec1e667d692 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -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()); diff --git a/llvm/lib/Target/AArch64/LLVMBuild.txt b/llvm/lib/Target/AArch64/LLVMBuild.txt index 620d3a857e8c..08ff46454519 100644 --- a/llvm/lib/Target/AArch64/LLVMBuild.txt +++ b/llvm/lib/Target/AArch64/LLVMBuild.txt @@ -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 diff --git a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp index 1eaf871867e0..0276ff140ac8 100644 --- a/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp @@ -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 diff --git a/llvm/lib/Target/ARM/ARMCallingConv.h b/llvm/lib/Target/ARM/ARMCallingConv.h index 615634551d90..7c692f03b440 100644 --- a/llvm/lib/Target/ARM/ARMCallingConv.h +++ b/llvm/lib/Target/ARM/ARMCallingConv.h @@ -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); diff --git a/llvm/lib/Target/ARM/ARMCallingConv.td b/llvm/lib/Target/ARM/ARMCallingConv.td index 61d2d83ddc40..55111f544ff7 100644 --- a/llvm/lib/Target/ARM/ARMCallingConv.td +++ b/llvm/lib/Target/ARM/ARMCallingConv.td @@ -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)>; diff --git a/llvm/lib/Target/ARM/ARMFastISel.cpp b/llvm/lib/Target/ARM/ARMFastISel.cpp index 1fc5ff6921c6..6c204bc0ed53 100644 --- a/llvm/lib/Target/ARM/ARMFastISel.cpp +++ b/llvm/lib/Target/ARM/ARMFastISel.cpp @@ -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); } } diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp index bb6bca51efde..6e511e68d7a2 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -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); } } diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp index 5c8007f101d9..7f85b93beac5 100644 --- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp +++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp @@ -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()); } diff --git a/llvm/lib/Target/ARM/LLVMBuild.txt b/llvm/lib/Target/ARM/LLVMBuild.txt index 7a6b3a8401ef..40f8c2c93b07 100644 --- a/llvm/lib/Target/ARM/LLVMBuild.txt +++ b/llvm/lib/Target/ARM/LLVMBuild.txt @@ -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 diff --git a/llvm/lib/Target/X86/LLVMBuild.txt b/llvm/lib/Target/X86/LLVMBuild.txt index bfc0eda21faf..5d09ced3d946 100644 --- a/llvm/lib/Target/X86/LLVMBuild.txt +++ b/llvm/lib/Target/X86/LLVMBuild.txt @@ -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 diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp index 8d27be30a277..cc1e79d7a292 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -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); diff --git a/llvm/lib/Target/X86/X86CallingConv.td b/llvm/lib/Target/X86/X86CallingConv.td index 4c49d68bec99..30d05c638146 100644 --- a/llvm/lib/Target/X86/X86CallingConv.td +++ b/llvm/lib/Target/X86/X86CallingConv.td @@ -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, diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp index e5e089d07d55..e5e84bb5f19e 100644 --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -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; } diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp index ff625325b4c9..9362c60ae497 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -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; diff --git a/llvm/lib/Target/X86/X86TargetMachine.cpp b/llvm/lib/Target/X86/X86TargetMachine.cpp index c15297134e4d..21f1ef9cb06e 100644 --- a/llvm/lib/Target/X86/X86TargetMachine.cpp +++ b/llvm/lib/Target/X86/X86TargetMachine.cpp @@ -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 { diff --git a/llvm/lib/Transforms/CFGuard/CFGuard.cpp b/llvm/lib/Transforms/CFGuard/CFGuard.cpp new file mode 100644 index 000000000000..9517e21ffdef --- /dev/null +++ b/llvm/lib/Transforms/CFGuard/CFGuard.cpp @@ -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); +} \ No newline at end of file diff --git a/llvm/lib/Transforms/CFGuard/CMakeLists.txt b/llvm/lib/Transforms/CFGuard/CMakeLists.txt new file mode 100644 index 000000000000..65d3a0f0b6f8 --- /dev/null +++ b/llvm/lib/Transforms/CFGuard/CMakeLists.txt @@ -0,0 +1,9 @@ +add_llvm_library(LLVMCFGuard + CFGuard.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms + + DEPENDS + intrinsics_gen + ) diff --git a/llvm/lib/Transforms/CFGuard/LLVMBuild.txt b/llvm/lib/Transforms/CFGuard/LLVMBuild.txt new file mode 100644 index 000000000000..d20cdb443eb3 --- /dev/null +++ b/llvm/lib/Transforms/CFGuard/LLVMBuild.txt @@ -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 diff --git a/llvm/lib/Transforms/CMakeLists.txt b/llvm/lib/Transforms/CMakeLists.txt index 74db9e53304d..dda5f6de11e3 100644 --- a/llvm/lib/Transforms/CMakeLists.txt +++ b/llvm/lib/Transforms/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(Vectorize) add_subdirectory(Hello) add_subdirectory(ObjCARC) add_subdirectory(Coroutines) +add_subdirectory(CFGuard) diff --git a/llvm/lib/Transforms/LLVMBuild.txt b/llvm/lib/Transforms/LLVMBuild.txt index 75c614fd2c47..5fb5efcc068c 100644 --- a/llvm/lib/Transforms/LLVMBuild.txt +++ b/llvm/lib/Transforms/LLVMBuild.txt @@ -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 diff --git a/llvm/test/Bitcode/calling-conventions.3.2.ll b/llvm/test/Bitcode/calling-conventions.3.2.ll index b60f1d70ca04..d968d802b9cd 100644 --- a/llvm/test/Bitcode/calling-conventions.3.2.ll +++ b/llvm/test/Bitcode/calling-conventions.3.2.ll @@ -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() diff --git a/llvm/test/Bitcode/calling-conventions.3.2.ll.bc b/llvm/test/Bitcode/calling-conventions.3.2.ll.bc index b3fad967db0e04778d63fcd3dda2a0e3777aea91..b7ac769a82b34aeabe70edab8562f53bc0601f31 100644 GIT binary patch literal 2568 zcmZvde{2(F7{}l4x?LIdT42LY+S+p?nMB8}MQGR7xE<;QrbQvaWTx$|Evt0ht*vFC zF*n+I1!q#m(5k2`5*3X}jTkZUM@Iqc(ir@isfe2!6UjyvMJ4JVK6f2!ckx~N-ury+ zd7t-rzR%mPh0a{KqY9yNgiwc)s%v}rV_(}pCmz_my>_F_SdK^~LNhXimX?vI4189? z2k)yr*qoE!SD`jq24!*O>T*SmUjB3kr@XtYWpHVWM{TS!D>R!cjmAVPdz!4>xSStQ zHxHJ1a*B7V4D>w-!)G$1F|Jutzb4uEM7sT;A)AA^`wa+H!qa$5PGRu$%MX#q;xJ9d zhm%#yw>&klH164=R@b7{$P1nb-9&C7e$1TlzB}91@R9BHaKHcWYi$kTPu=USvv<CK zw)_2$W?p;!rz;=qnjZXMY=*3+<aY_%<_AU{yJ-L|xo#rlLQiemzpM_5y*`wg<YlsK z^>q^=PNsRVZF8n_vCrt{ecKg9Cn3(xarxWoBFZ4;@yp{&UMmt*;cC6RvJFW>tyy`* zP$lR*KQtWq&02Hct@7&tAqvrNMjd5aB`5xsmcJ@FapTWRTFK%@A^9^KR}8fj5dsl@ zTe8~RUxFa9{*{|JNn)ECk|jQ(R(szm$qEUf);v?96+)1Fb)ZyFB<K*rgdQ&pIPS3i z{sj{jLV^FJN<<K4Kdz`Qb%NH6-g~T+2(q(qV@rn6;={L=03oHrZ`(@RC#E!7E=_6s z+HIxVgp`I3N>iH6uPAkbls0v}TuNN1ZoQ(aT7^*J4DdtnF)29h=?Sf`N@h>c=6H6X zg~Rvs;^9<}avG^-)x=efFezK~r$%(24{Lo?x#$*KmB;Kk+CG5U^SuKR7JpoU?`h>( z7T?Qa(O#Z4@HP2m#7vTypj1IhH91GjIS7;bwC)0_9V2z8+<G6i-}+(9dXBc7z-%AJ zY;nw*pR0<x@W(sx4V`=lW{zi*JnO=v^?3AJIfUKK@lFNbqv1m-eow0JwkvbQm2u*a z3^7At9l2rc)UY-$&gb3QQBr%lPdDnW_%=yfa<nBS&L^|xA=;e4tV7wgaOl7LtY2f+ zbXNJPpJ#XQAvPB2#Cu(QM8ijp@-?47q`HDszd4AhBr$oHYQjLwKoy31-T4vysb>9` zr1pHD{`|OhYC?BeruS*Kn$vXCk(f2_sGBdu#qaLLdsADtTurJ31@w>VN8Q>>r0&9q z?n#+mNSM^kxhuBy$ISVxHBH+=kbh6vrf6#d%<RCKN~o0I4f!bWz4dq~)z?0$CT23k zMF;UaRXg%!vwmvg#<2<A1xTQ^#+r`VPsCQmv*rQXlF!;sj#zS8sL_^hJ<+4UA*&t` za0g%W!&zd+rJ5bDm@}v@QmTsv)t?R`s3s;U0?gmXD}Eg(rX0jIik|qM)J+d-=VW5$ z?t8xeE^CHnrL$|t=%zf~l%SiS>T~p+=(q-<WpJmKR)*@KSNPr?Y>Kvn<Z0S+F1v49 zdv^GSOU=SnnkIDx4OXt3bP>NeKw8z5Ah7Be2XR%nvR}Y&M+H3byzN-bdITDV*^kC- z(7a?;_`0|a2nk>J74ngUOvV{B@{XfmN_Ymi`+s;2xNCu9u&)s39Pn4l+y?mdSYYFI z8u`l-HU{h^3A+UBh=ip`jr>Ik<G}9)2}=NbUc%DAQWBN~_MC*JfITZ=M}Zxdus47W zN!UqXha@ZmY_O~cG!S}eB-FRWmmR?BWfpG5vsmkhg{vOMI^9j&jc;KInU$+~4|`+S z%B}hY%Qf3L-DlXP2^)9wSD0d-og;q~WE96E@PGRNtd?U#7NKtp!}#3c2t2w29+;U% zD$M#r&RyP6&>Ik!zQ=-$KN#@_oZU>o+2I$q?0n1}@;I4JFSCnbdd=&d;fRNE2Lc5{ zjJRM$I{o3oJ9ntd>1LTQ!-y-%(i!!!4AT{6*O@3M92jC4HqtwviLml`qkhIKgmMa* z3eWl6MN}xD@V1c>Uze0pw1s*^OH!1$b8%&&+oJLoNU_ERR4i0P#cGRYq2$GOp>B~a zDqbW-1s2SL42@I+{Dq@IgU4htGA5(fL|Hr@(*|E-hZl%t{W^=8F*6%H-VTN_dl;&r vJrIa?wMV?+NPCMv!gPDQ?Ltt0aOdqzcQEP=M*Q8uaD%DA*boR5t_%7PVM^52 literal 1236 zcmbu8PfXKL9LIm#I`%fIE3m*&hV3?t#wFr5bS7Iiw_-}fWEhQH2!$Bp#RHlcIVhzR zEHdSv2acc~IG8x};=zOglMq1@nK3&Vj0y2#f;uk-#Mcf7q(_@<@0-v2zVGk*mNgnn zePT!euMVJCq(Kh=rTAm&agCf=BpQk+@99yUMAjHlWi>I_E}(jSbhJ3iQeImC*&8fg z*3DUl8CUv(Kvpu>-KIsEYM+{ziY%<-NEo02%y{)WWkg@;Bg&X368`!kO%B^i8>K|C z#5Pi-3u**C#B4f*tpN5%h*L$1s^^qO0R2Zqr4~RfThh#^ikF>FqpHHTt9_~xn0sJR z<^oP*Y=yH&36+seL{nkJV@hQ>kShc}ECdD$fkGiLAFR|<PXeBs7r0)2lu|E`lqUHH z56uf)cpKrH=GFg4b=g6g=tDwP`g2u>O&5}50ck!F6--^XOgF|m$u+QkqxR>7{XQ~U zJvZxkH(AwDYgBE$2bT?@#aJj|2;q!Q8lV-gCr41~`Gy$J9G+Ii3sU4M89YnJk$5Sf zPkLr@^!wKMeg41@hE{gj8Wi2As1}L5abSDP`o`C?6}c!!#(R4GWz!oEk{dcFy2+Sv za>cW)6+cc!V!a2ab?dk>+P>h1r5~-Y{!|El>_MW#zhmnPV%wIlWhTn*o3<Mj=Q(TX zrlq7V-Cj9{J9K~l9d^i=JJH!B_iw9Q*&5oJ{!yKt$gQ5*biK8i#oODlYR@b%sP3A+ zf7R1VH^=5V>zzz^rE!GL7AfNlB|i-*7bo%-3w0vAjz;P1oE~2shG?_c8OQa)m_ty^ zU4HFC<-85wLSzE?3P$=o-W7S$y34RO5zPEL#(tm%YZOnWhf_TGr-wEgz<}2>Vpg67 z;03JP=-n)GNcpSnWn8tE#T{C<O5D|)&8GV`?5m_@0;^>*p=HUm^5*RAH!Nw{IICq7 TgqBUEB~9K-Ny}2K!j$JP{_!k4 diff --git a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll index 8f7aa37cbed8..ac7feec77aa1 100644 --- a/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll +++ b/llvm/test/Bitcode/operand-bundles-bc-analyzer.ll @@ -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 diff --git a/llvm/test/CodeGen/AArch64/cfguard-checks.ll b/llvm/test/CodeGen/AArch64/cfguard-checks.ll new file mode 100644 index 000000000000..4b6a7ebc243d --- /dev/null +++ b/llvm/test/CodeGen/AArch64/cfguard-checks.ll @@ -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} diff --git a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll new file mode 100644 index 000000000000..25c53019d93c --- /dev/null +++ b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll @@ -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} diff --git a/llvm/test/CodeGen/ARM/cfguard-checks.ll b/llvm/test/CodeGen/ARM/cfguard-checks.ll new file mode 100644 index 000000000000..1835bcfc1b58 --- /dev/null +++ b/llvm/test/CodeGen/ARM/cfguard-checks.ll @@ -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} diff --git a/llvm/test/CodeGen/ARM/cfguard-module-flag.ll b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll new file mode 100644 index 000000000000..87878a084dcc --- /dev/null +++ b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll @@ -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} diff --git a/llvm/test/CodeGen/WinCFGuard/cfguard.ll b/llvm/test/CodeGen/WinCFGuard/cfguard.ll index 2ddd34632195..1018c1fa62c4 100644 --- a/llvm/test/CodeGen/WinCFGuard/cfguard.ll +++ b/llvm/test/CodeGen/WinCFGuard/cfguard.ll @@ -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 "} diff --git a/llvm/test/CodeGen/X86/cfguard-checks.ll b/llvm/test/CodeGen/X86/cfguard-checks.ll new file mode 100644 index 000000000000..5a930afef133 --- /dev/null +++ b/llvm/test/CodeGen/X86/cfguard-checks.ll @@ -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} diff --git a/llvm/test/CodeGen/X86/cfguard-module-flag.ll b/llvm/test/CodeGen/X86/cfguard-module-flag.ll new file mode 100644 index 000000000000..1b2e71c1a455 --- /dev/null +++ b/llvm/test/CodeGen/X86/cfguard-module-flag.ll @@ -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} diff --git a/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll b/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll new file mode 100644 index 000000000000..a554ebeeb902 --- /dev/null +++ b/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll @@ -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} diff --git a/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll b/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll new file mode 100644 index 000000000000..0f31f5ba4b6b --- /dev/null +++ b/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll @@ -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}