mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-05 12:51:16 +00:00
[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:
parent
eb2378b70c
commit
4c12c6cf3b
@ -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
|
||||
|
@ -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
|
||||
|
@ -29,3 +29,7 @@ void SanitizerSet::set(SanitizerKind K, bool Value) {
|
||||
void SanitizerSet::clear() {
|
||||
Kinds = 0;
|
||||
}
|
||||
|
||||
bool SanitizerSet::empty() const {
|
||||
return Kinds == 0;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user