blob: 676d5b3701b40692255cd3285b87d9ae439b7249 [file] [log] [blame]
Colin Cross16b23492016-01-06 14:41:07 -08001// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cc
16
17import (
18 "fmt"
19 "strings"
20
21 "github.com/google/blueprint"
22
23 "android/soong/common"
24)
25
26type sanitizerType int
27
28func init() {
29 pctx.StaticVariable("clangAsanLibDir", "${clangPath}/lib64/clang/3.8/lib/linux")
30}
31
32const (
33 asan sanitizerType = iota + 1
34 tsan
35)
36
37func (t sanitizerType) String() string {
38 switch t {
39 case asan:
40 return "asan"
41 case tsan:
42 return "tsan"
43 default:
44 panic(fmt.Errorf("unknown sanitizerType %d", t))
45 }
46}
47
48type SanitizeProperties struct {
49 // enable AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer
50 Sanitize struct {
51 Never bool `android:"arch_variant"`
52
53 // main sanitizers
54 Address bool `android:"arch_variant"`
55 Thread bool `android:"arch_variant"`
56
57 // local sanitizers
58 Undefined bool `android:"arch_variant"`
59 All_undefined bool `android:"arch_variant"`
60 Misc_undefined []string `android:"arch_variant"`
61 Coverage bool `android:"arch_variant"`
62
63 // value to pass to -fsantitize-recover=
64 Recover []string
65
66 // value to pass to -fsanitize-blacklist
67 Blacklist *string
68 } `android:"arch_variant"`
69
70 SanitizerEnabled bool `blueprint:"mutated"`
71 SanitizeDep bool `blueprint:"mutated"`
72}
73
74type sanitize struct {
75 Properties SanitizeProperties
76}
77
78func (sanitize *sanitize) props() []interface{} {
79 return []interface{}{&sanitize.Properties}
80}
81
82func (sanitize *sanitize) begin(ctx BaseModuleContext) {
83 // Don't apply sanitizers to NDK code.
84 if ctx.sdk() {
85 sanitize.Properties.Sanitize.Never = true
86 }
87
88 // Never always wins.
89 if sanitize.Properties.Sanitize.Never {
90 return
91 }
92
93 if ctx.ContainsProperty("sanitize") {
94 sanitize.Properties.SanitizerEnabled = true
95 }
96
97 var globalSanitizers []string
98 if ctx.clang() {
99 if ctx.Host() {
100 globalSanitizers = ctx.AConfig().SanitizeHost()
101 } else {
102 globalSanitizers = ctx.AConfig().SanitizeDevice()
103 }
104 }
105
106 // The sanitizer specified by the environment wins over the module.
107 if len(globalSanitizers) > 0 {
108 // wipe the enabled sanitizers
109 sanitize.Properties = SanitizeProperties{}
110 var found bool
111 if found, globalSanitizers = removeFromList("undefined", globalSanitizers); found {
112 sanitize.Properties.Sanitize.All_undefined = true
113 } else if found, globalSanitizers = removeFromList("default-ub", globalSanitizers); found {
114 sanitize.Properties.Sanitize.Undefined = true
115 }
116
117 if found, globalSanitizers = removeFromList("address", globalSanitizers); found {
118 sanitize.Properties.Sanitize.Address = true
119 }
120
121 if found, globalSanitizers = removeFromList("thread", globalSanitizers); found {
122 sanitize.Properties.Sanitize.Thread = true
123 }
124
125 if found, globalSanitizers = removeFromList("coverage", globalSanitizers); found {
126 sanitize.Properties.Sanitize.Coverage = true
127 }
128
129 if len(globalSanitizers) > 0 {
130 ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
131 }
132 sanitize.Properties.SanitizerEnabled = true
133 }
134
135 if !ctx.toolchain().Is64Bit() && sanitize.Properties.Sanitize.Thread {
136 // TSAN is not supported on 32-bit architectures
137 sanitize.Properties.Sanitize.Thread = false
138 // TODO(ccross): error for compile_multilib = "32"?
139 }
140
141 if sanitize.Properties.Sanitize.Coverage {
142 if !sanitize.Properties.Sanitize.Address {
143 ctx.ModuleErrorf(`Use of "coverage" also requires "address"`)
144 }
145 }
146}
147
148func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps {
149 if !sanitize.Properties.SanitizerEnabled { // || c.static() {
150 return deps
151 }
152
153 if ctx.Device() {
154 deps.SharedLibs = append(deps.SharedLibs, "libdl")
155 if sanitize.Properties.Sanitize.Address {
156 deps.StaticLibs = append(deps.StaticLibs, "libasan")
157 }
158 }
159
160 return deps
161}
162
163func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
164 if !sanitize.Properties.SanitizerEnabled {
165 return flags
166 }
167
168 if !ctx.clang() {
169 ctx.ModuleErrorf("Use of sanitizers requires clang")
170 }
171
172 var sanitizers []string
173
174 if sanitize.Properties.Sanitize.All_undefined {
175 sanitizers = append(sanitizers, "undefined")
176 if ctx.Device() {
177 ctx.ModuleErrorf("ubsan is not yet supported on the device")
178 }
179 } else {
180 if sanitize.Properties.Sanitize.Undefined {
181 sanitizers = append(sanitizers,
182 "bool",
183 "integer-divide-by-zero",
184 "return",
185 "returns-nonnull-attribute",
186 "shift-exponent",
187 "unreachable",
188 "vla-bound",
189 // TODO(danalbert): The following checks currently have compiler performance issues.
190 //"alignment",
191 //"bounds",
192 //"enum",
193 //"float-cast-overflow",
194 //"float-divide-by-zero",
195 //"nonnull-attribute",
196 //"null",
197 //"shift-base",
198 //"signed-integer-overflow",
199 // TODO(danalbert): Fix UB in libc++'s __tree so we can turn this on.
200 // https://llvm.org/PR19302
201 // http://reviews.llvm.org/D6974
202 // "object-size",
203 )
204 }
205 sanitizers = append(sanitizers, sanitize.Properties.Sanitize.Misc_undefined...)
206 }
207
208 if sanitize.Properties.Sanitize.Address {
209 if ctx.Arch().ArchType == common.Arm {
210 // Frame pointer based unwinder in ASan requires ARM frame setup.
211 // TODO: put in flags?
212 flags.RequiredInstructionSet = "arm"
213 }
214 flags.CFlags = append(flags.CFlags, "-fno-omit-frame-pointer")
215 flags.LdFlags = append(flags.LdFlags, "-Wl,-u,__asan_preinit")
216
217 // ASan runtime library must be the first in the link order.
218 runtimeLibrary := ctx.toolchain().AddressSanitizerRuntimeLibrary()
219 if runtimeLibrary != "" {
220 flags.libFlags = append([]string{"${clangAsanLibDir}/" + runtimeLibrary}, flags.libFlags...)
221 }
222 if ctx.Host() {
223 // -nodefaultlibs (provided with libc++) prevents the driver from linking
224 // libraries needed with -fsanitize=address. http://b/18650275 (WAI)
225 flags.LdFlags = append(flags.LdFlags, "-lm", "-lpthread")
226 flags.LdFlags = append(flags.LdFlags, "-Wl,--no-as-needed")
227 } else {
228 flags.CFlags = append(flags.CFlags, "-mllvm", "-asan-globals=0")
229 flags.DynamicLinker = "/system/bin/linker_asan"
230 if flags.Toolchain.Is64Bit() {
231 flags.DynamicLinker += "64"
232 }
233 }
234 sanitizers = append(sanitizers, "address")
235 }
236
237 if sanitize.Properties.Sanitize.Coverage {
238 flags.CFlags = append(flags.CFlags, "-fsanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp")
239 }
240
241 if sanitize.Properties.Sanitize.Recover != nil {
242 flags.CFlags = append(flags.CFlags, "-fsanitize-recover="+
243 strings.Join(sanitize.Properties.Sanitize.Recover, ","))
244 }
245
246 if len(sanitizers) > 0 {
247 sanitizeArg := "-fsanitize=" + strings.Join(sanitizers, ",")
248 flags.CFlags = append(flags.CFlags, sanitizeArg)
249 if ctx.Host() {
250 flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover=all")
251 flags.LdFlags = append(flags.LdFlags, sanitizeArg)
252 flags.LdFlags = append(flags.LdFlags, "-lrt", "-ldl")
253 } else {
254 if !sanitize.Properties.Sanitize.Address {
255 flags.CFlags = append(flags.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
256 }
257 }
258 }
259
260 blacklist := common.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blacklist)
261 if blacklist.Valid() {
262 flags.CFlags = append(flags.CFlags, "-fsanitize-blacklist="+blacklist.String())
263 flags.CFlagsDeps = append(flags.CFlagsDeps, blacklist.Path())
264 }
265
266 return flags
267}
268
269func (sanitize *sanitize) Sanitizer(t sanitizerType) bool {
270 if sanitize == nil {
271 return false
272 }
273
274 switch t {
275 case asan:
276 return sanitize.Properties.Sanitize.Address
277 case tsan:
278 return sanitize.Properties.Sanitize.Thread
279 default:
280 panic(fmt.Errorf("unknown sanitizerType %d", t))
281 }
282}
283
284func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
285 switch t {
286 case asan:
287 sanitize.Properties.Sanitize.Address = b
288 case tsan:
289 sanitize.Properties.Sanitize.Thread = b
290 default:
291 panic(fmt.Errorf("unknown sanitizerType %d", t))
292 }
293 if b {
294 sanitize.Properties.SanitizerEnabled = true
295 }
296}
297
298// Propagate asan requirements down from binaries
299func sanitizerDepsMutator(t sanitizerType) func(common.AndroidTopDownMutatorContext) {
300 return func(mctx common.AndroidTopDownMutatorContext) {
301 if c, ok := mctx.Module().(*Module); ok && c.sanitize.Sanitizer(t) {
302 mctx.VisitDepsDepthFirst(func(module blueprint.Module) {
303 if d, ok := mctx.Module().(*Module); ok && c.sanitize != nil &&
304 !c.sanitize.Properties.Sanitize.Never {
305 d.sanitize.Properties.SanitizeDep = true
306 }
307 })
308 }
309 }
310}
311
312// Create asan variants for modules that need them
313func sanitizerMutator(t sanitizerType) func(common.AndroidBottomUpMutatorContext) {
314 return func(mctx common.AndroidBottomUpMutatorContext) {
315 if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
316 if d, ok := c.linker.(baseLinkerInterface); ok && d.isDependencyRoot() && c.sanitize.Sanitizer(t) {
317 mctx.CreateVariations(t.String())
318 } else if c.sanitize.Properties.SanitizeDep {
319 modules := mctx.CreateVariations("", t.String())
320 modules[0].(*Module).sanitize.SetSanitizer(t, false)
321 modules[1].(*Module).sanitize.SetSanitizer(t, true)
322 modules[1].(*Module).appendVariantName("_" + t.String())
323 modules[0].(*Module).sanitize.Properties.SanitizeDep = false
324 modules[1].(*Module).sanitize.Properties.SanitizeDep = false
325 }
326 c.sanitize.Properties.SanitizeDep = false
327 }
328 }
329}