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

Side by Side 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: fix mmap use for x86-64 Created 9 years, 3 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698