diff options
author | Eugene Tarassov | 2011-11-24 21:29:24 +0000 |
---|---|---|
committer | Eugene Tarassov | 2011-11-24 21:29:24 +0000 |
commit | f3647b55e7958eb89dece294d99ac8389c9cb57b (patch) | |
tree | 817b9703820880e224e27c4c85e30ecfa352cd17 /agent/system/Windows/tcf/context-win32.c | |
parent | dafe1eefa355d67b2f9de21b90914077c1194ef0 (diff) | |
download | org.eclipse.tcf.agent-f3647b55e7958eb89dece294d99ac8389c9cb57b.tar.gz org.eclipse.tcf.agent-f3647b55e7958eb89dece294d99ac8389c9cb57b.tar.xz org.eclipse.tcf.agent-f3647b55e7958eb89dece294d99ac8389c9cb57b.zip |
TCF Agent: added "tcf/" prefix to system and machine dependent header files.
Diffstat (limited to 'agent/system/Windows/tcf/context-win32.c')
-rw-r--r-- | agent/system/Windows/tcf/context-win32.c | 1434 |
1 files changed, 1434 insertions, 0 deletions
diff --git a/agent/system/Windows/tcf/context-win32.c b/agent/system/Windows/tcf/context-win32.c new file mode 100644 index 00000000..9cf65927 --- /dev/null +++ b/agent/system/Windows/tcf/context-win32.c @@ -0,0 +1,1434 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * You may elect to redistribute this code under either of these licenses. + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ + +/* + * This module handles process/thread OS contexts and their state machine. + */ + +#include <tcf/config.h> + +#if defined(WIN32) + +#if ENABLE_DebugContext && !ENABLE_ContextProxy + +#include <stdlib.h> +#include <assert.h> +#include <errno.h> +#include <signal.h> +#include <tcf/framework/context.h> +#include <tcf/framework/events.h> +#include <tcf/framework/errors.h> +#include <tcf/framework/trace.h> +#include <tcf/framework/myalloc.h> +#include <tcf/framework/waitpid.h> +#include <tcf/framework/signames.h> +#include <tcf/services/breakpoints.h> +#include <tcf/services/memorymap.h> +#include <tcf/services/runctrl.h> +#include <system/Windows/tcf/context-win32.h> +#include <system/Windows/tcf/regset.h> +#include <system/Windows/tcf/windbgcache.h> + +#if !defined(USE_HW_BPS) +# define USE_HW_BPS 1 +#endif +#if USE_HW_BPS +# define MAX_HW_BPS 4 +#endif + +typedef struct ContextExtensionWin32 { + pid_t pid; + HANDLE handle; + DEBUG_EVENT debug_event; + EXCEPTION_DEBUG_INFO suspend_reason; + int stop_pending; + int start_pending; + REG_SET * regs; /* copy of context registers, updated when context stops */ + ErrorReport * regs_error; /* if not NULL, 'regs' is invalid */ + int regs_dirty; /* if not 0, 'regs' is modified and needs to be saved before context is continued */ + int trace_flag; + uint8_t step_opcodes[4]; + SIZE_T step_opcodes_len; + ContextAddress step_opcodes_addr; + struct DebugState * debug_state; +#if USE_HW_BPS + ContextBreakpoint * triggered_hw_bps[MAX_HW_BPS + 1]; + unsigned hw_bps_regs_generation; + DWORD skip_hw_bp_addr; +#endif +} ContextExtensionWin32; + +static size_t context_extension_offset = 0; + +#define EXT(ctx) ((ContextExtensionWin32 *)((char *)(ctx) + context_extension_offset)) + +typedef struct DebugState { + int error; + int state; + DWORD process_id; + DWORD debug_thread_id; + HANDLE debug_thread; + HANDLE debug_thread_semaphore; + HANDLE debug_event_inp; + HANDLE debug_event_out; + DWORD ini_thread_id; + HANDLE ini_thread_handle; + DWORD main_thread_id; + HANDLE main_thread_handle; + int reporting_debug_event; + int break_posted; + HANDLE break_thread; + LPVOID break_thread_code; + DWORD break_thread_id; + HANDLE file_handle; + DWORD64 base_address; + HANDLE module_handle; + DWORD64 module_address; + ContextAttachCallBack * attach_callback; + void * attach_data; +#if USE_HW_BPS + int ok_to_use_hw_bp; /* NtContinue() changes Dr6 and Dr7, so HW breakpoints should be disabled until NtContinue() is done */ + ContextBreakpoint * hw_bps[MAX_HW_BPS]; + unsigned hw_bps_generation; +#endif +} DebugState; + +#define DEBUG_STATE_INIT 0 +#define DEBUG_STATE_PRS_CREATED 1 +#define DEBUG_STATE_PRS_ATTACHED 2 + +typedef struct DebugEvent { + DebugState * debug_state; + DEBUG_EVENT win32_event; + DWORD continue_status; +} DebugEvent; + +static OSVERSIONINFOEX os_version; + +#define MAX_EXCEPTION_HANDLERS 8 +static ContextExceptionHandler * exception_handlers[MAX_EXCEPTION_HANDLERS]; +static unsigned exception_handler_cnt = 0; + +static MemoryErrorInfo mem_err_info; + +#include <tcf/framework/pid-hash.h> + +#define EXCEPTION_DEBUGGER_IO 0x406D1388 + +const char * context_suspend_reason(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + DWORD exception_code = ext->suspend_reason.ExceptionRecord.ExceptionCode; + static char buf[64]; + + if (exception_code == 0) return REASON_USER_REQUEST; + if (ext->suspend_reason.dwFirstChance) { + if (exception_code == EXCEPTION_SINGLE_STEP) return REASON_STEP; + if (exception_code == EXCEPTION_BREAKPOINT) return "Break Instruction"; + snprintf(buf, sizeof(buf), "Exception %#lx", exception_code); + } + else { + snprintf(buf, sizeof(buf), "Unhandled exception %#lx", exception_code); + } + return buf; +} + +static int get_signal_index(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + DWORD exception_code = ext->suspend_reason.ExceptionRecord.ExceptionCode; + + if (exception_code == 0) return 0; + return get_signal_from_code(exception_code); +} + +static const char * win32_debug_event_name(int event) { + switch (event) { + case CREATE_PROCESS_DEBUG_EVENT: + return "CREATE_PROCESS_DEBUG_EVENT"; + case CREATE_THREAD_DEBUG_EVENT: + return "CREATE_THREAD_DEBUG_EVENT"; + case EXCEPTION_DEBUG_EVENT: + return "EXCEPTION_DEBUG_EVENT"; + case EXIT_PROCESS_DEBUG_EVENT: + return "EXIT_PROCESS_DEBUG_EVENT"; + case EXIT_THREAD_DEBUG_EVENT: + return "EXIT_THREAD_DEBUG_EVENT"; + case LOAD_DLL_DEBUG_EVENT: + return "LOAD_DLL_DEBUG_EVENT"; + case OUTPUT_DEBUG_STRING_EVENT: + return "OUTPUT_DEBUG_STRING_EVENT"; + case UNLOAD_DLL_DEBUG_EVENT: + return "UNLOAD_DLL_DEBUG_EVENT"; + } + return "Unknown"; +} + +static int log_error(const char * fn, int ok) { + int err; + if (ok) return 0; + assert(is_dispatch_thread()); + err = set_win32_errno(GetLastError()); + trace(LOG_ALWAYS, "context: %s: %s", fn, errno_to_str(errno)); + return err; +} + +static void event_win32_context_exited(Context * ctx); + +static void get_registers(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + + if (ext->regs->ContextFlags) return; + + assert(!ctx->exited); + assert(context_has_state(ctx)); + assert(ctx->stopped); + + ext->regs->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; +#if USE_HW_BPS + ext->regs->ContextFlags |= CONTEXT_DEBUG_REGISTERS; +#endif + if (GetThreadContext(ext->handle, ext->regs) == 0) { + ext->regs_error = get_error_report(log_error("GetThreadContext", 0)); + } + else { + ext->trace_flag = (ext->regs->EFlags & 0x100) != 0; + trace(LOG_CONTEXT, "context: get regs OK: ctx %#lx, id %s, PC %#lx", + ctx, ctx->id, ext->regs->Eip); + } +} + +static DWORD event_win32_context_stopped(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + DebugState * debug_state = EXT(ctx->mem)->debug_state; + ContextAddress exception_addr = (ContextAddress)ext->suspend_reason.ExceptionRecord.ExceptionAddress; + DWORD exception_code = ext->suspend_reason.ExceptionRecord.ExceptionCode; + DWORD continue_status = DBG_CONTINUE; + + assert(is_dispatch_thread()); + assert(!ctx->exited); + assert(!ctx->stopped); + assert(ext->handle != NULL); + assert(ctx->parent != NULL); + + ext->stop_pending = 0; + ext->start_pending = 0; + + trace(LOG_CONTEXT, "context: stopped: ctx %#lx, id %s, exception %#lx", + ctx, ctx->id, exception_code); + + if (SuspendThread(ext->handle) == (DWORD)-1) { + DWORD err = GetLastError(); + ctx->exiting = 1; + if (err == ERROR_ACCESS_DENIED) { + /* Already exited */ + return DBG_CONTINUE; + } + log_error("SuspendThread", 0); + return DBG_EXCEPTION_NOT_HANDLED; + } + + if (ext->regs_error) { + release_error_report(ext->regs_error); + ext->regs_error = NULL; + } + memset(ext->regs, 0, sizeof(REG_SET)); + + ctx->signal = get_signal_index(ctx); + ctx->pending_signals = 0; + ctx->stopped = 1; + ctx->stopped_by_bp = 0; + ctx->stopped_by_cb = NULL; + if (exception_code == 0) { + ctx->stopped_by_exception = 0; + } + else if (ext->suspend_reason.dwFirstChance) { + ctx->stopped_by_exception = 0; + switch (exception_code) { + case EXCEPTION_SINGLE_STEP: + get_registers(ctx); + if (!ext->regs_error) { +#if USE_HW_BPS + if (ext->regs->Eip != ext->skip_hw_bp_addr) ext->skip_hw_bp_addr = 0; + if (ext->regs->Dr6 & 0xfu) { + int i, j = 0; + for (i = 0; i < MAX_HW_BPS; i++) { + if (ext->regs->Dr6 & (1u << i)) { + ContextBreakpoint * bp = debug_state->hw_bps[i]; + if (bp == NULL) continue; + if (bp->address == ext->regs->Eip && (bp->access_types & CTX_BP_ACCESS_INSTRUCTION)) { + ext->skip_hw_bp_addr = ext->regs->Eip; + } + ctx->stopped_by_cb = ext->triggered_hw_bps; + ctx->stopped_by_cb[j++] = bp; + ctx->stopped_by_cb[j] = NULL; + } + } + } +#endif + if (ext->step_opcodes_len > 0 && ext->step_opcodes[0] == 0x9c && ext->step_opcodes_addr != ext->regs->Eip) { + /* PUSHF instruction: need to clear trace flag from top of the stack */ + SIZE_T bcnt = 0; + ContextAddress buf = 0; + assert(ext->regs->EFlags & 0x100); + assert(ext->step_opcodes_addr == ext->regs->Eip - 1); + if (!ReadProcessMemory(EXT(ctx->mem)->handle, (LPCVOID)ext->regs->Esp, &buf, sizeof(ContextAddress), &bcnt) || bcnt != sizeof(ContextAddress)) { + log_error("ReadProcessMemory", 0); + } + else { + assert(buf & 0x100); + buf &= ~0x100; + if (!WriteProcessMemory(EXT(ctx->mem)->handle, (LPVOID)ext->regs->Esp, &buf, sizeof(ContextAddress), &bcnt) || bcnt != sizeof(ContextAddress)) { + log_error("WriteProcessMemory", 0); + } + } + } + } + if ((!ctx->stopped_by_cb && ext->step_opcodes_len == 0) || ext->regs_error) { + continue_status = DBG_EXCEPTION_NOT_HANDLED; + } + ext->step_opcodes_len = 0; + ext->step_opcodes_addr = 0; + break; + case EXCEPTION_BREAKPOINT: + get_registers(ctx); + if (!ext->regs_error) { + if (is_breakpoint_address(ctx, exception_addr)) { + ext->regs->Eip = exception_addr; + ext->regs_dirty = 1; + ctx->stopped_by_bp = 1; +#if USE_HW_BPS + if (!debug_state->ok_to_use_hw_bp) { + debug_state->ok_to_use_hw_bp = 1; + send_context_changed_event(ctx->mem); + memory_map_event_mapping_changed(ctx->mem); + } +#endif + } + else { + ext->regs->Eip = exception_addr; + ext->regs_dirty = 1; + } + } + else { + continue_status = DBG_EXCEPTION_NOT_HANDLED; + } + break; + case EXCEPTION_DEBUGGER_IO: + trace(LOG_ALWAYS, "Debugger IO request %#lx", + ext->suspend_reason.ExceptionRecord.ExceptionInformation[0]); + break; + default: + continue_status = DBG_EXCEPTION_NOT_HANDLED; + break; + } + if (continue_status == DBG_EXCEPTION_NOT_HANDLED) { + unsigned i; + for (i = 0; i < exception_handler_cnt; i++) { + if (exception_handlers[i](ctx, &ext->suspend_reason)) { + continue_status = DBG_CONTINUE; + } + } + } + if (continue_status == DBG_EXCEPTION_NOT_HANDLED) { + int intercept = 1; + ctx->stopped_by_exception = 1; + if (ctx->signal) { + ctx->pending_signals |= 1 << ctx->signal; + if (ctx->sig_dont_pass & (1 << ctx->signal)) { + continue_status = DBG_CONTINUE; + } + if (ctx->sig_dont_stop & (1 << ctx->signal)) { + intercept = 0; + } + } + if (intercept) ctx->pending_intercept = 1; + } + } + else { + ctx->stopped_by_exception = 1; + if (!ctx->mem->exiting) ctx->pending_intercept = 1; + continue_status = DBG_EXCEPTION_NOT_HANDLED; + } + send_context_stopped_event(ctx); + return continue_status; +} + +static void event_win32_context_started(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + trace(LOG_CONTEXT, "context: started: ctx %#lx, id %s", ctx, ctx->id); + assert(ctx->stopped); + ext->stop_pending = 0; + send_context_started_event(ctx); +} + +static void event_win32_context_exited(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + LINK * l = NULL; + trace(LOG_CONTEXT, "context: exited: ctx %#lx, id %s", ctx, ctx->id); + assert(!ctx->exited); + context_lock(ctx); + ctx->exiting = 1; + ext->stop_pending = 0; + if (ctx->stopped) send_context_started_event(ctx); + l = ctx->children.next; + while (l != &ctx->children) { + Context * c = cldl2ctxp(l); + l = l->next; + assert(c->parent == ctx); + if (!c->exited) event_win32_context_exited(c); + } + release_error_report(ext->regs_error); + loc_free(ext->regs); + ext->regs_error = NULL; + ext->regs = NULL; + send_context_exited_event(ctx); + if (ext->handle != NULL) { + if (ctx->mem != ctx) { + log_error("CloseHandle", CloseHandle(ext->handle)); + } + else if (os_version.dwMajorVersion <= 5) { + /* Bug in Windows XP: ContinueDebugEvent() does not close exited process handle */ + log_error("CloseHandle", CloseHandle(ext->handle)); + } + ext->handle = NULL; + } + if (ext->debug_state != NULL && ext->debug_state->file_handle != NULL) { + log_error("CloseHandle", CloseHandle(ext->debug_state->file_handle)); + ext->debug_state->file_handle = NULL; + } + ext->debug_state = NULL; + context_unlock(ctx); +} + +static DWORD WINAPI remote_thread_func(LPVOID args) { + return 0; +} + +static void break_process_event(void * args) { + Context * ctx = (Context *)args; + ContextExtensionWin32 * ext = EXT(ctx); + + if (ext->debug_state != NULL) { + LINK * l; + int cnt = 0; + DebugState * debug_state = ext->debug_state; + + if (!ctx->exited && debug_state->break_thread == NULL) { + for (l = ctx->children.next; l != &ctx->children; l = l->next) { + ContextExtensionWin32 * x = EXT(cldl2ctxp(l)); + if (x->stop_pending && SuspendThread(x->handle) != (DWORD)-1) cnt++; + } + if (cnt > 0) { + const SIZE_T buf_size = 0x100; + DWORD size = 0; + int error = 0; + + trace(LOG_CONTEXT, "context: creating remote thread in process %#lx, id %s", ctx, ctx->id); + if (debug_state->break_thread_code == NULL) { + debug_state->break_thread_code = VirtualAllocEx(ext->handle, + NULL, buf_size, MEM_COMMIT, PAGE_EXECUTE); + error = log_error("VirtualAllocEx", debug_state->break_thread_code != NULL); + } + + if (!error) error = log_error("WriteProcessMemory", WriteProcessMemory(ext->handle, + debug_state->break_thread_code, (LPCVOID)remote_thread_func, buf_size, &size) && size == buf_size); + + if (!error) error = log_error("CreateRemoteThread", (debug_state->break_thread = CreateRemoteThread(ext->handle, + 0, 0, (DWORD (WINAPI*)(LPVOID))debug_state->break_thread_code, NULL, 0, &debug_state->break_thread_id)) != NULL); + + if (error) { + debug_state->break_thread = NULL; + debug_state->break_thread_id = 0; + } + } + } + debug_state->break_posted = 0; + } + context_unlock(ctx); +} + +static int win32_resume(Context * ctx, int step) { + Context * prs = ctx->parent; + ContextExtensionWin32 * ext = EXT(ctx); + ContextExtensionWin32 * prs_ext = EXT(prs); + DebugState * debug_state = prs_ext->debug_state; + + assert(ctx->stopped); + assert(!ctx->exited); + + if (debug_state->reporting_debug_event) { + debug_state->reporting_debug_event++; + } + +#if USE_HW_BPS + + if (ext->skip_hw_bp_addr == 0 && skip_breakpoint(ctx, step)) return 0; + + /* Update debug registers */ + if (ext->skip_hw_bp_addr != 0 || ext->hw_bps_regs_generation != debug_state->hw_bps_generation) { + int i; + DWORD Dr7 = 0; + int step_over_hw_bp = 0; + + get_registers(ctx); + if (ext->regs_error) { + errno = set_error_report_errno(ext->regs_error); + return -1; + } + Dr7 = ext->regs->Dr7; + for (i = 0; i < MAX_HW_BPS; i++) { + ContextBreakpoint * bp = debug_state->hw_bps[i]; + if (bp != NULL && + ext->skip_hw_bp_addr == bp->address && + (bp->access_types & CTX_BP_ACCESS_INSTRUCTION)) { + /* Skipping the breakpoint */ + step_over_hw_bp = 1; + bp = NULL; + } + Dr7 &= ~(3u << (i * 2)); + if (bp != NULL) { + switch (i) { + case 0: + if (ext->regs->Dr0 != bp->address) { + ext->regs->Dr0 = bp->address; + ext->regs_dirty = 1; + } + break; + case 1: + if (ext->regs->Dr1 != bp->address) { + ext->regs->Dr1 = bp->address; + ext->regs_dirty = 1; + } + break; + case 2: + if (ext->regs->Dr2 != bp->address) { + ext->regs->Dr2 = bp->address; + ext->regs_dirty = 1; + } + break; + case 3: + if (ext->regs->Dr3 != bp->address) { + ext->regs->Dr3 = bp->address; + ext->regs_dirty = 1; + } + break; + } + Dr7 |= 1u << (i * 2); + if (bp->access_types == (CTX_BP_ACCESS_INSTRUCTION | CTX_BP_ACCESS_VIRTUAL)) { + Dr7 &= ~(3u << (i * 4 + 16)); + } + else if (bp->access_types == (CTX_BP_ACCESS_DATA_WRITE | CTX_BP_ACCESS_VIRTUAL)) { + Dr7 &= ~(3u << (i * 4 + 16)); + Dr7 |= 1u << (i * 4 + 16); + } + else if (bp->access_types == (CTX_BP_ACCESS_DATA_READ | CTX_BP_ACCESS_DATA_WRITE | CTX_BP_ACCESS_VIRTUAL)) { + Dr7 |= 3u << (i * 4 + 16); + } + else { + errno = set_errno(ERR_UNSUPPORTED, "Invalid hardware breakpoint: unsupported access mode"); + return -1; + } + if (bp->length == 1) { + Dr7 &= ~(3u << (i * 4 + 18)); + } + else if (bp->length == 2) { + Dr7 &= ~(3u << (i * 4 + 18)); + Dr7 |= 1u << (i * 4 + 18); + } + else if (bp->length == 4) { + Dr7 |= 3u << (i * 4 + 18); + } + else if (bp->length == 8) { + Dr7 &= ~(3u << (i * 4 + 18)); + Dr7 |= 2u << (i * 4 + 18); + } + else { + errno = set_errno(ERR_UNSUPPORTED, "Invalid hardware breakpoint: unsupported length"); + return -1; + } + } + } + if (ext->regs->Dr7 != Dr7) { + ext->regs->Dr7 = Dr7; + ext->regs_dirty = 1; + } + ext->hw_bps_regs_generation = debug_state->hw_bps_generation; + if (step_over_hw_bp) { + step = 1; + ext->hw_bps_regs_generation--; + } + else { + ext->skip_hw_bp_addr = 0; + } + } + +#else + + if (skip_breakpoint(ctx, step)) return 0; + +#endif + + /* Update CPU trace flag */ + if (!step && ext->trace_flag) { + get_registers(ctx); + ext->regs->EFlags &= ~0x100; + ext->regs_dirty = 1; + } + else if (step && !ext->trace_flag) { + get_registers(ctx); + ext->regs->EFlags |= 0x100; + ext->regs_dirty = 1; + } + + /* Flash registers if dirty */ + if (ext->regs_dirty) { + assert(ext->regs->ContextFlags); + if (ext->regs_error) { + trace(LOG_ALWAYS, "Can't resume thread, registers copy is invalid: ctx %#lx, id %s", ctx, ctx->id); + errno = set_error_report_errno(ext->regs_error); + return -1; + } + if (SetThreadContext(ext->handle, ext->regs) == 0) { + errno = log_error("SetThreadContext", 0); + return -1; + } + ext->trace_flag = (ext->regs->EFlags & 0x100) != 0; + ext->regs_dirty = 0; + } + + if (ext->trace_flag) { + get_registers(ctx); + if (ext->regs_error) { + set_error_report_errno(ext->regs_error); + return -1; + } + ext->step_opcodes_addr = ext->regs->Eip; + if (!ReadProcessMemory(prs_ext->handle, (LPCVOID)ext->regs->Eip, &ext->step_opcodes, + sizeof(ext->step_opcodes), &ext->step_opcodes_len) || ext->step_opcodes_len == 0) { + errno = log_error("ReadProcessMemory", 0); + return -1; + } + } + if (debug_state->reporting_debug_event) { + ext->start_pending = 1; + } + else { + for (;;) { + DWORD cnt = ResumeThread(ext->handle); + if (cnt == (DWORD)-1) { + errno = log_error("ResumeThread", 0); + return -1; + } + if (cnt <= 1) break; + } + } + + event_win32_context_started(ctx); + return 0; +} + +static int win32_terminate(Context * ctx) { + LINK * l; + ContextExtensionWin32 * ext = EXT(ctx); + DebugState * debug_state = ext->debug_state; + + if (debug_state->reporting_debug_event) { + debug_state->reporting_debug_event++; + } + + trace(LOG_CONTEXT, "context: terminating process %#lx, id %s", ctx, ctx->id); + if (!ctx->exiting) { + if (!TerminateProcess(ext->handle, 1)) { + errno = log_error("TerminateProcess", 0); + return -1; + } + ctx->exiting = 1; + for (l = ctx->children.next; l != &ctx->children; l = l->next) { + Context * c = cldl2ctxp(l); + if (!c->stopped) continue; + event_win32_context_started(c); + c->exiting = 1; + } + } + + return 0; +} + +static void debug_event_handler(DebugEvent * debug_event) { + DebugState * debug_state = debug_event->debug_state; + DEBUG_EVENT * win32_event = &debug_event->win32_event; + Context * prs = context_find_from_pid(win32_event->dwProcessId, 0); + Context * ctx = context_find_from_pid(win32_event->dwThreadId, 1); + ContextExtensionWin32 * ext = NULL; + + assert(ctx == NULL || ctx->parent == prs); + + switch (win32_event->dwDebugEventCode) { + case CREATE_PROCESS_DEBUG_EVENT: + if (debug_state->state == DEBUG_STATE_INIT) { + debug_state->state = DEBUG_STATE_PRS_CREATED; + debug_state->main_thread_id = win32_event->dwThreadId; + debug_state->main_thread_handle = win32_event->u.CreateProcessInfo.hThread; + debug_state->file_handle = win32_event->u.CreateProcessInfo.hFile; + debug_state->base_address = (uintptr_t)win32_event->u.CreateProcessInfo.lpBaseOfImage; + assert(prs == NULL); + assert(ctx == NULL); + ext = EXT(prs = create_context(pid2id(win32_event->dwProcessId, 0))); + prs->mem = prs; + prs->mem_access |= MEM_ACCESS_INSTRUCTION; + prs->mem_access |= MEM_ACCESS_DATA; + prs->mem_access |= MEM_ACCESS_USER; + prs->big_endian = big_endian_host(); + ext->pid = win32_event->dwProcessId; + ext->handle = win32_event->u.CreateProcessInfo.hProcess; + ext->debug_state = debug_state; + assert(ext->handle != NULL); + link_context(prs); + send_context_created_event(prs); + } + else { + /* This looks like a bug in Windows XP: */ + /* 1. according to the documentation, we should get only one CREATE_PROCESS_DEBUG_EVENT. */ + /* 2. if we don't suspend second process, debugee crashes. */ + assert(debug_state->ini_thread_handle == NULL); + debug_state->ini_thread_id = win32_event->dwThreadId; + debug_state->ini_thread_handle = win32_event->u.CreateProcessInfo.hThread; + SuspendThread(debug_state->ini_thread_handle); + CloseHandle(win32_event->u.CreateProcessInfo.hFile); + ResumeThread(debug_state->main_thread_handle); + } + break; + case CREATE_THREAD_DEBUG_EVENT: + assert(prs != NULL); + assert(ctx == NULL); + if (debug_state->state < DEBUG_STATE_PRS_ATTACHED) break; + if (debug_state->break_thread_id == win32_event->dwThreadId) break; + ext = EXT(ctx = create_context(pid2id(win32_event->dwThreadId, win32_event->dwProcessId))); + ext->regs = (REG_SET *)loc_alloc_zero(sizeof(REG_SET)); + ext->pid = win32_event->dwThreadId; + ext->handle = OpenThread(THREAD_ALL_ACCESS, FALSE, win32_event->dwThreadId); + ext->debug_state = debug_state; + ctx->mem = prs; + ctx->big_endian = prs->big_endian; + (ctx->parent = prs)->ref_count++; + list_add_last(&ctx->cldl, &prs->children); + link_context(ctx); + send_context_created_event(ctx); + debug_event->continue_status = event_win32_context_stopped(ctx); + ext->debug_event = *win32_event; + break; + case EXCEPTION_DEBUG_EVENT: + if (debug_state->state == DEBUG_STATE_PRS_CREATED && win32_event->u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) { + if (debug_state->ini_thread_handle != NULL) ResumeThread(debug_state->ini_thread_handle); + debug_state->attach_callback(0, prs, debug_state->attach_data); + debug_state->attach_callback = NULL; + debug_state->attach_data = NULL; + debug_state->state = DEBUG_STATE_PRS_ATTACHED; + ext = EXT(ctx = create_context(pid2id(debug_state->main_thread_id, win32_event->dwProcessId))); + ext->regs = (REG_SET *)loc_alloc_zero(sizeof(REG_SET)); + ext->pid = debug_state->main_thread_id; + ext->handle = OpenThread(THREAD_ALL_ACCESS, FALSE, debug_state->main_thread_id); + ext->debug_state = debug_state; + ctx->mem = prs; + ctx->big_endian = prs->big_endian; + (ctx->parent = prs)->ref_count++; + list_add_last(&ctx->cldl, &prs->children); + link_context(ctx); + send_context_created_event(ctx); + ctx->pending_intercept = 1; + debug_event->continue_status = event_win32_context_stopped(ctx); + ext->debug_event = *win32_event; + } + else if (ctx == NULL || ctx->exiting) { + /* Does not work as expected: debug_event->continue_status = DBG_EXCEPTION_NOT_HANDLED; */ + } + else { + assert(prs != NULL); + assert(!ctx->exited); + if (ctx->stopped) { + DWORD exception_code = win32_event->u.Exception.ExceptionRecord.ExceptionCode; +#if USE_HW_BPS + if (exception_code == EXCEPTION_SINGLE_STEP && win32_event->u.Exception.dwFirstChance) { + /* This event appears to be caused by a hardware breakpoint. + * It is safe to ignore the event - the breakpoint will be triggered again + * when the context resumed. */ + debug_event->continue_status = DBG_CONTINUE; + break; + } +#endif + trace(LOG_ALWAYS, "context: already stopped, id %s, exception 0x%08x", ctx->id, exception_code); + send_context_started_event(ctx); + } + ext = EXT(ctx); + memcpy(&ext->suspend_reason, &win32_event->u.Exception, sizeof(EXCEPTION_DEBUG_INFO)); + debug_event->continue_status = event_win32_context_stopped(ctx); + ext->debug_event = *win32_event; + } + break; + case EXIT_THREAD_DEBUG_EVENT: + assert(prs != NULL); + if (ctx && !ctx->exited) event_win32_context_exited(ctx); + if (debug_state->ini_thread_id == win32_event->dwThreadId) { + debug_state->ini_thread_id = 0; + debug_state->ini_thread_handle = NULL; + } + else if (debug_state->break_thread_id == win32_event->dwThreadId) { + ext = EXT(prs); + log_error("CloseHandle", CloseHandle(debug_state->break_thread)); + debug_state->break_thread = NULL; + debug_state->break_thread_id = 0; + } + break; + case EXIT_PROCESS_DEBUG_EVENT: + assert(prs != NULL); + if (ctx && !ctx->exited) event_win32_context_exited(ctx); + event_win32_context_exited(prs); + prs = NULL; + if (debug_state->attach_callback != NULL) { + int error = set_win32_errno(win32_event->u.ExitProcess.dwExitCode); + debug_state->attach_callback(error, NULL, debug_state->attach_data); + debug_state->attach_callback = NULL; + debug_state->attach_data = NULL; + } + break; + case LOAD_DLL_DEBUG_EVENT: + assert(prs != NULL); + debug_state->module_handle = win32_event->u.LoadDll.hFile; + debug_state->module_address = (uintptr_t)win32_event->u.LoadDll.lpBaseOfDll; + memory_map_event_module_loaded(prs); + if (debug_state->module_handle != NULL) { + log_error("CloseHandle", CloseHandle(debug_state->module_handle)); + } + debug_state->module_handle = NULL; + debug_state->module_address = 0; + break; + case UNLOAD_DLL_DEBUG_EVENT: + assert(prs != NULL); + debug_state->module_address = (uintptr_t)win32_event->u.UnloadDll.lpBaseOfDll; + memory_map_event_module_unloaded(prs); + debug_state->module_address = 0; + break; + case RIP_EVENT: + trace(LOG_ALWAYS, "System debugging error: debuggee pid %d, error type %d, error code %d", + win32_event->dwProcessId, win32_event->u.RipInfo.dwType, win32_event->u.RipInfo.dwError); + break; + } +} + +static void continue_debug_event(void * args) { + DebugEvent * debug_event = (DebugEvent *)args; + DebugState * debug_state = debug_event->debug_state; + Context * prs = context_find_from_pid(debug_state->process_id, 0); + + assert(debug_state->reporting_debug_event); + if (debug_state->reporting_debug_event > 1) { + debug_state->reporting_debug_event = 1; + post_event(continue_debug_event, debug_event); + return; + } + + trace(LOG_WAITPID, "continue debug event 1, process id %u", debug_state->process_id); + + if (prs != NULL && !prs->exited) { + LINK * l; + for (l = prs->children.next; l != &prs->children; l = l->next) { + Context * ctx = cldl2ctxp(l); + ContextExtensionWin32 * ext = EXT(ctx); + if (ctx->stopped || ctx->exited) { + ext->stop_pending = 0; + ext->start_pending = 0; + continue; + } + if (ext->stop_pending) { + memset(&ext->suspend_reason, 0, sizeof(ext->suspend_reason)); + event_win32_context_stopped(ctx); + ext->debug_event = debug_event->win32_event; + } + if (ext->start_pending) { + for (;;) { + DWORD cnt = ResumeThread(ext->handle); + if (cnt <= 1) break; + } + ext->start_pending = 0; + } + } + } + + trace(LOG_WAITPID, "continue debug event, process id %u", debug_state->process_id); + log_error("ReleaseSemaphore", SetEvent(debug_state->debug_event_inp)); + log_error("WaitForSingleObject", WaitForSingleObject(debug_state->debug_event_out, INFINITE) != WAIT_FAILED); + debug_state->reporting_debug_event = 0; + + if (prs != NULL && !prs->exited) { + LINK * l; + for (l = prs->children.next; l != &prs->children; l = l->next) { + Context * ctx = cldl2ctxp(l); + ContextExtensionWin32 * ext = EXT(ctx); + if (ext->start_pending) { + for (;;) { + DWORD cnt = ResumeThread(ext->handle); + if (cnt <= 1) break; + } + ext->start_pending = 0; + } + } + } + + log_error("ReleaseSemaphore", SetEvent(debug_state->debug_event_inp)); +} + +static void early_debug_event_handler(void * x) { + DebugEvent * debug_event = (DebugEvent *)x; + DebugState * debug_state = debug_event->debug_state; + DEBUG_EVENT * win32_event = &debug_event->win32_event; + + if (win32_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { + trace(LOG_WAITPID, "%s, process %d, thread %d, code %#lx", + win32_debug_event_name(win32_event->dwDebugEventCode), + win32_event->dwProcessId, win32_event->dwThreadId, + win32_event->u.Exception.ExceptionRecord.ExceptionCode); + } + else { + trace(LOG_WAITPID, "%s, process %d, thread %d", + win32_debug_event_name(win32_event->dwDebugEventCode), + win32_event->dwProcessId, win32_event->dwThreadId); + } + + debug_state->reporting_debug_event = 1; + debug_event_handler(debug_event); + post_event(continue_debug_event, debug_event); +} + +static void debugger_exit_handler(void * x) { + DebugState * debug_state = (DebugState *)x; + Context * prs = context_find_from_pid(debug_state->process_id, 0); + + trace(LOG_WAITPID, "debugger thread %d exited, debuggee pid %d", debug_state->debug_thread_id, debug_state->process_id); + + log_error("WaitForSingleObject", WaitForSingleObject(debug_state->debug_thread, INFINITE) != WAIT_FAILED); + log_error("CloseHandle", CloseHandle(debug_state->debug_thread)); + log_error("CloseHandle", CloseHandle(debug_state->debug_event_inp)); + log_error("CloseHandle", CloseHandle(debug_state->debug_event_out)); + + if (prs != NULL && !prs->exited) event_win32_context_exited(prs); + + loc_free(debug_state); +} + +static DWORD WINAPI debugger_thread_func(LPVOID x) { + DebugState * debug_state = (DebugState *)x; + DebugEvent debug_event; + + if (DebugActiveProcess(debug_state->process_id) == 0) { + debug_state->error = GetLastError(); + trace(LOG_ALWAYS, "Can't attach to a process: error %d", debug_state->error); + ReleaseSemaphore(debug_state->debug_thread_semaphore, 1, 0); + return 0; + } + + trace(LOG_WAITPID, "debugger thread %d started", GetCurrentThreadId()); + ReleaseSemaphore(debug_state->debug_thread_semaphore, 1, 0); + + memset(&debug_event, 0, sizeof(debug_event)); + + debug_event.debug_state = debug_state; + + for (;;) { + DEBUG_EVENT * win32_event = &debug_event.win32_event; + + memset(win32_event, 0, sizeof(DEBUG_EVENT)); + if (WaitForDebugEvent(win32_event, INFINITE) == 0) { + trace(LOG_ALWAYS, "WaitForDebugEvent() error %d", GetLastError()); + break; + } + + assert(debug_state->process_id == win32_event->dwProcessId); + debug_event.continue_status = DBG_CONTINUE; + + post_event(early_debug_event_handler, &debug_event); + WaitForSingleObject(debug_state->debug_event_inp, INFINITE); + if (ContinueDebugEvent(win32_event->dwProcessId, win32_event->dwThreadId, debug_event.continue_status) == 0) { + trace(LOG_ALWAYS, "Can't continue debug event: process %d, thread %d: error %d", + win32_event->dwProcessId, win32_event->dwThreadId, GetLastError()); + } + SetEvent(debug_state->debug_event_out); + WaitForSingleObject(debug_state->debug_event_inp, INFINITE); + + if (win32_event->dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; + if (win32_event->dwDebugEventCode == RIP_EVENT) break; + } + + post_event(debugger_exit_handler, debug_state); + return 0; +} + +int context_attach(pid_t pid, ContextAttachCallBack * done, void * data, int mode) { + int error = 0; + DebugState * debug_state = (DebugState *)loc_alloc_zero(sizeof(DebugState)); + + assert(done != NULL); + assert((mode & CONTEXT_ATTACH_SELF) == 0); + debug_state->process_id = pid; + debug_state->attach_callback = done; + debug_state->attach_data = data; + + debug_state->debug_event_inp = CreateEvent(NULL, 0, 0, NULL); + if (debug_state->debug_event_inp == NULL) error = log_error("CreateEvent", 0); + + if (!error) { + debug_state->debug_event_out = CreateEvent(NULL, 0, 0, NULL); + if (debug_state->debug_event_out == NULL) error = log_error("CreateEvent", 0); + } + + if (!error) { + debug_state->debug_thread_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + if (debug_state->debug_thread_semaphore == NULL) error = log_error("CreateSemaphore", 0); + } + + if (!error) { + debug_state->debug_thread = CreateThread(NULL, 0, debugger_thread_func, debug_state, 0, &debug_state->debug_thread_id); + if (debug_state->debug_thread == NULL) error = log_error("CreateThread", 0); + } + + if (!error) { + error = log_error("WaitForSingleObject", WaitForSingleObject(debug_state->debug_thread_semaphore, INFINITE) != WAIT_FAILED); + } + + if (!error) { + error = log_error("CloseHandle", CloseHandle(debug_state->debug_thread_semaphore)); + debug_state->debug_thread_semaphore = NULL; + } + + if (!error) { + error = set_win32_errno(debug_state->error); + } + + if (error) { + if (debug_state->debug_thread) log_error("WaitForSingleObject", WaitForSingleObject(debug_state->debug_thread, INFINITE) != WAIT_FAILED); + if (debug_state->debug_thread) log_error("CloseHandle", CloseHandle(debug_state->debug_thread)); + if (debug_state->debug_event_inp) log_error("CloseHandle", CloseHandle(debug_state->debug_event_inp)); + if (debug_state->debug_event_out) log_error("CloseHandle", CloseHandle(debug_state->debug_event_out)); + if (debug_state->debug_thread_semaphore) log_error("CloseHandle", CloseHandle(debug_state->debug_thread_semaphore)); + loc_free(debug_state); + errno = error; + return -1; + } + + add_waitpid_process(pid); + return 0; +} + +int context_has_state(Context * ctx) { + return ctx != NULL && ctx->parent != NULL; +} + +int context_stop(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + DebugState * debug_state = EXT(ctx->parent)->debug_state; + + trace(LOG_CONTEXT, "context:%s suspending ctx %#lx id %s", + ctx->pending_intercept ? "" : " temporary", ctx, ctx->id); + assert(context_has_state(ctx)); + assert(!ctx->stopped); + assert(!ctx->exited); + if (debug_state->reporting_debug_event) { + debug_state->reporting_debug_event++; + } + else if (!debug_state->break_posted) { + context_lock(ctx->parent); + post_event_with_delay(break_process_event, ctx->parent, 10000); + debug_state->break_posted = 1; + } + ext->stop_pending = 1; + return 0; +} + +int context_continue(Context * ctx) { + assert(is_dispatch_thread()); + assert(context_has_state(ctx)); + assert(ctx->stopped); + assert(!ctx->exited); + + trace(LOG_CONTEXT, "context: resuming ctx %#lx, id %s", ctx, ctx->id); + return win32_resume(ctx, 0); +} + +int context_single_step(Context * ctx) { + assert(is_dispatch_thread()); + assert(context_has_state(ctx)); + assert(ctx->stopped); + assert(!ctx->exited); + + trace(LOG_CONTEXT, "context: single step ctx %#lx, id %s", ctx, ctx->id); + return win32_resume(ctx, 1); +} + +static int context_terminate(Context * ctx) { + assert(is_dispatch_thread()); + assert(!context_has_state(ctx)); + assert(!ctx->exited); + + trace(LOG_CONTEXT, "context: terminate ctx %#lx, id %s", ctx, ctx->id); + return win32_terminate(ctx); +} + +int context_resume(Context * ctx, int mode, ContextAddress range_start, ContextAddress range_end) { + switch (mode) { + case RM_RESUME: + return context_continue(ctx); + case RM_STEP_INTO: + return context_single_step(ctx); + case RM_TERMINATE: + return context_terminate(ctx); + } + errno = ERR_UNSUPPORTED; + return -1; +} + +int context_can_resume(Context * ctx, int mode) { + switch (mode) { + case RM_RESUME: + return 1; + case RM_STEP_INTO: + return context_has_state(ctx); + case RM_TERMINATE: + return ctx != NULL && ctx->parent == NULL; + } + return 0; +} + +int context_read_mem(Context * ctx, ContextAddress address, void * buf, size_t size) { + ContextExtensionWin32 * ext = EXT(ctx = ctx->mem); + SIZE_T bcnt = 0; + + trace(LOG_CONTEXT, + "context: read memory ctx %#lx, id %s, address %#lx, size %d", + ctx, ctx->id, address, (int)size); + assert(is_dispatch_thread()); + mem_err_info.error = 0; + if (ReadProcessMemory(ext->handle, (LPCVOID)address, buf, size, &bcnt) == 0 || bcnt != size) { + DWORD error = GetLastError(); + size_t size_next = size; + size_t size_error = 1; + /* Check if a smaller block is readable */ + while (bcnt == 0) { + if (size_next <= 1) break; + size_next /= 2; + ReadProcessMemory(ext->handle, (LPCVOID)address, buf, size_next, &bcnt); + } + /* Find upper bound of the readable memory */ + while (bcnt < size) { + if (!ReadProcessMemory(ext->handle, (LPCVOID)(address + bcnt), + (char *)buf + bcnt, 1, NULL)) { + error = GetLastError(); + break; + } + bcnt++; + } + if (check_breakpoints_on_memory_read(ctx, address, buf, bcnt) < 0) return -1; + /* Find number of unreadable bytes */ + while (size_error < 0x100 && bcnt + size_error < size) { + if (ReadProcessMemory(ext->handle, (LPCVOID)(address + bcnt + size_error), + (char *)buf + bcnt + size_error, 1, NULL)) break; + if (error != GetLastError()) break; + size_error++; + } + mem_err_info.error = set_win32_errno(error); + mem_err_info.size_valid = bcnt; + mem_err_info.size_error = size_error; + return -1; + } + return check_breakpoints_on_memory_read(ctx, address, buf, size); +} + +int context_write_mem(Context * ctx, ContextAddress address, void * buf, size_t size) { + ContextExtensionWin32 * ext = EXT(ctx = ctx->mem); + SIZE_T bcnt = 0; + + trace(LOG_CONTEXT, + "context: write memory ctx %#lx, id %s, address %#lx, size %d", + ctx, ctx->id, address, (int)size); + assert(is_dispatch_thread()); + mem_err_info.error = 0; + if (check_breakpoints_on_memory_write(ctx, address, buf, size) < 0) return -1; + if (WriteProcessMemory(ext->handle, (LPVOID)address, buf, size, &bcnt) == 0 || bcnt != size) { + mem_err_info.error = set_win32_errno(GetLastError()); + mem_err_info.size_valid = bcnt; + mem_err_info.size_error = 1; + } + if (FlushInstructionCache(ext->handle, (LPCVOID)address, bcnt) == 0) { + mem_err_info.error = 0; + errno = log_error("FlushInstructionCache", 0); + return -1; + } + if (mem_err_info.error) { + errno = mem_err_info.error; + return -1; + } + return 0; +} + +#if ENABLE_ExtendedMemoryErrorReports +int context_get_mem_error_info(MemoryErrorInfo * info) { + if (mem_err_info.error == 0) { + set_errno(ERR_OTHER, "Extended memory error info not available"); + return -1; + } + *info = mem_err_info; + return 0; +} +#endif + +int context_write_reg(Context * ctx, RegisterDefinition * def, unsigned offs, unsigned size, void * buf) { + ContextExtensionWin32 * ext = EXT(ctx); + + assert(is_dispatch_thread()); + assert(offs + size <= def->size); + + get_registers(ctx); + if (ext->regs_error) { + set_error_report_errno(ext->regs_error); + return -1; + } + memcpy((uint8_t *)ext->regs + def->offset + offs, buf, size); + ext->regs_dirty = 1; + return 0; +} + +int context_read_reg(Context * ctx, RegisterDefinition * def, unsigned offs, unsigned size, void * buf) { + ContextExtensionWin32 * ext = EXT(ctx); + + assert(is_dispatch_thread()); + assert(offs + size <= def->size); + + get_registers(ctx); + if (ext->regs_error) { + set_error_report_errno(ext->regs_error); + return -1; + } + memcpy(buf, (uint8_t *)ext->regs + def->offset + offs, size); + return 0; +} + +unsigned context_word_size(Context * ctx) { + return sizeof(void *); +} + +int context_get_canonical_addr(Context * ctx, ContextAddress addr, + Context ** canonical_ctx, ContextAddress * canonical_addr, + ContextAddress * block_addr, ContextAddress * block_size) { + /* Direct mapping, page size is irrelevant */ + ContextAddress page_size = 0x100000; + assert(is_dispatch_thread()); + *canonical_ctx = ctx->mem; + if (canonical_addr != NULL) *canonical_addr = addr; + if (block_addr != NULL) *block_addr = addr & ~(page_size - 1); + if (block_size != NULL) *block_size = page_size; + return 0; +} + +Context * context_get_group(Context * ctx, int group) { + static Context * cpu_group = NULL; + switch (group) { + case CONTEXT_GROUP_INTERCEPT: + return ctx; + case CONTEXT_GROUP_CPU: + if (cpu_group == NULL) cpu_group = create_context("CPU"); + return cpu_group; + } + return ctx->mem; +} + +int context_get_supported_bp_access_types(Context * ctx) { +#if USE_HW_BPS + if (ctx->mem == ctx) return + CTX_BP_ACCESS_DATA_READ | + CTX_BP_ACCESS_DATA_WRITE | + CTX_BP_ACCESS_INSTRUCTION | + CTX_BP_ACCESS_VIRTUAL; +#endif + return 0; +} + +int context_plant_breakpoint(ContextBreakpoint * bp) { +#if USE_HW_BPS + int i; + Context * ctx = bp->ctx; + assert(bp->access_types); + if (ctx->mem == ctx && (bp->access_types & CTX_BP_ACCESS_VIRTUAL)) { + ContextExtensionWin32 * ext = EXT(ctx); + DebugState * debug_state = ext->debug_state; + if (debug_state->ok_to_use_hw_bp && bp->length <= 8 && ((1u << bp->length) & 0x116u)) { + if (bp->access_types == (CTX_BP_ACCESS_INSTRUCTION | CTX_BP_ACCESS_VIRTUAL)) { + /* Don't use more then 2 HW slots for regular instruction breakpoints */ + int cnt = 0; + for (i = 0; i < MAX_HW_BPS; i++) { + assert(debug_state->hw_bps[i] != bp); + if (debug_state->hw_bps[i] == NULL) continue; + if ((debug_state->hw_bps[i]->access_types & CTX_BP_ACCESS_INSTRUCTION) == 0) continue; + cnt++; + } + if (cnt >= MAX_HW_BPS / 2) { + errno = ERR_UNSUPPORTED; + return -1; + } + } + else if (bp->access_types != (CTX_BP_ACCESS_DATA_WRITE | CTX_BP_ACCESS_VIRTUAL) && + bp->access_types != (CTX_BP_ACCESS_DATA_READ | CTX_BP_ACCESS_DATA_WRITE | CTX_BP_ACCESS_VIRTUAL)) { + errno = ERR_UNSUPPORTED; + return -1; + } + for (i = 0; i < MAX_HW_BPS; i++) { + if (debug_state->hw_bps[i] == NULL || debug_state->hw_bps[i] == bp) { + debug_state->hw_bps[i] = bp; + debug_state->hw_bps_generation++; + return 0; + } + } + } + } +#endif + errno = ERR_UNSUPPORTED; + return -1; +} + +int context_unplant_breakpoint(ContextBreakpoint * bp) { +#if USE_HW_BPS + int i; + Context * ctx = bp->ctx; + if (ctx->mem == ctx && !ctx->exited) { + ContextExtensionWin32 * ext = EXT(ctx); + DebugState * debug_state = ext->debug_state; + for (i = 0; i < MAX_HW_BPS; i++) { + if (debug_state->hw_bps[i] == bp) { + debug_state->hw_bps[i] = NULL; + debug_state->hw_bps_generation++; + } + } + } +#endif + return 0; +} + +#if defined(_MSC_VER) + +static void add_map_region(MemoryMap * map, DWORD64 addr, ULONG size, char * file) { + MemoryRegion * r = NULL; + if (map->region_cnt >= map->region_max) { + map->region_max += 8; + map->regions = (MemoryRegion *)loc_realloc(map->regions, sizeof(MemoryRegion) * map->region_max); + } + r = map->regions + map->region_cnt++; + memset(r, 0, sizeof(MemoryRegion)); + r->addr = (ContextAddress)addr; + r->size = (ContextAddress)size; + r->file_name = loc_strdup(file); +} + +static BOOL CALLBACK modules_callback(PCWSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext) { + MemoryMap * map = (MemoryMap *)UserContext; + static char * fnm_buf = NULL; + static int fnm_max = 0; + int fnm_len = 0; + int fnm_err = 0; + + if (fnm_buf == NULL) { + fnm_max = 256; + fnm_buf = (char *)loc_alloc(fnm_max); + } + for (;;) { + fnm_len = WideCharToMultiByte(CP_UTF8, 0, ModuleName, -1, fnm_buf, fnm_max - 1, NULL, NULL); + if (fnm_len != 0) break; + fnm_err = GetLastError(); + if (fnm_err != ERROR_INSUFFICIENT_BUFFER) { + set_win32_errno(fnm_err); + trace(LOG_ALWAYS, "Can't get module name: %s", errno_to_str(errno)); + return TRUE; + } + fnm_max *= 2; + fnm_buf = (char *)loc_realloc(fnm_buf, fnm_max); + } + fnm_buf[fnm_len] = 0; + + add_map_region(map, ModuleBase, ModuleSize, fnm_buf); + + return TRUE; +} + +#endif + +int context_get_memory_map(Context * ctx, MemoryMap * map) { + ctx = ctx->mem; + assert(!ctx->exited); +#if defined(_MSC_VER) + { + ContextExtensionWin32 * ext = EXT(ctx); + if (!EnumerateLoadedModulesW64(ext->handle, modules_callback, map)) { + set_win32_errno(GetLastError()); + return -1; + } + } +#endif + return 0; +} + +HANDLE get_context_handle(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + return ext->handle; +} + +HANDLE get_context_file_handle(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + return ext->debug_state->file_handle; +} + +DWORD64 get_context_base_address(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + return ext->debug_state->base_address; +} + +HANDLE get_context_module_handle(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + return ext->debug_state->module_handle; +} + +DWORD64 get_context_module_address(Context * ctx) { + ContextExtensionWin32 * ext = EXT(ctx); + return ext->debug_state->module_address; +} + +void add_context_exception_handler(ContextExceptionHandler * h) { + assert(exception_handler_cnt < MAX_EXCEPTION_HANDLERS); + exception_handlers[exception_handler_cnt++] = h; +} + +static void eventpoint_at_main(Context * ctx, void * args) { + suspend_debug_context(ctx); +} + +static void waitpid_listener(int pid, int exited, int exit_code, int signal, int event_code, int syscall, void * args) { +} + +void init_contexts_sys_dep(void) { + context_extension_offset = context_extension(sizeof(ContextExtensionWin32)); + ini_context_pid_hash(); + add_waitpid_listener(waitpid_listener, NULL); + memset(&os_version, 0, sizeof(os_version)); + os_version.dwOSVersionInfoSize = sizeof(os_version); + GetVersionEx((OSVERSIONINFO *)&os_version); + create_eventpoint("main", NULL, eventpoint_at_main, NULL); +} + +#endif /* if ENABLE_DebugContext */ +#endif /* WIN32 */ |