]>
Commit | Line | Data |
---|---|---|
310df056 WL |
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 | */ | |
84778508 | 19 | |
2231197c | 20 | #include "qemu/osdep.h" |
84778508 BS |
21 | |
22 | #include "qemu.h" | |
76cad711 | 23 | #include "disas/disas.h" |
f348b6d1 | 24 | #include "qemu/path.h" |
84778508 | 25 | |
98b34d35 WL |
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 */ | |
84778508 | 28 | |
0475f8fa | 29 | #include "target_arch_reg.h" |
98b34d35 WL |
30 | #include "target_os_elf.h" |
31 | #include "target_os_stack.h" | |
32 | #include "target_os_thread.h" | |
0475f8fa | 33 | #include "target_os_user.h" |
84778508 | 34 | |
98b34d35 WL |
35 | abi_ulong target_stksiz; |
36 | abi_ulong target_stkbas; | |
84778508 | 37 | |
0475f8fa | 38 | static int elf_core_dump(int signr, CPUArchState *env); |
c09f12fe WL |
39 | static int load_elf_sections(const struct elfhdr *hdr, struct elf_phdr *phdr, |
40 | int fd, abi_ulong rbase, abi_ulong *baddrp); | |
0475f8fa | 41 | |
b4bebeee | 42 | static inline void memcpy_fromfs(void *to, const void *from, unsigned long n) |
84778508 | 43 | { |
b62f790c | 44 | memcpy(to, from, n); |
84778508 BS |
45 | } |
46 | ||
84778508 BS |
47 | #ifdef BSWAP_NEEDED |
48 | static void bswap_ehdr(struct elfhdr *ehdr) | |
49 | { | |
b62f790c | 50 | bswap16s(&ehdr->e_type); /* Object file type */ |
84778508 BS |
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 */ | |
b62f790c | 58 | bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ |
84778508 | 59 | bswap16s(&ehdr->e_phnum); /* Program header table entry count */ |
b62f790c | 60 | bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ |
84778508 | 61 | bswap16s(&ehdr->e_shnum); /* Section header table entry count */ |
b62f790c | 62 | bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ |
84778508 BS |
63 | } |
64 | ||
b62f790c | 65 | static void bswap_phdr(struct elf_phdr *phdr, int phnum) |
84778508 | 66 | { |
b62f790c WL |
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 | } | |
84778508 BS |
79 | } |
80 | ||
b62f790c | 81 | static void bswap_shdr(struct elf_shdr *shdr, int shnum) |
84778508 | 82 | { |
b62f790c WL |
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 | } | |
84778508 BS |
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 | } | |
b62f790c | 106 | |
0475f8fa WL |
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 | ||
b62f790c WL |
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) { } | |
0475f8fa | 120 | static void bswap_note(struct elf_note *en) { } |
b62f790c WL |
121 | |
122 | #endif /* ! BSWAP_NEEDED */ | |
84778508 | 123 | |
0475f8fa WL |
124 | #include "elfcore.c" |
125 | ||
84778508 BS |
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 | */ | |
b4bebeee | 132 | static abi_ulong copy_elf_strings(int argc, char **argv, void **page, |
84778508 BS |
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) { | |
9bb93180 | 144 | fprintf(stderr, "VFS: argc is wrong"); |
84778508 BS |
145 | exit(-1); |
146 | } | |
147 | tmp1 = tmp; | |
0456a177 WL |
148 | while (*tmp++) { |
149 | continue; | |
150 | } | |
84778508 BS |
151 | len = tmp - tmp1; |
152 | if (p < len) { /* this shouldn't happen - 128kB */ | |
0456a177 | 153 | return 0; |
84778508 BS |
154 | } |
155 | while (len) { | |
156 | --p; --tmp; --len; | |
157 | if (--offset < 0) { | |
158 | offset = p % TARGET_PAGE_SIZE; | |
3d558330 | 159 | pag = page[p / TARGET_PAGE_SIZE]; |
84778508 | 160 | if (!pag) { |
c580dee4 | 161 | pag = g_try_malloc0(TARGET_PAGE_SIZE); |
b4bebeee | 162 | page[p / TARGET_PAGE_SIZE] = pag; |
0456a177 | 163 | if (!pag) { |
84778508 | 164 | return 0; |
0456a177 | 165 | } |
84778508 BS |
166 | } |
167 | } | |
168 | if (len == 0 || offset == 0) { | |
169 | *(pag + offset) = *tmp; | |
0456a177 | 170 | } else { |
84778508 BS |
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 | ||
98b34d35 WL |
183 | static void setup_arg_pages(struct bsd_binprm *bprm, struct image_info *info, |
184 | abi_ulong *stackp, abi_ulong *stringp) | |
84778508 | 185 | { |
98b34d35 WL |
186 | abi_ulong stack_base, size; |
187 | abi_long addr; | |
84778508 | 188 | |
0456a177 WL |
189 | /* |
190 | * Create enough stack to hold everything. If we don't use it for args, | |
191 | * we'll use it for something else... | |
84778508 | 192 | */ |
312a0b1c | 193 | size = target_dflssiz; |
98b34d35 | 194 | stack_base = TARGET_USRSTACK - size; |
0456a177 WL |
195 | addr = target_mmap(stack_base , size + qemu_host_page_size, |
196 | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); | |
98b34d35 | 197 | if (addr == -1) { |
84778508 BS |
198 | perror("stk mmap"); |
199 | exit(-1); | |
200 | } | |
201 | /* we reserve one extra page at the top of the stack as guard */ | |
98b34d35 | 202 | target_mprotect(addr + size, qemu_host_page_size, PROT_NONE); |
84778508 | 203 | |
98b34d35 WL |
204 | target_stksiz = size; |
205 | target_stkbas = addr; | |
84778508 | 206 | |
98b34d35 WL |
207 | if (setup_initial_stack(bprm, stackp, stringp) != 0) { |
208 | perror("stk setup"); | |
209 | exit(-1); | |
84778508 | 210 | } |
84778508 BS |
211 | } |
212 | ||
213 | static void set_brk(abi_ulong start, abi_ulong end) | |
214 | { | |
0456a177 WL |
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 | } | |
84778508 BS |
226 | } |
227 | ||
228 | ||
0456a177 WL |
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 | */ | |
84778508 BS |
234 | static void padzero(abi_ulong elf_bss, abi_ulong last_bss) |
235 | { | |
0456a177 | 236 | abi_ulong nbyte; |
84778508 | 237 | |
0456a177 WL |
238 | if (elf_bss >= last_bss) { |
239 | return; | |
240 | } | |
84778508 | 241 | |
0456a177 WL |
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 | */ | |
8e3b0cbb | 249 | if (qemu_real_host_page_size() < qemu_host_page_size) { |
0456a177 WL |
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); | |
84778508 | 257 | } |
0456a177 | 258 | } |
84778508 | 259 | |
0456a177 WL |
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 | } | |
84778508 BS |
269 | } |
270 | ||
b4bebeee | 271 | static abi_ulong load_elf_interp(struct elfhdr *interp_elf_ex, |
84778508 BS |
272 | int interpreter_fd, |
273 | abi_ulong *interp_load_addr) | |
274 | { | |
b4bebeee | 275 | struct elf_phdr *elf_phdata = NULL; |
c09f12fe | 276 | abi_ulong rbase; |
b4bebeee | 277 | int retval; |
c09f12fe | 278 | abi_ulong baddr, error; |
84778508 | 279 | |
b4bebeee | 280 | error = 0; |
84778508 | 281 | |
b4bebeee | 282 | bswap_ehdr(interp_elf_ex); |
b4bebeee | 283 | /* First of all, some simple consistency checks */ |
0456a177 WL |
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)) { | |
b4bebeee WL |
286 | return ~((abi_ulong)0UL); |
287 | } | |
84778508 BS |
288 | |
289 | ||
b4bebeee | 290 | /* Now read in all of the header information */ |
0456a177 | 291 | if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE) { |
b4bebeee | 292 | return ~(abi_ulong)0UL; |
0456a177 | 293 | } |
84778508 | 294 | |
0456a177 WL |
295 | elf_phdata = (struct elf_phdr *) malloc(sizeof(struct elf_phdr) * |
296 | interp_elf_ex->e_phnum); | |
84778508 | 297 | |
0456a177 | 298 | if (!elf_phdata) { |
b4bebeee | 299 | return ~((abi_ulong)0UL); |
0456a177 | 300 | } |
84778508 | 301 | |
b4bebeee WL |
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 | } | |
84778508 | 310 | |
b4bebeee WL |
311 | retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET); |
312 | if (retval >= 0) { | |
0456a177 WL |
313 | retval = read(interpreter_fd, (char *) elf_phdata, |
314 | sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); | |
b4bebeee WL |
315 | } |
316 | if (retval < 0) { | |
317 | perror("load_elf_interp"); | |
318 | exit(-1); | |
ffa03665 | 319 | free(elf_phdata); |
b4bebeee WL |
320 | return retval; |
321 | } | |
b62f790c | 322 | bswap_phdr(elf_phdata, interp_elf_ex->e_phnum); |
84778508 | 323 | |
c09f12fe | 324 | rbase = 0; |
b4bebeee | 325 | if (interp_elf_ex->e_type == ET_DYN) { |
0456a177 WL |
326 | /* |
327 | * In order to avoid hardcoding the interpreter load | |
328 | * address in qemu, we allocate a big enough memory zone. | |
329 | */ | |
c09f12fe | 330 | rbase = target_mmap(0, INTERP_MAP_SIZE, PROT_NONE, |
0456a177 | 331 | MAP_PRIVATE | MAP_ANON, -1, 0); |
c09f12fe | 332 | if (rbase == -1) { |
b4bebeee WL |
333 | perror("mmap"); |
334 | exit(-1); | |
84778508 | 335 | } |
c09f12fe | 336 | } |
84778508 | 337 | |
c09f12fe WL |
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 | } | |
84778508 | 344 | |
b4bebeee | 345 | /* Now use mmap to map the library into memory. */ |
b4bebeee | 346 | close(interpreter_fd); |
b4bebeee | 347 | free(elf_phdata); |
84778508 | 348 | |
c09f12fe WL |
349 | *interp_load_addr = baddr; |
350 | return ((abi_ulong) interp_elf_ex->e_entry) + rbase; | |
84778508 BS |
351 | } |
352 | ||
353 | static int symfind(const void *s0, const void *s1) | |
354 | { | |
84778508 | 355 | struct elf_sym *sym = (struct elf_sym *)s1; |
b6235a75 | 356 | __typeof(sym->st_value) addr = *(uint64_t *)s0; |
84778508 | 357 | int result = 0; |
b6235a75 | 358 | |
c7c530cd | 359 | if (addr < sym->st_value) { |
84778508 | 360 | result = -1; |
c7c530cd | 361 | } else if (addr >= sym->st_value + sym->st_size) { |
84778508 BS |
362 | result = 1; |
363 | } | |
364 | return result; | |
365 | } | |
366 | ||
b6235a75 | 367 | static const char *lookup_symbolxx(struct syminfo *s, uint64_t orig_addr) |
84778508 BS |
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 | ||
0456a177 | 375 | /* binary search */ |
84778508 BS |
376 | struct elf_sym *sym; |
377 | ||
c7c530cd | 378 | sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), symfind); |
7cba04f6 | 379 | if (sym != NULL) { |
84778508 BS |
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; | |
0456a177 WL |
391 | return (sym0->st_value < sym1->st_value) ? -1 : |
392 | ((sym0->st_value > sym1->st_value) ? 1 : 0); | |
84778508 BS |
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; | |
29718712 | 402 | struct elf_sym *syms, *new_syms; |
84778508 BS |
403 | |
404 | lseek(fd, hdr->e_shoff, SEEK_SET); | |
405 | for (i = 0; i < hdr->e_shnum; i++) { | |
0456a177 | 406 | if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr)) { |
84778508 | 407 | return; |
0456a177 | 408 | } |
b62f790c | 409 | bswap_shdr(&sechdr, 1); |
84778508 BS |
410 | if (sechdr.sh_type == SHT_SYMTAB) { |
411 | symtab = sechdr; | |
0456a177 WL |
412 | lseek(fd, hdr->e_shoff + sizeof(sechdr) * sechdr.sh_link, |
413 | SEEK_SET); | |
414 | if (read(fd, &strtab, sizeof(strtab)) != sizeof(strtab)) { | |
84778508 | 415 | return; |
0456a177 | 416 | } |
b62f790c | 417 | bswap_shdr(&strtab, 1); |
84778508 BS |
418 | goto found; |
419 | } | |
420 | } | |
421 | return; /* Shouldn't happen... */ | |
422 | ||
0456a177 | 423 | found: |
84778508 BS |
424 | /* Now know where the strtab and symtab are. Snarf them. */ |
425 | s = malloc(sizeof(*s)); | |
426 | syms = malloc(symtab.sh_size); | |
29718712 SW |
427 | if (!syms) { |
428 | free(s); | |
84778508 | 429 | return; |
29718712 | 430 | } |
84778508 | 431 | s->disas_strtab = strings = malloc(strtab.sh_size); |
29718712 SW |
432 | if (!s->disas_strtab) { |
433 | free(s); | |
434 | free(syms); | |
84778508 | 435 | return; |
29718712 | 436 | } |
84778508 BS |
437 | |
438 | lseek(fd, symtab.sh_offset, SEEK_SET); | |
29718712 SW |
439 | if (read(fd, syms, symtab.sh_size) != symtab.sh_size) { |
440 | free(s); | |
441 | free(syms); | |
442 | free(strings); | |
84778508 | 443 | return; |
29718712 | 444 | } |
84778508 BS |
445 | |
446 | nsyms = symtab.sh_size / sizeof(struct elf_sym); | |
447 | ||
448 | i = 0; | |
449 | while (i < nsyms) { | |
84778508 | 450 | bswap_sym(syms + i); |
0456a177 | 451 | /* Throw away entries which we do not need. */ |
84778508 BS |
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 | } | |
c09f12fe WL |
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 | |
84778508 BS |
465 | i++; |
466 | } | |
29718712 | 467 | |
0456a177 WL |
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 | */ | |
29718712 SW |
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; | |
84778508 BS |
482 | |
483 | qsort(syms, nsyms, sizeof(*syms), symcmp); | |
484 | ||
485 | lseek(fd, strtab.sh_offset, SEEK_SET); | |
29718712 SW |
486 | if (read(fd, strings, strtab.sh_size) != strtab.sh_size) { |
487 | free(s); | |
488 | free(syms); | |
489 | free(strings); | |
84778508 | 490 | return; |
29718712 | 491 | } |
84778508 BS |
492 | s->disas_num_syms = nsyms; |
493 | #if ELF_CLASS == ELFCLASS32 | |
494 | s->disas_symtab.elf32 = syms; | |
032e51d7 | 495 | s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; |
84778508 BS |
496 | #else |
497 | s->disas_symtab.elf64 = syms; | |
032e51d7 | 498 | s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; |
84778508 BS |
499 | #endif |
500 | s->next = syminfos; | |
501 | syminfos = s; | |
502 | } | |
503 | ||
c09f12fe WL |
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 | ||
afcbcff8 | 600 | int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs, |
b4bebeee | 601 | struct image_info *info) |
84778508 BS |
602 | { |
603 | struct elfhdr elf_ex; | |
604 | struct elfhdr interp_elf_ex; | |
84778508 | 605 | int interpreter_fd = -1; /* avoid warning */ |
c09f12fe | 606 | abi_ulong load_addr; |
84778508 | 607 | int i; |
c09f12fe | 608 | struct elf_phdr *elf_ppnt; |
84778508 | 609 | struct elf_phdr *elf_phdata; |
c09f12fe WL |
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; | |
84778508 | 614 | abi_ulong reloc_func_desc = 0; |
84778508 | 615 | |
84778508 | 616 | load_addr = 0; |
84778508 | 617 | elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ |
84778508 | 618 | bswap_ehdr(&elf_ex); |
84778508 BS |
619 | |
620 | /* First of all, some simple consistency checks */ | |
621 | if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || | |
0456a177 | 622 | (!elf_check_arch(elf_ex.e_machine))) { |
84778508 BS |
623 | return -ENOEXEC; |
624 | } | |
625 | ||
626 | bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p); | |
0456a177 WL |
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); | |
84778508 BS |
629 | if (!bprm->p) { |
630 | retval = -E2BIG; | |
631 | } | |
632 | ||
633 | /* Now read in all of the header information */ | |
0456a177 | 634 | elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize * elf_ex.e_phnum); |
84778508 BS |
635 | if (elf_phdata == NULL) { |
636 | return -ENOMEM; | |
637 | } | |
638 | ||
639 | retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET); | |
b4bebeee WL |
640 | if (retval > 0) { |
641 | retval = read(bprm->fd, (char *)elf_phdata, | |
84778508 BS |
642 | elf_ex.e_phentsize * elf_ex.e_phnum); |
643 | } | |
644 | ||
645 | if (retval < 0) { | |
646 | perror("load_elf_binary"); | |
647 | exit(-1); | |
b4bebeee | 648 | free(elf_phdata); |
84778508 BS |
649 | return -errno; |
650 | } | |
651 | ||
b62f790c | 652 | bswap_phdr(elf_phdata, elf_ex.e_phnum); |
84778508 BS |
653 | elf_ppnt = elf_phdata; |
654 | ||
84778508 BS |
655 | elf_brk = 0; |
656 | ||
657 | ||
84778508 | 658 | elf_interpreter = NULL; |
0456a177 | 659 | for (i = 0; i < elf_ex.e_phnum; i++) { |
84778508 | 660 | if (elf_ppnt->p_type == PT_INTERP) { |
0456a177 | 661 | if (elf_interpreter != NULL) { |
b4bebeee | 662 | free(elf_phdata); |
84778508 BS |
663 | free(elf_interpreter); |
664 | close(bprm->fd); | |
665 | return -EINVAL; | |
666 | } | |
667 | ||
84778508 | 668 | elf_interpreter = (char *)malloc(elf_ppnt->p_filesz); |
84778508 | 669 | if (elf_interpreter == NULL) { |
b4bebeee | 670 | free(elf_phdata); |
84778508 BS |
671 | close(bprm->fd); |
672 | return -ENOMEM; | |
673 | } | |
674 | ||
675 | retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET); | |
b4bebeee | 676 | if (retval >= 0) { |
84778508 BS |
677 | retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz); |
678 | } | |
b4bebeee | 679 | if (retval < 0) { |
84778508 BS |
680 | perror("load_elf_binary2"); |
681 | exit(-1); | |
682 | } | |
683 | ||
84778508 BS |
684 | if (retval >= 0) { |
685 | retval = open(path(elf_interpreter), O_RDONLY); | |
b4bebeee | 686 | if (retval >= 0) { |
84778508 | 687 | interpreter_fd = retval; |
0456a177 | 688 | } else { |
84778508 BS |
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); | |
b4bebeee WL |
697 | if (retval >= 0) { |
698 | retval = read(interpreter_fd, bprm->buf, 128); | |
84778508 BS |
699 | } |
700 | } | |
701 | if (retval >= 0) { | |
0456a177 | 702 | interp_elf_ex = *((struct elfhdr *) bprm->buf); |
84778508 BS |
703 | } |
704 | if (retval < 0) { | |
705 | perror("load_elf_binary3"); | |
706 | exit(-1); | |
b4bebeee | 707 | free(elf_phdata); |
84778508 BS |
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 */ | |
b4bebeee | 717 | if (elf_interpreter) { |
84778508 | 718 | if (interp_elf_ex.e_ident[0] != 0x7f || |
ffa03665 | 719 | strncmp((char *)&interp_elf_ex.e_ident[1], "ELF", 3) != 0) { |
84778508 BS |
720 | free(elf_interpreter); |
721 | free(elf_phdata); | |
722 | close(bprm->fd); | |
723 | return -ELIBBAD; | |
724 | } | |
725 | } | |
726 | ||
0456a177 WL |
727 | /* |
728 | * OK, we are done with that, now set up the arg stuff, and then start this | |
729 | * sucker up | |
730 | */ | |
ffa03665 WL |
731 | if (!bprm->p) { |
732 | free(elf_interpreter); | |
733 | free(elf_phdata); | |
734 | close(bprm->fd); | |
735 | return -E2BIG; | |
84778508 BS |
736 | } |
737 | ||
738 | /* OK, This is the point of no return */ | |
739 | info->end_data = 0; | |
740 | info->end_code = 0; | |
84778508 BS |
741 | elf_entry = (abi_ulong) elf_ex.e_entry; |
742 | ||
c09f12fe WL |
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 | */ | |
84778508 | 762 | info->rss = 0; |
98b34d35 | 763 | setup_arg_pages(bprm, info, &bprm->p, &bprm->stringp); |
84778508 BS |
764 | info->start_stack = bprm->p; |
765 | ||
c09f12fe | 766 | info->elf_flags = elf_ex.e_flags; |
84778508 | 767 | |
c09f12fe WL |
768 | error = load_elf_sections(&elf_ex, elf_phdata, bprm->fd, et_dyn_addr, |
769 | &load_addr); | |
b4bebeee | 770 | for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { |
c09f12fe | 771 | if (elf_ppnt->p_type != PT_LOAD) { |
84778508 | 772 | continue; |
84778508 | 773 | } |
c09f12fe WL |
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 | } | |
84778508 BS |
782 | |
783 | if (elf_interpreter) { | |
ffa03665 WL |
784 | elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd, |
785 | &interp_load_addr); | |
84778508 BS |
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 | } | |
c09f12fe WL |
797 | } else { |
798 | interp_load_addr = et_dyn_addr; | |
799 | elf_entry += interp_load_addr; | |
84778508 BS |
800 | } |
801 | ||
802 | free(elf_phdata); | |
803 | ||
0456a177 | 804 | if (qemu_log_enabled()) { |
84778508 | 805 | load_symbols(&elf_ex, bprm->fd); |
0456a177 | 806 | } |
84778508 | 807 | |
ffa03665 | 808 | close(bprm->fd); |
84778508 | 809 | |
98b34d35 WL |
810 | bprm->p = target_create_elf_tables(bprm->p, bprm->argc, bprm->envc, |
811 | bprm->stringp, &elf_ex, load_addr, | |
c09f12fe | 812 | et_dyn_addr, interp_load_addr, info); |
84778508 | 813 | info->load_addr = reloc_func_desc; |
4436e2ff | 814 | info->brk = elf_brk; |
84778508 | 815 | info->start_stack = bprm->p; |
c09f12fe | 816 | info->load_bias = 0; |
84778508 | 817 | |
84778508 BS |
818 | info->entry = elf_entry; |
819 | ||
0475f8fa WL |
820 | #ifdef USE_ELF_CORE_DUMP |
821 | bprm->core_dump = &elf_core_dump; | |
822 | #else | |
823 | bprm->core_dump = NULL; | |
824 | #endif | |
825 | ||
84778508 BS |
826 | return 0; |
827 | } | |
828 | ||
84778508 BS |
829 | void do_init_thread(struct target_pt_regs *regs, struct image_info *infop) |
830 | { | |
a8fe6d5d WL |
831 | |
832 | target_thread_init(regs, infop); | |
84778508 | 833 | } |