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",