blob: c3e06af50a9c647ba6ffdcf724ab67dc2564b6dd [file] [log] [blame]
// 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);
}