https://bugs.winehq.org/show_bug.cgi?id=23757
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |focht@gmx.net Summary|Free Pascal 2.4.0 segfaults |Free Pascal 2.4.0 segfaults | |(Cygwin 1.5.18 TLS | |implementation overwrites | |Wine/glibc/pthread data | |near Tib->StackBase)
--- Comment #14 from Anastasius Focht focht@gmx.net --- Hello folks,
confirming, Wine still core dumps which is of course a serious matter.
Tracing is useless here because the app - or rather Cygwin - really messes with things at low level, making even debugging hard.
I found some references hinting at Cygwin version cygwin-1.5.18-1 (the 'cygwin1.dll' is from 2005-19-12).
It core dumps on a secondary thread creation.
--- snip --- 61002F43 894424 08 MOV DWORD PTR SS:[ESP+8],EAX 61002F47 31C0 XOR EAX,EAX 61002F49 894424 04 MOV DWORD PTR SS:[ESP+4],EAX 61002F4D C70424 D8910E61 MOV DWORD PTR SS:[ESP],610E91D8 61002F54 E8 97D30D00 CALL <JMP.&KERNEL32.CreateThread> ...
00CAED2C 610E91D8 ; pSecurity = cygwin1.610E91D8 -> SECURITY_ATTRIBUTES {Length=12., pSecurityDescriptor=NULL, InheritHandle=FALSE} 00CAED30 00000000 ; StackSize = 0 00CAED34 61003160 ; StartAddress = cygwin1.61003160 00CAED38 610E6D18 ; Parameter = 610E6D18 00CAED3C 00000000 ; CreationFlags = 0 00CAED40 610E6D1C ; pThreadId = cygwin1.610E6D1C -> 0 --- snip ---
The specified thread entry point is not immediately executed. Instead, Cygwin injects a nifty thread entry wrapper with the help of 'thread attach' notification from its 'dllmain' and really starts messing with things.
Starting at TIB StackBase, Cygwin uses a rather large chunk (~1 page) for the implementation of some TLS concept.
https://sourceware.org/viewvc/src/winsup/cygwin/cygtls.cc?diff_format=h&...
https://sourceware.org/viewvc/src/winsup/cygwin/cygtls.h?diff_format=h&v...
https://sourceware.org/viewvc/src/winsup/cygwin/sigproc.cc?diff_format=h&...
--- snip --- extern exception_list *_except_list asm ("%fs:0"); // exceptions.cc extern char *_tlsbase __asm__ ("%fs:4"); // cygtls.h extern char *_tlstop __asm__ ("%fs:8"); // cygtls.h
#define _my_tls (((_cygtls *) _tlsbase)[-1]) // cygtls.h --- snip ---
tlsbase = fs:[4] -> Wine TIB StackBase
This stack area (~4K) is padded with zeros (memset).
On all secondary threads, Wine's 'debug_info' structure is partially located in this stack area. The main thread uses static storage for this.
http://source.winehq.org/git/wine.git/blob/2fcecdb72e11b7adb03dc797c2ccc60fc...
The same thing happens for the main thread, one page gets "zapped", destroying everything that was there - without serious consequences (yet).
The reason for the core dump on secondary threads is indeed interesting ...
Debugging session, thread creation from the caller side -> 'RtlCreateUserThread':
http://source.winehq.org/git/wine.git/blob/2fcecdb72e11b7adb03dc797c2ccc60fc...
--- snip ---
Wine-dbg>p *info
{teb=0x7ffd0000, entry_point=0x61003160, entry_arg=0x610e6d18}
Wine-dbg>p teb->Tib
{ExceptionList=0xffffffff, StackBase=0x19270000, StackLimit=0x18f92000, SubSystemTib=0x0(nil), u={FiberData=0x0(nil), Version=0}, ArbitraryUserPointer=0x0(nil), Self=0x7ffd0000}
Wine-dbg>p *thread_data
{dr0=0, dr1=0, dr2=0, dr3=0, dr6=0x4000, dr7=0, fs=0x63, gs=0x6b, vm86_ptr=0x0(nil), debug_info=0x1926eb84, request_fd=0xc, reply_fd=0x9, wait_fd={0x10, 0x11}, wow64_redir=0, pthread_id=0x1926fb40, vm86={dpmi_vif=0, vm86_pending=0}, exit_frame=0x0(nil)}
Wine-dbg>x/10x 0x1926fb4c
0x1926fb4c: 00000000 00000000 00000000 00000000 0x1926fb5c: 00000000 00000000 00000000 00000000 0x1926fb6c: 00000000 00000000
Wine-dbg>n 543 if (pthread_create( &pthread_id, &attr, (void * (*)(void *))start_thread, info ))
Wine-dbg>x/10x 0x1926fb4c 0x1926fb4c: 00000001 f7774420 41418100 bb4a20c1 0x1926fb5c: 00000000 00000000 00000000 00000000 0x1926fb6c: 00000000 00000000 --- snip ---
The data dumps of certain stack areas will be of importance later. Just keep in mind that glibc/pthread library code puts internal data here.
Now debugging of the thread initialization from the other side. 'Winedbg' must be used in gdb proxy mode, otherwise the frame switch/locals won't yield results. You can't break directly at 'start_thread' for obvious reasons.
--- snip --- Wine-gdb> bt
#0 MODULE_DllThreadAttach (lpReserved=0x0) at /home/focht/projects/wine/wine.repo/src/dlls/ntdll/loader.c:1275 #1 0x7bc8d4cc in start_thread (info=0x7ffd0fb8) at /home/focht/projects/wine/wine.repo/src/dlls/ntdll/thread.c:424 #2 0xf75009dc in start_thread () from /lib/libpthread.so.0 #3 0xf7433a1e in clone () from /lib/libc.so.6
Wine-gdb> frame 1
#1 0x7bc8d4cc in start_thread (info=0x7ffd0fb8) at /home/focht/projects/wine/wine.repo/src/dlls/ntdll/thread.c:424 429 call_thread_entry_point( (LPTHREAD_START_ROUTINE)func, arg );
Wine-gdb> info locals
teb = 0x7ffd0000 thread_data = 0x7ffd01bc func = 0x61003160 arg = 0x610e6d18 debug_info = {str_pos = 0x1926eb8c "", out_pos = 0x1926ef8c "", strings = '\000' <repeats 1023 times>, output = '\000' <repeats 1023 times>} --- snip ---
I switched one stack frame up to show the address ranges being used at early thread initialization stage. The stack area in question spans [0x1926ebxx..0x1926ffff].
At later point, when the Cygwin thread entry wrapper runs, before switching to the real entry point:
--- snip --- Wine-dbg>x/10x 0x1926fb40
0x1926fb40: 1926fb40 7c49d5e0 1926fb40 00000001 0x1926fb50: f7737420 72241200 98b073c0 00000000 0x1926fb60: 00000000 00000000
...
Wine-dbg>info reg
Register dump: CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b EIP:610c4a77 ESP:1926da40 EBP:1926da48 EFLAGS:00010246( R- -- I Z- -P- ) EAX:00000000 EBX:00000000 ECX:0000012d EDX:00000000 ESI:1926f07c EDI:1926fb4c
Wine-dbg>si
0x610c4a77: repe stosl %es:(%edi)
Wine-dbg>si
0x610c4a77: repe stosl %es:(%edi)
Wine-dbg> Process of pid=0022 has terminated --- snip ---
0x1926fb40, 0x1926fb48 -> referencing 0x1926fb40 -> pthread_id ... 0x1926fb50 -> referencing 0xf7737420 -> syscall entry in VDSO page
--- snip --- Wine-dbg>x/10i 0xf7737420
0xf7737420 __kernel_vsyscall in [vdso].so: pushl %ecx 0xf7737421 __kernel_vsyscall+0x1 in [vdso].so: pushl %edx 0xf7737422 __kernel_vsyscall+0x2 in [vdso].so: pushl %ebp 0xf7737423 __kernel_vsyscall+0x3 in [vdso].so: movl %esp,%ebp 0xf7737425 __kernel_vsyscall+0x5 in [vdso].so: sysenter 0xf7737427 __kernel_vsyscall+0x7 in [vdso].so: nop --- snip ---
By overwriting everything until 'Tib->StackBase' with zero, Cygwin essentially kills libc/pthread specific stack storage, causing the core dump. SEH chain/__wine_exception_handler was already broken at this point, unable to handle SIGSEGV for diagnostics.
Maybe Cygwin changed their implementation in more recent versions to work differently given the code being 7+ years old. That rather boring investigation is left as exercise to the interested reader.
To prove my analysis, I modified the call to 'pthread_attr_setstack()', making it one page smaller than Wine allocated. This moves the glibc/pthread internal data by one page and also Wine's 'debug_info' which lives below, avoiding Cygwin's TLS code to zap this area. It makes the app work.
The application/GUI seems rather ancient, like a relict from old DOS times. It requires wineconsole/ncurses backend.
$ sha1sum fpc-2.4.0.i386-win32.exe 93b754bdc966e94fe002c624c275c4735e3551ab fpc-2.4.0.i386-win32.exe
$ du -sh fpc-2.4.0.i386-win32.exe 35M fpc-2.4.0.i386-win32.exe $ wine --version wine-1.7.33
Regards