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 | 93 +++++++++++++++++++++++++++++++++++++++++++++- loader/preloader.c | 36 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-)
diff --git a/loader/main.c b/loader/main.c index 214343ea26f..8667823de4a 100644 --- a/loader/main.c +++ b/loader/main.c @@ -49,12 +49,97 @@ const __attribute((visibility("default"))) struct wine_preload_info *wine_main_p
#ifdef __linux__
-/* the preloader will set this variable */ +/* the preloader will set these variables */ typedef void (*rtld_init_func)( struct link_map *map ); __attribute((visibility("default"))) rtld_init_func wine_rtld_init = NULL; +typedef void (*rtld_notify_func)(void); +__attribute((visibility("default"))) rtld_notify_func wine_rtld_map_start = NULL; +__attribute((visibility("default"))) rtld_notify_func wine_rtld_map_complete = NULL; +__attribute((visibility("default"))) rtld_notify_func wine_rtld_unmap_start = NULL; +__attribute((visibility("default"))) rtld_notify_func wine_rtld_unmap_complete = NULL;
+static pthread_mutex_t link_map_lock; static struct link_map link_map = {0};
+static void sync_wine_link_map(void) +{ + static struct r_debug *_r_debug; + struct link_map *next = &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 ); + } +} + +__attribute((visibility("default"))) 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 ); + + if (wine_rtld_map_start) wine_rtld_map_start(); + sync_wine_link_map(); + if (wine_rtld_map_complete) wine_rtld_map_complete(); + + pthread_mutex_unlock( &link_map_lock ); + + return ret; +} + +__attribute((visibility("default"))) 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 ); + + if (wine_rtld_unmap_start) wine_rtld_unmap_start(); + sync_wine_link_map(); + if (wine_rtld_unmap_complete) wine_rtld_unmap_complete(); + + pthread_mutex_unlock( &link_map_lock ); + + return ret; +} + #endif /* __linux__ */
/* canonicalize path and return its directory name */ @@ -193,6 +278,12 @@ 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 ); + if (wine_rtld_init) wine_rtld_init( &link_map ); #endif /* __linux__ */
diff --git a/loader/preloader.c b/loader/preloader.c index 49c4f0db9ad..81c639ee7a8 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -1432,6 +1432,28 @@ static void rtld_init( struct link_map *map ) STAP_PROBE2( rtld, init_complete, LM_ID_BASE, &_r_debug ); }
+static void rtld_map_start(void) +{ + STAP_PROBE2( rtld, map_start, LM_ID_BASE, &_r_debug ); +} + +static void rtld_map_complete(void) +{ + STAP_PROBE3( rtld, map_complete, LM_ID_BASE, &_r_debug, NULL ); + STAP_PROBE2( rtld, reloc_start, LM_ID_BASE, &_r_debug ); + STAP_PROBE3( rtld, reloc_complete, LM_ID_BASE, &_r_debug, NULL ); +} + +static void rtld_unmap_start(void) +{ + STAP_PROBE2( rtld, unmap_start, LM_ID_BASE, &_r_debug ); +} + +static void rtld_unmap_complete(void) +{ + STAP_PROBE2( rtld, unmap_complete, LM_ID_BASE, &_r_debug ); +} + /* * wld_start * @@ -1526,6 +1548,20 @@ void* wld_start( void **stack ) if (rtld_probe) *rtld_probe = rtld_init; else wld_printf( "wine_rtld_init not found\n" );
+ rtld_probe = find_symbol( &main_binary_map, "wine_rtld_map_start", STT_OBJECT ); + if (rtld_probe) *rtld_probe = rtld_map_start; + else wld_printf( "wine_rtld_map_start not found\n" ); + rtld_probe = find_symbol( &main_binary_map, "wine_rtld_map_complete", STT_OBJECT ); + if (rtld_probe) *rtld_probe = rtld_map_complete; + else wld_printf( "wine_rtld_map_complete not found\n" ); + + rtld_probe = find_symbol( &main_binary_map, "wine_rtld_unmap_start", STT_OBJECT ); + if (rtld_probe) *rtld_probe = rtld_unmap_start; + else wld_printf( "wine_rtld_unmap_start not found\n" ); + rtld_probe = find_symbol( &main_binary_map, "wine_rtld_unmap_complete", STT_OBJECT ); + if (rtld_probe) *rtld_probe = rtld_unmap_complete; + else wld_printf( "wine_rtld_unmap_complete 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)) );