]>
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 | ||
ca20cf32 BS |
107 | static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, |
108 | int clear_lsb) | |
5fe141fd FB |
109 | { |
110 | struct elf_shdr *symtab, *strtab, *shdr_table = NULL; | |
111 | struct elf_sym *syms = NULL; | |
5fe141fd FB |
112 | struct syminfo *s; |
113 | int nsyms, i; | |
114 | char *str = NULL; | |
115 | ||
5fafdf24 | 116 | shdr_table = load_at(fd, ehdr->e_shoff, |
5fe141fd FB |
117 | sizeof(struct elf_shdr) * ehdr->e_shnum); |
118 | if (!shdr_table) | |
119 | return -1; | |
3b46e624 | 120 | |
5fe141fd FB |
121 | if (must_swab) { |
122 | for (i = 0; i < ehdr->e_shnum; i++) { | |
123 | glue(bswap_shdr, SZ)(shdr_table + i); | |
124 | } | |
125 | } | |
3b46e624 | 126 | |
5fe141fd FB |
127 | symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); |
128 | if (!symtab) | |
129 | goto fail; | |
130 | syms = load_at(fd, symtab->sh_offset, symtab->sh_size); | |
131 | if (!syms) | |
132 | goto fail; | |
133 | ||
134 | nsyms = symtab->sh_size / sizeof(struct elf_sym); | |
49918a75 PB |
135 | |
136 | i = 0; | |
137 | while (i < nsyms) { | |
5fe141fd FB |
138 | if (must_swab) |
139 | glue(bswap_sym, SZ)(&syms[i]); | |
49918a75 PB |
140 | /* We are only interested in function symbols. |
141 | Throw everything else away. */ | |
142 | if (syms[i].st_shndx == SHN_UNDEF || | |
143 | syms[i].st_shndx >= SHN_LORESERVE || | |
144 | ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { | |
145 | nsyms--; | |
146 | if (i < nsyms) { | |
147 | syms[i] = syms[nsyms]; | |
148 | } | |
149 | continue; | |
150 | } | |
ca20cf32 BS |
151 | if (clear_lsb) { |
152 | /* The bottom address bit marks a Thumb or MIPS16 symbol. */ | |
153 | syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; | |
154 | } | |
49918a75 | 155 | i++; |
5fe141fd | 156 | } |
8ce3c44c | 157 | syms = g_realloc(syms, nsyms * sizeof(*syms)); |
49918a75 | 158 | |
8ce3c44c MA |
159 | qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); |
160 | for (i = 0; i < nsyms - 1; i++) { | |
161 | if (syms[i].st_size == 0) { | |
162 | syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; | |
e403e433 | 163 | } |
3e372cf8 | 164 | } |
49918a75 | 165 | |
5fe141fd FB |
166 | /* String table */ |
167 | if (symtab->sh_link >= ehdr->e_shnum) | |
168 | goto fail; | |
169 | strtab = &shdr_table[symtab->sh_link]; | |
170 | ||
171 | str = load_at(fd, strtab->sh_offset, strtab->sh_size); | |
172 | if (!str) | |
49918a75 | 173 | goto fail; |
5fe141fd FB |
174 | |
175 | /* Commit */ | |
7267c094 | 176 | s = g_malloc0(sizeof(*s)); |
49918a75 PB |
177 | s->lookup_symbol = glue(lookup_symbol, SZ); |
178 | glue(s->disas_symtab.elf, SZ) = syms; | |
5fe141fd FB |
179 | s->disas_num_syms = nsyms; |
180 | s->disas_strtab = str; | |
181 | s->next = syminfos; | |
182 | syminfos = s; | |
7267c094 | 183 | g_free(shdr_table); |
5fe141fd FB |
184 | return 0; |
185 | fail: | |
7267c094 AL |
186 | g_free(syms); |
187 | g_free(str); | |
188 | g_free(shdr_table); | |
5fe141fd FB |
189 | return -1; |
190 | } | |
191 | ||
5dce07e1 TH |
192 | static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, |
193 | uint64_t (*translate_fn)(void *, uint64_t), | |
194 | void *translate_opaque, uint8_t *data, | |
195 | struct elf_phdr *ph, int elf_machine) | |
196 | { | |
197 | struct elf_shdr *reltab, *shdr_table = NULL; | |
198 | struct elf_rela *rels = NULL; | |
199 | int nrels, i, ret = -1; | |
200 | elf_word wordval; | |
201 | void *addr; | |
202 | ||
203 | shdr_table = load_at(fd, ehdr->e_shoff, | |
204 | sizeof(struct elf_shdr) * ehdr->e_shnum); | |
205 | if (!shdr_table) { | |
206 | return -1; | |
207 | } | |
208 | if (must_swab) { | |
209 | for (i = 0; i < ehdr->e_shnum; i++) { | |
210 | glue(bswap_shdr, SZ)(&shdr_table[i]); | |
211 | } | |
212 | } | |
213 | ||
214 | reltab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_RELA); | |
215 | if (!reltab) { | |
216 | goto fail; | |
217 | } | |
218 | rels = load_at(fd, reltab->sh_offset, reltab->sh_size); | |
219 | if (!rels) { | |
220 | goto fail; | |
221 | } | |
222 | nrels = reltab->sh_size / sizeof(struct elf_rela); | |
223 | ||
224 | for (i = 0; i < nrels; i++) { | |
225 | if (must_swab) { | |
226 | glue(bswap_rela, SZ)(&rels[i]); | |
227 | } | |
228 | if (rels[i].r_offset < ph->p_vaddr || | |
229 | rels[i].r_offset >= ph->p_vaddr + ph->p_filesz) { | |
230 | continue; | |
231 | } | |
232 | addr = &data[rels[i].r_offset - ph->p_vaddr]; | |
233 | switch (elf_machine) { | |
234 | case EM_S390: | |
235 | switch (rels[i].r_info) { | |
236 | case R_390_RELATIVE: | |
237 | wordval = *(elf_word *)addr; | |
238 | if (must_swab) { | |
239 | bswapSZs(&wordval); | |
240 | } | |
241 | wordval = translate_fn(translate_opaque, wordval); | |
242 | if (must_swab) { | |
243 | bswapSZs(&wordval); | |
244 | } | |
245 | *(elf_word *)addr = wordval; | |
246 | break; | |
247 | default: | |
248 | fprintf(stderr, "Unsupported relocation type %i!\n", | |
249 | (int)rels[i].r_info); | |
250 | } | |
251 | } | |
252 | } | |
253 | ||
254 | ret = 0; | |
255 | fail: | |
256 | g_free(rels); | |
257 | g_free(shdr_table); | |
258 | return ret; | |
259 | } | |
260 | ||
409dbce5 AJ |
261 | static int glue(load_elf, SZ)(const char *name, int fd, |
262 | uint64_t (*translate_fn)(void *, uint64_t), | |
263 | void *translate_opaque, | |
9596ebb7 | 264 | int must_swab, uint64_t *pentry, |
ca20cf32 BS |
265 | uint64_t *lowaddr, uint64_t *highaddr, |
266 | int elf_machine, int clear_lsb) | |
5fe141fd FB |
267 | { |
268 | struct elfhdr ehdr; | |
269 | struct elf_phdr *phdr = NULL, *ph; | |
270 | int size, i, total_size; | |
d60fa42e | 271 | elf_word mem_size, file_size; |
fd93a799 | 272 | uint64_t addr, low = (uint64_t)-1, high = 0; |
9ee3c029 | 273 | uint8_t *data = NULL; |
45a50b16 | 274 | char label[128]; |
18674b26 | 275 | int ret = ELF_LOAD_FAILED; |
5fe141fd FB |
276 | |
277 | if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) | |
278 | goto fail; | |
279 | if (must_swab) { | |
280 | glue(bswap_ehdr, SZ)(&ehdr); | |
281 | } | |
282 | ||
ca20cf32 | 283 | switch (elf_machine) { |
7f70c937 BS |
284 | case EM_PPC64: |
285 | if (EM_PPC64 != ehdr.e_machine) | |
18674b26 AK |
286 | if (EM_PPC != ehdr.e_machine) { |
287 | ret = ELF_LOAD_WRONG_ARCH; | |
7f70c937 | 288 | goto fail; |
18674b26 | 289 | } |
7f70c937 BS |
290 | break; |
291 | case EM_X86_64: | |
292 | if (EM_X86_64 != ehdr.e_machine) | |
18674b26 AK |
293 | if (EM_386 != ehdr.e_machine) { |
294 | ret = ELF_LOAD_WRONG_ARCH; | |
7f70c937 | 295 | goto fail; |
18674b26 | 296 | } |
7f70c937 | 297 | break; |
16f04416 EI |
298 | case EM_MICROBLAZE: |
299 | if (EM_MICROBLAZE != ehdr.e_machine) | |
18674b26 AK |
300 | if (EM_MICROBLAZE_OLD != ehdr.e_machine) { |
301 | ret = ELF_LOAD_WRONG_ARCH; | |
16f04416 | 302 | goto fail; |
18674b26 | 303 | } |
16f04416 | 304 | break; |
7f70c937 | 305 | default: |
18674b26 AK |
306 | if (elf_machine != ehdr.e_machine) { |
307 | ret = ELF_LOAD_WRONG_ARCH; | |
7f70c937 | 308 | goto fail; |
18674b26 | 309 | } |
7f70c937 | 310 | } |
9042c0e2 | 311 | |
9ee3c029 | 312 | if (pentry) |
82790064 | 313 | *pentry = (uint64_t)(elf_sword)ehdr.e_entry; |
9ee3c029 | 314 | |
ca20cf32 | 315 | glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb); |
5fe141fd FB |
316 | |
317 | size = ehdr.e_phnum * sizeof(phdr[0]); | |
23bf2e76 SW |
318 | if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) { |
319 | goto fail; | |
320 | } | |
7267c094 | 321 | phdr = g_malloc0(size); |
5fe141fd FB |
322 | if (!phdr) |
323 | goto fail; | |
324 | if (read(fd, phdr, size) != size) | |
04d4b0c3 | 325 | goto fail; |
5fe141fd FB |
326 | if (must_swab) { |
327 | for(i = 0; i < ehdr.e_phnum; i++) { | |
328 | ph = &phdr[i]; | |
329 | glue(bswap_phdr, SZ)(ph); | |
330 | } | |
331 | } | |
3b46e624 | 332 | |
5fe141fd FB |
333 | total_size = 0; |
334 | for(i = 0; i < ehdr.e_phnum; i++) { | |
335 | ph = &phdr[i]; | |
336 | if (ph->p_type == PT_LOAD) { | |
d60fa42e FC |
337 | mem_size = ph->p_memsz; /* Size of the ROM */ |
338 | file_size = ph->p_filesz; /* Size of the allocated data */ | |
339 | data = g_malloc0(file_size); | |
5fe141fd | 340 | if (ph->p_filesz > 0) { |
d60fa42e | 341 | if (lseek(fd, ph->p_offset, SEEK_SET) < 0) { |
04d4b0c3 | 342 | goto fail; |
d60fa42e FC |
343 | } |
344 | if (read(fd, data, file_size) != file_size) { | |
04d4b0c3 | 345 | goto fail; |
d60fa42e | 346 | } |
5fe141fd | 347 | } |
83c1f87c PB |
348 | /* address_offset is hack for kernel images that are |
349 | linked at the wrong physical address. */ | |
409dbce5 AJ |
350 | if (translate_fn) { |
351 | addr = translate_fn(translate_opaque, ph->p_paddr); | |
5dce07e1 TH |
352 | glue(elf_reloc, SZ)(&ehdr, fd, must_swab, translate_fn, |
353 | translate_opaque, data, ph, elf_machine); | |
409dbce5 AJ |
354 | } else { |
355 | addr = ph->p_paddr; | |
356 | } | |
5fe141fd | 357 | |
7e9c7ffe HS |
358 | /* the entry pointer in the ELF header is a virtual |
359 | * address, if the text segments paddr and vaddr differ | |
360 | * we need to adjust the entry */ | |
361 | if (pentry && !translate_fn && | |
362 | ph->p_vaddr != ph->p_paddr && | |
363 | ehdr.e_entry >= ph->p_vaddr && | |
364 | ehdr.e_entry < ph->p_vaddr + ph->p_filesz && | |
365 | ph->p_flags & PF_X) { | |
366 | *pentry = ehdr.e_entry - ph->p_vaddr + ph->p_paddr; | |
367 | } | |
368 | ||
45a50b16 | 369 | snprintf(label, sizeof(label), "phdr #%d: %s", i, name); |
d60fa42e FC |
370 | |
371 | /* rom_add_elf_program() seize the ownership of 'data' */ | |
372 | rom_add_elf_program(label, data, file_size, mem_size, addr); | |
5fe141fd FB |
373 | |
374 | total_size += mem_size; | |
fd93a799 | 375 | if (addr < low) |
74287114 | 376 | low = addr; |
fd93a799 | 377 | if ((addr + mem_size) > high) |
74287114 | 378 | high = addr + mem_size; |
5fe141fd | 379 | |
9ee3c029 | 380 | data = NULL; |
5fe141fd FB |
381 | } |
382 | } | |
7267c094 | 383 | g_free(phdr); |
74287114 | 384 | if (lowaddr) |
82790064 | 385 | *lowaddr = (uint64_t)(elf_sword)low; |
74287114 | 386 | if (highaddr) |
82790064 | 387 | *highaddr = (uint64_t)(elf_sword)high; |
5fe141fd | 388 | return total_size; |
04d4b0c3 | 389 | fail: |
7267c094 AL |
390 | g_free(data); |
391 | g_free(phdr); | |
18674b26 | 392 | return ret; |
5fe141fd | 393 | } |