Creating a pexe content handler to translate and run pexes.

-Creates translator and linker applications, which launch the pexe
translator (pnacl-llc.nexe) and pexe loader (ld.nexe), passing a message
pipe handle.
-Updates IRT to include callbacks for pexe translator and pexe loader.
-Creates a content handler application, which contacts the translator
and loader services, communicating with them via message pipes. Uses a mojom
interface to pass a message pipe to each application.
-The translation process places a PEXE FILE into a temporary file, compiles
that pexe into a temporary OBJECT FILE, and finally links that object file
into a finalized temporary NEXE FILE. All file names are passed via message
pipes, and all files are unlinked after they are opened by the final service
which uses them.
-Adds some .pexe.mojo files to the nacl_nonsfi_apptests.

Test by running:
> ./mojo/tools/mojob.py build
> ./out/Debug/mojo_shell --enable-multiprocess ./out/Debug/shell_apptests.pexe.mojo

BUG=https://github.com/domokit/mojo/issues/396
R=mseaborn@chromium.org, phosek@chromium.org

Review URL: https://codereview.chromium.org/1382713002 .
diff --git a/DEPS b/DEPS
index 2c769ee..3ddc58f 100644
--- a/DEPS
+++ b/DEPS
@@ -314,6 +314,7 @@
     'action': [
         'python', 'src/build/download_nacl_toolchains.py',
         '--packages', 'pnacl_newlib',
+        '--packages', 'pnacl_translator',
         'sync', '--extract',
     ],
   },
diff --git a/mojo/nacl/nonsfi/BUILD.gn b/mojo/nacl/nonsfi/BUILD.gn
index cf13f41..5753024 100644
--- a/mojo/nacl/nonsfi/BUILD.gn
+++ b/mojo/nacl/nonsfi/BUILD.gn
@@ -17,7 +17,6 @@
       deps = [
         ":irt_mojo_nonsfi",
         "//mojo/edk/system",
-        "//mojo/gles2:mgl",
         "//mojo/public/cpp/environment:standalone",
         "//native_client/src/nonsfi/loader:elf_loader",
       ]
@@ -43,6 +42,9 @@
   sources = [
     "irt_mojo_nonsfi.cc",
     "irt_mojo_nonsfi.h",
+    "irt_pnacl_translator_compile.cc",
+    "irt_pnacl_translator_link.cc",
+    "irt_resource_open.cc",
     "nexe_launcher_nonsfi.cc",
   ]
 
@@ -52,14 +54,18 @@
     "//mojo/public/c/gpu:MGL_onscreen",
     "//mojo/public/c/gpu:MGL_signal_sync_point",
     "//mojo/public/c/system",
+    "//mojo/public/cpp/utility",
     "//mojo/public/platform/nacl:mojo_irt_header",
+    "//mojo/public/platform/native:mgl_thunks",
+    "//mojo/public/platform/native:mgl_onscreen_thunks",
     "//native_client/src/nonsfi/irt:irt_interfaces",
+    "//services/nacl:pnacl_translator_bindings",
   ]
 }
 
 group("mojo_nacl_nonsfi") {
   deps = [
-    "//services/nacl:nacl_content_handler_nonsfi",
+    "//services/nacl:content_handler_nonsfi",
   ]
 }
 
@@ -121,7 +127,7 @@
     output,
   ]
 
-  line = "#!mojo mojo:nacl_content_handler_nonsfi"
+  line = "#!mojo mojo:content_handler_nonsfi_nexe"
   args = [
     "--input=" + rebase_path(input, root_build_dir),
     "--output=" + rebase_path(output, root_build_dir),
diff --git a/mojo/nacl/nonsfi/README.md b/mojo/nacl/nonsfi/README.md
index 8fec9f8..3ecae5a 100644
--- a/mojo/nacl/nonsfi/README.md
+++ b/mojo/nacl/nonsfi/README.md
@@ -44,7 +44,7 @@
 nexe?).
 
 The BUILD.gn files in Mojo automatically make this pexe, translate it to a
-nexe, and prepend a line ("#!mojo mojo:nacl_content_handler_nonsfi") to the
+nexe, and prepend a line ("#!mojo mojo:content_handler_nonsfi_nexe") to the
 front of the file, at which point it is also runnable through the mojo_shell.
 These files, which start with this "#!" line, are ".mojo" files. The nexes can
 be run like this:
@@ -56,8 +56,6 @@
 TODO
 ====
 
-* Enable content handling of Non-SFI pexes, which should be translated and run
-as nexes.
-  * Use subzero (a NaCl project to improve translation speed) for pexe
-  translation.
-  * Enable caching of translated pexes.
+* Use subzero (a NaCl project to improve translation speed) for pexe
+translation.
+* Enable caching of translated pexes.
diff --git a/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc b/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc
index 4fed496..2c34e45 100644
--- a/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc
+++ b/mojo/nacl/nonsfi/irt_mojo_nonsfi.cc
@@ -8,15 +8,12 @@
 #include "mojo/public/platform/nacl/mgl_irt.h"
 #include "mojo/public/platform/nacl/mojo_irt.h"
 #include "native_client/src/public/irt_core.h"
+#include "native_client/src/untrusted/irt/irt_dev.h"
 
 namespace {
 
 MojoHandle g_mojo_handle = MOJO_HANDLE_INVALID;
-
-MojoResult _MojoGetInitialHandle(MojoHandle* handle) {
-  *handle = g_mojo_handle;
-  return MOJO_RESULT_OK;
-}
+bool g_running_translator = false;
 
 const struct nacl_irt_mojo kIrtMojo = {
     MojoCreateSharedBuffer,
@@ -37,7 +34,7 @@
     MojoCreateMessagePipe,
     MojoWriteMessage,
     MojoReadMessage,
-    _MojoGetInitialHandle,
+    nacl::MojoGetInitialHandle,
 };
 
 const struct nacl_irt_mgl kIrtMGL = {
@@ -57,23 +54,65 @@
     MGLSignalSyncPoint,
 };
 
+int NotPNaClFilter() {
+  return g_running_translator;
+}
+
 const struct nacl_irt_interface kIrtInterfaces[] = {
-    {NACL_IRT_MOJO_v0_1, &kIrtMojo, sizeof(kIrtMojo), nullptr},
-    {NACL_IRT_MGL_v0_1, &kIrtMGL, sizeof(kIrtMGL), nullptr},
-    {NACL_IRT_MGL_ONSCREEN_v0_1, &kIrtMGLOnScreen, sizeof(kIrtMGLOnScreen),
-      nullptr},
-    {NACL_IRT_MGL_SIGNAL_SYNC_POINT_v0_1, &kIrtMGLSignalSyncPoint,
-      sizeof(kIrtMGLSignalSyncPoint), nullptr},
+    // Interface to call Mojo functions
+    { NACL_IRT_MOJO_v0_1,
+      &kIrtMojo,
+      sizeof(kIrtMojo),
+      nullptr },
+    // Interface to call PNaCl translation
+    { NACL_IRT_PRIVATE_PNACL_TRANSLATOR_COMPILE_v0_1,
+      &nacl::nacl_irt_private_pnacl_translator_compile,
+      sizeof(nacl_irt_private_pnacl_translator_compile),
+      NotPNaClFilter },
+    // Interface to call PNaCl linking
+    { NACL_IRT_PRIVATE_PNACL_TRANSLATOR_LINK_v0_1,
+      &nacl::nacl_irt_private_pnacl_translator_link,
+      sizeof(nacl_irt_private_pnacl_translator_link),
+      NotPNaClFilter },
+    // Adds mechanism for opening object files, like crtbegin.o
+    { NACL_IRT_RESOURCE_OPEN_v0_1,
+      &nacl::nacl_irt_resource_open,
+      sizeof(nacl_irt_resource_open),
+      NotPNaClFilter },
+    // GPU functions which give control over MGL context
+    { NACL_IRT_MGL_v0_1,
+      &kIrtMGL,
+      sizeof(kIrtMGL),
+      nullptr },
+    // GPU functions which update framebuffer with respect to the display
+    { NACL_IRT_MGL_ONSCREEN_v0_1,
+      &kIrtMGLOnScreen,
+      sizeof(kIrtMGLOnScreen),
+      nullptr },
+    // GPU functions to synchronize CPU and GPU services
+    { NACL_IRT_MGL_SIGNAL_SYNC_POINT_v0_1,
+      &kIrtMGLSignalSyncPoint,
+      sizeof(kIrtMGLSignalSyncPoint),
+      nullptr },
 };
 
 }  // namespace
 
 namespace nacl {
 
+MojoResult MojoGetInitialHandle(MojoHandle* handle) {
+  *handle = g_mojo_handle;
+  return MOJO_RESULT_OK;
+}
+
 void MojoSetInitialHandle(MojoHandle handle) {
   g_mojo_handle = handle;
 }
 
+void MojoPnaclTranslatorEnable() {
+  g_running_translator = true;
+}
+
 size_t MojoIrtNonsfiQuery(const char* interface_ident,
                           void* table,
                           size_t tablesize) {
diff --git a/mojo/nacl/nonsfi/irt_mojo_nonsfi.h b/mojo/nacl/nonsfi/irt_mojo_nonsfi.h
index 2895902..e2d7b91 100644
--- a/mojo/nacl/nonsfi/irt_mojo_nonsfi.h
+++ b/mojo/nacl/nonsfi/irt_mojo_nonsfi.h
@@ -6,13 +6,27 @@
 #define MOJO_NACL_NONSFI_IRT_MOJO_NONSFI_H_
 
 #include "mojo/public/c/system/functions.h"
+#include "native_client/src/untrusted/irt/irt_dev.h"
 
 namespace nacl {
 
+extern const struct nacl_irt_private_pnacl_translator_compile
+    nacl_irt_private_pnacl_translator_compile;
+extern const struct nacl_irt_private_pnacl_translator_link
+    nacl_irt_private_pnacl_translator_link;
+extern const struct nacl_irt_resource_open
+    nacl_irt_resource_open;
+
 // Used to pass handle to application. If uncalled,
 // the handle defaults to MOJO_HANDLE_INVALID.
 void MojoSetInitialHandle(MojoHandle handle);
 
+MojoResult MojoGetInitialHandle(MojoHandle* handle);
+
+// Mechanism to control when nexes get access to the PNaCl translation
+// IRT functions.
+void MojoPnaclTranslatorEnable();
+
 // IRT interface query function which may be passed to nacl_irt_nonsfi_entry.
 // Queries for a mojo interface, then for the irt core.
 size_t MojoIrtNonsfiQuery(const char* interface_ident,
diff --git a/mojo/nacl/nonsfi/irt_pnacl_translator_compile.cc b/mojo/nacl/nonsfi/irt_pnacl_translator_compile.cc
new file mode 100644
index 0000000..024d407
--- /dev/null
+++ b/mojo/nacl/nonsfi/irt_pnacl_translator_compile.cc
@@ -0,0 +1,106 @@
+// Copyright 2015 The Chromium 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 <fcntl.h>
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "mojo/nacl/nonsfi/irt_mojo_nonsfi.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "native_client/src/untrusted/irt/irt_dev.h"
+#include "services/nacl/pnacl_compile.mojom.h"
+
+namespace {
+
+// Implements a mojom interface which allows the content handler to communicate
+// with the nexe compiler service.
+class PexeCompilerImpl : public mojo::nacl::PexeCompiler {
+ public:
+  PexeCompilerImpl(mojo::ScopedMessagePipeHandle handle,
+                   const struct nacl_irt_pnacl_compile_funcs* funcs)
+      : funcs_(funcs), strong_binding_(this, handle.Pass()) {}
+  void PexeCompile(const mojo::String& pexe_file_name,
+                   const mojo::Callback<void(mojo::String)>& callback)
+      override {
+    base::FilePath obj_file_name;
+    if (!CreateTemporaryFile(&obj_file_name))
+      LOG(FATAL) << "Could not make temporary object file";
+    // TODO(smklein): Use multiple object files to increase parallelism.
+    int obj_file_fd = open(obj_file_name.value().c_str(), O_RDWR, O_TRUNC);
+
+    if (obj_file_fd < 0)
+      LOG(FATAL) << "Could not create temp file for compiled pexe";
+
+    // TODO(smklein): Is there a less arbitrary number to choose?
+    uint32_t num_threads = 8;
+    size_t obj_file_fd_count = 1;
+    // Non-SFI mode requries PIC.
+    char relocation_model[] = "-relocation-model=pic";
+    // Since we are compiling pexes, the bitcode format is 'pnacl'.
+    char bitcode_format[] = "-bitcode-format=pnacl";
+    char* args[] = { relocation_model, bitcode_format, nullptr };
+    size_t argc = 2;
+    funcs_->init_callback(num_threads, &obj_file_fd, obj_file_fd_count,
+                          args, argc);
+
+    // Read the pexe using fread, and write the pexe into the callback function.
+    static const size_t kBufferSize = 0x100000;
+    scoped_ptr<char[]> buf(new char[kBufferSize]);
+    FILE* pexe_file_stream = fopen(pexe_file_name.get().c_str(), "r");
+    // Once the pexe has been opened, it is no longer needed, so we unlink it.
+    if (unlink(pexe_file_name.get().c_str()))
+      LOG(FATAL) << "Could not unlink temporary pexe file";
+    if (pexe_file_stream == nullptr)
+      LOG(FATAL) << "Could not open pexe for reading";
+    for (;;) {
+      size_t num_bytes_from_pexe = fread(buf.get(), 1, kBufferSize,
+                                         pexe_file_stream);
+      if (ferror(pexe_file_stream)) {
+        LOG(FATAL) << "Error reading from pexe file stream";
+      }
+      if (num_bytes_from_pexe == 0) {
+        break;
+      }
+      funcs_->data_callback(buf.get(), num_bytes_from_pexe);
+    }
+    buf.reset();
+
+    if (fclose(pexe_file_stream))
+      LOG(FATAL) << "Failed to close pexe file stream from compiler nexe";
+    funcs_->end_callback();
+
+    // Return the name of the object file.
+    callback.Run(mojo::String(obj_file_name.value()));
+  }
+ private:
+  const struct nacl_irt_pnacl_compile_funcs* funcs_;
+  mojo::StrongBinding<mojo::nacl::PexeCompiler> strong_binding_;
+};
+
+void ServeTranslateRequest(const struct nacl_irt_pnacl_compile_funcs* funcs) {
+  // Acquire the handle -- this is our mechanism to contact the
+  // content handler which called us.
+  MojoHandle handle;
+  nacl::MojoGetInitialHandle(&handle);
+
+  // Convert the MojoHandle into a ScopedMessagePipeHandle, and use that to
+  // implement the PexeCompiler interface.
+  PexeCompilerImpl impl(
+      mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(handle)).Pass(),
+      funcs);
+  mojo::RunLoop::current()->RunUntilIdle();
+}
+
+}  // namespace anonymous
+
+namespace nacl {
+
+const struct nacl_irt_private_pnacl_translator_compile
+    nacl_irt_private_pnacl_translator_compile = {
+  ServeTranslateRequest
+};
+
+}  // namespace nacl
diff --git a/mojo/nacl/nonsfi/irt_pnacl_translator_link.cc b/mojo/nacl/nonsfi/irt_pnacl_translator_link.cc
new file mode 100644
index 0000000..83d6a79
--- /dev/null
+++ b/mojo/nacl/nonsfi/irt_pnacl_translator_link.cc
@@ -0,0 +1,79 @@
+// Copyright 2015 The Chromium 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 <fcntl.h>
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "mojo/nacl/nonsfi/irt_mojo_nonsfi.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "native_client/src/untrusted/irt/irt_dev.h"
+#include "services/nacl/pnacl_link.mojom.h"
+
+namespace {
+
+typedef int (*LinkerCallback)(int, const int*, int);
+
+class PexeLinkerImpl : public mojo::nacl::PexeLinker {
+ public:
+  PexeLinkerImpl(mojo::ScopedMessagePipeHandle handle, LinkerCallback func)
+      : func_(func), strong_binding_(this, handle.Pass()) {}
+  void PexeLink(const mojo::String& object_file_name,
+                const mojo::Callback<void(mojo::String)>& callback)
+      override {
+    // Create a temporary .nexe file which will be the result of calling our
+    // linker.
+    base::FilePath nexe_file_name;
+    if (!CreateTemporaryFile(&nexe_file_name))
+      LOG(FATAL) << "Could not create temporary nexe file";
+    int nexe_file_fd = open(nexe_file_name.value().c_str(), O_RDWR);
+    if (nexe_file_fd < 0)
+      LOG(FATAL) << "Could not create temp file for linked nexe";
+
+    // Open our temporary object file. Additionally, unlink it, since it is a
+    // temporary file that is no longer needed after it is opened.
+    size_t obj_file_fd_count = 1;
+    int obj_file_fd = open(object_file_name.get().c_str(), O_RDONLY);
+    if (unlink(object_file_name.get().c_str()))
+      LOG(FATAL) << "Could not unlink temporary object file";
+    if (obj_file_fd < 0)
+      LOG(FATAL) << "Could not open object file";
+
+    if (func_(nexe_file_fd, &obj_file_fd, obj_file_fd_count))
+      LOG(FATAL) << "Error calling func on object file";
+
+    // Return the name of the nexe file.
+    callback.Run(mojo::String(nexe_file_name.value()));
+  }
+ private:
+  LinkerCallback func_;
+  mojo::StrongBinding<mojo::nacl::PexeLinker> strong_binding_;
+};
+
+void ServeLinkRequest(LinkerCallback func) {
+  // Acquire the handle -- this is our mechanism to contact the
+  // content handler which called us.
+  MojoHandle handle;
+  nacl::MojoGetInitialHandle(&handle);
+
+  // Convert the MojoHandle into a ScopedMessagePipeHandle, and use that to
+  // implement the PexeLinker interface.
+  PexeLinkerImpl impl(
+      mojo::ScopedMessagePipeHandle(mojo::MessagePipeHandle(handle)).Pass(),
+      func);
+  mojo::RunLoop::current()->RunUntilIdle();
+}
+
+}  // namespace anonymous
+
+namespace nacl {
+
+const struct nacl_irt_private_pnacl_translator_link
+    nacl_irt_private_pnacl_translator_link = {
+  ServeLinkRequest
+};
+
+}  // namespace nacl
diff --git a/mojo/nacl/nonsfi/irt_resource_open.cc b/mojo/nacl/nonsfi/irt_resource_open.cc
new file mode 100644
index 0000000..0587f97
--- /dev/null
+++ b/mojo/nacl/nonsfi/irt_resource_open.cc
@@ -0,0 +1,38 @@
+// Copyright 2015 The Chromium 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 <fcntl.h>
+
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "mojo/nacl/nonsfi/irt_mojo_nonsfi.h"
+#include "native_client/src/untrusted/irt/irt_dev.h"
+
+namespace {
+
+int IrtOpenResource(const char* filename, int* newfd) {
+  base::FilePath path;
+  if (!PathService::Get(base::DIR_MODULE, &path))
+    return ENOENT;
+  path = path.Append("pnacl_translation_files");
+  if (strcmp(filename, "libpnacl_irt_shim.a"))
+    path = path.Append(filename);
+  else
+    path = path.Append("libpnacl_irt_shim_dummy.a");
+  int rv = open(path.value().c_str(), O_RDONLY);
+  if (rv < 0)
+    return errno;
+  *newfd = rv;
+  return 0;
+}
+
+}  // namespace anonymous
+
+namespace nacl {
+
+const struct nacl_irt_resource_open nacl_irt_resource_open = {
+  IrtOpenResource,
+};
+
+}  // namespace nacl
diff --git a/mojo/nacl/nonsfi/nexe_launcher_nonsfi.cc b/mojo/nacl/nonsfi/nexe_launcher_nonsfi.cc
index c8edb8b..778ee73 100644
--- a/mojo/nacl/nonsfi/nexe_launcher_nonsfi.cc
+++ b/mojo/nacl/nonsfi/nexe_launcher_nonsfi.cc
@@ -12,10 +12,15 @@
 
 namespace nacl {
 
-void MojoLaunchNexeNonsfi(int nexe_fd, MojoHandle initial_handle) {
+void MojoLaunchNexeNonsfi(int nexe_fd, MojoHandle initial_handle,
+                          bool enable_translate_irt) {
   // Run -- also, closes the nexe_fd, removing the temp file.
   uintptr_t entry = NaClLoadElfFile(nexe_fd);
 
+  // Enable the translation section of the IRT, if requested.
+  if (enable_translate_irt) {
+    MojoPnaclTranslatorEnable();
+  }
   MojoSetInitialHandle(initial_handle);
   int argc = 1;
   char* argvp = const_cast<char*>("NaClMain");
diff --git a/mojo/nacl/nonsfi/nexe_launcher_nonsfi.h b/mojo/nacl/nonsfi/nexe_launcher_nonsfi.h
index 66172ec..b9e6512 100644
--- a/mojo/nacl/nonsfi/nexe_launcher_nonsfi.h
+++ b/mojo/nacl/nonsfi/nexe_launcher_nonsfi.h
@@ -13,7 +13,8 @@
  * Takes a fd to a nexe, and launches the nexe with the given
  * MojoHandle.
  */
-void MojoLaunchNexeNonsfi(int nexe_fd, MojoHandle initial_handle);
+void MojoLaunchNexeNonsfi(int nexe_fd, MojoHandle initial_handle,
+                          bool enable_translate_irt);
 
 } // namespace nacl
 
diff --git a/mojo/nacl/sfi/BUILD.gn b/mojo/nacl/sfi/BUILD.gn
index 1521e8f..ad61038 100644
--- a/mojo/nacl/sfi/BUILD.gn
+++ b/mojo/nacl/sfi/BUILD.gn
@@ -27,7 +27,7 @@
 
 group("mojo_nacl") {
   deps = [
-    "//services/nacl:nacl_content_handler",
+    "//services/nacl:content_handler_sfi",
   ]
 }
 
diff --git a/mojo/public/mojo_application.gni b/mojo/public/mojo_application.gni
index 9b759c1..faf9d09 100644
--- a/mojo/public/mojo_application.gni
+++ b/mojo/public/mojo_application.gni
@@ -218,11 +218,11 @@
       if (current_cpu == "pnacl") {
         input_file_extension = ".pexe"
         output_file_extension = ".pexe.mojo"
-        content_handler = "pexe_content_handler"
+        content_handler = "content_handler_nonsfi_pexe"
       } else {
         input_file_extension = ".nexe"
         output_file_extension = "_${target_cpu}.nexe.mojo"
-        content_handler = "nacl_content_handler"
+        content_handler = "content_handler_sfi"
       }
       input_file = "${base_target_name}${input_file_extension}"
       output_file = "${base_target_name}${output_file_extension}"
diff --git a/mojo/tools/data/nacl_nonsfi_apptests b/mojo/tools/data/nacl_nonsfi_apptests
index 790deb3..b72fae3 100644
--- a/mojo/tools/data/nacl_nonsfi_apptests
+++ b/mojo/tools/data/nacl_nonsfi_apptests
@@ -1,12 +1,46 @@
 # This file contains a list of Non-SFI NaCl Mojo gtest app tests.
 # This must be a valid python dictionary.
+#
+# Multiprocess mode is required for the nonsfi nacl tests: the content
+# handler for these tests must exist in a 32-bit process but the parent
+# may be a 64-bit process. This makes multithreading insufficient for
+# these tests.
 tests = [
   {
     "test": "mojo:monacl_test_nonsfi",
-    # Multiprocess mode is required for the nonsfi nacl tests: the content
-    # handler for these tests must exist in a 32-bit process but the parent
-    # may be a 64-bit process. This makes multithreading insufficient for
-    # these tests.
     "shell-args": ["--enable-multiprocess"],
   },
+
+  {
+    "test": "mojo:shell_apptests.pexe",
+    "shell-args": ["--enable-multiprocess"],
+  },
+  {
+    "test": "mojo:http_server_apptests.pexe",
+    "shell-args": ["--enable-multiprocess"],
+  },
+  {
+    "test": "mojo:clipboard_apptests.pexe",
+    "shell-args": ["--enable-multiprocess"],
+  },
+  {
+    "test": "mojo:example_apptests.pexe",
+    # ExampleApplicationTest.CheckCommandLineArg checks --example_apptest_arg.
+    "shell-args": ["--enable-multiprocess"],
+    "test-args": ["--example_apptest_arg"],
+  },
+  {
+    "test": "mojo:files_apptests.pexe",
+    "shell-args": ["--enable-multiprocess"],
+  },
+  {
+    "test": "mojo:mojo_view_manager_client_apptests.pexe",
+    "shell-args": ["--enable-multiprocess", "--args-for=mojo:native_viewport_service --use-headless-config --use-osmesa"],
+  },
+  # TODO(smklein) Include "view_manager_service_apptests" once it isn't as slow.
+  {
+    "test": "mojo:window_manager_apptests.pexe",
+    "shell-args": ["--enable-multiprocess"],
+  },
+
 ]
diff --git a/services/nacl/BUILD.gn b/services/nacl/BUILD.gn
index 15c97d5..2dd258c 100644
--- a/services/nacl/BUILD.gn
+++ b/services/nacl/BUILD.gn
@@ -3,10 +3,11 @@
 # found in the LICENSE file.
 
 import("//mojo/public/mojo_application.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
 
 # Trusted code
 if (!is_nacl) {
-  mojo_native_application("nacl_content_handler") {
+  mojo_native_application("content_handler_sfi") {
     sources = [
       "content_handler_main.cc",
     ]
@@ -31,7 +32,8 @@
   if (current_cpu == "x86" || current_cpu == "arm") {
     # Non-SFI NaCl can only be executed by a 32-bit process, so our
     # content handler must be built and launched as a 32-bit process as well.
-    mojo_native_application("nacl_content_handler_nonsfi_32_bit") {
+    mojo_native_application("content_handler_nonsfi_32_bit") {
+      output_name = "content_handler_nonsfi_nexe"
       sources = [
         "content_handler_main_nonsfi.cc",
       ]
@@ -41,42 +43,127 @@
         "//mojo/application:application",
         "//mojo/application:content_handler",
         "//mojo/data_pipe_utils",
-        "//mojo/public/platform/native:mgl_thunks",
-        "//mojo/public/platform/native:mgl_onscreen_thunks",
         "//mojo/message_pump",
         "//mojo/nacl/nonsfi:irt_mojo_nonsfi",
         "//mojo/public/cpp/application:application",
         "//native_client/src/nonsfi/loader:elf_loader",
       ]
     }
-
-    # Copy to the root build directory so that the '#!' prefix line for invoking
-    # our content handler can simply be "mojo:nacl_content_handler_nonsfi".
-    copy("nacl_content_handler_nonsfi_copy") {
+    mojo_native_application("content_handler_nonsfi_pexe_32_bit") {
+      output_name = "content_handler_nonsfi_pexe"
       sources = [
-        "${root_out_dir}/nacl_content_handler_nonsfi_32_bit.mojo",
+        "content_handler_main_nonsfi_pexe.cc",
+      ]
+
+      deps = [
+        ":pnacl_translator_bindings",
+        "//base",
+        "//mojo/application:application",
+        "//mojo/application:content_handler",
+        "//mojo/data_pipe_utils",
+        "//mojo/message_pump",
+        "//mojo/nacl/nonsfi:irt_mojo_nonsfi",
+        "//mojo/services/files/c:mojio",
+        "//native_client/src/nonsfi/loader:elf_loader",
+      ]
+    }
+    mojo_native_application("pnacl_compile_32_bit") {
+      output_name = "pnacl_compile"
+      sources = [
+        "pnacl_compile.cc",
+      ]
+
+      deps = [
+        ":pnacl_translator_bindings",
+        "//base",
+        "//mojo/nacl/nonsfi:irt_mojo_nonsfi",
+        "//mojo/public/cpp/application:standalone",
+        "//native_client/src/nonsfi/loader:elf_loader",
+      ]
+    }
+    mojo_native_application("pnacl_link_32_bit") {
+      output_name = "pnacl_link"
+      sources = [
+        "pnacl_link.cc",
+      ]
+
+      deps = [
+        ":pnacl_translator_bindings",
+        "//base",
+        "//mojo/nacl/nonsfi:irt_mojo_nonsfi",
+        "//mojo/public/cpp/application:standalone",
+        "//native_client/src/nonsfi/loader:elf_loader",
+      ]
+    }
+
+    # Copy all files necessary for pnacl translation into the output directory.
+    copy("pnacl_translation_files") {
+      file_location_prefix = "//native_client/toolchain/linux_x86/pnacl_translator/translator/x86-32-nonsfi"
+      sources = [
+        "${file_location_prefix}/bin/ld.nexe",
+        "${file_location_prefix}/bin/pnacl-llc.nexe",
+        "${file_location_prefix}/lib/crtbegin.o",
+        "${file_location_prefix}/lib/crtbegin_for_eh.o",
+        "${file_location_prefix}/lib/crtend.o",
+        "${file_location_prefix}/lib/libcrt_platform.a",
+        "${file_location_prefix}/lib/libgcc.a",
+        "${file_location_prefix}/lib/libpnacl_irt_shim_dummy.a",
       ]
       outputs = [
-        "${root_build_dir}/nacl_content_handler_nonsfi.mojo",
+        "${root_build_dir}/pnacl_translation_files/{{source_file_part}}",
       ]
-      deps = [
-        ":nacl_content_handler_nonsfi_32_bit",
-      ]
+    }
+
+    # Copy to the root build directory so that the '#!' prefix line for invoking
+    # our content handler can simply be "mojo:content_handler_nonsfi".
+    # Either way, build the same dependencies.
+    shared_deps = [
+      ":content_handler_nonsfi_32_bit",
+      ":content_handler_nonsfi_pexe_32_bit",
+      ":pnacl_compile_32_bit",
+      ":pnacl_link_32_bit",
+      ":pnacl_translation_files",
+    ]
+    if (root_out_dir != root_build_dir) {
+      copy("content_handler_nonsfi_copy") {
+        sources = [
+          "${root_out_dir}/content_handler_nonsfi_nexe.mojo",
+          "${root_out_dir}/content_handler_nonsfi_pexe.mojo",
+          "${root_out_dir}/pnacl_compile.mojo",
+          "${root_out_dir}/pnacl_link.mojo",
+        ]
+        outputs = [
+          "${root_build_dir}/{{source_file_part}}",
+        ]
+        deps = shared_deps
+      }
+    } else {
+      group("content_handler_nonsfi_copy") {
+        deps = shared_deps
+      }
     }
   }
 }
 
 # This group serves as a 64 to 32 bit transformation for Linux. If we are
 # using a 64 bit toolchain, build the nonsfi nacl content handler as 32 bit.
-group("nacl_content_handler_nonsfi") {
+group("content_handler_nonsfi") {
   deps = []
   if ((target_cpu == "x64" || target_cpu == "x86") && is_linux) {
     # The toolchain is hardcoded as 32-bit clang here -- although it must
     # be 32 bit (for nonsfi), it assumes clang. Ideally, the toolchain would
     # be defined as the 32 bit variant of whatever is being used (be it clang,
     # gcc, or something else).
-    deps += [ ":nacl_content_handler_nonsfi_copy(//build/toolchain/linux:clang_x86)" ]
+    deps +=
+        [ ":content_handler_nonsfi_copy(//build/toolchain/linux:clang_x86)" ]
   } else {
-    deps += [ ":nacl_content_handler_nonsfi_copy" ]
+    deps += [ ":content_handler_nonsfi_copy" ]
   }
 }
+
+mojom("pnacl_translator_bindings") {
+  sources = [
+    "pnacl_compile.mojom",
+    "pnacl_link.mojom",
+  ]
+}
diff --git a/services/nacl/content_handler_main_nonsfi.cc b/services/nacl/content_handler_main_nonsfi.cc
index 59b0dc7..5eee675 100644
--- a/services/nacl/content_handler_main_nonsfi.cc
+++ b/services/nacl/content_handler_main_nonsfi.cc
@@ -60,7 +60,7 @@
     MojoHandle handle =
         application_request.PassMessagePipe().release().value();
     // MojoLaunchNexeNonsfi takes ownership of the fd.
-    MojoLaunchNexeNonsfi(fd, handle);
+    MojoLaunchNexeNonsfi(fd, handle, false /* enable_translation_irt */);
   }
 
   mojo::ContentHandlerFactory content_handler_factory_;
diff --git a/services/nacl/content_handler_main_nonsfi_pexe.cc b/services/nacl/content_handler_main_nonsfi_pexe.cc
new file mode 100644
index 0000000..6a503d6
--- /dev/null
+++ b/services/nacl/content_handler_main_nonsfi_pexe.cc
@@ -0,0 +1,153 @@
+// Copyright 2015 The Chromium 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 <fcntl.h>
+
+#include "base/files/file_util.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/application/content_handler_factory.h"
+#include "mojo/data_pipe_utils/data_pipe_utils.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/nacl/nonsfi/nexe_launcher_nonsfi.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/files/c/lib/template_util.h"
+#include "services/nacl/pnacl_compile.mojom.h"
+#include "services/nacl/pnacl_link.mojom.h"
+
+namespace nacl {
+namespace content_handler {
+namespace {
+
+class CompilerUI {
+ public:
+  explicit CompilerUI(mojo::ScopedMessagePipeHandle handle) {
+    compiler_.Bind(mojo::InterfacePtrInfo<mojo::nacl::PexeCompiler>(
+        handle.Pass(), 0u));
+  }
+
+  // Synchronous method to compile pexe into object file.
+  mojo::String CompilePexe(mojo::String pexe_file_path) {
+    mojo::String output;
+    compiler_->PexeCompile(pexe_file_path, mojio::Capture(&output));
+    if (!compiler_.WaitForIncomingResponse())
+      LOG(FATAL) << "Waiting for pexe compiler failed";
+    return output;
+  }
+
+ private:
+  mojo::nacl::PexeCompilerPtr compiler_;
+};
+
+class LinkerUI {
+ public:
+  explicit LinkerUI(mojo::ScopedMessagePipeHandle handle) {
+    linker_.Bind(mojo::InterfacePtrInfo<mojo::nacl::PexeLinker>(
+        handle.Pass(), 0u));
+  }
+
+  // Synchronous method to link object file into nexe.
+  mojo::String LinkPexe(mojo::String object_file_path) {
+    mojo::String output;
+    linker_->PexeLink(object_file_path, mojio::Capture(&output));
+    if (!linker_.WaitForIncomingResponse())
+      LOG(FATAL) << "Waiting for pexe linker failed";
+    return output;
+  }
+
+ private:
+  mojo::nacl::PexeLinkerPtr linker_;
+};
+
+}  // namespace anonymous
+
+class PexeContentHandler : public mojo::ApplicationDelegate,
+                           public mojo::ContentHandlerFactory::Delegate {
+ public:
+  PexeContentHandler() : content_handler_factory_(this) {}
+
+ private:
+  // Overridden from ApplicationDelegate:
+  void Initialize(mojo::ApplicationImpl* app) override {
+    app->ConnectToService("mojo:pnacl_compile", &compiler_init_);
+    app->ConnectToService("mojo:pnacl_link", &linker_init_);
+  }
+
+  // Overridden from ApplicationDelegate:
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override {
+    connection->AddService(&content_handler_factory_);
+    return true;
+  }
+
+  // Overridden from ContentHandlerFactory::ManagedDelegate:
+  void RunApplication(
+      mojo::InterfaceRequest<mojo::Application> application_request,
+      mojo::URLResponsePtr response) override {
+    // Needed to use Mojo interfaces on this thread.
+    base::MessageLoop loop(mojo::common::MessagePumpMojo::Create());
+    // Create temporary file for pexe
+    base::FilePath pexe_file_path;
+    FILE* pexe_fp = CreateAndOpenTemporaryFile(&pexe_file_path);
+    if (!pexe_fp)
+      LOG(FATAL) << "Could not create temporary file for pexe";
+    // Acquire the pexe.
+    if (!mojo::common::BlockingCopyToFile(response->body.Pass(), pexe_fp))
+      LOG(FATAL) << "Could not copy pexe to file";
+    if (fclose(pexe_fp))
+      LOG(FATAL) << "Could not close pexe file";
+
+    // Compile the pexe into an object file
+    mojo::ScopedMessagePipeHandle parent_compile_pipe;
+    mojo::ScopedMessagePipeHandle child_compile_pipe;
+    if (CreateMessagePipe(nullptr, &parent_compile_pipe, &child_compile_pipe) !=
+        MOJO_RESULT_OK)
+      LOG(FATAL) << "Could not create message pipe to compiler";
+    compiler_init_->PexeCompilerStart(child_compile_pipe.Pass());
+
+    // Communicate with the compiler using a mojom interface.
+    CompilerUI compiler_ui(parent_compile_pipe.Pass());
+    mojo::String object_file = compiler_ui.CompilePexe(pexe_file_path.value());
+
+    // Link the object file into a nexe
+    mojo::ScopedMessagePipeHandle parent_link_pipe;
+    mojo::ScopedMessagePipeHandle child_link_pipe;
+    if (CreateMessagePipe(nullptr, &parent_link_pipe, &child_link_pipe) !=
+        MOJO_RESULT_OK)
+      LOG(FATAL) << "Could not create message pipe to linker";
+    linker_init_->PexeLinkerStart(child_link_pipe.Pass());
+
+    // Communicate with the linker using a mojom interface.
+    LinkerUI linker_ui(parent_link_pipe.Pass());
+    mojo::String nexe_file = linker_ui.LinkPexe(object_file);
+
+    // Open the nexe file and launch it (with our mojo handle)
+    int nexe_fd = open(nexe_file.get().c_str(), O_RDONLY);
+    if (unlink(nexe_file.get().c_str()))
+      LOG(FATAL) << "Could not unlink temporary nexe file";
+    if (nexe_fd < 0)
+      LOG(FATAL) << "Could not open nexe object file";
+
+    // Pass the handle connecting us with mojo_shell to the nexe.
+    MojoHandle handle = application_request.PassMessagePipe().release().value();
+    ::nacl::MojoLaunchNexeNonsfi(nexe_fd, handle,
+                                 false /* enable_translation_irt */);
+  }
+
+ private:
+  mojo::ContentHandlerFactory content_handler_factory_;
+  mojo::nacl::PexeCompilerInitPtr compiler_init_;
+  mojo::nacl::PexeLinkerInitPtr linker_init_;
+
+  DISALLOW_COPY_AND_ASSIGN(PexeContentHandler);
+};
+
+}  // namespace content_handler
+}  // namespace nacl
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(
+      new nacl::content_handler::PexeContentHandler());
+  return runner.Run(application_request);
+}
diff --git a/services/nacl/pnacl_compile.cc b/services/nacl/pnacl_compile.cc
new file mode 100644
index 0000000..b661bca
--- /dev/null
+++ b/services/nacl/pnacl_compile.cc
@@ -0,0 +1,73 @@
+// Copyright 2015 The Chromium 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 <fcntl.h>
+
+#include "base/at_exit.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "mojo/nacl/nonsfi/nexe_launcher_nonsfi.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/nacl/pnacl_compile.mojom.h"
+
+namespace mojo {
+namespace nacl {
+
+class PexeCompilerImpl : public PexeCompilerInit {
+ public:
+  void PexeCompilerStart(ScopedMessagePipeHandle handle) override {
+    base::FilePath path;
+    if (!PathService::Get(base::DIR_MODULE, &path))
+      LOG(FATAL) << "Could not find mojo root directory";
+    path = path.Append("pnacl_translation_files/pnacl-llc.nexe");
+    int nexe_fd = open(path.value().c_str(), O_RDONLY);
+    if (nexe_fd < 0)
+      LOG(FATAL) << "Could not open compiler nexe: " << path.value();
+    ::nacl::MojoLaunchNexeNonsfi(nexe_fd,
+                                 handle.release().value(),
+                                 true /* enable_translate_irt */);
+  }
+};
+
+class StrongBindingPexeCompilerImpl : public PexeCompilerImpl {
+ public:
+  explicit StrongBindingPexeCompilerImpl(InterfaceRequest<PexeCompilerInit>
+                                         request)
+      : strong_binding_(this, request.Pass()) {}
+
+ private:
+  StrongBinding<PexeCompilerInit> strong_binding_;
+};
+
+class MultiPexeCompiler : public ApplicationDelegate,
+                          public InterfaceFactory<PexeCompilerInit> {
+ public:
+  MultiPexeCompiler() {}
+
+  // From ApplicationDelegate
+  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+    connection->AddService<PexeCompilerInit>(this);
+    return true;
+  }
+
+  // From InterfaceFactory
+  void Create(ApplicationConnection* connection,
+              InterfaceRequest<PexeCompilerInit> request) override {
+    new StrongBindingPexeCompilerImpl(request.Pass());
+  }
+};
+
+}  // namespace nacl
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle application_request) {
+  base::AtExitManager at_exit;
+  mojo::ApplicationRunner runner(new mojo::nacl::MultiPexeCompiler());
+  return runner.Run(application_request);
+}
diff --git a/services/nacl/pnacl_compile.mojom b/services/nacl/pnacl_compile.mojom
new file mode 100644
index 0000000..64995c8
--- /dev/null
+++ b/services/nacl/pnacl_compile.mojom
@@ -0,0 +1,13 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.nacl;
+
+interface PexeCompilerInit {
+  PexeCompilerStart(handle<message_pipe> message_handle);
+};
+
+interface PexeCompiler {
+  PexeCompile(string pexe_file_name) => (string object_file_name);
+};
diff --git a/services/nacl/pnacl_link.cc b/services/nacl/pnacl_link.cc
new file mode 100644
index 0000000..61ffd17
--- /dev/null
+++ b/services/nacl/pnacl_link.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium 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 <fcntl.h>
+
+#include "base/at_exit.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "mojo/nacl/nonsfi/nexe_launcher_nonsfi.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_runner.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/nacl/pnacl_link.mojom.h"
+
+namespace mojo {
+namespace nacl {
+
+class PexeLinkerImpl : public PexeLinkerInit {
+ public:
+  void PexeLinkerStart(ScopedMessagePipeHandle handle) override {
+    base::FilePath path;
+    if (!PathService::Get(base::DIR_MODULE, &path))
+      LOG(FATAL) << "Could not find mojo root directory";
+    path = path.Append("pnacl_translation_files/ld.nexe");
+    int nexe_fd = open(path.value().c_str(), O_RDONLY);
+    if (nexe_fd < 0)
+      LOG(FATAL) << "Could not open linker nexe: " << path.value();
+    ::nacl::MojoLaunchNexeNonsfi(nexe_fd,
+                                 handle.release().value(),
+                                 true /* enable_translate_irt */);
+  }
+};
+
+class StrongBindingPexeLinkerImpl : public PexeLinkerImpl {
+ public:
+  explicit StrongBindingPexeLinkerImpl(InterfaceRequest<PexeLinkerInit> request)
+      : strong_binding_(this, request.Pass()) {}
+
+ private:
+  StrongBinding<PexeLinkerInit> strong_binding_;
+};
+
+class MultiPexeLinker : public ApplicationDelegate,
+                        public InterfaceFactory<PexeLinkerInit> {
+ public:
+  MultiPexeLinker() {}
+
+  // From ApplicationDelegate
+  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+    connection->AddService<PexeLinkerInit>(this);
+    return true;
+  }
+
+  // From InterfaceFactory
+  void Create(ApplicationConnection* connection,
+              InterfaceRequest<PexeLinkerInit> request) override {
+    new StrongBindingPexeLinkerImpl(request.Pass());
+  }
+};
+
+}  // namespace nacl
+}  // namespace mojo
+
+MojoResult MojoMain(MojoHandle application_request) {
+  base::AtExitManager at_exit;
+  mojo::ApplicationRunner runner(new mojo::nacl::MultiPexeLinker());
+  return runner.Run(application_request);
+}
diff --git a/services/nacl/pnacl_link.mojom b/services/nacl/pnacl_link.mojom
new file mode 100644
index 0000000..0881493
--- /dev/null
+++ b/services/nacl/pnacl_link.mojom
@@ -0,0 +1,13 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo.nacl;
+
+interface PexeLinkerInit {
+  PexeLinkerStart(handle<message_pipe> message_handle);
+};
+
+interface PexeLinker {
+  PexeLink(string object_file_name) => (string nexe_file_name);
+};