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*)&regs, 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*)&regs, 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