[Sanitizer] Refactor SanitizerArgs parsing in Driver.

Remove flag parsing details from the public header.
Use SanitizerSet to represent the set of enabled sanitizers.
Cleanup the implementation: update the comments to
reflect reality, remove dead code.

No functionality change.

llvm-svn: 221968
This commit is contained in:
Alexey Samsonov 2014-11-14 02:59:20 +00:00
parent eb2378b70c
commit 4c12c6cf3b
4 changed files with 282 additions and 268 deletions

View File

@ -37,6 +37,9 @@ public:
/// \brief Disable all sanitizers.
void clear();
/// \brief Returns true if at least one sanitizer is enabled.
bool empty() const;
};
} // end namespace clang

View File

@ -9,6 +9,7 @@
#ifndef LLVM_CLANG_DRIVER_SANITIZERARGS_H
#define LLVM_CLANG_DRIVER_SANITIZERARGS_H
#include "clang/Basic/Sanitizers.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include <string>
@ -20,33 +21,7 @@ class Driver;
class ToolChain;
class SanitizerArgs {
/// Assign ordinals to sanitizer flags. We'll use the ordinal values as
/// bit positions within \c Kind.
enum SanitizeOrdinal {
#define SANITIZER(NAME, ID) SO_##ID,
#define SANITIZER_GROUP(NAME, ID, ALIAS) SO_##ID##Group,
#include "clang/Basic/Sanitizers.def"
SO_Count
};
/// Bugs to catch at runtime.
enum SanitizeKind {
#define SANITIZER(NAME, ID) ID = 1 << SO_##ID,
#define SANITIZER_GROUP(NAME, ID, ALIAS) \
ID = ALIAS, ID##Group = 1 << SO_##ID##Group,
#include "clang/Basic/Sanitizers.def"
NeedsAsanRt = Address,
NeedsTsanRt = Thread,
NeedsMsanRt = Memory,
NeedsDfsanRt = DataFlow,
NeedsLeakDetection = Leak,
NeedsUbsanRt = Undefined | Integer,
NotAllowedWithTrap = Vptr,
HasZeroBaseShadow = Thread | Memory | DataFlow,
NeedsUnwindTables = Address | Thread | Memory | DataFlow
};
unsigned Kind;
SanitizerSet Sanitizers;
std::string BlacklistFile;
int SanitizeCoverage;
int MsanTrackOrigins;
@ -60,90 +35,27 @@ class SanitizerArgs {
/// Parses the sanitizer arguments from an argument list.
SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args);
bool needsAsanRt() const { return Kind & NeedsAsanRt; }
bool needsAsanRt() const { return Sanitizers.has(SanitizerKind::Address); }
bool needsSharedAsanRt() const { return AsanSharedRuntime; }
bool needsTsanRt() const { return Kind & NeedsTsanRt; }
bool needsMsanRt() const { return Kind & NeedsMsanRt; }
bool needsLeakDetection() const { return Kind & NeedsLeakDetection; }
bool needsTsanRt() const { return Sanitizers.has(SanitizerKind::Thread); }
bool needsMsanRt() const { return Sanitizers.has(SanitizerKind::Memory); }
bool needsLsanRt() const {
return needsLeakDetection() && !needsAsanRt();
return Sanitizers.has(SanitizerKind::Leak) &&
!Sanitizers.has(SanitizerKind::Address);
}
bool needsUbsanRt() const {
return !UbsanTrapOnError && (Kind & NeedsUbsanRt);
}
bool needsDfsanRt() const { return Kind & NeedsDfsanRt; }
bool needsUbsanRt() const;
bool needsDfsanRt() const { return Sanitizers.has(SanitizerKind::DataFlow); }
bool sanitizesVptr() const { return Kind & Vptr; }
bool notAllowedWithTrap() const { return Kind & NotAllowedWithTrap; }
bool hasZeroBaseShadow() const {
return (Kind & HasZeroBaseShadow) || AsanZeroBaseShadow;
}
bool needsUnwindTables() const { return Kind & NeedsUnwindTables; }
bool sanitizesVptr() const { return Sanitizers.has(SanitizerKind::Vptr); }
bool hasZeroBaseShadow() const;
bool needsUnwindTables() const;
bool linkCXXRuntimes() const { return LinkCXXRuntimes; }
void addArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;
private:
void clear();
bool getDefaultBlacklist(const Driver &D, std::string &BLPath);
/// Parse a single value from a -fsanitize= or -fno-sanitize= value list.
/// Returns OR of members of the \c SanitizeKind enumeration, or \c 0
/// if \p Value is not known.
static unsigned parse(const char *Value);
/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
/// invalid components.
static unsigned parse(const Driver &D, const llvm::opt::Arg *A,
bool DiagnoseErrors);
/// Parse a single flag of the form -f[no]sanitize=, or
/// -f*-sanitizer. Sets the masks defining required change of Kind value.
/// Returns true if the flag was parsed successfully.
static bool parse(const Driver &D, const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A, unsigned &Add, unsigned &Remove,
bool DiagnoseErrors);
/// Produce an argument string from ArgList \p Args, which shows how it
/// provides a sanitizer kind in \p Mask. For example, the argument list
/// "-fsanitize=thread,vptr -fsanitize=address" with mask \c NeedsUbsanRt
/// would produce "-fsanitize=vptr".
static std::string lastArgumentForKind(const Driver &D,
const llvm::opt::ArgList &Args,
unsigned Kind);
/// Produce an argument string from argument \p A, which shows how it provides
/// a value in \p Mask. For instance, the argument
/// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce
/// "-fsanitize=alignment".
static std::string describeSanitizeArg(const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A,
unsigned Mask);
/// Return the smallest superset of sanitizer set \p Kinds such that each
/// member of each group whose flag is set in \p Kinds has its flag set in the
/// result.
static unsigned expandGroups(unsigned Kinds);
/// Return the subset of \p Kinds supported by toolchain \p TC. If
/// \p DiagnoseErrors is true, produce an error diagnostic for each sanitizer
/// removed from \p Kinds.
static unsigned filterUnsupportedKinds(const ToolChain &TC, unsigned Kinds,
const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A,
bool DiagnoseErrors,
unsigned &DiagnosedKinds);
/// The flags in \p Mask are unsupported by \p TC. If present in \p Kinds,
/// remove them and produce an error diagnostic referring to \p A if
/// \p DiagnoseErrors is true.
static void filterUnsupportedMask(const ToolChain &TC, unsigned &Kinds,
unsigned Mask,
const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A,
bool DiagnoseErrors,
unsigned &DiagnosedKinds);
};
} // namespace driver

View File

@ -29,3 +29,7 @@ void SanitizerSet::set(SanitizerKind K, bool Value) {
void SanitizerSet::clear() {
Kinds = 0;
}
bool SanitizerSet::empty() const {
return Kinds == 0;
}

View File

@ -21,8 +21,120 @@
using namespace clang::driver;
using namespace llvm::opt;
namespace {
/// Assign ordinals to possible values of -fsanitize= flag.
/// We use the ordinal values as bit positions within \c SanitizeKind.
enum SanitizeOrdinal {
#define SANITIZER(NAME, ID) SO_##ID,
#define SANITIZER_GROUP(NAME, ID, ALIAS) SO_##ID##Group,
#include "clang/Basic/Sanitizers.def"
SO_Count
};
/// Represents a set of sanitizer kinds. It is also used to define:
/// 1) set of sanitizers each sanitizer group expands into.
/// 2) set of sanitizers sharing a specific property (e.g.
/// all sanitizers with zero-base shadow).
enum SanitizeKind {
#define SANITIZER(NAME, ID) ID = 1 << SO_##ID,
#define SANITIZER_GROUP(NAME, ID, ALIAS) \
ID = ALIAS, ID##Group = 1 << SO_##ID##Group,
#include "clang/Basic/Sanitizers.def"
NeedsUbsanRt = Undefined | Integer,
NotAllowedWithTrap = Vptr,
HasZeroBaseShadow = Thread | Memory | DataFlow,
NeedsUnwindTables = Address | Thread | Memory | DataFlow
};
}
/// Returns true if set of \p Sanitizers contain at least one sanitizer from
/// \p Kinds.
static bool hasOneOf(const clang::SanitizerSet &Sanitizers, unsigned Kinds) {
#define SANITIZER(NAME, ID) \
if (Sanitizers.has(clang::SanitizerKind::ID) && (Kinds & ID)) \
return true;
#include "clang/Basic/Sanitizers.def"
return false;
}
/// Adds all sanitizers from \p Kinds to \p Sanitizers.
static void addAllOf(clang::SanitizerSet &Sanitizers, unsigned Kinds) {
#define SANITIZER(NAME, ID) \
if (Kinds & ID) \
Sanitizers.set(clang::SanitizerKind::ID, true);
#include "clang/Basic/Sanitizers.def"
}
static unsigned toSanitizeKind(clang::SanitizerKind K) {
#define SANITIZER(NAME, ID) \
if (K == clang::SanitizerKind::ID) \
return ID;
#include "clang/Basic/Sanitizers.def"
llvm_unreachable("Invalid SanitizerKind!");
}
/// Parse a single value from a -fsanitize= or -fno-sanitize= value list.
/// Returns a member of the \c SanitizeKind enumeration, or \c 0
/// if \p Value is not known.
static unsigned parseValue(const char *Value);
/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
/// invalid components. Returns OR of members of \c SanitizeKind enumeration.
static unsigned parseArgValues(const Driver &D, const llvm::opt::Arg *A,
bool DiagnoseErrors);
/// Parse a single flag of the form -f[no]sanitize=.
/// Sets the masks defining required change of the set of sanitizers.
/// Returns true if the flag was parsed successfully.
static bool parseArgument(const Driver &D, const llvm::opt::Arg *A,
unsigned &Add, unsigned &Remove, bool DiagnoseErrors);
/// Produce an argument string from ArgList \p Args, which shows how it
/// provides some sanitizer kind from \p Mask. For example, the argument list
/// "-fsanitize=thread,vptr -fsanitize=address" with mask \c NeedsUbsanRt
/// would produce "-fsanitize=vptr".
static std::string lastArgumentForMask(const Driver &D,
const llvm::opt::ArgList &Args,
unsigned Mask);
static std::string lastArgumentForKind(const Driver &D,
const llvm::opt::ArgList &Args,
clang::SanitizerKind K) {
return lastArgumentForMask(D, Args, toSanitizeKind(K));
}
/// Produce an argument string from argument \p A, which shows how it provides
/// a value in \p Mask. For instance, the argument
/// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce
/// "-fsanitize=alignment".
static std::string describeSanitizeArg(const llvm::opt::Arg *A, unsigned Mask);
/// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers
/// this group enables.
static unsigned expandGroups(unsigned Kinds);
/// Return the subset of \p Kinds supported by toolchain \p TC. If
/// \p DiagnoseErrors is true, produce an error diagnostic for each sanitizer
/// removed from \p Kinds.
static unsigned filterUnsupportedKinds(const ToolChain &TC, unsigned Kinds,
const llvm::opt::Arg *A,
bool DiagnoseErrors,
unsigned &DiagnosedKinds);
bool SanitizerArgs::needsUbsanRt() const {
return !UbsanTrapOnError && hasOneOf(Sanitizers, NeedsUbsanRt);
}
bool SanitizerArgs::hasZeroBaseShadow() const {
return AsanZeroBaseShadow || hasOneOf(Sanitizers, HasZeroBaseShadow);
}
bool SanitizerArgs::needsUnwindTables() const {
return hasOneOf(Sanitizers, NeedsUnwindTables);
}
void SanitizerArgs::clear() {
Kind = 0;
Sanitizers.clear();
BlacklistFile = "";
SanitizeCoverage = 0;
MsanTrackOrigins = 0;
@ -45,7 +157,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend();
I != E; ++I) {
unsigned Add, Remove;
if (!parse(D, Args, *I, Add, Remove, true))
if (!parseArgument(D, *I, Add, Remove, true))
continue;
(*I)->claim();
@ -55,17 +167,17 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
Add &= ~AllRemove;
// At this point we have not expanded groups, so any unsupported sanitizers
// in Add are those which have been explicitly enabled. Diagnose them.
Add = filterUnsupportedKinds(TC, Add, Args, *I, /*DiagnoseErrors=*/true,
Add = filterUnsupportedKinds(TC, Add, *I, /*DiagnoseErrors=*/true,
DiagnosedKinds);
Add = expandGroups(Add);
// Group expansion may have enabled a sanitizer which is disabled later.
Add &= ~AllRemove;
// Silently discard any unsupported sanitizers implicitly enabled through
// group expansion.
Add = filterUnsupportedKinds(TC, Add, Args, *I, /*DiagnoseErrors=*/false,
Add = filterUnsupportedKinds(TC, Add, *I, /*DiagnoseErrors=*/false,
DiagnosedKinds);
Kind |= Add;
addAllOf(Sanitizers, Add);
}
UbsanTrapOnError =
@ -73,37 +185,37 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
options::OPT_fno_sanitize_undefined_trap_on_error, false);
// Warn about undefined sanitizer options that require runtime support.
if (UbsanTrapOnError && notAllowedWithTrap()) {
D.Diag(diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, NotAllowedWithTrap)
if (UbsanTrapOnError && hasOneOf(Sanitizers, NotAllowedWithTrap)) {
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
<< lastArgumentForMask(D, Args, NotAllowedWithTrap)
<< "-fsanitize-undefined-trap-on-error";
}
// Only one runtime library can be used at once.
bool NeedsAsan = needsAsanRt();
bool NeedsTsan = needsTsanRt();
bool NeedsMsan = needsMsanRt();
bool NeedsLsan = needsLeakDetection();
// Check for incompatible sanitizers.
bool NeedsAsan = Sanitizers.has(SanitizerKind::Address);
bool NeedsTsan = Sanitizers.has(SanitizerKind::Thread);
bool NeedsMsan = Sanitizers.has(SanitizerKind::Memory);
bool NeedsLsan = Sanitizers.has(SanitizerKind::Leak);
if (NeedsAsan && NeedsTsan)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, NeedsAsanRt)
<< lastArgumentForKind(D, Args, NeedsTsanRt);
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, SanitizerKind::Address)
<< lastArgumentForKind(D, Args, SanitizerKind::Thread);
if (NeedsAsan && NeedsMsan)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, NeedsAsanRt)
<< lastArgumentForKind(D, Args, NeedsMsanRt);
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, SanitizerKind::Address)
<< lastArgumentForKind(D, Args, SanitizerKind::Memory);
if (NeedsTsan && NeedsMsan)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, NeedsTsanRt)
<< lastArgumentForKind(D, Args, NeedsMsanRt);
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, SanitizerKind::Thread)
<< lastArgumentForKind(D, Args, SanitizerKind::Memory);
if (NeedsLsan && NeedsTsan)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, NeedsLeakDetection)
<< lastArgumentForKind(D, Args, NeedsTsanRt);
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, SanitizerKind::Leak)
<< lastArgumentForKind(D, Args, SanitizerKind::Thread);
if (NeedsLsan && NeedsMsan)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, NeedsLeakDetection)
<< lastArgumentForKind(D, Args, NeedsMsanRt);
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
<< lastArgumentForKind(D, Args, SanitizerKind::Leak)
<< lastArgumentForKind(D, Args, SanitizerKind::Memory);
// FIXME: Currently -fsanitize=leak is silently ignored in the presence of
// -fsanitize=address. Perhaps it should print an error, or perhaps
// -f(-no)sanitize=leak should change whether leak detection is enabled by
@ -120,11 +232,11 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
std::unique_ptr<llvm::SpecialCaseList> SCL(
llvm::SpecialCaseList::create(BLPath, BLError));
if (!SCL.get())
D.Diag(diag::err_drv_malformed_sanitizer_blacklist) << BLError;
D.Diag(clang::diag::err_drv_malformed_sanitizer_blacklist) << BLError;
else
BlacklistFile = BLPath;
} else {
D.Diag(diag::err_drv_no_such_file) << BLPath;
D.Diag(clang::diag::err_drv_no_such_file) << BLPath;
}
}
} else {
@ -150,7 +262,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
StringRef S = A->getValue();
if (S.getAsInteger(0, MsanTrackOrigins) || MsanTrackOrigins < 0 ||
MsanTrackOrigins > 2) {
D.Diag(diag::err_drv_invalid_value) << A->getAsString(Args) << S;
D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
}
}
}
@ -163,7 +275,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
// Legal values are 0..4.
if (S.getAsInteger(0, SanitizeCoverage) || SanitizeCoverage < 0 ||
SanitizeCoverage > 4)
D.Diag(diag::err_drv_invalid_value) << A->getAsString(Args) << S;
D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
}
}
@ -179,7 +291,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
// Legal values are 0 and 1, 2, but in future we may add more levels.
if (S.getAsInteger(0, AsanFieldPadding) || AsanFieldPadding < 0 ||
AsanFieldPadding > 2) {
D.Diag(diag::err_drv_invalid_value) << A->getAsString(Args) << S;
D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
}
}
@ -191,10 +303,10 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
case options::OPT__SLASH_MTd:
case options::OPT__SLASH_MDd:
case options::OPT__SLASH_LDd:
D.Diag(diag::err_drv_argument_not_allowed_with)
D.Diag(clang::diag::err_drv_argument_not_allowed_with)
<< WindowsDebugRTArg->getAsString(Args)
<< lastArgumentForKind(D, Args, NeedsAsanRt);
D.Diag(diag::note_drv_address_sanitizer_debug_runtime);
<< lastArgumentForKind(D, Args, SanitizerKind::Address);
D.Diag(clang::diag::note_drv_address_sanitizer_debug_runtime);
}
}
}
@ -206,11 +318,11 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const {
if (!Kind)
if (Sanitizers.empty())
return;
SmallString<256> SanitizeOpt("-fsanitize=");
#define SANITIZER(NAME, ID) \
if (Kind & ID) \
if (Sanitizers.has(SanitizerKind::ID)) \
SanitizeOpt += NAME ",";
#include "clang/Basic/Sanitizers.def"
SanitizeOpt.pop_back();
@ -231,144 +343,19 @@ void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args,
CmdArgs.push_back(Args.MakeArgString("-fsanitize-coverage=" +
llvm::utostr(SanitizeCoverage)));
// Workaround for PR16386.
if (needsMsanRt())
if (Sanitizers.has(SanitizerKind::Memory))
CmdArgs.push_back(Args.MakeArgString("-fno-assume-sane-operator-new"));
}
unsigned SanitizerArgs::parse(const char *Value) {
unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value)
#define SANITIZER(NAME, ID) .Case(NAME, ID)
#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID##Group)
#include "clang/Basic/Sanitizers.def"
.Default(SanitizeKind());
return ParsedKind;
}
unsigned SanitizerArgs::expandGroups(unsigned Kinds) {
#define SANITIZER(NAME, ID)
#define SANITIZER_GROUP(NAME, ID, ALIAS) if (Kinds & ID##Group) Kinds |= ID;
#include "clang/Basic/Sanitizers.def"
return Kinds;
}
void SanitizerArgs::filterUnsupportedMask(const ToolChain &TC, unsigned &Kinds,
unsigned Mask,
const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A,
bool DiagnoseErrors,
unsigned &DiagnosedKinds) {
unsigned MaskedKinds = Kinds & Mask;
if (!MaskedKinds)
return;
Kinds &= ~Mask;
// Do we have new kinds to diagnose?
if (DiagnoseErrors && (DiagnosedKinds & MaskedKinds) != MaskedKinds) {
// Only diagnose the new kinds.
std::string Desc =
describeSanitizeArg(Args, A, MaskedKinds & ~DiagnosedKinds);
TC.getDriver().Diag(diag::err_drv_unsupported_opt_for_target)
<< Desc << TC.getTriple().str();
DiagnosedKinds |= MaskedKinds;
}
}
unsigned SanitizerArgs::filterUnsupportedKinds(const ToolChain &TC,
unsigned Kinds,
const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A,
bool DiagnoseErrors,
unsigned &DiagnosedKinds) {
bool IsFreeBSD = TC.getTriple().getOS() == llvm::Triple::FreeBSD;
bool IsLinux = TC.getTriple().getOS() == llvm::Triple::Linux;
bool IsX86 = TC.getTriple().getArch() == llvm::Triple::x86;
bool IsX86_64 = TC.getTriple().getArch() == llvm::Triple::x86_64;
unsigned KindsToFilterOut = 0;
if (!(IsLinux && IsX86_64)) {
KindsToFilterOut |= Memory | DataFlow;
}
if (!((IsLinux || IsFreeBSD) && IsX86_64)) {
KindsToFilterOut |= Thread;
}
if (!(IsLinux && (IsX86 || IsX86_64))) {
KindsToFilterOut |= Function;
}
filterUnsupportedMask(TC, Kinds, KindsToFilterOut, Args, A, DiagnoseErrors,
DiagnosedKinds);
return Kinds;
}
unsigned SanitizerArgs::parse(const Driver &D, const llvm::opt::Arg *A,
bool DiagnoseErrors) {
unsigned Kind = 0;
for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
if (unsigned K = parse(A->getValue(I)))
Kind |= K;
else if (DiagnoseErrors)
D.Diag(diag::err_drv_unsupported_option_argument)
<< A->getOption().getName() << A->getValue(I);
}
return Kind;
}
bool SanitizerArgs::parse(const Driver &D, const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A, unsigned &Add,
unsigned &Remove, bool DiagnoseErrors) {
Add = 0;
Remove = 0;
if (A->getOption().matches(options::OPT_fsanitize_EQ)) {
Add = parse(D, A, DiagnoseErrors);
} else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) {
Remove = parse(D, A, DiagnoseErrors);
} else {
// Flag is not relevant to sanitizers.
return false;
}
return true;
}
std::string SanitizerArgs::lastArgumentForKind(const Driver &D,
const llvm::opt::ArgList &Args,
unsigned Kind) {
for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(),
E = Args.rend();
I != E; ++I) {
unsigned Add, Remove;
if (parse(D, Args, *I, Add, Remove, false) &&
(expandGroups(Add) & Kind))
return describeSanitizeArg(Args, *I, Kind);
Kind &= ~Remove;
}
llvm_unreachable("arg list didn't provide expected value");
}
std::string SanitizerArgs::describeSanitizeArg(const llvm::opt::ArgList &Args,
const llvm::opt::Arg *A,
unsigned Mask) {
if (!A->getOption().matches(options::OPT_fsanitize_EQ))
return A->getAsString(Args);
std::string Sanitizers;
for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
if (expandGroups(parse(A->getValue(I))) & Mask) {
if (!Sanitizers.empty())
Sanitizers += ",";
Sanitizers += A->getValue(I);
}
}
assert(!Sanitizers.empty() && "arg didn't provide expected value");
return "-fsanitize=" + Sanitizers;
}
bool SanitizerArgs::getDefaultBlacklist(const Driver &D, std::string &BLPath) {
const char *BlacklistFile = nullptr;
if (Kind & NeedsAsanRt)
if (Sanitizers.has(SanitizerKind::Address))
BlacklistFile = "asan_blacklist.txt";
else if (Kind & NeedsMsanRt)
else if (Sanitizers.has(SanitizerKind::Memory))
BlacklistFile = "msan_blacklist.txt";
else if (Kind & NeedsTsanRt)
else if (Sanitizers.has(SanitizerKind::Thread))
BlacklistFile = "tsan_blacklist.txt";
else if (Kind & NeedsDfsanRt)
else if (Sanitizers.has(SanitizerKind::DataFlow))
BlacklistFile = "dfsan_abilist.txt";
if (BlacklistFile) {
@ -379,3 +366,111 @@ bool SanitizerArgs::getDefaultBlacklist(const Driver &D, std::string &BLPath) {
}
return false;
}
unsigned parseValue(const char *Value) {
unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value)
#define SANITIZER(NAME, ID) .Case(NAME, ID)
#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID##Group)
#include "clang/Basic/Sanitizers.def"
.Default(SanitizeKind());
return ParsedKind;
}
unsigned expandGroups(unsigned Kinds) {
#define SANITIZER(NAME, ID)
#define SANITIZER_GROUP(NAME, ID, ALIAS) if (Kinds & ID##Group) Kinds |= ID;
#include "clang/Basic/Sanitizers.def"
return Kinds;
}
unsigned filterUnsupportedKinds(const ToolChain &TC, unsigned Kinds,
const llvm::opt::Arg *A, bool DiagnoseErrors,
unsigned &DiagnosedKinds) {
bool IsFreeBSD = TC.getTriple().getOS() == llvm::Triple::FreeBSD;
bool IsLinux = TC.getTriple().getOS() == llvm::Triple::Linux;
bool IsX86 = TC.getTriple().getArch() == llvm::Triple::x86;
bool IsX86_64 = TC.getTriple().getArch() == llvm::Triple::x86_64;
unsigned KindsToFilterOut = 0;
if (!(IsLinux && IsX86_64)) {
KindsToFilterOut |= Memory | DataFlow;
}
if (!((IsLinux || IsFreeBSD) && IsX86_64)) {
KindsToFilterOut |= Thread;
}
if (!(IsLinux && (IsX86 || IsX86_64))) {
KindsToFilterOut |= Function;
}
KindsToFilterOut &= Kinds;
// Do we have new kinds to diagnose?
unsigned KindsToDiagnose = KindsToFilterOut & ~DiagnosedKinds;
if (DiagnoseErrors && KindsToDiagnose) {
// Only diagnose the new kinds.
std::string Desc = describeSanitizeArg(A, KindsToDiagnose);
TC.getDriver().Diag(clang::diag::err_drv_unsupported_opt_for_target)
<< Desc << TC.getTriple().str();
DiagnosedKinds |= KindsToFilterOut;
}
return Kinds & ~KindsToFilterOut;
}
unsigned parseArgValues(const Driver &D, const llvm::opt::Arg *A,
bool DiagnoseErrors) {
unsigned Kind = 0;
for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
if (unsigned K = parseValue(A->getValue(I)))
Kind |= K;
else if (DiagnoseErrors)
D.Diag(clang::diag::err_drv_unsupported_option_argument)
<< A->getOption().getName() << A->getValue(I);
}
return Kind;
}
bool parseArgument(const Driver &D, const llvm::opt::Arg *A, unsigned &Add,
unsigned &Remove, bool DiagnoseErrors) {
Add = 0;
Remove = 0;
if (A->getOption().matches(options::OPT_fsanitize_EQ)) {
Add = parseArgValues(D, A, DiagnoseErrors);
return true;
}
if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) {
Remove = parseArgValues(D, A, DiagnoseErrors);
return true;
}
return false;
}
std::string lastArgumentForMask(const Driver &D, const llvm::opt::ArgList &Args,
unsigned Mask) {
for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(),
E = Args.rend();
I != E; ++I) {
unsigned Add, Remove;
if (parseArgument(D, *I, Add, Remove, false) &&
(expandGroups(Add) & Mask))
return describeSanitizeArg(*I, Mask);
Mask &= ~Remove;
}
llvm_unreachable("arg list didn't provide expected value");
}
std::string describeSanitizeArg(const llvm::opt::Arg *A, unsigned Mask) {
assert(A->getOption().matches(options::OPT_fsanitize_EQ)
&& "Invalid argument in describeSanitizerArg!");
std::string Sanitizers;
for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
if (expandGroups(parseValue(A->getValue(I))) & Mask) {
if (!Sanitizers.empty())
Sanitizers += ",";
Sanitizers += A->getValue(I);
}
}
assert(!Sanitizers.empty() && "arg didn't provide expected value");
return "-fsanitize=" + Sanitizers;
}