blob: c1d486df3c90188d79fbd0aee4c75fc1441bfcab [file] [log] [blame]
# Copyright 2020 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.
declare_args() {
# Default [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html)
# options (before the `ASAN_OPTIONS` environment variable is read at
# runtime). This can be set as a build argument to affect most "asan"
# variants in $variants (which see), or overridden in $toolchain_args in
# one of those variants. This can be a list of strings or a single string.
#
# Note that even if this is empty, programs in this build **cannot** define
# their own `__asan_default_options` C function. Instead, they can use a
# sanitizer_extra_options() target in their `deps` and then any options
# injected that way can override that option's setting in this list.
asan_default_options = [
"detect_stack_use_after_return=1",
# TODO(fxbug.dev/67985): Using default quarantine size of 256MiB causes
# asan bots to OOM, this reduces the per-app quarantine size until we
# investigate other possible fixes.
"quarantine_size_mb=64",
]
# Default [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html)
# options (before the `LSAN_OPTIONS` environment variable is read at
# runtime). This can be set as a build argument to affect most "lsan"
# variants in $variants (which see), or overridden in $toolchain_args in
# one of those variants. This can be a list of strings or a single string.
#
# Note that even if this is empty, programs in this build **cannot** define
# their own `__lsan_default_options` C function. Instead, they can use a
# sanitizer_extra_options() target in their `deps` and then any options
# injected that way can override that option's setting in this list.
lsan_default_options = []
# Default [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)
# options (before the `UBSAN_OPTIONS` environment variable is read at
# runtime). This can be set as a build argument to affect most "ubsan"
# variants in $variants (which see), or overridden in $toolchain_args in
# one of those variants. This can be a list of strings or a single string.
#
# Note that even if this is empty, programs in this build **cannot** define
# their own `__ubsan_default_options` C function. Instead, they can use a
# sanitizer_extra_options() target in their `deps` and then any options
# injected that way can override that option's setting in this list.
ubsan_default_options = [
"print_stacktrace=1",
"halt_on_error=1",
]
# Default [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html)
# options (before the `TSAN_OPTIONS` environment variable is read at runtime).
# This can be set as a build argument to affect most "tsan" variants in
# $variants (which see), or overrideen in $toolchain_args in one of those
# variants. This can be a list of strings or a single string.
#
# Note that even if this is empty, programs in this build **cannot** define
# their own `__tsan_default_options` C function. Instead, they can use a
# sanitizer_extra_options() target in their `deps` and then any options
# injected that way can override that option's setting in this list.
#
# TODO(fxbug.dev/89981): `ignore_noninstrumented_modules=1` can be reevaluated
# when/if we have an instrumented libstd for Rust.
tsan_default_options = [ "ignore_noninstrumented_modules=1" ]
# Default [Scudo](https://llvm.org/docs/ScudoHardenedAllocator.html) options
# (before the `SCUDO_OPTIONS` environment variable is read at runtime).
# Scudo is the memory allocator in Fuchsia's C library, so this affects all
# Fuchsia programs. This can be a list of strings or a single string.
#
# This operates similarly to [`asan_default_options`](#asan_default_options)
# and its cousins for other sanitizers, but is slightly different. If this
# variable is empty, then no `__scudo_default_options` function is injected
# into programs at all. Individual targets can use dependencies on
# sanitizer_extra_options() targets to cause options to be injected, and that
# will be compatible with any build-wide settings of `scudo_default_options`.
# Programs **can** define their own `__scudo_default_options` functions, but
# doing so will break all builds with this variable is set to nonempty, so
# any program in the build that needs such a setting (which should be only in
# tests) can use the sanitizer_extra_options() mechanism instead.
scudo_default_options = []
# Default [HawrdwareAddressSanitizer](https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html)
# options (before the `HWASAN_OPTIONS` environment variable is read at
# runtime). This can be set as a build argument to affect most "hwasan"
# variants in $variants (which see), or overridden in $toolchain_args in
# one of those variants. This can be a list of strings or a single string.
#
# Note that even if this is empty, programs in this build **cannot** define
# their own `__hwasan_default_options` C function. Instead, they can use a
# sanitizer_extra_options() target in their `deps` and then any options
# injected that way can override that option's setting in this list.
hwasan_default_options = []
}
# This lists the GN build arguments declared above.
# sanitizer_default_options() targets must have one of these names.
sanitizer_default_options_args = [
"asan_default_options",
"lsan_default_options",
"ubsan_default_options",
"scudo_default_options",
"tsan_default_options",
"hwasan_default_options",
]
# Adapt to being used in the Fuchsia GN build or in the Zircon build.
if (zircon_toolchain == false) {
_sanitizer_config_dir = "//build/config/sanitizers"
_hidden_config = "//build/config:symbol_visibility_hidden"
_no_sanitizers_config = "//build/config/sanitizers:no_sanitizers"
assert(!is_kernel)
} else {
_sanitizer_config_dir = "//build/config/zircon/instrumentation"
_hidden_config = "//build/config:symbol_visibility_hidden"
_no_sanitizers_config = "//build/config/sanitizers:no_sanitizers"
}
if (defined(toolchain_variant.tags)) {
_tags = toolchain_variant.tags
} else {
_tags = []
}
# Subroutine of the templates below. A source_set() using the same source
# file is defined, with differing defines depending on the invoker.
template("_sanitizer_default_options_target") {
string = string_join(":", invoker.args)
nonempty = (string != "" || invoker.default) && !is_kernel
if (nonempty) {
# This might only be in the executable() target's deps transitively via a
# static_library() or rust_library() target. In that case, a source_set()
# would not have its object files propagated to the final executable()
# link. So we play some shenanigans below to make sure the object file
# here never goes into a library but always goes into the final link.
#
# In the invoker.default case, the target is directly in the deps of each
# executable() target, so it will always be linked in directly and so a
# source_set() could work. However, the source_set() would contribute its
# object file to to {{objs}} while the shenanigans below contribute to
# {{libs}}, so the dependency ordering between the two would be lost.
type = "static_library"
} else {
type = "group"
}
target(type, target_name) {
forward_variables_from(invoker,
[
"deps",
"visibility",
"testonly",
])
if (!invoker.default) {
if (!defined(deps)) {
deps = []
}
deps += [ "${_sanitizer_config_dir}:${invoker.output_name}" ]
}
if (nonempty) {
# On Fuchsia, the ASan runtime is dynamically linked and needs to have
# the __asan_default_options symbol exported. In situations where the
# runtime is statically linked, it doesn't matter either way.
configs -= [ _hidden_config ]
# On non-Fuchsia systems, the flag parsing calls this function so early
# in startup that instrumented code cannot reliably run at all yet.
configs += [ _no_sanitizers_config ]
configs -= [ _no_sanitizers_config ]
configs += [ _no_sanitizers_config ]
# This is the default on Fuchsia but not on all host platforms.
# It's necessary to link into Rust binaries, which are PIE even
# on platforms where C binaries are not PIE by default.
cflags = [ "-fPIE" ]
defines = [
"DEFINE_SANITIZER_DEFAULT_OPTIONS=${invoker.default}",
"SANITIZER_DEFAULT_OPTIONS_NAME=${invoker.output_name}",
"SANITIZER_DEFAULT_OPTIONS_STRING=\"${string}\"",
]
sources = [ "//build/config/sanitizers/sanitizer_default_options.c" ]
# The library archive is in the link, but there's no symbol reference
# that would bring the object into the link from that archive. So we
# hijack the dependency on the library just to ensure that the object
# gets built, and then stuff it directly into the link with `libs`.
output_prefix_override = true
libs = process_file_template(
sources,
[ "{{source_out_dir}}/$target_name.{{source_file_part}}.o" ])
} else {
not_needed(invoker, [ "output_name" ])
}
}
}
# Provide the `*san_default_options` target for a sanitizer variant.
#
# This defines a source_set() target, whose $target_name is the same as the
# name of a GN build argument in scope. The template can't do declare_args()
# itself because the actual source location of the declare_args() block
# (rather than the template invocation) has to supply the documentation
# string via a comment preceding the variable definition. See the
# `asan_default_options` documentation string for an example that explains
# the semantics completely as each documentation string should.
#
# The sanitizer variant should set `toolchain_vars.sanitizer_default_options`
# to this label, and also include it in `implicit_deps` for executable-like
# targets. The C function signature `const char* __$target_name(void)` is
# defined by this target.
#
# Note this **always** links the `__$target_name` function into executables,
# even if it just returns the empty string. This means it always conflicts
# with program source defining that function itself. This could be avoided,
# since any sanitizer_extra_options() could depend on source_set() like this
# directly even if it were not directly in ${toolchain.implicit_deps}.
# However, it has two benefits in return:
# 1. Any code defining `__$target_name` breaks the (variant) build uniformly
# regardless of the `*_default_options` build argument settings used.
# 2. This serves as a dummy C module linked into each program in the variant.
# This ensures the compiler-generated references that get the sanitizer
# runtime linked in and initialized at program startup are in each program
# that has no other C/C++ modules linked in. This matters to e.g. a Rust
# link in an asan or lsan variant so it uses the sanitizer allocator even
# though the Rust-generated code itself doesn't call into the runtime.
# (This could be separated from the options callback function by just
# adding a dummy translation unit. Since we need both we just use this.)
#
# Parameters
#
# * deps
# - Optional: List of sanitizer_default_options() targets for other
# runtimes subsumed by this one. If this is set, then the $target_name
# function will fold in the option strings from each of the other build
# arguments represented in `deps` as well. Each option set in these
# other build arguments will be overridden by any setting for the same
# option in $target_name.
# - Type: list(label_no_toolchain)
#
template("sanitizer_default_options") {
assert(
sanitizer_default_options_args + [ target_name ] - [ target_name ] !=
sanitizer_default_options_args,
"sanitizer_default_options() target name must be listed in " +
"`sanitizer_default_options_args`: $sanitizer_default_options_args")
_sanitizer_default_options_target(target_name) {
forward_variables_from(invoker,
[
"deps",
"testonly",
"visibility",
])
default = true
output_name = target_name
# Fetch the build argument's value. The invoker scope inherited it from
# the global scope after the declare_args() block defined it there, making
# it accessible programmatically here.
args = invoker[target_name]
# Handle a single-string argument for flexible build argument input.
if (args == "$args") {
args = [ args ]
}
}
}
# Provide additional defaults for sanitizer runtime options.
#
# This defines a source_set() target. If the toolchain matches $tags, then
# this contributes $args to the built-in default sanitizer runtime options in
# programs that depend on this target. This target can then be used in
# `deps` of an executable or static library target to get $args into the
# runtime options list. Note that it does no good for a shared library
# target to have this target in `deps`, and this won't be detected by GN.
#
# Parameters
#
# * args
# - Required: List of "option=value" strings for the sanitizer runtime.
# - Type: list(string)
#
#
# * output_name
# - Required: Name of the GN build argument to affect.
# - Type: string (one of the `sanitizer_default_options_args` list).
#
# * tags
# - Required: The target defined is a no-op target in toolchains where
# $tags are not found in ${toolchain_variant.tags}. This makes it
# convenient to depend on this unconditionally.
# - Type: list(string)
#
template("sanitizer_extra_options") {
assert(defined(invoker.args),
"sanitizer_extra_options() requires `args` (a list of strings)")
assert(defined(invoker.tags),
"sanitizer_extra_options() requires `tags` (a list of strings)")
if (_tags + invoker.tags - invoker.tags == _tags) {
source_set(target_name) {
forward_variables_from(invoker,
[
"visibility",
"testonly",
])
not_needed(invoker,
[
"args",
"output_name",
])
}
} else {
_sanitizer_default_options_target(target_name) {
forward_variables_from(invoker,
[
"args",
"output_name",
"visibility",
"testonly",
])
default = false
}
}
}