Effectively implementing the debugger interface as described in https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/rtld-debugger-interface...
This is an actual attempt at having this upstream, split from https://gitlab.winehq.org/wine/wine/-/merge_requests/1074 which people have been finding useful for debugging Wine directly in GDB. This supports everything you would expect, from automatic symbol loading as well as cross-syscall backtraces with the new Python unwinder.
From: Rémi Bernon rbernon@codeweavers.com
--- tools/gitlab/image.docker | 1 + 1 file changed, 1 insertion(+)
diff --git a/tools/gitlab/image.docker b/tools/gitlab/image.docker index 8ff9055df2f..d7f2bd6edd9 100644 --- a/tools/gitlab/image.docker +++ b/tools/gitlab/image.docker @@ -50,6 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive; \ linux-libc-dev:amd64 linux-libc-dev:i386 \ ocl-icd-opencl-dev:amd64 ocl-icd-opencl-dev:i386 \ samba-dev:amd64 \ + systemtap-sdt-dev:amd64 systemtap-sdt-dev:i386 \ unixodbc-dev:amd64 unixodbc-dev:i386 \ x11proto-dev && \ apt-get install -y ccache netbase curl ca-certificates xserver-xorg-video-dummy xserver-xorg xfonts-base xinit fvwm \
From: Rémi Bernon rbernon@codeweavers.com
--- configure.ac | 1 + 1 file changed, 1 insertion(+)
diff --git a/configure.ac b/configure.ac index 8d92e4c8ab2..3d5172d45db 100644 --- a/configure.ac +++ b/configure.ac @@ -461,6 +461,7 @@ AC_CHECK_HEADERS(\ sys/random.h \ sys/resource.h \ sys/scsiio.h \ + sys/sdt.h \ sys/shm.h \ sys/signal.h \ sys/socketvar.h \
From: Rémi Bernon rbernon@codeweavers.com
And debugger interface as described in https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/rtld-debugger-interface... --- loader/main.c | 19 +++++++++++++++++ loader/preloader.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+)
diff --git a/loader/main.c b/loader/main.c index c258e025183..214343ea26f 100644 --- a/loader/main.c +++ b/loader/main.c @@ -33,6 +33,12 @@ #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"
@@ -41,6 +47,16 @@ extern char **environ; /* the preloader will set this variable */ const __attribute((visibility("default"))) struct wine_preload_info *wine_main_preload_info = NULL;
+#ifdef __linux__ + +/* the preloader will set this variable */ +typedef void (*rtld_init_func)( struct link_map *map ); +__attribute((visibility("default"))) rtld_init_func wine_rtld_init = NULL; + +static struct link_map link_map = {0}; + +#endif /* __linux__ */ + /* canonicalize path and return its directory name */ static char *realpath_dirname( const char *name ) { @@ -176,6 +192,9 @@ static void *load_ntdll( char *argv0 ) int main( int argc, char *argv[] ) { void *handle; +#ifdef __linux__ + if (wine_rtld_init) wine_rtld_init( &link_map ); +#endif /* __linux__ */
if ((handle = load_ntdll( argv[0] ))) { diff --git a/loader/preloader.c b/loader/preloader.c index d0551bae63a..49c4f0db9ad 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -85,12 +85,20 @@ #ifdef HAVE_SYS_LINK_H # include <sys/link.h> #endif +#ifdef HAVE_SYS_SDT_H +# include <sys/sdt.h> +#else +# define STAP_PROBE2(a,b,c,d) +# define STAP_PROBE3(a,b,c,d,e) +#endif
#include "wine/asm.h" #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) #define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0) @@ -1387,6 +1395,42 @@ static void set_process_name( int argc, char *argv[] ) for (i = 1; i < argc; i++) argv[i] -= off; }
+/* GDB integration */ +extern char __executable_start; +__attribute((visibility("default"))) void _dl_debug_state(void) {} +__attribute((visibility("default"))) struct r_debug _r_debug = +{ + .r_version = 1, /* no support for r_next */ + .r_state = RT_CONSISTENT, + .r_brk = (ElfW(Addr))(uintptr_t)_dl_debug_state, +}; + +/* sets the preloader r_debug address into DT_DEBUG */ +static void init_r_debug( struct wld_auxv *av ) +{ + const char *l_addr = &__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; +} + +static void rtld_init( struct link_map *map ) +{ + STAP_PROBE2( rtld, init_start, LM_ID_BASE, &_r_debug ); + _r_debug.r_map = map; + STAP_PROBE2( rtld, init_complete, LM_ID_BASE, &_r_debug ); +}
/* * wld_start @@ -1403,6 +1447,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 +1477,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 +1522,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)) );
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)) );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/ntdll/unix/virtual.c | 35 +++++++++++- loader/main.c | 114 +++++++++++++++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 75e6319c007..dc46026f094 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -79,6 +79,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(virtual); WINE_DECLARE_DEBUG_CHANNEL(module); WINE_DECLARE_DEBUG_CHANNEL(virtual_ranges);
+/* Gdb integration, in loader/main.c */ +static void (*wine_gdb_dll_loaded)( const void *module, const char *unix_path, INT_PTR offset ); +static void (*wine_gdb_dll_unload)( const void *module ); + struct preload_info { void *addr; @@ -2670,6 +2674,27 @@ static IMAGE_BASE_RELOCATION *process_relocation_block( char *page, IMAGE_BASE_R }
+/*********************************************************************** + * notify_gdb_dll_loaded + */ +static void notify_gdb_dll_loaded( void *module, const WCHAR *filename, INT_PTR offset ) +{ + UNICODE_STRING redir, nt_name; + OBJECT_ATTRIBUTES attr; + char *unix_path = NULL; + + RtlInitUnicodeString( &nt_name, filename ); + InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, 0 ); + get_redirect( &attr, &redir ); + + if (!nt_to_unix_file_name( &attr, &unix_path, FILE_OPEN )) + wine_gdb_dll_loaded( module, unix_path, offset ); + + free( redir.Buffer ); + free( unix_path ); +} + + /*********************************************************************** * map_image_into_view * @@ -2900,6 +2925,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const WCHAR *filena #ifdef VALGRIND_LOAD_PDB_DEBUGINFO VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, ptr - (char *)wine_server_get_ptr( image_info->base )); #endif + if (wine_gdb_dll_loaded) notify_gdb_dll_loaded( ptr, filename, ptr - (char *)wine_server_get_ptr( image_info->base ) ); return STATUS_SUCCESS; }
@@ -3256,6 +3282,9 @@ void virtual_init(void) int i; pthread_mutexattr_t attr;
+ wine_gdb_dll_loaded = dlsym( RTLD_DEFAULT, "wine_gdb_dll_loaded" ); + wine_gdb_dll_unload = dlsym( RTLD_DEFAULT, "wine_gdb_dll_unload" ); + pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); pthread_mutex_init( &virtual_mutex, &attr ); @@ -5598,7 +5627,11 @@ static NTSTATUS unmap_view_of_section( HANDLE process, PVOID addr, ULONG flags ) SERVER_END_REQ; if (!status) { - if (view->protect & SEC_IMAGE) release_builtin_module( view->base ); + if (view->protect & SEC_IMAGE) + { + if (wine_gdb_dll_unload) wine_gdb_dll_unload( view->base ); + release_builtin_module( view->base ); + } if (flags & MEM_PRESERVE_PLACEHOLDER) free_pages_preserve_placeholder( view, view->base, view->size ); else delete_view( view ); } diff --git a/loader/main.c b/loader/main.c index 8667823de4a..ad4a8f4c6bf 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; @@ -58,18 +67,43 @@ __attribute((visibility("default"))) rtld_notify_func wine_rtld_map_complete = N __attribute((visibility("default"))) rtld_notify_func wine_rtld_unmap_start = NULL; __attribute((visibility("default"))) rtld_notify_func wine_rtld_unmap_complete = NULL;
+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 link_map = {0}; +static struct list dll_map_entries = LIST_INIT( dll_map_entries ); + +static char *link_realpath( const char *path ) +{ + char *real; + if (!path) return NULL; + if (!(real = realpath( path, NULL ))) return strdup( path ); + return real; +}
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; + struct link_map_entry *pe_entry = NULL; + struct list *ptr;
if (!_r_debug) _r_debug = dlsym( RTLD_NEXT, "_r_debug" ); rtld_map = &_r_debug->r_map; wine_map = &next;
+ /* detach the PE link map */ + if ((ptr = list_head( &dll_map_entries ))) + { + pe_entry = LIST_ENTRY( ptr, struct link_map_entry, entry ); + if (pe_entry->map.l_prev) pe_entry->map.l_prev->l_next = NULL; + } + while (*rtld_map) { if (!*wine_map) @@ -81,7 +115,7 @@ static void sync_wine_link_map(void) 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_name = link_realpath( (*rtld_map)->l_name ); (*wine_map)->l_ld = (*rtld_map)->l_ld; rtld_map = &(*rtld_map)->l_next; wine_map = &(*wine_map)->l_next; @@ -100,6 +134,84 @@ static void sync_wine_link_map(void) free( tmp->l_name ); free( tmp ); } + + if (pe_entry) + { + /* attach PE link map back */ + pe_entry->map.l_prev = last; + last->l_next = &pe_entry->map; + } +} + +static void add_dll_to_pe_link_map( const void *module, const char *unix_path, INT_PTR offset ) +{ + struct link_map_entry *entry, *last_entry; + struct list *ptr; + + if (!(entry = calloc( 1, sizeof(*entry) ))) return; + + entry->module = module; + entry->map.l_addr = offset; + entry->map.l_name = link_realpath( unix_path ); + + if ((ptr = list_tail( &dll_map_entries ))) + { + last_entry = LIST_ENTRY( ptr, struct link_map_entry, entry ); + entry->map.l_prev = &last_entry->map; + last_entry->map.l_next = &entry->map; + } + + list_add_tail( &dll_map_entries, &entry->entry ); + if (!entry->map.l_prev) sync_wine_link_map(); +} + +__attribute((visibility("default"))) void wine_gdb_dll_loaded( const void *module, const char *unix_path, INT_PTR offset ) +{ + struct link_map_entry *entry; + + pthread_mutex_lock( &link_map_lock ); + + LIST_FOR_EACH_ENTRY( entry, &dll_map_entries, struct link_map_entry, entry ) + if (entry->module == module) break; + + if (&entry->entry == &dll_map_entries) + add_dll_to_pe_link_map( module, unix_path, offset ); + else + entry->map.l_addr = offset; + + if (wine_rtld_map_start) wine_rtld_map_start(); + if (wine_rtld_map_complete) wine_rtld_map_complete(); + + pthread_mutex_unlock( &link_map_lock ); +} + +__attribute((visibility("default"))) void wine_gdb_dll_unload( const void *module ) +{ + struct link_map *prev, *next; + struct link_map_entry *entry; + + pthread_mutex_lock( &link_map_lock ); + + LIST_FOR_EACH_ENTRY( entry, &dll_map_entries, struct link_map_entry, entry ) + if (entry->module == module) break; + + if (&entry->entry == &dll_map_entries) + { + pthread_mutex_unlock( &link_map_lock ); + return; + } + + list_remove( &entry->entry ); + if ((prev = entry->map.l_prev)) prev->l_next = entry->map.l_next; + if ((next = entry->map.l_next)) next->l_prev = entry->map.l_prev; + + if (wine_rtld_unmap_start) wine_rtld_unmap_start(); + if (wine_rtld_unmap_complete) wine_rtld_unmap_complete(); + + pthread_mutex_unlock( &link_map_lock ); + + free( entry->map.l_name ); + free( entry ); }
__attribute((visibility("default"))) void *dlopen( const char *file, int mode )
From: Rémi Bernon rbernon@codeweavers.com
--- tools/gdbunwind.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tools/gdbunwind.py
diff --git a/tools/gdbunwind.py b/tools/gdbunwind.py new file mode 100644 index 00000000000..aad303f3696 --- /dev/null +++ b/tools/gdbunwind.py @@ -0,0 +1,78 @@ +#!/bin/env python3 + +# Copyright 2021 Rémi Bernon for CodeWeavers +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +from gdb.unwinder import Unwinder +import gdb +import re + + +def registers(pc, sp, pending_frame): + ptr = gdb.lookup_type('void').pointer() + frame = sp + + if ptr.sizeof == 4: + frame += 16 if 'unix_call' in str(pc) else 32 + if ptr.sizeof == 8 and 'syscall' in str(pc): + rbp = pending_frame.read_register("rbp") + frame = rbp - 0x98 + + frame = gdb.execute(f'print *(struct syscall_frame *){int(frame)}', + from_tty=False, to_string=True) + + for line in frame.splitlines()[1:]: + if 'prev_frame' in line: break + if 'syscall_cfa' in line: break + if 'restore_flags' in line: continue + if 'syscall_flags' in line: continue + + reg, _, val = line.strip(',').split() + val = gdb.Value(int(val)).cast(ptr) + + if reg in ('eflags', 'cs', 'ss', 'ds', 'es', 'fs', 'gs'): + int32 = gdb.lookup_type('int') + val = val.cast(int32) + + yield reg, val + + +class WineSyscallFrameId(object): + def __init__(self, sp, pc): + self.sp = sp + self.pc = pc + + +class WineSyscallUnwinder(Unwinder): + def __init__(self): + super().__init__("WineSyscallUnwinder") + self.pattern = re.compile('__wine_(syscall|unix_call)') + + def __call__(self, pending_frame): + pc = pending_frame.read_register("pc") + if self.pattern.search(str(pc)) is None: + return None + + sp = pending_frame.read_register("sp") + frame = WineSyscallFrameId(sp, pc) + + unwind_info = pending_frame.create_unwind_info(frame) + for reg, val in registers(pc, sp, pending_frame): + unwind_info.add_saved_register(reg, val) + return unwind_info + + +gdb.unwinder.register_unwinder(None, WineSyscallUnwinder(), replace=True)
Not sure if this is relevant, but I'm currently trying to build wine on Linux on M1 Macs (aarch64, 16k pages) and this fails with your branch: ``` gcc -o loader/wine-preloader loader/preloader.o loader/preloader_mac.o -nostartfiles -nodefaultlibs -static \ -Wl,-Ttext=0x7d7d00000000 loader/preloader.o: in function `init_r_debug': /home/fabian/Programming/Wine/wine64/../wine-git/loader/preloader.c:1411:(.text+0x2568): relocation truncated to fit: R_AARCH64_ADR_PREL_PG_HI21 against symbol `__executable_start' defined in .note.gnu.build-id section in loader/wine-preloader collect2: error: ld returned 1 exit status ```
On Thu Dec 21 12:37:55 2023 +0000, Fabian Maurer wrote:
Not sure if this is relevant, but I'm currently trying to build wine on Linux on M1 Macs (aarch64, 16k pages) and this fails with your branch:
gcc -o loader/wine-preloader loader/preloader.o loader/preloader_mac.o -nostartfiles -nodefaultlibs -static \ -Wl,-Ttext=0x7d7d00000000 loader/preloader.o: in function `init_r_debug': /home/fabian/Programming/Wine/wine64/../wine-git/loader/preloader.c:1411:(.text+0x2568): relocation truncated to fit: R_AARCH64_ADR_PREL_PG_HI21 against symbol `__executable_start' defined in .note.gnu.build-id section in loader/wine-preloader collect2: error: ld returned 1 exit status
I think this is a LLD specific failure, looks like the same happens on x86_64. Funnily LLD doesn't even generate a PT_DYNAMIC program header, and so doesn't have DT_DEBUG entry where to put _r_debug anyway.
Fabian Maurer (@DarkShadow44) commented about dlls/ntdll/unix/virtual.c:
#ifdef VALGRIND_LOAD_PDB_DEBUGINFO VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, ptr - (char *)wine_server_get_ptr( image_info->base )); #endif
- if (wine_gdb_dll_loaded) notify_gdb_dll_loaded( ptr, filename, ptr - (char *)wine_server_get_ptr( image_info->base ) );
I found an issue here while trying to debug msvbvm50.exe from winetricks (vb5run).
The exe unpacks its own ADVPACK.DLL in its current working directory. Due to the loading order, wine still loads its builtin advpack.dll. But it still sets the module filename to `Z:/home/fabian/.wine/drive_c/users/fabian/Temp/IXP010.TMP/ADVPACK.DLL` According to Alexandre this is intentional, since programs should not know the DLL was substituted for a builtin one. However, this leads to gdb loading the symbols for the wrong DLL, making debugging advpack impossible. I guess we need to get the unix path and convert it to nt before we pass it to GDB, unless you have a better idea?
Also see [MR 4873](https://gitlab.winehq.org/wine/wine/-/merge_requests/4573) and [bug 56211](https://bugs.winehq.org/show_bug.cgi?id=56211)
I found another issue: When changing a DLL and adding a few newlines, then GDB still uses the old line numbers for the stacktrace / breakpoints. `wineboot -u` after changing the DLL helps.