Skip to content

Commit a65da88

Browse files
author
George Karpenkov
authored
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
1 parent dcd8bb8 commit a65da88

18 files changed

+62
-44
lines changed

include/swift/Driver/ToolChain.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,10 @@ class ToolChain {
199199
///
200200
/// \param args Invocation arguments.
201201
/// \param sanitizer Sanitizer name.
202+
/// \param shared Whether the library is shared
202203
virtual bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args,
203-
StringRef sanitizer) const;
204+
StringRef sanitizer,
205+
bool shared=true) const;
204206

205207
};
206208
} // end namespace driver

include/swift/Option/SanitizerOptions.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,9 @@ class DiagnosticEngine;
3232
// sanitizer dylib with a given name.
3333
/// \return Returns a SanitizerKind.
3434
OptionSet<SanitizerKind> parseSanitizerArgValues(
35-
const llvm::opt::ArgList &Args,
36-
const llvm::opt::Arg *A,
37-
const llvm::Triple &Triple,
38-
DiagnosticEngine &Diag,
39-
llvm::function_ref<bool(llvm::StringRef)> sanitizerRuntimeLibExists);
35+
const llvm::opt::ArgList &Args, const llvm::opt::Arg *A,
36+
const llvm::Triple &Triple, DiagnosticEngine &Diag,
37+
llvm::function_ref<bool(llvm::StringRef, bool)> sanitizerRuntimeLibExists);
4038

4139
/// \brief Parses a -sanitize-coverage= argument's value.
4240
llvm::SanitizerCoverageOptions parseSanitizerCoverageArgValue(

lib/Driver/Driver.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,8 +1356,8 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
13561356
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_EQ))
13571357
OI.SelectedSanitizers = parseSanitizerArgValues(
13581358
Args, A, TC.getTriple(), Diags,
1359-
[&](StringRef sanitizerName) {
1360-
return TC.sanitizerRuntimeLibExists(Args, sanitizerName);
1359+
[&](StringRef sanitizerName, bool shared) {
1360+
return TC.sanitizerRuntimeLibExists(Args, sanitizerName, shared);
13611361
});
13621362

13631363
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_coverage_EQ)) {

lib/Driver/ToolChain.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,8 @@ ToolChain::constructBatchJob(ArrayRef<const Job *> jobs,
317317

318318
bool
319319
ToolChain::sanitizerRuntimeLibExists(const ArgList &args,
320-
StringRef sanitizerName) const {
320+
StringRef sanitizerName,
321+
bool shared) const {
321322
// Assume no sanitizers are supported by default.
322323
// This method should be overriden by a platform-specific subclass.
323324
return false;

lib/Driver/ToolChains.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,18 +1132,21 @@ getSanitizerRuntimeLibNameForLinux(StringRef Sanitizer, const llvm::Triple &Trip
11321132
}
11331133

11341134
bool toolchains::Darwin::sanitizerRuntimeLibExists(
1135-
const ArgList &args, StringRef sanitizer) const {
1135+
const ArgList &args, StringRef sanitizer, bool shared) const {
11361136
SmallString<128> sanitizerLibPath;
11371137
getClangLibraryPath(*this, args, sanitizerLibPath);
11381138
llvm::sys::path::append(sanitizerLibPath,
1139-
getSanitizerRuntimeLibNameForDarwin(sanitizer, this->getTriple()));
1139+
getSanitizerRuntimeLibNameForDarwin(
1140+
sanitizer, this->getTriple(), shared));
11401141
return llvm::sys::fs::exists(sanitizerLibPath.str());
11411142
}
11421143

11431144
bool toolchains::GenericUnix::sanitizerRuntimeLibExists(
1144-
const ArgList &args, StringRef sanitizer) const {
1145+
const ArgList &args, StringRef sanitizer, bool shared) const {
11451146
SmallString<128> sanitizerLibPath;
11461147
getClangLibraryPath(*this, args, sanitizerLibPath);
1148+
1149+
// All libraries are static for linux.
11471150
llvm::sys::path::append(sanitizerLibPath,
11481151
getSanitizerRuntimeLibNameForLinux(sanitizer, this->getTriple()));
11491152
return llvm::sys::fs::exists(sanitizerLibPath.str());
@@ -1784,4 +1787,3 @@ std::string toolchains::Cygwin::getDefaultLinker() const {
17841787
std::string toolchains::Cygwin::getTargetForLinker() const {
17851788
return "";
17861789
}
1787-

lib/Driver/ToolChains.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ class LLVM_LIBRARY_VISIBILITY Darwin : public ToolChain {
3434
Darwin(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {}
3535
~Darwin() = default;
3636
bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args,
37-
StringRef sanitizerLibName)
37+
StringRef sanitizerLibName,
38+
bool shared)
3839
const override;
3940
};
4041

@@ -73,7 +74,8 @@ class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain {
7374
GenericUnix(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {}
7475
~GenericUnix() = default;
7576
bool sanitizerRuntimeLibExists(const llvm::opt::ArgList &args,
76-
StringRef sanitizerLibName)
77+
StringRef sanitizerLibName,
78+
bool shared)
7779
const override;
7880
};
7981

lib/Frontend/CompilerInvocation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
686686
if (const Arg *A = Args.getLastArg(options::OPT_sanitize_EQ)) {
687687
Opts.Sanitizers = parseSanitizerArgValues(
688688
Args, A, Triple, Diags,
689-
/* sanitizerRuntimeLibExists= */[](StringRef libName) {
689+
/* sanitizerRuntimeLibExists= */[](StringRef libName, bool shared) {
690690

691691
// The driver has checked the existence of the library
692692
// already.

lib/Option/SanitizerOptions.cpp

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ static StringRef toStringRef(const SanitizerKind kind) {
4141
llvm_unreachable("Unsupported sanitizer");
4242
}
4343

44+
static const char* toFileName(const SanitizerKind kind) {
45+
switch (kind) {
46+
case SanitizerKind::Address:
47+
return "asan";
48+
case SanitizerKind::Thread:
49+
return "tsan";
50+
case SanitizerKind::Fuzzer:
51+
return "fuzzer";
52+
}
53+
llvm_unreachable("Unsupported sanitizer");
54+
}
55+
4456
llvm::SanitizerCoverageOptions swift::parseSanitizerCoverageArgValue(
4557
const llvm::opt::Arg *A, const llvm::Triple &Triple,
4658
DiagnosticEngine &Diags, OptionSet<SanitizerKind> sanitizers) {
@@ -108,33 +120,43 @@ llvm::SanitizerCoverageOptions swift::parseSanitizerCoverageArgValue(
108120
return opts;
109121
}
110122

111-
static bool isTSanSupported(
112-
const llvm::Triple &Triple,
113-
llvm::function_ref<bool(llvm::StringRef)> sanitizerRuntimeLibExists) {
114-
115-
return Triple.isArch64Bit() && sanitizerRuntimeLibExists("tsan");
116-
}
117-
118123
OptionSet<SanitizerKind> swift::parseSanitizerArgValues(
119124
const llvm::opt::ArgList &Args,
120125
const llvm::opt::Arg *A,
121126
const llvm::Triple &Triple,
122127
DiagnosticEngine &Diags,
123-
llvm::function_ref<bool(llvm::StringRef)> sanitizerRuntimeLibExists) {
128+
llvm::function_ref<bool(llvm::StringRef, bool)> sanitizerRuntimeLibExists) {
124129
OptionSet<SanitizerKind> sanitizerSet;
125130

126131
// Find the sanitizer kind.
127132
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
128-
StringRef opt = A->getValue(i);
129-
if (opt == "address") {
130-
sanitizerSet |= SanitizerKind::Address;
131-
} else if (opt == "thread") {
132-
sanitizerSet |= SanitizerKind::Thread;
133-
} else if (opt == "fuzzer") {
134-
sanitizerSet |= SanitizerKind::Fuzzer;
135-
} else {
133+
auto kind = llvm::StringSwitch<Optional<SanitizerKind>>(A->getValue(i))
134+
.Case("address", SanitizerKind::Address)
135+
.Case("thread", SanitizerKind::Thread)
136+
.Case("fuzzer", SanitizerKind::Fuzzer)
137+
.Default(None);
138+
bool isShared = kind && *kind != SanitizerKind::Fuzzer;
139+
if (!kind) {
136140
Diags.diagnose(SourceLoc(), diag::error_unsupported_option_argument,
137141
A->getOption().getPrefixedName(), A->getValue(i));
142+
} else {
143+
// Support is determined by existance of the sanitizer library.
144+
bool sanitizerSupported =
145+
sanitizerRuntimeLibExists(toFileName(*kind), isShared);
146+
147+
// TSan is explicitly not supported for 32 bits.
148+
if (*kind == SanitizerKind::Thread && !Triple.isArch64Bit())
149+
sanitizerSupported = false;
150+
151+
if (!sanitizerSupported) {
152+
SmallString<128> b;
153+
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
154+
(A->getOption().getPrefixedName() + toStringRef(*kind))
155+
.toStringRef(b),
156+
Triple.getTriple());
157+
} else {
158+
sanitizerSet |= *kind;
159+
}
138160
}
139161
}
140162

@@ -159,16 +181,5 @@ OptionSet<SanitizerKind> swift::parseSanitizerArgValues(
159181
+ toStringRef(SanitizerKind::Thread)).toStringRef(b2));
160182
}
161183

162-
// Thread Sanitizer only works on OS X and the simulators. It's only supported
163-
// on 64 bit architectures.
164-
if ((sanitizerSet & SanitizerKind::Thread) &&
165-
!isTSanSupported(Triple, sanitizerRuntimeLibExists)) {
166-
SmallString<128> b;
167-
Diags.diagnose(SourceLoc(), diag::error_unsupported_opt_for_target,
168-
(A->getOption().getPrefixedName()
169-
+ toStringRef(SanitizerKind::Thread)).toStringRef(b),
170-
Triple.getTriple());
171-
}
172-
173184
return sanitizerSet;
174185
}

test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/darwin/libclang_rt.asan_ios_dynamic.dylib

Whitespace-only changes.

test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/darwin/libclang_rt.asan_iossim_dynamic.dylib

Whitespace-only changes.

test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/darwin/libclang_rt.asan_tvos_dynamic.dylib

Whitespace-only changes.

test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/darwin/libclang_rt.asan_tvossim_dynamic.dylib

Whitespace-only changes.

test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/darwin/libclang_rt.asan_watchos_dynamic.dylib

Whitespace-only changes.

test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/darwin/libclang_rt.asan_watchossim_dynamic.dylib

Whitespace-only changes.

test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/darwin/libclang_rt.fuzzer_osx.a

Whitespace-only changes.

test/Driver/Inputs/fake-resource-dir/lib/swift/clang/lib/linux/libclang_rt.fuzzer-x86_64.a

Whitespace-only changes.

test/Driver/fuzzer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %swiftc_driver -driver-print-jobs -sanitize=fuzzer,address %s | %FileCheck -check-prefix=LIBFUZZER %s
1+
// RUN: %swiftc_driver -driver-print-jobs -sanitize=fuzzer,address -resource-dir %S/Inputs/fake-resource-dir/lib/swift/ %s | %FileCheck -check-prefix=LIBFUZZER %s
22

33
// LIBFUZZER: libclang_rt.fuzzer
44
@_cdecl("LLVMFuzzerTestOneInput") public func fuzzOneInput(Data: UnsafePointer<CChar>, Size: CLong) -> CInt {

test/Driver/sanitizers.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// 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
2+
// 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
23
// 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
34
// 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
45
// 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 @@
5455
// TSAN_tvOS: unsupported option '-sanitize=thread' for target 'arm64-apple-tvos9.0'
5556
// TSAN_watchOS_SIM: unsupported option '-sanitize=thread' for target 'i386-apple-watchos2.0'
5657
// TSAN_watchOS: unsupported option '-sanitize=thread' for target 'armv7k-apple-watchos2.0'
58+
// FUZZER_NONEXISTENT: unsupported option '-sanitize=fuzzer' for target 'x86_64-apple-macosx10.9'
5759
// TSAN_LINUX: lib/swift/clang/lib/linux/libclang_rt.tsan-x86_64.a
5860

5961
// TSAN: -rpath @executable_path

0 commit comments

Comments
 (0)