| # 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", |
| ] |
| } |