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
48struct sect {
49  unsigned long vaddr;
50  unsigned long len;
51};
52
53int phcmp ();
54char *saveRead (int file, off_t offset, off_t len, char *name);
55int copy (int, int, off_t, off_t);
56int translate_syms (int, int, off_t, off_t, off_t, off_t);
57extern int errno;
58int *symTypeTable;
59
60main (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	    }
325fprintf (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
335copy (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. */
372combine (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
395phcmp (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
406char *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