diff options
author | red031000 <rubenru09@aol.com> | 2020-08-26 20:20:07 +0100 |
---|---|---|
committer | red031000 <rubenru09@aol.com> | 2020-08-26 20:22:12 +0100 |
commit | 9fe8425a19b3f5b835d25ca1960522f4e041e3ee (patch) | |
tree | 5f86555b379d77bbd000d0d446e24815bceaa613 /tools | |
parent | 3fca53d87d8610e48a3a6806cd4786e8a358edd0 (diff) |
move elf2dol into separate directory + makefile improvements
Diffstat (limited to 'tools')
-rw-r--r-- | tools/elf2dol/.gitignore | 2 | ||||
-rw-r--r-- | tools/elf2dol/Makefile (renamed from tools/Makefile) | 0 | ||||
-rw-r--r-- | tools/elf2dol/elf2dol.c (renamed from tools/elf2dol.c) | 1010 |
3 files changed, 507 insertions, 505 deletions
diff --git a/tools/elf2dol/.gitignore b/tools/elf2dol/.gitignore new file mode 100644 index 0000000..90ab230 --- /dev/null +++ b/tools/elf2dol/.gitignore @@ -0,0 +1,2 @@ +elf2dol +elf2dol.exe diff --git a/tools/Makefile b/tools/elf2dol/Makefile index 311b6b8..311b6b8 100644 --- a/tools/Makefile +++ b/tools/elf2dol/Makefile diff --git a/tools/elf2dol.c b/tools/elf2dol/elf2dol.c index 5cc4117..a3055ad 100644 --- a/tools/elf2dol.c +++ b/tools/elf2dol/elf2dol.c @@ -1,505 +1,505 @@ -#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/param.h>
-
-#ifndef MAX
-//! Get the maximum of two values
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#endif
-
-#ifndef MIN
-//! Get the minimum of two values
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-
-#define EI_NIDENT 16
-
-typedef struct {
- unsigned char e_ident[EI_NIDENT];
- uint16_t e_type;
- uint16_t e_machine;
- uint32_t e_version;
- uint32_t e_entry;
- uint32_t e_phoff;
- uint32_t e_shoff;
- uint32_t e_flags;
- uint16_t e_ehsize;
- uint16_t e_phentsize;
- uint16_t e_phnum;
- uint16_t e_shentsize;
- uint16_t e_shnum;
- uint16_t e_shstrndx;
-} Elf32_Ehdr;
-
-#define EI_CLASS 4
-#define EI_DATA 5
-#define EI_VERSION 6
-#define EI_PAD 7
-#define EI_NIDENT 16
-
-#define ELFCLASS32 1
-#define ELFDATA2MSB 2
-#define EV_CURRENT 1
-
-#define ET_EXEC 2
-#define EM_PPC 20
-
-typedef struct {
- uint32_t p_type;
- uint32_t p_offset;
- uint32_t p_vaddr;
- uint32_t p_paddr;
- uint32_t p_filesz;
- uint32_t p_memsz;
- uint32_t p_flags;
- uint32_t p_align;
-} Elf32_Phdr;
-
-#define PT_LOAD 1
-#define PF_R 4
-#define PF_W 2
-#define PF_X 1
-
-int verbosity = 0;
-
-#if BYTE_ORDER == BIG_ENDIAN
-
-#define swap32(x) (x)
-#define swaf16(x) (x)
-
-#else
-
-static inline uint32_t swap32(uint32_t v)
-{
- return (v >> 24) |
- ((v >> 8) & 0x0000FF00) |
- ((v << 8) & 0x00FF0000) |
- (v << 24);
-}
-
-static inline uint16_t swaf16(uint16_t v)
-{
- return (v >> 8) | (v << 8);
-}
-
-#endif /* BIG_ENDIAN */
-
-typedef struct {
-
- uint32_t text_off[7];
- uint32_t data_off[11];
- uint32_t text_addr[7];
- uint32_t data_addr[11];
- uint32_t text_size[7];
- uint32_t data_size[11];
- uint32_t bss_addr;
- uint32_t bss_size;
- uint32_t entry;
- uint32_t pad[7];
-} DOL_hdr;
-
-#define HAVE_BSS 1
-
-#define MAX_TEXT_SEGMENTS 7
-#define MAX_DATA_SEGMENTS 11
-
-#define DOL_ALIGNMENT 32
-
-#define DOL_ALIGN(x) (((x) + DOL_ALIGNMENT - 1) & ~(DOL_ALIGNMENT - 1))
-
-typedef struct {
- DOL_hdr header;
- int text_cnt;
- int data_cnt;
- uint32_t text_elf_off[7];
- uint32_t data_elf_off[11];
- uint32_t flags;
- FILE *elf;
-} DOL_map;
-
-// We need to track 2 PDHR sizes in the event this is for Wii, but for Gamecube
-// we only need the first element(s).
-uint32_t sdataSizes[2] = {0, 0};
-uint32_t sbssSizes[2] = {0, 0};
-
-void usage(const char *name)
-{
- fprintf(stderr, "Usage: %s [-h] [-v] [--] elf-file dol-file\n", name);
- fprintf(stderr, " Convert an ELF file to a DOL file (by segments)\n");
- fprintf(stderr, " Options:\n");
- fprintf(stderr, " -h Show this help\n");
- fprintf(stderr, " -v Be more verbose (twice for even more)\n");
-}
-
-#define die(x) { fprintf(stderr, x "\n"); exit(1); }
-#define perrordie(x) { perror(x); exit(1); }
-
-void ferrordie(FILE *f, const char *str)
-{
- if(ferror(f)) {
- fprintf(stderr, "Error while ");
- perrordie(str);
- } else if(feof(f)) {
- fprintf(stderr, "EOF while %s\n", str);
- exit(1);
- } else {
- fprintf(stderr, "Unknown error while %s\n", str);
- exit(1);
- }
-}
-
-void add_bss(DOL_map *map, uint32_t paddr, uint32_t memsz)
-{
- if(map->flags & HAVE_BSS) {
- uint32_t start = swap32(map->header.bss_addr);
- uint32_t size = swap32(map->header.bss_size);
- if ( (start+size) == paddr) {
- map->header.bss_size = swap32(size+memsz);
- }
- } else {
- map->header.bss_addr = swap32(paddr);
- map->header.bss_size = swap32(memsz);
- map->flags |= HAVE_BSS;
- }
-}
-
-void increment_bss_size(DOL_map *map, uint32_t memsz)
-{
- // because it can be byte swapped, we need to force the add via a temporary.
- uint32_t preAdd = swap32(map->header.bss_size);
- preAdd += memsz;
- map->header.bss_size = swap32(preAdd);
-}
-
-void read_elf_segments(DOL_map *map, const char *elf, uint32_t sdata_pdhr, uint32_t sbss_pdhr, const char *platform)
-{
- int read, i;
- Elf32_Ehdr ehdr;
- int isWii = !(strcmp(platform, "wii")) ? 1 : 0;
-
- if(verbosity >= 2)
- fprintf(stderr, "Reading ELF file...\n");
-
- map->elf = fopen(elf, "rb");
- if(!map->elf)
- perrordie("Could not open ELF file");
-
- read = fread(&ehdr, sizeof(ehdr), 1, map->elf);
- if(read != 1)
- ferrordie(map->elf, "reading ELF header");
-
- if(memcmp(&ehdr.e_ident[0], "\177ELF", 4))
- die("Invalid ELF header");
- if(ehdr.e_ident[EI_CLASS] != ELFCLASS32)
- die("Invalid ELF class");
- if(ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
- die("Invalid ELF byte order");
- if(ehdr.e_ident[EI_VERSION] != EV_CURRENT)
- die("Invalid ELF ident version");
- if(swap32(ehdr.e_version) != EV_CURRENT)
- die("Invalid ELF version");
- if(swaf16(ehdr.e_type) != ET_EXEC)
- die("ELF is not an executable");
- if(swaf16(ehdr.e_machine) != EM_PPC)
- die("Machine is not PowerPC");
- if(!swap32(ehdr.e_entry))
- die("ELF has no entrypoint");
-
- map->header.entry = ehdr.e_entry;
-
- if(verbosity >= 2)
- fprintf(stderr, "Valid ELF header found\n");
-
- uint16_t phnum = swaf16(ehdr.e_phnum);
- uint32_t phoff = swap32(ehdr.e_phoff);
- Elf32_Phdr *phdrs;
-
- if(!phnum || !phoff)
- die("ELF has no program headers");
-
- if(swaf16(ehdr.e_phentsize) != sizeof(Elf32_Phdr))
- die("Invalid program header entry size");
-
- phdrs = malloc(phnum * sizeof(Elf32_Phdr));
-
- if(fseek(map->elf, phoff, SEEK_SET) < 0)
- ferrordie(map->elf, "reading ELF program headers");
- read = fread(phdrs, sizeof(Elf32_Phdr), phnum, map->elf);
- if(read != phnum)
- ferrordie(map->elf, "reading ELF program headers");
-
- for(i=0; i<phnum; i++) {
- if(swap32(phdrs[i].p_type) == PT_LOAD) {
- uint32_t offset = swap32(phdrs[i].p_offset);
- uint32_t paddr = swap32(phdrs[i].p_paddr);
- uint32_t filesz = swap32(phdrs[i].p_filesz);
- uint32_t memsz = swap32(phdrs[i].p_memsz);
- uint32_t flags = swap32(phdrs[i].p_flags);
- if(memsz) {
- if(verbosity >= 2)
- fprintf(stderr, "PHDR %d: 0x%x [0x%x] -> 0x%08x [0x%x] flags 0x%x\n",
- i, offset, filesz, paddr, memsz, flags);
- if(flags & PF_X) {
- // TEXT segment
- if(!(flags & PF_R))
- fprintf(stderr, "Warning: non-readable segment %d\n", i);
- if(flags & PF_W)
- fprintf(stderr, "Warning: writable and executable segment %d\n", i);
- if(filesz > memsz) {
- fprintf(stderr, "Error: TEXT segment %d memory size (0x%x) smaller than file size (0x%x)\n",
- i, memsz, filesz);
- exit(1);
- } else if (memsz > filesz) {
- add_bss(map, paddr + filesz, memsz - filesz);
- }
- if(map->text_cnt >= MAX_TEXT_SEGMENTS) {
- die("Error: Too many TEXT segments");
- }
- map->header.text_addr[map->text_cnt] = swap32(paddr);
- map->header.text_size[map->text_cnt] = swap32(filesz);
- map->text_elf_off[map->text_cnt] = offset;
- map->text_cnt++;
- } else {
- // DATA or BSS segment
- if(!(flags & PF_R))
- fprintf(stderr, "Warning: non-readable segment %d\n", i);
- if(filesz == 0) {
- // BSS segment
- add_bss(map, paddr, memsz);
-
- // We need to keep PHDF sizes, so track these.
- if(i == sbss_pdhr) {
- sbssSizes[0] = memsz;
- } else if(isWii && i == sbss_pdhr + 2) {
- sbssSizes[1] = memsz;
- }
- } else {
- // DATA segment
- if(filesz > memsz) {
- fprintf(stderr, "Error: segment %d memory size (0x%x) is smaller than file size (0x%x)\n",
- i, memsz, filesz);
- exit(1);
- }
- if(map->data_cnt >= MAX_DATA_SEGMENTS) {
- die("Error: Too many DATA segments");
- }
- // Track sdata as well.
- if(i == sdata_pdhr) {
- sdataSizes[0] = memsz;
- } else if(isWii && i == sdata_pdhr + 2) {
- sdataSizes[1] = memsz;
- }
-
- map->header.data_addr[map->data_cnt] = swap32(paddr);
- map->header.data_size[map->data_cnt] = swap32(filesz);
- map->data_elf_off[map->data_cnt] = offset;
- map->data_cnt++;
- }
- }
-
- } else {
- if(verbosity >= 1)
- fprintf(stderr, "Skipping empty program header %d\n", i);
- }
- } else if(verbosity >= 1) {
- fprintf(stderr, "Skipping program header %d of type %d\n", i, swap32(phdrs[i].p_type));
- }
- }
- increment_bss_size(map, sdataSizes[0]);
- increment_bss_size(map, sdataSizes[1]);
- increment_bss_size(map, sbssSizes[0]);
- increment_bss_size(map, sbssSizes[1]);
- if(verbosity >= 2) {
- fprintf(stderr, "Segments:\n");
- for(i=0; i<map->text_cnt; i++) {
- fprintf(stderr, " TEXT %d: 0x%08x [0x%x] from ELF offset 0x%x\n",
- i, swap32(map->header.text_addr[i]), swap32(map->header.text_size[i]),
- map->text_elf_off[i]);
- }
- for(i=0; i<map->data_cnt; i++) {
- fprintf(stderr, " DATA %d: 0x%08x [0x%x] from ELF offset 0x%x\n",
- i, swap32(map->header.data_addr[i]), swap32(map->header.data_size[i]),
- map->data_elf_off[i]);
- }
- if(map->flags & HAVE_BSS)
- fprintf(stderr, " BSS segment: 0x%08x [0x%x]\n", swap32(map->header.bss_addr),
- swap32(map->header.bss_size));
- }
-}
-
-void map_dol(DOL_map *map)
-{
- uint32_t fpos;
- int i;
-
- if(verbosity >= 2)
- fprintf(stderr, "Laying out DOL file...\n");
-
- fpos = DOL_ALIGN(sizeof(DOL_hdr));
-
- for(i=0; i<map->text_cnt; i++) {
- if(verbosity >= 2)
- fprintf(stderr, " TEXT segment %d at 0x%x\n", i, fpos);
- map->header.text_off[i] = swap32(fpos);
- fpos = DOL_ALIGN(fpos + swap32(map->header.text_size[i]));
- }
- for(i=0; i<map->data_cnt; i++) {
- if(verbosity >= 2)
- fprintf(stderr, " DATA segment %d at 0x%x\n", i, fpos);
- map->header.data_off[i] = swap32(fpos);
- fpos = DOL_ALIGN(fpos + swap32(map->header.data_size[i]));
- }
-
- if(map->text_cnt == 0) {
- if(verbosity >= 1)
- fprintf(stderr, "Note: adding dummy TEXT segment to work around IOS bug\n");
- map->header.text_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
- }
- if(map->data_cnt == 0) {
- if(verbosity >= 1)
- fprintf(stderr, "Note: adding dummy DATA segment to work around IOS bug\n");
- map->header.data_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
- }
-}
-
-#define BLOCK (1024*1024)
-
-void fcpy(FILE *dst, FILE *src, uint32_t dst_off, uint32_t src_off, uint32_t size)
-{
- int left = size;
- int read;
- int written;
- int block;
- void *blockbuf;
-
- if(fseek(src, src_off, SEEK_SET) < 0)
- ferrordie(src, "reading ELF segment data");
- if(fseek(dst, dst_off, SEEK_SET) < 0)
- ferrordie(dst, "writing DOL segment data");
-
- blockbuf = malloc(MIN(BLOCK, left));
-
- while(left) {
- block = MIN(BLOCK, left);
- read = fread(blockbuf, 1, block, src);
- if(read != block) {
- free(blockbuf);
- ferrordie(src, "reading ELF segment data");
- }
- written = fwrite(blockbuf, 1, block, dst);
- if(written != block) {
- free(blockbuf);
- ferrordie(dst, "writing DOL segment data");
- }
- left -= block;
- }
- free(blockbuf);
-}
-
-void write_dol(DOL_map *map, const char *dol)
-{
- FILE *dolf;
- int written;
- int i;
-
- if(verbosity >= 2)
- fprintf(stderr, "Writing DOL file...\n");
-
- dolf = fopen(dol, "wb");
- if(!dolf)
- perrordie("Could not open DOL file");
-
- if(verbosity >= 2) {
- fprintf(stderr, "DOL header:\n");
- for(i=0; i<MAX(1,map->text_cnt); i++)
- fprintf(stderr, " TEXT %d @ 0x%08x [0x%x] off 0x%x\n", i,
- swap32(map->header.text_addr[i]), swap32(map->header.text_size[i]),
- swap32(map->header.text_off[i]));
- for(i=0; i<MAX(1,map->data_cnt); i++)
- fprintf(stderr, " DATA %d @ 0x%08x [0x%x] off 0x%x\n", i,
- swap32(map->header.data_addr[i]), swap32(map->header.data_size[i]),
- swap32(map->header.data_off[i]));
- if(swap32(map->header.bss_addr) && swap32(map->header.bss_size))
- fprintf(stderr, " BSS @ 0x%08x [0x%x]\n", swap32(map->header.bss_addr),
- swap32(map->header.bss_size));
- fprintf(stderr, " Entry: 0x%08x\n", swap32(map->header.entry));
- fprintf(stderr, "Writing DOL header...\n");
- }
-
- written = fwrite(&map->header, sizeof(DOL_hdr), 1, dolf);
- if(written != 1)
- ferrordie(dolf, "writing DOL header");
-
- for(i=0; i<map->text_cnt; i++) {
- if(verbosity >= 2)
- fprintf(stderr, "Writing TEXT segment %d...\n", i);
- fcpy(dolf, map->elf, swap32(map->header.text_off[i]), map->text_elf_off[i],
- swap32(map->header.text_size[i]));
- }
- for(i=0; i<map->data_cnt; i++) {
- if(verbosity >= 2)
- fprintf(stderr, "Writing DATA segment %d...\n", i);
- fcpy(dolf, map->elf, swap32(map->header.data_off[i]), map->data_elf_off[i],
- swap32(map->header.data_size[i]));
- }
-
- if(verbosity >= 2)
- fprintf(stderr, "All done!\n");
-
- fclose(map->elf);
- fclose(dolf);
-}
-
-int main(int argc, char **argv)
-{
- char **arg;
-
- if(argc < 2) {
- usage(argv[0]);
- return 1;
- }
- arg = &argv[1];
- argc--;
-
- while(argc && *arg[0] == '-') {
- if(!strcmp(*arg, "-h")) {
- usage(argv[0]);
- return 1;
- } else if(!strcmp(*arg, "-v")) {
- verbosity++;
- } else if(!strcmp(*arg, "--")) {
- arg++;
- argc--;
- break;
- } else {
- fprintf(stderr, "Unrecognized option %s\n", *arg);
- usage(argv[0]);
- return 1;
- }
- arg++;
- argc--;
- }
- if(argc < 2) {
- usage(argv[0]);
- exit(1);
- }
-
- const char *elf_file = arg[0];
- const char *dol_file = arg[1];
- uint32_t sdata_pdhr = atoi(arg[2]);
- uint32_t sbss_pdhr = atoi(arg[3]);
- const char *platform = arg[4];
-
- DOL_map map;
-
- memset(&map, 0, sizeof(map));
-
- read_elf_segments(&map, elf_file, sdata_pdhr, sbss_pdhr, platform);
- map_dol(&map);
- write_dol(&map, dol_file);
-
- return 0;
-}
+#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/param.h> + +#ifndef MAX +//! Get the maximum of two values +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef MIN +//! Get the minimum of two values +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#define EI_NIDENT 16 + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint32_t e_entry; + uint32_t e_phoff; + uint32_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +} Elf32_Ehdr; + +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_PAD 7 +#define EI_NIDENT 16 + +#define ELFCLASS32 1 +#define ELFDATA2MSB 2 +#define EV_CURRENT 1 + +#define ET_EXEC 2 +#define EM_PPC 20 + +typedef struct { + uint32_t p_type; + uint32_t p_offset; + uint32_t p_vaddr; + uint32_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +} Elf32_Phdr; + +#define PT_LOAD 1 +#define PF_R 4 +#define PF_W 2 +#define PF_X 1 + +int verbosity = 0; + +#if BYTE_ORDER == BIG_ENDIAN + +#define swap32(x) (x) +#define swaf16(x) (x) + +#else + +static inline uint32_t swap32(uint32_t v) +{ + return (v >> 24) | + ((v >> 8) & 0x0000FF00) | + ((v << 8) & 0x00FF0000) | + (v << 24); +} + +static inline uint16_t swaf16(uint16_t v) +{ + return (v >> 8) | (v << 8); +} + +#endif /* BIG_ENDIAN */ + +typedef struct { + + uint32_t text_off[7]; + uint32_t data_off[11]; + uint32_t text_addr[7]; + uint32_t data_addr[11]; + uint32_t text_size[7]; + uint32_t data_size[11]; + uint32_t bss_addr; + uint32_t bss_size; + uint32_t entry; + uint32_t pad[7]; +} DOL_hdr; + +#define HAVE_BSS 1 + +#define MAX_TEXT_SEGMENTS 7 +#define MAX_DATA_SEGMENTS 11 + +#define DOL_ALIGNMENT 32 + +#define DOL_ALIGN(x) (((x) + DOL_ALIGNMENT - 1) & ~(DOL_ALIGNMENT - 1)) + +typedef struct { + DOL_hdr header; + int text_cnt; + int data_cnt; + uint32_t text_elf_off[7]; + uint32_t data_elf_off[11]; + uint32_t flags; + FILE *elf; +} DOL_map; + +// We need to track 2 PDHR sizes in the event this is for Wii, but for Gamecube +// we only need the first element(s). +uint32_t sdataSizes[2] = {0, 0}; +uint32_t sbssSizes[2] = {0, 0}; + +void usage(const char *name) +{ + fprintf(stderr, "Usage: %s [-h] [-v] [--] elf-file dol-file\n", name); + fprintf(stderr, " Convert an ELF file to a DOL file (by segments)\n"); + fprintf(stderr, " Options:\n"); + fprintf(stderr, " -h Show this help\n"); + fprintf(stderr, " -v Be more verbose (twice for even more)\n"); +} + +#define die(x) { fprintf(stderr, x "\n"); exit(1); } +#define perrordie(x) { perror(x); exit(1); } + +void ferrordie(FILE *f, const char *str) +{ + if(ferror(f)) { + fprintf(stderr, "Error while "); + perrordie(str); + } else if(feof(f)) { + fprintf(stderr, "EOF while %s\n", str); + exit(1); + } else { + fprintf(stderr, "Unknown error while %s\n", str); + exit(1); + } +} + +void add_bss(DOL_map *map, uint32_t paddr, uint32_t memsz) +{ + if(map->flags & HAVE_BSS) { + uint32_t start = swap32(map->header.bss_addr); + uint32_t size = swap32(map->header.bss_size); + if ( (start+size) == paddr) { + map->header.bss_size = swap32(size+memsz); + } + } else { + map->header.bss_addr = swap32(paddr); + map->header.bss_size = swap32(memsz); + map->flags |= HAVE_BSS; + } +} + +void increment_bss_size(DOL_map *map, uint32_t memsz) +{ + // because it can be byte swapped, we need to force the add via a temporary. + uint32_t preAdd = swap32(map->header.bss_size); + preAdd += memsz; + map->header.bss_size = swap32(preAdd); +} + +void read_elf_segments(DOL_map *map, const char *elf, uint32_t sdata_pdhr, uint32_t sbss_pdhr, const char *platform) +{ + int read, i; + Elf32_Ehdr ehdr; + int isWii = !(strcmp(platform, "wii")) ? 1 : 0; + + if(verbosity >= 2) + fprintf(stderr, "Reading ELF file...\n"); + + map->elf = fopen(elf, "rb"); + if(!map->elf) + perrordie("Could not open ELF file"); + + read = fread(&ehdr, sizeof(ehdr), 1, map->elf); + if(read != 1) + ferrordie(map->elf, "reading ELF header"); + + if(memcmp(&ehdr.e_ident[0], "\177ELF", 4)) + die("Invalid ELF header"); + if(ehdr.e_ident[EI_CLASS] != ELFCLASS32) + die("Invalid ELF class"); + if(ehdr.e_ident[EI_DATA] != ELFDATA2MSB) + die("Invalid ELF byte order"); + if(ehdr.e_ident[EI_VERSION] != EV_CURRENT) + die("Invalid ELF ident version"); + if(swap32(ehdr.e_version) != EV_CURRENT) + die("Invalid ELF version"); + if(swaf16(ehdr.e_type) != ET_EXEC) + die("ELF is not an executable"); + if(swaf16(ehdr.e_machine) != EM_PPC) + die("Machine is not PowerPC"); + if(!swap32(ehdr.e_entry)) + die("ELF has no entrypoint"); + + map->header.entry = ehdr.e_entry; + + if(verbosity >= 2) + fprintf(stderr, "Valid ELF header found\n"); + + uint16_t phnum = swaf16(ehdr.e_phnum); + uint32_t phoff = swap32(ehdr.e_phoff); + Elf32_Phdr *phdrs; + + if(!phnum || !phoff) + die("ELF has no program headers"); + + if(swaf16(ehdr.e_phentsize) != sizeof(Elf32_Phdr)) + die("Invalid program header entry size"); + + phdrs = malloc(phnum * sizeof(Elf32_Phdr)); + + if(fseek(map->elf, phoff, SEEK_SET) < 0) + ferrordie(map->elf, "reading ELF program headers"); + read = fread(phdrs, sizeof(Elf32_Phdr), phnum, map->elf); + if(read != phnum) + ferrordie(map->elf, "reading ELF program headers"); + + for(i=0; i<phnum; i++) { + if(swap32(phdrs[i].p_type) == PT_LOAD) { + uint32_t offset = swap32(phdrs[i].p_offset); + uint32_t paddr = swap32(phdrs[i].p_paddr); + uint32_t filesz = swap32(phdrs[i].p_filesz); + uint32_t memsz = swap32(phdrs[i].p_memsz); + uint32_t flags = swap32(phdrs[i].p_flags); + if(memsz) { + if(verbosity >= 2) + fprintf(stderr, "PHDR %d: 0x%x [0x%x] -> 0x%08x [0x%x] flags 0x%x\n", + i, offset, filesz, paddr, memsz, flags); + if(flags & PF_X) { + // TEXT segment + if(!(flags & PF_R)) + fprintf(stderr, "Warning: non-readable segment %d\n", i); + if(flags & PF_W) + fprintf(stderr, "Warning: writable and executable segment %d\n", i); + if(filesz > memsz) { + fprintf(stderr, "Error: TEXT segment %d memory size (0x%x) smaller than file size (0x%x)\n", + i, memsz, filesz); + exit(1); + } else if (memsz > filesz) { + add_bss(map, paddr + filesz, memsz - filesz); + } + if(map->text_cnt >= MAX_TEXT_SEGMENTS) { + die("Error: Too many TEXT segments"); + } + map->header.text_addr[map->text_cnt] = swap32(paddr); + map->header.text_size[map->text_cnt] = swap32(filesz); + map->text_elf_off[map->text_cnt] = offset; + map->text_cnt++; + } else { + // DATA or BSS segment + if(!(flags & PF_R)) + fprintf(stderr, "Warning: non-readable segment %d\n", i); + if(filesz == 0) { + // BSS segment + add_bss(map, paddr, memsz); + + // We need to keep PHDF sizes, so track these. + if(i == sbss_pdhr) { + sbssSizes[0] = memsz; + } else if(isWii && i == sbss_pdhr + 2) { + sbssSizes[1] = memsz; + } + } else { + // DATA segment + if(filesz > memsz) { + fprintf(stderr, "Error: segment %d memory size (0x%x) is smaller than file size (0x%x)\n", + i, memsz, filesz); + exit(1); + } + if(map->data_cnt >= MAX_DATA_SEGMENTS) { + die("Error: Too many DATA segments"); + } + // Track sdata as well. + if(i == sdata_pdhr) { + sdataSizes[0] = memsz; + } else if(isWii && i == sdata_pdhr + 2) { + sdataSizes[1] = memsz; + } + + map->header.data_addr[map->data_cnt] = swap32(paddr); + map->header.data_size[map->data_cnt] = swap32(filesz); + map->data_elf_off[map->data_cnt] = offset; + map->data_cnt++; + } + } + + } else { + if(verbosity >= 1) + fprintf(stderr, "Skipping empty program header %d\n", i); + } + } else if(verbosity >= 1) { + fprintf(stderr, "Skipping program header %d of type %d\n", i, swap32(phdrs[i].p_type)); + } + } + increment_bss_size(map, sdataSizes[0]); + increment_bss_size(map, sdataSizes[1]); + increment_bss_size(map, sbssSizes[0]); + increment_bss_size(map, sbssSizes[1]); + if(verbosity >= 2) { + fprintf(stderr, "Segments:\n"); + for(i=0; i<map->text_cnt; i++) { + fprintf(stderr, " TEXT %d: 0x%08x [0x%x] from ELF offset 0x%x\n", + i, swap32(map->header.text_addr[i]), swap32(map->header.text_size[i]), + map->text_elf_off[i]); + } + for(i=0; i<map->data_cnt; i++) { + fprintf(stderr, " DATA %d: 0x%08x [0x%x] from ELF offset 0x%x\n", + i, swap32(map->header.data_addr[i]), swap32(map->header.data_size[i]), + map->data_elf_off[i]); + } + if(map->flags & HAVE_BSS) + fprintf(stderr, " BSS segment: 0x%08x [0x%x]\n", swap32(map->header.bss_addr), + swap32(map->header.bss_size)); + } +} + +void map_dol(DOL_map *map) +{ + uint32_t fpos; + int i; + + if(verbosity >= 2) + fprintf(stderr, "Laying out DOL file...\n"); + + fpos = DOL_ALIGN(sizeof(DOL_hdr)); + + for(i=0; i<map->text_cnt; i++) { + if(verbosity >= 2) + fprintf(stderr, " TEXT segment %d at 0x%x\n", i, fpos); + map->header.text_off[i] = swap32(fpos); + fpos = DOL_ALIGN(fpos + swap32(map->header.text_size[i])); + } + for(i=0; i<map->data_cnt; i++) { + if(verbosity >= 2) + fprintf(stderr, " DATA segment %d at 0x%x\n", i, fpos); + map->header.data_off[i] = swap32(fpos); + fpos = DOL_ALIGN(fpos + swap32(map->header.data_size[i])); + } + + if(map->text_cnt == 0) { + if(verbosity >= 1) + fprintf(stderr, "Note: adding dummy TEXT segment to work around IOS bug\n"); + map->header.text_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr))); + } + if(map->data_cnt == 0) { + if(verbosity >= 1) + fprintf(stderr, "Note: adding dummy DATA segment to work around IOS bug\n"); + map->header.data_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr))); + } +} + +#define BLOCK (1024*1024) + +void fcpy(FILE *dst, FILE *src, uint32_t dst_off, uint32_t src_off, uint32_t size) +{ + int left = size; + int read; + int written; + int block; + void *blockbuf; + + if(fseek(src, src_off, SEEK_SET) < 0) + ferrordie(src, "reading ELF segment data"); + if(fseek(dst, dst_off, SEEK_SET) < 0) + ferrordie(dst, "writing DOL segment data"); + + blockbuf = malloc(MIN(BLOCK, left)); + + while(left) { + block = MIN(BLOCK, left); + read = fread(blockbuf, 1, block, src); + if(read != block) { + free(blockbuf); + ferrordie(src, "reading ELF segment data"); + } + written = fwrite(blockbuf, 1, block, dst); + if(written != block) { + free(blockbuf); + ferrordie(dst, "writing DOL segment data"); + } + left -= block; + } + free(blockbuf); +} + +void write_dol(DOL_map *map, const char *dol) +{ + FILE *dolf; + int written; + int i; + + if(verbosity >= 2) + fprintf(stderr, "Writing DOL file...\n"); + + dolf = fopen(dol, "wb"); + if(!dolf) + perrordie("Could not open DOL file"); + + if(verbosity >= 2) { + fprintf(stderr, "DOL header:\n"); + for(i=0; i<MAX(1,map->text_cnt); i++) + fprintf(stderr, " TEXT %d @ 0x%08x [0x%x] off 0x%x\n", i, + swap32(map->header.text_addr[i]), swap32(map->header.text_size[i]), + swap32(map->header.text_off[i])); + for(i=0; i<MAX(1,map->data_cnt); i++) + fprintf(stderr, " DATA %d @ 0x%08x [0x%x] off 0x%x\n", i, + swap32(map->header.data_addr[i]), swap32(map->header.data_size[i]), + swap32(map->header.data_off[i])); + if(swap32(map->header.bss_addr) && swap32(map->header.bss_size)) + fprintf(stderr, " BSS @ 0x%08x [0x%x]\n", swap32(map->header.bss_addr), + swap32(map->header.bss_size)); + fprintf(stderr, " Entry: 0x%08x\n", swap32(map->header.entry)); + fprintf(stderr, "Writing DOL header...\n"); + } + + written = fwrite(&map->header, sizeof(DOL_hdr), 1, dolf); + if(written != 1) + ferrordie(dolf, "writing DOL header"); + + for(i=0; i<map->text_cnt; i++) { + if(verbosity >= 2) + fprintf(stderr, "Writing TEXT segment %d...\n", i); + fcpy(dolf, map->elf, swap32(map->header.text_off[i]), map->text_elf_off[i], + swap32(map->header.text_size[i])); + } + for(i=0; i<map->data_cnt; i++) { + if(verbosity >= 2) + fprintf(stderr, "Writing DATA segment %d...\n", i); + fcpy(dolf, map->elf, swap32(map->header.data_off[i]), map->data_elf_off[i], + swap32(map->header.data_size[i])); + } + + if(verbosity >= 2) + fprintf(stderr, "All done!\n"); + + fclose(map->elf); + fclose(dolf); +} + +int main(int argc, char **argv) +{ + char **arg; + + if(argc < 2) { + usage(argv[0]); + return 1; + } + arg = &argv[1]; + argc--; + + while(argc && *arg[0] == '-') { + if(!strcmp(*arg, "-h")) { + usage(argv[0]); + return 1; + } else if(!strcmp(*arg, "-v")) { + verbosity++; + } else if(!strcmp(*arg, "--")) { + arg++; + argc--; + break; + } else { + fprintf(stderr, "Unrecognized option %s\n", *arg); + usage(argv[0]); + return 1; + } + arg++; + argc--; + } + if(argc < 2) { + usage(argv[0]); + exit(1); + } + + const char *elf_file = arg[0]; + const char *dol_file = arg[1]; + uint32_t sdata_pdhr = atoi(arg[2]); + uint32_t sbss_pdhr = atoi(arg[3]); + const char *platform = arg[4]; + + DOL_map map; + + memset(&map, 0, sizeof(map)); + + read_elf_segments(&map, elf_file, sdata_pdhr, sbss_pdhr, platform); + map_dol(&map); + write_dol(&map, dol_file); + + return 0; +} |