Store the list of loaded dlls in the server, and generate debug events
internally.

diff --git a/server/debugger.c b/server/debugger.c
index b62d0d3..7b7b9b4 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -84,72 +84,85 @@
 
 
 /* initialise the fields that do not need to be filled by the client */
-static int fill_debug_event( struct thread *debugger, struct thread *thread,
-                             struct debug_event *event )
+static int fill_debug_event( struct debug_event *event, void *arg )
 {
+    struct process *debugger = event->debugger->process;
+    struct process *process;
+    struct thread *thread;
+    struct process_dll *dll;
     int handle;
 
     /* some events need special handling */
     switch(event->data.code)
     {
     case CREATE_THREAD_DEBUG_EVENT:
-        if ((event->data.info.create_thread.handle = alloc_handle( debugger->process, thread,
-               /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
-                                                              THREAD_ALL_ACCESS, FALSE )) == -1)
+        thread = arg;
+        /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
+        if ((handle = alloc_handle( debugger, thread, THREAD_ALL_ACCESS, FALSE )) == -1)
             return 0;
-        event->data.info.create_thread.teb   = thread->teb;
-        event->data.info.create_thread.start = thread->entry;
+        event->data.info.create_thread.handle = handle;
+        event->data.info.create_thread.teb    = thread->teb;
+        event->data.info.create_thread.start  = thread->entry;
         break;
     case CREATE_PROCESS_DEBUG_EVENT:
-        if ((handle = alloc_handle( debugger->process, thread->process,
-                                    /* documented: PROCESS_VM_READ | PROCESS_VM_WRITE */
-                                    PROCESS_ALL_ACCESS, FALSE )) == -1)
+        process = arg;
+        thread = process->thread_list;
+        /* documented: PROCESS_VM_READ | PROCESS_VM_WRITE */
+        if ((handle = alloc_handle( debugger, process, PROCESS_ALL_ACCESS, FALSE )) == -1)
             return 0;
         event->data.info.create_process.process = handle;
 
-        if ((handle = alloc_handle( debugger->process, thread,
-               /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
-                                    THREAD_ALL_ACCESS, FALSE )) == -1)
+        /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
+        if ((handle = alloc_handle( debugger, thread, THREAD_ALL_ACCESS, FALSE )) == -1)
         {
-            close_handle( debugger->process, event->data.info.create_process.process );
+            close_handle( debugger, event->data.info.create_process.process );
             return 0;
         }
         event->data.info.create_process.thread = handle;
 
         handle = -1;
-        if (thread->process->exe_file &&
-            ((handle = alloc_handle( debugger->process, thread->process->exe_file,
-                        /* the doc says write access too, but this doesn't seem a good idea */
-                                     GENERIC_READ, FALSE )) == -1))
+        if (process->exe.file &&
+            /* the doc says write access too, but this doesn't seem a good idea */
+            ((handle = alloc_handle( debugger, process->exe.file, GENERIC_READ, FALSE )) == -1))
         {
-            close_handle( debugger->process, event->data.info.create_process.process );
-            close_handle( debugger->process, event->data.info.create_process.thread );
+            close_handle( debugger, event->data.info.create_process.process );
+            close_handle( debugger, event->data.info.create_process.thread );
             return 0;
         }
         event->data.info.create_process.file       = handle;
         event->data.info.create_process.teb        = thread->teb;
-        event->data.info.create_process.base       = thread->process->module;
+        event->data.info.create_process.base       = process->exe.base;
         event->data.info.create_process.start      = thread->entry;
-        event->data.info.create_process.dbg_offset = 0;
-        event->data.info.create_process.dbg_size   = 0;
+        event->data.info.create_process.dbg_offset = process->exe.dbg_offset;
+        event->data.info.create_process.dbg_size   = process->exe.dbg_size;
         event->data.info.create_process.name       = 0;
         event->data.info.create_process.unicode    = 0;
         break;
     case LOAD_DLL_DEBUG_EVENT:
-        if ((handle = event->data.info.load_dll.handle) != -1)
-        {
-            if ((handle = duplicate_handle( thread->process, handle, debugger->process,
-                                            GENERIC_READ, FALSE, 0 )) == -1)
-                return 0;
-            event->data.info.load_dll.handle = handle;
-        }
+        dll = arg;
+        handle = -1;
+        if (dll->file &&
+            (handle = alloc_handle( debugger, dll->file, GENERIC_READ, FALSE )) == -1)
+            return 0;
+        event->data.info.load_dll.handle     = handle;
+        event->data.info.load_dll.base       = dll->base;
+        event->data.info.load_dll.dbg_offset = dll->dbg_offset;
+        event->data.info.load_dll.dbg_size   = dll->dbg_size;
+        event->data.info.load_dll.name       = dll->name;
+        event->data.info.load_dll.unicode    = 0;
         break;
     case EXIT_PROCESS_DEBUG_EVENT:
+        process = arg;
+        event->data.info.exit.exit_code = process->exit_code;
+        break;
     case EXIT_THREAD_DEBUG_EVENT:
+        thread = arg;
         event->data.info.exit.exit_code = thread->exit_code;
         break;
-    case EXCEPTION_DEBUG_EVENT:
     case UNLOAD_DLL_DEBUG_EVENT:
+        event->data.info.unload_dll.base = arg;
+        break;
+    case EXCEPTION_DEBUG_EVENT:
     case OUTPUT_DEBUG_STRING_EVENT:
     case RIP_EVENT:
         break;
@@ -333,7 +346,6 @@
     {
         if (event == debug_ctx->to_send) goto error;
         if (event->sender == thread) break;
-        event = event->next;
     }
     if (!event) goto error;
 
@@ -351,7 +363,7 @@
 }
 
 /* queue a debug event for a debugger */
-static struct debug_event *queue_debug_event( struct thread *thread, int code,
+static struct debug_event *queue_debug_event( struct thread *thread, int code, void *arg,
                                               debug_event_t *data )
 {
     struct thread *debugger = thread->process->debugger;
@@ -372,7 +384,7 @@
     if (data) memcpy( &event->data, data, sizeof(event->data) );
     event->data.code = code;
 
-    if (!fill_debug_event( debugger, thread, event ))
+    if (!fill_debug_event( event, arg ))
     {
         event->data.code = -1;  /* make sure we don't attempt to close handles */
         release_object( event );
@@ -385,15 +397,35 @@
 }
 
 /* generate a debug event from inside the server and queue it */
-void generate_debug_event( struct thread *thread, int code )
+void generate_debug_event( struct thread *thread, int code, void *arg )
 {
     if (thread->process->debugger)
     {
-        struct debug_event *event = queue_debug_event( thread, code, NULL );
+        struct debug_event *event = queue_debug_event( thread, code, arg, NULL );
         if (event) release_object( event );
     }
 }
 
+/* generate all startup events of a given process */
+void generate_startup_debug_events( struct process *process )
+{
+    struct process_dll *dll;
+    struct thread *thread = process->thread_list;
+
+    /* generate creation events */
+    generate_debug_event( thread, CREATE_PROCESS_DEBUG_EVENT, process );
+    while ((thread = thread->next))
+        generate_debug_event( thread, CREATE_THREAD_DEBUG_EVENT, thread );
+
+    /* generate dll events (in loading order, i.e. reverse list order) */
+    for (dll = &process->exe; dll->next; dll = dll->next);
+    while (dll != &process->exe)
+    {
+        generate_debug_event( process->thread_list, LOAD_DLL_DEBUG_EVENT, dll );
+        dll = dll->prev;
+    }
+}
+
 /* return a pointer to the context in case the thread is inside an exception event */
 CONTEXT *get_debug_context( struct thread *thread )
 {
@@ -487,10 +519,8 @@
     if (!process) return;
     if (debugger_attach( process, current ))
     {
-        struct thread *thread = process->thread_list;
-        generate_debug_event( thread, CREATE_PROCESS_DEBUG_EVENT );
-        while ((thread = thread->next)) generate_debug_event( thread, CREATE_THREAD_DEBUG_EVENT );
-        /* FIXME: load dll + breakpoint exception events */
+        generate_startup_debug_events( process );
+        /* FIXME: breakpoint exception event */
     }
     release_object( process );
 }
@@ -507,7 +537,8 @@
         return;
     }
     req->status = 0;
-    if (current->process->debugger && ((event = queue_debug_event( current, code, &req->event ))))
+    if (current->process->debugger && ((event = queue_debug_event( current, code,
+                                                                   NULL, &req->event ))))
     {
         /* wait for continue_debug_event */
         struct object *obj = &event->obj;
diff --git a/server/object.h b/server/object.h
index 3f87b85..6aa920d 100644
--- a/server/object.h
+++ b/server/object.h
@@ -154,7 +154,8 @@
 /* debugger functions */
 
 extern int debugger_attach( struct process *process, struct thread *debugger );
-extern void generate_debug_event( struct thread *thread, int code );
+extern void generate_debug_event( struct thread *thread, int code, void *arg );
+extern void generate_startup_debug_events( struct process *process );
 extern void debug_exit_thread( struct thread *thread );
 extern CONTEXT *get_debug_context( struct thread *thread );
 
diff --git a/server/process.c b/server/process.c
index ddd8226..30a9bf5 100644
--- a/server/process.c
+++ b/server/process.c
@@ -143,7 +143,6 @@
     process->prev            = NULL;
     process->thread_list     = NULL;
     process->debugger        = NULL;
-    process->exe_file        = NULL;
     process->handles         = NULL;
     process->exit_code       = STILL_ACTIVE;
     process->running_threads = 0;
@@ -157,6 +156,11 @@
     process->info            = NULL;
     process->ldt_copy        = NULL;
     process->ldt_flags       = NULL;
+    process->exe.next        = NULL;
+    process->exe.prev        = NULL;
+    process->exe.file        = NULL;
+    process->exe.dbg_offset  = 0;
+    process->exe.dbg_size    = 0;
     gettimeofday( &process->start_time, NULL );
     if ((process->next = first_process) != NULL) process->next->prev = process;
     first_process = process;
@@ -176,7 +180,7 @@
     /* retrieve the main exe file */
     if (process->info->exe_file != -1)
     {
-        if (!(process->exe_file = get_file_obj( parent, process->info->exe_file,
+        if (!(process->exe.file = get_file_obj( parent, process->info->exe_file,
                                                 GENERIC_READ ))) goto error;
         process->info->exe_file = -1;
     }
@@ -225,7 +229,7 @@
     else first_process = process->next;
     if (process->info) free( process->info );
     if (process->init_event) release_object( process->init_event );
-    if (process->exe_file) release_object( process->exe_file );
+    if (process->exe.file) release_object( process->exe.file );
 }
 
 /* dump a process on stdout for debugging purposes */
@@ -263,17 +267,68 @@
                                              access, &process_ops );
 }
 
+/* add a dll to a process list */
+static struct process_dll *process_load_dll( struct process *process, struct file *file,
+                                             void *base )
+{
+    struct process_dll *dll;
+
+    /* make sure we don't already have one with the same base address */
+    for (dll = process->exe.next; dll; dll = dll->next) if (dll->base == base)
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        return NULL;
+    }
+
+    if ((dll = mem_alloc( sizeof(*dll) )))
+    {
+        dll->prev = &process->exe;
+        dll->file = NULL;
+        dll->base = base;
+        if (file) dll->file = (struct file *)grab_object( file );
+        if ((dll->next = process->exe.next)) dll->next->prev = dll;
+        process->exe.next = dll;
+    }
+    return dll;
+}
+
+/* remove a dll from a process list */
+static void process_unload_dll( struct process *process, void *base )
+{
+    struct process_dll *dll;
+
+    for (dll = process->exe.next; dll; dll = dll->next)
+    {
+        if (dll->base == base)
+        {
+            if (dll->file) release_object( dll->file );
+            if (dll->next) dll->next->prev = dll->prev;
+            if (dll->prev) dll->prev->next = dll->next;
+            free( dll );
+            generate_debug_event( current, UNLOAD_DLL_DEBUG_EVENT, base );
+            return;
+        }
+    }
+    set_error( STATUS_INVALID_PARAMETER );
+}
+
 /* a process has been killed (i.e. its last thread died) */
-static void process_killed( struct process *process, int exit_code )
+static void process_killed( struct process *process )
 {
     assert( !process->thread_list );
-    process->exit_code = exit_code;
     gettimeofday( &process->end_time, NULL );
     release_object( process->handles );
     process->handles = NULL;
     free_console( process );
-    if (process->exe_file) release_object( process->exe_file );
-    process->exe_file = NULL;
+    while (process->exe.next)
+    {
+        struct process_dll *dll = process->exe.next;
+        process->exe.next = dll->next;
+        if (dll->file) release_object( dll->file );
+        free( dll );
+    }
+    if (process->exe.file) release_object( process->exe.file );
+    process->exe.file = NULL;
     wake_up( &process->obj, 0 );
     if (!--running_processes)
     {
@@ -308,8 +363,11 @@
     if (!--process->running_threads)
     {
         /* we have removed the last running thread, exit the process */
-        process_killed( process, thread->exit_code );
+        process->exit_code = thread->exit_code;
+        generate_debug_event( thread, EXIT_PROCESS_DEBUG_EVENT, process );
+        process_killed( process );
     }
+    else generate_debug_event( thread, EXIT_THREAD_DEBUG_EVENT, thread );
     release_object( thread );
 }
 
@@ -580,8 +638,8 @@
     req->cmd_show    = info->cmd_show;
     req->env_ptr     = info->env_ptr;
     strcpy( req->cmdline, info->cmdline );
-    if (current->process->exe_file)
-        req->exe_file = alloc_handle( current->process, current->process->exe_file,
+    if (current->process->exe.file)
+        req->exe_file = alloc_handle( current->process, current->process->exe.file,
                                       GENERIC_READ, 0 );
     free( info );
 }
@@ -595,9 +653,9 @@
         fatal_protocol_error( current, "init_process_done: no event\n" );
         return;
     }
-    current->entry  = req->entry;
-    process->module = req->module;
-    generate_debug_event( current, CREATE_PROCESS_DEBUG_EVENT );
+    current->entry    = req->entry;
+    process->exe.base = req->module;
+    generate_startup_debug_events( current->process );
     set_event( process->init_event );
     release_object( process->init_event );
     process->init_event = NULL;
@@ -678,3 +736,30 @@
         release_object( process );
     }
 }
+
+/* notify the server that a dll has been loaded */
+DECL_HANDLER(load_dll)
+{
+    struct process_dll *dll;
+    struct file *file = NULL;
+
+    if ((req->handle != -1) &&
+        !(file = get_file_obj( current->process, req->handle, GENERIC_READ ))) return;
+    
+    if ((dll = process_load_dll( current->process, file, req->base )))
+    {
+        dll->dbg_offset = req->dbg_offset;
+        dll->dbg_size   = req->dbg_size;
+        dll->name       = req->name;
+        /* only generate event if initialization is done */
+        if (!current->process->init_event)
+            generate_debug_event( current, LOAD_DLL_DEBUG_EVENT, dll );
+    }
+    if (file) release_object( file );
+}
+
+/* notify the server that a dll is being unloaded */
+DECL_HANDLER(unload_dll)
+{
+    process_unload_dll( current->process, req->base );
+}
diff --git a/server/process.h b/server/process.h
index e8e7cc6..9f21e05 100644
--- a/server/process.h
+++ b/server/process.h
@@ -15,6 +15,17 @@
 
 /* process structures */
 
+struct process_dll
+{
+    struct process_dll  *next;            /* per-process dll list */
+    struct process_dll  *prev;
+    struct file         *file;            /* dll file */
+    void                *base;            /* dll base address (in process addr space) */
+    void                *name;            /* ptr to ptr to name (in process addr space) */
+    int                  dbg_offset;      /* debug info offset */
+    int                  dbg_size;        /* debug info size */
+};
+
 struct process
 {
     struct object        obj;             /* object header */
@@ -22,7 +33,6 @@
     struct process      *prev;
     struct thread       *thread_list;     /* head of the thread list */
     struct thread       *debugger;        /* thread debugging this process */
-    struct file         *exe_file;        /* main exe file */
     struct object       *handles;         /* handle entries */
     int                  exit_code;       /* process exit code */
     int                  running_threads; /* number of threads running in this process */
@@ -35,9 +45,9 @@
     struct object       *console_in;      /* console input */
     struct object       *console_out;     /* console output */
     struct event        *init_event;      /* event for init done */
+    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 */
-    void                *module;          /* main module base address */
     struct new_process_request *info;     /* startup info (freed after startup) */
 };
 
diff --git a/server/ptrace.c b/server/ptrace.c
index 7567046..58339f6 100644
--- a/server/ptrace.c
+++ b/server/ptrace.c
@@ -120,15 +120,20 @@
 void detach_thread( struct thread *thread )
 {
     if (!thread->unix_pid) return;
-    kill( thread->unix_pid, SIGTERM );
-    if (thread->suspend + thread->process->suspend) continue_thread( thread );
     if (thread->attached)
     {
-        wait4_thread( thread, SIGTERM );
+        /* make sure it is stopped */
+        if (!(thread->suspend + thread->process->suspend)) stop_thread( thread );
+        kill( thread->unix_pid, SIGTERM );
         if (debug_level) fprintf( stderr, "%08x: *detached*\n", (unsigned int)thread );
         ptrace( PTRACE_DETACH, thread->unix_pid, 1, SIGTERM );
         thread->attached = 0;
     }
+    else
+    {
+        kill( thread->unix_pid, SIGTERM );
+        if (thread->suspend + thread->process->suspend) continue_thread( thread );
+    }
 }
 
 /* stop a thread (at the Unix level) */
diff --git a/server/request.c b/server/request.c
index 3067692..7f67c1b 100644
--- a/server/request.c
+++ b/server/request.c
@@ -383,18 +383,3 @@
 {
     set_select_events( &master_socket->obj, locked ? 0 : POLLIN );
 }
-
-/* debugger support operations */
-DECL_HANDLER(debugger)
-{
-    switch ( req->op )
-    {
-    case DEBUGGER_FREEZE_ALL:
-        suspend_all_threads();
-        break;
-
-    case DEBUGGER_UNFREEZE_ALL:
-        resume_all_threads();
-        break;
-    }
-}
diff --git a/server/request.h b/server/request.h
index dc4176a..978b691 100644
--- a/server/request.h
+++ b/server/request.h
@@ -36,7 +36,6 @@
 extern void lock_master_socket( int locked );
 
 extern void trace_request( enum request req, int fd );
-extern void trace_kill( struct thread *thread );
 extern void trace_reply( struct thread *thread );
 
 /* get the request buffer */
@@ -85,7 +84,8 @@
 DECL_HANDLER(set_thread_info);
 DECL_HANDLER(suspend_thread);
 DECL_HANDLER(resume_thread);
-DECL_HANDLER(debugger);
+DECL_HANDLER(load_dll);
+DECL_HANDLER(unload_dll);
 DECL_HANDLER(queue_apc);
 DECL_HANDLER(get_apcs);
 DECL_HANDLER(close_handle);
@@ -189,7 +189,8 @@
     { (void(*)())req_set_thread_info, sizeof(struct set_thread_info_request) },
     { (void(*)())req_suspend_thread, sizeof(struct suspend_thread_request) },
     { (void(*)())req_resume_thread, sizeof(struct resume_thread_request) },
-    { (void(*)())req_debugger, sizeof(struct debugger_request) },
+    { (void(*)())req_load_dll, sizeof(struct load_dll_request) },
+    { (void(*)())req_unload_dll, sizeof(struct unload_dll_request) },
     { (void(*)())req_queue_apc, sizeof(struct queue_apc_request) },
     { (void(*)())req_get_apcs, sizeof(struct get_apcs_request) },
     { (void(*)())req_close_handle, sizeof(struct close_handle_request) },
diff --git a/server/thread.c b/server/thread.c
index 8f30280..cf02b87 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -563,10 +563,9 @@
     thread->state = TERMINATED;
     thread->exit_code = exit_code;
     if (current == thread) current = NULL;
-    if (debug_level) trace_kill( thread );
+    if (debug_level)
+        fprintf( stderr,"%08x: *killed* exit_code=%d\n", (unsigned int)thread, exit_code );
     if (thread->wait) end_wait( thread );
-    generate_debug_event( thread, (thread->process->running_threads == 1) ?
-                          EXIT_PROCESS_DEBUG_EVENT : EXIT_THREAD_DEBUG_EVENT );
     debug_exit_thread( thread );
     abandon_mutexes( thread );
     remove_process_thread( thread->process, thread );
@@ -633,7 +632,7 @@
     current->entry    = req->entry;
     if (current->suspend + current->process->suspend > 0) stop_thread( current );
     if (current->process->running_threads > 1)
-        generate_debug_event( current, CREATE_THREAD_DEBUG_EVENT );
+        generate_debug_event( current, CREATE_THREAD_DEBUG_EVENT, current );
 }
 
 /* terminate a thread */
diff --git a/server/trace.c b/server/trace.c
index eaf4793..1313c15 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -361,9 +361,18 @@
     fprintf( stderr, " count=%d", req->count );
 }
 
-static void dump_debugger_request( const struct debugger_request *req )
+static void dump_load_dll_request( const struct load_dll_request *req )
 {
-    fprintf( stderr, " op=%d", req->op );
+    fprintf( stderr, " handle=%d,", req->handle );
+    fprintf( stderr, " base=%p,", req->base );
+    fprintf( stderr, " dbg_offset=%d,", req->dbg_offset );
+    fprintf( stderr, " dbg_size=%d,", req->dbg_size );
+    fprintf( stderr, " name=%p", req->name );
+}
+
+static void dump_unload_dll_request( const struct unload_dll_request *req )
+{
+    fprintf( stderr, " base=%p", req->base );
 }
 
 static void dump_queue_apc_request( const struct queue_apc_request *req )
@@ -1247,7 +1256,8 @@
     (dump_func)dump_set_thread_info_request,
     (dump_func)dump_suspend_thread_request,
     (dump_func)dump_resume_thread_request,
-    (dump_func)dump_debugger_request,
+    (dump_func)dump_load_dll_request,
+    (dump_func)dump_unload_dll_request,
     (dump_func)dump_queue_apc_request,
     (dump_func)dump_get_apcs_request,
     (dump_func)dump_close_handle_request,
@@ -1349,6 +1359,7 @@
     (dump_func)dump_resume_thread_reply,
     (dump_func)0,
     (dump_func)0,
+    (dump_func)0,
     (dump_func)dump_get_apcs_reply,
     (dump_func)0,
     (dump_func)dump_get_handle_info_reply,
@@ -1447,7 +1458,8 @@
     "set_thread_info",
     "suspend_thread",
     "resume_thread",
-    "debugger",
+    "load_dll",
+    "unload_dll",
     "queue_apc",
     "get_apcs",
     "close_handle",
@@ -1548,12 +1560,6 @@
     else fprintf( stderr, " )\n" );
 }
 
-void trace_kill( struct thread *thread )
-{
-    fprintf( stderr,"%08x: *killed* exit_code=%d\n",
-             (unsigned int)thread, thread->exit_code );
-}
-
 void trace_reply( struct thread *thread )
 {
     fprintf( stderr, "%08x: %s() = %x",