| This file describes where to start debugging Wine and how to write |
| useful bug reports. |
| |
| Crashes |
| ======= |
| |
| These usually show up like this: |
| |
| |Unexpected Windows program segfault - opcode = 8b |
| |Segmentation fault in Windows program 1b7:c41. |
| |Loading symbols from ELF file /root/wine/wine... |
| |....more Loading symbols from ... |
| |In 16 bit mode. |
| |Register dump: |
| | CS:01b7 SS:016f DS:0287 ES:0000 |
| | IP:0c41 SP:878a BP:8796 FLAGS:0246 |
| | AX:811e BX:0000 CX:0000 DX:0000 SI:0001 DI:ffff |
| |Stack dump: |
| |0x016f:0x878a: 0001 016f ffed 0000 0000 0287 890b 1e5b |
| |0x016f:0x879a: 01b7 0001 000d 1050 08b7 016f 0001 000d |
| |0x016f:0x87aa: 000a 0003 0004 0000 0007 0007 0190 0000 |
| |0x016f:0x87ba: |
| | |
| |0050: sel=0287 base=40211d30 limit=0b93f (bytes) 16-bit rw- |
| |Backtrace: |
| |0 0x01b7:0x0c41 (PXSRV_FONGETFACENAME+0x7c) |
| |1 0x01b7:0x1e5b (PXSRV_FONPUTCATFONT+0x2cd) |
| |2 0x01a7:0x05aa |
| |3 0x01b7:0x0768 (PXSRV_FONINITFONTS+0x81) |
| |4 0x014f:0x03ed (PDOXWIN_@SQLCURCB$Q6CBTYPEULN8CBSCTYPE+0x1b1) |
| |5 0x013f:0x00ac |
| | |
| |0x01b7:0x0c41 (PXSRV_FONGETFACENAME+0x7c): movw %es:0x38(%bx),%dx |
| |
| Steps to debug a crash. You may stop at any step, but please report the bug |
| and provide as much of the information gathered to the newsgroup or the |
| relevant developer as feasonable. |
| |
| 1. Get the reason for the crash. This is usually an access to an invalid |
| selector, an access to an out of range address in a valid selector, |
| popping a segmentregister from the stack or the like. When reporting a |
| crash, report this WHOLE crashdump even if it doesn't make sense to you. |
| |
| (In this case it is access to an invalid selector, for %es is 0000, as |
| seen in the register dump). |
| |
| 2. Determine where the reason came from. |
| Since this is usually a primary/secondary reaction to a failed or |
| misbehaving Wine function, rerun Wine with "-debugmsg +relay" (without ") |
| added to the commandline. This will get rather much output, but usually |
| the reason is located in the last call(s). Those lines usually look like |
| this: |
| |
| |Call KERNEL.90: LSTRLEN(0227:0692 "text") ret=01e7:2ce7 ds=0227 |
| ^^^^^^^^^ ^ ^^^^^^^^^ ^^^^^^ ^^^^^^^^^ ^^^^ |
| | | | | | |Datasegment |
| | | | | |Return address |
| | | | |textual parameter |
| | | | |
| | | |Argument(s). This one is a win16 segmented pointer. |
| | |Function called. |
| |The module, the function is called in. In this case it is KERNEL. |
| |
| |Ret KERNEL.90: LSTRLEN() retval=0x0004 ret=01e7:2ce7 ds=0227 |
| ^^^^^^ |
| |Returnvalue is 16 bit and has the value 4. |
| |
| |
| 3. If you have found a misbehaving function, try to find out why it |
| misbehaves. Find the function in the source code. Try to make sense of |
| the arguments passed. Usually there is a |
| "dprintf_xyz(stddeb,"Function(...)"...);" at the beginning of the |
| function. Rerun wine with "-debugmsg +xyz,+relay" added to the |
| commandline. |
| |
| 4. Additional information on how to debug using the internal debugger can be |
| found in debugger/README. |
| |
| 5. If those information isn't clear enough or if you want to know more about |
| what's happening in the function itself, try running wine with "-debugmsg |
| +all", which dumps ALL included debug information in wine. |
| |
| 6. If that isn't enough add more debug output for yourself into the |
| functions you find relevant. |
| You might also try to run the program in gdb instead of using the |
| WINE-debugger. |
| |
| 7. You can also set a breakpoint for that function. Start wine with the |
| "-debug" option added to the commandline. After loading the executable |
| wine will enter the internal debugger. Use "break KERNEL_LSTRLEN" |
| (replace by function you want to debug, CASE IS RELEVANT.) to set a |
| breakpoint. Then use "continue" to start normal program-execution. Wine |
| will stop if it reaches the breakpoint. If the program isn't yet at the |
| crashing call of that function, use "continue" again until you are about |
| to enter that function. You may now proceed with single-stepping the |
| function until you reach the point of crash. Use the other debugger |
| commands to print registers and the like. |
| |
| |
| Program hangs, nothing happens |
| ============================== |
| |
| Switch to UNIX shell, get the process-ID using "ps -a|grep wine", and do a |
| "kill -HUP <pid>" (without " and <>). Wine will then enter its internal |
| debugger and you can proceed as explained above. Also, you can use -debug |
| switch and then you can get into internal debugger by pressing Ctrl-C in |
| the terminal where you run Wine. |
| |
| Program reports an error with a Messagebox |
| ========================================== |
| |
| Sometimes programs are reporting failure using a more or less nondescript |
| messageboxes. We can debug this using the same method as Crashes, but there |
| is one problem... For setting up a message box the program also calls Wine |
| producing huge chunks of debug code. |
| |
| Since the failure happens usually directly before setting up the Messagebox |
| you can start wine with "-debug" added to the commandline, set a breakpoint |
| at "MessageBox32A" (called by win16 and win32 programs) and proceed with |
| "continue". With "-debugmsg +all" Wine will now stop directly directly |
| before setting up the Messagebox. Proceed as explained above. |
| |
| You can also run wine using "wine -debugmsg +relay program.exe 2>&1|less -i" |
| and in less search for messagebox. |
| |
| Disassembling programs: |
| ======================= |
| You may also try to disassemble the offending program to check for |
| undocumented features and/or use of them. |
| |
| The best, freely available, disassembler for Win16 programs is |
| Windows Codeback, archivename wcbxxx.zip, which usually can be found |
| in the Cica-Mirror subdirectory on the WINE ftpsites. (See ANNOUNCE). |
| Disassembling win32 programs is possible using the Windows Disassembler 32, |
| archivename something like w32dasm.zip on ftp.winsite.com and mirrors. |
| The shareware version does not allow saving of disassembly listings. |
| |
| [It also has a bug, it disassembles the dll and immediately after that |
| crashes, leaving a very large file caled 'winsys' in the directory of the |
| disassembled file. This file contains nothing of value (just the disassembly) |
| and can be safely deleted.] |
| |
| Understanding disassembled code is just a question of exercise. |
| |
| Most code out there uses standard C function entries (for it is usually |
| written in C). Win16 function entries usually look like that: |
| | push bp |
| | mov bp, sp |
| | ... function code .. |
| | retf XXXX <--------- XXXX is number of bytes of arguments |
| |
| This is a FAR function with no local storage. The arguments usually start |
| at [bp+6] with increasing offsets. Note, that [bp+6] belongs to the RIGHTMOST |
| argument, for exported win16 functions use the PASCAL calling convention. |
| So, if we use strcmp(a,b) with a and b both 32 bit variables b would be at |
| [bp+6] and a at [bp+10]. |
| Most functions make also use of local storage in the stackframe: |
| | enter 0086, 00 |
| | ... function code ... |
| | leave |
| | retf XXXX |
| This does mostly the same as above, but also adds 0x86 bytes of |
| stackstorage, which is accessed using [bp-xx]. |
| Before calling a function, arguments are pushed on the stack using something |
| like this: |
| | push word ptr [bp-02] <- will be at [bp+8] |
| | push di <- will be at [bp+6] |
| | call KERNEL.LSTRLEN |
| Here first the selector and then the offset to the passed string are pushed. |
| |
| Sample debugging session: |
| ========================= |
| |
| Let's debug the infamous Word SHARE.EXE messagebox: |
| |
| |marcus@jet $ wine winword.exe |
| | +---------------------------------------------+ |
| | | ! You must leave Windows and load SHARE.EXE| |
| | | before starting Word. | |
| | +---------------------------------------------+ |
| |
| |
| |marcus@jet $ wine winword.exe -debugmsg +relay -debug |
| |CallTo32(wndproc=0x40065bc0,hwnd=000001ac,msg=00000081,wp=00000000,lp=00000000) |
| |Win16 task 'winword': Breakpoint 1 at 0x01d7:0x001a |
| |CallTo16(func=0127:0070,ds=0927) |
| |Call WPROCS.24: TASK_RESCHEDULE() ret=00b7:1456 ds=0927 |
| |Ret WPROCS.24: TASK_RESCHEDULE() retval=0x8672 ret=00b7:1456 ds=0927 |
| |CallTo16(func=01d7:001a,ds=0927) |
| | AX=0000 BX=3cb4 CX=1f40 DX=0000 SI=0000 DI=0927 BP=0000 ES=11f7 |
| |Loading symbols: /home/marcus/wine/wine... |
| |Stopped on breakpoint 1 at 0x01d7:0x001a |
| |In 16 bit mode. |
| |Wine-dbg>break MessageBox32A <---- Set Breakpoint |
| |Breakpoint 2 at 0x40189100 (MessageBox32A [msgbox.c:190]) |
| |Wine-dbg>c <---- Continue |
| |Call KERNEL.91: INITTASK() ret=0157:0022 ds=08a7 |
| | AX=0000 BX=3cb4 CX=1f40 DX=0000 SI=0000 DI=08a7 ES=11d7 EFL=00000286 |
| |CallTo16(func=090f:085c,ds=0dcf,0x0000,0x0000,0x0000,0x0000,0x0800,0x0000,0x0000,0x0dcf) |
| |... <----- Much debugoutput |
| |Call KERNEL.136: GETDRIVETYPE(0x0000) ret=060f:097b ds=0927 |
| ^^^^^^ Drive 0 (A:) |
| |Ret KERNEL.136: GETDRIVETYPE() retval=0x0002 ret=060f:097b ds=0927 |
| ^^^^^^ DRIVE_REMOVEABLE |
| (It is a floppy diskdrive.) |
| |
| |Call KERNEL.136: GETDRIVETYPE(0x0001) ret=060f:097b ds=0927 |
| ^^^^^^ Drive 1 (B:) |
| |Ret KERNEL.136: GETDRIVETYPE() retval=0x0000 ret=060f:097b ds=0927 |
| ^^^^^^ DRIVE_CANNOTDETERMINE |
| (I don't have drive B: assigned) |
| |
| |Call KERNEL.136: GETDRIVETYPE(0x0002) ret=060f:097b ds=0927 |
| ^^^^^^^ Drive 2 (C:) |
| |Ret KERNEL.136: GETDRIVETYPE() retval=0x0003 ret=060f:097b ds=0927 |
| ^^^^^^ DRIVE_FIXED |
| (specified as a harddisk) |
| |
| |Call KERNEL.97: GETTEMPFILENAME(0x00c3,0x09278364"doc",0x0000,0927:8248) ret=060f:09b1 ds=0927 |
| ^^^^^^ ^^^^^ ^^^^^^^^^ |
| | | |buffer for fname |
| | |temporary name ~docXXXX.tmp |
| |Force use of Drive C:. |
| |
| |Warning: GetTempFileName returns 'C:~doc9281.tmp', which doesn't seem to be writeable. |
| |Please check your configuration file if this generates a failure. |
| |
| Whoops, it even detects that something is wrong! |
| |
| |Ret KERNEL.97: GETTEMPFILENAME() retval=0x9281 ret=060f:09b1 ds=0927 |
| ^^^^^^ Temporary storage ID |
| |
| |Call KERNEL.74: OPENFILE(0x09278248"C:~doc9281.tmp",0927:82da,0x1012) ret=060f:09d8 ds=0927 |
| ^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^ |
| |filename |OFSTRUCT |open mode: |
| |
| OF_CREATE|OF_SHARE_EXCLUSIVE|OF_READWRITE |
| |
| This fails, since my C: drive is in this case mounted readonly. |
| |
| |Ret KERNEL.74: OPENFILE() retval=0xffff ret=060f:09d8 ds=0927 |
| ^^^^^^ HFILE_ERROR16, yes, it failed. |
| |
| |Call USER.1: MESSAGEBOX(0x0000,0x09278376"Sie müssen Windows verlassen und SHARE.EXE laden bevor Sie Word starten.",0x00000000,0x1030) ret=060f:084f ds=0927 |
| |
| And MessageBox'ed. |
| |
| |Stopped on breakpoint 2 at 0x40189100 (MessageBox32A [msgbox.c:190]) |
| |190 { <- the sourceline |
| In 32 bit mode. |
| Wine-dbg> |
| |
| The code seems to find a writeable harddisk and tries to create a file |
| there. To work around this bug, you can define C: as a networkdrive, |
| which is ignored by the code above. |
| |
| Written by Marcus Meissner <msmeissn@cip.informatik.uni-erlangen.de>, |
| additions welcome. |