[Reland] "[devcoordinator] Limit use of fshost's loader service"

This is a reland of 8e288427c87350b017096cda98e88b95038c0181

Reland reason: Thought to be the cause of GI roller failure
but was confirmed to be a different change.

Original change's description:
> [devcoordinator] Limit use of fshost's loader service
>
> Clarify which things use and rely on the fshost loader service today,
> rather than swapping out devcoordinator's default loader for it and
> having things inherit it through FDIO_SPAWN_DEFAULT_LDSVC.
>
> Bug: 34633
>
> Change-Id: I3a1ac3b5e08f02dd15c3b6c930f84edb895f6546

Bug: 34633
Change-Id: I2a06e986317030349c1d1bd34363bcfdcf9464c1
diff --git a/zircon/system/core/devmgr/devcoordinator/BUILD.gn b/zircon/system/core/devmgr/devcoordinator/BUILD.gn
index 4b94b03..4f85f0d 100644
--- a/zircon/system/core/devmgr/devcoordinator/BUILD.gn
+++ b/zircon/system/core/devmgr/devcoordinator/BUILD.gn
@@ -67,6 +67,7 @@
     "$zx/system/fidl/fuchsia-device-manager:c",
     "$zx/system/fidl/fuchsia-device-manager:llcpp",
     "$zx/system/fidl/fuchsia-driver-test:c",
+    "$zx/system/fidl/fuchsia-ldsvc:llcpp",
     "$zx/system/fidl/fuchsia-io:c",
     "$zx/system/fidl/fuchsia-mem:c",
     "$zx/system/ulib/async-loop:async-loop-cpp",
diff --git a/zircon/system/core/devmgr/devcoordinator/coordinator.cc b/zircon/system/core/devmgr/devcoordinator/coordinator.cc
index 49fd5d72..60fbc2b 100644
--- a/zircon/system/core/devmgr/devcoordinator/coordinator.cc
+++ b/zircon/system/core/devmgr/devcoordinator/coordinator.cc
@@ -361,21 +361,11 @@
   return ZX_OK;
 }
 
-static zx_status_t dc_launch_devhost(Devhost* host, DevhostLoaderService* loader_service,
+static zx_status_t dc_launch_devhost(Devhost* host, const LoaderServiceConnector& loader_connector,
                                      const char* devhost_bin, const char* name,
                                      const char* const* env, zx_handle_t hrpc,
                                      const zx::resource& root_resource,
                                      zx::unowned_job devhost_job) {
-  zx::channel loader_connection;
-  if (loader_service != nullptr) {
-    zx_status_t status = loader_service->Connect(&loader_connection);
-    if (status != ZX_OK) {
-      log(ERROR, "devcoordinator: failed to use loader service: %s\n",
-          zx_status_get_string(status));
-      return status;
-    }
-  }
-
   // Give devhosts the root resource if we have it (in tests, we may not)
   // TODO: limit root resource to root devhost only
   zx::resource resource;
@@ -425,15 +415,17 @@
     };
   }
 
-  uint32_t spawn_flags = FDIO_SPAWN_CLONE_ENVIRON;
-  if (loader_connection.is_valid()) {
-    actions[actions_count++] = fdio_spawn_action_t{
-        .action = FDIO_SPAWN_ACTION_ADD_HANDLE,
-        .h = {.id = PA_HND(PA_LDSVC_LOADER, 0), .handle = loader_connection.release()},
-    };
-  } else {
-    spawn_flags |= FDIO_SPAWN_DEFAULT_LDSVC;
+  zx::channel loader_connection;
+  status = loader_connector(&loader_connection);
+  if (status != ZX_OK) {
+    log(ERROR, "devcoordinator: failed to get devhost loader connection: %s\n",
+        zx_status_get_string(status));
+    return status;
   }
+  actions[actions_count++] = fdio_spawn_action_t{
+      .action = FDIO_SPAWN_ACTION_ADD_HANDLE,
+      .h = {.id = PA_HND(PA_LDSVC_LOADER, 0), .handle = loader_connection.release()},
+  };
   ZX_ASSERT(actions_count <= kMaxActions);
 
   zx::process proc;
@@ -443,8 +435,8 @@
       devhost_bin,
       nullptr,
   };
-  status = fdio_spawn_etc(devhost_job->get(), spawn_flags, argv[0], argv, env, actions_count,
-                          actions, proc.reset_and_get_address(), err_msg);
+  status = fdio_spawn_etc(devhost_job->get(), FDIO_SPAWN_CLONE_ENVIRON, argv[0], argv, env,
+                          actions_count, actions, proc.reset_and_get_address(), err_msg);
   if (status != ZX_OK) {
     log(ERROR, "devcoordinator: launch devhost '%s': failed: %s: %s\n", name,
         zx_status_get_string(status), err_msg);
@@ -478,10 +470,9 @@
   fbl::Vector<const char*> env;
   boot_args().Collect("driver.", &env);
   env.push_back(nullptr);
-  status =
-      dc_launch_devhost(dh.get(), loader_service_, get_devhost_bin(config_.asan_drivers), name,
-                        env.get(), hrpc.release(), root_resource(),
-                        zx::unowned_job(config_.devhost_job));
+  status = dc_launch_devhost(dh.get(), loader_service_connector_,
+                             get_devhost_bin(config_.asan_drivers), name, env.get(), hrpc.release(),
+                             root_resource(), zx::unowned_job(config_.devhost_job));
   if (status != ZX_OK) {
     zx_handle_close(dh->hrpc());
     return status;
diff --git a/zircon/system/core/devmgr/devcoordinator/coordinator.h b/zircon/system/core/devmgr/devcoordinator/coordinator.h
index be4d36e..1e36e22 100644
--- a/zircon/system/core/devmgr/devcoordinator/coordinator.h
+++ b/zircon/system/core/devmgr/devcoordinator/coordinator.h
@@ -114,6 +114,8 @@
   bool suspend_fallback;
 };
 
+using LoaderServiceConnector = fit::function<zx_status_t(zx::channel*)>;
+
 class Coordinator {
  public:
   Coordinator(const Coordinator&) = delete;
@@ -205,8 +207,9 @@
   bool system_available() const { return system_available_; }
   void set_system_available(bool system_available) { system_available_ = system_available; }
   bool system_loaded() const { return system_loaded_; }
-  void set_loader_service(DevhostLoaderService* loader_service) {
-    loader_service_ = loader_service;
+
+  void set_loader_service_connector(LoaderServiceConnector loader_service_connector) {
+    loader_service_connector_ = std::move(loader_service_connector);
   }
 
   fbl::DoublyLinkedList<Driver*, Driver::Node>& drivers() { return drivers_; }
@@ -247,7 +250,7 @@
   bool launched_first_devhost_ = false;
   bool system_available_ = false;
   bool system_loaded_ = false;
-  DevhostLoaderService* loader_service_ = nullptr;
+  LoaderServiceConnector loader_service_connector_;
 
   // Services offered to the rest of the system.
   svc::Outgoing outgoing_services_;
diff --git a/zircon/system/core/devmgr/devcoordinator/main.cc b/zircon/system/core/devmgr/devcoordinator/main.cc
index 5aee442..134218e 100644
--- a/zircon/system/core/devmgr/devcoordinator/main.cc
+++ b/zircon/system/core/devmgr/devcoordinator/main.cc
@@ -6,6 +6,7 @@
 #include <fcntl.h>
 #include <fuchsia/boot/c/fidl.h>
 #include <fuchsia/hardware/virtioconsole/llcpp/fidl.h>
+#include <fuchsia/ldsvc/llcpp/fidl.h>
 #include <getopt.h>
 #include <lib/async-loop/cpp/loop.h>
 #include <lib/devmgr-launcher/processargs.h>
@@ -17,6 +18,7 @@
 #include <lib/fdio/spawn.h>
 #include <lib/fdio/unsafe.h>
 #include <lib/fdio/watcher.h>
+#include <lib/fit/optional.h>
 #include <lib/zx/debuglog.h>
 #include <lib/zx/event.h>
 #include <lib/zx/port.h>
@@ -31,7 +33,6 @@
 #include <unistd.h>
 #include <zircon/boot/image.h>
 #include <zircon/device/vfs.h>
-#include <zircon/dlfcn.h>
 #include <zircon/process.h>
 #include <zircon/processargs.h>
 #include <zircon/status.h>
@@ -63,7 +64,7 @@
 constexpr char kRootJobPath[] = "/bootsvc/" fuchsia_boot_RootJob_Name;
 constexpr char kRootResourcePath[] = "/bootsvc/" fuchsia_boot_RootResource_Name;
 
-struct {
+struct GlobalHandles {
   // The handle used to transmit messages to appmgr.
   zx::channel appmgr_client;
 
@@ -83,6 +84,12 @@
   // The handle used by device_name_provider to serve incoming requests.
   zx::channel device_name_provider_server;
 
+  // Handle to the loader service hosted in fshost, which allows loading from /boot and /system
+  // rather than specific packages.
+  // This isn't actually "optional", it's just initialized later.
+  // TODO(ZX-4860): Delete this once all dependencies have been removed.
+  fit::optional<llcpp::fuchsia::ldsvc::Loader::SyncClient> fshost_ldsvc;
+
   zx::job svc_job;
   zx::job fuchsia_job;
   zx::channel svchost_outgoing;
@@ -92,7 +99,28 @@
   // Used to bind the svchost to the virtual-console binary to provide fidl
   // services.
   zx::channel virtcon_fidl;
-} g_handles;
+
+  GlobalHandles() : fshost_ldsvc(zx::channel()) {}
+};
+
+static GlobalHandles g_handles;
+
+// TODO(ZX-4860): DEPRECATED. Do not add new dependencies on the fshost loader service!
+zx_status_t clone_fshost_ldsvc(zx::channel* loader) {
+  // Only valid to call this after fshost_ldsvc has been wired up.
+  ZX_ASSERT_MSG(g_handles.fshost_ldsvc.has_value(), "clone_fshost_ldsvc called too early");
+
+  zx::channel remote;
+  zx_status_t status = zx::channel::create(0, loader, &remote);
+  if (status != ZX_OK) {
+    return status;
+  }
+  auto result = g_handles.fshost_ldsvc->Clone(std::move(remote));
+  if (result.status() != ZX_OK) {
+    return result.status();
+  }
+  return result.Unwrap()->rv;
+}
 
 // Wait for the requested file.  Its parent directory must exist.
 zx_status_t wait_for_file(const char* path, zx::time deadline) {
@@ -138,8 +166,17 @@
   if (cmd != nullptr) {
     auto args = devmgr::ArgumentVector::FromCmdline(cmd);
     args.Print("autorun");
-    devmgr::devmgr_launch(g_handles.svc_job, name, args.argv(), nullptr, -1, nullptr, nullptr, 0,
-                          nullptr, FS_ALL);
+
+    zx::channel ldsvc;
+    zx_status_t status = clone_fshost_ldsvc(&ldsvc);
+    if (status != ZX_OK) {
+      fprintf(stderr, "devcoordinator: failed to clone fshost loader for console: %d\n", status);
+      return;
+    }
+
+    devmgr::devmgr_launch_with_loader(g_handles.svc_job, name, zx::vmo(), std::move(ldsvc),
+                                      args.argv(), nullptr, -1, nullptr, nullptr, 0, nullptr,
+                                      FS_ALL);
   }
 }
 
@@ -251,17 +288,25 @@
 
     struct stat s;
     if (!appmgr_started && stat(argv_appmgr[0], &s) == 0) {
+      zx::channel ldsvc;
+      zx_status_t status = clone_fshost_ldsvc(&ldsvc);
+      if (status != ZX_OK) {
+        fprintf(stderr, "devcoordinator: failed to clone fshost loader for appmgr: %d\n", status);
+        continue;
+      }
+
       unsigned int appmgr_hnd_count = 0;
       zx_handle_t appmgr_hnds[2] = {};
       uint32_t appmgr_ids[2] = {};
       if (g_handles.appmgr_server.is_valid()) {
-        assert(appmgr_hnd_count < fbl::count_of(appmgr_hnds));
+        ZX_ASSERT(appmgr_hnd_count < fbl::count_of(appmgr_hnds));
         appmgr_hnds[appmgr_hnd_count] = g_handles.appmgr_server.release();
         appmgr_ids[appmgr_hnd_count] = PA_DIRECTORY_REQUEST;
         appmgr_hnd_count++;
       }
-      devmgr::devmgr_launch(g_handles.fuchsia_job, "appmgr", argv_appmgr, nullptr, -1, appmgr_hnds,
-                            appmgr_ids, appmgr_hnd_count, nullptr, FS_FOR_APPMGR);
+      devmgr::devmgr_launch_with_loader(g_handles.fuchsia_job, "appmgr", zx::vmo(),
+                                        std::move(ldsvc), argv_appmgr, nullptr, -1, appmgr_hnds,
+                                        appmgr_ids, appmgr_hnd_count, nullptr, FS_FOR_APPMGR);
       appmgr_started = true;
     }
     if (!autorun_started) {
@@ -348,10 +393,18 @@
       }
     }
 
+    zx::channel ldsvc;
+    status = clone_fshost_ldsvc(&ldsvc);
+    if (status != ZX_OK) {
+      fprintf(stderr, "devcoordinator: failed to clone fshost loader for console: %d\n", status);
+      return 1;
+    }
+
     const char* argv_sh[] = {"/boot/bin/sh", nullptr};
     zx::process proc;
-    status = devmgr::devmgr_launch(g_handles.svc_job, "sh:console", argv_sh, envp, fd.release(),
-                                   nullptr, nullptr, 0, &proc, FS_ALL);
+    status = devmgr::devmgr_launch_with_loader(g_handles.svc_job, "sh:console", zx::vmo(),
+                                               std::move(ldsvc), argv_sh, envp, fd.release(),
+                                               nullptr, nullptr, 0, &proc, FS_ALL);
     if (status != ZX_OK) {
       printf("devcoordinator: failed to launch console shell (%s)\n", zx_status_get_string(status));
       return 1;
@@ -697,17 +750,19 @@
   zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
   uint32_t types[fbl::count_of(handles)];
   size_t n = 0;
-  zx_handle_t ldsvc;
 
-  // Pass "fs_root", and ldsvc handles to fshost.
-  if (zx_channel_create(0, g_handles.fs_root.reset_and_get_address(), &handles[n]) == ZX_OK) {
+  // Pass server ends of fs_root and ldsvc handles to fshost.
+  zx::channel fs_root_server;
+  if (zx::channel::create(0, &g_handles.fs_root, &fs_root_server) == ZX_OK) {
+    handles[n] = fs_root_server.release();
     types[n++] = PA_HND(PA_USER0, 0);
   }
-  if (zx_channel_create(0, &ldsvc, &handles[n]) == ZX_OK) {
+  zx::channel ldsvc_client, ldsvc_server;
+  if (zx::channel::create(0, &ldsvc_client, &ldsvc_server) == ZX_OK) {
+    handles[n] = ldsvc_server.release();
     types[n++] = PA_HND(PA_USER0, 2);
-  } else {
-    ldsvc = ZX_HANDLE_INVALID;
   }
+  g_handles.fshost_ldsvc.emplace(std::move(ldsvc_client));
 
   // The "public directory" of the fshost service.
   handles[n] = fshost_server.release();
@@ -765,9 +820,6 @@
 
   devmgr::devmgr_launch(g_handles.svc_job, "fshost", args.get(), env.get(), -1, handles, types, n,
                         nullptr, FS_BOOT | FS_DEV | FS_SVC);
-
-  // switch to system loader service provided by fshost
-  zx_handle_close(dl_set_loader_service(ldsvc));
 }
 
 void devmgr_vfs_init(devmgr::Coordinator* coordinator, const devmgr::DevmgrArgs& devmgr_args,
@@ -1226,7 +1278,23 @@
     if (status != ZX_OK) {
       return 1;
     }
-    coordinator.set_loader_service(loader_service.get());
+    coordinator.set_loader_service_connector(
+        [loader_service = std::move(loader_service)](zx::channel* c) {
+          zx_status_t status = loader_service->Connect(c);
+          if (status != ZX_OK) {
+            log(ERROR, "devcoordinator: failed to add devhost loader connection: %s\n",
+                zx_status_get_string(status));
+          }
+          return status;
+        });
+  } else {
+    coordinator.set_loader_service_connector([](zx::channel* c) {
+      zx_status_t status = clone_fshost_ldsvc(c);
+      if (status != ZX_OK) {
+        fprintf(stderr, "devcoordinator: failed to clone fshost loader for devhost: %d\n", status);
+      }
+      return status;
+    });
   }
 
   for (const char* path : devmgr_args.driver_search_paths) {
diff --git a/zircon/system/ulib/inspect/BUILD.gn b/zircon/system/ulib/inspect/BUILD.gn
index 92391a8..9057995 100644
--- a/zircon/system/ulib/inspect/BUILD.gn
+++ b/zircon/system/ulib/inspect/BUILD.gn
@@ -37,8 +37,8 @@
   ]
 
   deps = [
+    "$zx/system/ulib/fdio",
     "$zx/system/ulib/fit",
-    "$zx/system/ulib/syslog",
     "$zx/system/ulib/zx",
   ]
 }
diff --git a/zircon/system/ulib/kcounter/BUILD.gn b/zircon/system/ulib/kcounter/BUILD.gn
index f0bee94..e9cc67d 100644
--- a/zircon/system/ulib/kcounter/BUILD.gn
+++ b/zircon/system/ulib/kcounter/BUILD.gn
@@ -18,7 +18,6 @@
     "$zx/system/ulib/fzl",
     "$zx/system/ulib/inspect",
     "$zx/system/ulib/svc",
-    "$zx/system/ulib/syslog",
     "$zx/system/ulib/zircon",
     "$zx/system/ulib/zx",
   ]
diff --git a/zircon/system/ulib/kcounter/provider.cc b/zircon/system/ulib/kcounter/provider.cc
index 19a027d..578ef29 100644
--- a/zircon/system/ulib/kcounter/provider.cc
+++ b/zircon/system/ulib/kcounter/provider.cc
@@ -5,7 +5,6 @@
 #include <fuchsia/kernel/c/fidl.h>
 #include <lib/fidl-async/bind.h>
 #include <lib/kcounter/provider.h>
-#include <lib/syslog/global.h>
 #include <lib/zx/vmo.h>
 #include <string.h>
 #include <zircon/status.h>
diff --git a/zircon/system/ulib/zxtest/README.md b/zircon/system/ulib/zxtest/README.md
index 6b6cfb8..8358832 100644
--- a/zircon/system/ulib/zxtest/README.md
+++ b/zircon/system/ulib/zxtest/README.md
@@ -15,6 +15,23 @@
 * For feature requests, please describe the use case and the problems the missing feature is causing.
 
 
+## zxtest Dependencies
+
+zxtest requires some basic libc I/O functions. Typically, this is provided by
+including an fdio dependency in the test target that uses zxtest. However, the
+"core tests" in ``//zircon/system/utest/core`` provide their own libc I/O
+functions since the core tests can't use fdio. (This is why the zxtest library
+itself cannot depend on fdio.)
+
+Most other tests should ensure that they separately depend on fdio. If your test
+crashes and you see ``libc_io_functions_not_implemented_use_fdio_instead`` at
+the top of the stack trace, this is why.
+
+**TODO**: Consider splitting "zxtest" into "zxtest" and "zxtest-core", where the
+former includes fdio but the latter doesn't. This would help avoid the
+dependency weirdness where fdio has to be separately included by most users even
+though it's really needed by zxtest.
+
 ## Key Differences from 'Google Test'
 
 * Limited set of dependencies.