summaryrefslogtreecommitdiffstats
path: root/chrome/nacl
diff options
context:
space:
mode:
authormcgrathr@chromium.org <mcgrathr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-06 00:40:24 +0000
committermcgrathr@chromium.org <mcgrathr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-06 00:40:24 +0000
commitd67ee551133a8b07fdb0ae33fd417e9c082e1074 (patch)
tree0913e9dc592d3b3c9a5213b62d2361121a545a18 /chrome/nacl
parentbcc42fcbe0a522b6452eeda264c480dd7086d908 (diff)
downloadchromium_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.c575
-rw-r--r--chrome/nacl/nacl_helper_bootstrap_linux.x133
-rw-r--r--chrome/nacl/nacl_helper_bootstrap_munge_phdr.c66
-rwxr-xr-xchrome/nacl/nacl_helper_bootstrap_munge_phdr.py39
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))