blob: 7e1c55a5089694ea07cdeb45982a9b40e4070c42 [file] [log] [blame]
Ivan Lozano6cd99e62020-02-11 08:24:25 -05001// Copyright 2020 The Android Open Source Project
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 rust
16
17import (
hamzehc651b522021-04-29 12:50:47 -070018 "path/filepath"
19 "sort"
20 "strings"
21
Ivan Lozano6cd99e62020-02-11 08:24:25 -050022 "android/soong/android"
23 "android/soong/cc"
24 "android/soong/rust/config"
25)
26
27func init() {
28 android.RegisterModuleType("rust_fuzz", RustFuzzFactory)
hamzehc651b522021-04-29 12:50:47 -070029 android.RegisterSingletonType("rust_fuzz_packaging", rustFuzzPackagingFactory)
Ivan Lozano6cd99e62020-02-11 08:24:25 -050030}
31
32type fuzzDecorator struct {
33 *binaryDecorator
34
35 Properties cc.FuzzProperties
36 dictionary android.Path
37 corpus android.Paths
38 corpusIntermediateDir android.Path
39 config android.Path
40 data android.Paths
41 dataIntermediateDir android.Path
42}
43
44var _ compiler = (*binaryDecorator)(nil)
45
46// rust_binary produces a binary that is runnable on a device.
47func RustFuzzFactory() android.Module {
48 module, _ := NewRustFuzz(android.HostAndDeviceSupported)
49 return module.Init()
50}
51
52func NewRustFuzz(hod android.HostOrDeviceSupported) (*Module, *fuzzDecorator) {
53 module, binary := NewRustBinary(hod)
54 fuzz := &fuzzDecorator{
55 binaryDecorator: binary,
56 }
57
58 // Change the defaults for the binaryDecorator's baseCompiler
59 fuzz.binaryDecorator.baseCompiler.dir = "fuzz"
60 fuzz.binaryDecorator.baseCompiler.dir64 = "fuzz"
61 fuzz.binaryDecorator.baseCompiler.location = InstallInData
62 module.sanitize.SetSanitizer(cc.Fuzzer, true)
63 module.compiler = fuzz
64 return module, fuzz
65}
66
67func (fuzzer *fuzzDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
68 flags = fuzzer.binaryDecorator.compilerFlags(ctx, flags)
69
70 // `../lib` for installed fuzz targets (both host and device), and `./lib` for fuzz target packages.
71 flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
72 flags.LinkFlags = append(flags.LinkFlags, `-Wl,-rpath,\$$ORIGIN/lib`)
73
74 return flags
75}
76
77func (fuzzer *fuzzDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
Liz Kammer9c210862021-04-12 18:52:29 -040078 if libFuzzerRuntimeLibrary := config.LibFuzzerRuntimeLibrary(ctx.toolchain()); libFuzzerRuntimeLibrary != "" {
79 deps.StaticLibs = append(deps.StaticLibs, libFuzzerRuntimeLibrary)
80 }
Ivan Lozano6cd99e62020-02-11 08:24:25 -050081 deps.SharedLibs = append(deps.SharedLibs, "libc++")
82 deps.Rlibs = append(deps.Rlibs, "liblibfuzzer_sys")
83
84 deps = fuzzer.binaryDecorator.compilerDeps(ctx, deps)
85
86 return deps
87}
88
89func (fuzzer *fuzzDecorator) compilerProps() []interface{} {
90 return append(fuzzer.binaryDecorator.compilerProps(),
91 &fuzzer.Properties)
92}
93
94func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage {
95 return RlibLinkage
96}
97
Liz Kammer356f7d42021-01-26 09:18:53 -050098func (fuzzer *fuzzDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
Ivan Lozano6cd99e62020-02-11 08:24:25 -050099 return rlibAutoDep
100}
hamzehc651b522021-04-29 12:50:47 -0700101
102// Responsible for generating GNU Make rules that package fuzz targets into
103// their architecture & target/host specific zip file.
104type rustFuzzPackager struct {
105 packages android.Paths
106 fuzzTargets map[string]bool
107}
108
109func rustFuzzPackagingFactory() android.Singleton {
110 return &rustFuzzPackager{}
111}
112
113type fileToZip struct {
114 SourceFilePath android.Path
115 DestinationPathPrefix string
116}
117
118type archOs struct {
119 hostOrTarget string
120 arch string
121 dir string
122}
123
124func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
125
126 // Map between each architecture + host/device combination.
127 archDirs := make(map[archOs][]fileToZip)
128
129 // List of individual fuzz targets.
130 s.fuzzTargets = make(map[string]bool)
131
132 ctx.VisitAllModules(func(module android.Module) {
133 // Discard non-fuzz targets.
134 rustModule, ok := module.(*Module)
135 if !ok {
136 return
137 }
138
139 fuzzModule, ok := rustModule.compiler.(*fuzzDecorator)
140 if !ok {
141 return
142 }
143
144 // Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
145 // fuzz targets we're going to package anyway.
146 if !rustModule.Enabled() || rustModule.Properties.PreventInstall ||
147 rustModule.InRamdisk() || rustModule.InVendorRamdisk() || rustModule.InRecovery() {
148 return
149 }
150
151 // Discard modules that are in an unavailable namespace.
152 if !rustModule.ExportedToMake() {
153 return
154 }
155
156 hostOrTargetString := "target"
157 if rustModule.Host() {
158 hostOrTargetString = "host"
159 }
160
161 archString := rustModule.Arch().ArchType.String()
162 archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
163 archOs := archOs{hostOrTarget: hostOrTargetString, arch: archString, dir: archDir.String()}
164
165 var files []fileToZip
166 builder := android.NewRuleBuilder(pctx, ctx)
167
168 // Package the corpora into a zipfile.
169 if fuzzModule.corpus != nil {
170 corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
171 command := builder.Command().BuiltTool("soong_zip").
172 Flag("-j").
173 FlagWithOutput("-o ", corpusZip)
174 rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
175 command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.corpus)
176 files = append(files, fileToZip{corpusZip, ""})
177 }
178
179 // Package the data into a zipfile.
180 if fuzzModule.data != nil {
181 dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
182 command := builder.Command().BuiltTool("soong_zip").
183 FlagWithOutput("-o ", dataZip)
184 for _, f := range fuzzModule.data {
185 intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
186 command.FlagWithArg("-C ", intermediateDir)
187 command.FlagWithInput("-f ", f)
188 }
189 files = append(files, fileToZip{dataZip, ""})
190 }
191
192 // The executable.
193 files = append(files, fileToZip{rustModule.unstrippedOutputFile.Path(), ""})
194
195 // The dictionary.
196 if fuzzModule.dictionary != nil {
197 files = append(files, fileToZip{fuzzModule.dictionary, ""})
198 }
199
200 // Additional fuzz config.
201 if fuzzModule.config != nil {
202 files = append(files, fileToZip{fuzzModule.config, ""})
203 }
204
205 fuzzZip := archDir.Join(ctx, module.Name()+".zip")
206
207 command := builder.Command().BuiltTool("soong_zip").
208 Flag("-j").
209 FlagWithOutput("-o ", fuzzZip)
210
211 for _, file := range files {
212 if file.DestinationPathPrefix != "" {
213 command.FlagWithArg("-P ", file.DestinationPathPrefix)
214 } else {
215 command.Flag("-P ''")
216 }
217 command.FlagWithInput("-f ", file.SourceFilePath)
218 }
219
220 builder.Build("create-"+fuzzZip.String(),
221 "Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
222
223 // Don't add modules to 'make haiku-rust' that are set to not be
224 // exported to the fuzzing infrastructure.
225 if config := fuzzModule.Properties.Fuzz_config; config != nil {
226 if rustModule.Host() && !BoolDefault(config.Fuzz_on_haiku_host, true) {
227 return
228 } else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
229 return
230 }
231 }
232
233 s.fuzzTargets[module.Name()] = true
234 archDirs[archOs] = append(archDirs[archOs], fileToZip{fuzzZip, ""})
235 })
236
237 var archOsList []archOs
238 for archOs := range archDirs {
239 archOsList = append(archOsList, archOs)
240 }
241 sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].dir < archOsList[j].dir })
242
243 for _, archOs := range archOsList {
244 filesToZip := archDirs[archOs]
245 arch := archOs.arch
246 hostOrTarget := archOs.hostOrTarget
247 builder := android.NewRuleBuilder(pctx, ctx)
248 outputFile := android.PathForOutput(ctx, "fuzz-rust-"+hostOrTarget+"-"+arch+".zip")
249 s.packages = append(s.packages, outputFile)
250
251 command := builder.Command().BuiltTool("soong_zip").
252 Flag("-j").
253 FlagWithOutput("-o ", outputFile).
254 Flag("-L 0") // No need to try and re-compress the zipfiles.
255
256 for _, fileToZip := range filesToZip {
257 if fileToZip.DestinationPathPrefix != "" {
258 command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
259 } else {
260 command.Flag("-P ''")
261 }
262 command.FlagWithInput("-f ", fileToZip.SourceFilePath)
263 }
264 builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
265 "Create fuzz target packages for "+arch+"-"+hostOrTarget)
266 }
267
268}
269
270func (s *rustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
271 packages := s.packages.Strings()
272 sort.Strings(packages)
273
274 ctx.Strict("SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
275
276 // Preallocate the slice of fuzz targets to minimise memory allocations.
277 fuzzTargets := make([]string, 0, len(s.fuzzTargets))
278 for target, _ := range s.fuzzTargets {
279 fuzzTargets = append(fuzzTargets, target)
280 }
281 sort.Strings(fuzzTargets)
282 ctx.Strict("ALL_RUST_FUZZ_TARGETS", strings.Join(fuzzTargets, " "))
283}
284
285func (fuzz *fuzzDecorator) install(ctx ModuleContext) {
286 fuzz.binaryDecorator.baseCompiler.dir = filepath.Join(
287 "fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
288 fuzz.binaryDecorator.baseCompiler.dir64 = filepath.Join(
289 "fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
290 fuzz.binaryDecorator.baseCompiler.install(ctx)
291
292 if fuzz.Properties.Corpus != nil {
293 fuzz.corpus = android.PathsForModuleSrc(ctx, fuzz.Properties.Corpus)
294 }
295 if fuzz.Properties.Data != nil {
296 fuzz.data = android.PathsForModuleSrc(ctx, fuzz.Properties.Data)
297 }
298 if fuzz.Properties.Dictionary != nil {
299 fuzz.dictionary = android.PathForModuleSrc(ctx, *fuzz.Properties.Dictionary)
300 }
301}