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 | 100 ++++++++++++++++++++++++++++++++++++++++++++- loader/preloader.c | 38 +++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-)
diff --git a/loader/main.c b/loader/main.c index 242ff15accd..135aeb5b8c8 100644 --- a/loader/main.c +++ b/loader/main.c @@ -33,13 +33,102 @@ #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_rtld_init)( struct link_map *map ) = NULL; + +#ifdef __linux__ + +static pthread_mutex_t link_map_lock; +static struct link_map so_link_map = {0}; + +static void sync_wine_link_map(void) +{ + static struct r_debug *_r_debug; + struct link_map *next = &so_link_map, *last = NULL, **rtld_map, **wine_map; + + if (!_r_debug) _r_debug = dlsym( RTLD_NEXT, "_r_debug" ); + rtld_map = &_r_debug->r_map; + wine_map = &next; + + while (*rtld_map) + { + if (!*wine_map) + { + if (!(*wine_map = calloc( 1, sizeof(struct link_map) ))) break; + (*wine_map)->l_prev = last; + } + + last = *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_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 *tmp = next; + wine_map = &next->l_next; + next = *wine_map; + *wine_map = NULL; + free( tmp->l_name ); + free( tmp ); + } +} + +void *dlopen( const char *file, int mode ) +{ + static typeof(dlopen) *rtld_dlopen; + void *ret; + + pthread_mutex_lock( &link_map_lock ); + + if (!rtld_dlopen) rtld_dlopen = dlsym( RTLD_NEXT, "dlopen" ); + ret = rtld_dlopen( file, mode ); + + sync_wine_link_map(); + + pthread_mutex_unlock( &link_map_lock ); + + return ret; +} + +int dlclose( void *handle ) +{ + static typeof(dlclose) *rtld_dlclose; + int ret; + + pthread_mutex_lock( &link_map_lock ); + + if (!rtld_dlclose) rtld_dlclose = dlsym( RTLD_NEXT, "dlclose" ); + ret = rtld_dlclose( handle ); + + sync_wine_link_map(); + + pthread_mutex_unlock( &link_map_lock ); + + return ret; +} + +#endif /* __linux__ */
/* canonicalize path and return its directory name */ static char *realpath_dirname( const char *name ) @@ -176,6 +265,15 @@ static void *load_ntdll( char *argv0 ) int main( int argc, char *argv[] ) { void *handle; +#ifdef __linux__ + pthread_mutexattr_t attr; + pthread_mutexattr_init( &attr ); + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); + pthread_mutex_init( &link_map_lock, &attr ); + pthread_mutexattr_destroy( &attr ); +#endif /* __linux__ */ + + if (wine_rtld_init) wine_rtld_init( &so_link_map );
if ((handle = load_ntdll( argv[0] ))) { diff --git a/loader/preloader.c b/loader/preloader.c index d0551bae63a..8e6575733ba 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -90,6 +90,7 @@ #include "main.h"
#pragma GCC visibility push(hidden) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/* ELF definitions */ #define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref) @@ -1387,6 +1388,36 @@ static void set_process_name( int argc, char *argv[] ) for (i = 1; i < argc; i++) argv[i] -= off; }
+/* GDB hooks integration */ +extern void *__executable_start; +struct r_debug _r_debug = {0}; +void _dl_debug_state(void) {} + +/* sets the preloader r_debug address into DT_DEBUG */ +static void init_r_debug( struct wld_auxv *av ) +{ + const char *l_addr = (void *)&__executable_start; + ElfW(Phdr) *phdr, *ph; + ElfW(Dyn) *dyn = NULL; + int phnum; + + if (!(phnum = get_auxiliary( av, AT_PHNUM, 0 ))) return; + if (!(phdr = (void *)get_auxiliary( av, AT_PHDR, 0 ))) return; + + for (ph = phdr; ph < &phdr[phnum]; ++ph) if (ph->p_type == PT_DYNAMIC) break; + if (ph >= &phdr[phnum]) return; + + dyn = (void *)(ph->p_vaddr + l_addr); + while (dyn->d_tag != DT_DEBUG) dyn++; + + if (dyn->d_tag == DT_DEBUG) dyn->d_un.d_ptr = (uintptr_t)&_r_debug; +} + +void rtld_init( struct link_map *map ) +{ + _r_debug.r_map = map; + _dl_debug_state(); +}
/* * wld_start @@ -1403,6 +1434,7 @@ 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 **rtld_probe;
pargc = *stack; argv = (char **)pargc + 1; @@ -1432,6 +1464,8 @@ void* wld_start( void **stack ) dump_auxiliary( av ); #endif
+ init_r_debug( av ); + /* reserve memory that Wine needs */ if (reserve) preload_reserve( reserve ); for (i = 0; preload_info[i].size; i++) @@ -1475,6 +1509,10 @@ 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" );
+ rtld_probe = find_symbol( &main_binary_map, "wine_rtld_init", STT_OBJECT ); + if (rtld_probe) *rtld_probe = rtld_init; + else wld_printf( "wine_rtld_init 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)) );