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/FuzzerIOWindows.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp +++ compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp @@ -72,6 +72,19 @@ return IsFile(Path, Att); } +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 @@ -31,9 +31,13 @@ ATTRIBUTE_INTERFACE uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs]; +// Disable on Windows since it is not a main feature and causes a compilation +// error. +#if !LIBFUZZER_WINDOWS // Used by -fsanitize-coverage=stack-depth to track stack depth ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local uintptr_t __sancov_lowest_stack; +#endif namespace fuzzer { @@ -458,12 +462,18 @@ ATTRIBUTE_NO_SANITIZE_ALL void TracePC::RecordInitialStack() { +#if !LIBFUZZER_WINDOWS int stack; __sancov_lowest_stack = InitialStack = reinterpret_cast(&stack); +#endif } uintptr_t TracePC::GetMaxStackOffset() const { +#if !LIBFUZZER_WINDOWS return InitialStack - __sancov_lowest_stack; // Stack grows down +#else + return 0; +#endif } } // namespace fuzzer @@ -496,12 +506,26 @@ ATTRIBUTE_INTERFACE void __sanitizer_cov_8bit_counters_init(uint8_t *Start, uint8_t *Stop) { + // On Windows, Start actually points to a uint64_t located before the counter + // array (see sanitizer_coverage_win_sections.cc for why). So we need to + // to update Start to point to the counter array. +#if LIBFUZZER_WINDOWS + Start += sizeof(uint64_t); +#endif fuzzer::TPC.HandleInline8bitCountersInit(Start, Stop); } ATTRIBUTE_INTERFACE void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, const uintptr_t *pcs_end) { + // On Windows, pcs_beg actually points to a uint64_t located before the pc + // array (see sanitizer_coverage_win_sections.cc for why). So we need to + // to update pcs_beg to point to the pc array. +#if LIBFUZZER_WINDOWS + const uint8_t *pcs_beg_8bit = (const uint8_t *)pcs_beg; + pcs_beg_8bit += sizeof(uint64_t); + pcs_beg = (const uintptr_t *)pcs_beg_8bit; +#endif fuzzer::TPC.HandlePCsInit(pcs_beg, pcs_end); } 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 @@ -29,11 +29,18 @@ } TEST(Fuzzer, Basename) { - EXPECT_EQ(Basename("foo/bar"), "bar"); EXPECT_EQ(Basename("bar"), "bar"); +#if !LIBFUZZER_WINDOWS + EXPECT_EQ(Basename("foo/bar"), "bar"); EXPECT_EQ(Basename("/bar"), "bar"); EXPECT_EQ(Basename("foo/x"), "x"); EXPECT_EQ(Basename("foo/"), ""); +#else + EXPECT_EQ(Basename("foo\\bar"), "bar"); + 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 @@ -13,10 +13,28 @@ #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; +#pragma section(".SCOVG$A", read, write) // NOLINT +__declspec(allocate(".SCOVG$A")) uint32_t __start___sancov_guards = 0; +#pragma section(".SCOVG$Z", read, write) // NOLINT +__declspec(allocate(".SCOVG$Z")) uint32_t __stop___sancov_guards = 0; + +// Use align(1) to avoid adding any padding that will mess up clients trying to +// determine the start and end of the array. +#pragma section(".SCOVC$A", read, write) // NOLINT +__declspec(allocate(".SCOVC$A")) __declspec(align(1)) uint64_t + __start___sancov_cntrs = 0; +#pragma section(".SCOVC$Z", read, write) // NOLINT +__declspec(allocate(".SCOVC$Z")) __declspec(align(1)) uint64_t + __stop___sancov_cntrs = 0; + +// 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; } #endif // SANITIZER_WINDOWS Index: llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp +++ llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp @@ -809,8 +809,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 ".SCOVC$M"; + else if (Section == SanCovPCsSectionName) + return ".SCOVP$M"; + return ".SCOVG$M"; // 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,27 @@ +; 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 dso_local i32 @foo(i8*, i64) #0 { + %3 = alloca i32, align 4 + %4 = alloca i64, align 8 + %5 = alloca i8*, align 8 + store i64 %1, i64* %4, align 8 + store i8* %0, i8** %5, align 8 + %6 = load i64, i64* %4, align 8 + %7 = icmp eq i64 %6, 3 + br i1 %7, label %8, label %9 + + store i32 1, i32* %3, align 4 + br label %10 + + store i32 0, i32* %3, align 4 + br label %10 + + %11 = load i32, i32* %3, align 4 + ret i32 %11 +} + +; CHECK-DAG: section ".SCOVC{{\$M}}", +; CHECK-DAG: section ".SCOVP{{\$M}}",