diff --git a/TODO.txt b/TODO.txt index 376fc4b..8c19676 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,10 +1,11 @@ @update * common EXE to continue * call uninit() to each menu (disasm = set the next break point) ! -* fix fixed size space character +* fix fixed size 'space' character * disasm circular buffer (code only) @feature -* night mode -* call graph * call graph export for laptop +* input box +* command management for each menu +* night mode diff --git a/gintrace.g3a b/gintrace.g3a index f64716f..87325f3 100644 Binary files a/gintrace.g3a and b/gintrace.g3a differ diff --git a/include/gintrace/menu/callgraph.h b/include/gintrace/menu/callgraph.h new file mode 100644 index 0000000..c9897ab --- /dev/null +++ b/include/gintrace/menu/callgraph.h @@ -0,0 +1,39 @@ +#ifndef __GINTRACE_MENU_CALLGRAPH_H__ +# define __GINTRACE_MENU_CALLGRAPH_H__ + +#include +#include + +#include "gintrace/ubc.h" + +struct callnode { + struct ucontext context; + uintptr_t address; + enum { + callnode_type_root = 0, + callnode_type_bsr = 1, + callnode_type_bsrf = 2, + callnode_type_jsr = 3, + callnode_type_jmp = 4, + callnode_type_rte = 5, + callnode_type_rts = 6, + } type; + struct callnode *parent; + struct callnode *sibling; + struct callnode *child; +}; + +struct callgraph { + struct { + int hoffset; + int voffset; + } cursor; + struct callnode *root; + struct callnode *parent; + int callnode_counter; +}; + +/* extern menu information */ +extern struct menu menu_callgraph; + +#endif /*__GINTRACE_MENU_CALLGRAPH_H__*/ diff --git a/include/gintrace/ubc.h b/include/gintrace/ubc.h index 249ee27..cf3325a 100644 --- a/include/gintrace/ubc.h +++ b/include/gintrace/ubc.h @@ -127,13 +127,13 @@ struct sh7305_ubc_context { //--- /* ucontext: UBC context */ struct ucontext { - uint32_t reg[16]; - uint32_t gbr; - uint32_t macl; - uint32_t mach; - uint32_t ssr; - uint32_t spc; - uint32_t pr; + uintptr_t reg[16]; + uintptr_t gbr; + uintptr_t macl; + uintptr_t mach; + uintptr_t ssr; + uintptr_t spc; + uintptr_t pr; }; //--- @@ -149,6 +149,10 @@ extern int ubc_set_handler(void (*handler)(struct ucontext *ctx)); /* ubc_set_breakpoint(): Setup one breakpoint */ extern int ubc_set_breakpoint(int channel, void *address, void *mask); +/* ubc_block(): Block UBC interruption */ +extern int ubc_block(void); +/* ubc_unblock(): Unblock UBC interruption */ +extern int ubc_unblock(void); //--- // "low-level" interface diff --git a/src/gui/menu.c b/src/gui/menu.c index c1239f5..4aa1812 100644 --- a/src/gui/menu.c +++ b/src/gui/menu.c @@ -142,7 +142,7 @@ int menu_keyboard(struct menu_group *gmenu) if (gmenu == NULL) return (menu_retval_efault); key = getkey().key; - if (key == KEY_EXIT) { + if (key == KEY_EXE) { gmenu->is_open = 1; return (menu_retval_success); } diff --git a/src/main.c b/src/main.c index 2eef100..eb4add1 100644 --- a/src/main.c +++ b/src/main.c @@ -2,27 +2,75 @@ #include "gintrace/menu/disasm.h" #include "gintrace/menu/context.h" #include "gintrace/menu/hexdump.h" +#include "gintrace/menu/callgraph.h" #include "gintrace/ubc.h" #include #include #include +#include + +/* workaround test */ +extern struct tracer tracer; /* save the selected menu */ static struct menu_group *gmenu = NULL; -/* gintrac_handler(): UBC handler */ +/* syscall address */ +static void *syscall = NULL; + +/* gintrac_handler(): UBC handler + * @note: + * To force generate the callgraph, we use a dirty workaround to force break + * at each instruction. But, the disassembler menu can skip one instruction + * using OPTN key, so you should not "unskip" the user action. */ static void gintrace_handler(struct ucontext *context) { + static uintptr_t breakpoint = 0x00000000; + static uintptr_t spc = 0x00000000; + + /* force disable the UBC to avoid error */ + ubc_block(); + + /* check callgraph job */ + if (breakpoint != 0x00000000 + && spc != 0x00000000 + && spc != breakpoint) { + menu_callgraph.init(context); + spc = context->spc; + ubc_set_breakpoint(0, (void*)context->spc, NULL); + ubc_unblock(); + return; + } + + /* user break point */ gint_switch_to_gint(); menu_init(gmenu, context); while (menu_is_open(gmenu) == 0) { menu_draw(gmenu); menu_keyboard(gmenu); } + + /* if no instruction skip, restore */ + spc = context->spc; + ubc_set_breakpoint(0, (void*)context->spc, NULL); + breakpoint = tracer.next_break; + + /* unblock UBC interrupt */ + ubc_unblock(); gint_switch_to_casio(); } +/* casio_handler(): Casio handler */ +static void casio_handler(void) +{ + void (*bfile_openfile_os)(const uint16_t *filename, int mode, int p3); + + bfile_openfile_os = syscall; + bfile_openfile_os(u"\\fls0\\dyntest", BFile_ReadOnly, 0); +} + +/* main(): User entry */ int main(void) { /* initialize all internal menu and get the "first" menu to display */ @@ -30,16 +78,22 @@ int main(void) menu_register(gmenu, &menu_disasm, "Disasm"); menu_register(gmenu, &menu_context, "Context"); menu_register(gmenu, &menu_hexdump, "Hexdump"); - menu_register(gmenu, NULL, "CallG"); + menu_register(gmenu, &menu_callgraph, "CallG"); + + /* get syscall address */ + void **systab = *(void ***)0x8002007c; + syscall = systab[0x1da3]; + /* intialize UBC information */ ubc_install(); ubc_set_handler(&gintrace_handler); //ubc_set_breakpoint(0, (void*)0x80358a6c, NULL); - ubc_set_breakpoint(0, (void*)0x80358a7e, NULL); + ubc_set_breakpoint(0, syscall, NULL); /* try to trace the function */ - gint_switch((void *)0x80358a6c); + //gint_switch((void *)0x80358a6c); // stack menu (syscall %1e66) + gint_switch((void*)&casio_handler); //TODO : destructor part !! return (0); diff --git a/src/menu/callgraph/callgraph.c b/src/menu/callgraph/callgraph.c new file mode 100644 index 0000000..4b1f7ad --- /dev/null +++ b/src/menu/callgraph/callgraph.c @@ -0,0 +1,271 @@ +#include "gintrace/menu/callgraph.h" +#include "gintrace/ubc.h" +#include "gintrace/gui/menu.h" +#include "gintrace/gui/display.h" + +#include +#include +#include +#include +#include + +#include "./src/menu/disassembler/dictionary.h" + +/* define the menu information */ +/* TODO: find a way to have local information (session) */ +struct callgraph callgraph; + +//--- +// callode management +//--- +/* callnode_create(): create a new callnode */ +static struct callnode *callnode_create(struct callnode *parent, + struct ucontext *context, + int type, uintptr_t address) +{ + struct callnode *node; + + node = calloc(sizeof(struct callnode), 1); + if (node != NULL) { + memcpy(&node->context, context, sizeof(struct ucontext)); + node->type = type; + node->parent = parent; + node->address = address; + callgraph.callnode_counter += 1; + } + return (node); +} + +/* callnode_add_child(): Add child into the child queue */ +static void callnode_add_child(struct callnode *parent, struct callnode *child) +{ + struct callnode **sibling; + + sibling = &parent->child; + while (*sibling != NULL) + sibling = &(*sibling)->sibling; + *sibling = child; +} + +/* callnode_display(): Display callnode information */ +static void callnode_display(struct callnode *node, uint32_t bitmap[4], + int *row, int depth) +{ + static char line[256]; + char shift; + int idx; + int i; + + if (node == NULL || *row + callgraph.cursor.voffset >= GUI_DISP_NB_ROW) + return; + if (*row + callgraph.cursor.voffset < 0) { + *row = *row + 1; + callnode_display(node->child, bitmap, row, depth + 1); + callnode_display(node->sibling, bitmap, row, depth); + return; + } + + /* handle the bitmap (ugly / 20) */ + i = -1; + idx = 0; + while (++i < depth) { + idx = i >> 6; + if (idx >= 4) + break; + shift = i & 0x1f; + if ((bitmap[idx] & (1 << shift)) != 0) { + dtext((2 + callgraph.cursor.hoffset + (i << 2)) + * (FWIDTH + 1), (*row + + callgraph.cursor.voffset) + * (FHEIGHT + 1), C_BLACK, "| "); + } + } + + /* generate the line */ + char pipe = '|'; + const char *type = "(err)"; + bitmap[idx] |= 1 << (depth & 0x1f); + if (node->type == callnode_type_root) + type = "(root)"; + if (node->type == callnode_type_bsr) + type = "(bsr)"; + if (node->type == callnode_type_bsrf) + type = "(bsrf)"; + if (node->type == callnode_type_jsr) + type = "(jsr)"; + if (node->type == callnode_type_jmp) { + bitmap[idx] &= ~(1 << (depth & 0x1f)); + type = "(jmp)"; + pipe = '`'; + } + if (node->type == callnode_type_rte) { + bitmap[idx] &= ~(1 << (depth & 0x1f)); + type = "(rte)"; + pipe = '`'; + } + if (node->type == callnode_type_rts) { + bitmap[idx] &= ~(1 << (depth & 0x1f)); + type = "(rts)"; + pipe = '`'; + } + const char *addrname = + disasm_dictionary_check_syscalls((void*)node->address); + if (addrname == NULL) { + snprintf(line, 256, "%s %p", type, (void*)node->address); + } else { + snprintf(line, 256, "%s %p - %s", + type, (void*)node->address, addrname); + } + + /* display the line then check child and siblig */ + if (depth < 0) { + dtext((callgraph.cursor.hoffset + (i << 2)) * (FWIDTH + 1), + (*row + callgraph.cursor.voffset) * (FHEIGHT + 1), + C_BLACK, line); + } else { + dprint((2 + callgraph.cursor.hoffset + (i << 2)) * (FWIDTH + 1), + (*row + callgraph.cursor.voffset) * (FHEIGHT + 1), C_BLACK, + "%c-- %s", pipe, line); + } + *row = *row + 1; + callnode_display(node->child, bitmap, row, depth + 1); + callnode_display(node->sibling, bitmap, row, depth); +} + +//--- +// Menu +//--- + +/* callgraph_ctor: Menu constructor */ +static void callgraph_ctor(void) +{ + callgraph.cursor.hoffset = 0; + callgraph.cursor.voffset = 0; + callgraph.root = NULL; +} + +/* callgraph_init(): Invoked each time a break point occur */ +/* FIXME: recursive !! */ +static void callgraph_init(struct ucontext *context) +{ + struct callnode *node; + uintptr_t address; + uint16_t *pc; + uint32_t b; + int type; + + /* check root node */ + if (callgraph.root == NULL) { + node = callnode_create(NULL, context, + callnode_type_root, context->spc); + if (node == NULL) + return; + callgraph.root = node; + callgraph.parent = callgraph.root; + } + + /* check error */ + if (callgraph.parent == NULL) + return; + + /* check opcode */ + type = -1; + pc = (void*)(uintptr_t)context->spc; + if ((pc[0] & 0xf000) == 0xb000) { + type = callnode_type_bsr; + b = pc[0] & 0x0fff; + if ((b & 0x800) != 0) + b = (0xfffff000 | b); + address = context->spc + 4 + (b << 1); + } + if ((pc[0] & 0xf0ff) == 0x0003) { + type = callnode_type_bsrf; + b = (pc[0] & 0x0f00) >> 8; + address = context->reg[b]; + } + if ((pc[0] & 0xf0ff) == 0x400b) { + type = callnode_type_jsr; + b = (pc[0] & 0x0f00) >> 8; + address = context->reg[b]; + } + if ((pc[0] & 0xf0ff) == 0x402b) { + type = callnode_type_jmp; + b = (pc[0] & 0x0f00) >> 8; + address = context->reg[b]; + } + if ((pc[0] & 0xffff) == 0x002b) { + type = callnode_type_rte; + address = context->spc; + } + if ((pc[0] & 0xffff) == 0x000b) { + type = callnode_type_rts; + address = context->spc; + } + if (type == -1) + return; + + /* generate the node then link with its sibling */ + node = callnode_create(callgraph.parent, context, type, address); + if (node == NULL) + return; + callnode_add_child(callgraph.parent, node); + + /* find the next "current" node */ + if (node->type == callnode_type_bsr + || node->type == callnode_type_jsr + || node->type == callnode_type_bsrf + || node->type == callnode_type_jmp) { + callgraph.parent = node; + return; + } + while (callgraph.parent != NULL) { + if (callgraph.parent->type == callnode_type_jmp) { + callgraph.parent = callgraph.parent->parent; + continue; + } + callgraph.parent = callgraph.parent->parent; + return; + } +} + +/* callgraph_display(); Display trace information */ +static void callgraph_display(struct ucontext *context) +{ + uint32_t bitmap[4]; + int row; + + (void)context; + row = 0; + memset(bitmap, 0x00, sizeof(bitmap)); + callnode_display(callgraph.root, bitmap, &row, -1); +} + +/* callgraph_keyboard(): Handle one key event */ +/* TODO: remove the cursor, handle node selection ! */ +static int callgraph_keyboard(struct ucontext *context, int key) +{ + (void)context; + if (key == KEY_LEFT) + callgraph.cursor.hoffset += 1; + if (key == KEY_RIGHT) + callgraph.cursor.hoffset -= 1; + + if (key == KEY_UP) + callgraph.cursor.voffset += 1; + if (key == KEY_DOWN) + callgraph.cursor.voffset -= 1; + + return (0); +} + +//--- +// Define the menu +//--- +struct menu menu_callgraph = { + .ctor = &callgraph_ctor, + .init = &callgraph_init, + .display = &callgraph_display, + .keyboard = &callgraph_keyboard, + .command = NULL, + .dtor = NULL +}; diff --git a/src/menu/disassembler/disasm.c b/src/menu/disassembler/disasm.c index 901edc1..1ae55d5 100644 --- a/src/menu/disassembler/disasm.c +++ b/src/menu/disassembler/disasm.c @@ -259,7 +259,7 @@ int disasm_display_addr_info(int *row, struct buffcursor *cursor, /* check note */ if (cursor->note_idx != 0) { drect(0, (*row) * (FHEIGHT + 1) - 1, DWIDTH, - ((*row) * (FHEIGHT + 1) - 1) + (FHEIGHT + 2), + ((*row) * (FHEIGHT + 1) - 1) + (FHEIGHT + 1), 0x556655); dtext(tracer.disp.hoffset * (FWIDTH + 1), (*row) * (FHEIGHT + 1), C_WHITE, ptr); @@ -409,6 +409,7 @@ static int disasm_keyboard(struct ucontext *context, int key) int line_idx; /* horizontal update */ + (void)context; if (key == KEY_LEFT) tracer.disp.hoffset += 1; if (key == KEY_RIGHT) @@ -452,14 +453,9 @@ static int disasm_keyboard(struct ucontext *context, int key) /* skip instruction */ if (key == KEY_OPTN) { context->spc = tracer.next_break; - ubc_set_breakpoint(0, (void*)tracer.next_break, NULL); - return (1); - } - /* go to net break point */ - if (key == KEY_EXE) { - ubc_set_breakpoint(0, (void*)tracer.next_break, NULL); return (1); } + return(0); } diff --git a/src/ubc/ubc.c b/src/ubc/ubc.c index 7f1473c..932f557 100644 --- a/src/ubc/ubc.c +++ b/src/ubc/ubc.c @@ -94,3 +94,37 @@ int ubc_set_handler(void (*handler)(struct ucontext *ctxt)) ubc_handler = handler; return (0); } + + +static uint32_t __ubc_block_counter = 0; +static uint32_t __ubc_cbcr_ubde = 0; +static uint32_t __ubc_cbr0_ce = 0; + +/* ubc_block(): Block UBC interruption */ +int ubc_block(void) +{ + if (__ubc_block_counter == 0) { + __ubc_cbr0_ce = SH7305_UBC.CBR0.CE; + __ubc_cbcr_ubde = SH7305_UBC.CBCR.UBDE; + SH7305_UBC.CBCR.UBDE = 0; + SH7305_UBC.CBR0.CE = 0; + ubc_kernel_update(); + } + __ubc_block_counter += 1; + return (0); +} + + +/* ubc_block(): Block UBC interruption */ +int ubc_unblock(void) +{ + if (__ubc_block_counter <= 0) + return (-1); + __ubc_block_counter -= 1; + if (__ubc_block_counter == 0) { + SH7305_UBC.CBCR.UBDE = __ubc_cbcr_ubde; + SH7305_UBC.CBR0.CE = __ubc_cbr0_ce; + ubc_kernel_update(); + } + return (0); +}