383 lines
17 KiB
Plaintext
383 lines
17 KiB
Plaintext
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 "MessageBox32A" (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 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.
|
||
-------
|
||
|
||
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.
|