Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'agent/system/Windows/tcf')
-rw-r--r--agent/system/Windows/tcf/context-win32.c1434
-rw-r--r--agent/system/Windows/tcf/context-win32.h36
-rw-r--r--agent/system/Windows/tcf/pthreads-win32.c311
-rw-r--r--agent/system/Windows/tcf/pthreads-win32.h56
-rw-r--r--agent/system/Windows/tcf/regset.h23
-rw-r--r--agent/system/Windows/tcf/windbgcache.c405
-rw-r--r--agent/system/Windows/tcf/windbgcache.h251
7 files changed, 2516 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 */
diff --git a/agent/system/Windows/tcf/context-win32.h b/agent/system/Windows/tcf/context-win32.h
new file mode 100644
index 00000000..8c49751e
--- /dev/null
+++ b/agent/system/Windows/tcf/context-win32.h
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.
+ */
+
+#ifndef D_context_win32
+#define D_context_win32
+
+#include <tcf/config.h>
+#include <tcf/framework/context.h>
+
+extern HANDLE get_context_handle(Context * ctx);
+extern HANDLE get_context_file_handle(Context * ctx);
+extern HANDLE get_context_module_handle(Context * ctx);
+
+extern DWORD64 get_context_base_address(Context * ctx);
+extern DWORD64 get_context_module_address(Context * ctx);
+
+typedef int ContextExceptionHandler(Context *, EXCEPTION_DEBUG_INFO *);
+extern void add_context_exception_handler(ContextExceptionHandler * h);
+
+#endif /* D_context_win32 */
diff --git a/agent/system/Windows/tcf/pthreads-win32.c b/agent/system/Windows/tcf/pthreads-win32.c
new file mode 100644
index 00000000..08a64aa3
--- /dev/null
+++ b/agent/system/Windows/tcf/pthreads-win32.c
@@ -0,0 +1,311 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 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
+ *******************************************************************************/
+
+#include <tcf/config.h>
+
+#if defined(WIN32) && !defined(DISABLE_PTHREADS_WIN32)
+
+#include <assert.h>
+#include <tcf/framework/myalloc.h>
+#include <tcf/framework/errors.h>
+#include <system/Windows/tcf/pthreads-win32.h>
+
+/*********************************************************************
+ Support of pthreads on Windows is implemented according to
+ recommendations from the paper:
+
+ Strategies for Implementing POSIX Condition Variables on Win32
+ C++ Report, SIGS, Vol. 10, No. 5, June, 1998
+
+ Douglas C. Schmidt and Irfan Pyarali
+ Department of Computer Science
+ Washington University, St. Louis, Missouri
+**********************************************************************/
+
+/* TODO: POSIX pthread functions don't set errno */
+
+typedef struct {
+ int waiters_count;
+ CRITICAL_SECTION waiters_count_lock;
+ HANDLE sema;
+ HANDLE waiters_done;
+ size_t was_broadcast;
+} PThreadCond;
+
+int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr) {
+ assert(attr == NULL);
+ *mutex = (pthread_mutex_t)CreateMutex(NULL, FALSE, NULL);
+ if (*mutex == NULL) return set_win32_errno(GetLastError());
+ return 0;
+}
+
+int pthread_mutex_lock(pthread_mutex_t * mutex) {
+ assert(mutex != NULL);
+ assert(*mutex != NULL);
+ if (WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED) return set_win32_errno(GetLastError());
+ return 0;
+}
+
+int pthread_mutex_unlock(pthread_mutex_t * mutex) {
+ assert(mutex != NULL);
+ assert(*mutex != NULL);
+ if (!ReleaseMutex(*mutex)) return set_win32_errno(GetLastError());
+ return 0;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex) {
+ assert(mutex != NULL);
+ assert(*mutex != NULL);
+ if (!CloseHandle(*mutex)) return set_win32_errno(GetLastError());
+ return 0;
+}
+
+int pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * attr) {
+ PThreadCond * p = (PThreadCond *)loc_alloc_zero(sizeof(PThreadCond));
+ assert(attr == NULL);
+ p->waiters_count = 0;
+ p->was_broadcast = 0;
+ p->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
+ if (p->sema == NULL) return set_win32_errno(GetLastError());
+ InitializeCriticalSection(&p->waiters_count_lock);
+ p->waiters_done = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (p->waiters_done == NULL) return set_win32_errno(GetLastError());
+ *cond = (pthread_cond_t)p;
+ return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) {
+ DWORD res = 0;
+ int last_waiter = 0;
+ PThreadCond * p = (PThreadCond *)*cond;
+
+ EnterCriticalSection(&p->waiters_count_lock);
+ p->waiters_count++;
+ LeaveCriticalSection(&p->waiters_count_lock);
+
+ /* This call atomically releases the mutex and waits on the */
+ /* semaphore until <pthread_cond_signal> or <pthread_cond_broadcast> */
+ /* are called by another thread. */
+ res = SignalObjectAndWait(*mutex, p->sema, INFINITE, FALSE);
+ if (res == WAIT_FAILED) return set_win32_errno(GetLastError());
+
+ /* Re-acquire lock to avoid race conditions. */
+ EnterCriticalSection(&p->waiters_count_lock);
+
+ /* We're no longer waiting... */
+ p->waiters_count--;
+
+ /* Check to see if we're the last waiter after <pthread_cond_broadcast>. */
+ last_waiter = p->was_broadcast && p->waiters_count == 0;
+
+ LeaveCriticalSection(&p->waiters_count_lock);
+
+ /* If we're the last waiter thread during this particular broadcast */
+ /* then let all the other threads proceed. */
+ if (last_waiter) {
+ /* This call atomically signals the <waiters_done_> event and waits until */
+ /* it can acquire the <mutex>. This is required to ensure fairness. */
+ DWORD err = SignalObjectAndWait(p->waiters_done, *mutex, INFINITE, FALSE);
+ if (err == WAIT_FAILED) return set_win32_errno(GetLastError());
+ }
+ else {
+ /* Always regain the external mutex since that's the guarantee we */
+ /* give to our callers. */
+ DWORD err = WaitForSingleObject(*mutex, INFINITE);
+ if (err == WAIT_FAILED) return set_win32_errno(GetLastError());
+ }
+ assert(res == WAIT_OBJECT_0);
+ return 0;
+}
+
+int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec * abstime) {
+ DWORD res = 0;
+ int last_waiter = 0;
+ PThreadCond * p = (PThreadCond *)*cond;
+ DWORD timeout = 0;
+ struct timespec timenow;
+
+ if (clock_gettime(CLOCK_REALTIME, &timenow)) return errno;
+ if (abstime->tv_sec < timenow.tv_sec) return ETIMEDOUT;
+ if (abstime->tv_sec == timenow.tv_sec) {
+ if (abstime->tv_nsec <= timenow.tv_nsec) return ETIMEDOUT;
+ }
+ timeout = (DWORD)((abstime->tv_sec - timenow.tv_sec) * 1000 + (abstime->tv_nsec - timenow.tv_nsec) / 1000000 + 5);
+
+ EnterCriticalSection(&p->waiters_count_lock);
+ p->waiters_count++;
+ LeaveCriticalSection(&p->waiters_count_lock);
+
+ /* This call atomically releases the mutex and waits on the */
+ /* semaphore until <pthread_cond_signal> or <pthread_cond_broadcast> */
+ /* are called by another thread. */
+ res = SignalObjectAndWait(*mutex, p->sema, timeout, FALSE);
+ if (res == WAIT_FAILED) return set_win32_errno(GetLastError());
+
+ /* Re-acquire lock to avoid race conditions. */
+ EnterCriticalSection(&p->waiters_count_lock);
+
+ /* We're no longer waiting... */
+ p->waiters_count--;
+
+ /* Check to see if we're the last waiter after <pthread_cond_broadcast>. */
+ last_waiter = p->was_broadcast && p->waiters_count == 0;
+
+ LeaveCriticalSection(&p->waiters_count_lock);
+
+ /* If we're the last waiter thread during this particular broadcast */
+ /* then let all the other threads proceed. */
+ if (last_waiter) {
+ /* This call atomically signals the <waiters_done> event and waits until */
+ /* it can acquire the <mutex>. This is required to ensure fairness. */
+ DWORD err = SignalObjectAndWait(p->waiters_done, *mutex, INFINITE, FALSE);
+ if (err == WAIT_FAILED) return set_win32_errno(GetLastError());
+ }
+ else {
+ /* Always regain the external mutex since that's the guarantee we */
+ /* give to our callers. */
+ DWORD err = WaitForSingleObject(*mutex, INFINITE);
+ if (err == WAIT_FAILED) return set_win32_errno(GetLastError());
+ }
+
+ if (res == WAIT_TIMEOUT) return errno = ETIMEDOUT;
+ assert(res == WAIT_OBJECT_0);
+ return 0;
+}
+
+int pthread_cond_signal(pthread_cond_t * cond) {
+ int have_waiters = 0;
+ PThreadCond * p = (PThreadCond *)*cond;
+
+ EnterCriticalSection(&p->waiters_count_lock);
+ have_waiters = p->waiters_count > 0;
+ LeaveCriticalSection(&p->waiters_count_lock);
+
+ /* If there aren't any waiters, then this is a no-op. */
+ if (have_waiters) {
+ if (!ReleaseSemaphore(p->sema, 1, 0)) return set_win32_errno(GetLastError());
+ }
+ return 0;
+}
+
+int pthread_cond_broadcast(pthread_cond_t * cond) {
+ int have_waiters = 0;
+ PThreadCond * p = (PThreadCond *)*cond;
+
+ /* This is needed to ensure that <waiters_count_> and <was_broadcast_> are */
+ /* consistent relative to each other. */
+ EnterCriticalSection(&p->waiters_count_lock);
+
+ if (p->waiters_count > 0) {
+ /* We are broadcasting, even if there is just one waiter... */
+ /* Record that we are broadcasting, which helps optimize */
+ /* <pthread_cond_wait> for the non-broadcast case. */
+ p->was_broadcast = 1;
+ have_waiters = 1;
+ }
+
+ if (have_waiters) {
+ /* Wake up all the waiters atomically. */
+ if (!ReleaseSemaphore(p->sema, p->waiters_count, 0)) return set_win32_errno(GetLastError());
+
+ LeaveCriticalSection(&p->waiters_count_lock);
+
+ /* Wait for all the awakened threads to acquire the counting */
+ /* semaphore. */
+ if (WaitForSingleObject(p->waiters_done, INFINITE) == WAIT_FAILED) return set_win32_errno(GetLastError());
+ /* This assignment is okay, even without the <waiters_count_lock_> held */
+ /* because no other waiter threads can wake up to access it. */
+ p->was_broadcast = 0;
+ }
+ else {
+ LeaveCriticalSection(&p->waiters_count_lock);
+ }
+ return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t * cond) {
+ PThreadCond * p = (PThreadCond *)*cond;
+
+ DeleteCriticalSection(&p->waiters_count_lock);
+ if (!CloseHandle(p->sema)) return set_win32_errno(GetLastError());
+ if (!CloseHandle(p->waiters_done)) return set_win32_errno(GetLastError());
+
+ loc_free(p);
+ *cond = NULL;
+ return 0;
+}
+
+typedef struct ThreadArgs ThreadArgs;
+
+struct ThreadArgs {
+ void * (*start)(void *);
+ void * args;
+};
+
+static void start_thread(void * x) {
+ ThreadArgs a = *(ThreadArgs *)x;
+
+ loc_free(x);
+ ExitThread((DWORD)a.start(a.args));
+}
+
+int pthread_create(pthread_t * res, const pthread_attr_t * attr,
+ void * (*start)(void *), void * args) {
+ HANDLE thread = NULL;
+ DWORD thread_id = 0;
+ ThreadArgs * a = (ThreadArgs *)loc_alloc(sizeof(ThreadArgs));
+
+ a->start = start;
+ a->args = args;
+ thread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)start_thread, a, 0, &thread_id);
+ if (thread == NULL) {
+ int err = set_win32_errno(GetLastError());
+ loc_free(a);
+ return errno = err;
+ }
+ if (!CloseHandle(thread)) return set_win32_errno(GetLastError());
+ *res = (pthread_t)thread_id;
+ return 0;
+}
+
+int pthread_join(pthread_t thread_id, void ** value_ptr) {
+ int error = 0;
+ HANDLE thread = OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, FALSE, (DWORD)thread_id);
+
+ if (thread == NULL) return set_win32_errno(GetLastError());
+ if (WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) error = set_win32_errno(GetLastError());
+ if (!error && value_ptr != NULL && !GetExitCodeThread(thread, (LPDWORD)value_ptr)) error = set_win32_errno(GetLastError());
+ if (!CloseHandle(thread) && !error) error = set_win32_errno(GetLastError());
+ return error;
+}
+
+int pthread_detach(pthread_t thread_id) {
+ return 0;
+}
+
+pthread_t pthread_self(void) {
+ return (pthread_t)GetCurrentThreadId();
+}
+
+int pthread_equal(pthread_t thread1, pthread_t thread2) {
+ return thread1 == thread2;
+}
+
+int pthread_attr_init(pthread_attr_t * attr) {
+ *attr = NULL;
+ return 0;
+}
+
+#endif
diff --git a/agent/system/Windows/tcf/pthreads-win32.h b/agent/system/Windows/tcf/pthreads-win32.h
new file mode 100644
index 00000000..e98874e0
--- /dev/null
+++ b/agent/system/Windows/tcf/pthreads-win32.h
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2011 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
+ *******************************************************************************/
+
+/*
+ * PThreads emulation for Windows.
+ */
+
+#ifndef D_pthreads_win32
+#define D_pthreads_win32
+
+#if defined(__CYGWIN__)
+# include <cygwin/types.h>
+#else
+typedef struct pthread_struct * pthread_t;
+typedef struct pthread_attr_struct * pthread_attr_t;
+typedef struct pthread_mutex_struct * pthread_mutex_t;
+typedef struct pthread_cond_struct * pthread_cond_t;
+typedef struct pthread_mutexattr_struct * pthread_mutexattr_t;
+typedef struct pthread_condattr_struct * pthread_condattr_t;
+#endif
+
+extern int pthread_attr_init(pthread_attr_t * attr);
+
+extern int pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * attr);
+extern int pthread_cond_signal(pthread_cond_t * cond);
+extern int pthread_cond_broadcast(pthread_cond_t * cond);
+extern int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);
+extern int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
+ const struct timespec * abstime);
+extern int pthread_cond_destroy(pthread_cond_t * cond);
+
+extern int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr);
+extern int pthread_mutex_lock(pthread_mutex_t * mutex);
+extern int pthread_mutex_unlock(pthread_mutex_t * mutex);
+extern int pthread_mutex_destroy(pthread_mutex_t *mutex);
+
+extern pthread_t pthread_self(void);
+extern int pthread_create(pthread_t * thread, const pthread_attr_t * attr,
+ void * (*start_routine)(void *), void * arg);
+extern int pthread_join(pthread_t thread, void **value_ptr);
+extern int pthread_detach(pthread_t thread);
+extern int pthread_equal(pthread_t thread1, pthread_t thread2);
+
+#endif /* D_pthreads_win32 */
diff --git a/agent/system/Windows/tcf/regset.h b/agent/system/Windows/tcf/regset.h
new file mode 100644
index 00000000..fb4eab83
--- /dev/null
+++ b/agent/system/Windows/tcf/regset.h
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 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 header file provides definition of REG_SET - a structure that can
+ * hold values of target CPU registers.
+ */
+
+#if defined(WIN32)
+ typedef CONTEXT REG_SET;
+#endif
diff --git a/agent/system/Windows/tcf/windbgcache.c b/agent/system/Windows/tcf/windbgcache.c
new file mode 100644
index 00000000..cfd43476
--- /dev/null
+++ b/agent/system/Windows/tcf/windbgcache.c
@@ -0,0 +1,405 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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 provides access to Windows Portable Executable debug information.
+ * Current implementation delegates all its job to DBGHELP.DLL.
+ */
+
+#include <tcf/config.h>
+
+#if defined(WIN32) && !ENABLE_ELF
+# define ENABLE_PE_Symbols ((SERVICE_LineNumbers && !ENABLE_LineNumbersProxy) || (SERVICE_Symbols && !ENABLE_SymbolsProxy))
+# define ENABLE_EnumerateModules (SERVICE_MemoryMap && !ENABLE_ContextProxy)
+#else
+# define ENABLE_PE_Symbols 0
+# define ENABLE_EnumerateModules 0
+#endif
+
+#if ENABLE_PE_Symbols || ENABLE_EnumerateModules
+
+#include <assert.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <system/Windows/tcf/windbgcache.h>
+#include <system/Windows/tcf/context-win32.h>
+#include <tcf/framework/trace.h>
+#include <tcf/framework/myalloc.h>
+#include <tcf/services/memorymap.h>
+
+static HINSTANCE dbghelp_dll = NULL;
+
+#define SYM_SEARCH_PATH ""
+/* Path could contain "http://msdl.microsoft.com/download/symbols",
+ but access to Microsoft debug info server is too slow,
+ and dbghelp.dll caching is inadequate
+*/
+
+static wchar_t * pathes[] = {
+ L"%\\Debugging Tools for Windows (x86)\\dbghelp.dll",
+ L"%\\Debugging Tools for Windows\\dbghelp.dll",
+ L".\\dbghelp.dll",
+ L"dbghelp.dll",
+ NULL
+};
+
+#if ENABLE_PE_Symbols
+
+static void event_context_created(Context * ctx, void * client_data) {
+ HANDLE handle = NULL;
+ if (ctx->parent != NULL) return;
+ handle = get_context_handle(ctx);
+ assert(handle != NULL);
+ assert(ctx->mem == ctx);
+ if (!SymInitialize(handle, SYM_SEARCH_PATH, FALSE)) {
+ set_win32_errno(GetLastError());
+ trace(LOG_ALWAYS, "SymInitialize() error: %s", errno_to_str(errno));
+ }
+ if (!SymLoadModule64(handle, get_context_file_handle(ctx),
+ NULL, NULL, get_context_base_address(ctx), 0)) {
+ set_win32_errno(GetLastError());
+ trace(LOG_ALWAYS, "SymLoadModule64() error: %s", errno_to_str(errno));
+ }
+}
+
+static void event_context_exited(Context * ctx, void * client_data) {
+ HANDLE handle = NULL;
+ if (ctx->parent != NULL) return;
+ handle = get_context_handle(ctx);
+ assert(handle != NULL);
+ assert(ctx->mem == ctx);
+ if (!SymUnloadModule64(handle, get_context_base_address(ctx))) {
+ set_win32_errno(GetLastError());
+ trace(LOG_ALWAYS, "SymUnloadModule64(0x%Ix,0x%I64x) (context exit) error: %s",
+ handle, get_context_base_address(ctx), errno_to_str(errno));
+ }
+ if (!SymCleanup(handle)) {
+ set_win32_errno(GetLastError());
+ trace(LOG_ALWAYS, "SymCleanup() error: %s", errno_to_str(errno));
+ }
+}
+
+static void event_module_loaded(Context * ctx, void * client_data) {
+ HANDLE handle = get_context_handle(ctx);
+ assert(handle != NULL);
+ assert(ctx->mem == ctx);
+ if (!SymLoadModule64(handle, get_context_module_handle(ctx),
+ NULL, NULL, get_context_module_address(ctx), 0)) {
+ set_win32_errno(GetLastError());
+ trace(LOG_ALWAYS, "SymLoadModule64() error: %s", errno_to_str(errno));
+ }
+}
+
+static void event_module_unloaded(Context * ctx, void * client_data) {
+ HANDLE handle = get_context_handle(ctx);
+ assert(handle != NULL);
+ assert(ctx->mem == ctx);
+ if (!SymUnloadModule64(handle, get_context_module_address(ctx))) {
+ DWORD err = GetLastError();
+ /* Workaround:
+ * On Windows 7 first few UNLOAD_DLL_DEBUG_EVENT come without matching LOAD_DLL_DEBUG_EVENT,
+ * SymUnloadModule64() returns error 0x57 "The parameter is incorrect" for such events.
+ * No proper fix is found for this issue. */
+ if (err != 0x57) {
+ int n = set_win32_errno(err);
+ trace(LOG_ALWAYS, "SymUnloadModule64(0x%Ix,0x%I64x) (unload DLL) error: %s",
+ handle, get_context_module_address(ctx), errno_to_str(n));
+ }
+ }
+}
+
+static ContextEventListener ctx_listener = {
+ event_context_created,
+ event_context_exited,
+};
+
+static MemoryMapEventListener map_listener = {
+ event_module_loaded,
+ NULL,
+ event_module_unloaded
+};
+
+#endif
+
+static void CheckDLLVersion(void) {
+ DWORD handle = 0;
+ WCHAR fnm[_MAX_PATH];
+ BYTE * version_info = NULL;
+ DWORD size = GetModuleFileNameW(dbghelp_dll, fnm, _MAX_PATH);
+ fnm[size] = 0;
+ size = GetFileVersionInfoSizeW(fnm, &handle);
+ version_info = (BYTE *)loc_alloc_zero(size);
+ if (!GetFileVersionInfoW(fnm, handle, size, version_info)) {
+ trace(LOG_ALWAYS, "Cannot get DBGHELP.DLL version info: %s",
+ errno_to_str(set_win32_errno(GetLastError())));
+ }
+ else {
+ UINT vsfi_len = 0;
+ VS_FIXEDFILEINFO * vsfi = NULL;
+ VerQueryValueW(version_info, L"\\", (void**)&vsfi, &vsfi_len);
+ if (HIWORD(vsfi->dwFileVersionMS) < 6 || HIWORD(vsfi->dwFileVersionMS) == 6 && LOWORD(vsfi->dwFileVersionMS) < 9) {
+ char path[_MAX_PATH * 2];
+ trace(LOG_ALWAYS, "DBGHELP.DLL version is less then 6.9 - debug services might not work properly");
+ if (WideCharToMultiByte(CP_UTF8, 0, fnm, -1, path, sizeof(path), NULL, NULL)) {
+ trace(LOG_ALWAYS, "%s", path);
+ }
+ trace(LOG_ALWAYS, "DBGHELP.DLL version %d.%d.%d.%d",
+ HIWORD(vsfi->dwFileVersionMS), LOWORD(vsfi->dwFileVersionMS),
+ HIWORD(vsfi->dwFileVersionLS), LOWORD(vsfi->dwFileVersionLS));
+ }
+ }
+ loc_free(version_info);
+}
+
+static FARPROC GetProc(char * name) {
+ if (dbghelp_dll == NULL) {
+ wchar_t ** p = pathes;
+ while (dbghelp_dll == NULL && *p != NULL) {
+ if (**p == '%') {
+ HKEY key;
+ if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
+ 0, KEY_READ, &key) == ERROR_SUCCESS) {
+ wchar_t buf[FILE_PATH_SIZE];
+ DWORD size = sizeof(buf);
+ memset(buf, 0, sizeof(buf));
+ if (RegQueryValueExW(key,
+ L"ProgramFilesDir (x86)",
+ NULL, NULL, (LPBYTE)buf, &size) == ERROR_SUCCESS) {
+ wcsncat(buf, *p + 1, FILE_PATH_SIZE - size / sizeof(wchar_t));
+ dbghelp_dll = LoadLibraryW(buf);
+ }
+ if (dbghelp_dll == NULL) {
+ size = sizeof(buf);
+ memset(buf, 0, sizeof(buf));
+ if (RegQueryValueExW(key,
+ L"ProgramFilesDir",
+ NULL, NULL, (LPBYTE)buf, &size) == ERROR_SUCCESS) {
+ wcsncat(buf, *p + 1, FILE_PATH_SIZE - size / sizeof(wchar_t));
+ dbghelp_dll = LoadLibraryW(buf);
+ }
+ }
+ RegCloseKey(key);
+ }
+ }
+ else {
+ dbghelp_dll = LoadLibraryW(*p);
+ }
+ p++;
+ }
+ if (dbghelp_dll == NULL) {
+ assert(GetLastError() != 0);
+ return NULL;
+ }
+ CheckDLLVersion();
+#if ENABLE_PE_Symbols
+ add_context_event_listener(&ctx_listener, NULL);
+ add_memory_map_event_listener(&map_listener, NULL);
+#endif
+ }
+ return GetProcAddress(dbghelp_dll, name);
+}
+
+#endif
+
+#if ENABLE_PE_Symbols
+
+BOOL SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, PCSTR, BOOL);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymInitialize");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, UserSearchPath, fInvadeProcess);
+}
+
+DWORD SymGetOptions(void) {
+ typedef DWORD (FAR WINAPI * ProcType)(void);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymGetOptions");
+ if (proc == NULL) return 0;
+ }
+ return proc();
+}
+
+BOOL SymSetOptions(DWORD Options) {
+ typedef BOOL (FAR WINAPI * ProcType)(DWORD);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymSetOptions");
+ if (proc == NULL) return 0;
+ }
+ return proc(Options);
+}
+
+BOOL SymGetLineFromName(HANDLE hProcess, PCSTR ModuleName, PCSTR FileName, DWORD dwLineNumber, PLONG plDisplacement, PIMAGEHLP_LINE Line) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, PCSTR, PCSTR, DWORD, PLONG, PIMAGEHLP_LINE);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymGetLineFromName");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, ModuleName, FileName, dwLineNumber, plDisplacement, Line);
+}
+
+BOOL SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, DWORD, PDWORD, PIMAGEHLP_LINE);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymGetLineFromAddr");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, dwAddr, pdwDisplacement, Line);
+}
+
+BOOL SymGetLineNext(HANDLE hProcess, PIMAGEHLP_LINE Line) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, PIMAGEHLP_LINE);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymGetLineNext");
+ if (proc == NULL) return 0;
+ }
+ assert(Line != NULL);
+ assert(Line->Address != 0);
+ return proc(hProcess, Line);
+}
+
+BOOL SymGetTypeInfo(HANDLE hProcess, DWORD64 ModBase, ULONG TypeId, IMAGEHLP_SYMBOL_TYPE_INFO GetType, PVOID pInfo) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, DWORD64, ULONG, IMAGEHLP_SYMBOL_TYPE_INFO, PVOID);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymGetTypeInfo");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, ModBase, TypeId, GetType, pInfo);
+}
+
+BOOL SymFromIndex(HANDLE hProcess, ULONG64 BaseOfDll, DWORD Index, PSYMBOL_INFO Symbol) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, ULONG64, DWORD, PSYMBOL_INFO);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymFromIndex");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, BaseOfDll, Index, Symbol);
+}
+
+BOOL SymFromAddr(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymFromAddr");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, Address, Displacement, Symbol);
+}
+
+BOOL SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame, PIMAGEHLP_CONTEXT Context) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, PIMAGEHLP_STACK_FRAME, PIMAGEHLP_CONTEXT);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymSetContext");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, StackFrame, Context);
+}
+
+BOOL SymFromName(HANDLE hProcess, PCSTR Name, PSYMBOL_INFO Symbol) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, PCSTR, PSYMBOL_INFO);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymFromName");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, Name, Symbol);
+}
+
+BOOL SymEnumSymbols(HANDLE hProcess, ULONG64 BaseOfDll, PCSTR Mask, PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, PVOID UserContext) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, ULONG64, PCSTR, PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymEnumSymbols");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, BaseOfDll, Mask, EnumSymbolsCallback, UserContext);
+}
+
+BOOL SymGetTypeFromName(HANDLE hProcess, ULONG64 BaseOfDll, PCSTR Name, PSYMBOL_INFO Symbol) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, ULONG64, PCSTR, PSYMBOL_INFO);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymGetTypeFromName");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, BaseOfDll, Name, Symbol);
+}
+
+DWORD64 SymGetModuleBase64(HANDLE hProcess, ULONG64 Address) {
+ typedef DWORD64 (FAR WINAPI * ProcType)(HANDLE, ULONG64);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymGetModuleBase64");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, Address);
+}
+
+BOOL SymLoadModule64(HANDLE hProcess, HANDLE hFile, PCSTR ImageName, PCSTR ModuleName, DWORD64 BaseOfDll, DWORD SizeOfDll) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, HANDLE, PCSTR, PCSTR, DWORD64, DWORD);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymLoadModule64");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, hFile, ImageName, ModuleName, BaseOfDll, SizeOfDll);
+}
+
+BOOL SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, DWORD64);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymUnloadModule64");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, BaseOfDll);
+}
+
+BOOL SymCleanup(HANDLE hProcess) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("SymCleanup");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess);
+}
+
+#endif
+
+#if ENABLE_EnumerateModules
+
+BOOL LocEnumerateLoadedModulesW64(HANDLE hProcess, PENUMLOADED_MODULES_CALLBACKW64 Callback, PVOID UserContext) {
+ typedef BOOL (FAR WINAPI * ProcType)(HANDLE, PENUMLOADED_MODULES_CALLBACKW64, PVOID);
+ static ProcType proc = NULL;
+ if (proc == NULL) {
+ proc = (ProcType)GetProc("EnumerateLoadedModulesW64");
+ if (proc == NULL) return 0;
+ }
+ return proc(hProcess, Callback, UserContext);
+}
+
+#endif
diff --git a/agent/system/Windows/tcf/windbgcache.h b/agent/system/Windows/tcf/windbgcache.h
new file mode 100644
index 00000000..670cad7d
--- /dev/null
+++ b/agent/system/Windows/tcf/windbgcache.h
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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 provides access to Windows Portable Executable debug information.
+ */
+
+#ifndef D_windbgcache
+#define D_windbgcache
+
+#include <tcf/config.h>
+
+#if defined(WIN32)
+
+#if defined(__GNUC__)
+# include <imagehlp.h>
+#else
+# define _NO_CVCONST_H
+# include <dbghelp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__)
+typedef enum _IMAGEHLP_SYMBOL_TYPE_INFO {
+ TI_GET_SYMTAG,
+ TI_GET_SYMNAME,
+ TI_GET_LENGTH,
+ TI_GET_TYPE,
+ TI_GET_TYPEID,
+ TI_GET_BASETYPE,
+ TI_GET_ARRAYINDEXTYPEID,
+ TI_FINDCHILDREN,
+ TI_GET_DATAKIND,
+ TI_GET_ADDRESSOFFSET,
+ TI_GET_OFFSET,
+ TI_GET_VALUE,
+ TI_GET_COUNT,
+ TI_GET_CHILDRENCOUNT,
+ TI_GET_BITPOSITION,
+ TI_GET_VIRTUALBASECLASS,
+ TI_GET_VIRTUALTABLESHAPEID,
+ TI_GET_VIRTUALBASEPOINTEROFFSET,
+ TI_GET_CLASSPARENTID,
+ TI_GET_NESTED,
+ TI_GET_SYMINDEX,
+ TI_GET_LEXICALPARENT,
+ TI_GET_ADDRESS,
+ TI_GET_THISADJUST,
+ TI_GET_UDTKIND,
+ TI_IS_EQUIV_TO,
+ TI_GET_CALLING_CONVENTION,
+ TI_IS_CLOSE_EQUIV_TO,
+ TI_GTIEX_REQS_VALID,
+ TI_GET_VIRTUALBASEOFFSET,
+ TI_GET_VIRTUALBASEDISPINDEX,
+ TI_GET_IS_REFERENCE,
+ IMAGEHLP_SYMBOL_TYPE_INFO_MAX,
+} IMAGEHLP_SYMBOL_TYPE_INFO;
+
+typedef struct _TI_FINDCHILDREN_PARAMS {
+ ULONG Count;
+ ULONG Start;
+ ULONG ChildId[1];
+} TI_FINDCHILDREN_PARAMS;
+
+enum SymTagEnum {
+ SymTagNull,
+ SymTagExe,
+ SymTagCompiland,
+ SymTagCompilandDetails,
+ SymTagCompilandEnv,
+ SymTagFunction,
+ SymTagBlock,
+ SymTagData,
+ SymTagAnnotation,
+ SymTagLabel,
+ SymTagPublicSymbol,
+ SymTagUDT,
+ SymTagEnum,
+ SymTagFunctionType,
+ SymTagPointerType,
+ SymTagArrayType,
+ SymTagBaseType,
+ SymTagTypedef,
+ SymTagBaseClass,
+ SymTagFriend,
+ SymTagFunctionArgType,
+ SymTagFuncDebugStart,
+ SymTagFuncDebugEnd,
+ SymTagUsingNamespace,
+ SymTagVTableShape,
+ SymTagVTable,
+ SymTagCustom,
+ SymTagThunk,
+ SymTagCustomType,
+ SymTagManagedType,
+ SymTagDimension,
+ SymTagMax
+};
+
+#define SYMFLAG_VALUEPRESENT 0x00000001
+#define SYMFLAG_REGISTER 0x00000008
+#define SYMFLAG_REGREL 0x00000010
+#define SYMFLAG_FRAMEREL 0x00000020
+#define SYMFLAG_PARAMETER 0x00000040
+#define SYMFLAG_LOCAL 0x00000080
+#define SYMFLAG_CONSTANT 0x00000100
+#define SYMFLAG_EXPORT 0x00000200
+#define SYMFLAG_FORWARDER 0x00000400
+#define SYMFLAG_FUNCTION 0x00000800
+#define SYMFLAG_VIRTUAL 0x00001000
+#define SYMFLAG_THUNK 0x00002000
+#define SYMFLAG_TLSREL 0x00004000
+#define SYMFLAG_SLOT 0x00008000
+#define SYMFLAG_ILREL 0x00010000
+#define SYMFLAG_METADATA 0x00020000
+#define SYMFLAG_CLR_TOKEN 0x00040000
+
+typedef struct _SYMBOL_INFO {
+ ULONG SizeOfStruct;
+ ULONG TypeIndex;
+ ULONG64 Reserved[2];
+ ULONG Index;
+ ULONG Size;
+ ULONG64 ModBase;
+ ULONG Flags;
+ ULONG64 Value;
+ ULONG64 Address;
+ ULONG Register;
+ ULONG Scope;
+ ULONG Tag;
+ ULONG NameLen;
+ ULONG MaxNameLen;
+ CHAR Name[1];
+} SYMBOL_INFO, *PSYMBOL_INFO;
+
+typedef struct _IMAGEHLP_STACK_FRAME {
+ ULONG64 InstructionOffset;
+ ULONG64 ReturnOffset;
+ ULONG64 FrameOffset;
+ ULONG64 StackOffset;
+ ULONG64 BackingStoreOffset;
+ ULONG64 FuncTableEntry;
+ ULONG64 Params[4];
+ ULONG64 Reserved[5];
+ BOOL Virtual;
+ ULONG Reserved2;
+} IMAGEHLP_STACK_FRAME, *PIMAGEHLP_STACK_FRAME;
+
+typedef VOID IMAGEHLP_CONTEXT, *PIMAGEHLP_CONTEXT;
+
+typedef BOOL (CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACK)(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext);
+typedef BOOL (CALLBACK *PENUMLOADED_MODULES_CALLBACKW64)(PCWSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext);
+
+#endif /* defined(__GNUC__) */
+
+enum BasicType {
+ btNoType = 0,
+ btVoid = 1,
+ btChar = 2,
+ btWChar = 3,
+ btInt = 6,
+ btUInt = 7,
+ btFloat = 8,
+ btBCD = 9,
+ btBool = 10,
+ btLong = 13,
+ btULong = 14,
+ btCurrency = 25,
+ btDate = 26,
+ btVariant = 27,
+ btComplex = 28,
+ btBit = 29,
+ btBSTR = 30,
+ btHresult = 31
+};
+
+enum DataKind {
+ DataIsUnknown,
+ DataIsLocal,
+ DataIsStaticLocal,
+ DataIsParam,
+ DataIsObjectPtr,
+ DataIsFileStatic,
+ DataIsGlobal,
+ DataIsMember,
+ DataIsStaticMember,
+ DataIsConstant
+};
+
+#define SymInitialize LocSymInitialize
+#define SymGetOptions LocSymGetOptions
+#define SymSetOptions LocSymSetOptions
+#define SymGetLineFromName LocSymGetLineFromName
+#define SymGetLineFromAddr LocSymGetLineFromAddr
+#define SymGetLineNext LocSymGetLineNext
+#define SymGetTypeInfo LocSymGetTypeInfo
+#define SymFromIndex LocSymFromIndex
+#define SymFromAddr LocSymFromAddr
+#define SymSetContext LocSymSetContext
+#define SymFromName LocSymFromName
+#define SymEnumSymbols LocSymEnumSymbols
+#define SymGetTypeFromName LocSymGetTypeFromName
+#define SymGetModuleBase64 LocSymGetModuleBase64
+#define SymLoadModule64 LocSymLoadModule64
+#define SymUnloadModule64 LocSymUnloadModule64
+#define SymCleanup LocSymCleanup
+
+#define EnumerateLoadedModulesW64 LocEnumerateLoadedModulesW64
+
+extern BOOL SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess);
+extern DWORD SymGetOptions(void);
+extern BOOL SymSetOptions(DWORD Options);
+extern BOOL SymGetLineFromName(HANDLE hProcess, PCSTR ModuleName, PCSTR FileName, DWORD dwLineNumber, PLONG plDisplacement, PIMAGEHLP_LINE Line);
+extern BOOL SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line);
+extern BOOL SymGetLineNext(HANDLE hProcess, PIMAGEHLP_LINE Line);
+extern BOOL SymGetTypeInfo(HANDLE hProcess, DWORD64 ModBase, ULONG TypeId, IMAGEHLP_SYMBOL_TYPE_INFO GetType, PVOID pInfo);
+extern BOOL SymFromIndex(HANDLE hProcess, ULONG64 BaseOfDll, DWORD Index, PSYMBOL_INFO Symbol);
+extern BOOL SymFromAddr(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);
+extern BOOL SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame, PIMAGEHLP_CONTEXT Context);
+extern BOOL SymFromName(HANDLE hProcess, PCSTR Name, PSYMBOL_INFO Symbol);
+extern BOOL SymEnumSymbols(HANDLE hProcess, ULONG64 BaseOfDll, PCSTR Mask, PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, PVOID UserContext);
+extern BOOL SymGetTypeFromName(HANDLE hProcess, ULONG64 BaseOfDll, PCSTR Name, PSYMBOL_INFO Symbol);
+extern DWORD64 SymGetModuleBase64(HANDLE hProcess, ULONG64 Address);
+extern BOOL SymLoadModule64(HANDLE hProcess, HANDLE hFile, PCSTR ImageName, PCSTR ModuleName, DWORD64 BaseOfDll, DWORD SizeOfDll);
+extern BOOL SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll);
+extern BOOL SymCleanup(HANDLE hProcess);
+
+extern BOOL LocEnumerateLoadedModulesW64(HANDLE hProcess, PENUMLOADED_MODULES_CALLBACKW64 Callback, PVOID UserContext);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* defined(WIN32) */
+#endif /* D_windbgcache */

Back to the top