elf2ecoff.c revision 1.6
1/*	$NetBSD: elf2ecoff.c,v 1.6 1997/07/06 23:57:39 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 <sys/exec.h>
42#include <sys/exec_elf.h>
43#include <sys/exec_aout.h>
44#include <stdio.h>
45#include <sys/exec_ecoff.h>
46#include <sys/errno.h>
47#include <stdlib.h>
48#include <string.h>
49#include <limits.h>
50
51
52/* Elf Program segment permissions, in program header flags field */
53
54#define PF_X            (1 << 0)        /* Segment is executable */
55#define PF_W            (1 << 1)        /* Segment is writable */
56#define PF_R            (1 << 2)        /* Segment is readable */
57#define PF_MASKPROC     0xF0000000      /* Processor-specific reserved bits */
58
59
60#define	ISLAST(p)	(p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
61
62struct sect {
63  unsigned long vaddr;
64  unsigned long len;
65};
66
67int debug = 1;
68
69int phcmp (Elf32_Phdr *h1, Elf32_Phdr *h2);
70
71char *saveRead (int file, off_t offset, off_t len, char *name);
72void copy (int, int, off_t, off_t);
73void combine (struct sect *base, struct sect *new, int paddable);
74void translate_syms (int, int, off_t, off_t, off_t, off_t);
75int make_ecoff_section_hdrs(struct ecoff_exechdr *ep,
76				 struct ecoff_scnhdr *esecs);
77
78void write_ecoff_symhdr(int outfile, struct ecoff_exechdr *ep,
79			struct ecoff_symhdr *symhdrp,
80			long nesyms, long extstroff);
81
82extern int errno;
83int *symTypeTable;
84
85int
86main (int argc, char **argv, char **envp)
87{
88  Elf32_Ehdr ex;
89  Elf32_Phdr *ph;
90  Elf32_Shdr *sh;
91  char *shstrtab;
92  int strtabix, symtabix;
93  int i, pad;
94  struct sect text, data, bss;		/* a.out-compatible sections */
95  struct sect rdata, sdata, sbss; 	/* ECOFF-only sections */
96
97  struct ecoff_exechdr ep;
98  struct ecoff_scnhdr esecs [6];
99
100  int infile, outfile;
101  unsigned long cur_vma = ULONG_MAX;
102  int symflag = 0;
103  int nsecs = 0;
104
105  text.len = data.len = bss.len = 0;
106  text.vaddr = data.vaddr = bss.vaddr = 0;
107
108  rdata.len = sdata.len = sbss.len = 0;
109  rdata.vaddr = sdata.vaddr = sbss.vaddr = 0;
110
111  /* Check args... */
112  if (argc < 3 || argc > 4)
113    {
114    usage:
115      fprintf (stderr,
116	       "usage: elf2aout <elf executable> <a.out executable> [-s]\n");
117      exit (1);
118    }
119  if (argc == 4)
120    {
121      if (strcmp (argv [3], "-s"))
122	goto usage;
123      symflag = 1;
124    }
125
126  /* Try the input file... */
127  if ((infile = open (argv [1], O_RDONLY)) < 0)
128    {
129      fprintf (stderr, "Can't open %s for read: %s\n",
130	       argv [1], strerror (errno));
131      exit (1);
132    }
133
134  /* Read the header, which is at the beginning of the file... */
135  i = read (infile, &ex, sizeof ex);
136  if (i != sizeof ex)
137    {
138      fprintf (stderr, "ex: %s: %s.\n",
139	       argv [1], i ? strerror (errno) : "End of file reached");
140      exit (1);
141    }
142
143  /* Read the program headers... */
144  ph = (Elf32_Phdr *)saveRead (infile, ex.e_phoff,
145				ex.e_phnum * sizeof (Elf32_Phdr), "ph");
146  /* Read the section headers... */
147  sh = (Elf32_Shdr *)saveRead (infile, ex.e_shoff,
148				ex.e_shnum * sizeof (Elf32_Shdr), "sh");
149  /* Read in the section string table. */
150  shstrtab = saveRead (infile, sh [ex.e_shstrndx].sh_offset,
151		       sh [ex.e_shstrndx].sh_size, "shstrtab");
152  /* Read in the section string table. */
153  shstrtab = saveRead (infile, sh [ex.e_shstrndx].sh_offset,
154		       sh [ex.e_shstrndx].sh_size, "shstrtab");
155
156
157  /* Look for the symbol table and string table...
158     Also map section indices to symbol types for a.out */
159  symtabix = 0;
160  strtabix = 0;
161  for (i = 0; i < ex.e_shnum; i++)
162    {
163      char *name = shstrtab + sh [i].sh_name;
164      if (!strcmp (name, ".symtab"))
165	symtabix = i;
166      else if (!strcmp (name, ".strtab"))
167	strtabix = i;
168
169    }
170
171  /* Figure out if we can cram the program header into an ECOFF
172     header...  Basically, we can't handle anything but loadable
173     segments, but we can ignore some kinds of segments.  We can't
174     handle holes in the address space.  Segments may be out of order,
175     so we sort them first. */
176
177  qsort (ph, ex.e_phnum, sizeof (Elf32_Phdr),
178	( int (*)(const void *, const void *))phcmp);
179
180  for (i = 0; i < ex.e_phnum; i++)
181    {
182      /* Section types we can ignore... */
183      if (ph [i].p_type == Elf_pt_null || ph [i].p_type == Elf_pt_note ||
184	  ph [i].p_type == Elf_pt_phdr ||
185	  ph [i].p_type == Elf_pt_mips_reginfo) {
186
187	  if (debug) {
188	    fprintf(stderr,"  skipping PH %d type %d flags 0x%x\n",
189                    i, ph[i].p_type, ph[i].p_flags);
190	   }
191	   continue;
192	}
193
194      /* Section types we can't handle... */
195      else if (ph [i].p_type != Elf_pt_load)
196        {
197	  fprintf (stderr, "Program header %d type %d can't be converted.\n",
198		   i, ph[i].p_type);
199	  exit (1);
200	}
201      /* Writable (data) segment? */
202      if (ph [i].p_flags & PF_W)
203	{
204	  struct sect ndata, nbss;
205
206	  ndata.vaddr = ph [i].p_vaddr;
207	  ndata.len = ph [i].p_filesz;
208	  nbss.vaddr = ph [i].p_vaddr + ph [i].p_filesz;
209	  nbss.len = ph [i].p_memsz - ph [i].p_filesz;
210
211	  if (debug) {
212	    printf("  combinining PH %d type %d flags 0x%x with data, ndata = %ld, nbss =%ld\n", i, ph[i].p_type, ph[i].p_flags, ndata.len, nbss.len);
213	  }
214
215	  combine (&data, &ndata, 0);
216	  combine (&bss, &nbss, 1);
217	}
218      else
219	{
220	  struct sect ntxt;
221
222	  ntxt.vaddr = ph [i].p_vaddr;
223	  ntxt.len = ph [i].p_filesz;
224	  if (debug) {
225
226	    printf("  combinining PH %d type %d flags 0x%x with text, len = %ld\n",
227		 i, ph[i].p_type, ph[i].p_flags, ntxt.len);
228	  }
229
230
231	  combine (&text, &ntxt, 0);
232	}
233      /* Remember the lowest segment start address. */
234      if (ph [i].p_vaddr < cur_vma)
235	cur_vma = ph [i].p_vaddr;
236    }
237
238  /* Sections must be in order to be converted... */
239  if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
240      text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr)
241    {
242      fprintf (stderr, "Sections ordering prevents a.out conversion.\n");
243      exit (1);
244    }
245
246  /* If there's a data section but no text section, then the loader
247     combined everything into one section.   That needs to be the
248     text section, so just make the data section zero length following
249     text. */
250  if (data.len && !text.len)
251    {
252      text = data;
253      data.vaddr = text.vaddr + text.len;
254      data.len = 0;
255    }
256
257  /* If there is a gap between text and data, we'll fill it when we copy
258     the data, so update the length of the text segment as represented in
259     a.out to reflect that, since a.out doesn't allow gaps in the program
260     address space. */
261  if (text.vaddr + text.len < data.vaddr)
262    text.len = data.vaddr - text.vaddr;
263
264  /* We now have enough information to cons up an a.out header... */
265  ep.a.magic = ECOFF_OMAGIC;
266  ep.a.vstamp =  2 * 256 + 10;	/* compatible with version 2.10 */
267  ep.a.tsize = text.len;
268  ep.a.dsize = data.len;
269  ep.a.bsize = bss.len;
270  ep.a.entry = ex.e_entry;
271  ep.a.text_start = text.vaddr;
272  ep.a.data_start = data.vaddr;
273  ep.a.bss_start = bss.vaddr;
274  ep.a.gprmask = 0xf3fffffe;
275  bzero (&ep.a.cprmask, sizeof ep.a.cprmask);
276  ep.a.gp_value = 0; /* unused. */
277
278  ep.f.f_magic = ECOFF_MAGIC_MIPSEL;
279  ep.f.f_nscns = 6;
280  ep.f.f_timdat = 0;	/* bogus */
281  ep.f.f_symptr = 0;
282  ep.f.f_nsyms = sizeof(struct ecoff_symhdr);
283  ep.f.f_opthdr = sizeof ep.a;
284  ep.f.f_flags = 0x100f; /* Stripped, not sharable. */
285
286  bzero(esecs, sizeof(esecs));
287
288  /* Make  ECOFF section headers, with empty stubs for .rdata/.sdata/.sbss. */
289  make_ecoff_section_hdrs(&ep, esecs);
290
291  nsecs = ep.f.f_nscns;
292
293  /* Make the output file... */
294  if ((outfile = open (argv [2], O_WRONLY | O_CREAT, 0777)) < 0)
295    {
296      fprintf (stderr, "Unable to create %s: %s\n", argv [2], strerror (errno));
297      exit (1);
298    }
299
300  /* Write the headers... */
301  i = write (outfile, &ep.f, sizeof ep.f);
302  if (i != sizeof ep.f)
303    {
304      perror ("ep.f: write");
305      exit (1);
306
307    for (i = 0; i < nsecs; i++)
308      {
309        printf ("Section %d: %s phys %lx  size %lx  file offset %lx\n",
310	        i, esecs [i].s_name, esecs [i].s_paddr,
311	        esecs [i].s_size, esecs [i].s_scnptr);
312      }
313    }
314  fprintf (stderr, "wrote %d byte file header.\n", i);
315
316  i = write (outfile, &ep.a, sizeof ep.a);
317  if (i != sizeof ep.a)
318    {
319      perror ("ep.a: write");
320      exit (1);
321    }
322  fprintf (stderr, "wrote %d byte a.out header.\n", i);
323
324  i = write (outfile, &esecs, sizeof (esecs[0]) * nsecs);
325  if (i != sizeof (esecs[0]) * nsecs)
326    {
327      perror ("esecs: write");
328      exit (1);
329    }
330  fprintf (stderr, "wrote %d bytes of section headers.\n", i);
331
332
333  pad = ((sizeof ep.f + sizeof ep.a + sizeof esecs) & 15);
334  if (pad)
335    {
336      pad = 16 - pad;
337      i = write (outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad);
338      if (i < 0)
339	{
340	  perror ("ipad: write");
341	  exit (1);
342	}
343      fprintf (stderr, "wrote %d byte pad.\n", i);
344    }
345
346  /* Copy the loadable sections.   Zero-fill any gaps less than 64k;
347     complain about any zero-filling, and die if we're asked to zero-fill
348     more than 64k. */
349  for (i = 0; i < ex.e_phnum; i++)
350    {
351      /* Unprocessable sections were handled above, so just verify that
352	 the section can be loaded before copying. */
353      if (ph [i].p_type == Elf_pt_load && ph [i].p_filesz)
354	{
355	  if (cur_vma != ph [i].p_vaddr)
356	    {
357	      unsigned long gap = ph [i].p_vaddr - cur_vma;
358	      char obuf [1024];
359	      if (gap > 65536)
360		{
361		  fprintf (stderr, "Intersegment gap (%ld bytes) too large.\n",
362			   gap);
363		  exit (1);
364		}
365	      fprintf (stderr, "Warning: %ld byte intersegment gap.\n", gap);
366	      memset (obuf, 0, sizeof obuf);
367	      while (gap)
368		{
369		  int count = write (outfile, obuf, (gap > sizeof obuf
370						     ? sizeof obuf : gap));
371		  if (count < 0)
372		    {
373		      fprintf (stderr, "Error writing gap: %s\n",
374			       strerror (errno));
375		      exit (1);
376		    }
377		  gap -= count;
378		}
379	    }
380fprintf (stderr, "writing %d bytes...\n", ph [i].p_filesz);
381	  copy (outfile, infile, ph [i].p_offset, ph [i].p_filesz);
382	  cur_vma = ph [i].p_vaddr + ph [i].p_filesz;
383	}
384    }
385
386
387  /*
388   * Write a page of padding for boot PROMS that read entire pages.
389   * Without this, they may attempt to read past the end of the
390   * data section, incur an error, and refuse to boot.
391   */
392   {
393     char obuf [4096];
394     memset (obuf, 0, sizeof obuf);
395     if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) {
396	fprintf(stderr, "Error writing PROM padding: %s\n",
397		strerror(errno));
398	exit(1);
399     }
400   }
401
402  /* Looks like we won... */
403  exit (0);
404}
405
406void
407copy (out, in, offset, size)
408     int out, in;
409     off_t offset, size;
410{
411  char ibuf [4096];
412  int remaining, cur, count;
413
414  /* Go the the start of the ELF symbol table... */
415  if (lseek (in, offset, SEEK_SET) < 0)
416    {
417      perror ("copy: lseek");
418      exit (1);
419    }
420
421  remaining = size;
422  while (remaining)
423    {
424      cur = remaining;
425      if (cur > sizeof ibuf)
426	cur = sizeof ibuf;
427      remaining -= cur;
428      if ((count = read (in, ibuf, cur)) != cur)
429	{
430	  fprintf (stderr, "copy: read: %s\n",
431		   count ? strerror (errno) : "premature end of file");
432	  exit (1);
433	}
434      if ((count = write (out, ibuf, cur)) != cur)
435	{
436	  perror ("copy: write");
437	  exit (1);
438	}
439    }
440}
441
442/* Combine two segments, which must be contiguous.   If pad is true, it's
443   okay for there to be padding between. */
444void
445combine (base, new, pad)
446     struct sect *base, *new;
447     int pad;
448{
449  if (!base -> len)
450    *base = *new;
451  else if (new -> len)
452    {
453      if (base -> vaddr + base -> len != new -> vaddr)
454	{
455	  if (pad)
456	    base -> len = new -> vaddr - base -> vaddr;
457	  else
458	    {
459	      fprintf (stderr,
460		       "Non-contiguous data can't be converted.\n");
461	      exit (1);
462	    }
463	}
464      base -> len += new -> len;
465    }
466}
467
468int
469phcmp (h1, h2)
470     Elf32_Phdr *h1, *h2;
471{
472  if (h1 -> p_vaddr > h2 -> p_vaddr)
473    return 1;
474  else if (h1 -> p_vaddr < h2 -> p_vaddr)
475    return -1;
476  else
477    return 0;
478}
479
480char *saveRead (int file, off_t offset, off_t len, char *name)
481{
482  char *tmp;
483  int count;
484  off_t off;
485  if ((off = lseek (file, offset, SEEK_SET)) < 0)
486    {
487      fprintf (stderr, "%s: fseek: %s\n", name, strerror (errno));
488      exit (1);
489    }
490  if (!(tmp = (char *)malloc (len)))
491    {
492      fprintf (stderr, "%s: Can't allocate %ld bytes.\n", name, (long)len);
493      exit (1);
494    }
495  count = read (file, tmp, len);
496  if (count != len)
497    {
498      fprintf (stderr, "%s: read: %s.\n",
499	       name, count ? strerror (errno) : "End of file reached");
500      exit (1);
501    }
502  return tmp;
503}
504
505
506/*
507 * Construct  ECOFF section headers for .text, .data, and .bss,
508 *  with empty stubs for .rdata/.sdata/.sbss.  Follow the section ordering
509 * guaranteed by the mipsco toolchain:
510 *  .text, .rdata, .data.,  .sdata, .sbss, .bss.
511 *
512 * The ELF kernel we are translating has no sections corresponding
513 * to .rdata, .sdata and  .sbss.  Output zero-length sections for each,
514 * with no file contents and the correct ELF section flags.
515 * Some DECstation proms will not boot without this.
516 *
517 * XXX scan the ELF sectoin headers and map ELF .rodata to ECOFF .rdata
518 */
519int
520make_ecoff_section_hdrs(ep, esecs)
521	struct ecoff_exechdr *ep;
522	struct ecoff_scnhdr *esecs;
523
524{
525  ep->f.f_nscns = 6;	/* XXX */
526
527  strcpy (esecs [0].s_name, ".text");
528  strcpy (esecs [1].s_name, ".data");
529  strcpy (esecs [2].s_name, ".bss");
530
531  esecs [0].s_paddr = esecs [0].s_vaddr = ep->a.text_start;
532  esecs [1].s_paddr = esecs [1].s_vaddr = ep->a.data_start;
533  esecs [2].s_paddr = esecs [2].s_vaddr = ep->a.bss_start;
534  esecs [0].s_size = ep->a.tsize;
535  esecs [1].s_size = ep->a.dsize;
536  esecs [2].s_size = ep->a.bsize;
537
538  esecs [0].s_scnptr = ECOFF_TXTOFF (ep);
539  esecs [1].s_scnptr = ECOFF_DATOFF (ep);
540#if 0
541  esecs [2].s_scnptr = esecs [1].s_scnptr +
542	  ECOFF_ROUND (esecs [1].s_size, ECOFF_SEGMENT_ALIGNMENT (ep));
543#endif
544
545  esecs [0].s_relptr = esecs [1].s_relptr
546	  = esecs [2].s_relptr = 0;
547  esecs [0].s_lnnoptr = esecs [1].s_lnnoptr
548	  = esecs [2].s_lnnoptr = 0;
549  esecs [0].s_nreloc = esecs [1].s_nreloc = esecs [2].s_nreloc = 0;
550  esecs [0].s_nlnno = esecs [1].s_nlnno = esecs [2].s_nlnno = 0;
551
552  esecs[1].s_flags = 0x100;	/* ECOFF rdata */
553  esecs[3].s_flags = 0x200;	/* ECOFF sdata */
554  esecs[4].s_flags = 0x400;	/* ECOFF sbss */
555
556  /*
557   * Set the symbol-table offset  to point at the end of any sections
558   * we loaded above, so later code can use it to write symbol table info..
559   */
560  ep->f.f_symptr = esecs[1].s_scnptr + esecs[1].s_size;
561
562  return(ep->f.f_nscns);
563}
564