blob: 5b80a89e5c1462ffc1dca8727ef226734d5c4710 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2009 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.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* This module handles process/thread OS contexts and their state machine.
*/
#include <config.h>
#if defined(WIN32)
#if ENABLE_DebugContext && !ENABLE_ContextProxy
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <framework/context.h>
#include <framework/events.h>
#include <framework/errors.h>
#include <framework/trace.h>
#include <framework/myalloc.h>
#include <framework/waitpid.h>
#include <framework/signames.h>
#include <services/breakpoints.h>
#include <system/Windows/context-win32.h>
#include <system/Windows/regset.h>
typedef struct ContextExtensionWin32 {
pid_t pid;
HANDLE handle;
HANDLE file_handle;
DWORD64 base_address;
int module_loaded;
int module_unloaded;
HANDLE module_handle;
DWORD64 module_address;
int debug_started;
EXCEPTION_DEBUG_INFO pending_event;
EXCEPTION_DEBUG_INFO suspend_reason;
int context_stopped_async_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;
} ContextExtensionWin32;
static size_t context_extension_offset = 0;
#define EXT(ctx) ((ContextExtensionWin32 *)((char *)(ctx) + context_extension_offset))
typedef struct DebugThreadArgs {
int error;
DWORD context_id;
DWORD debug_thread_id;
HANDLE debug_thread;
HANDLE debug_thread_semaphore;
ContextAttachCallBack * attach_callback;
void * attach_data;
} DebugThreadArgs;
typedef struct DebugEvent {
DebugThreadArgs * debug_thread_args;
HANDLE event_semaphore;
DEBUG_EVENT event;
DWORD continue_status;
int early_event; /* Event received before debugger is fully attached */
struct DebugEvent * next;
} DebugEvent;
#include <system/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;
const char * desc = NULL;
static char buf[64];
if (exception_code == 0) return "Suspended";
if (ext->debug_started && exception_code == EXCEPTION_BREAKPOINT) return "Suspended";
if (exception_code == EXCEPTION_SINGLE_STEP) return "Step";
desc = signal_description(get_signal_from_code(exception_code));
if (desc != NULL) return desc;
snprintf(buf, sizeof(buf), "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;
if (ext->debug_started && exception_code == EXCEPTION_BREAKPOINT) return 0;
return get_signal_from_code(exception_code);
}
static 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(char * fn, int ok) {
int err;
if (ok) return 0;
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;
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 void event_win32_context_stopped(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
DWORD exception_code = ext->pending_event.ExceptionRecord.ExceptionCode;
if (ctx->exited || ctx->stopped && exception_code == 0) return;
memcpy(&ext->suspend_reason, &ext->pending_event, sizeof(EXCEPTION_DEBUG_INFO));
memset(&ext->pending_event, 0, sizeof(EXCEPTION_DEBUG_INFO));
trace(LOG_CONTEXT, "context: stopped: ctx %#lx, id %s, exception %#lx",
ctx, ctx->id, exception_code);
assert(is_dispatch_thread());
assert(!ctx->stopped);
assert(!ctx->exited);
assert(ext->handle != NULL);
assert(ctx->parent != NULL);
if (SuspendThread(ext->handle) == (DWORD)-1) {
DWORD err = GetLastError();
if (err == ERROR_ACCESS_DENIED && exception_code == 0) {
/* Already exited */
event_win32_context_exited(ctx);
return;
}
log_error("SuspendThread", 0);
return;
}
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_exception = 0;
switch (exception_code) {
case 0:
break;
case EXCEPTION_SINGLE_STEP:
break;
case EXCEPTION_BREAKPOINT:
get_registers(ctx);
if (!ext->regs_error && is_breakpoint_address(ctx, ext->regs->Eip - BREAK_SIZE)) {
ext->regs->Eip -= BREAK_SIZE;
ext->regs_dirty = 1;
ctx->stopped_by_bp = 1;
}
else {
ctx->pending_intercept = 1;
}
break;
case EXCEPTION_DEBUGGER_IO:
trace(LOG_ALWAYS, "Debugger IO request %#lx",
ext->suspend_reason.ExceptionRecord.ExceptionInformation[0]);
break;
default:
ctx->pending_signals |= 1 << ctx->signal;
if (ctx->signal != 0 && (ctx->sig_dont_stop & (1 << ctx->signal)) != 0) break;
ctx->stopped_by_exception = 1;
ctx->pending_intercept = 1;
break;
}
ctx->pending_step = 0;
send_context_stopped_event(ctx);
}
static void event_win32_context_stopped_async(void * arg) {
Context * ctx = (Context *)arg;
ContextExtensionWin32 * ext = EXT(ctx);
ext->context_stopped_async_pending = 0;
event_win32_context_stopped(ctx);
context_unlock(ctx);
}
static void event_win32_context_started(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
DWORD exception_code = ext->suspend_reason.ExceptionRecord.ExceptionCode;
trace(LOG_CONTEXT, "context: started: ctx %#lx, id %s", ctx, ctx->id);
assert(ctx->stopped);
if (ext->debug_started && exception_code == EXCEPTION_BREAKPOINT) ext->debug_started = 0;
send_context_started_event(ctx);
}
static void event_win32_context_exited(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
LINK * l = NULL;
assert(!ctx->exited);
context_lock(ctx);
if (ctx->stopped) event_win32_context_started(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));
}
ext->handle = NULL;
}
if (ext->file_handle != NULL) {
log_error("CloseHandle", CloseHandle(ext->file_handle));
ext->file_handle = NULL;
}
context_unlock(ctx);
}
static int win32_resume(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
if (ext->regs_dirty) {
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->pending_event.ExceptionRecord.ExceptionCode != 0) {
event_win32_context_started(ctx);
context_lock(ctx);
post_event(event_win32_context_stopped_async, ctx);
return 0;
}
if (ctx->parent->pending_signals & (1 << SIGKILL)) {
if (!ctx->parent->exiting && !TerminateProcess(EXT(ctx->parent)->handle, 1)) {
errno = log_error("TerminateProcess", 0);
return -1;
}
ctx->parent->pending_signals &= ~(1 << SIGKILL);
ctx->parent->exiting = 1;
}
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 void debug_event_handler(void * x) {
DebugEvent * args = (DebugEvent *)x;
HANDLE event_semaphore = args->event_semaphore;
while (args != NULL) {
DEBUG_EVENT * debug_event = &args->event;
Context * prs = context_find_from_pid(debug_event->dwProcessId, 0);
Context * ctx = context_find_from_pid(debug_event->dwThreadId, 1);
ContextExtensionWin32 * ext = NULL;
assert(ctx == NULL || ctx->parent == prs);
switch (debug_event->dwDebugEventCode) {
case CREATE_PROCESS_DEBUG_EVENT:
assert(prs == NULL);
assert(ctx == NULL);
ext = EXT(prs = create_context(pid2id(debug_event->dwProcessId, 0)));
prs->mem = prs;
ext->pid = debug_event->dwProcessId;
ext->handle = debug_event->u.CreateProcessInfo.hProcess;
ext->file_handle = debug_event->u.CreateProcessInfo.hFile;
ext->base_address = (uintptr_t)debug_event->u.CreateProcessInfo.lpBaseOfImage;
assert(ext->handle != NULL);
link_context(prs);
send_context_created_event(prs);
args->debug_thread_args->attach_callback(0, prs, args->debug_thread_args->attach_data);
args->debug_thread_args->attach_callback = NULL;
args->debug_thread_args->attach_data = NULL;
ext = EXT(ctx = create_context(pid2id(debug_event->dwThreadId, debug_event->dwProcessId)));
ctx->mem = prs;
ext->regs = (REG_SET *)loc_alloc_zero(sizeof(REG_SET));
ext->pid = debug_event->dwThreadId;
ext->handle = debug_event->u.CreateProcessInfo.hThread;
ext->debug_started = 1;
(ctx->parent = prs)->ref_count++;
list_add_first(&ctx->cldl, &prs->children);
link_context(ctx);
send_context_created_event(ctx);
break;
case CREATE_THREAD_DEBUG_EVENT:
assert(prs != NULL);
assert(ctx == NULL);
ext = EXT(ctx = create_context(pid2id(debug_event->dwThreadId, debug_event->dwProcessId)));
ctx->mem = prs;
ext->regs = (REG_SET *)loc_alloc_zero(sizeof(REG_SET));
ext->pid = debug_event->dwThreadId;
ext->handle = debug_event->u.CreateThread.hThread;
(ctx->parent = prs)->ref_count++;
list_add_first(&ctx->cldl, &prs->children);
link_context(ctx);
send_context_created_event(ctx);
event_win32_context_stopped(ctx);
break;
case EXCEPTION_DEBUG_EVENT:
assert(prs != NULL);
if (ctx == NULL) break;
if (args->early_event) break; /* Can anything be done about such exceptions? */
ext = EXT(ctx);
assert(ext->pending_event.ExceptionRecord.ExceptionCode == 0);
switch (args->event.u.Exception.ExceptionRecord.ExceptionCode) {
case EXCEPTION_SINGLE_STEP:
case EXCEPTION_BREAKPOINT:
case EXCEPTION_DEBUGGER_IO:
args->continue_status = DBG_CONTINUE;
break;
default:
args->continue_status = DBG_EXCEPTION_NOT_HANDLED;
break;
}
memcpy(&ext->pending_event, &args->event.u.Exception, sizeof(EXCEPTION_DEBUG_INFO));
if (!ctx->stopped) {
int signal = 0;
if (ext->context_stopped_async_pending) {
cancel_event(event_win32_context_stopped_async, ctx, 0);
ext->context_stopped_async_pending = 0;
}
else {
context_lock(ctx);
}
event_win32_context_stopped(ctx);
signal = get_signal_index(ctx);
if (signal != 0 && (ctx->sig_dont_pass & (1 << signal)) != 0) {
args->continue_status = DBG_CONTINUE;
}
context_unlock(ctx);
}
break;
case EXIT_THREAD_DEBUG_EVENT:
assert(prs != NULL);
if (ctx && !ctx->exited) event_win32_context_exited(ctx);
break;
case EXIT_PROCESS_DEBUG_EVENT:
assert(prs != NULL);
if (ctx && !ctx->exited) event_win32_context_exited(ctx);
event_win32_context_exited(prs);
break;
case LOAD_DLL_DEBUG_EVENT:
assert(prs != NULL);
ext = EXT(prs);
ext->module_loaded = 1;
ext->module_handle = args->event.u.LoadDll.hFile;
ext->module_address = (uintptr_t)args->event.u.LoadDll.lpBaseOfDll;
send_context_changed_event(prs);
if (ext->module_handle != NULL) {
log_error("CloseHandle", CloseHandle(ext->module_handle));
}
ext->module_handle = NULL;
ext->module_address = 0;
ext->module_loaded = 0;
break;
case UNLOAD_DLL_DEBUG_EVENT:
assert(prs != NULL);
ext = EXT(prs);
ext->module_unloaded = 1;
ext->module_address = (uintptr_t)args->event.u.UnloadDll.lpBaseOfDll;
send_context_changed_event(prs);
ext->module_address = 0;
ext->module_unloaded = 0;
break;
case RIP_EVENT:
trace(LOG_ALWAYS, "System debugging error: debuggee pid %d, error type %d, error code %d",
debug_event->dwProcessId, debug_event->u.RipInfo.dwType, debug_event->u.RipInfo.dwError);
break;
}
args = args->next;
}
log_error("ReleaseSemaphore", ReleaseSemaphore(event_semaphore, 1, 0));
}
static void debugger_exit_handler(void * x) {
DebugThreadArgs * args = (DebugThreadArgs *)x;
Context * prs = context_find_from_pid(args->context_id, 0);
trace(LOG_WAITPID, "debugger thread %d exited, debuggee pid %d",
args->debug_thread_id, args->context_id);
log_error("WaitForSingleObject", WaitForSingleObject(args->debug_thread, INFINITE) != WAIT_FAILED);
log_error("CloseHandle", CloseHandle(args->debug_thread));
log_error("CloseHandle", CloseHandle(args->debug_thread_semaphore));
if (prs != NULL && !prs->exited) event_win32_context_exited(prs);
loc_free(args);
}
static DWORD WINAPI debugger_thread_func(LPVOID x) {
DebugThreadArgs * args = (DebugThreadArgs *)x;
HANDLE event_semaphore = CreateSemaphore(NULL, 0, 1, NULL);
DebugEvent event_buffer;
DebugEvent create_process;
DebugEvent fantom_process;
int state = 0;
if (event_semaphore == NULL) {
args->error = GetLastError();
trace(LOG_ALWAYS, "Can't create semaphore: error %d", args->error);
ReleaseSemaphore(args->debug_thread_semaphore, 1, 0);
return 0;
}
if (DebugActiveProcess(args->context_id) == 0) {
args->error = GetLastError();
trace(LOG_ALWAYS, "Can't attach to a process: error %d", args->error);
ReleaseSemaphore(args->debug_thread_semaphore, 1, 0);
CloseHandle(event_semaphore);
return 0;
}
trace(LOG_WAITPID, "debugger thread %d started", GetCurrentThreadId());
memset(&event_buffer, 0, sizeof(event_buffer));
memset(&create_process, 0, sizeof(create_process));
memset(&fantom_process, 0, sizeof(fantom_process));
event_buffer.debug_thread_args = args;
event_buffer.event_semaphore = event_semaphore;
create_process.debug_thread_args = args;
create_process.event_semaphore = event_semaphore;
fantom_process.debug_thread_args = args;
fantom_process.event_semaphore = event_semaphore;
for (;;) {
DEBUG_EVENT * debug_event = &event_buffer.event;
memset(debug_event, 0, sizeof(DEBUG_EVENT));
if (WaitForDebugEvent(debug_event, INFINITE) == 0) {
trace(LOG_ALWAYS, "WaitForDebugEvent() error %d", GetLastError());
break;
}
if (debug_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
trace(LOG_WAITPID, "%s, process %d, thread %d, code %#lx",
win32_debug_event_name(debug_event->dwDebugEventCode),
debug_event->dwProcessId, debug_event->dwThreadId,
debug_event->u.Exception.ExceptionRecord.ExceptionCode);
}
else {
trace(LOG_WAITPID, "%s, process %d, thread %d",
win32_debug_event_name(debug_event->dwDebugEventCode),
debug_event->dwProcessId, debug_event->dwThreadId);
}
assert(args->context_id == debug_event->dwProcessId);
event_buffer.continue_status = DBG_CONTINUE;
switch (debug_event->dwDebugEventCode) {
case CREATE_PROCESS_DEBUG_EVENT:
if (state == 0) {
memcpy(&create_process, &event_buffer, sizeof(event_buffer));
state++;
}
else {
/* This looks like a bug in Windows: */
/* 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(fantom_process.event.u.CreateProcessInfo.hThread == NULL);
memcpy(&fantom_process, &event_buffer, sizeof(event_buffer));
SuspendThread(fantom_process.event.u.CreateProcessInfo.hThread);
CloseHandle(fantom_process.event.u.CreateProcessInfo.hFile);
fantom_process.event.u.CreateProcessInfo.hFile = NULL;
ResumeThread(create_process.event.u.CreateProcessInfo.hThread);
}
break;
default:
if (fantom_process.event.dwThreadId == debug_event->dwThreadId) {
if (debug_event->dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT) {
memset(&fantom_process, 0, sizeof(fantom_process));
}
else if (debug_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
event_buffer.continue_status = DBG_EXCEPTION_NOT_HANDLED;
}
else if (debug_event->dwDebugEventCode == LOAD_DLL_DEBUG_EVENT) {
if (debug_event->u.LoadDll.hFile != NULL) CloseHandle(debug_event->u.LoadDll.hFile);
}
break;
}
if (state == 0) {
if (debug_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
event_buffer.continue_status = DBG_EXCEPTION_NOT_HANDLED;
}
break;
}
if (state == 1 && debug_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT &&
debug_event->u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) {
post_event(debug_event_handler, &create_process);
ReleaseSemaphore(args->debug_thread_semaphore, 1, 0);
WaitForSingleObject(event_semaphore, INFINITE);
while (create_process.next != NULL) {
DebugEvent * e = create_process.next;
create_process.next = e->next;
loc_free(e);
}
state++;
if (fantom_process.event.u.CreateProcessInfo.hThread != NULL) {
ResumeThread(fantom_process.event.u.CreateProcessInfo.hThread);
}
}
if (state == 2) {
post_event(debug_event_handler, &event_buffer);
WaitForSingleObject(event_semaphore, INFINITE);
}
else {
/* Delay posting event to foreground thread until debugger is fully attached */
DebugEvent * e = (DebugEvent *)loc_alloc(sizeof(DebugEvent));
DebugEvent ** p = &create_process.next;
while (*p != NULL) p = &(*p)->next;
memcpy(e, &event_buffer, sizeof(DebugEvent));
e->early_event = 1;
*p = e;
}
break;
}
if (ContinueDebugEvent(debug_event->dwProcessId, debug_event->dwThreadId, event_buffer.continue_status) == 0) {
trace(LOG_ALWAYS, "Can't continue debug event: process %d, thread %d, error %d",
debug_event->dwProcessId, debug_event->dwThreadId, GetLastError());
break;
}
if (debug_event->dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break;
if (debug_event->dwDebugEventCode == RIP_EVENT) break;
}
if (state < 2) ReleaseSemaphore(args->debug_thread_semaphore, 1, 0);
CloseHandle(event_semaphore);
post_event(debugger_exit_handler, args);
return 0;
}
int context_attach(pid_t pid, ContextAttachCallBack * done, void * data, int selfattach) {
DebugThreadArgs * args = (DebugThreadArgs *)loc_alloc_zero(sizeof(DebugThreadArgs));
assert(done != NULL);
assert(!selfattach);
args->context_id = pid;
args->attach_callback = done;
args->attach_data = data;
args->debug_thread_semaphore = CreateSemaphore(NULL, 0, 1, NULL);
if (args->debug_thread_semaphore == NULL) {
int err = log_error("CreateSemaphore", 0);
loc_free(args);
errno = err;
return -1;
}
args->debug_thread = CreateThread(NULL, 0, debugger_thread_func, args, 0, &args->debug_thread_id);
if (args->debug_thread == NULL) {
int err = log_error("CreateThread", 0);
log_error("CloseHandle", CloseHandle(args->debug_thread_semaphore));
loc_free(args);
errno = err;
return -1;
}
log_error("WaitForSingleObject", WaitForSingleObject(args->debug_thread_semaphore, INFINITE) != WAIT_FAILED);
if (args->error) {
log_error("WaitForSingleObject", WaitForSingleObject(args->debug_thread, INFINITE) != WAIT_FAILED);
log_error("CloseHandle", CloseHandle(args->debug_thread));
log_error("CloseHandle", CloseHandle(args->debug_thread_semaphore));
loc_free(args);
set_win32_errno(args->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);
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 (SuspendThread(ext->handle) == (DWORD)-1) {
if (GetLastError() != ERROR_ACCESS_DENIED) {
errno = log_error("SuspendThread", 0);
return -1;
}
}
if (!ext->context_stopped_async_pending) {
context_lock(ctx);
post_event(event_win32_context_stopped_async, ctx);
ext->context_stopped_async_pending = 1;
}
return 0;
}
int context_continue(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
assert(is_dispatch_thread());
assert(context_has_state(ctx));
assert(ctx->stopped);
assert(!ctx->exited);
assert(!ctx->pending_step);
if (skip_breakpoint(ctx, 0)) return 0;
trace(LOG_CONTEXT, "context: resuming ctx %#lx, id %s", ctx, ctx->id);
if (ext->trace_flag) {
get_registers(ctx);
ext->regs->EFlags &= ~0x100;
ext->regs_dirty = 1;
}
return win32_resume(ctx);
}
int context_single_step(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
assert(is_dispatch_thread());
assert(context_has_state(ctx));
assert(ctx->stopped);
assert(!ctx->exited);
assert(!ctx->pending_step);
if (skip_breakpoint(ctx, 1)) return 0;
trace(LOG_CONTEXT, "context: single step ctx %#lx, id %s", ctx, ctx->id);
if (!ext->trace_flag) {
get_registers(ctx);
ext->regs->EFlags |= 0x100;
ext->regs_dirty = 1;
}
ctx->pending_step = 1;
return win32_resume(ctx);
}
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 %zd",
ctx, ctx->id, address, size);
assert(is_dispatch_thread());
if (ReadProcessMemory(ext->handle, (LPCVOID)address, buf, size, &bcnt) == 0 || bcnt != size) {
errno = log_error("ReadProcessMemory", 0);
return -1;
}
check_breakpoints_on_memory_read(ctx, address, buf, size);
return 0;
}
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 %zd",
ctx, ctx->id, address, size);
assert(is_dispatch_thread());
ctx = ctx->mem;
check_breakpoints_on_memory_write(ctx, address, buf, size);
if (WriteProcessMemory(ext->handle, (LPVOID)address, buf, size, &bcnt) == 0 || bcnt != size) {
DWORD err = GetLastError();
if (err == ERROR_ACCESS_DENIED) errno = set_win32_errno(err);
else errno = log_error("WriteProcessMemory", 0);
return -1;
}
if (FlushInstructionCache(ext->handle, (LPCVOID)address, size) == 0) {
errno = log_error("FlushInstructionCache", 0);
return -1;
}
return 0;
}
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 *);
}
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->file_handle;
}
HANDLE get_context_module_handle(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
return ext->module_handle;
}
DWORD64 get_context_base_address(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
return ext->base_address;
}
DWORD64 get_context_module_address(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
return ext->module_address;
}
int is_context_module_loaded(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
return ext->module_loaded;
}
int is_context_module_unloaded(Context * ctx) {
ContextExtensionWin32 * ext = EXT(ctx);
return ext->module_unloaded;
}
void init_contexts_sys_dep(void) {
context_extension_offset = context_extension(sizeof(ContextExtensionWin32));
ini_context_pid_hash();
}
#endif /* if ENABLE_DebugContext */
#endif /* WIN32 */