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(