Home | History | Annotate | Line # | Download | only in elf2ecoff
elf2ecoff.c revision 1.2
      1 /* $NetBSD: elf2ecoff.c,v 1.2 1996/09/29 22:01:45 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 <machine/elf.h>
     42 #include <stdio.h>
     43 #include <sys/exec_ecoff.h>
     44 #include <sys/errno.h>
     45 #include <string.h>
     46 #include <limits.h>
     47 
     48 struct sect {
     49   unsigned long vaddr;
     50   unsigned long len;
     51 };
     52 
     53 int phcmp ();
     54 char *saveRead (int file, off_t offset, off_t len, char *name);
     55 int copy (int, int, off_t, off_t);
     56 int translate_syms (int, int, off_t, off_t, off_t, off_t);
     57 extern int errno;
     58 int *symTypeTable;
     59 
     60 main (int argc, char **argv, char **envp)
     61 {
     62   struct ehdr ex;
     63   struct phdr *ph;
     64   struct shdr *sh;
     65   struct sym *symtab;
     66   char *shstrtab;
     67   int strtabix, symtabix;
     68   int i, pad;
     69   struct sect text, data, bss;
     70   struct ecoff_exechdr ep;
     71   struct ecoff_scnhdr esecs [3];
     72   int infile, outfile;
     73   unsigned long cur_vma = ULONG_MAX;
     74   int symflag = 0;
     75 
     76   text.len = data.len = bss.len = 0;
     77   text.vaddr = data.vaddr = bss.vaddr = 0;
     78 
     79   /* Check args... */
     80   if (argc < 3 || argc > 4)
     81     {
     82     usage:
     83       fprintf (stderr,
     84 	       "usage: elf2aout <elf executable> <a.out executable> [-s]\n");
     85       exit (1);
     86     }
     87   if (argc == 4)
     88     {
     89       if (strcmp (argv [3], "-s"))
     90 	goto usage;
     91       symflag = 1;
     92     }
     93 
     94   /* Try the input file... */
     95   if ((infile = open (argv [1], O_RDONLY)) < 0)
     96     {
     97       fprintf (stderr, "Can't open %s for read: %s\n",
     98 	       argv [1], strerror (errno));
     99       exit (1);
    100     }
    101 
    102   /* Read the header, which is at the beginning of the file... */
    103   i = read (infile, &ex, sizeof ex);
    104   if (i != sizeof ex)
    105     {
    106       fprintf (stderr, "ex: %s: %s.\n",
    107 	       argv [1], i ? strerror (errno) : "End of file reached");
    108       exit (1);
    109     }
    110 
    111   /* Read the program headers... */
    112   ph = (struct phdr *)saveRead (infile, ex.phoff,
    113 				ex.phcount * sizeof (struct phdr), "ph");
    114   /* Read the section headers... */
    115   sh = (struct shdr *)saveRead (infile, ex.shoff,
    116 				ex.shcount * sizeof (struct shdr), "sh");
    117   /* Read in the section string table. */
    118   shstrtab = saveRead (infile, sh [ex.shstrndx].offset,
    119 		       sh [ex.shstrndx].size, "shstrtab");
    120 
    121   /* Figure out if we can cram the program header into an ECOFF
    122      header...  Basically, we can't handle anything but loadable
    123      segments, but we can ignore some kinds of segments.  We can't
    124      handle holes in the address space.  Segments may be out of order,
    125      so we sort them first. */
    126 
    127   qsort (ph, ex.phcount, sizeof (struct phdr), phcmp);
    128 
    129   for (i = 0; i < ex.phcount; i++)
    130     {
    131       /* Section types we can ignore... */
    132       if (ph [i].type == PT_NULL || ph [i].type == PT_NOTE ||
    133 	  ph [i].type == PT_PHDR || ph [i].type == PT_MIPS_REGINFO)
    134 	continue;
    135       /* Section types we can't handle... */
    136       else if (ph [i].type != PT_LOAD)
    137         {
    138 	  fprintf (stderr, "Program header %d type %d can't be converted.\n");
    139 	  exit (1);
    140 	}
    141       /* Writable (data) segment? */
    142       if (ph [i].flags & PF_W)
    143 	{
    144 	  struct sect ndata, nbss;
    145 
    146 	  ndata.vaddr = ph [i].vaddr;
    147 	  ndata.len = ph [i].filesz;
    148 	  nbss.vaddr = ph [i].vaddr + ph [i].filesz;
    149 	  nbss.len = ph [i].memsz - ph [i].filesz;
    150 
    151 	  combine (&data, &ndata, 0);
    152 	  combine (&bss, &nbss, 1);
    153 	}
    154       else
    155 	{
    156 	  struct sect ntxt;
    157 
    158 	  ntxt.vaddr = ph [i].vaddr;
    159 	  ntxt.len = ph [i].filesz;
    160 
    161 	  combine (&text, &ntxt);
    162 	}
    163       /* Remember the lowest segment start address. */
    164       if (ph [i].vaddr < cur_vma)
    165 	cur_vma = ph [i].vaddr;
    166     }
    167 
    168   /* Sections must be in order to be converted... */
    169   if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
    170       text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr)
    171     {
    172       fprintf (stderr, "Sections ordering prevents a.out conversion.\n");
    173       exit (1);
    174     }
    175 
    176   /* If there's a data section but no text section, then the loader
    177      combined everything into one section.   That needs to be the
    178      text section, so just make the data section zero length following
    179      text. */
    180   if (data.len && !text.len)
    181     {
    182       text = data;
    183       data.vaddr = text.vaddr + text.len;
    184       data.len = 0;
    185     }
    186 
    187   /* If there is a gap between text and data, we'll fill it when we copy
    188      the data, so update the length of the text segment as represented in
    189      a.out to reflect that, since a.out doesn't allow gaps in the program
    190      address space. */
    191   if (text.vaddr + text.len < data.vaddr)
    192     text.len = data.vaddr - text.vaddr;
    193 
    194   /* We now have enough information to cons up an a.out header... */
    195   ep.a.magic = ECOFF_OMAGIC;
    196   ep.a.vstamp = 200;
    197   ep.a.tsize = text.len;
    198   ep.a.dsize = data.len;
    199   ep.a.bsize = bss.len;
    200   ep.a.entry = ex.entry;
    201   ep.a.text_start = text.vaddr;
    202   ep.a.data_start = data.vaddr;
    203   ep.a.bss_start = bss.vaddr;
    204   ep.a.gprmask = 0xf3fffffe;
    205   bzero (&ep.a.cprmask, sizeof ep.a.cprmask);
    206   ep.a.gp_value = 0; /* unused. */
    207 
    208   ep.f.f_magic = ECOFF_MAGIC_MIPSEL;
    209   ep.f.f_nscns = 3;
    210   ep.f.f_timdat = 0;	/* bogus */
    211   ep.f.f_symptr = 0;
    212   ep.f.f_nsyms = 0;
    213   ep.f.f_opthdr = sizeof ep.a;
    214   ep.f.f_flags = 0x100f; /* Stripped, not sharable. */
    215 
    216   strcpy (esecs [0].s_name, ".text");
    217   strcpy (esecs [1].s_name, ".data");
    218   strcpy (esecs [2].s_name, ".bss");
    219   esecs [0].s_paddr = esecs [0].s_vaddr = ep.a.text_start;
    220   esecs [1].s_paddr = esecs [1].s_vaddr = ep.a.data_start;
    221   esecs [2].s_paddr = esecs [2].s_vaddr = ep.a.bss_start;
    222   esecs [0].s_size = ep.a.tsize;
    223   esecs [1].s_size = ep.a.dsize;
    224   esecs [2].s_size = ep.a.bsize;
    225 
    226   esecs [0].s_scnptr = ECOFF_TXTOFF (&ep);
    227   esecs [1].s_scnptr = ECOFF_DATOFF (&ep);
    228   esecs [2].s_scnptr = esecs [1].s_scnptr +
    229 	  ECOFF_ROUND (esecs [1].s_size, ECOFF_SEGMENT_ALIGNMENT (&ep));
    230   esecs [0].s_relptr = esecs [1].s_relptr
    231 	  = esecs [2].s_relptr = 0;
    232   esecs [0].s_lnnoptr = esecs [1].s_lnnoptr
    233 	  = esecs [2].s_lnnoptr = 0;
    234   esecs [0].s_nreloc = esecs [1].s_nreloc = esecs [2].s_nreloc = 0;
    235   esecs [0].s_nlnno = esecs [1].s_nlnno = esecs [2].s_nlnno = 0;
    236   esecs [0].s_flags = 0x20;
    237   esecs [1].s_flags = 0x40;
    238   esecs [2].s_flags = 0x82;
    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 
    247   /* Write the headers... */
    248   i = write (outfile, &ep.f, sizeof ep.f);
    249   if (i != sizeof ep.f)
    250     {
    251       perror ("ep.f: write");
    252       exit (1);
    253 
    254   for (i = 0; i < 6; i++)
    255     {
    256       printf ("Section %d: %s phys %x  size %x  file offset %x\n",
    257 	      i, esecs [i].s_name, esecs [i].s_paddr,
    258 	      esecs [i].s_size, esecs [i].s_scnptr);
    259     }
    260     }
    261   fprintf (stderr, "wrote %d byte file header.\n", i);
    262 
    263   i = write (outfile, &ep.a, sizeof ep.a);
    264   if (i != sizeof ep.a)
    265     {
    266       perror ("ep.a: write");
    267       exit (1);
    268     }
    269   fprintf (stderr, "wrote %d byte a.out header.\n", i);
    270 
    271   i = write (outfile, &esecs, sizeof esecs);
    272   if (i != sizeof esecs)
    273     {
    274       perror ("esecs: write");
    275       exit (1);
    276     }
    277   fprintf (stderr, "wrote %d bytes of section headers.\n", i);
    278 
    279   if (pad = ((sizeof ep.f + sizeof ep.a + sizeof esecs) & 15))
    280     {
    281       pad = 16 - pad;
    282       i = write (outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad);
    283       if (i < 0)
    284 	{
    285 	  perror ("ipad: write");
    286 	  exit (1);
    287 	}
    288       fprintf (stderr, "wrote %d byte pad.\n", i);
    289     }
    290 
    291   /* Copy the loadable sections.   Zero-fill any gaps less than 64k;
    292      complain about any zero-filling, and die if we're asked to zero-fill
    293      more than 64k. */
    294   for (i = 0; i < ex.phcount; i++)
    295     {
    296       /* Unprocessable sections were handled above, so just verify that
    297 	 the section can be loaded before copying. */
    298       if (ph [i].type == PT_LOAD && ph [i].filesz)
    299 	{
    300 	  if (cur_vma != ph [i].vaddr)
    301 	    {
    302 	      unsigned long gap = ph [i].vaddr - cur_vma;
    303 	      char obuf [1024];
    304 	      if (gap > 65536)
    305 		{
    306 		  fprintf (stderr, "Intersegment gap (%d bytes) too large.\n",
    307 			   gap);
    308 		  exit (1);
    309 		}
    310 	      fprintf (stderr, "Warning: %d byte intersegment gap.\n", gap);
    311 	      memset (obuf, 0, sizeof obuf);
    312 	      while (gap)
    313 		{
    314 		  int count = write (outfile, obuf, (gap > sizeof obuf
    315 						     ? sizeof obuf : gap));
    316 		  if (count < 0)
    317 		    {
    318 		      fprintf (stderr, "Error writing gap: %s\n",
    319 			       strerror (errno));
    320 		      exit (1);
    321 		    }
    322 		  gap -= count;
    323 		}
    324 	    }
    325 fprintf (stderr, "writing %d bytes...\n", ph [i].filesz);
    326 	  copy (outfile, infile, ph [i].offset, ph [i].filesz);
    327 	  cur_vma = ph [i].vaddr + ph [i].filesz;
    328 	}
    329     }
    330 
    331   /* Looks like we won... */
    332   exit (0);
    333 }
    334 
    335 copy (out, in, offset, size)
    336      int out, in;
    337      off_t offset, size;
    338 {
    339   char ibuf [4096];
    340   int remaining, cur, count;
    341 
    342   /* Go the the start of the ELF symbol table... */
    343   if (lseek (in, offset, SEEK_SET) < 0)
    344     {
    345       perror ("copy: lseek");
    346       exit (1);
    347     }
    348 
    349   remaining = size;
    350   while (remaining)
    351     {
    352       cur = remaining;
    353       if (cur > sizeof ibuf)
    354 	cur = sizeof ibuf;
    355       remaining -= cur;
    356       if ((count = read (in, ibuf, cur)) != cur)
    357 	{
    358 	  fprintf (stderr, "copy: read: %s\n",
    359 		   count ? strerror (errno) : "premature end of file");
    360 	  exit (1);
    361 	}
    362       if ((count = write (out, ibuf, cur)) != cur)
    363 	{
    364 	  perror ("copy: write");
    365 	  exit (1);
    366 	}
    367     }
    368 }
    369 
    370 /* Combine two segments, which must be contiguous.   If pad is true, it's
    371    okay for there to be padding between. */
    372 combine (base, new, pad)
    373      struct sect *base, *new;
    374      int pad;
    375 {
    376   if (!base -> len)
    377     *base = *new;
    378   else if (new -> len)
    379     {
    380       if (base -> vaddr + base -> len != new -> vaddr)
    381 	{
    382 	  if (pad)
    383 	    base -> len = new -> vaddr - base -> vaddr;
    384 	  else
    385 	    {
    386 	      fprintf (stderr,
    387 		       "Non-contiguous data can't be converted.\n");
    388 	      exit (1);
    389 	    }
    390 	}
    391       base -> len += new -> len;
    392     }
    393 }
    394 
    395 phcmp (h1, h2)
    396      struct phdr *h1, *h2;
    397 {
    398   if (h1 -> vaddr > h2 -> vaddr)
    399     return 1;
    400   else if (h1 -> vaddr < h2 -> vaddr)
    401     return -1;
    402   else
    403     return 0;
    404 }
    405 
    406 char *saveRead (int file, off_t offset, off_t len, char *name)
    407 {
    408   char *tmp;
    409   int count;
    410   off_t off;
    411   if ((off = lseek (file, offset, SEEK_SET)) < 0)
    412     {
    413       fprintf (stderr, "%s: fseek: %s\n", name, strerror (errno));
    414       exit (1);
    415     }
    416   if (!(tmp = (char *)malloc (len)))
    417     {
    418       fprintf (stderr, "%s: Can't allocate %d bytes.\n", name, len);
    419       exit (1);
    420     }
    421   count = read (file, tmp, len);
    422   if (count != len)
    423     {
    424       fprintf (stderr, "%s: read: %s.\n",
    425 	       name, count ? strerror (errno) : "End of file reached");
    426       exit (1);
    427     }
    428   return tmp;
    429 }
    430