[zircon] [gn] Build userboot
This brings in loadable_module() build support. userboot is built in its
own bespoke environment and needs some bespoke actions using toolchain
utilities like nm, so some utility templates are provided for that pattern.
Bug: BLD-325
Test: gn gen && ninja
Change-Id: I2a246980dca36cd0a730656a621192e5475325d0
diff --git a/zircon/BUILD.gn b/zircon/BUILD.gn
index 3cf4320..d4c2d78 100644
--- a/zircon/BUILD.gn
+++ b/zircon/BUILD.gn
@@ -93,6 +93,7 @@
cpu = cpu
deps = [
# TODO: This indirectly reaches everything that builds so far.
+ "$zx/system/core/userboot",
"$zx/system/ulib/c",
]
}
diff --git a/zircon/make/module-userlib.mk b/zircon/make/module-userlib.mk
index d0a5d70..d59fee5 100644
--- a/zircon/make/module-userlib.mk
+++ b/zircon/make/module-userlib.mk
@@ -115,7 +115,7 @@
-shared -soname $(_SONAME) -s \
$< $(_LIBS) -o $(@:.abi.stamp=.so.abi).new
# Sanity check that the ABI stub really matches the actual DSO.
- $(NOECHO)$(SHELLEXEC) scripts/shlib-symbols '$(NM)' $(@:.abi.stamp=.so.abi).new | \
+ $(NOECHO)$(SHELLEXEC) scripts/shlib-symbols '$(NM)' $(@:.abi.stamp=.so.abi).new /dev/stdout | \
cmp $(<:.o=.h) -
# Move it into place only if it's changed.
$(NOECHO)\
@@ -127,7 +127,7 @@
$(NOECHO)touch $@
$(MODULE_LIBNAME).abi.h: $(MODULE_LIBNAME).so scripts/shlib-symbols
- $(NOECHO)$(SHELLEXEC) scripts/shlib-symbols -z '$(NM)' $< > $@
+ $(NOECHO)$(SHELLEXEC) scripts/shlib-symbols '$(NM)' -z $< $@
$(MODULE_LIBNAME).abi.o: $(MODULE_LIBNAME).abi.h scripts/dso-abi.h
$(NOECHO)$(CC) $(GLOBAL_COMPILEFLAGS) $(ARCH_COMPILEFLAGS) \
diff --git a/zircon/public/gn/BUILDCONFIG.gn b/zircon/public/gn/BUILDCONFIG.gn
index 857ca83..60e83d74 100644
--- a/zircon/public/gn/BUILDCONFIG.gn
+++ b/zircon/public/gn/BUILDCONFIG.gn
@@ -574,7 +574,7 @@
}
###
-### "Terminal" (executable) target types.
+### "Terminal" (executable and loadable_module) target types.
### These are the targets that do variant selection.
###
@@ -1047,7 +1047,7 @@
}
}
-# Build a C/C++ executable to run on the target Fuchsia system.
+# Build a C/C++ executable to run on the target system.
#
# Parameters
#
@@ -1189,6 +1189,104 @@
}
}
+# Build a C/C++ loadable module to run on the target system.
+#
+# Parameters
+#
+# install_path
+# Optional: If false (the default), nothing happens--the binary won't
+# be installed anywhere automatically. Set this to a string containing
+# the install path where this module will appear in a BOOTFS
+# or package filesystem image built containing it.
+# Default: false
+# Type: string or false
+#
+# See `gn help loadable_module()` for additional parameters and details.
+template("loadable_module") {
+ assert(!is_kernel)
+ assert(!is_host)
+ _variant_target(target_name) {
+ # This is for _variant_target().
+ target = {
+ shlib = true
+ match = "loadable_module"
+ type = "_shlib_toolchain_target"
+ }
+
+ # This is for _shlib_toolchain_target().
+ target_type = "loadable_module"
+ forward_variables_from(invoker,
+ "*",
+ [
+ "metadata",
+ "target_type",
+ "testonly",
+ "visibility",
+ ])
+ forward_variables_from(invoker,
+ [
+ "testonly",
+ "visibility",
+ ])
+
+ # Elaborate the toolchain's defaults to compute the output file name.
+ if (!defined(output_name)) {
+ output_name = target_name
+ }
+ output_name += toolchain.output_name_suffix
+ if (!defined(output_extension)) {
+ output_extension = "so"
+ }
+ output_file = "$target_out_dir/$output_name"
+ if (output_extension != "") {
+ output_file += ".$output_extension"
+ }
+
+ metadata = {
+ if (defined(invoker.metadata)) {
+ forward_variables_from(invoker.metadata, "*")
+ }
+
+ # Every terminal target provides these metadata keys. The first is
+ # used the data key for the output of the link, as a file name
+ # relative to $root_build_dir appropriate for command-line
+ # contexts. The second is used as a walk key to provide a
+ # dependency barrier against e.g. shared_library() deps or other
+ # executable() data_deps.
+ link_output = [ rebase_path(output_file + toolchain.link_output_suffix,
+ root_build_dir) ]
+ link_barrier = []
+ if (current_os != "mac" && current_os != "win") {
+ elf_link_output = link_output
+ }
+ }
+
+ if (defined(invoker.install_path)) {
+ target.main_metadata = {
+ manifest_inputs = [ output_file ]
+ manifest_lines =
+ [ "${install_path}=" + rebase_path(output_file, root_build_dir) ]
+ }
+
+ # Also define an alias with the variant suffix. _variant_target will
+ # make this redirect to the specific variant toolchain chosen for
+ # this target. In only that toolchain, the metadata will give the
+ # binary a second install path with the variant suffix.
+ extension = get_path_info(invoker.install_path, "extension")
+ if (extension != "") {
+ extension = ".$extension"
+ }
+ target.variant_metadata = {
+ manifest_inputs = [ output_file ]
+ manifest_lines = [ get_path_info(invoker.install_path, "dir") + "/" +
+ get_path_info(invoker.install_path, "name") +
+ toolchain.variant_suffix + extension + "=" +
+ rebase_path(output_file, root_build_dir) ]
+ }
+ }
+ }
+}
+
###
### set_defaults()
###
@@ -1207,6 +1305,7 @@
"executable",
"host_tool",
"library",
+ "loadable_module",
"shared_library",
"source_set",
"static_library",
diff --git a/zircon/public/gn/toolchain/c_utils.gni b/zircon/public/gn/toolchain/c_utils.gni
new file mode 100644
index 0000000..1a73ff5
--- /dev/null
+++ b/zircon/public/gn/toolchain/c_utils.gni
@@ -0,0 +1,121 @@
+# Copyright 2019 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.
+
+# Like action() but using utility binaries from the C/C++ toolchain.
+# This is the way to use `nm`, `objcopy`, etc.
+#
+# Parameters
+#
+# utils
+# Required: Utilities to use, e.g. "nm", "objcopy", "objdump".
+# Type: list(string)
+#
+# script
+# Optional: Same as for action(). The script receives an implicit
+# argument for each element in $utils giving the command to run.
+# If omitted, then $utils must have only one element and that command
+# itself will be run with $args just as given.
+# Type: file
+#
+# args
+# Required: Same as for action(). If there is a $script,
+# the implicit $utils arguments precede $args.
+# Type: list(string)
+#
+# deps, inputs, outputs, sources, testonly, visibility
+# See action().
+#
+template("toolchain_utils_action") {
+ assert(defined(invoker.utils),
+ "toolchain_utils_action(\"$target_name\") requires `utils`")
+ forward_variables_from(invoker, [ "script" ])
+
+ utils_inputs = []
+ utils_paths = []
+ if (toolchain.tool_dir != "") {
+ # The toolchain binaries are in a specified location (e.g. a prebuilt).
+ foreach(util, invoker.utils) {
+ if (util == "cc") {
+ util = toolchain.cc
+ } else {
+ util = "${toolchain.tool_prefix}${util}"
+ }
+ util = "${toolchain.tool_dir}/${util}"
+ utils_inputs += [ util ]
+ utils_paths += [ rebase_path(util, root_build_dir) ]
+ }
+ } else {
+ # The toolchain binaries are just found in the system PATH.
+ # They won't be used as dependencies.
+ if (!defined(script)) {
+ # If there's no script, utils_paths[0] will become the script.
+ # But the script has to be a known location that's a dependency.
+ # So use a dummy wrapper as the script.
+ script = "/usr/bin/env"
+ }
+ foreach(util, invoker.utils) {
+ if (util == "cc") {
+ util = toolchain.cc
+ } else {
+ util = "${toolchain.tool_prefix}${util}"
+ }
+ utils_paths += [ util ]
+ }
+ }
+
+ action(target_name) {
+ forward_variables_from(invoker,
+ "*",
+ [
+ "args",
+ "script",
+ "utils",
+ ])
+ if (!defined(inputs)) {
+ inputs = []
+ }
+ inputs += utils_inputs
+ if (defined(script)) {
+ args = utils_paths + invoker.args
+ } else {
+ script = rebase_path(utils_paths[0], "", root_build_dir)
+ assert(
+ utils_paths == [ utils_paths[0] ],
+ "toolchain_utils_action(\"$target_name\") without `script` must have exactly one element in `utils`")
+ args = invoker.args
+ }
+ }
+}
+
+# generated_file() containing output file name of a linking target.
+#
+# Parameters
+#
+# deps, outputs, testonly, visibility
+# See generated_file(). $deps determines the contents as described below.
+#
+# This produces a generated_file() target with $outputs as given. The
+# contents of the file list the link outputs reached by $deps. That is,
+# for each target that links a binary (executable(), loadable_module(),
+# shared_library() via library(), etc.) the path relative to
+# $root_build_dir of the actual binary created by the linker (before
+# stripping) will be listed, one per line. The dependency traversal is
+# pruned at each linking target, so e.g. if an executable() is listed then
+# neither its shared_library() dependencies nor any $data_deps leading to
+# loadable_module() or executable() targets will be listed.
+#
+template("link_output_rspfile") {
+ generated_file(target_name) {
+ forward_variables_from(invoker,
+ [
+ "deps",
+ "outputs",
+ "testonly",
+ "visibility",
+ ])
+ data_keys = [ "link_output" ]
+ walk_keys = [ "link_barrier" ]
+ output_conversion = "list lines"
+ }
+}
diff --git a/zircon/scripts/shlib-symbols b/zircon/scripts/shlib-symbols
index 9fcf4d2..da1ce64 100755
--- a/zircon/scripts/shlib-symbols
+++ b/zircon/scripts/shlib-symbols
@@ -35,6 +35,18 @@
set -o pipefail -e
fi
+usage() {
+ echo >&2 "Usage: $0 NM [-a] [-z] DSO OUTPUT [DEPFILE]"
+ exit 2
+}
+
+if [ $# -lt 3 ]; then
+ usage
+fi
+
+NM="$1"
+shift
+
show_address=false
zero_function_size=false
while [ $# -gt 0 ]; do
@@ -46,13 +58,26 @@
shift
done
-if [ $# -ne 2 ]; then
- echo >&2 "Usage: $0 [-a] [-z] NM DSO"
- exit 2
+if [ $# -ne 2 -a $# -ne 3 ]; then
+ usage
fi
-NM="$1"
-DSO="$2"
+DSO="$1"
+OUTPUT="$2"
+DEPFILE="$3"
+
+USED_FILES=()
+if [[ "$DSO" == @* ]]; then
+ rspfile="${DSO#@}"
+ DSO="$(< "$rspfile")"
+ USED_FILES+=("$rspfile" "$DSO")
+else
+ USED_FILES+=("$DSO")
+fi
+
+if [ -n "$DEPFILE" ]; then
+ echo "$OUTPUT: ${USED_FILES[*]}" > "$DEPFILE"
+fi
dump_names() {
"$NM" -P -g -D -S "$DSO"
@@ -113,4 +138,5 @@
done
}
-dump_names | massage
+trap 'rm -f "$OUTPUT" ${DEPFILE:+$"DEPFILE"}' ERR HUP INT TERM
+dump_names | massage > "$OUTPUT"
diff --git a/zircon/system/core/userboot/BUILD.gn b/zircon/system/core/userboot/BUILD.gn
new file mode 100644
index 0000000..0d322cb
--- /dev/null
+++ b/zircon/system/core/userboot/BUILD.gn
@@ -0,0 +1,154 @@
+# Copyright 2019 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("$zx/public/gn/config/levels.gni")
+import("$zx/public/gn/config/standard.gni")
+import("$zx/public/gn/toolchain/c_utils.gni")
+import("$zx/public/gn/toolchain/environment.gni")
+import("$zx/public/gn/toolchain/environment_redirect.gni")
+
+# userboot gets its own toolchain for its special build requirements.
+if (current_toolchain == default_toolchain) {
+ # Define the special toolchain itself only in the default toolchain.
+ foreach(cpu, standard_fuchsia_cpus) {
+ environment("userboot") {
+ cpu = cpu
+ configs += standard_fuchsia_configs + [ ":userboot_config" ]
+
+ # This doesn't get a separate shlib sub-toolchain, but the main one
+ # can do loadable_module().
+ solink = true
+
+ if (opt_level < 2) {
+ # userboot doesn't stay sufficiently pure without optimization.
+ toolchain_args = {
+ opt_level = 2
+ }
+ }
+
+ # userboot can't use any instrumentation runtimes.
+ exclude_variant_tags = [ "instrumented" ]
+ }
+ }
+} else if (toolchain.environment == "userboot") {
+ # Everything in userboot gets compiled this way.
+ config("userboot_config") {
+ configs = [
+ "$zx/public/gn/config:no_sanitizers",
+ "$zx/public/gn/config:user",
+ ]
+
+ # Everything is statically linked together with no PLT or GOT.
+ # No $inputs needed here since the depfile will list it.
+ cflags = [
+ "-include",
+ rebase_path("$zx/kernel/include/hidden.h", root_build_dir),
+ ]
+ defines = [
+ "HIDDEN",
+ "WITH_LZ4_NOALLOC",
+ ]
+ }
+
+ # This is the output of target ":gen-vdso-syms-ld", below.
+ vdso_syms_ld = "$target_gen_dir/vdso-syms.ld"
+
+ # This file holds the name of the vDSO binary to extract symbols from.
+ vdso_syms_rspfile = "$target_gen_dir/vdso-syms.h.rsp"
+
+ # userboot is a reentrant DSO (no writable segment) with an entry point.
+ loadable_module("userboot") {
+ sources = [
+ "bootdata.c",
+ "bootfs.c",
+ "loader-service.c",
+ "option.c",
+ "start.c",
+ "userboot-elf.c",
+ "util.c",
+ ]
+ libs = [ vdso_syms_ld ]
+ deps = [
+ ":gen-vdso-syms-ld",
+ "$zx/system/ulib/bootdata",
+ "$zx/system/ulib/elfload",
+ "$zx/system/ulib/ldmsg",
+ "$zx/system/ulib/runtime",
+ "$zx/system/ulib/zircon:headers",
+ "$zx/third_party/ulib/lz4",
+ "$zx/third_party/ulib/musl/src/string:minimal_str",
+ "$zx/third_party/ulib/musl/src/string:stdmem",
+ ]
+ }
+
+ # This generated header lists all the ABI symbols in the vDSO with their
+ # addresses. It's used to generate vdso-syms.ld, below.
+ toolchain_utils_action("gen-vdso-syms-header") {
+ visibility = [ ":gen-vdso-syms-ld" ]
+ sources = [
+ vdso_syms_rspfile,
+ ]
+ deps = [
+ ":gen-vdso-syms-header-rspfile",
+ ]
+ outputs = [
+ "$target_gen_dir/vdso-syms.h",
+ ]
+ depfile = "${outputs[0]}.d"
+ utils = [ "nm" ]
+ script = "$zx/scripts/shlib-symbols"
+ args = [
+ "-a",
+ "@" + rebase_path(vdso_syms_rspfile, root_build_dir),
+ rebase_path(outputs[0], root_build_dir),
+ rebase_path(depfile, root_build_dir),
+ ]
+ }
+
+ link_output_rspfile("gen-vdso-syms-header-rspfile") {
+ visibility = [ ":gen-vdso-syms-header" ]
+ deps = [
+ "$zx/system/ulib/zircon",
+ ]
+ outputs = [
+ vdso_syms_rspfile,
+ ]
+ }
+
+ # This generated linker script defines symbols for each vDSO entry point
+ # giving the relative address where it will be found at runtime. With
+ # this hack, the userboot code doesn't need to do any special work to
+ # find the vDSO and its entry points, keeping the code far simpler.
+ toolchain_utils_action("gen-vdso-syms-ld") {
+ visibility = [ ":*" ]
+ outputs = [
+ vdso_syms_ld,
+ ]
+ deps = [
+ ":gen-vdso-syms-header",
+ ]
+ sources = get_target_outputs(deps[0])
+ inputs = [
+ "vdso-syms.ld.h",
+ ]
+ utils = [ "cc" ]
+ args = [
+ "-o",
+ rebase_path(outputs[0], root_build_dir),
+ "-E",
+ "-P",
+ "-include",
+ rebase_path(inputs[0], root_build_dir),
+ rebase_path(sources[0], root_build_dir),
+ ]
+ }
+} else {
+ # In any other toolchain, just redirect to the proper toolchain.
+ environment_redirect("userboot") {
+ environment_label = ":userboot"
+ deps = [
+ ":userboot",
+ ]
+ }
+}
diff --git a/zircon/system/core/userboot/rules.mk b/zircon/system/core/userboot/rules.mk
index 06ebc4f..b26f4ec 100644
--- a/zircon/system/core/userboot/rules.mk
+++ b/zircon/system/core/userboot/rules.mk
@@ -77,7 +77,7 @@
$(BUILDDIR)/$(LOCAL_DIR)/vdso-syms.h: $(BUILDDIR)/system/ulib/zircon/libzircon.so
@$(MKDIR)
$(call BUILDECHO,generating $@)
- $(NOECHO)$(SHELLEXEC) scripts/shlib-symbols -a '$(NM)' $< > $@
+ $(NOECHO)$(SHELLEXEC) scripts/shlib-symbols '$(NM)' -a $< $@
GENERATED += $(BUILDDIR)/$(LOCAL_DIR)/vdso-syms.h
# This generated linker script defines symbols for each vDSO entry point
diff --git a/zircon/system/ulib/bootdata/BUILD.gn b/zircon/system/ulib/bootdata/BUILD.gn
new file mode 100644
index 0000000..ce26fb5
--- /dev/null
+++ b/zircon/system/ulib/bootdata/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2019 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.
+
+library("bootdata") {
+ sources = [
+ "decompress.c",
+ ]
+ deps = [
+ "$zx/system/ulib/c",
+ "$zx/system/ulib/zircon",
+ "$zx/third_party/ulib/lz4",
+ ]
+}
diff --git a/zircon/system/ulib/elfload/BUILD.gn b/zircon/system/ulib/elfload/BUILD.gn
new file mode 100644
index 0000000..a7760d4
--- /dev/null
+++ b/zircon/system/ulib/elfload/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2019 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.
+
+library("elfload") {
+ sources = [
+ "elf-load.c",
+ ]
+ deps = [
+ "$zx/system/ulib/zircon",
+ ]
+}
diff --git a/zircon/third_party/ulib/musl/src/string/BUILD.gn b/zircon/third_party/ulib/musl/src/string/BUILD.gn
index 3eb2b2d..6a85d22 100644
--- a/zircon/third_party/ulib/musl/src/string/BUILD.gn
+++ b/zircon/third_party/ulib/musl/src/string/BUILD.gn
@@ -59,7 +59,7 @@
if (current_cpu == "arm64") {
sources += [
"$zx/third_party/lib/cortex-strings/src/aarch64/memchr.S",
- "$zx/third_party/lib/cortex-strings/src/aarch64/memcmp.S",
+ "aarch64/memcmp.S",
]
} else {
sources += [
diff --git a/zircon/third_party/ulib/musl/src/string/aarch64/memcmp.S b/zircon/third_party/ulib/musl/src/string/aarch64/memcmp.S
new file mode 100644
index 0000000..c6c40a4
--- /dev/null
+++ b/zircon/third_party/ulib/musl/src/string/aarch64/memcmp.S
@@ -0,0 +1,14 @@
+// Copyright 2019 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.
+
+#include "asm.h"
+
+// The memcmp implementation is verbatim from cortex-strings.
+// But for userboot we need to make the symbol hidden.
+
+#include "third_party/lib/cortex-strings/src/aarch64/memcmp.S"
+
+#ifdef HIDDEN
+ .hidden memcmp
+#endif