OLD | NEW |
(Empty) | |
| 1 //===-- srpc_main.cpp - PNaCl sandboxed translator invocation -------------===// |
| 2 // |
| 3 // The LLVM Compiler Infrastructure |
| 4 // |
| 5 // This file is distributed under the University of Illinois Open Source |
| 6 // License. See LICENSE.TXT for details. |
| 7 // |
| 8 //===----------------------------------------------------------------------===// |
| 9 // |
| 10 // Main invocation of the sandboxed translator through SRPC. |
| 11 // |
| 12 //===----------------------------------------------------------------------===// |
| 13 |
| 14 #if defined(PNACL_BROWSER_TRANSLATOR) |
| 15 |
| 16 // Headers which are not properly part of the SDK are included by their |
| 17 // path in the NaCl tree. |
| 18 #include "native_client/src/shared/srpc/nacl_srpc.h" |
| 19 #ifdef __pnacl__ |
| 20 #include "native_client/src/untrusted/nacl/pnacl.h" |
| 21 #endif // __pnacl__ |
| 22 |
| 23 #include "SRPCStreamer.h" |
| 24 |
| 25 #include "llvm/ADT/STLExtras.h" |
| 26 #include "llvm/Option/Option.h" |
| 27 #include "llvm/Support/ErrorHandling.h" |
| 28 |
| 29 #include <argz.h> |
| 30 #include <cstdio> |
| 31 #include <cstdlib> |
| 32 #include <cstring> |
| 33 #include <string> |
| 34 |
| 35 using namespace llvm; |
| 36 using namespace llvm::opt; |
| 37 using std::string; |
| 38 |
| 39 // Imported from pnacl-llc.cpp |
| 40 extern int llc_main(int argc, char **argv); |
| 41 |
| 42 namespace { |
| 43 |
| 44 // The filename used internally for looking up the bitcode file. |
| 45 const char kBitcodeFilename[] = "pnacl.pexe"; |
| 46 // The filename used internally for looking up the object code file. |
| 47 const char kObjectFilename[] = "pnacl.o"; |
| 48 // Maximum number of modules supported for splitting. Can't be changed without |
| 49 // also changing the SRPC signature for StreamInitWithSplit |
| 50 const int kMaxModuleSplit = 16; |
| 51 // Object which manages streaming bitcode over SRPC and threading. |
| 52 SRPCStreamer *srpc_streamer; |
| 53 // FDs of the object file(s). |
| 54 int object_file_fd[kMaxModuleSplit]; |
| 55 |
| 56 DataStreamer *NaClBitcodeStreamer; |
| 57 |
| 58 int DoTranslate(ArgStringList *CmdLineArgs) { |
| 59 if (CmdLineArgs == NULL) { |
| 60 return 1; |
| 61 } |
| 62 // Make an argv array from the input vector. |
| 63 size_t argc = CmdLineArgs->size(); |
| 64 char **argv = new char *[argc + 1]; |
| 65 for (size_t i = 0; i < argc; ++i) { |
| 66 // llc_main will not mutate the command line, so this is safe. |
| 67 argv[i] = const_cast<char *>((*CmdLineArgs)[i]); |
| 68 } |
| 69 argv[argc] = NULL; |
| 70 // Call main. |
| 71 return llc_main(static_cast<int>(argc), argv); |
| 72 } |
| 73 |
| 74 ArgStringList *CommandLineFromArgz(char *str, size_t str_len) { |
| 75 char *entry = str; |
| 76 ArgStringList *CmdLineArgs = new ArgStringList; |
| 77 while (entry != NULL && str_len) { |
| 78 // Call strdup(entry) since the str argument will ultimately be |
| 79 // freed by the SRPC message sender. |
| 80 CmdLineArgs->push_back(strdup(entry)); |
| 81 entry = argz_next(str, str_len, entry); |
| 82 } |
| 83 return CmdLineArgs; |
| 84 } |
| 85 |
| 86 void AddFixedArguments(ArgStringList *CmdLineArgs) { |
| 87 // Add fixed arguments to the command line. These specify the bitcode |
| 88 // and object code filenames, removing them from the contract with the |
| 89 // coordinator. |
| 90 CmdLineArgs->push_back(kBitcodeFilename); |
| 91 CmdLineArgs->push_back("-o"); |
| 92 CmdLineArgs->push_back(kObjectFilename); |
| 93 } |
| 94 |
| 95 bool AddDefaultCPU(ArgStringList *CmdLineArgs) { |
| 96 #if defined(__pnacl__) |
| 97 switch (__builtin_nacl_target_arch()) { |
| 98 case PnaclTargetArchitectureX86_32: { |
| 99 CmdLineArgs->push_back("-mcpu=pentium4m"); |
| 100 break; |
| 101 } |
| 102 case PnaclTargetArchitectureX86_64: { |
| 103 CmdLineArgs->push_back("-mcpu=x86-64"); |
| 104 break; |
| 105 } |
| 106 case PnaclTargetArchitectureARM_32: { |
| 107 CmdLineArgs->push_back("-mcpu=cortex-a9"); |
| 108 break; |
| 109 } |
| 110 default: |
| 111 fprintf(stderr, "no target architecture match.\n"); |
| 112 return false; |
| 113 } |
| 114 // Some cases for building this with nacl-gcc. |
| 115 #elif defined(__i386__) |
| 116 CmdLineArgs->push_back("-mcpu=pentium4m"); |
| 117 #elif defined(__x86_64__) |
| 118 CmdLineArgs->push_back("-mcpu=x86-64"); |
| 119 #elif defined(__arm__) |
| 120 CmdLineArgs->push_back("-mcpu=cortex-a9"); |
| 121 #else |
| 122 #error "Unknown architecture" |
| 123 #endif |
| 124 return true; |
| 125 } |
| 126 |
| 127 bool HasCPUOverride(ArgStringList *CmdLineArgs) { |
| 128 const char *Mcpu = "-mcpu"; |
| 129 size_t McpuLen = strlen(Mcpu); |
| 130 for (size_t i = 0; i < CmdLineArgs->size(); ++i) { |
| 131 if (strncmp((*CmdLineArgs)[i], Mcpu, McpuLen) == 0) { |
| 132 return true; |
| 133 } |
| 134 } |
| 135 return false; |
| 136 } |
| 137 |
| 138 ArgStringList *GetDefaultCommandLine() { |
| 139 ArgStringList *command_line = new ArgStringList; |
| 140 // First, those common to all architectures. |
| 141 static const char *common_args[] = { "pnacl_translator", "-filetype=obj" }; |
| 142 for (size_t i = 0; i < array_lengthof(common_args); ++i) { |
| 143 command_line->push_back(common_args[i]); |
| 144 } |
| 145 // Then those particular to a platform. |
| 146 static const char *llc_args_x8632[] = { "-mtriple=i686-none-nacl-gnu", NULL }; |
| 147 static const char *llc_args_x8664[] = { "-mtriple=x86_64-none-nacl-gnu", |
| 148 NULL }; |
| 149 static const char *llc_args_arm[] = { |
| 150 "-mtriple=armv7a-none-nacl-gnueabi", "-mattr=+neon", |
| 151 "-float-abi=hard", NULL |
| 152 }; |
| 153 |
| 154 const char **llc_args = NULL; |
| 155 #if defined(__pnacl__) |
| 156 switch (__builtin_nacl_target_arch()) { |
| 157 case PnaclTargetArchitectureX86_32: { |
| 158 llc_args = llc_args_x8632; |
| 159 break; |
| 160 } |
| 161 case PnaclTargetArchitectureX86_64: { |
| 162 llc_args = llc_args_x8664; |
| 163 break; |
| 164 } |
| 165 case PnaclTargetArchitectureARM_32: { |
| 166 llc_args = llc_args_arm; |
| 167 break; |
| 168 } |
| 169 default: |
| 170 fprintf(stderr, "no target architecture match.\n"); |
| 171 delete command_line; |
| 172 return NULL; |
| 173 } |
| 174 // Some cases for building this with nacl-gcc. |
| 175 #elif defined(__i386__) |
| 176 (void)llc_args_x8664; |
| 177 (void)llc_args_arm; |
| 178 llc_args = llc_args_x8632; |
| 179 #elif defined(__x86_64__) |
| 180 (void)llc_args_x8632; |
| 181 (void)llc_args_arm; |
| 182 llc_args = llc_args_x8664; |
| 183 #elif defined(__arm__) |
| 184 (void)llc_args_x8632; |
| 185 (void)llc_args_x8664; |
| 186 llc_args = llc_args_arm; |
| 187 #else |
| 188 #error "Unknown architecture" |
| 189 #endif |
| 190 for (size_t i = 0; llc_args[i] != NULL; i++) |
| 191 command_line->push_back(llc_args[i]); |
| 192 return command_line; |
| 193 } |
| 194 |
| 195 // Data passed from main thread to compile thread. |
| 196 // Takes ownership of the commandline vector. |
| 197 class StreamingThreadData { |
| 198 public: |
| 199 StreamingThreadData(int module_count, ArgStringList *cmd_line_vec) |
| 200 : module_count_(module_count), cmd_line_vec_(cmd_line_vec) {} |
| 201 ArgStringList *CmdLineVec() const { return cmd_line_vec_.get(); } |
| 202 int module_count_; |
| 203 const std::unique_ptr<ArgStringList> cmd_line_vec_; |
| 204 }; |
| 205 |
| 206 void *run_streamed(void *arg) { |
| 207 StreamingThreadData *data = reinterpret_cast<StreamingThreadData *>(arg); |
| 208 data->CmdLineVec()->push_back("-streaming-bitcode"); |
| 209 if (DoTranslate(data->CmdLineVec()) != 0) { |
| 210 // llc_main only returns 1 (as opposed to calling report_fatal_error) |
| 211 // in conditions we never expect to see in the browser (e.g. bad |
| 212 // command-line flags). |
| 213 srpc_streamer->setFatalError("llc_main unspecified failure"); |
| 214 return NULL; |
| 215 } |
| 216 delete data; |
| 217 return NULL; |
| 218 } |
| 219 |
| 220 // Actually do the work for stream initialization. |
| 221 void do_stream_init(NaClSrpcRpc *rpc, NaClSrpcArg **out_args, |
| 222 NaClSrpcClosure *done, StreamingThreadData* thread_data) { |
| 223 NaClSrpcClosureRunner runner(done); |
| 224 rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
| 225 srpc_streamer = new SRPCStreamer(); |
| 226 std::string StrError; |
| 227 |
| 228 NaClBitcodeStreamer = srpc_streamer->init( |
| 229 run_streamed, reinterpret_cast<void *>(thread_data), &StrError); |
| 230 if (NaClBitcodeStreamer) { |
| 231 rpc->result = NACL_SRPC_RESULT_OK; |
| 232 out_args[0]->arrays.str = strdup("no error"); |
| 233 } else { |
| 234 out_args[0]->arrays.str = strdup(StrError.c_str()); |
| 235 } |
| 236 } |
| 237 |
| 238 void stream_init_with_split(NaClSrpcRpc *rpc, NaClSrpcArg **in_args, |
| 239 NaClSrpcArg **out_args, NaClSrpcClosure *done) { |
| 240 ArgStringList *cmd_line_vec = GetDefaultCommandLine(); |
| 241 if (!cmd_line_vec) { |
| 242 NaClSrpcClosureRunner runner(done); |
| 243 rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
| 244 out_args[0]->arrays.str = strdup("Failed to get default commandline."); |
| 245 return; |
| 246 } |
| 247 AddFixedArguments(cmd_line_vec); |
| 248 |
| 249 int num_modules = in_args[0]->u.ival; |
| 250 if (num_modules < 1 || num_modules > kMaxModuleSplit) { |
| 251 NaClSrpcClosureRunner runner(done); |
| 252 rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
| 253 out_args[0]->arrays.str = strdup("Invalid module split count."); |
| 254 return; |
| 255 } |
| 256 |
| 257 StreamingThreadData *thread_data = |
| 258 new StreamingThreadData(num_modules, cmd_line_vec); |
| 259 |
| 260 for (int i = 1; i <= num_modules; i++) { |
| 261 object_file_fd[i - 1] = in_args[i]->u.hval; |
| 262 } |
| 263 |
| 264 char *command_line = in_args[kMaxModuleSplit + 1]->arrays.carr; |
| 265 size_t command_line_len = in_args[kMaxModuleSplit + 1]->u.count; |
| 266 std::unique_ptr<ArgStringList> extra_vec( |
| 267 CommandLineFromArgz(command_line, command_line_len)); |
| 268 cmd_line_vec->insert(cmd_line_vec->end(), extra_vec->begin(), |
| 269 extra_vec->end()); |
| 270 // Make sure some -mcpu override exists for now to prevent |
| 271 // auto-cpu feature detection from triggering instructions that |
| 272 // we do not validate yet. |
| 273 if (!HasCPUOverride(extra_vec.get())) { |
| 274 AddDefaultCPU(cmd_line_vec); |
| 275 } |
| 276 extra_vec.reset(NULL); |
| 277 |
| 278 // cmd_line_vec is freed by the translation thread in run_streamed. |
| 279 do_stream_init(rpc, out_args, done, thread_data); |
| 280 } |
| 281 |
| 282 // Invoked by the StreamChunk RPC. Receives a chunk of the bitcode and |
| 283 // buffers it for later retrieval by the compilation thread. |
| 284 void stream_chunk(NaClSrpcRpc *rpc, NaClSrpcArg **in_args, |
| 285 NaClSrpcArg **out_args, NaClSrpcClosure *done) { |
| 286 NaClSrpcClosureRunner runner(done); |
| 287 rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
| 288 size_t len = in_args[0]->u.count; |
| 289 unsigned char *bytes = |
| 290 reinterpret_cast<unsigned char *>(in_args[0]->arrays.carr); |
| 291 if (srpc_streamer->gotChunk(bytes, len) != len) { |
| 292 return; |
| 293 } |
| 294 rpc->result = NACL_SRPC_RESULT_OK; |
| 295 } |
| 296 |
| 297 // Invoked by the StreamEnd RPC. Waits until the compilation finishes, |
| 298 // then returns. Returns an int indicating whether the bitcode is a |
| 299 // shared library, a string with the soname, a string with dependencies, |
| 300 // and a string which contains an error message if applicable. |
| 301 void stream_end(NaClSrpcRpc *rpc, NaClSrpcArg **in_args, NaClSrpcArg **out_args, |
| 302 NaClSrpcClosure *done) { |
| 303 NaClSrpcClosureRunner runner(done); |
| 304 // TODO(eliben): We don't really use shared libraries now. At some |
| 305 // point this should be cleaned up from SRPC as well. |
| 306 out_args[0]->u.ival = false; |
| 307 out_args[1]->arrays.str = strdup(""); |
| 308 out_args[2]->arrays.str = strdup(""); |
| 309 rpc->result = NACL_SRPC_RESULT_APP_ERROR; |
| 310 std::string StrError; |
| 311 if (srpc_streamer->streamEnd(&StrError)) { |
| 312 out_args[3]->arrays.str = strdup(StrError.c_str()); |
| 313 return; |
| 314 } |
| 315 // SRPC deletes the strings returned when the closure is invoked. |
| 316 // Therefore we need to use strdup. |
| 317 out_args[3]->arrays.str = strdup(""); |
| 318 rpc->result = NACL_SRPC_RESULT_OK; |
| 319 } |
| 320 |
| 321 const struct NaClSrpcHandlerDesc srpc_methods[] = { |
| 322 // Protocol for streaming: |
| 323 // StreamInitWithSplit(num_split, obj_fd x 16, cmdline_flags) -> error_str |
| 324 // StreamChunk(data) + |
| 325 // StreamEnd() -> (is_shared_lib,soname,dependencies,error_str) |
| 326 { "StreamInitWithSplit:ihhhhhhhhhhhhhhhhC:s", stream_init_with_split }, |
| 327 { "StreamChunk:C:", stream_chunk }, |
| 328 { "StreamEnd::isss", stream_end }, |
| 329 { NULL, NULL }, |
| 330 }; |
| 331 |
| 332 } // namespace |
| 333 |
| 334 int getObjectFileFD(unsigned Index) { |
| 335 assert(Index < kMaxModuleSplit); |
| 336 return object_file_fd[Index]; |
| 337 } |
| 338 |
| 339 DataStreamer *getNaClBitcodeStreamer() { return NaClBitcodeStreamer; } |
| 340 |
| 341 // Called from the compilation thread |
| 342 void FatalErrorHandler(void *user_data, const std::string& reason, |
| 343 bool gen_crash_diag) { |
| 344 srpc_streamer->setFatalError(reason); |
| 345 } |
| 346 |
| 347 fatal_error_handler_t getSRPCErrorHandler() { return FatalErrorHandler; } |
| 348 |
| 349 int srpc_main(int argc, char **argv) { |
| 350 if (!NaClSrpcModuleInit()) { |
| 351 return 1; |
| 352 } |
| 353 |
| 354 if (!NaClSrpcAcceptClientConnection(srpc_methods)) { |
| 355 return 1; |
| 356 } |
| 357 NaClSrpcModuleFini(); |
| 358 return 0; |
| 359 } |
| 360 |
| 361 #endif // __native_client__ |
OLD | NEW |