diff --git a/zircon/system/ulib/runtests-utils/fuchsia-run-test.cpp b/zircon/system/ulib/runtests-utils/fuchsia-run-test.cpp
index b689f790f306a3c1722308db2cc944bd3ab7efd7..59ae6c03dc961de46613b93d7b89084174963e02 100644
--- a/zircon/system/ulib/runtests-utils/fuchsia-run-test.cpp
+++ b/zircon/system/ulib/runtests-utils/fuchsia-run-test.cpp
@@ -8,6 +8,7 @@
 #include <fcntl.h>
 #include <libgen.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -49,7 +50,17 @@ fbl::String BaseName(const fbl::String& path) {
     return ret;
 }
 
-void TestFileComponentInfo(const fbl::String path,
+fbl::String RootName(const fbl::String& path) {
+  const size_t i = strspn(path.c_str(), "/");
+  const char* start = &path.c_str()[i];
+  const char* end = strchr(start, '/');
+  if (end == nullptr) {
+    end = &path.c_str()[path.size()];
+  }
+  return fbl::String::Concat({"/", fbl::String(start, end - start)});
+}
+
+void TestFileComponentInfo(const fbl::String& path,
                            fbl::String* component_url_out,
                            fbl::String* cmx_file_path_out) {
     if (strncmp(path.c_str(), kPkgPrefix, strlen(kPkgPrefix)) != 0) {
@@ -303,11 +314,34 @@ std::unique_ptr<Result> FuchsiaRunTest(const char* argv[],
         return std::make_unique<Result>(path, FAILED_TO_LAUNCH, 0);
     }
 
+    // The TEST_ROOT_DIR environment variable allows tests that could be stored in
+    // "/system" or "/boot" to discern where they are running, and modify paths
+    // accordingly.
+    //
+    // TODO(BLD-463): The hard-coded set of prefixes is not ideal. Ideally, this
+    // would instead set the "root" to the parent directory of the "test/"
+    // subdirectory where globbing was done to collect the set of tests in
+    // DiscoverAndRunTests().  But then it's not clear what should happen if
+    // using `-f` to provide a list of paths instead of directories to glob.
+    const fbl::String root = RootName(path);
+    fbl::String root_var;
+    fbl::Vector<const char*> env_vars;
+    if (root == "/system" || root == "/boot") {
+      for (size_t i = 0; environ[i] != nullptr; ++i) {
+        env_vars.push_back(environ[i]);
+      }
+      root_var = fbl::String::Concat({"TEST_ROOT_DIR=", root});
+      env_vars.push_back(root_var.c_str());
+      env_vars.push_back(nullptr);
+    }
+    const char* const* env_vars_p =
+        !env_vars.is_empty() ? env_vars.begin() : nullptr;
+
     fds[1].release(); // To avoid double close since fdio_spawn_etc() closes it.
     zx::process process;
     char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
     status = fdio_spawn_etc(test_job.get(), FDIO_SPAWN_CLONE_ALL,
-                            args[0], args, nullptr,
+                            args[0], args, env_vars_p,
                             fdio_actions.size(), fdio_actions.get(),
                             process.reset_and_get_address(), err_msg);
     if (status != ZX_OK) {
diff --git a/zircon/system/ulib/runtests-utils/include/runtests-utils/fuchsia-run-test.h b/zircon/system/ulib/runtests-utils/include/runtests-utils/fuchsia-run-test.h
index f637fefca1560ea570a59734564a59aae15067cf..b2161d386fd5b9eff4388ff477becac141c17df9 100644
--- a/zircon/system/ulib/runtests-utils/include/runtests-utils/fuchsia-run-test.h
+++ b/zircon/system/ulib/runtests-utils/include/runtests-utils/fuchsia-run-test.h
@@ -31,7 +31,7 @@ constexpr char kPkgPrefix[] = "/pkgfs/packages/";
 // Code which uses this url:
 // https://fuchsia.googlesource.com/fuchsia/+/master/garnet/bin/appmgr/root_loader.cc
 //
-void TestFileComponentInfo(const fbl::String path,
+void TestFileComponentInfo(const fbl::String& path,
                            fbl::String* component_url_out,
                            fbl::String* cmx_file_path_out);
 
diff --git a/zircon/system/ulib/zbi-bootfs/test/zbi-bootfs-test.cpp b/zircon/system/ulib/zbi-bootfs/test/zbi-bootfs-test.cpp
index dd3b0bc504593d20f278b02c08046adcf5750c17..461f415ecbb051463bd917c7cb339e9a01718ff4 100644
--- a/zircon/system/ulib/zbi-bootfs/test/zbi-bootfs-test.cpp
+++ b/zircon/system/ulib/zbi-bootfs/test/zbi-bootfs-test.cpp
@@ -5,8 +5,10 @@
 #include <cerrno>
 #include <fcntl.h>
 #include <getopt.h>
-#include <string>
+#include <stdlib.h>
 
+#include <fbl/string.h>
+#include <zircon/assert.h>
 #include <zircon/boot/image.h>
 #include <zircon/process.h>
 #include <zircon/status.h>
@@ -18,14 +20,23 @@
 #include <unittest/unittest.h>
 #include <zbi-bootfs/zbi-bootfs.h>
 
-#define file_path "boot/testdata/zbi-bootfs/test-image.zbi"
 #define file_name "nand_image"
 
+static const char* image_path() {
+    static fbl::String path;
+    if (path.empty()) {
+        char* root_dir = getenv("TEST_ROOT_DIR");
+        ZX_ASSERT(root_dir != nullptr);
+        path = fbl::String::Concat({root_dir, "/testdata/zbi-bootfs/test-image.zbi"});
+    }
+    return path.c_str();
+}
+
 static bool ZbiInit(void) {
     BEGIN_TEST;
     zbi_bootfs::ZbiBootfsParser image;
     size_t byte_offset = 0;
-    const char* input = file_path;
+    const char* input = image_path();
 
     // Check good input
     zx::vmo vmo_out;
@@ -49,7 +60,7 @@ static bool ZbiInitBadInput(void) {
 static bool ZbiProcessSuccess(void) {
     BEGIN_TEST;
     zbi_bootfs::ZbiBootfsParser image;
-    const char* input = file_path;
+    const char* input = image_path();
     const char* filename = file_name;
     size_t byte_offset = 0;
 
@@ -66,7 +77,7 @@ static bool ZbiProcessSuccess(void) {
 static bool ZbiProcessBadOffset(void) {
     BEGIN_TEST;
     zbi_bootfs::ZbiBootfsParser image;
-    const char* input = file_path;
+    const char* input = image_path();
     const char* filename = file_name;
     zx::vmo vmo_out;
 
@@ -82,7 +93,7 @@ static bool ZbiProcessBadOffset(void) {
 static bool ZbiProcessBadFile(void) {
     BEGIN_TEST;
     zbi_bootfs::ZbiBootfsParser image;
-    const char* input = file_path;
+    const char* input = image_path();
     size_t byte_offset = 0;
     zx::vmo vmo_out;
 
diff --git a/zircon/system/utest/devfs/fidl-tests.cpp b/zircon/system/utest/devfs/fidl-tests.cpp
index 8ebb6de4531f1eee83e4b4f00bdff2ae184cf46a..f28b19be466d8355e8ff4a8dc693fb49993ba617 100644
--- a/zircon/system/utest/devfs/fidl-tests.cpp
+++ b/zircon/system/utest/devfs/fidl-tests.cpp
@@ -14,6 +14,7 @@
 #include <zircon/device/vfs.h>
 #include <zircon/syscalls.h>
 
+#include <stdlib.h>
 #include <utility>
 
 namespace {
@@ -106,7 +107,9 @@ bool TestFidlOpen() {
         ASSERT_EQ(zx::channel::create(0, &dev_client, &dev_server), ZX_OK);
         fdio_ns_t* ns;
         ASSERT_EQ(fdio_ns_get_installed(&ns), ZX_OK);
-        ASSERT_EQ(fdio_ns_connect(ns, "/boot", ZX_FS_RIGHT_READABLE, dev_server.release()), ZX_OK);
+        char* root_dir = getenv("TEST_ROOT_DIR");
+        ASSERT_TRUE(root_dir != nullptr);
+        ASSERT_EQ(fdio_ns_connect(ns, root_dir, ZX_FS_RIGHT_READABLE, dev_server.release()), ZX_OK);
         ASSERT_TRUE(FidlOpenValidator(dev_client, "lib", fuchsia_io_NodeInfoTag_directory, 0));
         ASSERT_TRUE(FidlOpenErrorValidator(dev_client));
     }
diff --git a/zircon/system/utest/fdio/BUILD.gn b/zircon/system/utest/fdio/BUILD.gn
index fcd0984281827d16746227c7d23faab68a48c869..6f18c30a63b1597b97a661b6bef154e28038e583 100644
--- a/zircon/system/utest/fdio/BUILD.gn
+++ b/zircon/system/utest/fdio/BUILD.gn
@@ -14,6 +14,7 @@ test("fdio") {
     "fdio_socketpair.c",
   ]
   deps = [
+    "$zx/system/ulib/fbl",
     "$zx/system/ulib/fdio",
     "$zx/system/ulib/unittest",
     "$zx/system/ulib/zircon",
diff --git a/zircon/system/utest/fdio/fdio_atexit.cpp b/zircon/system/utest/fdio/fdio_atexit.cpp
index d2019f7991b750c5bf2723613ccd03116beae6b4..75267b83212182ae5ca1130d6361c32bfffe0527 100644
--- a/zircon/system/utest/fdio/fdio_atexit.cpp
+++ b/zircon/system/utest/fdio/fdio_atexit.cpp
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <fbl/string.h>
 #include <lib/fdio/spawn.h>
 #include <lib/zx/process.h>
 #include <lib/zx/time.h>
-
 #include <unittest/unittest.h>
 
 static int64_t join(const zx::process& process) {
@@ -23,7 +23,10 @@ static bool exit_in_accept_test(void) {
     zx::process process;
     zx_status_t status;
 
-    const char* argv[] = {"/boot/bin/accept-child", nullptr};
+    char* root_dir = getenv("TEST_ROOT_DIR");
+    ASSERT_TRUE(root_dir != nullptr);
+    const fbl::String path = fbl::String::Concat({root_dir, "/bin/accept-child"});
+    const char* argv[] = {path.c_str(), nullptr};
     status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
                         argv[0], argv, process.reset_and_get_address());
     ASSERT_EQ(ZX_OK, status);
diff --git a/zircon/system/utest/fidl/run_with_handle_policy_tests.cpp b/zircon/system/utest/fidl/run_with_handle_policy_tests.cpp
index d4fa364782b77392718179dc5318e4a45323d0d9..fa94a5ed30b4150b3475f97584ced840fedcd3b5 100644
--- a/zircon/system/utest/fidl/run_with_handle_policy_tests.cpp
+++ b/zircon/system/utest/fidl/run_with_handle_policy_tests.cpp
@@ -11,6 +11,7 @@
 
 #include <fbl/algorithm.h>
 #include <fbl/auto_call.h>
+#include <fbl/string.h>
 #include <fbl/unique_fd.h>
 #include <lib/fdio/io.h>
 #include <lib/fdio/spawn.h>
@@ -108,14 +109,16 @@ bool LaunchHelper(const char* argv[]) {
     END_HELPER;
 }
 
-// This test app contains a subset of fidl-tests; refer to BUILD.gn
-static constexpr char kTestApp[] = "/boot/bin/fidl-handle-policy-test-app";
-
 bool TestWithStrictHandlePolicy() {
     BEGIN_TEST;
 
+    // This test app contains a subset of fidl-tests; refer to BUILD.gn
+    char* root_dir = getenv("TEST_ROOT_DIR");
+    ASSERT_TRUE(root_dir != nullptr);
+    const fbl::String test_app =
+        fbl::String::Concat({root_dir, "/bin/fidl-handle-policy-test-app"});
     const char* args[] = {
-        kTestApp,
+        test_app.c_str(),
         nullptr
     };
     ASSERT_TRUE(LaunchHelper(args));
diff --git a/zircon/system/utest/runtests-utils/BUILD.gn b/zircon/system/utest/runtests-utils/BUILD.gn
index 2760a8e1911263777897a70447af1f31e5d04841..b9bbffd3737db969a1dd9d36fc30e9e3cc5a46a3 100644
--- a/zircon/system/utest/runtests-utils/BUILD.gn
+++ b/zircon/system/utest/runtests-utils/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("$zx/public/gn/resource.gni")
+
 test("runtests-utils") {
   sources = [
     "runtests-utils-test-utils.cpp",
@@ -21,6 +23,7 @@ test("runtests-utils") {
     deps += [ "$zx/system/ulib/memfs" ]
     data_deps = [
       ":publish-data-helper",
+      ":test-data",
     ]
   } else {
     sources += [ "posix-test-main.cpp" ]
@@ -44,4 +47,14 @@ if (is_fuchsia) {
     # from the inner runtests instance to the outer one so they make it
     # into summary.json.
   }
+
+  resource("test-data") {
+    sources = [
+      "test-data",
+    ]
+    outputs = [
+      "testdata/runtests-utils/test-data",
+    ]
+  }
 }
+
diff --git a/zircon/system/utest/runtests-utils/fuchsia-run-test.cpp b/zircon/system/utest/runtests-utils/fuchsia-run-test.cpp
index eb70aad540354b1c80d80bbdada4b6568173a9d4..86229dab9ab40709fc36a74014ffef8193efdcdf 100644
--- a/zircon/system/utest/runtests-utils/fuchsia-run-test.cpp
+++ b/zircon/system/utest/runtests-utils/fuchsia-run-test.cpp
@@ -144,12 +144,20 @@ bool TestFileComponentInfoTest() {
     END_TEST;
 }
 
+static ScopedTestFile NewPublishFile(const fbl::String& test_name) {
+    char* root_dir = getenv("TEST_ROOT_DIR");
+    ZX_ASSERT(root_dir != nullptr);
+    const fbl::String path =
+        fbl::String::Concat({root_dir, "/bin/publish-data-helper"});
+    return ScopedTestFile(test_name.c_str(), path.c_str());
+}
+
 bool RunTestDontPublishData() {
     BEGIN_TEST;
 
     ScopedTestDir test_dir;
     fbl::String test_name = JoinPath(test_dir.path(), "publish-data-helper");
-    ScopedTestFile file(test_name.c_str(), "/boot/bin/publish-data-helper");
+    auto file = NewPublishFile(test_name);
 
     const char* argv[] = {test_name.c_str(), nullptr};
     std::unique_ptr<Result> result = PlatformRunTest(argv, nullptr, nullptr);
@@ -166,7 +174,7 @@ bool RunTestsPublishData() {
 
     ScopedTestDir test_dir;
     fbl::String test_name = JoinPath(test_dir.path(), "publish-data-helper");
-    ScopedTestFile file(test_name.c_str(), "/boot/bin/publish-data-helper");
+    auto file = NewPublishFile(test_name);
     int num_failed = 0;
     fbl::Vector<std::unique_ptr<Result>> results;
     const signed char verbosity = 77;
@@ -188,7 +196,7 @@ bool RunAllTestsPublishData() {
 
     ScopedTestDir test_dir;
     fbl::String test_name = JoinPath(test_dir.path(), "publish-data-helper");
-    ScopedTestFile file(test_name.c_str(), "/boot/bin/publish-data-helper");
+    auto file = NewPublishFile(test_name);
 
     const fbl::String output_dir =
         JoinPath(test_dir.path(), "run-all-tests-output-1");
@@ -242,15 +250,63 @@ bool RunAllTestsPublishData() {
     END_TEST;
 }
 
+bool RunTestRootDir() {
+    BEGIN_TEST;
+
+    ScopedTestDir test_dir;
+    fbl::String test_name = JoinPath(test_dir.path(), "succeed.sh");
+    const char* argv[] = {test_name.c_str(), nullptr};
+
+    // This test should have gotten TEST_ROOT_DIR. Confirm that we can find our
+    // artifact in the "testdata/" directory under TEST_ROOT_DIR.
+    char* root_dir = getenv("TEST_ROOT_DIR");
+    ASSERT_TRUE(root_dir != nullptr);
+    {
+        const fbl::String testdata_path =
+            fbl::String::Concat({root_dir, "/testdata/runtests-utils/test-data"});
+        FILE* testdata_file = fopen(testdata_path.c_str(), "r");
+        ASSERT_TRUE(testdata_file);
+        char buf[1024];
+        memset(buf, 0, sizeof(buf));
+        EXPECT_LT(0, fread(buf, sizeof(buf[0]), sizeof(buf), testdata_file));
+        fclose(testdata_file);
+        constexpr char kExpectedStr[] = "Hello world!\n";
+        EXPECT_STR_EQ(kExpectedStr, buf);
+    }
+
+    // Run a test and confirm TEST_ROOT_DIR gets passed along.
+    {
+        const char script_contents[] = "echo -n $TEST_ROOT_DIR";
+        ScopedScriptFile script(argv[0], script_contents);
+        fbl::String output_filename = JoinPath(test_dir.path(), "test.out");
+        std::unique_ptr<Result> result =
+            PlatformRunTest(argv, nullptr, output_filename.c_str());
+
+        FILE* output_file = fopen(output_filename.c_str(), "r");
+        ASSERT_TRUE(output_file);
+        char buf[1024];
+        memset(buf, 0, sizeof(buf));
+        EXPECT_LT(0, fread(buf, sizeof(buf[0]), sizeof(buf), output_file));
+        fclose(output_file);
+        EXPECT_STR_EQ(root_dir, buf);
+        EXPECT_STR_EQ(argv[0], result->name.c_str());
+        EXPECT_EQ(SUCCESS, result->launch_status);
+        EXPECT_EQ(0, result->return_code);
+    }
+
+    END_TEST;
+}
+
 BEGIN_TEST_CASE(FuchsiaComponentInfo)
 RUN_TEST_SMALL(TestFileComponentInfoTest)
 END_TEST_CASE(FuchsiaComponentInfo)
 
-BEGIN_TEST_CASE(PublishDataTests)
+BEGIN_TEST_CASE(PlatformRunTests)
 RUN_TEST(RunTestDontPublishData)
 RUN_TEST_MEDIUM(RunTestsPublishData)
 RUN_TEST_MEDIUM(RunAllTestsPublishData)
-END_TEST_CASE(PublishDataTests)
+RUN_TEST_MEDIUM(RunTestRootDir)
+END_TEST_CASE(PlatformRunTests)
 
 } // namespace
 } // namespace runtests
diff --git a/zircon/system/utest/runtests-utils/test-data b/zircon/system/utest/runtests-utils/test-data
new file mode 100644
index 0000000000000000000000000000000000000000..cd0875583aabe89ee197ea133980a9085d08e497
--- /dev/null
+++ b/zircon/system/utest/runtests-utils/test-data
@@ -0,0 +1 @@
+Hello world!
diff --git a/zircon/system/utest/spawn/spawn.cpp b/zircon/system/utest/spawn/spawn.cpp
index ebc226e155b535474e219b527945da7004847903..3e819b22a9bd0281c9b0c0880cf8a2c753f5ca40 100644
--- a/zircon/system/utest/spawn/spawn.cpp
+++ b/zircon/system/utest/spawn/spawn.cpp
@@ -4,7 +4,6 @@
 
 #include <unittest/unittest.h>
 
-#include <fcntl.h>
 #include <lib/fdio/io.h>
 #include <lib/fdio/spawn.h>
 #include <lib/fdio/fd.h>
@@ -14,14 +13,47 @@
 #include <lib/zx/job.h>
 #include <lib/zx/process.h>
 #include <lib/zx/socket.h>
-#include <stdlib.h>
-#include <unistd.h>
+#include <zircon/assert.h>
 #include <zircon/limits.h>
 #include <zircon/processargs.h>
 #include <zircon/syscalls/policy.h>
 
-static constexpr char kSpawnChild[] = "/boot/bin/spawn-child";
-static constexpr char kSpawnLauncher[] = "/boot/bin/spawn-launcher";
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static constexpr size_t kBufSz = 1024;
+
+static void init_path(const char* file, char* path) {
+    char* root_dir = getenv("TEST_ROOT_DIR");
+    ZX_ASSERT(root_dir != nullptr);
+    const char* infix = "/bin/";
+    memset(path, '\0', kBufSz);
+    strncpy(path, root_dir, kBufSz - 1);
+    strncat(path, infix, kBufSz - strlen(path) - 1);
+    strncat(path, file, kBufSz - strlen(path) - 1);
+}
+
+static const char* spawn_child() {
+  static char path[kBufSz];
+  static bool init = false;
+  if (!init) {
+      init = true;
+      init_path("spawn-child", path);
+  }
+  return path;
+}
+
+static const char* spawn_launcher() {
+  static char path[kBufSz];
+  static bool init = false;
+  if (!init) {
+      init = true;
+      init_path("spawn-launcher", path);
+  }
+  return path;
+}
 
 static bool has_fd(int fd) {
     zx_handle_t handle = ZX_HANDLE_INVALID;
@@ -49,25 +81,25 @@ static bool spawn_control_test(void) {
     zx::process process;
 
     {
-        const char* argv[] = {kSpawnChild, nullptr};
+        const char* argv[] = {spawn_child(), nullptr};
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(43, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--argc", nullptr};
+        const char* argv[] = {spawn_child(), "--argc", nullptr};
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(2, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--argc", "three", "four", "five", nullptr};
+        const char* argv[] = {spawn_child(), "--argc", "three", "four", "five", nullptr};
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(5, join(process));
     }
@@ -80,7 +112,7 @@ static bool spawn_launcher_test(void) {
 
     zx_status_t status;
     zx::process process;
-    const char* argv[] = {kSpawnLauncher, kSpawnChild, nullptr};
+    const char* argv[] = {spawn_launcher(), spawn_child(), nullptr};
 
     // Check that we can spawn the lancher process in a job and that the
     // launcher process can launch the child.
@@ -88,7 +120,7 @@ static bool spawn_launcher_test(void) {
         zx::job job;
         ASSERT_EQ(ZX_OK, zx::job::create(*zx::job::default_job(), 0, &job));
 
-        status = fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, kSpawnLauncher,
+        status = fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, spawn_launcher(),
                             argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(43, join(process));
@@ -106,7 +138,7 @@ static bool spawn_launcher_test(void) {
         };
         ASSERT_EQ(ZX_OK, job.set_policy(ZX_JOB_POL_RELATIVE, ZX_JOB_POL_BASIC, &policy, 1));
 
-        status = fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, kSpawnLauncher,
+        status = fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, spawn_launcher(),
                             argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(401, join(process));
@@ -122,18 +154,18 @@ static bool spawn_invalid_args_test(void) {
     zx_status_t status;
     zx::process process;
 
-    const char* argv[] = {kSpawnChild, nullptr};
+    const char* argv[] = {spawn_child(), nullptr};
 
     status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
                         "/bogus/not/a/file", argv, process.reset_and_get_address());
     ASSERT_EQ(ZX_ERR_NOT_FOUND, status);
 
     status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                        kSpawnChild, NULL, process.reset_and_get_address());
+                        spawn_child(), NULL, process.reset_and_get_address());
     ASSERT_EQ(ZX_ERR_INVALID_ARGS, status);
 
     status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                        kSpawnChild, argv + 1, process.reset_and_get_address());
+                        spawn_child(), argv + 1, process.reset_and_get_address());
     ASSERT_EQ(ZX_ERR_INVALID_ARGS, status);
 
     END_TEST;
@@ -148,57 +180,57 @@ static bool spawn_flags_test(void) {
     {
         // We can't actually launch a process without FDIO_SPAWN_DEFAULT_LDSVC
         // because we can't load the PT_INTERP.
-        const char* argv[] = {kSpawnChild, "--flags", "none", nullptr};
-        status = fdio_spawn(ZX_HANDLE_INVALID, 0, kSpawnChild, argv,
+        const char* argv[] = {spawn_child(), "--flags", "none", nullptr};
+        status = fdio_spawn(ZX_HANDLE_INVALID, 0, spawn_child(), argv,
                             process.reset_and_get_address());
         ASSERT_EQ(ZX_ERR_INVALID_ARGS, status);
         EXPECT_FALSE(process.is_valid());
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--flags", "none", nullptr};
+        const char* argv[] = {spawn_child(), "--flags", "none", nullptr};
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_DEFAULT_LDSVC,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(51, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--flags", "job", nullptr};
+        const char* argv[] = {spawn_child(), "--flags", "job", nullptr};
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_DEFAULT_LDSVC,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(52, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--flags", "namespace", nullptr};
+        const char* argv[] = {spawn_child(), "--flags", "namespace", nullptr};
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_DEFAULT_LDSVC | FDIO_SPAWN_CLONE_NAMESPACE,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(53, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--flags", "stdio", nullptr};
+        const char* argv[] = {spawn_child(), "--flags", "stdio", nullptr};
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_DEFAULT_LDSVC | FDIO_SPAWN_CLONE_STDIO,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(54, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--flags", "environ", nullptr};
+        const char* argv[] = {spawn_child(), "--flags", "environ", nullptr};
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_DEFAULT_LDSVC | FDIO_SPAWN_CLONE_ENVIRON,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(55, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--flags", "all", nullptr};
+        const char* argv[] = {spawn_child(), "--flags", "all", nullptr};
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(56, join(process));
     }
@@ -215,49 +247,49 @@ static bool spawn_environ_test(void) {
     setenv("SPAWN_TEST_PARENT", "1", 1);
 
     {
-        const char* argv[] = {kSpawnChild, "--env", "empty", nullptr};
+        const char* argv[] = {spawn_child(), "--env", "empty", nullptr};
         const char* env[] = {nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_DEFAULT_LDSVC,
-                                kSpawnChild, argv, env, 0, nullptr,
+                                spawn_child(), argv, env, 0, nullptr,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(61, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--env", "one", nullptr};
+        const char* argv[] = {spawn_child(), "--env", "one", nullptr};
         const char* env[] = {"SPAWN_TEST_CHILD=1", nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_DEFAULT_LDSVC,
-                                kSpawnChild, argv, env, 0, nullptr,
+                                spawn_child(), argv, env, 0, nullptr,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(62, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--env", "one", nullptr};
+        const char* argv[] = {spawn_child(), "--env", "one", nullptr};
         const char* env[] = {"SPAWN_TEST_CHILD=1", nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, env, 0, nullptr,
+                                spawn_child(), argv, env, 0, nullptr,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(62, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--env", "two", nullptr};
+        const char* argv[] = {spawn_child(), "--env", "two", nullptr};
         const char* env[] = {"SPAWN_TEST_CHILD=1", "SPAWN_TEST_CHILD2=1", nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, env, 0, nullptr,
+                                spawn_child(), argv, env, 0, nullptr,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(63, join(process));
     }
 
     {
-        const char* argv[] = {kSpawnChild, "--env", "clone", nullptr};
+        const char* argv[] = {spawn_child(), "--env", "clone", nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 0, nullptr,
+                                spawn_child(), argv, nullptr, 0, nullptr,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(64, join(process));
@@ -277,7 +309,7 @@ static bool spawn_actions_fd_test(void) {
     {
         const char* argv[] = {nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 0, nullptr,
+                                spawn_child(), argv, nullptr, 0, nullptr,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_ERR_INVALID_ARGS, status);
     }
@@ -289,7 +321,7 @@ static bool spawn_actions_fd_test(void) {
 
         const char* argv[] = {nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 1, &action,
+                                spawn_child(), argv, nullptr, 1, &action,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(42, join(process));
@@ -310,9 +342,9 @@ static bool spawn_actions_fd_test(void) {
         action.fd.local_fd = fd;
         action.fd.target_fd = 21;
 
-        const char* argv[] = {kSpawnChild, "--action", "clone-fd", nullptr};
+        const char* argv[] = {spawn_child(), "--action", "clone-fd", nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 1, &action,
+                                spawn_child(), argv, nullptr, 1, &action,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(71, join(process));
@@ -332,9 +364,9 @@ static bool spawn_actions_fd_test(void) {
         action.fd.local_fd = fd;
         action.fd.target_fd = 21;
 
-        const char* argv[] = {kSpawnChild, "--action", "transfer-fd", nullptr};
+        const char* argv[] = {spawn_child(), "--action", "transfer-fd", nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 1, &action,
+                                spawn_child(), argv, nullptr, 1, &action,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(72, join(process));
@@ -355,9 +387,9 @@ static bool spawn_actions_fd_test(void) {
         actions[1].fd.local_fd = fd;
         actions[1].fd.target_fd = 22;
 
-        const char* argv[] = {kSpawnChild, "--action", "clone-and-transfer-fd", nullptr};
+        const char* argv[] = {spawn_child(), "--action", "clone-and-transfer-fd", nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 2, actions,
+                                spawn_child(), argv, nullptr, 2, actions,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(73, join(process));
@@ -382,9 +414,9 @@ static bool spawn_actions_ns_test(void) {
         action.ns.prefix = "/foo/bar/baz";
         action.ns.handle = h1.release();
 
-        const char* argv[] = {kSpawnChild, "--action", "ns-entry", nullptr};
+        const char* argv[] = {spawn_child(), "--action", "ns-entry", nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 1, &action,
+                                spawn_child(), argv, nullptr, 1, &action,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(74, join(process));
@@ -408,9 +440,9 @@ static bool spawn_actions_h_test(void) {
         action.h.id = PA_USER0;
         action.h.handle = h1.release();
 
-        const char* argv[] = {kSpawnChild, "--action", "add-handle", nullptr};
+        const char* argv[] = {spawn_child(), "--action", "add-handle", nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 1, &action,
+                                spawn_child(), argv, nullptr, 1, &action,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(75, join(process));
@@ -432,9 +464,9 @@ static bool spawn_actions_name_test(void) {
         actions[1].action = FDIO_SPAWN_ACTION_SET_NAME;
         actions[1].name.data = "proc-name-1";
 
-        const char* argv[] = {kSpawnChild, nullptr};
+        const char* argv[] = {spawn_child(), nullptr};
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 2, actions,
+                                spawn_child(), argv, nullptr, 2, actions,
                                 process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(43, join(process));
@@ -452,14 +484,14 @@ static bool spawn_errors_test(void) {
     zx_status_t status;
     zx::process process;
     char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
-    const char* argv[] = {kSpawnChild, nullptr};
+    const char* argv[] = {spawn_child(), nullptr};
 
     ASSERT_EQ(ZX_ERR_INVALID_ARGS,
-              fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
+              fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, spawn_child(),
                          nullptr, process.reset_and_get_address()));
 
     ASSERT_EQ(ZX_ERR_INVALID_ARGS,
-              fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
+              fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, spawn_child(),
                              argv, nullptr, 1, nullptr, process.reset_and_get_address(), nullptr));
 
     {
@@ -469,7 +501,7 @@ static bool spawn_errors_test(void) {
         action.ns.handle = ZX_HANDLE_INVALID;
 
         ASSERT_EQ(ZX_ERR_INVALID_ARGS,
-                fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
+                fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, spawn_child(),
                                argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr));
     }
 
@@ -480,7 +512,7 @@ static bool spawn_errors_test(void) {
         action.h.handle = ZX_HANDLE_INVALID;
 
         ASSERT_EQ(ZX_ERR_INVALID_ARGS,
-                fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
+                fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, spawn_child(),
                                argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr));
     }
 
@@ -490,14 +522,14 @@ static bool spawn_errors_test(void) {
         action.name.data = nullptr;
 
         ASSERT_EQ(ZX_ERR_INVALID_ARGS,
-                fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
+                fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, spawn_child(),
                                argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr));
     }
 
     {
         const char* empty_argv[] = {nullptr};
         ASSERT_EQ(ZX_ERR_INVALID_ARGS,
-                  fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, kSpawnChild,
+                  fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, spawn_child(),
                              empty_argv, process.reset_and_get_address()));
     }
 
@@ -510,7 +542,7 @@ static bool spawn_errors_test(void) {
         zx::job job;
         ASSERT_EQ(ZX_OK, zx::job::default_job()->duplicate(0, &job));
         ASSERT_EQ(ZX_ERR_ACCESS_DENIED,
-                  fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, kSpawnChild,
+                  fdio_spawn(job.get(), FDIO_SPAWN_CLONE_ALL, spawn_child(),
                              argv, process.reset_and_get_address()));
     }
 
@@ -518,7 +550,7 @@ static bool spawn_errors_test(void) {
         ASSERT_EQ(30, dup2(0, 30));
         ASSERT_EQ(0, close(0));
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_OK, status);
         EXPECT_EQ(43, join(process));
         ASSERT_EQ(0, dup2(30, 0));
@@ -532,7 +564,7 @@ static bool spawn_errors_test(void) {
         fdio_t* io = fdio_zxio_create(&storage);
         ASSERT_EQ(0, fdio_bind_to_fd(io, 0, 0));
         status = fdio_spawn(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                            kSpawnChild, argv, process.reset_and_get_address());
+                            spawn_child(), argv, process.reset_and_get_address());
         ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, status);
         ASSERT_EQ(0, close(0));
         ASSERT_EQ(0, dup2(30, 0));
@@ -551,7 +583,7 @@ static bool spawn_errors_test(void) {
         action.fd.target_fd = 21;
 
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr);
+                                spawn_child(), argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, status);
         ASSERT_EQ(0, close(fd));
     }
@@ -568,7 +600,7 @@ static bool spawn_errors_test(void) {
         action.fd.target_fd = 21;
 
         status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
-                                kSpawnChild, argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr);
+                                spawn_child(), argv, nullptr, 1, &action, process.reset_and_get_address(), nullptr);
         ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, status);
         ASSERT_EQ(-1, close(fd));
     }
@@ -583,7 +615,7 @@ static bool spawn_vmo_test(void) {
     zx::process process;
 
     {
-        int fd = open(kSpawnChild, O_RDONLY);
+        int fd = open(spawn_child(), O_RDONLY);
         ASSERT_GE(fd, 0);
         zx_handle_t vmo;
         ASSERT_EQ(ZX_OK, fdio_get_vmo_clone(fd, &vmo));
@@ -592,7 +624,7 @@ static bool spawn_vmo_test(void) {
         zx_handle_t exec_vmo;
         ASSERT_EQ(ZX_OK, zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &exec_vmo));
 
-        const char* argv[] = {kSpawnChild, nullptr};
+        const char* argv[] = {spawn_child(), nullptr};
         status = fdio_spawn_vmo(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
                                 exec_vmo, argv, nullptr, 0, nullptr,
                                 process.reset_and_get_address(), nullptr);
diff --git a/zircon/system/utest/vdso-variant/vdso-variant.c b/zircon/system/utest/vdso-variant/vdso-variant.c
index a385c1243c329c1a49407824ffcf7023e7ac5775..fa606ed9873048e2c75baab6703cb1ea97516364 100644
--- a/zircon/system/utest/vdso-variant/vdso-variant.c
+++ b/zircon/system/utest/vdso-variant/vdso-variant.c
@@ -2,12 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <fcntl.h>
 #include <launchpad/launchpad.h>
+#include <zircon/assert.h>
 #include <zircon/syscalls.h>
 #include <zircon/status.h>
 #include <lib/fdio/io.h>
+
+#include <fcntl.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #define VDSO_FILE "/boot/kernel/vdso/test1"
@@ -41,7 +45,13 @@ int main(void) {
     launchpad_create(ZX_HANDLE_INVALID, "vdso-variant-helper", &lp);
     launchpad_clone(lp, LP_CLONE_ALL);
     launchpad_set_args(lp, 1, (const char*[]){"vdso-variant-helper"});
-    launchpad_load_from_file(lp, "/boot/bin/vdso-variant-helper");
+    char* root_dir = getenv("TEST_ROOT_DIR");
+    ZX_ASSERT(root_dir != NULL);
+    static const char kHelperPath[] = "/bin/vdso-variant-helper";
+    char path[strlen(root_dir) + strlen(kHelperPath) + 1];
+    strcpy(path, root_dir);
+    strcat(path, kHelperPath);
+    launchpad_load_from_file(lp, path);
     zx_handle_t proc;
     const char* errmsg;
     status = launchpad_go(lp, &proc, &errmsg);