| // Copyright 2013 Google Inc. All Rights Reserved. |
| // |
| // The entry point of bionic's dynamic linker/loader on NaCl. |
| // |
| |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #include <irt_syscalls.h> |
| #include <private/at_sysinfo.h> |
| |
| unsigned __nacl_linker_args(int envc, char** envp, int argc, char** argv); |
| unsigned __nacl_linker_init(unsigned **elfdata); |
| void __init_irt_from_auxv(unsigned *auxv); |
| |
| |
| void _start(unsigned **info) { |
| int envc = (int)info[1]; |
| int argc = (int)info[2]; |
| char **argv = (char**)&info[3]; |
| char **envp = argv + argc + 1; |
| unsigned *auxv = (unsigned *)(envp + envc + 1); |
| int i, j; |
| unsigned entry; |
| int arg_off = 0; |
| unsigned **elfdata; |
| |
| /* Initialize IRT table. sel_ldr passes __nacl_irt_query by AT_SYSINFO */ |
| __init_irt_from_auxv(auxv); |
| |
| |
| |
| /* As we will shift |argv| to remove the loader from it and we will |
| * fill some auxv entries, we cannot reuse |info| so we will |
| * allocate |elfdata| on stack of this function. |
| * |
| * Also note that we must share this data with both the loader and |
| * the main program because the loader passes the pointer to this |
| * region to the main program using TLS_SLOT_BIONIC_PREINIT. |
| * |
| * +--------------------------------------+ <- __nacl_linker_init and |
| * | fini | entry point of main |
| * +--------------------------------------+ program take this pointer. |
| * | envc | |
| * +--------------------------------------+ <- __linker_init takes this. |
| * | argc | |
| * +--------------------------------------+ |
| * . | Contains loader arguments |
| * . loader_argv | <- and main program args. |
| * . main_argv | We need to remove the |
| * | | linker args after processing |
| * | | them. |
| * | | |
| * +--------------------------------------+ |
| * | NULL | |
| * +--------------------------------------+ |
| * | | |
| * . | |
| * . envp | |
| * . | |
| * | | |
| * +--------------------------------------+ |
| * | NULL | ^ |
| * +--------------------------------------+ | Fields before here are |
| * | AT_SYSINFO auxv (12 elements) | +-- filled by this function. |
| * | __nacl_irt_query | |
| * | AT_BASE | +-- __nacl_linker_init will |
| * | 0x20000 (base address of the loader) | | fill auxv after AT_BASE. |
| * | AT_PHDR | v |
| * | Program header for main program | |
| * | AT_PHNUM | |
| * | # of program headers in main program | |
| * | AT_ENTRY | |
| * | Entry address of main program | |
| * | AT_NULL | |
| * | NULL | |
| * +--------------------------------------+ |
| */ |
| |
| int args_used = __nacl_linker_args(envc, envp, argc, argv); |
| |
| /* For chrome, we normally don't get a command-line, so add main.nexe |
| * as the default to load. */ |
| if (argc <= args_used) { |
| argv[0] = "main.nexe"; |
| args_used--; |
| } |
| |
| /* 3 for fini, envc, and argc. The number of argv will be decreased |
| * by the number of args consumed by the loader, and plus one for both |
| * argv and envp for their NULL terminators. We will have 12 |
| * elements in auxv. */ |
| elfdata = (unsigned **)alloca((3 + (argc - args_used) + 1 + envc + 1 + 12) * |
| sizeof(unsigned *)); |
| j = 0; |
| elfdata[j++] = (unsigned *)info[0]; |
| elfdata[j++] = (unsigned *)envc; |
| |
| elfdata[j++] = (unsigned *)(argc - args_used); |
| for (i = args_used; i < argc; i++) |
| elfdata[j++] = (unsigned *)argv[i]; |
| |
| elfdata[j++] = NULL; |
| for (i = 0; i < envc; i++) |
| elfdata[j++] = (unsigned *)envp[i]; |
| elfdata[j++] = NULL; |
| elfdata[j++] = (unsigned *)AT_SYSINFO; |
| elfdata[j++] = (unsigned *) _IRT_SYSCALL_PTR; |
| /* This field will be updated in __nacl_linker_init in |
| * bionic/linker/linker.cpp. */ |
| elfdata[j++] = (unsigned *)AT_NULL; |
| elfdata[j] = NULL; |
| |
| entry = __nacl_linker_init(&elfdata[0]); |
| if (!elfdata[j]) { |
| static const char kErrorMsg[] = "__nacl_linker_init did not update auxv"; |
| static const int kStderrFd = 2; |
| write(kStderrFd, kErrorMsg, sizeof(kErrorMsg) - 1); |
| exit(1); |
| } |
| |
| ((void (*)(unsigned **))entry)(elfdata); |
| } |