[userboot] Use hermetic-decompressor for BOOTFS decompression

The hermetic module is embedded in the kernel and turned into a
VMO passed to userboot, which can use the library normally with
a little plumbing.  The module VMO is later available for reuse
at /boot/kernel/lib/hermetic/decompress-zbi.so.

Change-Id: I4e777771700c56728d1afb0d9125403a9653d065
diff --git a/zircon/kernel/lib/userabi/BUILD.gn b/zircon/kernel/lib/userabi/BUILD.gn
index ed44b05..5b8cf08 100644
--- a/zircon/kernel/lib/userabi/BUILD.gn
+++ b/zircon/kernel/lib/userabi/BUILD.gn
@@ -55,6 +55,11 @@
 #     and no other linking targets; see link_output_rspfile().
 #     - Type: list(label)
 #
+#   writable
+#     - Optional: Writable segments allowed (not for userboot or vDSO!).
+#     - Type: bool
+#     - Default: false
+#
 template("rodso") {
   rspfile_target = "$target_name.rsp"
   rspfile = "$target_gen_dir/$rspfile_target"
@@ -104,6 +109,11 @@
         "--depfile",
         rebase_path(depfile, root_build_dir),
         rebase_path(header, root_build_dir),
+      ]
+      if (defined(invoker.writable) && invoker.writable) {
+        args += [ "--writable" ]
+      }
+      args += [
         invoker.name,
         "@" + rebase_path(rspfile, root_build_dir),
       ]
@@ -138,11 +148,16 @@
       # on the toolchain selected.  Fortunately, we already depend on the
       # generated $header, which is always regenerated whenever the image
       # binary itself changes.  So this indirect dependency is sufficient.
+      if (defined(invoker.writable) && invoker.writable) {
+        code_or_data = "DATA"
+      } else {
+        code_or_data = "CODE"
+      }
       write_file(sources[0],
                  [
                    "#include \"rodso-asm.h\"",
                    "#include \"$header\"",
-                   "RODSO_IMAGE($rodso_name, ${invoker.name})",
+                   "RODSO_IMAGE($rodso_name, ${invoker.name}, $code_or_data)",
                  ])
     }
   } else {
@@ -249,6 +264,8 @@
     ]
     include_dirs = [ target_gen_dir ]
     deps = [
+      ":decompress_zbi-code.h",
+      ":decompress_zbi.image",
       ":headers",
       ":rodso",
       ":userboot-code.h",
@@ -267,4 +284,15 @@
       "userboot",
     ]
   }
+
+  rodso("decompress_zbi") {
+    visibility = [ ":*" ]
+    name = "DECOMPRESS_ZBI"
+    deps = [
+      "$zx/system/ulib/hermetic-decompressor:decompress-lz4f",
+    ]
+
+    # It's hermetic, but it's allowed to have data and bss.
+    writable = true
+  }
 }
diff --git a/zircon/kernel/lib/userabi/gen-rodso-code.sh b/zircon/kernel/lib/userabi/gen-rodso-code.sh
index 94736d5..38d9f69 100755
--- a/zircon/kernel/lib/userabi/gen-rodso-code.sh
+++ b/zircon/kernel/lib/userabi/gen-rodso-code.sh
@@ -17,7 +17,8 @@
 # of entries in the table.
 
 usage() {
-  echo >&2 "Usage: $0 NM READELF [--depfile DEPFILE] OUTFILE {NAME DSO}..."
+  echo >&2 "\
+Usage: $0 NM READELF [--depfile DEPFILE] [--writable] OUTFILE {NAME DSO}..."
   exit 2
 }
 
@@ -104,10 +105,14 @@
       echo "#define ${1}_CODE_START $vaddr" >> $OUTFILE
       echo "#define ${1}_CODE_END (((${1}_CODE_START + $filesz + (1 << PAGE_SIZE_SHIFT) - 1) >> PAGE_SIZE_SHIFT) << PAGE_SIZE_SHIFT)" >> $OUTFILE
       ;;
-    # Make sure there's no writable segment.
+    # Make sure there's no writable segment unless --writable allows it.
     *LOAD*W*)
-      echo >&2 "$0: writable segment: $line"
-      exit 1
+      $WRITABLE || {
+        echo >&2 "$0: writable segment: $line"
+        exit 1
+      }
+      echo "#define ${1}_DATA_START $vaddr" >> $OUTFILE
+      echo "#define ${1}_DATA_END (((${1}_DATA_START + $filesz + (1 << PAGE_SIZE_SHIFT) - 1) >> PAGE_SIZE_SHIFT) << PAGE_SIZE_SHIFT)" >> $OUTFILE
       ;;
     # Section header for .dynsym, e.g.:
     #  [ 3] .dynsym           DYNSYM          0000000000001268 001268 000018 18   A  6   1  8
@@ -146,6 +151,13 @@
 trap 'rm -f "$OUTFILE" ${DEPFILE:+"$DEPFILE"}' ERR HUP INT TERM
 
 while [ $# -gt 0 ]; do
+  if [ "$1" = --writable ]; then
+    WRITABLE=true
+    shift
+  else
+    WRITABLE=false
+  fi
+
   if [ $# -lt 2 ]; then
     usage
   fi
diff --git a/zircon/kernel/lib/userabi/include/lib/userabi/rodso.h b/zircon/kernel/lib/userabi/include/lib/userabi/rodso.h
index e9dbe3e..f20bd5e 100644
--- a/zircon/kernel/lib/userabi/include/lib/userabi/rodso.h
+++ b/zircon/kernel/lib/userabi/include/lib/userabi/rodso.h
@@ -9,41 +9,52 @@
 #include <object/handle.h>
 #include <object/vm_object_dispatcher.h>
 
-// An RoDso object describes one DSO image built with the rodso.ld layout.
-class RoDso {
+// An EmbeddedVmo object describes a page-aligned file embedded in the kernel.
+class EmbeddedVmo {
 public:
-    const fbl::RefPtr<VmObjectDispatcher>& vmo() const {
-        return vmo_;
-    }
+    EmbeddedVmo() = delete;
+    EmbeddedVmo(const char* name, const void* image, size_t size);
+
+    const auto& vmo() const { return vmo_; }
     HandleOwner vmo_handle() const;
 
     size_t size() const { return size_; }
 
-    bool valid_code_mapping(uint64_t vmo_offset, size_t size) const {
-        return vmo_offset == code_start_ && size == size_ - code_start_;
-    }
-
-    zx_status_t Map(fbl::RefPtr<VmAddressRegionDispatcher> vmar,
-                    size_t offset) const;
-
 protected:
-
-    RoDso(const char* name, const void* image, size_t size,
-          uintptr_t code_start);
-
     zx_rights_t vmo_rights() const { return vmo_rights_; }
 
-private:
-
     zx_status_t MapSegment(fbl::RefPtr<VmAddressRegionDispatcher> vmar,
                            bool code,
                            size_t vmar_offset,
                            size_t start_offset,
                            size_t end_offset) const;
 
+private:
     const char* name_;
     fbl::RefPtr<VmObjectDispatcher> vmo_;
     zx_rights_t vmo_rights_;
-    uintptr_t code_start_;
     size_t size_;
 };
+
+// An RoDso object describes one DSO image built with the rodso.ld layout.
+class RoDso : public EmbeddedVmo {
+public:
+    bool valid_code_mapping(uint64_t vmo_offset, size_t code_size) const {
+        return vmo_offset == code_start_ && code_size == size() - code_start_;
+    }
+
+    zx_status_t Map(fbl::RefPtr<VmAddressRegionDispatcher> vmar,
+                    size_t offset) const;
+
+protected:
+    RoDso(const char* name, const void* image, size_t size,
+          uintptr_t code_start) : EmbeddedVmo(name, image, size),
+                                  code_start_(code_start) {
+        DEBUG_ASSERT(code_start > 0);
+        DEBUG_ASSERT(code_start < size);
+        DEBUG_ASSERT(IS_PAGE_ALIGNED(code_start));
+    }
+
+private:
+    uintptr_t code_start_;
+};
diff --git a/zircon/kernel/lib/userabi/include/lib/userabi/userboot.h b/zircon/kernel/lib/userabi/include/lib/userabi/userboot.h
index ec1ae2d0..65c0ed5 100644
--- a/zircon/kernel/lib/userabi/include/lib/userabi/userboot.h
+++ b/zircon/kernel/lib/userabi/include/lib/userabi/userboot.h
@@ -42,11 +42,13 @@
     kFirstVdso,
     kLastVdso = kFirstVdso + static_cast<uint32_t>(VdsoVariant::COUNT) - 1,
 
+    // These get passed along to userland to be recognized by ZX_PROP_NAME.
+    // The decompressor is also used by userboot itself.
     // The remainder are VMO handles that userboot doesn't care about.
-    // They just get passed along to userland to be recognized by ZX_PROP_NAME.
-    kFirstKernelFile,
+    kUserbootDecompressor,
+    kFirstKernelFile = kUserbootDecompressor,
 
-    kCrashlog = kFirstKernelFile,
+    kCrashlog,
     kCounterNames,
     kCounters,
 #if ENABLE_ENTROPY_COLLECTOR_TEST
diff --git a/zircon/kernel/lib/userabi/rodso-asm.h b/zircon/kernel/lib/userabi/rodso-asm.h
index 86b1fc1d..90012ea 100644
--- a/zircon/kernel/lib/userabi/rodso-asm.h
+++ b/zircon/kernel/lib/userabi/rodso-asm.h
@@ -17,7 +17,7 @@
     // that uses C syntax like 1L instead of plain integers
     // and arithmetic operations that the assembler can handle.
     .if \size % (1 << PAGE_SIZE_SHIFT)
-        .error "\name size \size is not multiple of PAGE_SIZE"
+        .error "\name size \size is not a multiple of PAGE_SIZE"
     .endif
     .section .rodata.rodso_image.\name,"a"
     .p2align PAGE_SIZE_SHIFT
@@ -25,8 +25,8 @@
 
 // The whole thing can't be just an assembler macro because a macro
 // operand cannot be a string like .incbin needs for the filename.
-#define RODSO_IMAGE(name, NAME) \
-    rodso_image_section name, NAME##_CODE_END; \
+#define RODSO_IMAGE(name, NAME, CODE_or_DATA) \
+    rodso_image_section name, NAME##_##CODE_or_DATA##_END; \
     DATA(name##_image) \
-    .incbin NAME##_FILENAME, 0, NAME##_CODE_END; \
+    .incbin NAME##_FILENAME, 0, NAME##_##CODE_or_DATA##_END; \
     END_DATA(name##_image)
diff --git a/zircon/kernel/lib/userabi/rodso.cc b/zircon/kernel/lib/userabi/rodso.cc
index 0ae41ba..b260296 100644
--- a/zircon/kernel/lib/userabi/rodso.cc
+++ b/zircon/kernel/lib/userabi/rodso.cc
@@ -15,18 +15,15 @@
 #include <object/vm_address_region_dispatcher.h>
 #include <object/vm_object_dispatcher.h>
 
-RoDso::RoDso(const char* name, const void* image, size_t size,
-             uintptr_t code_start)
-    : name_(name), code_start_(code_start), size_(size) {
+EmbeddedVmo::EmbeddedVmo(const char* name, const void* image, size_t size) :
+    name_(name), size_(size) {
     DEBUG_ASSERT(IS_PAGE_ALIGNED(size));
-    DEBUG_ASSERT(IS_PAGE_ALIGNED(code_start));
-    DEBUG_ASSERT(code_start > 0);
-    DEBUG_ASSERT(code_start < size);
     fbl::RefPtr<Dispatcher> dispatcher;
 
     // create vmo out of ro data mapped in kernel space
     fbl::RefPtr<VmObject> vmo;
-    zx_status_t status = VmObjectPaged::CreateFromWiredPages(image, size, true, &vmo);
+    zx_status_t status =
+        VmObjectPaged::CreateFromWiredPages(image, size, true, &vmo);
     ASSERT(status == ZX_OK);
 
     // build and point a dispatcher at it
@@ -42,17 +39,17 @@
     vmo_rights_ |= ZX_RIGHT_EXECUTE;
 }
 
-HandleOwner RoDso::vmo_handle() const {
+HandleOwner EmbeddedVmo::vmo_handle() const {
     return Handle::Make(vmo_, vmo_rights_);
 }
 
 // Map one segment from our VM object.
-zx_status_t RoDso::MapSegment(fbl::RefPtr<VmAddressRegionDispatcher> vmar,
-                              bool code,
-                              size_t vmar_offset,
-                              size_t start_offset,
-                              size_t end_offset) const {
-
+zx_status_t EmbeddedVmo::MapSegment(
+    fbl::RefPtr<VmAddressRegionDispatcher> vmar,
+    bool code,
+    size_t vmar_offset,
+    size_t start_offset,
+    size_t end_offset) const {
     uint32_t flags = ZX_VM_SPECIFIC | ZX_VM_PERM_READ;
     if (code)
         flags |= ZX_VM_PERM_EXECUTE;
@@ -85,6 +82,6 @@
     zx_status_t status = MapSegment(vmar, false, offset, 0, code_start_);
     if (status == ZX_OK)
         status = MapSegment(ktl::move(vmar), true,
-                            offset + code_start_, code_start_, size_);
+                            offset + code_start_, code_start_, size());
     return status;
 }
diff --git a/zircon/kernel/lib/userabi/rodso.gni b/zircon/kernel/lib/userabi/rodso.gni
deleted file mode 100644
index 4e45d15..0000000
--- a/zircon/kernel/lib/userabi/rodso.gni
+++ /dev/null
@@ -1,83 +0,0 @@
-# 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/toolchain/c_utils.gni")
-
-# Generate a header file with address constants from an RODSO layout object.
-#
-# The userboot loadable_module() and the vdso (libzircon) shared_library()
-# both use the special RODSO layout (by default in lld or via rodso.ld in
-# gold).  The kernel needs to use address constants extracted from these
-# ELF files' headers and symbols.
-#
-# This generates two targets: $target_name creates the $outputs file;
-# and $target_name.rsp generates "$target_gen_dir/$target_name.rsp"
-# via link_output_rspfile().
-#
-# Parameters
-#
-#   outputs
-#     Required: See action().
-#
-#   deps
-#     Required; Should reach the loadable_module() or library() target
-#     and no other linking targets; see link_output_rspfile().
-#     Type: list(label)
-#
-template("rodso_code_header") {
-  main_target_name = target_name
-  rspfile_target_name = "$main_target_name.rsp"
-  rspfile = "$target_gen_dir/$target_name.rsp"
-
-  link_output_rspfile(rspfile_target_name) {
-    forward_variables_from(invoker,
-                           [
-                             "deps",
-                             "testonly",
-                             "visibility",
-                           ])
-    if (defined(visibility)) {
-      visibility += [ ":$main_target_name" ]
-    }
-    outputs = [
-      rspfile,
-    ]
-  }
-
-  toolchain_utils_action(main_target_name) {
-    forward_variables_from(invoker,
-                           [
-                             "testonly",
-                             "visibility",
-                           ])
-
-    deps = [
-      ":$rspfile_target_name",
-    ]
-    outputs = invoker.outputs
-    assert(outputs == [ outputs[0] ],
-           "rodso_code_header() outputs must have exactly one element")
-    depfile = "${outputs[0]}.d"
-
-    script = "$zx/kernel/lib/userabi/gen-rodso-code.sh"
-    utils = [
-      "nm",
-      "readelf",
-    ]
-    sources = [
-      rspfile,
-    ]
-    args = [
-      "--depfile",
-      rebase_path(depfile, root_build_dir),
-      rebase_path(outputs[0], root_build_dir),
-      invoker.name,
-      "@" + rebase_path(rspfile, root_build_dir),
-    ]
-
-    metadata = {
-      generated_sources = rebase_path(outputs, root_build_dir)
-    }
-  }
-}
diff --git a/zircon/kernel/lib/userabi/userboot.cc b/zircon/kernel/lib/userabi/userboot.cc
index 129845b..0fe05fa 100644
--- a/zircon/kernel/lib/userabi/userboot.cc
+++ b/zircon/kernel/lib/userabi/userboot.cc
@@ -49,11 +49,13 @@
 
 constexpr size_t stack_size = ZIRCON_DEFAULT_STACK_SIZE;
 
+#include "decompress_zbi-code.h"
 #include "userboot-code.h"
 
 // This is defined in assembly via RODSO_IMAGE (see rodso-asm.h);
 // userboot-code.h gives details about the image's size and layout.
 extern "C" const char userboot_image[];
+extern "C" const char decompress_zbi_image[];
 
 KCOUNTER(init_time, "init.userboot.time.msec")
 
@@ -168,6 +170,13 @@
 }
 
 void bootstrap_vmos(Handle** handles) {
+    {
+        EmbeddedVmo decompress_zbi("lib/hermetic/decompress-zbi.so",
+                                   decompress_zbi_image,
+                                   DECOMPRESS_ZBI_DATA_END);
+        handles[kUserbootDecompressor] = decompress_zbi.vmo_handle().release();
+    }
+
     size_t rsize;
     void* rbase = platform_get_ramdisk(&rsize);
     if (rbase) {
diff --git a/zircon/kernel/lib/userabi/userboot/BUILD.gn b/zircon/kernel/lib/userabi/userboot/BUILD.gn
index 11596869..ef0389c 100644
--- a/zircon/kernel/lib/userabi/userboot/BUILD.gn
+++ b/zircon/kernel/lib/userabi/userboot/BUILD.gn
@@ -20,11 +20,14 @@
       # can do loadable_module().
       solink = true
 
-      if (opt_level < 2) {
-        # userboot doesn't stay sufficiently pure without optimization.
-        toolchain_args = {
+      toolchain_args = {
+        if (opt_level < 2) {
+          # userboot doesn't stay sufficiently pure without optimization.
           opt_level = 2
         }
+
+        # No runtime to print asserts, so can't compile them in.
+        assert_level = 0
       }
 
       # userboot can't use any instrumentation runtimes.
@@ -77,9 +80,9 @@
     deps = [
       ":gen-vdso-syms-ld",
       "$zx/kernel/lib/userabi:headers",
-      "$zx/system/ulib/bootdata",
       "$zx/system/ulib/elf-psabi",
       "$zx/system/ulib/elfload",
+      "$zx/system/ulib/hermetic-decompressor",
       "$zx/system/ulib/ldmsg",
       "$zx/system/ulib/processargs",
       "$zx/system/ulib/zircon",
diff --git a/zircon/kernel/lib/userabi/userboot/bootdata.cc b/zircon/kernel/lib/userabi/userboot/bootdata.cc
index 1413df0..746ed9e 100644
--- a/zircon/kernel/lib/userabi/userboot/bootdata.cc
+++ b/zircon/kernel/lib/userabi/userboot/bootdata.cc
@@ -5,12 +5,51 @@
 #include "bootdata.h"
 #include "util.h"
 
-#include <bootdata/decompress.h>
+#include <lib/hermetic-decompressor/hermetic-decompressor.h>
 #include <zircon/boot/bootdata.h>
+#include <zircon/boot/image.h>
 #include <zircon/syscalls.h>
 #include <string.h>
 
+namespace {
+
+class EngineService {
+public:
+    using Magic = HermeticDecompressorEngineService::Magic;
+    static constexpr Magic kLz4fMagic =
+        HermeticDecompressorEngineService::kLz4fMagic;
+
+    EngineService(zx_handle_t job, zx_handle_t engine, zx_handle_t vdso) :
+        job_(job), engine_(engine), vdso_(vdso) {}
+
+    auto job() const { return zx::unowned_job{*job_}; }
+
+    zx_status_t GetEngine(Magic magic, zx::unowned_vmo* vmo) {
+        if (magic == kLz4fMagic) {
+            *vmo = zx::unowned_vmo{engine_};
+            return ZX_OK;
+        }
+        return ZX_ERR_NOT_FOUND;
+    }
+
+    zx_status_t GetVdso(zx::unowned_vmo* vmo) {
+        *vmo = zx::unowned_vmo{vdso_->get()};
+        return ZX_OK;
+    }
+
+private:
+    zx::unowned_job job_;
+    zx::unowned_vmo engine_;
+    zx::unowned_vmo vdso_;
+};
+
+using Decompressor = HermeticDecompressorWithEngineService<EngineService>;
+
+}
+
 zx_handle_t bootdata_get_bootfs(zx_handle_t log, zx_handle_t vmar_self,
+                                zx_handle_t job,
+                                zx_handle_t engine_vmo, zx_handle_t vdso_vmo,
                                 zx_handle_t bootdata_vmo) {
     size_t off = 0;
     for (;;) {
@@ -32,13 +71,20 @@
             }
             break;
 
-        case BOOTDATA_BOOTFS_BOOT:;
-            const char* errmsg;
-            zx_handle_t bootfs_vmo;
-            status = decompress_bootdata(vmar_self, bootdata_vmo, off,
-                                         bootdata.length + sizeof(bootdata),
-                                         &bootfs_vmo, &errmsg);
-            check(log, status, "%s", errmsg);
+        case BOOTDATA_BOOTFS_BOOT: {
+            zx::vmo bootfs_vmo;
+            if (bootdata.flags & ZBI_FLAG_STORAGE_COMPRESSED) {
+                status = zx::vmo::create(bootdata.extra, 0, &bootfs_vmo);
+                check(log, status,
+                      "cannot create BOOTFS VMO (%u bytes)", bootdata.extra);
+                status = Decompressor(job, engine_vmo, vdso_vmo)
+                    (*zx::unowned_vmo{bootdata_vmo},
+                     off + sizeof(bootdata), bootdata.length,
+                     bootfs_vmo, 0, bootdata.extra);
+                check(log, status, "failed to decompress BOOTFS");
+            } else {
+                fail(log, "uncompressed BOOTFS not supported");
+            }
 
             // Signal that we've already processed this one.
             bootdata.type = BOOTDATA_BOOTFS_DISCARD;
@@ -47,7 +93,9 @@
                                     sizeof(bootdata.type)),
                   "zx_vmo_write failed on bootdata VMO\n");
 
-            return bootfs_vmo;
+            printl(log, "decompressed bootfs to VMO!\n");
+            return bootfs_vmo.release();
+        }
         }
 
         off += BOOTDATA_ALIGN(sizeof(bootdata) + bootdata.length);
diff --git a/zircon/kernel/lib/userabi/userboot/bootdata.h b/zircon/kernel/lib/userabi/userboot/bootdata.h
index 71fa95f..1ccf251 100644
--- a/zircon/kernel/lib/userabi/userboot/bootdata.h
+++ b/zircon/kernel/lib/userabi/userboot/bootdata.h
@@ -7,4 +7,6 @@
 #include <zircon/types.h>
 
 zx_handle_t bootdata_get_bootfs(zx_handle_t log, zx_handle_t vmar_self,
+                                zx_handle_t job,
+                                zx_handle_t engine_vmo, zx_handle_t vdso_vmo,
                                 zx_handle_t bootdata_vmo);
diff --git a/zircon/kernel/lib/userabi/userboot/start.cc b/zircon/kernel/lib/userabi/userboot/start.cc
index 318a165..e1c57ca 100644
--- a/zircon/kernel/lib/userabi/userboot/start.cc
+++ b/zircon/kernel/lib/userabi/userboot/start.cc
@@ -195,7 +195,11 @@
     // We need it to load devmgr and libc from.
     // Later bootfs sections will be processed by devmgr.
     zx::vmo bootfs_vmo{
-        bootdata_get_bootfs(log.get(), vmar_self.get(), handles[kZbi])};
+        bootdata_get_bootfs(log.get(), vmar_self.get(),
+                            handles[kRootJob],
+                            handles[kUserbootDecompressor],
+                            handles[kFirstVdso],
+                            handles[kZbi])};
 
     // TODO(mdempsky): Push further down the stack? Seems unnecessary to
     // mark the entire bootfs VMO as executable.
@@ -305,6 +309,15 @@
     check(log.get(), status,
           "zx_handle_duplicate failed on child thread handle");
 
+    for (const auto& h : handles) {
+        zx_info_handle_basic_t info;
+        status = zx_object_get_info(h, ZX_INFO_HANDLE_BASIC,
+                                    &info, sizeof(info), nullptr, nullptr);
+        check(log.get(), status,
+              "bad handle %d is %x",
+              (int)(&h - &handles[0]), h);
+    }
+
     // Now send the bootstrap message.  This transfers away all the handles
     // we have left except the process and thread themselves.
     status = to_child.write(0, &child_message, sizeof(child_message),
diff --git a/zircon/system/ulib/hermetic-compute/BUILD.gn b/zircon/system/ulib/hermetic-compute/BUILD.gn
index 373fa71..548fed5 100644
--- a/zircon/system/ulib/hermetic-compute/BUILD.gn
+++ b/zircon/system/ulib/hermetic-compute/BUILD.gn
@@ -20,25 +20,35 @@
     visibility = [ ":*" ]
     defines = [ "HERMETIC_COMPUTE_MODULE" ]
   }
-} else {
-  # This is the library for loading and launching hermetic engine modules.
-  library("hermetic-compute") {
-    sources = [
-      "get-vdso.cc",
+}
+
+# This is the library for loading and launching hermetic engine modules.
+library("hermetic-compute") {
+  sources = []
+  public_deps = [
+    # <lib/hermetic-compute/hermetic-compute.h> -> <fbl/macros.h>
+    "$zx/system/ulib/fbl:headers",
+
+    # <lib/hermetic-compute/hermetic-compute.h> -> <lib/zx/vmar.h>
+    "$zx/system/ulib/zx:headers",
+  ]
+
+  # Just the headers can be used in the hermetic engine environment itself.
+  if (toolchain.environment != "hermetic") {
+    sources += [
       "hermetic-compute.cc",
       "launch.cc",
     ]
-    public_deps = [
-      # <lib/hermetic-compute/hermetic-compute.h> -> <fbl/macros.h>
-      "$zx/system/ulib/fbl:headers",
-
-      # <lib/hermetic-compute/hermetic-compute.h> -> <lib/zx/vmar.h>
-      "$zx/system/ulib/zx:headers",
-    ]
     deps = [
       "$zx/system/ulib/elfload",
-      "$zx/system/ulib/fdio",
       "$zx/system/ulib/zx",
     ]
   }
+
+  # Other "hermetic" environments like userboot can use most of the
+  # library, but not this.
+  if (!is_hermetic) {
+    sources += [ "get-vdso.cc" ]
+    deps += [ "$zx/system/ulib/fdio" ]
+  }
 }
diff --git a/zircon/system/ulib/hermetic-compute/include/lib/hermetic-compute/hermetic-compute.h b/zircon/system/ulib/hermetic-compute/include/lib/hermetic-compute/hermetic-compute.h
index 1e81caa..43e9fb3 100644
--- a/zircon/system/ulib/hermetic-compute/include/lib/hermetic-compute/hermetic-compute.h
+++ b/zircon/system/ulib/hermetic-compute/include/lib/hermetic-compute/hermetic-compute.h
@@ -183,7 +183,7 @@
         // short-circuit.  This can be called to report a failure in a
         // complex transfer.
         void Abort(zx_status_t status) {
-            ZX_ASSERT(status != ZX_OK);
+            ZX_DEBUG_ASSERT(status != ZX_OK);
             status_ = status;
         }
 
diff --git a/zircon/system/ulib/hermetic-compute/include/lib/hermetic-compute/vmo-span.h b/zircon/system/ulib/hermetic-compute/include/lib/hermetic-compute/vmo-span.h
index f48eda9..3cd4fbd 100644
--- a/zircon/system/ulib/hermetic-compute/include/lib/hermetic-compute/vmo-span.h
+++ b/zircon/system/ulib/hermetic-compute/include/lib/hermetic-compute/vmo-span.h
@@ -32,8 +32,8 @@
     VmoSpan(const zx::vmo& vmo,  uint64_t offset, size_t size) :
         vmo_(zx::unowned_vmo{vmo}), offset_(offset), size_(size) {
         if constexpr (!Leaky) {
-            ZX_ASSERT(offset % PAGE_SIZE == 0);
-            ZX_ASSERT(size % PAGE_SIZE == 0);
+            ZX_DEBUG_ASSERT(offset % PAGE_SIZE == 0);
+            ZX_DEBUG_ASSERT(size % PAGE_SIZE == 0);
         }
     }
 
diff --git a/zircon/system/ulib/hermetic-decompressor/BUILD.gn b/zircon/system/ulib/hermetic-decompressor/BUILD.gn
index 563be82..ba32572 100644
--- a/zircon/system/ulib/hermetic-decompressor/BUILD.gn
+++ b/zircon/system/ulib/hermetic-decompressor/BUILD.gn
@@ -20,7 +20,6 @@
 
 template("engine_module") {
   hermetic_module(target_name) {
-    visibility = [ ":*" ]
     ldflags = [ "-Wl,-z,stack-size=4096" ]
     forward_variables_from(invoker, "*")
     deps += [
diff --git a/zircon/system/ulib/hermetic-decompressor/get-engine.cc b/zircon/system/ulib/hermetic-decompressor/get-engine.cc
index 03b9806..551abb2 100644
--- a/zircon/system/ulib/hermetic-decompressor/get-engine.cc
+++ b/zircon/system/ulib/hermetic-decompressor/get-engine.cc
@@ -41,8 +41,10 @@
 zx::vmo cache_lz4f, cache_zstd;
 
 constexpr Decompressor kDecompressors[] = {
-    {"hermetic/decompress-lz4f.so", &cache_lz4f, 0x184D2204},
-    {"hermetic/decompress-zstd.so", &cache_zstd, 0xFD2FB528},
+    {"hermetic/decompress-lz4f.so", &cache_lz4f,
+     HermeticDecompressorEngineService::kLz4fMagic},
+    {"hermetic/decompress-zstd.so", &cache_zstd,
+     HermeticDecompressorEngineService::kZstdMagic},
 };
 
 } // namespace
diff --git a/zircon/system/ulib/hermetic-decompressor/include/lib/hermetic-decompressor/hermetic-decompressor.h b/zircon/system/ulib/hermetic-decompressor/include/lib/hermetic-decompressor/hermetic-decompressor.h
index 600fb19..b552fc7 100644
--- a/zircon/system/ulib/hermetic-decompressor/include/lib/hermetic-decompressor/hermetic-decompressor.h
+++ b/zircon/system/ulib/hermetic-decompressor/include/lib/hermetic-decompressor/hermetic-decompressor.h
@@ -22,16 +22,24 @@
     // Reading this much is enough to identify the format.
     using Magic = uint32_t;
 
+    // These are the magic numbers for the formats GetEngine groks.  They're
+    // only here because they aren't exported by the normal public headers of
+    // the format support libraries themselves.
+    static constexpr Magic kLz4fMagic = 0x184D2204;
+    static constexpr Magic kZstdMagic = 0xFD2FB528;
+
     // This finds the appropriate decompression kernel for the magic number
     // found at the beginning of the compressed image.  It should return
     // ZX_ERR_NOT_FOUND for an unrecognized magic number.
     zx_status_t GetEngine(Magic magic, zx::unowned_vmo* vmo);
 
     // This finds the appropriate vDSO to support a decompression kernel.
-    zx_status_t GetVdso(zx::unowned_vmo* vmo) {
+    zx_status_t GetVdso(zx::unowned_vmo* vmo) const {
         *vmo = zx::unowned_vmo{HermeticComputeProcess::GetVdso()};
         return ZX_OK;
     }
+
+    auto job() const { return zx::job::default_job(); }
 };
 
 template <typename EngineService>
@@ -69,7 +77,7 @@
 
         // Set up the engine.
         HermeticComputeProcess hcp;
-        status = hcp.Init(*zx::job::default_job(), "hermetic-decompressor");
+        status = hcp.Init(*engine_service_.job(), "hermetic-decompressor");
         if (status != ZX_OK) {
             return status;
         }