Skip to content

Commit 7e042bb

Browse files
committedAug 30, 2018
[libFuzzer] Port to Windows
Summary: Port libFuzzer to windows-msvc. This patch allows libFuzzer targets to be built and run on Windows, using -fsanitize=fuzzer and/or fsanitize=fuzzer-no-link. It allows these forms of coverage instrumentation to work on Windows as well. It does not fix all issues, such as those with -fsanitize-coverage=stack-depth, which is not usable on Windows as of this patch. It also does not fix any libFuzzer integration tests. Nearly all of them fail to compile, fixing them will come in a later patch, so libFuzzer tests are disabled on Windows until them. Patch By: metzman Reviewers: morehouse, rnk Reviewed By: morehouse, rnk Subscribers: #sanitizers, delcypher, morehouse, kcc, eraman Differential Revision: https://reviews.llvm.org/D51022 llvm-svn: 341082
1 parent a733d08 commit 7e042bb

File tree

15 files changed

+161
-26
lines changed

15 files changed

+161
-26
lines changed
 

Diff for: ‎clang/lib/Driver/ToolChains/MSVC.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,17 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
365365
CmdArgs.push_back(Args.MakeArgString(std::string("-implib:") + ImplibName));
366366
}
367367

368+
if (TC.getSanitizerArgs().needsFuzzer()) {
369+
if (!Args.hasArg(options::OPT_shared))
370+
CmdArgs.push_back(
371+
Args.MakeArgString(std::string("-wholearchive:") +
372+
TC.getCompilerRTArgString(Args, "fuzzer", false)));
373+
CmdArgs.push_back(Args.MakeArgString("-debug"));
374+
// Prevent the linker from padding sections we use for instrumentation
375+
// arrays.
376+
CmdArgs.push_back(Args.MakeArgString("-incremental:no"));
377+
}
378+
368379
if (TC.getSanitizerArgs().needsAsanRt()) {
369380
CmdArgs.push_back(Args.MakeArgString("-debug"));
370381
CmdArgs.push_back(Args.MakeArgString("-incremental:no"));
@@ -1298,6 +1309,8 @@ MSVCToolChain::ComputeEffectiveClangTriple(const ArgList &Args,
12981309
SanitizerMask MSVCToolChain::getSupportedSanitizers() const {
12991310
SanitizerMask Res = ToolChain::getSupportedSanitizers();
13001311
Res |= SanitizerKind::Address;
1312+
Res |= SanitizerKind::Fuzzer;
1313+
Res |= SanitizerKind::FuzzerNoLink;
13011314
Res &= ~SanitizerKind::CFIMFCall;
13021315
return Res;
13031316
}

Diff for: ‎compiler-rt/cmake/config-ix.cmake

+4-1
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,10 @@ else()
619619
endif()
620620

621621
if (COMPILER_RT_HAS_SANITIZER_COMMON AND FUZZER_SUPPORTED_ARCH AND
622-
OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|FreeBSD|OpenBSD|Fuchsia")
622+
OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|FreeBSD|OpenBSD|Fuchsia|Windows" AND
623+
# TODO: Support builds with MSVC.
624+
NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" AND
625+
NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
623626
set(COMPILER_RT_HAS_FUZZER TRUE)
624627
else()
625628
set(COMPILER_RT_HAS_FUZZER FALSE)

Diff for: ‎compiler-rt/lib/fuzzer/FuzzerDefs.h

+7
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,15 @@
129129

130130
#if LIBFUZZER_WINDOWS
131131
#define ATTRIBUTE_INTERFACE __declspec(dllexport)
132+
// This is used for __sancov_lowest_stack which is needed for
133+
// -fsanitize-coverage=stack-depth. That feature is not yet available on
134+
// Windows, so make the symbol static to avoid linking errors.
135+
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
136+
__attribute__((tls_model("initial-exec"))) thread_local static
132137
#else
133138
#define ATTRIBUTE_INTERFACE __attribute__((visibility("default")))
139+
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
140+
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local
134141
#endif
135142

136143
namespace fuzzer {

Diff for: ‎compiler-rt/lib/fuzzer/FuzzerIO.cpp

-8
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,6 @@ std::string DirPlusFile(const std::string &DirPath,
100100
return DirPath + GetSeparator() + FileName;
101101
}
102102

103-
std::string Basename(const std::string &Path, char Separator) {
104-
size_t Pos = Path.rfind(Separator);
105-
if (Pos == std::string::npos)
106-
return Path;
107-
assert(Pos < Path.size());
108-
return Path.substr(Pos + 1);
109-
}
110-
111103
void DupAndCloseStderr() {
112104
int OutputFd = DuplicateFile(2);
113105
if (OutputFd > 0) {

Diff for: ‎compiler-rt/lib/fuzzer/FuzzerIO.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
6868

6969
char GetSeparator();
7070
// Similar to the basename utility: returns the file name w/o the dir prefix.
71-
std::string Basename(const std::string &Path, char Separator = GetSeparator());
71+
std::string Basename(const std::string &Path);
7272

7373
FILE* OpenFile(int Fd, const char *Mode);
7474

Diff for: ‎compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ size_t FileSize(const std::string &Path) {
4646
return St.st_size;
4747
}
4848

49+
std::string Basename(const std::string &Path) {
50+
size_t Pos = Path.rfind(GetSeparator());
51+
if (Pos == std::string::npos) return Path;
52+
assert(Pos < Path.size());
53+
return Path.substr(Pos + 1);
54+
}
55+
4956
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
5057
Vector<std::string> *V, bool TopDir) {
5158
auto E = GetEpoch(Dir);

Diff for: ‎compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,26 @@ bool IsFile(const std::string &Path) {
7272
return IsFile(Path, Att);
7373
}
7474

75+
std::string Basename(const std::string &Path) {
76+
size_t Pos = Path.find_last_of("/\\");
77+
if (Pos == std::string::npos) return Path;
78+
assert(Pos < Path.size());
79+
return Path.substr(Pos + 1);
80+
}
81+
82+
size_t FileSize(const std::string &Path) {
83+
WIN32_FILE_ATTRIBUTE_DATA attr;
84+
if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
85+
Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
86+
Path.c_str(), GetLastError());
87+
return 0;
88+
}
89+
ULARGE_INTEGER size;
90+
size.HighPart = attr.nFileSizeHigh;
91+
size.LowPart = attr.nFileSizeLow;
92+
return size.QuadPart;
93+
}
94+
7595
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
7696
Vector<std::string> *V, bool TopDir) {
7797
auto E = GetEpoch(Dir);

Diff for: ‎compiler-rt/lib/fuzzer/FuzzerTracePC.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ ATTRIBUTE_INTERFACE
3232
uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
3333

3434
// Used by -fsanitize-coverage=stack-depth to track stack depth
35-
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec")))
36-
thread_local uintptr_t __sancov_lowest_stack;
35+
ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack;
3736

3837
namespace fuzzer {
3938

Diff for: ‎compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,9 @@ const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
179179
}
180180

181181
std::string DisassembleCmd(const std::string &FileName) {
182-
if (ExecuteCommand("dumpbin /summary > nul") == 0)
182+
Vector<std::string> command_vector;
183+
command_vector.push_back("dumpbin /summary > nul");
184+
if (ExecuteCommand(Command(command_vector)) == 0)
183185
return "dumpbin /disasm " + FileName;
184186
Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n");
185187
exit(1);

Diff for: ‎compiler-rt/lib/fuzzer/tests/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS --driver-mode=g++)
1717

1818
if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
1919
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lc++ -lpthread)
20+
elseif(WIN32)
21+
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++)
2022
else()
2123
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++ -lpthread)
2224
endif()

Diff for: ‎compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ TEST(Fuzzer, Basename) {
3434
EXPECT_EQ(Basename("/bar"), "bar");
3535
EXPECT_EQ(Basename("foo/x"), "x");
3636
EXPECT_EQ(Basename("foo/"), "");
37+
#if LIBFUZZER_WINDOWS
38+
EXPECT_EQ(Basename("foo\\bar"), "bar");
39+
EXPECT_EQ(Basename("foo\\bar/baz"), "baz");
40+
EXPECT_EQ(Basename("\\bar"), "bar");
41+
EXPECT_EQ(Basename("foo\\x"), "x");
42+
EXPECT_EQ(Basename("foo\\"), "");
43+
#endif
3744
}
3845

3946
TEST(Fuzzer, CrossOver) {

Diff for: ‎compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc

+47-6
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,57 @@
77
//
88
//===----------------------------------------------------------------------===//
99
//
10-
// This file defines delimiters for Sanitizer Coverage's section.
10+
// This file defines delimiters for Sanitizer Coverage's section. It contains
11+
// Windows specific tricks to coax the linker into giving us the start and stop
12+
// addresses of a section, as ELF linkers can do, to get the size of certain
13+
// arrays. According to https://msdn.microsoft.com/en-us/library/7977wcck.aspx
14+
// sections with the same name before "$" are sorted alphabetically by the
15+
// string that comes after "$" and merged into one section. We take advantage
16+
// of this by putting data we want the size of into the middle (M) of a section,
17+
// by using the letter "M" after "$". We get the start of this data (ie:
18+
// __start_section_name) by making the start variable come at the start of the
19+
// section (using the letter A after "$"). We do the same to get the end of the
20+
// data by using the letter "Z" after "$" to make the end variable come after
21+
// the data. Note that because of our technique the address of the start
22+
// variable is actually the address of data that comes before our middle
23+
// section. We also need to prevent the linker from adding any padding. Each
24+
// technique we use for this is explained in the comments below.
1125
//===----------------------------------------------------------------------===//
1226

1327
#include "sanitizer_platform.h"
1428
#if SANITIZER_WINDOWS
1529
#include <stdint.h>
16-
#pragma section(".SCOV$A", read, write) // NOLINT
17-
#pragma section(".SCOV$Z", read, write) // NOLINT
1830
extern "C" {
19-
__declspec(allocate(".SCOV$A")) uint32_t __start___sancov_guards = 0;
20-
__declspec(allocate(".SCOV$Z")) uint32_t __stop___sancov_guards = 0;
31+
// The Guard array and counter array should both be merged into the .data
32+
// section to reduce the number of PE sections However, because PCTable is
33+
// constant it should be merged with the .rdata section.
34+
#pragma section(".SCOV$GA", read, write) // NOLINT
35+
// Use align(1) to avoid adding any padding that will mess up clients trying to
36+
// determine the start and end of the array.
37+
__declspec(allocate(".SCOV$GA")) __declspec(align(1)) uint64_t
38+
__start___sancov_guards = 0;
39+
#pragma section(".SCOV$GZ", read, write) // NOLINT
40+
__declspec(allocate(".SCOV$GZ")) __declspec(align(1)) uint64_t
41+
__stop___sancov_guards = 0;
42+
43+
#pragma section(".SCOV$CA", read, write) // NOLINT
44+
__declspec(allocate(".SCOV$CA")) __declspec(align(1)) uint64_t
45+
__start___sancov_cntrs = 0;
46+
#pragma section(".SCOV$CZ", read, write) // NOLINT
47+
__declspec(allocate(".SCOV$CZ")) __declspec(align(1)) uint64_t
48+
__stop___sancov_cntrs = 0;
49+
50+
#pragma comment(linker, "/MERGE:.SCOV=.data")
51+
52+
// Use uint64_t so there won't be any issues if the linker tries to word align
53+
// the pc array.
54+
#pragma section(".SCOVP$A", read) // NOLINT
55+
__declspec(allocate(".SCOVP$A")) __declspec(align(1)) uint64_t
56+
__start___sancov_pcs = 0;
57+
#pragma section(".SCOVP$Z", read) // NOLINT
58+
__declspec(allocate(".SCOVP$Z")) __declspec(align(1)) uint64_t
59+
__stop___sancov_pcs = 0;
60+
61+
#pragma comment(linker, "/MERGE:.SCOVP=.rdata")
2162
}
22-
#endif // SANITIZER_WINDOWS
63+
#endif // SANITIZER_WINDOWS

Diff for: ‎compiler-rt/test/CMakeLists.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)
6262
compiler_rt_test_runtime(sanitizer_common)
6363

6464
# OpenBSD not supporting asan, cannot run the tests
65-
if(COMPILER_RT_BUILD_LIBFUZZER AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD" AND NOT ANDROID)
65+
# Tests are broken for now on Windows
66+
if(COMPILER_RT_BUILD_LIBFUZZER
67+
AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD"
68+
AND NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows" AND NOT ANDROID)
6669
compiler_rt_test_runtime(fuzzer)
6770
endif()
6871

Diff for: ‎llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp

+33-6
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,20 @@ Function *SanitizerCoverageModule::CreateInitCallsForSections(
273273
auto SecStart = SecStartEnd.first;
274274
auto SecEnd = SecStartEnd.second;
275275
Function *CtorFunc;
276+
Value *SecStartPtr = nullptr;
277+
// Account for the fact that on windows-msvc __start_* symbols actually
278+
// point to a uint64_t before the start of the array.
279+
if (TargetTriple.getObjectFormat() == Triple::COFF) {
280+
auto SecStartI8Ptr = IRB.CreatePointerCast(SecStart, Int8PtrTy);
281+
auto GEP = IRB.CreateGEP(SecStartI8Ptr,
282+
ConstantInt::get(IntptrTy, sizeof(uint64_t)));
283+
SecStartPtr = IRB.CreatePointerCast(GEP, Ty);
284+
} else {
285+
SecStartPtr = IRB.CreatePointerCast(SecStart, Ty);
286+
}
276287
std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions(
277288
M, SanCovModuleCtorName, InitFunctionName, {Ty, Ty},
278-
{IRB.CreatePointerCast(SecStart, Ty), IRB.CreatePointerCast(SecEnd, Ty)});
289+
{SecStartPtr, IRB.CreatePointerCast(SecEnd, Ty)});
279290

280291
if (TargetTriple.supportsCOMDAT()) {
281292
// Use comdat to dedup CtorFunc.
@@ -397,9 +408,20 @@ bool SanitizerCoverageModule::runOnModule(Module &M) {
397408
Function *InitFunction = declareSanitizerInitFunction(
398409
M, SanCovPCsInitName, {IntptrPtrTy, IntptrPtrTy});
399410
IRBuilder<> IRBCtor(Ctor->getEntryBlock().getTerminator());
400-
IRBCtor.CreateCall(InitFunction,
401-
{IRB.CreatePointerCast(SecStartEnd.first, IntptrPtrTy),
402-
IRB.CreatePointerCast(SecStartEnd.second, IntptrPtrTy)});
411+
Value *SecStartPtr = nullptr;
412+
// Account for the fact that on windows-msvc __start_pc_table actually
413+
// points to a uint64_t before the start of the PC table.
414+
if (TargetTriple.getObjectFormat() == Triple::COFF) {
415+
auto SecStartI8Ptr = IRB.CreatePointerCast(SecStartEnd.first, Int8PtrTy);
416+
auto GEP = IRB.CreateGEP(SecStartI8Ptr,
417+
ConstantInt::get(IntptrTy, sizeof(uint64_t)));
418+
SecStartPtr = IRB.CreatePointerCast(GEP, IntptrPtrTy);
419+
} else {
420+
SecStartPtr = IRB.CreatePointerCast(SecStartEnd.first, IntptrPtrTy);
421+
}
422+
IRBCtor.CreateCall(
423+
InitFunction,
424+
{SecStartPtr, IRB.CreatePointerCast(SecStartEnd.second, IntptrPtrTy)});
403425
}
404426
// We don't reference these arrays directly in any of our runtime functions,
405427
// so we need to prevent them from being dead stripped.
@@ -809,8 +831,13 @@ void SanitizerCoverageModule::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
809831

810832
std::string
811833
SanitizerCoverageModule::getSectionName(const std::string &Section) const {
812-
if (TargetTriple.getObjectFormat() == Triple::COFF)
813-
return ".SCOV$M";
834+
if (TargetTriple.getObjectFormat() == Triple::COFF) {
835+
if (Section == SanCovCountersSectionName)
836+
return ".SCOV$CM";
837+
if (Section == SanCovPCsSectionName)
838+
return ".SCOVP$M";
839+
return ".SCOV$GM"; // For SanCovGuardsSectionName.
840+
}
814841
if (TargetTriple.isOSBinFormatMachO())
815842
return "__DATA,__" + Section;
816843
return "__" + Section;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
; Checks that the PC and 8-bit Counter Arrays are placed in their own sections in COFF binaries.
2+
; RUN: opt < %s -sancov -sanitizer-coverage-level=1 -sanitizer-coverage-inline-8bit-counters=1 -sanitizer-coverage-pc-table=1 -S | FileCheck %s
3+
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
4+
target triple = "x86_64-pc-windows-msvc19.14.26433"
5+
6+
define void @foo() {
7+
entry:
8+
ret void
9+
}
10+
11+
; CHECK-DAG: section ".SCOV{{\$}}CM",
12+
; CHECK-DAG: section ".SCOVP{{\$}}M",

0 commit comments

Comments
 (0)
Please sign in to comment.