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