OLD | NEW |
| (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 | |
OLD | NEW |