xref: /netbsd-src/usr.bin/elf2ecoff/elf2ecoff.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: elf2ecoff.c,v 1.7 1997/07/07 00:02:16 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 = 0;
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