blob: 71813c5511e108ede0ece04f3a79c31c82ff3fa3 [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
12#include "clang/Driver/ArgList.h"
13
14namespace clang {
15namespace driver {
16
17class SanitizerArgs {
18 /// Assign ordinals to sanitizer flags. We'll use the ordinal values as
19 /// bit positions within \c Kind.
20 enum SanitizeOrdinal {
21#define SANITIZER(NAME, ID) SO_##ID,
22#include "clang/Basic/Sanitizers.def"
23 SO_Count
24 };
25
26 /// Bugs to catch at runtime.
27 enum SanitizeKind {
28#define SANITIZER(NAME, ID) ID = 1 << SO_##ID,
29#define SANITIZER_GROUP(NAME, ID, ALIAS) ID = ALIAS,
30#include "clang/Basic/Sanitizers.def"
Alexey Samsonov4d1a6e42012-11-29 22:36:21 +000031 NeedsAsanRt = AddressFull,
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000032 NeedsTsanRt = Thread,
Evgeniy Stepanov09ccf392012-12-03 13:20:43 +000033 NeedsMsanRt = Memory,
Will Dietzb8540362012-11-27 15:01:55 +000034 NeedsUbsanRt = (Undefined & ~Bounds) | Integer
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000035 };
36 unsigned Kind;
Alexey Samsonov91ecfa62012-12-03 19:12:58 +000037 std::string BlacklistFile;
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000038
39 public:
Alexey Samsonov91ecfa62012-12-03 19:12:58 +000040 SanitizerArgs() : Kind(0), BlacklistFile("") {}
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000041 /// Parses the sanitizer arguments from an argument list.
42 SanitizerArgs(const Driver &D, const ArgList &Args);
43
44 bool needsAsanRt() const { return Kind & NeedsAsanRt; }
45 bool needsTsanRt() const { return Kind & NeedsTsanRt; }
Evgeniy Stepanov09ccf392012-12-03 13:20:43 +000046 bool needsMsanRt() const { return Kind & NeedsMsanRt; }
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000047 bool needsUbsanRt() const { return Kind & NeedsUbsanRt; }
48
49 bool sanitizesVptr() const { return Kind & Vptr; }
Alexey Samsonov4d1a6e42012-11-29 22:36:21 +000050
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000051 void addArgs(const ArgList &Args, ArgStringList &CmdArgs) const {
52 if (!Kind)
53 return;
54 llvm::SmallString<256> SanitizeOpt("-fsanitize=");
55#define SANITIZER(NAME, ID) \
56 if (Kind & ID) \
57 SanitizeOpt += NAME ",";
58#include "clang/Basic/Sanitizers.def"
59 SanitizeOpt.pop_back();
60 CmdArgs.push_back(Args.MakeArgString(SanitizeOpt));
Alexey Samsonov91ecfa62012-12-03 19:12:58 +000061 if (!BlacklistFile.empty()) {
62 llvm::SmallString<64> BlacklistOpt("-fsanitize-blacklist=");
63 BlacklistOpt += BlacklistFile;
64 CmdArgs.push_back(Args.MakeArgString(BlacklistOpt));
65 }
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000066 }
67
68 private:
69 /// Parse a single value from a -fsanitize= or -fno-sanitize= value list.
70 /// Returns a member of the \c SanitizeKind enumeration, or \c 0 if \p Value
71 /// is not known.
72 static unsigned parse(const char *Value) {
73 return llvm::StringSwitch<SanitizeKind>(Value)
74#define SANITIZER(NAME, ID) .Case(NAME, ID)
75#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID)
76#include "clang/Basic/Sanitizers.def"
77 .Default(SanitizeKind());
78 }
79
80 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
81 /// invalid components.
Alexey Samsonov3325b162012-11-28 17:34:24 +000082 static unsigned parse(const Driver &D, const Arg *A, bool DiagnoseErrors) {
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000083 unsigned Kind = 0;
84 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
85 if (unsigned K = parse(A->getValue(I)))
86 Kind |= K;
Alexey Samsonov3325b162012-11-28 17:34:24 +000087 else if (DiagnoseErrors)
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000088 D.Diag(diag::err_drv_unsupported_option_argument)
89 << A->getOption().getName() << A->getValue(I);
90 }
91 return Kind;
92 }
93
Alexey Samsonov3325b162012-11-28 17:34:24 +000094 /// Parse a single flag of the form -f[no]sanitize=, or
95 /// -f*-sanitizer. Sets the masks defining required change of Kind value.
96 /// Returns true if the flag was parsed successfully.
97 static bool parse(const Driver &D, const ArgList &Args, const Arg *A,
98 unsigned &Add, unsigned &Remove, bool DiagnoseErrors) {
99 Add = 0;
100 Remove = 0;
101 const char *DeprecatedReplacement = 0;
102 if (A->getOption().matches(options::OPT_faddress_sanitizer)) {
103 Add = Address;
104 DeprecatedReplacement = "-fsanitize=address";
105 } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) {
106 Remove = Address;
107 DeprecatedReplacement = "-fno-sanitize=address";
108 } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) {
109 Add = Thread;
110 DeprecatedReplacement = "-fsanitize=thread";
111 } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) {
112 Remove = Thread;
113 DeprecatedReplacement = "-fno-sanitize=thread";
114 } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) {
115 Add = Undefined;
116 DeprecatedReplacement = "-fsanitize=undefined";
117 } else if (A->getOption().matches(options::OPT_fbounds_checking) ||
118 A->getOption().matches(options::OPT_fbounds_checking_EQ)) {
119 Add = Bounds;
120 DeprecatedReplacement = "-fsanitize=bounds";
121 } else if (A->getOption().matches(options::OPT_fsanitize_EQ)) {
122 Add = parse(D, A, DiagnoseErrors);
123 } else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) {
124 Remove = parse(D, A, DiagnoseErrors);
125 } else {
126 // Flag is not relevant to sanitizers.
127 return false;
128 }
129 // If this is a deprecated synonym, produce a warning directing users
130 // towards the new spelling.
131 if (DeprecatedReplacement && DiagnoseErrors)
132 D.Diag(diag::warn_drv_deprecated_arg)
133 << A->getAsString(Args) << DeprecatedReplacement;
134 return true;
135 }
136
137 /// Produce an argument string from ArgList \p Args, which shows how it
138 /// provides a sanitizer kind in \p Mask. For example, the argument list
139 /// "-fsanitize=thread,vptr -faddress-sanitizer" with mask \c NeedsUbsanRt
140 /// would produce "-fsanitize=vptr".
141 static std::string lastArgumentForKind(const Driver &D, const ArgList &Args,
142 unsigned Kind) {
143 for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend();
144 I != E; ++I) {
145 unsigned Add, Remove;
146 if (parse(D, Args, *I, Add, Remove, false) &&
147 (Add & Kind))
148 return describeSanitizeArg(Args, *I, Kind);
149 Kind &= ~Remove;
150 }
151 llvm_unreachable("arg list didn't provide expected value");
152 }
153
Alexey Samsonovbb1071c2012-11-06 15:09:03 +0000154 /// Produce an argument string from argument \p A, which shows how it provides
155 /// a value in \p Mask. For instance, the argument
156 /// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce
157 /// "-fsanitize=alignment".
158 static std::string describeSanitizeArg(const ArgList &Args, const Arg *A,
159 unsigned Mask) {
160 if (!A->getOption().matches(options::OPT_fsanitize_EQ))
161 return A->getAsString(Args);
162
163 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I)
164 if (parse(A->getValue(I)) & Mask)
165 return std::string("-fsanitize=") + A->getValue(I);
166
167 llvm_unreachable("arg didn't provide expected value");
168 }
169};
170
171} // namespace driver
172} // namespace clang
173
174#endif // CLANG_LIB_DRIVER_SANITIZERARGS_H_