elf2ecoff.c revision 1.4
1/*	$NetBSD: elf2ecoff.c,v 1.4 1997/01/09 20:18:37 tls 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
57struct sect {
58  unsigned long vaddr;
59  unsigned long len;
60};
61
62int phcmp ();
63char *saveRead (int file, off_t offset, off_t len, char *name);
64int copy (int, int, off_t, off_t);
65int translate_syms (int, int, off_t, off_t, off_t, off_t);
66extern int errno;
67int *symTypeTable;
68
69main (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	    }
334fprintf (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
344copy (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. */
381combine (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
404int
405phcmp (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
416char *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