diff --git a/garnet/packages/tools/BUILD.gn b/garnet/packages/tools/BUILD.gn
index 89a572e342cbc8492e580663c16697a2bf4a4ef6..7068e5abc7353ae029a2f583cca00f55cb3d8f3b 100644
--- a/garnet/packages/tools/BUILD.gn
+++ b/garnet/packages/tools/BUILD.gn
@@ -42,6 +42,15 @@ group("zxdb") {
   ]
 }
 
+group("fidl_tools") {
+  testonly = true
+  public_deps = [
+    "//garnet/go/src/fidl",
+    "//tools/fidlgen_llcpp_zircon",
+    "//garnet/packages/tools:gidl",
+  ]
+}
+
 group("all") {
   testonly = true
   public_deps = [
@@ -51,9 +60,8 @@ group("all") {
     "//garnet/packages/tools:crashpad_database_util",
     "//garnet/packages/tools:cs",
     "//garnet/packages/tools:curl",
-    "//garnet/go/src/fidl",
+    "//garnet/packages/tools:fidl_tools",
     "//garnet/packages/tools:fidlmerge",
-    "//garnet/packages/tools:gidl",
     "//garnet/packages/tools:iperf",
     "//garnet/packages/tools:magma",
     "//garnet/packages/tools:make-efi",
diff --git a/tools/fidlgen_llcpp_zircon/BUILD.gn b/tools/fidlgen_llcpp_zircon/BUILD.gn
new file mode 100644
index 0000000000000000000000000000000000000000..ee567c8e1b52e1cc03f69cf70f26e4189702ee22
--- /dev/null
+++ b/tools/fidlgen_llcpp_zircon/BUILD.gn
@@ -0,0 +1,45 @@
+# Copyright 2018 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("//build/host.gni")
+import("//build/compiled_action.gni")
+import("//build/config/fuchsia/zircon.gni")
+
+executable("fidlgen_llcpp_zircon") {
+  sources = [
+    "llcpp_codegen.cc",
+    "llcpp_codegen.h",
+    "main.cc",
+  ]
+  deps = [ "//third_party/rapidjson" ]
+  data_deps = [ "//garnet/go/src/fidl" ]
+}
+
+compiled_action("validate") {
+  tool = ":fidlgen_llcpp_zircon"
+  args = [
+    "validate",
+    rebase_path(zircon_root_build_dir, root_build_dir),
+    rebase_path("$root_build_dir/tools/fidlgen_llcpp", root_build_dir),
+    rebase_path("$target_gen_dir/fidlgen_llcpp_zircon_validated.stamp", root_build_dir),
+    rebase_path("$target_gen_dir/fidlgen_llcpp_zircon_validated.d", root_build_dir)
+  ]
+  depfile = "$target_gen_dir/fidlgen_llcpp_zircon_validated.d"
+  inputs = [ "$zircon_root_build_dir/fidl_gen.json" ]
+  outputs = [ "$target_gen_dir/fidlgen_llcpp_zircon_validated.stamp" ]
+}
+
+compiled_action("update") {
+  tool = ":fidlgen_llcpp_zircon"
+  args = [
+    "update",
+    rebase_path(zircon_root_build_dir, root_build_dir),
+    rebase_path("$root_build_dir/tools/fidlgen_llcpp", root_build_dir),
+    rebase_path("$target_gen_dir/fidlgen_llcpp_zircon_updated.stamp", root_build_dir),
+    rebase_path("$target_gen_dir/fidlgen_llcpp_zircon_updated.d", root_build_dir)
+  ]
+  depfile = "$target_gen_dir/fidlgen_llcpp_zircon_updated.d"
+  inputs = [ "$zircon_root_build_dir/fidl_gen.json" ]
+  outputs = [ "$target_gen_dir/fidlgen_llcpp_zircon_updated.stamp" ]
+}
diff --git a/tools/fidlgen_llcpp_zircon/README.md b/tools/fidlgen_llcpp_zircon/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..35da4c4e90efd9e461673d56dc280500559aee0b
--- /dev/null
+++ b/tools/fidlgen_llcpp_zircon/README.md
@@ -0,0 +1,25 @@
+## Generated FIDL Low-Level C++ Bindings
+
+Because of BLD-427 and BLD-353 blocking invoking `fidlgen` from the zircon build,
+we intend to check in copies of llcpp bindings for select FIDL libraries,
+as a workaround, to support limited use of llcpp.
+
+Each checked in library can be referenced similar to the C bindings, e.g. whereas one would write
+`"$zx/system/fidl/fuchsia-mem:c"` to get the auto-generated C FIDL bindings, the llcpp version is
+`"$zx/system/fidl/fuchsia-mem:llcpp"`.
+
+When using it in source code, whereas one would write `#include <fuchsia/mem/c/fidl.h>` to import
+the C bindings header, the corresponding llcpp directive would be
+`#include <fuchsia/mem/llcpp/fidl.h>`.
+
+To regenerate all the bindings, simply run the following command:
+```bash
+fx build -k 0 tools/fidlgen_llcpp_zircon:update
+```
+The `-k 0` switches would keep the build going even if parts of zircon failed to build.
+
+[TODO(yifeit): Implement] As extra precaution measure, the full build will validate that the
+generated bindings are up-to-date. You may manually run the same check with the following command:
+```bash
+fx build tools/fidlgen_llcpp_zircon:validate
+```
diff --git a/tools/fidlgen_llcpp_zircon/llcpp_codegen.cc b/tools/fidlgen_llcpp_zircon/llcpp_codegen.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9441db0bc0c3ed243211103dd9beda1ca5337038
--- /dev/null
+++ b/tools/fidlgen_llcpp_zircon/llcpp_codegen.cc
@@ -0,0 +1,146 @@
+// 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 <cstdio>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "llcpp_codegen.h"
+#include "rapidjson/document.h"
+#include "rapidjson/error/en.h"
+#include "rapidjson/istreamwrapper.h"
+
+namespace fs = std::filesystem;
+
+namespace {
+
+[[noreturn]] void FatalError(const std::string& info) {
+  std::cerr << "Error: " << info << ", errno: " << strerror(errno) << std::endl;
+  exit(1);
+}
+
+rapidjson::Document ReadMetadata(const fs::path& path) {
+  rapidjson::Document metadata;
+  std::ifstream contents(path);
+  if (contents.fail()) {
+    FatalError("Failed to read GN metadata at " + std::string(path));
+  }
+  rapidjson::IStreamWrapper isw(contents);
+  rapidjson::ParseResult parse_result = metadata.ParseStream(isw);
+  if (!parse_result) {
+    FatalError("Failed to parse " + std::string(path) + ", " +
+               rapidjson::GetParseError_En(parse_result.Code()) + ", offset " +
+               std::to_string(parse_result.Offset()));
+  }
+  if (!metadata.IsArray()) {
+    FatalError("Metadata is not an array");
+  }
+  return metadata;
+}
+
+struct Target {
+  fs::path gen_dir;
+  std::vector<fs::path> fidl_sources;
+  std::vector<std::string> args;
+};
+
+std::vector<Target> AllTargets(const fs::path& zircon_build_root) {
+  std::vector<Target> targets_vector;
+  const auto metadata = ReadMetadata(zircon_build_root / "fidl_gen.json");
+  for (auto value_it = metadata.Begin();
+       value_it != metadata.End(); ++value_it) {
+    const auto& target = *value_it;
+    const rapidjson::Value& args = target["args"];
+    if (!args.IsArray()) {
+      FatalError("args in metadata JSON must be an array");
+    }
+    std::vector<std::string> args_vector;
+    for (auto arg_it = args.Begin(); arg_it != args.End(); ++arg_it) {
+      args_vector.emplace_back(arg_it->GetString());
+    }
+    const rapidjson::Value& fidl_sources = target["fidl_sources"];
+    if (!fidl_sources.IsArray()) {
+      FatalError("fidl_sources in metadata JSON must be an array");
+    }
+    std::vector<fs::path> fidl_sources_vector;
+    for (auto it = fidl_sources.Begin(); it != fidl_sources.End(); ++it) {
+      fidl_sources_vector.emplace_back(fs::path(it->GetString()));
+    }
+    targets_vector.push_back(Target {
+      .gen_dir = fs::path(target["target_gen_dir"].GetString()),
+      .args = std::move(args_vector),
+      .fidl_sources = std::move(fidl_sources_vector)
+    });
+  }
+  return targets_vector;
+}
+
+// Run a command with the specified command, working directory, and arguments.
+void RunCommand(const std::string& cmd, const std::string& working_directory,
+                std::vector<std::string> args) {
+  pid_t pid = fork();
+  int status;
+  switch (pid) {
+  case -1:
+    FatalError("Failed to fork");
+  case 0: {
+    status = chdir(working_directory.c_str());
+    if (status != 0) {
+      FatalError("Failed to chdir to " + working_directory);
+    }
+    std::vector<char *> c_args;
+    c_args.push_back(const_cast<char *>(cmd.c_str()));
+    for (const auto& arg : args) {
+      c_args.push_back(const_cast<char *>(arg.c_str()));
+    }
+    c_args.push_back(nullptr);
+    execv(cmd.c_str(), &c_args[0]);
+    FatalError("when executing " + cmd + ", execv should not return");
+  }
+  default:
+    pid_t ret_pid = waitpid(pid, &status, 0);
+    if (pid != ret_pid) {
+      FatalError("when executing " + cmd +
+                 ", unexpected return value from waitpid: " +
+                 std::to_string(ret_pid));
+    }
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+      FatalError(cmd + " returned an error: " + std::to_string(status));
+    }
+  }
+}
+
+} // namespace
+
+bool DoValidate(std::filesystem::path zircon_build_root,
+                std::filesystem::path fidlgen_llcpp_path,
+                std::vector<fs::path>* out_dependencies) {
+  auto all_targets = AllTargets(zircon_build_root);
+  for (const auto& target : all_targets) {
+    for (const auto& source : target.fidl_sources) {
+      out_dependencies->push_back(zircon_build_root / source);
+    }
+    // TODO(yifeit): Implement.
+  }
+  return true;
+}
+
+void DoUpdate(fs::path zircon_build_root,
+              fs::path fidlgen_llcpp_path,
+              std::vector<fs::path>* out_dependencies) {
+  const auto all_targets = AllTargets(zircon_build_root);
+  for (const auto& target : all_targets) {
+    for (const auto& source : target.fidl_sources) {
+      out_dependencies->push_back(zircon_build_root / source);
+    }
+    RunCommand(fidlgen_llcpp_path, zircon_build_root, target.args);
+  }
+}
diff --git a/tools/fidlgen_llcpp_zircon/llcpp_codegen.h b/tools/fidlgen_llcpp_zircon/llcpp_codegen.h
new file mode 100644
index 0000000000000000000000000000000000000000..997addb8932ffe85fe0cb71d41bce8205790bf14
--- /dev/null
+++ b/tools/fidlgen_llcpp_zircon/llcpp_codegen.h
@@ -0,0 +1,23 @@
+// 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.
+
+#ifndef TOOLS_FIDLGEN_LLCPP_ZIRCON_LLCPP_CODEGEN_H_
+#define TOOLS_FIDLGEN_LLCPP_ZIRCON_LLCPP_CODEGEN_H_
+
+#include <filesystem>
+#include <string>
+#include <vector>
+
+// Validate without touching the checked-in sources.
+// Returns true when sources are up-to-date.
+bool DoValidate(std::filesystem::path zircon_build_root,
+                std::filesystem::path fidlgen_llcpp_path,
+                std::vector<std::filesystem::path>* out_dependencies);
+
+// Update the checked-in sources.
+void DoUpdate(std::filesystem::path zircon_build_root,
+              std::filesystem::path fidlgen_llcpp_path,
+              std::vector<std::filesystem::path>* out_dependencies);
+
+#endif  // TOOLS_FIDLGEN_LLCPP_ZIRCON_LLCPP_CODEGEN_H_
diff --git a/tools/fidlgen_llcpp_zircon/main.cc b/tools/fidlgen_llcpp_zircon/main.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1b5685d934510724c85515f0b279b3b631b07506
--- /dev/null
+++ b/tools/fidlgen_llcpp_zircon/main.cc
@@ -0,0 +1,88 @@
+// 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 <cstdio>
+#include <cstdlib>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <string.h>
+#include <utility>
+
+#include "llcpp_codegen.h"
+
+namespace fs = std::filesystem;
+
+static void Usage(const char* exe_name) {
+  fprintf(
+      stderr,
+      "Generate or validate the checked-in low-level C++ bindings in zircon.\n"
+      "Usage: %s (validate|update) ZIRCON_BUILDROOT FIDLGEN_LLCPP_PATH "
+      "STAMP DEPFILE\n"
+      "ZIRCON_BUILDROOT is the root build directory of the Zircon GN build.\n"
+      "FIDLGEN_LLCPP_PATH is the path to the fidlgen_llcpp executable.\n"
+      "STAMP is the output path to a file indicating the success of the tool.\n"
+      "DEPFILE is the output path to a depfile describing the FIDL files\n"
+      "which when updated should trigger a re-run of this tool.\n"
+      "\n"
+      "When validate is specified, it will validate that the generated\n"
+      "bindings are up-to-date, exiting with an error if not so.\n"
+      "Files in the source tree are not modified.\n"
+      "\n"
+      "When update is specified, it will regenerate the bindings in\n"
+      "zircon/system/fidl from GN metadata.\n"
+      "\n",
+      exe_name);
+}
+
+int main(int argc, char** argv) {
+  if (argc != 6) {
+    std::cerr << argv[0] << ": Invalid arguments" << "\n\n";
+    Usage(argv[0]);
+    return -1;
+  }
+  // Since we're dealing with two builds, it's less ambiguous if we start with
+  // all absolute paths in the beginning, then convert to relative paths
+  // where required, similar to rebase_path in GN.
+  fs::path zircon_build_root = fs::absolute(argv[2]);
+  fs::path fidlgen_llcpp_path = fs::absolute(argv[3]);
+  fs::path stamp_path = fs::absolute(argv[4]);
+  fs::remove(stamp_path);
+  fs::path depfile_path = fs::absolute(argv[5]);
+  fs::remove(depfile_path);
+
+  std::vector<fs::path> dependencies;
+  if (strcmp(argv[1], "validate") == 0) {
+    DoValidate(zircon_build_root, fidlgen_llcpp_path, &dependencies);
+  } else if (strcmp(argv[1], "update") == 0) {
+    DoUpdate(zircon_build_root, fidlgen_llcpp_path, &dependencies);
+  } else {
+    std::cerr << argv[0] << ": Expected validate or update, not " << argv[1]
+              << "\n\n";
+    Usage(argv[0]);
+    return -1;
+  }
+  // Generate stamp file
+  std::fstream stamp;
+  stamp.open(stamp_path, std::ios::out);
+  if (!stamp) {
+    std::cerr << "Failed to stamp " << stamp_path << std::endl;
+    return -1;
+  }
+  stamp.close();
+  // Generate depfile
+  std::fstream depfile;
+  depfile.open(depfile_path, std::ios::out);
+  if (!depfile) {
+    std::cerr << "Failed to create depfile " << depfile_path << std::endl;
+    return -1;
+  }
+  depfile << fs::relative(stamp_path).string() << ":";
+  for (const auto& dep : dependencies) {
+    depfile << " " << fs::relative(dep).string();
+  }
+  depfile << std::endl;
+  depfile.close();
+  return 0;
+}
\ No newline at end of file
diff --git a/zircon/system/fidl/fuchsia-mem/gen/llcpp/fidl.cc b/zircon/system/fidl/fuchsia-mem/gen/llcpp/fidl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9a0d935d6a27e370b61ba66b1f2447cdc31d3274
--- /dev/null
+++ b/zircon/system/fidl/fuchsia-mem/gen/llcpp/fidl.cc
@@ -0,0 +1,10 @@
+// WARNING: This file is machine generated by fidlgen.
+
+#include <fuchsia/mem/llcpp/fidl.h>
+#include <memory>
+
+namespace fuchsia {
+namespace mem {
+
+}  // namespace mem
+}  // namespace fuchsia
diff --git a/zircon/system/fidl/fuchsia-mem/gen/llcpp/include/fuchsia/mem/llcpp/fidl.h b/zircon/system/fidl/fuchsia-mem/gen/llcpp/include/fuchsia/mem/llcpp/fidl.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d1169911f59cef7d316071f6a3b5549ad7b83ee
--- /dev/null
+++ b/zircon/system/fidl/fuchsia-mem/gen/llcpp/include/fuchsia/mem/llcpp/fidl.h
@@ -0,0 +1,56 @@
+// WARNING: This file is machine generated by fidlgen.
+
+#pragma once
+
+#include <lib/fidl/internal.h>
+#include <lib/fidl/cpp/vector_view.h>
+#include <lib/fidl/cpp/string_view.h>
+#include <lib/fidl/llcpp/array.h>
+#include <lib/fidl/llcpp/coding.h>
+#include <lib/fidl/llcpp/traits.h>
+#include <lib/fidl/llcpp/transaction.h>
+#include <lib/zx/vmo.h>
+#include <zircon/fidl.h>
+
+namespace fuchsia {
+namespace mem {
+
+struct Buffer;
+
+extern "C" const fidl_type_t fuchsia_mem_BufferTable;
+
+// A Buffer for data whose size is not necessarily a multiple of the page
+// size.
+//
+// VMO objects have a physical size that is always a multiple of the page
+// size. As such, VMO alone cannot serve as a buffer for arbitrarly sized
+// data. |fuchsia.mem.Buffer| is a standard struct that aggregate the VMO
+// and its size.
+struct Buffer {
+  static constexpr const fidl_type_t* Type = &fuchsia_mem_BufferTable;
+  static constexpr uint32_t MaxNumHandles = 1;
+  static constexpr uint32_t PrimarySize = 16;
+  [[maybe_unused]]
+  static constexpr uint32_t MaxOutOfLine = 0;
+
+  // The vmo.
+  ::zx::vmo vmo{};
+
+  // The size of the data in the vmo in bytes. This size must be smaller
+  // than the physical size of the vmo.
+  uint64_t size{};
+};
+
+}  // namespace mem
+}  // namespace fuchsia
+
+namespace fidl {
+
+template <>
+struct IsFidlType<::fuchsia::mem::Buffer> : public std::true_type {};
+static_assert(std::is_standard_layout_v<::fuchsia::mem::Buffer>);
+static_assert(offsetof(::fuchsia::mem::Buffer, vmo) == 0);
+static_assert(offsetof(::fuchsia::mem::Buffer, size) == 8);
+static_assert(sizeof(::fuchsia::mem::Buffer) == ::fuchsia::mem::Buffer::PrimarySize);
+
+}  // namespace fidl