/* * Convert an ELF binary into BCS ECOFF format. * We merge all program headers into a single ECOFF section for simplicity. * However, PROM version 01.14 on AV4300 will fail to load a BCS binary * unless it has a non-empty data section, so we put the smallest possible * bunch of zeroes as a second section to appease it. */ void convert_elf(const char *infile, int infd, int outfd, Elf_Ehdr *ehdr) { struct ecoff_exechdr ehead; struct ecoff_scnhdr escn[2]; Elf_Phdr *phdr; off_t outpos; uint delta, ptload; Elf_Addr minaddr, maxaddr; int n, last; phdr = calloc(ehdr->e_phnum, sizeof(Elf_Phdr)); if (phdr == NULL) err(1, "calloc"); for (n = 0; n < ehdr->e_phnum; n++) { if (lseek(infd, ehdr->e_phoff + n * ehdr->e_phentsize, SEEK_SET) == (off_t) -1) err(1, "seek"); if (read(infd, phdr + n, sizeof *phdr) != sizeof(*phdr)) err(1, "read"); } ptload = 0; for (n = 0; n < ehdr->e_phnum; n++) if (phdr[n].p_type == PT_LOAD) ptload++; if (ptload > 3) errx(1, "%s: too many PT_LOAD program headers", infile); maxaddr = 0; minaddr = (Elf_Addr)-1; for (n = 0; n < ehdr->e_phnum; n++) { if (phdr[n].p_type != PT_LOAD) continue; if (phdr[n].p_paddr < minaddr) minaddr = phdr[n].p_paddr; if (phdr[n].p_paddr + phdr[n].p_memsz > maxaddr) maxaddr = phdr[n].p_paddr + phdr[n].p_memsz; } maxaddr = round(maxaddr, MINIMAL_ALIGN); /* * Header */ memset(&ehead, 0, sizeof ehead); memset(&escn, 0, sizeof escn); ehead.f.f_magic = 0x016d; /* MC88OMAGIC */ ehead.f.f_nscns = 2; ehead.f.f_opthdr = sizeof ehead.a; ehead.f.f_flags = 0x020f; /* F_RELFLG | F_EXEC | F_LNNO | 8 | F_AR16WR */ ehead.a.magic = ZMAGIC; ehead.a.tsize = maxaddr - minaddr; /* ignored */ ehead.a.dsize = MINIMAL_ALIGN; /* ignored */ ehead.a.bsize = 0; /* ignored */ ehead.a.entry = ehdr->e_entry; ehead.a.text_start = minaddr; /* ignored */ ehead.a.data_start = maxaddr; /* ignored */ n = write(outfd, &ehead, sizeof(ehead)); if (n != sizeof(ehead)) err(1, "write"); /* * Sections */ strncpy(escn[0].s_name, ".text", sizeof escn[0].s_name); escn[0].s_paddr = minaddr; /* ignored, 1:1 mapping */ escn[0].s_size = maxaddr - minaddr; escn[0].s_scnptr = round(sizeof(ehead) + sizeof(escn), MINIMAL_ALIGN); escn[0].s_flags = 0x20; /* STYP_TEXT */ strncpy(escn[1].s_name, ".data", sizeof escn[1].s_name); escn[1].s_paddr = escn[0].s_paddr + escn[0].s_size; escn[1].s_size = MINIMAL_ALIGN; escn[1].s_scnptr = escn[0].s_scnptr + escn[0].s_size; escn[1].s_flags = 0x40; /* STYP_DATA */ /* adjust load addresses */ escn[0].s_vaddr = escn[0].s_paddr; escn[1].s_vaddr = escn[1].s_paddr; n = write(outfd, &escn, sizeof(escn)); if (n != sizeof(escn)) err(1, "write"); /* * Copy ``text'' section (all PT_LOAD program headers). */ outpos = escn[0].s_scnptr; if (lseek(outfd, outpos, SEEK_SET) == (off_t) -1) err(1, "seek"); for (n = 0, last = -1; n < ehdr->e_phnum; n++) { if (phdr[n].p_type != PT_LOAD) continue; if (last >= 0) { delta = (phdr[n].p_paddr - phdr[last].p_paddr) - phdr[last].p_memsz; if (delta != 0) { zerobits(outfd, delta); outpos += delta; } } #ifdef DEBUG printf("copying %s: source %x dest %llx size %x\n", escn[0].s_name, phdr[n].p_offset, outpos, phdr[n].p_filesz); #endif if (lseek(infd, phdr[n].p_offset, SEEK_SET) == (off_t) -1) err(1, "seek"); copybits(infd, outfd, phdr[n].p_filesz); delta = phdr[n].p_memsz - phdr[n].p_filesz; if (delta != 0) zerobits(outfd, delta); outpos += phdr[n].p_memsz; last = n; } free(phdr); /* * Fill ``data'' section. */ zerobits(outfd, escn[1].s_size); outpos += escn[1].s_size; /* * Round file to a multiple of 512 bytes, since ``recent'' PROM * (such as rev 1.20 on AV530) will reject files not being properly * rounded to a multiple of 512 bytes. */ if ((outpos % ECOFF_ALIGN) != 0) zerobits(outfd, ECOFF_ALIGN - (outpos % ECOFF_ALIGN)); }
int main(int argc, char *argv[]) { struct exec head; struct ecoff_exechdr ehead; struct ecoff_scnhdr escn[3]; int infd, outfd; int n; if (argc != 3) usage(); infd = open(argv[1], O_RDONLY); if (infd < 0) err(1, argv[1]); outfd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0644); if (outfd < 0) err(1, argv[2]); n = read(infd, &head, sizeof(head)); if (n < sizeof(head)) err(1, "read"); if (N_BADMAG(head)) { printf("%s: bad magic number\n", argv[1]); exit(1); } if (head.a_trsize || head.a_drsize) { printf("%s: has relocations\n", argv[1]); exit(1); } /* * Header */ ehead.f.f_magic = 0x016d; /* MC88OMAGIC */ ehead.f.f_nscns = 3; ehead.f.f_timdat = 0; /* ignored */ ehead.f.f_symptr = 0; /* ignored */ ehead.f.f_nsyms = 0; /* ignored */ ehead.f.f_opthdr = sizeof ehead.a; ehead.f.f_flags = 0x020f; /* F_RELFLG | F_EXEC | F_LNNO | 8 | F_AR16WR */ ehead.a.magic = N_GETMAGIC(head); ehead.a.vstamp = 0; /* ignored */ ehead.a.tsize = head.a_text; /* ignored */ ehead.a.dsize = head.a_data; /* ignored */ ehead.a.bsize = head.a_bss; /* ignored */ ehead.a.entry = head.a_entry; ehead.a.text_start = N_TXTADDR(head); /* ignored */ ehead.a.data_start = N_DATADDR(head); /* ignored */ n = write(outfd, &ehead, sizeof(ehead)); if (n != sizeof(ehead)) err(1, "write"); /* * Sections. * Note that we merge .bss into .data since the PROM will not * clear it and locore does not do this either. */ strncpy(escn[0].s_name, ".text", sizeof escn[0].s_name); escn[0].s_paddr = N_TXTADDR(head); /* ignored, 1:1 mapping */ escn[0].s_size = round(head.a_text, 8); escn[0].s_scnptr = round(sizeof(ehead) + sizeof(escn), 0x10); escn[0].s_relptr = 0; escn[0].s_lnnoptr = 0; escn[0].s_nlnno = 0; escn[0].s_flags = 0x20; /* STYP_TEXT */ strncpy(escn[1].s_name, ".data", sizeof escn[1].s_name); escn[1].s_paddr = N_DATADDR(head); /* ignored, 1:1 mapping */ escn[1].s_scnptr = escn[0].s_scnptr + escn[0].s_size; escn[1].s_size = round(head.a_data + head.a_bss, 8); escn[1].s_relptr = 0; escn[1].s_lnnoptr = 0; escn[1].s_nlnno = 0; escn[1].s_flags = 0x40; /* STYP_DATA */ strncpy(escn[2].s_name, ".bss", sizeof escn[2].s_name); escn[2].s_paddr = N_BSSADDR(head) + head.a_bss; /* ignored, 1:1 mapping */ escn[2].s_scnptr = 0; /* nothing in the file */ escn[2].s_size = 0; escn[2].s_relptr = 0; escn[2].s_lnnoptr = 0; escn[2].s_nlnno = 0; escn[2].s_flags = 0x80; /* STYP_BSS */ /* adjust load addresses */ escn[0].s_paddr += (head.a_entry & ~(__LDPGSZ - 1)) - __LDPGSZ; escn[1].s_paddr += (head.a_entry & ~(__LDPGSZ - 1)) - __LDPGSZ; escn[2].s_paddr += (head.a_entry & ~(__LDPGSZ - 1)) - __LDPGSZ; escn[0].s_vaddr = escn[0].s_paddr; escn[1].s_vaddr = escn[1].s_paddr; escn[2].s_vaddr = escn[2].s_paddr; n = write(outfd, &escn, sizeof(escn)); if (n != sizeof(escn)) err(1, "write"); /* * Copy text section */ #ifdef DEBUG printf("copying %s: source %lx dest %lx size %x\n", escn[0].s_name, N_TXTOFF(head), escn[0].s_scnptr, head.a_text); #endif lseek(outfd, escn[0].s_scnptr, SEEK_SET); lseek(infd, N_TXTOFF(head), SEEK_SET); copybits(infd, outfd, head.a_text); /* * Copy data section */ #ifdef DEBUG printf("copying %s: source %lx dest %lx size %x\n", escn[1].s_name, N_DATOFF(head), escn[1].s_scnptr, head.a_data); #endif lseek(outfd, escn[1].s_scnptr, SEEK_SET); lseek(infd, N_DATOFF(head), SEEK_SET); copybits(infd, outfd, head.a_data); /* * ``Copy'' bss section */ #ifdef DEBUG printf("copying %s: size %lx\n", escn[2].s_name, round(head.a_data + head.a_bss, 8) - head.a_data); #endif zerobits(outfd, round(head.a_data + head.a_bss, 8) - head.a_data); close(infd); close(outfd); exit(0); }