blob: debdaebc0025305ae1da9e0e2e641396225e0033 [file] [log] [blame]
Alexey Samsonovbb1071c2012-11-06 15:09:03 +00001//===--- SanitizerArgs.h - Arguments for sanitizer tools -------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#ifndef CLANG_LIB_DRIVER_SANITIZERARGS_H_
10#define CLANG_LIB_DRIVER_SANITIZERARGS_H_
11
Chandler Carruth55fc8732012-12-04 09:13:33 +000012#include "clang/Driver/Arg.h"
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000013#include "clang/Driver/ArgList.h"
Chandler Carruth55fc8732012-12-04 09:13:33 +000014#include "clang/Driver/Driver.h"
15#include "clang/Driver/DriverDiagnostic.h"
16#include "clang/Driver/Options.h"
17#include "llvm/ADT/StringSwitch.h"
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000018
19namespace clang {
20namespace driver {
21
22class SanitizerArgs {
23 /// Assign ordinals to sanitizer flags. We'll use the ordinal values as
24 /// bit positions within \c Kind.
25 enum SanitizeOrdinal {
26#define SANITIZER(NAME, ID) SO_##ID,
27#include "clang/Basic/Sanitizers.def"
28 SO_Count
29 };
30
31 /// Bugs to catch at runtime.
32 enum SanitizeKind {
33#define SANITIZER(NAME, ID) ID = 1 << SO_##ID,
34#define SANITIZER_GROUP(NAME, ID, ALIAS) ID = ALIAS,
35#include "clang/Basic/Sanitizers.def"
Alexey Samsonov4d1a6e42012-11-29 22:36:21 +000036 NeedsAsanRt = AddressFull,
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000037 NeedsTsanRt = Thread,
Evgeniy Stepanov09ccf392012-12-03 13:20:43 +000038 NeedsMsanRt = Memory,
Will Dietzb8540362012-11-27 15:01:55 +000039 NeedsUbsanRt = (Undefined & ~Bounds) | Integer
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000040 };
41 unsigned Kind;
Alexey Samsonov91ecfa62012-12-03 19:12:58 +000042 std::string BlacklistFile;
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000043
44 public:
Alexey Samsonov91ecfa62012-12-03 19:12:58 +000045 SanitizerArgs() : Kind(0), BlacklistFile("") {}
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000046 /// Parses the sanitizer arguments from an argument list.
47 SanitizerArgs(const Driver &D, const ArgList &Args);
48
49 bool needsAsanRt() const { return Kind & NeedsAsanRt; }
50 bool needsTsanRt() const { return Kind & NeedsTsanRt; }
Evgeniy Stepanov09ccf392012-12-03 13:20:43 +000051 bool needsMsanRt() const { return Kind & NeedsMsanRt; }
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000052 bool needsUbsanRt() const { return Kind & NeedsUbsanRt; }
53
54 bool sanitizesVptr() const { return Kind & Vptr; }
Alexey Samsonov4d1a6e42012-11-29 22:36:21 +000055
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000056 void addArgs(const ArgList &Args, ArgStringList &CmdArgs) const {
57 if (!Kind)
58 return;
59 llvm::SmallString<256> SanitizeOpt("-fsanitize=");
60#define SANITIZER(NAME, ID) \
61 if (Kind & ID) \
62 SanitizeOpt += NAME ",";
63#include "clang/Basic/Sanitizers.def"
64 SanitizeOpt.pop_back();
65 CmdArgs.push_back(Args.MakeArgString(SanitizeOpt));
Alexey Samsonov91ecfa62012-12-03 19:12:58 +000066 if (!BlacklistFile.empty()) {
67 llvm::SmallString<64> BlacklistOpt("-fsanitize-blacklist=");
68 BlacklistOpt += BlacklistFile;
69 CmdArgs.push_back(Args.MakeArgString(BlacklistOpt));
70 }
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000071 }
72
73 private:
74 /// Parse a single value from a -fsanitize= or -fno-sanitize= value list.
75 /// Returns a member of the \c SanitizeKind enumeration, or \c 0 if \p Value
76 /// is not known.
77 static unsigned parse(const char *Value) {
78 return llvm::StringSwitch<SanitizeKind>(Value)
79#define SANITIZER(NAME, ID) .Case(NAME, ID)
80#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID)
81#include "clang/Basic/Sanitizers.def"
82 .Default(SanitizeKind());
83 }
84
85 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
86 /// invalid components.
Alexey Samsonov3325b162012-11-28 17:34:24 +000087 static unsigned parse(const Driver &D, const Arg *A, bool DiagnoseErrors) {
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000088 unsigned Kind = 0;
89 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
90 if (unsigned K = parse(A->getValue(I)))
91 Kind |= K;
Alexey Samsonov3325b162012-11-28 17:34:24 +000092 else if (DiagnoseErrors)
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000093 D.Diag(diag::err_drv_unsupported_option_argument)
94 << A->getOption().getName() << A->getValue(I);
95 }
96 return Kind;
97 }
98
Alexey Samsonov3325b162012-11-28 17:34:24 +000099 /// Parse a single flag of the form -f[no]sanitize=, or
100 /// -f*-sanitizer. Sets the masks defining required change of Kind value.
101 /// Returns true if the flag was parsed successfully.
102 static bool parse(const Driver &D, const ArgList &Args, const Arg *A,
103 unsigned &Add, unsigned &Remove, bool DiagnoseErrors) {
104 Add = 0;
105 Remove = 0;
106 const char *DeprecatedReplacement = 0;
107 if (A->getOption().matches(options::OPT_faddress_sanitizer)) {
108 Add = Address;
109 DeprecatedReplacement = "-fsanitize=address";
110 } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) {
111 Remove = Address;
112 DeprecatedReplacement = "-fno-sanitize=address";
113 } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) {
114 Add = Thread;
115 DeprecatedReplacement = "-fsanitize=thread";
116 } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) {
117 Remove = Thread;
118 DeprecatedReplacement = "-fno-sanitize=thread";
119 } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) {
120 Add = Undefined;
121 DeprecatedReplacement = "-fsanitize=undefined";
122 } else if (A->getOption().matches(options::OPT_fbounds_checking) ||
123 A->getOption().matches(options::OPT_fbounds_checking_EQ)) {
124 Add = Bounds;
125 DeprecatedReplacement = "-fsanitize=bounds";
126 } else if (A->getOption().matches(options::OPT_fsanitize_EQ)) {
127 Add = parse(D, A, DiagnoseErrors);
128 } else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) {
129 Remove = parse(D, A, DiagnoseErrors);
130 } else {
131 // Flag is not relevant to sanitizers.
132 return false;
133 }
134 // If this is a deprecated synonym, produce a warning directing users
135 // towards the new spelling.
136 if (DeprecatedReplacement && DiagnoseErrors)
137 D.Diag(diag::warn_drv_deprecated_arg)
138 << A->getAsString(Args) << DeprecatedReplacement;
139 return true;
140 }
141
142 /// Produce an argument string from ArgList \p Args, which shows how it
143 /// provides a sanitizer kind in \p Mask. For example, the argument list
144 /// "-fsanitize=thread,vptr -faddress-sanitizer" with mask \c NeedsUbsanRt
145 /// would produce "-fsanitize=vptr".
146 static std::string lastArgumentForKind(const Driver &D, const ArgList &Args,
147 unsigned Kind) {
148 for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend();
149 I != E; ++I) {
150 unsigned Add, Remove;
151 if (parse(D, Args, *I, Add, Remove, false) &&
152 (Add & Kind))
153 return describeSanitizeArg(Args, *I, Kind);
154 Kind &= ~Remove;
155 }
156 llvm_unreachable("arg list didn't provide expected value");
157 }
158
Alexey Samsonovbb1071c2012-11-06 15:09:03 +0000159 /// Produce an argument string from argument \p A, which shows how it provides
160 /// a value in \p Mask. For instance, the argument
161 /// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce
162 /// "-fsanitize=alignment".
163 static std::string describeSanitizeArg(const ArgList &Args, const Arg *A,
164 unsigned Mask) {
165 if (!A->getOption().matches(options::OPT_fsanitize_EQ))
166 return A->getAsString(Args);
167
168 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I)
169 if (parse(A->getValue(I)) & Mask)
170 return std::string("-fsanitize=") + A->getValue(I);
171
172 llvm_unreachable("arg didn't provide expected value");
173 }
174};
175
176} // namespace driver
177} // namespace clang
178
179#endif // CLANG_LIB_DRIVER_SANITIZERARGS_H_