/*********************************************************************** * symbol_get_function_line_status * * Find the symbol nearest to a given address. */ enum dbg_line_status symbol_get_function_line_status(const ADDRESS64* addr) { IMAGEHLP_LINE64 il; DWORD disp; ULONG64 disp64, start; DWORD_PTR lin = (DWORD_PTR)memory_to_linear_addr(addr); char buffer[sizeof(SYMBOL_INFO) + 256]; SYMBOL_INFO* sym = (SYMBOL_INFO*)buffer; struct dbg_type func; il.SizeOfStruct = sizeof(il); sym->SizeOfStruct = sizeof(SYMBOL_INFO); sym->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO); /* do we have some info for lin address ? */ if (!SymFromAddr(dbg_curr_process->handle, lin, &disp64, sym)) { ADDRESS64 jumpee; /* some compilers insert thunks in their code without debug info associated * take care of this situation */ if (be_cpu->is_jump((void*)lin, &jumpee)) return symbol_get_function_line_status(&jumpee); return dbg_no_line_info; } switch (sym->Tag) { case SymTagThunk: /* FIXME: so far dbghelp doesn't return the 16 <=> 32 thunks * and furthermore, we no longer take care of them !!! */ return dbg_in_a_thunk; case SymTagFunction: case SymTagPublicSymbol: break; default: WINE_FIXME("Unexpected sym-tag 0x%08x\n", sym->Tag); case SymTagData: return dbg_no_line_info; } /* we should have a function now */ if (!SymGetLineFromAddr64(dbg_curr_process->handle, lin, &disp, &il)) return dbg_no_line_info; func.module = sym->ModBase; func.id = sym->info; if (symbol_get_debug_start(&func, &start) && lin < start) return dbg_not_on_a_line_number; if (!sym->Size) sym->Size = 0x100000; if (il.FileName && il.FileName[0] && disp < sym->Size) return (disp == 0) ? dbg_on_a_line_number : dbg_not_on_a_line_number; return dbg_no_line_info; }
/*********************************************************************** * break_should_continue * * Determine if we should continue execution after a SIGTRAP signal when * executing in the given mode. */ BOOL break_should_continue(ADDRESS64* addr, DWORD code) { enum dbg_exec_mode mode = dbg_curr_thread->exec_mode; if (dbg_curr_thread->stopped_xpoint > 0) { if (!should_stop(dbg_curr_thread->stopped_xpoint)) return TRUE; switch (dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].xpoint_type) { case be_xpoint_break: case be_xpoint_watch_exec: dbg_printf("Stopped on breakpoint %d at ", dbg_curr_thread->stopped_xpoint); print_address(&dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].addr, TRUE); dbg_printf("\n"); break; case be_xpoint_watch_read: case be_xpoint_watch_write: dbg_printf("Stopped on watchpoint %d at ", dbg_curr_thread->stopped_xpoint); print_address(addr, TRUE); dbg_printf(" new value %s\n", wine_dbgstr_longlong(dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].w.oldval)); } return FALSE; } /* * If our mode indicates that we are stepping line numbers, * get the current function, and figure out if we are exactly * on a line number or not. */ if (mode == dbg_exec_step_over_line || mode == dbg_exec_step_into_line) { if (symbol_get_function_line_status(addr) == dbg_on_a_line_number) dbg_curr_thread->exec_count--; } else if (mode == dbg_exec_step_over_insn || mode == dbg_exec_step_into_insn) dbg_curr_thread->exec_count--; if (dbg_curr_thread->exec_count > 0 || mode == dbg_exec_finish) { /* * We still need to execute more instructions. */ return TRUE; } /* no breakpoint, continue if in continuous mode */ return mode == dbg_exec_cont || mode == dbg_exec_finish; }
/*********************************************************************** * 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; }