Determine whether a given sanitizer is available based on the presenc… (#14919)

Determine whether a given sanitizer is available based on the presence of the library.

rdar://37192887
This commit is contained in:
George Karpenkov
2018-03-02 16:57:45 -08:00
committed by GitHub
parent dcd8bb8c5a
commit a65da887f2
18 changed files with 62 additions and 44 deletions

View File

@@ -199,8 +199,10 @@ public:
///
/// \param args Invocation arguments.
/// \param sanitizer Sanitizer name.
/// \param shared Whether the library is shared
virtual bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args,
StringRef sanitizer) const;
StringRef sanitizer,
bool shared=true) const;
};
} // end namespace driver

View File

@@ -32,11 +32,9 @@ class DiagnosticEngine;
// sanitizer dylib with a given name.
/// \return Returns a SanitizerKind.
OptionSet<SanitizerKind> parseSanitizerArgValues(
const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A,
const llvm::Triple &Triple,
DiagnosticEngine &Diag,
llvm::function_ref<bool(llvm::StringRef)> sanitizerRuntimeLibExists);
const llvm::opt::ArgList &Args, const llvm::opt::Arg *A,
const llvm::Triple &Triple, DiagnosticEngine &Diag,
llvm::function_ref<bool(llvm::StringRef, bool)> sanitizerRuntimeLibExists);
/// \brief Parses a -sanitize-coverage= argument's value.
llvm::SanitizerCoverageOptions parseSanitizerCoverageArgValue(

View File

@@ -1356,8 +1356,8 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_EQ))
OI.SelectedSanitizers = parseSanitizerArgValues(
Args, A, TC.getTriple(), Diags,
[&](StringRef sanitizerName) {
return TC.sanitizerRuntimeLibExists(Args, sanitizerName);
[&](StringRef sanitizerName, bool shared) {
return TC.sanitizerRuntimeLibExists(Args, sanitizerName, shared);
});
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_coverage_EQ)) {

View File

@@ -317,7 +317,8 @@ ToolChain::constructBatchJob(ArrayRef<const Job *> jobs,
bool
ToolChain::sanitizerRuntimeLibExists(const ArgList &args,
StringRef sanitizerName) const {
StringRef sanitizerName,
bool shared) const {
// Assume no sanitizers are supported by default.
// This method should be overriden by a platform-specific subclass.
return false;

View File

@@ -1132,18 +1132,21 @@ getSanitizerRuntimeLibNameForLinux(StringRef Sanitizer, const llvm::Triple &Trip
}
bool toolchains::Darwin::sanitizerRuntimeLibExists(
const ArgList &args, StringRef sanitizer) const {
const ArgList &args, StringRef sanitizer, bool shared) const {
SmallString<128> sanitizerLibPath;
getClangLibraryPath(*this, args, sanitizerLibPath);
llvm::sys::path::append(sanitizerLibPath,
getSanitizerRuntimeLibNameForDarwin(sanitizer, this->getTriple()));
getSanitizerRuntimeLibNameForDarwin(
sanitizer, this->getTriple(), shared));
return llvm::sys::fs::exists(sanitizerLibPath.str());
}
bool toolchains::GenericUnix::sanitizerRuntimeLibExists(
const ArgList &args, StringRef sanitizer) const {
const ArgList &args, StringRef sanitizer, bool shared) const {
SmallString<128> sanitizerLibPath;
getClangLibraryPath(*this, args, sanitizerLibPath);
// All libraries are static for linux.
llvm::sys::path::append(sanitizerLibPath,
getSanitizerRuntimeLibNameForLinux(sanitizer, this->getTriple()));
return llvm::sys::fs::exists(sanitizerLibPath.str());
@@ -1784,4 +1787,3 @@ std::string toolchains::Cygwin::getDefaultLinker() const {
std::string toolchains::Cygwin::getTargetForLinker() const {
return "";
}

View File

@@ -34,7 +34,8 @@ public:
Darwin(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {}
~Darwin() = default;
bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args,
StringRef sanitizerLibName)
StringRef sanitizerLibName,
bool shared)
const override;
};
@@ -73,7 +74,8 @@ public:
GenericUnix(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {}
~GenericUnix() = default;
bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args,
StringRef sanitizerLibName)
StringRef sanitizerLibName,
bool shared)
const override;
};

View File

@@ -686,7 +686,7 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_EQ)) {
Opts.Sanitizers = parseSanitizerArgValues(
Args, A, Triple, Diags,
/* sanitizerRuntimeLibExists= */[](StringRef libName) {
/* sanitizerRuntimeLibExists= */[](StringRef libName, bool shared) {
// The driver has checked the existence of the library
// already.

View File

@@ -41,6 +41,18 @@ static StringRef toStringRef(const SanitizerKind kind) {
llvm_unreachable("Unsupported sanitizer");
}
static const char* toFileName(const SanitizerKind kind) {
switch (kind) {
case SanitizerKind::Address:
return "asan";
case SanitizerKind::Thread:
return "tsan";
case SanitizerKind::Fuzzer:
return "fuzzer";
}
llvm_unreachable("Unsupported sanitizer");
}
llvm::SanitizerCoverageOptions swift::parseSanitizerCoverageArgValue(
const llvm::opt::Arg *A, const llvm::Triple &Triple,
DiagnosticEngine &Diags, OptionSet<SanitizerKind> sanitizers) {
@@ -108,33 +120,43 @@ llvm::SanitizerCoverageOptions swift::parseSanitizerCoverageArgValue(
return opts;
}
static bool isTSanSupported(
const llvm::Triple &Triple,
llvm::function_ref<bool(llvm::StringRef)> sanitizerRuntimeLibExists) {
return Triple.isArch64Bit() && sanitizerRuntimeLibExists("tsan");
}
OptionSet<SanitizerKind> swift::parseSanitizerArgValues(
const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A,
const llvm::Triple &Triple,
DiagnosticEngine &Diags,
llvm::function_ref<bool(llvm::StringRef)> sanitizerRuntimeLibExists) {
llvm::function_ref<bool(llvm::StringRef, bool)> sanitizerRuntimeLibExists) {
OptionSet<SanitizerKind> sanitizerSet;
// Find the sanitizer kind.
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
StringRef opt = A->getValue(i);
if (opt == "address") {
sanitizerSet |= SanitizerKind::Address;
} else if (opt == "thread") {
sanitizerSet |= SanitizerKind::Thread;
} else if (opt == "fuzzer") {
sanitizerSet |= SanitizerKind::Fuzzer;
} else {
auto kind = llvm::StringSwitch<Optional<SanitizerKind>>(A->getValue(i))
.Case("address", SanitizerKind::Address)
.Case("thread", SanitizerKind::Thread)
.Case("fuzzer", SanitizerKind::Fuzzer)
.Default(None);
bool isShared = kind && *kind != SanitizerKind::Fuzzer;
if (!kind) {
Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument,
A->getOption().getPrefixedName(), A->getValue(i));
} else {
// Support is determined by existance of the sanitizer library.
bool sanitizerSupported =
sanitizerRuntimeLibExists(toFileName(*kind), isShared);
// TSan is explicitly not supported for 32 bits.
if (*kind == SanitizerKind::Thread && !Triple.isArch64Bit())
sanitizerSupported = false;
if (!sanitizerSupported) {
SmallString<128> b;
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
(A->getOption().getPrefixedName() + toStringRef(*kind))
.toStringRef(b),
Triple.getTriple());
} else {
sanitizerSet |= *kind;
}
}
}
@@ -159,16 +181,5 @@ OptionSet<SanitizerKind> swift::parseSanitizerArgValues(
+ toStringRef(SanitizerKind::Thread)).toStringRef(b2));
}
// Thread Sanitizer only works on OS X and the simulators. It's only supported
// on 64 bit architectures.
if ((sanitizerSet & SanitizerKind::Thread) &&
!isTSanSupported(Triple, sanitizerRuntimeLibExists)) {
SmallString<128> b;
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
(A->getOption().getPrefixedName()
+ toStringRef(SanitizerKind::Thread)).toStringRef(b),
Triple.getTriple());
}
return sanitizerSet;
}

View File

@@ -1,4 +1,4 @@
// RUN: %swiftc_driver -driver-print-jobs -sanitize=fuzzer,address %s | %FileCheck -check-prefix=LIBFUZZER %s
// RUN: %swiftc_driver -driver-print-jobs -sanitize=fuzzer,address -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ %s | %FileCheck -check-prefix=LIBFUZZER %s
// LIBFUZZER: libclang_rt.fuzzer
@_cdecl("LLVMFuzzerTestOneInput") public func fuzzOneInput(Data: UnsafePointer<CChar>, Size: CLong) -> CInt {

View File

@@ -1,4 +1,5 @@
// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=address -target x86_64-apple-macosx10.9 %s | %FileCheck -check-prefix=ASAN -check-prefix=ASAN_OSX %s
// RUN: not %swiftc_driver -driver-print-jobs -sanitize=fuzzer -target x86_64-apple-macosx10.9 -resource-dir %S/Inputs/nonexistent-resource-dir %s 2>&1 | %FileCheck -check-prefix=FUZZER_NONEXISTENT %s
// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=address -target x86_64-apple-ios7.1 %s | %FileCheck -check-prefix=ASAN -check-prefix=ASAN_IOSSIM %s
// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=address -target arm64-apple-ios7.1 %s | %FileCheck -check-prefix=ASAN -check-prefix=ASAN_IOS %s
// RUN: %swiftc_driver -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ -driver-print-jobs -sanitize=address -target x86_64-apple-tvos9.0 %s | %FileCheck -check-prefix=ASAN -check-prefix=ASAN_tvOS_SIM %s
@@ -54,6 +55,7 @@
// TSAN_tvOS: unsupported option '-sanitize=thread' for target 'arm64-apple-tvos9.0'
// TSAN_watchOS_SIM: unsupported option '-sanitize=thread' for target 'i386-apple-watchos2.0'
// TSAN_watchOS: unsupported option '-sanitize=thread' for target 'armv7k-apple-watchos2.0'
// FUZZER_NONEXISTENT: unsupported option '-sanitize=fuzzer' for target 'x86_64-apple-macosx10.9'
// TSAN_LINUX: lib/swift/clang/lib/linux/libclang_rt.tsan-x86_64.a
// TSAN: -rpath @executable_path