From 8dba715297f6904e053bef61ea411ee4956195c9 Mon Sep 17 00:00:00 2001 From: Yatis Date: Mon, 8 Mar 2021 21:38:09 +0100 Subject: [PATCH] Gintracer v0.7.0 - Tracing sessions @add * support multiple sessions * support multiple color schematics (night, day, ...) * support OS-specific notes information @update * add stability * syscall information * wrap gint display API * menus interface --- TODO.txt | 3 + gintrace.g3a | Bin 119304 -> 119828 bytes include/gintrace/gui/menu.h | 16 +- include/gintrace/menu/context.h | 2 +- include/gintrace/menu/disasm.h | 8 +- include/gintrace/tracer.h | 55 ++++++ src/gui/menu.c | 10 +- src/main.c | 135 ++++++-------- src/menu/callgraph.c | 155 ++++++++-------- src/menu/context.c | 77 ++++---- src/menu/disasm.c | 312 +++++++++++++++++--------------- src/menu/hexdump.c | 46 ++--- src/tracer.c | 58 ++++++ src/ubc/handler.c | 69 +++++++ utils/a.out | Bin 0 -> 16536 bytes utils/abcd | Bin 0 -> 242455 bytes utils/main.c | 54 ++++++ 17 files changed, 609 insertions(+), 391 deletions(-) create mode 100644 include/gintrace/tracer.h create mode 100644 src/tracer.c create mode 100644 src/ubc/handler.c create mode 100755 utils/a.out create mode 100644 utils/abcd create mode 100644 utils/main.c diff --git a/TODO.txt b/TODO.txt index a49be5b..ecf6a5e 100644 --- a/TODO.txt +++ b/TODO.txt @@ -3,7 +3,10 @@ * fix fx9860 support @update +* context: refacto printXY() ! * hexdump: search commands +* callgraph: improve user interface. +* disasm: set manually multiple breakpoint * savefile * load saved file * list color scheme on SMEM. diff --git a/gintrace.g3a b/gintrace.g3a index 73c19b6f76dfb039f8e45d0e53658e03bc71840a..f309e1f45fbba47d2371a3ce5c331056ecfa346f 100644 GIT binary patch delta 23263 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 SJ(_hv#!kMGA1L^n)&CnI!q<2J delta 22736 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 /* mcontext: menu context internal structure */ -struct mcontext { +struct context { struct { int hoffset; int voffset; diff --git a/include/gintrace/menu/disasm.h b/include/gintrace/menu/disasm.h index e319083..d985956 100644 --- a/include/gintrace/menu/disasm.h +++ b/include/gintrace/menu/disasm.h @@ -1,5 +1,5 @@ -#ifndef __GINTRACE_TRACER_H__ -# define __GINTRACE_TRACER_H__ +#ifndef __GINTRACE_DISASM_H__ +# define __GINTRACE_DISASM_H__ #include #include @@ -17,7 +17,7 @@ struct buffcursor { }; /* tracer: internal information used to display disassembly view */ -struct tracer { +struct disasm { /* circular buffer information. * Note that the circular buffer is very special, it refert, on the * first level, the "instruction address" then, the second level its @@ -53,4 +53,4 @@ struct tracer { /* extern menu information */ extern struct menu menu_disasm; -#endif /*__GINTRACE_TRACER_H__*/ +#endif /*__GINTRACE_DISASM_H__*/ diff --git a/include/gintrace/tracer.h b/include/gintrace/tracer.h new file mode 100644 index 0000000..8594440 --- /dev/null +++ b/include/gintrace/tracer.h @@ -0,0 +1,55 @@ +#ifndef __GINTRACE_TRACER_H__ +# define __GINTRACE_TRACER_H__ + +#include +#include + +#include "gintrace/menu/hexdump.h" +#include "gintrace/menu/callgraph.h" +#include "gintrace/menu/disasm.h" +#include "gintrace/menu/context.h" +#include "gintrace/ubc.h" + +/* tsession: define the trace session information */ +struct tsession { + /* session information */ + struct { + void *starting; + struct ucontext *context; + } info; + + /* display information */ + struct { + struct menu_group *gmenu; + } display; + + /* menu information */ + struct { + struct disasm disasm; + struct callgraph callgraph; + struct context context; + struct hexdump hexdump; + } menu; +}; + +//--- +// User API +//--- +/* tracer_new_session(): Create a new session + * @note + * This function will create a new session based on the given menu + * information and the firt breakpoint address. + * */ +#define TRACER_DISASM (1 << 0) +#define TRACER_CONTEXT (1 << 1) +#define TRACER_HEXDUMP (1 << 2) +#define TRACER_CALLGRAPH (1 << 3) +extern struct tsession *tracer_create_session(void *address, int menu); + +/* tracer_get_session(): Get the current session. */ +extern struct tsession *tracer_get_session(void); + +/* tracer_get_session(): Set the current session. */ +extern struct tsession *tracer_set_session(struct tsession *session); + +#endif /*__GINTRACE_TRACER_H__*/ diff --git a/src/gui/menu.c b/src/gui/menu.c index 5c613d6..cf2b92c 100644 --- a/src/gui/menu.c +++ b/src/gui/menu.c @@ -12,11 +12,12 @@ #include /* menu_create(): Create a group of menus */ -int menu_create(struct menu_group **gmenu) +int menu_create(struct menu_group **gmenu, volatile void *arg) { *gmenu = calloc(sizeof(struct menu_group), 1); if (*gmenu == NULL) return (menu_retval_enomem); + (*gmenu)->arg = arg; return (menu_retval_success); } @@ -38,7 +39,7 @@ int menu_register(struct menu_group *gmenu, if (*node == NULL) return (menu_retval_enomem); if (menu != NULL && menu->ctor != NULL) - menu->ctor(); + menu->ctor((void*)gmenu->arg); if (gmenu->selected == NULL) gmenu->selected = menu; (*node)->menu = menu; @@ -71,7 +72,7 @@ int menu_unregister(struct menu_group *gmenu, const char *name) } /* menu_init(): Initialize all menu */ -int menu_init(struct menu_group *gmenu, volatile void *arg) +int menu_init(struct menu_group *gmenu) { struct menu_list *node; @@ -80,10 +81,9 @@ int menu_init(struct menu_group *gmenu, volatile void *arg) node = gmenu->list; while (node != NULL) { if (node->menu != NULL && node->menu->init != NULL) - node->menu->init((void*)arg); + node->menu->init((void*)gmenu->arg); node = node->next; } - gmenu->arg = arg; gmenu->is_open = 0; return (menu_retval_success); diff --git a/src/main.c b/src/main.c index 171cb8e..12ec687 100644 --- a/src/main.c +++ b/src/main.c @@ -1,90 +1,62 @@ -#include "gintrace/gui/menu.h" -#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 "gintrace/tracer.h" -#include -#include -#include #include +#include +#include +#include -/* workaround test */ -extern struct tracer tracer; - -/* save the selected menu */ -static struct menu_group *gmenu = NULL; - -/* 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 */ - if (tracer.skip == 0) { - spc = context->spc; - ubc_set_breakpoint(0, (void*)context->spc, NULL); - breakpoint = tracer.next_break; - } else { - ubc_set_breakpoint(0, (void*)tracer.next_break, NULL); - breakpoint = tracer.next_break; - spc = 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\\abcdefgijklmn", BFile_ReadOnly, 0); - -#if 0 - void (*debug_menu_filesystem)(void) = syscall; - debug_menu_filesystem(); -#endif -} /* main(): User entry */ int main(void) { + struct tsession *session; + void **systab; + void *syscall; + + /* get syscall address */ + systab = *(void ***)0x8002007c; + //syscall = systab[0x1e48]; // Fugue_debug_menu + syscall = systab[0x1da3]; // Bfile_OpenFile_OS + + + /* prepare tracer */ + session = tracer_create_session(syscall, + TRACER_DISASM | TRACER_CONTEXT | TRACER_HEXDUMP); + if (session == NULL) { + dclear(C_WHITE); + dtext(0, 0, C_BLACK, "Unable to create tracer session"); + dtext(0, 10, C_BLACK, "Press [MENU]..."); + dupdate(); + while (1) { getkey(); } + } + tracer_set_session(session); + + + + //--- + // TEST part + //--- + void (*bfile_openfile_os)(const uint16_t *filename, int mode, int p3); + + gint_switch_to_casio(); + bfile_openfile_os = syscall; + bfile_openfile_os(u"\\\\fls0\\abcdefgijklmn", BFile_ReadOnly, 0); + gint_switch_to_gint(); + + + + //--- + // Epilogue + // TODO: restart the trace ? + //--- + dclear(C_WHITE); + dtext(0, 0, C_BLACK, "session come to the end"); + dtext(0, 10, C_BLACK, "Press [MENU] to exit"); + dupdate(); + while (1) { getkey(); } + return (0); + +#if 0 /* initialize all internal menu and get the "first" menu to display */ menu_create(&gmenu); menu_register(gmenu, &menu_disasm, "Disasm"); @@ -97,6 +69,8 @@ int main(void) //syscall = systab[0x1e48]; // Fugue_debug_menu syscall = systab[0x1da3]; // Bfile_OpenFile_OS + /* prepare tracer */ + tracer_create_session(syscall, TRACER_DISASM | TRACER_CONTEXT | TRACER_HEXDUMP); /* intialize UBC information */ ubc_install(); @@ -108,4 +82,5 @@ int main(void) //TODO : destructor part !! return (0); +#endif } diff --git a/src/menu/callgraph.c b/src/menu/callgraph.c index 3b29b96..8044d8c 100644 --- a/src/menu/callgraph.c +++ b/src/menu/callgraph.c @@ -1,5 +1,6 @@ #include "gintrace/menu/callgraph.h" #include "gintrace/ubc.h" +#include "gintrace/tracer.h" #include "gintrace/gui/menu.h" #include "gintrace/gui/display.h" #include "gintrace/gui/input.h" @@ -13,13 +14,6 @@ #include "./src/menu/internal/dictionary.h" -/* define the menu information */ -/* TODO: find a way to have local information (session) */ -struct callgraph callgraph; - -/* internal buffer */ -static char line[256]; - //--- // callode management //--- @@ -85,7 +79,7 @@ static size_t callnode_generate_info(char *buf, } /* callnode_get_size(): Count the number of bytes that the callgraph take */ -static size_t callnode_export(int fd, struct callnode *node) +static size_t callnode_export(int fd, struct callnode *node, char line[]) { if (node == NULL) return (0); @@ -103,14 +97,19 @@ static size_t callnode_export(int fd, struct callnode *node) } /* check other node */ - size += callnode_export(fd, node->child); - size += callnode_export(fd, node->sibling); + size += callnode_export(fd, node->child, line); + size += callnode_export(fd, node->sibling, line); return (size); } /* callnode_display(): Display callnode information */ -static void callnode_display(struct callnode *node, uint32_t bitmap[4], - int *row, int depth) +struct cdinfo { + struct callgraph *callgraph; + uint32_t bitmap[4]; + int row; +}; +static void callnode_display(struct callnode *node, + struct cdinfo *info, int depth, char line[]) { char shift; char pipe; @@ -119,8 +118,11 @@ static void callnode_display(struct callnode *node, uint32_t bitmap[4], int x; int y; - if (node == NULL || *row + callgraph.cursor.voffset >= GUI_DISP_NB_ROW) + if (node == NULL + || info->row + info->callgraph->cursor.voffset >= GUI_DISP_NB_ROW) { return; + } + /* handle the bitmap (ugly / 20) */ i = -1; idx = 0; @@ -129,40 +131,40 @@ static void callnode_display(struct callnode *node, uint32_t bitmap[4], if (idx >= 4) break; shift = i & 0x1f; - if ((bitmap[idx] & (1 << shift)) != 0 - && *row + callgraph.cursor.voffset >= 0) { - x = callgraph.cursor.hoffset + (i << 2) + 2; - y = callgraph.cursor.voffset + (*row); + if ((info->bitmap[idx] & (1 << shift)) != 0 + && info->row + info->callgraph->cursor.voffset >= 0) { + x = info->callgraph->cursor.hoffset + (i << 2) + 2; + y = info->callgraph->cursor.voffset + info->row; gtextXY(x, y, "| "); } } /* generate the line */ pipe = '|'; - bitmap[idx] |= 1 << (depth & 0x1f); + info->bitmap[idx] |= 1 << (depth & 0x1f); if (node->type == callnode_type_jmp || node->type == callnode_type_rte || node->type == callnode_type_rts) { - bitmap[idx] &= ~(1 << (depth & 0x1f)); + info->bitmap[idx] &= ~(1 << (depth & 0x1f)); pipe = '`'; } /* display the line then check child and siblig */ - if (*row + callgraph.cursor.voffset >= 0) { + if (info->row + info->callgraph->cursor.voffset >= 0) { callnode_generate_info(line, 256, node); if (depth < 0) { - x = callgraph.cursor.hoffset + (i << 2); - y = callgraph.cursor.voffset + (*row); + x = info->callgraph->cursor.hoffset + (i << 2); + y = info->callgraph->cursor.voffset + info->row; gtextXY(x, y, line); } else { - x = callgraph.cursor.hoffset + (i << 2) + 2; - y = callgraph.cursor.voffset + (*row); + x = info->callgraph->cursor.hoffset + (i << 2) + 2; + y = info->callgraph->cursor.voffset + info->row; gprintXY(x, y, "%c-- %s", pipe, line); } } - *row = *row + 1; - callnode_display(node->child, bitmap, row, depth + 1); - callnode_display(node->sibling, bitmap, row, depth); + info->row = info->row + 1; + callnode_display(node->child, info, depth + 1, line); + callnode_display(node->sibling, info, depth, line); } //--- @@ -170,16 +172,15 @@ static void callnode_display(struct callnode *node, uint32_t bitmap[4], //--- /* callgraph_ctor: Menu constructor */ -static void callgraph_ctor(void) +static void callgraph_ctor(struct tsession *session) { - callgraph.cursor.hoffset = 0; - callgraph.cursor.voffset = 0; - callgraph.root = NULL; + session->menu.callgraph.cursor.hoffset = 0; + session->menu.callgraph.cursor.voffset = 0; + session->menu.callgraph.root = NULL; } /* callgraph_init(): Invoked each time a break point occur */ -/* FIXME: recursive !! */ -static void callgraph_init(struct ucontext *context) +static void callgraph_init(struct tsession *session) { struct callnode *node; uintptr_t address; @@ -188,114 +189,114 @@ static void callgraph_init(struct ucontext *context) int type; /* check root node */ - if (callgraph.root == NULL) { - node = callnode_create(NULL, context, - callnode_type_root, context->spc); + if (session->menu.callgraph.root == NULL) { + node = callnode_create(NULL, session->info.context, + callnode_type_root, session->info.context->spc); if (node == NULL) return; - callgraph.root = node; - callgraph.parent = callgraph.root; + session->menu.callgraph.root = node; + session->menu.callgraph.parent = session->menu.callgraph.root; } /* check error */ - if (callgraph.parent == NULL) + if (session->menu.callgraph.parent == NULL) return; /* check opcode */ type = -1; - pc = (void*)(uintptr_t)context->spc; + pc = (void*)(uintptr_t)session->info.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); + address = session->info.context->spc + 4 + (b << 1); } if ((pc[0] & 0xf0ff) == 0x0003) { type = callnode_type_bsrf; b = (pc[0] & 0x0f00) >> 8; - address = context->reg[b]; + address = session->info.context->reg[b]; } if ((pc[0] & 0xf0ff) == 0x400b) { type = callnode_type_jsr; b = (pc[0] & 0x0f00) >> 8; - address = context->reg[b]; + address = session->info.context->reg[b]; } if ((pc[0] & 0xf0ff) == 0x402b) { type = callnode_type_jmp; b = (pc[0] & 0x0f00) >> 8; - address = context->reg[b]; + address = session->info.context->reg[b]; } if ((pc[0] & 0xffff) == 0x002b) { type = callnode_type_rte; - address = context->spc; + address = session->info.context->spc; } if ((pc[0] & 0xffff) == 0x000b) { type = callnode_type_rts; - address = context->spc; + address = session->info.context->spc; } if (type == -1) return; /* generate the node then link with its sibling */ - node = callnode_create(callgraph.parent, context, type, address); + node = callnode_create(session->menu.callgraph.parent, + session->info.context, type, address); if (node == NULL) return; - callnode_add_child(callgraph.parent, node); + callnode_add_child(session->menu.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; + session->menu.callgraph.parent = node; return; } - while (callgraph.parent != NULL) { - if (callgraph.parent->type == callnode_type_jmp) { - callgraph.parent = callgraph.parent->parent; + while (session->menu.callgraph.parent != NULL) { + if (session->menu.callgraph.parent->type == callnode_type_jmp){ + session->menu.callgraph.parent = + session->menu.callgraph.parent->parent; continue; } - callgraph.parent = callgraph.parent->parent; + session->menu.callgraph.parent = + session->menu.callgraph.parent->parent; return; } } /* callgraph_display(); Display trace information */ -static void callgraph_display(struct ucontext *context) +static void callgraph_display(struct tsession *session) { - uint32_t bitmap[4]; - int row; + struct cdinfo info; + char line[256]; - (void)context; - row = 0; - memset(bitmap, 0x00, sizeof(bitmap)); - callnode_display(callgraph.root, bitmap, &row, -1); + memset(&info, 0x00, sizeof(info)); + info.callgraph = &session->menu.callgraph; + callnode_display(session->menu.callgraph.root, &info, -1, line); } /* callgraph_keyboard(): Handle one key event */ -static int callgraph_keyboard(struct ucontext *context, int key) +static int callgraph_keyboard(struct tsession *session, int key) { - (void)context; if (key == KEY_LEFT) - callgraph.cursor.hoffset += 1; + session->menu.callgraph.cursor.hoffset += 1; if (key == KEY_RIGHT) - callgraph.cursor.hoffset -= 1; + session->menu.callgraph.cursor.hoffset -= 1; if (key == KEY_UP) - callgraph.cursor.voffset += 1; + session->menu.callgraph.cursor.voffset += 1; if (key == KEY_DOWN) - callgraph.cursor.voffset -= 1; + session->menu.callgraph.cursor.voffset -= 1; return (0); } /* callgraph_command(): handle user command */ -static void callgraph_command(struct ucontext *context, int argc, char **argv) +static void callgraph_command(struct tsession *session, int argc, char **argv) { /* check useless export */ - (void)context; - if (callgraph.root == NULL) { + if (session->menu.callgraph.root == NULL) { input_write("nothing to export"); return; } @@ -311,7 +312,6 @@ static void callgraph_command(struct ucontext *context, int argc, char **argv) } /* convert the filename (arg2) into Bfile pathname */ - /* TODO: handle special extention */ int i = -1; uint16_t pathname[14 + strlen(argv[1]) + 1]; memcpy(pathname, u"\\\\fls0\\", 14); @@ -322,6 +322,7 @@ static void callgraph_command(struct ucontext *context, int argc, char **argv) /* check if the file exist */ input_write_noint("Check if the file exist"); gint_switch_to_casio(); + char line[256]; int fd = BFile_Open(pathname, BFile_ReadOnly); if (fd >= 0) { gint_switch_to_gint(); @@ -345,7 +346,7 @@ static void callgraph_command(struct ucontext *context, int argc, char **argv) /* create the file then dump information */ gint_switch_to_gint(); - int size = callnode_export(-1, callgraph.root); + int size = callnode_export(-1, session->menu.callgraph.root, line); input_write_noint("Create the file (%d)", size); gint_switch_to_casio(); fd = BFile_Create(pathname, BFile_File, &size); @@ -367,7 +368,7 @@ static void callgraph_command(struct ucontext *context, int argc, char **argv) gint_switch_to_gint(); input_write_noint("Open success, now write..."); gint_switch_to_casio(); - callnode_export(fd, callgraph.root); + callnode_export(fd, session->menu.callgraph.root, line); BFile_Close(fd); gint_switch_to_gint(); input_write("success"); @@ -377,10 +378,10 @@ static void callgraph_command(struct ucontext *context, int argc, char **argv) // Define the menu //--- struct menu menu_callgraph = { - .ctor = &callgraph_ctor, - .init = &callgraph_init, - .display = &callgraph_display, - .keyboard = &callgraph_keyboard, - .command = &callgraph_command, + .ctor = (void*)&callgraph_ctor, + .init = (void*)&callgraph_init, + .display = (void*)&callgraph_display, + .keyboard = (void*)&callgraph_keyboard, + .command = (void*)&callgraph_command, .dtor = NULL }; diff --git a/src/menu/context.c b/src/menu/context.c index 84a90c3..331b701 100644 --- a/src/menu/context.c +++ b/src/menu/context.c @@ -1,5 +1,6 @@ #include "gintrace/menu/context.h" #include "gintrace/ubc.h" +#include "gintrace/tracer.h" #include "gintrace/gui/menu.h" #include "gintrace/gui/display.h" @@ -8,9 +9,6 @@ #include "./src/menu/internal/dictionary.h" -/* define the menu information */ -/* TODO: find a way to have local information (session) */ -struct mcontext mcontext; //--- // Internal information @@ -50,58 +48,57 @@ static void printXY(int column, int row, const char *text, uintptr_t reg) } /* context_ctor: Menu constructor */ -static void context_ctor(void) +static void context_ctor(struct tsession *session) { - mcontext.cursor.hoffset = 0; - mcontext.cursor.voffset = 0; + session->menu.context.cursor.hoffset = 0; + session->menu.context.cursor.voffset = 0; } /* context_display(); Display trace information */ -static void context_display(struct ucontext *context) +static void context_display(struct tsession *session) { int x; int y; - x = mcontext.cursor.hoffset; - y = mcontext.cursor.voffset; - printXY(0 + x, 0 + y, "gbr : %p", context->gbr); - printXY(0 + x, 1 + y, "macl : %p", context->mach); - printXY(0 + x, 2 + y, "mach : %p", context->macl); - printXY(0 + x, 3 + y, "ssr : %p", context->ssr); - printXY(0 + x, 4 + y, "pr : %p", context->pr); - printXY(0 + x, 5 + y, "spc : %p", context->spc); + x = session->menu.context.cursor.hoffset; + y = session->menu.context.cursor.voffset; + printXY(0 + x, 0 + y, "gbr : %p", session->info.context->gbr); + printXY(0 + x, 1 + y, "macl : %p", session->info.context->mach); + printXY(0 + x, 2 + y, "mach : %p", session->info.context->macl); + printXY(0 + x, 3 + y, "ssr : %p", session->info.context->ssr); + printXY(0 + x, 4 + y, "pr : %p", session->info.context->pr); + printXY(0 + x, 5 + y, "spc : %p", session->info.context->spc); - printXY(0 + x, 7 + y, "r0 : %p", context->reg[0]); - printXY(0 + x, 8 + y, "r1 : %p", context->reg[1]); - printXY(0 + x, 9 + y, "r2 : %p", context->reg[2]); - printXY(0 + x, 10 + y, "r3 : %p", context->reg[3]); - printXY(0 + x, 11 + y, "r4 : %p", context->reg[4]); - printXY(0 + x, 12 + y, "r5 : %p", context->reg[5]); - printXY(0 + x, 13 + y, "r6 : %p", context->reg[6]); - printXY(0 + x, 14 + y, "r7 : %p", context->reg[7]); - printXY(0 + x, 15 + y, "r8 : %p", context->reg[8]); - printXY(0 + x, 16 + y, "r9 : %p", context->reg[9]); - printXY(0 + x, 17 + y, "r10 : %p", context->reg[10]); - printXY(0 + x, 18 + y, "r11 : %p", context->reg[11]); - printXY(0 + x, 19 + y, "r12 : %p", context->reg[12]); - printXY(0 + x, 20 + y, "r13 : %p", context->reg[13]); - printXY(0 + x, 21 + y, "r14 : %p", context->reg[14]); - printXY(0 + x, 22 + y, "r15 : %p", context->reg[15]); + printXY(0 + x, 7 + y, "r0 : %p", session->info.context->reg[0]); + printXY(0 + x, 8 + y, "r1 : %p", session->info.context->reg[1]); + printXY(0 + x, 9 + y, "r2 : %p", session->info.context->reg[2]); + printXY(0 + x, 10 + y, "r3 : %p", session->info.context->reg[3]); + printXY(0 + x, 11 + y, "r4 : %p", session->info.context->reg[4]); + printXY(0 + x, 12 + y, "r5 : %p", session->info.context->reg[5]); + printXY(0 + x, 13 + y, "r6 : %p", session->info.context->reg[6]); + printXY(0 + x, 14 + y, "r7 : %p", session->info.context->reg[7]); + printXY(0 + x, 15 + y, "r8 : %p", session->info.context->reg[8]); + printXY(0 + x, 16 + y, "r9 : %p", session->info.context->reg[9]); + printXY(0 + x, 17 + y, "r10 : %p", session->info.context->reg[10]); + printXY(0 + x, 18 + y, "r11 : %p", session->info.context->reg[11]); + printXY(0 + x, 19 + y, "r12 : %p", session->info.context->reg[12]); + printXY(0 + x, 20 + y, "r13 : %p", session->info.context->reg[13]); + printXY(0 + x, 21 + y, "r14 : %p", session->info.context->reg[14]); + printXY(0 + x, 22 + y, "r15 : %p", session->info.context->reg[15]); } /* context_keyboard(): Handle one key event */ -static int context_keyboard(struct ucontext *context, int key) +static int context_keyboard(struct tsession *session, int key) { - (void)context; if (key == KEY_LEFT) - mcontext.cursor.hoffset += 1; + session->menu.context.cursor.hoffset += 1; if (key == KEY_RIGHT) - mcontext.cursor.hoffset -= 1; + session->menu.context.cursor.hoffset -= 1; if (key == KEY_UP) - mcontext.cursor.voffset += 1; + session->menu.context.cursor.voffset += 1; if (key == KEY_DOWN) - mcontext.cursor.voffset -= 1; + session->menu.context.cursor.voffset -= 1; return (0); } @@ -110,10 +107,10 @@ static int context_keyboard(struct ucontext *context, int key) // Define the menu //--- struct menu menu_context = { - .ctor = &context_ctor, + .ctor = (void*)&context_ctor, .init = NULL, - .display = &context_display, - .keyboard = &context_keyboard, + .display = (void*)&context_display, + .keyboard = (void*)&context_keyboard, .command = NULL, .dtor = NULL }; diff --git a/src/menu/disasm.c b/src/menu/disasm.c index 140090f..9c8bc88 100644 --- a/src/menu/disasm.c +++ b/src/menu/disasm.c @@ -1,5 +1,6 @@ #include "gintrace/menu/disasm.h" #include "gintrace/ubc.h" +#include "gintrace/tracer.h" #include "gintrace/gui/menu.h" #include "gintrace/gui/display.h" #include "gintrace/gui/input.h" @@ -12,10 +13,6 @@ #include #include -/* define the menu information */ -/* TODO: find a way to have local information (session) */ -struct tracer tracer; - /* disasm_util_line_update(): Little helper to update the line index * @nte: @@ -28,16 +25,17 @@ struct tracer tracer; * * @return * - the new line index */ -static int disasm_util_line_update(int line_idx, int direction) +static int disasm_util_line_update(struct disasm *disasm, + int line_idx, int direction) { line_idx = line_idx + direction; while (1) { if (line_idx < 0) { - line_idx += (int)tracer.buffer.size.height; + line_idx += (int)disasm->buffer.size.height; continue; } - if (line_idx >= (int)tracer.buffer.size.height) { - line_idx -= (int)tracer.buffer.size.height; + if (line_idx >= (int)disasm->buffer.size.height) { + line_idx -= (int)disasm->buffer.size.height; continue; } break; @@ -59,9 +57,9 @@ static int disasm_util_line_update(int line_idx, int direction) * type & 1 -> check hardware module register * type & 2 -> check syscall address * type & 4 -> check OS-specific address */ -static void disasm_check_special_addr(char *buffer, void *address, int type) +static void disasm_check_special_addr(struct disasm *disasm, + char *buffer, void *address, int type) { - extern struct tracer tracer; const char *addrname; addrname = NULL; @@ -73,7 +71,7 @@ static void disasm_check_special_addr(char *buffer, void *address, int type) addrname = dictionary_notes_check(address); if (addrname == NULL) return; - snprintf(buffer, tracer.buffer.size.width, + snprintf(buffer, disasm->buffer.size.width, "@note: %p -> %s\n", address, addrname); } @@ -91,7 +89,8 @@ static void disasm_check_special_addr(char *buffer, void *address, int type) * @arg: * - line Line index, used to find the buffer line to store information * - pc Current Program Counter which store the instruction address */ -static void disasm_get_mnemonic_info(int line, uint16_t *pc) +static void disasm_get_mnemonic_info(struct disasm *disasm, + int line, uint16_t *pc) { const struct opcode *opcode_info; uint16_t opcode; @@ -99,29 +98,29 @@ static void disasm_get_mnemonic_info(int line, uint16_t *pc) int note; /* Wipe note */ - tracer.buffer.raw[line][0][0] = '\0'; - tracer.buffer.raw[line][1][0] = '\0'; - tracer.buffer.raw[line][2][0] = '\0'; - tracer.buffer.raw[line][3][0] = '\0'; - tracer.buffer.raw[line][4][0] = '\0'; + disasm->buffer.raw[line][0][0] = '\0'; + disasm->buffer.raw[line][1][0] = '\0'; + disasm->buffer.raw[line][2][0] = '\0'; + disasm->buffer.raw[line][3][0] = '\0'; + disasm->buffer.raw[line][4][0] = '\0'; /* check special address (register, syscall, ...) */ note = 1; - disasm_check_special_addr(tracer.buffer.raw[line][note], pc, 6); - if (tracer.buffer.raw[line][note][0] != '\0') + disasm_check_special_addr(disasm, disasm->buffer.raw[line][note], pc, 6); + if (disasm->buffer.raw[line][note][0] != '\0') note += 1; /* generate the "default" string info (only the address and word) */ - snprintf(&tracer.buffer.raw[line][0][0], - tracer.buffer.size.width, "%.8lx %.4x ", + snprintf(&disasm->buffer.raw[line][0][0], + disasm->buffer.size.width, "%.8lx %.4x ", (uintptr_t)pc, (unsigned int)pc[0]); /* Try to get the current opcode */ opcode = pc[0]; opcode_info = dictionary_opcodes_check(opcode); if (opcode_info == NULL) { - snprintf(&tracer.buffer.raw[line][0][14], - tracer.buffer.size.width - 14, + snprintf(&disasm->buffer.raw[line][0][14], + disasm->buffer.size.width - 14, ".word\t%#.4x", (int)opcode); return; } @@ -131,13 +130,14 @@ static void disasm_get_mnemonic_info(int line, uint16_t *pc) arg[i] = dictionary_opcodes_get_arg(opcode_info, opcode, i, pc); if (arg[i] == 0x00000000) continue; - disasm_check_special_addr(tracer.buffer.raw[line][note++], + disasm_check_special_addr(disasm, + disasm->buffer.raw[line][note++], (void*)arg[i], 7); } /* generate the complete mnemonic information */ - snprintf(&tracer.buffer.raw[line][0][14], - tracer.buffer.size.width - 14, + snprintf(&disasm->buffer.raw[line][0][14], + disasm->buffer.size.width - 14, opcode_info->name, arg[0], arg[1], arg[2]); } @@ -152,13 +152,13 @@ static void disasm_get_mnemonic_info(int line, uint16_t *pc) * * @return * The note index max of the line_idx */ -static int disasm_util_note_counter(int line_idx) +static int disasm_util_note_counter(struct disasm *disasm, int line_idx) { int note_idx; note_idx = 0; while (++note_idx < 5) { - if (tracer.buffer.raw[line_idx][note_idx][0] == '\0') + if (disasm->buffer.raw[line_idx][note_idx][0] == '\0') break; } return (note_idx - 1); @@ -188,12 +188,13 @@ static int disasm_util_row_update(int *row, int direction) * - line_idx Line index * - pc Memory address * - direction Direction to push the limit */ -static void disasm_util_line_fetch(int line_idx, uint16_t *pc, int direction) +static void disasm_util_line_fetch(struct disasm *disasm, int line_idx, + uint16_t *pc, int direction) { - if (tracer.buffer.raw[line_idx][0][0] == '\0') { - disasm_get_mnemonic_info(line_idx, pc); - line_idx = disasm_util_line_update(line_idx, direction); - tracer.buffer.raw[line_idx][0][0] = '\0'; + if (disasm->buffer.raw[line_idx][0][0] == '\0') { + disasm_get_mnemonic_info(disasm, line_idx, pc); + line_idx = disasm_util_line_update(disasm, line_idx, direction); + disasm->buffer.raw[line_idx][0][0] = '\0'; } } @@ -217,43 +218,44 @@ static void disasm_util_line_fetch(int line_idx, uint16_t *pc, int direction) * @return * - 0 Row is still on the screen * - 1 Row is out-of-screen */ -int disasm_display_addr_info(int *row, struct buffcursor *cursor, - struct ucontext *context, int direction, int pc) +int disasm_display_addr_info(struct tsession *session, int *row, + struct buffcursor *cursor, int direction, int pc) { + struct disasm *disasm = &session->menu.disasm; int note_max_idx; void *ptr; /* generate the line if missing and move the limit */ - disasm_util_line_fetch(cursor->line_idx, - &tracer.buffer.anchor.addr[pc], direction); + disasm_util_line_fetch(disasm, cursor->line_idx, + &disasm->buffer.anchor.addr[pc], direction); /* get the last note index if downward */ note_max_idx = 5; if (direction == 1) - note_max_idx = disasm_util_note_counter(cursor->line_idx); + note_max_idx = disasm_util_note_counter(disasm, cursor->line_idx); /* walk trough the notes */ while (cursor->note_idx <= note_max_idx && cursor->note_idx >= 0) { /* check loop condition */ - ptr = tracer.buffer.raw[cursor->line_idx][cursor->note_idx]; + ptr = disasm->buffer.raw[cursor->line_idx][cursor->note_idx]; if (((char*)ptr)[0] == '\0') break; /* check note */ if (cursor->note_idx != 0) { - gnoteXY(tracer.disp.hoffset, *row, ptr); + gnoteXY(disasm->disp.hoffset, *row, ptr); } else { /* check instruction */ - gtextXY(tracer.disp.hoffset, *row, ptr); + gtextXY(disasm->disp.hoffset, *row, ptr); /* highlight SPC if possible */ - ptr = &tracer.buffer.anchor.addr[pc]; - if ((uintptr_t)ptr == context->spc) + ptr = &disasm->buffer.anchor.addr[pc]; + if ((uintptr_t)ptr == session->info.context->spc) ghreverse((*row) * (FHEIGHT+1) - 1, FHEIGHT+2); /* draw next break / instruction */ - if (tracer.next_break != context->spc - && ptr == (void*)tracer.next_break) { + if (disasm->next_break != session->info.context->spc + && ptr == (void*)disasm->next_break) { ghline(((*row) + 0) * (FHEIGHT + 1) - 1); ghline(((*row) + 1) * (FHEIGHT + 1) - 1); } @@ -273,67 +275,75 @@ int disasm_display_addr_info(int *row, struct buffcursor *cursor, // Menu functions //--- /* disasm_ctor(): disasm menu constructor */ -static void disasm_ctor(void) +static void disasm_ctor(struct tsession *session) { - memset(&tracer, 0x00, sizeof(struct tracer)); - tracer.buffer.size.width = GUI_DISP_NB_COLUMN * 2; - tracer.buffer.size.height = GUI_DISP_NB_ROW * 2 + 2; - tracer.buffer.raw = calloc(sizeof(void*), tracer.buffer.size.height); - for (size_t i = 0; i < tracer.buffer.size.height; ++i) { - tracer.buffer.raw[i] = calloc(sizeof(char*), 5); - tracer.buffer.raw[i][0] = calloc(tracer.buffer.size.width, 1); - tracer.buffer.raw[i][1] = calloc(tracer.buffer.size.width, 1); - tracer.buffer.raw[i][2] = calloc(tracer.buffer.size.width, 1); - tracer.buffer.raw[i][3] = calloc(tracer.buffer.size.width, 1); - tracer.buffer.raw[i][4] = calloc(tracer.buffer.size.width, 1); + struct disasm *disasm = &session->menu.disasm; + + memset(disasm, 0x00, sizeof(struct disasm)); + disasm->buffer.size.width = GUI_DISP_NB_COLUMN * 2; + disasm->buffer.size.height = GUI_DISP_NB_ROW * 2 + 2; + disasm->buffer.raw = calloc(sizeof(void*), disasm->buffer.size.height); + for (size_t i = 0; i < disasm->buffer.size.height; ++i) { + disasm->buffer.raw[i] = calloc(sizeof(char*), 5); + disasm->buffer.raw[i][0] = calloc(disasm->buffer.size.width, 1); + disasm->buffer.raw[i][1] = calloc(disasm->buffer.size.width, 1); + disasm->buffer.raw[i][2] = calloc(disasm->buffer.size.width, 1); + disasm->buffer.raw[i][3] = calloc(disasm->buffer.size.width, 1); + disasm->buffer.raw[i][4] = calloc(disasm->buffer.size.width, 1); } - tracer.buffer.cursor.line_idx = 0; - tracer.buffer.cursor.note_idx = 0; - tracer.disp.hoffset = 0; - tracer.disp.voffset = 0; + disasm->buffer.cursor.line_idx = 0; + disasm->buffer.cursor.note_idx = 0; + disasm->disp.hoffset = 0; + disasm->disp.voffset = 0; } /* disasm_dtor(): disasm menu destructor */ -static void disasm_dtor(void) +static void disasm_dtor(struct tsession *session) { - if (tracer.buffer.raw != NULL) { - for (size_t i = 0; i < tracer.buffer.size.height; ++i) { - if (tracer.buffer.raw[i] == NULL) + struct disasm *disasm; + + disasm = &session->menu.disasm; + if (disasm->buffer.raw != NULL) { + for (size_t i = 0; i < disasm->buffer.size.height; ++i) { + if (disasm->buffer.raw[i] == NULL) continue; - free(tracer.buffer.raw[i][0]); - free(tracer.buffer.raw[i][1]); - free(tracer.buffer.raw[i][2]); - free(tracer.buffer.raw[i][3]); - free(tracer.buffer.raw[i][4]); - free(tracer.buffer.raw[i]); + free(disasm->buffer.raw[i][0]); + free(disasm->buffer.raw[i][1]); + free(disasm->buffer.raw[i][2]); + free(disasm->buffer.raw[i][3]); + free(disasm->buffer.raw[i][4]); + free(disasm->buffer.raw[i]); } - free(tracer.buffer.raw); + free(disasm->buffer.raw); } - memset(&tracer, 0x00, sizeof(struct tracer)); + memset(disasm, 0x00, sizeof(struct disasm)); } /* disasm_init(): Called at each breakpoint, update the internal buffer */ -static void disasm_init(struct ucontext *context) +static void disasm_init(struct tsession *session) { + struct disasm *disasm = &session->menu.disasm; int a; - tracer.skip = 0; - tracer.buffer.anchor.addr = (void*)(uintptr_t)((context->spc + 1) & ~1); - tracer.next_break = context->spc; - tracer.next_instruction = context->spc; + disasm->skip = 0; + disasm->buffer.anchor.addr = + (void*)(uintptr_t)((session->info.context->spc + 1) & ~1); + disasm->next_break = session->info.context->spc; + disasm->next_instruction = session->info.context->spc; - tracer.buffer.cursor.note_idx = 0; - tracer.buffer.cursor.line_idx = 0; - a = disasm_util_line_update(tracer.buffer.cursor.line_idx, -1); - tracer.buffer.raw[a][0][0] = '\0'; - tracer.buffer.raw[tracer.buffer.cursor.line_idx][0][0] = '\0'; - disasm_util_line_fetch(tracer.buffer.cursor.line_idx, - &tracer.buffer.anchor.addr[0], 1); + disasm->buffer.cursor.note_idx = 0; + disasm->buffer.cursor.line_idx = 0; + a = disasm_util_line_update(disasm, disasm->buffer.cursor.line_idx, -1); + disasm->buffer.raw[a][0][0] = '\0'; + disasm->buffer.raw[disasm->buffer.cursor.line_idx][0][0] = '\0'; + disasm_util_line_fetch(disasm, disasm->buffer.cursor.line_idx, + &disasm->buffer.anchor.addr[0], 1); } /* disasm_display(); Display trace information */ -static void disasm_display(struct ucontext *context) +static void disasm_display(struct tsession *session) { + struct disasm *disasm = &session->menu.disasm; struct buffcursor cursor; int row; int pc; @@ -341,12 +351,12 @@ static void disasm_display(struct ucontext *context) /* display the first part (current middle line and before, upward) */ pc = 0; row = GUI_DISP_NB_ROW / 2; - cursor.line_idx = tracer.buffer.cursor.line_idx; - cursor.note_idx = tracer.buffer.cursor.note_idx; - disasm_display_addr_info(&row, &cursor, context, -1, pc); + cursor.line_idx = disasm->buffer.cursor.line_idx; + cursor.note_idx = disasm->buffer.cursor.note_idx; + disasm_display_addr_info(session, &row, &cursor, -1, pc); while (row >= 0) { - disasm_display_addr_info(&row, &cursor, context, -1, pc); - cursor.line_idx = disasm_util_line_update(cursor.line_idx, -1); + disasm_display_addr_info(session, &row, &cursor, -1, pc); + cursor.line_idx = disasm_util_line_update(disasm, cursor.line_idx, -1); cursor.note_idx = 0; pc = pc - 1; } @@ -360,82 +370,82 @@ static void disasm_display(struct ucontext *context) * information, get the note index max then start displaying lines. */ pc = 0; row = (GUI_DISP_NB_ROW / 2) + 1; - cursor.line_idx = tracer.buffer.cursor.line_idx; - cursor.note_idx = tracer.buffer.cursor.note_idx - 1; + cursor.line_idx = disasm->buffer.cursor.line_idx; + cursor.note_idx = disasm->buffer.cursor.note_idx - 1; if (cursor.note_idx < 0) { pc = 1; - cursor.line_idx = disasm_util_line_update(cursor.line_idx, 1); - disasm_util_line_fetch(cursor.line_idx, - &tracer.buffer.anchor.addr[pc], 1); - cursor.note_idx = disasm_util_note_counter(cursor.line_idx); + cursor.line_idx = disasm_util_line_update(disasm, cursor.line_idx, 1); + disasm_util_line_fetch(disasm, cursor.line_idx, + &disasm->buffer.anchor.addr[pc], 1); + cursor.note_idx = disasm_util_note_counter(disasm, cursor.line_idx); } while (row <= GUI_DISP_NB_ROW) { - disasm_display_addr_info(&row, &cursor, context, 1, pc); + disasm_display_addr_info(session, &row, &cursor, 1, pc); pc = pc + 1; - cursor.line_idx = disasm_util_line_update(cursor.line_idx, 1); - disasm_util_line_fetch(cursor.line_idx, - &tracer.buffer.anchor.addr[pc], 1); - cursor.note_idx = disasm_util_note_counter(cursor.line_idx); + cursor.line_idx = disasm_util_line_update(disasm, cursor.line_idx, 1); + disasm_util_line_fetch(disasm, cursor.line_idx, + &disasm->buffer.anchor.addr[pc], 1); + cursor.note_idx = disasm_util_note_counter(disasm, cursor.line_idx); } } /* disasm_keyboard(): Handle one key event */ -static int disasm_keyboard(struct ucontext *context, int key) +static int disasm_keyboard(struct tsession *session, int key) { + struct disasm *disasm = &session->menu.disasm; int note_idx; int line_idx; /* horizontal update */ - (void)context; if (key == KEY_LEFT) - tracer.disp.hoffset += 1; + disasm->disp.hoffset += 1; if (key == KEY_RIGHT) - tracer.disp.hoffset -= 1; + disasm->disp.hoffset -= 1; /* vertical update */ if (key == KEY_UP) { - tracer.buffer.cursor.note_idx += 1; - note_idx = tracer.buffer.cursor.note_idx; - line_idx = tracer.buffer.cursor.line_idx; + disasm->buffer.cursor.note_idx += 1; + note_idx = disasm->buffer.cursor.note_idx; + line_idx = disasm->buffer.cursor.line_idx; if (note_idx >= 5 - || tracer.buffer.raw[line_idx][note_idx][0] == '\0') { - tracer.buffer.anchor.addr = - &tracer.buffer.anchor.addr[-1]; - line_idx = disasm_util_line_update(line_idx, -1); - tracer.buffer.cursor.line_idx = line_idx; - tracer.buffer.cursor.note_idx = 0; + || disasm->buffer.raw[line_idx][note_idx][0] == '\0') { + disasm->buffer.anchor.addr = + &disasm->buffer.anchor.addr[-1]; + line_idx = disasm_util_line_update(disasm, line_idx, -1); + disasm->buffer.cursor.line_idx = line_idx; + disasm->buffer.cursor.note_idx = 0; } } if (key == KEY_DOWN) { - tracer.buffer.cursor.note_idx -= 1; - note_idx = tracer.buffer.cursor.note_idx; - line_idx = tracer.buffer.cursor.line_idx; + disasm->buffer.cursor.note_idx -= 1; + note_idx = disasm->buffer.cursor.note_idx; + line_idx = disasm->buffer.cursor.line_idx; if (note_idx < 0) { - tracer.buffer.anchor.addr = - &tracer.buffer.anchor.addr[1]; - line_idx = disasm_util_line_update(line_idx, 1); - disasm_util_line_fetch(line_idx, tracer.memory, 1); - note_idx = disasm_util_note_counter(line_idx); - tracer.buffer.cursor.line_idx = line_idx; - tracer.buffer.cursor.note_idx = note_idx; + disasm->buffer.anchor.addr = + &disasm->buffer.anchor.addr[1]; + line_idx = disasm_util_line_update(disasm, line_idx, 1); + disasm_util_line_fetch(disasm, line_idx, disasm->memory, 1); + note_idx = disasm_util_note_counter(disasm, line_idx); + disasm->buffer.cursor.line_idx = line_idx; + disasm->buffer.cursor.note_idx = note_idx; } } /* move break point */ if (key == KEY_PLUS) - tracer.next_break += 2; + disasm->next_break += 2; if (key == KEY_MINUS) - tracer.next_break -= 2; + disasm->next_break -= 2; if (key == KEY_NEG) - tracer.next_break = (uintptr_t)tracer.buffer.anchor.addr; + disasm->next_break = (uintptr_t)disasm->buffer.anchor.addr; /* skip instruction */ if (key == KEY_OPTN) { - context->spc = tracer.next_break; + session->info.context->spc = disasm->next_break; return (1); } if (key == KEY_VARS) { - tracer.skip = 1; + disasm->skip = 1; return (1); } @@ -443,8 +453,9 @@ static int disasm_keyboard(struct ucontext *context, int key) } /* disasm_command(): Handle user command */ -static void disasm_command(struct ucontext *context, int argc, char **argv) +static void disasm_command(struct tsession *session, int argc, char **argv) { + struct disasm *disasm; uintptr_t address; uint8_t action; int idx; @@ -501,27 +512,28 @@ static void disasm_command(struct ucontext *context, int argc, char **argv) if ((action & 2) != 0) { if (address >= 0x1fff) goto error_part; -#ifdef FXCG50 + #ifdef FXCG50 uintptr_t *systab = *(uintptr_t **)0x8002007c; -#endif -#ifdef FX9860 + #endif + #ifdef FX9860 uintptr_t *systab = *(uintptr_t **)0x8001007c; -#endif + #endif if (idx == 2) { input_write("syscall %x: %p", address, systab[address]); return; } address = systab[address]; } - tracer.buffer.anchor.addr = (void*)(address & ~1); - tracer.buffer.cursor.note_idx = 0; - tracer.buffer.cursor.line_idx = 0; - i = disasm_util_line_update(tracer.buffer.cursor.line_idx, -1); - tracer.buffer.raw[i][0][0] = '\0'; - tracer.buffer.raw[tracer.buffer.cursor.line_idx][0][0] = '\0'; - disasm_util_line_fetch(tracer.buffer.cursor.line_idx, - &tracer.buffer.anchor.addr[0], 1); - disasm_display(context); + disasm = &session->menu.disasm; + disasm->buffer.anchor.addr = (void*)(address & ~1); + disasm->buffer.cursor.note_idx = 0; + disasm->buffer.cursor.line_idx = 0; + i = disasm_util_line_update(disasm, disasm->buffer.cursor.line_idx, -1); + disasm->buffer.raw[i][0][0] = '\0'; + disasm->buffer.raw[disasm->buffer.cursor.line_idx][0][0] = '\0'; + disasm_util_line_fetch(disasm, disasm->buffer.cursor.line_idx, + &disasm->buffer.anchor.addr[0], 1); + disasm_display(session); return; /* error part */ @@ -533,10 +545,10 @@ error_part: // Define the menu //--- struct menu menu_disasm = { - .ctor = &disasm_ctor, - .init = &disasm_init, - .display = &disasm_display, - .keyboard = &disasm_keyboard, - .command = &disasm_command, - .dtor = &disasm_dtor + .ctor = (void*)&disasm_ctor, + .init = (void*)&disasm_init, + .display = (void*)&disasm_display, + .keyboard = (void*)&disasm_keyboard, + .command = (void*)&disasm_command, + .dtor = (void*)&disasm_dtor }; diff --git a/src/menu/hexdump.c b/src/menu/hexdump.c index 684d674..065247e 100644 --- a/src/menu/hexdump.c +++ b/src/menu/hexdump.c @@ -1,5 +1,6 @@ #include "gintrace/menu/hexdump.h" #include "gintrace/ubc.h" +#include "gintrace/tracer.h" #include "gintrace/gui/menu.h" #include "gintrace/gui/display.h" #include "gintrace/gui/input.h" @@ -8,29 +9,24 @@ #include #include -/* define the menu information */ -/* TODO: find a way to have local information (session) */ -struct hexdump hexdump; - /* hexdump_ctor: Menu constructor */ -static void hexdump_ctor(void) +static void hexdump_ctor(struct tsession *session) { - hexdump.cursor.hoffset = 0; - hexdump.cursor.voffset = 0; - hexdump.addr = (void*)0x88000000; + session->menu.hexdump.cursor.hoffset = 0; + session->menu.hexdump.cursor.voffset = 0; + session->menu.hexdump.addr = (void*)0x88000000; } /* hexdump_display(); Display trace information */ -static void hexdump_display(struct ucontext *context) +static void hexdump_display(struct tsession *session) { uint8_t *record; int x; int y; - (void)context; - record = hexdump.addr; - x = hexdump.cursor.hoffset; - y = hexdump.cursor.voffset; + record = session->menu.hexdump.addr; + x = session->menu.hexdump.cursor.hoffset; + y = session->menu.hexdump.cursor.voffset; for (int i = 0; i < GUI_DISP_NB_ROW; ++i) { gprintXY(x - 2, y + i, "%p", record); for (int j = 0; j < 8; ++j) { @@ -48,29 +44,27 @@ static void hexdump_display(struct ucontext *context) } /* hexdump_keyboard(): Handle one key event */ -static int hexdump_keyboard(struct ucontext *context, int key) +static int hexdump_keyboard(struct tsession *session, int key) { - (void)context; if (key == KEY_LEFT) - hexdump.cursor.hoffset += 1; + session->menu.hexdump.cursor.hoffset += 1; if (key == KEY_RIGHT) - hexdump.cursor.hoffset -= 1; + session->menu.hexdump.cursor.hoffset -= 1; if (key == KEY_UP) - hexdump.addr = &hexdump.addr[-8]; + session->menu.hexdump.addr = &session->menu.hexdump.addr[-8]; if (key == KEY_DOWN) - hexdump.addr = &hexdump.addr[8]; + session->menu.hexdump.addr = &session->menu.hexdump.addr[8]; return (0); } /* hexdump_command(): Handle user command */ -static void hexdump_command(struct ucontext *context, int argc, char **argv) +static void hexdump_command(struct tsession *session, int argc, char **argv) { uintptr_t address; int i; - (void)context; if (argc != 2) return; if (strcmp(argv[0], "jmp") != 0) { @@ -96,17 +90,17 @@ static void hexdump_command(struct ucontext *context, int argc, char **argv) input_write("'%s': second argument error", argv[0]); return; } - hexdump.addr = (void*)(address & ~3); + session->menu.hexdump.addr = (void*)(address & ~3); } //--- // Define the menu //--- struct menu menu_hexdump = { - .ctor = &hexdump_ctor, + .ctor = (void*)&hexdump_ctor, .init = NULL, - .display = &hexdump_display, - .keyboard = &hexdump_keyboard, - .command = &hexdump_command, + .display = (void*)&hexdump_display, + .keyboard = (void*)&hexdump_keyboard, + .command = (void*)&hexdump_command, .dtor = NULL }; diff --git a/src/tracer.c b/src/tracer.c new file mode 100644 index 0000000..08ce18f --- /dev/null +++ b/src/tracer.c @@ -0,0 +1,58 @@ +#include "gintrace/tracer.h" + +#include +#include + +/* global session (TODO: thread-safe) */ +struct tsession *session = NULL; + +/* tracer_new_session(): Create a new session */ +struct tsession *tracer_create_session(void *address, int menu) +{ + extern void gintrace_handler(struct ucontext *context); + struct tsession *session; + + if (address == NULL || (menu & (TRACER_DISASM + | TRACER_CONTEXT + | TRACER_HEXDUMP + | TRACER_CALLGRAPH)) == 0) { + return(NULL); + } + + session = calloc(sizeof(struct tsession), 1); + if (session == NULL) + return (NULL); + if (menu_create(&session->display.gmenu, session) != 0) { + free(session); + return (NULL); + } + if ((menu & TRACER_DISASM) != 0) + menu_register(session->display.gmenu, &menu_disasm, "Disasm"); + if ((menu & TRACER_CONTEXT) != 0) + menu_register(session->display.gmenu, &menu_context, "Context"); + if ((menu & TRACER_HEXDUMP) != 0) + menu_register(session->display.gmenu, &menu_hexdump, "Hexdump"); + if ((menu & TRACER_CALLGRAPH) != 0) + menu_register(session->display.gmenu, &menu_callgraph, "CallG"); + + ubc_install(); + ubc_set_handler(&gintrace_handler); + ubc_set_breakpoint(0, address, NULL); + return (session); +} + +/* tracer_get_session(): Get the current session. */ +struct tsession *tracer_get_session(void) +{ + return (session); +} + +/* tracer_get_session(): Set the current session. */ +struct tsession *tracer_set_session(struct tsession *new) +{ + void *old; + + old = session; + session = new; + return (old); +} diff --git a/src/ubc/handler.c b/src/ubc/handler.c new file mode 100644 index 0000000..688f6ba --- /dev/null +++ b/src/ubc/handler.c @@ -0,0 +1,69 @@ +#include "gintrace/gui/menu.h" +#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 "gintrace/tracer.h" + +#include + +/* 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. */ +//FIXME: move breakpoint +//FIXME: move spc +void gintrace_handler(struct ucontext *context) +{ + static uintptr_t breakpoint = 0x00000000; + static uintptr_t spc = 0x00000000; + struct tsession *session; + + /* force disable the UBC to avoid error */ + ubc_block(); + + /* check session validity */ + session = tracer_get_session(); + if (session == NULL) { + ubc_unblock(); + return; + } + session->info.context = context; + + /* check callgraph job */ + if (breakpoint != 0x00000000 + && spc != 0x00000000 + && spc != breakpoint) { + menu_callgraph.init(session); + spc = context->spc; + ubc_set_breakpoint(0, (void*)context->spc, NULL); + ubc_unblock(); + return; + } + + /* user break point */ + gint_switch_to_gint(); + menu_init(session->display.gmenu); + while (menu_is_open(session->display.gmenu) == 0) { + menu_draw(session->display.gmenu); + menu_keyboard(session->display.gmenu); + } + + /* if no instruction skip, restore */ + if (session->menu.disasm.skip == 0) { + spc = context->spc; + ubc_set_breakpoint(0, (void*)context->spc, NULL); + breakpoint = session->menu.disasm.next_break; + } else { + breakpoint = session->menu.disasm.next_break; + ubc_set_breakpoint(0, (void*)breakpoint, NULL); + spc = breakpoint; + } + + + /* unblock UBC interrupt */ + ubc_unblock(); + gint_switch_to_casio(); +} diff --git a/utils/a.out b/utils/a.out new file mode 100755 index 0000000000000000000000000000000000000000..a262e58c305a624aabeb7a0e77d9e687c8f7c299 GIT binary patch literal 16536 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 HgBAY*`<~1z literal 0 HcmV?d00001 diff --git a/utils/abcd b/utils/abcd new file mode 100644 index 0000000000000000000000000000000000000000..b5b0e9328ac75e9539bfa0101594e9bd83ea8a3f GIT binary patch literal 242455 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 literal 0 HcmV?d00001 diff --git a/utils/main.c b/utils/main.c new file mode 100644 index 0000000..bc51dee --- /dev/null +++ b/utils/main.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +struct callnode { + struct callnode *parent; + struct callnode *sibling; + struct callnode *child; +}; + +static uint32_t get_lword(uint8_t *file) +{ + return (file[0] << 24 | file[1] << 16 | file[2] << 8 | file[3]); +} + +static void generate_binary_tree(struct callnode **node, uint8_t *file) +{ + *node = calloc(sizeof(struct callnode), 1); + if (*node == NULL) { + fprintf(stderr, "ENOMEM\n"); + exit(84); + } + size_t size = get_lword(file); + printf("size: %lx\n", size); + generate_binary_tree(node, &file[size]); +} + +int main(int argc, char **argv) +{ + if (argc != 2) + return (84); + int fd = open(argv[1], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "unable to open '%s'\n", argv[1]); + return (84); + } + uint8_t *map = calloc(512 * 1024, 1); + if (map == NULL) { + fprintf(stderr, "ENOMEM\n"); + return (84); + } + size_t size = read(fd, map, 512 * 1024); + printf("file size: %ld\n", size); + printf("lword = %x\n", get_lword(map)); + + for (size_t i = 0; i < size; i++) { + if (i != 0 && (i & 7) == 0) + printf("\n"); + printf("%.2x", map[i]); + } + return (0); +}