Linux Audio

Check our new training course

Loading...
v3.1
  1/*
  2 * Copyright (c) 1995
  3 *	Ted Lemon (hereinafter referred to as the author)
  4 *
  5 * Redistribution and use in source and binary forms, with or without
  6 * modification, are permitted provided that the following conditions
  7 * are met:
  8 * 1. Redistributions of source code must retain the above copyright
  9 *    notice, this list of conditions and the following disclaimer.
 10 * 2. Redistributions in binary form must reproduce the above copyright
 11 *    notice, this list of conditions and the following disclaimer in the
 12 *    documentation and/or other materials provided with the distribution.
 13 * 3. The name of the author may not be used to endorse or promote products
 14 *    derived from this software without specific prior written permission.
 15 *
 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 26 * SUCH DAMAGE.
 27 */
 28
 29/* elf2ecoff.c
 30
 31   This program converts an elf executable to an ECOFF executable.
 32   No symbol table is retained.   This is useful primarily in building
 33   net-bootable kernels for machines (e.g., DECstation and Alpha) which
 34   only support the ECOFF object file format. */
 35
 36#include <stdio.h>
 37#include <string.h>
 38#include <errno.h>
 39#include <sys/types.h>
 40#include <fcntl.h>
 41#include <unistd.h>
 42#include <elf.h>
 43#include <limits.h>
 44#include <netinet/in.h>
 45#include <stdlib.h>
 46
 47#include "ecoff.h"
 48
 49/*
 50 * Some extra ELF definitions
 51 */
 52#define PT_MIPS_REGINFO 0x70000000	/* Register usage information */
 
 53
 54/* -------------------------------------------------------------------- */
 55
 56struct sect {
 57	unsigned long vaddr;
 58	unsigned long len;
 59};
 60
 61int *symTypeTable;
 62int must_convert_endian;
 63int format_bigendian;
 64
 65static void copy(int out, int in, off_t offset, off_t size)
 66{
 67	char ibuf[4096];
 68	int remaining, cur, count;
 69
 70	/* Go to the start of the ELF symbol table... */
 71	if (lseek(in, offset, SEEK_SET) < 0) {
 72		perror("copy: lseek");
 73		exit(1);
 74	}
 75
 76	remaining = size;
 77	while (remaining) {
 78		cur = remaining;
 79		if (cur > sizeof ibuf)
 80			cur = sizeof ibuf;
 81		remaining -= cur;
 82		if ((count = read(in, ibuf, cur)) != cur) {
 83			fprintf(stderr, "copy: read: %s\n",
 84				count ? strerror(errno) :
 85				"premature end of file");
 86			exit(1);
 87		}
 88		if ((count = write(out, ibuf, cur)) != cur) {
 89			perror("copy: write");
 90			exit(1);
 91		}
 92	}
 93}
 94
 95/*
 96 * Combine two segments, which must be contiguous.   If pad is true, it's
 97 * okay for there to be padding between.
 98 */
 99static void combine(struct sect *base, struct sect *new, int pad)
100{
101	if (!base->len)
102		*base = *new;
103	else if (new->len) {
104		if (base->vaddr + base->len != new->vaddr) {
105			if (pad)
106				base->len = new->vaddr - base->vaddr;
107			else {
108				fprintf(stderr,
109					"Non-contiguous data can't be converted.\n");
110				exit(1);
111			}
112		}
113		base->len += new->len;
114	}
115}
116
117static int phcmp(const void *v1, const void *v2)
118{
119	const Elf32_Phdr *h1 = v1;
120	const Elf32_Phdr *h2 = v2;
121
122	if (h1->p_vaddr > h2->p_vaddr)
123		return 1;
124	else if (h1->p_vaddr < h2->p_vaddr)
125		return -1;
126	else
127		return 0;
128}
129
130static char *saveRead(int file, off_t offset, off_t len, char *name)
131{
132	char *tmp;
133	int count;
134	off_t off;
135	if ((off = lseek(file, offset, SEEK_SET)) < 0) {
136		fprintf(stderr, "%s: fseek: %s\n", name, strerror(errno));
137		exit(1);
138	}
139	if (!(tmp = (char *) malloc(len))) {
140		fprintf(stderr, "%s: Can't allocate %ld bytes.\n", name,
141			len);
142		exit(1);
143	}
144	count = read(file, tmp, len);
145	if (count != len) {
146		fprintf(stderr, "%s: read: %s.\n",
147			name,
148			count ? strerror(errno) : "End of file reached");
149		exit(1);
150	}
151	return tmp;
152}
153
154#define swab16(x) \
155	((unsigned short)( \
156		(((unsigned short)(x) & (unsigned short)0x00ffU) << 8) | \
157		(((unsigned short)(x) & (unsigned short)0xff00U) >> 8) ))
158
159#define swab32(x) \
160	((unsigned int)( \
161		(((unsigned int)(x) & (unsigned int)0x000000ffUL) << 24) | \
162		(((unsigned int)(x) & (unsigned int)0x0000ff00UL) <<  8) | \
163		(((unsigned int)(x) & (unsigned int)0x00ff0000UL) >>  8) | \
164		(((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24) ))
165
166static void convert_elf_hdr(Elf32_Ehdr * e)
167{
168	e->e_type = swab16(e->e_type);
169	e->e_machine = swab16(e->e_machine);
170	e->e_version = swab32(e->e_version);
171	e->e_entry = swab32(e->e_entry);
172	e->e_phoff = swab32(e->e_phoff);
173	e->e_shoff = swab32(e->e_shoff);
174	e->e_flags = swab32(e->e_flags);
175	e->e_ehsize = swab16(e->e_ehsize);
176	e->e_phentsize = swab16(e->e_phentsize);
177	e->e_phnum = swab16(e->e_phnum);
178	e->e_shentsize = swab16(e->e_shentsize);
179	e->e_shnum = swab16(e->e_shnum);
180	e->e_shstrndx = swab16(e->e_shstrndx);
181}
182
183static void convert_elf_phdrs(Elf32_Phdr * p, int num)
184{
185	int i;
186
187	for (i = 0; i < num; i++, p++) {
188		p->p_type = swab32(p->p_type);
189		p->p_offset = swab32(p->p_offset);
190		p->p_vaddr = swab32(p->p_vaddr);
191		p->p_paddr = swab32(p->p_paddr);
192		p->p_filesz = swab32(p->p_filesz);
193		p->p_memsz = swab32(p->p_memsz);
194		p->p_flags = swab32(p->p_flags);
195		p->p_align = swab32(p->p_align);
196	}
197
198}
199
200static void convert_elf_shdrs(Elf32_Shdr * s, int num)
201{
202	int i;
203
204	for (i = 0; i < num; i++, s++) {
205		s->sh_name = swab32(s->sh_name);
206		s->sh_type = swab32(s->sh_type);
207		s->sh_flags = swab32(s->sh_flags);
208		s->sh_addr = swab32(s->sh_addr);
209		s->sh_offset = swab32(s->sh_offset);
210		s->sh_size = swab32(s->sh_size);
211		s->sh_link = swab32(s->sh_link);
212		s->sh_info = swab32(s->sh_info);
213		s->sh_addralign = swab32(s->sh_addralign);
214		s->sh_entsize = swab32(s->sh_entsize);
215	}
216}
217
218static void convert_ecoff_filehdr(struct filehdr *f)
219{
220	f->f_magic = swab16(f->f_magic);
221	f->f_nscns = swab16(f->f_nscns);
222	f->f_timdat = swab32(f->f_timdat);
223	f->f_symptr = swab32(f->f_symptr);
224	f->f_nsyms = swab32(f->f_nsyms);
225	f->f_opthdr = swab16(f->f_opthdr);
226	f->f_flags = swab16(f->f_flags);
227}
228
229static void convert_ecoff_aouthdr(struct aouthdr *a)
230{
231	a->magic = swab16(a->magic);
232	a->vstamp = swab16(a->vstamp);
233	a->tsize = swab32(a->tsize);
234	a->dsize = swab32(a->dsize);
235	a->bsize = swab32(a->bsize);
236	a->entry = swab32(a->entry);
237	a->text_start = swab32(a->text_start);
238	a->data_start = swab32(a->data_start);
239	a->bss_start = swab32(a->bss_start);
240	a->gprmask = swab32(a->gprmask);
241	a->cprmask[0] = swab32(a->cprmask[0]);
242	a->cprmask[1] = swab32(a->cprmask[1]);
243	a->cprmask[2] = swab32(a->cprmask[2]);
244	a->cprmask[3] = swab32(a->cprmask[3]);
245	a->gp_value = swab32(a->gp_value);
246}
247
248static void convert_ecoff_esecs(struct scnhdr *s, int num)
249{
250	int i;
251
252	for (i = 0; i < num; i++, s++) {
253		s->s_paddr = swab32(s->s_paddr);
254		s->s_vaddr = swab32(s->s_vaddr);
255		s->s_size = swab32(s->s_size);
256		s->s_scnptr = swab32(s->s_scnptr);
257		s->s_relptr = swab32(s->s_relptr);
258		s->s_lnnoptr = swab32(s->s_lnnoptr);
259		s->s_nreloc = swab16(s->s_nreloc);
260		s->s_nlnno = swab16(s->s_nlnno);
261		s->s_flags = swab32(s->s_flags);
262	}
263}
264
265int main(int argc, char *argv[])
266{
267	Elf32_Ehdr ex;
268	Elf32_Phdr *ph;
269	Elf32_Shdr *sh;
270	char *shstrtab;
271	int i, pad;
272	struct sect text, data, bss;
273	struct filehdr efh;
274	struct aouthdr eah;
275	struct scnhdr esecs[6];
276	int infile, outfile;
277	unsigned long cur_vma = ULONG_MAX;
278	int addflag = 0;
279	int nosecs;
280
281	text.len = data.len = bss.len = 0;
282	text.vaddr = data.vaddr = bss.vaddr = 0;
283
284	/* Check args... */
285	if (argc < 3 || argc > 4) {
286	      usage:
287		fprintf(stderr,
288			"usage: elf2ecoff <elf executable> <ecoff executable> [-a]\n");
289		exit(1);
290	}
291	if (argc == 4) {
292		if (strcmp(argv[3], "-a"))
293			goto usage;
294		addflag = 1;
295	}
296
297	/* Try the input file... */
298	if ((infile = open(argv[1], O_RDONLY)) < 0) {
299		fprintf(stderr, "Can't open %s for read: %s\n",
300			argv[1], strerror(errno));
301		exit(1);
302	}
303
304	/* Read the header, which is at the beginning of the file... */
305	i = read(infile, &ex, sizeof ex);
306	if (i != sizeof ex) {
307		fprintf(stderr, "ex: %s: %s.\n",
308			argv[1],
309			i ? strerror(errno) : "End of file reached");
310		exit(1);
311	}
312
313	if (ex.e_ident[EI_DATA] == ELFDATA2MSB)
314		format_bigendian = 1;
315
316	if (ntohs(0xaa55) == 0xaa55) {
317		if (!format_bigendian)
318			must_convert_endian = 1;
319	} else {
320		if (format_bigendian)
321			must_convert_endian = 1;
322	}
323	if (must_convert_endian)
324		convert_elf_hdr(&ex);
325
326	/* Read the program headers... */
327	ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff,
328				     ex.e_phnum * sizeof(Elf32_Phdr),
329				     "ph");
330	if (must_convert_endian)
331		convert_elf_phdrs(ph, ex.e_phnum);
332	/* Read the section headers... */
333	sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff,
334				     ex.e_shnum * sizeof(Elf32_Shdr),
335				     "sh");
336	if (must_convert_endian)
337		convert_elf_shdrs(sh, ex.e_shnum);
338	/* Read in the section string table. */
339	shstrtab = saveRead(infile, sh[ex.e_shstrndx].sh_offset,
340			    sh[ex.e_shstrndx].sh_size, "shstrtab");
341
342	/* Figure out if we can cram the program header into an ECOFF
343	   header...  Basically, we can't handle anything but loadable
344	   segments, but we can ignore some kinds of segments.  We can't
345	   handle holes in the address space.  Segments may be out of order,
346	   so we sort them first. */
347
348	qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp);
349
350	for (i = 0; i < ex.e_phnum; i++) {
351		/* Section types we can ignore... */
352		if (ph[i].p_type == PT_NULL || ph[i].p_type == PT_NOTE ||
353		    ph[i].p_type == PT_PHDR
354		    || ph[i].p_type == PT_MIPS_REGINFO)
 
 
 
355			continue;
356		/* Section types we can't handle... */
357		else if (ph[i].p_type != PT_LOAD) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358			fprintf(stderr,
359				"Program header %d type %d can't be converted.\n",
360				ex.e_phnum, ph[i].p_type);
361			exit(1);
362		}
363		/* Writable (data) segment? */
364		if (ph[i].p_flags & PF_W) {
365			struct sect ndata, nbss;
366
367			ndata.vaddr = ph[i].p_vaddr;
368			ndata.len = ph[i].p_filesz;
369			nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz;
370			nbss.len = ph[i].p_memsz - ph[i].p_filesz;
371
372			combine(&data, &ndata, 0);
373			combine(&bss, &nbss, 1);
374		} else {
375			struct sect ntxt;
376
377			ntxt.vaddr = ph[i].p_vaddr;
378			ntxt.len = ph[i].p_filesz;
379
380			combine(&text, &ntxt, 0);
381		}
382		/* Remember the lowest segment start address. */
383		if (ph[i].p_vaddr < cur_vma)
384			cur_vma = ph[i].p_vaddr;
385	}
386
387	/* Sections must be in order to be converted... */
388	if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
389	    text.vaddr + text.len > data.vaddr
390	    || data.vaddr + data.len > bss.vaddr) {
391		fprintf(stderr,
392			"Sections ordering prevents a.out conversion.\n");
393		exit(1);
394	}
395
396	/* If there's a data section but no text section, then the loader
397	   combined everything into one section.   That needs to be the
398	   text section, so just make the data section zero length following
399	   text. */
400	if (data.len && !text.len) {
401		text = data;
402		data.vaddr = text.vaddr + text.len;
403		data.len = 0;
404	}
405
406	/* If there is a gap between text and data, we'll fill it when we copy
407	   the data, so update the length of the text segment as represented in
408	   a.out to reflect that, since a.out doesn't allow gaps in the program
409	   address space. */
410	if (text.vaddr + text.len < data.vaddr)
411		text.len = data.vaddr - text.vaddr;
412
413	/* We now have enough information to cons up an a.out header... */
414	eah.magic = OMAGIC;
415	eah.vstamp = 200;
416	eah.tsize = text.len;
417	eah.dsize = data.len;
418	eah.bsize = bss.len;
419	eah.entry = ex.e_entry;
420	eah.text_start = text.vaddr;
421	eah.data_start = data.vaddr;
422	eah.bss_start = bss.vaddr;
423	eah.gprmask = 0xf3fffffe;
424	memset(&eah.cprmask, '\0', sizeof eah.cprmask);
425	eah.gp_value = 0;	/* unused. */
426
427	if (format_bigendian)
428		efh.f_magic = MIPSEBMAGIC;
429	else
430		efh.f_magic = MIPSELMAGIC;
431	if (addflag)
432		nosecs = 6;
433	else
434		nosecs = 3;
435	efh.f_nscns = nosecs;
436	efh.f_timdat = 0;	/* bogus */
437	efh.f_symptr = 0;
438	efh.f_nsyms = 0;
439	efh.f_opthdr = sizeof eah;
440	efh.f_flags = 0x100f;	/* Stripped, not sharable. */
441
442	memset(esecs, 0, sizeof esecs);
443	strcpy(esecs[0].s_name, ".text");
444	strcpy(esecs[1].s_name, ".data");
445	strcpy(esecs[2].s_name, ".bss");
446	if (addflag) {
447		strcpy(esecs[3].s_name, ".rdata");
448		strcpy(esecs[4].s_name, ".sdata");
449		strcpy(esecs[5].s_name, ".sbss");
450	}
451	esecs[0].s_paddr = esecs[0].s_vaddr = eah.text_start;
452	esecs[1].s_paddr = esecs[1].s_vaddr = eah.data_start;
453	esecs[2].s_paddr = esecs[2].s_vaddr = eah.bss_start;
454	if (addflag) {
455		esecs[3].s_paddr = esecs[3].s_vaddr = 0;
456		esecs[4].s_paddr = esecs[4].s_vaddr = 0;
457		esecs[5].s_paddr = esecs[5].s_vaddr = 0;
458	}
459	esecs[0].s_size = eah.tsize;
460	esecs[1].s_size = eah.dsize;
461	esecs[2].s_size = eah.bsize;
462	if (addflag) {
463		esecs[3].s_size = 0;
464		esecs[4].s_size = 0;
465		esecs[5].s_size = 0;
466	}
467	esecs[0].s_scnptr = N_TXTOFF(efh, eah);
468	esecs[1].s_scnptr = N_DATOFF(efh, eah);
469#define ECOFF_SEGMENT_ALIGNMENT(a) 0x10
470#define ECOFF_ROUND(s, a) (((s)+(a)-1)&~((a)-1))
471	esecs[2].s_scnptr = esecs[1].s_scnptr +
472	    ECOFF_ROUND(esecs[1].s_size, ECOFF_SEGMENT_ALIGNMENT(&eah));
473	if (addflag) {
474		esecs[3].s_scnptr = 0;
475		esecs[4].s_scnptr = 0;
476		esecs[5].s_scnptr = 0;
477	}
478	esecs[0].s_relptr = esecs[1].s_relptr = esecs[2].s_relptr = 0;
479	esecs[0].s_lnnoptr = esecs[1].s_lnnoptr = esecs[2].s_lnnoptr = 0;
480	esecs[0].s_nreloc = esecs[1].s_nreloc = esecs[2].s_nreloc = 0;
481	esecs[0].s_nlnno = esecs[1].s_nlnno = esecs[2].s_nlnno = 0;
482	if (addflag) {
483		esecs[3].s_relptr = esecs[4].s_relptr
484		    = esecs[5].s_relptr = 0;
485		esecs[3].s_lnnoptr = esecs[4].s_lnnoptr
486		    = esecs[5].s_lnnoptr = 0;
487		esecs[3].s_nreloc = esecs[4].s_nreloc = esecs[5].s_nreloc =
488		    0;
489		esecs[3].s_nlnno = esecs[4].s_nlnno = esecs[5].s_nlnno = 0;
490	}
491	esecs[0].s_flags = 0x20;
492	esecs[1].s_flags = 0x40;
493	esecs[2].s_flags = 0x82;
494	if (addflag) {
495		esecs[3].s_flags = 0x100;
496		esecs[4].s_flags = 0x200;
497		esecs[5].s_flags = 0x400;
498	}
499
500	/* Make the output file... */
501	if ((outfile = open(argv[2], O_WRONLY | O_CREAT, 0777)) < 0) {
502		fprintf(stderr, "Unable to create %s: %s\n", argv[2],
503			strerror(errno));
504		exit(1);
505	}
506
507	if (must_convert_endian)
508		convert_ecoff_filehdr(&efh);
509	/* Write the headers... */
510	i = write(outfile, &efh, sizeof efh);
511	if (i != sizeof efh) {
512		perror("efh: write");
513		exit(1);
514
515		for (i = 0; i < nosecs; i++) {
516			printf
517			    ("Section %d: %s phys %lx  size %lx  file offset %lx\n",
518			     i, esecs[i].s_name, esecs[i].s_paddr,
519			     esecs[i].s_size, esecs[i].s_scnptr);
520		}
521	}
522	fprintf(stderr, "wrote %d byte file header.\n", i);
523
524	if (must_convert_endian)
525		convert_ecoff_aouthdr(&eah);
526	i = write(outfile, &eah, sizeof eah);
527	if (i != sizeof eah) {
528		perror("eah: write");
529		exit(1);
530	}
531	fprintf(stderr, "wrote %d byte a.out header.\n", i);
532
533	if (must_convert_endian)
534		convert_ecoff_esecs(&esecs[0], nosecs);
535	i = write(outfile, &esecs, nosecs * sizeof(struct scnhdr));
536	if (i != nosecs * sizeof(struct scnhdr)) {
537		perror("esecs: write");
538		exit(1);
539	}
540	fprintf(stderr, "wrote %d bytes of section headers.\n", i);
541
542	pad = (sizeof(efh) + sizeof(eah) + nosecs * sizeof(struct scnhdr)) & 15;
543	if (pad) {
544		pad = 16 - pad;
545		i = write(outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad);
546		if (i < 0) {
547			perror("ipad: write");
548			exit(1);
549		}
550		fprintf(stderr, "wrote %d byte pad.\n", i);
551	}
552
553	/*
554	 * Copy the loadable sections.   Zero-fill any gaps less than 64k;
555	 * complain about any zero-filling, and die if we're asked to zero-fill
556	 * more than 64k.
557	 */
558	for (i = 0; i < ex.e_phnum; i++) {
559		/* Unprocessable sections were handled above, so just verify that
560		   the section can be loaded before copying. */
561		if (ph[i].p_type == PT_LOAD && ph[i].p_filesz) {
562			if (cur_vma != ph[i].p_vaddr) {
563				unsigned long gap =
564				    ph[i].p_vaddr - cur_vma;
565				char obuf[1024];
566				if (gap > 65536) {
567					fprintf(stderr,
568						"Intersegment gap (%ld bytes) too large.\n",
569						gap);
570					exit(1);
571				}
572				fprintf(stderr,
573					"Warning: %ld byte intersegment gap.\n",
574					gap);
575				memset(obuf, 0, sizeof obuf);
576				while (gap) {
577					int count =
578					    write(outfile, obuf,
579						  (gap >
580						   sizeof obuf ? sizeof
581						   obuf : gap));
582					if (count < 0) {
583						fprintf(stderr,
584							"Error writing gap: %s\n",
585							strerror(errno));
586						exit(1);
587					}
588					gap -= count;
589				}
590			}
591			fprintf(stderr, "writing %d bytes...\n",
592				ph[i].p_filesz);
593			copy(outfile, infile, ph[i].p_offset,
594			     ph[i].p_filesz);
595			cur_vma = ph[i].p_vaddr + ph[i].p_filesz;
596		}
597	}
598
599	/*
600	 * Write a page of padding for boot PROMS that read entire pages.
601	 * Without this, they may attempt to read past the end of the
602	 * data section, incur an error, and refuse to boot.
603	 */
604	{
605		char obuf[4096];
606		memset(obuf, 0, sizeof obuf);
607		if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) {
608			fprintf(stderr, "Error writing PROM padding: %s\n",
609				strerror(errno));
610			exit(1);
611		}
612	}
613
614	/* Looks like we won... */
615	exit(0);
616}
v4.17
  1/*
  2 * Copyright (c) 1995
  3 *	Ted Lemon (hereinafter referred to as the author)
  4 *
  5 * Redistribution and use in source and binary forms, with or without
  6 * modification, are permitted provided that the following conditions
  7 * are met:
  8 * 1. Redistributions of source code must retain the above copyright
  9 *    notice, this list of conditions and the following disclaimer.
 10 * 2. Redistributions in binary form must reproduce the above copyright
 11 *    notice, this list of conditions and the following disclaimer in the
 12 *    documentation and/or other materials provided with the distribution.
 13 * 3. The name of the author may not be used to endorse or promote products
 14 *    derived from this software without specific prior written permission.
 15 *
 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 26 * SUCH DAMAGE.
 27 */
 28
 29/* elf2ecoff.c
 30
 31   This program converts an elf executable to an ECOFF executable.
 32   No symbol table is retained.	  This is useful primarily in building
 33   net-bootable kernels for machines (e.g., DECstation and Alpha) which
 34   only support the ECOFF object file format. */
 35
 36#include <stdio.h>
 37#include <string.h>
 38#include <errno.h>
 39#include <sys/types.h>
 40#include <fcntl.h>
 41#include <unistd.h>
 42#include <elf.h>
 43#include <limits.h>
 44#include <netinet/in.h>
 45#include <stdlib.h>
 46
 47#include "ecoff.h"
 48
 49/*
 50 * Some extra ELF definitions
 51 */
 52#define PT_MIPS_REGINFO 	0x70000000	/* Register usage information */
 53#define PT_MIPS_ABIFLAGS	0x70000003	/* Records ABI related flags  */
 54
 55/* -------------------------------------------------------------------- */
 56
 57struct sect {
 58	unsigned long vaddr;
 59	unsigned long len;
 60};
 61
 62int *symTypeTable;
 63int must_convert_endian;
 64int format_bigendian;
 65
 66static void copy(int out, int in, off_t offset, off_t size)
 67{
 68	char ibuf[4096];
 69	int remaining, cur, count;
 70
 71	/* Go to the start of the ELF symbol table... */
 72	if (lseek(in, offset, SEEK_SET) < 0) {
 73		perror("copy: lseek");
 74		exit(1);
 75	}
 76
 77	remaining = size;
 78	while (remaining) {
 79		cur = remaining;
 80		if (cur > sizeof ibuf)
 81			cur = sizeof ibuf;
 82		remaining -= cur;
 83		if ((count = read(in, ibuf, cur)) != cur) {
 84			fprintf(stderr, "copy: read: %s\n",
 85				count ? strerror(errno) :
 86				"premature end of file");
 87			exit(1);
 88		}
 89		if ((count = write(out, ibuf, cur)) != cur) {
 90			perror("copy: write");
 91			exit(1);
 92		}
 93	}
 94}
 95
 96/*
 97 * Combine two segments, which must be contiguous.   If pad is true, it's
 98 * okay for there to be padding between.
 99 */
100static void combine(struct sect *base, struct sect *new, int pad)
101{
102	if (!base->len)
103		*base = *new;
104	else if (new->len) {
105		if (base->vaddr + base->len != new->vaddr) {
106			if (pad)
107				base->len = new->vaddr - base->vaddr;
108			else {
109				fprintf(stderr,
110					"Non-contiguous data can't be converted.\n");
111				exit(1);
112			}
113		}
114		base->len += new->len;
115	}
116}
117
118static int phcmp(const void *v1, const void *v2)
119{
120	const Elf32_Phdr *h1 = v1;
121	const Elf32_Phdr *h2 = v2;
122
123	if (h1->p_vaddr > h2->p_vaddr)
124		return 1;
125	else if (h1->p_vaddr < h2->p_vaddr)
126		return -1;
127	else
128		return 0;
129}
130
131static char *saveRead(int file, off_t offset, off_t len, char *name)
132{
133	char *tmp;
134	int count;
135	off_t off;
136	if ((off = lseek(file, offset, SEEK_SET)) < 0) {
137		fprintf(stderr, "%s: fseek: %s\n", name, strerror(errno));
138		exit(1);
139	}
140	if (!(tmp = (char *) malloc(len))) {
141		fprintf(stderr, "%s: Can't allocate %ld bytes.\n", name,
142			len);
143		exit(1);
144	}
145	count = read(file, tmp, len);
146	if (count != len) {
147		fprintf(stderr, "%s: read: %s.\n",
148			name,
149			count ? strerror(errno) : "End of file reached");
150		exit(1);
151	}
152	return tmp;
153}
154
155#define swab16(x) \
156	((unsigned short)( \
157		(((unsigned short)(x) & (unsigned short)0x00ffU) << 8) | \
158		(((unsigned short)(x) & (unsigned short)0xff00U) >> 8) ))
159
160#define swab32(x) \
161	((unsigned int)( \
162		(((unsigned int)(x) & (unsigned int)0x000000ffUL) << 24) | \
163		(((unsigned int)(x) & (unsigned int)0x0000ff00UL) <<  8) | \
164		(((unsigned int)(x) & (unsigned int)0x00ff0000UL) >>  8) | \
165		(((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24) ))
166
167static void convert_elf_hdr(Elf32_Ehdr * e)
168{
169	e->e_type = swab16(e->e_type);
170	e->e_machine = swab16(e->e_machine);
171	e->e_version = swab32(e->e_version);
172	e->e_entry = swab32(e->e_entry);
173	e->e_phoff = swab32(e->e_phoff);
174	e->e_shoff = swab32(e->e_shoff);
175	e->e_flags = swab32(e->e_flags);
176	e->e_ehsize = swab16(e->e_ehsize);
177	e->e_phentsize = swab16(e->e_phentsize);
178	e->e_phnum = swab16(e->e_phnum);
179	e->e_shentsize = swab16(e->e_shentsize);
180	e->e_shnum = swab16(e->e_shnum);
181	e->e_shstrndx = swab16(e->e_shstrndx);
182}
183
184static void convert_elf_phdrs(Elf32_Phdr * p, int num)
185{
186	int i;
187
188	for (i = 0; i < num; i++, p++) {
189		p->p_type = swab32(p->p_type);
190		p->p_offset = swab32(p->p_offset);
191		p->p_vaddr = swab32(p->p_vaddr);
192		p->p_paddr = swab32(p->p_paddr);
193		p->p_filesz = swab32(p->p_filesz);
194		p->p_memsz = swab32(p->p_memsz);
195		p->p_flags = swab32(p->p_flags);
196		p->p_align = swab32(p->p_align);
197	}
198
199}
200
201static void convert_elf_shdrs(Elf32_Shdr * s, int num)
202{
203	int i;
204
205	for (i = 0; i < num; i++, s++) {
206		s->sh_name = swab32(s->sh_name);
207		s->sh_type = swab32(s->sh_type);
208		s->sh_flags = swab32(s->sh_flags);
209		s->sh_addr = swab32(s->sh_addr);
210		s->sh_offset = swab32(s->sh_offset);
211		s->sh_size = swab32(s->sh_size);
212		s->sh_link = swab32(s->sh_link);
213		s->sh_info = swab32(s->sh_info);
214		s->sh_addralign = swab32(s->sh_addralign);
215		s->sh_entsize = swab32(s->sh_entsize);
216	}
217}
218
219static void convert_ecoff_filehdr(struct filehdr *f)
220{
221	f->f_magic = swab16(f->f_magic);
222	f->f_nscns = swab16(f->f_nscns);
223	f->f_timdat = swab32(f->f_timdat);
224	f->f_symptr = swab32(f->f_symptr);
225	f->f_nsyms = swab32(f->f_nsyms);
226	f->f_opthdr = swab16(f->f_opthdr);
227	f->f_flags = swab16(f->f_flags);
228}
229
230static void convert_ecoff_aouthdr(struct aouthdr *a)
231{
232	a->magic = swab16(a->magic);
233	a->vstamp = swab16(a->vstamp);
234	a->tsize = swab32(a->tsize);
235	a->dsize = swab32(a->dsize);
236	a->bsize = swab32(a->bsize);
237	a->entry = swab32(a->entry);
238	a->text_start = swab32(a->text_start);
239	a->data_start = swab32(a->data_start);
240	a->bss_start = swab32(a->bss_start);
241	a->gprmask = swab32(a->gprmask);
242	a->cprmask[0] = swab32(a->cprmask[0]);
243	a->cprmask[1] = swab32(a->cprmask[1]);
244	a->cprmask[2] = swab32(a->cprmask[2]);
245	a->cprmask[3] = swab32(a->cprmask[3]);
246	a->gp_value = swab32(a->gp_value);
247}
248
249static void convert_ecoff_esecs(struct scnhdr *s, int num)
250{
251	int i;
252
253	for (i = 0; i < num; i++, s++) {
254		s->s_paddr = swab32(s->s_paddr);
255		s->s_vaddr = swab32(s->s_vaddr);
256		s->s_size = swab32(s->s_size);
257		s->s_scnptr = swab32(s->s_scnptr);
258		s->s_relptr = swab32(s->s_relptr);
259		s->s_lnnoptr = swab32(s->s_lnnoptr);
260		s->s_nreloc = swab16(s->s_nreloc);
261		s->s_nlnno = swab16(s->s_nlnno);
262		s->s_flags = swab32(s->s_flags);
263	}
264}
265
266int main(int argc, char *argv[])
267{
268	Elf32_Ehdr ex;
269	Elf32_Phdr *ph;
270	Elf32_Shdr *sh;
 
271	int i, pad;
272	struct sect text, data, bss;
273	struct filehdr efh;
274	struct aouthdr eah;
275	struct scnhdr esecs[6];
276	int infile, outfile;
277	unsigned long cur_vma = ULONG_MAX;
278	int addflag = 0;
279	int nosecs;
280
281	text.len = data.len = bss.len = 0;
282	text.vaddr = data.vaddr = bss.vaddr = 0;
283
284	/* Check args... */
285	if (argc < 3 || argc > 4) {
286	      usage:
287		fprintf(stderr,
288			"usage: elf2ecoff <elf executable> <ecoff executable> [-a]\n");
289		exit(1);
290	}
291	if (argc == 4) {
292		if (strcmp(argv[3], "-a"))
293			goto usage;
294		addflag = 1;
295	}
296
297	/* Try the input file... */
298	if ((infile = open(argv[1], O_RDONLY)) < 0) {
299		fprintf(stderr, "Can't open %s for read: %s\n",
300			argv[1], strerror(errno));
301		exit(1);
302	}
303
304	/* Read the header, which is at the beginning of the file... */
305	i = read(infile, &ex, sizeof ex);
306	if (i != sizeof ex) {
307		fprintf(stderr, "ex: %s: %s.\n",
308			argv[1],
309			i ? strerror(errno) : "End of file reached");
310		exit(1);
311	}
312
313	if (ex.e_ident[EI_DATA] == ELFDATA2MSB)
314		format_bigendian = 1;
315
316	if (ntohs(0xaa55) == 0xaa55) {
317		if (!format_bigendian)
318			must_convert_endian = 1;
319	} else {
320		if (format_bigendian)
321			must_convert_endian = 1;
322	}
323	if (must_convert_endian)
324		convert_elf_hdr(&ex);
325
326	/* Read the program headers... */
327	ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff,
328				     ex.e_phnum * sizeof(Elf32_Phdr),
329				     "ph");
330	if (must_convert_endian)
331		convert_elf_phdrs(ph, ex.e_phnum);
332	/* Read the section headers... */
333	sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff,
334				     ex.e_shnum * sizeof(Elf32_Shdr),
335				     "sh");
336	if (must_convert_endian)
337		convert_elf_shdrs(sh, ex.e_shnum);
 
 
 
338
339	/* Figure out if we can cram the program header into an ECOFF
340	   header...  Basically, we can't handle anything but loadable
341	   segments, but we can ignore some kinds of segments.	We can't
342	   handle holes in the address space.  Segments may be out of order,
343	   so we sort them first. */
344
345	qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp);
346
347	for (i = 0; i < ex.e_phnum; i++) {
348		/* Section types we can ignore... */
349		switch (ph[i].p_type) {
350		case PT_NULL:
351		case PT_NOTE:
352		case PT_PHDR:
353		case PT_MIPS_REGINFO:
354		case PT_MIPS_ABIFLAGS:
355			continue;
356
357		case PT_LOAD:
358			/* Writable (data) segment? */
359			if (ph[i].p_flags & PF_W) {
360				struct sect ndata, nbss;
361
362				ndata.vaddr = ph[i].p_vaddr;
363				ndata.len = ph[i].p_filesz;
364				nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz;
365				nbss.len = ph[i].p_memsz - ph[i].p_filesz;
366
367				combine(&data, &ndata, 0);
368				combine(&bss, &nbss, 1);
369			} else {
370				struct sect ntxt;
371
372				ntxt.vaddr = ph[i].p_vaddr;
373				ntxt.len = ph[i].p_filesz;
374
375				combine(&text, &ntxt, 0);
376			}
377			/* Remember the lowest segment start address. */
378			if (ph[i].p_vaddr < cur_vma)
379				cur_vma = ph[i].p_vaddr;
380			break;
381
382		default:
383			/* Section types we can't handle... */
384			fprintf(stderr,
385				"Program header %d type %d can't be converted.\n",
386				ex.e_phnum, ph[i].p_type);
387			exit(1);
388		}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389	}
390
391	/* Sections must be in order to be converted... */
392	if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
393	    text.vaddr + text.len > data.vaddr
394	    || data.vaddr + data.len > bss.vaddr) {
395		fprintf(stderr,
396			"Sections ordering prevents a.out conversion.\n");
397		exit(1);
398	}
399
400	/* If there's a data section but no text section, then the loader
401	   combined everything into one section.   That needs to be the
402	   text section, so just make the data section zero length following
403	   text. */
404	if (data.len && !text.len) {
405		text = data;
406		data.vaddr = text.vaddr + text.len;
407		data.len = 0;
408	}
409
410	/* If there is a gap between text and data, we'll fill it when we copy
411	   the data, so update the length of the text segment as represented in
412	   a.out to reflect that, since a.out doesn't allow gaps in the program
413	   address space. */
414	if (text.vaddr + text.len < data.vaddr)
415		text.len = data.vaddr - text.vaddr;
416
417	/* We now have enough information to cons up an a.out header... */
418	eah.magic = OMAGIC;
419	eah.vstamp = 200;
420	eah.tsize = text.len;
421	eah.dsize = data.len;
422	eah.bsize = bss.len;
423	eah.entry = ex.e_entry;
424	eah.text_start = text.vaddr;
425	eah.data_start = data.vaddr;
426	eah.bss_start = bss.vaddr;
427	eah.gprmask = 0xf3fffffe;
428	memset(&eah.cprmask, '\0', sizeof eah.cprmask);
429	eah.gp_value = 0;	/* unused. */
430
431	if (format_bigendian)
432		efh.f_magic = MIPSEBMAGIC;
433	else
434		efh.f_magic = MIPSELMAGIC;
435	if (addflag)
436		nosecs = 6;
437	else
438		nosecs = 3;
439	efh.f_nscns = nosecs;
440	efh.f_timdat = 0;	/* bogus */
441	efh.f_symptr = 0;
442	efh.f_nsyms = 0;
443	efh.f_opthdr = sizeof eah;
444	efh.f_flags = 0x100f;	/* Stripped, not sharable. */
445
446	memset(esecs, 0, sizeof esecs);
447	strcpy(esecs[0].s_name, ".text");
448	strcpy(esecs[1].s_name, ".data");
449	strcpy(esecs[2].s_name, ".bss");
450	if (addflag) {
451		strcpy(esecs[3].s_name, ".rdata");
452		strcpy(esecs[4].s_name, ".sdata");
453		strcpy(esecs[5].s_name, ".sbss");
454	}
455	esecs[0].s_paddr = esecs[0].s_vaddr = eah.text_start;
456	esecs[1].s_paddr = esecs[1].s_vaddr = eah.data_start;
457	esecs[2].s_paddr = esecs[2].s_vaddr = eah.bss_start;
458	if (addflag) {
459		esecs[3].s_paddr = esecs[3].s_vaddr = 0;
460		esecs[4].s_paddr = esecs[4].s_vaddr = 0;
461		esecs[5].s_paddr = esecs[5].s_vaddr = 0;
462	}
463	esecs[0].s_size = eah.tsize;
464	esecs[1].s_size = eah.dsize;
465	esecs[2].s_size = eah.bsize;
466	if (addflag) {
467		esecs[3].s_size = 0;
468		esecs[4].s_size = 0;
469		esecs[5].s_size = 0;
470	}
471	esecs[0].s_scnptr = N_TXTOFF(efh, eah);
472	esecs[1].s_scnptr = N_DATOFF(efh, eah);
473#define ECOFF_SEGMENT_ALIGNMENT(a) 0x10
474#define ECOFF_ROUND(s, a) (((s)+(a)-1)&~((a)-1))
475	esecs[2].s_scnptr = esecs[1].s_scnptr +
476	    ECOFF_ROUND(esecs[1].s_size, ECOFF_SEGMENT_ALIGNMENT(&eah));
477	if (addflag) {
478		esecs[3].s_scnptr = 0;
479		esecs[4].s_scnptr = 0;
480		esecs[5].s_scnptr = 0;
481	}
482	esecs[0].s_relptr = esecs[1].s_relptr = esecs[2].s_relptr = 0;
483	esecs[0].s_lnnoptr = esecs[1].s_lnnoptr = esecs[2].s_lnnoptr = 0;
484	esecs[0].s_nreloc = esecs[1].s_nreloc = esecs[2].s_nreloc = 0;
485	esecs[0].s_nlnno = esecs[1].s_nlnno = esecs[2].s_nlnno = 0;
486	if (addflag) {
487		esecs[3].s_relptr = esecs[4].s_relptr
488		    = esecs[5].s_relptr = 0;
489		esecs[3].s_lnnoptr = esecs[4].s_lnnoptr
490		    = esecs[5].s_lnnoptr = 0;
491		esecs[3].s_nreloc = esecs[4].s_nreloc = esecs[5].s_nreloc =
492		    0;
493		esecs[3].s_nlnno = esecs[4].s_nlnno = esecs[5].s_nlnno = 0;
494	}
495	esecs[0].s_flags = 0x20;
496	esecs[1].s_flags = 0x40;
497	esecs[2].s_flags = 0x82;
498	if (addflag) {
499		esecs[3].s_flags = 0x100;
500		esecs[4].s_flags = 0x200;
501		esecs[5].s_flags = 0x400;
502	}
503
504	/* Make the output file... */
505	if ((outfile = open(argv[2], O_WRONLY | O_CREAT, 0777)) < 0) {
506		fprintf(stderr, "Unable to create %s: %s\n", argv[2],
507			strerror(errno));
508		exit(1);
509	}
510
511	if (must_convert_endian)
512		convert_ecoff_filehdr(&efh);
513	/* Write the headers... */
514	i = write(outfile, &efh, sizeof efh);
515	if (i != sizeof efh) {
516		perror("efh: write");
517		exit(1);
518
519		for (i = 0; i < nosecs; i++) {
520			printf
521			    ("Section %d: %s phys %lx  size %lx	 file offset %lx\n",
522			     i, esecs[i].s_name, esecs[i].s_paddr,
523			     esecs[i].s_size, esecs[i].s_scnptr);
524		}
525	}
526	fprintf(stderr, "wrote %d byte file header.\n", i);
527
528	if (must_convert_endian)
529		convert_ecoff_aouthdr(&eah);
530	i = write(outfile, &eah, sizeof eah);
531	if (i != sizeof eah) {
532		perror("eah: write");
533		exit(1);
534	}
535	fprintf(stderr, "wrote %d byte a.out header.\n", i);
536
537	if (must_convert_endian)
538		convert_ecoff_esecs(&esecs[0], nosecs);
539	i = write(outfile, &esecs, nosecs * sizeof(struct scnhdr));
540	if (i != nosecs * sizeof(struct scnhdr)) {
541		perror("esecs: write");
542		exit(1);
543	}
544	fprintf(stderr, "wrote %d bytes of section headers.\n", i);
545
546	pad = (sizeof(efh) + sizeof(eah) + nosecs * sizeof(struct scnhdr)) & 15;
547	if (pad) {
548		pad = 16 - pad;
549		i = write(outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad);
550		if (i < 0) {
551			perror("ipad: write");
552			exit(1);
553		}
554		fprintf(stderr, "wrote %d byte pad.\n", i);
555	}
556
557	/*
558	 * Copy the loadable sections.	 Zero-fill any gaps less than 64k;
559	 * complain about any zero-filling, and die if we're asked to zero-fill
560	 * more than 64k.
561	 */
562	for (i = 0; i < ex.e_phnum; i++) {
563		/* Unprocessable sections were handled above, so just verify that
564		   the section can be loaded before copying. */
565		if (ph[i].p_type == PT_LOAD && ph[i].p_filesz) {
566			if (cur_vma != ph[i].p_vaddr) {
567				unsigned long gap =
568				    ph[i].p_vaddr - cur_vma;
569				char obuf[1024];
570				if (gap > 65536) {
571					fprintf(stderr,
572						"Intersegment gap (%ld bytes) too large.\n",
573						gap);
574					exit(1);
575				}
576				fprintf(stderr,
577					"Warning: %ld byte intersegment gap.\n",
578					gap);
579				memset(obuf, 0, sizeof obuf);
580				while (gap) {
581					int count =
582					    write(outfile, obuf,
583						  (gap >
584						   sizeof obuf ? sizeof
585						   obuf : gap));
586					if (count < 0) {
587						fprintf(stderr,
588							"Error writing gap: %s\n",
589							strerror(errno));
590						exit(1);
591					}
592					gap -= count;
593				}
594			}
595			fprintf(stderr, "writing %d bytes...\n",
596				ph[i].p_filesz);
597			copy(outfile, infile, ph[i].p_offset,
598			     ph[i].p_filesz);
599			cur_vma = ph[i].p_vaddr + ph[i].p_filesz;
600		}
601	}
602
603	/*
604	 * Write a page of padding for boot PROMS that read entire pages.
605	 * Without this, they may attempt to read past the end of the
606	 * data section, incur an error, and refuse to boot.
607	 */
608	{
609		char obuf[4096];
610		memset(obuf, 0, sizeof obuf);
611		if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) {
612			fprintf(stderr, "Error writing PROM padding: %s\n",
613				strerror(errno));
614			exit(1);
615		}
616	}
617
618	/* Looks like we won... */
619	exit(0);
620}