Debugging: Add code to print backtrace for guest on SIGSEGV
[nativeclient.git] / service_runtime / sel_main.c
blob873f018108bd7fd4906f9240463283dd8d8a84eb
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 <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
43 #if NACL_WINDOWS
44 # include <io.h>
45 #else
46 # include <netinet/in.h>
47 # include <stdint.h>
48 # include <fcntl.h>
49 # include <sys/types.h>
50 # include <fcntl.h>
51 # include <sys/socket.h>
52 # include <time.h>
53 # include <unistd.h>
54 #endif
56 #include "native_client/service_runtime/nacl_globals.h"
58 #include "native_client/service_runtime/expiration.h"
59 #include "native_client/service_runtime/gio.h"
60 #include "native_client/service_runtime/nacl_log.h"
61 #include "native_client/service_runtime/nacl_sync.h"
62 #include "native_client/service_runtime/nacl_sync_checked.h"
63 #include "native_client/service_runtime/nacl_app.h"
64 #include "native_client/service_runtime/nacl_all_modules.h"
65 #include "native_client/platform_qual_test/nacl_os_qualify.h"
66 #include "native_client/service_runtime/nacl_syscall_common.h"
67 #include "native_client/service_runtime/sel_ldr.h"
69 #include "native_client/intermodule_comm/nacl_imc_c.h"
71 #include "native_client/tools/libsrpc/nacl_srpc.h"
73 int verbosity = 0;
75 static void VmentryPrinter(void *state,
76 struct NaClVmmapEntry *vmep)
78 printf("page num 0x%06x\n", (uint32_t)vmep->page_num);
79 printf("num pages %d\n", (uint32_t)vmep->npages);
80 printf("prot bits %x\n", vmep->prot);
81 fflush(stdout);
84 static void PrintVmmap(struct NaClApp *nap)
86 printf("In PrintVmmap\n");
87 fflush(stdout);
88 NaClXMutexLock(&nap->mu);
89 NaClVmmapVisit(&nap->mem_map, VmentryPrinter, (void *) 0);
91 NaClXMutexUnlock(&nap->mu);
95 struct redir {
96 struct redir *next;
97 int nacl_desc;
98 enum {
99 HOST_DESC,
100 IMC_DESC,
101 IMC_ADDR
102 } tag;
103 union {
104 struct {
105 int d;
106 int mode;
107 } host;
108 NaClHandle handle;
109 struct NaClSocketAddress addr;
110 } u;
113 int ImportModeMap(char opt)
115 switch (opt) {
116 case 'h':
117 return O_RDWR;
118 case 'r':
119 return O_RDONLY;
120 case 'w':
121 return O_WRONLY;
123 fprintf(stderr, ("option %c not understood as a host descriptor"
124 " import mode\n"),
125 opt);
126 exit(1);
127 /* NOTREACHED */
130 #if defined(HAVE_SDL)
131 #include <SDL.h>
132 #endif
134 int NaClDup2(int d_old, int d_new)
136 if (d_old == d_new) {
137 return 0;
139 if (DUP2(d_old, d_new) == -1) {
140 fprintf(stderr, "sel_ldr: dup2 for log file failed\n");
141 exit(1);
143 return 1;
146 #ifdef __GNUC__
149 * GDB's canonical overlay managment routine.
150 * We need its symbol in the symbol table so don't inline it.
153 static void __attribute__ ((noinline)) _ovly_debug_event (void)
156 * The asm volatile is here as instructed by the GCC docs.
157 * It's not enough to declare a function noinline.
158 * GCC will still look inside the function to see if it's worth calling.
160 asm volatile ("");
163 #endif
165 static void StopForDebuggerInit (const struct NaClApp *state)
167 /* Put xlate_base in a place where gdb can find it. */
168 nacl_global_xlate_base = state->xlate_base;
170 #ifdef __GNUC__
171 _ovly_debug_event ();
172 #endif
175 static void trace_stack(struct sigcontext_struct *regs)
177 fprintf(stderr, " top_of_stack, code_addr=%lx\n", regs->eip);
178 int frame = regs->ebp;
179 int base = 0x10000000; /* TODO: don't hard code this */
180 while(frame) {
181 int ret_addr = *(int *) (base + frame + 4);
182 int next_frame = *(int *) (base + frame);
183 fprintf(stderr, " stack_frame=%x, code_addr=%x\n", frame, ret_addr);
184 frame = next_frame;
188 static void print_regs(struct sigcontext_struct *regs)
190 #define PRINT_REG(name) \
191 fprintf(stderr, " " #name "=0x%lx (%li)\n", regs->name, regs->name);
192 PRINT_REG(eip);
193 PRINT_REG(eax);
194 PRINT_REG(ebx);
195 PRINT_REG(ecx);
196 PRINT_REG(edx);
197 #undef PRINT_REG
200 static int orig_gs;
202 static void handle_signal(int arg)
204 struct sigcontext_struct *regs = (void *) (&arg + 1);
205 /* Need to restore %gs even if we don't use TLS variables.
206 Stack protector value is fetched from %gs. */
207 __asm__("mov %0, %%gs" : : "r" (orig_gs));
208 /* Disable this handler. */
209 signal(SIGSEGV, SIG_DFL);
210 fprintf(stderr, "Caught signal\n");
211 print_regs(regs);
212 trace_stack(regs);
213 fprintf(stderr, "Signal handler done, exiting\n");
214 _exit(1);
217 static void set_up_signal_handler()
219 stack_t st;
220 st.ss_size = 2048;
221 st.ss_sp = malloc(st.ss_size);
222 st.ss_flags = 0;
223 if(sigaltstack(&st, NULL) < 0) {
224 perror("sigaltstack");
225 exit(1);
228 __asm__("mov %%gs, %0" : "=r" (orig_gs));
230 struct sigaction act;
231 act.sa_handler = handle_signal;
232 sigemptyset(&act.sa_mask);
233 act.sa_flags = SA_ONSTACK;
234 if(sigaction(SIGSEGV, &act, NULL) < 0) {
235 perror("sigaction");
236 exit(1);
241 * Note that we cannot use the 3 arg declaration for main, since SDL
242 * preprocessor defines main to SDLmain and then provides a
243 * replacement main that performs other startup initialization. The
244 * SDL startup code only supplies 2 args to the renamed SDLmain, and
245 * if we used the 3 arg version here we'd pick up some garbage off the
246 * stack for envp. Instead, we make envp be a local variable, and
247 * initialize it from the eviron global variable.
249 int main(int ac,
250 char **av)
252 char **envp;
253 int opt;
254 char *rest;
255 struct redir *entry;
256 struct redir *redir_queue;
257 struct redir **redir_qend;
259 struct NaClApp state;
260 char *nacl_file = 0;
261 int main_thread_only = 1;
262 int export_addr_to = -2;
263 int dump_sock_addr_to = -1;
265 struct NaClApp *nap;
267 struct GioFile gout;
268 NaClErrorCode errcode;
269 struct GioMemoryFileSnapshot gf;
271 int ret_code;
272 extern char **environ;
273 char *log_file = NULL;
274 int log_desc = -1;
275 FILE *log_stream;
276 char *env_verbosity;
277 struct GioFile log_gio;
279 set_up_signal_handler();
281 /* do expiration check first */
282 #if 0
283 if (NaClHasExpired()) {
284 fprintf(stderr, "This version of Native Client has expired.\n");
285 fprintf(stderr, "Please visit: http://code.google.com/p/nativeclient/\n");
286 exit(-1);
288 #endif
290 ret_code = 1;
291 redir_queue = NULL;
292 redir_qend = &redir_queue;
295 * Set an exception handler so Windows won't show a message box if
296 * an exception occurs
298 WINDOWS_EXCEPTION_TRY;
300 NaClAllModulesInit();
302 log_file = getenv("NACLLOG");
304 if (NULL != (env_verbosity = getenv("NACLVERBOSITY"))) {
305 int v = strtol(env_verbosity, (char **) 0, 0);
307 if (v >= 0) {
308 verbosity = v;
309 NaClLogSetVerbosity(v);
312 fflush((FILE *) NULL);
314 if (!GioFileRefCtor(&gout, stdout)) {
315 fprintf(stderr, "Could not create general standard output channel\n");
316 return 1;
319 while ((opt = getopt(ac, av, "a:dD:f:h:i:l:mMP:r:vw:X:E:")) != -1) {
320 switch (opt) {
321 case 'a':
322 /* import IMC socket address */
323 entry = malloc(sizeof *entry);
324 if (NULL == entry) {
325 fprintf(stderr, "No memory for redirection queue\n");
326 return 1;
328 entry->next = NULL;
329 entry->nacl_desc = strtol(optarg, &rest, 0);
330 entry->tag = IMC_ADDR;
331 strncpy(entry->u.addr.path, rest + 1, NACL_PATH_MAX);
332 /* NUL terminate */
333 entry->u.addr.path[NACL_PATH_MAX-1] = '\0';
334 *redir_qend = entry;
335 redir_qend = &entry->next;
336 break;
337 case 'd':
338 fprintf(stderr, "DEBUG MODE ENABLED\n");
339 NaClInsecurelyBypassAllAclChecks();
340 /* NaClIgnoreValidatorResult(); */
341 break;
342 case 'h':
343 case 'r':
344 case 'w':
345 /* import host descriptor */
346 entry = malloc(sizeof *entry);
347 if (NULL == entry) {
348 fprintf(stderr, "No memory for redirection queue\n");
349 return 1;
351 entry->next = NULL;
352 entry->nacl_desc = strtol(optarg, &rest, 0);
353 entry->tag = HOST_DESC;
354 entry->u.host.d = strtol(rest+1, (char **) 0, 0);
355 entry->u.host.mode = ImportModeMap(opt);
356 *redir_qend = entry;
357 redir_qend = &entry->next;
358 break;
359 case 'i':
360 /* import IMC handle */
361 entry = malloc(sizeof *entry);
362 if (NULL == entry) {
363 fprintf(stderr, "No memory for redirection queue\n");
364 return 1;
366 entry->next = NULL;
367 entry->nacl_desc = strtol(optarg, &rest, 0);
368 entry->tag = IMC_DESC;
369 entry->u.handle = (NaClHandle) strtol(rest+1, (char **) 0, 0);
370 *redir_qend = entry;
371 redir_qend = &entry->next;
372 break;
373 case 'l':
374 log_file = optarg;
375 break;
376 case 'm':
377 main_thread_only = 1;
378 break;
379 case 'M':
380 main_thread_only = 0;
381 break;
382 case 'D':
383 dump_sock_addr_to = strtol(optarg, (char **) 0, 0);
384 break;
385 case 'f':
386 nacl_file = optarg;
387 break;
388 case 'P':
389 /* Conduit to convey the descriptor ID to the application code. */
390 NaClSrpcFileDescriptor = strtol(optarg, (char **) 0, 0);
391 break;
392 case 'v':
393 ++verbosity;
394 NaClLogIncrVerbosity();
395 break;
396 case 'X':
397 export_addr_to = strtol(optarg, (char **) 0, 0);
398 break;
399 case 'E':
400 if(putenv(optarg) != 0) {
401 perror("putenv");
402 return 1;
404 break;
405 default:
406 fprintf(stderr,
407 "Usage: sel_ldr [-a d:addr]\n"
408 " [-h d:D] [-r d:D] [-w d:D] [-i d:D]\n"
409 " [-f nacl_file]\n"
410 " [-P SRPC port number]\n"
411 "\n"
412 " [-D desc]\n"
413 " [-X d] [-dmMv]\n"
414 " [-E var=val]\n"
415 "\n");
416 fprintf(stderr,
417 " -a associates an IMC address with application descriptor d\n"
418 " -h\n"
419 " -r\n"
420 " -w associate a host POSIX descriptor D with app desc d\n"
421 " that was opened in O_RDWR, O_RDONLY, and O_WRONLY modes\n"
422 " respectively\n"
423 " -i associates an IMC handle D with app desc d\n"
424 " -f file to load\n"
425 " -P set SRPC port number for SRPC calls\n"
426 " -v increases verbosity\n"
427 " -X create a bound socket and export the address via an\n"
428 " IMC message to a corresponding NaCl app descriptor\n"
429 " (use -1 to create the bound socket / address descriptor\n"
430 " pair, but that no export via IMC should occur)\n"
431 " [NB: -m and -M only applies to the SDL builds\n ]\n");
432 fprintf(stderr,
433 " -m enforce that certain syscalls can only be made from\n"
434 " the main NaCl app thread, so that we're enforcing (bug)\n"
435 " compatibility with standalone windows apps that must\n"
436 " make certain (e.g., graphics) calls from the main\n"
437 " thread"
438 " -M allow syscalls to be made from any NaCl app thread,\n"
439 " since these (windows-library-using) syscalls are\n"
440 " actually done via a work queue using the sel_ldr\n"
441 " main thread anyway\n"
442 " -E set environment variable\n"
443 "\n"
444 " (testing flags)\n"
445 " -d debug mode (allow access to files!)\n"
446 " -D dump bound socket address (if any) to this POSIX\n"
447 " descriptor\n");
448 return -1;
452 /* SDL does not pass 3 args to re-defined main */
453 envp = environ;
456 * change stdout/stderr to log file now, so that subsequent error
457 * messages will go there. unfortunately, error messages that
458 * result from getopt processing -- usually out-of-memory, which
459 * shouldn't happen -- won't show up.
461 if (NULL != log_file) {
463 log_desc = open(log_file, O_WRONLY | O_APPEND | O_CREAT, 0777);
464 if (-1 == log_desc) {
465 fprintf(stderr, "Could not create log file\n");
466 return 1;
469 log_stream = FDOPEN(log_desc, "a");
470 if (NULL == log_stream) {
471 fprintf(stderr, "Could not fdopen log stream\n");
472 return 1;
474 GioFileRefCtor(&log_gio, log_stream);
475 NaClLogSetGio((struct Gio *) &log_gio);
478 if (!nacl_file && optind < ac) {
479 nacl_file = av[optind];
480 ++optind;
482 if (!nacl_file) {
483 fprintf(stderr, "No nacl file specified\n");
484 return 1;
487 /* to be passed to NaClMain, eventually... */
488 av[--optind] = "NaClMain";
490 if (0 == GioMemoryFileSnapshotCtor(&gf, nacl_file)) {
491 perror("sel_main");
492 fprintf(stderr, "Cannot open \"%s\".\n", nacl_file);
493 return 1;
496 if (!NaClAppCtor(&state)) {
497 fprintf(stderr, "Error while constructing app state\n");
498 goto done_file_dtor;
501 state.restrict_to_main_thread = main_thread_only;
503 nap = &state;
504 errcode = LOAD_OK;
507 * in order to report load error to the browser plugin through the
508 * secure command channel, we do not immediate jump to cleanup code
509 * on error. rather, we continue processing (assuming earlier
510 * errors do not make it inappropriate) until the secure command
511 * channel is set up, and then bail out.
515 * Ensure this operating system platform is supported.
517 if (!NaClOsIsSupported()) {
518 errcode = LOAD_UNSUPPORTED_OS_PLATFORM;
519 nap->module_load_status = errcode;
520 fprintf(stderr, "Error while loading \"%s\": %s\n",
521 nacl_file,
522 NaClErrorString(errcode));
525 if (LOAD_OK == errcode) {
526 errcode = NaClAppLoadFile((struct Gio *) &gf, nap);
527 if (LOAD_OK != errcode) {
528 nap->module_load_status = errcode;
529 fprintf(stderr, "Error while loading \"%s\": %s\n",
530 nacl_file,
531 NaClErrorString(errcode));
535 if (LOAD_OK == errcode) {
536 if (verbosity) {
537 gprintf((struct Gio *) &gout, "printing NaClApp details\n");
538 NaClAppPrintDetails(nap, (struct Gio *) &gout);
542 * Finish setting up the NaCl App. This includes dup'ing
543 * descriptors 0-2 and making them available to the NaCl App.
545 errcode = NaClAppPrepareToLaunch(nap,
547 (-1 == log_desc) ? 1 : log_desc,
548 (-1 == log_desc) ? 2 : log_desc);
549 if (LOAD_OK != errcode) {
550 nap->module_load_status = errcode;
551 fprintf(stderr, "NaClAppPrepareToLaunch returned %d", errcode);
555 /* Give debuggers a well known point at which xlate_base is known. */
556 StopForDebuggerInit (&state);
559 * Execute additional I/O redirections. NB: since the NaClApp
560 * takes ownership of host / IMC socket descriptors, all but
561 * the first run will not get access if the NaClApp closes
562 * them. Currently a normal NaClApp process exit does not
563 * close descriptors, since the underlying host OS will do so
564 * as part of service runtime exit.
566 for (entry = redir_queue; NULL != entry; entry = entry->next) {
567 switch (entry->tag) {
568 case HOST_DESC:
569 NaClAddHostDescriptor(nap, entry->u.host.d,
570 entry->u.host.mode, entry->nacl_desc);
571 break;
572 case IMC_DESC:
573 NaClAddImcHandle(nap, entry->u.handle, entry->nacl_desc);
574 break;
575 case IMC_ADDR:
576 NaClAddImcAddr(nap, &entry->u.addr, entry->nacl_desc);
577 break;
581 * If export_addr_to is set to a non-negative integer, we create a
582 * bound socket and socket address pair and bind the former to
583 * descriptor 3 and the latter to descriptor 4. The socket address
584 * is written out to the export_addr_to descriptor.
586 * The service runtime also accepts a connection on the bound socket
587 * and spawns a secure command channel thread to service it.
589 * If export_addr_to is -1, we only create the bound socket and
590 * socket address pair, and we do not export to an IMC socket. This
591 * use case is typically only used in testing, where we only "dump"
592 * the socket address to stdout or similar channel.
594 if (-2 < export_addr_to) {
595 NaClCreateServiceSocket(nap);
596 if (0 <= export_addr_to) {
597 NaClSendServiceAddressTo(nap, export_addr_to);
599 * NB: spawns a thread that uses the command channel. we do
600 * this after NaClAppLoadFile so that NaClApp object is more
601 * fully populated. Hereafter any changes to nap should be done
602 * while holding locks.
604 NaClSecureCommandChannel(nap);
608 * may have created a thread, so need to synchronize uses of nap
609 * contents
612 if (-1 != dump_sock_addr_to) {
613 NaClDumpServiceAddressTo(nap, dump_sock_addr_to);
617 * Print out a marker for scripts to use to mark the start of app
618 * output.
620 NaClLog(1, "NACL: Application output follows\n");
623 * Make sure all the file buffers are flushed before entering
624 * the application code.
626 fflush((FILE *) NULL);
628 NaClXMutexLock(&nap->mu);
629 nap->module_load_status = LOAD_OK;
630 NaClXCondVarBroadcast(&nap->cv);
631 NaClXMutexUnlock(&nap->mu);
633 if (NULL != nap->secure_channel) {
635 * wait for start_module RPC call on secure channel thread.
637 NaClWaitForModuleStartStatusCall(nap);
641 * error reporting done; can quit now if there was an error earlier.
643 if (LOAD_OK != errcode) {
644 goto done;
648 * only nap->ehdrs.e_entry is usable, no symbol table is
649 * available.
651 if (!NaClCreateMainThread(nap,
652 ac - optind,
653 av + optind,
654 envp)) {
655 fprintf(stderr, "creating main thread failed\n");
656 goto done;
659 ret_code = NaClWaitForMainThreadToExit(nap);
662 * exit_group or equiv kills any still running threads while module
663 * addr space is still valid. otherwise we'd have to kill threads
664 * before we clean up the address space.
666 _exit(ret_code);
668 done:
669 fflush(stdout);
671 if (verbosity) {
672 gprintf((struct Gio *) &gout, "exiting -- printing NaClApp details\n");
673 NaClAppPrintDetails(nap, (struct Gio *) &gout);
675 printf("Dumping vmmap.\n"); fflush(stdout);
676 PrintVmmap(nap);
677 fflush(stdout);
679 printf("appdtor\n");
680 fflush(stdout);
683 NaClAppDtor(&state);
685 done_file_dtor:
686 if ((*((struct Gio *) &gf)->vtbl->Close)((struct Gio *) &gf) == -1) {
687 fprintf(stderr, "Error while closing \"%s\".\n", av[optind]);
689 (*((struct Gio *) &gf)->vtbl->Dtor)((struct Gio *) &gf);
691 if (verbosity > 0) {
692 printf("Done.\n");
694 fflush(stdout);
696 NaClAllModulesFini();
698 WINDOWS_EXCEPTION_CATCH;
700 _exit(ret_code);