diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Driver/SanitizerArgs.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Driver/SanitizerArgs.cpp | 524 |
1 files changed, 354 insertions, 170 deletions
diff --git a/contrib/llvm/tools/clang/lib/Driver/SanitizerArgs.cpp b/contrib/llvm/tools/clang/lib/Driver/SanitizerArgs.cpp index b64f027..bd7bc21 100644 --- a/contrib/llvm/tools/clang/lib/Driver/SanitizerArgs.cpp +++ b/contrib/llvm/tools/clang/lib/Driver/SanitizerArgs.cpp @@ -21,93 +21,263 @@ 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, + RequiresPIE = Memory | DataFlow, + NeedsUnwindTables = Address | Thread | Memory | DataFlow, + SupportsCoverage = Address | Memory | Leak | Undefined | Integer, + RecoverableByDefault = Undefined | Integer, + Unrecoverable = Address | Unreachable | Return, + LegacyFsanitizeRecoverMask = Undefined | Integer +}; +} + +/// 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); + +/// 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); + +/// Produce a string containing comma-separated names of sanitizers in \p +/// Sanitizers set. +static std::string toString(const clang::SanitizerSet &Sanitizers); + +/// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers +/// this group enables. +static unsigned expandGroups(unsigned Kinds); + +static unsigned getToolchainUnsupportedKinds(const ToolChain &TC) { + 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 Unsupported = 0; + if (!(IsLinux && IsX86_64)) { + Unsupported |= Memory | DataFlow; + } + if (!((IsLinux || IsFreeBSD) && IsX86_64)) { + Unsupported |= Thread; + } + if (!(IsLinux && (IsX86 || IsX86_64))) { + Unsupported |= Function; + } + return Unsupported; +} + +bool SanitizerArgs::needsUbsanRt() const { + return !UbsanTrapOnError && hasOneOf(Sanitizers, NeedsUbsanRt); +} + +bool SanitizerArgs::requiresPIE() const { + return AsanZeroBaseShadow || hasOneOf(Sanitizers, RequiresPIE); +} + +bool SanitizerArgs::needsUnwindTables() const { + return hasOneOf(Sanitizers, NeedsUnwindTables); +} + void SanitizerArgs::clear() { - Kind = 0; + Sanitizers.clear(); + RecoverableSanitizers.clear(); BlacklistFile = ""; + SanitizeCoverage = 0; MsanTrackOrigins = 0; + AsanFieldPadding = 0; AsanZeroBaseShadow = false; UbsanTrapOnError = false; AsanSharedRuntime = false; -} - -SanitizerArgs::SanitizerArgs() { - clear(); + LinkCXXRuntimes = false; } SanitizerArgs::SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args) { clear(); - unsigned AllAdd = 0; // All kinds of sanitizers that were turned on - // at least once (possibly, disabled further). unsigned AllRemove = 0; // During the loop below, the accumulated set of // sanitizers disabled by the current sanitizer // argument or any argument after it. unsigned DiagnosedKinds = 0; // All Kinds we have diagnosed up to now. // Used to deduplicate diagnostics. + unsigned Kinds = 0; + unsigned NotSupported = getToolchainUnsupportedKinds(TC); const Driver &D = TC.getDriver(); 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)) - continue; - (*I)->claim(); - - AllAdd |= expandGroups(Add); - AllRemove |= expandGroups(Remove); - - // Avoid diagnosing any sanitizer which is disabled later. - 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, - 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, - DiagnosedKinds); - - Kind |= Add; + const auto *Arg = *I; + if (Arg->getOption().matches(options::OPT_fsanitize_EQ)) { + Arg->claim(); + unsigned Add = parseArgValues(D, Arg, true); + + // Avoid diagnosing any sanitizer which is disabled later. + 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. + if (unsigned KindsToDiagnose = Add & NotSupported & ~DiagnosedKinds) { + // Only diagnose the new kinds. + std::string Desc = describeSanitizeArg(*I, KindsToDiagnose); + D.Diag(diag::err_drv_unsupported_opt_for_target) + << Desc << TC.getTriple().str(); + DiagnosedKinds |= KindsToDiagnose; + } + Add &= ~NotSupported; + + 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 &= ~NotSupported; + + Kinds |= Add; + } else if (Arg->getOption().matches(options::OPT_fno_sanitize_EQ)) { + Arg->claim(); + unsigned Remove = parseArgValues(D, Arg, true); + AllRemove |= expandGroups(Remove); + } + } + addAllOf(Sanitizers, Kinds); + + // Parse -f(no-)?sanitize-recover flags. + unsigned RecoverableKinds = RecoverableByDefault; + unsigned DiagnosedUnrecoverableKinds = 0; + for (const auto *Arg : Args) { + if (Arg->getOption().matches(options::OPT_fsanitize_recover)) { + // FIXME: Add deprecation notice, and then remove this flag. + RecoverableKinds |= expandGroups(LegacyFsanitizeRecoverMask); + Arg->claim(); + } else if (Arg->getOption().matches(options::OPT_fno_sanitize_recover)) { + // FIXME: Add deprecation notice, and then remove this flag. + RecoverableKinds &= ~expandGroups(LegacyFsanitizeRecoverMask); + Arg->claim(); + } else if (Arg->getOption().matches(options::OPT_fsanitize_recover_EQ)) { + unsigned Add = parseArgValues(D, Arg, true); + // Report error if user explicitly tries to recover from unrecoverable + // sanitizer. + if (unsigned KindsToDiagnose = + Add & Unrecoverable & ~DiagnosedUnrecoverableKinds) { + SanitizerSet SetToDiagnose; + addAllOf(SetToDiagnose, KindsToDiagnose); + D.Diag(diag::err_drv_unsupported_option_argument) + << Arg->getOption().getName() << toString(SetToDiagnose); + DiagnosedUnrecoverableKinds |= KindsToDiagnose; + } + RecoverableKinds |= expandGroups(Add); + Arg->claim(); + } else if (Arg->getOption().matches(options::OPT_fno_sanitize_recover_EQ)) { + RecoverableKinds &= ~expandGroups(parseArgValues(D, Arg, true)); + Arg->claim(); + } } + RecoverableKinds &= Kinds; + RecoverableKinds &= ~Unrecoverable; + addAllOf(RecoverableSanitizers, RecoverableKinds); UbsanTrapOnError = Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error, 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 @@ -124,19 +294,18 @@ 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 { // If no -fsanitize-blacklist option is specified, try to look up for // blacklist in the resource directory. std::string BLPath; - if (getDefaultBlacklistForKind(D, Kind, BLPath) && - llvm::sys::fs::exists(BLPath)) + if (getDefaultBlacklist(D, BLPath) && llvm::sys::fs::exists(BLPath)) BlacklistFile = BLPath; } @@ -155,32 +324,85 @@ 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; } } } } + // Parse -fsanitize-coverage=N. Currently one of asan/msan/lsan is required. + if (hasOneOf(Sanitizers, SupportsCoverage)) { + if (Arg *A = Args.getLastArg(options::OPT_fsanitize_coverage)) { + StringRef S = A->getValue(); + // Legal values are 0..4. + if (S.getAsInteger(0, SanitizeCoverage) || SanitizeCoverage < 0 || + SanitizeCoverage > 4) + D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; + } + } + if (NeedsAsan) { AsanSharedRuntime = Args.hasArg(options::OPT_shared_libasan) || (TC.getTriple().getEnvironment() == llvm::Triple::Android); AsanZeroBaseShadow = (TC.getTriple().getEnvironment() == llvm::Triple::Android); + if (Arg *A = + Args.getLastArg(options::OPT_fsanitize_address_field_padding)) { + StringRef S = A->getValue(); + // 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(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; + } + } + + if (Arg *WindowsDebugRTArg = + Args.getLastArg(options::OPT__SLASH_MTd, options::OPT__SLASH_MT, + options::OPT__SLASH_MDd, options::OPT__SLASH_MD, + options::OPT__SLASH_LDd, options::OPT__SLASH_LD)) { + switch (WindowsDebugRTArg->getOption().getID()) { + case options::OPT__SLASH_MTd: + case options::OPT__SLASH_MDd: + case options::OPT__SLASH_LDd: + D.Diag(clang::diag::err_drv_argument_not_allowed_with) + << WindowsDebugRTArg->getAsString(Args) + << lastArgumentForKind(D, Args, SanitizerKind::Address); + D.Diag(clang::diag::note_drv_address_sanitizer_debug_runtime); + } + } + } + + // Parse -link-cxx-sanitizer flag. + LinkCXXRuntimes = + Args.hasArg(options::OPT_fsanitize_link_cxx_runtime) || D.CCCIsCXX(); +} + +static std::string toString(const clang::SanitizerSet &Sanitizers) { + std::string Res; +#define SANITIZER(NAME, ID) \ + if (Sanitizers.has(clang::SanitizerKind::ID)) { \ + if (!Res.empty()) \ + Res += ","; \ + Res += NAME; \ } +#include "clang/Basic/Sanitizers.def" + return Res; } 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) \ - SanitizeOpt += NAME ","; -#include "clang/Basic/Sanitizers.def" - SanitizeOpt.pop_back(); - CmdArgs.push_back(Args.MakeArgString(SanitizeOpt)); + CmdArgs.push_back(Args.MakeArgString("-fsanitize=" + toString(Sanitizers))); + + if (!RecoverableSanitizers.empty()) + CmdArgs.push_back(Args.MakeArgString("-fsanitize-recover=" + + toString(RecoverableSanitizers))); + + if (UbsanTrapOnError) + CmdArgs.push_back("-fsanitize-undefined-trap-on-error"); + if (!BlacklistFile.empty()) { SmallString<64> BlacklistOpt("-fsanitize-blacklist="); BlacklistOpt += BlacklistFile; @@ -190,13 +412,38 @@ void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args, if (MsanTrackOrigins) CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins=" + llvm::utostr(MsanTrackOrigins))); - + if (AsanFieldPadding) + CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" + + llvm::utostr(AsanFieldPadding))); + if (SanitizeCoverage) + 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<SanitizeKind>(Value) #define SANITIZER(NAME, ID) .Case(NAME, ID) #define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID##Group) @@ -205,107 +452,65 @@ 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) { - 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; - if (!(IsLinux && IsX86_64)) { - filterUnsupportedMask(TC, Kinds, Thread | Memory | DataFlow, Args, A, - DiagnoseErrors, DiagnosedKinds); - } - if (!(IsLinux && (IsX86 || IsX86_64))) { - filterUnsupportedMask(TC, Kinds, Function, Args, A, DiagnoseErrors, - DiagnosedKinds); - } - return Kinds; -} - -unsigned SanitizerArgs::parse(const Driver &D, const llvm::opt::Arg *A, - bool DiagnoseErrors) { - unsigned Kind = 0; +unsigned parseArgValues(const Driver &D, const llvm::opt::Arg *A, + bool DiagnoseErrors) { + assert((A->getOption().matches(options::OPT_fsanitize_EQ) || + A->getOption().matches(options::OPT_fno_sanitize_EQ) || + A->getOption().matches(options::OPT_fsanitize_recover_EQ) || + A->getOption().matches(options::OPT_fno_sanitize_recover_EQ)) && + "Invalid argument in parseArgValues!"); + unsigned Kinds = 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; -} + const char *Value = A->getValue(I); + unsigned Kind; + // Special case: don't accept -fsanitize=all. + if (A->getOption().matches(options::OPT_fsanitize_EQ) && + 0 == strcmp("all", Value)) + Kind = 0; + else + Kind = parseValue(Value); -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; + if (Kind) + Kinds |= Kind; + else if (DiagnoseErrors) + D.Diag(clang::diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Value; } - return true; + return Kinds; } -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; + const auto *Arg = *I; + if (Arg->getOption().matches(options::OPT_fsanitize_EQ)) { + unsigned AddKinds = expandGroups(parseArgValues(D, Arg, false)); + if (AddKinds & Mask) + return describeSanitizeArg(Arg, Mask); + } else if (Arg->getOption().matches(options::OPT_fno_sanitize_EQ)) { + unsigned RemoveKinds = expandGroups(parseArgValues(D, Arg, false)); + Mask &= ~RemoveKinds; + } } 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); @@ -315,24 +520,3 @@ std::string SanitizerArgs::describeSanitizeArg(const llvm::opt::ArgList &Args, assert(!Sanitizers.empty() && "arg didn't provide expected value"); return "-fsanitize=" + Sanitizers; } - -bool SanitizerArgs::getDefaultBlacklistForKind(const Driver &D, unsigned Kind, - 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; -} |