| This file describes where to start debugging Wine. If at any point | |
| you get stuck and want to ask for help, please read the file | |
| documentation/bugreports for information on 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 'TRACE(<channel>,"(...)\n");' | |
| 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. See documentation/debug-msgs. | |
| You might also try to run the program in gdb instead of using the | |
| WINE-debugger. If you do that, use "handle SIGSEGV nostop noprint" | |
| to disable the handling of seg faults inside gdb (needed for Win16). | |
| If you don't use the "-desktop" or "-managed" option, | |
| start the WINE process with "-sync", or chances are good to get X into | |
| an unusable state. | |
| 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 "MessageBoxA" (called by win16 and win32 programs) and proceed with | |
| "continue". With "-debugmsg +all" Wine will now stop 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 w32dsm87.zip (or similar) on ftp.winsite.com | |
| and mirrors. The shareware version does not allow saving of disassembly | |
| listings. | |
| You can also use the newer (and in the full version better) Interactive | |
| Disassembler (IDA) from the ftp sites mentioned at the end of the document. | |
| Understanding disassembled code is mostly 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 MessageBoxA <---- Set Breakpoint | |
| |Breakpoint 2 at 0x40189100 (MessageBoxA [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 (MessageBoxA [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. | |
| ------- | |
| Here are some useful debugging tips, added by Andreas Mohr: | |
| a) If you have a program crashing at such an early loader phase that you can't | |
| use the Wine debugger normally, but Wine already executes the program's | |
| start code, then you may use a special trick: | |
| You should do a | |
| wine -debugmsg +relay program | |
| to get a listing of the functions the program calls in its start function. | |
| Now you do a | |
| wine -debug winfile.exe | |
| This way, you get into Wine-dbg. Now you can set a breakpoint on any | |
| function the program calls in the start function and just type "c" to bypass | |
| the eventual calls of Winfile to this function until you are finally at the | |
| place where this function gets called by the crashing start function. | |
| Now you can proceed with your debugging as usual. | |
| b) If you try to run a program and it quits after showing an error messagebox, | |
| the problem can usually be identified in the return value of one of the | |
| functions executed before MessageBox(). | |
| That's why you should re-run the program with e.g. | |
| wine -debugmsg +relay <program name> &>relmsg | |
| Then do a "more relmsg" and search for the last occurrence of a call to the string "MESSAGEBOX". | |
| This is a line like | |
| Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff | |
| In my example the lines before the call to MessageBox() look like that: | |
| Call KERNEL.96: FREELIBRARY(0x0347) ret=01cf:1033 ds=01ff | |
| CallTo16(func=033f:0072,ds=01ff,0x0000) | |
| Ret KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:1033 ds=01ff | |
| Call KERNEL.96: FREELIBRARY(0x036f) ret=01cf:1043 ds=01ff | |
| CallTo16(func=0367:0072,ds=01ff,0x0000) | |
| Ret KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:1043 ds=01ff | |
| Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff | |
| CallTo16(func=0317:0072,ds=01ff,0x0000) | |
| Ret KERNEL.96: FREELIBRARY() retval=0x0001 ret=01cf:105c ds=01ff | |
| Call USER.171: WINHELP(0x02ac,0x01ff05b4 "COMET.HLP",0x0002,0x00000000) ret=01cf:1070 ds=01ff | |
| CallTo16(func=0117:0080,ds=01ff) | |
| Call WPROCS.24: TASK_RESCHEDULE() ret=00a7:0a2d ds=002b | |
| Ret WPROCS.24: TASK_RESCHEDULE() retval=0x0000 ret=00a7:0a2d ds=002b | |
| Ret USER.171: WINHELP() retval=0x0001 ret=01cf:1070 ds=01ff | |
| Call KERNEL.96: FREELIBRARY(0x01be) ret=01df:3e29 ds=01ff | |
| Ret KERNEL.96: FREELIBRARY() retval=0x0000 ret=01df:3e29 ds=01ff | |
| Call KERNEL.52: FREEPROCINSTANCE(0x02cf00ba) ret=01f7:1460 ds=01ff | |
| Ret KERNEL.52: FREEPROCINSTANCE() retval=0x0001 ret=01f7:1460 ds=01ff | |
| Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff | |
| I think that the call to MessageBox() in this example is _not_ caused | |
| by a wrong result value of some previously executed function (it's | |
| happening quite often like that), but instead the messagebox complains | |
| about a runtime error at 0x0004:0x1056. | |
| As the segment value of the address is only "4", I think that that is | |
| only an internal program value. But the offset address reveals something | |
| quite interesting: | |
| Offset 1056 is _very_ close to the return address of FREELIBRARY(): | |
| Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff | |
| ^^^^ | |
| Provided that segment 0x0004 is indeed | |
| segment 0x1cf, we now we can use IDA (available at | |
| ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip) | |
| to disassemble the part that caused the error. We just have to find | |
| the address of the call to FreeLibrary(). Some lines before that the | |
| runtime error occurred. But be careful ! In some cases you don't have | |
| to disassemble the main program, but instead some DLL called by it in | |
| order to find the correct place where the runtime error occurred. That | |
| can be determined by finding the origin of the segment value (in this | |
| case 0x1cf). | |
| c) If you have created a relay file of some crashing program and want to set a | |
| breakpoint at a certain location which is not yet available as the | |
| program loads the breakpoint's segment during execution, | |
| you may set a breakpoint to GetVersion16/32 as those functions are called | |
| very often. | |
| Then do a "c" until you are able to set this breakpoint without error message. | |
| d) Some useful programs: | |
| IDA: ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip | |
| *Very* good DOS disassembler ! It's badly needed for debugging Wine sometimes. | |
| XRAY: ftp://ftp.th-darmstadt.de/pub/machines/ms-dos/SimTel/msdos/asmutil/xray15.zip | |
| Traces DOS calls (Int 21h, DPMI, ...). Use it with Windows to correct | |
| file management problems etc. | |
| pedump: http://oak.oakland.edu/pub/simtelnet/win95/prog/pedump.zip | |
| Dumps the imports and exports of a PE (Portable Executable) DLL. | |
| Some basic debugger usages: | |
| =========================== | |
| After starting you program with | |
| wine -debug myprog.exe | |
| the program loads and you get a prompt at the program starting point. | |
| Then you can set breakpoints: | |
| b RoutineName (by outine name) OR | |
| b *0x812575 (by address) | |
| Then you hit 'c' (continue) to run the program. It stops at | |
| the breakpoint. You can type | |
| step (to step one line) OR | |
| stepi (to step one machine instruction at a time; | |
| here, it helps to know the basic 386 | |
| instruction set) | |
| info reg (to see registers) | |
| info stack (to see hex values in the stack) | |
| info local (to see local variables) | |
| list <line number> (to list source code) | |
| x <variable name> (to examine a variable; only works if code | |
| is not compiled with optimization) | |
| x 0x4269978 (to examine a memory location) | |
| ? (help) | |
| q (quit) | |
| By hitting Enter, you repeat the last command. |