/* * mon.cpp - cxmon main program * * cxmon (C) 1997-2004 Christian Bauer, Marc Hellwig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sysdeps.h" #include #include #include #include #include #include #include #if defined(HAVE_READLINE_H) extern "C" { #include } #elif defined(HAVE_READLINE_READLINE_H) extern "C" { #include } #endif #if defined(HAVE_HISTORY_H) extern "C" { #include } #elif defined(HAVE_READLINE_HISTORY_H) extern "C" { #include } #endif #include "mon.h" #include "mon_cmd.h" #include "mon_lowmem.h" #ifndef VERSION #define VERSION "3" #endif // Break points BREAK_POINT_SET active_break_points; BREAK_POINT_SET disabled_break_points; // Buffer we're operating on bool mon_use_real_mem = false; uint32 mon_mem_size; static uint8 *mem; // Streams for input, output and error messages FILE *monin, *monout, *monerr = NULL; // Input line static char *input; static char *in_ptr; char *mon_args_ptr; // Current address, value of '.' in expressions uintptr mon_dot_address; // Current value of ':' in expression static uint32 colon_value; // Scanner variables enum Token mon_token; // Last token read uintptr mon_number; // Contains the number if mon_token==T_NUMBER char *mon_string; // Contains the string if mon_token==T_STRING char *mon_name; // Contains the variable name if mon_token==T_NAME // List of installed commands struct CmdSpec { const char *name; // Name of command void (*func)(); // Function that executes this command }; static CmdSpec *cmds; // Array of CmdSpecs static int num_cmds; // Number of installed commands static char *cmd_help; // Help text for commands // List of variables typedef std::map var_map; static var_map vars; // Prototypes static void init_abort(); static void exit_abort(); static void read_line(char *prompt); // Scanner static char get_char(); static void put_back(char c); static enum Token get_hex_number(uintptr &i); static enum Token get_dec_number(uintptr &i); static enum Token get_char_number(uintptr &i); static enum Token get_string(char *&str); static enum Token get_hex_or_name(uintptr &i, char *&name); static bool eor_expr(uintptr *number); // Parser static bool and_expr(uintptr *number); static bool shift_expr(uintptr *number); static bool add_expr(uintptr *number); static bool mul_expr(uintptr *number); static bool factor(uintptr *number); /* * Add command to mon */ void mon_add_command(const char *name, void (*func)(), const char *help_text) { num_cmds++; if (cmds) cmds = (CmdSpec *)realloc(cmds, num_cmds * sizeof(CmdSpec)); else cmds = (CmdSpec *)malloc(sizeof(CmdSpec)); cmds[num_cmds - 1].name = name; cmds[num_cmds - 1].func = func; if (help_text) { if (cmd_help) { cmd_help = (char *)realloc(cmd_help, strlen(cmd_help) + strlen(help_text) + 1); strcat(cmd_help, help_text); } else cmd_help = strdup(help_text); } } /* * Print error message */ void mon_error(const char *s) { fprintf(monerr == NULL? stdout : monerr, "*** %s\n", s); } /* * CTRL-C pressed? */ static bool was_aborted; static struct sigaction my_sa; #ifdef __BEOS__ static void handle_abort(int sig, void *arg, vregs *r) #else static void handle_abort(int sig) #endif { was_aborted = true; } static void init_abort() { was_aborted = false; sigemptyset(&my_sa.sa_mask); #ifdef __BEOS__ my_sa.sa_handler = (__signal_func_ptr)handle_abort; my_sa.sa_userdata = 0; #else my_sa.sa_handler = handle_abort; #endif my_sa.sa_flags = 0; sigaction(SIGINT, &my_sa, NULL); } static void exit_abort() { my_sa.sa_handler = SIG_DFL; sigaction(SIGINT, &my_sa, NULL); } bool mon_aborted() { bool ret = was_aborted; was_aborted = false; return ret; } /* * Access to buffer */ uint32 (*mon_read_byte)(uintptr adr); uint32 mon_read_byte_buffer(uintptr adr) { return mem[adr % mon_mem_size]; } uint32 mon_read_byte_real(uintptr adr) { return *(uint8 *)adr; } void (*mon_write_byte)(uintptr adr, uint32 b); void mon_write_byte_buffer(uintptr adr, uint32 b) { mem[adr % mon_mem_size] = b; } void mon_write_byte_real(uintptr adr, uint32 b) { *(uint8 *)adr = b; } uint32 mon_read_half(uintptr adr) { return (mon_read_byte(adr) << 8) | mon_read_byte(adr+1); } void mon_write_half(uintptr adr, uint32 w) { mon_write_byte(adr, w >> 8); mon_write_byte(adr+1, w); } uint32 mon_read_word(uintptr adr) { return (mon_read_byte(adr) << 24) | (mon_read_byte(adr+1) << 16) | (mon_read_byte(adr+2) << 8) | mon_read_byte(adr+3); } void mon_write_word(uintptr adr, uint32 l) { mon_write_byte(adr, l >> 24); mon_write_byte(adr+1, l >> 16); mon_write_byte(adr+2, l >> 8); mon_write_byte(adr+3, l); } /* * Read a line from the keyboard */ static void read_line(char *prompt) { #ifdef HAVE_LIBREADLINE if (input) free(input); input = readline(prompt); if (input) { if (*input) add_history(input); } else { // EOF, quit cxmon input = (char *)malloc(2); input[0] = 'x'; input[1] = 0; fprintf(monout, "x\n"); } in_ptr = input; #else static const unsigned INPUT_LENGTH = 256; if (!input) input = (char *)malloc(INPUT_LENGTH); fprintf(monout, prompt); fflush(monout); fgets(in_ptr = input, INPUT_LENGTH, monin); char *s = strchr(input, '\n'); if (s != NULL) *s = 0; #endif } /* * Read a character from the input line */ static char get_char() { return *in_ptr++; } /* * Stuff back a character into the input line */ static void put_back(char c) { *(--in_ptr) = c; } /* * Scanner: Get a token from the input line */ enum Token mon_get_token() { char c = get_char(); // Skip spaces while (isspace(c)) c = get_char(); switch (c) { case 0: return mon_token = T_END; case '(': return mon_token = T_LPAREN; case ')': return mon_token = T_RPAREN; case '.': return mon_token = T_DOT; case ':': return mon_token = T_COLON; case ',': return mon_token = T_COMMA; case '+': return mon_token = T_PLUS; case '-': return mon_token = T_MINUS; case '*': return mon_token = T_MUL; case '/': return mon_token = T_DIV; case '%': return mon_token = T_MOD; case '&': return mon_token = T_AND; case '|': return mon_token = T_OR; case '^': return mon_token = T_EOR; case '<': if (get_char() == '<') return mon_token = T_SHIFTL; else { mon_error("Unrecognized token"); return mon_token = T_NULL; } case '>': if (get_char() == '>') return mon_token = T_SHIFTR; else { mon_error("Unrecognized token"); return mon_token = T_NULL; } case '~': return mon_token = T_NOT; case '=': return mon_token = T_ASSIGN; case '$': if ((mon_token = get_hex_number(mon_number)) == T_NULL) mon_error("'$' must be followed by hexadecimal number"); return mon_token; case '_': if ((mon_token = get_dec_number(mon_number)) == T_NULL) mon_error("'_' must be followed by decimal number"); return mon_token; case '\'': return mon_token = get_char_number(mon_number); case '"': return mon_token = get_string(mon_string); default: if (isalnum(c)) { put_back(c); return mon_token = get_hex_or_name(mon_number, mon_name); } mon_error("Unrecognized token"); return mon_token = T_NULL; } } static enum Token get_hex_number(uintptr &i) { char c = get_char(); i = 0; if (!isxdigit(c)) return T_NULL; do { c = tolower(c); if (c < 'a') i = (i << 4) + (c - '0'); else i = (i << 4) + (c - 'a' + 10); c = get_char(); } while (isxdigit(c)); if (isalnum(c)) return T_NULL; else { put_back(c); return T_NUMBER; } } static enum Token get_dec_number(uintptr &i) { char c = get_char(); i = 0; if (!isdigit(c)) return T_NULL; do { i = (i * 10) + (c - '0'); c = get_char(); } while (isdigit(c)); if (isalnum(c)) return T_NULL; else { put_back(c); return T_NUMBER; } } static enum Token get_char_number(uintptr &i) { char c; i = 0; while ((c = get_char()) != 0) { if (c == '\'') return T_NUMBER; i = (i << 8) + (uint8)c; } mon_error("Unterminated character constant"); return T_NULL; } static enum Token get_string(char *&str) { // Remember start of string char *old_in_ptr = in_ptr; // Determine string length char c; unsigned n = 0; while ((c = get_char()) != 0) { n++; if (c == '"') break; } if (c == 0) { mon_error("Unterminated string"); return T_NULL; } // Allocate new buffer (n: size needed including terminating 0) str = (char *)realloc(str, n); // Copy string to buffer char *p = str; in_ptr = old_in_ptr; while (--n) *p++ = get_char(); *p++ = 0; get_char(); // skip closing '"' return T_STRING; } static enum Token get_hex_or_name(uintptr &i, char *&name) { // Remember start of token char *old_in_ptr = in_ptr; // Try hex number first if (get_hex_number(i) == T_NUMBER) return T_NUMBER; // Not a hex number, must be a variable name; determine its length in_ptr = old_in_ptr; char c = get_char(); unsigned n = 1; do { n++; c = get_char(); } while (isalnum(c)); // Allocate new buffer (n: size needed including terminating 0) name = (char *)realloc(name, n); // Copy name to buffer in_ptr = old_in_ptr; char *p = name; while (--n) *p++ = get_char(); *p = 0; return T_NAME; } /* * expression = eor_expr {OR eor_expr} * true: OK, false: Error */ bool mon_expression(uintptr *number) { uintptr accu, expr; if (!eor_expr(&accu)) return false; for (;;) switch (mon_token) { case T_OR: mon_get_token(); if (!eor_expr(&expr)) return false; accu |= expr; break; default: *number = accu; return true; } } /* * eor_expr = and_expr {EOR and_expr} * true: OK, false: Error */ static bool eor_expr(uintptr *number) { uintptr accu, expr; if (!and_expr(&accu)) return false; for (;;) switch (mon_token) { case T_EOR: mon_get_token(); if (!and_expr(&expr)) return false; accu ^= expr; break; default: *number = accu; return true; } } /* * and_expr = shift_expr {AND shift_expr} * true: OK, false: Error */ static bool and_expr(uintptr *number) { uintptr accu, expr; if (!shift_expr(&accu)) return false; for (;;) switch (mon_token) { case T_AND: mon_get_token(); if (!shift_expr(&expr)) return false; accu &= expr; break; default: *number = accu; return true; } } /* * shift_expr = add_expr {(SHIFTL | SHIFTR) add_expr} * true: OK, false: Error */ static bool shift_expr(uintptr *number) { uintptr accu, expr; if (!add_expr(&accu)) return false; for (;;) switch (mon_token) { case T_SHIFTL: mon_get_token(); if (!add_expr(&expr)) return false; accu <<= expr; break; case T_SHIFTR: mon_get_token(); if (!add_expr(&expr)) return false; accu >>= expr; break; default: *number = accu; return true; } } /* * add_expr = mul_expr {(PLUS | MINUS) mul_expr} * true: OK, false: Error */ static bool add_expr(uintptr *number) { uintptr accu, expr; if (!mul_expr(&accu)) return false; for (;;) switch (mon_token) { case T_PLUS: mon_get_token(); if (!mul_expr(&expr)) return false; accu += expr; break; case T_MINUS: mon_get_token(); if (!mul_expr(&expr)) return false; accu -= expr; break; default: *number = accu; return true; } } /* * mul_expr = factor {(MUL | DIV | MOD) factor} * true: OK, false: Error */ static bool mul_expr(uintptr *number) { uintptr accu, fact; if (!factor(&accu)) return false; for (;;) switch (mon_token) { case T_MUL: mon_get_token(); if (!factor(&fact)) return false; accu *= fact; break; case T_DIV: mon_get_token(); if (!factor(&fact)) return false; if (fact == 0) { mon_error("Division by 0"); return false; } accu /= fact; break; case T_MOD: mon_get_token(); if (!factor(&fact)) return false; if (fact == 0) { mon_error("Division by 0"); return false; } accu %= fact; break; default: *number = accu; return true; } } /* * factor = NUMBER | NAME | DOT | COLON | (PLUS | MINUS | NOT) factor | LPAREN expression RPAREN * true: OK, false: Error */ static bool factor(uintptr *number) { switch (mon_token) { case T_NUMBER: *number = mon_number; mon_get_token(); return true; case T_NAME:{ var_map::const_iterator v = vars.find(mon_name); if (v == vars.end()) return false; else { *number = v->second; mon_get_token(); return true; } } case T_DOT: *number = mon_dot_address; mon_get_token(); return true; case T_COLON: *number = colon_value; mon_get_token(); return true; case T_PLUS: mon_get_token(); return factor(number); case T_MINUS: mon_get_token(); if (factor(number)) { *number = -*number; return true; } else return false; case T_NOT: mon_get_token(); if (factor(number)) { *number = ~*number; return true; } else return false; case T_LPAREN: mon_get_token(); if (mon_expression(number)) if (mon_token == T_RPAREN) { mon_get_token(); return true; } else { mon_error("Missing ')'"); return false; } else { mon_error("Error in expression"); return false; } case T_END: mon_error("Required argument missing"); return false; default: mon_error("'(' or number expected"); return false; } } /* * Set/clear/show variables * set [var[=value]] */ static void set_var() { if (mon_token == T_END) { // Show all variables if (vars.empty()) fprintf(monout, "No variables defined\n"); else { var_map::const_iterator v = vars.begin(), end = vars.end(); for (v=vars.begin(); v!=end; ++v) fprintf(monout, "%s = %08lx\n", v->first.c_str(), v->second); } } else if (mon_token == T_NAME) { std::string var_name = mon_name; mon_get_token(); if (mon_token == T_ASSIGN) { // Set variable uintptr value; mon_get_token(); if (!mon_expression(&value)) return; if (mon_token != T_END) { mon_error("Too many arguments"); return; } vars[var_name] = value; } else if (mon_token == T_END) { // Clear variable vars.erase(var_name); } else mon_error("'=' expected"); } else mon_error("Variable name expected"); } /* * Clear all variables * cv */ static void clear_vars() { vars.clear(); } /* * Display help * h */ static void help_or_hunt() { if (mon_token != T_END) { hunt(); return; } fprintf(monout, "x Quit mon\n" "h This help text\n"); fprintf(monout, cmd_help); } /* * Display command list * ?? */ static void mon_cmd_list() { for (int i=0; i> address; if (is_disabled_break_points) disabled_break_points.insert(address); else active_break_points.insert(address); } fclose(file); } /* * Initialize mon */ void mon_init() { cmds = NULL; num_cmds = 0; cmd_help = NULL; mon_add_command("??", mon_cmd_list, "?? Show list of commands\n"); mon_add_command("ver", version, "ver Show version\n"); mon_add_command("?", print_expr, "? expression Calculate expression\n"); mon_add_command("@", reallocate, "@ [size] Reallocate buffer\n"); mon_add_command("i", ascii_dump, "i [start [end]] ASCII memory dump\n"); mon_add_command("m", memory_dump, "m [start [end]] Hex/ASCII memory dump\n"); mon_add_command("b", binary_dump, "b [start [end]] Binary memory dump\n"); mon_add_command("ba", break_point_add, "ba [address] Add a break point\n"); mon_add_command("br", break_point_remove, "br [breakpoints#] Remove a break point. If # is 0, remove all break points.\n"); mon_add_command("bd", break_point_disable, "bd [breakpoints#] Disable a break point. If # is 0, disable all break points.\n"); mon_add_command("be", break_point_enable, "be [breakpoints#] Enable a break point. If # is 0, enable all break points.\n"); mon_add_command("bi", break_point_info, "bi List all break points\n"); mon_add_command("bs", break_point_save, "bs \"file\" Save all break points to a file\n"); mon_add_command("bl", break_point_load, "bl \"file\" Load break points from a file\n"); mon_add_command("d", disassemble_ppc, "d [start [end]] Disassemble PowerPC code\n"); mon_add_command("d65", disassemble_6502, "d65 [start [end]] Disassemble 6502 code\n"); mon_add_command("d68", disassemble_680x0, "d68 [start [end]] Disassemble 680x0 code\n"); mon_add_command("d80", disassemble_z80, "d80 [start [end]] Disassemble Z80 code\n"); mon_add_command("d86", disassemble_80x86_32, "d86 [start [end]] Disassemble 80x86 (32-bit) code\n"); mon_add_command("d8086", disassemble_80x86_16, "d8086 [start [end]] Disassemble 80x86 (16-bit) code\n"); mon_add_command("d8664", disassemble_x86_64, "d8664 [start [end]] Disassemble x86-64 code\n"); mon_add_command(":", modify, ": start string Modify memory\n"); mon_add_command("f", fill, "f start end string Fill memory\n"); mon_add_command("y", apply_byte, "y[b|h|w] start end expr Apply expression to memory\n"); mon_add_command("yb", apply_byte, NULL); mon_add_command("yh", apply_half, NULL); mon_add_command("yw", apply_word, NULL); mon_add_command("t", transfer, "t start end dest Transfer memory\n"); mon_add_command("c", compare, "c start end dest Compare memory\n"); mon_add_command("h", help_or_hunt, "h start end string Search for byte string\n"); mon_add_command("\\", shell_command, "\\ \"command\" Execute shell command\n"); mon_add_command("ls", mon_exec, "ls [args] List directory contents\n"); mon_add_command("rm", mon_exec, "rm [args] Remove file(s)\n"); mon_add_command("cp", mon_exec, "cp [args] Copy file(s)\n"); mon_add_command("mv", mon_exec, "mv [args] Move file(s)\n"); mon_add_command("cd", mon_change_dir, "cd directory Change current directory\n"); mon_add_command("o", redir_output, "o [\"file\"] Redirect output\n"); mon_add_command("[", load_data, "[ start \"file\" Load data from file\n"); mon_add_command("]", save_data, "] start size \"file\" Save data to file\n"); mon_add_command("set", set_var, "set [var[=value]] Set/clear/show variables\n"); mon_add_command("cv", clear_vars, "cv Clear all variables\n"); mon_read_byte = NULL; mon_write_byte = NULL; input = NULL; mon_string = NULL; mon_name = NULL; } /* * Deinitialize mon */ void mon_exit() { if (cmds) { free(cmds); cmds = NULL; } num_cmds = 0; cmd_help = NULL; if (input) { free(input); input = NULL; } if (mon_string) { free(mon_string); mon_string = NULL; } if (mon_name) { free(mon_name); mon_name = NULL; } } /* * Main function, read-execute loop */ void mon(int argc, char **argv) { bool done = false, interactive = true; // Setup input/output streams monin = stdin; monout = stdout; monerr = stdout; // Make argc/argv point to the actual arguments const char *prg_name = argv[0]; if (argc) argc--; argv++; // Parse arguments mon_macos_mode = false; mon_use_real_mem = false; while (argc > 0) { if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) { printf("Usage: %s [-m] [-r] [command...]\n", prg_name); exit(0); } else if (strcmp(argv[0], "-m") == 0) mon_macos_mode = true; else if (strcmp(argv[0], "-r") == 0) mon_use_real_mem = true; else break; argc--; argv++; } interactive = (argc == 0); // Set up memory access functions if not supplied by the user if (mon_read_byte == NULL) { if (mon_use_real_mem) mon_read_byte = mon_read_byte_real; else mon_read_byte = mon_read_byte_buffer; } if (mon_write_byte == NULL) { if (mon_use_real_mem) mon_write_byte = mon_write_byte_real; else mon_write_byte = mon_write_byte_buffer; } // Allocate buffer if (!mon_use_real_mem) { mon_mem_size = 0x100000; mem = (uint8 *)malloc(mon_mem_size); // Print banner if (interactive) fprintf(monerr, "\n *** cxmon V" VERSION " by Christian Bauer and Marc Hellwig ***\n" " *** Press 'h' for help ***\n\n"); } // Clear variables vars.clear(); // In MacOS mode, pull in the lowmem globals as variables if (mon_macos_mode) { const lowmem_info *l = lowmem; while (l->name) { vars[l->name] = l->addr; l++; } } init_abort(); // Read and parse command line char *cmd = NULL; while (!done) { if (interactive) { char prompt[16]; sprintf(prompt, "[%0*lx]-> ", int(2 * sizeof(mon_dot_address)), mon_dot_address); read_line(prompt); if (!input) { done = true; continue; } } else { if (argc == 0) { done = true; break; } else { unsigned n = strlen(argv[0]) + 1; input = (char *)realloc(input, n); strcpy(in_ptr = input, argv[0]); argc--; argv++; } } // Skip leading spaces char c = get_char(); while (isspace(c)) c = get_char(); put_back(c); if (!c) continue; // blank line // Read command word char *p = in_ptr; while (isgraph(c)) c = get_char(); put_back(c); unsigned n = in_ptr - p; cmd = (char *)realloc(cmd, n + 1); memcpy(cmd, p, n); cmd[n] = 0; // Execute command if (strcmp(cmd, "x") == 0) { // Exit done = true; continue; } for (int i=0; i