blob: e2a01486b35d5782283b5a75aad1c6efdd3385cf [file] [log] [blame]
# 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" ]
}
}