Index: include/clang/Driver/Driver.h =================================================================== --- include/clang/Driver/Driver.h +++ include/clang/Driver/Driver.h @@ -179,9 +179,6 @@ /// stored in it, and will clean them up when torn down. mutable llvm::StringMap ToolChains; - /// Parsed arguments passed to sanitizer tools. - mutable llvm::OwningPtr SanitizerArguments; - private: /// TranslateInputArgs - Create a new derived argument list from the input /// arguments, after applying the standard argument translations. @@ -406,10 +403,6 @@ std::pair getIncludeExcludeOptionFlagMasks() const; public: - /// \brief Returns parsed arguments to sanitizer tools. - const SanitizerArgs & - getOrParseSanitizerArgs(const llvm::opt::ArgList &Args) const; - /// GetReleaseVersion - Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and /// return the grouped values as integers. Numbers which are not /// provided are set to 0. Index: include/clang/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -25,6 +25,7 @@ /// 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 }; @@ -32,7 +33,8 @@ /// Bugs to catch at runtime. enum SanitizeKind { #define SANITIZER(NAME, ID) ID = 1 << SO_##ID, -#define SANITIZER_GROUP(NAME, ID, ALIAS) ID = ALIAS, +#define SANITIZER_GROUP(NAME, ID, ALIAS) \ + ID = ALIAS, ID##Group = 1 << SO_##ID##Group, #include "clang/Basic/Sanitizers.def" NeedsAsanRt = Address, NeedsTsanRt = Thread, @@ -47,17 +49,13 @@ std::string BlacklistFile; bool MsanTrackOrigins; - enum AsanZeroBaseShadowKind { - AZBSK_Default, // Default value is toolchain-specific. - AZBSK_On, - AZBSK_Off - } AsanZeroBaseShadow; + bool AsanZeroBaseShadow; bool UbsanTrapOnError; public: SanitizerArgs(); /// Parses the sanitizer arguments from an argument list. - SanitizerArgs(const Driver &D, const llvm::opt::ArgList &Args); + SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args); bool needsAsanRt() const { return Kind & NeedsAsanRt; } bool needsTsanRt() const { return Kind & NeedsTsanRt; } @@ -73,17 +71,15 @@ bool sanitizesVptr() const { return Kind & Vptr; } bool notAllowedWithTrap() const { return Kind & NotAllowedWithTrap; } - bool hasZeroBaseShadow(const ToolChain &TC) const { - return (Kind & HasZeroBaseShadow) || hasAsanZeroBaseShadow(TC); + bool hasZeroBaseShadow() const { + return (Kind & HasZeroBaseShadow) || AsanZeroBaseShadow; } - void addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, + void addArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; private: void clear(); - bool hasAsanZeroBaseShadow(const ToolChain &TC) const; - /// 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. @@ -119,6 +115,11 @@ static bool getDefaultBlacklistForKind(const Driver &D, unsigned Kind, std::string &BLPath); + + static unsigned expandGroups(unsigned Kinds); + + static unsigned filterUnsupported(const ToolChain &TC, unsigned Kinds, + bool DiagnoseErrors); }; } // namespace driver Index: include/clang/Driver/ToolChain.h =================================================================== --- include/clang/Driver/ToolChain.h +++ include/clang/Driver/ToolChain.h @@ -73,6 +73,8 @@ Tool *getLink() const; Tool *getClangAs() const; + mutable OwningPtr SanitizerArguments; + protected: ToolChain(const Driver &D, const llvm::Triple &T, const llvm::opt::ArgList &Args); Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -16,7 +16,6 @@ #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Job.h" #include "clang/Driver/Options.h" -#include "clang/Driver/SanitizerArgs.h" #include "clang/Driver/Tool.h" #include "clang/Driver/ToolChain.h" #include "llvm/ADT/ArrayRef.h" @@ -2056,10 +2055,3 @@ return std::make_pair(IncludedFlagsBitmask, ExcludedFlagsBitmask); } - -const SanitizerArgs & -Driver::getOrParseSanitizerArgs(const ArgList &Args) const { - if (!SanitizerArguments.get()) - SanitizerArguments.reset(new SanitizerArgs(*this, Args)); - return *SanitizerArguments.get(); -} Index: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -25,7 +25,7 @@ Kind = 0; BlacklistFile = ""; MsanTrackOrigins = false; - AsanZeroBaseShadow = AZBSK_Default; + AsanZeroBaseShadow = false; UbsanTrapOnError = false; } @@ -33,15 +33,27 @@ clear(); } -SanitizerArgs::SanitizerArgs(const Driver &D, const llvm::opt::ArgList &Args) { +SanitizerArgs::SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args) { clear(); unsigned AllKinds = 0; // All kinds of sanitizers that were turned on // at least once (possibly, disabled further). + const Driver &D = TC.getDriver(); for (ArgList::const_iterator I = Args.begin(), E = Args.end(); I != E; ++I) { unsigned Add, Remove; if (!parse(D, Args, *I, Add, Remove, true)) continue; (*I)->claim(); + + // At this point we have not expanded groups, so any unsupported sanitizers + // in Add are those which have been explicitly enabled. Diagnose them. + Add = filterUnsupported(TC, Add, /*DiagnoseErrors=*/true); + + Add = expandGroups(Add); + + // Silently discard any unsupported sanitizers implicitly enabled through + // group expansion. + Add = filterUnsupported(TC, Add, /*DiagnoseErrors=*/false); + Kind |= Add; Kind &= ~Remove; AllKinds |= Add; @@ -148,21 +160,25 @@ // Parse -f(no-)sanitize-address-zero-base-shadow options. if (NeedsAsan) { - if (Arg *A = Args.getLastArg( - options::OPT_fsanitize_address_zero_base_shadow, - options::OPT_fno_sanitize_address_zero_base_shadow)) - AsanZeroBaseShadow = A->getOption().matches( - options::OPT_fsanitize_address_zero_base_shadow) - ? AZBSK_On - : AZBSK_Off; + bool IsAndroid = (TC.getTriple().getEnvironment() == llvm::Triple::Android); + bool ZeroBaseShadowDefault = IsAndroid; + AsanZeroBaseShadow = + Args.hasFlag(options::OPT_fsanitize_address_zero_base_shadow, + options::OPT_fno_sanitize_address_zero_base_shadow, + ZeroBaseShadowDefault); + // Zero-base shadow is a requirement on Android. + if (IsAndroid && !AsanZeroBaseShadow) { + D.Diag(diag::err_drv_argument_not_allowed_with) + << "-fno-sanitize-address-zero-base-shadow" + << lastArgumentForKind(D, Args, Address); + } } } -void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, +void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const { if (!Kind) return; - const Driver &D = TC.getDriver(); SmallString<256> SanitizeOpt("-fsanitize="); #define SANITIZER(NAME, ID) \ if (Kind & ID) \ @@ -179,36 +195,19 @@ if (MsanTrackOrigins) CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins")); - if (needsAsanRt()) { - if (hasAsanZeroBaseShadow(TC)) { - CmdArgs.push_back( - Args.MakeArgString("-fsanitize-address-zero-base-shadow")); - } else if (TC.getTriple().getEnvironment() == llvm::Triple::Android) { - // Zero-base shadow is a requirement on Android. - D.Diag(diag::err_drv_argument_not_allowed_with) - << "-fno-sanitize-address-zero-base-shadow" - << lastArgumentForKind(D, Args, Address); - } - } + if (AsanZeroBaseShadow) + CmdArgs.push_back( + Args.MakeArgString("-fsanitize-address-zero-base-shadow")); // Workaround for PR16386. if (needsMsanRt()) CmdArgs.push_back(Args.MakeArgString("-fno-assume-sane-operator-new")); } -bool SanitizerArgs::hasAsanZeroBaseShadow(const ToolChain &TC) const { - if (!needsAsanRt()) - return false; - if (AsanZeroBaseShadow != AZBSK_Default) - return AsanZeroBaseShadow == AZBSK_On; - // Zero-base shadow is used by default only on Android. - return TC.getTriple().getEnvironment() == llvm::Triple::Android; -} - unsigned SanitizerArgs::parse(const char *Value) { unsigned ParsedKind = llvm::StringSwitch(Value) #define SANITIZER(NAME, ID) .Case(NAME, ID) -#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID) +#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID##Group) #include "clang/Basic/Sanitizers.def" .Default(SanitizeKind()); // Assume -fsanitize=address implies -fsanitize=init-order,use-after-return. @@ -219,6 +218,49 @@ 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; +} + +unsigned SanitizerArgs::filterUnsupported(const ToolChain &TC, unsigned Kinds, + bool DiagnoseErrors) { + if (!(TC.getTriple().getOS() == llvm::Triple::Linux && + TC.getTriple().getArch() == llvm::Triple::x86_64)) { + if (Kinds & Memory) { + Kinds &= ~Memory; + if (DiagnoseErrors) { + TC.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) + << "-fsanitize=memory" << TC.getTriple().str(); + } + } + + if (Kinds & DataFlow) { + Kinds &= ~DataFlow; + if (DiagnoseErrors) { + TC.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) + << "-fsanitize=dataflow" << TC.getTriple().str(); + } + } + } + + if (!(TC.getTriple().getOS() == llvm::Triple::Linux && + (TC.getTriple().getArch() == llvm::Triple::x86 || + TC.getTriple().getArch() == llvm::Triple::x86_64))) { + if (Kinds & Function) { + Kinds &= ~Function; + if (DiagnoseErrors) { + TC.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) + << "-fsanitize=function" << TC.getTriple().str(); + } + } + } + + return Kinds; +} + unsigned SanitizerArgs::parse(const Driver &D, const llvm::opt::Arg *A, bool DiagnoseErrors) { unsigned Kind = 0; @@ -282,7 +324,7 @@ I != E; ++I) { unsigned Add, Remove; if (parse(D, Args, *I, Add, Remove, false) && - (Add & Kind)) + (expandGroups(Add) & Kind)) return describeSanitizeArg(Args, *I, Kind); Kind &= ~Remove; } @@ -296,7 +338,7 @@ return A->getAsString(Args); for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) - if (parse(A->getValue(I)) & Mask) + if (expandGroups(parse(A->getValue(I))) & Mask) return std::string("-fsanitize=") + A->getValue(I); llvm_unreachable("arg didn't provide expected value"); Index: lib/Driver/ToolChain.cpp =================================================================== --- lib/Driver/ToolChain.cpp +++ lib/Driver/ToolChain.cpp @@ -13,6 +13,7 @@ #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" +#include "clang/Driver/SanitizerArgs.h" #include "clang/Driver/ToolChain.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Option/Arg.h" @@ -43,7 +44,9 @@ } const SanitizerArgs& ToolChain::getSanitizerArgs() const { - return D.getOrParseSanitizerArgs(Args); + if (!SanitizerArguments.get()) + SanitizerArguments.reset(new SanitizerArgs(*this, Args)); + return *SanitizerArguments.get(); } std::string ToolChain::getDefaultUniversalArchName() const { Index: lib/Driver/ToolChains.cpp =================================================================== --- lib/Driver/ToolChains.cpp +++ lib/Driver/ToolChains.cpp @@ -293,7 +293,7 @@ } } - const SanitizerArgs &Sanitize = getDriver().getOrParseSanitizerArgs(Args); + const SanitizerArgs &Sanitize = getSanitizerArgs(); // Add Ubsan runtime library, if required. if (Sanitize.needsUbsanRt()) { @@ -2715,7 +2715,7 @@ } bool Linux::isPIEDefault() const { - return getSanitizerArgs().hasZeroBaseShadow(*this); + return getSanitizerArgs().hasZeroBaseShadow(); } /// DragonFly - DragonFly tool chain which can call as(1) and ld(1) directly. Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -2883,8 +2883,8 @@ Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_show_template_tree); Args.AddLastArg(CmdArgs, options::OPT_fno_elide_type); - const SanitizerArgs &Sanitize = D.getOrParseSanitizerArgs(Args); - Sanitize.addArgs(getToolChain(), Args, CmdArgs); + const SanitizerArgs &Sanitize = getToolChain().getSanitizerArgs(); + Sanitize.addArgs(Args, CmdArgs); if (!Args.hasFlag(options::OPT_fsanitize_recover, options::OPT_fno_sanitize_recover, @@ -6118,10 +6118,10 @@ const Driver &D = ToolChain.getDriver(); const bool isAndroid = ToolChain.getTriple().getEnvironment() == llvm::Triple::Android; - const SanitizerArgs &Sanitize = D.getOrParseSanitizerArgs(Args); + const SanitizerArgs &Sanitize = ToolChain.getSanitizerArgs(); const bool IsPIE = !Args.hasArg(options::OPT_shared) && - (Args.hasArg(options::OPT_pie) || Sanitize.hasZeroBaseShadow(ToolChain)); + (Args.hasArg(options::OPT_pie) || Sanitize.hasZeroBaseShadow()); ArgStringList CmdArgs; @@ -6652,7 +6652,7 @@ ImplibName.str())); } - if (getToolChain().getDriver().getOrParseSanitizerArgs(Args).needsAsanRt()) { + if (getToolChain().getSanitizerArgs().needsAsanRt()) { CmdArgs.push_back(Args.MakeArgString("-debug")); CmdArgs.push_back(Args.MakeArgString("-incremental:no")); SmallString<128> LibSanitizer(getToolChain().getDriver().ResourceDir); Index: test/Driver/fsanitize.c =================================================================== --- test/Driver/fsanitize.c +++ test/Driver/fsanitize.c @@ -7,6 +7,9 @@ // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED // CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|bounds|enum|bool),?){16}"}} +// RUN: %clang -target x86_64-apple-darwin10 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-DARWIN +// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|bounds|enum|bool),?){15}"}} + // RUN: %clang -target x86_64-linux-gnu -fsanitize=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTEGER // CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift),?){4}"}} @@ -152,3 +155,12 @@ // RUN: %clang -target x86_64-linux-gnu -fsanitize=zzz %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DIAG1 // CHECK-DIAG1: unsupported argument 'zzz' to option 'fsanitize=' // CHECK-DIAG1-NOT: unsupported argument 'zzz' to option 'fsanitize=' + +// RUN: %clang -target i686-linux-gnu -fsanitize=memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MSAN-X86 +// CHECK-MSAN-X86: error: unsupported option '-fsanitize=memory' for target 'i686--linux-gnu' + +// RUN: %clang -target x86_64-apple-darwin10 -fsanitize=memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MSAN-DARWIN +// CHECK-MSAN-DARWIN: unsupported option '-fsanitize=memory' for target 'x86_64-apple-darwin10' + +// RUN: %clang -target x86_64-apple-darwin10 -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSAN-DARWIN +// CHECK-FSAN-DARWIN: unsupported option '-fsanitize=function' for target 'x86_64-apple-darwin10'