summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbuild/install-build-deps.sh2
-rw-r--r--chrome/common/chrome_paths.cc9
-rw-r--r--chrome/common/chrome_paths.h1
-rw-r--r--chrome/nacl.gypi116
-rw-r--r--chrome/nacl/nacl_fork_delegate_linux.cc18
-rw-r--r--chrome/nacl/nacl_helper_bootstrap_linux.c433
-rw-r--r--chrome/nacl/nacl_helper_bootstrap_linux.x93
-rw-r--r--chrome/nacl/nacl_helper_bootstrap_munge_phdr.c66
-rwxr-xr-xchrome/nacl/nacl_helper_bootstrap_munge_phdr.py36
-rw-r--r--chrome/nacl/nacl_helper_exports.txt10
-rw-r--r--chrome/nacl/nacl_helper_linux.cc26
11 files changed, 734 insertions, 76 deletions
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index 7908608..f4907bd 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -103,7 +103,7 @@ chromeos_dev_list="libpulse-dev"
# Packages need for development
dev_list="apache2.2-bin bison fakeroot flex g++ gperf language-pack-fr
libapache2-mod-php5 libasound2-dev libbz2-dev libcairo2-dev
- libcups2-dev libdbus-glib-1-dev libgconf2-dev
+ libcups2-dev libdbus-glib-1-dev libelf-dev libgconf2-dev
libgl1-mesa-dev libglu1-mesa-dev libglib2.0-dev libgnome-keyring-dev
libgtk2.0-dev libjpeg62-dev libkrb5-dev libnspr4-dev libnss3-dev
libpam0g-dev libsctp-dev libsqlite3-dev libxslt1-dev libxss-dev
diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc
index 0758f1e..7f223cf 100644
--- a/chrome/common/chrome_paths.cc
+++ b/chrome/common/chrome_paths.cc
@@ -53,8 +53,10 @@ const FilePath::CharType kInternalNaClPluginFileName[] =
#endif
#if defined(OS_POSIX) && !defined(OS_MACOSX)
-// File name of the nacl_helper, Linux only.
+// File name of the nacl_helper and nacl_helper_bootstrap, Linux only.
const FilePath::CharType kInternalNaClHelperFileName[] =
+ FILE_PATH_LITERAL("nacl_helper");
+const FilePath::CharType kInternalNaClHelperBootstrapFileName[] =
FILE_PATH_LITERAL("nacl_helper_bootstrap");
#endif
@@ -246,6 +248,11 @@ bool PathProvider(int key, FilePath* result) {
return false;
cur = cur.Append(kInternalNaClHelperFileName);
break;
+ case chrome::FILE_NACL_HELPER_BOOTSTRAP:
+ if (!PathService::Get(base::DIR_MODULE, &cur))
+ return false;
+ cur = cur.Append(kInternalNaClHelperBootstrapFileName);
+ break;
#endif
case chrome::FILE_RESOURCES_PACK:
#if defined(OS_MACOSX)
diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h
index 4998213..4b89a4e 100644
--- a/chrome/common/chrome_paths.h
+++ b/chrome/common/chrome_paths.h
@@ -71,6 +71,7 @@ enum {
#if defined(OS_POSIX) && !defined(OS_MACOSX)
FILE_NACL_HELPER, // Full path to Linux nacl_helper executable.
+ FILE_NACL_HELPER_BOOTSTRAP, // ... and nacl_helper_bootstrap executable.
#endif
FILE_NACL_PLUGIN, // Full path to the internal NaCl plugin file.
FILE_LIBAVCODEC, // Full path to libavcodec media decoding
diff --git a/chrome/nacl.gypi b/chrome/nacl.gypi
index abfab38..42c0035 100644
--- a/chrome/nacl.gypi
+++ b/chrome/nacl.gypi
@@ -183,9 +183,7 @@
['OS=="linux"', {
'targets': [
{
- 'target_name': 'nacl_helper.so',
- # 'executable' will be overridden below when we add the -shared
- # flag; here it prevents gyp from using the --whole-archive flag
+ 'target_name': 'nacl_helper',
'type': 'executable',
'include_dirs': [
'..',
@@ -194,7 +192,7 @@
'nacl',
],
'sources': [
- '../chrome/nacl/nacl_helper_linux.cc',
+ 'nacl/nacl_helper_linux.cc',
],
'conditions': [
['toolkit_uses_gtk == 1', {
@@ -203,36 +201,108 @@
],
}],
],
+ 'cflags': ['-fPIE'],
'link_settings': {
- # NOTE: '-shared' overrides 'executable' above
- 'ldflags': ['-shared',
- '-Wl,--version-script=chrome/nacl/nacl_helper_exports.txt',
- ],
+ 'ldflags': ['-pie'],
},
},
{
- 'target_name': 'nacl_helper_bootstrap',
+ 'target_name': 'nacl_helper_bootstrap_munge_phdr',
'type': 'executable',
- 'dependencies': [
- 'nacl_helper.so',
+ 'toolsets': ['host'],
+ 'sources': [
+ 'nacl/nacl_helper_bootstrap_munge_phdr.c',
+ ],
+ 'libraries': [
+ '-lelf',
+ ],
+ # This is an ugly kludge because gyp doesn't actually treat
+ # host_arch=x64 target_arch=ia32 as proper cross compilation.
+ # It still wants to compile the "host" program with -m32 et
+ # al. Though a program built that way can indeed run on the
+ # x86-64 host, we cannot reliably build this program on such a
+ # host because Ubuntu does not provide the full suite of
+ # x86-32 libraries in packages that can be installed on an
+ # x86-64 host; in particular, libelf is missing. So here we
+ # use the hack of eliding all the -m* flags from the
+ # compilation lines, getting the command close to what they
+ # would be if gyp were to really build properly for the host.
+ # TODO(bradnelson): Clean up with proper cross support.
+ 'conditions': [
+ ['host_arch=="x64"', {
+ 'cflags/': [['exclude', '-m.*']],
+ 'ldflags/': [['exclude', '-m.*']],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'nacl_helper_bootstrap_raw',
+ 'type': 'executable',
+ 'include_dirs': [
+ '..',
],
'sources': [
- '../chrome/nacl/nacl_helper_bootstrap_linux.c',
+ 'nacl/nacl_helper_bootstrap_linux.c',
+ # We list the linker script here for documentation purposes.
+ # But even this doesn't make gyp treat it as a dependency,
+ # so incremental builds won't relink when the script changes.
+ # TODO(bradnelson): Fix the dependency handling.
+ 'nacl/nacl_helper_bootstrap_linux.x',
+ ],
+ 'cflags': [
+ # The tiny standalone bootstrap program is incompatible with
+ # -fstack-protector, which might be on by default. That switch
+ # requires using the standard libc startup code, which we do not.
+ '-fno-stack-protector',
+ # We don't want to compile it PIC (or its cousin PIE), because
+ # it goes at an absolute address anyway, and because any kind
+ # of PIC complicates life for the x86-32 assembly code. We
+ # append -fno-* flags here instead of using a 'cflags!' stanza
+ # to remove -f* flags, just in case some system's compiler
+ # defaults to using PIC for everything.
+ '-fno-pic', '-fno-PIC',
+ '-fno-pie', '-fno-PIE',
],
- # TODO(bradchen): Delete the -B argument when Gold supports
- # -Ttext properly. Until then use ld.bfd.
'link_settings': {
- 'ldflags': ['-B', 'tools/ld_bfd',
- # Force text segment at 0x10000 (64KB)
- # The max-page-size option is needed on x86-64 linux
- # where 4K pages are not the default in the BFD linker.
- '-Wl,-Ttext-segment,10000,-z,max-page-size=0x1000',
- # reference nacl_helper as a shared library
- '<(PRODUCT_DIR)/nacl_helper.so',
- '-Wl,-rpath,<(SHARED_LIB_DIR)',
- ],
+ 'ldflags': [
+ # TODO(bradchen): Delete the -B argument when Gold is verified
+ # to produce good results with our custom linker script.
+ # Until then use ld.bfd.
+ '-B', 'tools/ld_bfd',
+ # This programs is (almost) entirely standalone. It has
+ # its own startup code, so no crt1.o for it. It is
+ # statically linked, and on x86 it actually does not use
+ # libc at all. However, on ARM it needs a few (safe)
+ # things from libc, so we don't use '-nostdlib' here.
+ '-static', '-nostartfiles',
+ # Link with our custom linker script to get out special layout.
+ # TODO(bradnelson): Use some <(foo) instead of chrome/ here.
+ '-Wl,--script=chrome/nacl/nacl_helper_bootstrap_linux.x',
+ # On x86-64, the default page size with some
+ # linkers is 2M rather than the real Linux page
+ # size of 4K. A larger page size is incompatible
+ # with our custom linker script's special layout.
+ '-Wl,-z,max-page-size=0x1000',
+ ],
},
},
+ {
+ 'target_name': 'nacl_helper_bootstrap',
+ 'dependencies': [
+ 'nacl_helper_bootstrap_raw',
+ 'nacl_helper_bootstrap_munge_phdr#host',
+ ],
+ 'type': 'none',
+ 'actions': [{
+ 'action_name': 'munge_phdr',
+ 'inputs': ['nacl/nacl_helper_bootstrap_munge_phdr.py',
+ '<(PRODUCT_DIR)/nacl_helper_bootstrap_munge_phdr',
+ '<(PRODUCT_DIR)/nacl_helper_bootstrap_raw'],
+ 'outputs': ['<(PRODUCT_DIR)/nacl_helper_bootstrap'],
+ 'message': 'Munging ELF program header',
+ 'action': ['python', '<@(_inputs)', '<@(_outputs)']
+ }],
+ }
],
}],
],
diff --git a/chrome/nacl/nacl_fork_delegate_linux.cc b/chrome/nacl/nacl_fork_delegate_linux.cc
index e5ee01a..26fae53 100644
--- a/chrome/nacl/nacl_fork_delegate_linux.cc
+++ b/chrome/nacl/nacl_fork_delegate_linux.cc
@@ -26,6 +26,8 @@ NaClForkDelegate::NaClForkDelegate()
sandboxed_(false),
fd_(-1) {}
+const char kNaClHelperAtZero[] = "--at-zero";
+
void NaClForkDelegate::Init(const bool sandboxed,
const int browserdesc,
const int sandboxdesc) {
@@ -48,18 +50,18 @@ void NaClForkDelegate::Init(const bool sandboxed,
const bool use_helper = CommandLine::ForCurrentProcess()->HasSwitch(
switches::kNaClLinuxHelper);
FilePath helper_exe;
- if (use_helper && PathService::Get(chrome::FILE_NACL_HELPER, &helper_exe)) {
+ FilePath helper_bootstrap_exe;
+ if (use_helper &&
+ PathService::Get(chrome::FILE_NACL_HELPER, &helper_exe) &&
+ PathService::Get(chrome::FILE_NACL_HELPER_BOOTSTRAP,
+ &helper_bootstrap_exe)) {
CommandLine::StringVector argv = CommandLine::ForCurrentProcess()->argv();
- argv[0] = helper_exe.value();
+ argv[0] = helper_bootstrap_exe.value();
+ argv[1] = helper_exe.value();
+ argv[2] = kNaClHelperAtZero;
base::LaunchOptions options;
options.fds_to_remap = &fds_to_map;
options.clone_flags = CLONE_FS | SIGCHLD;
- // LD_BIND_NOW forces non-lazy binding in the dynamic linker, to
- // prevent the linker from trying to look at the text of the nacl_helper
- // program after it has been replaced by the nacl module.
- base::environment_vector env;
- env.push_back(std::make_pair("LD_BIND_NOW", "1"));
- options.environ = &env;
ready_ = base::LaunchProcess(argv, options, NULL);
// parent and error cases are handled below
}
diff --git a/chrome/nacl/nacl_helper_bootstrap_linux.c b/chrome/nacl/nacl_helper_bootstrap_linux.c
index 7a0ace7..c534c39 100644
--- a/chrome/nacl/nacl_helper_bootstrap_linux.c
+++ b/chrome/nacl/nacl_helper_bootstrap_linux.c
@@ -2,23 +2,428 @@
* 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 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 <link.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+#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.
+ * 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;
+}
+
+
+/*
+ * 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 *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(": ", 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("Cannot open dynamic linker! ", "errno", my_errno, NULL, 0);
+ return result;
+}
+
+static void my_pread(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);
+ if ((size_t) result != bufsz)
+ fail(fail_message, "read count", result, NULL, 0);
+}
+
+static uintptr_t my_mmap(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("Failed to map from dynamic linker! ",
+ segment_type, segnum, "errno", my_errno);
+ return (uintptr_t) result;
+}
+
+static void my_mprotect(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! ",
+ "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(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("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[MAX_PHNUM];
+ 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);
-#include <stdlib.h>
+ if (ehdr.e_type != ET_DYN)
+ fail("Dynamic linker not ET_DYN! ",
+ "e_type", ehdr.e_type, NULL, 0);
-/* reserve 1GB of space */
-#define ONEGIG (1 << 30)
-char nacl_reserved_space[ONEGIG];
+ my_pread("Failed to read program headers from dynamic linker! ",
+ fd, phdr, sizeof(phdr[0]) * ehdr.e_phnum, ehdr.e_phoff);
-void nacl_helper_init(int argc, char *argv[],
- const char *nacl_reserved_space);
+ 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);
-int main(int argc, char *argv[]) {
- nacl_helper_init(argc, argv, nacl_reserved_space);
- abort();
- return 0; // convince the tools I'm sane.
+ /*
+ * 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("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("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;
+
+ /*
+ * 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(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,
+ round_down(ph->p_offset, pagesize));
+
+ handle_bss(ph - phdr, ph, load_bias, pagesize);
+ }
+ }
+
+ sys_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.
+ */
+#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" /* Restore the saved SP. */
+ "blx r0\n" /* Jump to the entry point. */
+ );
+#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..5eae077
--- /dev/null
+++ b/chrome/nacl/nacl_helper_bootstrap_linux.x
@@ -0,0 +1,93 @@
+/* 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;
+
+/*
+ * This is the top of the range we are trying to reserve, which is 1G
+ * for x86-32 and ARM. For an x86-64 zero-based sandbox, this really
+ * needs to be 36G.
+ */
+RESERVE_TOP = 1 << 30;
+
+/*
+ * 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;
+ reserve PT_LOAD FLAGS(0);
+ stack PT_GNU_STACK FLAGS(6); /* RW, no E */
+}
+
+/*
+ * Now we lay out the sections across those segments.
+ */
+SECTIONS {
+ /*
+ * Here is the program itself.
+ */
+ .text TEXT_START + SIZEOF_HEADERS : {
+ *(.note.gnu.build-id)
+ *(.text*)
+ *(.rodata*)
+ *(.eh_frame*)
+ } :text
+ etext = .;
+
+ /*
+ * 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 : {
+ . = RESERVE_TOP - RESERVE_START;
+ } :reserve
+
+ /*
+ * 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..c3a3931
--- /dev/null
+++ b/chrome/nacl/nacl_helper_bootstrap_munge_phdr.py
@@ -0,0 +1,36 @@
+#!/usr/bin/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]
+ sys.exit(1)
+ [prog, munger, infile, outfile] = argv
+ tmpfile = outfile + '.tmp'
+ shutil.copy(infile, tmpfile)
+ segment_num = '1'
+ subprocess.check_call([munger, tmpfile, segment_num])
+ shutil.move(tmpfile, outfile)
+
+if __name__ == '__main__':
+ Main(sys.argv)
diff --git a/chrome/nacl/nacl_helper_exports.txt b/chrome/nacl/nacl_helper_exports.txt
deleted file mode 100644
index af930c1..0000000
--- a/chrome/nacl/nacl_helper_exports.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-# gnu-ld version script for exporting desired symbols from nacl_helper
-#
-
-NACL_HELPER_1_0 {
- global:
- nacl_helper_init;
- nacl_helper_get_1G_address;
- local:
- *;
-};
diff --git a/chrome/nacl/nacl_helper_linux.cc b/chrome/nacl/nacl_helper_linux.cc
index b53cf8d..7ffeadb 100644
--- a/chrome/nacl/nacl_helper_linux.cc
+++ b/chrome/nacl/nacl_helper_linux.cc
@@ -23,6 +23,7 @@
#include "content/common/main_function_params.h"
#include "content/common/unix_domain_socket_posix.h"
#include "ipc/ipc_switches.h"
+#include "native_client/src/trusted/service_runtime/sel_memory.h"
namespace {
@@ -116,33 +117,20 @@ void HandleForkRequest(const std::vector<int>& child_fds) {
} // namespace
-static const void* g_nacl_reserved_space = NULL;
-extern "C" __attribute__((visibility("default")))
-const void* nacl_helper_get_1G_address() {
- return g_nacl_reserved_space;
-}
+static const char kNaClHelperAtZero[] = "at-zero";
-// nacl_helper_init does the real work of this module. It is invoked as
-// a static constructor and never returns, preventing main() from the
-// nacl_helper_bootstrap program from being called.
-//
-// NOTE This routine must not return.
-extern "C" __attribute__((visibility("default")))
-void nacl_helper_init(int argc, char *argv[],
- const char *nacl_reserved_space) {
+int main(int argc, char *argv[]) {
CommandLine::Init(argc, argv);
base::AtExitManager exit_manager;
base::RandUint64(); // acquire /dev/urandom fd before sandbox is raised
std::vector<int> empty; // for SendMsg() calls
g_suid_sandbox_active = (NULL != getenv("SBX_D"));
- g_nacl_reserved_space = nacl_reserved_space;
- if (!nacl_reserved_space) {
- VLOG(1) << "nacl_reserved_space is NULL";
- } else {
- VLOG(1) << "nacl_reserved_space is at "
- << (void *)nacl_reserved_space;
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(kNaClHelperAtZero)) {
+ g_nacl_prereserved_sandbox_addr = (void *) (uintptr_t) 0x10000;
}
+
// Send the zygote a message to let it know we are ready to help
if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor,
kNaClHelperStartupAck,