Home | History | Annotate | Line # | Download | only in elf2aout
elf2aout.c revision 1.2
      1 /* $NetBSD: elf2aout.c,v 1.2 1996/09/29 22:01:44 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 /* elf2aout.c
     32 
     33    This program converts an elf executable to a NetBSD a.out executable.
     34    The minimal symbol table is copied, but the debugging symbols and
     35    other informational sections are not. */
     36 
     37 #include <sys/types.h>
     38 #include <fcntl.h>
     39 #include <unistd.h>
     40 #include <machine/elf.h>
     41 #include <stdio.h>
     42 #include <a.out.h>
     43 #include <sys/errno.h>
     44 #include <string.h>
     45 #include <limits.h>
     46 
     47 struct sect {
     48   unsigned long vaddr;
     49   unsigned long len;
     50 };
     51 int phcmp ();
     52 char *saveRead (int file, off_t offset, off_t len, char *name);
     53 int copy (int, int, off_t, off_t);
     54 int translate_syms (int, int, off_t, off_t, off_t, off_t);
     55 extern int errno;
     56 int *symTypeTable;
     57 
     58 main (int argc, char **argv, char **envp)
     59 {
     60   struct ehdr ex;
     61   struct phdr *ph;
     62   struct shdr *sh;
     63   struct sym *symtab;
     64   char *shstrtab;
     65   int strtabix, symtabix;
     66   int i;
     67   struct sect text, data, bss;
     68   struct exec aex;
     69   int infile, outfile;
     70   unsigned long cur_vma = ULONG_MAX;
     71   int symflag = 0;
     72 
     73   text.len = data.len = bss.len = 0;
     74   text.vaddr = data.vaddr = bss.vaddr = 0;
     75 
     76   /* Check args... */
     77   if (argc < 3 || argc > 4)
     78     {
     79     usage:
     80       fprintf (stderr,
     81 	       "usage: elf2aout <elf executable> <a.out executable> [-s]\n");
     82       exit (1);
     83     }
     84   if (argc == 4)
     85     {
     86       if (strcmp (argv [3], "-s"))
     87 	goto usage;
     88       symflag = 1;
     89     }
     90 
     91   /* Try the input file... */
     92   if ((infile = open (argv [1], O_RDONLY)) < 0)
     93     {
     94       fprintf (stderr, "Can't open %s for read: %s\n",
     95 	       argv [1], strerror (errno));
     96       exit (1);
     97     }
     98 
     99   /* Read the header, which is at the beginning of the file... */
    100   i = read (infile, &ex, sizeof ex);
    101   if (i != sizeof ex)
    102     {
    103       fprintf (stderr, "ex: %s: %s.\n",
    104 	       argv [1], i ? strerror (errno) : "End of file reached");
    105       exit (1);
    106     }
    107 
    108   /* Read the program headers... */
    109   ph = (struct phdr *)saveRead (infile, ex.phoff,
    110 				ex.phcount * sizeof (struct phdr), "ph");
    111   /* Read the section headers... */
    112   sh = (struct shdr *)saveRead (infile, ex.shoff,
    113 				ex.shcount * sizeof (struct shdr), "sh");
    114   /* Read in the section string table. */
    115   shstrtab = saveRead (infile, sh [ex.shstrndx].offset,
    116 		       sh [ex.shstrndx].size, "shstrtab");
    117 
    118   /* Find space for a table matching ELF section indices to a.out symbol
    119      types. */
    120   symTypeTable = (int *)malloc (ex.shcount * sizeof (int));
    121   if (!symTypeTable)
    122     {
    123       fprintf (stderr, "symTypeTable: can't allocate.\n");
    124       exit (1);
    125     }
    126   memset (symTypeTable, 0, ex.shcount * sizeof (int));
    127 
    128   /* Look for the symbol table and string table...
    129      Also map section indices to symbol types for a.out */
    130   for (i = 0; i < ex.shcount; i++)
    131     {
    132       char *name = shstrtab + sh [i].name;
    133       if (!strcmp (name, ".symtab"))
    134 	symtabix = i;
    135       else if (!strcmp (name, ".strtab"))
    136 	strtabix = i;
    137       else if (!strcmp (name, ".text") || !strcmp (name, ".rodata"))
    138 	symTypeTable [i] = N_TEXT;
    139       else if (!strcmp (name, ".data") || !strcmp (name, ".sdata") ||
    140 	       !strcmp (name, ".lit4") || !strcmp (name, ".lit8"))
    141 	symTypeTable [i] = N_DATA;
    142       else if (!strcmp (name, ".bss") || !strcmp (name, ".sbss"))
    143 	symTypeTable [i] = N_BSS;
    144     }
    145 
    146   /* Figure out if we can cram the program header into an a.out header...
    147      Basically, we can't handle anything but loadable segments, but we
    148      can ignore some kinds of segments.   We can't handle holes in the
    149      address space, and we handle start addresses other than 0x1000 by
    150      hoping that the loader will know where to load - a.out doesn't have
    151      an explicit load address.   Segments may be out of order, so we
    152      sort them first. */
    153   qsort (ph, ex.phcount, sizeof (struct phdr), phcmp);
    154   for (i = 0; i < ex.phcount; i++)
    155     {
    156       /* Section types we can ignore... */
    157       if (ph [i].type == PT_NULL || ph [i].type == PT_NOTE ||
    158 	  ph [i].type == PT_PHDR || ph [i].type == PT_MIPS_REGINFO)
    159 	continue;
    160       /* Section types we can't handle... */
    161       else if (ph [i].type != PT_LOAD)
    162         {
    163 	  fprintf (stderr, "Program header %d type %d can't be converted.\n");
    164 	  exit (1);
    165 	}
    166       /* Writable (data) segment? */
    167       if (ph [i].flags & PF_W)
    168 	{
    169 	  struct sect ndata, nbss;
    170 
    171 	  ndata.vaddr = ph [i].vaddr;
    172 	  ndata.len = ph [i].filesz;
    173 	  nbss.vaddr = ph [i].vaddr + ph [i].filesz;
    174 	  nbss.len = ph [i].memsz - ph [i].filesz;
    175 
    176 	  combine (&data, &ndata, 0);
    177 	  combine (&bss, &nbss, 1);
    178 	}
    179       else
    180 	{
    181 	  struct sect ntxt;
    182 
    183 	  ntxt.vaddr = ph [i].vaddr;
    184 	  ntxt.len = ph [i].filesz;
    185 
    186 	  combine (&text, &ntxt);
    187 	}
    188       /* Remember the lowest segment start address. */
    189       if (ph [i].vaddr < cur_vma)
    190 	cur_vma = ph [i].vaddr;
    191     }
    192 
    193   /* Sections must be in order to be converted... */
    194   if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
    195       text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr)
    196     {
    197       fprintf (stderr, "Sections ordering prevents a.out conversion.\n");
    198       exit (1);
    199     }
    200 
    201   /* If there's a data section but no text section, then the loader
    202      combined everything into one section.   That needs to be the
    203      text section, so just make the data section zero length following
    204      text. */
    205   if (data.len && !text.len)
    206     {
    207       text = data;
    208       data.vaddr = text.vaddr + text.len;
    209       data.len = 0;
    210     }
    211 
    212   /* If there is a gap between text and data, we'll fill it when we copy
    213      the data, so update the length of the text segment as represented in
    214      a.out to reflect that, since a.out doesn't allow gaps in the program
    215      address space. */
    216   if (text.vaddr + text.len < data.vaddr)
    217     text.len = data.vaddr - text.vaddr;
    218 
    219   /* We now have enough information to cons up an a.out header... */
    220   aex.a_midmag = htonl ((symflag << 26) | (MID_PMAX << 16) | OMAGIC);
    221   aex.a_text = text.len;
    222   aex.a_data = data.len;
    223   aex.a_bss = bss.len;
    224   aex.a_entry = ex.entry;
    225   aex.a_syms = (sizeof (struct nlist) *
    226 		(symtabix != -1
    227 		 ? sh [symtabix].size / sizeof (struct sym) : 0));
    228   aex.a_trsize = 0;
    229   aex.a_drsize = 0;
    230 
    231   /* Make the output file... */
    232   if ((outfile = open (argv [2], O_WRONLY | O_CREAT, 0777)) < 0)
    233     {
    234       fprintf (stderr, "Unable to create %s: %s\n", argv [2], strerror (errno));
    235       exit (1);
    236     }
    237   /* Write the header... */
    238   i = write (outfile, &aex, sizeof aex);
    239   if (i != sizeof aex)
    240     {
    241       perror ("aex: write");
    242       exit (1);
    243     }
    244 
    245   /* Copy the loadable sections.   Zero-fill any gaps less than 64k;
    246      complain about any zero-filling, and die if we're asked to zero-fill
    247      more than 64k. */
    248   for (i = 0; i < ex.phcount; i++)
    249     {
    250       /* Unprocessable sections were handled above, so just verify that
    251 	 the section can be loaded before copying. */
    252       if (ph [i].type == PT_LOAD && ph [i].filesz)
    253 	{
    254 	  if (cur_vma != ph [i].vaddr)
    255 	    {
    256 	      unsigned long gap = ph [i].vaddr - cur_vma;
    257 	      char obuf [1024];
    258 	      if (gap > 65536)
    259 		{
    260 		  fprintf (stderr, "Intersegment gap (%d bytes) too large.\n",
    261 			   gap);
    262 		  exit (1);
    263 		}
    264 	      fprintf (stderr, "Warning: %d byte intersegment gap.\n", gap);
    265 	      memset (obuf, 0, sizeof obuf);
    266 	      while (gap)
    267 		{
    268 		  int count = write (outfile, obuf, (gap > sizeof obuf
    269 						     ? sizeof obuf : gap));
    270 		  if (count < 0)
    271 		    {
    272 		      fprintf (stderr, "Error writing gap: %s\n",
    273 			       strerror (errno));
    274 		      exit (1);
    275 		    }
    276 		  gap -= count;
    277 		}
    278 	    }
    279 	  copy (outfile, infile, ph [i].offset, ph [i].filesz);
    280 	  cur_vma = ph [i].vaddr + ph [i].filesz;
    281 	}
    282     }
    283 
    284   /* Copy and translate the symbol table... */
    285   translate_syms (outfile, infile, sh [symtabix].offset, sh [symtabix].size,
    286 		  sh [strtabix].offset, sh [strtabix].size);
    287 
    288   /* Looks like we won... */
    289   exit (0);
    290 }
    291 
    292 /* translate_syms (out, in, offset, size)
    293 
    294    Read the ELF symbol table from in at offset; translate it into a.out
    295    nlist format and write it to out. */
    296 
    297 translate_syms (out, in, symoff, symsize, stroff, strsize)
    298      int out, in;
    299      off_t symoff, symsize;
    300      off_t stroff, strsize;
    301 {
    302 # define SYMS_PER_PASS	64
    303   struct sym inbuf [64];
    304   struct nlist outbuf [64];
    305   int i, remaining, cur;
    306   char *oldstrings;
    307   char *newstrings, *nsp;
    308   int newstringsize;
    309 
    310   /* Zero the unused fields in the output buffer.. */
    311   memset (outbuf, 0, sizeof outbuf);
    312 
    313   /* Find number of symbols to process... */
    314   remaining = symsize / sizeof (struct sym);
    315 
    316   /* Suck in the old string table... */
    317   oldstrings = saveRead (in, stroff, strsize, "string table");
    318 
    319   /* Allocate space for the new one.   XXX We make the wild assumption that
    320      no two symbol table entries will point at the same place in the
    321      string table - if that assumption is bad, this could easily blow up. */
    322   newstringsize = strsize + remaining;
    323   newstrings = (char *)malloc (newstringsize);
    324   if (!newstrings)
    325     {
    326       fprintf (stderr, "No memory for new string table!\n");
    327       exit (1);
    328     }
    329   /* Initialize the table pointer... */
    330   nsp = newstrings;
    331 
    332   /* Go the the start of the ELF symbol table... */
    333   if (lseek (in, symoff, SEEK_SET) < 0)
    334     {
    335       perror ("translate_syms: lseek");
    336       exit (1);
    337     }
    338 
    339   /* Translate and copy symbols... */
    340   while (remaining)
    341     {
    342       cur = remaining;
    343       if (cur > SYMS_PER_PASS)
    344 	cur = SYMS_PER_PASS;
    345       remaining -= cur;
    346       if ((i = read (in, inbuf, cur * sizeof (struct sym)))
    347 	  != cur * sizeof (struct sym))
    348 	{
    349 	  if (i < 0)
    350 	    perror ("translate_syms");
    351 	  else
    352 	    fprintf (stderr, "translate_syms: premature end of file.\n");
    353 	  exit (1);
    354 	}
    355 
    356       /* Do the translation... */
    357       for (i = 0; i < cur; i++)
    358 	{
    359 	  /* Copy the symbol into the new table, but prepend an underscore. */
    360 	  *nsp = '_';
    361 	  strcpy (nsp + 1, oldstrings + inbuf [i].name);
    362 	  outbuf [i].n_un.n_strx = nsp - newstrings + 4;
    363 	  nsp += strlen (nsp) + 1;
    364 
    365 	  /* Convert ELF symbol type/section/etc info into a.out type info. */
    366 	  if (inbuf [i].type == STT_FILE)
    367 	    outbuf [i].n_type = N_FN;
    368 	  else if (inbuf [i].shndx == SHN_UNDEF)
    369 	    outbuf [i].n_type = N_UNDF;
    370 	  else if (inbuf [i].shndx == SHN_ABS)
    371 	    outbuf [i].n_type = N_ABS;
    372 	  else if (inbuf [i].shndx == SHN_COMMON ||
    373 		 inbuf [i].shndx == SHN_MIPS_ACOMMON)
    374 	    outbuf [i].n_type = N_COMM;
    375 	  else
    376 	    outbuf [i].n_type = symTypeTable [inbuf [i].shndx];
    377 	  if (inbuf [i].binding == STB_GLOBAL)
    378 	    outbuf [i].n_type |= N_EXT;
    379 	  /* Symbol values in executables should be compatible. */
    380 	  outbuf [i].n_value = inbuf [i].value;
    381 	}
    382       /* Write out the symbols... */
    383       if ((i = write (out, outbuf, cur * sizeof (struct nlist)))
    384 	  != cur * sizeof (struct nlist))
    385 	{
    386 	  fprintf (stderr, "translate_syms: write: %s\n", strerror (errno));
    387 	  exit (1);
    388 	}
    389     }
    390   /* Write out the string table length... */
    391   if (write (out, &newstringsize, sizeof newstringsize)
    392       != sizeof newstringsize)
    393     {
    394       fprintf (stderr,
    395 	       "translate_syms: newstringsize: %s\n", strerror (errno));
    396       exit (1);
    397     }
    398   /* Write out the string table... */
    399   if (write (out, newstrings, newstringsize) != newstringsize)
    400     {
    401       fprintf (stderr, "translate_syms: newstrings: %s\n", strerror (errno));
    402       exit (1);
    403     }
    404 }
    405 
    406 copy (out, in, offset, size)
    407      int out, in;
    408      off_t offset, size;
    409 {
    410   char ibuf [4096];
    411   int remaining, cur, count;
    412 
    413   /* Go the the start of the ELF symbol table... */
    414   if (lseek (in, offset, SEEK_SET) < 0)
    415     {
    416       perror ("copy: lseek");
    417       exit (1);
    418     }
    419 
    420   remaining = size;
    421   while (remaining)
    422     {
    423       cur = remaining;
    424       if (cur > sizeof ibuf)
    425 	cur = sizeof ibuf;
    426       remaining -= cur;
    427       if ((count = read (in, ibuf, cur)) != cur)
    428 	{
    429 	  fprintf (stderr, "copy: read: %s\n",
    430 		   count ? strerror (errno) : "premature end of file");
    431 	  exit (1);
    432 	}
    433       if ((count = write (out, ibuf, cur)) != cur)
    434 	{
    435 	  perror ("copy: write");
    436 	  exit (1);
    437 	}
    438     }
    439 }
    440 
    441 /* Combine two segments, which must be contiguous.   If pad is true, it's
    442    okay for there to be padding between. */
    443 combine (base, new, pad)
    444      struct sect *base, *new;
    445      int pad;
    446 {
    447   if (!base -> len)
    448     *base = *new;
    449   else if (new -> len)
    450     {
    451       if (base -> vaddr + base -> len != new -> vaddr)
    452 	{
    453 	  if (pad)
    454 	    base -> len = new -> vaddr - base -> vaddr;
    455 	  else
    456 	    {
    457 	      fprintf (stderr,
    458 		       "Non-contiguous data can't be converted.\n");
    459 	      exit (1);
    460 	    }
    461 	}
    462       base -> len += new -> len;
    463     }
    464 }
    465 
    466 phcmp (h1, h2)
    467      struct phdr *h1, *h2;
    468 {
    469   if (h1 -> vaddr > h2 -> vaddr)
    470     return 1;
    471   else if (h1 -> vaddr < h2 -> vaddr)
    472     return -1;
    473   else
    474     return 0;
    475 }
    476 
    477 char *saveRead (int file, off_t offset, off_t len, char *name)
    478 {
    479   char *tmp;
    480   int count;
    481   off_t off;
    482   if ((off = lseek (file, offset, SEEK_SET)) < 0)
    483     {
    484       fprintf (stderr, "%s: fseek: %s\n", name, strerror (errno));
    485       exit (1);
    486     }
    487   if (!(tmp = (char *)malloc (len)))
    488     {
    489       fprintf (stderr, "%s: Can't allocate %d bytes.\n", name, len);
    490       exit (1);
    491     }
    492   count = read (file, tmp, len);
    493   if (count != len)
    494     {
    495       fprintf (stderr, "%s: read: %s.\n",
    496 	       name, count ? strerror (errno) : "End of file reached");
    497       exit (1);
    498     }
    499   return tmp;
    500 }
    501