//-------------------------------------------------------------------------- bool pc_debmod_t::should_stop_appcall(thid_t tid, const debug_event_t *event, ea_t ea) { if ( inherited::should_stop_appcall(tid, event, ea) ) return true; // Check if the current instruction is a "RET" and then dereferences // the contents of SP to find the return address. IF it matches, it is // time to stop regvals_t regs; regs.resize(X86_NREGS); do { // Start by reading registers if ( dbg_read_registers(tid, X86_RC_GENERAL, regs.begin()) != 1 ) break; // Get the opcodes uchar opcode; if ( dbg_read_memory((ea_t)regs[X86_REG_IP].ival, &opcode, 1) != 1 ) break; // Check for "RET" and "RET n" if ( opcode != 0xC3 && opcode != 0xC2 ) break; // Dereference value at ESP ea_t at_sp = BADADDR; if ( dbg_read_memory((ea_t)regs[X86_REG_SP].ival, &at_sp, sizeof(at_sp)) != sizeof(at_sp) ) break; return ea == at_sp; // time to stop! } while ( false ); return false; }
void read_memory(struct drpc_packet *pkt, char *buf) { void *addr; int size; int rc; addr = *(void **) (buf + 8); size = *(int *) (buf + 16); rc = dbg_read_memory(session, addr, size, buf); if (rc < 0) pkt->result = E_FAIL; *(int *) (buf + ((size + 3) / 4) * 4) = size; }
BOOL memory_disasm_one_insn(ADDRESS64* addr) { char ch; print_address(addr, TRUE); dbg_printf(": "); if (!dbg_read_memory(memory_to_linear_addr(addr), &ch, sizeof(ch))) { dbg_printf("-- no code accessible --\n"); return FALSE; } be_cpu->disasm_one_insn(addr, TRUE); dbg_printf("\n"); return TRUE; }
//-------------------------------------------------------------------------- // determine the future bpt size and read the original instruction from memory. // if necessary, the bpt address may be adjusted. // return the number of read bytes. // this function is overloaded in arm debugger subclasses. int debmod_t::read_bpt_orgbytes(ea_t *p_ea, int *p_len, uchar *buf, int bufsize) { int len = *p_len; if ( (debugger_flags & DBG_FLAG_CAN_CONT_BPT) == 0 ) { // we must save the original bytes before adding the bpt QASSERT(30017, bufsize >= len); if ( dbg_read_memory(*p_ea, buf, len) <= 0 ) return -1; } else { // if the debuger can itself continue from bpts, // orgbytes will not be used by the kernel. however // we must return something because non-empty orgbytes mean // that a soft bpt is active. we return zeroes in b->orgbytes. } return len; }
/*********************************************************************** * get_watched_value * * Returns the value watched by watch point 'num'. */ static BOOL get_watched_value(int num, DWORD64* val) { DWORD64 buf[1]; if (!dbg_read_memory(memory_to_linear_addr(&dbg_curr_process->bp[num].addr), buf, dbg_curr_process->bp[num].w.len + 1)) return FALSE; switch (dbg_curr_process->bp[num].w.len + 1) { case 8: *val = *(DWORD64*)buf; break; case 4: *val = *(DWORD*)buf; break; case 2: *val = *(WORD*)buf; break; case 1: *val = *(BYTE*)buf; break; default: RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); } return TRUE; }
/*********************************************************************** * memory_read_value * * Read a memory value. */ BOOL memory_read_value(const struct dbg_lvalue* lvalue, DWORD size, void* result) { BOOL ret = FALSE; if (lvalue->cookie == DLV_TARGET) { void* linear = memory_to_linear_addr(&lvalue->addr); if (!(ret = dbg_read_memory(linear, result, size))) memory_report_invalid_addr(linear); } else { if (lvalue->addr.Offset) { memcpy(result, (void*)(DWORD_PTR)lvalue->addr.Offset, size); ret = TRUE; } } return TRUE; }
/*********************************************************************** * break_add_break * * Add a breakpoint. */ BOOL break_add_break(const ADDRESS64* addr, BOOL verbose, BOOL swbp) { int num; BYTE ch; struct dbg_breakpoint* bp = dbg_curr_process->bp; int type = swbp ? be_xpoint_break : be_xpoint_watch_exec; if ((num = find_xpoint(addr, type)) >= 1) { bp[num].refcount++; dbg_printf("Breakpoint %d at ", num); print_address(&bp[num].addr, TRUE); dbg_printf(" (refcount=%d)\n", bp[num].refcount); return TRUE; } if (!dbg_read_memory(memory_to_linear_addr(addr), &ch, sizeof(ch))) { if (verbose) { dbg_printf("Invalid address "); print_bare_address(addr); dbg_printf(", can't set breakpoint\n"); } return FALSE; } if ((num = init_xpoint(type, addr)) == -1) return FALSE; dbg_printf("Breakpoint %d at ", num); print_address(&bp[num].addr, TRUE); dbg_printf("\n"); return TRUE; }
static DWORD dbg_handle_exception(const EXCEPTION_RECORD* rec, BOOL first_chance) { BOOL is_debug = FALSE; THREADNAME_INFO* pThreadName; struct dbg_thread* pThread; assert(dbg_curr_thread); WINE_TRACE("exception=%x first_chance=%c\n", rec->ExceptionCode, first_chance ? 'Y' : 'N'); switch (rec->ExceptionCode) { case EXCEPTION_BREAKPOINT: case EXCEPTION_SINGLE_STEP: is_debug = TRUE; break; case EXCEPTION_NAME_THREAD: pThreadName = (THREADNAME_INFO*)(rec->ExceptionInformation); if (pThreadName->dwThreadID == -1) pThread = dbg_curr_thread; else pThread = dbg_get_thread(dbg_curr_process, pThreadName->dwThreadID); if(!pThread) { dbg_printf("Thread ID=%04x not in our list of threads -> can't rename\n", pThreadName->dwThreadID); return DBG_CONTINUE; } if (dbg_read_memory(pThreadName->szName, pThread->name, 9)) dbg_printf("Thread ID=%04x renamed using MS VC6 extension (name==\"%s\")\n", pThread->tid, pThread->name); return DBG_CONTINUE; } if (first_chance && !is_debug && !DBG_IVAR(BreakOnFirstChance) && !(rec->ExceptionFlags & EH_STACK_INVALID)) { /* pass exception to program except for debug exceptions */ return DBG_EXCEPTION_NOT_HANDLED; } if (!is_debug) { /* print some infos */ dbg_printf("%s: ", first_chance ? "First chance exception" : "Unhandled exception"); switch (rec->ExceptionCode) { case EXCEPTION_INT_DIVIDE_BY_ZERO: dbg_printf("divide by zero"); break; case EXCEPTION_INT_OVERFLOW: dbg_printf("overflow"); break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: dbg_printf("array bounds"); break; case EXCEPTION_ILLEGAL_INSTRUCTION: dbg_printf("illegal instruction"); break; case EXCEPTION_STACK_OVERFLOW: dbg_printf("stack overflow"); break; case EXCEPTION_PRIV_INSTRUCTION: dbg_printf("privileged instruction"); break; case EXCEPTION_ACCESS_VIOLATION: if (rec->NumberParameters == 2) dbg_printf("page fault on %s access to 0x%08lx", rec->ExceptionInformation[0] == EXCEPTION_WRITE_FAULT ? "write" : rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT ? "execute" : "read", rec->ExceptionInformation[1]); else dbg_printf("page fault"); break; case EXCEPTION_DATATYPE_MISALIGNMENT: dbg_printf("Alignment"); break; case DBG_CONTROL_C: dbg_printf("^C"); break; case CONTROL_C_EXIT: dbg_printf("^C"); break; case STATUS_POSSIBLE_DEADLOCK: { ADDRESS64 addr; addr.Mode = AddrModeFlat; addr.Offset = rec->ExceptionInformation[0]; dbg_printf("wait failed on critical section "); print_address(&addr, FALSE); } if (!DBG_IVAR(BreakOnCritSectTimeOut)) { dbg_printf("\n"); return DBG_EXCEPTION_NOT_HANDLED; } break; case EXCEPTION_WINE_STUB: { char dll[32], name[64]; memory_get_string(dbg_curr_process, (void*)rec->ExceptionInformation[0], TRUE, FALSE, dll, sizeof(dll)); if (HIWORD(rec->ExceptionInformation[1])) memory_get_string(dbg_curr_process, (void*)rec->ExceptionInformation[1], TRUE, FALSE, name, sizeof(name)); else sprintf( name, "%ld", rec->ExceptionInformation[1] ); dbg_printf("unimplemented function %s.%s called", dll, name); } break; case EXCEPTION_WINE_ASSERTION: dbg_printf("assertion failed"); break; case EXCEPTION_VM86_INTx: dbg_printf("interrupt %02lx in vm86 mode", rec->ExceptionInformation[0]); break; case EXCEPTION_VM86_STI: dbg_printf("sti in vm86 mode"); break; case EXCEPTION_VM86_PICRETURN: dbg_printf("PIC return in vm86 mode"); break; case EXCEPTION_FLT_DENORMAL_OPERAND: dbg_printf("denormal float operand"); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: dbg_printf("divide by zero"); break; case EXCEPTION_FLT_INEXACT_RESULT: dbg_printf("inexact float result"); break; case EXCEPTION_FLT_INVALID_OPERATION: dbg_printf("invalid float operation"); break; case EXCEPTION_FLT_OVERFLOW: dbg_printf("floating point overflow"); break; case EXCEPTION_FLT_UNDERFLOW: dbg_printf("floating point underflow"); break; case EXCEPTION_FLT_STACK_CHECK: dbg_printf("floating point stack check"); break; case CXX_EXCEPTION: if(rec->NumberParameters == 3 && rec->ExceptionInformation[0] == CXX_FRAME_MAGIC) dbg_printf("C++ exception(object = 0x%08lx, type = 0x%08lx)", rec->ExceptionInformation[1], rec->ExceptionInformation[2]); else dbg_printf("C++ exception with strange parameter count %d or magic 0x%08lx", rec->NumberParameters, rec->ExceptionInformation[0]); break; default: dbg_printf("0x%08x", rec->ExceptionCode); break; } } if( (rec->ExceptionFlags & EH_STACK_INVALID) ) { dbg_printf( ", invalid program stack" ); } if (dbg_exception_prolog(is_debug, first_chance, rec)) { dbg_interactiveP = TRUE; return 0; } dbg_exception_epilog(); return DBG_CONTINUE; }
/*********************************************************************** * memory_examine * * Implementation of the 'x' command. */ void memory_examine(const struct dbg_lvalue *lvalue, int count, char format) { int i; char buffer[256]; ADDRESS64 addr; void *linear; types_extract_as_address(lvalue, &addr); linear = memory_to_linear_addr(&addr); if (format != 'i' && count > 1) { print_address(&addr, FALSE); dbg_printf(": "); } switch (format) { case 'u': if (count == 1) count = 256; memory_get_string(dbg_curr_process, linear, TRUE, TRUE, buffer, min(count, sizeof(buffer))); dbg_printf("%s\n", buffer); return; case 's': if (count == 1) count = 256; memory_get_string(dbg_curr_process, linear, TRUE, FALSE, buffer, min(count, sizeof(buffer))); dbg_printf("%s\n", buffer); return; case 'i': while (count-- && memory_disasm_one_insn(&addr)); return; case 'g': while (count--) { GUID guid; if (!dbg_read_memory(linear, &guid, sizeof(guid))) { memory_report_invalid_addr(linear); break; } dbg_printf("{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); linear = (char*)linear + sizeof(guid); addr.Offset += sizeof(guid); if (count) { print_address(&addr, FALSE); dbg_printf(": "); } } return; #define DO_DUMP2(_t,_l,_f,_vv) { \ _t _v; \ for (i = 0; i < count; i++) { \ if (!dbg_read_memory(linear, &_v, sizeof(_t))) \ { memory_report_invalid_addr(linear); break; } \ dbg_printf(_f, (_vv)); \ addr.Offset += sizeof(_t); \ linear = (char*)linear + sizeof(_t); \ if ((i % (_l)) == (_l) - 1 && i != count - 1) \ { \ dbg_printf("\n"); \ print_address(&addr, FALSE); \ dbg_printf(": "); \ } \ } \ dbg_printf("\n"); \ } \ return #define DO_DUMP(_t,_l,_f) DO_DUMP2(_t,_l,_f,_v) case 'x': DO_DUMP(int, 4, " %8.8x"); case 'd': DO_DUMP(unsigned int, 4, " %10d"); case 'w': DO_DUMP(unsigned short, 8, " %04x"); case 'c': DO_DUMP2(char, 32, " %c", (_v < 0x20) ? ' ' : _v); case 'b': DO_DUMP2(char, 16, " %02x", (_v) & 0xff); } }
//-------------------------------------------------------------------------- int idaapi debmod_t::dbg_update_bpts( update_bpt_info_t *bpts, int nadd, int ndel) { // Write breakpoints to the process int cnt = 0; update_bpt_info_t *b; update_bpt_info_t *end = bpts + nadd; for ( b=bpts; b != end; b++ ) { int code = b->code; if ( code != BPT_OK ) continue; uchar len; char buf[32]; int nread = 0; ea_t ea = b->ea; if ( b->type == BPT_SOFT ) { len = bpt_code.size(); #ifdef __ARM__ if ( (ea & 1) != 0 ) // T bit is set, use a thumb breakpoint { ea--; len = 2; } #endif nread = len; if ( (debugger_flags & DBG_FLAG_CAN_CONT_BPT) == 0 ) { // we must save the original bytes before adding the bpt QASSERT(30017, sizeof(buf) >= len); if ( dbg_read_memory(ea, buf, len) <= 0 ) code = BPT_READ_ERROR; } } else { len = b->size; } if ( code == BPT_OK ) code = dbg_add_bpt(b->type, ea, len) ? BPT_OK : BPT_WRITE_ERROR; b->code = code; if ( code == BPT_OK ) { cnt++; if ( nread > 0 ) b->orgbytes = bytevec_t(buf, nread); } } // Delete breakpoints from the process. end += ndel; for ( ; b != end; b++ ) { b->code = BPT_OK; int len = b->type == BPT_SOFT ? b->orgbytes.size() : b->size; if ( dbg_del_bpt(b->type, b->ea, b->orgbytes.begin(), len) > 0 ) cnt++; else b->code = BPT_WRITE_ERROR; } return cnt; }
static BOOL fill_sym_lvalue(const SYMBOL_INFO* sym, ULONG_PTR base, struct dbg_lvalue* lvalue, char* buffer, size_t sz) { if (buffer) buffer[0] = '\0'; if (sym->Flags & SYMFLAG_REGISTER) { DWORD_PTR* pval; if (!memory_get_register(sym->Register, &pval, buffer, sz)) return FALSE; lvalue->cookie = DLV_HOST; lvalue->addr.Offset = (DWORD_PTR)pval; } else if (sym->Flags & SYMFLAG_REGREL) { DWORD_PTR* pval; size_t l; *buffer++ = '['; sz--; if (!memory_get_register(sym->Register, &pval, buffer, sz)) return FALSE; l = strlen(buffer); sz -= l; buffer += l; lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = (ULONG64)*pval + sym->Address; if ((LONG_PTR)sym->Address >= 0) snprintf(buffer, sz, "+%ld]", (ULONG_PTR)sym->Address); else snprintf(buffer, sz, "-%ld]", -(LONG_PTR)sym->Address); } else if (sym->Flags & SYMFLAG_VALUEPRESENT) { struct dbg_type type; VARIANT v; type.module = sym->ModBase; type.id = sym->info; if (!types_get_info(&type, TI_GET_VALUE, &v)) { if (buffer) snprintf(buffer, sz, "Couldn't get full value information for %s", sym->Name); return FALSE; } else if (v.n1.n2.vt & VT_BYREF) { /* FIXME: this won't work for pointers or arrays, as we don't always * know, if the value to be dereferenced lies in debuggee or * debugger address space. */ if (sym->Tag == SymTagPointerType || sym->Tag == SymTagArrayType) { if (buffer) snprintf(buffer, sz, "Couldn't dereference pointer for const value for %s", sym->Name); return FALSE; } /* this is likely Wine's dbghelp which passes const values by reference * (object is managed by dbghelp, hence in debugger address space) */ lvalue->cookie = DLV_HOST; lvalue->addr.Offset = (DWORD_PTR)sym->Value; } else { DWORD* pdw = (DWORD*)lexeme_alloc_size(sizeof(*pdw)); lvalue->cookie = DLV_HOST; lvalue->addr.Offset = (DWORD_PTR)pdw; *pdw = sym->Value; } } else if (sym->Flags & SYMFLAG_LOCAL) { lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = base + sym->Address; } else if (sym->Flags & SYMFLAG_TLSREL) { PROCESS_BASIC_INFORMATION pbi; THREAD_BASIC_INFORMATION tbi; DWORD_PTR addr; PEB peb; PEB_LDR_DATA ldr_data; PLIST_ENTRY head, current; LDR_MODULE ldr_module; unsigned tlsindex = -1; if (NtQueryInformationProcess(dbg_curr_process->handle, ProcessBasicInformation, &pbi, sizeof(pbi), NULL) || NtQueryInformationThread(dbg_curr_thread->handle, ThreadBasicInformation, &tbi, sizeof(tbi), NULL)) { tls_error: if (buffer) snprintf(buffer, sz, "Cannot read TLS address\n"); return FALSE; } addr = (DWORD_PTR)&(((TEB*)tbi.TebBaseAddress)->ThreadLocalStoragePointer); if (!dbg_read_memory((void*)addr, &addr, sizeof(addr)) || !dbg_read_memory(pbi.PebBaseAddress, &peb, sizeof(peb)) || !dbg_read_memory(peb.LdrData, &ldr_data, sizeof(ldr_data))) goto tls_error; current = ldr_data.InLoadOrderModuleList.Flink; head = &((PEB_LDR_DATA*)peb.LdrData)->InLoadOrderModuleList; do { if (!dbg_read_memory(CONTAINING_RECORD(current, LDR_MODULE, InLoadOrderModuleList), &ldr_module, sizeof(ldr_module))) goto tls_error; if ((DWORD_PTR)ldr_module.BaseAddress == sym->ModBase) { tlsindex = ldr_module.TlsIndex; break; } current = ldr_module.InLoadOrderModuleList.Flink; } while (current != head); addr += tlsindex * sizeof(DWORD_PTR); if (!dbg_read_memory((void*)addr, &addr, sizeof(addr))) goto tls_error; lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = addr + sym->Address; } else { lvalue->cookie = DLV_TARGET; lvalue->addr.Offset = sym->Address; } lvalue->addr.Mode = AddrModeFlat; lvalue->type.module = sym->ModBase; lvalue->type.id = sym->TypeIndex; return TRUE; }
/*********************************************************************** * break_restart_execution * * Set the bp to the correct state to restart execution * in the given mode. */ void break_restart_execution(int count) { ADDRESS64 addr; enum dbg_line_status status; enum dbg_exec_mode mode, ret_mode; ADDRESS64 callee; void* linear; memory_get_current_pc(&addr); linear = memory_to_linear_addr(&addr); /* * This is the mode we will be running in after we finish. We would like * to be able to modify this in certain cases. */ ret_mode = mode = dbg_curr_thread->exec_mode; /* we've stopped on a xpoint (other than step over) */ if (dbg_curr_thread->stopped_xpoint > 0) { /* * If we have set a new value, then save it in the BP number. */ if (count != 0 && mode == dbg_exec_cont) { dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].skipcount = count; } /* If we've stopped on a breakpoint, single step over it (, then run) */ if (is_xpoint_break(dbg_curr_thread->stopped_xpoint)) mode = dbg_exec_step_into_insn; } else if (mode == dbg_exec_cont && count > 1) { dbg_printf("Not stopped at any breakpoint; argument ignored.\n"); } if (mode == dbg_exec_finish && be_cpu->is_function_return(linear)) { mode = ret_mode = dbg_exec_step_into_insn; } /* * See if the function we are stepping into has debug info * and line numbers. If not, then we step over it instead. * FIXME - we need to check for things like thunks or trampolines, * as the actual function may in fact have debug info. */ if (be_cpu->is_function_call(linear, &callee)) { status = symbol_get_function_line_status(&callee); #if 0 /* FIXME: we need to get the thunk type */ /* * Anytime we have a trampoline, step over it. */ if ((mode == EXEC_STEP_OVER || mode == EXEC_STEPI_OVER) && status == dbg_in_a_thunk) { WINE_WARN("Not stepping into trampoline at %p (no lines)\n", memory_to_linear_addr(&callee)); mode = EXEC_STEP_OVER_TRAMPOLINE; } #endif if (mode == dbg_exec_step_into_line && status == dbg_no_line_info) { WINE_WARN("Not stepping into function at %p (no lines)\n", memory_to_linear_addr(&callee)); mode = dbg_exec_step_over_line; } } if (mode == dbg_exec_step_into_line && symbol_get_function_line_status(&addr) == dbg_no_line_info) { dbg_printf("Single stepping until exit from function,\n" "which has no line number information.\n"); ret_mode = mode = dbg_exec_finish; } switch (mode) { case dbg_exec_cont: /* Continuous execution */ be_cpu->single_step(&dbg_context, FALSE); break_set_xpoints(TRUE); break; #if 0 case EXEC_STEP_OVER_TRAMPOLINE: /* * This is the means by which we step over our conversion stubs * in callfrom*.s and callto*.s. We dig the appropriate address * off the stack, and we set the breakpoint there instead of the * address just after the call. */ be_cpu->get_addr(dbg_curr_thread->handle, &dbg_context, be_cpu_addr_stack, &addr); /* FIXME: we assume stack grows as on an i386 */ addr.Offset += 2 * sizeof(unsigned int); dbg_read_memory(memory_to_linear_addr(&addr), &addr.Offset, sizeof(addr.Offset)); dbg_curr_process->bp[0].addr = addr; dbg_curr_process->bp[0].enabled = TRUE; dbg_curr_process->bp[0].refcount = 1; dbg_curr_process->bp[0].skipcount = 0; dbg_curr_process->bp[0].xpoint_type = be_xpoint_break; dbg_curr_process->bp[0].condition = NULL; be_cpu->single_step(&dbg_context, FALSE); break_set_xpoints(TRUE); break; #endif case dbg_exec_finish: case dbg_exec_step_over_insn: /* Stepping over a call */ case dbg_exec_step_over_line: /* Stepping over a call */ if (be_cpu->is_step_over_insn(linear)) { be_cpu->disasm_one_insn(&addr, FALSE); dbg_curr_process->bp[0].addr = addr; dbg_curr_process->bp[0].enabled = TRUE; dbg_curr_process->bp[0].refcount = 1; dbg_curr_process->bp[0].skipcount = 0; dbg_curr_process->bp[0].xpoint_type = be_xpoint_break; dbg_curr_process->bp[0].condition = NULL; be_cpu->single_step(&dbg_context, FALSE); break_set_xpoints(TRUE); break; } /* else fall through to single-stepping */ case dbg_exec_step_into_line: /* Single-stepping a line */ case dbg_exec_step_into_insn: /* Single-stepping an instruction */ be_cpu->single_step(&dbg_context, TRUE); break; default: RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL); } dbg_curr_thread->step_over_bp = dbg_curr_process->bp[0]; dbg_curr_thread->exec_mode = ret_mode; }