diff options
author | mcgrathr@chromium.org <mcgrathr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-06 00:40:24 +0000 |
---|---|---|
committer | mcgrathr@chromium.org <mcgrathr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-06 00:40:24 +0000 |
commit | d67ee551133a8b07fdb0ae33fd417e9c082e1074 (patch) | |
tree | 0913e9dc592d3b3c9a5213b62d2361121a545a18 /chrome/nacl | |
parent | bcc42fcbe0a522b6452eeda264c480dd7086d908 (diff) | |
download | chromium_src-d67ee551133a8b07fdb0ae33fd417e9c082e1074.zip chromium_src-d67ee551133a8b07fdb0ae33fd417e9c082e1074.tar.gz chromium_src-d67ee551133a8b07fdb0ae33fd417e9c082e1074.tar.bz2 |
Revert 113074 - Use nacl_helper_bootstrap from native_client repository
These sources have been moved over to the native_client repository.
Remove them from chromium/src altogether and just make the gyp files
refer to the native_client stuff.
BUG= none
TEST= linux still builds
R=sehr@google.com,noelallen@chromium.org
Review URL: http://codereview.chromium.org/8799016
TBR=mcgrathr@chromium.org
Review URL: http://codereview.chromium.org/8811002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113078 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/nacl')
-rw-r--r-- | chrome/nacl/nacl_helper_bootstrap_linux.c | 575 | ||||
-rw-r--r-- | chrome/nacl/nacl_helper_bootstrap_linux.x | 133 | ||||
-rw-r--r-- | chrome/nacl/nacl_helper_bootstrap_munge_phdr.c | 66 | ||||
-rwxr-xr-x | chrome/nacl/nacl_helper_bootstrap_munge_phdr.py | 39 |
4 files changed, 813 insertions, 0 deletions
diff --git a/chrome/nacl/nacl_helper_bootstrap_linux.c b/chrome/nacl/nacl_helper_bootstrap_linux.c new file mode 100644 index 0000000..61e8a1f --- /dev/null +++ b/chrome/nacl/nacl_helper_bootstrap_linux.c @@ -0,0 +1,575 @@ +/* Copyright (c) 2011 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * This is a standalone program that loads and runs the dynamic linker. + * This program itself must be linked statically. To keep it small, it's + * written to avoid all dependencies on libc and standard startup code. + * Hence, this should be linked using -nostartfiles. It must be compiled + * with -fno-stack-protector to ensure the compiler won't emit code that + * presumes some special setup has been done. + * + * On ARM, the compiler will emit calls to some libc functions, so we + * cannot link with -nostdlib. The functions it does use (memset and + * __aeabi_* functions for integer division) are sufficiently small and + * self-contained in ARM's libc.a that we don't have any problem using + * the libc definitions though we aren't using the rest of libc or doing + * any of the setup it might expect. + */ + +#include <elf.h> +#include <fcntl.h> +#include <limits.h> +#include <link.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/mman.h> + +#define MAX_PHNUM 12 + +/* + * This exact magic argument string is recognized in check_r_debug_arg, below. + * Requiring the argument to have those Xs as a template both simplifies + * our argument matching code and saves us from having to reformat the + * whole stack to find space for a string longer than the original argument. + */ +#define R_DEBUG_TEMPLATE_PREFIX "--r_debug=0x" +#define R_DEBUG_TEMPLATE_DIGITS "XXXXXXXXXXXXXXXX" +static const char kRDebugTemplate[] = + R_DEBUG_TEMPLATE_PREFIX R_DEBUG_TEMPLATE_DIGITS; +static const size_t kRDebugPrefixLen = sizeof(R_DEBUG_TEMPLATE_PREFIX) - 1; + + +/* + * We're not using <string.h> functions here, to avoid dependencies. + * In the x86 libc, even "simple" functions like memset and strlen can + * depend on complex startup code, because in newer libc + * implementations they are defined using STT_GNU_IFUNC. + */ + +static void my_bzero(void *buf, size_t n) { + char *p = buf; + while (n-- > 0) + *p++ = 0; +} + +static size_t my_strlen(const char *s) { + size_t n = 0; + while (*s++ != '\0') + ++n; + return n; +} + +static int my_strcmp(const char *a, const char *b) { + while (*a == *b) { + if (*a == '\0') + return 0; + ++a; + ++b; + } + return (int) (unsigned char) *a - (int) (unsigned char) *b; +} + + +/* + * Get inline functions for system calls. + */ +static int my_errno; +#define SYS_ERRNO my_errno +#include "third_party/lss/linux_syscall_support.h" + + +/* + * We're avoiding libc, so no printf. The only nontrivial thing we need + * is rendering numbers, which is, in fact, pretty trivial. + */ +static void iov_int_string(int value, struct kernel_iovec *iov, + char *buf, size_t bufsz) { + char *p = &buf[bufsz]; + do { + --p; + *p = "0123456789"[value % 10]; + value /= 10; + } while (value != 0); + iov->iov_base = p; + iov->iov_len = &buf[bufsz] - p; +} + +#define STRING_IOV(string_constant, cond) \ + { (void *) string_constant, cond ? (sizeof(string_constant) - 1) : 0 } + +__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), + { (void *) filename, my_strlen(filename) }, + STRING_IOV(": ", 1), + { (void *) message, my_strlen(message) }, + { (void *) item1, item1 == NULL ? 0 : my_strlen(item1) }, + STRING_IOV("=", item1 != NULL), + {}, + STRING_IOV(", ", item1 != NULL && item2 != NULL), + { (void *) item2, item2 == NULL ? 0 : my_strlen(item2) }, + STRING_IOV("=", item2 != NULL), + {}, + { "\n", 1 }, + }; + const int niov = sizeof(iov) / sizeof(iov[0]); + + if (item1 != NULL) + iov_int_string(value1, &iov[6], valbuf1, sizeof(valbuf1)); + if (item2 != NULL) + iov_int_string(value1, &iov[10], valbuf2, sizeof(valbuf2)); + + sys_writev(2, iov, niov); + sys_exit_group(2); + while (1) *(volatile int *) 0 = 0; /* Crash. */ +} + + +static int my_open(const char *file, int oflag) { + int result = sys_open(file, oflag, 0); + if (result < 0) + fail(file, "Cannot open ELF file! ", "errno", my_errno, NULL, 0); + return result; +} + +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(file, fail_message, "errno", my_errno, NULL, 0); + if ((size_t) result != bufsz) + fail(file, fail_message, "read count", result, NULL, 0); +} + +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) + void *result = sys_mmap2((void *) address, size, prot, flags, fd, pos >> 12); +#else + void *result = sys_mmap((void *) address, size, prot, flags, fd, pos); +#endif + if (result == MAP_FAILED) + fail(file, "Failed to map segment! ", + segment_type, segnum, "errno", my_errno); + return (uintptr_t) result; +} + +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(file, "Failed to mprotect segment hole! ", + "segment", segnum, "errno", my_errno); +} + + +static int prot_from_phdr(const ElfW(Phdr) *phdr) { + int prot = 0; + if (phdr->p_flags & PF_R) + prot |= PROT_READ; + if (phdr->p_flags & PF_W) + prot |= PROT_WRITE; + if (phdr->p_flags & PF_X) + prot |= PROT_EXEC; + return prot; +} + +static uintptr_t round_up(uintptr_t value, uintptr_t size) { + return (value + size - 1) & -size; +} + +static uintptr_t round_down(uintptr_t value, uintptr_t size) { + return value & -size; +} + +/* + * Handle the "bss" portion of a segment, where the memory size + * exceeds the file size and we zero-fill the difference. For any + * whole pages in this region, we over-map anonymous pages. For the + * sub-page remainder, we zero-fill bytes directly. + */ +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; + ElfW(Addr) file_page_end = round_up(file_end, pagesize); + ElfW(Addr) page_end = round_up(ph->p_vaddr + load_bias + + ph->p_memsz, pagesize); + if (page_end > file_page_end) + 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)) + my_bzero((void *) file_end, file_page_end - file_end); + } +} + +/* + * Open an ELF file and load it into memory. + */ +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(filename, "Failed to read ELF header from file! ", + fd, &ehdr, sizeof(ehdr), 0); + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || + ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || + ehdr.e_ident[EI_MAG3] != ELFMAG3 || + ehdr.e_version != EV_CURRENT || + ehdr.e_ehsize != sizeof(ehdr) || + ehdr.e_phentsize != sizeof(ElfW(Phdr))) + fail(filename, "File has no valid ELF header!", NULL, 0, NULL, 0); + + switch (ehdr.e_machine) { +#if defined(__i386__) + case EM_386: +#elif defined(__x86_64__) + case EM_X86_64: +#elif defined(__arm__) + case EM_ARM: +#else +# error "Don't know the e_machine value for this architecture!" +#endif + break; + default: + 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(filename, "ELF file has unreasonable ", + "e_phnum", ehdr.e_phnum, NULL, 0); + + if (ehdr.e_type != ET_DYN) + fail(filename, "ELF file not ET_DYN! ", + "e_type", ehdr.e_type, NULL, 0); + + 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(filename, "ELF file has no PT_LOAD header!", + NULL, 0, NULL, 0); + + /* + * ELF requires that PT_LOAD segments be in ascending order of p_vaddr. + * Find the last one to calculate the whole address span of the image. + */ + const ElfW(Phdr) *first_load = &phdr[i]; + const ElfW(Phdr) *last_load = &phdr[ehdr.e_phnum - 1]; + while (last_load > first_load && last_load->p_type != PT_LOAD) + --last_load; + + size_t span = last_load->p_vaddr + last_load->p_memsz - first_load->p_vaddr; + + /* + * Map the first segment and reserve the space used for the rest and + * for holes between segments. + */ + 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, + round_down(first_load->p_offset, pagesize)); + + const ElfW(Addr) load_bias = mapping - round_down(first_load->p_vaddr, + pagesize); + + if (first_load->p_offset > ehdr.e_phoff || + first_load->p_filesz < ehdr.e_phoff + (ehdr.e_phnum * sizeof(ElfW(Phdr)))) + fail(filename, "First load segment of ELF file does not contain phdrs!", + NULL, 0, NULL, 0); + + 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; + + /* + * Map the remaining segments, and protect any holes between them. + */ + const ElfW(Phdr) *ph; + for (ph = first_load + 1; ph <= last_load; ++ph) { + if (ph->p_type == PT_LOAD) { + ElfW(Addr) last_page_end = round_up(last_end, pagesize); + + last_end = ph->p_vaddr + load_bias + ph->p_memsz; + ElfW(Addr) start = round_down(ph->p_vaddr + load_bias, pagesize); + ElfW(Addr) end = round_up(last_end, pagesize); + + if (start > last_page_end) + my_mprotect(filename, + ph - phdr, last_page_end, start - last_page_end, PROT_NONE); + + 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(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; +} + + +/* + * GDB looks for this symbol name when it cannot find PT_DYNAMIC->DT_DEBUG. + * We don't have a PT_DYNAMIC, so it will find this. Now all we have to do + * is arrange for this space to be filled in with the dynamic linker's + * _r_debug contents after they're initialized. That way, attaching GDB to + * this process or examining its core file will find the PIE we loaded, the + * dynamic linker, and all the shared libraries, making debugging pleasant. + */ +struct r_debug _r_debug __attribute__((nocommon, section(".r_debug"))); + +/* + * If the argument matches the kRDebugTemplate string, then replace + * the 16 Xs with the hexadecimal address of our _r_debug variable. + */ +static int check_r_debug_arg(char *arg) { + if (my_strcmp(arg, kRDebugTemplate) == 0) { + uintptr_t addr = (uintptr_t) &_r_debug; + size_t i = 16; + while (i-- > 0) { + arg[kRDebugPrefixLen + i] = "0123456789abcdef"[addr & 0xf]; + addr >>= 4; + } + return 1; + } + return 0; +} + + +/* + * 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]; + + /* + * If one of our arguments is the kRDebugTemplate string, then + * we'll modify that argument string in place to specify the + * address of our _r_debug structure. + */ + for (i = 1; i < argc; ++i) { + if (check_r_debug_arg(argv[i])) + break; + } + + /* + * 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(".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. */ + "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(".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, %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(".pushsection \".text\",\"ax\",%progbits\n" + ".globl _start\n" + ".type _start,#function\n" + "_start:\n" +#if defined(__thumb2__) + ".thumb\n" + ".syntax unified\n" +#endif + "mov fp, #0\n" + "mov lr, #0\n" + "mov r4, sp\n" /* Save starting SP in r4. */ + "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!" +#endif diff --git a/chrome/nacl/nacl_helper_bootstrap_linux.x b/chrome/nacl/nacl_helper_bootstrap_linux.x new file mode 100644 index 0000000..89acd90 --- /dev/null +++ b/chrome/nacl/nacl_helper_bootstrap_linux.x @@ -0,0 +1,133 @@ +/* Copyright (c) 2011 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * This is a custom linker script used to build nacl_helper_bootstrap. + * It has a very special layout. This script will only work with input + * that is kept extremely minimal. If there are unexpected input sections + * not named here, the result will not be correct. + * + * We need to use a standalone loader program rather than just using a + * dynamically-linked program here because its entire address space will be + * taken over for the NaCl untrusted address space. A normal program would + * cause dynamic linker data structures to point to its .dynamic section, + * which is no longer available after startup. + * + * We need this special layout (and the nacl_helper_bootstrap_munge_phdr + * step) because simply having bss space large enough to reserve the + * address space would cause the kernel loader to think we're using that + * much anonymous memory and refuse to execute the program on a machine + * with not much memory available. + */ + +/* + * Set the entry point to the symbol called _start, which we define in assembly. + */ +ENTRY(_start) + +/* + * This is the address where the program text starts. + * We set this as low as we think we can get away with. + * The common settings for sysctl vm.mmap_min_addr range from 4k to 64k. + */ +TEXT_START = 0x10000; + +/* + * The symbol RESERVE_TOP is the top of the range we are trying to reserve. + * This is set via --defsym on the linker command line, because the correct + * value differs for each machine. It's not defined at all if we do not + * actually need any space reserved for this configuration. + */ + +/* + * We specify the program headers we want explicitly, to get the layout + * exactly right and to give the "reserve" segment p_flags of zero, so + * that it gets mapped as PROT_NONE. + */ +PHDRS { + text PT_LOAD FILEHDR PHDRS; + data PT_LOAD; + reserve PT_LOAD FLAGS(0); + r_debug PT_LOAD; + note PT_NOTE; + stack PT_GNU_STACK FLAGS(6); /* RW, no E */ +} + +/* + * Now we lay out the sections across those segments. + */ +SECTIONS { + . = TEXT_START + SIZEOF_HEADERS; + + /* + * The build ID note usually comes first. + * It's both part of the text PT_LOAD segment (like other rodata) and + * it's what the PT_NOTE header points to. + */ + .note.gnu.build-id : { + *(.note.gnu.build-id) + } :text :note + + /* + * Here is the program itself. + */ + .text : { + *(.text*) + } :text + .rodata : { + *(.rodata*) + *(.eh_frame*) + } + + etext = .; + + /* + * Adjust the address for the data segment. We want to adjust up to + * the same address within the page on the next page up. + */ + . = (ALIGN(CONSTANT(MAXPAGESIZE)) - + ((CONSTANT(MAXPAGESIZE) - .) & (CONSTANT(MAXPAGESIZE) - 1))); + . = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE)); + + .data : { + *(.data*) + } :data + .bss : { + *(.bss*) + } + + /* + * Now we move up to the next p_align increment, and place the dummy + * segment there. The linker emits this segment with the p_vaddr and + * p_memsz we want, which reserves the address space. But the linker + * gives it a p_filesz of zero. We have to edit the phdr after link + * time to give it a p_filesz matching its p_memsz. That way, the + * kernel doesn't think we are preallocating a huge amount of memory. + * It just maps it from the file, i.e. way off the end of the file, + * which is perfect for reserving the address space. + */ + . = ALIGN(CONSTANT(COMMONPAGESIZE)); + RESERVE_START = .; + .reserve : { + . += DEFINED(RESERVE_TOP) ? (RESERVE_TOP - RESERVE_START) : 0; + } :reserve + + /* + * This must be placed above the reserved address space, so it won't + * be clobbered by NaCl. We want this to be visible at its fixed address + * in the memory image so the debugger can make sense of things. + */ + .r_debug : { + *(.r_debug) + } :r_debug + + /* + * These are empty input sections the linker generates. + * If we don't discard them, they pollute the flags in the output segment. + */ + /DISCARD/ : { + *(.iplt) + *(.rel*) + *(.igot.plt) + } +} diff --git a/chrome/nacl/nacl_helper_bootstrap_munge_phdr.c b/chrome/nacl/nacl_helper_bootstrap_munge_phdr.c new file mode 100644 index 0000000..87fe73f --- /dev/null +++ b/chrome/nacl/nacl_helper_bootstrap_munge_phdr.c @@ -0,0 +1,66 @@ +/* Copyright (c) 2011 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * This is a trivial program to edit an ELF file in place, making + * one crucial modification to a program header. It's invoked: + * bootstrap_phdr_hacker FILENAME SEGMENT_NUMBER + * where SEGMENT_NUMBER is the zero-origin index of the program header + * we'll touch. This is a PT_LOAD with p_filesz of zero. We change its + * p_filesz to match its p_memsz value. + */ + +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <libelf.h> +#include <stdlib.h> +#include <unistd.h> + +int main(int argc, char **argv) { + if (argc != 3) + error(1, 0, "Usage: %s FILENAME SEGMENT_NUMBER", argv[0]); + + const char *const file = argv[1]; + const int segment = atoi(argv[2]); + + int fd = open(file, O_RDWR); + if (fd < 0) + error(2, errno, "Cannot open %s for read/write", file); + + if (elf_version(EV_CURRENT) == EV_NONE) + error(2, 0, "elf_version: %s", elf_errmsg(-1)); + + Elf *elf = elf_begin(fd, ELF_C_RDWR, NULL); + if (elf == NULL) + error(2, 0, "elf_begin: %s", elf_errmsg(-1)); + + if (elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT) == 0) + error(2, 0, "elf_flagelf: %s", elf_errmsg(-1)); + + GElf_Phdr phdr; + GElf_Phdr *ph = gelf_getphdr(elf, segment, &phdr); + if (ph == NULL) + error(2, 0, "gelf_getphdr: %s", elf_errmsg(-1)); + + if (ph->p_type != PT_LOAD) + error(3, 0, "Program header %d is %u, not PT_LOAD", + segment, (unsigned int) ph->p_type); + if (ph->p_filesz != 0) + error(3, 0, "Program header %d has nonzero p_filesz", segment); + + ph->p_filesz = ph->p_memsz; + if (gelf_update_phdr(elf, segment, ph) == 0) + error(2, 0, "gelf_update_phdr: %s", elf_errmsg(-1)); + + if (elf_flagphdr(elf, ELF_C_SET, ELF_F_DIRTY) == 0) + error(2, 0, "elf_flagphdr: %s", elf_errmsg(-1)); + + if (elf_update(elf, ELF_C_WRITE) < 0) + error(2, 0, "elf_update: %s", elf_errmsg(-1)); + + close(fd); + + return 0; +} diff --git a/chrome/nacl/nacl_helper_bootstrap_munge_phdr.py b/chrome/nacl/nacl_helper_bootstrap_munge_phdr.py new file mode 100755 index 0000000..2b33050 --- /dev/null +++ b/chrome/nacl/nacl_helper_bootstrap_munge_phdr.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""This takes three command-line arguments: + MUNGE-PHDR-PROGRAM file name of program built from + nacl_helper_bootstrap_munge_phdr.c + INFILE raw linked ELF file name + OUTFILE output file name + +We just run the MUNGE-PHDR-PROGRAM on a copy of INFILE. +That modifies the file in place. Then we move it to OUTFILE. + +We only have this wrapper script because nacl_helper_bootstrap_munge_phdr.c +wants to modify a file in place (and it would be a much longer and more +fragile program if it created a fresh ELF output file instead). +""" + +import shutil +import subprocess +import sys + + +def Main(argv): + if len(argv) != 4: + print 'Usage: %s MUNGE-PHDR-PROGRAM INFILE OUTFILE' % argv[0] + return 1 + [prog, munger, infile, outfile] = argv + tmpfile = outfile + '.tmp' + shutil.copy(infile, tmpfile) + segment_num = '2' + subprocess.check_call([munger, tmpfile, segment_num]) + shutil.move(tmpfile, outfile) + return 0 + + +if __name__ == '__main__': + sys.exit(Main(sys.argv)) |