3 * Ted Lemon (hereinafter referred to as the author)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
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. */
39 #include <sys/types.h>
44 #include <netinet/in.h>
50 * Some extra ELF definitions
52 #define PT_MIPS_REGINFO 0x70000000 /* Register usage information */
54 /* -------------------------------------------------------------------- */
62 int must_convert_endian;
65 static void copy(int out, int in, off_t offset, off_t size)
68 int remaining, cur, count;
70 /* Go to the start of the ELF symbol table... */
71 if (lseek(in, offset, SEEK_SET) < 0) {
72 perror("copy: lseek");
79 if (cur > sizeof ibuf)
82 if ((count = read(in, ibuf, cur)) != cur) {
83 fprintf(stderr, "copy: read: %s\n",
84 count ? strerror(errno) :
85 "premature end of file");
88 if ((count = write(out, ibuf, cur)) != cur) {
89 perror("copy: write");
96 * Combine two segments, which must be contiguous. If pad is true, it's
97 * okay for there to be padding between.
99 static void combine(struct sect *base, struct sect *new, int pad)
104 if (base->vaddr + base->len != new->vaddr) {
106 base->len = new->vaddr - base->vaddr;
109 "Non-contiguous data can't be converted.\n");
113 base->len += new->len;
117 static int phcmp(const void *v1, const void *v2)
119 const Elf32_Phdr *h1 = v1;
120 const Elf32_Phdr *h2 = v2;
122 if (h1->p_vaddr > h2->p_vaddr)
124 else if (h1->p_vaddr < h2->p_vaddr)
130 static char *saveRead(int file, off_t offset, off_t len, char *name)
135 if ((off = lseek(file, offset, SEEK_SET)) < 0) {
136 fprintf(stderr, "%s: fseek: %s\n", name, strerror(errno));
139 if (!(tmp = (char *) malloc(len))) {
140 fprintf(stderr, "%s: Can't allocate %ld bytes.\n", name,
144 count = read(file, tmp, len);
146 fprintf(stderr, "%s: read: %s.\n",
148 count ? strerror(errno) : "End of file reached");
156 (((unsigned short)(x) & (unsigned short)0x00ffU) << 8) | \
157 (((unsigned short)(x) & (unsigned short)0xff00U) >> 8) ))
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) ))
166 static void convert_elf_hdr(Elf32_Ehdr * e)
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);
183 static void convert_elf_phdrs(Elf32_Phdr * p, int num)
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);
200 static void convert_elf_shdrs(Elf32_Shdr * s, int num)
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);
218 static void convert_ecoff_filehdr(struct filehdr *f)
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);
229 static void convert_ecoff_aouthdr(struct aouthdr *a)
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);
248 static void convert_ecoff_esecs(struct scnhdr *s, int num)
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);
265 int main(int argc, char *argv[])
271 struct sect text, data, bss;
274 struct scnhdr esecs[6];
276 unsigned long cur_vma = ULONG_MAX;
280 text.len = data.len = bss.len = 0;
281 text.vaddr = data.vaddr = bss.vaddr = 0;
284 if (argc < 3 || argc > 4) {
287 "usage: elf2ecoff <elf executable> <ecoff executable> [-a]\n");
291 if (strcmp(argv[3], "-a"))
296 /* Try the input file... */
297 if ((infile = open(argv[1], O_RDONLY)) < 0) {
298 fprintf(stderr, "Can't open %s for read: %s\n",
299 argv[1], strerror(errno));
303 /* Read the header, which is at the beginning of the file... */
304 i = read(infile, &ex, sizeof ex);
305 if (i != sizeof ex) {
306 fprintf(stderr, "ex: %s: %s.\n",
308 i ? strerror(errno) : "End of file reached");
312 if (ex.e_ident[EI_DATA] == ELFDATA2MSB)
313 format_bigendian = 1;
315 if (ntohs(0xaa55) == 0xaa55) {
316 if (!format_bigendian)
317 must_convert_endian = 1;
319 if (format_bigendian)
320 must_convert_endian = 1;
322 if (must_convert_endian)
323 convert_elf_hdr(&ex);
325 /* Read the program headers... */
326 ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff,
327 ex.e_phnum * sizeof(Elf32_Phdr),
329 if (must_convert_endian)
330 convert_elf_phdrs(ph, ex.e_phnum);
331 /* Read the section headers... */
332 sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff,
333 ex.e_shnum * sizeof(Elf32_Shdr),
335 if (must_convert_endian)
336 convert_elf_shdrs(sh, ex.e_shnum);
338 /* Figure out if we can cram the program header into an ECOFF
339 header... Basically, we can't handle anything but loadable
340 segments, but we can ignore some kinds of segments. We can't
341 handle holes in the address space. Segments may be out of order,
342 so we sort them first. */
344 qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp);
346 for (i = 0; i < ex.e_phnum; i++) {
347 /* Section types we can ignore... */
348 if (ph[i].p_type == PT_NULL || ph[i].p_type == PT_NOTE ||
349 ph[i].p_type == PT_PHDR
350 || ph[i].p_type == PT_MIPS_REGINFO)
352 /* Section types we can't handle... */
353 else if (ph[i].p_type != PT_LOAD) {
355 "Program header %d type %d can't be converted.\n",
356 ex.e_phnum, ph[i].p_type);
359 /* Writable (data) segment? */
360 if (ph[i].p_flags & PF_W) {
361 struct sect ndata, nbss;
363 ndata.vaddr = ph[i].p_vaddr;
364 ndata.len = ph[i].p_filesz;
365 nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz;
366 nbss.len = ph[i].p_memsz - ph[i].p_filesz;
368 combine(&data, &ndata, 0);
369 combine(&bss, &nbss, 1);
373 ntxt.vaddr = ph[i].p_vaddr;
374 ntxt.len = ph[i].p_filesz;
376 combine(&text, &ntxt, 0);
378 /* Remember the lowest segment start address. */
379 if (ph[i].p_vaddr < cur_vma)
380 cur_vma = ph[i].p_vaddr;
383 /* Sections must be in order to be converted... */
384 if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
385 text.vaddr + text.len > data.vaddr
386 || data.vaddr + data.len > bss.vaddr) {
388 "Sections ordering prevents a.out conversion.\n");
392 /* If there's a data section but no text section, then the loader
393 combined everything into one section. That needs to be the
394 text section, so just make the data section zero length following
396 if (data.len && !text.len) {
398 data.vaddr = text.vaddr + text.len;
402 /* If there is a gap between text and data, we'll fill it when we copy
403 the data, so update the length of the text segment as represented in
404 a.out to reflect that, since a.out doesn't allow gaps in the program
406 if (text.vaddr + text.len < data.vaddr)
407 text.len = data.vaddr - text.vaddr;
409 /* We now have enough information to cons up an a.out header... */
412 eah.tsize = text.len;
413 eah.dsize = data.len;
415 eah.entry = ex.e_entry;
416 eah.text_start = text.vaddr;
417 eah.data_start = data.vaddr;
418 eah.bss_start = bss.vaddr;
419 eah.gprmask = 0xf3fffffe;
420 memset(&eah.cprmask, '\0', sizeof eah.cprmask);
421 eah.gp_value = 0; /* unused. */
423 if (format_bigendian)
424 efh.f_magic = MIPSEBMAGIC;
426 efh.f_magic = MIPSELMAGIC;
431 efh.f_nscns = nosecs;
432 efh.f_timdat = 0; /* bogus */
435 efh.f_opthdr = sizeof eah;
436 efh.f_flags = 0x100f; /* Stripped, not sharable. */
438 memset(esecs, 0, sizeof esecs);
439 strcpy(esecs[0].s_name, ".text");
440 strcpy(esecs[1].s_name, ".data");
441 strcpy(esecs[2].s_name, ".bss");
443 strcpy(esecs[3].s_name, ".rdata");
444 strcpy(esecs[4].s_name, ".sdata");
445 strcpy(esecs[5].s_name, ".sbss");
447 esecs[0].s_paddr = esecs[0].s_vaddr = eah.text_start;
448 esecs[1].s_paddr = esecs[1].s_vaddr = eah.data_start;
449 esecs[2].s_paddr = esecs[2].s_vaddr = eah.bss_start;
451 esecs[3].s_paddr = esecs[3].s_vaddr = 0;
452 esecs[4].s_paddr = esecs[4].s_vaddr = 0;
453 esecs[5].s_paddr = esecs[5].s_vaddr = 0;
455 esecs[0].s_size = eah.tsize;
456 esecs[1].s_size = eah.dsize;
457 esecs[2].s_size = eah.bsize;
463 esecs[0].s_scnptr = N_TXTOFF(efh, eah);
464 esecs[1].s_scnptr = N_DATOFF(efh, eah);
465 #define ECOFF_SEGMENT_ALIGNMENT(a) 0x10
466 #define ECOFF_ROUND(s, a) (((s)+(a)-1)&~((a)-1))
467 esecs[2].s_scnptr = esecs[1].s_scnptr +
468 ECOFF_ROUND(esecs[1].s_size, ECOFF_SEGMENT_ALIGNMENT(&eah));
470 esecs[3].s_scnptr = 0;
471 esecs[4].s_scnptr = 0;
472 esecs[5].s_scnptr = 0;
474 esecs[0].s_relptr = esecs[1].s_relptr = esecs[2].s_relptr = 0;
475 esecs[0].s_lnnoptr = esecs[1].s_lnnoptr = esecs[2].s_lnnoptr = 0;
476 esecs[0].s_nreloc = esecs[1].s_nreloc = esecs[2].s_nreloc = 0;
477 esecs[0].s_nlnno = esecs[1].s_nlnno = esecs[2].s_nlnno = 0;
479 esecs[3].s_relptr = esecs[4].s_relptr
480 = esecs[5].s_relptr = 0;
481 esecs[3].s_lnnoptr = esecs[4].s_lnnoptr
482 = esecs[5].s_lnnoptr = 0;
483 esecs[3].s_nreloc = esecs[4].s_nreloc = esecs[5].s_nreloc =
485 esecs[3].s_nlnno = esecs[4].s_nlnno = esecs[5].s_nlnno = 0;
487 esecs[0].s_flags = 0x20;
488 esecs[1].s_flags = 0x40;
489 esecs[2].s_flags = 0x82;
491 esecs[3].s_flags = 0x100;
492 esecs[4].s_flags = 0x200;
493 esecs[5].s_flags = 0x400;
496 /* Make the output file... */
497 if ((outfile = open(argv[2], O_WRONLY | O_CREAT, 0777)) < 0) {
498 fprintf(stderr, "Unable to create %s: %s\n", argv[2],
503 if (must_convert_endian)
504 convert_ecoff_filehdr(&efh);
505 /* Write the headers... */
506 i = write(outfile, &efh, sizeof efh);
507 if (i != sizeof efh) {
508 perror("efh: write");
511 for (i = 0; i < nosecs; i++) {
513 ("Section %d: %s phys %lx size %lx file offset %lx\n",
514 i, esecs[i].s_name, esecs[i].s_paddr,
515 esecs[i].s_size, esecs[i].s_scnptr);
518 fprintf(stderr, "wrote %d byte file header.\n", i);
520 if (must_convert_endian)
521 convert_ecoff_aouthdr(&eah);
522 i = write(outfile, &eah, sizeof eah);
523 if (i != sizeof eah) {
524 perror("eah: write");
527 fprintf(stderr, "wrote %d byte a.out header.\n", i);
529 if (must_convert_endian)
530 convert_ecoff_esecs(&esecs[0], nosecs);
531 i = write(outfile, &esecs, nosecs * sizeof(struct scnhdr));
532 if (i != nosecs * sizeof(struct scnhdr)) {
533 perror("esecs: write");
536 fprintf(stderr, "wrote %d bytes of section headers.\n", i);
538 pad = (sizeof(efh) + sizeof(eah) + nosecs * sizeof(struct scnhdr)) & 15;
541 i = write(outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad);
543 perror("ipad: write");
546 fprintf(stderr, "wrote %d byte pad.\n", i);
550 * Copy the loadable sections. Zero-fill any gaps less than 64k;
551 * complain about any zero-filling, and die if we're asked to zero-fill
554 for (i = 0; i < ex.e_phnum; i++) {
555 /* Unprocessable sections were handled above, so just verify that
556 the section can be loaded before copying. */
557 if (ph[i].p_type == PT_LOAD && ph[i].p_filesz) {
558 if (cur_vma != ph[i].p_vaddr) {
560 ph[i].p_vaddr - cur_vma;
564 "Intersegment gap (%ld bytes) too large.\n",
569 "Warning: %ld byte intersegment gap.\n",
571 memset(obuf, 0, sizeof obuf);
580 "Error writing gap: %s\n",
587 fprintf(stderr, "writing %d bytes...\n",
589 copy(outfile, infile, ph[i].p_offset,
591 cur_vma = ph[i].p_vaddr + ph[i].p_filesz;
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.
602 memset(obuf, 0, sizeof obuf);
603 if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) {
604 fprintf(stderr, "Error writing PROM padding: %s\n",
610 /* Looks like we won... */