blob: 40869832e057eab13b5c83ce72e6b0864ae2e43e [file] [log] [blame]
# Copyright 2023 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/cpp/library_headers.gni")
import("//build/cpp/verify_public_symbols.gni")
import("//build/toolchain/generated_ifs_file.gni")
import("//build/toolchain/ifs_extract.gni")
import("//build/toolchain/ifs_shared_library.gni")
import("//build/toolchain/toolchain_environment.gni")
import("//build/toolchain/zircon/user_basic_redirect.gni")
import("distribution_manifest_prefix.gni")
import("testing/test_elf_object.gni")
# This is for executables linked to use the startup dynamic linker. This does
# not bring any API or ABI access to the executable() at compile or link time;
# use the `ld` target for that. At link time, it ensures the PT_INTERP is set
# correctly. It also brings data_deps for packaging the startup dynamic
# linker; note, the startup dynamic linker can be harmlessly packaged with the
# executable and its dependencies even if in fact remote dynamic linking is
# going to be used instead. Finally, this target brings metadata for matching
# that dynamic linker in test code using <lib/ld/testing/test-elf-object.h>.
group("startup") {
public_configs = [ ":abi-interp" ]
data_deps = [ ":ld-startup.user" ]
}
# This is for users of <lib/ld/remote-dynamic-linker.h> and related APIs. It
# provides compile and link time dependencies for using that API. It also
# brings data_deps and metadata for packaging the required stub dynamic linker
# and for matching it in test code using <lib/ld/testing/test-elf-object.h>.
group("remote") {
public_deps = [ ":headers" ]
data_deps = [ ":ld-stub.user" ]
}
# Make sure that there's no transitive deps on libzircon, so header-only
# use of the library doesn't affect dynamic linking dependencies.
if (zircon_toolchain == false) {
_headers_dep = ""
} else {
_headers_dep = ":headers"
}
# This provides access to additional <lib/ld/...> headers for support code in
# the `ld` namespace, which isn't itself part of of the passive ABI.
library_headers("headers") {
headers = [
"lib/ld/bootstrap.h",
"lib/ld/decoded-module-in-memory.h",
"lib/ld/decoded-module.h",
"lib/ld/diagnostics.h",
"lib/ld/dl-phdr-info.h",
"lib/ld/load-module.h",
"lib/ld/load.h",
"lib/ld/log-posix.h",
"lib/ld/memory.h",
"lib/ld/tlsdesc.h",
]
public_deps = [
":abi-headers",
"//sdk/lib/fit",
"//src/lib/symbolizer-markup",
"//zircon/kernel/lib/arch:headers",
]
if (is_fuchsia) {
headers += [
"lib/ld/remote-abi-heap.h",
"lib/ld/remote-abi-stub.h",
"lib/ld/remote-abi-transcriber.h",
"lib/ld/remote-abi.h",
"lib/ld/remote-decoded-module.h",
"lib/ld/remote-dynamic-linker.h",
"lib/ld/remote-load-module.h",
"lib/ld/remote-perfect-symbol-filter.h",
"lib/ld/remote-zygote.h",
]
public_deps += [
# <lib/ld/remote-decoded-module.h> has `#include <fbl/ref_ptr.h>`.
"//zircon/system/ulib/fbl",
]
public_deps += [
"//src/zircon/lib/zircon:headers",
"//zircon/system/ulib/zx$_headers_dep",
]
}
}
library_headers("ld-log-posix.headers") {
headers = [ "lib/ld/log-posix.h" ]
}
static_library("ld-log-posix") {
public_deps = [ ":ld-log-posix.headers" ]
sources = [ "log-posix.cc" ]
}
library_headers("ld-log-zircon.headers") {
headers = [ "lib/ld/log-zircon.h" ]
public_deps = [ "//zircon/system/ulib/zx$_headers_dep" ]
}
static_library("ld-log-zircon") {
public_deps = [ ":ld-log-zircon.headers" ]
sources = [ "log-zircon.cc" ]
deps = [
"//zircon/system/ulib/zircon-internal",
"//zircon/system/ulib/zx",
]
}
# This is the SONAME reflecting the passive ABI defined here.
# It has to match what's in ld.ifs and in ld::abi::kSoname (<lib/ld/abi.h>).
abi_soname = "ld.so.1"
config("abi-soname") {
ldflags = [ "-Wl,-soname=$abi_soname" ]
}
config("abi-interp") {
if (is_fuchsia) {
libprefix = toolchain_variant.libprefix
} else {
libprefix = ""
}
ldflags = [ "-Wl,-dynamic-linker=$libprefix$abi_soname" ]
}
generated_ifs_file("ld.ifs") {
visibility = [ ":*" ]
soname = abi_soname
symbols = [
{
name = "_ld_abi"
type = "Object"
},
{
name = "_r_debug"
type = "Object"
},
]
}
# This is the linkable target for using the passive ABI.
# It gives access to the passive ABI dynamic linkage symbols
# and to the header files declaring the `ld::abi` namespace.
ifs_shared_library("ld") {
deps = [ ":ld.ifs" ]
gen_outputs = get_target_outputs(deps[0])
abi = gen_outputs[0]
public_deps = [ ":abi-headers" ]
}
# This provides access to the <lib/ld/...> headers that specify the passive ABI
# defined in the `ld::abi` C++ namespace.
library_headers("abi-headers") {
headers = [
"lib/ld/abi.h",
"lib/ld/module.h",
"lib/ld/tls.h",
]
public_deps = [ "//src/lib/elfldltl:headers" ]
}
# This provides the __tls_get_addr ABI symbol used by TLS GD accesses when
# TLSDESC is not in use. This implementation uses the passive ABI to resolve
# references in the initial exec set's static TLS layout. A different
# implementation would be required to also support post-startup modules.
source_set("static-tls-get-addr") {
sources = [ "static-tls-get-addr.cc" ]
deps = [ "." ]
}
# This is the means to depend on ld-stub or ld-startup, which are visibility
# restricted to this file. It defines a target to bring the runtime file into
# the Fuchsia package (or host test data). It also verifies the runtime ABI of
# the file matches ld.ifs so the build will fail rather than deliver known
# ABI-breaking dynamic linker implementations. Finally, it provides
# test_elf_object() metadata so that rolling up into a test_elf_load_set() will
# package a dynamic linker binary for runtime use like a link-time dependency.
#
# The ld_abi_user() targets `ld-stub.user` or `ld-startup.user` can appear in
# $deps or $data_deps lists (it shouldn't matter which, these targets will only
# actually contribute data_deps). These are not what's used to link against
# the ld ABI and get the right DT_NEEDED or PT_INTERP; that's `:ld` for the ABI
# (and DT_NEEDED), and config("abi-interp") for PT_INTERP.
template("ld_abi_user") {
main_target = target_name
redirect_target = "$main_target.redirect"
verify_target = "$main_target.verify-abi"
extract_target = "$main_target.ifs"
ifs_file = "$target_out_dir/$extract_target"
ld_deps = invoker.deps
ld_label = ld_deps[0]
assert(ld_deps == [ ld_label ])
if (defined(invoker.select) && invoker.select) {
# The ld-stub's install location is unrelated to the dependent executable
# itself, since ld-stub.so is just data for it and not meant to be
# compatible with its own ABI. It has a path under lib/ just to make it
# easy to get an executable VMO under the Fuchsia package sandbox rules.
dist_target = redirect_target
} else {
dist_target = "$main_target.dist"
# This target rolls up the distribution_entries metadata from :ld-startup,
# which may be in a different toolchain whose $root_out_dir isn't known
# here. That uses just the bare $abi_soname as installation destination.
# That's also what goes into PT_INTERP strings for uninstrumented
# executables (that is, ones built in variants with an empty libprefix).
#
# This is further prefixed by test packaging. In the non-Fuchsia case,
# that expects a flat name and will insert the "lib/", so this isn't
# manifest rewriting step isn't really doing anything. There is no
# non-test case for non-Fuchsia,
#
# For Fuchsia, it's the full path within the package namespace. This is
# further prefixed in test cases; but there are also non-test cases. It
# needs the "lib/" path prefix, but it also needs any to append to that any
# ${toolchain_variant.libprefix} that the dependent executable() will be
# using in its PT_INTERP. That can only be added here because the
# user_basic_redirect() very well may reach a different user.basic variant
# that doesn't have the same (or any) libprefix.
distribution_manifest_prefix(dist_target) {
forward_variables_from(invoker,
[
"visibility",
"testonly",
])
deps = [ ":$redirect_target" ]
if (is_fuchsia) {
# libprefix has a trailing slash, but prefix should not.
prefix = get_path_info("lib/${toolchain_variant.libprefix}any", "dir")
}
}
}
# Leave breadcrumbs for test code to use via metadata. For non-test code,
# this just serves to reach the data_deps and metadata of various sorts.
test_elf_object(main_target) {
forward_variables_from(invoker,
[
"visibility",
"testonly",
])
deps = [ ":$dist_target" ]
}
# Get into the right toolchain and variant.
user_basic_redirect(redirect_target) {
visibility = [ ":*" ]
forward_variables_from(invoker,
[
"select",
"testonly",
])
public_deps = [ ":$verify_target" ]
}
ifs_extract(extract_target) {
visibility = [ ":*" ]
forward_variables_from(invoker, [ "testonly" ])
deps = ld_deps
outputs = [ ifs_file ]
}
verify_public_symbols(verify_target) {
visibility = [ ":*" ]
forward_variables_from(invoker, [ "testonly" ])
deps = [ ":$extract_target" ]
current = ifs_file
reference = "ld.ifs"
library_name = get_label_info(ld_label, "label_with_toolchain")
}
}
# There's no real difference between a shared library and a loadable module
# with a SONAME, but loadable_module() does its own variant selection. Since
# the stub ld.so is really just a data ABI image, there's no meaningful sense
# in which it could be in the same variant as its users.
loadable_module("ld-stub") {
visibility = [ "./*" ]
# Default output_name and output_dir produces this.
output_path = "$root_out_dir/$target_name.so"
configs += [ ":abi-soname" ]
if (is_linux) {
configs -= [ "//build/config/linux:implicit-host-libs" ]
}
# This should in fact have no dependencies of any kind, since it contains
# only data and leaf assembly code. Make sure no implicit ones sneak in.
ldflags = [ "-nostdlib" ]
deps = [
":abi",
":tlsdesc.no-gc",
]
# Since it has no C++ code (only a data definition), it doesn't really matter
# for this to use the user.basic build environment as the startup dynamic
# linker must. But we don't want it to have any instrumentation hair that
# might be generated even with no C++ functions.
exclude_toolchain_tags = [ "instrumented" ]
# Compute the ELF flavor to use in the installed file name.
if (toolchain_variant.tags + [ "ilp32" ] - [ "ilp32" ] !=
toolchain_variant.tags || current_cpu == "arm" || current_cpu == "x86" ||
current_cpu == "riscv") {
elf = "elf32le"
} else {
elf = "elf64le"
}
metadata = {
distribution_entries = [
{
# The install location doesn't depend on libprefix, since the place
# it's installed is where it's just data for a different program. But
# file name it's installed under depends on ELF class/data and machine.
# This matches ld::RemoteAbiStub<...>::kFilename with "lib/" prefixed.
destination = "lib/ld-stub-$elf-$current_cpu.so"
source = rebase_path(output_path, root_build_dir)
label = get_label_info(":$target_name", "label_with_toolchain")
},
]
}
}
ld_abi_user("ld-stub.user") {
select = true # loadable_module() will do variant selection.
deps = [ ":ld-stub" ]
}
source_set("abi") {
visibility = [ ":*" ]
public = [ "mutable-abi.h" ]
sources = [ "mutable-abi.cc" ]
deps = [ ":headers" ]
}
source_set("startup-bootstrap") {
visibility = [ ":*" ]
public = [ "startup-bootstrap.h" ]
public_deps = [
":headers",
"//src/lib/elfldltl",
# TODO(https://fxbug.dev/42080826): This gets users of startup-bootstrap.h
# the config that plumbs the HAVE_LLVM_PROFDATA predefine used there.
"//src/lib/llvm-profdata",
]
}
# This is a proper archive library of the bits from libc and the stubs that can
# be used in the standalone implementation. Having the source_set() targets as
# direct or transitive deps not via a `complete_static_lib=true` archive would
# eagerly link unused code into the standalone binary and rely on linker GC to
# remove it, which is suboptimal.
static_library("standalone") {
visibility = [
"./*",
"//src/devices/bin/driver_manager/tests/modules/*",
"//src/devices/lib/driver-host-passive-abi/*",
"//zircon/kernel/lib/userabi/*",
"//zircon/system/utest/core/threads/*",
]
complete_static_lib = true
sources = [ "standalone-assert.cc" ]
deps = [
"//sdk/lib/c/stdlib:hermetic",
"//sdk/lib/c/string:hermetic",
"//sdk/lib/c/strings:hermetic",
"//zircon/system/public",
]
if (is_linux) {
deps += [ ":linux-syscalls" ]
}
public_configs = [ "//build/config:standalone" ]
}
source_set("posix.headers") {
visibility = [ "./*" ]
public = [ "posix.h" ]
public_deps = [
":ld-log-posix.headers",
":startup-load",
"//src/lib/elfldltl",
]
}
source_set("zircon.headers") {
visibility = [ "./*" ]
public = [ "zircon.h" ]
public_deps = [
":ld-log-zircon.headers",
":startup-load",
"//src/lib/elfldltl",
]
}
source_set("zircon") {
visibility = [ ":*" ]
public_deps = [ ":zircon.headers" ]
sources = [
"ldsvc.cc",
"procargs.cc",
]
deps = [
":processargs",
":startup-diagnostics",
"//zircon/system/ulib/ldmsg",
"//zircon/system/ulib/zircon-internal",
]
}
source_set("startup-load") {
visibility = [ ":*" ]
public = [ "startup-load.h" ]
public_deps = [
":abi",
":allocator",
":headers",
":startup-bootstrap",
":startup-diagnostics.headers",
"//src/lib/elfldltl",
"//zircon/system/ulib/fbl",
]
}
shared_library("ld-startup") {
visibility = [ ":*" ]
configs += [ ":abi-soname" ]
sources = []
deps = [
":standalone",
":startup-diagnostics",
":startup-load",
":startup-ubsan",
":tlsdesc",
]
if (is_fuchsia) {
sources += [
"zircon-startup.S",
"zircon-startup.cc",
]
deps += [
":fuchsia-debugdata",
":zircon",
"//src/lib/llvm-profdata",
"//src/zircon/lib/zircon",
"//zircon/kernel/lib/arch",
"//zircon/system/ulib/zx",
]
} else {
sources += [
"posix-startup.S",
"posix-startup.cc",
]
deps += [
":posix.headers",
"//zircon/kernel/lib/arch:headers",
]
# There is no way to get the data out anyway.
configs += [ "//build/config:no_profile" ]
}
output_path = "$root_out_dir/lib$target_name.so"
metadata = {
distribution_entries = [
{
destination = abi_soname # Rewritten by ld_abi_user().
source = rebase_path(output_path, root_build_dir)
label = get_label_info(":$target_name", "label_with_toolchain")
},
]
}
}
source_set("startup-diagnostics.headers") {
visibility = [ ":*" ]
public = [ "startup-diagnostics.h" ]
public_deps = [
":headers",
"//src/lib/elfldltl",
]
}
source_set("startup-diagnostics") {
visibility = [ ":*" ]
sources = [ "startup-diagnostics.cc" ]
deps = [
":startup-diagnostics.headers",
"//sdk/lib/c/stdio/printf_core:wrapper",
]
if (is_fuchsia) {
deps += [
":ld-log-zircon",
":zircon.headers",
]
} else {
deps += [
":ld-log-posix",
":posix.headers",
]
}
}
# The full ubsan runtime is likely to depend on more libc functions than
# the standalone ld-startup build provides. Instead instantiate
# ubsan-custom to provide a bespoke ubsan runtime that reuses the normal
# diagnostics code.
source_set("startup-ubsan") {
visibility = [ ":*" ]
if (is_ubsan) {
sources = [ "startup-ubsan.cc" ]
deps = [
":abi-headers",
":startup-diagnostics",
"//src/lib/ubsan-custom:handlers",
]
# ubsan-custom:handlers confers the no-ubsan config via public_configs so
# the headers can't use `#if __has_feature(undefined_behavior_sanitizer)`
# alone. Provide a macro so they can do `#if ... || LD_UBSAN` instead.
defines = [ "LD_UBSAN=1" ]
}
}
source_set("allocator") {
visibility = [ ":*" ]
public = [ "allocator.h" ]
public_deps = [
":startup-diagnostics.headers",
"//src/lib/trivial-allocator",
]
deps = [ "//src/lib/trivial-allocator:stub-delete" ]
}
source_set("linux-syscalls") {
visibility = [ ":*" ]
sources = [ "linux-syscalls.cc" ]
include_dirs = [ "//third_party/linux-syscall-support/src" ]
deps = [ "//sdk/lib/c/stdio:snprintf" ]
}
library_headers("fuchsia-debugdata.headers") {
headers = [ "lib/ld/fuchsia-debugdata.h" ]
}
static_library("fuchsia-debugdata") {
public_deps = [ ":fuchsia-debugdata.headers" ]
sources = [ "fuchsia-debugdata.cc" ]
deps = [
"//sdk/lib/fidl_base",
"//zircon/system/ulib/zx",
]
}
library_headers("processargs") {
headers = [ "lib/ld/processargs.h" ]
public_deps = [
"//sdk/lib/fdio:constants",
"//zircon/system/ulib/zx",
]
}
# The TLSDESC runtime assembly code is provided in library form as needed for
# use by an in-process dynamic linker (ld-startup and libdl). The stub dynamic
# linker includes this code but with neither references nor exported symbols,
# so it needs a mandatory-linked version of the code that's immune to GC.
template("_tlsdesc_runtime") {
target(invoker.target_type, target_name) {
public_deps = [
":headers",
"//zircon/kernel/lib/arch",
]
sources = [
"tlsdesc-runtime-static.S",
"tlsdesc-runtime-undefined-weak.S",
]
defines = [ "TLSDESC_RETAIN=${invoker.retain}" ]
}
}
_tlsdesc_runtime("tlsdesc") {
target_type = "static_library"
retain = ""
}
_tlsdesc_runtime("tlsdesc.no-gc") {
target_type = "source_set"
retain = "R"
}
ld_abi_user("ld-startup.user") {
deps = [ ":ld-startup" ]
}
group("tests") {
testonly = true
deps = [
"test:tests",
# These just get the images built and their ABIs verified.
":ld-startup.user",
":ld-stub.user",
]
}