Fix file permissions: set executable bit
[nativeclient.git] / service_runtime / sel_main.c
blobb171062cdd8c22d86c21ba333842e66aa5a24c41
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * NaCl Simple/secure ELF loader (NaCl SEL).
35 #include "native_client/include/portability.h"
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #if NACL_WINDOWS
43 # include <io.h>
44 #else
45 # include <netinet/in.h>
46 # include <stdint.h>
47 # include <fcntl.h>
48 # include <sys/types.h>
49 # include <fcntl.h>
50 # include <sys/socket.h>
51 # include <time.h>
52 # include <unistd.h>
53 #endif
55 #include "native_client/service_runtime/nacl_globals.h"
57 #include "native_client/service_runtime/expiration.h"
58 #include "native_client/service_runtime/nacl_log.h"
59 #include "native_client/service_runtime/sel_ldr.h"
60 #include "native_client/service_runtime/nacl_app.h"
61 #include "native_client/service_runtime/nacl_all_modules.h"
62 #include "native_client/service_runtime/nacl_syscall_common.h"
64 #include "native_client/intermodule_comm/nacl_imc_c.h"
66 #include "native_client/tools/libsrpc/nacl_srpc.h"
68 int verbosity = 0;
70 void VmentryPrinter(void *state,
71 struct NaClVmmapEntry *vmep)
73 printf("page num 0x%06x\n", (uint32_t)vmep->page_num);
74 printf("num pages %d\n", (uint32_t)vmep->npages);
75 printf("prot bits %x\n", vmep->prot);
76 fflush(stdout);
79 void PrintVmmap(struct NaClApp *nap)
81 uintptr_t page_num;
83 printf("In PrintVmmap\n"); fflush(stdout);
84 NaClVmmapVisit(&nap->mem_map, VmentryPrinter, (void *) 0);
86 page_num = NaClVmmapFindSpace(&nap->mem_map, 1024);
87 if (verbosity > 0) {
88 printf("found a 4MB hole at page number 0x%06x\n", (uint32_t)page_num);
89 fflush(stdout);
94 struct redir {
95 struct redir *next;
96 int nacl_desc;
97 enum {
98 HOST_DESC,
99 IMC_DESC,
100 IMC_ADDR,
101 } tag;
102 union {
103 struct {
104 int d;
105 int mode;
106 } host;
107 NaClHandle handle;
108 struct NaClSocketAddress addr;
109 } u;
112 int ImportModeMap(char opt)
114 switch (opt) {
115 case 'h':
116 return O_RDWR;
117 case 'r':
118 return O_RDONLY;
119 case 'w':
120 return O_WRONLY;
122 fprintf(stderr, ("option %c not understood as a host descriptor"
123 " import mode\n"),
124 opt);
125 exit(1);
126 /* NOTREACHED */
129 #if defined(HAVE_SDL)
130 #include <SDL.h>
131 #endif
133 int NaClDup2(d_old, d_new)
135 if (d_old == d_new) {
136 return 0;
138 if (
139 #if NACL_WINDOWS
140 _dup2(d_old, d_new)
141 #else
142 dup2(d_old, d_new)
143 #endif
144 == -1) {
145 fprintf(stderr, "sel_ldr: dup2 for log file failed\n");
146 exit(1);
148 return 1;
152 * Note that we cannot use the 3 arg declaration for main, since SDL
153 * preprocessor defines main to SDLmain and then provides a
154 * replacement main that performs other startup initialization. The
155 * SDL startup code only supplies 2 args to the renamed SDLmain, and
156 * if we used the 3 arg version here we'd pick up some garbage off the
157 * stack for envp. Instead, we make envp be a local variable, and
158 * initialize it from the eviron global variable.
160 int main(int ac,
161 char **av)
163 char **envp;
164 int opt;
165 char *rest;
166 struct redir *entry;
167 struct redir *redir_queue;
168 struct redir **redir_qend;
170 struct NaClApp state;
171 char *nacl_file = 0;
172 int main_thread_only = 1;
173 int export_addr_to = -2;
174 int dump_sock_addr_to = -1;
176 struct NaClApp *nap;
178 struct GioFile gout;
179 NaClErrorCode errcode;
180 struct GioMemoryFileSnapshot gf;
181 int (*fp)(int, char **, char **);
182 int ret_code;
183 extern char **environ;
184 char *log_file = NULL;
186 /* do expiration check first */
187 if (NaClHasExpired()) {
188 fprintf(stderr, "This version of Native Client has expired.\n");
189 fprintf(stderr, "Please visit: http://code.google.com/p/nativeclient/\n");
190 exit(-1);
193 /* SDL does not pass 3 args to re-defined main */
194 envp = environ;
196 ret_code = -1;
197 redir_queue = NULL;
198 redir_qend = &redir_queue;
202 * Set an exception handler so Windows won't show a message box if
203 * an exception occurs
205 WINDOWS_EXCEPTION_TRY;
207 NaClAllModulesInit();
209 if (!GioFileRefCtor(&gout, stdout)) {
210 fprintf(stderr, "Could not create general standard output channel\n");
211 return 1;
214 while ((opt = getopt(ac, av, "a:dD:f:h:i:l:mMP:r:vw:X:")) != -1) {
215 switch (opt) {
216 case 'a':
217 /* import IMC socket address */
218 entry = malloc(sizeof *entry);
219 if (NULL == entry) {
220 fprintf(stderr, "No memory for redirection queue\n");
221 return 1;
223 entry->next = NULL;
224 entry->nacl_desc = strtol(optarg, &rest, 0);
225 entry->tag = IMC_ADDR;
226 strncpy(entry->u.addr.path, rest + 1, NACL_PATH_MAX);
227 /* NUL terminate */
228 entry->u.addr.path[NACL_PATH_MAX-1] = '\0';
229 *redir_qend = entry;
230 redir_qend = &entry->next;
231 break;
232 case 'd':
233 fprintf(stderr, "DEBUG MODE ENABLED\n");
234 NaClInsecurelyBypassAllAclChecks();
235 NaClIgnoreValidatorResult();
236 break;
237 case 'h':
238 case 'r':
239 case 'w':
240 /* import host descriptor */
241 entry = malloc(sizeof *entry);
242 if (NULL == entry) {
243 fprintf(stderr, "No memory for redirection queue\n");
244 return 1;
246 entry->next = NULL;
247 entry->nacl_desc = strtol(optarg, &rest, 0);
248 entry->tag = HOST_DESC;
249 entry->u.host.d = strtol(rest+1, (char **) 0, 0);
250 entry->u.host.mode = ImportModeMap(opt);
251 *redir_qend = entry;
252 redir_qend = &entry->next;
253 break;
254 case 'i':
255 /* import IMC handle */
256 entry = malloc(sizeof *entry);
257 if (NULL == entry) {
258 fprintf(stderr, "No memory for redirection queue\n");
259 return 1;
261 entry->next = NULL;
262 entry->nacl_desc = strtol(optarg, &rest, 0);
263 entry->tag = IMC_DESC;
264 entry->u.handle = (NaClHandle) strtol(rest+1, (char **) 0, 0);
265 *redir_qend = entry;
266 redir_qend = &entry->next;
267 break;
268 case 'l':
269 log_file = optarg;
270 break;
271 case 'm':
272 main_thread_only = 1;
273 break;
274 case 'M':
275 main_thread_only = 0;
276 break;
277 case 'D':
278 dump_sock_addr_to = strtol(optarg, (char **) 0, 0);
279 break;
280 case 'f':
281 nacl_file = optarg;
282 break;
283 case 'P':
284 /* Conduit to convey the descriptor ID to the application code. */
285 NaClSrpcFileDescriptor = strtol(optarg, (char **) 0, 0);
286 break;
287 case 'v':
288 ++verbosity;
289 NaClLogIncrVerbosity();
290 break;
291 case 'X':
292 export_addr_to = strtol(optarg, (char **) 0, 0);
293 break;
294 default:
295 fprintf(stderr,
296 "Usage: sel_ldr [-a d:addr]\n"
297 " [-h d:D] [-r d:D] [-w d:D] [-i d:D]\n"
298 " [-f nacl_file]\n"
299 " [-P SRPC port number]\n"
300 "\n"
301 " [-D desc]\n"
302 " [-X d] [-dmMv]\n"
303 "\n"
304 " -a associates an IMC address with application descriptor d\n"
305 " -h\n"
306 " -r\n"
307 " -w associate a host POSIX descriptor D with app desc d\n"
308 " that was opened in O_RDWR, O_RDONLY, and O_WRONLY modes\n"
309 " respectively\n"
310 " -i associates an IMC handle D with app desc d\n"
311 " -f file to load\n"
312 " -P set SRPC port number for SRPC calls\n"
313 " -v increases verbosity\n"
314 " -X create a bound socket and export the address via an\n"
315 " IMC message to a corresponding NaCl app descriptor\n"
316 " (use -1 to create the bound socket / address descriptor\n"
317 " pair, but that no export via IMC should occur)\n"
318 " [NB: -m and -M only applies to the SDL builds\n ]\n"
319 " -m enforce that certain syscalls can only be made from\n"
320 " the main NaCl app thread, so that we're enforcing (bug)\n"
321 " compatibility with standalone windows apps that must\n"
322 " make certain (e.g., graphics) calls from the main\n"
323 " thread"
324 " -M allow syscalls to be made from any NaCl app thread,\n"
325 " since these (windows-library-using) syscalls are\n"
326 " actually done via a work queue using the sel_ldr\n"
327 " main thread anyway\n"
328 "\n"
329 " (testing flags)\n"
330 " -d debug mode (allow access to files!)\n"
331 " -D dump bound socket address (if any) to this POSIX\n"
332 " descriptor\n"
334 return -1;
339 * change stdout/stderr to log file now, so that subsequent error
340 * messages will go there. unfortunately, error messages that
341 * result from getopt processing -- usually out-of-memory, which
342 * shouldn't happen -- won't show up.
344 if (NULL != log_file) {
345 int d;
346 int should_close = 1;
348 d = open(log_file, O_WRONLY | O_APPEND | O_CREAT, 0777);
349 if (-1 == d) {
350 fprintf(stderr, "Could not create log file\n");
351 return 1;
353 should_close &= NaClDup2(d, 1);
354 should_close &= NaClDup2(d, 2);
355 if (should_close) {
356 close(d);
360 if (!nacl_file && optind < ac) {
361 nacl_file = av[optind];
362 ++optind;
364 if (!nacl_file) {
365 fprintf(stderr, "No nacl file specified\n");
366 return 1;
369 /* to be passed to NaClMain, eventually... */
370 av[--optind] = "NaClMain";
372 if (0 == GioMemoryFileSnapshotCtor(&gf, nacl_file)) {
373 perror("sel_main");
374 fprintf(stderr, "Cannot open \"%s\".\n", nacl_file);
375 return 1;
378 if (!NaClAppCtor(&state)) {
379 fprintf(stderr, "Error while constructing app state\n");
380 goto done_file_dtor;
383 state.restrict_to_main_thread = main_thread_only;
385 errcode = NaClAppLoadFile((struct Gio *) &gf, &state);
386 nap = &state;
388 if (LOAD_OK != errcode) {
389 fprintf(stderr, "Error while loading \"%s\": %s\n",
390 nacl_file,
391 NaClErrorString(errcode));
392 goto done;
394 if (verbosity) {
395 gprintf((struct Gio *) &gout, "printing NaClApp details\n");
396 NaClAppPrintDetails(nap, (struct Gio *) &gout);
399 fp = 0; /* eliminate compile-time warnings */
402 * Finish setting up the NaCl App. This includes dup'ing
403 * descriptors 0-2 and making them available to the NaCl App.
405 ret_code = NaClAppPrepareToLaunch(nap);
406 if (LOAD_OK != ret_code) {
407 fprintf(stderr, "NaClAppPrepareToLaunch returned %d", ret_code);
408 goto done;
412 * Execute additional I/O redirections. NB: since the NaClApp
413 * takes ownership of host / IMC socket descriptors, all but
414 * the first run will not get access if the NaClApp closes
415 * them. Currently a normal NaClApp process exit does not
416 * close descriptors, since the underlying host OS will do so
417 * as part of service runtime exit.
419 for (entry = redir_queue; NULL != entry; entry = entry->next) {
420 switch (entry->tag) {
421 case HOST_DESC:
422 NaClAddHostDescriptor(nap, entry->u.host.d,
423 entry->u.host.mode, entry->nacl_desc);
424 break;
425 case IMC_DESC:
426 NaClAddImcHandle(nap, entry->u.handle, entry->nacl_desc);
427 break;
428 case IMC_ADDR:
429 NaClAddImcAddr(nap, &entry->u.addr, entry->nacl_desc);
430 break;
435 * If export_addr_to is set to a non-negative integer, we
436 * create a bound socket and socket address pair and bind the
437 * former to descriptor 3 and the latter to descritor 4. The
438 * socket address is written out to the export_addr_to IMC
439 * socket. Since the other I/O redirections have been done,
440 * those descriptor numbers are available as a send target.
441 * Note that if those specified descriptors 3 and 4, they will
442 * get overwritten here.
444 * If export_addr_to is -1, we only create the bound socket
445 * and socket address pair, and we do not export to an IMC
446 * socket. This use case is typically only used in testing,
447 * where we only "dump" the socket address to stdout or
448 * similar channel.
450 if (-2 < export_addr_to) {
451 NaClCreateServiceSocket(nap);
452 if (0 <= export_addr_to) {
453 NaClSendServiceAddressTo(nap, export_addr_to);
457 if (-1 != dump_sock_addr_to) {
458 NaClDumpServiceAddressTo(nap, dump_sock_addr_to);
462 * Print out a marker for scripts to use to mark the start of app
463 * output.
465 NaClLog(1, "NACL: Application output follows\n");
468 * Make sure all the file buffers are flushed before entering
469 * the application code.
471 fflush(NULL);
474 * only nap->ehdrs.e_entry is usable, no symbol table is
475 * available.
477 if (!NaClCreateMainThread(nap,
478 ac - optind,
479 av + optind,
480 envp)) {
481 fprintf(stderr, "creating main thread failed\n");
482 goto done;
485 ret_code = NaClWaitForMainThreadToExit(nap);
486 _exit(ret_code);
488 done:
489 fflush(stdout);
491 if (verbosity) {
492 gprintf((struct Gio *) &gout, "exiting -- printing NaClApp details\n");
493 NaClAppPrintDetails(nap, (struct Gio *) &gout);
495 printf("Dumping vmmap.\n"); fflush(stdout);
496 PrintVmmap(nap);
497 fflush(stdout);
499 printf("appdtor\n");
500 fflush(stdout);
503 NaClAppDtor(&state);
505 done_file_dtor:
506 if ((*((struct Gio *) &gf)->vtbl->Close)((struct Gio *) &gf) == -1) {
507 fprintf(stderr, "Error while closing \"%s\".\n", av[optind]);
509 (*((struct Gio *) &gf)->vtbl->Dtor)((struct Gio *) &gf);
511 if (verbosity > 0) {
512 printf("Done.\n");
514 fflush(stdout);
516 NaClAllModulesFini();
518 WINDOWS_EXCEPTION_CATCH;
520 _exit(ret_code);