From 729a2462fb1727537dd1be451cdd23c34e43767b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 4 Apr 2020 11:30:14 +0200 Subject: [PATCH] winedbg: Add support for hardware watchpoints. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: RĂ©mi Bernon Signed-off-by: Alexandre Julliard --- programs/winedbg/gdbproxy.c | 186 ++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/programs/winedbg/gdbproxy.c b/programs/winedbg/gdbproxy.c index 8e1267a0183..81fc6489851 100644 --- a/programs/winedbg/gdbproxy.c +++ b/programs/winedbg/gdbproxy.c @@ -67,6 +67,17 @@ WINE_DEFAULT_DEBUG_CHANNEL(winedbg); +struct gdb_xpoint +{ + struct list entry; + int pid; + int tid; + enum be_xpoint_type type; + void *addr; + int size; + unsigned long value; +}; + struct gdb_context { /* gdb information */ @@ -86,6 +97,7 @@ struct gdb_context /* generic GDB thread information */ int exec_tid; /* tid used in step & continue */ int other_tid; /* tid to be used in any other operation */ + struct list xpoint_list; /* current Win32 trap env */ DEBUG_EVENT de; DWORD de_reply; @@ -98,6 +110,64 @@ struct gdb_context BOOL no_ack_mode; }; +static void gdbctx_delete_xpoint(struct gdb_context *gdbctx, struct dbg_thread *thread, + dbg_ctx_t *ctx, struct gdb_xpoint *x) +{ + struct dbg_process *process = thread->process; + struct backend_cpu *cpu = process->be_cpu; + + if (!cpu->remove_Xpoint(process->handle, process->process_io, ctx, x->type, x->addr, x->value, x->size)) + ERR("%04x:%04x: Couldn't remove breakpoint at:%p/%x type:%d\n", process->pid, thread->tid, x->addr, x->size, x->type); + + list_remove(&x->entry); + HeapFree(GetProcessHeap(), 0, x); +} + +static void gdbctx_insert_xpoint(struct gdb_context *gdbctx, struct dbg_thread *thread, + dbg_ctx_t *ctx, enum be_xpoint_type type, void *addr, int size) +{ + struct dbg_process *process = thread->process; + struct backend_cpu *cpu = process->be_cpu; + struct gdb_xpoint *x; + unsigned long value; + + if (!cpu->insert_Xpoint(process->handle, process->process_io, ctx, type, addr, &value, size)) + { + ERR("%04x:%04x: Couldn't insert breakpoint at:%p/%x type:%d\n", process->pid, thread->tid, addr, size, type); + return; + } + + if (!(x = HeapAlloc(GetProcessHeap(), 0, sizeof(struct gdb_xpoint)))) + { + ERR("%04x:%04x: Couldn't allocate memory for breakpoint at:%p/%x type:%d\n", process->pid, thread->tid, addr, size, type); + return; + } + + x->pid = process->pid; + x->tid = thread->tid; + x->type = type; + x->addr = addr; + x->size = size; + x->value = value; + list_add_head(&gdbctx->xpoint_list, &x->entry); +} + +static struct gdb_xpoint *gdb_find_xpoint(struct gdb_context *gdbctx, struct dbg_thread *thread, + enum be_xpoint_type type, void *addr, int size) +{ + struct gdb_xpoint *x; + + LIST_FOR_EACH_ENTRY(x, &gdbctx->xpoint_list, struct gdb_xpoint, entry) + { + if (thread && (x->pid != thread->process->pid || x->tid != thread->tid)) + continue; + if (x->type == type && x->addr == addr && x->size == size) + return x; + } + + return NULL; +} + static BOOL tgt_process_gdbproxy_read(HANDLE hProcess, const void* addr, void* buffer, SIZE_T len, SIZE_T* rlen) { @@ -775,6 +845,34 @@ static inline void packet_reply_register_hex_to(struct gdb_context* gdbctx, dbg_ * =============================================== * */ +static void packet_reply_status_xpoints(struct gdb_context* gdbctx, struct dbg_thread *thread, + dbg_ctx_t *ctx) +{ + struct dbg_process *process = thread->process; + struct backend_cpu *cpu = process->be_cpu; + struct gdb_xpoint *x; + + LIST_FOR_EACH_ENTRY(x, &gdbctx->xpoint_list, struct gdb_xpoint, entry) + { + if (x->pid != process->pid || x->tid != thread->tid) + continue; + if (!cpu->is_watchpoint_set(ctx, x->value)) + continue; + if (x->type == be_xpoint_watch_write) + { + packet_reply_add(gdbctx, "watch:"); + packet_reply_val(gdbctx, (unsigned long)x->addr, sizeof(x->addr)); + packet_reply_add(gdbctx, ";"); + } + if (x->type == be_xpoint_watch_read) + { + packet_reply_add(gdbctx, "rwatch:"); + packet_reply_val(gdbctx, (unsigned long)x->addr, sizeof(x->addr)); + packet_reply_add(gdbctx, ";"); + } + } +} + static enum packet_return packet_reply_status(struct gdb_context* gdbctx) { struct dbg_process *process = gdbctx->process; @@ -800,6 +898,7 @@ static enum packet_return packet_reply_status(struct gdb_context* gdbctx) packet_reply_add(gdbctx, "thread:"); packet_reply_val(gdbctx, gdbctx->de.dwThreadId, 4); packet_reply_add(gdbctx, ";"); + packet_reply_status_xpoints(gdbctx, thread, &ctx); for (i = 0; i < backend->gdb_num_regs; i++) { @@ -924,6 +1023,90 @@ static enum packet_return packet_continue_signal(struct gdb_context* gdbctx) return packet_reply_status(gdbctx); } +static enum packet_return packet_delete_breakpoint(struct gdb_context* gdbctx) +{ + struct dbg_process *process = gdbctx->process; + struct dbg_thread *thread; + struct backend_cpu *cpu; + struct gdb_xpoint *x; + dbg_ctx_t ctx; + char type; + void *addr; + int size; + + if (!process) return packet_error; + if (!(cpu = process->be_cpu)) return packet_error; + + if (sscanf(gdbctx->in_packet, "%c,%p,%x", &type, &addr, &size) < 3) + return packet_error; + + if (type == '0') + return packet_error; + + LIST_FOR_EACH_ENTRY(thread, &process->threads, struct dbg_thread, entry) + { + if (!cpu->get_context(thread->handle, &ctx)) + continue; + if ((type == '1') && (x = gdb_find_xpoint(gdbctx, thread, be_xpoint_watch_exec, addr, size))) + gdbctx_delete_xpoint(gdbctx, thread, &ctx, x); + if ((type == '2' || type == '4') && (x = gdb_find_xpoint(gdbctx, thread, be_xpoint_watch_read, addr, size))) + gdbctx_delete_xpoint(gdbctx, thread, &ctx, x); + if ((type == '3' || type == '4') && (x = gdb_find_xpoint(gdbctx, thread, be_xpoint_watch_write, addr, size))) + gdbctx_delete_xpoint(gdbctx, thread, &ctx, x); + cpu->set_context(thread->handle, &ctx); + } + + while ((type == '1') && (x = gdb_find_xpoint(gdbctx, NULL, be_xpoint_watch_exec, addr, size))) + gdbctx_delete_xpoint(gdbctx, NULL, NULL, x); + while ((type == '2' || type == '4') && (x = gdb_find_xpoint(gdbctx, NULL, be_xpoint_watch_read, addr, size))) + gdbctx_delete_xpoint(gdbctx, NULL, NULL, x); + while ((type == '3' || type == '4') && (x = gdb_find_xpoint(gdbctx, NULL, be_xpoint_watch_write, addr, size))) + gdbctx_delete_xpoint(gdbctx, NULL, NULL, x); + + return packet_ok; +} + +static enum packet_return packet_insert_breakpoint(struct gdb_context* gdbctx) +{ + struct dbg_process *process = gdbctx->process; + struct dbg_thread *thread; + struct backend_cpu *cpu; + dbg_ctx_t ctx; + char type; + void *addr; + int size; + + if (!process) return packet_error; + if (!(cpu = process->be_cpu)) return packet_error; + + if (memchr(gdbctx->in_packet, ';', gdbctx->in_packet_len)) + { + FIXME("breakpoint commands not supported\n"); + return packet_error; + } + + if (sscanf(gdbctx->in_packet, "%c,%p,%x", &type, &addr, &size) < 3) + return packet_error; + + if (type == '0') + return packet_error; + + LIST_FOR_EACH_ENTRY(thread, &process->threads, struct dbg_thread, entry) + { + if (!cpu->get_context(thread->handle, &ctx)) + continue; + if (type == '1') + gdbctx_insert_xpoint(gdbctx, thread, &ctx, be_xpoint_watch_exec, addr, size); + if (type == '2' || type == '4') + gdbctx_insert_xpoint(gdbctx, thread, &ctx, be_xpoint_watch_read, addr, size); + if (type == '3' || type == '4') + gdbctx_insert_xpoint(gdbctx, thread, &ctx, be_xpoint_watch_write, addr, size); + cpu->set_context(thread->handle, &ctx); + } + + return packet_ok; +} + static enum packet_return packet_detach(struct gdb_context* gdbctx) { detach_debuggee(gdbctx, FALSE); @@ -1815,6 +1998,8 @@ static struct packet_entry packet_entries[] = {'s', packet_step}, {'T', packet_thread_alive}, {'v', packet_verbose}, + {'z', packet_delete_breakpoint}, + {'Z', packet_insert_breakpoint}, }; static BOOL extract_packets(struct gdb_context* gdbctx) @@ -2069,6 +2254,7 @@ static BOOL gdb_init_context(struct gdb_context* gdbctx, unsigned flags, unsigne gdbctx->exec_tid = -1; gdbctx->other_tid = -1; + list_init(&gdbctx->xpoint_list); gdbctx->last_sig = 0; gdbctx->in_trap = FALSE; gdbctx->process = NULL;