From 4c12c6cf3b08151ca44e84669f96942da6809c28 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Fri, 14 Nov 2014 02:59:20 +0000 Subject: [PATCH] [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 --- clang/include/clang/Basic/Sanitizers.h | 3 + clang/include/clang/Driver/SanitizerArgs.h | 112 ++-------- clang/lib/Basic/Sanitizers.cpp | 4 + clang/lib/Driver/SanitizerArgs.cpp | 331 +++++++++++++++++++---------- 4 files changed, 232 insertions(+), 218 deletions(-) diff --git a/clang/include/clang/Basic/Sanitizers.h b/clang/include/clang/Basic/Sanitizers.h index 423eaf6..868b331 100644 --- a/clang/include/clang/Basic/Sanitizers.h +++ b/clang/include/clang/Basic/Sanitizers.h @@ -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 diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index 8246bb5..01e3cb7 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -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 @@ -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 diff --git a/clang/lib/Basic/Sanitizers.cpp b/clang/lib/Basic/Sanitizers.cpp index 7bc884d..e9aaa36 100644 --- a/clang/lib/Basic/Sanitizers.cpp +++ b/clang/lib/Basic/Sanitizers.cpp @@ -29,3 +29,7 @@ void SanitizerSet::set(SanitizerKind K, bool Value) { void SanitizerSet::clear() { Kinds = 0; } + +bool SanitizerSet::empty() const { + return Kinds == 0; +} diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 0490cc0..d2b3a91 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -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 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,11 +343,31 @@ 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) { +bool SanitizerArgs::getDefaultBlacklist(const Driver &D, std::string &BLPath) { + const char *BlacklistFile = nullptr; + if (Sanitizers.has(SanitizerKind::Address)) + BlacklistFile = "asan_blacklist.txt"; + else if (Sanitizers.has(SanitizerKind::Memory)) + BlacklistFile = "msan_blacklist.txt"; + else if (Sanitizers.has(SanitizerKind::Thread)) + BlacklistFile = "tsan_blacklist.txt"; + else if (Sanitizers.has(SanitizerKind::DataFlow)) + BlacklistFile = "dfsan_abilist.txt"; + + if (BlacklistFile) { + SmallString<64> Path(D.ResourceDir); + llvm::sys::path::append(Path, BlacklistFile); + BLPath = Path.str(); + return true; + } + return false; +} + +unsigned parseValue(const char *Value) { unsigned ParsedKind = llvm::StringSwitch(Value) #define SANITIZER(NAME, ID) .Case(NAME, ID) #define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID##Group) @@ -244,44 +376,21 @@ unsigned SanitizerArgs::parse(const char *Value) { return ParsedKind; } -unsigned SanitizerArgs::expandGroups(unsigned Kinds) { +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; } -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) { +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; @@ -292,64 +401,70 @@ unsigned SanitizerArgs::filterUnsupportedKinds(const ToolChain &TC, if (!(IsLinux && (IsX86 || IsX86_64))) { KindsToFilterOut |= Function; } - filterUnsupportedMask(TC, Kinds, KindsToFilterOut, Args, A, DiagnoseErrors, - DiagnosedKinds); - return Kinds; + 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 SanitizerArgs::parse(const Driver &D, const llvm::opt::Arg *A, - bool DiagnoseErrors) { +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 = parse(A->getValue(I))) + if (unsigned K = parseValue(A->getValue(I))) Kind |= K; else if (DiagnoseErrors) - D.Diag(diag::err_drv_unsupported_option_argument) + D.Diag(clang::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) { +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 = 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; + Add = parseArgValues(D, A, DiagnoseErrors); + return true; + } + if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) { + Remove = parseArgValues(D, A, DiagnoseErrors); + return true; } - return true; + return false; } -std::string SanitizerArgs::lastArgumentForKind(const Driver &D, - const llvm::opt::ArgList &Args, - unsigned Kind) { +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 (parse(D, Args, *I, Add, Remove, false) && - (expandGroups(Add) & Kind)) - return describeSanitizeArg(Args, *I, Kind); - Kind &= ~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 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 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(parse(A->getValue(I))) & Mask) { + if (expandGroups(parseValue(A->getValue(I))) & Mask) { if (!Sanitizers.empty()) Sanitizers += ","; Sanitizers += A->getValue(I); @@ -359,23 +474,3 @@ std::string SanitizerArgs::describeSanitizeArg(const llvm::opt::ArgList &Args, 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) - BlacklistFile = "asan_blacklist.txt"; - else if (Kind & NeedsMsanRt) - BlacklistFile = "msan_blacklist.txt"; - else if (Kind & NeedsTsanRt) - BlacklistFile = "tsan_blacklist.txt"; - else if (Kind & NeedsDfsanRt) - BlacklistFile = "dfsan_abilist.txt"; - - if (BlacklistFile) { - SmallString<64> Path(D.ResourceDir); - llvm::sys::path::append(Path, BlacklistFile); - BLPath = Path.str(); - return true; - } - return false; -} -- 2.7.4