| # 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 [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 = [] |
| } |
| |
| # 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", |
| ] |
| |
| # 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:no_sanitizers" |
| assert(!is_kernel) |
| } else { |
| _sanitizer_config_dir = "//build/config/zircon/instrumentation" |
| _hidden_config = "//build/config/zircon:visibility_hidden" |
| _no_sanitizers_config = "//build/config/zircon: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.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 |
| } |
| } |
| } |