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

Side by Side Diff: chrome/nacl/nacl_helper_bootstrap_linux.c

Issue 8800034: Use nacl_helper_bootstrap from native_client repository (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased Created 9 years 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/nacl.gypi ('k') | chrome/nacl/nacl_helper_bootstrap_linux.x » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 * found in the LICENSE file.
4 *
5 * This is a standalone program that loads and runs the dynamic linker.
6 * This program itself must be linked statically. To keep it small, it's
7 * written to avoid all dependencies on libc and standard startup code.
8 * Hence, this should be linked using -nostartfiles. It must be compiled
9 * with -fno-stack-protector to ensure the compiler won't emit code that
10 * presumes some special setup has been done.
11 *
12 * On ARM, the compiler will emit calls to some libc functions, so we
13 * cannot link with -nostdlib. The functions it does use (memset and
14 * __aeabi_* functions for integer division) are sufficiently small and
15 * self-contained in ARM's libc.a that we don't have any problem using
16 * the libc definitions though we aren't using the rest of libc or doing
17 * any of the setup it might expect.
18 */
19
20 #include <elf.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <link.h>
24 #include <stddef.h>
25 #include <stdint.h>
26 #include <sys/mman.h>
27
28 #define MAX_PHNUM 12
29
30 /*
31 * This exact magic argument string is recognized in check_r_debug_arg, below.
32 * Requiring the argument to have those Xs as a template both simplifies
33 * our argument matching code and saves us from having to reformat the
34 * whole stack to find space for a string longer than the original argument.
35 */
36 #define R_DEBUG_TEMPLATE_PREFIX "--r_debug=0x"
37 #define R_DEBUG_TEMPLATE_DIGITS "XXXXXXXXXXXXXXXX"
38 static const char kRDebugTemplate[] =
39 R_DEBUG_TEMPLATE_PREFIX R_DEBUG_TEMPLATE_DIGITS;
40 static const size_t kRDebugPrefixLen = sizeof(R_DEBUG_TEMPLATE_PREFIX) - 1;
41
42
43 /*
44 * We're not using <string.h> functions here, to avoid dependencies.
45 * In the x86 libc, even "simple" functions like memset and strlen can
46 * depend on complex startup code, because in newer libc
47 * implementations they are defined using STT_GNU_IFUNC.
48 */
49
50 static void my_bzero(void *buf, size_t n) {
51 char *p = buf;
52 while (n-- > 0)
53 *p++ = 0;
54 }
55
56 static size_t my_strlen(const char *s) {
57 size_t n = 0;
58 while (*s++ != '\0')
59 ++n;
60 return n;
61 }
62
63 static int my_strcmp(const char *a, const char *b) {
64 while (*a == *b) {
65 if (*a == '\0')
66 return 0;
67 ++a;
68 ++b;
69 }
70 return (int) (unsigned char) *a - (int) (unsigned char) *b;
71 }
72
73
74 /*
75 * Get inline functions for system calls.
76 */
77 static int my_errno;
78 #define SYS_ERRNO my_errno
79 #include "third_party/lss/linux_syscall_support.h"
80
81
82 /*
83 * We're avoiding libc, so no printf. The only nontrivial thing we need
84 * is rendering numbers, which is, in fact, pretty trivial.
85 */
86 static void iov_int_string(int value, struct kernel_iovec *iov,
87 char *buf, size_t bufsz) {
88 char *p = &buf[bufsz];
89 do {
90 --p;
91 *p = "0123456789"[value % 10];
92 value /= 10;
93 } while (value != 0);
94 iov->iov_base = p;
95 iov->iov_len = &buf[bufsz] - p;
96 }
97
98 #define STRING_IOV(string_constant, cond) \
99 { (void *) string_constant, cond ? (sizeof(string_constant) - 1) : 0 }
100
101 __attribute__((noreturn)) static void fail(const char *filename,
102 const char *message,
103 const char *item1, int value1,
104 const char *item2, int value2) {
105 char valbuf1[32];
106 char valbuf2[32];
107 struct kernel_iovec iov[] = {
108 STRING_IOV("bootstrap_helper: ", 1),
109 { (void *) filename, my_strlen(filename) },
110 STRING_IOV(": ", 1),
111 { (void *) message, my_strlen(message) },
112 { (void *) item1, item1 == NULL ? 0 : my_strlen(item1) },
113 STRING_IOV("=", item1 != NULL),
114 {},
115 STRING_IOV(", ", item1 != NULL && item2 != NULL),
116 { (void *) item2, item2 == NULL ? 0 : my_strlen(item2) },
117 STRING_IOV("=", item2 != NULL),
118 {},
119 { "\n", 1 },
120 };
121 const int niov = sizeof(iov) / sizeof(iov[0]);
122
123 if (item1 != NULL)
124 iov_int_string(value1, &iov[6], valbuf1, sizeof(valbuf1));
125 if (item2 != NULL)
126 iov_int_string(value1, &iov[10], valbuf2, sizeof(valbuf2));
127
128 sys_writev(2, iov, niov);
129 sys_exit_group(2);
130 while (1) *(volatile int *) 0 = 0; /* Crash. */
131 }
132
133
134 static int my_open(const char *file, int oflag) {
135 int result = sys_open(file, oflag, 0);
136 if (result < 0)
137 fail(file, "Cannot open ELF file! ", "errno", my_errno, NULL, 0);
138 return result;
139 }
140
141 static void my_pread(const char *file, const char *fail_message,
142 int fd, void *buf, size_t bufsz, uintptr_t pos) {
143 ssize_t result = sys_pread64(fd, buf, bufsz, pos);
144 if (result < 0)
145 fail(file, fail_message, "errno", my_errno, NULL, 0);
146 if ((size_t) result != bufsz)
147 fail(file, fail_message, "read count", result, NULL, 0);
148 }
149
150 static uintptr_t my_mmap(const char *file,
151 const char *segment_type, unsigned int segnum,
152 uintptr_t address, size_t size,
153 int prot, int flags, int fd, uintptr_t pos) {
154 #if defined(__NR_mmap2)
155 void *result = sys_mmap2((void *) address, size, prot, flags, fd, pos >> 12);
156 #else
157 void *result = sys_mmap((void *) address, size, prot, flags, fd, pos);
158 #endif
159 if (result == MAP_FAILED)
160 fail(file, "Failed to map segment! ",
161 segment_type, segnum, "errno", my_errno);
162 return (uintptr_t) result;
163 }
164
165 static void my_mprotect(const char *file, unsigned int segnum,
166 uintptr_t address, size_t size, int prot) {
167 if (sys_mprotect((void *) address, size, prot) < 0)
168 fail(file, "Failed to mprotect segment hole! ",
169 "segment", segnum, "errno", my_errno);
170 }
171
172
173 static int prot_from_phdr(const ElfW(Phdr) *phdr) {
174 int prot = 0;
175 if (phdr->p_flags & PF_R)
176 prot |= PROT_READ;
177 if (phdr->p_flags & PF_W)
178 prot |= PROT_WRITE;
179 if (phdr->p_flags & PF_X)
180 prot |= PROT_EXEC;
181 return prot;
182 }
183
184 static uintptr_t round_up(uintptr_t value, uintptr_t size) {
185 return (value + size - 1) & -size;
186 }
187
188 static uintptr_t round_down(uintptr_t value, uintptr_t size) {
189 return value & -size;
190 }
191
192 /*
193 * Handle the "bss" portion of a segment, where the memory size
194 * exceeds the file size and we zero-fill the difference. For any
195 * whole pages in this region, we over-map anonymous pages. For the
196 * sub-page remainder, we zero-fill bytes directly.
197 */
198 static void handle_bss(const char *file,
199 unsigned int segnum, const ElfW(Phdr) *ph,
200 ElfW(Addr) load_bias, size_t pagesize) {
201 if (ph->p_memsz > ph->p_filesz) {
202 ElfW(Addr) file_end = ph->p_vaddr + load_bias + ph->p_filesz;
203 ElfW(Addr) file_page_end = round_up(file_end, pagesize);
204 ElfW(Addr) page_end = round_up(ph->p_vaddr + load_bias +
205 ph->p_memsz, pagesize);
206 if (page_end > file_page_end)
207 my_mmap(file, "bss segment", segnum,
208 file_page_end, page_end - file_page_end,
209 prot_from_phdr(ph), MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
210 if (file_page_end > file_end && (ph->p_flags & PF_W))
211 my_bzero((void *) file_end, file_page_end - file_end);
212 }
213 }
214
215 /*
216 * Open an ELF file and load it into memory.
217 */
218 static ElfW(Addr) load_elf_file(const char *filename,
219 size_t pagesize,
220 ElfW(Addr) *out_phdr,
221 ElfW(Addr) *out_phnum,
222 const char **out_interp) {
223 int fd = my_open(filename, O_RDONLY);
224
225 ElfW(Ehdr) ehdr;
226 my_pread(filename, "Failed to read ELF header from file! ",
227 fd, &ehdr, sizeof(ehdr), 0);
228
229 if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
230 ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
231 ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
232 ehdr.e_ident[EI_MAG3] != ELFMAG3 ||
233 ehdr.e_version != EV_CURRENT ||
234 ehdr.e_ehsize != sizeof(ehdr) ||
235 ehdr.e_phentsize != sizeof(ElfW(Phdr)))
236 fail(filename, "File has no valid ELF header!", NULL, 0, NULL, 0);
237
238 switch (ehdr.e_machine) {
239 #if defined(__i386__)
240 case EM_386:
241 #elif defined(__x86_64__)
242 case EM_X86_64:
243 #elif defined(__arm__)
244 case EM_ARM:
245 #else
246 # error "Don't know the e_machine value for this architecture!"
247 #endif
248 break;
249 default:
250 fail(filename, "ELF file has wrong architecture! ",
251 "e_machine", ehdr.e_machine, NULL, 0);
252 break;
253 }
254
255 ElfW(Phdr) phdr[MAX_PHNUM];
256 if (ehdr.e_phnum > sizeof(phdr) / sizeof(phdr[0]) || ehdr.e_phnum < 1)
257 fail(filename, "ELF file has unreasonable ",
258 "e_phnum", ehdr.e_phnum, NULL, 0);
259
260 if (ehdr.e_type != ET_DYN)
261 fail(filename, "ELF file not ET_DYN! ",
262 "e_type", ehdr.e_type, NULL, 0);
263
264 my_pread(filename, "Failed to read program headers from ELF file! ",
265 fd, phdr, sizeof(phdr[0]) * ehdr.e_phnum, ehdr.e_phoff);
266
267 size_t i = 0;
268 while (i < ehdr.e_phnum && phdr[i].p_type != PT_LOAD)
269 ++i;
270 if (i == ehdr.e_phnum)
271 fail(filename, "ELF file has no PT_LOAD header!",
272 NULL, 0, NULL, 0);
273
274 /*
275 * ELF requires that PT_LOAD segments be in ascending order of p_vaddr.
276 * Find the last one to calculate the whole address span of the image.
277 */
278 const ElfW(Phdr) *first_load = &phdr[i];
279 const ElfW(Phdr) *last_load = &phdr[ehdr.e_phnum - 1];
280 while (last_load > first_load && last_load->p_type != PT_LOAD)
281 --last_load;
282
283 size_t span = last_load->p_vaddr + last_load->p_memsz - first_load->p_vaddr;
284
285 /*
286 * Map the first segment and reserve the space used for the rest and
287 * for holes between segments.
288 */
289 const uintptr_t mapping = my_mmap(filename, "segment", first_load - phdr,
290 round_down(first_load->p_vaddr, pagesize),
291 span, prot_from_phdr(first_load),
292 MAP_PRIVATE, fd,
293 round_down(first_load->p_offset, pagesize));
294
295 const ElfW(Addr) load_bias = mapping - round_down(first_load->p_vaddr,
296 pagesize);
297
298 if (first_load->p_offset > ehdr.e_phoff ||
299 first_load->p_filesz < ehdr.e_phoff + (ehdr.e_phnum * sizeof(ElfW(Phdr))))
300 fail(filename, "First load segment of ELF file does not contain phdrs!",
301 NULL, 0, NULL, 0);
302
303 handle_bss(filename, first_load - phdr, first_load, load_bias, pagesize);
304
305 ElfW(Addr) last_end = first_load->p_vaddr + load_bias + first_load->p_memsz;
306
307 /*
308 * Map the remaining segments, and protect any holes between them.
309 */
310 const ElfW(Phdr) *ph;
311 for (ph = first_load + 1; ph <= last_load; ++ph) {
312 if (ph->p_type == PT_LOAD) {
313 ElfW(Addr) last_page_end = round_up(last_end, pagesize);
314
315 last_end = ph->p_vaddr + load_bias + ph->p_memsz;
316 ElfW(Addr) start = round_down(ph->p_vaddr + load_bias, pagesize);
317 ElfW(Addr) end = round_up(last_end, pagesize);
318
319 if (start > last_page_end)
320 my_mprotect(filename,
321 ph - phdr, last_page_end, start - last_page_end, PROT_NONE);
322
323 my_mmap(filename, "segment", ph - phdr,
324 start, end - start,
325 prot_from_phdr(ph), MAP_PRIVATE | MAP_FIXED, fd,
326 round_down(ph->p_offset, pagesize));
327
328 handle_bss(filename, ph - phdr, ph, load_bias, pagesize);
329 }
330 }
331
332 if (out_interp != NULL) {
333 /*
334 * Find the PT_INTERP header, if there is one.
335 */
336 for (i = 0; i < ehdr.e_phnum; ++i) {
337 if (phdr[i].p_type == PT_INTERP) {
338 /*
339 * The PT_INTERP isn't really required to sit inside the first
340 * (or any) load segment, though it normally does. So we can
341 * easily avoid an extra read in that case.
342 */
343 if (phdr[i].p_offset >= first_load->p_offset &&
344 phdr[i].p_filesz <= first_load->p_filesz) {
345 *out_interp = (const char *) (phdr[i].p_vaddr + load_bias);
346 } else {
347 static char interp_buffer[PATH_MAX + 1];
348 if (phdr[i].p_filesz >= sizeof(interp_buffer)) {
349 fail(filename, "ELF file has unreasonable PT_INTERP size! ",
350 "segment", i, "p_filesz", phdr[i].p_filesz);
351 }
352 my_pread(filename, "Cannot read PT_INTERP segment contents!",
353 fd, interp_buffer, phdr[i].p_filesz, phdr[i].p_offset);
354 *out_interp = interp_buffer;
355 }
356 break;
357 }
358 }
359 }
360
361 sys_close(fd);
362
363 if (out_phdr != NULL)
364 *out_phdr = (ehdr.e_phoff - first_load->p_offset +
365 first_load->p_vaddr + load_bias);
366 if (out_phnum != NULL)
367 *out_phnum = ehdr.e_phnum;
368
369 return ehdr.e_entry + load_bias;
370 }
371
372
373 /*
374 * GDB looks for this symbol name when it cannot find PT_DYNAMIC->DT_DEBUG.
375 * We don't have a PT_DYNAMIC, so it will find this. Now all we have to do
376 * is arrange for this space to be filled in with the dynamic linker's
377 * _r_debug contents after they're initialized. That way, attaching GDB to
378 * this process or examining its core file will find the PIE we loaded, the
379 * dynamic linker, and all the shared libraries, making debugging pleasant.
380 */
381 struct r_debug _r_debug __attribute__((nocommon, section(".r_debug")));
382
383 /*
384 * If the argument matches the kRDebugTemplate string, then replace
385 * the 16 Xs with the hexadecimal address of our _r_debug variable.
386 */
387 static int check_r_debug_arg(char *arg) {
388 if (my_strcmp(arg, kRDebugTemplate) == 0) {
389 uintptr_t addr = (uintptr_t) &_r_debug;
390 size_t i = 16;
391 while (i-- > 0) {
392 arg[kRDebugPrefixLen + i] = "0123456789abcdef"[addr & 0xf];
393 addr >>= 4;
394 }
395 return 1;
396 }
397 return 0;
398 }
399
400
401 /*
402 * This is the main loading code. It's called with the starting stack pointer.
403 * This points to a sequence of pointer-size words:
404 * [0] argc
405 * [1..argc] argv[0..argc-1]
406 * [1+argc] NULL
407 * [2+argc..] envp[0..]
408 * NULL
409 * auxv[0].a_type
410 * auxv[1].a_un.a_val
411 * ...
412 * It returns the dynamic linker's runtime entry point address, where
413 * we should jump to. This is called by the machine-dependent _start
414 * code (below). On return, it restores the original stack pointer
415 * and jumps to this entry point.
416 *
417 * argv[0] is the uninteresting name of this bootstrap program. argv[1] is
418 * the real program file name we'll open, and also the argv[0] for that
419 * program. We need to modify argc, move argv[1..] back to the argv[0..]
420 * position, and also examine and modify the auxiliary vector on the stack.
421 */
422 ElfW(Addr) do_load(uintptr_t *stack) {
423 size_t i;
424
425 /*
426 * First find the end of the auxiliary vector.
427 */
428 int argc = stack[0];
429 char **argv = (char **) &stack[1];
430 const char *program = argv[1];
431 char **envp = &argv[argc + 1];
432 char **ep = envp;
433 while (*ep != NULL)
434 ++ep;
435 ElfW(auxv_t) *auxv = (ElfW(auxv_t) *) (ep + 1);
436 ElfW(auxv_t) *av = auxv;
437 while (av->a_type != AT_NULL)
438 ++av;
439 size_t stack_words = (uintptr_t *) (av + 1) - &stack[1];
440
441 if (argc < 2)
442 fail("Usage", "PROGRAM ARGS...", NULL, 0, NULL, 0);
443
444 /*
445 * Now move everything back to eat our original argv[0]. When we've done
446 * that, envp and auxv will start one word back from where they were.
447 */
448 --argc;
449 --envp;
450 auxv = (ElfW(auxv_t) *) ep;
451 stack[0] = argc;
452 for (i = 1; i < stack_words; ++i)
453 stack[i] = stack[i + 1];
454
455 /*
456 * If one of our arguments is the kRDebugTemplate string, then
457 * we'll modify that argument string in place to specify the
458 * address of our _r_debug structure.
459 */
460 for (i = 1; i < argc; ++i) {
461 if (check_r_debug_arg(argv[i]))
462 break;
463 }
464
465 /*
466 * Record the auxv entries that are specific to the file loaded.
467 * The incoming entries point to our own static executable.
468 */
469 ElfW(auxv_t) *av_entry = NULL;
470 ElfW(auxv_t) *av_phdr = NULL;
471 ElfW(auxv_t) *av_phnum = NULL;
472 size_t pagesize = 0;
473
474 for (av = auxv;
475 av_entry == NULL || av_phdr == NULL || av_phnum == NULL || pagesize == 0;
476 ++av) {
477 switch (av->a_type) {
478 case AT_NULL:
479 fail("startup",
480 "Failed to find AT_ENTRY, AT_PHDR, AT_PHNUM, or AT_PAGESZ!",
481 NULL, 0, NULL, 0);
482 /*NOTREACHED*/
483 break;
484 case AT_ENTRY:
485 av_entry = av;
486 break;
487 case AT_PAGESZ:
488 pagesize = av->a_un.a_val;
489 break;
490 case AT_PHDR:
491 av_phdr = av;
492 break;
493 case AT_PHNUM:
494 av_phnum = av;
495 break;
496 }
497 }
498
499 /* Load the program and point the auxv elements at its phdrs and entry. */
500 const char *interp = NULL;
501 av_entry->a_un.a_val = load_elf_file(program,
502 pagesize,
503 &av_phdr->a_un.a_val,
504 &av_phnum->a_un.a_val,
505 &interp);
506
507 ElfW(Addr) entry = av_entry->a_un.a_val;
508
509 if (interp != NULL) {
510 /*
511 * There was a PT_INTERP, so we have a dynamic linker to load.
512 */
513 entry = load_elf_file(interp, pagesize, NULL, NULL, NULL);
514 }
515
516 return entry;
517 }
518
519 /*
520 * We have to define the actual entry point code (_start) in assembly for
521 * each machine. The kernel startup protocol is not compatible with the
522 * normal C function calling convention. Here, we call do_load (above)
523 * using the normal C convention as per the ABI, with the starting stack
524 * pointer as its argument; restore the original starting stack; and
525 * finally, jump to the dynamic linker's entry point address.
526 */
527 #if defined(__i386__)
528 asm(".pushsection \".text\",\"ax\",@progbits\n"
529 ".globl _start\n"
530 ".type _start,@function\n"
531 "_start:\n"
532 "xorl %ebp, %ebp\n"
533 "movl %esp, %ebx\n" /* Save starting SP in %ebx. */
534 "andl $-16, %esp\n" /* Align the stack as per ABI. */
535 "pushl %ebx\n" /* Argument: stack block. */
536 "call do_load\n"
537 "movl %ebx, %esp\n" /* Restore the saved SP. */
538 "jmp *%eax\n" /* Jump to the entry point. */
539 ".popsection"
540 );
541 #elif defined(__x86_64__)
542 asm(".pushsection \".text\",\"ax\",@progbits\n"
543 ".globl _start\n"
544 ".type _start,@function\n"
545 "_start:\n"
546 "xorq %rbp, %rbp\n"
547 "movq %rsp, %rbx\n" /* Save starting SP in %rbx. */
548 "andq $-16, %rsp\n" /* Align the stack as per ABI. */
549 "movq %rbx, %rdi\n" /* Argument: stack block. */
550 "call do_load\n"
551 "movq %rbx, %rsp\n" /* Restore the saved SP. */
552 "jmp *%rax\n" /* Jump to the entry point. */
553 ".popsection"
554 );
555 #elif defined(__arm__)
556 asm(".pushsection \".text\",\"ax\",%progbits\n"
557 ".globl _start\n"
558 ".type _start,#function\n"
559 "_start:\n"
560 #if defined(__thumb2__)
561 ".thumb\n"
562 ".syntax unified\n"
563 #endif
564 "mov fp, #0\n"
565 "mov lr, #0\n"
566 "mov r4, sp\n" /* Save starting SP in r4. */
567 "mov r0, sp\n" /* Argument: stack block. */
568 "bl do_load\n"
569 "mov sp, r4\n" /* Restore the saved SP. */
570 "blx r0\n" /* Jump to the entry point. */
571 ".popsection"
572 );
573 #else
574 # error "Need stack-preserving _start code for this architecture!"
575 #endif
OLDNEW
« no previous file with comments | « chrome/nacl.gypi ('k') | chrome/nacl/nacl_helper_bootstrap_linux.x » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698