// Copyright 2016 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cc import ( "path/filepath" "strings" "github.com/google/blueprint/proptools" "android/soong/android" "android/soong/cc/config" ) type FuzzProperties struct { // Optional list of seed files to be installed to the fuzz target's output // directory. Corpus []string `android:"path"` // Optional dictionary to be installed to the fuzz target's output directory. Dictionary *string `android:"path"` } func init() { android.RegisterModuleType("cc_fuzz", FuzzFactory) android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory) } // cc_fuzz creates a host/device fuzzer binary. Host binaries can be found at // $ANDROID_HOST_OUT/fuzz/, and device binaries can be found at /data/fuzz on // your device, or $ANDROID_PRODUCT_OUT/data/fuzz in your build tree. func FuzzFactory() android.Module { module := NewFuzz(android.HostAndDeviceSupported) return module.Init() } func NewFuzzInstaller() *baseInstaller { return NewBaseInstaller("fuzz", "fuzz", InstallInData) } type fuzzBinary struct { *binaryDecorator *baseCompiler Properties FuzzProperties dictionary android.Path corpus android.Paths corpusIntermediateDir android.Path } func (fuzz *fuzzBinary) linkerProps() []interface{} { props := fuzz.binaryDecorator.linkerProps() props = append(props, &fuzz.Properties) return props } func (fuzz *fuzzBinary) linkerInit(ctx BaseModuleContext) { // Add ../lib[64] to rpath so that out/host/linux-x86/fuzz/ can // find out/host/linux-x86/lib[64]/library.so runpaths := []string{"../lib"} for _, runpath := range runpaths { if ctx.toolchain().Is64Bit() { runpath += "64" } fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths = append( fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths, runpath) } // add "" to rpath so that fuzzer binaries can find libraries in their own fuzz directory fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths = append( fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths, "") fuzz.binaryDecorator.linkerInit(ctx) } func (fuzz *fuzzBinary) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.StaticLibs = append(deps.StaticLibs, config.LibFuzzerRuntimeLibrary(ctx.toolchain())) deps = fuzz.binaryDecorator.linkerDeps(ctx, deps) return deps } func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags { flags = fuzz.binaryDecorator.linkerFlags(ctx, flags) return flags } func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) { fuzz.binaryDecorator.baseInstaller.dir = filepath.Join( "fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName()) fuzz.binaryDecorator.baseInstaller.dir64 = filepath.Join( "fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName()) fuzz.binaryDecorator.baseInstaller.install(ctx, file) fuzz.corpus = android.PathsForModuleSrc(ctx, fuzz.Properties.Corpus) builder := android.NewRuleBuilder() intermediateDir := android.PathForModuleOut(ctx, "corpus") for _, entry := range fuzz.corpus { builder.Command().Text("cp"). Input(entry). Output(intermediateDir.Join(ctx, entry.Base())) } builder.Build(pctx, ctx, "copy_corpus", "copy corpus") fuzz.corpusIntermediateDir = intermediateDir if fuzz.Properties.Dictionary != nil { fuzz.dictionary = android.PathForModuleSrc(ctx, *fuzz.Properties.Dictionary) if fuzz.dictionary.Ext() != ".dict" { ctx.PropertyErrorf("dictionary", "Fuzzer dictionary %q does not have '.dict' extension", fuzz.dictionary.String()) } } } func NewFuzz(hod android.HostOrDeviceSupported) *Module { module, binary := NewBinary(hod) binary.baseInstaller = NewFuzzInstaller() module.sanitize.SetSanitizer(fuzzer, true) fuzz := &fuzzBinary{ binaryDecorator: binary, baseCompiler: NewBaseCompiler(), } module.compiler = fuzz module.linker = fuzz module.installer = fuzz // The fuzzer runtime is not present for darwin host modules, disable cc_fuzz modules when targeting darwin. android.AddLoadHook(module, func(ctx android.LoadHookContext) { disableDarwinAndLinuxBionic := struct { Target struct { Darwin struct { Enabled *bool } Linux_bionic struct { Enabled *bool } } }{} disableDarwinAndLinuxBionic.Target.Darwin.Enabled = BoolPtr(false) disableDarwinAndLinuxBionic.Target.Linux_bionic.Enabled = BoolPtr(false) ctx.AppendProperties(&disableDarwinAndLinuxBionic) }) // Statically link the STL. This allows fuzz target deployment to not have to // include the STL. android.AddLoadHook(module, func(ctx android.LoadHookContext) { staticStlLinkage := struct { Target struct { Linux_glibc struct { Stl *string } } }{} staticStlLinkage.Target.Linux_glibc.Stl = proptools.StringPtr("libc++_static") ctx.AppendProperties(&staticStlLinkage) }) return module } // Responsible for generating GNU Make rules that package fuzz targets into // their architecture & target/host specific zip file. type fuzzPackager struct { packages android.Paths } func fuzzPackagingFactory() android.Singleton { return &fuzzPackager{} } type fileToZip struct { SourceFilePath android.Path DestinationPathPrefix string } func (s *fuzzPackager) GenerateBuildActions(ctx android.SingletonContext) { // Map between each architecture + host/device combination, and the files that // need to be packaged (in the tuple of {source file, destination folder in // archive}). archDirs := make(map[android.OutputPath][]fileToZip) ctx.VisitAllModules(func(module android.Module) { // Discard non-fuzz targets. ccModule, ok := module.(*Module) if !ok { return } fuzzModule, ok := ccModule.compiler.(*fuzzBinary) if !ok { return } // Discard vendor-NDK-linked modules, they're duplicates of fuzz targets // we're going to package anyway. if ccModule.UseVndk() || !ccModule.Enabled() { return } hostOrTargetString := "target" if ccModule.Host() { hostOrTargetString = "host" } archString := ccModule.Arch().ArchType.String() archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString) // The executable. archDirs[archDir] = append(archDirs[archDir], fileToZip{ccModule.outputFile.Path(), ccModule.Name()}) // The corpora. for _, corpusEntry := range fuzzModule.corpus { archDirs[archDir] = append(archDirs[archDir], fileToZip{corpusEntry, ccModule.Name() + "/corpus"}) } // The dictionary. if fuzzModule.dictionary != nil { archDirs[archDir] = append(archDirs[archDir], fileToZip{fuzzModule.dictionary, ccModule.Name()}) } }) for archDir, filesToZip := range archDirs { arch := archDir.Base() hostOrTarget := filepath.Base(filepath.Dir(archDir.String())) builder := android.NewRuleBuilder() outputFile := android.PathForOutput(ctx, "fuzz-"+hostOrTarget+"-"+arch+".zip") s.packages = append(s.packages, outputFile) command := builder.Command().BuiltTool(ctx, "soong_zip"). Flag("-j"). FlagWithOutput("-o ", outputFile) for _, fileToZip := range filesToZip { command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix). FlagWithInput("-f ", fileToZip.SourceFilePath) } builder.Build(pctx, ctx, "create-fuzz-package-"+arch+"-"+hostOrTarget, "Create fuzz target packages for "+arch+"-"+hostOrTarget) } } func (s *fuzzPackager) MakeVars(ctx android.MakeVarsContext) { // TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's // ready to handle phony targets created in Soong. In the meantime, this // exports the phony 'fuzz' target and dependencies on packages to // core/main.mk so that we can use dist-for-goals. ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(s.packages.Strings(), " ")) }