Skip to content

Commit

Permalink
libFuzzer: support LLVM 6.0 compiler-rt fuzzer (ldc-developers#2285)
Browse files Browse the repository at this point in the history
libFuzzer was moved to compiler-rt, and therefore its location and filename has changed.
Tested locally on OSX only.
  • Loading branch information
JohanEngelen authored Aug 24, 2017
1 parent f981955 commit 4a820f1
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 74 deletions.
28 changes: 19 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -766,21 +766,31 @@ function(copy_compilerrt_lib llvm_lib_name ldc_lib_name fixup_dylib)
endif()
endfunction()
if (LDC_INSTALL_LLVM_RUNTIME_LIBS)
# Locate LLVM sanitizer runtime libraries, and copy them to our lib folder
# Note: libFuzzer is part of compiler-rt version >= 6.0, but was part of LLVM =< 5.0

if(APPLE)
copy_compilerrt_lib("darwin/libclang_rt.asan_osx_dynamic.dylib" "libldc_rt.asan_osx_dynamic.dylib" TRUE)
if(NOT (LDC_LLVM_VER LESS 600))
copy_compilerrt_lib("darwin/libclang_rt.fuzzer_osx.a" "libldc_rt.fuzzer_osx.a" FALSE)
endif()
elseif(UNIX)
copy_compilerrt_lib("linux/libclang_rt.asan-x86_64.a" "libldc_rt.asan-x86_64.a" FALSE)
if(NOT (LDC_LLVM_VER LESS 600))
copy_compilerrt_lib("linux/libclang_rt.fuzzer-x86_64.a" "libldc_rt.fuzzer-x86_64.a" FALSE)
endif()
endif()

# Locate libFuzzer runtime library, and copy it to our lib folder
set(LLVM_LIBFUZZER_PATH ${LLVM_LIBRARY_DIRS}/libFuzzer.a)
if(EXISTS ${LLVM_LIBFUZZER_PATH})
message(STATUS "Copying libFuzzer library: ${LLVM_LIBFUZZER_PATH} --> libFuzzer.a")
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/lib${LIB_SUFFIX})
configure_file(${LLVM_LIBFUZZER_PATH} ${PROJECT_BINARY_DIR}/lib${LIB_SUFFIX}/libFuzzer.a COPYONLY)
install(FILES ${PROJECT_BINARY_DIR}/lib${LIB_SUFFIX}/libFuzzer.a DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX})
else()
message(STATUS "Not found: ${LLVM_LIBFUZZER_PATH}")
if(LDC_LLVM_VER LESS 600)
set(LLVM_LIBFUZZER_PATH ${LLVM_LIBRARY_DIRS}/libFuzzer.a)
if(EXISTS ${LLVM_LIBFUZZER_PATH})
message(STATUS "Copying libFuzzer library: ${LLVM_LIBFUZZER_PATH} --> libFuzzer.a")
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/lib${LIB_SUFFIX})
configure_file(${LLVM_LIBFUZZER_PATH} ${PROJECT_BINARY_DIR}/lib${LIB_SUFFIX}/libFuzzer.a COPYONLY)
install(FILES ${PROJECT_BINARY_DIR}/lib${LIB_SUFFIX}/libFuzzer.a DESTINATION ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX})
else()
message(STATUS "Not found: ${LLVM_LIBFUZZER_PATH}")
endif()
endif()
endif()

Expand Down
104 changes: 42 additions & 62 deletions driver/linker-gcc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,98 +180,78 @@ void ArgsBuilder::addLTOLinkFlags() {

//////////////////////////////////////////////////////////////////////////////

// Returns true on success.
bool addDarwinASanLinkFlags(std::vector<std::string> &args) {
std::string searchPaths[] = {
exe_path::prependLibDir("libldc_rt.asan_osx_dynamic.dylib"),
exe_path::prependLibDir("libclang_rt.asan_osx_dynamic.dylib"),
};

for (const auto &filepath : searchPaths) {
if (llvm::sys::fs::exists(filepath)) {
args.push_back(filepath);

// Add @executable_path to rpath to support having the dylib copied with
// the executable.
args.push_back("-rpath");
args.push_back("@executable_path");

// Add the path to the resource dir to rpath to support using the dylib
// from the default location without copying.
args.push_back("-rpath");
args.push_back(llvm::sys::path::parent_path(filepath));

return true;
}
}

// We did not find the library.
return false;
}

// Returns the arch name as used in the compiler_rt libs.
// FIXME: implement correctly for non-x86 platforms (e.g. ARM)
llvm::StringRef getCompilerRTArchName() {
return global.params.targetTriple->getArchName();
}

bool addUnixlikeASanLinkFlags(std::vector<std::string> &args) {
// Returns the libname as full path and with arch suffix and extension.
// For example, with name="libldc_rt.fuzzer", the returned string is
// "libldc_rt.fuzzer_osx.a" on Darwin.
std::string getFullCompilerRTLibPath(llvm::StringRef name,
bool sharedLibrary = false) {
if (global.params.targetTriple->isOSDarwin()) {
return exe_path::prependLibDir(
name + (sharedLibrary ? "_osx_dynamic.dylib" : "_osx.a"));
} else {
return exe_path::prependLibDir(name + "-" + getCompilerRTArchName() +
(sharedLibrary ? ".so" : ".a"));
}
}

void ArgsBuilder::addASanLinkFlags() {
// Examples: "libclang_rt.asan-x86_64.a" or "libclang_rt.asan-arm.a" and
// "libclang_rt.asan-x86_64.so"

auto arch = getCompilerRTArchName();

// TODO: let user choose to link with shared lib. In case of shared ASan, I
// think we also need to statically link with
// libclang_rt.asan-preinit-<arch>.a
bool linkSharedASan = false;
const char *extension = linkSharedASan ? ".so" : ".a";

// TODO: let user choose to link with shared lib.
// In case of shared ASan, I think we also need to statically link with
// libclang_rt.asan-preinit-<arch>.a on Linux. On Darwin, the only option is
// to use the shared library.
bool linkSharedASan = global.params.targetTriple->isOSDarwin();
std::string searchPaths[] = {
exe_path::prependLibDir("libldc_rt.asan-" + llvm::Twine(arch) +
extension),
exe_path::prependLibDir("libclang_rt.asan-" + llvm::Twine(arch) +
extension),
getFullCompilerRTLibPath("libldc_rt.asan", linkSharedASan),
getFullCompilerRTLibPath("libclang_rt.asan", linkSharedASan),
};

for (const auto &filepath : searchPaths) {
if (llvm::sys::fs::exists(filepath)) {
args.push_back(filepath);

if (linkSharedASan) {
// TODO: add -rpath
// Add @executable_path to rpath to support having the shared lib copied
// with the executable.
args.push_back("-rpath");
args.push_back("@executable_path");

// Add the path to the resource dir to rpath to support using the shared
// lib from the default location without copying.
args.push_back("-rpath");
args.push_back(llvm::sys::path::parent_path(filepath));
}

return true;
return;
}
}

// We did not find the library.
return false;
}

void ArgsBuilder::addASanLinkFlags() {
bool success = false;
if (global.params.targetTriple->isOSDarwin()) {
success = addDarwinASanLinkFlags(args);
} else {
success = addUnixlikeASanLinkFlags(args);
}

if (!success) {
// Fallback, requires Clang. The asan library contains a versioned symbol
// name and a linker error will happen when the LDC-LLVM and Clang-LLVM
// versions don't match.
args.push_back("-fsanitize=address");
}
// When we reach here, we did not find the ASan library.
// Fallback, requires Clang. The asan library contains a versioned symbol
// name and a linker error will happen when the LDC-LLVM and Clang-LLVM
// versions don't match.
args.push_back("-fsanitize=address");
}

// Adds all required link flags for -fsanitize=fuzzer when libFuzzer library is
// found.
void ArgsBuilder::addFuzzLinkFlags() {
std::string searchPaths[] = {
#if LDC_LLVM_VER >= 600
getFullCompilerRTLibPath("libldc_rt.fuzzer"),
getFullCompilerRTLibPath("libclang_rt.fuzzer"),
#else
exe_path::prependLibDir("libFuzzer.a"),
exe_path::prependLibDir("libLLVMFuzzer.a"),
#endif
};

for (const auto &filepath : searchPaths) {
Expand Down
2 changes: 1 addition & 1 deletion tests/sanitizers/fsanitize_fuzzer.d
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
bool FuzzMe(const ubyte* data, size_t dataSize)
{
// CHECK: call {{.*}}_sanitizer_cov_trace_pc_guard
// CHECK: call {{.*}}_sanitizer_cov_trace_cmp
// CHECK: call {{.*}}_sanitizer_cov_trace_{{(const_)?}}cmp

return dataSize >= 3 &&
data[0] == 'F' &&
Expand Down
3 changes: 2 additions & 1 deletion tests/sanitizers/link_fuzzer.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// RUN: %ldc -v -fsanitize=fuzzer %s | FileCheck %s
// RUN: not %ldc -v -fsanitize=fuzzer -link-no-cpp %s | FileCheck %s --check-prefix=NOCPP

// CHECK: libFuzzer.a
// "libFuzzer.a" before LLVM 6.0, "lib(ldc|clang)_rt.fuzzer.*.a" since LLVM 6.0
// CHECK: {{(libFuzzer\.a|_rt\.fuzzer.*\.a)}}
// CHECK-SAME: -l{{(c|stdc)}}++

// NOCPP-NOT: -l{{(c|stdc)}}++
Expand Down
2 changes: 1 addition & 1 deletion tests/sanitizers/lit.local.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ if (platform.system() == 'Darwin') or (platform.system() == 'Linux'):
if m is not None:
config.available_features.add('ASan')
continue
m = re.match('.*Fuzzer.*', file)
m = re.match('.*(F|f)uzzer.*', file)
if m is not None:
config.available_features.add('Fuzzer')
continue
Expand Down

0 comments on commit 4a820f1

Please sign in to comment.