| Index: chrome/nacl/nacl_helper_bootstrap_linux.c
|
| diff --git a/chrome/nacl/nacl_helper_bootstrap_linux.c b/chrome/nacl/nacl_helper_bootstrap_linux.c
|
| index c534c395d94f8b01f01d05187ac911b355b617e8..68d004c33596c28e01a480b6e69a7f7265479959 100644
|
| --- a/chrome/nacl/nacl_helper_bootstrap_linux.c
|
| +++ b/chrome/nacl/nacl_helper_bootstrap_linux.c
|
| @@ -19,6 +19,7 @@
|
|
|
| #include <elf.h>
|
| #include <fcntl.h>
|
| +#include <limits.h>
|
| #include <link.h>
|
| #include <stddef.h>
|
| #include <stdint.h>
|
| @@ -26,16 +27,6 @@
|
|
|
| #define MAX_PHNUM 12
|
|
|
| -#if defined(__i386__)
|
| -# define DYNAMIC_LINKER "/lib/ld-linux.so.2"
|
| -#elif defined(__x86_64__)
|
| -# define DYNAMIC_LINKER "/lib64/ld-linux-x86-64.so.2"
|
| -#elif defined(__ARM_EABI__)
|
| -# define DYNAMIC_LINKER "/lib/ld-linux.so.3"
|
| -#else
|
| -# error "Don't know the dynamic linker file name for this architecture!"
|
| -#endif
|
| -
|
|
|
| /*
|
| * We're not using <string.h> functions here, to avoid dependencies.
|
| @@ -85,14 +76,15 @@ static void iov_int_string(int value, struct kernel_iovec *iov,
|
| #define STRING_IOV(string_constant, cond) \
|
| { (void *) string_constant, cond ? (sizeof(string_constant) - 1) : 0 }
|
|
|
| -__attribute__((noreturn)) static void fail(const char *message,
|
| +__attribute__((noreturn)) static void fail(const char *filename,
|
| + const char *message,
|
| const char *item1, int value1,
|
| const char *item2, int value2) {
|
| char valbuf1[32];
|
| char valbuf2[32];
|
| struct kernel_iovec iov[] = {
|
| - STRING_IOV("bootstrap_helper", 1),
|
| - STRING_IOV(DYNAMIC_LINKER, 1),
|
| + STRING_IOV("bootstrap_helper: ", 1),
|
| + { (void *) filename, my_strlen(filename) },
|
| STRING_IOV(": ", 1),
|
| { (void *) message, my_strlen(message) },
|
| { (void *) item1, item1 == NULL ? 0 : my_strlen(item1) },
|
| @@ -120,20 +112,21 @@ __attribute__((noreturn)) static void fail(const char *message,
|
| static int my_open(const char *file, int oflag) {
|
| int result = sys_open(file, oflag, 0);
|
| if (result < 0)
|
| - fail("Cannot open dynamic linker! ", "errno", my_errno, NULL, 0);
|
| + fail(file, "Cannot open ELF file! ", "errno", my_errno, NULL, 0);
|
| return result;
|
| }
|
|
|
| -static void my_pread(const char *fail_message,
|
| +static void my_pread(const char *file, const char *fail_message,
|
| int fd, void *buf, size_t bufsz, uintptr_t pos) {
|
| ssize_t result = sys_pread64(fd, buf, bufsz, pos);
|
| if (result < 0)
|
| - fail(fail_message, "errno", my_errno, NULL, 0);
|
| + fail(file, fail_message, "errno", my_errno, NULL, 0);
|
| if ((size_t) result != bufsz)
|
| - fail(fail_message, "read count", result, NULL, 0);
|
| + fail(file, fail_message, "read count", result, NULL, 0);
|
| }
|
|
|
| -static uintptr_t my_mmap(const char *segment_type, unsigned int segnum,
|
| +static uintptr_t my_mmap(const char *file,
|
| + const char *segment_type, unsigned int segnum,
|
| uintptr_t address, size_t size,
|
| int prot, int flags, int fd, uintptr_t pos) {
|
| #if defined(__NR_mmap2)
|
| @@ -142,15 +135,15 @@ static uintptr_t my_mmap(const char *segment_type, unsigned int segnum,
|
| void *result = sys_mmap((void *) address, size, prot, flags, fd, pos);
|
| #endif
|
| if (result == MAP_FAILED)
|
| - fail("Failed to map from dynamic linker! ",
|
| + fail(file, "Failed to map segment! ",
|
| segment_type, segnum, "errno", my_errno);
|
| return (uintptr_t) result;
|
| }
|
|
|
| -static void my_mprotect(unsigned int segnum,
|
| +static void my_mprotect(const char *file, unsigned int segnum,
|
| uintptr_t address, size_t size, int prot) {
|
| if (sys_mprotect((void *) address, size, prot) < 0)
|
| - fail("Failed to mprotect hole in dynamic linker! ",
|
| + fail(file, "Failed to mprotect segment hole! ",
|
| "segment", segnum, "errno", my_errno);
|
| }
|
|
|
| @@ -180,7 +173,8 @@ static uintptr_t round_down(uintptr_t value, uintptr_t size) {
|
| * whole pages in this region, we over-map anonymous pages. For the
|
| * sub-page remainder, we zero-fill bytes directly.
|
| */
|
| -static void handle_bss(unsigned int segnum, const ElfW(Phdr) *ph,
|
| +static void handle_bss(const char *file,
|
| + unsigned int segnum, const ElfW(Phdr) *ph,
|
| ElfW(Addr) load_bias, size_t pagesize) {
|
| if (ph->p_memsz > ph->p_filesz) {
|
| ElfW(Addr) file_end = ph->p_vaddr + load_bias + ph->p_filesz;
|
| @@ -188,7 +182,7 @@ static void handle_bss(unsigned int segnum, const ElfW(Phdr) *ph,
|
| ElfW(Addr) page_end = round_up(ph->p_vaddr + load_bias +
|
| ph->p_memsz, pagesize);
|
| if (page_end > file_page_end)
|
| - my_mmap("bss segment", segnum,
|
| + my_mmap(file, "bss segment", segnum,
|
| file_page_end, page_end - file_page_end,
|
| prot_from_phdr(ph), MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
| if (file_page_end > file_end && (ph->p_flags & PF_W))
|
| @@ -197,52 +191,17 @@ static void handle_bss(unsigned int segnum, const ElfW(Phdr) *ph,
|
| }
|
|
|
| /*
|
| - * This is the main loading code. It's called with the address of the
|
| - * auxiliary vector on the stack, which we need to examine and modify.
|
| - * It returns the dynamic linker's runtime entry point address, where
|
| - * we should jump to. This is called by the machine-dependent _start
|
| - * code (below). On return, it restores the original stack pointer
|
| - * and jumps to this entry point.
|
| + * Open an ELF file and load it into memory.
|
| */
|
| -ElfW(Addr) do_load(ElfW(auxv_t) *auxv) {
|
| - /*
|
| - * Record the auxv entries that are specific to the file loaded.
|
| - * The incoming entries point to our own static executable.
|
| - */
|
| - ElfW(auxv_t) *av_entry = NULL;
|
| - ElfW(auxv_t) *av_phdr = NULL;
|
| - ElfW(auxv_t) *av_phnum = NULL;
|
| - size_t pagesize = 0;
|
| -
|
| - ElfW(auxv_t) *av;
|
| - for (av = auxv;
|
| - av_entry == NULL || av_phdr == NULL || av_phnum == NULL || pagesize == 0;
|
| - ++av) {
|
| - switch (av->a_type) {
|
| - case AT_NULL:
|
| - fail("Failed to find AT_ENTRY, AT_PHDR, AT_PHNUM, or AT_PAGESZ!",
|
| - NULL, 0, NULL, 0);
|
| - /*NOTREACHED*/
|
| - break;
|
| - case AT_ENTRY:
|
| - av_entry = av;
|
| - break;
|
| - case AT_PAGESZ:
|
| - pagesize = av->a_un.a_val;
|
| - break;
|
| - case AT_PHDR:
|
| - av_phdr = av;
|
| - break;
|
| - case AT_PHNUM:
|
| - av_phnum = av;
|
| - break;
|
| - }
|
| - }
|
| -
|
| - int fd = my_open(DYNAMIC_LINKER, O_RDONLY);
|
| +static ElfW(Addr) load_elf_file(const char *filename,
|
| + size_t pagesize,
|
| + ElfW(Addr) *out_phdr,
|
| + ElfW(Addr) *out_phnum,
|
| + const char **out_interp) {
|
| + int fd = my_open(filename, O_RDONLY);
|
|
|
| ElfW(Ehdr) ehdr;
|
| - my_pread("Failed to read ELF header from dynamic linker! ",
|
| + my_pread(filename, "Failed to read ELF header from file! ",
|
| fd, &ehdr, sizeof(ehdr), 0);
|
|
|
| if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
|
| @@ -252,7 +211,7 @@ ElfW(Addr) do_load(ElfW(auxv_t) *auxv) {
|
| ehdr.e_version != EV_CURRENT ||
|
| ehdr.e_ehsize != sizeof(ehdr) ||
|
| ehdr.e_phentsize != sizeof(ElfW(Phdr)))
|
| - fail("Dynamic linker has no valid ELF header!", NULL, 0, NULL, 0);
|
| + fail(filename, "File has no valid ELF header!", NULL, 0, NULL, 0);
|
|
|
| switch (ehdr.e_machine) {
|
| #if defined(__i386__)
|
| @@ -266,28 +225,28 @@ ElfW(Addr) do_load(ElfW(auxv_t) *auxv) {
|
| #endif
|
| break;
|
| default:
|
| - fail("Dynamic linker has wrong architecture! ",
|
| + fail(filename, "ELF file has wrong architecture! ",
|
| "e_machine", ehdr.e_machine, NULL, 0);
|
| break;
|
| }
|
|
|
| ElfW(Phdr) phdr[MAX_PHNUM];
|
| if (ehdr.e_phnum > sizeof(phdr) / sizeof(phdr[0]) || ehdr.e_phnum < 1)
|
| - fail("Dynamic linker has unreasonable ",
|
| + fail(filename, "ELF file has unreasonable ",
|
| "e_phnum", ehdr.e_phnum, NULL, 0);
|
|
|
| if (ehdr.e_type != ET_DYN)
|
| - fail("Dynamic linker not ET_DYN! ",
|
| + fail(filename, "ELF file not ET_DYN! ",
|
| "e_type", ehdr.e_type, NULL, 0);
|
|
|
| - my_pread("Failed to read program headers from dynamic linker! ",
|
| + my_pread(filename, "Failed to read program headers from ELF file! ",
|
| fd, phdr, sizeof(phdr[0]) * ehdr.e_phnum, ehdr.e_phoff);
|
|
|
| size_t i = 0;
|
| while (i < ehdr.e_phnum && phdr[i].p_type != PT_LOAD)
|
| ++i;
|
| if (i == ehdr.e_phnum)
|
| - fail("Dynamic linker has no PT_LOAD header!",
|
| + fail(filename, "ELF file has no PT_LOAD header!",
|
| NULL, 0, NULL, 0);
|
|
|
| /*
|
| @@ -305,7 +264,7 @@ ElfW(Addr) do_load(ElfW(auxv_t) *auxv) {
|
| * Map the first segment and reserve the space used for the rest and
|
| * for holes between segments.
|
| */
|
| - const uintptr_t mapping = my_mmap("segment", first_load - phdr,
|
| + const uintptr_t mapping = my_mmap(filename, "segment", first_load - phdr,
|
| round_down(first_load->p_vaddr, pagesize),
|
| span, prot_from_phdr(first_load),
|
| MAP_PRIVATE, fd,
|
| @@ -316,16 +275,10 @@ ElfW(Addr) do_load(ElfW(auxv_t) *auxv) {
|
|
|
| if (first_load->p_offset > ehdr.e_phoff ||
|
| first_load->p_filesz < ehdr.e_phoff + (ehdr.e_phnum * sizeof(ElfW(Phdr))))
|
| - fail("First load segment of dynamic linker does not contain phdrs!",
|
| + fail(filename, "First load segment of ELF file does not contain phdrs!",
|
| NULL, 0, NULL, 0);
|
|
|
| - /* Point the auxv elements at the dynamic linker's phdrs and entry. */
|
| - av_phdr->a_un.a_val = (ehdr.e_phoff - first_load->p_offset +
|
| - first_load->p_vaddr + load_bias);
|
| - av_phnum->a_un.a_val = ehdr.e_phnum;
|
| - av_entry->a_un.a_val = ehdr.e_entry + load_bias;
|
| -
|
| - handle_bss(first_load - phdr, first_load, load_bias, pagesize);
|
| + handle_bss(filename, first_load - phdr, first_load, load_bias, pagesize);
|
|
|
| ElfW(Addr) last_end = first_load->p_vaddr + load_bias + first_load->p_memsz;
|
|
|
| @@ -342,68 +295,205 @@ ElfW(Addr) do_load(ElfW(auxv_t) *auxv) {
|
| ElfW(Addr) end = round_up(last_end, pagesize);
|
|
|
| if (start > last_page_end)
|
| - my_mprotect(ph - phdr, last_page_end, start - last_page_end, PROT_NONE);
|
| + my_mprotect(filename,
|
| + ph - phdr, last_page_end, start - last_page_end, PROT_NONE);
|
|
|
| - my_mmap("segment", ph - phdr,
|
| + my_mmap(filename, "segment", ph - phdr,
|
| start, end - start,
|
| prot_from_phdr(ph), MAP_PRIVATE | MAP_FIXED, fd,
|
| round_down(ph->p_offset, pagesize));
|
|
|
| - handle_bss(ph - phdr, ph, load_bias, pagesize);
|
| + handle_bss(filename, ph - phdr, ph, load_bias, pagesize);
|
| + }
|
| + }
|
| +
|
| + if (out_interp != NULL) {
|
| + /*
|
| + * Find the PT_INTERP header, if there is one.
|
| + */
|
| + for (i = 0; i < ehdr.e_phnum; ++i) {
|
| + if (phdr[i].p_type == PT_INTERP) {
|
| + /*
|
| + * The PT_INTERP isn't really required to sit inside the first
|
| + * (or any) load segment, though it normally does. So we can
|
| + * easily avoid an extra read in that case.
|
| + */
|
| + if (phdr[i].p_offset >= first_load->p_offset &&
|
| + phdr[i].p_filesz <= first_load->p_filesz) {
|
| + *out_interp = (const char *) (phdr[i].p_vaddr + load_bias);
|
| + } else {
|
| + static char interp_buffer[PATH_MAX + 1];
|
| + if (phdr[i].p_filesz >= sizeof(interp_buffer)) {
|
| + fail(filename, "ELF file has unreasonable PT_INTERP size! ",
|
| + "segment", i, "p_filesz", phdr[i].p_filesz);
|
| + }
|
| + my_pread(filename, "Cannot read PT_INTERP segment contents!",
|
| + fd, interp_buffer, phdr[i].p_filesz, phdr[i].p_offset);
|
| + *out_interp = interp_buffer;
|
| + }
|
| + break;
|
| + }
|
| }
|
| }
|
|
|
| sys_close(fd);
|
|
|
| + if (out_phdr != NULL)
|
| + *out_phdr = (ehdr.e_phoff - first_load->p_offset +
|
| + first_load->p_vaddr + load_bias);
|
| + if (out_phnum != NULL)
|
| + *out_phnum = ehdr.e_phnum;
|
| +
|
| return ehdr.e_entry + load_bias;
|
| }
|
|
|
| /*
|
| - * We have to define the actual entry point code (_start) in assembly
|
| - * for each machine. The kernel startup protocol is not compatible
|
| - * with the normal C function calling convention. Here, we calculate
|
| - * the address of the auxiliary vector on the stack; call do_load
|
| - * (above) using the normal C convention as per the ABI; restore the
|
| - * original starting stack; and finally, jump to the dynamic linker's
|
| - * entry point address.
|
| + * This is the main loading code. It's called with the starting stack pointer.
|
| + * This points to a sequence of pointer-size words:
|
| + * [0] argc
|
| + * [1..argc] argv[0..argc-1]
|
| + * [1+argc] NULL
|
| + * [2+argc..] envp[0..]
|
| + * NULL
|
| + * auxv[0].a_type
|
| + * auxv[1].a_un.a_val
|
| + * ...
|
| + * It returns the dynamic linker's runtime entry point address, where
|
| + * we should jump to. This is called by the machine-dependent _start
|
| + * code (below). On return, it restores the original stack pointer
|
| + * and jumps to this entry point.
|
| + *
|
| + * argv[0] is the uninteresting name of this bootstrap program. argv[1] is
|
| + * the real program file name we'll open, and also the argv[0] for that
|
| + * program. We need to modify argc, move argv[1..] back to the argv[0..]
|
| + * position, and also examine and modify the auxiliary vector on the stack.
|
| + */
|
| +ElfW(Addr) do_load(uintptr_t *stack) {
|
| + size_t i;
|
| +
|
| + /*
|
| + * First find the end of the auxiliary vector.
|
| + */
|
| + int argc = stack[0];
|
| + char **argv = (char **) &stack[1];
|
| + const char *program = argv[1];
|
| + char **envp = &argv[argc + 1];
|
| + char **ep = envp;
|
| + while (*ep != NULL)
|
| + ++ep;
|
| + ElfW(auxv_t) *auxv = (ElfW(auxv_t) *) (ep + 1);
|
| + ElfW(auxv_t) *av = auxv;
|
| + while (av->a_type != AT_NULL)
|
| + ++av;
|
| + size_t stack_words = (uintptr_t *) (av + 1) - &stack[1];
|
| +
|
| + if (argc < 2)
|
| + fail("Usage", "PROGRAM ARGS...", NULL, 0, NULL, 0);
|
| +
|
| + /*
|
| + * Now move everything back to eat our original argv[0]. When we've done
|
| + * that, envp and auxv will start one word back from where they were.
|
| + */
|
| + --argc;
|
| + --envp;
|
| + auxv = (ElfW(auxv_t) *) ep;
|
| + stack[0] = argc;
|
| + for (i = 1; i < stack_words; ++i)
|
| + stack[i] = stack[i + 1];
|
| +
|
| + /*
|
| + * Record the auxv entries that are specific to the file loaded.
|
| + * The incoming entries point to our own static executable.
|
| + */
|
| + ElfW(auxv_t) *av_entry = NULL;
|
| + ElfW(auxv_t) *av_phdr = NULL;
|
| + ElfW(auxv_t) *av_phnum = NULL;
|
| + size_t pagesize = 0;
|
| +
|
| + for (av = auxv;
|
| + av_entry == NULL || av_phdr == NULL || av_phnum == NULL || pagesize == 0;
|
| + ++av) {
|
| + switch (av->a_type) {
|
| + case AT_NULL:
|
| + fail("startup",
|
| + "Failed to find AT_ENTRY, AT_PHDR, AT_PHNUM, or AT_PAGESZ!",
|
| + NULL, 0, NULL, 0);
|
| + /*NOTREACHED*/
|
| + break;
|
| + case AT_ENTRY:
|
| + av_entry = av;
|
| + break;
|
| + case AT_PAGESZ:
|
| + pagesize = av->a_un.a_val;
|
| + break;
|
| + case AT_PHDR:
|
| + av_phdr = av;
|
| + break;
|
| + case AT_PHNUM:
|
| + av_phnum = av;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + /* Load the program and point the auxv elements at its phdrs and entry. */
|
| + const char *interp = NULL;
|
| + av_entry->a_un.a_val = load_elf_file(program,
|
| + pagesize,
|
| + &av_phdr->a_un.a_val,
|
| + &av_phnum->a_un.a_val,
|
| + &interp);
|
| +
|
| + ElfW(Addr) entry = av_entry->a_un.a_val;
|
| +
|
| + if (interp != NULL) {
|
| + /*
|
| + * There was a PT_INTERP, so we have a dynamic linker to load.
|
| + */
|
| + entry = load_elf_file(interp, pagesize, NULL, NULL, NULL);
|
| + }
|
| +
|
| + return entry;
|
| +}
|
| +
|
| +/*
|
| + * We have to define the actual entry point code (_start) in assembly for
|
| + * each machine. The kernel startup protocol is not compatible with the
|
| + * normal C function calling convention. Here, we call do_load (above)
|
| + * using the normal C convention as per the ABI, with the starting stack
|
| + * pointer as its argument; restore the original starting stack; and
|
| + * finally, jump to the dynamic linker's entry point address.
|
| */
|
| #if defined(__i386__)
|
| -asm(".globl _start\n"
|
| +asm(".pushsection \".text\",\"ax\",@progbits\n"
|
| + ".globl _start\n"
|
| ".type _start,@function\n"
|
| "_start:\n"
|
| "xorl %ebp, %ebp\n"
|
| "movl %esp, %ebx\n" /* Save starting SP in %ebx. */
|
| "andl $-16, %esp\n" /* Align the stack as per ABI. */
|
| - "movl (%ebx), %eax\n" /* argc */
|
| - "leal 8(%ebx,%eax,4), %ecx\n" /* envp */
|
| - /* Find the envp element that is NULL, and auxv is past there. */
|
| - "0: addl $4, %ecx\n"
|
| - "cmpl $0, -4(%ecx)\n"
|
| - "jne 0b\n"
|
| - "pushl %ecx\n" /* Argument: auxv. */
|
| + "pushl %ebx\n" /* Argument: stack block. */
|
| "call do_load\n"
|
| "movl %ebx, %esp\n" /* Restore the saved SP. */
|
| "jmp *%eax\n" /* Jump to the entry point. */
|
| + ".popsection"
|
| );
|
| #elif defined(__x86_64__)
|
| -asm(".globl _start\n"
|
| +asm(".pushsection \".text\",\"ax\",@progbits\n"
|
| + ".globl _start\n"
|
| ".type _start,@function\n"
|
| "_start:\n"
|
| "xorq %rbp, %rbp\n"
|
| "movq %rsp, %rbx\n" /* Save starting SP in %rbx. */
|
| "andq $-16, %rsp\n" /* Align the stack as per ABI. */
|
| - "movq (%rbx), %rax\n" /* argc */
|
| - "leaq 16(%rbx,%rax,8), %rdi\n" /* envp */
|
| - /* Find the envp element that is NULL, and auxv is past there. */
|
| - "0: addq $8, %rdi\n"
|
| - "cmpq $0, -8(%rdi)\n"
|
| - "jne 0b\n"
|
| - "call do_load\n" /* Argument already in %rdi: auxv */
|
| + "movq %rbx, %rdi\n" /* Argument: stack block. */
|
| + "call do_load\n"
|
| "movq %rbx, %rsp\n" /* Restore the saved SP. */
|
| "jmp *%rax\n" /* Jump to the entry point. */
|
| + ".popsection"
|
| );
|
| #elif defined(__arm__)
|
| -asm(".globl _start\n"
|
| +asm(".pushsection \".text\",\"ax\",%progbits\n"
|
| + ".globl _start\n"
|
| ".type _start,#function\n"
|
| "_start:\n"
|
| #if defined(__thumb2__)
|
| @@ -413,16 +503,11 @@ asm(".globl _start\n"
|
| "mov fp, #0\n"
|
| "mov lr, #0\n"
|
| "mov r4, sp\n" /* Save starting SP in r4. */
|
| - "ldr r1, [r4]\n" /* argc */
|
| - "add r1, r1, #2\n"
|
| - "add r0, r4, r1, asl #2\n" /* envp */
|
| - /* Find the envp element that is NULL, and auxv is past there. */
|
| - "0: ldr r1, [r0], #4\n"
|
| - "cmp r1, #0\n"
|
| - "bne 0b\n"
|
| + "mov r0, sp\n" /* Argument: stack block. */
|
| "bl do_load\n"
|
| "mov sp, r4\n" /* Restore the saved SP. */
|
| "blx r0\n" /* Jump to the entry point. */
|
| + ".popsection"
|
| );
|
| #else
|
| # error "Need stack-preserving _start code for this architecture!"
|
|
|