1 |
/* |
2 |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* 3. The name of the author may not be used to endorse or promote products |
13 |
* derived from this software without specific prior written permission. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
* |
27 |
* |
28 |
* $Id: file_ecoff.c,v 1.3 2007/06/17 23:32:20 debug Exp $ |
29 |
* |
30 |
* COMMENT: ECOFF file support |
31 |
*/ |
32 |
|
33 |
/* Note: Included from file.c. */ |
34 |
|
35 |
|
36 |
#include "exec_ecoff.h" |
37 |
|
38 |
|
39 |
/* Special symbol format used by Microsoft-ish COFF files: */ |
40 |
struct ms_sym { |
41 |
unsigned char name[8]; |
42 |
unsigned char value[4]; |
43 |
unsigned char section[2]; |
44 |
unsigned char type[2]; |
45 |
unsigned char storage_class; |
46 |
unsigned char n_aux_syms; |
47 |
}; |
48 |
|
49 |
|
50 |
/* |
51 |
* file_load_ecoff(): |
52 |
* |
53 |
* Loads an ecoff binary image into the emulated memory. The entry point |
54 |
* (read from the ecoff header) is stored in the specified CPU's registers. |
55 |
*/ |
56 |
static void file_load_ecoff(struct machine *m, struct memory *mem, |
57 |
char *filename, uint64_t *entrypointp, |
58 |
int arch, uint64_t *gpp, int *byte_orderp) |
59 |
{ |
60 |
struct ecoff_exechdr exechdr; |
61 |
int f_magic, f_nscns, f_nsyms; |
62 |
int a_magic; |
63 |
off_t f_symptr, a_tsize, a_dsize, a_bsize; |
64 |
uint64_t a_entry, a_tstart, a_dstart, a_bstart, a_gp, end_addr=0; |
65 |
char *format_name; |
66 |
struct ecoff_scnhdr scnhdr; |
67 |
FILE *f; |
68 |
int len, secn, total_len, chunk_size; |
69 |
int encoding = ELFDATA2LSB; /* Assume little-endian. See below */ |
70 |
int program_byte_order = -1; |
71 |
unsigned char buf[8192]; |
72 |
|
73 |
f = fopen(filename, "r"); |
74 |
if (f == NULL) { |
75 |
perror(filename); |
76 |
exit(1); |
77 |
} |
78 |
|
79 |
len = fread(&exechdr, 1, sizeof(exechdr), f); |
80 |
if (len != sizeof(exechdr)) { |
81 |
fprintf(stderr, " not a complete ecoff image\n"); |
82 |
exit(1); |
83 |
} |
84 |
|
85 |
/* |
86 |
* The following code looks a bit ugly, but it should work. The ECOFF |
87 |
* 16-bit magic value seems to be stored in MSB byte order for |
88 |
* big-endian binaries, and LSB byte order for little-endian binaries. |
89 |
* |
90 |
* The unencode() assumes little-endianness by default. |
91 |
*/ |
92 |
unencode(f_magic, &exechdr.f.f_magic, uint16_t); |
93 |
switch (f_magic) { |
94 |
case ((ECOFF_MAGIC_MIPSEB & 0xff) << 8) + |
95 |
((ECOFF_MAGIC_MIPSEB >> 8) & 0xff): |
96 |
format_name = "MIPS1 BE"; |
97 |
encoding = ELFDATA2MSB; |
98 |
break; |
99 |
case ECOFF_MAGIC_MIPSEB: |
100 |
/* NOTE: Big-endian header, little-endian code! */ |
101 |
format_name = "MIPS1 BE-LE"; |
102 |
encoding = ELFDATA2MSB; |
103 |
program_byte_order = ELFDATA2LSB; |
104 |
break; |
105 |
case ECOFF_MAGIC_MIPSEL: |
106 |
format_name = "MIPS1 LE"; |
107 |
encoding = ELFDATA2LSB; |
108 |
break; |
109 |
case ((ECOFF_MAGIC_MIPSEB2 & 0xff) << 8) + |
110 |
((ECOFF_MAGIC_MIPSEB2 >> 8) & 0xff): |
111 |
format_name = "MIPS2 BE"; |
112 |
encoding = ELFDATA2MSB; |
113 |
break; |
114 |
case ECOFF_MAGIC_MIPSEL2: |
115 |
format_name = "MIPS2 LE"; |
116 |
encoding = ELFDATA2LSB; |
117 |
break; |
118 |
case ((ECOFF_MAGIC_MIPSEB3 & 0xff) << 8) + |
119 |
((ECOFF_MAGIC_MIPSEB3 >> 8) & 0xff): |
120 |
format_name = "MIPS3 BE"; |
121 |
encoding = ELFDATA2MSB; |
122 |
break; |
123 |
case ECOFF_MAGIC_MIPSEL3: |
124 |
format_name = "MIPS3 LE"; |
125 |
encoding = ELFDATA2LSB; |
126 |
break; |
127 |
default: |
128 |
fprintf(stderr, "%s: unimplemented ECOFF format, magic = " |
129 |
"0x%04x\n", filename, (int)f_magic); |
130 |
exit(1); |
131 |
} |
132 |
|
133 |
/* Read various header information: */ |
134 |
unencode(f_nscns, &exechdr.f.f_nscns, uint16_t); |
135 |
unencode(f_symptr, &exechdr.f.f_symptr, uint32_t); |
136 |
unencode(f_nsyms, &exechdr.f.f_nsyms, uint32_t); |
137 |
debug("ECOFF, %s, %i sections, %i symbols @ 0x%lx\n", |
138 |
format_name, f_nscns, f_nsyms, (long)f_symptr); |
139 |
|
140 |
unencode(a_magic, &exechdr.a.magic, uint16_t); |
141 |
unencode(a_tsize, &exechdr.a.tsize, uint32_t); |
142 |
unencode(a_dsize, &exechdr.a.dsize, uint32_t); |
143 |
unencode(a_bsize, &exechdr.a.bsize, uint32_t); |
144 |
debug("magic 0x%04x, tsize 0x%x, dsize 0x%x, bsize 0x%x\n", |
145 |
a_magic, (int)a_tsize, (int)a_dsize, (int)a_bsize); |
146 |
|
147 |
unencode(a_tstart, &exechdr.a.text_start, uint32_t); |
148 |
unencode(a_dstart, &exechdr.a.data_start, uint32_t); |
149 |
unencode(a_bstart, &exechdr.a.bss_start, uint32_t); |
150 |
debug("text @ 0x%08x, data @ 0x%08x, bss @ 0x%08x\n", |
151 |
(int)a_tstart, (int)a_dstart, (int)a_bstart); |
152 |
|
153 |
unencode(a_entry, &exechdr.a.entry, uint32_t); |
154 |
unencode(a_gp, &exechdr.a.gp_value, uint32_t); |
155 |
debug("entrypoint 0x%08x, gp = 0x%08x\n", |
156 |
(int)a_entry, (int)a_gp); |
157 |
|
158 |
/* |
159 |
* Special hack for a MACH/pmax kernel, I don't know how applicable |
160 |
* this is for other files: |
161 |
* there are no sections (!), and a_magic = 0x0108 instead of |
162 |
* 0x0107 as it is on most other (E)COFF files I've seen. |
163 |
* |
164 |
* Then load everything after the header to the text start address. |
165 |
*/ |
166 |
if (f_nscns == 0 && a_magic == 0x108) { |
167 |
uint64_t where = a_tstart; |
168 |
total_len = 0; |
169 |
fseek(f, 0x50, SEEK_SET); |
170 |
while (!feof(f)) { |
171 |
chunk_size = 256; |
172 |
len = fread(buf, 1, chunk_size, f); |
173 |
|
174 |
if (len > 0) |
175 |
m->cpus[0]->memory_rw(m->cpus[0], mem, where, |
176 |
&buf[0], len, MEM_WRITE, NO_EXCEPTIONS); |
177 |
where += len; |
178 |
total_len += len; |
179 |
} |
180 |
debug("MACH/pmax hack (!), read 0x%x bytes\n", total_len); |
181 |
} |
182 |
|
183 |
/* Go through all the section headers: */ |
184 |
for (secn=0; secn<f_nscns; secn++) { |
185 |
off_t s_scnptr, s_relptr, s_lnnoptr, oldpos; |
186 |
int s_nreloc, s_nlnno, s_flags; |
187 |
int s_size; |
188 |
unsigned int i; |
189 |
uint64_t s_paddr, s_vaddr; |
190 |
|
191 |
/* Read a section header: */ |
192 |
len = fread(&scnhdr, 1, sizeof(scnhdr), f); |
193 |
if (len != sizeof(scnhdr)) { |
194 |
fprintf(stderr, "%s: incomplete section " |
195 |
"header %i\n", filename, secn); |
196 |
exit(1); |
197 |
} |
198 |
|
199 |
/* Show the section name: */ |
200 |
debug("section "); |
201 |
for (i=0; i<sizeof(scnhdr.s_name); i++) |
202 |
if (scnhdr.s_name[i] >= 32 && scnhdr.s_name[i] < 127) |
203 |
debug("%c", scnhdr.s_name[i]); |
204 |
else |
205 |
break; |
206 |
debug(" ("); |
207 |
|
208 |
unencode(s_paddr, &scnhdr.s_paddr, uint32_t); |
209 |
unencode(s_vaddr, &scnhdr.s_vaddr, uint32_t); |
210 |
unencode(s_size, &scnhdr.s_size, uint32_t); |
211 |
unencode(s_scnptr, &scnhdr.s_scnptr, uint32_t); |
212 |
unencode(s_relptr, &scnhdr.s_relptr, uint32_t); |
213 |
unencode(s_lnnoptr, &scnhdr.s_lnnoptr, uint32_t); |
214 |
unencode(s_nreloc, &scnhdr.s_nreloc, uint16_t); |
215 |
unencode(s_nlnno, &scnhdr.s_nlnno, uint16_t); |
216 |
unencode(s_flags, &scnhdr.s_flags, uint32_t); |
217 |
|
218 |
debug("0x%x @ 0x%08x, offset 0x%lx, flags 0x%x)\n", |
219 |
(int)s_size, (int)s_vaddr, (long)s_scnptr, (int)s_flags); |
220 |
|
221 |
end_addr = s_vaddr + s_size; |
222 |
|
223 |
if (s_relptr != 0) { |
224 |
/* |
225 |
* TODO: Read this url, or similar: |
226 |
* http://www.iecc.com/linker/linker07.html |
227 |
*/ |
228 |
fprintf(stderr, "%s: relocatable code/data in " |
229 |
"section nr %i: not yet implemented\n", |
230 |
filename, secn); |
231 |
exit(1); |
232 |
} |
233 |
|
234 |
/* Loadable? Then load the section: */ |
235 |
if (s_scnptr != 0 && s_size != 0 && |
236 |
s_vaddr != 0 && !(s_flags & 0x02)) { |
237 |
/* Remember the current file offset: */ |
238 |
oldpos = ftello(f); |
239 |
|
240 |
/* Load the section into emulated memory: */ |
241 |
fseek(f, s_scnptr, SEEK_SET); |
242 |
total_len = 0; |
243 |
chunk_size = 1; |
244 |
if ((s_vaddr & 0xf) == 0) chunk_size = 0x10; |
245 |
if ((s_vaddr & 0xff) == 0) chunk_size = 0x100; |
246 |
if ((s_vaddr & 0xfff) == 0) chunk_size = 0x1000; |
247 |
while (total_len < s_size) { |
248 |
len = chunk_size; |
249 |
if (total_len + len > s_size) |
250 |
len = s_size - total_len; |
251 |
len = fread(buf, 1, chunk_size, f); |
252 |
if (len == 0) { |
253 |
debug("!!! total_len = %i, " |
254 |
"chunk_size = %i, len = %i\n", |
255 |
total_len, chunk_size, len); |
256 |
break; |
257 |
} |
258 |
|
259 |
m->cpus[0]->memory_rw(m->cpus[0], mem, s_vaddr, |
260 |
&buf[0], len, MEM_WRITE, NO_EXCEPTIONS); |
261 |
s_vaddr += len; |
262 |
total_len += len; |
263 |
} |
264 |
|
265 |
/* Return to position inside the section headers: */ |
266 |
fseek(f, oldpos, SEEK_SET); |
267 |
} |
268 |
} |
269 |
|
270 |
if (f_symptr != 0 && f_nsyms != 0) { |
271 |
struct ecoff_symhdr symhdr; |
272 |
int sym_magic, iextMax, issExtMax, issMax, crfd; |
273 |
off_t cbRfdOffset, cbExtOffset, cbSsExtOffset, cbSsOffset; |
274 |
char *symbol_data; |
275 |
struct ecoff_extsym *extsyms; |
276 |
int nsymbols, sym_nr; |
277 |
|
278 |
fseek(f, f_symptr, SEEK_SET); |
279 |
|
280 |
len = fread(&symhdr, 1, sizeof(symhdr), f); |
281 |
if (len != sizeof(symhdr)) { |
282 |
fprintf(stderr, "%s: not a complete " |
283 |
"ecoff image: symhdr broken\n", filename); |
284 |
exit(1); |
285 |
} |
286 |
|
287 |
unencode(sym_magic, &symhdr.magic, uint16_t); |
288 |
unencode(crfd, &symhdr.crfd, uint32_t); |
289 |
unencode(cbRfdOffset, &symhdr.cbRfdOffset, uint32_t); |
290 |
unencode(issMax, &symhdr.issMax, uint32_t); |
291 |
unencode(cbSsOffset, &symhdr.cbSsOffset, uint32_t); |
292 |
unencode(issExtMax, &symhdr.issExtMax, uint32_t); |
293 |
unencode(cbSsExtOffset, &symhdr.cbSsExtOffset, uint32_t); |
294 |
unencode(iextMax, &symhdr.iextMax, uint32_t); |
295 |
unencode(cbExtOffset, &symhdr.cbExtOffset, uint32_t); |
296 |
|
297 |
if (sym_magic != MIPS_MAGIC_SYM) { |
298 |
unsigned char *ms_sym_buf; |
299 |
struct ms_sym *sym; |
300 |
int n_real_symbols = 0; |
301 |
|
302 |
debug("bad symbol magic, assuming Microsoft format: "); |
303 |
|
304 |
/* |
305 |
* See http://www.lisoleg.net/lisoleg/elfandlib/ |
306 |
* Microsoft%20Portable%20Executable%20COFF%20For |
307 |
* mat%20Specification.txt |
308 |
* for more details. |
309 |
*/ |
310 |
CHECK_ALLOCATION(ms_sym_buf = |
311 |
malloc(sizeof(struct ms_sym) * f_nsyms)); |
312 |
fseek(f, f_symptr, SEEK_SET); |
313 |
len = fread(ms_sym_buf, 1, |
314 |
sizeof(struct ms_sym) * f_nsyms, f); |
315 |
sym = (struct ms_sym *) ms_sym_buf; |
316 |
for (sym_nr=0; sym_nr<f_nsyms; sym_nr++) { |
317 |
char name[300]; |
318 |
uint32_t v, t, altname; |
319 |
/* debug("sym %5i: '", sym_nr); |
320 |
for (i=0; i<8 && sym->name[i]; i++) |
321 |
debug("%c", sym->name[i]); */ |
322 |
v = sym->value[0] + (sym->value[1] << 8) |
323 |
+ (sym->value[2] << 16) |
324 |
+ ((uint64_t)sym->value[3] << 24); |
325 |
altname = sym->name[4] + (sym->name[5] << 8) |
326 |
+ (sym->name[6] << 16) |
327 |
+ ((uint64_t)sym->name[3] << 24); |
328 |
t = (sym->type[1] << 8) + sym->type[0]; |
329 |
/* TODO: big endian COFF? */ |
330 |
/* debug("' value=0x%x type=0x%04x", v, t); */ |
331 |
|
332 |
if (t == 0x20 && sym->name[0]) { |
333 |
memcpy(name, sym->name, 8); |
334 |
name[8] = '\0'; |
335 |
add_symbol_name(&m->symbol_context, |
336 |
v, 0, name, 0, -1); |
337 |
n_real_symbols ++; |
338 |
} else if (t == 0x20 && !sym->name[0]) { |
339 |
off_t ofs; |
340 |
ofs = f_symptr + altname + |
341 |
sizeof(struct ms_sym) * f_nsyms; |
342 |
fseek(f, ofs, SEEK_SET); |
343 |
fread(name, 1, sizeof(name), f); |
344 |
name[sizeof(name)-1] = '\0'; |
345 |
/* debug(" [altname=0x%x '%s']", |
346 |
altname, name); */ |
347 |
add_symbol_name(&m->symbol_context, |
348 |
v, 0, name, 0, -1); |
349 |
n_real_symbols ++; |
350 |
} |
351 |
|
352 |
|
353 |
if (sym->n_aux_syms) { |
354 |
int n = sym->n_aux_syms; |
355 |
/* debug(" aux='"); */ |
356 |
while (n-- > 0) { |
357 |
sym ++; sym_nr ++; |
358 |
/* for (i=0; i<8 && |
359 |
sym->name[i]; i++) |
360 |
debug("%c", |
361 |
sym->name[i]); */ |
362 |
} |
363 |
/* debug("'"); */ |
364 |
} |
365 |
/* debug("\n"); */ |
366 |
sym ++; |
367 |
} |
368 |
|
369 |
debug("%i symbols\n", n_real_symbols); |
370 |
free(ms_sym_buf); |
371 |
|
372 |
goto skip_normal_coff_symbols; |
373 |
} |
374 |
|
375 |
debug("symbol header: magic = 0x%x\n", sym_magic); |
376 |
|
377 |
debug("%i symbols @ 0x%08x (strings @ 0x%08x)\n", |
378 |
iextMax, cbExtOffset, cbSsExtOffset); |
379 |
|
380 |
CHECK_ALLOCATION(symbol_data = malloc(issExtMax + 2)); |
381 |
memset(symbol_data, 0, issExtMax + 2); |
382 |
fseek(f, cbSsExtOffset, SEEK_SET); |
383 |
fread(symbol_data, 1, issExtMax + 1, f); |
384 |
|
385 |
nsymbols = iextMax; |
386 |
|
387 |
CHECK_ALLOCATION(extsyms = |
388 |
malloc(iextMax * sizeof(struct ecoff_extsym))); |
389 |
memset(extsyms, 0, iextMax * sizeof(struct ecoff_extsym)); |
390 |
fseek(f, cbExtOffset, SEEK_SET); |
391 |
fread(extsyms, 1, iextMax * sizeof(struct ecoff_extsym), f); |
392 |
|
393 |
/* Unencode the strindex and value first: */ |
394 |
for (sym_nr=0; sym_nr<nsymbols; sym_nr++) { |
395 |
uint64_t value, strindex; |
396 |
|
397 |
unencode(strindex, &extsyms[sym_nr].es_strindex, |
398 |
uint32_t); |
399 |
unencode(value, &extsyms[sym_nr].es_value, uint32_t); |
400 |
|
401 |
extsyms[sym_nr].es_strindex = strindex; |
402 |
extsyms[sym_nr].es_value = value; |
403 |
} |
404 |
|
405 |
for (sym_nr=0; sym_nr<nsymbols; sym_nr++) { |
406 |
/* debug("symbol%6i: 0x%08x = %s\n", |
407 |
sym_nr, (int)extsyms[sym_nr].es_value, |
408 |
symbol_data + extsyms[sym_nr].es_strindex); */ |
409 |
|
410 |
add_symbol_name(&m->symbol_context, |
411 |
extsyms[sym_nr].es_value, 0, |
412 |
symbol_data + extsyms[sym_nr].es_strindex, 0, -1); |
413 |
} |
414 |
|
415 |
free(extsyms); |
416 |
free(symbol_data); |
417 |
|
418 |
skip_normal_coff_symbols: |
419 |
; |
420 |
} |
421 |
|
422 |
fclose(f); |
423 |
|
424 |
*entrypointp = a_entry; |
425 |
*gpp = a_gp; |
426 |
m->file_loaded_end_addr = end_addr; |
427 |
|
428 |
if (program_byte_order != -1) |
429 |
encoding = program_byte_order; |
430 |
|
431 |
if (encoding == ELFDATA2LSB) |
432 |
*byte_orderp = EMUL_LITTLE_ENDIAN; |
433 |
else |
434 |
*byte_orderp = EMUL_BIG_ENDIAN; |
435 |
|
436 |
n_executables_loaded ++; |
437 |
} |
438 |
|