elf2ecoff.c revision 1.6 1 /* $NetBSD: elf2ecoff.c,v 1.6 1997/07/06 23:57:39 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 /* elf2ecoff.c
32
33 This program converts an elf executable to an ECOFF executable.
34 No symbol table is retained. This is useful primarily in building
35 net-bootable kernels for machines (e.g., DECstation and Alpha) which
36 only support the ECOFF object file format. */
37
38 #include <sys/types.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <sys/exec.h>
42 #include <sys/exec_elf.h>
43 #include <sys/exec_aout.h>
44 #include <stdio.h>
45 #include <sys/exec_ecoff.h>
46 #include <sys/errno.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <limits.h>
50
51
52 /* Elf Program segment permissions, in program header flags field */
53
54 #define PF_X (1 << 0) /* Segment is executable */
55 #define PF_W (1 << 1) /* Segment is writable */
56 #define PF_R (1 << 2) /* Segment is readable */
57 #define PF_MASKPROC 0xF0000000 /* Processor-specific reserved bits */
58
59
60 #define ISLAST(p) (p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
61
62 struct sect {
63 unsigned long vaddr;
64 unsigned long len;
65 };
66
67 int debug = 1;
68
69 int phcmp (Elf32_Phdr *h1, Elf32_Phdr *h2);
70
71 char *saveRead (int file, off_t offset, off_t len, char *name);
72 void copy (int, int, off_t, off_t);
73 void combine (struct sect *base, struct sect *new, int paddable);
74 void translate_syms (int, int, off_t, off_t, off_t, off_t);
75 int make_ecoff_section_hdrs(struct ecoff_exechdr *ep,
76 struct ecoff_scnhdr *esecs);
77
78 void write_ecoff_symhdr(int outfile, struct ecoff_exechdr *ep,
79 struct ecoff_symhdr *symhdrp,
80 long nesyms, long extstroff);
81
82 extern int errno;
83 int *symTypeTable;
84
85 int
86 main (int argc, char **argv, char **envp)
87 {
88 Elf32_Ehdr ex;
89 Elf32_Phdr *ph;
90 Elf32_Shdr *sh;
91 char *shstrtab;
92 int strtabix, symtabix;
93 int i, pad;
94 struct sect text, data, bss; /* a.out-compatible sections */
95 struct sect rdata, sdata, sbss; /* ECOFF-only sections */
96
97 struct ecoff_exechdr ep;
98 struct ecoff_scnhdr esecs [6];
99
100 int infile, outfile;
101 unsigned long cur_vma = ULONG_MAX;
102 int symflag = 0;
103 int nsecs = 0;
104
105 text.len = data.len = bss.len = 0;
106 text.vaddr = data.vaddr = bss.vaddr = 0;
107
108 rdata.len = sdata.len = sbss.len = 0;
109 rdata.vaddr = sdata.vaddr = sbss.vaddr = 0;
110
111 /* Check args... */
112 if (argc < 3 || argc > 4)
113 {
114 usage:
115 fprintf (stderr,
116 "usage: elf2aout <elf executable> <a.out executable> [-s]\n");
117 exit (1);
118 }
119 if (argc == 4)
120 {
121 if (strcmp (argv [3], "-s"))
122 goto usage;
123 symflag = 1;
124 }
125
126 /* Try the input file... */
127 if ((infile = open (argv [1], O_RDONLY)) < 0)
128 {
129 fprintf (stderr, "Can't open %s for read: %s\n",
130 argv [1], strerror (errno));
131 exit (1);
132 }
133
134 /* Read the header, which is at the beginning of the file... */
135 i = read (infile, &ex, sizeof ex);
136 if (i != sizeof ex)
137 {
138 fprintf (stderr, "ex: %s: %s.\n",
139 argv [1], i ? strerror (errno) : "End of file reached");
140 exit (1);
141 }
142
143 /* Read the program headers... */
144 ph = (Elf32_Phdr *)saveRead (infile, ex.e_phoff,
145 ex.e_phnum * sizeof (Elf32_Phdr), "ph");
146 /* Read the section headers... */
147 sh = (Elf32_Shdr *)saveRead (infile, ex.e_shoff,
148 ex.e_shnum * sizeof (Elf32_Shdr), "sh");
149 /* Read in the section string table. */
150 shstrtab = saveRead (infile, sh [ex.e_shstrndx].sh_offset,
151 sh [ex.e_shstrndx].sh_size, "shstrtab");
152 /* Read in the section string table. */
153 shstrtab = saveRead (infile, sh [ex.e_shstrndx].sh_offset,
154 sh [ex.e_shstrndx].sh_size, "shstrtab");
155
156
157 /* Look for the symbol table and string table...
158 Also map section indices to symbol types for a.out */
159 symtabix = 0;
160 strtabix = 0;
161 for (i = 0; i < ex.e_shnum; i++)
162 {
163 char *name = shstrtab + sh [i].sh_name;
164 if (!strcmp (name, ".symtab"))
165 symtabix = i;
166 else if (!strcmp (name, ".strtab"))
167 strtabix = i;
168
169 }
170
171 /* Figure out if we can cram the program header into an ECOFF
172 header... Basically, we can't handle anything but loadable
173 segments, but we can ignore some kinds of segments. We can't
174 handle holes in the address space. Segments may be out of order,
175 so we sort them first. */
176
177 qsort (ph, ex.e_phnum, sizeof (Elf32_Phdr),
178 ( int (*)(const void *, const void *))phcmp);
179
180 for (i = 0; i < ex.e_phnum; i++)
181 {
182 /* Section types we can ignore... */
183 if (ph [i].p_type == Elf_pt_null || ph [i].p_type == Elf_pt_note ||
184 ph [i].p_type == Elf_pt_phdr ||
185 ph [i].p_type == Elf_pt_mips_reginfo) {
186
187 if (debug) {
188 fprintf(stderr," skipping PH %d type %d flags 0x%x\n",
189 i, ph[i].p_type, ph[i].p_flags);
190 }
191 continue;
192 }
193
194 /* Section types we can't handle... */
195 else if (ph [i].p_type != Elf_pt_load)
196 {
197 fprintf (stderr, "Program header %d type %d can't be converted.\n",
198 i, ph[i].p_type);
199 exit (1);
200 }
201 /* Writable (data) segment? */
202 if (ph [i].p_flags & PF_W)
203 {
204 struct sect ndata, nbss;
205
206 ndata.vaddr = ph [i].p_vaddr;
207 ndata.len = ph [i].p_filesz;
208 nbss.vaddr = ph [i].p_vaddr + ph [i].p_filesz;
209 nbss.len = ph [i].p_memsz - ph [i].p_filesz;
210
211 if (debug) {
212 printf(" combinining PH %d type %d flags 0x%x with data, ndata = %ld, nbss =%ld\n", i, ph[i].p_type, ph[i].p_flags, ndata.len, nbss.len);
213 }
214
215 combine (&data, &ndata, 0);
216 combine (&bss, &nbss, 1);
217 }
218 else
219 {
220 struct sect ntxt;
221
222 ntxt.vaddr = ph [i].p_vaddr;
223 ntxt.len = ph [i].p_filesz;
224 if (debug) {
225
226 printf(" combinining PH %d type %d flags 0x%x with text, len = %ld\n",
227 i, ph[i].p_type, ph[i].p_flags, ntxt.len);
228 }
229
230
231 combine (&text, &ntxt, 0);
232 }
233 /* Remember the lowest segment start address. */
234 if (ph [i].p_vaddr < cur_vma)
235 cur_vma = ph [i].p_vaddr;
236 }
237
238 /* Sections must be in order to be converted... */
239 if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
240 text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr)
241 {
242 fprintf (stderr, "Sections ordering prevents a.out conversion.\n");
243 exit (1);
244 }
245
246 /* If there's a data section but no text section, then the loader
247 combined everything into one section. That needs to be the
248 text section, so just make the data section zero length following
249 text. */
250 if (data.len && !text.len)
251 {
252 text = data;
253 data.vaddr = text.vaddr + text.len;
254 data.len = 0;
255 }
256
257 /* If there is a gap between text and data, we'll fill it when we copy
258 the data, so update the length of the text segment as represented in
259 a.out to reflect that, since a.out doesn't allow gaps in the program
260 address space. */
261 if (text.vaddr + text.len < data.vaddr)
262 text.len = data.vaddr - text.vaddr;
263
264 /* We now have enough information to cons up an a.out header... */
265 ep.a.magic = ECOFF_OMAGIC;
266 ep.a.vstamp = 2 * 256 + 10; /* compatible with version 2.10 */
267 ep.a.tsize = text.len;
268 ep.a.dsize = data.len;
269 ep.a.bsize = bss.len;
270 ep.a.entry = ex.e_entry;
271 ep.a.text_start = text.vaddr;
272 ep.a.data_start = data.vaddr;
273 ep.a.bss_start = bss.vaddr;
274 ep.a.gprmask = 0xf3fffffe;
275 bzero (&ep.a.cprmask, sizeof ep.a.cprmask);
276 ep.a.gp_value = 0; /* unused. */
277
278 ep.f.f_magic = ECOFF_MAGIC_MIPSEL;
279 ep.f.f_nscns = 6;
280 ep.f.f_timdat = 0; /* bogus */
281 ep.f.f_symptr = 0;
282 ep.f.f_nsyms = sizeof(struct ecoff_symhdr);
283 ep.f.f_opthdr = sizeof ep.a;
284 ep.f.f_flags = 0x100f; /* Stripped, not sharable. */
285
286 bzero(esecs, sizeof(esecs));
287
288 /* Make ECOFF section headers, with empty stubs for .rdata/.sdata/.sbss. */
289 make_ecoff_section_hdrs(&ep, esecs);
290
291 nsecs = ep.f.f_nscns;
292
293 /* Make the output file... */
294 if ((outfile = open (argv [2], O_WRONLY | O_CREAT, 0777)) < 0)
295 {
296 fprintf (stderr, "Unable to create %s: %s\n", argv [2], strerror (errno));
297 exit (1);
298 }
299
300 /* Write the headers... */
301 i = write (outfile, &ep.f, sizeof ep.f);
302 if (i != sizeof ep.f)
303 {
304 perror ("ep.f: write");
305 exit (1);
306
307 for (i = 0; i < nsecs; i++)
308 {
309 printf ("Section %d: %s phys %lx size %lx file offset %lx\n",
310 i, esecs [i].s_name, esecs [i].s_paddr,
311 esecs [i].s_size, esecs [i].s_scnptr);
312 }
313 }
314 fprintf (stderr, "wrote %d byte file header.\n", i);
315
316 i = write (outfile, &ep.a, sizeof ep.a);
317 if (i != sizeof ep.a)
318 {
319 perror ("ep.a: write");
320 exit (1);
321 }
322 fprintf (stderr, "wrote %d byte a.out header.\n", i);
323
324 i = write (outfile, &esecs, sizeof (esecs[0]) * nsecs);
325 if (i != sizeof (esecs[0]) * nsecs)
326 {
327 perror ("esecs: write");
328 exit (1);
329 }
330 fprintf (stderr, "wrote %d bytes of section headers.\n", i);
331
332
333 pad = ((sizeof ep.f + sizeof ep.a + sizeof esecs) & 15);
334 if (pad)
335 {
336 pad = 16 - pad;
337 i = write (outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad);
338 if (i < 0)
339 {
340 perror ("ipad: write");
341 exit (1);
342 }
343 fprintf (stderr, "wrote %d byte pad.\n", i);
344 }
345
346 /* Copy the loadable sections. Zero-fill any gaps less than 64k;
347 complain about any zero-filling, and die if we're asked to zero-fill
348 more than 64k. */
349 for (i = 0; i < ex.e_phnum; i++)
350 {
351 /* Unprocessable sections were handled above, so just verify that
352 the section can be loaded before copying. */
353 if (ph [i].p_type == Elf_pt_load && ph [i].p_filesz)
354 {
355 if (cur_vma != ph [i].p_vaddr)
356 {
357 unsigned long gap = ph [i].p_vaddr - cur_vma;
358 char obuf [1024];
359 if (gap > 65536)
360 {
361 fprintf (stderr, "Intersegment gap (%ld bytes) too large.\n",
362 gap);
363 exit (1);
364 }
365 fprintf (stderr, "Warning: %ld byte intersegment gap.\n", gap);
366 memset (obuf, 0, sizeof obuf);
367 while (gap)
368 {
369 int count = write (outfile, obuf, (gap > sizeof obuf
370 ? sizeof obuf : gap));
371 if (count < 0)
372 {
373 fprintf (stderr, "Error writing gap: %s\n",
374 strerror (errno));
375 exit (1);
376 }
377 gap -= count;
378 }
379 }
380 fprintf (stderr, "writing %d bytes...\n", ph [i].p_filesz);
381 copy (outfile, infile, ph [i].p_offset, ph [i].p_filesz);
382 cur_vma = ph [i].p_vaddr + ph [i].p_filesz;
383 }
384 }
385
386
387 /*
388 * Write a page of padding for boot PROMS that read entire pages.
389 * Without this, they may attempt to read past the end of the
390 * data section, incur an error, and refuse to boot.
391 */
392 {
393 char obuf [4096];
394 memset (obuf, 0, sizeof obuf);
395 if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) {
396 fprintf(stderr, "Error writing PROM padding: %s\n",
397 strerror(errno));
398 exit(1);
399 }
400 }
401
402 /* Looks like we won... */
403 exit (0);
404 }
405
406 void
407 copy (out, in, offset, size)
408 int out, in;
409 off_t offset, size;
410 {
411 char ibuf [4096];
412 int remaining, cur, count;
413
414 /* Go the the start of the ELF symbol table... */
415 if (lseek (in, offset, SEEK_SET) < 0)
416 {
417 perror ("copy: lseek");
418 exit (1);
419 }
420
421 remaining = size;
422 while (remaining)
423 {
424 cur = remaining;
425 if (cur > sizeof ibuf)
426 cur = sizeof ibuf;
427 remaining -= cur;
428 if ((count = read (in, ibuf, cur)) != cur)
429 {
430 fprintf (stderr, "copy: read: %s\n",
431 count ? strerror (errno) : "premature end of file");
432 exit (1);
433 }
434 if ((count = write (out, ibuf, cur)) != cur)
435 {
436 perror ("copy: write");
437 exit (1);
438 }
439 }
440 }
441
442 /* Combine two segments, which must be contiguous. If pad is true, it's
443 okay for there to be padding between. */
444 void
445 combine (base, new, pad)
446 struct sect *base, *new;
447 int pad;
448 {
449 if (!base -> len)
450 *base = *new;
451 else if (new -> len)
452 {
453 if (base -> vaddr + base -> len != new -> vaddr)
454 {
455 if (pad)
456 base -> len = new -> vaddr - base -> vaddr;
457 else
458 {
459 fprintf (stderr,
460 "Non-contiguous data can't be converted.\n");
461 exit (1);
462 }
463 }
464 base -> len += new -> len;
465 }
466 }
467
468 int
469 phcmp (h1, h2)
470 Elf32_Phdr *h1, *h2;
471 {
472 if (h1 -> p_vaddr > h2 -> p_vaddr)
473 return 1;
474 else if (h1 -> p_vaddr < h2 -> p_vaddr)
475 return -1;
476 else
477 return 0;
478 }
479
480 char *saveRead (int file, off_t offset, off_t len, char *name)
481 {
482 char *tmp;
483 int count;
484 off_t off;
485 if ((off = lseek (file, offset, SEEK_SET)) < 0)
486 {
487 fprintf (stderr, "%s: fseek: %s\n", name, strerror (errno));
488 exit (1);
489 }
490 if (!(tmp = (char *)malloc (len)))
491 {
492 fprintf (stderr, "%s: Can't allocate %ld bytes.\n", name, (long)len);
493 exit (1);
494 }
495 count = read (file, tmp, len);
496 if (count != len)
497 {
498 fprintf (stderr, "%s: read: %s.\n",
499 name, count ? strerror (errno) : "End of file reached");
500 exit (1);
501 }
502 return tmp;
503 }
504
505
506 /*
507 * Construct ECOFF section headers for .text, .data, and .bss,
508 * with empty stubs for .rdata/.sdata/.sbss. Follow the section ordering
509 * guaranteed by the mipsco toolchain:
510 * .text, .rdata, .data., .sdata, .sbss, .bss.
511 *
512 * The ELF kernel we are translating has no sections corresponding
513 * to .rdata, .sdata and .sbss. Output zero-length sections for each,
514 * with no file contents and the correct ELF section flags.
515 * Some DECstation proms will not boot without this.
516 *
517 * XXX scan the ELF sectoin headers and map ELF .rodata to ECOFF .rdata
518 */
519 int
520 make_ecoff_section_hdrs(ep, esecs)
521 struct ecoff_exechdr *ep;
522 struct ecoff_scnhdr *esecs;
523
524 {
525 ep->f.f_nscns = 6; /* XXX */
526
527 strcpy (esecs [0].s_name, ".text");
528 strcpy (esecs [1].s_name, ".data");
529 strcpy (esecs [2].s_name, ".bss");
530
531 esecs [0].s_paddr = esecs [0].s_vaddr = ep->a.text_start;
532 esecs [1].s_paddr = esecs [1].s_vaddr = ep->a.data_start;
533 esecs [2].s_paddr = esecs [2].s_vaddr = ep->a.bss_start;
534 esecs [0].s_size = ep->a.tsize;
535 esecs [1].s_size = ep->a.dsize;
536 esecs [2].s_size = ep->a.bsize;
537
538 esecs [0].s_scnptr = ECOFF_TXTOFF (ep);
539 esecs [1].s_scnptr = ECOFF_DATOFF (ep);
540 #if 0
541 esecs [2].s_scnptr = esecs [1].s_scnptr +
542 ECOFF_ROUND (esecs [1].s_size, ECOFF_SEGMENT_ALIGNMENT (ep));
543 #endif
544
545 esecs [0].s_relptr = esecs [1].s_relptr
546 = esecs [2].s_relptr = 0;
547 esecs [0].s_lnnoptr = esecs [1].s_lnnoptr
548 = esecs [2].s_lnnoptr = 0;
549 esecs [0].s_nreloc = esecs [1].s_nreloc = esecs [2].s_nreloc = 0;
550 esecs [0].s_nlnno = esecs [1].s_nlnno = esecs [2].s_nlnno = 0;
551
552 esecs[1].s_flags = 0x100; /* ECOFF rdata */
553 esecs[3].s_flags = 0x200; /* ECOFF sdata */
554 esecs[4].s_flags = 0x400; /* ECOFF sbss */
555
556 /*
557 * Set the symbol-table offset to point at the end of any sections
558 * we loaded above, so later code can use it to write symbol table info..
559 */
560 ep->f.f_symptr = esecs[1].s_scnptr + esecs[1].s_size;
561
562 return(ep->f.f_nscns);
563 }
564