Index: clang/lib/Driver/ToolChains/MSVC.cpp =================================================================== --- clang/lib/Driver/ToolChains/MSVC.cpp +++ clang/lib/Driver/ToolChains/MSVC.cpp @@ -365,6 +365,17 @@ CmdArgs.push_back(Args.MakeArgString(std::string("-implib:") + ImplibName)); } + if (TC.getSanitizerArgs().needsFuzzer()) { + if (!Args.hasArg(options::OPT_shared)) + CmdArgs.push_back( + Args.MakeArgString(std::string("-wholearchive:") + + TC.getCompilerRTArgString(Args, "fuzzer", false))); + CmdArgs.push_back(Args.MakeArgString("-debug")); + // Prevent the linker from padding sections we use for instrumentation + // arrays. + CmdArgs.push_back(Args.MakeArgString("-incremental:no")); + } + if (TC.getSanitizerArgs().needsAsanRt()) { CmdArgs.push_back(Args.MakeArgString("-debug")); CmdArgs.push_back(Args.MakeArgString("-incremental:no")); @@ -1298,6 +1309,8 @@ SanitizerMask MSVCToolChain::getSupportedSanitizers() const { SanitizerMask Res = ToolChain::getSupportedSanitizers(); Res |= SanitizerKind::Address; + Res |= SanitizerKind::Fuzzer; + Res |= SanitizerKind::FuzzerNoLink; Res &= ~SanitizerKind::CFIMFCall; return Res; } Index: compiler-rt/cmake/config-ix.cmake =================================================================== --- compiler-rt/cmake/config-ix.cmake +++ compiler-rt/cmake/config-ix.cmake @@ -619,7 +619,7 @@ endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND FUZZER_SUPPORTED_ARCH AND - OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|FreeBSD|OpenBSD|Fuchsia") + OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|FreeBSD|OpenBSD|Fuchsia|Windows") set(COMPILER_RT_HAS_FUZZER TRUE) else() set(COMPILER_RT_HAS_FUZZER FALSE) Index: compiler-rt/lib/fuzzer/FuzzerDefs.h =================================================================== --- compiler-rt/lib/fuzzer/FuzzerDefs.h +++ compiler-rt/lib/fuzzer/FuzzerDefs.h @@ -129,8 +129,15 @@ #if LIBFUZZER_WINDOWS #define ATTRIBUTE_INTERFACE __declspec(dllexport) +// This is used for __sancov_lowest_stack which is needed for +// -fsanitize-coverage=stack-depth. That feature is not yet available on +// Windows, so make the symbol static to avoid linking errors. +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \ + __attribute__((tls_model("initial-exec"))) thread_local static #else #define ATTRIBUTE_INTERFACE __attribute__((visibility("default"))) +#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \ + ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local #endif namespace fuzzer { Index: compiler-rt/lib/fuzzer/FuzzerIO.h =================================================================== --- compiler-rt/lib/fuzzer/FuzzerIO.h +++ compiler-rt/lib/fuzzer/FuzzerIO.h @@ -68,7 +68,7 @@ char GetSeparator(); // Similar to the basename utility: returns the file name w/o the dir prefix. -std::string Basename(const std::string &Path, char Separator = GetSeparator()); +std::string Basename(const std::string &Path); FILE* OpenFile(int Fd, const char *Mode); Index: compiler-rt/lib/fuzzer/FuzzerIO.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerIO.cpp +++ compiler-rt/lib/fuzzer/FuzzerIO.cpp @@ -100,14 +100,6 @@ return DirPath + GetSeparator() + FileName; } -std::string Basename(const std::string &Path, char Separator) { - size_t Pos = Path.rfind(Separator); - if (Pos == std::string::npos) - return Path; - assert(Pos < Path.size()); - return Path.substr(Pos + 1); -} - void DupAndCloseStderr() { int OutputFd = DuplicateFile(2); if (OutputFd > 0) { Index: compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp +++ compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp @@ -46,6 +46,13 @@ return St.st_size; } +std::string Basename(const std::string &Path) { + size_t Pos = Path.rfind(GetSeparator()); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); +} + void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, Vector *V, bool TopDir) { auto E = GetEpoch(Dir); Index: compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp +++ compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp @@ -72,6 +72,26 @@ return IsFile(Path, Att); } +std::string Basename(const std::string &Path) { + size_t Pos = Path.find_last_of("/\\"); + if (Pos == std::string::npos) return Path; + assert(Pos < Path.size()); + return Path.substr(Pos + 1); +} + +size_t FileSize(const std::string &Path) { + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) { + Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return 0; + } + ULARGE_INTEGER size; + size.HighPart = attr.nFileSizeHigh; + size.LowPart = attr.nFileSizeLow; + return size.QuadPart; +} + void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, Vector *V, bool TopDir) { auto E = GetEpoch(Dir); Index: compiler-rt/lib/fuzzer/FuzzerTracePC.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerTracePC.cpp +++ compiler-rt/lib/fuzzer/FuzzerTracePC.cpp @@ -32,8 +32,7 @@ uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs]; // Used by -fsanitize-coverage=stack-depth to track stack depth -ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) -thread_local uintptr_t __sancov_lowest_stack; +ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack; namespace fuzzer { Index: compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp +++ compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp @@ -179,7 +179,9 @@ } std::string DisassembleCmd(const std::string &FileName) { - if (ExecuteCommand("dumpbin /summary > nul") == 0) + Vector command_vector; + command_vector.push_back("dumpbin /summary > nul"); + if (ExecuteCommand(Command(command_vector)) == 0) return "dumpbin /disasm " + FileName; Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n"); exit(1); Index: compiler-rt/lib/fuzzer/tests/CMakeLists.txt =================================================================== --- compiler-rt/lib/fuzzer/tests/CMakeLists.txt +++ compiler-rt/lib/fuzzer/tests/CMakeLists.txt @@ -17,6 +17,8 @@ if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lc++ -lpthread) +elseif(WIN32) + list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++) else() list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++ -lpthread) endif() Index: compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp =================================================================== --- compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp +++ compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp @@ -34,6 +34,13 @@ EXPECT_EQ(Basename("/bar"), "bar"); EXPECT_EQ(Basename("foo/x"), "x"); EXPECT_EQ(Basename("foo/"), ""); +#if LIBFUZZER_WINDOWS + EXPECT_EQ(Basename("foo\\bar"), "bar"); + EXPECT_EQ(Basename("foo\\bar/baz"), "baz"); + EXPECT_EQ(Basename("\\bar"), "bar"); + EXPECT_EQ(Basename("foo\\x"), "x"); + EXPECT_EQ(Basename("foo\\"), ""); +#endif } TEST(Fuzzer, CrossOver) { Index: compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc =================================================================== --- compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc +++ compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc @@ -7,16 +7,57 @@ // //===----------------------------------------------------------------------===// // -// This file defines delimiters for Sanitizer Coverage's section. +// This file defines delimiters for Sanitizer Coverage's section. It contains +// Windows specific tricks to coax the linker into giving us the start and stop +// addresses of a section, as ELF linkers can do, to get the size of certain +// arrays. According to https://msdn.microsoft.com/en-us/library/7977wcck.aspx +// sections with the same name before "$" are sorted alphabetically by the +// string that comes after "$" and merged into one section. We take advantage +// of this by putting data we want the size of into the middle (M) of a section, +// by using the letter "M" after "$". We get the start of this data (ie: +// __start_section_name) by making the start variable come at the start of the +// section (using the letter A after "$"). We do the same to get the end of the +// data by using the letter "Z" after "$" to make the end variable come after +// the data. Note that because of our technique the address of the start +// variable is actually the address of data that comes before our middle +// section. We also need to prevent the linker from adding any padding. Each +// technique we use for this is explained in the comments below. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_WINDOWS #include -#pragma section(".SCOV$A", read, write) // NOLINT -#pragma section(".SCOV$Z", read, write) // NOLINT extern "C" { -__declspec(allocate(".SCOV$A")) uint32_t __start___sancov_guards = 0; -__declspec(allocate(".SCOV$Z")) uint32_t __stop___sancov_guards = 0; +// The Guard array and counter array should both be merged into the .data +// section to reduce the number of PE sections However, because PCTable is +// constant it should be merged with the .rdata section. +#pragma section(".SCOV$GA", read, write) // NOLINT +// Use align(1) to avoid adding any padding that will mess up clients trying to +// determine the start and end of the array. +__declspec(allocate(".SCOV$GA")) __declspec(align(1)) uint64_t + __start___sancov_guards = 0; +#pragma section(".SCOV$GZ", read, write) // NOLINT +__declspec(allocate(".SCOV$GZ")) __declspec(align(1)) uint64_t + __stop___sancov_guards = 0; + +#pragma section(".SCOV$CA", read, write) // NOLINT +__declspec(allocate(".SCOV$CA")) __declspec(align(1)) uint64_t + __start___sancov_cntrs = 0; +#pragma section(".SCOV$CZ", read, write) // NOLINT +__declspec(allocate(".SCOV$CZ")) __declspec(align(1)) uint64_t + __stop___sancov_cntrs = 0; + +#pragma comment(linker, "/MERGE:.SCOV=.data") + +// Use uint64_t so there won't be any issues if the linker tries to word align +// the pc array. +#pragma section(".SCOVP$A", read) // NOLINT +__declspec(allocate(".SCOVP$A")) __declspec(align(1)) uint64_t + __start___sancov_pcs = 0; +#pragma section(".SCOVP$Z", read) // NOLINT +__declspec(allocate(".SCOVP$Z")) __declspec(align(1)) uint64_t + __stop___sancov_pcs = 0; + +#pragma comment(linker, "/MERGE:.SCOVP=.rdata") } -#endif // SANITIZER_WINDOWS +#endif // SANITIZER_WINDOWS Index: compiler-rt/test/CMakeLists.txt =================================================================== --- compiler-rt/test/CMakeLists.txt +++ compiler-rt/test/CMakeLists.txt @@ -62,7 +62,10 @@ compiler_rt_test_runtime(sanitizer_common) # OpenBSD not supporting asan, cannot run the tests - if(COMPILER_RT_BUILD_LIBFUZZER AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD" AND NOT ANDROID) + # Tests are broken for now on Windows + if(COMPILER_RT_BUILD_LIBFUZZER + AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD" + AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" AND NOT ANDROID) compiler_rt_test_runtime(fuzzer) endif() Index: llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp +++ llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp @@ -273,9 +273,20 @@ auto SecStart = SecStartEnd.first; auto SecEnd = SecStartEnd.second; Function *CtorFunc; + Value *SecStartPtr = nullptr; + // Account for the fact that on windows-msvc __start_* symbols actually + // point to a uint64_t before the start of the array. + if (TargetTriple.getObjectFormat() == Triple::COFF) { + auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy); + auto GEP = IRB.CreateGEP(SecStartI8Ptr, + ConstantInt::get(IntptrTy, sizeof(uint64_t))); + SecStartPtr = IRB.CreatePointerCast(GEP, Ty); + } else { + SecStartPtr = IRB.CreatePointerCast(SecStart, Ty); + } std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions( M, SanCovModuleCtorName, InitFunctionName, {Ty, Ty}, - {IRB.CreatePointerCast(SecStart, Ty), IRB.CreatePointerCast(SecEnd, Ty)}); + {SecStartPtr, IRB.CreatePointerCast(SecEnd, Ty)}); if (TargetTriple.supportsCOMDAT()) { // Use comdat to dedup CtorFunc. @@ -397,9 +408,20 @@ Function *InitFunction = declareSanitizerInitFunction( M, SanCovPCsInitName, {IntptrPtrTy, IntptrPtrTy}); IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator()); - IRBCtor.CreateCall(InitFunction, - {IRB.CreatePointerCast(SecStartEnd.first, IntptrPtrTy), - IRB.CreatePointerCast(SecStartEnd.second, IntptrPtrTy)}); + Value *SecStartPtr = nullptr; + // Account for the fact that on windows-msvc __start_pc_table actually + // points to a uint64_t before the start of the PC table. + if (TargetTriple.getObjectFormat() == Triple::COFF) { + auto SecStartI8Ptr = IRB.CreatePointerCast(SecStartEnd.first, Int8PtrTy); + auto GEP = IRB.CreateGEP(SecStartI8Ptr, + ConstantInt::get(IntptrTy, sizeof(uint64_t))); + SecStartPtr = IRB.CreatePointerCast(GEP, IntptrPtrTy); + } else { + SecStartPtr = IRB.CreatePointerCast(SecStartEnd.first, IntptrPtrTy); + } + IRBCtor.CreateCall( + InitFunction, + {SecStartPtr, IRB.CreatePointerCast(SecStartEnd.second, IntptrPtrTy)}); } // We don't reference these arrays directly in any of our runtime functions, // so we need to prevent them from being dead stripped. @@ -809,8 +831,13 @@ std::string SanitizerCoverageModule::getSectionName(const std::string &Section) const { - if (TargetTriple.getObjectFormat() == Triple::COFF) - return ".SCOV$M"; + if (TargetTriple.getObjectFormat() == Triple::COFF) { + if (Section == SanCovCountersSectionName) + return ".SCOV$CM"; + if (Section == SanCovPCsSectionName) + return ".SCOVP$M"; + return ".SCOV$GM"; // For SanCovGuardsSectionName. + } if (TargetTriple.isOSBinFormatMachO()) return "__DATA,__" + Section; return "__" + Section; Index: llvm/test/Instrumentation/SanitizerCoverage/coff-pc-table-inline-8bit-counters.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/SanitizerCoverage/coff-pc-table-inline-8bit-counters.ll @@ -0,0 +1,12 @@ +; Checks that the PC and 8-bit Counter Arrays are placed in their own sections in COFF binaries. +; RUN: opt < %s -sancov -sanitizer-coverage-level=1 -sanitizer-coverage-inline-8bit-counters=1 -sanitizer-coverage-pc-table=1 -S | FileCheck %s +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.14.26433" + +define void @foo() { +entry: + ret void +} + +; CHECK-DAG: section ".SCOV{{\$}}CM", +; CHECK-DAG: section ".SCOVP{{\$}}M",