OLD | NEW |
1 /* Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 /* Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 * Use of this source code is governed by a BSD-style license that can be | 2 * Use of this source code is governed by a BSD-style license that can be |
3 * found in the LICENSE file. | 3 * found in the LICENSE file. |
4 * | 4 * |
5 * Bootstraping the nacl_helper. This executable reserves the bottom 1G | 5 * This is a standalone program that loads and runs the dynamic linker. |
6 * of the address space, then invokes nacl_helper_init. Note that, | 6 * This program itself must be linked statically. To keep it small, it's |
7 * as the text of this executable will eventually be overwritten by the | 7 * written to avoid all dependencies on libc and standard startup code. |
8 * native_client module, nacl_helper_init must not attempt to return. | 8 * Hence, this should be linked using -nostartfiles. It must be compiled |
9 */ | 9 * with -fno-stack-protector to ensure the compiler won't emit code that |
10 | 10 * presumes some special setup has been done. |
11 #include <stdlib.h> | 11 * |
12 | 12 * On ARM, the compiler will emit calls to some libc functions, so we |
13 /* reserve 1GB of space */ | 13 * cannot link with -nostdlib. The functions it does use (memset and |
14 #define ONEGIG (1 << 30) | 14 * __aeabi_* functions for integer division) are sufficiently small and |
15 char nacl_reserved_space[ONEGIG]; | 15 * self-contained in ARM's libc.a that we don't have any problem using |
16 | 16 * the libc definitions though we aren't using the rest of libc or doing |
17 void nacl_helper_init(int argc, char *argv[], | 17 * any of the setup it might expect. |
18 const char *nacl_reserved_space); | 18 */ |
19 | 19 |
20 int main(int argc, char *argv[]) { | 20 #include <elf.h> |
21 nacl_helper_init(argc, argv, nacl_reserved_space); | 21 #include <fcntl.h> |
22 abort(); | 22 #include <link.h> |
23 return 0; // convince the tools I'm sane. | 23 #include <stddef.h> |
24 } | 24 #include <stdint.h> |
| 25 #include <sys/mman.h> |
| 26 |
| 27 #define MAX_PHNUM 12 |
| 28 |
| 29 #if defined(__i386__) |
| 30 # define DYNAMIC_LINKER "/lib/ld-linux.so.2" |
| 31 #elif defined(__x86_64__) |
| 32 # define DYNAMIC_LINKER "/lib64/ld-linux-x86-64.so.2" |
| 33 #elif defined(__ARM_EABI__) |
| 34 # define DYNAMIC_LINKER "/lib/ld-linux.so.3" |
| 35 #else |
| 36 # error "Don't know the dynamic linker file name for this architecture!" |
| 37 #endif |
| 38 |
| 39 |
| 40 /* |
| 41 * We're not using <string.h> functions here, to avoid dependencies. |
| 42 * In the x86 libc, even "simple" functions like memset and strlen can |
| 43 * depend on complex startup code, because in newer libc |
| 44 * implementations they are defined using STT_GNU_IFUNC. |
| 45 */ |
| 46 |
| 47 static void my_bzero(void *buf, size_t n) { |
| 48 char *p = buf; |
| 49 while (n-- > 0) |
| 50 *p++ = 0; |
| 51 } |
| 52 |
| 53 static size_t my_strlen(const char *s) { |
| 54 size_t n = 0; |
| 55 while (*s++ != '\0') |
| 56 ++n; |
| 57 return n; |
| 58 } |
| 59 |
| 60 |
| 61 /* |
| 62 * Get inline functions for system calls. |
| 63 */ |
| 64 static int my_errno; |
| 65 #define SYS_ERRNO my_errno |
| 66 #include "third_party/lss/linux_syscall_support.h" |
| 67 |
| 68 |
| 69 /* |
| 70 * We're avoiding libc, so no printf. The only nontrivial thing we need |
| 71 * is rendering numbers, which is, in fact, pretty trivial. |
| 72 */ |
| 73 static void iov_int_string(int value, struct kernel_iovec *iov, |
| 74 char *buf, size_t bufsz) { |
| 75 char *p = &buf[bufsz]; |
| 76 do { |
| 77 --p; |
| 78 *p = "0123456789"[value % 10]; |
| 79 value /= 10; |
| 80 } while (value != 0); |
| 81 iov->iov_base = p; |
| 82 iov->iov_len = &buf[bufsz] - p; |
| 83 } |
| 84 |
| 85 #define STRING_IOV(string_constant, cond) \ |
| 86 { (void *) string_constant, cond ? (sizeof(string_constant) - 1) : 0 } |
| 87 |
| 88 __attribute__((noreturn)) static void fail(const char *message, |
| 89 const char *item1, int value1, |
| 90 const char *item2, int value2) { |
| 91 char valbuf1[32]; |
| 92 char valbuf2[32]; |
| 93 struct kernel_iovec iov[] = { |
| 94 STRING_IOV("bootstrap_helper", 1), |
| 95 STRING_IOV(DYNAMIC_LINKER, 1), |
| 96 STRING_IOV(": ", 1), |
| 97 { (void *) message, my_strlen(message) }, |
| 98 { (void *) item1, item1 == NULL ? 0 : my_strlen(item1) }, |
| 99 STRING_IOV("=", item1 != NULL), |
| 100 {}, |
| 101 STRING_IOV(", ", item1 != NULL && item2 != NULL), |
| 102 { (void *) item2, item2 == NULL ? 0 : my_strlen(item2) }, |
| 103 STRING_IOV("=", item2 != NULL), |
| 104 {}, |
| 105 { "\n", 1 }, |
| 106 }; |
| 107 const int niov = sizeof(iov) / sizeof(iov[0]); |
| 108 |
| 109 if (item1 != NULL) |
| 110 iov_int_string(value1, &iov[6], valbuf1, sizeof(valbuf1)); |
| 111 if (item2 != NULL) |
| 112 iov_int_string(value1, &iov[10], valbuf2, sizeof(valbuf2)); |
| 113 |
| 114 sys_writev(2, iov, niov); |
| 115 sys_exit_group(2); |
| 116 while (1) *(volatile int *) 0 = 0; /* Crash. */ |
| 117 } |
| 118 |
| 119 |
| 120 static int my_open(const char *file, int oflag) { |
| 121 int result = sys_open(file, oflag, 0); |
| 122 if (result < 0) |
| 123 fail("Cannot open dynamic linker! ", "errno", my_errno, NULL, 0); |
| 124 return result; |
| 125 } |
| 126 |
| 127 static void my_pread(const char *fail_message, |
| 128 int fd, void *buf, size_t bufsz, uintptr_t pos) { |
| 129 ssize_t result = sys_pread64(fd, buf, bufsz, pos); |
| 130 if (result < 0) |
| 131 fail(fail_message, "errno", my_errno, NULL, 0); |
| 132 if ((size_t) result != bufsz) |
| 133 fail(fail_message, "read count", result, NULL, 0); |
| 134 } |
| 135 |
| 136 static uintptr_t my_mmap(const char *segment_type, unsigned int segnum, |
| 137 uintptr_t address, size_t size, |
| 138 int prot, int flags, int fd, uintptr_t pos) { |
| 139 #if defined(__NR_mmap2) |
| 140 void *result = sys_mmap2((void *) address, size, prot, flags, fd, pos >> 12); |
| 141 #else |
| 142 void *result = sys_mmap((void *) address, size, prot, flags, fd, pos); |
| 143 #endif |
| 144 if (result == MAP_FAILED) |
| 145 fail("Failed to map from dynamic linker! ", |
| 146 segment_type, segnum, "errno", my_errno); |
| 147 return (uintptr_t) result; |
| 148 } |
| 149 |
| 150 static void my_mprotect(unsigned int segnum, |
| 151 uintptr_t address, size_t size, int prot) { |
| 152 if (sys_mprotect((void *) address, size, prot) < 0) |
| 153 fail("Failed to mprotect hole in dynamic linker! ", |
| 154 "segment", segnum, "errno", my_errno); |
| 155 } |
| 156 |
| 157 |
| 158 static int prot_from_phdr(const ElfW(Phdr) *phdr) { |
| 159 int prot = 0; |
| 160 if (phdr->p_flags & PF_R) |
| 161 prot |= PROT_READ; |
| 162 if (phdr->p_flags & PF_W) |
| 163 prot |= PROT_WRITE; |
| 164 if (phdr->p_flags & PF_X) |
| 165 prot |= PROT_EXEC; |
| 166 return prot; |
| 167 } |
| 168 |
| 169 static uintptr_t round_up(uintptr_t value, uintptr_t size) { |
| 170 return (value + size - 1) & -size; |
| 171 } |
| 172 |
| 173 static uintptr_t round_down(uintptr_t value, uintptr_t size) { |
| 174 return value & -size; |
| 175 } |
| 176 |
| 177 /* |
| 178 * Handle the "bss" portion of a segment, where the memory size |
| 179 * exceeds the file size and we zero-fill the difference. For any |
| 180 * whole pages in this region, we over-map anonymous pages. For the |
| 181 * sub-page remainder, we zero-fill bytes directly. |
| 182 */ |
| 183 static void handle_bss(unsigned int segnum, const ElfW(Phdr) *ph, |
| 184 ElfW(Addr) load_bias, size_t pagesize) { |
| 185 if (ph->p_memsz > ph->p_filesz) { |
| 186 ElfW(Addr) file_end = ph->p_vaddr + load_bias + ph->p_filesz; |
| 187 ElfW(Addr) file_page_end = round_up(file_end, pagesize); |
| 188 ElfW(Addr) page_end = round_up(ph->p_vaddr + load_bias + |
| 189 ph->p_memsz, pagesize); |
| 190 if (page_end > file_page_end) |
| 191 my_mmap("bss segment", segnum, |
| 192 file_page_end, page_end - file_page_end, |
| 193 prot_from_phdr(ph), MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0); |
| 194 if (file_page_end > file_end && (ph->p_flags & PF_W)) |
| 195 my_bzero((void *) file_end, file_page_end - file_end); |
| 196 } |
| 197 } |
| 198 |
| 199 /* |
| 200 * This is the main loading code. It's called with the address of the |
| 201 * auxiliary vector on the stack, which we need to examine and modify. |
| 202 * It returns the dynamic linker's runtime entry point address, where |
| 203 * we should jump to. This is called by the machine-dependent _start |
| 204 * code (below). On return, it restores the original stack pointer |
| 205 * and jumps to this entry point. |
| 206 */ |
| 207 ElfW(Addr) do_load(ElfW(auxv_t) *auxv) { |
| 208 /* |
| 209 * Record the auxv entries that are specific to the file loaded. |
| 210 * The incoming entries point to our own static executable. |
| 211 */ |
| 212 ElfW(auxv_t) *av_entry = NULL; |
| 213 ElfW(auxv_t) *av_phdr = NULL; |
| 214 ElfW(auxv_t) *av_phnum = NULL; |
| 215 size_t pagesize = 0; |
| 216 |
| 217 ElfW(auxv_t) *av; |
| 218 for (av = auxv; |
| 219 av_entry == NULL || av_phdr == NULL || av_phnum == NULL || pagesize == 0; |
| 220 ++av) { |
| 221 switch (av->a_type) { |
| 222 case AT_NULL: |
| 223 fail("Failed to find AT_ENTRY, AT_PHDR, AT_PHNUM, or AT_PAGESZ!", |
| 224 NULL, 0, NULL, 0); |
| 225 /*NOTREACHED*/ |
| 226 break; |
| 227 case AT_ENTRY: |
| 228 av_entry = av; |
| 229 break; |
| 230 case AT_PAGESZ: |
| 231 pagesize = av->a_un.a_val; |
| 232 break; |
| 233 case AT_PHDR: |
| 234 av_phdr = av; |
| 235 break; |
| 236 case AT_PHNUM: |
| 237 av_phnum = av; |
| 238 break; |
| 239 } |
| 240 } |
| 241 |
| 242 int fd = my_open(DYNAMIC_LINKER, O_RDONLY); |
| 243 |
| 244 ElfW(Ehdr) ehdr; |
| 245 my_pread("Failed to read ELF header from dynamic linker! ", |
| 246 fd, &ehdr, sizeof(ehdr), 0); |
| 247 |
| 248 if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || |
| 249 ehdr.e_ident[EI_MAG1] != ELFMAG1 || |
| 250 ehdr.e_ident[EI_MAG2] != ELFMAG2 || |
| 251 ehdr.e_ident[EI_MAG3] != ELFMAG3 || |
| 252 ehdr.e_version != EV_CURRENT || |
| 253 ehdr.e_ehsize != sizeof(ehdr) || |
| 254 ehdr.e_phentsize != sizeof(ElfW(Phdr))) |
| 255 fail("Dynamic linker has no valid ELF header!", NULL, 0, NULL, 0); |
| 256 |
| 257 switch (ehdr.e_machine) { |
| 258 #if defined(__i386__) |
| 259 case EM_386: |
| 260 #elif defined(__x86_64__) |
| 261 case EM_X86_64: |
| 262 #elif defined(__arm__) |
| 263 case EM_ARM: |
| 264 #else |
| 265 # error "Don't know the e_machine value for this architecture!" |
| 266 #endif |
| 267 break; |
| 268 default: |
| 269 fail("Dynamic linker has wrong architecture! ", |
| 270 "e_machine", ehdr.e_machine, NULL, 0); |
| 271 break; |
| 272 } |
| 273 |
| 274 ElfW(Phdr) phdr[MAX_PHNUM]; |
| 275 if (ehdr.e_phnum > sizeof(phdr) / sizeof(phdr[0]) || ehdr.e_phnum < 1) |
| 276 fail("Dynamic linker has unreasonable ", |
| 277 "e_phnum", ehdr.e_phnum, NULL, 0); |
| 278 |
| 279 if (ehdr.e_type != ET_DYN) |
| 280 fail("Dynamic linker not ET_DYN! ", |
| 281 "e_type", ehdr.e_type, NULL, 0); |
| 282 |
| 283 my_pread("Failed to read program headers from dynamic linker! ", |
| 284 fd, phdr, sizeof(phdr[0]) * ehdr.e_phnum, ehdr.e_phoff); |
| 285 |
| 286 size_t i = 0; |
| 287 while (i < ehdr.e_phnum && phdr[i].p_type != PT_LOAD) |
| 288 ++i; |
| 289 if (i == ehdr.e_phnum) |
| 290 fail("Dynamic linker has no PT_LOAD header!", |
| 291 NULL, 0, NULL, 0); |
| 292 |
| 293 /* |
| 294 * ELF requires that PT_LOAD segments be in ascending order of p_vaddr. |
| 295 * Find the last one to calculate the whole address span of the image. |
| 296 */ |
| 297 const ElfW(Phdr) *first_load = &phdr[i]; |
| 298 const ElfW(Phdr) *last_load = &phdr[ehdr.e_phnum - 1]; |
| 299 while (last_load > first_load && last_load->p_type != PT_LOAD) |
| 300 --last_load; |
| 301 |
| 302 size_t span = last_load->p_vaddr + last_load->p_memsz - first_load->p_vaddr; |
| 303 |
| 304 /* |
| 305 * Map the first segment and reserve the space used for the rest and |
| 306 * for holes between segments. |
| 307 */ |
| 308 const uintptr_t mapping = my_mmap("segment", first_load - phdr, |
| 309 round_down(first_load->p_vaddr, pagesize), |
| 310 span, prot_from_phdr(first_load), |
| 311 MAP_PRIVATE, fd, |
| 312 round_down(first_load->p_offset, pagesize)); |
| 313 |
| 314 const ElfW(Addr) load_bias = mapping - round_down(first_load->p_vaddr, |
| 315 pagesize); |
| 316 |
| 317 if (first_load->p_offset > ehdr.e_phoff || |
| 318 first_load->p_filesz < ehdr.e_phoff + (ehdr.e_phnum * sizeof(ElfW(Phdr)))) |
| 319 fail("First load segment of dynamic linker does not contain phdrs!", |
| 320 NULL, 0, NULL, 0); |
| 321 |
| 322 /* Point the auxv elements at the dynamic linker's phdrs and entry. */ |
| 323 av_phdr->a_un.a_val = (ehdr.e_phoff - first_load->p_offset + |
| 324 first_load->p_vaddr + load_bias); |
| 325 av_phnum->a_un.a_val = ehdr.e_phnum; |
| 326 av_entry->a_un.a_val = ehdr.e_entry + load_bias; |
| 327 |
| 328 handle_bss(first_load - phdr, first_load, load_bias, pagesize); |
| 329 |
| 330 ElfW(Addr) last_end = first_load->p_vaddr + load_bias + first_load->p_memsz; |
| 331 |
| 332 /* |
| 333 * Map the remaining segments, and protect any holes between them. |
| 334 */ |
| 335 const ElfW(Phdr) *ph; |
| 336 for (ph = first_load + 1; ph <= last_load; ++ph) { |
| 337 if (ph->p_type == PT_LOAD) { |
| 338 ElfW(Addr) last_page_end = round_up(last_end, pagesize); |
| 339 |
| 340 last_end = ph->p_vaddr + load_bias + ph->p_memsz; |
| 341 ElfW(Addr) start = round_down(ph->p_vaddr + load_bias, pagesize); |
| 342 ElfW(Addr) end = round_up(last_end, pagesize); |
| 343 |
| 344 if (start > last_page_end) |
| 345 my_mprotect(ph - phdr, last_page_end, start - last_page_end, PROT_NONE); |
| 346 |
| 347 my_mmap("segment", ph - phdr, |
| 348 start, end - start, |
| 349 prot_from_phdr(ph), MAP_PRIVATE | MAP_FIXED, fd, |
| 350 round_down(ph->p_offset, pagesize)); |
| 351 |
| 352 handle_bss(ph - phdr, ph, load_bias, pagesize); |
| 353 } |
| 354 } |
| 355 |
| 356 sys_close(fd); |
| 357 |
| 358 return ehdr.e_entry + load_bias; |
| 359 } |
| 360 |
| 361 /* |
| 362 * We have to define the actual entry point code (_start) in assembly |
| 363 * for each machine. The kernel startup protocol is not compatible |
| 364 * with the normal C function calling convention. Here, we calculate |
| 365 * the address of the auxiliary vector on the stack; call do_load |
| 366 * (above) using the normal C convention as per the ABI; restore the |
| 367 * original starting stack; and finally, jump to the dynamic linker's |
| 368 * entry point address. |
| 369 */ |
| 370 #if defined(__i386__) |
| 371 asm(".globl _start\n" |
| 372 ".type _start,@function\n" |
| 373 "_start:\n" |
| 374 "xorl %ebp, %ebp\n" |
| 375 "movl %esp, %ebx\n" /* Save starting SP in %ebx. */ |
| 376 "andl $-16, %esp\n" /* Align the stack as per ABI. */ |
| 377 "movl (%ebx), %eax\n" /* argc */ |
| 378 "leal 8(%ebx,%eax,4), %ecx\n" /* envp */ |
| 379 /* Find the envp element that is NULL, and auxv is past there. */ |
| 380 "0: addl $4, %ecx\n" |
| 381 "cmpl $0, -4(%ecx)\n" |
| 382 "jne 0b\n" |
| 383 "pushl %ecx\n" /* Argument: auxv. */ |
| 384 "call do_load\n" |
| 385 "movl %ebx, %esp\n" /* Restore the saved SP. */ |
| 386 "jmp *%eax\n" /* Jump to the entry point. */ |
| 387 ); |
| 388 #elif defined(__x86_64__) |
| 389 asm(".globl _start\n" |
| 390 ".type _start,@function\n" |
| 391 "_start:\n" |
| 392 "xorq %rbp, %rbp\n" |
| 393 "movq %rsp, %rbx\n" /* Save starting SP in %rbx. */ |
| 394 "andq $-16, %rsp\n" /* Align the stack as per ABI. */ |
| 395 "movq (%rbx), %rax\n" /* argc */ |
| 396 "leaq 16(%rbx,%rax,8), %rdi\n" /* envp */ |
| 397 /* Find the envp element that is NULL, and auxv is past there. */ |
| 398 "0: addq $8, %rdi\n" |
| 399 "cmpq $0, -8(%rdi)\n" |
| 400 "jne 0b\n" |
| 401 "call do_load\n" /* Argument already in %rdi: auxv */ |
| 402 "movq %rbx, %rsp\n" /* Restore the saved SP. */ |
| 403 "jmp *%rax\n" /* Jump to the entry point. */ |
| 404 ); |
| 405 #elif defined(__arm__) |
| 406 asm(".globl _start\n" |
| 407 ".type _start,#function\n" |
| 408 "_start:\n" |
| 409 #if defined(__thumb2__) |
| 410 ".thumb\n" |
| 411 ".syntax unified\n" |
| 412 #endif |
| 413 "mov fp, #0\n" |
| 414 "mov lr, #0\n" |
| 415 "mov r4, sp\n" /* Save starting SP in r4. */ |
| 416 "ldr r1, [r4]\n" /* argc */ |
| 417 "add r1, r1, #2\n" |
| 418 "add r0, r4, r1, asl #2\n" /* envp */ |
| 419 /* Find the envp element that is NULL, and auxv is past there. */ |
| 420 "0: ldr r1, [r0], #4\n" |
| 421 "cmp r1, #0\n" |
| 422 "bne 0b\n" |
| 423 "bl do_load\n" |
| 424 "mov sp, r4\n" /* Restore the saved SP. */ |
| 425 "blx r0\n" /* Jump to the entry point. */ |
| 426 ); |
| 427 #else |
| 428 # error "Need stack-preserving _start code for this architecture!" |
| 429 #endif |
OLD | NEW |