GDB remote debugging
Ove Kaaven
ovehk at ping.uio.no
Wed May 9 16:12:55 CDT 2001
For any sufficiently disillusioned Wine hackers, here's my current
experimental code for letting GDB attach to a Wine process. It's
reasonably featured (exceptions are caught, changing threads work,
single-stepping works, software breakpoints work, you can do backtraces,
change registers and variables, etc). You can also do "info shared" to get
a list of loaded .sos, and use the "sharedlibrary" command to load their
symbols, but I strongly recommend against loading symbols for
libpthread.so, or you'll destroy the neat "info thread" display.
This code has helped me quite a bit in my debugging, actually, when
winedbg couldn't be used...
Apply autoconf after patching.
Engage with ./configure --enable-remote-gdb
I assume Alexandre isn't going to like some of the stuff in here, but I
suppose it's up to him whether to take it or leave it...
Log:
Ove Kaaven <ovek at transgaming.com>
Experimental GDB remote debugging feature for Wine.
Index: configure.in
===================================================================
RCS file: /cvsroot/winex/wine/configure.in,v
retrieving revision 1.1.1.19
retrieving revision 1.30
diff -u -r1.1.1.19 -r1.30
--- configure.in 2001/05/09 18:39:02 1.1.1.19
+++ configure.in 2001/05/09 18:50:39 1.30
@@ -12,6 +12,7 @@
LIBEXT=so # library type .so or .a
TRACE_MSGS=yes # the TRACE() macro
DEBUG_MSGS=yes # the TRACE(), WARN(), and FIXME() macros.
+GDB_REMOTE=no
CURSES=yes
OPENGL=normal
@@ -27,6 +28,10 @@
[ --disable-trace compile out TRACE messages],
[if test "$enableval" = "no"; then TRACE_MSGS="no"; fi])
+AC_ARG_ENABLE(remote-gdb,
+[ --enable-remote-gdb allow debugging with gdb in remote mode],
+[if test "$enableval" = "yes"; then GDB_REMOTE="yes"; fi])
+
AC_ARG_WITH(curses,
[ --without-curses do not use curses],
[if test "$withval" = "no"; then CURSES="no"; fi])
@@ -45,6 +50,11 @@
then
AC_DEFINE(NO_TRACE_MSGS)
fi
+fi
+
+if test "$GDB_REMOTE" = "yes"
+then
+ AC_DEFINE(ENABLE_GDB_REMOTE)
fi
dnl **** Check for some programs ****
Index: include/acconfig.h
===================================================================
RCS file: /cvsroot/winex/wine/include/acconfig.h,v
retrieving revision 1.1.1.10
retrieving revision 1.12
diff -u -r1.1.1.10 -r1.12
--- include/acconfig.h 2001/05/01 05:45:06 1.1.1.10
+++ include/acconfig.h 2001/05/01 05:57:52 1.12
@@ -57,6 +57,9 @@
/* Define if TRACE messages are to be compiled out */
#undef NO_TRACE_MSGS
+/* Define to enable attaching gdb in remote mode */
+#undef ENABLE_GDB_REMOTE
+
/* Define if the struct statfs has the member bavail */
#undef STATFS_HAS_BAVAIL
Index: include/config.h.in
===================================================================
RCS file: /cvsroot/winex/wine/include/config.h.in,v
retrieving revision 1.1.1.13
retrieving revision 1.16
diff -u -r1.1.1.13 -r1.16
--- include/config.h.in 2001/05/01 05:45:12 1.1.1.13
+++ include/config.h.in 2001/05/01 05:57:52 1.16
@@ -97,6 +97,9 @@
/* Define if TRACE messages are to be compiled out */
#undef NO_TRACE_MSGS
+/* Define to enable attaching gdb in remote mode */
+#undef ENABLE_GDB_REMOTE
+
/* Define if the struct statfs has the member bavail */
#undef STATFS_HAS_BAVAIL
Index: server/Makefile.in
===================================================================
RCS file: /cvsroot/winex/wine/server/Makefile.in,v
retrieving revision 1.1.1.3
retrieving revision 1.2
diff -u -r1.1.1.3 -r1.2
--- server/Makefile.in 2000/12/06 15:53:07 1.1.1.3
+++ server/Makefile.in 2001/04/15 00:16:41 1.2
@@ -16,6 +16,7 @@
device.c \
event.c \
file.c \
+ gdbremote.c \
handle.c \
main.c \
mapping.c \
Index: server/context_i386.c
===================================================================
RCS file: /cvsroot/winex/wine/server/context_i386.c,v
retrieving revision 1.1.1.4
retrieving revision 1.3
diff -u -r1.1.1.4 -r1.3
--- server/context_i386.c 2001/05/01 05:48:13 1.1.1.4
+++ server/context_i386.c 2001/05/01 05:57:52 1.3
@@ -88,7 +88,7 @@
}
/* retrieve a thread context */
-static void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
+void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
{
int pid = thread->unix_pid;
if (flags & CONTEXT_FULL)
@@ -144,7 +144,7 @@
/* set a thread context */
-static void set_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
+void set_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
{
int pid = thread->unix_pid;
if (flags & CONTEXT_FULL)
@@ -206,7 +206,7 @@
#elif defined(__sun) || defined(__sun__)
/* retrieve a thread context */
-static void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
+void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
{
int pid = thread->unix_pid;
if (flags & CONTEXT_FULL)
@@ -257,7 +257,7 @@
/* set a thread context */
-static void set_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
+void set_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
{
int pid = thread->unix_pid;
if (flags & CONTEXT_FULL)
@@ -315,7 +315,7 @@
#include <machine/reg.h>
/* retrieve a thread context */
-static void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
+void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
{
int pid = thread->unix_pid;
if (flags & CONTEXT_FULL)
@@ -366,7 +366,7 @@
/* set a thread context */
-static void set_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
+void set_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
{
int pid = thread->unix_pid;
if (flags & CONTEXT_FULL)
Index: server/debugger.c
===================================================================
RCS file: /cvsroot/winex/wine/server/debugger.c,v
retrieving revision 1.1.1.8
retrieving revision 1.2
diff -u -r1.1.1.8 -r1.2
--- server/debugger.c 2001/03/05 22:55:54 1.1.1.8
+++ server/debugger.c 2001/04/22 04:35:16 1.2
@@ -4,6 +4,8 @@
* Copyright (C) 1999 Alexandre Julliard
*/
+#include "config.h"
+
#include <assert.h>
#include <string.h>
#include <stdio.h>
@@ -14,6 +16,7 @@
#include "process.h"
#include "thread.h"
#include "request.h"
+#include "gdbremote.h"
enum debug_event_state { EVENT_QUEUED, EVENT_SENT, EVENT_CONTINUED };
@@ -218,7 +221,13 @@
/* link an event at the end of the queue */
static void link_event( struct debug_event *event )
{
- struct debug_ctx *debug_ctx = event->debugger->debug_ctx;
+ struct debug_ctx *debug_ctx = event->debugger ?
+ event->debugger->debug_ctx :
+#ifdef ENABLE_GDB_REMOTE
+ event->sender->process->gdb_ctx;
+#else
+ NULL;
+#endif
assert( debug_ctx );
grab_object( event );
@@ -291,7 +300,7 @@
}
if (event->sender->context == &event->context) event->sender->context = NULL;
release_object( event->sender );
- release_object( event->debugger );
+ if (event->debugger) release_object( event->debugger );
}
static void debug_ctx_dump( struct object *obj, int verbose )
@@ -320,10 +329,16 @@
}
/* continue a debug event */
-static int continue_debug_event( struct process *process, struct thread *thread, int status )
+int continue_debug_event( struct process *process, struct thread *thread, int status )
{
struct debug_event *event;
- struct debug_ctx *debug_ctx = current->debug_ctx;
+ struct debug_ctx *debug_ctx = current ?
+ current->debug_ctx :
+#ifdef ENABLE_GDB_REMOTE
+ process->gdb_ctx;
+#else
+ NULL;
+#endif
if (!debug_ctx || process->debugger != current || thread->process != process) goto error;
@@ -359,7 +374,7 @@
assert( code > 0 && code <= NB_DEBUG_EVENTS );
/* cannot queue a debug event for myself */
- assert( debugger->process != thread->process );
+ assert( !debugger || debugger->process != thread->process );
/* build the event */
if (!(event = alloc_object( &debug_event_ops, -1 ))) return NULL;
@@ -367,7 +382,7 @@
event->prev = NULL;
event->state = EVENT_QUEUED;
event->sender = (struct thread *)grab_object( thread );
- event->debugger = (struct thread *)grab_object( debugger );
+ event->debugger = debugger ? (struct thread *)grab_object( debugger ) : NULL;
event->data.code = code;
if (!fill_debug_event[code-1]( event, arg ))
@@ -482,6 +497,44 @@
}
}
+#ifdef ENABLE_GDB_REMOTE
+int create_gdb_ctx( struct process *process )
+{
+ struct debug_ctx *debug_ctx;
+
+ assert( !process->debugger );
+
+ if (!process->gdb_ctx) /* need to allocate a context */
+ {
+ if (!(debug_ctx = alloc_object( &debug_ctx_ops, -1 ))) return 0;
+ debug_ctx->event_head = NULL;
+ debug_ctx->event_tail = NULL;
+ process->gdb_ctx = debug_ctx;
+ }
+ return 1;
+}
+
+void destroy_gdb_ctx( struct process *process )
+{
+ release_object( process->gdb_ctx );
+ process->gdb_ctx = NULL;
+}
+
+void send_gdb_events( struct process *process )
+{
+ struct debug_event *event;
+
+ assert( !process->debugger );
+ if ((event = find_event_to_send( process->gdb_ctx )))
+ {
+ event->state = EVENT_SENT;
+ event->sender->debug_event = event;
+ recv_gdb_event( process->gdb_socket, event->sender,
+ &event->data, &event->context );
+ }
+}
+#endif
+
/* Wait for a debug event */
DECL_HANDLER(wait_debug_event)
{
@@ -558,7 +611,11 @@
DECL_HANDLER(queue_exception_event)
{
req->handle = 0;
- if (current->process->debugger)
+ if (current->process->debugger
+#ifdef ENABLE_GDB_REMOTE
+ || current->process->gdb_ctx
+#endif
+ )
{
struct debug_event_exception data;
struct debug_event *event;
@@ -581,6 +638,10 @@
}
release_object( event );
}
+#ifdef ENABLE_GDB_REMOTE
+ if (!current->process->debugger)
+ notify_gdb_event( current->process->gdb_socket );
+#endif
}
}
Index: server/gdbremote.c
===================================================================
RCS file: gdbremote.c
diff -N gdbremote.c
--- /dev/null Mon Dec 11 17:26:27 2000
+++ /tmp/cvsiCeLj3 Wed May 9 13:35:56 2001
@@ -0,0 +1,1066 @@
+/*
+ * GDB remote debugging request handling
+ *
+ * Copyright (C) 2001 Ove Kåven (based on request.c by Alexandre Julliard)
+ */
+
+#include "config.h"
+
+#ifdef ENABLE_GDB_REMOTE
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#include <unistd.h>
+
+#include "winnt.h"
+#include "winbase.h"
+#include "wincon.h"
+#include "thread.h"
+#include "process.h"
+#include "server.h"
+#include "request.h"
+#include "wine/port.h"
+
+#define BUFMAX 400
+
+#ifdef __i386__
+#define STEP_FLAG 0x00000100
+#endif
+
+#define STATE_RUNNING 0
+#define STATE_SUSPENDED 1
+#define STATE_TRAPPED 2
+#define STATE_DETACHING 3
+
+#define STEP_FREE 0
+#define STEP_TRACE_1 1
+#define STEP_TRACE_2 2
+
+int create_suspended = 0;
+
+struct remote_gdb
+{
+ struct object obj; /* object header */
+ struct process *process; /* process to debug */
+ int state, step; /* debug state */
+ char inbuf[BUFMAX]; /* incoming packet */
+ char outbuf[BUFMAX]; /* outbound packet */
+ unsigned int inpos, outpos;
+ unsigned int inlen, outlen;
+ unsigned int outpk;
+ int last_sig, is_first, is_fatal;
+ struct thread *run_thread, *dbg_thread, *q_thread;
+};
+
+static void remote_gdb_dump( struct object *obj, int verbose );
+static void remote_gdb_poll_event( struct object *obj, int event );
+static void remote_gdb_destroy( struct object *obj );
+
+static const struct object_ops remote_gdb_ops =
+{
+ sizeof(struct remote_gdb), /* size */
+ remote_gdb_dump, /* dump */
+ no_add_queue, /* add_queue */
+ NULL, /* remove_queue */
+ NULL, /* signaled */
+ NULL, /* satisfied */
+ NULL, /* get_poll_events */
+ remote_gdb_poll_event, /* poll_event */
+ no_get_fd, /* get_fd */
+ no_flush, /* flush */
+ no_get_file_info, /* get_file_info */
+ remote_gdb_destroy /* destroy */
+};
+
+static void gdb_socket_detach( struct gdb_socket *sock, struct remote_gdb *gdb );
+
+static const char hexd[16]="0123456789abcdef";
+
+/* from context_i386.c */
+extern void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context );
+extern void set_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context );
+
+/* from debugger.c */
+extern int continue_debug_event( struct process *process, struct thread *thread, int status );
+extern int create_gdb_ctx( struct process *process );
+extern void destroy_gdb_ctx( struct process *process );
+extern void send_gdb_events( struct process *process );
+
+static inline int dhex(unsigned char ch)
+{
+ if (ch >= '0' && ch <= '9') return ch - '0';
+ if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
+ if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
+ return -1;
+}
+
+static void memhex(const char*mem, char*hex, unsigned int count)
+{
+ unsigned int c;
+ for (c=0; c<count; c++) {
+ unsigned char ch = mem[c];
+ *hex++ = hexd[ch >> 4];
+ *hex++ = hexd[ch & 0xf];
+ }
+}
+
+static void hexmem(const char*hex, char*mem, unsigned int count)
+{
+ unsigned int c;
+ for (c=0; c<count; c++) {
+ unsigned ch = dhex(*hex++) << 4;
+ ch += dhex(*hex++);
+ mem[c] = ch;
+ }
+}
+
+static void whex(unsigned long m, char*hex, unsigned int count)
+{
+ unsigned int c, s = (count-1)*4;
+ for (c=0; c<count; c++,s-=4)
+ *hex++ = hexd[(m >> s) & 0xf];
+}
+
+static unsigned long rhex(const char*hex, unsigned int count)
+{
+ unsigned long m = 0;
+ unsigned int c;
+ for (c=0; c<count; c++) {
+ int ch = dhex(*hex++);
+ if (ch == -1) return m;
+ m = (m << 4) | ch;
+ }
+ return m;
+}
+
+static void start_gdb_packet( struct remote_gdb *gdb )
+{
+ gdb->outbuf[gdb->outlen++] = '$';
+ gdb->outpk = gdb->outlen;
+}
+
+static void send_gdb_packet( struct remote_gdb *gdb )
+{
+ /* compute checksum */
+ unsigned int c;
+ char cksum = 0;
+ for (c=gdb->outpk; c<gdb->outlen; c++) cksum += gdb->outbuf[c];
+ gdb->outbuf[gdb->outlen++] = '#';
+ memhex(&cksum, gdb->outbuf + gdb->outlen, 1);
+ gdb->outlen+=2;
+#ifdef COMMAND_DEBUG
+ fprintf(stderr, "reply: %*.*s\n", (int)(gdb->outlen-gdb->outpk)+1, (int)(gdb->outlen-gdb->outpk)+1, gdb->outbuf+gdb->outpk-1);
+#endif
+ if (gdb->outpk == 1) set_select_events( &gdb->obj, POLLIN | POLLOUT );
+}
+
+static void send_status_reply( struct remote_gdb *gdb, int e )
+{
+ start_gdb_packet( gdb );
+ if (e > 0) {
+ gdb->outbuf[gdb->outlen++] = 'E';
+ whex(e, gdb->outbuf + gdb->outlen, 2);
+ gdb->outlen += 2;
+ }
+ else if (e == 0) {
+ gdb->outbuf[gdb->outlen++] = 'O';
+ gdb->outbuf[gdb->outlen++] = 'K';
+ }
+ send_gdb_packet( gdb );
+}
+
+static void send_signal_reply( struct remote_gdb *gdb )
+{
+ start_gdb_packet( gdb );
+ gdb->outbuf[gdb->outlen++] = 'T';
+ whex(gdb->last_sig, gdb->outbuf + gdb->outlen, 2);
+ gdb->outlen += 2;
+ strcpy(&gdb->outbuf[gdb->outlen], "thread:");
+ gdb->outlen += strlen("thread:");
+ whex(gdb->dbg_thread->unix_pid, gdb->outbuf + gdb->outlen, 4);
+ gdb->outlen += 4;
+ /* FIXME: also give gdb eip, esp, and ebp, like gdbserver does */
+ gdb->outbuf[gdb->outlen++] = ';';
+ send_gdb_packet( gdb );
+}
+
+static struct thread *find_thread( struct remote_gdb *gdb, char *data )
+{
+ struct thread *thread;
+ if (strcmp(data, "-1") == 0) {
+ /* "all threads" */
+ thread = NULL;
+ }
+ else if (strcmp(data, "0") == 0) {
+ /* "pick any thread", pick the first thread,
+ * which is what most people are interested in */
+ thread = gdb->process->thread_list;
+ if (!thread) return thread;
+ while (thread->proc_next)
+ thread = thread->proc_next;
+ }
+ else {
+ unsigned pid = rhex(data, 8);
+ thread = gdb->process->thread_list;
+ while (thread && thread->unix_pid != pid)
+ thread = thread->proc_next;
+ }
+ return thread;
+}
+
+static void set_thread( struct remote_gdb *gdb, char t, char *data )
+{
+ struct thread *thread = find_thread( gdb, data );
+ switch (t) {
+ case 'c': /* run thread */
+ if (strcmp(data, "-1") == 0) gdb->run_thread = NULL; /* all threads */
+ else if (thread) gdb->run_thread = thread;
+ else fprintf(stderr, "unknown run thread: %s\n", data);
+ break;
+ case 'g': /* debug thread */
+ if (thread) gdb->dbg_thread = thread;
+ else fprintf(stderr, "unknown debug thread: %s\n", data);
+ break;
+ }
+ send_status_reply( gdb, 0 );
+}
+
+static void check_gdb_event( struct remote_gdb *gdb )
+{
+ send_gdb_events( gdb->process );
+}
+
+static int init_step( struct remote_gdb *gdb )
+{
+ int ret = STEP_FREE;
+#ifdef __i386__
+#ifdef RUN_DEBUG
+ fprintf(stderr, "initiating single stepping\n");
+#endif
+ if (gdb->dbg_thread->context)
+ {
+ if (gdb->dbg_thread->context->EFlags & STEP_FLAG) {
+ ret = STEP_TRACE_2;
+ } else {
+ ret = STEP_TRACE_1;
+ gdb->dbg_thread->context->EFlags |= STEP_FLAG;
+ }
+ }
+ else if (suspend_for_ptrace( gdb->dbg_thread ))
+ {
+ CONTEXT context;
+ get_thread_context( gdb->dbg_thread, CONTEXT_CONTROL, &context );
+ if (context.EFlags & STEP_FLAG) {
+ ret = STEP_TRACE_2;
+ } else {
+ ret = STEP_TRACE_1;
+ context.EFlags |= STEP_FLAG;
+ set_thread_context( gdb->dbg_thread, CONTEXT_CONTROL, &context );
+ }
+ resume_thread( gdb->dbg_thread );
+ }
+ else fprintf(stderr, "cannot single step\n");
+#endif
+ return ret;
+}
+
+static void clean_step( struct remote_gdb *gdb )
+{
+#ifdef __i386__
+ if (gdb->step != STEP_TRACE_1) return;
+#ifdef RUN_DEBUG
+ fprintf(stderr, "clearing single stepping\n");
+#endif
+ if (gdb->dbg_thread->context)
+ {
+ gdb->dbg_thread->context->EFlags &= ~STEP_FLAG;
+ }
+ else if (suspend_for_ptrace( gdb->dbg_thread ))
+ {
+ CONTEXT context;
+ get_thread_context( gdb->dbg_thread, CONTEXT_CONTROL, &context );
+ if (context.EFlags & STEP_FLAG) {
+ context.EFlags &= ~STEP_FLAG;
+ set_thread_context( gdb->dbg_thread, CONTEXT_CONTROL, &context );
+ }
+ resume_thread( gdb->dbg_thread );
+ }
+ else fprintf(stderr, "cannot clear single step\n");
+#endif
+}
+
+static void continue_signal( struct remote_gdb *gdb, char *data, int step )
+{
+ int nstep = STEP_FREE;
+ char*sdata = strtok(data, ";");
+ char*adata = strtok(NULL, "");
+ if (*sdata) {
+ int sig = rhex(sdata, 2);
+ if (sig != gdb->last_sig)
+ fprintf(stderr, "can't resume signal %d\n", sig);
+ }
+ if (adata && *adata) {
+ unsigned long addr = rhex(adata, 8);
+ fprintf(stderr, "FIXME: resume at addr %08lx\n", addr);
+ }
+
+ if (step) nstep = init_step( gdb );
+
+ switch (gdb->state) {
+ case STATE_SUSPENDED:
+#ifdef RUN_DEBUG
+ fprintf(stderr, "resuming from suspension\n");
+#endif
+ fprintf(stderr, "can't resume signal %d\n", gdb->last_sig);
+ gdb->state = STATE_RUNNING;
+ gdb->step = nstep;
+ resume_process( gdb->process );
+ break;
+ case STATE_TRAPPED:
+#ifdef RUN_DEBUG
+ fprintf(stderr, "continuing from exception fault\n");
+#endif
+ gdb->state = STATE_RUNNING;
+ gdb->step = nstep;
+ continue_debug_event( gdb->process, gdb->dbg_thread, DBG_EXCEPTION_NOT_HANDLED );
+ break;
+ }
+
+ /* check for other pending exceptions */
+ check_gdb_event( gdb );
+}
+
+static void continue_exec( struct remote_gdb *gdb, char *data, int step )
+{
+ int ostep = gdb->step, nstep = STEP_FREE;
+ if (*data) {
+ unsigned long addr = rhex(data, 8);
+ fprintf(stderr, "FIXME: resume at addr %08lx\n", addr);
+ }
+
+ if (step) nstep = init_step( gdb );
+
+ switch (gdb->state) {
+ case STATE_SUSPENDED:
+#ifdef RUN_DEBUG
+ fprintf(stderr, "resuming from suspension\n");
+#endif
+ gdb->state = STATE_RUNNING;
+ gdb->step = nstep;
+ resume_process( gdb->process );
+ break;
+ case STATE_TRAPPED:
+#ifdef RUN_DEBUG
+ fprintf(stderr, "continuing from exception fault\n");
+#endif
+ gdb->state = STATE_RUNNING;
+ gdb->step = nstep;
+ continue_debug_event( gdb->process, gdb->dbg_thread, (ostep == STEP_TRACE_2) ?
+ DBG_EXCEPTION_NOT_HANDLED :
+ DBG_CONTINUE );
+ break;
+ }
+
+ /* check for other pending exceptions */
+ check_gdb_event( gdb );
+}
+
+static void detach_exec( struct remote_gdb *gdb )
+{
+ do {
+ switch (gdb->state) {
+ case STATE_SUSPENDED:
+ resume_process( gdb->process );
+ break;
+ case STATE_TRAPPED:
+ if (gdb->last_sig == SIGTRAP)
+ continue_debug_event( gdb->process, gdb->dbg_thread, DBG_CONTINUE );
+ else
+ continue_debug_event( gdb->process, gdb->dbg_thread, DBG_EXCEPTION_NOT_HANDLED );
+ break;
+ }
+
+ /* kill remaining pending exceptions */
+ gdb->state = STATE_DETACHING;
+ check_gdb_event( gdb );
+ } while (gdb->state == STATE_TRAPPED);
+}
+
+static void read_regs( struct remote_gdb *gdb )
+{
+ CONTEXT context;
+ if (gdb->dbg_thread->context)
+ {
+ context = *gdb->dbg_thread->context;
+ }
+ else if (suspend_for_ptrace( gdb->dbg_thread ))
+ {
+ get_thread_context( gdb->dbg_thread, CONTEXT_FULL, &context );
+ resume_thread( gdb->dbg_thread );
+ }
+#ifdef __i386__
+ {
+ unsigned regs[16];
+ regs[0] = context.Eax;
+ regs[1] = context.Ecx;
+ regs[2] = context.Edx;
+ regs[3] = context.Ebx;
+ regs[4] = context.Esp;
+ regs[5] = context.Ebp;
+ regs[6] = context.Esi;
+ regs[7] = context.Edi;
+ regs[8] = context.Eip;
+ regs[9] = context.EFlags;
+ regs[10] = context.SegCs;
+ regs[11] = context.SegSs;
+ regs[12] = context.SegDs;
+ regs[13] = context.SegEs;
+ regs[14] = context.SegFs;
+ regs[15] = context.SegGs;
+
+ start_gdb_packet( gdb );
+ memhex((char*)®s, gdb->outbuf + gdb->outlen, sizeof(regs));
+ gdb->outlen += sizeof(regs)*2;
+ send_gdb_packet( gdb );
+ }
+#endif
+}
+
+static void write_regs( struct remote_gdb *gdb, char *data )
+{
+ CONTEXT context;
+#ifdef __i386__
+ {
+ unsigned regs[16];
+ hexmem(data, (char*)®s, sizeof(regs));
+
+ context.Eax = regs[0];
+ context.Ecx = regs[1];
+ context.Edx = regs[2];
+ context.Ebx = regs[3];
+ context.Esp = regs[4];
+ context.Ebp = regs[5];
+ context.Esi = regs[6];
+ context.Edi = regs[7];
+ context.Eip = regs[8];
+ context.EFlags = regs[9];
+ context.SegCs = regs[10];
+ context.SegSs = regs[11];
+ context.SegDs = regs[12];
+ context.SegEs = regs[13];
+ context.SegFs = regs[14];
+ context.SegGs = regs[15];
+ }
+#endif
+ if (gdb->dbg_thread->context)
+ {
+ *gdb->dbg_thread->context = context;
+ }
+ else if (suspend_for_ptrace( gdb->dbg_thread ))
+ {
+ set_thread_context( gdb->dbg_thread, CONTEXT_FULL, &context );
+ resume_thread( gdb->dbg_thread );
+ }
+ send_status_reply( gdb, 0 );
+}
+
+static void read_memory( struct remote_gdb *gdb, char *data )
+{
+ void *addr;
+ unsigned long len, ilen;
+ if (sscanf(data, "%p,%lx", &addr, &len) != 2)
+ {
+ send_status_reply( gdb, 1 );
+ return;
+ }
+ start_gdb_packet( gdb );
+ if (suspend_for_ptrace( gdb->dbg_thread ))
+ {
+ unsigned int tmp;
+ while (len > 0)
+ {
+ ilen = (len > sizeof(int)) ? sizeof(int) : len;
+ if (read_thread_int( gdb->dbg_thread, addr, &tmp ) == -1) break;
+ memhex((char*)&tmp, gdb->outbuf + gdb->outlen, ilen);
+ gdb->outlen += ilen*2;
+ addr += ilen;
+ len -= ilen;
+ }
+ resume_thread( gdb->dbg_thread );
+ }
+ send_gdb_packet( gdb );
+}
+
+static void write_memory( struct remote_gdb *gdb, char *data )
+{
+ void *addr;
+ unsigned long len, ilen;
+ char *mdata;
+ if (sscanf(strtok(data, ":"), "%p,%lx", &addr, &len) != 2)
+ {
+ send_status_reply( gdb, 1 );
+ return;
+ }
+ mdata = strtok(NULL, "");
+ if (suspend_for_ptrace( gdb->dbg_thread ))
+ {
+ unsigned int tmp;
+ while (len > 0)
+ {
+ ilen = (len > sizeof(int)) ? sizeof(int) : len;
+ if (ilen < sizeof(int))
+ if (read_thread_int( gdb->dbg_thread, addr, &tmp ) == -1) break;
+ hexmem(mdata, (char*)&tmp, ilen);
+ if (write_thread_int( gdb->dbg_thread, addr, tmp, ~0 ) == -1) break;
+ mdata += ilen*2;
+ addr += ilen;
+ len -= ilen;
+ }
+ resume_thread( gdb->dbg_thread );
+ send_status_reply( gdb, 0 );
+ }
+ else send_status_reply( gdb, 1 );
+}
+
+static void answer_query( struct remote_gdb *gdb, char *data )
+{
+ char *cmd;
+ cmd = strtok(data, ",");
+ if (strcmp(cmd, "C") == 0) { /* get current thread id */
+ start_gdb_packet( gdb );
+ gdb->outbuf[gdb->outlen++] = 'Q';
+ gdb->outbuf[gdb->outlen++] = 'C';
+ whex(gdb->dbg_thread->unix_pid, gdb->outbuf + gdb->outlen, 4);
+ gdb->outlen += 4;
+ send_gdb_packet( gdb );
+ return;
+ }
+ else if (strcmp(cmd, "fThreadInfo") == 0) { /* get first thread id */
+ assert( gdb->state != STATE_RUNNING );
+ gdb->q_thread = gdb->process->thread_list;
+ start_gdb_packet( gdb );
+ gdb->outbuf[gdb->outlen++] = 'm';
+ whex(gdb->q_thread->unix_pid, gdb->outbuf + gdb->outlen, 4);
+ gdb->outlen += 4;
+ send_gdb_packet( gdb );
+ }
+ else if (strcmp(cmd, "sThreadInfo") == 0) { /* get next thread id */
+ assert( gdb->state != STATE_RUNNING );
+ gdb->q_thread = gdb->q_thread->proc_next;
+ start_gdb_packet( gdb );
+ if (gdb->q_thread) {
+ gdb->outbuf[gdb->outlen++] = 'm';
+ whex(gdb->q_thread->unix_pid, gdb->outbuf + gdb->outlen, 4);
+ gdb->outlen += 4;
+ }
+ else gdb->outbuf[gdb->outlen++] = 'l';
+ send_gdb_packet( gdb );
+ }
+ else if (strcmp(cmd, "ThreadExtraInfo") == 0) { /* get thread info */
+ struct thread *thread = find_thread( gdb, strtok(NULL, "") );
+ start_gdb_packet( gdb );
+ if (thread) {
+ char text[128];
+ /* any text is allowed... */
+ sprintf(text, "teb=%p, state=%s", thread->teb,
+ thread->context ? "FAULTED" :
+ thread->suspend ? "SUSPENDED" :
+ thread->state == TERMINATED ? "TERMINATED" : "RUNNING");
+ memhex(text, gdb->outbuf + gdb->outlen, strlen(text));
+ gdb->outlen += strlen(text)*2;
+ }
+ send_gdb_packet( gdb );
+ }
+ else {
+ fprintf(stderr, "unknown query: %s\n", cmd);
+ send_status_reply( gdb, -1 );
+ }
+}
+
+static void process_gdb_packet( struct remote_gdb *gdb )
+{
+ /* compute checksum */
+ unsigned int c;
+ char cksum = 0, ck, *data;
+ for (c=1; c<gdb->inlen; c++) cksum += gdb->inbuf[c];
+ hexmem(gdb->inbuf + gdb->inlen + 1, &ck, 1);
+
+#ifdef COMMAND_DEBUG
+ fprintf(stderr, "command (cks=%02x, ck=%02x): %*.*s\n", cksum, ck, (int)gdb->inlen+3, (int)gdb->inlen+3, gdb->inbuf);
+#endif
+
+ /* null-terminate data */
+ gdb->inbuf[gdb->inlen] = 0;
+ data = gdb->inbuf + 2;
+
+ if (cksum == ck) {
+ /* packet OK */
+ struct thread *old_current = current;
+ gdb->outbuf[gdb->outlen++] = '+';
+ current = NULL;
+ switch (gdb->inbuf[1]) {
+ case '?': /* last signal */
+ send_signal_reply( gdb );
+ break;
+ case 'C': /* continue with signal */
+ continue_signal( gdb, data, 0 );
+ break;
+ case 'D': /* detach */
+ /* don't need to do anything, we detach
+ when the connection is closed anyway */
+ break;
+ case 'G': /* write registers */
+ write_regs( gdb, data );
+ break;
+ case 'H': /* set thread */
+ set_thread( gdb, *data, data+1 );
+ break;
+ case 'M': /* write memory */
+ write_memory( gdb, data );
+ break;
+ case 'S': /* step with signal */
+ continue_signal( gdb, data, 1 );
+ break;
+ case 'T': /* thread still alive */
+ send_status_reply( gdb, find_thread( gdb, data ) ? 0 : 1 );
+ break;
+ case 'c': /* continue running */
+ continue_exec( gdb, data, 0 );
+ break;
+ case 'g': /* read registers */
+ read_regs( gdb );
+ break;
+ case 'm': /* read memory */
+ read_memory( gdb, data );
+ break;
+ case 'q': /* query */
+ answer_query( gdb, data );
+ break;
+ case 's': /* step */
+ continue_exec( gdb, data, 1 );
+ break;
+ default:
+ fprintf(stderr, "unknown gdb packet type %c\n", gdb->inbuf[1]);
+ send_status_reply( gdb, -1 );
+ }
+ current = old_current;
+ }
+ else {
+ /* failed checksum */
+ fprintf(stderr, "bad gdb packet checksum, type=%c\n", gdb->inbuf[1]);
+ gdb->outbuf[gdb->outlen++] = '-';
+ }
+ set_select_events( &gdb->obj, POLLIN | POLLOUT );
+}
+
+static void process_gdb_event( struct remote_gdb *gdb, struct thread *thread,
+ debug_event_t *data, CONTEXT *context )
+{
+ EXCEPTION_RECORD *rec;
+ struct thread *old_current = current;
+
+ assert( data->code == EXCEPTION_DEBUG_EVENT );
+ assert( gdb->state == STATE_RUNNING || gdb->state == STATE_DETACHING );
+
+ rec = &data->info.exception.record;
+
+#ifdef RUN_DEBUG
+ fprintf(stderr, "received exception %08lx\n", rec->ExceptionCode);
+#endif
+
+ current = NULL;
+
+ switch (rec->ExceptionCode) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_PRIV_INSTRUCTION:
+ case EXCEPTION_STACK_OVERFLOW:
+ case EXCEPTION_GUARD_PAGE:
+ gdb->last_sig = SIGSEGV;
+ break;
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ gdb->last_sig = SIGBUS;
+ break;
+ case EXCEPTION_SINGLE_STEP:
+ if (gdb->step == STEP_FREE || thread != gdb->dbg_thread) {
+ /* the app is doing something strange, let it */
+ continue_debug_event( gdb->process, thread, DBG_EXCEPTION_NOT_HANDLED );
+ goto dont_process;
+ }
+#ifdef RUN_DEBUG
+ fprintf(stderr, "completed single stepping\n");
+#endif
+ case EXCEPTION_BREAKPOINT:
+ gdb->last_sig = SIGTRAP;
+ break;
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ gdb->last_sig = SIGFPE;
+ break;
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ case EXCEPTION_INT_OVERFLOW:
+ gdb->last_sig = SIGFPE;
+ break;
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ gdb->last_sig = SIGILL;
+ break;
+ case CONTROL_C_EXIT:
+ gdb->last_sig = SIGINT;
+ break;
+ case EXCEPTION_CRITICAL_SECTION_WAIT:
+ /* grr... let's just keep these at bay so they don't disturb us... */
+ if (thread != gdb->dbg_thread)
+ continue_debug_event( gdb->process, thread, DBG_CONTINUE );
+ goto dont_process;
+ default:
+ fprintf(stderr, "unhandled exception code %08lx\n", rec->ExceptionCode);
+ gdb->last_sig = SIGABRT;
+ break;
+ }
+
+ clean_step( gdb );
+
+ gdb->is_first = data->info.exception.first;
+ gdb->is_fatal = (rec->ExceptionFlags & EH_NONCONTINUABLE) != 0;
+ if (gdb->run_thread) gdb->run_thread = thread;
+ gdb->dbg_thread = thread;
+ if (gdb->state == STATE_RUNNING) send_signal_reply( gdb );
+ gdb->state = STATE_TRAPPED;
+dont_process:
+ current = old_current;
+}
+
+static void exit_gdb_process( struct remote_gdb *gdb )
+{
+ assert( gdb->state == STATE_RUNNING || gdb->state == STATE_DETACHING );
+ if (gdb->state == STATE_RUNNING) {
+ start_gdb_packet( gdb );
+ gdb->outbuf[gdb->outlen++] = 'W';
+ whex(gdb->process->exit_code, gdb->outbuf + gdb->outlen, 2);
+ gdb->outlen += 2;
+ send_gdb_packet( gdb );
+ }
+}
+
+static struct remote_gdb *create_remote_gdb( int fd, struct process *process )
+{
+ struct remote_gdb *gdb;
+
+ if (!(gdb = alloc_object( &remote_gdb_ops, fd ))) return NULL;
+ gdb->process = (struct process *)grab_object( process );
+ gdb->inpos = 0;
+ gdb->outpos = 0;
+ gdb->inlen = 0;
+ gdb->outlen = 0;
+ gdb->outpk = 0;
+
+ fprintf(stderr, "gdb connected on fd %d\n", gdb->obj.fd);
+
+ /* gdb expects a breakpoint when attaching */
+ if (!create_suspended) suspend_process( gdb->process );
+ gdb->state = STATE_SUSPENDED;
+ gdb->step = STEP_FREE;
+ gdb->last_sig = SIGTRAP;
+ gdb->run_thread = NULL;
+ /* find the first thread, which is what most people are interested in */
+ gdb->dbg_thread = gdb->process->thread_list;
+ while (gdb->dbg_thread->proc_next)
+ gdb->dbg_thread = gdb->dbg_thread->proc_next;
+
+ set_select_events( &gdb->obj, POLLIN );
+
+ return gdb;
+}
+
+static void remote_gdb_dump( struct object *obj, int verbose )
+{
+ struct remote_gdb *gdb = (struct remote_gdb *)obj;
+ assert( obj->ops == &remote_gdb_ops );
+ fprintf( stderr, "GDB debug socket fd=%d\n", gdb->obj.fd );
+}
+
+/* handle a socket event */
+static void remote_gdb_poll_event( struct object *obj, int event )
+{
+ struct remote_gdb *gdb = (struct remote_gdb *)obj;
+ assert( obj->ops == &remote_gdb_ops );
+
+ if (event & (POLLERR | POLLHUP)) {
+ fprintf(stderr, "gdb hup\n");
+ release_object( gdb );
+ return;
+ }
+ if (event & POLLIN) {
+ int len = read( gdb->obj.fd, gdb->inbuf + gdb->inpos, BUFMAX - gdb->inpos );
+ if (len > 0) {
+ char *bp, *cp, *ep;
+ int pl = gdb->inlen ? (gdb->inlen + 3) : 0;
+ cp = pl ? (gdb->inbuf + pl) : NULL;
+ ep = NULL;
+#ifdef PACKET_DEBUG
+ fprintf(stderr, "cmd from gdb: %*.*s\n", len, len, gdb->inbuf + gdb->inpos);
+ fprintf(stderr, "i: inpos=%d, inlen=%d, len=%d, pl=%d, buf=%p, cp=%p, ep=%p\n", gdb->inpos, gdb->inlen, len, pl, gdb->inbuf, cp, ep);
+#endif
+ do {
+ /* locate packet begin markers */
+ bp = memchr(gdb->inbuf + gdb->inpos, '$', len);
+ if (!cp) {
+ /* locate packet end markers */
+ ep = memchr(gdb->inbuf + gdb->inpos, '#', len);
+ if (ep) {
+ cp = ep + 3;
+ pl = cp - gdb->inbuf;
+ }
+ }
+ /* discard garbage and incomplete packets */
+ if (bp && bp != gdb->inbuf && (!cp || bp < cp)) {
+ int d = bp - (gdb->inbuf + gdb->inpos);
+#ifdef PACKET_DEBUG
+ fprintf(stderr, "g: inpos=%d, d=%d, len=%d, pl=%d, buf=%p, bp=%p, cp=%p, ep=%p\n", gdb->inpos, d, len, pl, gdb->inbuf, bp, cp, ep);
+#endif
+ len -= d;
+ if (len) memmove(gdb->inbuf, bp, len);
+ gdb->inpos = 0;
+ gdb->inlen = 0;
+ pl = 0;
+ cp = NULL;
+ continue;
+ }
+ /* check packets */
+#ifdef PACKET_DEBUG
+ fprintf(stderr, "c: inpos=%d, inlen=%d, len=%d, pl=%d, buf=%p, bp=%p, cp=%p, ep=%p\n", gdb->inpos, gdb->inlen, len, pl, gdb->inbuf, bp, cp, ep);
+#endif
+ gdb->inpos += len;
+ len = 0;
+ if (ep) {
+ if (pl > BUFMAX) {
+ /* packet too long, abort */
+ gdb->inpos = 0;
+ gdb->inlen = 0;
+ break;
+ }
+ gdb->inlen = ep - gdb->inbuf;
+ assert( pl == (gdb->inlen+3) );
+ }
+ else if (gdb->inpos >= BUFMAX) {
+ /* packet too long, abort */
+ gdb->inpos = 0;
+ gdb->inlen = 0;
+ break;
+ }
+ /* process complete packets */
+#ifdef PACKET_DEBUG
+ fprintf(stderr, "p: inpos=%d, inlen=%d, len=%d, pl=%d, buf=%p, bp=%p, cp=%p, ep=%p\n", gdb->inpos, gdb->inlen, len, pl, gdb->inbuf, bp, cp, ep);
+#endif
+ if (pl && gdb->inpos >= pl) {
+ assert( gdb->inbuf[0] == '$' );
+ process_gdb_packet( gdb );
+ /* remove processed packet and restart */
+ len = gdb->inpos - pl;
+ if (len) memmove(gdb->inbuf, cp, len);
+ gdb->inpos = 0;
+ gdb->inlen = 0;
+ pl = 0;
+ cp = NULL;
+ }
+ else break;
+ } while (1);
+ } else {
+ if (gdb->outlen)
+ set_select_events( &gdb->obj, POLLOUT );
+ else {
+ release_object( gdb );
+ return;
+ }
+ }
+ }
+ if (event & POLLOUT) {
+ if (gdb->outpos < gdb->outlen) {
+ int len = write( gdb->obj.fd, gdb->outbuf + gdb->outpos, gdb->outlen - gdb->outpos );
+ if (len > 0) {
+#ifdef PACKET_DEBUG
+ fprintf(stderr, "reply to gdb: %*.*s\n", len, len, gdb->outbuf + gdb->outpos);
+#endif
+ gdb->outpos += len;
+ }
+ if (gdb->outpos < gdb->outlen)
+ return;
+ }
+ gdb->outpos = 0;
+ gdb->outlen = 0;
+ set_select_events( &gdb->obj, POLLIN );
+ }
+}
+
+static void remote_gdb_destroy( struct object *obj )
+{
+ struct remote_gdb *gdb = (struct remote_gdb *)obj;
+ assert( obj->ops == &remote_gdb_ops );
+ clean_step( gdb );
+ if (create_suspended) suspend_process( gdb->process );
+ if (gdb->state != STATE_RUNNING)
+ detach_exec( gdb );
+ fprintf( stderr, "gdb detached" );
+ release_object( gdb->process );
+ gdb_socket_detach( gdb->process->gdb_socket, gdb );
+}
+
+struct gdb_socket
+{
+ struct object obj; /* object header */
+ struct process *process; /* process to debug */
+ struct remote_gdb *current; /* current debugger */
+};
+
+static void gdb_socket_dump( struct object *obj, int verbose );
+static void gdb_socket_poll_event( struct object *obj, int event );
+static void gdb_socket_destroy( struct object *obj );
+
+static const struct object_ops gdb_socket_ops =
+{
+ sizeof(struct gdb_socket), /* size */
+ gdb_socket_dump, /* dump */
+ no_add_queue, /* add_queue */
+ NULL, /* remove_queue */
+ NULL, /* signaled */
+ NULL, /* satisfied */
+ NULL, /* get_poll_events */
+ gdb_socket_poll_event, /* poll_event */
+ no_get_fd, /* get_fd */
+ no_flush, /* flush */
+ no_get_file_info, /* get_file_info */
+ gdb_socket_destroy /* destroy */
+};
+
+static void gdb_socket_dump( struct object *obj, int verbose )
+{
+ struct gdb_socket *sock = (struct gdb_socket *)obj;
+ assert( obj->ops == &gdb_socket_ops );
+ fprintf( stderr, "GDB listen socket fd=%d\n", sock->obj.fd );
+}
+
+/* handle a socket event */
+static void gdb_socket_poll_event( struct object *obj, int event )
+{
+ struct gdb_socket *sock = (struct gdb_socket *)obj;
+ assert( obj->ops == &gdb_socket_ops );
+
+ if (event & (POLLERR | POLLHUP))
+ {
+ /* this is not supposed to happen */
+ fprintf( stderr, "wineserver: Error on gdb socket\n" );
+ release_object( obj );
+ }
+ else if (event & POLLIN)
+ {
+ struct sockaddr_in dummy;
+ int len = sizeof(dummy);
+ int client = accept( sock->obj.fd, (struct sockaddr *) &dummy, &len );
+ if (client != -1) {
+ if (sock->current || sock->process->debugger) {
+ /* already debugging, reject new debugger */
+ fprintf( stderr, "process already debugged, rejecting new connection\n" );
+ close(client);
+ }
+ else {
+ if (create_gdb_ctx( sock->process))
+ sock->current = create_remote_gdb( client, sock->process );
+ else {
+ fprintf( stderr, "failed to create debug context, rejecting connection\n" );
+ close(client);
+ }
+ }
+ }
+ }
+}
+
+static void gdb_socket_destroy( struct object *obj )
+{
+ struct gdb_socket *sock = (struct gdb_socket *)obj;
+ assert( obj->ops == &gdb_socket_ops );
+ if (sock->current)
+ exit_gdb_process( sock->current );
+}
+
+static void gdb_socket_detach( struct gdb_socket *sock, struct remote_gdb *gdb )
+{
+ destroy_gdb_ctx( sock->process );
+ if (sock->current == gdb) sock->current = NULL;
+}
+
+struct gdb_socket *open_gdb_socket( struct process *process )
+{
+ struct gdb_socket *sock;
+ struct sockaddr_in addr;
+ socklen_t slen = sizeof(addr);
+ int fd;
+
+ if ((fd = socket( AF_INET, SOCK_STREAM, 0 )) == -1)
+ {
+ perror( "socket" );
+ return NULL;
+ }
+ if (listen( fd, 5 ) == -1)
+ {
+ perror( "listen" );
+ goto sock_error;
+ }
+ if (getsockname( fd, (struct sockaddr *)&addr, &slen ) == -1)
+ {
+ perror( "getsockname" );
+ goto sock_error;
+ }
+ if (!(sock = alloc_object( &gdb_socket_ops, fd ))) {
+ fprintf( stderr, "alloc_object: out of memory" );
+ goto sock_error;
+ }
+ sock->process = process;
+ sock->current = NULL;
+ set_select_events( &sock->obj, POLLIN );
+
+ fprintf(stderr, "gdb socket ready at: target remote localhost:%d\n", ntohs(addr.sin_port));
+ if (create_suspended) suspend_process( process );
+ return sock;
+
+sock_error:
+ close(fd);
+ return NULL;
+}
+
+void close_gdb_socket( struct gdb_socket *sock )
+{
+ release_object( sock );
+}
+
+void notify_gdb_event( struct gdb_socket *sock )
+{
+ if (sock && sock->current && sock->current->state == STATE_RUNNING)
+ check_gdb_event( sock->current );
+}
+
+void recv_gdb_event( struct gdb_socket *sock, struct thread *thread,
+ debug_event_t *data, CONTEXT *context )
+{
+ if (sock && sock->current)
+ process_gdb_event( sock->current, thread, data, context );
+}
+
+#endif /* ENABLE_GDB_REMOTE */
Index: server/gdbremote.h
===================================================================
RCS file: gdbremote.h
diff -N gdbremote.h
--- /dev/null Mon Dec 11 17:26:27 2000
+++ /tmp/cvs9tZCqb Wed May 9 13:35:56 2001
@@ -0,0 +1,22 @@
+/*
+ * Wine server requests
+ *
+ * Copyright (C) 1999 Alexandre Julliard
+ */
+
+#ifndef __WINE_SERVER_GDBREMOTE_H
+#define __WINE_SERVER_GDBREMOTE_H
+
+#ifndef __WINE_SERVER__
+#error This file can only be used in the Wine server
+#endif
+
+struct gdb_socket;
+
+extern struct gdb_socket *open_gdb_socket( struct process *process );
+extern void close_gdb_socket( struct gdb_socket *sock );
+extern void notify_gdb_event( struct gdb_socket *sock );
+extern void recv_gdb_event( struct gdb_socket *sock, struct thread *thread,
+ debug_event_t *data, CONTEXT *context );
+
+#endif /* __WINE_SERVER_GDBREMOTE_H */
Index: server/process.c
===================================================================
RCS file: /cvsroot/winex/wine/server/process.c,v
retrieving revision 1.1.1.9
retrieving revision 1.3
diff -u -r1.1.1.9 -r1.3
--- server/process.c 2001/03/05 22:55:56 1.1.1.9
+++ server/process.c 2001/04/22 04:35:16 1.3
@@ -26,6 +26,7 @@
#include "process.h"
#include "thread.h"
#include "request.h"
+#include "gdbremote.h"
/* process structure */
@@ -190,6 +191,11 @@
close( request_pipe[1] );
if (!(thread = create_thread( request_pipe[0], process ))) goto error;
+#ifdef ENABLE_GDB_REMOTE
+ process->gdb_socket = open_gdb_socket( process );
+ process->gdb_ctx = NULL;
+#endif
+
set_select_events( &process->obj, POLLIN ); /* start listening to events */
release_object( process );
return thread;
@@ -424,6 +430,9 @@
{
assert( !process->thread_list );
gettimeofday( &process->end_time, NULL );
+#ifdef ENABLE_GDB_REMOTE
+ if (process->gdb_socket) close_gdb_socket( process->gdb_socket );
+#endif
if (process->handles) release_object( process->handles );
process->handles = NULL;
free_console( process );
Index: server/process.h
===================================================================
RCS file: /cvsroot/winex/wine/server/process.h,v
retrieving revision 1.1.1.3
retrieving revision 1.3
diff -u -r1.1.1.3 -r1.3
--- server/process.h 2001/03/05 22:55:56 1.1.1.3
+++ server/process.h 2001/04/22 04:35:16 1.3
@@ -15,6 +15,9 @@
struct msg_queue;
struct atom_table;
+#ifdef ENABLE_GDB_REMOTE
+struct gdb_socket;
+#endif
/* process structures */
@@ -54,6 +57,10 @@
struct process_dll exe; /* main exe file */
void *ldt_copy; /* pointer to LDT copy in client addr space */
void *ldt_flags; /* pointer to LDT flags in client addr space */
+#ifdef ENABLE_GDB_REMOTE
+ struct gdb_socket *gdb_socket; /* gdb remote debugging socket */
+ struct debug_ctx *gdb_ctx; /* gdb debugger context */
+#endif
};
struct process_snapshot
More information about the wine-patches
mailing list