blob: c7d1ea68dd477db36ac520902041f92a4ac8fab7 [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;
37
38 public:
39 SanitizerArgs() : Kind(0) {}
40 /// Parses the sanitizer arguments from an argument list.
41 SanitizerArgs(const Driver &D, const ArgList &Args);
42
43 bool needsAsanRt() const { return Kind & NeedsAsanRt; }
44 bool needsTsanRt() const { return Kind & NeedsTsanRt; }
Evgeniy Stepanov09ccf392012-12-03 13:20:43 +000045 bool needsMsanRt() const { return Kind & NeedsMsanRt; }
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000046 bool needsUbsanRt() const { return Kind & NeedsUbsanRt; }
47
48 bool sanitizesVptr() const { return Kind & Vptr; }
Alexey Samsonov4d1a6e42012-11-29 22:36:21 +000049
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000050 void addArgs(const ArgList &Args, ArgStringList &CmdArgs) const {
51 if (!Kind)
52 return;
53 llvm::SmallString<256> SanitizeOpt("-fsanitize=");
54#define SANITIZER(NAME, ID) \
55 if (Kind & ID) \
56 SanitizeOpt += NAME ",";
57#include "clang/Basic/Sanitizers.def"
58 SanitizeOpt.pop_back();
59 CmdArgs.push_back(Args.MakeArgString(SanitizeOpt));
60 }
61
62 private:
63 /// Parse a single value from a -fsanitize= or -fno-sanitize= value list.
64 /// Returns a member of the \c SanitizeKind enumeration, or \c 0 if \p Value
65 /// is not known.
66 static unsigned parse(const char *Value) {
67 return llvm::StringSwitch<SanitizeKind>(Value)
68#define SANITIZER(NAME, ID) .Case(NAME, ID)
69#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID)
70#include "clang/Basic/Sanitizers.def"
71 .Default(SanitizeKind());
72 }
73
74 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
75 /// invalid components.
Alexey Samsonov3325b162012-11-28 17:34:24 +000076 static unsigned parse(const Driver &D, const Arg *A, bool DiagnoseErrors) {
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000077 unsigned Kind = 0;
78 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
79 if (unsigned K = parse(A->getValue(I)))
80 Kind |= K;
Alexey Samsonov3325b162012-11-28 17:34:24 +000081 else if (DiagnoseErrors)
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000082 D.Diag(diag::err_drv_unsupported_option_argument)
83 << A->getOption().getName() << A->getValue(I);
84 }
85 return Kind;
86 }
87
Alexey Samsonov3325b162012-11-28 17:34:24 +000088 /// Parse a single flag of the form -f[no]sanitize=, or
89 /// -f*-sanitizer. Sets the masks defining required change of Kind value.
90 /// Returns true if the flag was parsed successfully.
91 static bool parse(const Driver &D, const ArgList &Args, const Arg *A,
92 unsigned &Add, unsigned &Remove, bool DiagnoseErrors) {
93 Add = 0;
94 Remove = 0;
95 const char *DeprecatedReplacement = 0;
96 if (A->getOption().matches(options::OPT_faddress_sanitizer)) {
97 Add = Address;
98 DeprecatedReplacement = "-fsanitize=address";
99 } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) {
100 Remove = Address;
101 DeprecatedReplacement = "-fno-sanitize=address";
102 } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) {
103 Add = Thread;
104 DeprecatedReplacement = "-fsanitize=thread";
105 } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) {
106 Remove = Thread;
107 DeprecatedReplacement = "-fno-sanitize=thread";
108 } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) {
109 Add = Undefined;
110 DeprecatedReplacement = "-fsanitize=undefined";
111 } else if (A->getOption().matches(options::OPT_fbounds_checking) ||
112 A->getOption().matches(options::OPT_fbounds_checking_EQ)) {
113 Add = Bounds;
114 DeprecatedReplacement = "-fsanitize=bounds";
115 } else if (A->getOption().matches(options::OPT_fsanitize_EQ)) {
116 Add = parse(D, A, DiagnoseErrors);
117 } else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) {
118 Remove = parse(D, A, DiagnoseErrors);
119 } else {
120 // Flag is not relevant to sanitizers.
121 return false;
122 }
123 // If this is a deprecated synonym, produce a warning directing users
124 // towards the new spelling.
125 if (DeprecatedReplacement && DiagnoseErrors)
126 D.Diag(diag::warn_drv_deprecated_arg)
127 << A->getAsString(Args) << DeprecatedReplacement;
128 return true;
129 }
130
131 /// Produce an argument string from ArgList \p Args, which shows how it
132 /// provides a sanitizer kind in \p Mask. For example, the argument list
133 /// "-fsanitize=thread,vptr -faddress-sanitizer" with mask \c NeedsUbsanRt
134 /// would produce "-fsanitize=vptr".
135 static std::string lastArgumentForKind(const Driver &D, const ArgList &Args,
136 unsigned Kind) {
137 for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend();
138 I != E; ++I) {
139 unsigned Add, Remove;
140 if (parse(D, Args, *I, Add, Remove, false) &&
141 (Add & Kind))
142 return describeSanitizeArg(Args, *I, Kind);
143 Kind &= ~Remove;
144 }
145 llvm_unreachable("arg list didn't provide expected value");
146 }
147
Alexey Samsonovbb1071c2012-11-06 15:09:03 +0000148 /// Produce an argument string from argument \p A, which shows how it provides
149 /// a value in \p Mask. For instance, the argument
150 /// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce
151 /// "-fsanitize=alignment".
152 static std::string describeSanitizeArg(const ArgList &Args, const Arg *A,
153 unsigned Mask) {
154 if (!A->getOption().matches(options::OPT_fsanitize_EQ))
155 return A->getAsString(Args);
156
157 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I)
158 if (parse(A->getValue(I)) & Mask)
159 return std::string("-fsanitize=") + A->getValue(I);
160
161 llvm_unreachable("arg didn't provide expected value");
162 }
163};
164
165} // namespace driver
166} // namespace clang
167
168#endif // CLANG_LIB_DRIVER_SANITIZERARGS_H_