Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2440)

Unified Diff: chrome/nacl/nacl_helper_bootstrap_linux.c

Issue 7795010: Use chain-loading for Linux nacl_helper (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 7a0ace7ae2fc51fd6d6dd634e1236505e0aa9fa9..5184db1a3baea574bacb8d19941bfe0bb4872718 100644
--- a/chrome/nacl/nacl_helper_bootstrap_linux.c
+++ b/chrome/nacl/nacl_helper_bootstrap_linux.c
@@ -2,23 +2,654 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
- * Bootstraping the nacl_helper. This executable reserves the bottom 1G
- * of the address space, then invokes nacl_helper_init. Note that,
- * as the text of this executable will eventually be overwritten by the
- * native_client module, nacl_helper_init must not attempt to return.
+ * This is a standalone program that loads and run the dynamic linker.
Brad Chen 2011/08/29 23:04:57 'runs'
+ * 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 <asm/unistd.h>
+#include <elf.h>
+#include <fcntl.h>
+#include <link.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#define TEXT_START_ADDRESS 0x10000
+#define ADDRESS_SPACE_RESERVE (1 << 30) /* one gigabyte */
+#define BSS_RESERVE_SIZE (ADDRESS_SPACE_RESERVE - TEXT_START_ADDRESS)
+
+#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.
+ * 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;
+}
+
+
+/*
+ * Forward declaration.
+ */
+static void fail(const char *message,
+ const char *item1, int value1,
+ const char *item2, int value2) __attribute__((noreturn));
+
+static void open_failure(int error) {
+ fail("Cannot open dynamic linker! ", "errno", error, NULL, 0);
+}
+
+static void check_pread(const char *fail_message, size_t bufsz,
+ int error, size_t read_count) {
+ if (error != 0)
+ fail(fail_message, "errno", error, NULL, 0);
+ if (read_count != bufsz)
+ fail(fail_message, "read count", read_count, NULL, 0);
+}
+
+static void mmap_failure(const char *segment_type, unsigned int segnum,
+ int error) {
+ fail("Failed to map from dynamic linker! ",
+ segment_type, segnum, "errno", error);
+}
+
+static void mprotect_failure(unsigned int segnum, int error) {
+ fail("Failed to mprotect hole in dynamic linker! ",
+ "segment", segnum, "errno", error);
+}
+
+
+/*
+ * Hand-rolled system call stubs for the few things we use.
*/
-#include <stdlib.h>
+#if defined(__i386__)
Brad Chen 2011/08/29 23:04:57 I tend to agree with Mark that these syscall stubs
-/* reserve 1GB of space */
-#define ONEGIG (1 << 30)
-char nacl_reserved_space[ONEGIG];
+__attribute__((noreturn)) static void my_exit(int status) {
+ asm volatile("int $0x80"
Mark Seaborn 2011/08/29 22:26:50 The Chromium tree already has code for doing sysca
+ :: "a" (__NR_exit_group), "b" (status)
+ : "cc");
+ while (1) *(volatile int *) 0 = 0;
+}
-void nacl_helper_init(int argc, char *argv[],
- const char *nacl_reserved_space);
+static void my_writev(int fd, const struct iovec *iov, int niov) {
+ asm volatile("int $0x80"
+ :: "a" (__NR_writev), "b" (fd), "c" (iov), "d" (niov)
+ : "cc");
+}
+
+static int my_open(const char *file, int oflag) {
+ int result;
+ asm volatile("int $0x80"
+ : "=a" (result)
+ : "0" (__NR_open), "b" (file), "c" (oflag)
+ : "cc");
+ if (result < 0)
+ open_failure(-result);
+ return result;
+}
-int main(int argc, char *argv[]) {
- nacl_helper_init(argc, argv, nacl_reserved_space);
- abort();
- return 0; // convince the tools I'm sane.
+static void my_close(int fd) {
+ int result;
+ asm volatile("int $0x80"
+ : "=a" (result)
+ : "0" (__NR_close), "b" (fd)
+ : "cc");
}
+
+static void my_pread(const char *fail_message,
+ int fd, void *buf, size_t bufsz, uintptr_t pos) {
+ int result;
+ /*
+ * Ideally we would use a memory output operand rather than the
+ * "memory" clobber. But the compiler can't handle that here,
+ * because it makes it want to use too many registers.
+ */
+ asm volatile("int $0x80"
+ : "=a" (result)
+ : "0" (__NR_pread64), "b" (fd), "c" (buf), "d" (bufsz),
+ "S" (pos), "D" (0)
+ : "cc", "memory");
+ check_pread(fail_message, bufsz, result < 0 ? -result : 0, result);
+}
+
+static uintptr_t my_mmap(const char *segment_type, unsigned int segnum,
+ uintptr_t address, size_t size, int prot, int flags,
+ int fd, uint32_t pos) {
+ uintptr_t result;
+ const struct {
+ unsigned int args[6];
+ } args = {
+ { address, size, prot, flags, fd, pos }
+ };
+ asm volatile("int $0x80"
+ : "=a" (result)
+ : "0" (__NR_mmap), "b" (&args), "m" (args)
+ : "cc");
+ if (result > (uintptr_t) -4096)
+ mmap_failure(segment_type, segnum, -result);
+ return result;
+}
+
+static void my_mprotect(unsigned int segnum,
+ uintptr_t address, size_t size, int prot) {
+ int result;
+ asm volatile("int $0x80"
+ : "=a" (result)
+ : "b" (address), "c" (size), "d" (prot)
+ : "cc");
+ if (result < 0)
+ mprotect_failure(segnum, -result);
+}
+
+#elif defined(__x86_64__)
+
+__attribute__((noreturn)) static void my_exit(int status) {
+ asm volatile("syscall"
+ :: "a" (__NR_exit_group), "D" (status)
+ : "cc", "rcx", "r11");
+ while (1) *(volatile int *) 0 = 0;
+}
+
+static void my_writev(int fd, const struct iovec *iov, int niov) {
+ asm volatile("syscall"
+ :: "a" (__NR_writev), "D" (fd), "S" (iov), "d" (niov)
+ : "cc", "rcx", "r11");
+}
+
+static int my_open(const char *file, int oflag) {
+ int result;
+ asm volatile("syscall"
+ : "=a" (result)
+ : "0" (__NR_open), "D" (file), "S" (oflag)
+ : "cc", "rcx", "r11");
+ if (result < 0)
+ open_failure(-result);
+ return result;
+}
+
+static void my_close(int fd) {
+ int result;
+ asm volatile("syscall"
+ : "=a" (result)
+ : "0" (__NR_close), "D" (fd)
+ : "cc", "rcx", "r11");
+}
+
+static void my_pread(const char *fail_message,
+ int fd, void *buf, size_t bufsz, uintptr_t pos) {
+ int result;
+ asm volatile("mov %6, %%r10; syscall"
+ : "=a" (result), "=m" (*(struct { char b[bufsz]; } *) buf)
+ : "0" (__NR_pread64), "D" (fd), "S" (buf), "d" (bufsz), "g" (pos)
+ : "cc", "rcx", "r11", "r10");
+ check_pread(fail_message, bufsz, result < 0 ? -result : 0, result);
+}
+
+static uintptr_t my_mmap(const char *segment_type, unsigned int segnum,
+ uintptr_t address, size_t size, int prot, int flags,
+ int fd, off_t pos) {
+ uintptr_t result;
+ asm volatile("mov %5, %%r10; mov %6, %%r8; mov %7, %%r9; syscall"
+ : "=a" (result)
+ : "0" (__NR_mmap), "D" (address), "S" (size),
+ "d" ((long int) prot), "g" ((long int) flags),
+ "g" ((long int) fd), "g" (pos)
+ : "cc", "rcx", "r11", "r10", "r9", "r8");
+ if (result > (uintptr_t) -4096)
+ mmap_failure(segment_type, segnum, -result);
+ return result;
+}
+
+static void my_mprotect(unsigned int segnum,
+ uintptr_t address, size_t size, int prot) {
+ int result;
+ asm volatile("syscall"
+ : "=a" (result)
+ : "0" (__NR_mprotect), "D" (address), "S" (size), "d" (prot)
+ : "cc", "rcx", "r11");
+ if (result < 0)
+ mprotect_failure(segnum, -result);
+}
+
+#elif defined(__ARM_EABI__)
+
+/*
+ * We must touch r7 behind the compiler's back when compiling for
+ * Thumb. r7 is the Thumb frame pointer and the compiler won't honor
+ * our private use of it without getting confused and clobbering
+ * things. Since a few extra instructions here don't hurt, we just
+ * use the same method for non-Thumb builds too.
+ */
+#define SYSCALL_ASM \
+ "mov %[scratch], r7\n" \
+ "mov r7, %[nr]\n" \
+ "swi 0\n" \
+ "mov r7, %[scratch]"
+
+__attribute__((noreturn)) static void my_exit(int status) {
+ int scratch;
+ register int a1 asm ("a1") = status;
+ asm volatile(SYSCALL_ASM
+ : [scratch] "=&r" (scratch)
+ : [nr] "i" (__NR_exit_group), "r" (a1));
+ while (1) *(volatile int *) 0 = 0;
+}
+
+static void my_writev(int fd, const struct iovec *iov, int niov) {
+ int scratch;
+ register int a1 asm ("a1") = fd;
+ register int a2 asm ("a2") = (uintptr_t) iov;
+ register int a3 asm ("a3") = niov;
+ asm volatile(SYSCALL_ASM
+ : [scratch] "=&r" (scratch), "=r" (a1)
+ : [nr] "i" (__NR_writev), "r" (a1), "r" (a2), "r" (a3));
+}
+
+static int my_open(const char *file, int oflag) {
+ int scratch;
+ register int result asm ("a1") = (uintptr_t) file;
+ register int a2 asm ("a2") = oflag;
+ asm volatile(SYSCALL_ASM
+ : [scratch] "=&r" (scratch), "=r" (result)
+ : [nr] "i" (__NR_open), "r" (result), "r" (a2));
+ if (result < 0)
+ open_failure(-result);
+ return result;
+}
+
+static void my_close(int fd) {
+ int scratch;
+ register int result asm ("a1") = fd;
+ asm volatile(SYSCALL_ASM
+ : [scratch] "=&r" (scratch), "=r" (result)
+ : [nr] "i" (__NR_close), "r" (result));
+}
+
+static void my_pread(const char *fail_message,
+ int fd, void *buf, size_t bufsz, uintptr_t pos) {
+ int scratch;
+ register int result asm ("a1") = fd;
+ register int a2 asm ("a2") = (uintptr_t) buf;
+ register int a3 asm ("a3") = bufsz;
+ register int a4 asm ("a4") = 0;
+ register int v1 asm ("v1");
+ register int v2 asm ("v2");
+#if defined(__ARMEL__)
+ v1 = pos;
+ v2 = 0;
+#else
+ v1 = 0;
+ v2 = pos;
+#endif
+ asm volatile(SYSCALL_ASM
+ : [scratch] "=&r" (scratch), "=r" (result)
+ : [nr] "i" (__NR_pread64), "r" (result), "r" (a2), "r" (a3),
+ "r" (a4), "r" (v1), "r" (v2));
+ check_pread(fail_message, bufsz, result < 0 ? -result : 0, result);
+}
+
+static uintptr_t my_mmap(const char *segment_type, unsigned int segnum,
+ uintptr_t address, size_t size, int prot, int flags,
+ int fd, off_t pos) {
+ int scratch;
+ register uintptr_t result asm ("a1") = address;
+ register int a2 asm ("a2") = size;
+ register int a3 asm ("a3") = prot;
+ register int a4 asm ("a4") = flags;
+ register int v1 asm ("v1") = fd;
+ register int v2 asm ("v2") = pos >> 12;
+ asm volatile(SYSCALL_ASM
+ : [scratch] "=&r" (scratch), "=r" (result)
+ : [nr] "i" (__NR_mmap2), "r" (result), "r" (a2), "r" (a3),
+ "r" (a4), "r" (v1), "r" (v2));
+ if (result > (uintptr_t) -4096)
+ mmap_failure(segment_type, segnum, -result);
+ return result;
+}
+
+static void my_mprotect(unsigned int segnum,
+ uintptr_t address, size_t size, int prot) {
+ int scratch;
+ register int result asm ("a1") = address;
+ register int a2 asm ("a2") = size;
+ register int a3 asm ("a3") = prot;
+ asm volatile(SYSCALL_ASM
+ : [scratch] "=&r" (scratch), "=r" (result)
+ : [nr] "i" (__NR_mprotect), "r" (result), "r" (a2), "r" (a3));
+ if (result < 0)
+ mprotect_failure(segnum, -result);
+}
+
+#else
+# error "Need inline syscall implementation for this architecture!"
+#endif
+
+/*
+ * We're avoiding libc, so no printf. The only nontrivial thing we need
+ * is rendering numbers, which is, in fact, pretty trivial.
+ */
+static void itoa(int value, struct iovec *iov, char *buf, size_t bufsz) {
+ char *p = &buf[bufsz];
+ do {
+ *--p = "0123456789"[value % 10];
Brad Chen 2011/08/29 23:04:57 Yikes! I bet this code works just fine but I'm pre
+ 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 *message,
+ const char *item1, int value1,
+ const char *item2, int value2) {
+ char valbuf1[32], valbuf2[32];
+ struct iovec iov[] = {
+ STRING_IOV("bootstrap_helper", 1),
+ STRING_IOV(DYNAMIC_LINKER, 1),
+ 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)
+ itoa(value1, &iov[6], valbuf1, sizeof(valbuf1));
+ if (item2 != NULL)
+ itoa(value1, &iov[10], valbuf2, sizeof(valbuf2));
+
+ my_writev(2, iov, niov);
+ my_exit(2);
+}
+
+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;
+}
+
+/*
+ * 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(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 = (file_end + pagesize - 1) & -pagesize;
+ ElfW(Addr) page_end = (ph->p_vaddr + load_bias +
+ ph->p_memsz + pagesize - 1) & -pagesize;
+ if (page_end > file_page_end)
+ my_mmap("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);
+ }
+}
+
+/*
+ * 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.
+ */
+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);
+
+ ElfW(Ehdr) ehdr;
+ my_pread("Failed to read ELF header from dynamic linker! ",
+ 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("Dynamic linker 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("Dynamic linker has wrong architecture! ",
+ "e_machine", ehdr.e_machine, NULL, 0);
+ break;
+ }
+
+ ElfW(Phdr) phdr[12];
Brad Chen 2011/08/29 23:04:57 make this 12 a constant please.
+ if (ehdr.e_phnum > sizeof(phdr) / sizeof(phdr[0]) || ehdr.e_phnum < 1)
+ fail("Dynamic linker has unreasonable ",
+ "e_phnum", ehdr.e_phnum, NULL, 0);
+
+ if (ehdr.e_type != ET_DYN)
+ fail("Dynamic linker not ET_DYN! ",
+ "e_type", ehdr.e_type, NULL, 0);
+
+ my_pread("Failed to read program headers from dynamic linker! ",
+ 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!",
+ NULL, 0, NULL, 0);
+
+ 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;
+
+ const uintptr_t mapping = my_mmap("segment", first_load - phdr,
+ first_load->p_vaddr & -pagesize, span,
+ prot_from_phdr(first_load), MAP_PRIVATE, fd,
+ first_load->p_offset & -pagesize);
+
+ const ElfW(Addr) load_bias = ((uintptr_t) mapping -
+ (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("First load segment of dynamic linker 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);
+
+ ElfW(Addr) last_end = first_load->p_vaddr + load_bias + first_load->p_memsz;
+
+ const ElfW(Phdr) *ph;
+ for (ph = first_load + 1; ph <= last_load; ++ph)
+ if (ph->p_type == PT_LOAD) {
+ ElfW(Addr) last_page_end = (last_end + pagesize - 1) & -pagesize;
+
+ last_end = ph->p_vaddr + load_bias + ph->p_memsz;
+ ElfW(Addr) start = (ph->p_vaddr + load_bias) & -pagesize;
+ ElfW(Addr) end = (last_end + pagesize - 1) & -pagesize;
+
+ if (start > last_page_end)
+ my_mprotect(ph - phdr, last_page_end, start - last_page_end, PROT_NONE);
+
+ my_mmap("segment", ph - phdr,
+ start, end - start,
+ prot_from_phdr(ph), MAP_PRIVATE | MAP_FIXED, fd,
+ ph->p_offset & -pagesize);
+
+ handle_bss(ph - phdr, ph, load_bias, pagesize);
+ }
+
+ my_close(fd);
+
+ 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.
Brad Chen 2011/08/29 23:04:57 This is pretty cool code, but again, if we could u
+ */
+#if defined(__i386__)
+asm(".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. */
+ "call do_load\n"
+ "movl %ebx, %esp\n" /* Restore the saved SP. */
+ "jmp *%eax\n"
+ ); /* Jump to the entry point. */
+#elif defined(__x86_64__)
+asm(".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, %rsp\n" /* Restore the saved SP. */
+ "jmp *%rax\n" /* Jump to the entry point. */
+ );
+#elif defined(__arm__)
+asm(".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. */
+ "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"
+ "bl do_load\n"
+ "mov sp, r4\n"
+ "blx r0\n"
+ );
+#else
+# error "Need stack-preserving _start code for this architecture!"
+#endif

Powered by Google App Engine
This is Rietveld 408576698