This makes it possible to debug Wine application in GDB almost like any other Unix application. Split from https://gitlab.winehq.org/wine/wine/-/merge_requests/1074.
-- v4: ntdll: Maintain a PE module link map and expose it to GDB. ntdll: Pass a UNICODE_STRING to load_builtin and virtual_map_image.
From: Rémi Bernon rbernon@codeweavers.com
To make sure gdb will unwind through it, and ignore that the syscall frame is inner its caller frame on the thread stack. --- dlls/ntdll/unix/signal_i386.c | 2 ++ dlls/ntdll/unix/signal_x86_64.c | 2 ++ 2 files changed, 4 insertions(+)
diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index d665e281176..23441273d0f 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -2537,6 +2537,7 @@ __ASM_GLOBAL_FUNC( signal_exit_thread, * __wine_syscall_dispatcher */ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, + __ASM_CFI(".cfi_signal_frame\n\t") "movl %fs:0x1f8,%ecx\n\t" /* x86_thread_data()->syscall_frame */ "movw $0,0x02(%ecx)\n\t" /* frame->restore_flags */ "popl 0x08(%ecx)\n\t" /* frame->eip */ @@ -2727,6 +2728,7 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, * __wine_unix_call_dispatcher */ __ASM_GLOBAL_FUNC( __wine_unix_call_dispatcher, + __ASM_CFI(".cfi_signal_frame\n\t") "movl %fs:0x1f8,%ecx\n\t" /* x86_thread_data()->syscall_frame */ "movw $0,0x02(%ecx)\n\t" /* frame->restore_flags */ "popl 0x08(%ecx)\n\t" /* frame->eip */ diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index fa8660ca914..478ed731661 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2628,6 +2628,7 @@ __ASM_GLOBAL_FUNC( signal_exit_thread, * __wine_syscall_dispatcher */ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, + __ASM_CFI(".cfi_signal_frame\n\t") #ifdef __APPLE__ "movq %gs:0x30,%rcx\n\t" "movq 0x328(%rcx),%rcx\n\t" @@ -2850,6 +2851,7 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, * __wine_unix_call_dispatcher */ __ASM_GLOBAL_FUNC( __wine_unix_call_dispatcher, + __ASM_CFI(".cfi_signal_frame\n\t") "movq %rcx,%r10\n\t" #ifdef __APPLE__ "movq %gs:0x30,%rcx\n\t"
From: Rémi Bernon rbernon@codeweavers.com
Effectively supporting dynamically loaded libraries when running Wine under GDB without WINELOADERNOEXEC=1.
Credits to Jinoh Kang for the idea. --- loader/main.c | 88 +++++++++++++++++++++++++++++++++++++++++++++- loader/preloader.c | 15 ++++++++ 2 files changed, 102 insertions(+), 1 deletion(-)
diff --git a/loader/main.c b/loader/main.c index 242ff15accd..b7805473da8 100644 --- a/loader/main.c +++ b/loader/main.c @@ -33,13 +33,99 @@ #ifdef HAVE_SYS_SYSCTL_H # include <sys/sysctl.h> #endif +#ifdef HAVE_LINK_H +# include <link.h> +#endif +#ifdef HAVE_SYS_LINK_H +# include <sys/link.h> +#endif
#include "main.h"
extern char **environ;
-/* the preloader will set this variable */ +/* the preloader will set these variables */ const struct wine_preload_info *wine_main_preload_info = NULL; +void (*wine_dl_debug_state)(void) = NULL; +struct r_debug *wine_r_debug = NULL; + +#ifdef __linux__ + +static struct link_map so_link_map = {.l_name = (char *)""}; +static pthread_mutex_t link_map_lock = PTHREAD_MUTEX_INITIALIZER; + +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; + + if (!_r_debug) _r_debug = dlsym( RTLD_NEXT, "_r_debug" ); + rtld_map = &_r_debug->r_map; + wine_map = &next; + + pthread_mutex_lock( &link_map_lock ); + + while (*rtld_map) + { + if (!*wine_map) + { + if (!(*wine_map = calloc( 1, sizeof(struct link_map) ))) break; + (*wine_map)->l_prev = prev; + } + + prev = *wine_map; + (*wine_map)->l_addr = (*rtld_map)->l_addr; + (*wine_map)->l_name = strdup( (*rtld_map)->l_name ); + (*wine_map)->l_ld = (*rtld_map)->l_ld; + rtld_map = &(*rtld_map)->l_next; + wine_map = &(*wine_map)->l_next; + } + + /* remove the remaining wine entries */ + next = *wine_map; + *wine_map = NULL; + + while (next) + { + struct link_map *prev = next; + wine_map = &next->l_next; + next = *wine_map; + *wine_map = NULL; + free( prev->l_name ); + free( prev ); + } + + pthread_mutex_unlock( &link_map_lock ); + + if (wine_r_debug) wine_r_debug->r_map = &so_link_map; + if (wine_dl_debug_state) wine_dl_debug_state(); +} + +void *dlopen( const char *file, int mode ) +{ + static typeof(dlopen) *rtld_dlopen; + void *ret; + + if (!rtld_dlopen) rtld_dlopen = dlsym( RTLD_NEXT, "dlopen" ); + ret = rtld_dlopen( file, mode ); + + sync_wine_link_map(); + return ret; +} + +int dlclose( void *handle ) +{ + static typeof(dlclose) *rtld_dlclose; + int ret; + + if (!rtld_dlclose) rtld_dlclose = dlsym( RTLD_NEXT, "dlclose" ); + ret = rtld_dlclose( handle ); + + sync_wine_link_map(); + return ret; +} + +#endif /* __linux__ */
/* canonicalize path and return its directory name */ static char *realpath_dirname( const char *name ) diff --git a/loader/preloader.c b/loader/preloader.c index 72556f09720..9fa00786281 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -1362,6 +1362,9 @@ static void set_process_name( int argc, char *argv[] ) for (i = 1; i < argc; i++) argv[i] -= off; }
+/* GDB hooks integration */ +struct r_debug _r_debug = {0}; +void _dl_debug_state(void) {}
/* * wld_start @@ -1378,6 +1381,8 @@ void* wld_start( void **stack ) struct wld_auxv new_av[8], delete_av[3], *av; struct wld_link_map main_binary_map, ld_so_map; struct wine_preload_info **wine_main_preload_info; + void (**wine_dl_debug_state)(void); + struct r_debug **wine_r_debug;
pargc = *stack; argv = (char **)pargc + 1; @@ -1450,6 +1455,16 @@ void* wld_start( void **stack ) if (wine_main_preload_info) *wine_main_preload_info = preload_info; else wld_printf( "wine_main_preload_info not found\n" );
+ /* provide r_debug to inform GDB of loaded modules */ + wine_r_debug = find_symbol( &main_binary_map, "wine_r_debug", STT_OBJECT ); + if (wine_r_debug) *wine_r_debug = &_r_debug; + else wld_printf( "wine_r_debug not found\n" ); + + /* provide _dl_debug_state callback to trigger GDB hooks */ + wine_dl_debug_state = find_symbol( &main_binary_map, "wine_dl_debug_state", STT_OBJECT ); + if (wine_dl_debug_state) *wine_dl_debug_state = _dl_debug_state; + else wld_printf( "wine_dl_debug_state not found\n" ); + #define SET_NEW_AV(n,type,val) new_av[n].a_type = (type); new_av[n].a_un.a_val = (val); SET_NEW_AV( 0, AT_PHDR, (unsigned long)main_binary_map.l_phdr ); SET_NEW_AV( 1, AT_PHENT, sizeof(ElfW(Phdr)) );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/ntdll/unix/loader.c | 12 +++++----- dlls/ntdll/unix/unix_private.h | 2 +- dlls/ntdll/unix/virtual.c | 40 +++++++++++++++++----------------- 3 files changed, 26 insertions(+), 28 deletions(-)
diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 7243be49489..42111cec8ca 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -1636,17 +1636,15 @@ done: * Load the builtin dll if specified by load order configuration. * Return STATUS_IMAGE_ALREADY_LOADED if we should keep the native one that we have found. */ -NTSTATUS load_builtin( const pe_image_info_t *image_info, WCHAR *filename, +NTSTATUS load_builtin( const pe_image_info_t *image_info, UNICODE_STRING *nt_name, void **module, SIZE_T *size, ULONG_PTR zero_bits ) { WORD machine = image_info->machine; /* request same machine as the native one */ NTSTATUS status; - UNICODE_STRING nt_name; SECTION_IMAGE_INFORMATION info; enum loadorder loadorder;
- init_unicode_string( &nt_name, filename ); - loadorder = get_load_order( &nt_name ); + loadorder = get_load_order( nt_name );
if (loadorder == LO_DISABLED) return STATUS_DLL_NOT_FOUND;
@@ -1657,7 +1655,7 @@ NTSTATUS load_builtin( const pe_image_info_t *image_info, WCHAR *filename, } else if (image_info->image_flags & IMAGE_FLAGS_WineFakeDll) { - TRACE( "%s is a fake Wine dll\n", debugstr_w(filename) ); + TRACE( "%s is a fake Wine dll\n", debugstr_us(nt_name) ); if (loadorder == LO_NATIVE) return STATUS_DLL_NOT_FOUND; loadorder = LO_BUILTIN; /* builtin with no fallback since mapping a fake dll is not useful */ } @@ -1668,9 +1666,9 @@ NTSTATUS load_builtin( const pe_image_info_t *image_info, WCHAR *filename, case LO_NATIVE_BUILTIN: return STATUS_IMAGE_ALREADY_LOADED; case LO_BUILTIN: - return find_builtin_dll( &nt_name, module, size, &info, zero_bits, machine, FALSE ); + return find_builtin_dll( nt_name, module, size, &info, zero_bits, machine, FALSE ); default: - status = find_builtin_dll( &nt_name, module, size, &info, zero_bits, machine, (loadorder == LO_DEFAULT) ); + status = find_builtin_dll( nt_name, module, size, &info, zero_bits, machine, (loadorder == LO_DEFAULT) ); if (status == STATUS_DLL_NOT_FOUND || status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) return STATUS_IMAGE_ALREADY_LOADED; return status; diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 9b3d92c7620..7ee7012a175 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -152,7 +152,7 @@ extern void *create_startup_info( const UNICODE_STRING *nt_image, const RTL_USER DWORD *info_size ) DECLSPEC_HIDDEN; extern char **build_envp( const WCHAR *envW ) DECLSPEC_HIDDEN; extern NTSTATUS exec_wineloader( char **argv, int socketfd, const pe_image_info_t *pe_info ) DECLSPEC_HIDDEN; -extern NTSTATUS load_builtin( const pe_image_info_t *image_info, WCHAR *filename, +extern NTSTATUS load_builtin( const pe_image_info_t *image_info, UNICODE_STRING *nt_name, void **addr_ptr, SIZE_T *size_ptr, ULONG_PTR zero_bits ) DECLSPEC_HIDDEN; extern BOOL is_builtin_path( const UNICODE_STRING *path, WORD *machine ) DECLSPEC_HIDDEN; extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const WCHAR *curdir, WCHAR **image, diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 2a00ac52811..6576248a27f 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2202,7 +2202,7 @@ static NTSTATUS map_pe_header( void *ptr, size_t size, int fd, BOOL *removable ) * Map an executable (PE format) image into an existing view. * virtual_mutex must be held by caller. */ -static NTSTATUS map_image_into_view( struct file_view *view, const WCHAR *filename, int fd, void *orig_base, +static NTSTATUS map_image_into_view( struct file_view *view, UNICODE_STRING *nt_name, int fd, void *orig_base, SIZE_T header_size, ULONG image_flags, int shared_fd, BOOL removable ) { IMAGE_DOS_HEADER *dos; @@ -2218,7 +2218,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const WCHAR *filena char *ptr = view->base; SIZE_T total_size = view->size;
- TRACE_(module)( "mapping PE file %s at %p-%p\n", debugstr_w(filename), ptr, ptr + total_size ); + TRACE_(module)( "mapping PE file %s at %p-%p\n", debugstr_us(nt_name), ptr, ptr + total_size );
/* map the header */
@@ -2292,7 +2292,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const WCHAR *filena if (sec->VirtualAddress > total_size || end > total_size || end < sec->VirtualAddress) { WARN_(module)( "%s section %.8s too large (%x+%lx/%lx)\n", - debugstr_w(filename), sec->Name, (int)sec->VirtualAddress, map_size, total_size ); + debugstr_us(nt_name), sec->Name, (int)sec->VirtualAddress, map_size, total_size ); return status; }
@@ -2300,13 +2300,13 @@ static NTSTATUS map_image_into_view( struct file_view *view, const WCHAR *filena (sec->Characteristics & IMAGE_SCN_MEM_WRITE)) { TRACE_(module)( "%s mapping shared section %.8s at %p off %x (%x) size %lx (%lx) flags %x\n", - debugstr_w(filename), sec->Name, ptr + sec->VirtualAddress, + debugstr_us(nt_name), sec->Name, ptr + sec->VirtualAddress, (int)sec->PointerToRawData, (int)pos, file_size, map_size, (int)sec->Characteristics ); if (map_file_into_view( view, shared_fd, sec->VirtualAddress, map_size, pos, VPROT_COMMITTED | VPROT_READ | VPROT_WRITE, FALSE ) != STATUS_SUCCESS) { - ERR_(module)( "Could not map %s shared section %.8s\n", debugstr_w(filename), sec->Name ); + ERR_(module)( "Could not map %s shared section %.8s\n", debugstr_us(nt_name), sec->Name ); return status; }
@@ -2327,7 +2327,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const WCHAR *filena }
TRACE_(module)( "mapping %s section %.8s at %p off %x size %x virt %x flags %x\n", - debugstr_w(filename), sec->Name, ptr + sec->VirtualAddress, + debugstr_us(nt_name), sec->Name, ptr + sec->VirtualAddress, (int)sec->PointerToRawData, (int)sec->SizeOfRawData, (int)sec->Misc.VirtualSize, (int)sec->Characteristics );
@@ -2345,7 +2345,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const WCHAR *filena removable ) != STATUS_SUCCESS) { ERR_(module)( "Could not map %s section %.8s, file probably truncated\n", - debugstr_w(filename), sec->Name ); + debugstr_us(nt_name), sec->Name ); return status; }
@@ -2381,7 +2381,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const WCHAR *filena
if (!set_vprot( view, ptr + sec->VirtualAddress, size, vprot ) && (vprot & VPROT_EXEC)) ERR( "failed to set %08x protection on %s section %.8s, noexec filesystem?\n", - (int)sec->Characteristics, debugstr_w(filename), sec->Name ); + (int)sec->Characteristics, debugstr_us(nt_name), sec->Name ); }
#ifdef VALGRIND_LOAD_PDB_DEBUGINFO @@ -2446,7 +2446,7 @@ static unsigned int get_mapping_info( HANDLE handle, ACCESS_MASK access, unsigne */ static NTSTATUS virtual_map_image( HANDLE mapping, ACCESS_MASK access, void **addr_ptr, SIZE_T *size_ptr, ULONG_PTR zero_bits, HANDLE shared_file, ULONG alloc_type, - pe_image_info_t *image_info, WCHAR *filename, BOOL is_builtin ) + pe_image_info_t *image_info, UNICODE_STRING *nt_name, BOOL is_builtin ) { unsigned int vprot = SEC_IMAGE | SEC_FILE | VPROT_COMMITTED | VPROT_READ | VPROT_EXEC | VPROT_WRITECOPY; int unix_fd = -1, needs_close; @@ -2479,7 +2479,7 @@ static NTSTATUS virtual_map_image( HANDLE mapping, ACCESS_MASK access, void **ad if (status) status = map_view( &view, NULL, size, alloc_type & MEM_TOP_DOWN, vprot, get_zero_bits_mask( zero_bits ), 0 ); if (status) goto done;
- status = map_image_into_view( view, filename, unix_fd, base, image_info->header_size, + status = map_image_into_view( view, nt_name, unix_fd, base, image_info->header_size, image_info->image_flags, shared_fd, needs_close ); if (status == STATUS_SUCCESS) { @@ -2524,7 +2524,6 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P ACCESS_MASK access; SIZE_T size; pe_image_info_t *image_info = NULL; - WCHAR *filename; void *base; int unix_handle = -1, needs_close; unsigned int vprot, sec_flags; @@ -2560,12 +2559,13 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P
if (image_info) { - filename = (WCHAR *)(image_info + 1); + UNICODE_STRING nt_name; + init_unicode_string( &nt_name, (WCHAR *)(image_info + 1) ); /* check if we can replace that mapping with the builtin */ - res = load_builtin( image_info, filename, addr_ptr, size_ptr, zero_bits ); + res = load_builtin( image_info, &nt_name, addr_ptr, size_ptr, zero_bits ); if (res == STATUS_IMAGE_ALREADY_LOADED) res = virtual_map_image( handle, access, addr_ptr, size_ptr, zero_bits, shared_file, - alloc_type, image_info, filename, FALSE ); + alloc_type, image_info, &nt_name, FALSE ); if (shared_file) NtClose( shared_file ); free( image_info ); return res; @@ -2787,8 +2787,8 @@ NTSTATUS virtual_map_builtin_module( HANDLE mapping, void **module, SIZE_T *size HANDLE shared_file; pe_image_info_t *image_info = NULL; ACCESS_MASK access = SECTION_MAP_READ | SECTION_MAP_EXECUTE; + UNICODE_STRING nt_name; NTSTATUS status; - WCHAR *filename;
if ((status = get_mapping_info( mapping, access, &sec_flags, &full_size, &shared_file, &image_info ))) return status; @@ -2797,27 +2797,27 @@ NTSTATUS virtual_map_builtin_module( HANDLE mapping, void **module, SIZE_T *size
*module = NULL; *size = 0; - filename = (WCHAR *)(image_info + 1); + init_unicode_string( &nt_name, (WCHAR *)(image_info + 1) );
if (!(image_info->image_flags & IMAGE_FLAGS_WineBuiltin)) /* ignore non-builtins */ { - WARN( "%s found in WINEDLLPATH but not a builtin, ignoring\n", debugstr_w(filename) ); + WARN( "%s found in WINEDLLPATH but not a builtin, ignoring\n", debugstr_us(&nt_name) ); status = STATUS_DLL_NOT_FOUND; } else if (machine && image_info->machine != machine) { - TRACE( "%s is for arch %04x, continuing search\n", debugstr_w(filename), image_info->machine ); + TRACE( "%s is for arch %04x, continuing search\n", debugstr_us(&nt_name), image_info->machine ); status = STATUS_IMAGE_MACHINE_TYPE_MISMATCH; } else if (prefer_native && (image_info->dll_charact & IMAGE_DLLCHARACTERISTICS_PREFER_NATIVE)) { - TRACE( "%s has prefer-native flag, ignoring builtin\n", debugstr_w(filename) ); + TRACE( "%s has prefer-native flag, ignoring builtin\n", debugstr_us(&nt_name) ); status = STATUS_IMAGE_ALREADY_LOADED; } else { status = virtual_map_image( mapping, SECTION_MAP_READ | SECTION_MAP_EXECUTE, - module, size, zero_bits, shared_file, 0, image_info, filename, TRUE ); + module, size, zero_bits, shared_file, 0, image_info, &nt_name, TRUE ); virtual_fill_image_information( image_info, info ); }
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 | 113 ++++++++++++++++++++++++++++++++- 4 files changed, 142 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 42111cec8ca..6d276daaead 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -1439,6 +1439,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 * @@ -1489,6 +1513,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; } @@ -1619,6 +1644,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 7ee7012a175..f43e7025f34 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -158,6 +158,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 6576248a27f..fe0c55d3932 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2566,6 +2566,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) notify_gdb_native_dll_loaded( *addr_ptr, &nt_name ); if (shared_file) NtClose( shared_file ); free( image_info ); return res; @@ -4774,6 +4775,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 b7805473da8..adbe952e96c 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,13 +60,23 @@ struct r_debug *wine_r_debug = NULL;
#ifdef __linux__
+struct link_map_entry +{ + struct link_map map; + const void *module; + struct list entry; +}; + +static struct list pe_link_map_entries = LIST_INIT( pe_link_map_entries ); +static struct link_map so_link_map; +static struct link_map pe_link_map = {.l_prev = &so_link_map, .l_name = (char *)""}; static struct link_map so_link_map = {.l_name = (char *)""}; static pthread_mutex_t link_map_lock = PTHREAD_MUTEX_INITIALIZER;
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; @@ -65,15 +84,18 @@ static void sync_wine_link_map(void)
pthread_mutex_lock( &link_map_lock );
+ /* 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; (*wine_map)->l_addr = (*rtld_map)->l_addr; (*wine_map)->l_name = strdup( (*rtld_map)->l_name ); (*wine_map)->l_ld = (*rtld_map)->l_ld; @@ -95,12 +117,97 @@ static void sync_wine_link_map(void) free( prev ); }
+ /* link PE link map back */ + pe_link_map.l_prev->l_next = &pe_link_map; + pthread_mutex_unlock( &link_map_lock );
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( 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; + } + + pthread_mutex_unlock( &link_map_lock ); + + sync_wine_link_map(); +} + +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; + + pthread_mutex_unlock( &link_map_lock ); + + sync_wine_link_map(); + + free( entry->map.l_name ); + free( entry ); +} + void *dlopen( const char *file, int mode ) { static typeof(dlopen) *rtld_dlopen;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=127205
Your paranoid android.
=== debian11 (32 bit report) ===
ntdll: virtual: Timeout
v4: Fix notifications for native modules too. I believe this let GDB know about test executables as well third party DLLs.
This merge request was closed by Rémi Bernon.