Index: tools/pnacl-llc/srpc_main.cpp |
diff --git a/tools/pnacl-llc/srpc_main.cpp b/tools/pnacl-llc/srpc_main.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..40d3242b9c08e45bf7e160c66281974a1deb327b |
--- /dev/null |
+++ b/tools/pnacl-llc/srpc_main.cpp |
@@ -0,0 +1,361 @@ |
+//===-- srpc_main.cpp - PNaCl sandboxed translator invocation -------------===// |
+// |
+// The LLVM Compiler Infrastructure |
+// |
+// This file is distributed under the University of Illinois Open Source |
+// License. See LICENSE.TXT for details. |
+// |
+//===----------------------------------------------------------------------===// |
+// |
+// Main invocation of the sandboxed translator through SRPC. |
+// |
+//===----------------------------------------------------------------------===// |
+ |
+#if defined(PNACL_BROWSER_TRANSLATOR) |
+ |
+// Headers which are not properly part of the SDK are included by their |
+// path in the NaCl tree. |
+#include "native_client/src/shared/srpc/nacl_srpc.h" |
+#ifdef __pnacl__ |
+#include "native_client/src/untrusted/nacl/pnacl.h" |
+#endif // __pnacl__ |
+ |
+#include "SRPCStreamer.h" |
+ |
+#include "llvm/ADT/STLExtras.h" |
+#include "llvm/Option/Option.h" |
+#include "llvm/Support/ErrorHandling.h" |
+ |
+#include <argz.h> |
+#include <cstdio> |
+#include <cstdlib> |
+#include <cstring> |
+#include <string> |
+ |
+using namespace llvm; |
+using namespace llvm::opt; |
+using std::string; |
+ |
+// Imported from pnacl-llc.cpp |
+extern int llc_main(int argc, char **argv); |
+ |
+namespace { |
+ |
+// The filename used internally for looking up the bitcode file. |
+const char kBitcodeFilename[] = "pnacl.pexe"; |
+// The filename used internally for looking up the object code file. |
+const char kObjectFilename[] = "pnacl.o"; |
+// Maximum number of modules supported for splitting. Can't be changed without |
+// also changing the SRPC signature for StreamInitWithSplit |
+const int kMaxModuleSplit = 16; |
+// Object which manages streaming bitcode over SRPC and threading. |
+SRPCStreamer *srpc_streamer; |
+// FDs of the object file(s). |
+int object_file_fd[kMaxModuleSplit]; |
+ |
+DataStreamer *NaClBitcodeStreamer; |
+ |
+int DoTranslate(ArgStringList *CmdLineArgs) { |
+ if (CmdLineArgs == NULL) { |
+ return 1; |
+ } |
+ // Make an argv array from the input vector. |
+ size_t argc = CmdLineArgs->size(); |
+ char **argv = new char *[argc + 1]; |
+ for (size_t i = 0; i < argc; ++i) { |
+ // llc_main will not mutate the command line, so this is safe. |
+ argv[i] = const_cast<char *>((*CmdLineArgs)[i]); |
+ } |
+ argv[argc] = NULL; |
+ // Call main. |
+ return llc_main(static_cast<int>(argc), argv); |
+} |
+ |
+ArgStringList *CommandLineFromArgz(char *str, size_t str_len) { |
+ char *entry = str; |
+ ArgStringList *CmdLineArgs = new ArgStringList; |
+ while (entry != NULL && str_len) { |
+ // Call strdup(entry) since the str argument will ultimately be |
+ // freed by the SRPC message sender. |
+ CmdLineArgs->push_back(strdup(entry)); |
+ entry = argz_next(str, str_len, entry); |
+ } |
+ return CmdLineArgs; |
+} |
+ |
+void AddFixedArguments(ArgStringList *CmdLineArgs) { |
+ // Add fixed arguments to the command line. These specify the bitcode |
+ // and object code filenames, removing them from the contract with the |
+ // coordinator. |
+ CmdLineArgs->push_back(kBitcodeFilename); |
+ CmdLineArgs->push_back("-o"); |
+ CmdLineArgs->push_back(kObjectFilename); |
+} |
+ |
+bool AddDefaultCPU(ArgStringList *CmdLineArgs) { |
+#if defined(__pnacl__) |
+ switch (__builtin_nacl_target_arch()) { |
+ case PnaclTargetArchitectureX86_32: { |
+ CmdLineArgs->push_back("-mcpu=pentium4m"); |
+ break; |
+ } |
+ case PnaclTargetArchitectureX86_64: { |
+ CmdLineArgs->push_back("-mcpu=x86-64"); |
+ break; |
+ } |
+ case PnaclTargetArchitectureARM_32: { |
+ CmdLineArgs->push_back("-mcpu=cortex-a9"); |
+ break; |
+ } |
+ default: |
+ fprintf(stderr, "no target architecture match.\n"); |
+ return false; |
+ } |
+// Some cases for building this with nacl-gcc. |
+#elif defined(__i386__) |
+ CmdLineArgs->push_back("-mcpu=pentium4m"); |
+#elif defined(__x86_64__) |
+ CmdLineArgs->push_back("-mcpu=x86-64"); |
+#elif defined(__arm__) |
+ CmdLineArgs->push_back("-mcpu=cortex-a9"); |
+#else |
+#error "Unknown architecture" |
+#endif |
+ return true; |
+} |
+ |
+bool HasCPUOverride(ArgStringList *CmdLineArgs) { |
+ const char *Mcpu = "-mcpu"; |
+ size_t McpuLen = strlen(Mcpu); |
+ for (size_t i = 0; i < CmdLineArgs->size(); ++i) { |
+ if (strncmp((*CmdLineArgs)[i], Mcpu, McpuLen) == 0) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+ArgStringList *GetDefaultCommandLine() { |
+ ArgStringList *command_line = new ArgStringList; |
+ // First, those common to all architectures. |
+ static const char *common_args[] = { "pnacl_translator", "-filetype=obj" }; |
+ for (size_t i = 0; i < array_lengthof(common_args); ++i) { |
+ command_line->push_back(common_args[i]); |
+ } |
+ // Then those particular to a platform. |
+ static const char *llc_args_x8632[] = { "-mtriple=i686-none-nacl-gnu", NULL }; |
+ static const char *llc_args_x8664[] = { "-mtriple=x86_64-none-nacl-gnu", |
+ NULL }; |
+ static const char *llc_args_arm[] = { |
+ "-mtriple=armv7a-none-nacl-gnueabi", "-mattr=+neon", |
+ "-float-abi=hard", NULL |
+ }; |
+ |
+ const char **llc_args = NULL; |
+#if defined(__pnacl__) |
+ switch (__builtin_nacl_target_arch()) { |
+ case PnaclTargetArchitectureX86_32: { |
+ llc_args = llc_args_x8632; |
+ break; |
+ } |
+ case PnaclTargetArchitectureX86_64: { |
+ llc_args = llc_args_x8664; |
+ break; |
+ } |
+ case PnaclTargetArchitectureARM_32: { |
+ llc_args = llc_args_arm; |
+ break; |
+ } |
+ default: |
+ fprintf(stderr, "no target architecture match.\n"); |
+ delete command_line; |
+ return NULL; |
+ } |
+// Some cases for building this with nacl-gcc. |
+#elif defined(__i386__) |
+ (void)llc_args_x8664; |
+ (void)llc_args_arm; |
+ llc_args = llc_args_x8632; |
+#elif defined(__x86_64__) |
+ (void)llc_args_x8632; |
+ (void)llc_args_arm; |
+ llc_args = llc_args_x8664; |
+#elif defined(__arm__) |
+ (void)llc_args_x8632; |
+ (void)llc_args_x8664; |
+ llc_args = llc_args_arm; |
+#else |
+#error "Unknown architecture" |
+#endif |
+ for (size_t i = 0; llc_args[i] != NULL; i++) |
+ command_line->push_back(llc_args[i]); |
+ return command_line; |
+} |
+ |
+// Data passed from main thread to compile thread. |
+// Takes ownership of the commandline vector. |
+class StreamingThreadData { |
+public: |
+ StreamingThreadData(int module_count, ArgStringList *cmd_line_vec) |
+ : module_count_(module_count), cmd_line_vec_(cmd_line_vec) {} |
+ ArgStringList *CmdLineVec() const { return cmd_line_vec_.get(); } |
+ int module_count_; |
+ const std::unique_ptr<ArgStringList> cmd_line_vec_; |
+}; |
+ |
+void *run_streamed(void *arg) { |
+ StreamingThreadData *data = reinterpret_cast<StreamingThreadData *>(arg); |
+ data->CmdLineVec()->push_back("-streaming-bitcode"); |
+ if (DoTranslate(data->CmdLineVec()) != 0) { |
+ // llc_main only returns 1 (as opposed to calling report_fatal_error) |
+ // in conditions we never expect to see in the browser (e.g. bad |
+ // command-line flags). |
+ srpc_streamer->setFatalError("llc_main unspecified failure"); |
+ return NULL; |
+ } |
+ delete data; |
+ return NULL; |
+} |
+ |
+// Actually do the work for stream initialization. |
+void do_stream_init(NaClSrpcRpc *rpc, NaClSrpcArg **out_args, |
+ NaClSrpcClosure *done, StreamingThreadData* thread_data) { |
+ NaClSrpcClosureRunner runner(done); |
+ rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
+ srpc_streamer = new SRPCStreamer(); |
+ std::string StrError; |
+ |
+ NaClBitcodeStreamer = srpc_streamer->init( |
+ run_streamed, reinterpret_cast<void *>(thread_data), &StrError); |
+ if (NaClBitcodeStreamer) { |
+ rpc->result = NACL_SRPC_RESULT_OK; |
+ out_args[0]->arrays.str = strdup("no error"); |
+ } else { |
+ out_args[0]->arrays.str = strdup(StrError.c_str()); |
+ } |
+} |
+ |
+void stream_init_with_split(NaClSrpcRpc *rpc, NaClSrpcArg **in_args, |
+ NaClSrpcArg **out_args, NaClSrpcClosure *done) { |
+ ArgStringList *cmd_line_vec = GetDefaultCommandLine(); |
+ if (!cmd_line_vec) { |
+ NaClSrpcClosureRunner runner(done); |
+ rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
+ out_args[0]->arrays.str = strdup("Failed to get default commandline."); |
+ return; |
+ } |
+ AddFixedArguments(cmd_line_vec); |
+ |
+ int num_modules = in_args[0]->u.ival; |
+ if (num_modules < 1 || num_modules > kMaxModuleSplit) { |
+ NaClSrpcClosureRunner runner(done); |
+ rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
+ out_args[0]->arrays.str = strdup("Invalid module split count."); |
+ return; |
+ } |
+ |
+ StreamingThreadData *thread_data = |
+ new StreamingThreadData(num_modules, cmd_line_vec); |
+ |
+ for (int i = 1; i <= num_modules; i++) { |
+ object_file_fd[i - 1] = in_args[i]->u.hval; |
+ } |
+ |
+ char *command_line = in_args[kMaxModuleSplit + 1]->arrays.carr; |
+ size_t command_line_len = in_args[kMaxModuleSplit + 1]->u.count; |
+ std::unique_ptr<ArgStringList> extra_vec( |
+ CommandLineFromArgz(command_line, command_line_len)); |
+ cmd_line_vec->insert(cmd_line_vec->end(), extra_vec->begin(), |
+ extra_vec->end()); |
+ // Make sure some -mcpu override exists for now to prevent |
+ // auto-cpu feature detection from triggering instructions that |
+ // we do not validate yet. |
+ if (!HasCPUOverride(extra_vec.get())) { |
+ AddDefaultCPU(cmd_line_vec); |
+ } |
+ extra_vec.reset(NULL); |
+ |
+ // cmd_line_vec is freed by the translation thread in run_streamed. |
+ do_stream_init(rpc, out_args, done, thread_data); |
+} |
+ |
+// Invoked by the StreamChunk RPC. Receives a chunk of the bitcode and |
+// buffers it for later retrieval by the compilation thread. |
+void stream_chunk(NaClSrpcRpc *rpc, NaClSrpcArg **in_args, |
+ NaClSrpcArg **out_args, NaClSrpcClosure *done) { |
+ NaClSrpcClosureRunner runner(done); |
+ rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
+ size_t len = in_args[0]->u.count; |
+ unsigned char *bytes = |
+ reinterpret_cast<unsigned char *>(in_args[0]->arrays.carr); |
+ if (srpc_streamer->gotChunk(bytes, len) != len) { |
+ return; |
+ } |
+ rpc->result = NACL_SRPC_RESULT_OK; |
+} |
+ |
+// Invoked by the StreamEnd RPC. Waits until the compilation finishes, |
+// then returns. Returns an int indicating whether the bitcode is a |
+// shared library, a string with the soname, a string with dependencies, |
+// and a string which contains an error message if applicable. |
+void stream_end(NaClSrpcRpc *rpc, NaClSrpcArg **in_args, NaClSrpcArg **out_args, |
+ NaClSrpcClosure *done) { |
+ NaClSrpcClosureRunner runner(done); |
+ // TODO(eliben): We don't really use shared libraries now. At some |
+ // point this should be cleaned up from SRPC as well. |
+ out_args[0]->u.ival = false; |
+ out_args[1]->arrays.str = strdup(""); |
+ out_args[2]->arrays.str = strdup(""); |
+ rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
+ std::string StrError; |
+ if (srpc_streamer->streamEnd(&StrError)) { |
+ out_args[3]->arrays.str = strdup(StrError.c_str()); |
+ return; |
+ } |
+ // SRPC deletes the strings returned when the closure is invoked. |
+ // Therefore we need to use strdup. |
+ out_args[3]->arrays.str = strdup(""); |
+ rpc->result = NACL_SRPC_RESULT_OK; |
+} |
+ |
+const struct NaClSrpcHandlerDesc srpc_methods[] = { |
+ // Protocol for streaming: |
+ // StreamInitWithSplit(num_split, obj_fd x 16, cmdline_flags) -> error_str |
+ // StreamChunk(data) + |
+ // StreamEnd() -> (is_shared_lib,soname,dependencies,error_str) |
+ { "StreamInitWithSplit:ihhhhhhhhhhhhhhhhC:s", stream_init_with_split }, |
+ { "StreamChunk:C:", stream_chunk }, |
+ { "StreamEnd::isss", stream_end }, |
+ { NULL, NULL }, |
+}; |
+ |
+} // namespace |
+ |
+int getObjectFileFD(unsigned Index) { |
+ assert(Index < kMaxModuleSplit); |
+ return object_file_fd[Index]; |
+} |
+ |
+DataStreamer *getNaClBitcodeStreamer() { return NaClBitcodeStreamer; } |
+ |
+// Called from the compilation thread |
+void FatalErrorHandler(void *user_data, const std::string& reason, |
+ bool gen_crash_diag) { |
+ srpc_streamer->setFatalError(reason); |
+} |
+ |
+fatal_error_handler_t getSRPCErrorHandler() { return FatalErrorHandler; } |
+ |
+int srpc_main(int argc, char **argv) { |
+ if (!NaClSrpcModuleInit()) { |
+ return 1; |
+ } |
+ |
+ if (!NaClSrpcAcceptClientConnection(srpc_methods)) { |
+ return 1; |
+ } |
+ NaClSrpcModuleFini(); |
+ return 0; |
+} |
+ |
+#endif // __native_client__ |