2 * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
8 * NaCl Simple/secure ELF loader (NaCl SEL).
10 #include "native_client/src/include/portability.h"
11 #include "native_client/src/include/portability_io.h"
14 #include <crt_externs.h>
29 #include "native_client/src/shared/gio/gio.h"
30 #include "native_client/src/shared/imc/nacl_imc_c.h"
31 #include "native_client/src/shared/platform/nacl_check.h"
32 #include "native_client/src/shared/platform/nacl_exit.h"
33 #include "native_client/src/shared/platform/nacl_log.h"
34 #include "native_client/src/shared/platform/nacl_sync.h"
35 #include "native_client/src/shared/platform/nacl_sync_checked.h"
36 #include "native_client/src/shared/srpc/nacl_srpc.h"
38 #include "native_client/src/trusted/desc/nacl_desc_base.h"
39 #include "native_client/src/trusted/desc/nacl_desc_io.h"
40 #include "native_client/src/trusted/fault_injection/fault_injection.h"
41 #include "native_client/src/trusted/fault_injection/test_injection.h"
42 #include "native_client/src/trusted/perf_counter/nacl_perf_counter.h"
43 #include "native_client/src/trusted/service_runtime/env_cleanser.h"
44 #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
45 #include "native_client/src/trusted/service_runtime/load_file.h"
46 #include "native_client/src/trusted/service_runtime/nacl_app.h"
47 #include "native_client/src/trusted/service_runtime/nacl_all_modules.h"
48 #include "native_client/src/trusted/service_runtime/nacl_bootstrap_channel_error_reporter.h"
49 #include "native_client/src/trusted/service_runtime/nacl_debug_init.h"
50 #include "native_client/src/trusted/service_runtime/nacl_error_log_hook.h"
51 #include "native_client/src/trusted/service_runtime/nacl_globals.h"
52 #include "native_client/src/trusted/service_runtime/nacl_runtime_host_interface.h"
53 #include "native_client/src/trusted/service_runtime/nacl_signal.h"
54 #include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
55 #include "native_client/src/trusted/service_runtime/nacl_valgrind_hooks.h"
56 #include "native_client/src/trusted/service_runtime/osx/mach_exception_handler.h"
57 #include "native_client/src/trusted/service_runtime/outer_sandbox.h"
58 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
59 #include "native_client/src/trusted/service_runtime/sel_qualify.h"
60 #include "native_client/src/trusted/service_runtime/win/exception_patch/ntdll_patch.h"
61 #include "native_client/src/trusted/service_runtime/win/debug_exception_handler.h"
64 static void (*g_enable_outer_sandbox_func)(void) =
66 NaClEnableOuterSandbox;
71 void NaClSetEnableOuterSandboxFunc(void (*func)(void)) {
72 g_enable_outer_sandbox_func = func;
75 static void VmentryPrinter(void *state,
76 struct NaClVmmapEntry *vmep) {
77 UNREFERENCED_PARAMETER(state);
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);
84 static void PrintVmmap(struct NaClApp *nap) {
85 printf("In PrintVmmap\n");
87 NaClXMutexLock(&nap->mu);
88 NaClVmmapVisit(&nap->mem_map, VmentryPrinter, (void *) 0);
90 NaClXMutexUnlock(&nap->mu);
107 struct NaClSocketAddress addr;
111 int ImportModeMap(char opt) {
120 fprintf(stderr, ("option %c not understood as a host descriptor"
127 static void PrintUsage(void) {
128 /* NOTE: this is broken up into multiple statements to work around
129 the constant string size limit */
131 "Usage: sel_ldr [-h d:D] [-r d:D] [-w d:D] [-i d:D]\n"
134 " [-X d] [-acFglQRsSQv]\n"
135 " -- [nacl_file] [args]\n"
140 " -w associate a host POSIX descriptor D with app desc d\n"
141 " that was opened in O_RDWR, O_RDONLY, and O_WRONLY modes\n"
143 " -i associates an IMC handle D with app desc d\n"
144 " -f file to load; if omitted, 1st arg after \"--\" is loaded\n"
145 " -B additional ELF file to load as a blob library\n"
146 " -v increases verbosity\n"
147 " -X create a bound socket and export the address via an\n"
148 " IMC message to a corresponding inherited IMC app descriptor\n"
149 " (use -1 to create the bound socket / address descriptor\n"
150 " pair, but that no export via IMC should occur)\n");
152 " -R an RPC supplies the NaCl module.\n"
153 " No nacl_file argument is expected, and the -f flag cannot be\n"
154 " used with this flag.\n"
157 " -a allow file access plus some other syscalls! dangerous!\n"
158 " -c ignore validator! dangerous! Repeating this option twice skips\n"
159 " validation completely.\n"
160 " -F fuzz testing; quit after loading NaCl app\n"
161 " -g enable gdb debug stub. Not secure on x86-64 Windows.\n"
162 " -l <file> write log output to the given file\n"
163 " -q quiet; suppress diagnostic/warning messages at startup\n"
164 " -Q disable platform qualification (dangerous!)\n"
165 " -s safely stub out non-validating instructions\n"
166 " -S enable signal handling. Not supported on Windows.\n"
167 " -E <name=value>|<name> set an environment variable\n"
168 " -Z use fixed feature x86 CPU mode\n"
170 " (For full effect, put -l and -q at the beginning.)\n"
171 ); /* easier to add new flags/lines */
175 static const struct option longopts[] = {
176 { "r_debug", required_argument, NULL, 'D' },
177 { "reserved_at_zero", required_argument, NULL, 'z' },
181 static int my_getopt(int argc, char *const *argv, const char *shortopts) {
182 return getopt_long(argc, argv, shortopts, longopts, NULL);
185 #define my_getopt getopt
188 int NaClSelLdrMain(int argc, char **argv) {
192 struct redir *redir_queue;
193 struct redir **redir_qend;
196 struct NaClApp state;
197 char *nacl_file = NULL;
198 char *blob_library_file = NULL;
199 int rpc_supplies_nexe = 0;
200 int export_addr_to = -1;
202 struct NaClApp *nap = &state;
205 NaClErrorCode errcode = LOAD_INTERNAL;
206 struct NaClDesc *blob_file = NULL;
209 struct DynArray env_vars;
213 int fuzzing_quit_after_load = 0;
214 int debug_mode_bypass_acl_checks = 0;
215 int debug_mode_ignore_validator = 0;
216 int debug_mode_startup_signal = 0;
217 int skip_qualification = 0;
218 int handle_signals = 0;
219 int enable_debug_stub = 0;
220 struct NaClPerfCounter time_all_main;
222 struct NaClEnvCleanser env_cleanser;
225 /* Mac dynamic libraries cannot access the environ variable directly. */
226 envp = (const char **) *_NSGetEnviron();
228 /* Overzealous code style check is overzealous. */
229 /* @IGNORE_LINES_FOR_CODE_HYGIENE[1] */
230 extern char **environ;
231 envp = (const char **) environ;
236 redir_qend = &redir_queue;
238 memset(&state, 0, sizeof state);
239 NaClAllModulesInit();
240 NaClBootstrapChannelErrorReporterInit();
241 NaClErrorLogHookInit(NaClBootstrapChannelErrorReporter, &state);
243 verbosity = NaClLogGetVerbosity();
245 NaClPerfCounterCtor(&time_all_main, "SelMain");
247 fflush((FILE *) NULL);
249 NaClDebugExceptionHandlerStandaloneHandleArgs(argc, argv);
251 if (!GioFileRefCtor(&gout, stdout)) {
252 fprintf(stderr, "Could not create general standard output channel\n");
255 if (!NaClAppCtor(&state)) {
256 NaClLog(LOG_FATAL, "NaClAppCtor() failed\n");
258 if (!DynArrayCtor(&env_vars, 0)) {
259 NaClLog(LOG_FATAL, "Failed to allocate env var array\n");
262 * On platforms with glibc getopt, require POSIXLY_CORRECT behavior,
263 * viz, no reordering of the arglist -- stop argument processing as
264 * soon as an unrecognized argument is encountered, so that, for
265 * example, in the invocation
267 * sel_ldr foo.nexe -vvv
269 * the -vvv flags are made available to the nexe, rather than being
270 * consumed by getopt. This makes the behavior of the Linux build
271 * of sel_ldr consistent with the Windows and OSX builds.
273 while ((opt = my_getopt(argc, argv,
277 "aB:cdeE:f:Fgh:i:l:qQr:RsSvw:X:Z")) != -1) {
281 fprintf(stderr, "DEBUG MODE ENABLED (bypass acl)\n");
282 debug_mode_bypass_acl_checks = 1;
285 blob_library_file = optarg;
288 ++debug_mode_ignore_validator;
291 debug_mode_startup_signal = 1;
295 NaClHandleRDebug(optarg, argv[0]);
299 nap->enable_exception_handling = 1;
303 * For simplicity, we treat the environment variables as a
304 * list of strings rather than a key/value mapping. We do not
305 * try to prevent duplicate keys or require the strings to be
306 * of the form "KEY=VALUE". This is in line with how execve()
309 * We expect that most callers passing "-E" will either pass
310 * in a fixed list or will construct the list using a
311 * high-level language, in which case de-duplicating keys
312 * outside of sel_ldr is easier. However, we could do
313 * de-duplication here if it proves to be worthwhile.
315 if (!DynArraySet(&env_vars, env_vars.num_entries, optarg)) {
316 NaClLog(LOG_FATAL, "Adding item to env_vars failed\n");
323 fuzzing_quit_after_load = 1;
327 enable_debug_stub = 1;
333 /* import host descriptor */
334 entry = malloc(sizeof *entry);
336 fprintf(stderr, "No memory for redirection queue\n");
340 entry->nacl_desc = strtol(optarg, &rest, 0);
341 entry->tag = HOST_DESC;
342 entry->u.host.d = strtol(rest+1, (char **) 0, 0);
343 entry->u.host.mode = ImportModeMap(opt);
345 redir_qend = &entry->next;
348 /* import IMC handle */
349 entry = malloc(sizeof *entry);
351 fprintf(stderr, "No memory for redirection queue\n");
355 entry->nacl_desc = strtol(optarg, &rest, 0);
356 entry->tag = IMC_DESC;
357 entry->u.handle = (NaClHandle) strtol(rest+1, (char **) 0, 0);
359 redir_qend = &entry->next;
362 if (NULL != optarg) {
364 * change stdout/stderr to log file now, so that subsequent error
365 * messages will go there. unfortunately, error messages that
366 * result from getopt processing -- usually out-of-memory, which
367 * shouldn't happen -- won't show up.
369 NaClLogSetFile(optarg);
377 fprintf(stderr, "PLATFORM QUALIFICATION DISABLED BY -Q - "
378 "Native Client's sandbox will be unreliable!\n");
379 skip_qualification = 1;
382 rpc_supplies_nexe = 1;
384 /* case 'r': with 'h' and 'w' above */
386 if (nap->validator->stubout_mode_implemented) {
387 nap->validator_stub_out_mode = 1;
390 "stub_out_mode is not supported, disabled\n");
398 NaClLogIncrVerbosity();
400 /* case 'w': with 'h' and 'r' above */
402 export_addr_to = strtol(optarg, (char **) 0, 0);
406 NaClHandleReservedAtZero(optarg);
410 if (nap->validator->readonly_text_implemented) {
411 NaClLog(LOG_WARNING, "Enabling Fixed-Feature CPU Mode\n");
412 nap->fixed_feature_cpu_mode = 1;
413 if (!nap->validator->FixCPUFeatures(nap->cpu_features)) {
415 "This CPU lacks features required by "
416 "fixed-function CPU mode.\n");
421 "fixed_feature_cpu_mode is not supported\n");
426 fprintf(stderr, "ERROR: unknown option: [%c]\n\n", opt);
432 if (debug_mode_startup_signal) {
434 fprintf(stderr, "DEBUG startup signal not supported on Windows\n");
438 * SIGCONT is ignored by default, so this doesn't actually do anything
439 * by itself. The purpose of raising the signal is to get a debugger
440 * to stop and inspect the process before it does anything else. When
441 * sel_ldr is started via nacl_helper_bootstrap, it needs to run as far
442 * as doing its option processing and calling NaClHandleRDebug before
443 * the debugger will understand the association between the address
444 * space and the sel_ldr binary and its dependent shared libraries.
445 * When the debugger stops for the signal, the hacker can run the
446 * "sharedlibrary" command (if the debugger is GDB) and thereafter
447 * it becomes possible to set symbolic breakpoints and so forth.
449 fprintf(stderr, "DEBUG taking startup signal (SIGCONT) now\n");
454 if (debug_mode_ignore_validator == 1) {
456 fprintf(stderr, "DEBUG MODE ENABLED (ignore validator)\n");
457 } else if (debug_mode_ignore_validator > 1) {
459 fprintf(stderr, "DEBUG MODE ENABLED (skip validator)\n");
464 char const *separator = "";
466 fprintf(stderr, "sel_ldr argument list:\n");
467 for (ix = 0; ix < argc; ++ix) {
468 fprintf(stderr, "%s%s", separator, argv[ix]);
474 if (debug_mode_bypass_acl_checks) {
475 NaClInsecurelyBypassAllAclChecks();
478 if (rpc_supplies_nexe) {
479 if (NULL != nacl_file) {
481 "sel_ldr: mutually exclusive flags -f and -R both used\n");
484 /* post: NULL == nacl_file */
485 if (export_addr_to < 0) {
487 "sel_ldr: -R requires -X to set up secure command channel\n");
491 if (NULL == nacl_file && optind < argc) {
492 nacl_file = argv[optind];
495 if (NULL == nacl_file) {
496 fprintf(stderr, "No nacl file specified\n");
499 /* post: NULL != nacl_file */
502 * post condition established by the above code (in Hoare logic
505 * NULL == nacl_file iff rpc_supplies_nexe
507 * so hence forth, testing !rpc_supplies_nexe suffices for
508 * establishing NULL != nacl_file.
510 CHECK((NULL == nacl_file) == rpc_supplies_nexe);
512 /* to be passed to NaClMain, eventually... */
513 argv[--optind] = (char *) "NaClMain";
515 state.ignore_validator_result = (debug_mode_ignore_validator > 0);
516 state.skip_validator = (debug_mode_ignore_validator > 1);
518 if (getenv("NACL_UNTRUSTED_EXCEPTION_HANDLING") != NULL) {
519 state.enable_exception_handling = 1;
522 * TODO(mseaborn): Always enable the Mach exception handler on Mac
523 * OS X, and remove handle_signals and sel_ldr's "-S" option.
525 if (state.enable_exception_handling || enable_debug_stub ||
526 (handle_signals && NACL_OSX)) {
528 state.attach_debug_exception_handler_func =
529 NaClDebugExceptionHandlerStandaloneAttach;
531 /* NaCl's signal handler is always enabled on Linux. */
533 if (!NaClInterceptMachExceptions()) {
534 fprintf(stderr, "ERROR setting up Mach exception interception.\n");
538 # error Unknown host OS
545 * in order to report load error to the browser plugin through the
546 * secure command channel, we do not immediate jump to cleanup code
547 * on error. rather, we continue processing (assuming earlier
548 * errors do not make it inappropriate) until the secure command
549 * channel is set up, and then bail out.
553 * Ensure the platform qualification checks pass.
555 * NACL_DANGEROUS_SKIP_QUALIFICATION_TEST is used by tsan / memcheck
556 * (see src/third_party/valgrind/).
558 if (!skip_qualification &&
559 getenv("NACL_DANGEROUS_SKIP_QUALIFICATION_TEST") != NULL) {
561 fprintf(stderr, "PLATFORM QUALIFICATION DISABLED BY ENVIRONMENT - "
562 "Native Client's sandbox will be unreliable!\n");
563 skip_qualification = 1;
566 if (!skip_qualification) {
567 NaClErrorCode pq_error = NACL_FI_VAL("pq", NaClErrorCode,
568 NaClRunSelQualificationTests());
569 if (LOAD_OK != pq_error) {
571 nap->module_load_status = pq_error;
573 fprintf(stderr, "Error while loading \"%s\": %s\n",
574 NULL != nacl_file ? nacl_file
575 : "(no file, to-be-supplied-via-RPC)",
576 NaClErrorString(errcode));
581 NaClSignalHandlerInit();
584 * Patch the Windows exception dispatcher to be safe in the case of
585 * faults inside x86-64 sandboxed code. The sandbox is not secure
586 * on 64-bit Windows without this.
588 #if (NACL_WINDOWS && NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && \
589 NACL_BUILD_SUBARCH == 64)
590 NaClPatchWindowsExceptionDispatcher();
592 NaClSignalTestCrashOnStartup();
595 * Open both files first because (on Mac OS X at least)
596 * NaClAppLoadFile() enables an outer sandbox.
598 if (NULL != blob_library_file) {
599 NaClFileNameForValgrind(blob_library_file);
600 blob_file = (struct NaClDesc *) NaClDescIoDescOpen(blob_library_file,
601 NACL_ABI_O_RDONLY, 0);
602 if (NULL == blob_file) {
604 fprintf(stderr, "Cannot open \"%s\".\n", blob_library_file);
607 NaClPerfCounterMark(&time_all_main, "SnapshotBlob");
608 NaClPerfCounterIntervalLast(&time_all_main);
611 NaClAppInitialDescriptorHookup(nap);
613 if (!rpc_supplies_nexe) {
614 if (LOAD_OK == errcode) {
615 NaClLog(2, "Loading nacl file %s (non-RPC)\n", nacl_file);
616 errcode = NaClAppLoadFileFromFilename(nap, nacl_file);
617 if (LOAD_OK != errcode && !quiet) {
618 fprintf(stderr, "Error while loading \"%s\": %s\n",
620 NaClErrorString(errcode));
622 ("Using the wrong type of nexe (nacl-x86-32"
623 " on an x86-64 or vice versa)\n"
624 "or a corrupt nexe file may be"
625 " responsible for this error.\n"));
627 NaClPerfCounterMark(&time_all_main, "AppLoadEnd");
628 NaClPerfCounterIntervalLast(&time_all_main);
631 if (fuzzing_quit_after_load) {
637 * Execute additional I/O redirections. NB: since the NaClApp
638 * takes ownership of host / IMC socket descriptors, all but
639 * the first run will not get access if the NaClApp closes
640 * them. Currently a normal NaClApp process exit does not
641 * close descriptors, since the underlying host OS will do so
642 * as part of service runtime exit.
644 NaClLog(4, "Processing I/O redirection/inheritance from command line\n");
645 for (entry = redir_queue; NULL != entry; entry = entry->next) {
646 switch (entry->tag) {
648 NaClAddHostDescriptor(nap, entry->u.host.d,
649 entry->u.host.mode, entry->nacl_desc);
652 NaClAddImcHandle(nap, entry->u.handle, entry->nacl_desc);
658 * If export_addr_to is set to a non-negative integer, we create a
659 * bound socket and socket address pair and bind the former to
660 * descriptor NACL_SERVICE_PORT_DESCRIPTOR (3 [see sel_ldr.h]) and
661 * the latter to descriptor NACL_SERVICE_ADDRESS_DESCRIPTOR (4).
662 * The socket address is sent to the export_addr_to descriptor.
664 * The service runtime also accepts a connection on the bound socket
665 * and spawns a secure command channel thread to service it.
667 if (0 <= export_addr_to) {
668 NaClCreateServiceSocket(nap);
670 * LOG_FATAL errors that occur before NaClSetUpBootstrapChannel will
671 * not be reported via the crash log mechanism (for Chromium
672 * embedding of NaCl, shown in the JavaScript console).
674 * Some errors, such as due to NaClRunSelQualificationTests, do not
675 * trigger a LOG_FATAL but instead set module_load_status to be sent
676 * in the start_module RPC reply. Log messages associated with such
677 * errors would be seen, since NaClSetUpBootstrapChannel will get
680 NaClSetUpBootstrapChannel(nap, (NaClHandle) export_addr_to);
682 * NB: spawns a thread that uses the command channel. we do
683 * this after NaClAppLoadFile so that NaClApp object is more
684 * fully populated. Hereafter any changes to nap should be done
685 * while holding locks.
687 NaClSecureCommandChannel(nap);
691 * May have created a thread, so need to synchronize uses of nap
692 * contents henceforth.
695 if (rpc_supplies_nexe) {
696 errcode = NaClWaitForLoadModuleCommand(nap);
697 NaClPerfCounterMark(&time_all_main, "WaitForLoad");
698 NaClPerfCounterIntervalLast(&time_all_main);
701 if (LOAD_OK == errcode) {
703 gprintf((struct Gio *) &gout, "printing NaClApp details\n");
704 NaClAppPrintDetails(nap, (struct Gio *) &gout);
709 * Tell the debug stub to bind a TCP port before enabling the outer
710 * sandbox. This is only needed on Mac OS X since that is the only
711 * platform where we have an outer sandbox in standalone sel_ldr.
712 * In principle this call should work on all platforms, but Windows
713 * XP seems to have some problems when we do bind()/listen() on a
714 * separate thread from accept().
716 if (enable_debug_stub && NACL_OSX) {
717 if (!NaClDebugBindSocket()) {
723 * Enable the outer sandbox, if one is defined. Do this as soon as
726 * This must come after NaClWaitForLoadModuleCommand(), which waits
727 * for another thread to have called NaClAppLoadFile().
728 * NaClAppLoadFile() does not work inside the Mac outer sandbox in
729 * standalone sel_ldr when using a dynamic code area because it uses
730 * NaClCreateMemoryObject() which opens a file in /tmp.
732 * We cannot enable the sandbox if file access is enabled.
734 if (!NaClAclBypassChecks && g_enable_outer_sandbox_func != NULL) {
735 g_enable_outer_sandbox_func();
738 if (NULL != blob_library_file) {
739 if (nap->irt_loaded) {
740 NaClLog(LOG_INFO, "IRT loaded via command channel; ignoring -B irt\n");
741 } else if (LOAD_OK == errcode) {
742 NaClLog(2, "Loading blob file %s\n", blob_library_file);
743 errcode = NaClAppLoadFileDynamically(nap, blob_file,
745 if (LOAD_OK == errcode) {
748 fprintf(stderr, "Error while loading \"%s\": %s\n",
750 NaClErrorString(errcode));
752 NaClPerfCounterMark(&time_all_main, "BlobLoaded");
753 NaClPerfCounterIntervalLast(&time_all_main);
756 NaClDescUnref(blob_file);
758 gprintf((struct Gio *) &gout, "printing post-IRT NaClApp details\n");
759 NaClAppPrintDetails(nap, (struct Gio *) &gout);
764 * Print out a marker for scripts to use to mark the start of app
767 NaClLog(1, "NACL: Application output follows\n");
770 * Make sure all the file buffers are flushed before entering
771 * the application code.
773 fflush((FILE *) NULL);
775 if (NULL != nap->secure_service) {
776 NaClErrorCode start_result;
778 * wait for start_module RPC call on secure channel thread.
780 start_result = NaClWaitForStartModuleCommand(nap);
781 NaClPerfCounterMark(&time_all_main, "WaitedForStartModuleCommand");
782 NaClPerfCounterIntervalLast(&time_all_main);
783 if (LOAD_OK == errcode) {
784 errcode = start_result;
787 NaClAppStartModule(nap, NULL, NULL);
791 * error reporting done; can quit now if there was an error earlier.
793 if (LOAD_OK != errcode) {
795 "Not running app code since errcode is %s (%d)\n",
796 NaClErrorString(errcode),
801 if (!DynArraySet(&env_vars, env_vars.num_entries, NULL)) {
802 NaClLog(LOG_FATAL, "Adding env_vars NULL terminator failed\n");
805 NaClEnvCleanserCtor(&env_cleanser, 0);
806 if (!NaClEnvCleanserInit(&env_cleanser, envp,
807 (char const *const *)env_vars.ptr_array)) {
808 NaClLog(LOG_FATAL, "Failed to initialise env cleanser\n");
811 if (!NaClAppLaunchServiceThreads(nap)) {
812 fprintf(stderr, "Launch service threads failed\n");
815 if (enable_debug_stub) {
816 if (!NaClDebugInit(nap)) {
820 NACL_TEST_INJECTION(BeforeMainThreadLaunches, ());
821 if (!NaClCreateMainThread(nap,
824 NaClEnvCleanserEnvironment(&env_cleanser))) {
825 fprintf(stderr, "creating main thread failed\n");
829 NaClEnvCleanserDtor(&env_cleanser);
831 NaClPerfCounterMark(&time_all_main, "CreateMainThread");
832 NaClPerfCounterIntervalLast(&time_all_main);
833 DynArrayDtor(&env_vars);
835 ret_code = NaClWaitForMainThreadToExit(nap);
836 NaClPerfCounterMark(&time_all_main, "WaitForMainThread");
837 NaClPerfCounterIntervalLast(&time_all_main);
839 NaClPerfCounterMark(&time_all_main, "SelMainEnd");
840 NaClPerfCounterIntervalTotal(&time_all_main);
843 * exit_group or equiv kills any still running threads while module
844 * addr space is still valid. otherwise we'd have to kill threads
845 * before we clean up the address space.
853 gprintf((struct Gio *) &gout, "exiting -- printing NaClApp details\n");
854 NaClAppPrintDetails(nap, (struct Gio *) &gout);
856 printf("Dumping vmmap.\n"); fflush(stdout);
861 * If there is a secure command channel, we sent an RPC reply with
862 * the reason that the nexe was rejected. If we exit now, that
863 * reply may still be in-flight and the various channel closure (esp
864 * reverse channel) may be detected first. This would result in a
865 * crash being reported, rather than the error in the RPC reply.
866 * Instead, we wait for the hard-shutdown on the command channel.
868 if (LOAD_OK != errcode) {
869 NaClBlockIfCommandChannelExists(nap);
878 NaClSignalHandlerFini();
880 NaClAllModulesFini();
884 /* Unreachable, but having the return prevents a compiler error. */