From: Rémi Bernon rbernon@codeweavers.com
--- dlls/ntdll/unix/loader.c | 26 +++++++ dlls/ntdll/unix/unix_private.h | 1 + dlls/ntdll/unix/virtual.c | 5 ++ loader/main.c | 122 +++++++++++++++++++++++++++++++-- 4 files changed, 150 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index b3b3553d723..28407f5f25a 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -1400,6 +1400,30 @@ static inline char *prepend_build_dir_path( char *ptr, const char *ext, const ch }
+static void notify_gdb_dll_loaded( void *module, const char *unix_path ) +{ + static void (*wine_gdb_dll_loaded)( const void *module, const char *unix_path ); + if (!wine_gdb_dll_loaded) wine_gdb_dll_loaded = dlsym( RTLD_DEFAULT, "wine_gdb_dll_loaded" ); + if (wine_gdb_dll_loaded) wine_gdb_dll_loaded( module, unix_path ); +} + +void notify_gdb_native_dll_loaded( void *module, UNICODE_STRING *nt_name ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING redir; + char *unix_path; + + InitializeObjectAttributes( &attr, nt_name, OBJ_CASE_INSENSITIVE, 0, 0 ); + get_redirect( &attr, &redir ); + + if (!nt_to_unix_file_name( &attr, &unix_path, FILE_OPEN )) + notify_gdb_dll_loaded( module, unix_path ); + + free( redir.Buffer ); + free( unix_path ); +} + + /*********************************************************************** * open_dll_file * @@ -1450,6 +1474,7 @@ static NTSTATUS open_builtin_pe_file( const char *name, OBJECT_ATTRIBUTES *attr, { status = virtual_map_builtin_module( mapping, module, size, image_info, zero_bits, machine, prefer_native ); NtClose( mapping ); + if (!status) notify_gdb_dll_loaded( *module, name ); } return status; } @@ -1580,6 +1605,7 @@ static NTSTATUS find_builtin_dll( UNICODE_STRING *nt_name, void **module, SIZE_T
if (found_image) status = STATUS_IMAGE_MACHINE_TYPE_MISMATCH; WARN( "cannot find builtin library for %s\n", debugstr_us(nt_name) ); + if (!status) notify_gdb_native_dll_loaded( *module, nt_name ); done: if (status >= 0 && ext) { diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index ca0df6d4c38..ecf3cee9a62 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -183,6 +183,7 @@ extern BOOL is_builtin_path( const UNICODE_STRING *path, WORD *machine ) DECLSPE extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const WCHAR *curdir, WCHAR **image, void **module ) DECLSPEC_HIDDEN; extern NTSTATUS load_start_exe( WCHAR **image, void **module ) DECLSPEC_HIDDEN; +extern void notify_gdb_native_dll_loaded( void *module, UNICODE_STRING *nt_name ) DECLSPEC_HIDDEN; extern void start_server( BOOL debug ) DECLSPEC_HIDDEN;
extern unsigned int server_call_unlocked( void *req_ptr ) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 97ae2b7a700..72bc2d2214e 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2548,6 +2548,7 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P if (res == STATUS_IMAGE_ALREADY_LOADED) res = virtual_map_image( handle, access, addr_ptr, size_ptr, zero_bits, shared_file, alloc_type, image_info, &nt_name, FALSE ); + if (!res || res == STATUS_IMAGE_NOT_AT_BASE) notify_gdb_native_dll_loaded( *addr_ptr, &nt_name ); if (shared_file) NtClose( shared_file ); free( image_info ); return res; @@ -4740,6 +4741,10 @@ NTSTATUS WINAPI NtUnmapViewOfSection( HANDLE process, PVOID addr ) SERVER_END_REQ; if (!status) { + static void (*wine_gdb_dll_unload)( const void *module ); + if (!wine_gdb_dll_unload) wine_gdb_dll_unload = dlsym( RTLD_DEFAULT, "wine_gdb_dll_unload" ); + if (wine_gdb_dll_unload) wine_gdb_dll_unload( view->base ); + if (view->protect & SEC_IMAGE) release_builtin_module( view->base ); delete_view( view ); } diff --git a/loader/main.c b/loader/main.c index fc91596c5aa..20ec55e5c38 100644 --- a/loader/main.c +++ b/loader/main.c @@ -20,6 +20,13 @@
#include "config.h"
+#include <stdarg.h> +#include <stddef.h> + +#include "windef.h" +#include "winbase.h" +#include "winnt.h" + #include <fcntl.h> #include <pthread.h> #include <stdio.h> @@ -40,6 +47,8 @@ # include <sys/link.h> #endif
+#include "wine/list.h" + #include "main.h"
extern char **environ; @@ -51,30 +60,50 @@ struct r_debug *wine_r_debug = NULL;
#ifdef __linux__
+struct link_map_entry +{ + struct link_map map; + const void *module; + struct list entry; +}; + static pthread_mutex_t link_map_lock; static struct link_map so_link_map = {0}; +static struct link_map pe_link_map = {.l_prev = &so_link_map, .l_name = (char *)""}; +static struct list pe_link_map_entries = LIST_INIT( pe_link_map_entries ); + +static char *strdup_link_name( const char *path ) +{ + char *real; + if (!path || !(real = realpath( path, NULL ))) return strdup( path ); + if (!strncmp( real, "/run/host", 9 )) memmove( real, real + 9, strlen( real ) - 8 ); + return real; +}
static void sync_wine_link_map(void) { static struct r_debug *_r_debug; - struct link_map *next = &so_link_map, *prev = NULL, **rtld_map, **wine_map; + struct link_map *next = &so_link_map, **rtld_map, **wine_map;
if (!_r_debug) _r_debug = dlsym( RTLD_NEXT, "_r_debug" ); rtld_map = &_r_debug->r_map; wine_map = &next;
+ /* unlink PE link map */ + pe_link_map.l_prev->l_next = NULL; + while (*rtld_map) { if (!*wine_map) { if (!(*wine_map = calloc( 1, sizeof(struct link_map) ))) break; - (*wine_map)->l_prev = prev; + (*wine_map)->l_prev = pe_link_map.l_prev; }
- prev = *wine_map; + pe_link_map.l_prev = *wine_map; free( (*wine_map)->l_name ); (*wine_map)->l_addr = (*rtld_map)->l_addr; - (*wine_map)->l_name = strdup( (*rtld_map)->l_name ); + (*wine_map)->l_name = strdup_link_name( (*rtld_map)->l_name ); (*wine_map)->l_ld = (*rtld_map)->l_ld; rtld_map = &(*rtld_map)->l_next; wine_map = &(*wine_map)->l_next; @@ -94,10 +123,95 @@ static void sync_wine_link_map(void) free( prev ); }
+ /* link PE link map back */ + pe_link_map.l_prev->l_next = &pe_link_map; + if (wine_r_debug) wine_r_debug->r_map = &so_link_map; if (wine_dl_debug_state) wine_dl_debug_state(); }
+static void add_dll_to_pe_link_map( const void *module, const char *unix_path ) +{ + const IMAGE_DOS_HEADER *dos = (const IMAGE_DOS_HEADER *)module; + const IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((const BYTE *)dos + dos->e_lfanew); + struct link_map_entry *entry; + struct link_map *map_end; + struct list *tail; + + if (!(entry = calloc( 1, sizeof(*entry) ))) return; + + entry->module = module; + entry->map.l_addr = (char *)module - (char *)nt->OptionalHeader.ImageBase; + entry->map.l_name = strdup_link_name( unix_path ); + + if ((tail = list_tail( &pe_link_map_entries ))) + { + entry->map.l_prev = &LIST_ENTRY( tail, struct link_map_entry, entry )->map; + entry->map.l_prev->l_next = &entry->map; + } + else + { + map_end = &pe_link_map; + while (map_end->l_next) map_end = map_end->l_next; + + map_end->l_next = &entry->map; + map_end->l_next->l_prev = map_end; + } + + list_add_tail( &pe_link_map_entries, &entry->entry ); +} + +void wine_gdb_dll_loaded( const void *module, const char *unix_path ) +{ + struct link_map_entry *entry; + + pthread_mutex_lock( &link_map_lock ); + + LIST_FOR_EACH_ENTRY( entry, &pe_link_map_entries, struct link_map_entry, entry ) + if (entry->module == module) break; + + if (&entry->entry == &pe_link_map_entries) + add_dll_to_pe_link_map( module, unix_path ); + else + { + const IMAGE_DOS_HEADER *dos = (const IMAGE_DOS_HEADER *)module; + const IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)((const BYTE *)dos + dos->e_lfanew); + entry->map.l_addr = (char *)module - (char *)nt->OptionalHeader.ImageBase; + } + + sync_wine_link_map(); + + pthread_mutex_unlock( &link_map_lock ); +} + +void wine_gdb_dll_unload( const void *module ) +{ + struct link_map_entry *entry; + + pthread_mutex_lock( &link_map_lock ); + + LIST_FOR_EACH_ENTRY( entry, &pe_link_map_entries, struct link_map_entry, entry ) + if (entry->module == module) break; + + if (&entry->entry == &pe_link_map_entries) + { + pthread_mutex_unlock( &link_map_lock ); + return; + } + + list_remove( &entry->entry ); + + if (entry->map.l_prev) entry->map.l_prev->l_next = entry->map.l_next; + if (entry->map.l_next) entry->map.l_next->l_prev = entry->map.l_prev; + + sync_wine_link_map(); + + pthread_mutex_unlock( &link_map_lock ); + + free( entry->map.l_name ); + free( entry ); +} + void *dlopen( const char *file, int mode ) { static typeof(dlopen) *rtld_dlopen;