elf2aout.c revision 1.3 1 /* $NetBSD: elf2aout.c,v 1.3 1996/10/16 00:27:05 jonathan Exp $ */
2
3 /*
4 * Copyright (c) 1995
5 * Ted Lemon (hereinafter referred to as the author)
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 /* elf2aout.c
32
33 This program converts an elf executable to a NetBSD a.out executable.
34 The minimal symbol table is copied, but the debugging symbols and
35 other informational sections are not. */
36
37 #include <sys/types.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <sys/exec_elf.h>
41 #include <sys/exec_aout.h>
42 #include <stdio.h>
43 #include <a.out.h>
44 #include <sys/errno.h>
45 #include <string.h>
46 #include <limits.h>
47
48
49 /* Elf Program segment permissions, in program header flags field */
50
51 #define PF_X (1 << 0) /* Segment is executable */
52 #define PF_W (1 << 1) /* Segment is writable */
53 #define PF_R (1 << 2) /* Segment is readable */
54 #define PF_MASKPROC 0xF0000000 /* Processor-specific reserved bits */
55
56 struct sect {
57 unsigned long vaddr;
58 unsigned long len;
59 };
60 int phcmp ();
61 char *saveRead (int file, off_t offset, off_t len, char *name);
62 int copy (int, int, off_t, off_t);
63 int translate_syms (int, int, off_t, off_t, off_t, off_t);
64 extern int errno;
65 int *symTypeTable;
66
67 main (int argc, char **argv, char **envp)
68 {
69 Elf32_Ehdr ex;
70 Elf32_Phdr *ph;
71 Elf32_Shdr *sh;
72 struct sym *symtab;
73 char *shstrtab;
74 int strtabix, symtabix;
75 int i;
76 struct sect text, data, bss;
77 struct exec aex;
78 int infile, outfile;
79 unsigned long cur_vma = ULONG_MAX;
80 int symflag = 0;
81
82 text.len = data.len = bss.len = 0;
83 text.vaddr = data.vaddr = bss.vaddr = 0;
84
85 /* Check args... */
86 if (argc < 3 || argc > 4)
87 {
88 usage:
89 fprintf (stderr,
90 "usage: elf2aout <elf executable> <a.out executable> [-s]\n");
91 exit (1);
92 }
93 if (argc == 4)
94 {
95 if (strcmp (argv [3], "-s"))
96 goto usage;
97 symflag = 1;
98 }
99
100 /* Try the input file... */
101 if ((infile = open (argv [1], O_RDONLY)) < 0)
102 {
103 fprintf (stderr, "Can't open %s for read: %s\n",
104 argv [1], strerror (errno));
105 exit (1);
106 }
107
108 /* Read the header, which is at the beginning of the file... */
109 i = read (infile, &ex, sizeof ex);
110 if (i != sizeof ex)
111 {
112 fprintf (stderr, "ex: %s: %s.\n",
113 argv [1], i ? strerror (errno) : "End of file reached");
114 exit (1);
115 }
116
117 /* Read the program headers... */
118 ph = (Elf32_Phdr *)saveRead (infile, ex.e_phoff,
119 ex.e_phnum * sizeof (Elf32_Phdr), "ph");
120 /* Read the section headers... */
121 sh = (Elf32_Shdr *)saveRead (infile, ex.e_shoff,
122 ex.e_shnum * sizeof (Elf32_Shdr), "sh");
123 /* Read in the section string table. */
124 shstrtab = saveRead (infile, sh [ex.e_shstrndx].sh_offset,
125 sh [ex.e_shstrndx].sh_size, "shstrtab");
126
127 /* Find space for a table matching ELF section indices to a.out symbol
128 types. */
129 symTypeTable = (int *)malloc (ex.e_shnum * sizeof (int));
130 if (!symTypeTable)
131 {
132 fprintf (stderr, "symTypeTable: can't allocate.\n");
133 exit (1);
134 }
135 memset (symTypeTable, 0, ex.e_shnum * sizeof (int));
136
137 /* Look for the symbol table and string table...
138 Also map section indices to symbol types for a.out */
139 for (i = 0; i < ex.e_shnum; i++)
140 {
141 char *name = shstrtab + sh [i].sh_name;
142 if (!strcmp (name, ".symtab"))
143 symtabix = i;
144 else if (!strcmp (name, ".strtab"))
145 strtabix = i;
146 else if (!strcmp (name, ".text") || !strcmp (name, ".rodata"))
147 symTypeTable [i] = N_TEXT;
148 else if (!strcmp (name, ".data") || !strcmp (name, ".sdata") ||
149 !strcmp (name, ".lit4") || !strcmp (name, ".lit8"))
150 symTypeTable [i] = N_DATA;
151 else if (!strcmp (name, ".bss") || !strcmp (name, ".sbss"))
152 symTypeTable [i] = N_BSS;
153 }
154
155 /* Figure out if we can cram the program header into an a.out header...
156 Basically, we can't handle anything but loadable segments, but we
157 can ignore some kinds of segments. We can't handle holes in the
158 address space, and we handle start addresses other than 0x1000 by
159 hoping that the loader will know where to load - a.out doesn't have
160 an explicit load address. Segments may be out of order, so we
161 sort them first. */
162 qsort (ph, ex.e_phnum, sizeof (Elf32_Phdr), phcmp);
163 for (i = 0; i < ex.e_phnum; i++)
164 {
165 /* Section types we can ignore... */
166 if (ph [i].p_type == Elf_pt_null || ph [i].p_type == Elf_pt_note ||
167 ph [i].p_type == Elf_pt_phdr || ph [i].p_type == Elf_pt_mips_reginfo)
168 continue;
169 /* Section types we can't handle... */
170 else if (ph [i].p_type != Elf_pt_load)
171 {
172 fprintf (stderr, "Program header %d type %d can't be converted.\n");
173 exit (1);
174 }
175 /* Writable (data) segment? */
176 if (ph [i].p_flags & PF_W)
177 {
178 struct sect ndata, nbss;
179
180 ndata.vaddr = ph [i].p_vaddr;
181 ndata.len = ph [i].p_filesz;
182 nbss.vaddr = ph [i].p_vaddr + ph [i].p_filesz;
183 nbss.len = ph [i].p_memsz - ph [i].p_filesz;
184
185 combine (&data, &ndata, 0);
186 combine (&bss, &nbss, 1);
187 }
188 else
189 {
190 struct sect ntxt;
191
192 ntxt.vaddr = ph [i].p_vaddr;
193 ntxt.len = ph [i].p_filesz;
194
195 combine (&text, &ntxt);
196 }
197 /* Remember the lowest segment start address. */
198 if (ph [i].p_vaddr < cur_vma)
199 cur_vma = ph [i].p_vaddr;
200 }
201
202 /* Sections must be in order to be converted... */
203 if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
204 text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr)
205 {
206 fprintf (stderr, "Sections ordering prevents a.out conversion.\n");
207 exit (1);
208 }
209
210 /* If there's a data section but no text section, then the loader
211 combined everything into one section. That needs to be the
212 text section, so just make the data section zero length following
213 text. */
214 if (data.len && !text.len)
215 {
216 text = data;
217 data.vaddr = text.vaddr + text.len;
218 data.len = 0;
219 }
220
221 /* If there is a gap between text and data, we'll fill it when we copy
222 the data, so update the length of the text segment as represented in
223 a.out to reflect that, since a.out doesn't allow gaps in the program
224 address space. */
225 if (text.vaddr + text.len < data.vaddr)
226 text.len = data.vaddr - text.vaddr;
227
228 /* We now have enough information to cons up an a.out header... */
229 aex.a_midmag = htonl ((symflag << 26) | (MID_PMAX << 16) | OMAGIC);
230 aex.a_text = text.len;
231 aex.a_data = data.len;
232 aex.a_bss = bss.len;
233 aex.a_entry = ex.e_entry;
234 aex.a_syms = (sizeof (struct nlist) *
235 (symtabix != -1
236 ? sh [symtabix].sh_size / sizeof (Elf32_Sym) : 0));
237 aex.a_trsize = 0;
238 aex.a_drsize = 0;
239
240 /* Make the output file... */
241 if ((outfile = open (argv [2], O_WRONLY | O_CREAT, 0777)) < 0)
242 {
243 fprintf (stderr, "Unable to create %s: %s\n", argv [2], strerror (errno));
244 exit (1);
245 }
246 /* Write the header... */
247 i = write (outfile, &aex, sizeof aex);
248 if (i != sizeof aex)
249 {
250 perror ("aex: write");
251 exit (1);
252 }
253
254 /* Copy the loadable sections. Zero-fill any gaps less than 64k;
255 complain about any zero-filling, and die if we're asked to zero-fill
256 more than 64k. */
257 for (i = 0; i < ex.e_phnum; i++)
258 {
259 /* Unprocessable sections were handled above, so just verify that
260 the section can be loaded before copying. */
261 if (ph [i].p_type == Elf_pt_load && ph [i].p_filesz)
262 {
263 if (cur_vma != ph [i].p_vaddr)
264 {
265 unsigned long gap = ph [i].p_vaddr - cur_vma;
266 char obuf [1024];
267 if (gap > 65536)
268 {
269 fprintf (stderr, "Intersegment gap (%d bytes) too large.\n",
270 gap);
271 exit (1);
272 }
273 fprintf (stderr, "Warning: %d byte intersegment gap.\n", gap);
274 memset (obuf, 0, sizeof obuf);
275 while (gap)
276 {
277 int count = write (outfile, obuf, (gap > sizeof obuf
278 ? sizeof obuf : gap));
279 if (count < 0)
280 {
281 fprintf (stderr, "Error writing gap: %s\n",
282 strerror (errno));
283 exit (1);
284 }
285 gap -= count;
286 }
287 }
288 copy (outfile, infile, ph [i].p_offset, ph [i].p_filesz);
289 cur_vma = ph [i].p_vaddr + ph [i].p_filesz;
290 }
291 }
292
293 /* Copy and translate the symbol table... */
294 translate_syms (outfile, infile,
295 sh [symtabix].sh_offset, sh [symtabix].sh_size,
296 sh [strtabix].sh_offset, sh [strtabix].sh_size);
297
298 /* Looks like we won... */
299 exit (0);
300 }
301
302 /* translate_syms (out, in, offset, size)
303
304 Read the ELF symbol table from in at offset; translate it into a.out
305 nlist format and write it to out. */
306
307 translate_syms (out, in, symoff, symsize, stroff, strsize)
308 int out, in;
309 off_t symoff, symsize;
310 off_t stroff, strsize;
311 {
312 # define SYMS_PER_PASS 64
313 Elf32_Sym inbuf [64];
314 struct nlist outbuf [64];
315 int i, remaining, cur;
316 char *oldstrings;
317 char *newstrings, *nsp;
318 int newstringsize;
319
320 /* Zero the unused fields in the output buffer.. */
321 memset (outbuf, 0, sizeof outbuf);
322
323 /* Find number of symbols to process... */
324 remaining = symsize / sizeof (Elf32_Sym);
325
326 /* Suck in the old string table... */
327 oldstrings = saveRead (in, stroff, strsize, "string table");
328
329 /* Allocate space for the new one. XXX We make the wild assumption that
330 no two symbol table entries will point at the same place in the
331 string table - if that assumption is bad, this could easily blow up. */
332 newstringsize = strsize + remaining;
333 newstrings = (char *)malloc (newstringsize);
334 if (!newstrings)
335 {
336 fprintf (stderr, "No memory for new string table!\n");
337 exit (1);
338 }
339 /* Initialize the table pointer... */
340 nsp = newstrings;
341
342 /* Go the the start of the ELF symbol table... */
343 if (lseek (in, symoff, SEEK_SET) < 0)
344 {
345 perror ("translate_syms: lseek");
346 exit (1);
347 }
348
349 /* Translate and copy symbols... */
350 while (remaining)
351 {
352 cur = remaining;
353 if (cur > SYMS_PER_PASS)
354 cur = SYMS_PER_PASS;
355 remaining -= cur;
356 if ((i = read (in, inbuf, cur * sizeof (Elf32_Sym)))
357 != cur * sizeof (Elf32_Sym))
358 {
359 if (i < 0)
360 perror ("translate_syms");
361 else
362 fprintf (stderr, "translate_syms: premature end of file.\n");
363 exit (1);
364 }
365
366 /* Do the translation... */
367 for (i = 0; i < cur; i++)
368 {
369 int binding, type;
370
371 /* Copy the symbol into the new table, but prepend an underscore. */
372 *nsp = '_';
373 strcpy (nsp + 1, oldstrings + inbuf [i].st_name);
374 outbuf [i].n_un.n_strx = nsp - newstrings + 4;
375 nsp += strlen (nsp) + 1;
376
377 type = ELF_SYM_TYPE(inbuf[i].st_info);
378 binding = ELF_SYM_BIND(inbuf[i].st_info);
379
380 /* Convert ELF symbol type/section/etc info into a.out type info. */
381 if (type == Elf_estt_file)
382 outbuf [i].n_type = N_FN;
383 else if (inbuf [i].st_shndx == Elf_eshn_undefined)
384 outbuf [i].n_type = N_UNDF;
385 else if (inbuf [i].st_shndx == Elf_eshn_absolute)
386 outbuf [i].n_type = N_ABS;
387 else if (inbuf [i].st_shndx == Elf_eshn_common ||
388 inbuf [i].st_shndx == Elf_eshn_mips_acommon)
389 outbuf [i].n_type = N_COMM;
390 else
391 outbuf [i].n_type = symTypeTable [inbuf [i].st_shndx];
392 if (binding == Elf_estb_global)
393 outbuf [i].n_type |= N_EXT;
394 /* Symbol values in executables should be compatible. */
395 outbuf [i].n_value = inbuf [i].st_value;
396 }
397 /* Write out the symbols... */
398 if ((i = write (out, outbuf, cur * sizeof (struct nlist)))
399 != cur * sizeof (struct nlist))
400 {
401 fprintf (stderr, "translate_syms: write: %s\n", strerror (errno));
402 exit (1);
403 }
404 }
405 /* Write out the string table length... */
406 if (write (out, &newstringsize, sizeof newstringsize)
407 != sizeof newstringsize)
408 {
409 fprintf (stderr,
410 "translate_syms: newstringsize: %s\n", strerror (errno));
411 exit (1);
412 }
413 /* Write out the string table... */
414 if (write (out, newstrings, newstringsize) != newstringsize)
415 {
416 fprintf (stderr, "translate_syms: newstrings: %s\n", strerror (errno));
417 exit (1);
418 }
419 }
420
421 copy (out, in, offset, size)
422 int out, in;
423 off_t offset, size;
424 {
425 char ibuf [4096];
426 int remaining, cur, count;
427
428 /* Go the the start of the ELF symbol table... */
429 if (lseek (in, offset, SEEK_SET) < 0)
430 {
431 perror ("copy: lseek");
432 exit (1);
433 }
434
435 remaining = size;
436 while (remaining)
437 {
438 cur = remaining;
439 if (cur > sizeof ibuf)
440 cur = sizeof ibuf;
441 remaining -= cur;
442 if ((count = read (in, ibuf, cur)) != cur)
443 {
444 fprintf (stderr, "copy: read: %s\n",
445 count ? strerror (errno) : "premature end of file");
446 exit (1);
447 }
448 if ((count = write (out, ibuf, cur)) != cur)
449 {
450 perror ("copy: write");
451 exit (1);
452 }
453 }
454 }
455
456 /* Combine two segments, which must be contiguous. If pad is true, it's
457 okay for there to be padding between. */
458 combine (base, new, pad)
459 struct sect *base, *new;
460 int pad;
461 {
462 if (!base -> len)
463 *base = *new;
464 else if (new -> len)
465 {
466 if (base -> vaddr + base -> len != new -> vaddr)
467 {
468 if (pad)
469 base -> len = new -> vaddr - base -> vaddr;
470 else
471 {
472 fprintf (stderr,
473 "Non-contiguous data can't be converted.\n");
474 exit (1);
475 }
476 }
477 base -> len += new -> len;
478 }
479 }
480
481 int
482 phcmp (h1, h2)
483 Elf32_Phdr *h1, *h2;
484 {
485 if (h1 -> p_vaddr > h2 -> p_vaddr)
486 return 1;
487 else if (h1 -> p_vaddr < h2 -> p_vaddr)
488 return -1;
489 else
490 return 0;
491 }
492
493 char *saveRead (int file, off_t offset, off_t len, char *name)
494 {
495 char *tmp;
496 int count;
497 off_t off;
498 if ((off = lseek (file, offset, SEEK_SET)) < 0)
499 {
500 fprintf (stderr, "%s: fseek: %s\n", name, strerror (errno));
501 exit (1);
502 }
503 if (!(tmp = (char *)malloc (len)))
504 {
505 fprintf (stderr, "%s: Can't allocate %d bytes.\n", name, len);
506 exit (1);
507 }
508 count = read (file, tmp, len);
509 if (count != len)
510 {
511 fprintf (stderr, "%s: read: %s.\n",
512 name, count ? strerror (errno) : "End of file reached");
513 exit (1);
514 }
515 return tmp;
516 }
517