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(