Enabling 64-bit mojo shell to launch 32-bit child to handle nonsfi content.

This patch allows the nonsfi content handler to be used from the mojo
shell in two ways. First, it can be built for a purely 32-bit system
(--target-cpu=x86), and run without "--enable-multiprocess". Alternatively,
the nonsfi content handler can be run from a 64-bit version of the mojo
shell, as long as "--enable-multiprocess" is used. This will fork/exec a
32-bit child whenever the nonsfi content handler is detected.

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

Review URL: https://codereview.chromium.org/1341873002 .
diff --git a/services/nacl/BUILD.gn b/services/nacl/BUILD.gn
index 37f2515..b058913 100644
--- a/services/nacl/BUILD.gn
+++ b/services/nacl/BUILD.gn
@@ -26,20 +26,51 @@
     data_deps = [ "//nacl_bindings:irt_mojo(//native_client/build/toolchain/nacl:irt_${target_cpu})" ]
   }
 
-  mojo_native_application("nacl_content_handler_nonsfi") {
-    sources = [
-      "content_handler_main_nonsfi.cc",
-    ]
+  if (current_cpu == "x86") {
+    # 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") {
+      sources = [
+        "content_handler_main_nonsfi.cc",
+      ]
 
-    deps = [
-      "//base",
-      "//mojo/application:application",
-      "//mojo/application:content_handler",
-      "//mojo/data_pipe_utils",
-      "//mojo/message_pump",
-      "//mojo/nacl:irt_mojo_nonsfi",
-      "//mojo/public/cpp/application:application",
-      "//native_client/src/nonsfi/loader:elf_loader",
-    ]
+      deps = [
+        "//base",
+        "//mojo/application:application",
+        "//mojo/application:content_handler",
+        "//mojo/data_pipe_utils",
+        "//mojo/message_pump",
+        "//mojo/nacl: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") {
+      sources = [
+        "${root_out_dir}/nacl_content_handler_nonsfi_32_bit.mojo",
+      ]
+      outputs = [
+        "${root_build_dir}/nacl_content_handler_nonsfi.mojo",
+      ]
+      deps = [
+        ":nacl_content_handler_nonsfi_32_bit",
+      ]
+    }
+  }
+}
+
+# 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") {
+  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)" ]
   }
 }
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index 56c6596..42473af 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -83,7 +83,7 @@
         "desktop/main.cc",
       ]
 
-      deps = shell_common_deps
+      deps = shell_common_deps + [ ":mojo_shell_child_32_bit" ]
 
       data_deps = shell_common_data_deps
     }
@@ -105,6 +105,37 @@
       "//mojo/message_pump",
     ]
   }
+
+  if (current_cpu == "x86") {
+    # The 32-bit version of the mojo_shell_child should be accessible in the
+    # root build directory, just like the regular mojo_shell_child, so that
+    # the 32-bit version can be accessed by simply adding a suffix to the path.
+    copy("mojo_shell_child_32_bit_copy") {
+      sources = [
+        "${root_out_dir}/mojo_shell_child",
+      ]
+      outputs = [
+        "${root_build_dir}/mojo_shell_child_32",
+      ]
+      deps = [
+        ":mojo_shell_child",
+      ]
+    }
+  }
+
+  group("mojo_shell_child_32_bit") {
+    # This group is required to run any 32-bit child process.
+    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 = [
+        ":mojo_shell_child_32_bit_copy(//build/toolchain/linux:clang_x86)",
+      ]
+    }
+  }
 }  # !mojo_use_prebuilt_mojo_shell
 
 # Files used both by mojo_shell and mojo_shell_child (and tests).
diff --git a/shell/child_process_host.cc b/shell/child_process_host.cc
index b0bc6ad..8e3908f 100644
--- a/shell/child_process_host.cc
+++ b/shell/child_process_host.cc
@@ -42,12 +42,16 @@
   DCHECK(!child_process_.IsValid());
 }
 
-void ChildProcessHost::Start() {
+void ChildProcessHost::Start(bool require_32_bit) {
   DCHECK(!child_process_.IsValid());
 
   scoped_ptr<LaunchData> launch_data(new LaunchData());
   launch_data->child_path = context_->mojo_shell_child_path();
-
+#if defined(ARCH_CPU_64_BITS)
+  if (require_32_bit)
+    launch_data->child_path =
+        context_->mojo_shell_child_path().InsertBeforeExtensionASCII("_32");
+#endif
   // TODO(vtl): Add something for |slave_info|.
   // TODO(vtl): The "unretained this" is wrong (see also below).
   mojo::ScopedMessagePipeHandle handle(mojo::embedder::ConnectToSlave(
diff --git a/shell/child_process_host.h b/shell/child_process_host.h
index 7ae2d9b..3a1928b 100644
--- a/shell/child_process_host.h
+++ b/shell/child_process_host.h
@@ -40,7 +40,7 @@
   // TODO(vtl): Consider using weak pointers and removing this requirement.
   // TODO(vtl): This should probably take a callback instead.
   // TODO(vtl): Consider merging this with |StartApp()|.
-  void Start();
+  void Start(bool require_32_bit);
 
   // Waits for the child process to terminate, and returns its exit code.
   // Note: If |Start()| has been called, this must not be called until the
diff --git a/shell/child_process_host_unittest.cc b/shell/child_process_host_unittest.cc
index d4f22cc..6591760 100644
--- a/shell/child_process_host_unittest.cc
+++ b/shell/child_process_host_unittest.cc
@@ -49,7 +49,7 @@
       scoped_ptr<base::MessagePump>(new mojo::common::MessagePumpMojo()));
   context.Init();
   TestChildProcessHost child_process_host(&context);
-  child_process_host.Start();
+  child_process_host.Start(false /* require_32_bit */);
   message_loop.Run();  // This should run until |DidStart()|.
   child_process_host.ExitNow(123);
   int exit_code = child_process_host.Join();
@@ -73,7 +73,7 @@
       scoped_ptr<base::MessagePump>(new mojo::common::MessagePumpMojo()));
   context.Init();
   TestChildProcessHost child_process_host(&context);
-  child_process_host.Start();
+  child_process_host.Start(false /* require_32_bit */);
   message_loop.Run();  // This should run until |DidStart()|.
   // Send |ExitNow()| first, so that the |StartApp()| below won't actually be
   // processed, and we'll just get a connection error.
diff --git a/shell/out_of_process_native_runner.cc b/shell/out_of_process_native_runner.cc
index 39cf1f7..9ac1dbd 100644
--- a/shell/out_of_process_native_runner.cc
+++ b/shell/out_of_process_native_runner.cc
@@ -4,14 +4,45 @@
 
 #include "shell/out_of_process_native_runner.h"
 
+#include <elf.h>
+#include <string>
+
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
+#include "base/strings/string_util.h"
 #include "shell/child_controller.mojom.h"
 #include "shell/child_process_host.h"
 #include "shell/in_process_native_runner.h"
 
+namespace {
+
+// Determines if content handler must be run as 32 bit application
+// based on elf header. Returns false on error.
+bool Require32Bit(const base::FilePath& app_path) {
+  if (sizeof(void*) == 4) {
+    // CPU arch is already 32 bits
+    return true;
+  } else {
+    char data[EI_NIDENT];
+    // Read e_ident from the elf file
+    if (sizeof(data) != ReadFile(app_path, data, sizeof(data))) {
+      DCHECK(false);
+      return false;
+    }
+    // Check the magic elf number
+    if (memcmp(data, ELFMAG, SELFMAG)) {
+      DCHECK(false);
+      return false;
+    }
+    // Identify the architecture required
+    return data[EI_CLASS] == ELFCLASS32;
+  }
+}
+
+}  // namespace
+
 namespace shell {
 
 OutOfProcessNativeRunner::OutOfProcessNativeRunner(Context* context)
@@ -37,7 +68,7 @@
   app_completed_callback_ = app_completed_callback;
 
   child_process_host_.reset(new ChildProcessHost(context_));
-  child_process_host_->Start();
+  child_process_host_->Start(Require32Bit(app_path));
 
   // TODO(vtl): |app_path.AsUTF8Unsafe()| is unsafe.
   child_process_host_->StartApp(