[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