]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * ELF loading code | |
3 | * | |
4 | * Copyright (c) 2013 Stacey D. Son | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "qemu/osdep.h" | |
21 | ||
22 | #include "qemu.h" | |
23 | #include "disas/disas.h" | |
24 | #include "qemu/path.h" | |
25 | ||
26 | static abi_ulong target_auxents; /* Where the AUX entries are in target */ | |
27 | static size_t target_auxents_sz; /* Size of AUX entries including AT_NULL */ | |
28 | ||
29 | #include "target_arch_reg.h" | |
30 | #include "target_os_elf.h" | |
31 | #include "target_os_stack.h" | |
32 | #include "target_os_thread.h" | |
33 | #include "target_os_user.h" | |
34 | ||
35 | abi_ulong target_stksiz; | |
36 | abi_ulong target_stkbas; | |
37 | ||
38 | static int elf_core_dump(int signr, CPUArchState *env); | |
39 | static int load_elf_sections(const struct elfhdr *hdr, struct elf_phdr *phdr, | |
40 | int fd, abi_ulong rbase, abi_ulong *baddrp); | |
41 | ||
42 | static inline void memcpy_fromfs(void *to, const void *from, unsigned long n) | |
43 | { | |
44 | memcpy(to, from, n); | |
45 | } | |
46 | ||
47 | #ifdef BSWAP_NEEDED | |
48 | static void bswap_ehdr(struct elfhdr *ehdr) | |
49 | { | |
50 | bswap16s(&ehdr->e_type); /* Object file type */ | |
51 | bswap16s(&ehdr->e_machine); /* Architecture */ | |
52 | bswap32s(&ehdr->e_version); /* Object file version */ | |
53 | bswaptls(&ehdr->e_entry); /* Entry point virtual address */ | |
54 | bswaptls(&ehdr->e_phoff); /* Program header table file offset */ | |
55 | bswaptls(&ehdr->e_shoff); /* Section header table file offset */ | |
56 | bswap32s(&ehdr->e_flags); /* Processor-specific flags */ | |
57 | bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ | |
58 | bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ | |
59 | bswap16s(&ehdr->e_phnum); /* Program header table entry count */ | |
60 | bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ | |
61 | bswap16s(&ehdr->e_shnum); /* Section header table entry count */ | |
62 | bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ | |
63 | } | |
64 | ||
65 | static void bswap_phdr(struct elf_phdr *phdr, int phnum) | |
66 | { | |
67 | int i; | |
68 | ||
69 | for (i = 0; i < phnum; i++, phdr++) { | |
70 | bswap32s(&phdr->p_type); /* Segment type */ | |
71 | bswap32s(&phdr->p_flags); /* Segment flags */ | |
72 | bswaptls(&phdr->p_offset); /* Segment file offset */ | |
73 | bswaptls(&phdr->p_vaddr); /* Segment virtual address */ | |
74 | bswaptls(&phdr->p_paddr); /* Segment physical address */ | |
75 | bswaptls(&phdr->p_filesz); /* Segment size in file */ | |
76 | bswaptls(&phdr->p_memsz); /* Segment size in memory */ | |
77 | bswaptls(&phdr->p_align); /* Segment alignment */ | |
78 | } | |
79 | } | |
80 | ||
81 | static void bswap_shdr(struct elf_shdr *shdr, int shnum) | |
82 | { | |
83 | int i; | |
84 | ||
85 | for (i = 0; i < shnum; i++, shdr++) { | |
86 | bswap32s(&shdr->sh_name); | |
87 | bswap32s(&shdr->sh_type); | |
88 | bswaptls(&shdr->sh_flags); | |
89 | bswaptls(&shdr->sh_addr); | |
90 | bswaptls(&shdr->sh_offset); | |
91 | bswaptls(&shdr->sh_size); | |
92 | bswap32s(&shdr->sh_link); | |
93 | bswap32s(&shdr->sh_info); | |
94 | bswaptls(&shdr->sh_addralign); | |
95 | bswaptls(&shdr->sh_entsize); | |
96 | } | |
97 | } | |
98 | ||
99 | static void bswap_sym(struct elf_sym *sym) | |
100 | { | |
101 | bswap32s(&sym->st_name); | |
102 | bswaptls(&sym->st_value); | |
103 | bswaptls(&sym->st_size); | |
104 | bswap16s(&sym->st_shndx); | |
105 | } | |
106 | ||
107 | static void bswap_note(struct elf_note *en) | |
108 | { | |
109 | bswap32s(&en->n_namesz); | |
110 | bswap32s(&en->n_descsz); | |
111 | bswap32s(&en->n_type); | |
112 | } | |
113 | ||
114 | #else /* ! BSWAP_NEEDED */ | |
115 | ||
116 | static void bswap_ehdr(struct elfhdr *ehdr) { } | |
117 | static void bswap_phdr(struct elf_phdr *phdr, int phnum) { } | |
118 | static void bswap_shdr(struct elf_shdr *shdr, int shnum) { } | |
119 | static void bswap_sym(struct elf_sym *sym) { } | |
120 | static void bswap_note(struct elf_note *en) { } | |
121 | ||
122 | #endif /* ! BSWAP_NEEDED */ | |
123 | ||
124 | #include "elfcore.c" | |
125 | ||
126 | /* | |
127 | * 'copy_elf_strings()' copies argument/envelope strings from user | |
128 | * memory to free pages in kernel mem. These are in a format ready | |
129 | * to be put directly into the top of new user memory. | |
130 | * | |
131 | */ | |
132 | static abi_ulong copy_elf_strings(int argc, char **argv, void **page, | |
133 | abi_ulong p) | |
134 | { | |
135 | char *tmp, *tmp1, *pag = NULL; | |
136 | int len, offset = 0; | |
137 | ||
138 | if (!p) { | |
139 | return 0; /* bullet-proofing */ | |
140 | } | |
141 | while (argc-- > 0) { | |
142 | tmp = argv[argc]; | |
143 | if (!tmp) { | |
144 | fprintf(stderr, "VFS: argc is wrong"); | |
145 | exit(-1); | |
146 | } | |
147 | tmp1 = tmp; | |
148 | while (*tmp++) { | |
149 | continue; | |
150 | } | |
151 | len = tmp - tmp1; | |
152 | if (p < len) { /* this shouldn't happen - 128kB */ | |
153 | return 0; | |
154 | } | |
155 | while (len) { | |
156 | --p; --tmp; --len; | |
157 | if (--offset < 0) { | |
158 | offset = p % TARGET_PAGE_SIZE; | |
159 | pag = page[p / TARGET_PAGE_SIZE]; | |
160 | if (!pag) { | |
161 | pag = g_try_malloc0(TARGET_PAGE_SIZE); | |
162 | page[p / TARGET_PAGE_SIZE] = pag; | |
163 | if (!pag) { | |
164 | return 0; | |
165 | } | |
166 | } | |
167 | } | |
168 | if (len == 0 || offset == 0) { | |
169 | *(pag + offset) = *tmp; | |
170 | } else { | |
171 | int bytes_to_copy = (len > offset) ? offset : len; | |
172 | tmp -= bytes_to_copy; | |
173 | p -= bytes_to_copy; | |
174 | offset -= bytes_to_copy; | |
175 | len -= bytes_to_copy; | |
176 | memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1); | |
177 | } | |
178 | } | |
179 | } | |
180 | return p; | |
181 | } | |
182 | ||
183 | static void setup_arg_pages(struct bsd_binprm *bprm, struct image_info *info, | |
184 | abi_ulong *stackp, abi_ulong *stringp) | |
185 | { | |
186 | abi_ulong stack_base, size; | |
187 | abi_long addr; | |
188 | ||
189 | /* | |
190 | * Create enough stack to hold everything. If we don't use it for args, | |
191 | * we'll use it for something else... | |
192 | */ | |
193 | size = target_dflssiz; | |
194 | stack_base = TARGET_USRSTACK - size; | |
195 | addr = target_mmap(stack_base , size + qemu_host_page_size, | |
196 | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); | |
197 | if (addr == -1) { | |
198 | perror("stk mmap"); | |
199 | exit(-1); | |
200 | } | |
201 | /* we reserve one extra page at the top of the stack as guard */ | |
202 | target_mprotect(addr + size, qemu_host_page_size, PROT_NONE); | |
203 | ||
204 | target_stksiz = size; | |
205 | target_stkbas = addr; | |
206 | ||
207 | if (setup_initial_stack(bprm, stackp, stringp) != 0) { | |
208 | perror("stk setup"); | |
209 | exit(-1); | |
210 | } | |
211 | } | |
212 | ||
213 | static void set_brk(abi_ulong start, abi_ulong end) | |
214 | { | |
215 | /* page-align the start and end addresses... */ | |
216 | start = HOST_PAGE_ALIGN(start); | |
217 | end = HOST_PAGE_ALIGN(end); | |
218 | if (end <= start) { | |
219 | return; | |
220 | } | |
221 | if (target_mmap(start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC, | |
222 | MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == -1) { | |
223 | perror("cannot mmap brk"); | |
224 | exit(-1); | |
225 | } | |
226 | } | |
227 | ||
228 | ||
229 | /* | |
230 | * We need to explicitly zero any fractional pages after the data | |
231 | * section (i.e. bss). This would contain the junk from the file that | |
232 | * should not be in memory. | |
233 | */ | |
234 | static void padzero(abi_ulong elf_bss, abi_ulong last_bss) | |
235 | { | |
236 | abi_ulong nbyte; | |
237 | ||
238 | if (elf_bss >= last_bss) { | |
239 | return; | |
240 | } | |
241 | ||
242 | /* | |
243 | * XXX: this is really a hack : if the real host page size is | |
244 | * smaller than the target page size, some pages after the end | |
245 | * of the file may not be mapped. A better fix would be to | |
246 | * patch target_mmap(), but it is more complicated as the file | |
247 | * size must be known. | |
248 | */ | |
249 | if (qemu_real_host_page_size() < qemu_host_page_size) { | |
250 | abi_ulong end_addr, end_addr1; | |
251 | end_addr1 = REAL_HOST_PAGE_ALIGN(elf_bss); | |
252 | end_addr = HOST_PAGE_ALIGN(elf_bss); | |
253 | if (end_addr1 < end_addr) { | |
254 | mmap((void *)g2h_untagged(end_addr1), end_addr - end_addr1, | |
255 | PROT_READ | PROT_WRITE | PROT_EXEC, | |
256 | MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0); | |
257 | } | |
258 | } | |
259 | ||
260 | nbyte = elf_bss & (qemu_host_page_size - 1); | |
261 | if (nbyte) { | |
262 | nbyte = qemu_host_page_size - nbyte; | |
263 | do { | |
264 | /* FIXME - what to do if put_user() fails? */ | |
265 | put_user_u8(0, elf_bss); | |
266 | elf_bss++; | |
267 | } while (--nbyte); | |
268 | } | |
269 | } | |
270 | ||
271 | static abi_ulong load_elf_interp(struct elfhdr *interp_elf_ex, | |
272 | int interpreter_fd, | |
273 | abi_ulong *interp_load_addr) | |
274 | { | |
275 | struct elf_phdr *elf_phdata = NULL; | |
276 | abi_ulong rbase; | |
277 | int retval; | |
278 | abi_ulong baddr, error; | |
279 | ||
280 | error = 0; | |
281 | ||
282 | bswap_ehdr(interp_elf_ex); | |
283 | /* First of all, some simple consistency checks */ | |
284 | if ((interp_elf_ex->e_type != ET_EXEC && interp_elf_ex->e_type != ET_DYN) || | |
285 | !elf_check_arch(interp_elf_ex->e_machine)) { | |
286 | return ~((abi_ulong)0UL); | |
287 | } | |
288 | ||
289 | ||
290 | /* Now read in all of the header information */ | |
291 | if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE) { | |
292 | return ~(abi_ulong)0UL; | |
293 | } | |
294 | ||
295 | elf_phdata = (struct elf_phdr *) malloc(sizeof(struct elf_phdr) * | |
296 | interp_elf_ex->e_phnum); | |
297 | ||
298 | if (!elf_phdata) { | |
299 | return ~((abi_ulong)0UL); | |
300 | } | |
301 | ||
302 | /* | |
303 | * If the size of this structure has changed, then punt, since | |
304 | * we will be doing the wrong thing. | |
305 | */ | |
306 | if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) { | |
307 | free(elf_phdata); | |
308 | return ~((abi_ulong)0UL); | |
309 | } | |
310 | ||
311 | retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET); | |
312 | if (retval >= 0) { | |
313 | retval = read(interpreter_fd, (char *) elf_phdata, | |
314 | sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); | |
315 | } | |
316 | if (retval < 0) { | |
317 | perror("load_elf_interp"); | |
318 | exit(-1); | |
319 | free(elf_phdata); | |
320 | return retval; | |
321 | } | |
322 | bswap_phdr(elf_phdata, interp_elf_ex->e_phnum); | |
323 | ||
324 | rbase = 0; | |
325 | if (interp_elf_ex->e_type == ET_DYN) { | |
326 | /* | |
327 | * In order to avoid hardcoding the interpreter load | |
328 | * address in qemu, we allocate a big enough memory zone. | |
329 | */ | |
330 | rbase = target_mmap(0, INTERP_MAP_SIZE, PROT_NONE, | |
331 | MAP_PRIVATE | MAP_ANON, -1, 0); | |
332 | if (rbase == -1) { | |
333 | perror("mmap"); | |
334 | exit(-1); | |
335 | } | |
336 | } | |
337 | ||
338 | error = load_elf_sections(interp_elf_ex, elf_phdata, interpreter_fd, rbase, | |
339 | &baddr); | |
340 | if (error != 0) { | |
341 | perror("load_elf_sections"); | |
342 | exit(-1); | |
343 | } | |
344 | ||
345 | /* Now use mmap to map the library into memory. */ | |
346 | close(interpreter_fd); | |
347 | free(elf_phdata); | |
348 | ||
349 | *interp_load_addr = baddr; | |
350 | return ((abi_ulong) interp_elf_ex->e_entry) + rbase; | |
351 | } | |
352 | ||
353 | static int symfind(const void *s0, const void *s1) | |
354 | { | |
355 | struct elf_sym *sym = (struct elf_sym *)s1; | |
356 | __typeof(sym->st_value) addr = *(uint64_t *)s0; | |
357 | int result = 0; | |
358 | ||
359 | if (addr < sym->st_value) { | |
360 | result = -1; | |
361 | } else if (addr >= sym->st_value + sym->st_size) { | |
362 | result = 1; | |
363 | } | |
364 | return result; | |
365 | } | |
366 | ||
367 | static const char *lookup_symbolxx(struct syminfo *s, uint64_t orig_addr) | |
368 | { | |
369 | #if ELF_CLASS == ELFCLASS32 | |
370 | struct elf_sym *syms = s->disas_symtab.elf32; | |
371 | #else | |
372 | struct elf_sym *syms = s->disas_symtab.elf64; | |
373 | #endif | |
374 | ||
375 | /* binary search */ | |
376 | struct elf_sym *sym; | |
377 | ||
378 | sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), symfind); | |
379 | if (sym != NULL) { | |
380 | return s->disas_strtab + sym->st_name; | |
381 | } | |
382 | ||
383 | return ""; | |
384 | } | |
385 | ||
386 | /* FIXME: This should use elf_ops.h */ | |
387 | static int symcmp(const void *s0, const void *s1) | |
388 | { | |
389 | struct elf_sym *sym0 = (struct elf_sym *)s0; | |
390 | struct elf_sym *sym1 = (struct elf_sym *)s1; | |
391 | return (sym0->st_value < sym1->st_value) ? -1 : | |
392 | ((sym0->st_value > sym1->st_value) ? 1 : 0); | |
393 | } | |
394 | ||
395 | /* Best attempt to load symbols from this ELF object. */ | |
396 | static void load_symbols(struct elfhdr *hdr, int fd) | |
397 | { | |
398 | unsigned int i, nsyms; | |
399 | struct elf_shdr sechdr, symtab, strtab; | |
400 | char *strings; | |
401 | struct syminfo *s; | |
402 | struct elf_sym *syms, *new_syms; | |
403 | ||
404 | lseek(fd, hdr->e_shoff, SEEK_SET); | |
405 | for (i = 0; i < hdr->e_shnum; i++) { | |
406 | if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr)) { | |
407 | return; | |
408 | } | |
409 | bswap_shdr(&sechdr, 1); | |
410 | if (sechdr.sh_type == SHT_SYMTAB) { | |
411 | symtab = sechdr; | |
412 | lseek(fd, hdr->e_shoff + sizeof(sechdr) * sechdr.sh_link, | |
413 | SEEK_SET); | |
414 | if (read(fd, &strtab, sizeof(strtab)) != sizeof(strtab)) { | |
415 | return; | |
416 | } | |
417 | bswap_shdr(&strtab, 1); | |
418 | goto found; | |
419 | } | |
420 | } | |
421 | return; /* Shouldn't happen... */ | |
422 | ||
423 | found: | |
424 | /* Now know where the strtab and symtab are. Snarf them. */ | |
425 | s = malloc(sizeof(*s)); | |
426 | syms = malloc(symtab.sh_size); | |
427 | if (!syms) { | |
428 | free(s); | |
429 | return; | |
430 | } | |
431 | s->disas_strtab = strings = malloc(strtab.sh_size); | |
432 | if (!s->disas_strtab) { | |
433 | free(s); | |
434 | free(syms); | |
435 | return; | |
436 | } | |
437 | ||
438 | lseek(fd, symtab.sh_offset, SEEK_SET); | |
439 | if (read(fd, syms, symtab.sh_size) != symtab.sh_size) { | |
440 | free(s); | |
441 | free(syms); | |
442 | free(strings); | |
443 | return; | |
444 | } | |
445 | ||
446 | nsyms = symtab.sh_size / sizeof(struct elf_sym); | |
447 | ||
448 | i = 0; | |
449 | while (i < nsyms) { | |
450 | bswap_sym(syms + i); | |
451 | /* Throw away entries which we do not need. */ | |
452 | if (syms[i].st_shndx == SHN_UNDEF || | |
453 | syms[i].st_shndx >= SHN_LORESERVE || | |
454 | ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { | |
455 | nsyms--; | |
456 | if (i < nsyms) { | |
457 | syms[i] = syms[nsyms]; | |
458 | } | |
459 | continue; | |
460 | } | |
461 | #if defined(TARGET_ARM) || defined(TARGET_MIPS) | |
462 | /* The bottom address bit marks a Thumb or MIPS16 symbol. */ | |
463 | syms[i].st_value &= ~(target_ulong)1; | |
464 | #endif | |
465 | i++; | |
466 | } | |
467 | ||
468 | /* | |
469 | * Attempt to free the storage associated with the local symbols | |
470 | * that we threw away. Whether or not this has any effect on the | |
471 | * memory allocation depends on the malloc implementation and how | |
472 | * many symbols we managed to discard. | |
473 | */ | |
474 | new_syms = realloc(syms, nsyms * sizeof(*syms)); | |
475 | if (new_syms == NULL) { | |
476 | free(s); | |
477 | free(syms); | |
478 | free(strings); | |
479 | return; | |
480 | } | |
481 | syms = new_syms; | |
482 | ||
483 | qsort(syms, nsyms, sizeof(*syms), symcmp); | |
484 | ||
485 | lseek(fd, strtab.sh_offset, SEEK_SET); | |
486 | if (read(fd, strings, strtab.sh_size) != strtab.sh_size) { | |
487 | free(s); | |
488 | free(syms); | |
489 | free(strings); | |
490 | return; | |
491 | } | |
492 | s->disas_num_syms = nsyms; | |
493 | #if ELF_CLASS == ELFCLASS32 | |
494 | s->disas_symtab.elf32 = syms; | |
495 | s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; | |
496 | #else | |
497 | s->disas_symtab.elf64 = syms; | |
498 | s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; | |
499 | #endif | |
500 | s->next = syminfos; | |
501 | syminfos = s; | |
502 | } | |
503 | ||
504 | /* Check the elf header and see if this a target elf binary. */ | |
505 | int is_target_elf_binary(int fd) | |
506 | { | |
507 | uint8_t buf[128]; | |
508 | struct elfhdr elf_ex; | |
509 | ||
510 | if (lseek(fd, 0L, SEEK_SET) < 0) { | |
511 | return 0; | |
512 | } | |
513 | if (read(fd, buf, sizeof(buf)) < 0) { | |
514 | return 0; | |
515 | } | |
516 | ||
517 | elf_ex = *((struct elfhdr *)buf); | |
518 | bswap_ehdr(&elf_ex); | |
519 | ||
520 | if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || | |
521 | (!elf_check_arch(elf_ex.e_machine))) { | |
522 | return 0; | |
523 | } else { | |
524 | return 1; | |
525 | } | |
526 | } | |
527 | ||
528 | static int | |
529 | load_elf_sections(const struct elfhdr *hdr, struct elf_phdr *phdr, int fd, | |
530 | abi_ulong rbase, abi_ulong *baddrp) | |
531 | { | |
532 | struct elf_phdr *elf_ppnt; | |
533 | abi_ulong baddr; | |
534 | int i; | |
535 | bool first; | |
536 | ||
537 | /* | |
538 | * Now we do a little grungy work by mmaping the ELF image into | |
539 | * the correct location in memory. At this point, we assume that | |
540 | * the image should be loaded at fixed address, not at a variable | |
541 | * address. | |
542 | */ | |
543 | first = true; | |
544 | for (i = 0, elf_ppnt = phdr; i < hdr->e_phnum; i++, elf_ppnt++) { | |
545 | int elf_prot = 0; | |
546 | abi_ulong error; | |
547 | ||
548 | /* XXX Skip memsz == 0. */ | |
549 | if (elf_ppnt->p_type != PT_LOAD) { | |
550 | continue; | |
551 | } | |
552 | ||
553 | if (elf_ppnt->p_flags & PF_R) { | |
554 | elf_prot |= PROT_READ; | |
555 | } | |
556 | if (elf_ppnt->p_flags & PF_W) { | |
557 | elf_prot |= PROT_WRITE; | |
558 | } | |
559 | if (elf_ppnt->p_flags & PF_X) { | |
560 | elf_prot |= PROT_EXEC; | |
561 | } | |
562 | ||
563 | error = target_mmap(TARGET_ELF_PAGESTART(rbase + elf_ppnt->p_vaddr), | |
564 | (elf_ppnt->p_filesz + | |
565 | TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)), | |
566 | elf_prot, | |
567 | (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE), | |
568 | fd, | |
569 | (elf_ppnt->p_offset - | |
570 | TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); | |
571 | if (error == -1) { | |
572 | perror("mmap"); | |
573 | exit(-1); | |
574 | } else if (elf_ppnt->p_memsz != elf_ppnt->p_filesz) { | |
575 | abi_ulong start_bss, end_bss; | |
576 | ||
577 | start_bss = rbase + elf_ppnt->p_vaddr + elf_ppnt->p_filesz; | |
578 | end_bss = rbase + elf_ppnt->p_vaddr + elf_ppnt->p_memsz; | |
579 | ||
580 | /* | |
581 | * Calling set_brk effectively mmaps the pages that we need for the | |
582 | * bss and break sections. | |
583 | */ | |
584 | set_brk(start_bss, end_bss); | |
585 | padzero(start_bss, end_bss); | |
586 | } | |
587 | ||
588 | if (first) { | |
589 | baddr = TARGET_ELF_PAGESTART(rbase + elf_ppnt->p_vaddr); | |
590 | first = false; | |
591 | } | |
592 | } | |
593 | ||
594 | if (baddrp != NULL) { | |
595 | *baddrp = baddr; | |
596 | } | |
597 | return 0; | |
598 | } | |
599 | ||
600 | int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs, | |
601 | struct image_info *info) | |
602 | { | |
603 | struct elfhdr elf_ex; | |
604 | struct elfhdr interp_elf_ex; | |
605 | int interpreter_fd = -1; /* avoid warning */ | |
606 | abi_ulong load_addr; | |
607 | int i; | |
608 | struct elf_phdr *elf_ppnt; | |
609 | struct elf_phdr *elf_phdata; | |
610 | abi_ulong elf_brk; | |
611 | int error, retval; | |
612 | char *elf_interpreter; | |
613 | abi_ulong baddr, elf_entry, et_dyn_addr, interp_load_addr = 0; | |
614 | abi_ulong reloc_func_desc = 0; | |
615 | ||
616 | load_addr = 0; | |
617 | elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ | |
618 | bswap_ehdr(&elf_ex); | |
619 | ||
620 | /* First of all, some simple consistency checks */ | |
621 | if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || | |
622 | (!elf_check_arch(elf_ex.e_machine))) { | |
623 | return -ENOEXEC; | |
624 | } | |
625 | ||
626 | bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p); | |
627 | bprm->p = copy_elf_strings(bprm->envc, bprm->envp, bprm->page, bprm->p); | |
628 | bprm->p = copy_elf_strings(bprm->argc, bprm->argv, bprm->page, bprm->p); | |
629 | if (!bprm->p) { | |
630 | retval = -E2BIG; | |
631 | } | |
632 | ||
633 | /* Now read in all of the header information */ | |
634 | elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize * elf_ex.e_phnum); | |
635 | if (elf_phdata == NULL) { | |
636 | return -ENOMEM; | |
637 | } | |
638 | ||
639 | retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET); | |
640 | if (retval > 0) { | |
641 | retval = read(bprm->fd, (char *)elf_phdata, | |
642 | elf_ex.e_phentsize * elf_ex.e_phnum); | |
643 | } | |
644 | ||
645 | if (retval < 0) { | |
646 | perror("load_elf_binary"); | |
647 | exit(-1); | |
648 | free(elf_phdata); | |
649 | return -errno; | |
650 | } | |
651 | ||
652 | bswap_phdr(elf_phdata, elf_ex.e_phnum); | |
653 | elf_ppnt = elf_phdata; | |
654 | ||
655 | elf_brk = 0; | |
656 | ||
657 | ||
658 | elf_interpreter = NULL; | |
659 | for (i = 0; i < elf_ex.e_phnum; i++) { | |
660 | if (elf_ppnt->p_type == PT_INTERP) { | |
661 | if (elf_interpreter != NULL) { | |
662 | free(elf_phdata); | |
663 | free(elf_interpreter); | |
664 | close(bprm->fd); | |
665 | return -EINVAL; | |
666 | } | |
667 | ||
668 | elf_interpreter = (char *)malloc(elf_ppnt->p_filesz); | |
669 | if (elf_interpreter == NULL) { | |
670 | free(elf_phdata); | |
671 | close(bprm->fd); | |
672 | return -ENOMEM; | |
673 | } | |
674 | ||
675 | retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET); | |
676 | if (retval >= 0) { | |
677 | retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz); | |
678 | } | |
679 | if (retval < 0) { | |
680 | perror("load_elf_binary2"); | |
681 | exit(-1); | |
682 | } | |
683 | ||
684 | if (retval >= 0) { | |
685 | retval = open(path(elf_interpreter), O_RDONLY); | |
686 | if (retval >= 0) { | |
687 | interpreter_fd = retval; | |
688 | } else { | |
689 | perror(elf_interpreter); | |
690 | exit(-1); | |
691 | /* retval = -errno; */ | |
692 | } | |
693 | } | |
694 | ||
695 | if (retval >= 0) { | |
696 | retval = lseek(interpreter_fd, 0, SEEK_SET); | |
697 | if (retval >= 0) { | |
698 | retval = read(interpreter_fd, bprm->buf, 128); | |
699 | } | |
700 | } | |
701 | if (retval >= 0) { | |
702 | interp_elf_ex = *((struct elfhdr *) bprm->buf); | |
703 | } | |
704 | if (retval < 0) { | |
705 | perror("load_elf_binary3"); | |
706 | exit(-1); | |
707 | free(elf_phdata); | |
708 | free(elf_interpreter); | |
709 | close(bprm->fd); | |
710 | return retval; | |
711 | } | |
712 | } | |
713 | elf_ppnt++; | |
714 | } | |
715 | ||
716 | /* Some simple consistency checks for the interpreter */ | |
717 | if (elf_interpreter) { | |
718 | if (interp_elf_ex.e_ident[0] != 0x7f || | |
719 | strncmp((char *)&interp_elf_ex.e_ident[1], "ELF", 3) != 0) { | |
720 | free(elf_interpreter); | |
721 | free(elf_phdata); | |
722 | close(bprm->fd); | |
723 | return -ELIBBAD; | |
724 | } | |
725 | } | |
726 | ||
727 | /* | |
728 | * OK, we are done with that, now set up the arg stuff, and then start this | |
729 | * sucker up | |
730 | */ | |
731 | if (!bprm->p) { | |
732 | free(elf_interpreter); | |
733 | free(elf_phdata); | |
734 | close(bprm->fd); | |
735 | return -E2BIG; | |
736 | } | |
737 | ||
738 | /* OK, This is the point of no return */ | |
739 | info->end_data = 0; | |
740 | info->end_code = 0; | |
741 | elf_entry = (abi_ulong) elf_ex.e_entry; | |
742 | ||
743 | /* XXX Join this with PT_INTERP search? */ | |
744 | baddr = 0; | |
745 | for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { | |
746 | if (elf_ppnt->p_type != PT_LOAD) { | |
747 | continue; | |
748 | } | |
749 | baddr = elf_ppnt->p_vaddr; | |
750 | break; | |
751 | } | |
752 | ||
753 | et_dyn_addr = 0; | |
754 | if (elf_ex.e_type == ET_DYN && baddr == 0) { | |
755 | et_dyn_addr = ELF_ET_DYN_LOAD_ADDR; | |
756 | } | |
757 | ||
758 | /* | |
759 | * Do this so that we can load the interpreter, if need be. We will | |
760 | * change some of these later | |
761 | */ | |
762 | info->rss = 0; | |
763 | setup_arg_pages(bprm, info, &bprm->p, &bprm->stringp); | |
764 | info->start_stack = bprm->p; | |
765 | ||
766 | info->elf_flags = elf_ex.e_flags; | |
767 | ||
768 | error = load_elf_sections(&elf_ex, elf_phdata, bprm->fd, et_dyn_addr, | |
769 | &load_addr); | |
770 | for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { | |
771 | if (elf_ppnt->p_type != PT_LOAD) { | |
772 | continue; | |
773 | } | |
774 | if (elf_ppnt->p_memsz > elf_ppnt->p_filesz) | |
775 | elf_brk = MAX(elf_brk, et_dyn_addr + elf_ppnt->p_vaddr + | |
776 | elf_ppnt->p_memsz); | |
777 | } | |
778 | if (error != 0) { | |
779 | perror("load_elf_sections"); | |
780 | exit(-1); | |
781 | } | |
782 | ||
783 | if (elf_interpreter) { | |
784 | elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd, | |
785 | &interp_load_addr); | |
786 | reloc_func_desc = interp_load_addr; | |
787 | ||
788 | close(interpreter_fd); | |
789 | free(elf_interpreter); | |
790 | ||
791 | if (elf_entry == ~((abi_ulong)0UL)) { | |
792 | printf("Unable to load interpreter\n"); | |
793 | free(elf_phdata); | |
794 | exit(-1); | |
795 | return 0; | |
796 | } | |
797 | } else { | |
798 | interp_load_addr = et_dyn_addr; | |
799 | elf_entry += interp_load_addr; | |
800 | } | |
801 | ||
802 | free(elf_phdata); | |
803 | ||
804 | if (qemu_log_enabled()) { | |
805 | load_symbols(&elf_ex, bprm->fd); | |
806 | } | |
807 | ||
808 | close(bprm->fd); | |
809 | ||
810 | bprm->p = target_create_elf_tables(bprm->p, bprm->argc, bprm->envc, | |
811 | bprm->stringp, &elf_ex, load_addr, | |
812 | et_dyn_addr, interp_load_addr, info); | |
813 | info->load_addr = reloc_func_desc; | |
814 | info->brk = elf_brk; | |
815 | info->start_stack = bprm->p; | |
816 | info->load_bias = 0; | |
817 | ||
818 | info->entry = elf_entry; | |
819 | ||
820 | #ifdef USE_ELF_CORE_DUMP | |
821 | bprm->core_dump = &elf_core_dump; | |
822 | #else | |
823 | bprm->core_dump = NULL; | |
824 | #endif | |
825 | ||
826 | return 0; | |
827 | } | |
828 | ||
829 | void do_init_thread(struct target_pt_regs *regs, struct image_info *infop) | |
830 | { | |
831 | ||
832 | target_thread_init(regs, infop); | |
833 | } |