diff options
author | ProjectRevoTPP <projectrevotpp@hotmail.com> | 2020-08-26 14:31:15 -0400 |
---|---|---|
committer | ProjectRevoTPP <projectrevotpp@hotmail.com> | 2020-08-26 14:31:15 -0400 |
commit | 875b7e0a43f28919894e6d7d2cad7ee6ae0e8f95 (patch) | |
tree | 2c62860e5d1640632eecdd27f85815302c1ce3c7 /tools |
initial commit
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Makefile | 8 | ||||
-rw-r--r-- | tools/elf2dol.c | 505 |
2 files changed, 513 insertions, 0 deletions
diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..311b6b8 --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,8 @@ +CC := gcc +CFLAGS := -O3 -Wall -s + +elf2dol: elf2dol.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + $(RM) elf2dol
\ No newline at end of file diff --git a/tools/elf2dol.c b/tools/elf2dol.c new file mode 100644 index 0000000..5cc4117 --- /dev/null +++ b/tools/elf2dol.c @@ -0,0 +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;
+}
|