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 = 0;
63int format_bigendian = 0;
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", argv[1],
308			i ? strerror(errno) : "End of file reached");
309		exit(1);
310	}
311
312	if (ex.e_ident[EI_DATA] == ELFDATA2MSB)
313		format_bigendian = 1;
314
315	if (ntohs(0xaa55) == 0xaa55) {
316		if (!format_bigendian)
317			must_convert_endian = 1;
318	} else {
319		if (format_bigendian)
320			must_convert_endian = 1;
321	}
322	if (must_convert_endian)
323		convert_elf_hdr(&ex);
324
325	/* Read the program headers... */
326	ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff,
327				     ex.e_phnum * sizeof(Elf32_Phdr), "ph");
328	if (must_convert_endian)
329		convert_elf_phdrs(ph, ex.e_phnum);
330	/* Read the section headers... */
331	sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff,
332				     ex.e_shnum * sizeof(Elf32_Shdr),
333				     "sh");
334	if (must_convert_endian)
335		convert_elf_shdrs(sh, ex.e_shnum);
336	/* Read in the section string table. */
337	shstrtab = saveRead(infile, sh[ex.e_shstrndx].sh_offset,
338			    sh[ex.e_shstrndx].sh_size, "shstrtab");
339
340	/* Figure out if we can cram the program header into an ECOFF
341	   header...  Basically, we can't handle anything but loadable
342	   segments, but we can ignore some kinds of segments.  We can't
343	   handle holes in the address space.  Segments may be out of order,
344	   so we sort them first. */
345
346	qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp);
347
348	for (i = 0; i < ex.e_phnum; i++) {
349		/* Section types we can ignore... */
350		if (ph[i].p_type == PT_NULL || ph[i].p_type == PT_NOTE ||
351		    ph[i].p_type == PT_PHDR
352		    || ph[i].p_type == PT_MIPS_REGINFO)
353			continue;
354		/* Section types we can't handle... */
355		else if (ph[i].p_type != PT_LOAD) {
356			fprintf(stderr,
357				"Program header %d type %d can't be converted.\n",
358				ex.e_phnum, ph[i].p_type);
359			exit(1);
360		}
361		/* Writable (data) segment? */
362		if (ph[i].p_flags & PF_W) {
363			struct sect ndata, nbss;
364
365			ndata.vaddr = ph[i].p_vaddr;
366			ndata.len = ph[i].p_filesz;
367			nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz;
368			nbss.len = ph[i].p_memsz - ph[i].p_filesz;
369
370			combine(&data, &ndata, 0);
371			combine(&bss, &nbss, 1);
372		} else {
373			struct sect ntxt;
374
375			ntxt.vaddr = ph[i].p_vaddr;
376			ntxt.len = ph[i].p_filesz;
377
378			combine(&text, &ntxt, 0);
379		}
380		/* Remember the lowest segment start address. */
381		if (ph[i].p_vaddr < cur_vma)
382			cur_vma = ph[i].p_vaddr;
383	}
384
385	/* Sections must be in order to be converted... */
386	if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
387	    text.vaddr + text.len > data.vaddr
388	    || data.vaddr + data.len > bss.vaddr) {
389		fprintf(stderr,
390			"Sections ordering prevents a.out conversion.\n");
391		exit(1);
392	}
393
394	/*
395	 * If there's a data section but no text section, then the loader
396	 * combined everything into one section.   That needs to be the text
397	 * section, so just make the data section zero length following text.
398	 */
399	if (data.len && !text.len) {
400		text = data;
401		data.vaddr = text.vaddr + text.len;
402		data.len = 0;
403	}
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	 */
411	if (text.vaddr + text.len < data.vaddr)
412		text.len = data.vaddr - text.vaddr;
413
414	/* We now have enough information to cons up an a.out header... */
415	eah.magic = OMAGIC;
416	eah.vstamp = 200;
417	eah.tsize = text.len;
418	eah.dsize = data.len;
419	eah.bsize = bss.len;
420	eah.entry = ex.e_entry;
421	eah.text_start = text.vaddr;
422	eah.data_start = data.vaddr;
423	eah.bss_start = bss.vaddr;
424	eah.gprmask = 0xf3fffffe;
425	memset(&eah.cprmask, '\0', sizeof(eah.cprmask));
426	eah.gp_value = 0;	/* unused. */
427
428	if (format_bigendian)
429		efh.f_magic = MIPSEBMAGIC;
430	else
431		efh.f_magic = MIPSELMAGIC;
432	if (addflag)
433		nosecs = 6;
434	else
435		nosecs = 3;
436	efh.f_nscns = nosecs;
437	efh.f_timdat = 0;	/* bogus */
438	efh.f_symptr = 0;
439	efh.f_nsyms = 0;
440	efh.f_opthdr = sizeof(eah);
441	efh.f_flags = 0x100f;	/* Stripped, not sharable. */
442
443	memset(esecs, 0, sizeof(esecs));
444	strcpy(esecs[0].s_name, ".text");
445	strcpy(esecs[1].s_name, ".data");
446	strcpy(esecs[2].s_name, ".bss");
447	if (addflag) {
448		strcpy(esecs[3].s_name, ".rdata");
449		strcpy(esecs[4].s_name, ".sdata");
450		strcpy(esecs[5].s_name, ".sbss");
451	}
452	esecs[0].s_paddr = esecs[0].s_vaddr = eah.text_start;
453	esecs[1].s_paddr = esecs[1].s_vaddr = eah.data_start;
454	esecs[2].s_paddr = esecs[2].s_vaddr = eah.bss_start;
455	if (addflag) {
456		esecs[3].s_paddr = esecs[3].s_vaddr = 0;
457		esecs[4].s_paddr = esecs[4].s_vaddr = 0;
458		esecs[5].s_paddr = esecs[5].s_vaddr = 0;
459	}
460	esecs[0].s_size = eah.tsize;
461	esecs[1].s_size = eah.dsize;
462	esecs[2].s_size = eah.bsize;
463	if (addflag) {
464		esecs[3].s_size = 0;
465		esecs[4].s_size = 0;
466		esecs[5].s_size = 0;
467	}
468	esecs[0].s_scnptr = N_TXTOFF(efh, eah);
469	esecs[1].s_scnptr = N_DATOFF(efh, eah);
470#define ECOFF_SEGMENT_ALIGNMENT(a) 0x10
471#define ECOFF_ROUND(s,a) (((s)+(a)-1)&~((a)-1))
472	esecs[2].s_scnptr = esecs[1].s_scnptr +
473	    ECOFF_ROUND(esecs[1].s_size, ECOFF_SEGMENT_ALIGNMENT(&eah));
474	if (addflag) {
475		esecs[3].s_scnptr = 0;
476		esecs[4].s_scnptr = 0;
477		esecs[5].s_scnptr = 0;
478	}
479	esecs[0].s_relptr = esecs[1].s_relptr = esecs[2].s_relptr = 0;
480	esecs[0].s_lnnoptr = esecs[1].s_lnnoptr = esecs[2].s_lnnoptr = 0;
481	esecs[0].s_nreloc = esecs[1].s_nreloc = esecs[2].s_nreloc = 0;
482	esecs[0].s_nlnno = esecs[1].s_nlnno = esecs[2].s_nlnno = 0;
483	if (addflag) {
484		esecs[3].s_relptr = esecs[4].s_relptr
485		    = esecs[5].s_relptr = 0;
486		esecs[3].s_lnnoptr = esecs[4].s_lnnoptr
487		    = esecs[5].s_lnnoptr = 0;
488		esecs[3].s_nreloc = esecs[4].s_nreloc = esecs[5].s_nreloc =
489		    0;
490		esecs[3].s_nlnno = esecs[4].s_nlnno = esecs[5].s_nlnno = 0;
491	}
492	esecs[0].s_flags = 0x20;
493	esecs[1].s_flags = 0x40;
494	esecs[2].s_flags = 0x82;
495	if (addflag) {
496		esecs[3].s_flags = 0x100;
497		esecs[4].s_flags = 0x200;
498		esecs[5].s_flags = 0x400;
499	}
500
501	/* Make the output file... */
502	if ((outfile = open(argv[2], O_WRONLY | O_CREAT, 0777)) < 0) {
503		fprintf(stderr, "Unable to create %s: %s\n", argv[2],
504			strerror(errno));
505		exit(1);
506	}
507
508	if (must_convert_endian)
509		convert_ecoff_filehdr(&efh);
510	/* Write the headers... */
511	i = write(outfile, &efh, sizeof(efh));
512	if (i != sizeof efh) {
513		perror("efh: write");
514		exit(1);
515	}
516
517	for (i = 0; i < nosecs; i++) {
518		printf("Section %d: %s phys %lx  size %lx  file offset %lx\n",
519		       i, esecs[i].s_name, esecs[i].s_paddr,
520		       esecs[i].s_size, esecs[i].s_scnptr);
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		/*
560		 * Unprocessable sections were handled above, so just verify
561		 * that the section can be loaded before copying.
562		 */
563		if (ph[i].p_type != PT_LOAD || ph[i].p_filesz == 0)
564			continue;
565
566		if (cur_vma != ph[i].p_vaddr) {
567			unsigned long gap = ph[i].p_vaddr - cur_vma;
568			char obuf[1024];
569
570			if (gap > 65536) {
571				fprintf(stderr, "Intersegment gap (%ld "
572				        "bytes) too large.\n", gap);
573				exit(1);
574			}
575			fprintf(stderr, "Warning: %ld byte intersegment gap.\n",
576					gap);
577			memset(obuf, 0, sizeof(obuf));
578			while (gap) {
579				int count = write(outfile, obuf,
580				   gap > sizeof(obuf) ?  sizeof(obuf) : gap);
581				if (count < 0) {
582					fprintf(stderr,
583						"Error writing gap: %s\n",
584						strerror(errno));
585					exit(1);
586				}
587				gap -= count;
588			}
589		}
590		fprintf(stderr, "writing %d bytes...\n", ph[i].p_filesz);
591		copy(outfile, infile, ph[i].p_offset, ph[i].p_filesz);
592		cur_vma = ph[i].p_vaddr + ph[i].p_filesz;
593	}
594
595	/*
596	 * Write a page of padding for boot PROMS that read entire pages.
597	 * Without this, they may attempt to read past the end of the
598	 * data section, incur an error, and refuse to boot.
599	 */
600	{
601		char obuf[4096];
602		memset(obuf, 0, sizeof(obuf));
603		if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) {
604			fprintf(stderr, "Error writing PROM padding: %s\n",
605				strerror(errno));
606			exit(1);
607		}
608	}
609
610	/* Looks like we won... */
611	exit(0);
612}
613