| 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 |