| # Copyright 2019 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import("//build/fuzzing/fuzzer.gni") |
| import("//build/go/go_build.gni") |
| import("//build/go/go_library.gni") |
| |
| nl = "$0x0a" # newline character |
| |
| go_fuzzer_wrapper_template = |
| "// Code generated by //build/go/go_fuzzer.gni; DO NOT EDIT.${nl}${nl}" + |
| "//line //build/go/go_fuzzer_wrapper.go:1${nl}" + |
| read_file("//build/go/go_fuzzer_wrapper.go", "string") |
| |
| # Defines a Go fuzz target. |
| # |
| # gopackage (required) |
| # The Go package exporting the target function. |
| # |
| # gofunction (optional, default: "Fuzz") |
| # The name of the exported target function. |
| # |
| # deps (required) |
| # List of labels representing go_library targets this target depends on. |
| # |
| # Additionally, the `cmx`, `dictionary`, and `options` parameters |
| # defined for `fuzzer` are supported. |
| |
| template("go_fuzzer") { |
| assert(defined(invoker.gopackage), |
| "gopackage must be defined for $target_name") |
| assert(defined(invoker.deps), "deps must be defined for $target_name") |
| |
| fuzzer_name = target_name |
| gopackage = invoker.gopackage |
| gofunction = "Fuzz" |
| if (defined(invoker.gofunction)) { |
| gofunction = invoker.gofunction |
| } |
| |
| # Building a Go fuzzer involves a few steps: |
| # 1. Generate the wrapper source file. |
| source = go_fuzzer_wrapper_template |
| source = string_replace(source, "GO_FUZZER_PKG", gopackage) |
| source = string_replace(source, "GO_FUZZER_FUNC", gofunction) |
| wrapper_name = "${fuzzer_name}_wrapper" |
| source_name = "${wrapper_name}.go" |
| write_file("$target_gen_dir/$source_name", source) |
| |
| # 2. Define a go_library rule containing that source file. |
| library_name = "${wrapper_name}_library" |
| go_library(library_name) { |
| name = wrapper_name |
| source_dir = target_gen_dir |
| sources = [ source_name ] |
| forward_variables_from(invoker, [ "deps" ]) |
| } |
| |
| # 3a. Build the wrapper package as a C archive with Go's libfuzzer instrumentation. |
| go_build(wrapper_name) { |
| gopackages = [ wrapper_name ] |
| carchive = true |
| tags = [ "libfuzzer" ] |
| gcflags = [ "all=-d=libfuzzer" ] |
| deps = [ ":$library_name" ] |
| } |
| |
| # 3b. Alternately, create a fuzzer unit test to ensure fuzzer code is built even when not fuzzing. |
| test_wrapper_name = "${fuzzer_name}_test_wrapper" |
| go_build(test_wrapper_name) { |
| gopackages = [ wrapper_name ] |
| carchive = true |
| deps = [ ":$library_name" ] |
| } |
| |
| # 4. Link the resulting C archive as though it was a normal C++ fuzzer. |
| fuzzer(fuzzer_name) { |
| forward_variables_from(invoker, |
| [ |
| "cmx", |
| "dictionary", |
| "options", |
| ]) |
| |
| # Examine the toolchain configs to detect if it is being built as part of a fuzzer variant. |
| # If not, the fuzzer unit test will be built instead. |
| fuzzer_configs = [ "//build/config/sanitizers:fuzzer" ] |
| if (toolchain_variant.configs + fuzzer_configs - fuzzer_configs == |
| toolchain_variant.configs) { |
| wrapper_name = test_wrapper_name |
| } |
| |
| # As noted in go_build.gni, Go libraries do not generate distinct outputs for each variants, |
| # but always use the output of the base toolchain. |
| deps = [ ":${wrapper_name}(${toolchain_variant.base})" ] |
| base_root_out_dir = get_label_info(deps[0], "root_out_dir") |
| base_library_name = get_label_info(deps[0], "name") |
| libs = [ "$base_root_out_dir/${base_library_name}.a" ] |
| } |
| } |