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)) );