aboutsummaryrefslogtreecommitdiff
path: root/lib/Driver/Tools.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Driver/Tools.cpp')
-rw-r--r--lib/Driver/Tools.cpp183
1 files changed, 162 insertions, 21 deletions
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index 05487c4193..d1109ff730 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1452,13 +1452,149 @@ static bool UseRelaxAll(Compilation &C, const ArgList &Args) {
RelaxDefault);
}
+namespace {
+struct 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,
+#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,
+#include "clang/Basic/Sanitizers.def"
+
+ NeedsAsanRt = Address,
+ NeedsTsanRt = Thread,
+ NeedsUbsanRt = Undefined
+ };
+ unsigned Kind;
+
+ SanitizerArgs() : Kind(0) {}
+
+ bool needsAsanRt() const { return Kind & NeedsAsanRt; }
+ bool needsTsanRt() const { return Kind & NeedsTsanRt; }
+ bool needsUbsanRt() const { return Kind & NeedsUbsanRt; }
+
+ /// 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 parse(const char *Value) {
+ return llvm::StringSwitch<SanitizeKind>(Value)
+#define SANITIZER(NAME, ID) .Case(NAME, ID)
+#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID)
+#include "clang/Basic/Sanitizers.def"
+ .Default(SanitizeKind());
+ }
+
+ /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
+ /// invalid components.
+ static unsigned parse(const Driver &D, const Arg *A) {
+ unsigned Kind = 0;
+ for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
+ if (unsigned K = parse(A->getValue(I)))
+ Kind |= K;
+ else
+ D.Diag(diag::err_drv_unsupported_option_argument)
+ << A->getOption().getName() << A->getValue(I);
+ }
+ return Kind;
+ }
+
+ void addArgs(const ArgList &Args, ArgStringList &CmdArgs) const {
+ if (Kind & Address)
+ CmdArgs.push_back("-faddress-sanitizer");
+ if (Kind & Thread)
+ CmdArgs.push_back("-fthread-sanitizer");
+ if (Kind & Undefined)
+ CmdArgs.push_back("-fcatch-undefined-behavior");
+ if (!Kind)
+ return;
+ llvm::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));
+ }
+};
+}
+
+/// 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 Arg *A, unsigned Mask) {
+ if (!A->getOption().matches(options::OPT_fsanitize_EQ))
+ return A->getOption().getName();
+
+ for (unsigned I = 0, N = A->getNumValues(); I != N; ++I)
+ if (SanitizerArgs::parse(A->getValue(I)) & Mask)
+ return std::string("-fsanitize=") + A->getValue(I);
+
+ llvm_unreachable("arg didn't provide expected value");
+}
+
+/// Parse the sanitizer arguments from an argument list.
+static SanitizerArgs getSanitizerArgs(const Driver &D, const ArgList &Args) {
+ SanitizerArgs Sanitize;
+
+ const Arg *AsanArg, *TsanArg, *UbsanArg;
+
+ for (ArgList::const_iterator I = Args.begin(), E = Args.end(); I != E; ++I) {
+ unsigned Add = 0, Remove = 0;
+ if ((*I)->getOption().matches(options::OPT_faddress_sanitizer))
+ Add = SanitizerArgs::Address;
+ else if ((*I)->getOption().matches(options::OPT_fno_address_sanitizer))
+ Remove = SanitizerArgs::Address;
+ else if ((*I)->getOption().matches(options::OPT_fthread_sanitizer))
+ Add = SanitizerArgs::Thread;
+ else if ((*I)->getOption().matches(options::OPT_fno_thread_sanitizer))
+ Remove = SanitizerArgs::Thread;
+ else if ((*I)->getOption().matches(options::OPT_fcatch_undefined_behavior))
+ Add = SanitizerArgs::Undefined;
+ else if ((*I)->getOption().matches(options::OPT_fsanitize_EQ))
+ Add = SanitizerArgs::parse(D, *I);
+ else if ((*I)->getOption().matches(options::OPT_fno_sanitize_EQ))
+ Remove = SanitizerArgs::parse(D, *I);
+ else
+ continue;
+
+ (*I)->claim();
+
+ Sanitize.Kind |= Add;
+ Sanitize.Kind &= ~Remove;
+
+ if (Add & SanitizerArgs::NeedsAsanRt) AsanArg = *I;
+ if (Add & SanitizerArgs::NeedsTsanRt) TsanArg = *I;
+ if (Add & SanitizerArgs::NeedsUbsanRt) UbsanArg = *I;
+ }
+
+ // Only one runtime library can be used at once.
+ // FIXME: Allow Ubsan to be combined with the other two.
+ bool NeedsAsan = Sanitize.needsAsanRt();
+ bool NeedsTsan = Sanitize.needsTsanRt();
+ bool NeedsUbsan = Sanitize.needsUbsanRt();
+ if (NeedsAsan + NeedsTsan + NeedsUbsan > 1)
+ D.Diag(diag::err_drv_argument_not_allowed_with)
+ << describeSanitizeArg(NeedsAsan ? AsanArg : TsanArg,
+ NeedsAsan ? SanitizerArgs::NeedsAsanRt
+ : SanitizerArgs::NeedsTsanRt)
+ << describeSanitizeArg(NeedsUbsan ? UbsanArg : TsanArg,
+ NeedsUbsan ? SanitizerArgs::NeedsUbsanRt
+ : SanitizerArgs::NeedsTsanRt);
+
+ return Sanitize;
+}
+
/// If AddressSanitizer is enabled, add appropriate linker flags (Linux).
/// This needs to be called before we add the C run-time (malloc, etc).
static void addAsanRTLinux(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs) {
- if (!Args.hasFlag(options::OPT_faddress_sanitizer,
- options::OPT_fno_address_sanitizer, false))
- return;
if(TC.getTriple().getEnvironment() == llvm::Triple::Android) {
if (!Args.hasArg(options::OPT_shared)) {
if (!Args.hasArg(options::OPT_pie))
@@ -1490,9 +1626,6 @@ static void addAsanRTLinux(const ToolChain &TC, const ArgList &Args,
/// This needs to be called before we add the C run-time (malloc, etc).
static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs) {
- if (!Args.hasFlag(options::OPT_fthread_sanitizer,
- options::OPT_fno_thread_sanitizer, false))
- return;
if (!Args.hasArg(options::OPT_shared)) {
// LibTsan is "libclang_rt.tsan-<ArchName>.a" in the Linux library
// resource directory.
@@ -1511,8 +1644,6 @@ static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args,
/// (Linux).
static void addUbsanRTLinux(const ToolChain &TC, const ArgList &Args,
ArgStringList &CmdArgs) {
- if (!Args.hasArg(options::OPT_fcatch_undefined_behavior))
- return;
if (!Args.hasArg(options::OPT_shared)) {
// LibUbsan is "libclang_rt.ubsan-<ArchName>.a" in the Linux library
// resource directory.
@@ -2370,7 +2501,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-ffreestanding");
// Forward -f (flag) options which we can pass directly.
- Args.AddLastArg(CmdArgs, options::OPT_fcatch_undefined_behavior);
Args.AddLastArg(CmdArgs, options::OPT_femit_all_decls);
Args.AddLastArg(CmdArgs, options::OPT_fheinous_gnu_extensions);
Args.AddLastArg(CmdArgs, options::OPT_flimit_debug_info);
@@ -2380,6 +2510,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_show_template_tree);
Args.AddLastArg(CmdArgs, options::OPT_fno_elide_type);
+ SanitizerArgs Sanitize = getSanitizerArgs(D, Args);
+ Sanitize.addArgs(Args, CmdArgs);
+
// Report and error for -faltivec on anything other then PowerPC.
if (const Arg *A = Args.getLastArg(options::OPT_faltivec))
if (!(getToolChain().getTriple().getArch() == llvm::Triple::ppc ||
@@ -2390,14 +2523,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (getToolChain().SupportsProfiling())
Args.AddLastArg(CmdArgs, options::OPT_pg);
- if (Args.hasFlag(options::OPT_faddress_sanitizer,
- options::OPT_fno_address_sanitizer, false))
- CmdArgs.push_back("-faddress-sanitizer");
-
- if (Args.hasFlag(options::OPT_fthread_sanitizer,
- options::OPT_fno_thread_sanitizer, false))
- CmdArgs.push_back("-fthread-sanitizer");
-
// -flax-vector-conversions is default.
if (!Args.hasFlag(options::OPT_flax_vector_conversions,
options::OPT_fno_lax_vector_conversions))
@@ -2544,9 +2669,20 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// -frtti is default.
if (!Args.hasFlag(options::OPT_frtti, options::OPT_fno_rtti) ||
- KernelOrKext)
+ KernelOrKext) {
CmdArgs.push_back("-fno-rtti");
+ // -fno-rtti cannot usefully be combined with -fsanitize=vptr.
+ if (Sanitize.Kind & SanitizerArgs::Vptr) {
+ llvm::StringRef NoRttiArg =
+ Args.getLastArg(options::OPT_mkernel,
+ options::OPT_fapple_kext,
+ options::OPT_fno_rtti)->getOption().getName();
+ D.Diag(diag::err_drv_argument_not_allowed_with)
+ << "-fsanitize=vptr" << NoRttiArg;
+ }
+ }
+
// -fshort-enums=0 is default for all architectures except Hexagon.
if (Args.hasFlag(options::OPT_fshort_enums,
options::OPT_fno_short_enums,
@@ -5972,8 +6108,11 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs);
+ SanitizerArgs Sanitize = getSanitizerArgs(D, Args);
+
// Call this before we add the C++ ABI library.
- addUbsanRTLinux(getToolChain(), Args, CmdArgs);
+ if (Sanitize.needsUbsanRt())
+ addUbsanRTLinux(getToolChain(), Args, CmdArgs);
if (D.CCCIsCXX &&
!Args.hasArg(options::OPT_nostdlib) &&
@@ -5989,8 +6128,10 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
}
// Call this before we add the C run-time.
- addAsanRTLinux(getToolChain(), Args, CmdArgs);
- addTsanRTLinux(getToolChain(), Args, CmdArgs);
+ if (Sanitize.needsAsanRt())
+ addAsanRTLinux(getToolChain(), Args, CmdArgs);
+ if (Sanitize.needsTsanRt())
+ addTsanRTLinux(getToolChain(), Args, CmdArgs);
if (!Args.hasArg(options::OPT_nostdlib)) {
if (!Args.hasArg(options::OPT_nodefaultlibs)) {