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()); + CreateSanitizerStatReport(IRB, SSK); +} 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,18 @@ 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) + 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 +599,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 +637,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 @@ -2793,6 +2793,9 @@ 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 +2836,8 @@ StaticRuntimes.push_back("cfi"); if (SanArgs.needsCfiDiagRt()) StaticRuntimes.push_back("cfi_diag"); + if (SanArgs.needsStatsRt()) + StaticRuntimes.push_back("stats"); } // Should be called before we add system libraries (C++ ABI, libstdc++/libc++, 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,60 @@ +// 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 --check-prefix=CHECK-ELF %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fsanitize=cfi-vcall,cfi-nvcall,cfi-derived-cast,cfi-unrelated-cast,cfi-icall -fsanitize-stats -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MACHO %s +// RUN: %clang_cc1 -triple x86_64-pc-windows -fsanitize=cfi-vcall,cfi-nvcall,cfi-derived-cast,cfi-unrelated-cast,cfi-icall -fsanitize-stats -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-COFF %s + +// CHECK: [[VCALL:@[^ ]*]] = private global [2 x i8*] zeroinitializer, section +// CHECK-ELF: "san_stat" +// CHECK-MACHO: "__DATA,san_stat" +// CHECK-COFF: "san_stat$u" +// CHECK: [[NVCALL:@[^ ]*]] = private global [2 x i8*] [i8* null, i8* inttoptr (i64 2305843009213693952 to i8*)], section +// CHECK-ELF: "san_stat" +// CHECK-MACHO: "__DATA,san_stat" +// CHECK-COFF: "san_stat$u" +// CHECK: [[DCAST:@[^ ]*]] = private global [2 x i8*] [i8* null, i8* inttoptr (i64 4611686018427387904 to i8*)], section +// CHECK-ELF: "san_stat" +// CHECK-MACHO: "__DATA,san_stat" +// CHECK-COFF: "san_stat$u" +// CHECK: [[UCAST:@[^ ]*]] = private global [2 x i8*] [i8* null, i8* inttoptr (i64 6917529027641081856 to i8*)], section +// CHECK-ELF: "san_stat" +// CHECK-MACHO: "__DATA,san_stat" +// CHECK-COFF: "san_stat$u" +// CHECK: [[ICALL:@[^ ]*]] = private global [2 x i8*] [i8* null, i8* inttoptr (i64 -9223372036854775808 to i8*)], section +// CHECK-ELF: "san_stat" +// CHECK-MACHO: "__DATA,san_stat" +// CHECK-COFF: "san_stat$u" + +struct A { + virtual void vf(); + void nvf(); +}; +struct B : A {}; + +// CHECK: @vcall +extern "C" void vcall(A *a) { + // CHECK: call void @__sanitizer_stat_report(i8* bitcast ([2 x i8*]* [[VCALL]] to i8*)) + a->vf(); +} + +// CHECK: @nvcall +extern "C" void nvcall(A *a) { + // CHECK: call void @__sanitizer_stat_report(i8* bitcast ([2 x i8*]* [[NVCALL]] to i8*)) + a->nvf(); +} + +// CHECK: @dcast +extern "C" void dcast(A *a) { + // CHECK: call void @__sanitizer_stat_report(i8* bitcast ([2 x i8*]* [[DCAST]] to i8*)) + static_cast(a); +} + +// CHECK: @ucast +extern "C" void ucast(void *a) { + // CHECK: call void @__sanitizer_stat_report(i8* bitcast ([2 x i8*]* [[UCAST]] to i8*)) + reinterpret_cast(a); +} + +// CHECK: @icall +extern "C" void icall(void (*p)()) { + // CHECK: call void @__sanitizer_stat_report(i8* bitcast ([2 x i8*]* [[ICALL]] to i8*)) + p(); +} 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,38 @@ // 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: "-whole-archive" "{{[^"]*}}libclang_rt.stats-x86_64.a" "-no-whole-archive" + +// 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 \