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