diff --git a/emulator/.gitignore b/emulator/.gitignore index 6cc19e7..8f11c72 100644 --- a/emulator/.gitignore +++ b/emulator/.gitignore @@ -1,3 +1,6 @@ # Original DLL and related ELF CPU73050.dll CPU73050.elf + +# Compiled program +list-registers diff --git a/emulator/Makefile b/emulator/Makefile new file mode 100644 index 0000000..fa4f26c --- /dev/null +++ b/emulator/Makefile @@ -0,0 +1,5 @@ +list-registers: list-registers.c + gcc $< -o $@ -Wall -Wextra -O2 + +CPU73050.elf: CPU73050.dll + objcopy CPU73050.dll -O elf32-i386 CPU73050.elf diff --git a/emulator/list-registers.c b/emulator/list-registers.c new file mode 100644 index 0000000..f21cb57 --- /dev/null +++ b/emulator/list-registers.c @@ -0,0 +1,330 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +//--- +// Structure of CPU73050.dll +//--- + +/* First is an address to the list of register tables + struct reg (*TABLE_LIST_ADDRESS)[][] + (second-level arrays are not stored contiguously) */ +uint32_t const TABLE_LIST_ADDRESS = 0x100903e0; +/* Just following is the list of peripheral modules + struct module MODULE_LIST[] + Unlike register tables this is not a pointer, it's the whole table */ +uint32_t const MODULE_LIST = 0x100903e4; + +/* Each module has the following structure (yet to be discovered entirely) */ +struct module +{ + uint32_t pointer_to_name; + uint32_t pointer_to_description; + uint32_t unknown[28]; +}; +/* Each peripheral register has this structure */ +struct reg +{ + uint32_t pointer_to_name; + uint32_t pointer_to_description; + uint32_t unknown_1; + uint32_t address; + uint32_t unknown_2[2]; + uint32_t data_size; + uint32_t access_size; + uint32_t unknown_3[2]; + + uint32_t f1_other; + uint32_t f1_u8; + uint32_t f1_u16; + uint32_t f1_u32; + + uint32_t f2_other; + uint32_t f2_u8; + uint32_t f2_u16; + uint32_t f2_u32; + + uint32_t unknown_6[13]; +}; + +//--- +// ELF utilities +//--- + +void check_header(Elf32_Ehdr *h) +{ + /* Signature */ + assert(h->e_ident[EI_MAG0] == ELFMAG0); + assert(h->e_ident[EI_MAG1] == ELFMAG1); + assert(h->e_ident[EI_MAG2] == ELFMAG2); + assert(h->e_ident[EI_MAG3] == ELFMAG3); + /* Class */ + assert(h->e_ident[EI_CLASS] == ELFCLASS32); + /* Little endian */ + assert(h->e_ident[EI_DATA] == ELFDATA2LSB); + /* Must have a section table */ + assert(h->e_shoff != 0); +} + +Elf32_Shdr *get_section(Elf32_Ehdr *h, int id) +{ + return (void *)h + h->e_shoff + id * h->e_shentsize; +} + +/* Get data from virtual address by searching all sections */ +void *get_virtual(Elf32_Ehdr *elf, uint32_t address) +{ + for(int id = 0; id < elf->e_shnum; id++) + { + /* Skip sections of uninteresting types */ + Elf32_Shdr *section = get_section(elf, id); + int type = section->sh_type; + if(type == SHT_NULL || type == SHT_STRTAB) continue; + + /* Check if in bounds of virtual address */ + int32_t offset = address - section->sh_addr; + if(offset < 0 || offset >= (int32_t)section->sh_size) continue; + + return (void *)elf + section->sh_offset + offset; + } + + return NULL; +} + +void print_sections(Elf32_Ehdr *elf) +{ + Elf32_Shdr *strtable_section = get_section(elf, elf->e_shstrndx); + char const *strtable = (void *)elf + strtable_section->sh_offset; + + printf("Sections:\n"); + printf(" Name Size Virtual\n"); + + for(int id = 0; id < elf->e_shnum; id++) + { + Elf32_Shdr *section = get_section(elf, id); + int type = section->sh_type; + if(type == SHT_NULL || type == SHT_STRTAB) continue; + + char const *name = strtable + section->sh_name; + printf(" %2d %-8s %08x %08x\n", id, name, section->sh_size, + section->sh_addr); + } + +} + +//--- +// Data analysis heuristics +//--- + +int valid_looking_string(uint8_t const *str) +{ + int i = 0; + while(i < 256 && str[i] >= 32 && str[i] <= 0x7f) i++; + return (i > 0) && (i < 256) && !str[i]; +} + +int valid_looking_module(Elf32_Ehdr *elf, struct module *m) +{ + /* Check that there are valid printable ASCII name and description */ + if((m->pointer_to_name & 0xfff00000) != 0x10000000) return 0; + if((m->pointer_to_description & 0xfff00000) != 0x10000000) return 0; + + uint8_t const *name = get_virtual(elf, m->pointer_to_name); + if(!valid_looking_string(name)) return 0; + + return 1; +} + +int valid_looking_reg(Elf32_Ehdr *elf, struct reg *r) +{ + /* Check that there is a valid name */ + if((r->pointer_to_name & 0xfff00000) != 0x10000000) return 0; + if((r->pointer_to_description & 0xfff00000) != 0x10000000) return 0; + + /* Check that the name is printable ASCII */ + uint8_t const *name = get_virtual(elf, r->pointer_to_name); + if(!valid_looking_string(name)) return 0; + + return 1; +} + +//--- +// Analysis of CPU73050.elf to find SH7305 register lists +//--- + +void print_module(Elf32_Ehdr *elf, struct module *m) +{ + if(m == NULL) + { + printf(" (Virtual) Name: Description:\n"); + return; + } + + char const *name = get_virtual(elf, m->pointer_to_name); + char const *desc = get_virtual(elf, m->pointer_to_description); + printf(" %-10s %s\n", name, desc); +} + +void print_reg_f(char str[8][9], ...) +{ + va_list args; + va_start(args, str); + + for(int i = 0; i < 8; i++) + { + uint32_t ptr = va_arg(args, uint32_t); + if(ptr == 0) sprintf(str[i], "%-8s", ""); + else sprintf(str[i], "%08x", ptr); + } + + va_end(args); +} + +void print_reg(Elf32_Ehdr *elf, struct reg *r) +{ + if(r == NULL) + { + printf(" (Virtual) Name: Address: Data size: " + "Access size: f1_u8 f2_u8 f1_u16 f2_u16 " + "f1_u32 f2_u32 f1_def f2_def Description:\n"); + return; + } + + char const *name = get_virtual(elf, r->pointer_to_name); + char const *desc = get_virtual(elf, r->pointer_to_description); + + char s1[9], s2[9]; + + sprintf(s1, "%08x", r->data_size); + if(r->data_size == 1) sprintf(s1, "u8"); + if(r->data_size == 2) sprintf(s1, "u16"); + if(r->data_size == 4) sprintf(s1, "u32"); + + sprintf(s2, "%08x", r->access_size); + if(r->access_size == 1) sprintf(s2, "u8"); + if(r->access_size == 2) sprintf(s2, "u16"); + if(r->access_size == 4) sprintf(s2, "u32"); + + char fstr[8][9]; + print_reg_f(fstr, r->f1_u8, r->f2_u8, r->f1_u16, r->f2_u16, r->f1_u32, + r->f2_u32, r->f1_other, r->f2_other); + + printf(" %-16s %08x %-8s %-8s ", name, r->address, s1, s2); + for(int i = 0; i < 8; i++) printf("%s ", fstr[i]); + printf(" %s\n", desc); +} + +int main(int argc, char **argv) +{ + int e; + assert(sizeof(struct module) == 0x78); + assert(sizeof(struct reg) == 0x7c); + + char const *input = (argc == 1) ? "CPU73050.elf" : argv[1]; + printf("Extracting register lists from %s\n\n", input); + + int fd = open(input, O_RDONLY); + if(fd < 0) { perror("open"); return 1; } + + struct stat statbuf; + e = fstat(fd, &statbuf); + if(e < 0) { perror("fstat"); close(fd); return 1; } + + void *buf = malloc(statbuf.st_size); + if(!buf) { perror("malloc"); close(fd); return 1; } + + e = read(fd, buf, statbuf.st_size); + close(fd); + if(e != statbuf.st_size) { perror("read"); return 1; } + + /** Parse ELF header **/ + + Elf32_Ehdr *elf = buf; + check_header(elf); + print_sections(elf); + + /** List modules **/ + + struct module *modules = get_virtual(elf, MODULE_LIST); + printf("\nModule list:\n"); + print_module(elf, NULL); + + for(int i = 0; 1; i++) + { + struct module *m = &modules[i]; + if(!valid_looking_module(elf, m)) break; + printf(" %08x ", MODULE_LIST + i*(int)sizeof(struct module)); + print_module(elf, m); + } + + /** List virtual addresses of tables **/ + + uint32_t TABLE_LIST = *(uint32_t *)get_virtual(elf,TABLE_LIST_ADDRESS); + uint32_t *tables = get_virtual(elf, TABLE_LIST); + + printf("\nThe table list at %08x (HARDCODED) has these tables:\n", + TABLE_LIST); + + for(int i = 0; tables[i]; i++) + { + printf(" %08x", tables[i]); + if((i % 8) == 7 || !tables[i+1]) printf("\n"); + } + + /** List contents of tables **/ + + int total = 0; + + for(int i = 0; tables[i]; i++) + { + uint32_t entry = tables[i]; + struct reg *table = get_virtual(elf, entry); + + char header[256]; + sprintf(header, "\nIn the table at %08x:\n", entry); + int header_printed = 0; + + int k = 0; + while(valid_looking_reg(elf, table + k)) + { + if(!header_printed) + { + printf("%s", header); + print_reg(elf, NULL); + header_printed = 1; + } + + struct reg *r = table + k; + printf(" %08x ", entry + k * (int)sizeof(struct reg)); + print_reg(elf, r); + + k++; + } + total += k; + + if(k == 0) + { + /* Try to view it as a string */ + uint8_t const *str = (void *)table; + if(valid_looking_string(str)) + { + printf("\nThe table at %08x is a string:\n", + entry); + printf(" %s\n", str); + } + else printf("\nNothing in the table at %08x\n", entry); + } + } + + free(elf); + return 0; +}