blob: b99e5a73d14712e4ffe4d7a39162e3cd83378a8b [file] [edit]
# Copyright 2024 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/toolchain/generated_ifs_file.gni")
import("//build/toolchain/ifs_shared_library.gni")
# Generate an ld::RemotePerfectSymbolFilterMaker function.
#
# This takes a static list of symbol names and emits generated C++ code for a
# filter allowing only those symbols. The generated function takes a runtime
# ld::RemoteDecodedModule and returns an ld::RemoteLoadModule::SymbolFilter
# that can be attached to it. See <lib/ld/remote-perfect-symbol-filter.h> for
# the complete C++ details. What's generated is actually a function templated
# on the elfldltl::Elf<...> instantiation for the runtime ELF format in use. A
# generated "$target_name.h" header declares the function template and one or
# more explicit extern instantiations. An isolated file of generated code
# defines those function template instantiations.
#
# This template produces three targets:
#
# * "$target_name" is a source_set() that must be added to $deps in order
# to call the generated $output_name C++ function. This enables using
# `#include "$target_name.h"` to get the generated function(s) declared.
#
# * "$target_name.ifs" is a generated_ifs_file() that represents the shared
# library ABI exposed through the filter. This target is only instantiated
# in $default_toolchain, with output in "$target_gen_dir/$target_name.ifs".
#
# * "$target_name.link" is an ifs_shared_library() using that ABI. If modules
# that will be loaded at runtime are linked against this at build time in
# lieu of their real runtime dependency, those builds will fail if they
# reference any symbols not permitted by the filter.
#
# Parameters
#
# * output_name
# - Required: C++ (unscoped) identifier of the function template to define.
# - Type: string
#
# * diagnostics
# - Required: C++ class meeting the elfldltl Diagnostics API, the parameter
# to the ld::RemotePerfectSymbolFilterMaker template. This class should be
# declared in some header file listed in $includes.
# - Type: string
#
# * namespace
# - Optional: C++ namespace to contain $output_name. If omitted, then
# $output_name is declared in the global namespace.
# - Type: string
#
# * includes
# - Optional: List of `#include` directives; each one is the verbatim string
# to emit after `#include `, i.e. including `<...>` or `"..."` quotes.
# Note that $include_dirs and $deps can be passed to
# - Type: list(string)
#
# * symbols
# - Required: The allow-list of symbols the filter will recognize.
# - Type: list(string)
#
# * allow_undefs
# - Optional: If true, the $output_name function will ignore any symbol in
# the $symbols list that is not found in the runtime module. By default,
# the $diagnostics object's `UndefinedSymbol` method will be called and
# can choose whether the function will continue or fail.
# - Type: bool
# - Default: false
#
# * max_overhead
# - Optional: This can set a compile-time limit on the overhead of the
# elfldltl::PerfectSymbolSet. Since GN doesn't have floating-point
# numbers, here this is expressed as a percentage, so the C++ template
# parameter is $max_overhead / 100.0. The default value and meaning are
# described fully in <lib/elfldltl/perfect-symbol-table.h>.
# - Type: integer
#
# * soname
# - Optional: Passed to generated_ifs_file().
# - Type: string
#
# * deps, data_deps, include_dirs, public_deps, testonly, visibility
# - Optional: See source_set().
#
# * elves
# - Optional: List of elfldltl::Elf<...> instantiations for which the
# $output_name function will be defined. By default, only the default
# template instantiation will be defined, for the ELF format for which the
# remote dynamic linking code itself was compiled.
# - Type: list(string)
# - Default: [ "" ]
#
template("remote_perfect_symbol_filter") {
main_target = target_name
ifs_target = "$target_name.ifs"
stub_target = "$target_name.link"
cc_target = "_perfect_symbol_filter.$target_name.cc"
h_target = "_perfect_symbol_filter.$target_name.h"
config_target = "_perfect_symbol_filter.$target_name.config"
gen_dir =
get_label_info(":$main_target($default_toolchain)", "target_gen_dir")
source_set(main_target) {
forward_variables_from(invoker,
[
"data_deps",
"deps",
"include_dirs",
"public_deps",
"testonly",
"visibility",
])
public = [ "$gen_dir/$main_target.h" ]
public_configs = [ ":$config_target" ]
sources = [ "$gen_dir/$main_target.cc" ]
if (!defined(deps)) {
deps = []
}
deps += [
":$cc_target($default_toolchain)",
":$h_target($default_toolchain)",
"//sdk/lib/ld:headers",
]
}
ifs_shared_library(stub_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
if (defined(visibility)) {
visibility += [ ":$ifs_target" ]
}
abi = "$gen_dir/$ifs_target.ifs"
deps = [ ":$ifs_target" ]
}
config(config_target) {
visibility = [ ":$main_target" ]
include_dirs = [ gen_dir ]
}
if (current_toolchain == default_toolchain) {
gen_label = get_label_info(":$main_target", "label_no_toolchain")
generated_ifs_file(ifs_target) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
"symbols",
"soname",
])
}
if (defined(invoker.elves)) {
elves = invoker.elves
} else {
elves = [ "" ]
}
if (defined(invoker.max_overhead)) {
overhead = ", (${invoker.max_overhead} / 100.0)"
} else {
overhead = ""
}
allow_undefs = defined(invoker.allow_undefs) && invoker.allow_undefs
namespace = {
if (defined(invoker.namespace)) {
start = [
"namespace ${invoker.namespace} {",
"",
]
end = [
"",
"} // namespace ${invoker.namespace}",
]
} else {
start = []
end = []
}
}
generated_file(h_target) {
visibility = [ ":$main_target" ]
forward_variables_from(invoker, [ "testonly" ])
outputs = [ "$gen_dir/$main_target.h" ]
output_conversion = "list lines"
contents = [
"// Generated by $gen_label. DO NOT EDIT!",
"",
"#pragma once",
"",
"#include <lib/ld/remote-perfect-symbol-filter.h>",
]
if (defined(invoker.includes)) {
foreach(file, invoker.includes) {
contents += [ "#include $file" ]
}
}
contents += [ "" ]
contents += namespace.start
contents += [
"template <class Elf = elfldltl::Elf<>>",
"ld::RemoteLoadModule<Elf>::SymbolFilter " + "${invoker.output_name}(" +
"${invoker.diagnostics}&, " +
"typename RemoteDecodedModule<Elf>::Ptr module);",
"",
]
foreach(elf, elves) {
contents +=
[ "extern template ld::RemotePerfectSymbolFilterMaker<" +
"${invoker.diagnostics}, $elf> ${invoker.output_name}<$elf>;" ]
}
contents += namespace.end
}
generated_file(cc_target) {
visibility = [ ":$main_target" ]
forward_variables_from(invoker, [ "testonly" ])
outputs = [ "$gen_dir/$main_target.cc" ]
output_conversion = "list lines"
contents = [
"// Generated by $gen_label. DO NOT EDIT!",
"",
"#include \"$main_target.h\"",
"",
"namespace {",
"",
"constexpr auto kSymbols = elfldltl::PerfectSymbolTable({",
]
foreach(symbol, invoker.symbols) {
if (symbol == "$symbol") {
contents += [ " \"$symbol\"," ]
} else {
contents += [ " \"${symbol.name}\"," ]
}
}
contents += [
"});",
"",
"} // namespace ",
"",
]
contents += namespace.start
contents += [
"template <class Elf>",
"ld::RemoteLoadModule<Elf>::SymbolFilter ${invoker.output_name}(",
" ${invoker.diagnostics}& diag,",
" typename RemoteDecodedModule<Elf>::Ptr decoded_module) {",
" return ld::RemotePerfectSymbolFilter<kSymbols, Elf$overhead>(",
" diag, std::move(decoded_module), $allow_undefs);",
"}",
"",
]
foreach(elf, elves) {
contents +=
[ "template ld::RemotePerfectSymbolFilterMaker<" +
"${invoker.diagnostics}, $elf> ${invoker.output_name}<$elf>;" ]
}
contents += namespace.end
}
} else {
not_needed(invoker,
[
"diagnostics",
"elves",
"includes",
"max_overhead",
"namespace",
"output_name",
"soname",
"symbols",
])
}
}