]>
Commit | Line | Data |
---|---|---|
5fe141fd FB |
1 | static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) |
2 | { | |
3 | bswap16s(&ehdr->e_type); /* Object file type */ | |
4 | bswap16s(&ehdr->e_machine); /* Architecture */ | |
5 | bswap32s(&ehdr->e_version); /* Object file version */ | |
6 | bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ | |
7 | bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ | |
8 | bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ | |
9 | bswap32s(&ehdr->e_flags); /* Processor-specific flags */ | |
10 | bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ | |
11 | bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ | |
12 | bswap16s(&ehdr->e_phnum); /* Program header table entry count */ | |
13 | bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ | |
14 | bswap16s(&ehdr->e_shnum); /* Section header table entry count */ | |
15 | bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ | |
16 | } | |
17 | ||
18 | static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) | |
19 | { | |
20 | bswap32s(&phdr->p_type); /* Segment type */ | |
21 | bswapSZs(&phdr->p_offset); /* Segment file offset */ | |
22 | bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ | |
23 | bswapSZs(&phdr->p_paddr); /* Segment physical address */ | |
24 | bswapSZs(&phdr->p_filesz); /* Segment size in file */ | |
25 | bswapSZs(&phdr->p_memsz); /* Segment size in memory */ | |
26 | bswap32s(&phdr->p_flags); /* Segment flags */ | |
27 | bswapSZs(&phdr->p_align); /* Segment alignment */ | |
28 | } | |
29 | ||
30 | static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) | |
31 | { | |
32 | bswap32s(&shdr->sh_name); | |
33 | bswap32s(&shdr->sh_type); | |
34 | bswapSZs(&shdr->sh_flags); | |
35 | bswapSZs(&shdr->sh_addr); | |
36 | bswapSZs(&shdr->sh_offset); | |
37 | bswapSZs(&shdr->sh_size); | |
38 | bswap32s(&shdr->sh_link); | |
39 | bswap32s(&shdr->sh_info); | |
40 | bswapSZs(&shdr->sh_addralign); | |
41 | bswapSZs(&shdr->sh_entsize); | |
42 | } | |
43 | ||
44 | static void glue(bswap_sym, SZ)(struct elf_sym *sym) | |
45 | { | |
46 | bswap32s(&sym->st_name); | |
47 | bswapSZs(&sym->st_value); | |
48 | bswapSZs(&sym->st_size); | |
49 | bswap16s(&sym->st_shndx); | |
50 | } | |
51 | ||
5dce07e1 TH |
52 | static void glue(bswap_rela, SZ)(struct elf_rela *rela) |
53 | { | |
54 | bswapSZs(&rela->r_offset); | |
55 | bswapSZs(&rela->r_info); | |
56 | bswapSZs((elf_word *)&rela->r_addend); | |
57 | } | |
58 | ||
5fafdf24 | 59 | static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, |
5fe141fd FB |
60 | int n, int type) |
61 | { | |
62 | int i; | |
63 | for(i=0;i<n;i++) { | |
64 | if (shdr_table[i].sh_type == type) | |
65 | return shdr_table + i; | |
66 | } | |
67 | return NULL; | |
68 | } | |
69 | ||
49918a75 PB |
70 | static int glue(symfind, SZ)(const void *s0, const void *s1) |
71 | { | |
a8170e5e | 72 | hwaddr addr = *(hwaddr *)s0; |
49918a75 PB |
73 | struct elf_sym *sym = (struct elf_sym *)s1; |
74 | int result = 0; | |
c7c530cd | 75 | if (addr < sym->st_value) { |
49918a75 | 76 | result = -1; |
c7c530cd | 77 | } else if (addr >= sym->st_value + sym->st_size) { |
49918a75 PB |
78 | result = 1; |
79 | } | |
80 | return result; | |
81 | } | |
82 | ||
ca20cf32 | 83 | static const char *glue(lookup_symbol, SZ)(struct syminfo *s, |
a8170e5e | 84 | hwaddr orig_addr) |
49918a75 PB |
85 | { |
86 | struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); | |
49918a75 PB |
87 | struct elf_sym *sym; |
88 | ||
c7c530cd SW |
89 | sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), |
90 | glue(symfind, SZ)); | |
660f11be | 91 | if (sym != NULL) { |
49918a75 PB |
92 | return s->disas_strtab + sym->st_name; |
93 | } | |
94 | ||
95 | return ""; | |
96 | } | |
97 | ||
98 | static int glue(symcmp, SZ)(const void *s0, const void *s1) | |
99 | { | |
100 | struct elf_sym *sym0 = (struct elf_sym *)s0; | |
101 | struct elf_sym *sym1 = (struct elf_sym *)s1; | |
102 | return (sym0->st_value < sym1->st_value) | |
103 | ? -1 | |
104 | : ((sym0->st_value > sym1->st_value) ? 1 : 0); | |
105 | } | |
106 | ||
040425f8 AB |
107 | static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, |
108 | int clear_lsb, symbol_fn_t sym_cb) | |
5fe141fd | 109 | { |
040425f8 AB |
110 | struct elf_shdr *symtab, *strtab; |
111 | g_autofree struct elf_shdr *shdr_table = NULL; | |
112 | g_autofree struct elf_sym *syms = NULL; | |
113 | g_autofree char *str = NULL; | |
5fe141fd FB |
114 | struct syminfo *s; |
115 | int nsyms, i; | |
5fe141fd | 116 | |
5fafdf24 | 117 | shdr_table = load_at(fd, ehdr->e_shoff, |
5fe141fd | 118 | sizeof(struct elf_shdr) * ehdr->e_shnum); |
040425f8 AB |
119 | if (!shdr_table) { |
120 | return ; | |
121 | } | |
3b46e624 | 122 | |
5fe141fd FB |
123 | if (must_swab) { |
124 | for (i = 0; i < ehdr->e_shnum; i++) { | |
125 | glue(bswap_shdr, SZ)(shdr_table + i); | |
126 | } | |
127 | } | |
3b46e624 | 128 | |
5fe141fd | 129 | symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); |
040425f8 AB |
130 | if (!symtab) { |
131 | return; | |
132 | } | |
5fe141fd | 133 | syms = load_at(fd, symtab->sh_offset, symtab->sh_size); |
040425f8 AB |
134 | if (!syms) { |
135 | return; | |
136 | } | |
5fe141fd FB |
137 | |
138 | nsyms = symtab->sh_size / sizeof(struct elf_sym); | |
49918a75 | 139 | |
a2480ffa MC |
140 | /* String table */ |
141 | if (symtab->sh_link >= ehdr->e_shnum) { | |
040425f8 | 142 | return; |
a2480ffa MC |
143 | } |
144 | strtab = &shdr_table[symtab->sh_link]; | |
145 | ||
146 | str = load_at(fd, strtab->sh_offset, strtab->sh_size); | |
147 | if (!str) { | |
040425f8 | 148 | return; |
a2480ffa MC |
149 | } |
150 | ||
49918a75 PB |
151 | i = 0; |
152 | while (i < nsyms) { | |
a2480ffa | 153 | if (must_swab) { |
5fe141fd | 154 | glue(bswap_sym, SZ)(&syms[i]); |
a2480ffa MC |
155 | } |
156 | if (sym_cb) { | |
157 | sym_cb(str + syms[i].st_name, syms[i].st_info, | |
158 | syms[i].st_value, syms[i].st_size); | |
159 | } | |
49918a75 PB |
160 | /* We are only interested in function symbols. |
161 | Throw everything else away. */ | |
162 | if (syms[i].st_shndx == SHN_UNDEF || | |
163 | syms[i].st_shndx >= SHN_LORESERVE || | |
164 | ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { | |
165 | nsyms--; | |
166 | if (i < nsyms) { | |
167 | syms[i] = syms[nsyms]; | |
168 | } | |
169 | continue; | |
170 | } | |
ca20cf32 BS |
171 | if (clear_lsb) { |
172 | /* The bottom address bit marks a Thumb or MIPS16 symbol. */ | |
173 | syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; | |
174 | } | |
49918a75 | 175 | i++; |
5fe141fd | 176 | } |
49918a75 | 177 | |
040425f8 AB |
178 | /* check we have symbols left */ |
179 | if (nsyms == 0) { | |
180 | return; | |
181 | } | |
182 | ||
183 | syms = g_realloc(syms, nsyms * sizeof(*syms)); | |
8ce3c44c MA |
184 | qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); |
185 | for (i = 0; i < nsyms - 1; i++) { | |
186 | if (syms[i].st_size == 0) { | |
187 | syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; | |
e403e433 | 188 | } |
3e372cf8 | 189 | } |
49918a75 | 190 | |
5fe141fd | 191 | /* Commit */ |
7267c094 | 192 | s = g_malloc0(sizeof(*s)); |
49918a75 | 193 | s->lookup_symbol = glue(lookup_symbol, SZ); |
040425f8 | 194 | glue(s->disas_symtab.elf, SZ) = g_steal_pointer(&syms); |
5fe141fd | 195 | s->disas_num_syms = nsyms; |
040425f8 | 196 | s->disas_strtab = g_steal_pointer(&str); |
5fe141fd FB |
197 | s->next = syminfos; |
198 | syminfos = s; | |
5fe141fd FB |
199 | } |
200 | ||
5dce07e1 TH |
201 | static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, |
202 | uint64_t (*translate_fn)(void *, uint64_t), | |
203 | void *translate_opaque, uint8_t *data, | |
204 | struct elf_phdr *ph, int elf_machine) | |
205 | { | |
206 | struct elf_shdr *reltab, *shdr_table = NULL; | |
207 | struct elf_rela *rels = NULL; | |
208 | int nrels, i, ret = -1; | |
209 | elf_word wordval; | |
210 | void *addr; | |
211 | ||
212 | shdr_table = load_at(fd, ehdr->e_shoff, | |
213 | sizeof(struct elf_shdr) * ehdr->e_shnum); | |
214 | if (!shdr_table) { | |
215 | return -1; | |
216 | } | |
217 | if (must_swab) { | |
218 | for (i = 0; i < ehdr->e_shnum; i++) { | |
219 | glue(bswap_shdr, SZ)(&shdr_table[i]); | |
220 | } | |
221 | } | |
222 | ||
223 | reltab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_RELA); | |
224 | if (!reltab) { | |
225 | goto fail; | |
226 | } | |
227 | rels = load_at(fd, reltab->sh_offset, reltab->sh_size); | |
228 | if (!rels) { | |
229 | goto fail; | |
230 | } | |
231 | nrels = reltab->sh_size / sizeof(struct elf_rela); | |
232 | ||
233 | for (i = 0; i < nrels; i++) { | |
234 | if (must_swab) { | |
235 | glue(bswap_rela, SZ)(&rels[i]); | |
236 | } | |
237 | if (rels[i].r_offset < ph->p_vaddr || | |
238 | rels[i].r_offset >= ph->p_vaddr + ph->p_filesz) { | |
239 | continue; | |
240 | } | |
241 | addr = &data[rels[i].r_offset - ph->p_vaddr]; | |
242 | switch (elf_machine) { | |
243 | case EM_S390: | |
244 | switch (rels[i].r_info) { | |
245 | case R_390_RELATIVE: | |
246 | wordval = *(elf_word *)addr; | |
247 | if (must_swab) { | |
248 | bswapSZs(&wordval); | |
249 | } | |
250 | wordval = translate_fn(translate_opaque, wordval); | |
251 | if (must_swab) { | |
252 | bswapSZs(&wordval); | |
253 | } | |
254 | *(elf_word *)addr = wordval; | |
255 | break; | |
256 | default: | |
257 | fprintf(stderr, "Unsupported relocation type %i!\n", | |
258 | (int)rels[i].r_info); | |
259 | } | |
260 | } | |
261 | } | |
262 | ||
263 | ret = 0; | |
264 | fail: | |
265 | g_free(rels); | |
266 | g_free(shdr_table); | |
267 | return ret; | |
268 | } | |
269 | ||
696aa04c LM |
270 | /* |
271 | * Given 'nhdr', a pointer to a range of ELF Notes, search through them | |
272 | * for a note matching type 'elf_note_type' and return a pointer to | |
273 | * the matching ELF note. | |
274 | */ | |
275 | static struct elf_note *glue(get_elf_note_type, SZ)(struct elf_note *nhdr, | |
276 | elf_word note_size, | |
277 | elf_word phdr_align, | |
278 | elf_word elf_note_type) | |
279 | { | |
280 | elf_word nhdr_size = sizeof(struct elf_note); | |
281 | elf_word elf_note_entry_offset = 0; | |
282 | elf_word note_type; | |
283 | elf_word nhdr_namesz; | |
284 | elf_word nhdr_descsz; | |
285 | ||
286 | if (nhdr == NULL) { | |
287 | return NULL; | |
288 | } | |
289 | ||
290 | note_type = nhdr->n_type; | |
291 | while (note_type != elf_note_type) { | |
292 | nhdr_namesz = nhdr->n_namesz; | |
293 | nhdr_descsz = nhdr->n_descsz; | |
294 | ||
295 | elf_note_entry_offset = nhdr_size + | |
296 | QEMU_ALIGN_UP(nhdr_namesz, phdr_align) + | |
297 | QEMU_ALIGN_UP(nhdr_descsz, phdr_align); | |
298 | ||
299 | /* | |
300 | * If the offset calculated in this iteration exceeds the | |
301 | * supplied size, we are done and no matching note was found. | |
302 | */ | |
303 | if (elf_note_entry_offset > note_size) { | |
304 | return NULL; | |
305 | } | |
306 | ||
307 | /* skip to the next ELF Note entry */ | |
308 | nhdr = (void *)nhdr + elf_note_entry_offset; | |
309 | note_type = nhdr->n_type; | |
310 | } | |
311 | ||
312 | return nhdr; | |
313 | } | |
314 | ||
409dbce5 | 315 | static int glue(load_elf, SZ)(const char *name, int fd, |
4366e1db | 316 | uint64_t (*elf_note_fn)(void *, void *, bool), |
409dbce5 AJ |
317 | uint64_t (*translate_fn)(void *, uint64_t), |
318 | void *translate_opaque, | |
9596ebb7 | 319 | int must_swab, uint64_t *pentry, |
ca20cf32 | 320 | uint64_t *lowaddr, uint64_t *highaddr, |
6cdda0ff AM |
321 | uint32_t *pflags, int elf_machine, |
322 | int clear_lsb, int data_swab, | |
a2480ffa MC |
323 | AddressSpace *as, bool load_rom, |
324 | symbol_fn_t sym_cb) | |
5fe141fd FB |
325 | { |
326 | struct elfhdr ehdr; | |
327 | struct elf_phdr *phdr = NULL, *ph; | |
328 | int size, i, total_size; | |
816b9fe4 | 329 | elf_word mem_size, file_size, data_offset; |
fd93a799 | 330 | uint64_t addr, low = (uint64_t)-1, high = 0; |
816b9fe4 | 331 | GMappedFile *mapped_file = NULL; |
9ee3c029 | 332 | uint8_t *data = NULL; |
45a50b16 | 333 | char label[128]; |
18674b26 | 334 | int ret = ELF_LOAD_FAILED; |
5fe141fd FB |
335 | |
336 | if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) | |
337 | goto fail; | |
338 | if (must_swab) { | |
339 | glue(bswap_ehdr, SZ)(&ehdr); | |
340 | } | |
341 | ||
8cf6e9da AF |
342 | if (elf_machine <= EM_NONE) { |
343 | /* The caller didn't specify an ARCH, we can figure it out */ | |
344 | elf_machine = ehdr.e_machine; | |
345 | } | |
346 | ||
ca20cf32 | 347 | switch (elf_machine) { |
7f70c937 | 348 | case EM_PPC64: |
7cc47221 PC |
349 | if (ehdr.e_machine != EM_PPC64) { |
350 | if (ehdr.e_machine != EM_PPC) { | |
18674b26 | 351 | ret = ELF_LOAD_WRONG_ARCH; |
7f70c937 | 352 | goto fail; |
18674b26 | 353 | } |
7cc47221 | 354 | } |
7f70c937 BS |
355 | break; |
356 | case EM_X86_64: | |
7cc47221 PC |
357 | if (ehdr.e_machine != EM_X86_64) { |
358 | if (ehdr.e_machine != EM_386) { | |
18674b26 | 359 | ret = ELF_LOAD_WRONG_ARCH; |
7f70c937 | 360 | goto fail; |
18674b26 | 361 | } |
7cc47221 | 362 | } |
7f70c937 | 363 | break; |
16f04416 | 364 | case EM_MICROBLAZE: |
7cc47221 PC |
365 | if (ehdr.e_machine != EM_MICROBLAZE) { |
366 | if (ehdr.e_machine != EM_MICROBLAZE_OLD) { | |
18674b26 | 367 | ret = ELF_LOAD_WRONG_ARCH; |
16f04416 | 368 | goto fail; |
18674b26 | 369 | } |
7cc47221 | 370 | } |
16f04416 | 371 | break; |
98dbe5ac PC |
372 | case EM_MOXIE: |
373 | if (ehdr.e_machine != EM_MOXIE) { | |
374 | if (ehdr.e_machine != EM_MOXIE_OLD) { | |
375 | ret = ELF_LOAD_WRONG_ARCH; | |
376 | goto fail; | |
377 | } | |
378 | } | |
379 | break; | |
56f26045 AR |
380 | case EM_MIPS: |
381 | case EM_NANOMIPS: | |
382 | if ((ehdr.e_machine != EM_MIPS) && | |
383 | (ehdr.e_machine != EM_NANOMIPS)) { | |
384 | ret = ELF_LOAD_WRONG_ARCH; | |
385 | goto fail; | |
386 | } | |
387 | break; | |
7f70c937 | 388 | default: |
18674b26 AK |
389 | if (elf_machine != ehdr.e_machine) { |
390 | ret = ELF_LOAD_WRONG_ARCH; | |
7f70c937 | 391 | goto fail; |
18674b26 | 392 | } |
7f70c937 | 393 | } |
9042c0e2 | 394 | |
6cdda0ff AM |
395 | if (pflags) { |
396 | *pflags = (elf_word)ehdr.e_flags; | |
397 | } | |
9ee3c029 | 398 | if (pentry) |
7d37435b | 399 | *pentry = (uint64_t)(elf_sword)ehdr.e_entry; |
9ee3c029 | 400 | |
a2480ffa | 401 | glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); |
5fe141fd FB |
402 | |
403 | size = ehdr.e_phnum * sizeof(phdr[0]); | |
23bf2e76 SW |
404 | if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) { |
405 | goto fail; | |
406 | } | |
7267c094 | 407 | phdr = g_malloc0(size); |
5fe141fd FB |
408 | if (!phdr) |
409 | goto fail; | |
410 | if (read(fd, phdr, size) != size) | |
04d4b0c3 | 411 | goto fail; |
5fe141fd FB |
412 | if (must_swab) { |
413 | for(i = 0; i < ehdr.e_phnum; i++) { | |
414 | ph = &phdr[i]; | |
415 | glue(bswap_phdr, SZ)(ph); | |
416 | } | |
417 | } | |
3b46e624 | 418 | |
816b9fe4 SG |
419 | /* |
420 | * Since we want to be able to modify the mapped buffer, we set the | |
421 | * 'writeble' parameter to 'true'. Modifications to the buffer are not | |
422 | * written back to the file. | |
423 | */ | |
424 | mapped_file = g_mapped_file_new_from_fd(fd, true, NULL); | |
425 | if (!mapped_file) { | |
426 | goto fail; | |
427 | } | |
428 | ||
5fe141fd FB |
429 | total_size = 0; |
430 | for(i = 0; i < ehdr.e_phnum; i++) { | |
431 | ph = &phdr[i]; | |
432 | if (ph->p_type == PT_LOAD) { | |
d60fa42e FC |
433 | mem_size = ph->p_memsz; /* Size of the ROM */ |
434 | file_size = ph->p_filesz; /* Size of the allocated data */ | |
816b9fe4 SG |
435 | data_offset = ph->p_offset; /* Offset where the data is located */ |
436 | ||
437 | if (file_size > 0) { | |
438 | if (g_mapped_file_get_length(mapped_file) < | |
439 | file_size + data_offset) { | |
04d4b0c3 | 440 | goto fail; |
d60fa42e | 441 | } |
816b9fe4 SG |
442 | |
443 | data = (uint8_t *)g_mapped_file_get_contents(mapped_file); | |
444 | data += data_offset; | |
5fe141fd | 445 | } |
bf173339 PM |
446 | |
447 | /* The ELF spec is somewhat vague about the purpose of the | |
448 | * physical address field. One common use in the embedded world | |
449 | * is that physical address field specifies the load address | |
450 | * and the virtual address field specifies the execution address. | |
451 | * Segments are packed into ROM or flash, and the relocation | |
452 | * and zero-initialization of data is done at runtime. This | |
453 | * means that the memsz header represents the runtime size of the | |
454 | * segment, but the filesz represents the loadtime size. If | |
455 | * we try to honour the memsz value for an ELF file like this | |
456 | * we will end up with overlapping segments (which the | |
457 | * loader.c code will later reject). | |
458 | * We support ELF files using this scheme by by checking whether | |
459 | * paddr + memsz for this segment would overlap with any other | |
460 | * segment. If so, then we assume it's using this scheme and | |
461 | * truncate the loaded segment to the filesz size. | |
462 | * If the segment considered as being memsz size doesn't overlap | |
463 | * then we use memsz for the segment length, to handle ELF files | |
464 | * which assume that the loader will do the zero-initialization. | |
465 | */ | |
466 | if (mem_size > file_size) { | |
467 | /* If this segment's zero-init portion overlaps another | |
468 | * segment's data or zero-init portion, then truncate this one. | |
469 | * Invalid ELF files where the segments overlap even when | |
470 | * only file_size bytes are loaded will be rejected by | |
471 | * the ROM overlap check in loader.c, so we don't try to | |
472 | * explicitly detect those here. | |
473 | */ | |
474 | int j; | |
475 | elf_word zero_start = ph->p_paddr + file_size; | |
476 | elf_word zero_end = ph->p_paddr + mem_size; | |
477 | ||
478 | for (j = 0; j < ehdr.e_phnum; j++) { | |
479 | struct elf_phdr *jph = &phdr[j]; | |
480 | ||
481 | if (i != j && jph->p_type == PT_LOAD) { | |
482 | elf_word other_start = jph->p_paddr; | |
483 | elf_word other_end = jph->p_paddr + jph->p_memsz; | |
484 | ||
485 | if (!(other_start >= zero_end || | |
486 | zero_start >= other_end)) { | |
487 | mem_size = file_size; | |
488 | break; | |
489 | } | |
490 | } | |
491 | } | |
492 | } | |
493 | ||
41a26351 SG |
494 | if (mem_size > INT_MAX - total_size) { |
495 | ret = ELF_LOAD_TOO_BIG; | |
496 | goto fail; | |
497 | } | |
498 | ||
83c1f87c PB |
499 | /* address_offset is hack for kernel images that are |
500 | linked at the wrong physical address. */ | |
409dbce5 AJ |
501 | if (translate_fn) { |
502 | addr = translate_fn(translate_opaque, ph->p_paddr); | |
5dce07e1 TH |
503 | glue(elf_reloc, SZ)(&ehdr, fd, must_swab, translate_fn, |
504 | translate_opaque, data, ph, elf_machine); | |
409dbce5 AJ |
505 | } else { |
506 | addr = ph->p_paddr; | |
507 | } | |
5fe141fd | 508 | |
7ef295ea PC |
509 | if (data_swab) { |
510 | int j; | |
511 | for (j = 0; j < file_size; j += (1 << data_swab)) { | |
512 | uint8_t *dp = data + j; | |
513 | switch (data_swab) { | |
514 | case (1): | |
515 | *(uint16_t *)dp = bswap16(*(uint16_t *)dp); | |
516 | break; | |
517 | case (2): | |
518 | *(uint32_t *)dp = bswap32(*(uint32_t *)dp); | |
519 | break; | |
520 | case (3): | |
521 | *(uint64_t *)dp = bswap64(*(uint64_t *)dp); | |
522 | break; | |
523 | default: | |
524 | g_assert_not_reached(); | |
525 | } | |
526 | } | |
527 | } | |
528 | ||
7e9c7ffe HS |
529 | /* the entry pointer in the ELF header is a virtual |
530 | * address, if the text segments paddr and vaddr differ | |
531 | * we need to adjust the entry */ | |
532 | if (pentry && !translate_fn && | |
533 | ph->p_vaddr != ph->p_paddr && | |
534 | ehdr.e_entry >= ph->p_vaddr && | |
535 | ehdr.e_entry < ph->p_vaddr + ph->p_filesz && | |
536 | ph->p_flags & PF_X) { | |
537 | *pentry = ehdr.e_entry - ph->p_vaddr + ph->p_paddr; | |
538 | } | |
539 | ||
816b9fe4 SG |
540 | /* Some ELF files really do have segments of zero size; |
541 | * just ignore them rather than trying to create empty | |
542 | * ROM blobs, because the zero-length blob can falsely | |
543 | * trigger the overlapping-ROM-blobs check. | |
544 | */ | |
545 | if (mem_size != 0) { | |
f33e5e62 PM |
546 | if (load_rom) { |
547 | snprintf(label, sizeof(label), "phdr #%d: %s", i, name); | |
548 | ||
816b9fe4 SG |
549 | /* |
550 | * rom_add_elf_program() takes its own reference to | |
551 | * 'mapped_file'. | |
552 | */ | |
553 | rom_add_elf_program(label, mapped_file, data, file_size, | |
554 | mem_size, addr, as); | |
f33e5e62 | 555 | } else { |
5579b524 PMD |
556 | MemTxResult res; |
557 | ||
558 | res = address_space_write(as ? as : &address_space_memory, | |
559 | addr, MEMTXATTRS_UNSPECIFIED, | |
560 | data, file_size); | |
561 | if (res != MEMTX_OK) { | |
562 | goto fail; | |
563 | } | |
f33e5e62 | 564 | } |
34f1b23f | 565 | } |
5fe141fd FB |
566 | |
567 | total_size += mem_size; | |
fd93a799 | 568 | if (addr < low) |
74287114 | 569 | low = addr; |
fd93a799 | 570 | if ((addr + mem_size) > high) |
74287114 | 571 | high = addr + mem_size; |
5fe141fd | 572 | |
9ee3c029 | 573 | data = NULL; |
696aa04c LM |
574 | |
575 | } else if (ph->p_type == PT_NOTE && elf_note_fn) { | |
576 | struct elf_note *nhdr = NULL; | |
577 | ||
578 | file_size = ph->p_filesz; /* Size of the range of ELF notes */ | |
816b9fe4 SG |
579 | data_offset = ph->p_offset; /* Offset where the notes are located */ |
580 | ||
581 | if (file_size > 0) { | |
582 | if (g_mapped_file_get_length(mapped_file) < | |
583 | file_size + data_offset) { | |
696aa04c LM |
584 | goto fail; |
585 | } | |
816b9fe4 SG |
586 | |
587 | data = (uint8_t *)g_mapped_file_get_contents(mapped_file); | |
588 | data += data_offset; | |
696aa04c LM |
589 | } |
590 | ||
591 | /* | |
592 | * Search the ELF notes to find one with a type matching the | |
593 | * value passed in via 'translate_opaque' | |
594 | */ | |
595 | nhdr = (struct elf_note *)data; | |
596 | assert(translate_opaque != NULL); | |
597 | nhdr = glue(get_elf_note_type, SZ)(nhdr, file_size, ph->p_align, | |
598 | *(uint64_t *)translate_opaque); | |
599 | if (nhdr != NULL) { | |
600 | bool is64 = | |
601 | sizeof(struct elf_note) == sizeof(struct elf64_note); | |
602 | elf_note_fn((void *)nhdr, (void *)&ph->p_align, is64); | |
603 | } | |
696aa04c | 604 | data = NULL; |
5fe141fd FB |
605 | } |
606 | } | |
4366e1db | 607 | |
74287114 | 608 | if (lowaddr) |
82790064 | 609 | *lowaddr = (uint64_t)(elf_sword)low; |
74287114 | 610 | if (highaddr) |
82790064 | 611 | *highaddr = (uint64_t)(elf_sword)high; |
816b9fe4 | 612 | ret = total_size; |
04d4b0c3 | 613 | fail: |
a1ecb438 PM |
614 | if (mapped_file) { |
615 | g_mapped_file_unref(mapped_file); | |
616 | } | |
7267c094 | 617 | g_free(phdr); |
18674b26 | 618 | return ret; |
5fe141fd | 619 | } |