#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SWAP_ENDIAN_16BIT(num) (((num&0xff00)>>8)|((num&0x00ff)<<8))
#define SWAP_ENDIAN_32BIT(num) (((num&0xff000000)>>24)|((num&0x00ff0000)>>8)|((num&0x0000ff00)<<8)|((num&0x000000ff)<<24))
#define ELF_HEADER_SIZE 52
#define DOL_HEADER_SIZE 256
#define SECTION_SIZE 32
#define BSS_SECTION_SIZE 32
#define TEXT_OFFSET_OFFSET 4 // TEXT_ADDRESS_OFFSET always immediately follows this entry
#define TEXT_SIZE_OFFSET 16
#define BSS_ADDRESS_OFFSET 8
#define BSS_SIZE_OFFSET 20
#define DATA_OFFSET_OFFSET 4 // DATA_ADDRESS_OFFSET always immediately follows this entry
#define DATA_SIZE_OFFSET 16
typedef struct {
unsigned long text_offset[7];
unsigned long data_offset[11];
unsigned long text_address[7];
unsigned long data_address[11];
unsigned long text_size[7];
unsigned long data_size[11];
unsigned long bss_address;
unsigned long bss_size;
unsigned long entry_point;
} dol_header;
int main (int argc, const char * argv[])
{
FILE *dol_file, *elf_file;
long dol_file_size;
void *dol_buffer;
dol_header dh;
short f, num_text_sections=0, num_data_sections=0, total_sections=0, elf_header_size;
static char elf_header[52] = {0x7f,0x45,0x4c,0x46,1,2,1,0,0,0,0,0,0,0,0,0,0,2,0,0x14,0,0,0,1,0,0,0,0,0,0,0,0x34,0,0,0,0,0x80,0,0,0,0,0x34,0,0x20,0,0,0,0,0,0,0,0};
static char text_section[32] = {0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0,0,0,32};
static char data_section[32] = {0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,32};
static char bss_section[32] = {0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,32};
static char padding[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
static char prog_version[80] = "dol2elf v0.1 - cmonkey 2012\n\n";
if(argc != 3) {
printf("%s", prog_version);
printf("This program will convert a retail DOL file back into an ELF file\n");
printf("which will be compatible with makedol.exe\n\n");
printf("Usage : dol2elf <input_dol_file> <output_elf_file>\n");
return 0;
}
// try and open the DOL file. Exit if we can't open it.
dol_file = fopen(argv[1], "rb");
if(!dol_file) {
printf("Couldn't open DOL for reading. Check filename is correct\n");
return 0;
}
for(f=0;f<7;f++)
fread(&dh.text_offset[f], 1, 4, dol_file);
for(f=0;f<11;f++)
fread(&dh.data_offset[f], 1, 4, dol_file);
for(f=0;f<7;f++)
fread(&dh.text_address[f], 1, 4, dol_file);
for(f=0;f<11;f++)
fread(&dh.data_address[f], 1, 4, dol_file);
for(f=0;f<7;f++)
fread(&dh.text_size[f], 1, 4, dol_file);
for(f=0;f<11;f++)
fread(&dh.data_size[f], 1, 4, dol_file);
// get BSS address
fread(&dh.bss_address, 1, 4, dol_file);
// get BSS size
fread(&dh.bss_size, 1, 4, dol_file);
// get module entry point
fread(&dh.entry_point, 1, 4, dol_file);
// display details from DOL header
printf("%s", prog_version);
printf("BSS address:\t%08lX\nBSS size:\t%08lX\n\n", SWAP_ENDIAN_32BIT(dh.bss_address), SWAP_ENDIAN_32BIT(dh.bss_size));
printf("Entry point:\t%08lX\n\n", SWAP_ENDIAN_32BIT(dh.entry_point));
for(f=0;f<7;f++) {
if(dh.text_offset[f] != 0 && dh.text_address[f] != 0 && dh.text_size[f] != 0) {
printf("Text section %d: Offset=%08lX Address=%08lX Size=%08lX\n", f, SWAP_ENDIAN_32BIT(dh.text_offset[f]), SWAP_ENDIAN_32BIT(dh.text_address[f]), SWAP_ENDIAN_32BIT(dh.text_size[f]));
num_text_sections++;
}
}
printf("\n");
for(f=0;f<11;f++) {
if(dh.data_offset[f] != 0 && dh.data_address[f] != 0 && dh.data_size[f] != 0) {
printf("Data section %d: Offset=%08lX Address=%08lX Size=%08lX\n", f, SWAP_ENDIAN_32BIT(dh.data_offset[f]), SWAP_ENDIAN_32BIT(dh.data_address[f]), SWAP_ENDIAN_32BIT(dh.data_size[f]));
num_data_sections++;
}
}
total_sections = num_data_sections + num_text_sections + 1; // + 1 for BSS section
printf("\nNumber of text sections: %d\n", num_text_sections);
printf("Number of data sections: %d\n", num_data_sections);
// calculate size of ELF header
elf_header_size = 52 + (total_sections * SECTION_SIZE) + 12; // 52 bytes for ELF header + 12 bytes for padding
printf("\nSize of ELF header will be %d bytes\n", elf_header_size);
elf_file = fopen(argv[2],"wb");
if(!elf_file) {
printf("Couldn't open ELF for writing. Check you have write access to the directory.\n");
fclose(dol_file);
return 0;
}
// write the ELF header
fwrite(&elf_header,1,52,elf_file);
// insert the module entry point into the header
fseek(elf_file,24,SEEK_SET);
fwrite(&dh.entry_point,1,4,elf_file);
// insert total number of program sections into the header
fseek(elf_file,44,SEEK_SET);
total_sections = SWAP_ENDIAN_16BIT(total_sections);
fwrite(&total_sections,1,2,elf_file);
total_sections = SWAP_ENDIAN_16BIT(total_sections);
// move file pointer to end of ELF header
fseek(elf_file, 52, SEEK_SET); // ELF header is 52 bytes long
// write the text sections
for(f=0;f<num_text_sections;f++) {
fwrite(&text_section, 1, 32, elf_file);
}
// write the BSS section
fwrite(&bss_section, 1, 32, elf_file);
// write the data sections
for(f=0;f<num_data_sections;f++) {
fwrite(&data_section, 1, 32, elf_file);
}
// finally pad the ELF header
fwrite(&padding, 1, 12, elf_file);
// move file pointer to end of ELF header
fseek(elf_file, 52, SEEK_SET);
// now the ELF header is written we can go back and 'fill in the blanks'!
// first we fill in the blanks for the text sections
for(f=0;f<num_text_sections;f++) {
dh.text_offset[f] = SWAP_ENDIAN_32BIT(dh.text_offset[f])+elf_header_size-DOL_HEADER_SIZE;
dh.text_offset[f] = SWAP_ENDIAN_32BIT(dh.text_offset[f]);
fseek(elf_file, TEXT_OFFSET_OFFSET+(f*SECTION_SIZE)+ELF_HEADER_SIZE, SEEK_SET);
fwrite(&dh.text_offset[f], 1, 4, elf_file);
fwrite(&dh.text_address[f], 1, 4, elf_file);
fseek(elf_file, TEXT_SIZE_OFFSET+(f*SECTION_SIZE)+ELF_HEADER_SIZE, SEEK_SET);
fwrite(&dh.text_size[f], 1, 4, elf_file);
fwrite(&dh.text_size[f], 1, 4, elf_file); // text size is written twice
}
// next we fill in the blanks in the BSS section
fseek(elf_file, BSS_ADDRESS_OFFSET+(num_text_sections*SECTION_SIZE)+ELF_HEADER_SIZE, SEEK_SET);
fwrite(&dh.bss_address, 1, 4, elf_file);
fseek(elf_file, BSS_SIZE_OFFSET+(num_text_sections*SECTION_SIZE)+ELF_HEADER_SIZE, SEEK_SET);
fwrite(&dh.bss_size, 1, 4, elf_file);
// finally we fill in the blanks for the data sections
for(f=0;f<num_data_sections;f++) {
dh.data_offset[f] = SWAP_ENDIAN_32BIT(dh.data_offset[f])+elf_header_size-DOL_HEADER_SIZE;
dh.data_offset[f] = SWAP_ENDIAN_32BIT(dh.data_offset[f]);
fseek(elf_file, DATA_OFFSET_OFFSET+(f*SECTION_SIZE)+ELF_HEADER_SIZE+(num_text_sections*SECTION_SIZE)+BSS_SECTION_SIZE, SEEK_SET);
fwrite(&dh.data_offset[f], 1, 4, elf_file);
fwrite(&dh.data_address[f], 1, 4, elf_file);
fseek(elf_file, DATA_SIZE_OFFSET+(f*SECTION_SIZE)+ELF_HEADER_SIZE+(num_text_sections*SECTION_SIZE)+BSS_SECTION_SIZE, SEEK_SET);
fwrite(&dh.data_size[f], 1, 4, elf_file);
fwrite(&dh.data_size[f], 1, 4, elf_file); // data size is written twice
}
// that's the ELF header done with, now we need to strap the rest of the DOL file onto the end of the ELF header to complete the file
fseek(dol_file, 0, SEEK_END);
dol_file_size = ftell(dol_file)-DOL_HEADER_SIZE;
printf("Size of DOL content (entire file minus DOL header) = %ld bytes\n", dol_file_size);
printf("Size of final ELF should be %ld bytes\n", dol_file_size+elf_header_size);
fseek(dol_file, DOL_HEADER_SIZE, SEEK_SET); // move file pointer to first byte of DOL content
dol_buffer = (char *)malloc(dol_file_size); // allocate memory to hold the content of the DOL file
if(!dol_buffer) {
printf("Unable to allocate memory for reading contents of DOL file. Application will now terminate.\n");
fclose(dol_file);
fclose(elf_file);
exit(1);
}
fread(dol_buffer, 1, dol_file_size, dol_file);
fseek(elf_file, elf_header_size, SEEK_SET); // make sure we're in correct position in ELF file to start writing the content
fwrite(dol_buffer, 1, dol_file_size, elf_file);
free(dol_buffer); // free the memory now that we're done with it
// finally close the two files
fclose(dol_file);
fclose(elf_file);
return 0;
}