Home | History | Annotate | Line # | Download | only in elf2aout
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