fxdoc/emulator/list-registers.c

434 lines
11 KiB
C

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <elf.h>
//---
// Structure of CPU73050.dll
//---
/* Large tabe of [struct module] objects */
uint32_t const MODULE_TABLE = 0x100903e4;
/* List of register tables, pin input tables, pin output tables, and setting
tables (one of each per module) */
uint32_t const REGISTER_TABLE_LIST = 0x10090184;
uint32_t const PIN_INPUT_TABLE_LIST = 0x1009021c;
uint32_t const PIN_OUTPUT_TABLE_LIST = 0x100902b4;
uint32_t const SETTING_TABLE_LIST = 0x1009034c;
/* These function pointers are trivial reads used at a lot of places */
uint32_t const REGu8_fun1_generic = 0x10028982;
uint32_t const REGu16_fun1_generic = 0x10028987;
uint32_t const REGu32_fun1_generic = 0x1002898b;
/* 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_1[4];
uint32_t register_count;
uint32_t pin_input_count;
uint32_t pin_output_count;
uint32_t setting_count;
uint32_t unknown_2[20];
};
/* 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];
};
struct pin_input
{
uint32_t pointer_to_name;
uint32_t pointer_to_description;
uint32_t unknown_1[3];
uint32_t zero_1[6]; /* Always zero in reachable instances */
uint32_t func;
uint32_t zero_2[6]; /* Always zero in reachable instances */
};
struct pin_output
{
uint32_t pointer_to_name;
uint32_t pointer_to_description;
uint32_t unknown[3];
uint32_t zero_1[4]; /* Always zero in reachable instances */
};
struct setting
{
uint32_t pointer_to_name;
uint32_t pointer_to_description;
uint32_t zero_1[1]; /* Always zero in reachable instances */
uint32_t unknown;
uint32_t zero_2[3]; /* Always zero in reachable instances */
uint32_t func1;
uint32_t func2;
uint32_t zero_3[3]; /* Always zero in reachable instances */
};
//---
// 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;
}
//---
// 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][32], ...)
{
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 if(ptr == REGu8_fun1_generic ||
ptr == REGu16_fun1_generic ||
ptr == REGu32_fun1_generic)
{
sprintf(str[i], "\x1b[32m%08x\x1b[0m", ptr);
}
else
{
sprintf(str[i], "%08x", ptr);
}
}
va_end(args);
}
void print_reg(Elf32_Ehdr *elf, struct reg *r)
{
if(r == NULL)
{
printf(" Name: Address: Data size: "
"Access size: read8 write8 read16 write16 "
"read32 write32 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][32];
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);
}
void print_pin_input(Elf32_Ehdr *elf, struct pin_input *p)
{
if(p == NULL)
{
printf(" Name: ?1: ?2: ?3: func: "
"Description:\n");
return;
}
char const *name = get_virtual(elf, p->pointer_to_name);
char const *desc = get_virtual(elf, p->pointer_to_description);
printf(" %-8s %08x %08x %08x %08x %s\n", name,
p->unknown_1[0], p->unknown_1[1], p->unknown_1[2],
p->func, desc);
}
void print_pin_output(Elf32_Ehdr *elf, struct pin_output *p)
{
if(p == NULL)
{
printf(" Name: ?1: ?2: ?3: "
"Description:\n");
return;
}
char const *name = get_virtual(elf, p->pointer_to_name);
char const *desc = get_virtual(elf, p->pointer_to_description);
printf(" %-8s %08x %08x %08x %s\n", name,
p->unknown[0], p->unknown[1], p->unknown[2], desc);
}
void print_setting(Elf32_Ehdr *elf, struct setting *s)
{
if(s == NULL)
{
printf(" Name: ?1: func1: func2: "
"Description:\n");
return;
}
char const *name = get_virtual(elf, s->pointer_to_name);
char const *desc = get_virtual(elf, s->pointer_to_description);
printf(" %-8s %08x %08x %08x %s\n", name, s->unknown,
s->func1, s->func2, 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);
/** Reminder of extracted addresses **/
printf("\nAddresses extracted via reverse-engineering:\n");
printf(" Module table: 100903e4\n");
printf(" Register table list: 10090184\n");
printf(" Pin input table list: 1009021c\n");
printf(" Pin output table list: 100902b4\n");
printf(" Setting table list: 1009034c\n");
/** List modules **/
struct module *modules = get_virtual(elf, MODULE_TABLE);
printf("\nModule list:\n");
print_module(elf, NULL);
int i = 0;
while(1)
{
struct module *m = &modules[i];
if(!valid_looking_module(elf, m)) break;
printf(" %08x ", MODULE_TABLE + i*(int)sizeof(struct module));
print_module(elf, m);
i++;
}
int module_count = i - 1;
printf("Total: %d modules (excluding CPU73050)\n", module_count);
/** Print information for each module **/
uint32_t *register_tables = get_virtual(elf, REGISTER_TABLE_LIST);
uint32_t *pin_input_tables = get_virtual(elf, PIN_INPUT_TABLE_LIST);
uint32_t *pin_output_tables = get_virtual(elf, PIN_OUTPUT_TABLE_LIST);
uint32_t *setting_tables = get_virtual(elf, SETTING_TABLE_LIST);
int entry_count = 0;
for(int i = 0; i < module_count; i++)
{
struct reg *regs = get_virtual(elf, register_tables[i]);
struct pin_input *pin_inputs = get_virtual(elf, pin_input_tables[i]);
struct pin_output *pin_outputs = get_virtual(elf, pin_output_tables[i]);
struct setting *settings = get_virtual(elf, setting_tables[i]);
struct module *m = &modules[i];
char *name = get_virtual(elf, m->pointer_to_name);
printf("\nRegister table for %s (%d registers):\n", name,
m->register_count);
if(m->register_count)
{
print_reg(elf, NULL);
for(uint k = 0; k < m->register_count; k++)
print_reg(elf, regs + k);
}
else printf(" No registers!\n");
if(m->pin_input_count)
{
printf("Pin input table for %s (%d pins):\n", name,
m->pin_input_count);
print_pin_input(elf, NULL);
for(uint k = 0; k < m->pin_input_count; k++)
print_pin_input(elf, pin_inputs + k);
}
if(m->pin_output_count)
{
printf("Pin output table for %s (%d pins):\n", name,
m->pin_output_count);
print_pin_output(elf, NULL);
for(uint k = 0; k < m->pin_output_count; k++)
print_pin_output(elf, pin_outputs + k);
}
if(m->setting_count)
{
printf("Setting table for %s (%d settings):\n", name,
m->setting_count);
print_setting(elf, NULL);
for(uint k = 0; k < m->setting_count; k++)
print_setting(elf, settings + k);
}
entry_count += m->register_count;
entry_count += m->pin_input_count;
entry_count += m->pin_output_count;
entry_count += m->setting_count;
}
printf("\nFound a total of %d objects.\n", entry_count);
free(elf);
return 0;
}