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