Index: docs/SanitizerStats.rst =================================================================== --- /dev/null +++ docs/SanitizerStats.rst @@ -0,0 +1,62 @@ +============== +SanitizerStats +============== + +.. contents:: + :local: + +Introduction +============ + +The sanitizers support a simple mechanism for gathering profiling statistics +to help understand the overhead associated with sanitizers. + +How to build and run +==================== + +SanitizerStats can currently only be used with :doc:`ControlFlowIntegrity`. +In addition to ``-fsanitize=cfi*``, pass the ``-fsanitize-stats`` flag. +This will cause the program to count the number of times that each control +flow integrity check in the program fires. + +At run time, set the ``SANITIZER_STATS_PATH`` environment variable to direct +statistics output to a file. The file will be written on process exit. +The following substitutions will be applied to the environment variable: + + - ``%b`` -- The executable basename. + - ``%p`` -- The process ID. + +You can also send the ``SIGUSR2`` signal to a process to make it write +sanitizer statistics immediately. + +The ``sanstats`` program can be used to dump statistics. It takes as a +command line argument the path to a statistics file produced by a program +compiled with ``-fsanitize-stats``. + +The output of ``sanstats`` is in four columns, separated by spaces. The first +column is the file and line number of the call site. The second column is +the function name. The third column is the type of statistic gathered (in +this case, the type of control flow integrity check). The fourth column is +the call count. + +Example: + +.. code-block:: console + + $ cat -n vcall.cc + 1 struct A { + 2 virtual void f() {} + 3 }; + 4 + 5 __attribute__((noinline)) void g(A *a) { + 6 a->f(); + 7 } + 8 + 9 int main() { + 10 A a; + 11 g(&a); + 12 } + $ clang++ -fsanitize=cfi -flto -fuse-ld=gold vcall.cc -fsanitize-stats -g + $ SANITIZER_STATS_PATH=a.stats ./a.out + $ sanstats a.stats + vcall.cc:6 _Z1gP1A cfi-vcall 1 Index: docs/UsersManual.rst =================================================================== --- docs/UsersManual.rst +++ docs/UsersManual.rst @@ -1038,6 +1038,11 @@ Enable simple code coverage in addition to certain sanitizers. See :doc:`SanitizerCoverage` for more details. +**-f[no-]sanitize-stats** + + Enable simple statistics gathering for the enabled sanitizers. + See :doc:`SanitizerStats` for more details. + .. option:: -fsanitize-undefined-trap-on-error Deprecated alias for ``-fsanitize-trap=undefined``. Index: docs/index.rst =================================================================== --- docs/index.rst +++ docs/index.rst @@ -28,6 +28,7 @@ DataFlowSanitizer LeakSanitizer SanitizerCoverage + SanitizerStats SanitizerSpecialCaseList ControlFlowIntegrity SafeStack Index: include/clang/AST/ASTConsumer.h =================================================================== --- include/clang/AST/ASTConsumer.h +++ include/clang/AST/ASTConsumer.h @@ -94,9 +94,10 @@ /// The default implementation passes it to HandleTopLevelDecl. virtual void HandleImplicitImportDecl(ImportDecl *D); - /// \brief Handle a pragma that appends to Linker Options. Currently this - /// only exists to support Microsoft's #pragma comment(linker, "/foo"). - virtual void HandleLinkerOptionPragma(llvm::StringRef Opts) {} + /// \brief Handle a pragma or command line flag that appends to Linker + /// Options. This exists to support Microsoft's + /// #pragma comment(linker, "/foo") and the frontend flag --linker-option=. + virtual void HandleLinkerOption(llvm::StringRef Opts) {} /// \brief Handle a pragma that emits a mismatch identifier and value to the /// object file for the linker to work with. Currently, this only exists to Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -252,6 +252,8 @@ HelpText<"Run the BB vectorization passes">; def dependent_lib : Joined<["--"], "dependent-lib=">, HelpText<"Add dependent library">; +def linker_option : Joined<["--"], "linker-option=">, + HelpText<"Add linker option">; def fsanitize_coverage_type : Joined<["-"], "fsanitize-coverage-type=">, HelpText<"Sanitizer coverage type">; def fsanitize_coverage_indirect_calls Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -640,6 +640,12 @@ def fno_sanitize_cfi_cross_dso : Flag<["-"], "fno-sanitize-cfi-cross-dso">, Group, Flags<[CC1Option]>, HelpText<"Disable control flow integrity (CFI) checks for cross-DSO calls.">; +def fsanitize_stats : Flag<["-"], "fsanitize-stats">, + Group, Flags<[CC1Option]>, + HelpText<"Enable sanitizer statistics gathering.">; +def fno_sanitize_stats : Flag<["-"], "fno-sanitize-stats">, + Group, Flags<[CC1Option]>, + HelpText<"Disable sanitizer statistics gathering.">; def funsafe_math_optimizations : Flag<["-"], "funsafe-math-optimizations">, Group; def fno_unsafe_math_optimizations : Flag<["-"], "fno-unsafe-math-optimizations">, Index: include/clang/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -36,6 +36,7 @@ bool AsanSharedRuntime = false; bool LinkCXXRuntimes = false; bool NeedPIE = false; + bool Stats = false; public: /// Parses the sanitizer arguments from an argument list. @@ -56,6 +57,7 @@ } bool needsCfiRt() const; bool needsCfiDiagRt() const; + bool needsStatsRt() const { return Stats; } bool requiresPIE() const; bool needsUnwindTables() const; Index: include/clang/Frontend/CodeGenOptions.h =================================================================== --- include/clang/Frontend/CodeGenOptions.h +++ include/clang/Frontend/CodeGenOptions.h @@ -164,6 +164,9 @@ /// A list of dependent libraries. std::vector DependentLibraries; + /// A list of linker options to embed in the object file. + std::vector LinkerOptions; + /// Name of the profile file to use as output for -fprofile-instr-generate /// and -fprofile-generate. std::string InstrProfileOutput; Index: include/clang/Frontend/CodeGenOptions.def =================================================================== --- include/clang/Frontend/CodeGenOptions.def +++ include/clang/Frontend/CodeGenOptions.def @@ -134,6 +134,7 @@ ///< in sanitizer coverage. CODEGENOPT(SanitizeCoverage8bitCounters, 1, 0) ///< Use 8-bit frequency counters ///< in sanitizer coverage. +CODEGENOPT(SanitizeStats , 1, 0) ///< Collect statistics for sanitizers. CODEGENOPT(SimplifyLibCalls , 1, 1) ///< Set when -fbuiltin is enabled. CODEGENOPT(SoftFloat , 1, 0) ///< -soft-float. CODEGENOPT(StrictEnums , 1, 0) ///< Optimize based on strict enum definition. Index: include/clang/Frontend/MultiplexConsumer.h =================================================================== --- include/clang/Frontend/MultiplexConsumer.h +++ include/clang/Frontend/MultiplexConsumer.h @@ -44,7 +44,7 @@ void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override; void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override; void HandleImplicitImportDecl(ImportDecl *D) override; - void HandleLinkerOptionPragma(llvm::StringRef Opts) override; + void HandleLinkerOption(llvm::StringRef Opts) override; void HandleDetectMismatch(llvm::StringRef Name, llvm::StringRef Value) override; void HandleDependentLibrary(llvm::StringRef Lib) override; Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -26,6 +26,7 @@ #include "clang/Frontend/CodeGenOptions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Metadata.h" +#include "llvm/Transforms/Utils/SanitizerStats.h" using namespace clang; using namespace CodeGen; @@ -2551,6 +2552,22 @@ return; SanitizerScope SanScope(this); + llvm::SanitizerStatKind SSK; + switch (TCK) { + case CFITCK_VCall: + SSK = llvm::SanStat_CFI_VCall; + break; + case CFITCK_NVCall: + SSK = llvm::SanStat_CFI_NVCall; + break; + case CFITCK_DerivedCast: + SSK = llvm::SanStat_CFI_DerivedCast; + break; + case CFITCK_UnrelatedCast: + SSK = llvm::SanStat_CFI_UnrelatedCast; + break; + } + EmitSanitizerStatReport(SSK); llvm::Metadata *MD = CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0)); Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -32,6 +32,7 @@ #include "llvm/IR/MDBuilder.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Transforms/Utils/SanitizerStats.h" using namespace clang; using namespace CodeGen; @@ -3852,6 +3853,7 @@ if (SanOpts.has(SanitizerKind::CFIICall) && (!TargetDecl || !isa(TargetDecl))) { SanitizerScope SanScope(this); + EmitSanitizerStatReport(llvm::SanStat_CFI_ICall); llvm::Metadata *MD = CGM.CreateMetadataIdentifierForType(QualType(FnType, 0)); llvm::Value *BitSetName = llvm::MetadataAsValue::get(getLLVMContext(), MD); Index: lib/CodeGen/CodeGenAction.cpp =================================================================== --- lib/CodeGen/CodeGenAction.cpp +++ lib/CodeGen/CodeGenAction.cpp @@ -214,8 +214,8 @@ Gen->HandleVTable(RD); } - void HandleLinkerOptionPragma(llvm::StringRef Opts) override { - Gen->HandleLinkerOptionPragma(Opts); + void HandleLinkerOption(llvm::StringRef Opts) override { + Gen->HandleLinkerOption(Opts); } void HandleDetectMismatch(llvm::StringRef Name, Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -36,6 +36,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/IR/ValueHandle.h" #include "llvm/Support/Debug.h" +#include "llvm/Transforms/Utils/SanitizerStats.h" namespace llvm { class BasicBlock; @@ -3187,6 +3188,8 @@ Address EmitPointerWithAlignment(const Expr *Addr, AlignmentSource *Source = nullptr); + void EmitSanitizerStatReport(llvm::SanitizerStatKind SSK); + private: QualType getVarArgType(const Expr *Arg); Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -1956,3 +1956,12 @@ << FD->getDeclName() << TargetDecl->getDeclName() << MissingFeature; } } + +void CodeGenFunction::EmitSanitizerStatReport(llvm::SanitizerStatKind SSK) { + if (!CGM.getCodeGenOpts().SanitizeStats) + return; + + llvm::IRBuilder<> IRB(Builder.GetInsertBlock(), Builder.GetInsertPoint()); + IRB.SetCurrentDebugLocation(Builder.getCurrentDebugLocation()); + CGM.getSanStats().create(IRB, SSK); +} Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -33,6 +33,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/IR/Module.h" #include "llvm/IR/ValueHandle.h" +#include "llvm/Transforms/Utils/SanitizerStats.h" namespace llvm { class Module; @@ -289,6 +290,7 @@ llvm::MDNode *NoObjCARCExceptionsMetadata; std::unique_ptr PGOReader; InstrProfStats PGOStats; + std::unique_ptr SanStats; // A set of references that have only been seen via a weakref so far. This is // used to remove the weak of the reference if we ever see a direct reference @@ -1126,6 +1128,8 @@ /// \breif Get the declaration of std::terminate for the platform. llvm::Constant *getTerminateFn(); + llvm::SanitizerStatReport &getSanStats(); + private: llvm::Constant * GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D, Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -392,6 +392,8 @@ if (CoverageMapping) CoverageMapping->emit(); emitLLVMUsed(); + if (SanStats) + SanStats->finish(); if (CodeGenOpts.Autolink && (Context.getLangOpts().Modules || !LinkerOptionsMetadata.empty())) { @@ -4007,3 +4009,10 @@ Target.getTargetOpts().Features); } } + +llvm::SanitizerStatReport &CodeGenModule::getSanStats() { + if (!SanStats) + SanStats = llvm::make_unique(&getModule()); + + return *SanStats; +} Index: lib/CodeGen/ModuleBuilder.cpp =================================================================== --- lib/CodeGen/ModuleBuilder.cpp +++ lib/CodeGen/ModuleBuilder.cpp @@ -103,8 +103,10 @@ PreprocessorOpts, CodeGenOpts, *M, Diags, CoverageInfo)); - for (size_t i = 0, e = CodeGenOpts.DependentLibraries.size(); i < e; ++i) - HandleDependentLibrary(CodeGenOpts.DependentLibraries[i]); + for (auto &&Lib : CodeGenOpts.DependentLibraries) + HandleDependentLibrary(Lib); + for (auto &&Opt : CodeGenOpts.LinkerOptions) + HandleLinkerOption(Opt); } void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) override { @@ -222,7 +224,7 @@ Builder->EmitVTable(RD); } - void HandleLinkerOptionPragma(llvm::StringRef Opts) override { + void HandleLinkerOption(llvm::StringRef Opts) override { Builder->AppendLinkerOptions(Opts); } Index: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -431,6 +431,9 @@ NeedPIE |= CfiCrossDso; } + Stats = Args.hasFlag(options::OPT_fsanitize_stats, + options::OPT_fno_sanitize_stats, false); + // Parse -f(no-)?sanitize-coverage flags if coverage is supported by the // enabled sanitizers. if (AllAddedKinds & SupportsCoverage) { @@ -548,6 +551,20 @@ return Res; } +static void addIncludeLinkerOption(const ToolChain &TC, + const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs, + StringRef SymbolName) { + SmallString<64> LinkerOptionFlag; + LinkerOptionFlag = "--linker-option=/include:"; + if (TC.getTriple().getArch() == llvm::Triple::x86) { + // Win32 mangles C function names with a '_' prefix. + LinkerOptionFlag += '_'; + } + LinkerOptionFlag += SymbolName; + CmdArgs.push_back(Args.MakeArgString(LinkerOptionFlag)); +} + void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs, types::ID InputType) const { @@ -584,6 +601,9 @@ if (CfiCrossDso) CmdArgs.push_back(Args.MakeArgString("-fsanitize-cfi-cross-dso")); + if (Stats) + CmdArgs.push_back(Args.MakeArgString("-fsanitize-stats")); + if (AsanFieldPadding) CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" + llvm::utostr(AsanFieldPadding))); @@ -619,6 +639,18 @@ CmdArgs.push_back(Args.MakeArgString( "--dependent-lib=" + TC.getCompilerRT(Args, "ubsan_standalone_cxx"))); } + if (TC.getTriple().isOSWindows() && needsStatsRt()) { + CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" + + TC.getCompilerRT(Args, "stats_client"))); + + // The main executable must export the stats runtime. + // FIXME: Only exporting from the main executable (e.g. based on whether the + // translation unit defines main()) would save a little space, but having + // multiple copies of the runtime shouldn't hurt. + CmdArgs.push_back(Args.MakeArgString("--dependent-lib=" + + TC.getCompilerRT(Args, "stats"))); + addIncludeLinkerOption(TC, Args, CmdArgs, "__sanitizer_stats_register"); + } } SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A, Index: lib/Driver/ToolChains.cpp =================================================================== --- lib/Driver/ToolChains.cpp +++ lib/Driver/ToolChains.cpp @@ -413,6 +413,13 @@ AddLinkSanitizerLibArgs(Args, CmdArgs, "ubsan"); if (Sanitize.needsTsanRt()) AddLinkSanitizerLibArgs(Args, CmdArgs, "tsan"); + if (Sanitize.needsStatsRt()) { + StringRef OS = isTargetMacOS() ? "osx" : "iossim"; + AddLinkRuntimeLib(Args, CmdArgs, + (Twine("libclang_rt.stats_client_") + OS + ".a").str(), + /*AlwaysLink=*/true); + AddLinkSanitizerLibArgs(Args, CmdArgs, "stats"); + } // Otherwise link libSystem, then the dynamic runtime library, and finally any // target specific static runtime library. Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -2749,12 +2749,12 @@ static void addSanitizerRuntime(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs, StringRef Sanitizer, - bool IsShared) { - // Static runtimes must be forced into executable, so we wrap them in + bool IsShared, bool IsWhole) { + // Wrap any static runtimes that must be forced into executable in // whole-archive. - if (!IsShared) CmdArgs.push_back("-whole-archive"); + if (IsWhole) CmdArgs.push_back("-whole-archive"); CmdArgs.push_back(TC.getCompilerRTArgString(Args, Sanitizer, IsShared)); - if (!IsShared) CmdArgs.push_back("-no-whole-archive"); + if (IsWhole) CmdArgs.push_back("-no-whole-archive"); } // Tries to use a file with the list of dynamic symbols that need to be exported @@ -2787,12 +2787,17 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, SmallVectorImpl &SharedRuntimes, SmallVectorImpl &StaticRuntimes, - SmallVectorImpl &HelperStaticRuntimes) { + SmallVectorImpl &NonWholeStaticRuntimes, + SmallVectorImpl &HelperStaticRuntimes, + SmallVectorImpl &RequiredSymbols) { const SanitizerArgs &SanArgs = TC.getSanitizerArgs(); // Collect shared runtimes. if (SanArgs.needsAsanRt() && SanArgs.needsSharedAsanRt()) { SharedRuntimes.push_back("asan"); } + // The stats_client library is also statically linked into DSOs. + if (SanArgs.needsStatsRt()) + StaticRuntimes.push_back("stats_client"); // Collect static runtimes. if (Args.hasArg(options::OPT_shared) || TC.getTriple().isAndroid()) { @@ -2833,6 +2838,10 @@ StaticRuntimes.push_back("cfi"); if (SanArgs.needsCfiDiagRt()) StaticRuntimes.push_back("cfi_diag"); + if (SanArgs.needsStatsRt()) { + NonWholeStaticRuntimes.push_back("stats"); + RequiredSymbols.push_back("__sanitizer_stats_register"); + } } // Should be called before we add system libraries (C++ ABI, libstdc++/libc++, @@ -2840,18 +2849,27 @@ static bool addSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { SmallVector SharedRuntimes, StaticRuntimes, - HelperStaticRuntimes; + NonWholeStaticRuntimes, HelperStaticRuntimes, RequiredSymbols; collectSanitizerRuntimes(TC, Args, SharedRuntimes, StaticRuntimes, - HelperStaticRuntimes); + NonWholeStaticRuntimes, HelperStaticRuntimes, + RequiredSymbols); for (auto RT : SharedRuntimes) - addSanitizerRuntime(TC, Args, CmdArgs, RT, true); + addSanitizerRuntime(TC, Args, CmdArgs, RT, true, false); for (auto RT : HelperStaticRuntimes) - addSanitizerRuntime(TC, Args, CmdArgs, RT, false); + addSanitizerRuntime(TC, Args, CmdArgs, RT, false, true); bool AddExportDynamic = false; for (auto RT : StaticRuntimes) { - addSanitizerRuntime(TC, Args, CmdArgs, RT, false); + addSanitizerRuntime(TC, Args, CmdArgs, RT, false, true); + AddExportDynamic |= !addSanitizerDynamicList(TC, Args, CmdArgs, RT); + } + for (auto RT : NonWholeStaticRuntimes) { + addSanitizerRuntime(TC, Args, CmdArgs, RT, false, false); AddExportDynamic |= !addSanitizerDynamicList(TC, Args, CmdArgs, RT); } + for (auto S : RequiredSymbols) { + CmdArgs.push_back("-u"); + CmdArgs.push_back(Args.MakeArgString(S)); + } // If there is a static runtime with no dynamic list, force all the symbols // to be dynamic to be sure we export sanitizer interface functions. if (AddExportDynamic) Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -623,6 +623,7 @@ Opts.SanitizeMemoryUseAfterDtor = Args.hasArg(OPT_fsanitize_memory_use_after_dtor); Opts.SanitizeCfiCrossDso = Args.hasArg(OPT_fsanitize_cfi_cross_dso); + Opts.SanitizeStats = Args.hasArg(OPT_fsanitize_stats); Opts.SSPBufferSize = getLastArgIntValue(Args, OPT_stack_protector_buffer_size, 8, Diags); Opts.StackRealignment = Args.hasArg(OPT_mstackrealign); @@ -697,6 +698,7 @@ } Opts.DependentLibraries = Args.getAllArgValues(OPT_dependent_lib); + Opts.LinkerOptions = Args.getAllArgValues(OPT_linker_option); bool NeedLocTracking = false; if (Arg *A = Args.getLastArg(OPT_Rpass_EQ)) { Index: lib/Frontend/MultiplexConsumer.cpp =================================================================== --- lib/Frontend/MultiplexConsumer.cpp +++ lib/Frontend/MultiplexConsumer.cpp @@ -317,9 +317,9 @@ Consumer->HandleImplicitImportDecl(D); } -void MultiplexConsumer::HandleLinkerOptionPragma(llvm::StringRef Opts) { +void MultiplexConsumer::HandleLinkerOption(llvm::StringRef Opts) { for (auto &Consumer : Consumers) - Consumer->HandleLinkerOptionPragma(Opts); + Consumer->HandleLinkerOption(Opts); } void MultiplexConsumer::HandleDetectMismatch(llvm::StringRef Name, llvm::StringRef Value) { Index: lib/Sema/SemaAttr.cpp =================================================================== --- lib/Sema/SemaAttr.cpp +++ lib/Sema/SemaAttr.cpp @@ -275,7 +275,7 @@ case PCK_Unknown: llvm_unreachable("unexpected pragma comment kind"); case PCK_Linker: - Consumer.HandleLinkerOptionPragma(Arg); + Consumer.HandleLinkerOption(Arg); return; case PCK_Lib: Consumer.HandleDependentLibrary(Arg); Index: test/CodeGen/linker-option.c =================================================================== --- /dev/null +++ test/CodeGen/linker-option.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s --linker-option=/include:foo -triple i686-pc-win32 -emit-llvm -o - | FileCheck %s + +// CHECK: !llvm.module.flags = !{{{.*}}} +// CHECK: !{{[0-9]+}} = !{i32 6, !"Linker Options", ![[link_opts:[0-9]+]]} +// CHECK: ![[link_opts]] = !{![[msvcrt:[0-9]+]]} +// CHECK: ![[msvcrt]] = !{!"/include:foo"} + +int f(); Index: test/CodeGenCXX/cfi-stats.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/cfi-stats.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall,cfi-nvcall,cfi-derived-cast,cfi-unrelated-cast,cfi-icall -fsanitize-stats -emit-llvm -o - %s | FileCheck --check-prefix=CHECK %s + +// CHECK: [[STATS:@[^ ]*]] = private global { i8*, i32, [5 x [2 x i8*]] } { i8* null, i32 5, [5 x [2 x i8*]] +// CHECK: {{\[\[}}2 x i8*] zeroinitializer, +// CHECK: [2 x i8*] [i8* null, i8* inttoptr (i64 2305843009213693952 to i8*)], +// CHECK: [2 x i8*] [i8* null, i8* inttoptr (i64 4611686018427387904 to i8*)], +// CHECK: [2 x i8*] [i8* null, i8* inttoptr (i64 6917529027641081856 to i8*)], +// CHECK: [2 x i8*] [i8* null, i8* inttoptr (i64 -9223372036854775808 to i8*)]] } + +// CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* [[CTOR:@[^ ]*]], i8* null }] + +struct A { + virtual void vf(); + void nvf(); +}; +struct B : A {}; + +// CHECK: @vcall +extern "C" void vcall(A *a) { + // CHECK: call void @__sanitizer_stat_report({{.*}}[[STATS]]{{.*}}i64 0, i32 2, i64 0 + a->vf(); +} + +// CHECK: @nvcall +extern "C" void nvcall(A *a) { + // CHECK: call void @__sanitizer_stat_report({{.*}}[[STATS]]{{.*}}i64 0, i32 2, i64 1 + a->nvf(); +} + +// CHECK: @dcast +extern "C" void dcast(A *a) { + // CHECK: call void @__sanitizer_stat_report({{.*}}[[STATS]]{{.*}}i64 0, i32 2, i64 2 + static_cast(a); +} + +// CHECK: @ucast +extern "C" void ucast(void *a) { + // CHECK: call void @__sanitizer_stat_report({{.*}}[[STATS]]{{.*}}i64 0, i32 2, i64 3 + reinterpret_cast(a); +} + +// CHECK: @icall +extern "C" void icall(void (*p)()) { + // CHECK: call void @__sanitizer_stat_report({{.*}}[[STATS]]{{.*}}i64 0, i32 2, i64 4 + p(); +} + +// CHECK: define private void [[CTOR]]() +// CHECK-NEXT: call void @__sanitizer_stat_init(i8* bitcast ({ i8*, i32, [5 x [2 x i8*]] }* [[STATS]] to i8*)) +// CHECK-NEXT: ret void Index: test/Driver/fsanitize.c =================================================================== --- test/Driver/fsanitize.c +++ test/Driver/fsanitize.c @@ -272,6 +272,9 @@ // CHECK-CFI-NO-CROSS-DSO: -emit-llvm-bc // CHECK-CFI-NO-CROSS-DSO-NOT: -fsanitize-cfi-cross-dso +// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -fsanitize-stats -flto -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-STATS +// CHECK-CFI-STATS: -fsanitize-stats + // RUN: %clang_cl -fsanitize=address -c -MDd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL // RUN: %clang_cl -fsanitize=address -c -MTd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL // RUN: %clang_cl -fsanitize=address -c -LDd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL Index: test/Driver/sanitizer-ld.c =================================================================== --- test/Driver/sanitizer-ld.c +++ test/Driver/sanitizer-ld.c @@ -345,6 +345,39 @@ // CHECK-SAFESTACK-LINUX: "-lpthread" // CHECK-SAFESTACK-LINUX: "-ldl" +// RUN: %clang -fsanitize=cfi -fsanitize-stats %s -### -o %t.o 2>&1 \ +// RUN: -target x86_64-unknown-linux \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-CFI-STATS-LINUX %s +// CHECK-CFI-STATS-LINUX: "{{.*}}ld{{(.exe)?}}" +// CHECK-CFI-STATS-LINUX: "-whole-archive" "{{[^"]*}}libclang_rt.stats_client-x86_64.a" "-no-whole-archive" +// CHECK-CFI-STATS-LINUX-NOT: "-whole-archive" +// CHECK-CFI-STATS-LINUX: "{{[^"]*}}libclang_rt.stats-x86_64.a" + +// RUN: %clang -fsanitize=cfi -fsanitize-stats %s -### -o %t.o 2>&1 \ +// RUN: -target x86_64-apple-darwin \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-CFI-STATS-DARWIN %s +// CHECK-CFI-STATS-DARWIN: "{{.*}}ld{{(.exe)?}}" +// CHECK-CFI-STATS-DARWIN: "{{[^"]*}}libclang_rt.stats_client_osx.a" +// CHECK-CFI-STATS-DARWIN: "{{[^"]*}}libclang_rt.stats_osx_dynamic.dylib" + +// RUN: %clang -fsanitize=cfi -fsanitize-stats %s -### -o %t.o 2>&1 \ +// RUN: -target x86_64-pc-windows \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-CFI-STATS-WIN64 %s +// CHECK-CFI-STATS-WIN64: "--dependent-lib={{[^"]*}}clang_rt.stats_client-x86_64.lib" +// CHECK-CFI-STATS-WIN64: "--dependent-lib={{[^"]*}}clang_rt.stats-x86_64.lib" +// CHECK-CFI-STATS-WIN64: "--linker-option=/include:__sanitizer_stats_register" + +// RUN: %clang -fsanitize=cfi -fsanitize-stats %s -### -o %t.o 2>&1 \ +// RUN: -target i686-pc-windows \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree \ +// RUN: | FileCheck --check-prefix=CHECK-CFI-STATS-WIN32 %s +// CHECK-CFI-STATS-WIN32: "--dependent-lib={{[^"]*}}clang_rt.stats_client-i386.lib" +// CHECK-CFI-STATS-WIN32: "--dependent-lib={{[^"]*}}clang_rt.stats-i386.lib" +// CHECK-CFI-STATS-WIN32: "--linker-option=/include:___sanitizer_stats_register" + // RUN: %clang -no-canonical-prefixes %s -### -o %t.o 2>&1 \ // RUN: -target arm-linux-androideabi -fsanitize=safe-stack \ // RUN: --sysroot=%S/Inputs/basic_android_tree \