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