]>
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; | |
b4bebeee | 159 | pag = (char *)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 | { | |
c7c530cd | 355 | target_ulong addr = *(target_ulong *)s0; |
84778508 BS |
356 | struct elf_sym *sym = (struct elf_sym *)s1; |
357 | int result = 0; | |
c7c530cd | 358 | if (addr < sym->st_value) { |
84778508 | 359 | result = -1; |
c7c530cd | 360 | } else if (addr >= sym->st_value + sym->st_size) { |
84778508 BS |
361 | result = 1; |
362 | } | |
363 | return result; | |
364 | } | |
365 | ||
366 | static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr) | |
367 | { | |
368 | #if ELF_CLASS == ELFCLASS32 | |
369 | struct elf_sym *syms = s->disas_symtab.elf32; | |
370 | #else | |
371 | struct elf_sym *syms = s->disas_symtab.elf64; | |
372 | #endif | |
373 | ||
0456a177 | 374 | /* binary search */ |
84778508 BS |
375 | struct elf_sym *sym; |
376 | ||
c7c530cd | 377 | sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), symfind); |
7cba04f6 | 378 | if (sym != NULL) { |
84778508 BS |
379 | return s->disas_strtab + sym->st_name; |
380 | } | |
381 | ||
382 | return ""; | |
383 | } | |
384 | ||
385 | /* FIXME: This should use elf_ops.h */ | |
386 | static int symcmp(const void *s0, const void *s1) | |
387 | { | |
388 | struct elf_sym *sym0 = (struct elf_sym *)s0; | |
389 | struct elf_sym *sym1 = (struct elf_sym *)s1; | |
0456a177 WL |
390 | return (sym0->st_value < sym1->st_value) ? -1 : |
391 | ((sym0->st_value > sym1->st_value) ? 1 : 0); | |
84778508 BS |
392 | } |
393 | ||
394 | /* Best attempt to load symbols from this ELF object. */ | |
395 | static void load_symbols(struct elfhdr *hdr, int fd) | |
396 | { | |
397 | unsigned int i, nsyms; | |
398 | struct elf_shdr sechdr, symtab, strtab; | |
399 | char *strings; | |
400 | struct syminfo *s; | |
29718712 | 401 | struct elf_sym *syms, *new_syms; |
84778508 BS |
402 | |
403 | lseek(fd, hdr->e_shoff, SEEK_SET); | |
404 | for (i = 0; i < hdr->e_shnum; i++) { | |
0456a177 | 405 | if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr)) { |
84778508 | 406 | return; |
0456a177 | 407 | } |
b62f790c | 408 | bswap_shdr(&sechdr, 1); |
84778508 BS |
409 | if (sechdr.sh_type == SHT_SYMTAB) { |
410 | symtab = sechdr; | |
0456a177 WL |
411 | lseek(fd, hdr->e_shoff + sizeof(sechdr) * sechdr.sh_link, |
412 | SEEK_SET); | |
413 | if (read(fd, &strtab, sizeof(strtab)) != sizeof(strtab)) { | |
84778508 | 414 | return; |
0456a177 | 415 | } |
b62f790c | 416 | bswap_shdr(&strtab, 1); |
84778508 BS |
417 | goto found; |
418 | } | |
419 | } | |
420 | return; /* Shouldn't happen... */ | |
421 | ||
0456a177 | 422 | found: |
84778508 BS |
423 | /* Now know where the strtab and symtab are. Snarf them. */ |
424 | s = malloc(sizeof(*s)); | |
425 | syms = malloc(symtab.sh_size); | |
29718712 SW |
426 | if (!syms) { |
427 | free(s); | |
84778508 | 428 | return; |
29718712 | 429 | } |
84778508 | 430 | s->disas_strtab = strings = malloc(strtab.sh_size); |
29718712 SW |
431 | if (!s->disas_strtab) { |
432 | free(s); | |
433 | free(syms); | |
84778508 | 434 | return; |
29718712 | 435 | } |
84778508 BS |
436 | |
437 | lseek(fd, symtab.sh_offset, SEEK_SET); | |
29718712 SW |
438 | if (read(fd, syms, symtab.sh_size) != symtab.sh_size) { |
439 | free(s); | |
440 | free(syms); | |
441 | free(strings); | |
84778508 | 442 | return; |
29718712 | 443 | } |
84778508 BS |
444 | |
445 | nsyms = symtab.sh_size / sizeof(struct elf_sym); | |
446 | ||
447 | i = 0; | |
448 | while (i < nsyms) { | |
84778508 | 449 | bswap_sym(syms + i); |
0456a177 | 450 | /* Throw away entries which we do not need. */ |
84778508 BS |
451 | if (syms[i].st_shndx == SHN_UNDEF || |
452 | syms[i].st_shndx >= SHN_LORESERVE || | |
453 | ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { | |
454 | nsyms--; | |
455 | if (i < nsyms) { | |
456 | syms[i] = syms[nsyms]; | |
457 | } | |
458 | continue; | |
459 | } | |
c09f12fe WL |
460 | #if defined(TARGET_ARM) || defined(TARGET_MIPS) |
461 | /* The bottom address bit marks a Thumb or MIPS16 symbol. */ | |
462 | syms[i].st_value &= ~(target_ulong)1; | |
463 | #endif | |
84778508 BS |
464 | i++; |
465 | } | |
29718712 | 466 | |
0456a177 WL |
467 | /* |
468 | * Attempt to free the storage associated with the local symbols | |
469 | * that we threw away. Whether or not this has any effect on the | |
470 | * memory allocation depends on the malloc implementation and how | |
471 | * many symbols we managed to discard. | |
472 | */ | |
29718712 SW |
473 | new_syms = realloc(syms, nsyms * sizeof(*syms)); |
474 | if (new_syms == NULL) { | |
475 | free(s); | |
476 | free(syms); | |
477 | free(strings); | |
478 | return; | |
479 | } | |
480 | syms = new_syms; | |
84778508 BS |
481 | |
482 | qsort(syms, nsyms, sizeof(*syms), symcmp); | |
483 | ||
484 | lseek(fd, strtab.sh_offset, SEEK_SET); | |
29718712 SW |
485 | if (read(fd, strings, strtab.sh_size) != strtab.sh_size) { |
486 | free(s); | |
487 | free(syms); | |
488 | free(strings); | |
84778508 | 489 | return; |
29718712 | 490 | } |
84778508 BS |
491 | s->disas_num_syms = nsyms; |
492 | #if ELF_CLASS == ELFCLASS32 | |
493 | s->disas_symtab.elf32 = syms; | |
032e51d7 | 494 | s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; |
84778508 BS |
495 | #else |
496 | s->disas_symtab.elf64 = syms; | |
032e51d7 | 497 | s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; |
84778508 BS |
498 | #endif |
499 | s->next = syminfos; | |
500 | syminfos = s; | |
501 | } | |
502 | ||
c09f12fe WL |
503 | /* Check the elf header and see if this a target elf binary. */ |
504 | int is_target_elf_binary(int fd) | |
505 | { | |
506 | uint8_t buf[128]; | |
507 | struct elfhdr elf_ex; | |
508 | ||
509 | if (lseek(fd, 0L, SEEK_SET) < 0) { | |
510 | return 0; | |
511 | } | |
512 | if (read(fd, buf, sizeof(buf)) < 0) { | |
513 | return 0; | |
514 | } | |
515 | ||
516 | elf_ex = *((struct elfhdr *)buf); | |
517 | bswap_ehdr(&elf_ex); | |
518 | ||
519 | if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || | |
520 | (!elf_check_arch(elf_ex.e_machine))) { | |
521 | return 0; | |
522 | } else { | |
523 | return 1; | |
524 | } | |
525 | } | |
526 | ||
527 | static int | |
528 | load_elf_sections(const struct elfhdr *hdr, struct elf_phdr *phdr, int fd, | |
529 | abi_ulong rbase, abi_ulong *baddrp) | |
530 | { | |
531 | struct elf_phdr *elf_ppnt; | |
532 | abi_ulong baddr; | |
533 | int i; | |
534 | bool first; | |
535 | ||
536 | /* | |
537 | * Now we do a little grungy work by mmaping the ELF image into | |
538 | * the correct location in memory. At this point, we assume that | |
539 | * the image should be loaded at fixed address, not at a variable | |
540 | * address. | |
541 | */ | |
542 | first = true; | |
543 | for (i = 0, elf_ppnt = phdr; i < hdr->e_phnum; i++, elf_ppnt++) { | |
544 | int elf_prot = 0; | |
545 | abi_ulong error; | |
546 | ||
547 | /* XXX Skip memsz == 0. */ | |
548 | if (elf_ppnt->p_type != PT_LOAD) { | |
549 | continue; | |
550 | } | |
551 | ||
552 | if (elf_ppnt->p_flags & PF_R) { | |
553 | elf_prot |= PROT_READ; | |
554 | } | |
555 | if (elf_ppnt->p_flags & PF_W) { | |
556 | elf_prot |= PROT_WRITE; | |
557 | } | |
558 | if (elf_ppnt->p_flags & PF_X) { | |
559 | elf_prot |= PROT_EXEC; | |
560 | } | |
561 | ||
562 | error = target_mmap(TARGET_ELF_PAGESTART(rbase + elf_ppnt->p_vaddr), | |
563 | (elf_ppnt->p_filesz + | |
564 | TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)), | |
565 | elf_prot, | |
566 | (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE), | |
567 | fd, | |
568 | (elf_ppnt->p_offset - | |
569 | TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); | |
570 | if (error == -1) { | |
571 | perror("mmap"); | |
572 | exit(-1); | |
573 | } else if (elf_ppnt->p_memsz != elf_ppnt->p_filesz) { | |
574 | abi_ulong start_bss, end_bss; | |
575 | ||
576 | start_bss = rbase + elf_ppnt->p_vaddr + elf_ppnt->p_filesz; | |
577 | end_bss = rbase + elf_ppnt->p_vaddr + elf_ppnt->p_memsz; | |
578 | ||
579 | /* | |
580 | * Calling set_brk effectively mmaps the pages that we need for the | |
581 | * bss and break sections. | |
582 | */ | |
583 | set_brk(start_bss, end_bss); | |
584 | padzero(start_bss, end_bss); | |
585 | } | |
586 | ||
587 | if (first) { | |
588 | baddr = TARGET_ELF_PAGESTART(rbase + elf_ppnt->p_vaddr); | |
589 | first = false; | |
590 | } | |
591 | } | |
592 | ||
593 | if (baddrp != NULL) { | |
594 | *baddrp = baddr; | |
595 | } | |
596 | return 0; | |
597 | } | |
598 | ||
afcbcff8 | 599 | int load_elf_binary(struct bsd_binprm *bprm, struct target_pt_regs *regs, |
b4bebeee | 600 | struct image_info *info) |
84778508 BS |
601 | { |
602 | struct elfhdr elf_ex; | |
603 | struct elfhdr interp_elf_ex; | |
84778508 | 604 | int interpreter_fd = -1; /* avoid warning */ |
c09f12fe | 605 | abi_ulong load_addr; |
84778508 | 606 | int i; |
c09f12fe | 607 | struct elf_phdr *elf_ppnt; |
84778508 | 608 | struct elf_phdr *elf_phdata; |
c09f12fe WL |
609 | abi_ulong elf_brk; |
610 | int error, retval; | |
611 | char *elf_interpreter; | |
612 | abi_ulong baddr, elf_entry, et_dyn_addr, interp_load_addr = 0; | |
84778508 | 613 | abi_ulong reloc_func_desc = 0; |
84778508 | 614 | |
84778508 | 615 | load_addr = 0; |
84778508 | 616 | elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ |
84778508 | 617 | bswap_ehdr(&elf_ex); |
84778508 BS |
618 | |
619 | /* First of all, some simple consistency checks */ | |
620 | if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || | |
0456a177 | 621 | (!elf_check_arch(elf_ex.e_machine))) { |
84778508 BS |
622 | return -ENOEXEC; |
623 | } | |
624 | ||
625 | bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p); | |
0456a177 WL |
626 | bprm->p = copy_elf_strings(bprm->envc, bprm->envp, bprm->page, bprm->p); |
627 | bprm->p = copy_elf_strings(bprm->argc, bprm->argv, bprm->page, bprm->p); | |
84778508 BS |
628 | if (!bprm->p) { |
629 | retval = -E2BIG; | |
630 | } | |
631 | ||
632 | /* Now read in all of the header information */ | |
0456a177 | 633 | elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize * elf_ex.e_phnum); |
84778508 BS |
634 | if (elf_phdata == NULL) { |
635 | return -ENOMEM; | |
636 | } | |
637 | ||
638 | retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET); | |
b4bebeee WL |
639 | if (retval > 0) { |
640 | retval = read(bprm->fd, (char *)elf_phdata, | |
84778508 BS |
641 | elf_ex.e_phentsize * elf_ex.e_phnum); |
642 | } | |
643 | ||
644 | if (retval < 0) { | |
645 | perror("load_elf_binary"); | |
646 | exit(-1); | |
b4bebeee | 647 | free(elf_phdata); |
84778508 BS |
648 | return -errno; |
649 | } | |
650 | ||
b62f790c | 651 | bswap_phdr(elf_phdata, elf_ex.e_phnum); |
84778508 BS |
652 | elf_ppnt = elf_phdata; |
653 | ||
84778508 BS |
654 | elf_brk = 0; |
655 | ||
656 | ||
84778508 | 657 | elf_interpreter = NULL; |
0456a177 | 658 | for (i = 0; i < elf_ex.e_phnum; i++) { |
84778508 | 659 | if (elf_ppnt->p_type == PT_INTERP) { |
0456a177 | 660 | if (elf_interpreter != NULL) { |
b4bebeee | 661 | free(elf_phdata); |
84778508 BS |
662 | free(elf_interpreter); |
663 | close(bprm->fd); | |
664 | return -EINVAL; | |
665 | } | |
666 | ||
84778508 | 667 | elf_interpreter = (char *)malloc(elf_ppnt->p_filesz); |
84778508 | 668 | if (elf_interpreter == NULL) { |
b4bebeee | 669 | free(elf_phdata); |
84778508 BS |
670 | close(bprm->fd); |
671 | return -ENOMEM; | |
672 | } | |
673 | ||
674 | retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET); | |
b4bebeee | 675 | if (retval >= 0) { |
84778508 BS |
676 | retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz); |
677 | } | |
b4bebeee | 678 | if (retval < 0) { |
84778508 BS |
679 | perror("load_elf_binary2"); |
680 | exit(-1); | |
681 | } | |
682 | ||
84778508 BS |
683 | if (retval >= 0) { |
684 | retval = open(path(elf_interpreter), O_RDONLY); | |
b4bebeee | 685 | if (retval >= 0) { |
84778508 | 686 | interpreter_fd = retval; |
0456a177 | 687 | } else { |
84778508 BS |
688 | perror(elf_interpreter); |
689 | exit(-1); | |
690 | /* retval = -errno; */ | |
691 | } | |
692 | } | |
693 | ||
694 | if (retval >= 0) { | |
695 | retval = lseek(interpreter_fd, 0, SEEK_SET); | |
b4bebeee WL |
696 | if (retval >= 0) { |
697 | retval = read(interpreter_fd, bprm->buf, 128); | |
84778508 BS |
698 | } |
699 | } | |
700 | if (retval >= 0) { | |
0456a177 | 701 | interp_elf_ex = *((struct elfhdr *) bprm->buf); |
84778508 BS |
702 | } |
703 | if (retval < 0) { | |
704 | perror("load_elf_binary3"); | |
705 | exit(-1); | |
b4bebeee | 706 | free(elf_phdata); |
84778508 BS |
707 | free(elf_interpreter); |
708 | close(bprm->fd); | |
709 | return retval; | |
710 | } | |
711 | } | |
712 | elf_ppnt++; | |
713 | } | |
714 | ||
715 | /* Some simple consistency checks for the interpreter */ | |
b4bebeee | 716 | if (elf_interpreter) { |
84778508 | 717 | if (interp_elf_ex.e_ident[0] != 0x7f || |
ffa03665 | 718 | strncmp((char *)&interp_elf_ex.e_ident[1], "ELF", 3) != 0) { |
84778508 BS |
719 | free(elf_interpreter); |
720 | free(elf_phdata); | |
721 | close(bprm->fd); | |
722 | return -ELIBBAD; | |
723 | } | |
724 | } | |
725 | ||
0456a177 WL |
726 | /* |
727 | * OK, we are done with that, now set up the arg stuff, and then start this | |
728 | * sucker up | |
729 | */ | |
ffa03665 WL |
730 | if (!bprm->p) { |
731 | free(elf_interpreter); | |
732 | free(elf_phdata); | |
733 | close(bprm->fd); | |
734 | return -E2BIG; | |
84778508 BS |
735 | } |
736 | ||
737 | /* OK, This is the point of no return */ | |
738 | info->end_data = 0; | |
739 | info->end_code = 0; | |
740 | info->start_mmap = (abi_ulong)ELF_START_MMAP; | |
741 | info->mmap = 0; | |
742 | elf_entry = (abi_ulong) elf_ex.e_entry; | |
743 | ||
c09f12fe WL |
744 | /* XXX Join this with PT_INTERP search? */ |
745 | baddr = 0; | |
746 | for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { | |
747 | if (elf_ppnt->p_type != PT_LOAD) { | |
748 | continue; | |
749 | } | |
750 | baddr = elf_ppnt->p_vaddr; | |
751 | break; | |
752 | } | |
753 | ||
754 | et_dyn_addr = 0; | |
755 | if (elf_ex.e_type == ET_DYN && baddr == 0) { | |
756 | et_dyn_addr = ELF_ET_DYN_LOAD_ADDR; | |
757 | } | |
758 | ||
759 | /* | |
760 | * Do this so that we can load the interpreter, if need be. We will | |
761 | * change some of these later | |
762 | */ | |
84778508 | 763 | info->rss = 0; |
98b34d35 | 764 | setup_arg_pages(bprm, info, &bprm->p, &bprm->stringp); |
84778508 BS |
765 | info->start_stack = bprm->p; |
766 | ||
c09f12fe | 767 | info->elf_flags = elf_ex.e_flags; |
84778508 | 768 | |
c09f12fe WL |
769 | error = load_elf_sections(&elf_ex, elf_phdata, bprm->fd, et_dyn_addr, |
770 | &load_addr); | |
b4bebeee | 771 | for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { |
c09f12fe | 772 | if (elf_ppnt->p_type != PT_LOAD) { |
84778508 | 773 | continue; |
84778508 | 774 | } |
c09f12fe WL |
775 | if (elf_ppnt->p_memsz > elf_ppnt->p_filesz) |
776 | elf_brk = MAX(elf_brk, et_dyn_addr + elf_ppnt->p_vaddr + | |
777 | elf_ppnt->p_memsz); | |
778 | } | |
779 | if (error != 0) { | |
780 | perror("load_elf_sections"); | |
781 | exit(-1); | |
782 | } | |
84778508 BS |
783 | |
784 | if (elf_interpreter) { | |
ffa03665 WL |
785 | elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd, |
786 | &interp_load_addr); | |
84778508 BS |
787 | reloc_func_desc = interp_load_addr; |
788 | ||
789 | close(interpreter_fd); | |
790 | free(elf_interpreter); | |
791 | ||
792 | if (elf_entry == ~((abi_ulong)0UL)) { | |
793 | printf("Unable to load interpreter\n"); | |
794 | free(elf_phdata); | |
795 | exit(-1); | |
796 | return 0; | |
797 | } | |
c09f12fe WL |
798 | } else { |
799 | interp_load_addr = et_dyn_addr; | |
800 | elf_entry += interp_load_addr; | |
84778508 BS |
801 | } |
802 | ||
803 | free(elf_phdata); | |
804 | ||
0456a177 | 805 | if (qemu_log_enabled()) { |
84778508 | 806 | load_symbols(&elf_ex, bprm->fd); |
0456a177 | 807 | } |
84778508 | 808 | |
ffa03665 | 809 | close(bprm->fd); |
84778508 | 810 | |
98b34d35 WL |
811 | bprm->p = target_create_elf_tables(bprm->p, bprm->argc, bprm->envc, |
812 | bprm->stringp, &elf_ex, load_addr, | |
c09f12fe | 813 | et_dyn_addr, interp_load_addr, info); |
84778508 BS |
814 | info->load_addr = reloc_func_desc; |
815 | info->start_brk = info->brk = elf_brk; | |
84778508 | 816 | info->start_stack = bprm->p; |
c09f12fe | 817 | info->load_bias = 0; |
84778508 | 818 | |
84778508 BS |
819 | info->entry = elf_entry; |
820 | ||
0475f8fa WL |
821 | #ifdef USE_ELF_CORE_DUMP |
822 | bprm->core_dump = &elf_core_dump; | |
823 | #else | |
824 | bprm->core_dump = NULL; | |
825 | #endif | |
826 | ||
84778508 BS |
827 | return 0; |
828 | } | |
829 | ||
84778508 BS |
830 | void do_init_thread(struct target_pt_regs *regs, struct image_info *infop) |
831 | { | |
a8fe6d5d WL |
832 | |
833 | target_thread_init(regs, infop); | |
84778508 | 834 | } |