]>
Commit | Line | Data |
---|---|---|
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 | ||
52 | static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, | |
53 | int n, int type) | |
54 | { | |
55 | int i; | |
56 | for(i=0;i<n;i++) { | |
57 | if (shdr_table[i].sh_type == type) | |
58 | return shdr_table + i; | |
59 | } | |
60 | return NULL; | |
61 | } | |
62 | ||
63 | static int glue(symfind, SZ)(const void *s0, const void *s1) | |
64 | { | |
65 | struct elf_sym *key = (struct elf_sym *)s0; | |
66 | struct elf_sym *sym = (struct elf_sym *)s1; | |
67 | int result = 0; | |
68 | if (key->st_value < sym->st_value) { | |
69 | result = -1; | |
70 | } else if (key->st_value >= sym->st_value + sym->st_size) { | |
71 | result = 1; | |
72 | } | |
73 | return result; | |
74 | } | |
75 | ||
76 | static const char *glue(lookup_symbol, SZ)(struct syminfo *s, | |
77 | target_phys_addr_t orig_addr) | |
78 | { | |
79 | struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); | |
80 | struct elf_sym key; | |
81 | struct elf_sym *sym; | |
82 | ||
83 | key.st_value = orig_addr; | |
84 | ||
85 | sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), glue(symfind, SZ)); | |
86 | if (sym != NULL) { | |
87 | return s->disas_strtab + sym->st_name; | |
88 | } | |
89 | ||
90 | return ""; | |
91 | } | |
92 | ||
93 | static int glue(symcmp, SZ)(const void *s0, const void *s1) | |
94 | { | |
95 | struct elf_sym *sym0 = (struct elf_sym *)s0; | |
96 | struct elf_sym *sym1 = (struct elf_sym *)s1; | |
97 | return (sym0->st_value < sym1->st_value) | |
98 | ? -1 | |
99 | : ((sym0->st_value > sym1->st_value) ? 1 : 0); | |
100 | } | |
101 | ||
102 | static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, | |
103 | int clear_lsb) | |
104 | { | |
105 | struct elf_shdr *symtab, *strtab, *shdr_table = NULL; | |
106 | struct elf_sym *syms = NULL; | |
107 | struct syminfo *s; | |
108 | int nsyms, i; | |
109 | char *str = NULL; | |
110 | ||
111 | shdr_table = load_at(fd, ehdr->e_shoff, | |
112 | sizeof(struct elf_shdr) * ehdr->e_shnum); | |
113 | if (!shdr_table) | |
114 | return -1; | |
115 | ||
116 | if (must_swab) { | |
117 | for (i = 0; i < ehdr->e_shnum; i++) { | |
118 | glue(bswap_shdr, SZ)(shdr_table + i); | |
119 | } | |
120 | } | |
121 | ||
122 | symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); | |
123 | if (!symtab) | |
124 | goto fail; | |
125 | syms = load_at(fd, symtab->sh_offset, symtab->sh_size); | |
126 | if (!syms) | |
127 | goto fail; | |
128 | ||
129 | nsyms = symtab->sh_size / sizeof(struct elf_sym); | |
130 | ||
131 | i = 0; | |
132 | while (i < nsyms) { | |
133 | if (must_swab) | |
134 | glue(bswap_sym, SZ)(&syms[i]); | |
135 | /* We are only interested in function symbols. | |
136 | Throw everything else away. */ | |
137 | if (syms[i].st_shndx == SHN_UNDEF || | |
138 | syms[i].st_shndx >= SHN_LORESERVE || | |
139 | ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { | |
140 | nsyms--; | |
141 | if (i < nsyms) { | |
142 | syms[i] = syms[nsyms]; | |
143 | } | |
144 | continue; | |
145 | } | |
146 | if (clear_lsb) { | |
147 | /* The bottom address bit marks a Thumb or MIPS16 symbol. */ | |
148 | syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; | |
149 | } | |
150 | i++; | |
151 | } | |
152 | if (nsyms) { | |
153 | syms = qemu_realloc(syms, nsyms * sizeof(*syms)); | |
154 | ||
155 | qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); | |
156 | } else { | |
157 | qemu_free(syms); | |
158 | syms = NULL; | |
159 | } | |
160 | ||
161 | /* String table */ | |
162 | if (symtab->sh_link >= ehdr->e_shnum) | |
163 | goto fail; | |
164 | strtab = &shdr_table[symtab->sh_link]; | |
165 | ||
166 | str = load_at(fd, strtab->sh_offset, strtab->sh_size); | |
167 | if (!str) | |
168 | goto fail; | |
169 | ||
170 | /* Commit */ | |
171 | s = qemu_mallocz(sizeof(*s)); | |
172 | s->lookup_symbol = glue(lookup_symbol, SZ); | |
173 | glue(s->disas_symtab.elf, SZ) = syms; | |
174 | s->disas_num_syms = nsyms; | |
175 | s->disas_strtab = str; | |
176 | s->next = syminfos; | |
177 | syminfos = s; | |
178 | qemu_free(shdr_table); | |
179 | return 0; | |
180 | fail: | |
181 | qemu_free(syms); | |
182 | qemu_free(str); | |
183 | qemu_free(shdr_table); | |
184 | return -1; | |
185 | } | |
186 | ||
187 | static int glue(load_elf, SZ)(const char *name, int fd, | |
188 | uint64_t (*translate_fn)(void *, uint64_t), | |
189 | void *translate_opaque, | |
190 | int must_swab, uint64_t *pentry, | |
191 | uint64_t *lowaddr, uint64_t *highaddr, | |
192 | int elf_machine, int clear_lsb) | |
193 | { | |
194 | struct elfhdr ehdr; | |
195 | struct elf_phdr *phdr = NULL, *ph; | |
196 | int size, i, total_size; | |
197 | elf_word mem_size; | |
198 | uint64_t addr, low = (uint64_t)-1, high = 0; | |
199 | uint8_t *data = NULL; | |
200 | char label[128]; | |
201 | ||
202 | if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) | |
203 | goto fail; | |
204 | if (must_swab) { | |
205 | glue(bswap_ehdr, SZ)(&ehdr); | |
206 | } | |
207 | ||
208 | switch (elf_machine) { | |
209 | case EM_PPC64: | |
210 | if (EM_PPC64 != ehdr.e_machine) | |
211 | if (EM_PPC != ehdr.e_machine) | |
212 | goto fail; | |
213 | break; | |
214 | case EM_X86_64: | |
215 | if (EM_X86_64 != ehdr.e_machine) | |
216 | if (EM_386 != ehdr.e_machine) | |
217 | goto fail; | |
218 | break; | |
219 | default: | |
220 | if (elf_machine != ehdr.e_machine) | |
221 | goto fail; | |
222 | } | |
223 | ||
224 | if (pentry) | |
225 | *pentry = (uint64_t)(elf_sword)ehdr.e_entry; | |
226 | ||
227 | glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb); | |
228 | ||
229 | size = ehdr.e_phnum * sizeof(phdr[0]); | |
230 | lseek(fd, ehdr.e_phoff, SEEK_SET); | |
231 | phdr = qemu_mallocz(size); | |
232 | if (!phdr) | |
233 | goto fail; | |
234 | if (read(fd, phdr, size) != size) | |
235 | goto fail; | |
236 | if (must_swab) { | |
237 | for(i = 0; i < ehdr.e_phnum; i++) { | |
238 | ph = &phdr[i]; | |
239 | glue(bswap_phdr, SZ)(ph); | |
240 | } | |
241 | } | |
242 | ||
243 | total_size = 0; | |
244 | for(i = 0; i < ehdr.e_phnum; i++) { | |
245 | ph = &phdr[i]; | |
246 | if (ph->p_type == PT_LOAD) { | |
247 | mem_size = ph->p_memsz; | |
248 | /* XXX: avoid allocating */ | |
249 | data = qemu_mallocz(mem_size); | |
250 | if (ph->p_filesz > 0) { | |
251 | if (lseek(fd, ph->p_offset, SEEK_SET) < 0) | |
252 | goto fail; | |
253 | if (read(fd, data, ph->p_filesz) != ph->p_filesz) | |
254 | goto fail; | |
255 | } | |
256 | /* address_offset is hack for kernel images that are | |
257 | linked at the wrong physical address. */ | |
258 | if (translate_fn) { | |
259 | addr = translate_fn(translate_opaque, ph->p_paddr); | |
260 | } else { | |
261 | addr = ph->p_paddr; | |
262 | } | |
263 | ||
264 | snprintf(label, sizeof(label), "phdr #%d: %s", i, name); | |
265 | rom_add_blob_fixed(label, data, mem_size, addr); | |
266 | ||
267 | total_size += mem_size; | |
268 | if (addr < low) | |
269 | low = addr; | |
270 | if ((addr + mem_size) > high) | |
271 | high = addr + mem_size; | |
272 | ||
273 | qemu_free(data); | |
274 | data = NULL; | |
275 | } | |
276 | } | |
277 | qemu_free(phdr); | |
278 | if (lowaddr) | |
279 | *lowaddr = (uint64_t)(elf_sword)low; | |
280 | if (highaddr) | |
281 | *highaddr = (uint64_t)(elf_sword)high; | |
282 | return total_size; | |
283 | fail: | |
284 | qemu_free(data); | |
285 | qemu_free(phdr); | |
286 | return -1; | |
287 | } |