Before going into the depths of debugging in Wine, here's a small overview of process and thread handling in Wine. It has to be clear that there are two different beasts: processes/threads from the Unix point of view and processes/threads from a Windows point of view.
Each Windows' thread is implemented as a Unix thread, meaning that all threads of a same Windows' process share the same (unix) address space.
In the following:
W-process means a process in Windows' terminology
U-process means a process in Unix' terminology
W-thread means a thread in Windows' terminology
A W-process is made of one or several
W-threads. Each
W-thread is mapped to one and only one
U-process. All
U-processes of a same
W-process share the same address space.
Each Unix process can be identified by two values:
the Unix process id (upid in the following)
the Windows's thread id (tid)
Each Windows' process has also a Windows' process id
(wpid in the following). It must be clear
that upid and wpid are
different and shall not be used instead of the other.
Wpid and tid are
defined (Windows) system wide. They must not be confused
with process or thread handles which, as any handle, is an
indirection to a system object (in this case process or
thread). A same process can have several different handles
on the same kernel object. The handles can be defined as
local (the values is only valid in a process), or system
wide (the same handle can be used by any
W-process).
When talking of debugging in Wine, there are at least two levels to think of:
the Windows' debugging API.
the Wine integrated debugger, dubbed winedbg.
Wine implements most of the Windows' debugging API. The first part of the debugging APIs (in KERNEL32.DLL) allows a W-process, called the debugger, to control the execution of another W-process, the debuggee. To control means stopping/resuming execution, enabling/disabling single stepping, setting breakpoints, reading/writing debuggee memory... Another part of the debugging APIs resides in DBGHELP.DLL (and its ancestor IMAGEHLP.DLL) and lets a debugger look into symbols and types from any module (if the module has been compiled with the proper options).
winedbg is a Winelib application making use of these APIs (KERNEL32.DLL's debugging API and DBGHELP.DLL) to allow debugging both any Wine or Winelib applications as well as Wine itself (kernel and all DLLs).
Any application (either a Windows' native executable, or a Winelib application) can be run through winedbg. Command line options and tricks are the same as for wine:
winedbg telnet.exe
winedbg hl.exe -windowed
winedbg can also be launched without any
command line argument: winedbg is started
without any attached process. You can get a list of running
W-processes (and their
wpid's) using the info
process command, and then, with the
attach command, pick up the
wpid of the W-process
you want to debug. This is a neat feature as it allows you
to debug an already started application.
When something goes wrong, Windows tracks this as an exception. Exceptions exist for segmentation violation, stack overflow, division by zero, etc.
When an exception occurs, Wine checks if the
W-process is debugged. If so, the
exception event is sent to the debugger, which takes care of
it: end of the story. This mechanism is part of the standard
Windows' debugging API.
If the W-process is not debugged, Wine
tries to launch a debugger. This debugger (normally
winedbg, see III Configuration for more
details), at startup, attaches to the
W-process which generated the exception
event. In this case, you are able to look at the causes of
the exception, and either fix the causes (and continue
further the execution) or dig deeper to understand what went
wrong.
If winedbg is the standard debugger, the pass and cont commands are the two ways to let the process go further for the handling of the exception event.
To be more precise on the way Wine (and Windows) generates exception events, when a fault occurs (segmentation violation, stack overflow...), the event is first sent to the debugger (this is known as a first chance exception). The debugger can give two answers:
the debugger had the ability to correct what's generated the exception, and is now able to continue process execution.
the debugger couldn't correct the cause of the first chance exception. Wine will now try to walk the list of exception handlers to see if one of them can handle the exception. If no exception handler is found, the exception is sent once again to the debugger to indicate the failure of the exception handling.
Note: since some of Wine's code uses exceptions and
try/catchblocks to provide some functionality, winedbg can be entered in such cases with segv exceptions. This happens, for example, withIsBadReadPtrfunction. In that case, the pass command shall be used, to let the handling of the exception to be done by thecatchblock inIsBadReadPtr.
You can stop the debugger while it's running by hitting Ctrl-C in its window. This will stop the debugged process, and let you manipulate the current context.
Wine supports the new XP APIs, allowing for a debugger to detach from a program being debugged (see detach command).
This section describes where to start debugging Wine. If at any point you get stuck and want to ask for help, please read the How to Report A Bug section of the Wine Users Guide for information on how to write useful bug reports.
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 bug report as feasible.
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 segment register 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).
Determine the cause of the crash. Since this is usually
a primary/secondary reaction to a failed or misbehaving
Wine function, rerun Wine with the WINEDEBUG=+relay
environment variable set. This will
generate quite a lot of 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.
If you have found a misbehaving Wine 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 WINE_DEFAULT_DEBUG_CHANNEL(<channel>);
at the beginning of the source file. Rerun wine with the
WINEDEBUG=+xyz,+relay environment variable set.
Occasionally there are additional debug channels defined at the
beginning of the source file in the form
WINE_DECLARE_DEBUG_CHANNEL(<channel>);
If so the offending function may also uses one of these alternate
channels. Look through the the function for
TRACE_(<channel>)(" ... /n"); and add any
additional channels to the commandline.
Additional information on how to debug using the internal debugger can be found in programs/winedbg/README.
If this information isn't clear enough or if you want to
know more about what's happening in the function itself,
try running wine with WINEDEBUG=+all,
which dumps ALL included debug information in wine.
It is often necessary to limit the debug output produced.
That can be done by piping the output through grep,
or alternatively with registry keys. See
Section 1.5.3 for more information.
If even that isn't enough, add more debug output for yourself
into the functions you find relevant. See The section on Debug
Logging in this guide for more information. 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).
You can also set a breakpoint for that function. Start wine
using winedbg instead of
wine. Once the debugger is running enter
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.
Start the program with winedbg instead of wine. When the program locks up switch to the winedbg's terminal and press Ctrl-C. This will stop the program and let you debug the program as you would for a crash.
Sometimes programs are reporting failure using 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 winedbg and set a
breakpoint at MessageBoxA (called by win16
and win32 programs) and proceed with
continue. With WINEDEBUG=+all
Wine will now stop directly before setting
up the Messagebox. Proceed as explained above.
You can also run wine using WINEDEBUG=+relay wine program.exe 2>&1 | less -i and in less search for "MessageBox".
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, archive name wcbxxx.zip (e.g. wcb105a.zip).
Disassembling win32 programs is possible using eg. GoVest by Ansgar Trimborn. It can be found here.
You can also use the newer and better Interactive Disassembler (IDA) from DataRescue. Take a look in the AppDB for links to various versions of IDA.
Another popular disassembler is Windows Disassembler 32 from URSoft. Look for a file called w32dsm87.zip (or similar) on winsite.com or softpedia.com. It seems that Windows Disassembler 32 currently has problems working correctly under Wine, so use IDA or GoVest.
Also of considerable fame amongst disassemblers is SoftIce from NuMega. That software has since been acquired by CompuWare (http://www.compuware.com/) and made part of their Windows driver development suite. Newer versions of SoftIce needs to run as a Windows Service and therefore won't currently work under Wine.
If nothing works for you, you might try one of the disassemblers found in Google's directory.
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.
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 $ WINEDEBUG=+relay,-debug wine winword.exe
|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 writable.
|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"You must close Windows and load SHARE.EXE before you start Word.",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 writable 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.
Here are some additional debugging tips:
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
WINEDEBUG=+relay wine program
to get a listing of the functions the program calls in its start function.
Now you do a
winedbg winfile.exe
This way, you get into winedbg. 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.
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.
WINEDEBUG=+relay wine <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 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).
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.
After starting your program with
winedbg myprog.exe
the program loads and you get a prompt at the program starting point. Then you can set breakpoints:
b RoutineName (by routine 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.
Some useful programs:
Simple win32 disassembler that works well with Wine.
IDA Pro is highly recommended, but is not free. DataRescue does however make trial versions available.
Take a look in the AppDB for links to various versions of IDA.
Traces DOS calls (Int 21h, DPMI, ...). Use it with Windows to correct file management problems etc.
Dumps the imports and exports of a PE (Portable Executable) DLL.
Dumps the imports and exports of a PE (Portable Executable) DLL.
Wine uses several different kinds of memory addresses.
Linear addresses can be everything from 0x0 up to 0xffffffff. In Wine on Linux they are often around e.g. 0x08000000, 0x00400000 (std. Win32 program load address), 0x40000000. Every Win32 process has its own private 4GB address space (that is, from 0x0 up to 0xffffffff).
These are the "normal" Win16 addresses, called SEGPTR. They have a segment:offset notation, e.g. 0x01d7:0x0012. The segment part usually is a "selector", which always has the lowest 3 bits set. Some sample selectors are 0x1f7, 0x16f, 0x8f. If these bits are set except for the lowest bit, as e.g. with 0x1f6,xi then it might be a handle to global memory. Just set the lowest bit to get the selector in these cases. A selector kind of "points" to a certain linear (see above) base address. It has more or less three important attributes: segment base address, segment limit, segment access rights.
Example:
Selector 0x1f7 (0x40320000, 0x0000ffff, r-x) So 0x1f7 has a base address of 0x40320000, the segment's last address is 0x4032ffff (limit 0xffff), and it's readable and executable. So an address of 0x1f7:0x2300 would be the linear address of 0x40322300.
They, too, have a segment:offset notation. But they are completely different from "normal" Win16 addresses, as they just represent at most 1MB of memory: The segment part can be anything from 0 to 0xffff, and it's the same with the offset part.
Now the strange thing is the calculation that's behind these addresses: Just calculate segment*16 + offset in order to get a "linear DOS" address. So e.g. 0x0f04:0x3628 results in 0xf040 + 0x3628 = 0x12668. And the highest address you can get is 0xfffff (1MB), of course. In Wine, this "linear DOS" address of 0x12668 has to be added to the linear base address of the corresponding DOS memory allocated for dosmod in order to get the true linear address of a DOS seg:offs address. And make sure that you're doing this in the correct process with the correct linear address space, of course ;-)
The Windows' debugging API uses a registry entry to know which debugger to invoke when an unhandled exception occurs (see On exceptions for some details). Two values in key
[MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug]
Determine the behavior:
This is the command line used to launch the debugger
(it uses two printf formats
(%ld) to pass context dependent
information to the debugger). You should put here a
complete path to your debugger
(winedbg can of course be used, but
any other Windows' debugging API aware debugger will do).
The path to the debugger you choose to use must be reachable
via one of the DOS drives configured under /dosdevices in
your $WINEPREFIX or ~/.wine folder.
If this value is zero, a message box will ask the user if he/she wishes to launch the debugger when an unhandled exception occurs. Otherwise, the debugger is automatically started.
A regular Wine registry looks like:
[MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug] 957636538
"Auto"=dword:00000001
"Debugger"="winedbg %ld %ld"
Note 1: Creating this key is mandatory. Not doing so will not fire the debugger when an exception occurs.
Note 2: wineinstall (available in Wine source) sets up this correctly. However, due to some limitation of the registry installed, if a previous Wine installation exists, it's safer to remove the whole
[MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug]key before running again wineinstall to regenerate this key.
winedbg can be configured through a number of options. Those options are stored in the registry, on a per user basis. The key is (in my registry)
[HKCU\\Software\\Wine\\WineDbg]
Those options can be read/written while inside winedbg, as part of the debugger expressions. To refer to one of these options, its name must be prefixed by a $ sign. For example,
set $BreakAllThreadsStartup = 1
sets the option BreakAllThreadsStartup to
TRUE.
All the options are read from the registry when winedbg starts (if no corresponding value is found, a default value is used), and are written back to the registry when winedbg exits (hence, all modifications to those options are automatically saved when winedbg terminates).
Here's the list of all options:
BreakAllThreadsStartupSet to TRUE if at all threads start-up the debugger stops set to FALSE if only at the first thread startup of a given process the debugger stops. FALSE by default.
BreakOnCritSectTimeOutSet to TRUE if the debugger stops when a critical section times out (5 minutes); TRUE by default.
BreakOnAttachSet to TRUE if when winedbg attaches to an existing process after an unhandled exception, winedbg shall be entered on the first attach event. Since the attach event is meaningless in the context of an exception event (the next event which is the exception event is of course relevant), that option is likely to be FALSE.
BreakOnFirstChance An exception can generate two debug events. The first
one is passed to the debugger (known as a first
chance) just after the exception. The debugger can
then decide either to resume execution (see
winedbg's cont
command) or pass the exception up to the exception
handler chain in the program (if it exists)
(winedbg implements this through
the pass command). If none of the
exception handlers takes care of the exception, the
exception event is sent again to the debugger (known
as last chance exception). You cannot pass on a last
exception. When the
BreakOnFirstChance exception is
TRUE, then winedbg is entered for
both first and last chance execptions (to
FALSE, it's only entered for last
chance exceptions).
AlwaysShowThunkSet to TRUE if the debugger, when looking up for a symbol from its name, displays all the thunks with that name. The default value (FALSE) allows not to have to choose between a symbol and all the import thunks from all the DLLs using that symbols.
When setting WINEDEBUG to +relay and debugging,
you might get a lot of output. You can limit the output by
configuring the value RelayExclude in the registry, located
under the key:
[HKCU\\Software\\Wine\\Debug]
Set the value of RelayExclude to a semicolon-separated list of calls to exclude. Example: "RtlEnterCriticalSection;RtlLeaveCriticalSection;kernel32.97;kernel32.98".
RelayInclude is an option similar to RelayExclude, except that functions listed here will be the only ones included in the output. Also see section 3.4.3.5 for more options.
If your application runs too slow with +relay to get meaningful output and you're stuck with multi-GB relay log files, but you're not sure what to exclude, here's a trick to get you started. First, run your application for a minute or so, piping it's output to a file on disk:
WINEDEBUG=+relay wine <appname.exe> &>relay.log
Then run this command to see which calls are performed the most:
awk -F'(' '{print $1}' < relay.log | awk '{print $2}' | sort | uniq -c | sort
Exclude the bottom-most calls with RelayExclude after making sure that they are irrelevant, then run your application again.
Expressions in Wine Debugger are mostly written in a C form. However, there are a few discrepancies:
Identifiers can take a '!' in their names. This allow
mainly to access symbols from different DLLs like
USER32!CreateWindowExA.
In cast operation, when specifying a structure or an union, you must use the struct or union keyword (even if your program uses a typedef).
When specifying an identifier by its name, if several symbols with the same name exist, the debugger will prompt for the symbol you want to use. Pick up the one you want from its number.
In lots of cases, you can also use regular expressions for looking for a symbol.
winedbg defines its own set of variables. The configuration variables from above are part of them. Some others include:
$ThreadId ID of the W-thread currently
examined by the debugger
$ProcessId ID of the W-thread currently
examined by the debugger
All CPU registers are also available, using $ as a prefix. You can use info regs to get a list of avaible CPU registers
The $ThreadId and
$ProcessId variables can be handy to set
conditional breakpoints on a given thread or process.
Table 1-1. WineDbg's misc. commands
| abort | aborts the debugger |
| quit | exits the debugger |
| attach N | attach to a W-process (N is its ID, numeric or hexadecimal (0xN)). IDs can be obtained using the info process command. Note the info process command returns hexadecimal values. |
| detach | detach from a W-process. |
| help | prints some help on the commands |
| help info | prints some help on info commands |
Table 1-2. WineDbg's flow control commands
| cont, c | continue execution until next breakpoint or exception. |
| pass | pass the exception event up to the filter chain. |
| step, s | continue execution until next 'C' line of code (enters function call) |
| next, n | continue execution until next 'C' line of code (doesn't enter function call) |
| stepi, si | execute next assembly instruction (enters function call) |
| nexti, ni | execute next assembly instruction (doesn't enter function call) |
| finish, f | execute until current function is exited |
cont, step, next, stepi, nexti can be postfixed by a number (N), meaning that the command must be executed N times.
Table 1-3. WineDbg's break & watch points
| enable N | enables (break|watch)point #N |
| disable N | disables (break|watch)point #N |
| delete N | deletes (break|watch)point #N |
| cond N | removes any existing condition to (break|watch)point N |
| cond N <expr> | adds condition <expr> to (break|watch)point N. <expr> will be evaluated each time the breakpoint is hit. If the result is a zero value, the breakpoint isn't triggered |
| break * N | adds a breakpoint at address N |
| break <id> | adds a breakpoint at the address of symbol <id> |
| break <id> N | adds a breakpoint at the address of symbol <id> (N ?) |
| break N | adds a breakpoint at line N of current source file |
| break | adds a breakpoint at current $PC address |
| watch * N | adds a watch command (on write) at address N (on 4 bytes) |
| watch <id> | adds a watch command (on write) at the address of symbol <id> |
| info break | lists all (break|watch)points (with state) |
You can use the symbol EntryPoint to stand for the entry point of the Dll.
When setting a break/watch-point by <id>, if the symbol cannot be found (for example, the symbol is contained in a not yet loaded module), winedbg will recall the name of the symbol and will try to set the breakpoint each time a new module is loaded (until it succeeds).
Table 1-4. WineDbg's stack manipulation
| bt | print calling stack of current thread |
| bt N | print calling stack of thread of ID N (note: this doesn't change the position of the current frame as manipulated by the up and dn commands) |
| up | goes up one frame in current thread's stack |
| up N | goes up N frames in current thread's stack |
| dn | goes down one frame in current thread's stack |
| dn N | goes down N frames in current thread's stack |
| frame N | set N as the current frame for current thread's stack |
| info local | prints information on local variables for current function frame |
Table 1-5. WineDbg's directory & source file manipulation
| show dir | prints the list of dir:s where source files are looked for |
| dir <pathname> | adds <pathname> to the list of dir:s where to look for source files |
| dir | deletes the list of dir:s where to look for source files |
| symbolfile <pathname> | loads external symbol definition |
| symbolfile <pathname> N | loads external symbol definition (applying an offset of N to addresses) |
| list | lists 10 source lines forwards from current position |
| list - | lists 10 source lines backwards from current position |
| list N | lists 10 source lines from line N in current file |
| list <path>:N | lists 10 source lines from line N in file <path> |
| list <id> | lists 10 source lines of function <id> |
| list * N | lists 10 source lines from address N |
You can specify the end target (to change the 10 lines value) using the ','. For example:
A display is an expression that's evaluated and printed after the execution of any winedbg command.
winedbg will automatically detect if the expression you entered contains a local variable. If so, display will only be shown if the context is still in the same function as the one the debugger was in when the display expression was entered.
Table 1-7. WineDbg's displays
| info display | lists the active displays |
| display | print the active displays' values (as done each time the debugger stops) |
| display <expr> | adds a display for expression <expr> |
| display /fmt <expr> | adds a display for expression <expr>. Printing evaluated <expr> is done using the given format (see print command for more on formats) |
| del display N, undisplay N | deletes display #N |
Table 1-9. WineDbg's memory management
| x <expr> | examines memory at <expr> address |
| x /fmt <expr> | examines memory at <expr> address using format /fmt |
| print <expr> | prints the value of <expr> (possibly using its type) |
| print /fmt <expr> | prints the value of <expr> (possibly using its type) |
| set <lval> = <expr> | writes the value of <expr> in <lval> |
| whatis <expr> | prints the C type of expression <expr> |
/fmt is either /<letter> or /<count><letter> letter can be
| s | an ASCII string |
| u | an Unicode UTF16 string |
| i | instructions (disassemble) |
| x | 32 bit unsigned hexadecimal integer |
| d | 32 bit signed decimal integer |
| w | 16 bit unsigned hexadecimal integer |
| c | character (only printable 0x20-0x7f are actually printed) |
| b | 8 bit unsigned hexadecimal integer |
| g | GUID |
Table 1-10. WineDbg's Win32 objects management
| info class | lists all Windows' classes registered in Wine |
| info class <id> | prints information on Windows's class <id> |
| info share | lists all the dynamic libraries loaded in the debugged program (including .so files, NE and PE DLLs) |
| info share <N> | prints information on module at address <N> |
| info regs | prints the value of the CPU registers |
| info all-regs | prints the value of the CPU and Floating Point registers |
| info segment <N> | prints information on segment <N> (i386 only) |
| info segment | lists all allocated segments (i386 only) |
| info stack | prints the values on top of the stack |
| info map | lists all virtual mappings used by the debugged program |
| info map <N> | lists all virtual mappings used by the program of pid <N> |
| info wnd <N> | prints information of Window of handle <N> |
| info wnd | lists all the window hierarchy starting from the desktop window |
| info process | lists all w-processes in Wine session |
| info thread | lists all w-threads in Wine session |
| info exception | lists the exception frames (starting from current stack frame) |
It is possible to turn on and off debug messages as you are debugging using the set command. See Chapter 2 for more details on debug channels.
WineDbg can act as a remote monitor for GDB. This allows to
use all the power of GDB, but while debugging wine and/or
any Win32 application. To enable this mode, just add
--gdb to winedbg command line. You'll
end up on a GDB prompt. You'll have to use the GDB commands
(not WineDbg's).
However, some limitation in GDB while debugging wine (see below) don't appear in this mode:
GDB will correctly present Win32 thread information and breakpoint behavior
Moreover, it also provides support for the Dwarf II debug format (which became the default format (instead of stabs) in gcc 3.1).
A few Wine extensions available through the monitor command.
This section will describe how you can debug Wine using the GDB mode of winedbg and some graphical front ends to GDB for those of you who really like graphical debuggers.
Use the following steps, in this order:
Start the Wine debugger with a command line like:
winedbg --gdb --no-start <name_of_exe_to_debug.exe>
Start ddd
In ddd, use the 'Open File' or 'Open Program' to point to the Wine executable (which is either wine-pthread or wine-kthread depending on your settings).
In the output of 1/, there's a line like
target remote localhost:32878copy that line and paste into ddd command pane (the one with the (gdb) prompt)
Use the following steps, in this order:
Start the Wine debugger with a command line like:
winedbg --gdb --no-start <name_of_exe_to_debug.exe>
In the output of 1/, there's a line like
target remote localhost:32878Start kdbg with
kdbg -r localhost:32878 winelocalhost:32878 is not a fixed value, but has been printed in step 1/. 'wine' should also be the full path to the Wine executable (which is either wine-pthread or wine-kthread depending on your settings).
You can also use other debuggers (like gdb), but you must be aware of a few items:
You need to attach the unix debugger to the correct unix
process (representing the correct windows thread) (you can
"guess" it from a ps fax for example:
When running the emulator, usually the first two
upids are for the Windows' application
running the desktop, the first thread of the application is
generally the third upid; when running a
Winelib program, the first thread of the application is
generally the first upid)
Note: If you plan to used gdb for a multi-threaded Wine application (native or Winelib), then gdb will be able to handle the multiple threads directly only if:
In the unfortunate case (no direct thread support in gdb because one of the above conditions is false), you'll have to spawn a different gdb session for each Windows' thread you wish to debug (which means no synchronization for debugging purposes between the various threads).
Wine is running on the pthread model (it won't work in the kthread one). See the Wine architecture documentation for further details.
gdb supports the multi-threading (you need gdb at least 5.0 for that).
Here's how to get info about the current execution status of a certain Wine process:
Change into your Wine source dir and enter:
$ gdb wine
Switch to another console and enter ps ax | grep wine to find all wine processes. Inside gdb, repeat for all Wine processes:
(gdb) attach PID
with PID being the process ID of one of the Wine processes. Use
(gdb) bt
to get the backtrace of the current Wine process, i.e. the function call history. That way you can find out what the current process is doing right now. And then you can use several times:
(gdb) n
or maybe even
(gdb) b SomeFunction
and
(gdb) c
to set a breakpoint at a certain function and continue up to that function. Finally you can enter
(gdb) detach
to detach from the Wine process.
You can use any Windows' debugging API compliant debugger with Wine. Some reports have been made of success with VisualStudio debugger (in remote mode, only the hub runs in Wine). GoVest fully runs in Wine.
Table 1-13. Debuggers comparison
| WineDbg | gdb |
| WineDbg debugs a Windows' process: the various threads will be handled by the same WineDbg session, and a breakpoint will be triggered for any thread of the W-process | gdb debugs a Windows' thread: a separate gdb session is needed for each thread of a Windows' process and a breakpoint will be triggered only for the w-thread debugged |
| WineDbg supports debug information from stabs (standard Unix format) and Microsoft's C, CodeView, .DBG | GDB supports debug information from stabs (standard Unix format) and Dwarf II. |
To better manage the large volume of debugging messages that Wine can generate, we divide the messages on a component basis, and classify them based on the severity of the reported problem. Therefore a message belongs to a channel and a class respectively.
This section will describe the debugging classes, how you can create a new debugging channel, what the debugging API is, and how you can control the debugging output. A picture is worth a thousand words, so here are a few examples of the debugging API in action:
ERR("lock_count == 0 ... please report\n");
FIXME("Unsupported RTL style!\n");
WARN(": file seems to be truncated!\n");
TRACE("[%p]: new horz extent = %d\n", hwnd, extent );
MESSAGE( "Could not create graphics driver '%s'\n", buffer );
A debugging class categorizes a message based on the severity of the reported problem. There is a fixed set of classes, and you must carefully choose the appropriate one for your messages. There are five classes of messages:
Messages in this class are meant to signal unimplemented features, known bugs, etc. They serve as a constant and active reminder of what needs to be done.
Messages in this class indicate serious errors in Wine, such as as conditions that should never happen by design.
These are warning messages. You should report a warning when something unwanted happens, and the function cannot deal with the condition. This is seldomly used since proper functions can usually report failures back to the caller. Think twice before making the message a warning.
These are detailed debugging messages that are mainly useful to debug a component. These are turned off unless explicitly enabled.
There messages are intended for the end user. They do not belong to any channel. As with warnings, you will seldomly need to output such messages.
Each component is assigned a debugging channel. The identifier of the channel must be a valid C identifier (reserved word like int or static are premitted). To use a new channel, simply use it in your code. It will be picked up automatically by the build process.
Typically, a file contains code pertaining to only one component,
and as such, there is only one channel to output to. You can declare
a default chanel for the file using the
WINE_DEFAULT_DEBUG_CHANNEL() macro:
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(xxx);
...
FIXME("some unimplemented feature", ...);
...
if (zero != 0)
ERR("This should never be non-null: %d", zero);
...
In rare situations there is a need to output to more than one debug channel per file. In such cases, you need to declare all the additional channels at the top of the file, and use the _-version of the debugging macros:
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(xxx);
WINE_DECLARE_DEBUG_CHANNEL(yyy);
WINE_DECLARE_DEBUG_CHANNEL(zzz);
...
FIXME("this one goes to xxx channel");
...
FIXME_(yyy)("Some other msg for the yyy channel");
...
WARN_(zzz)("And yet another msg on another channel!");
...
To test whether the debugging channel xxx is
enabled, use the TRACE_ON, WARN_ON,
FIXME_ON, or ERR_ON macros. For
example:
if(TRACE_ON(atom)){
...blah...
}
You should normally need to test only if TRACE_ON,
all the others are very seldomly used. With careful coding, you
can avoid the use of these macros, which is generally desired.
Resource identifiers can be either strings or numbers. To
make life a bit easier for outputting these beasts (and to
help you avoid the need to build the message in memory), I
introduced a new function called debugres.
The function is defined in wine/debug.h and has the following prototype:
LPSTR debugres(const void *id);
It takes a pointer to the resource id and returns a nicely formatted string of the identifier (which can be a string or a number, depending on the value of the high word). Numbers are formatted as such:
#xxxx
while strings as:
'some-string'
Simply use it in your code like this:
#include "wine/debug.h"
...
TRACE("resource is %s", debugres(myresource));
Many times strings need to be massaged before output:
they may be NULL, contain control
characters, or they may be too long. Similarly, Unicode
strings need to be converted to ASCII for usage with
the debugging API. For all this, you can use the
debugstr_[aw]n? familly of functions:
HANDLE32 WINAPI YourFunc(LPCSTR s)
{
FIXME("(%s): stub\n", debugstr_a(s));
}
It is possible to turn on and off debugging output from within the debugger using the set command. Please see the WineDbg Command Reference section (Section 1.7.10) for how to do this.
You can do the same using the task manager (taskmgr) and selecting your application in the application list. Right clicking on the application, and selecting the debug option in the popup menu, will let you select the modifications you want on the debug channels.
Another way to conditionally log debug output (e.g. in case of very large installers which may create gigabytes of log output) is to create a pipe:
$ mknod /tmp/debug_pipe p
and then to run wine like that:
$ WINEDEBUG=+relay,+snoop wine setup.exe &>/tmp/debug_pipe
Since the pipe is initially blocking (and thus wine as a whole), you have to activate it by doing:
$ cat /tmp/debug_pipe
(press Ctrl-C to stop pasting the pipe content)
Once you are about to approach the problematic part of the program, you just do:
$ cat /tmp/debug_pipe >/tmp/wine.log
to capture specifically the part that interests you from the pipe without wasting excessive amounts of HDD space and slowing down installation considerably.
The WINEDEBUG environment variable
controls the output of the debug messages.
It has the following syntax:
WINEDEBUG= [yyy]#xxx[,[yyy1]#xxx1]*
where # is either + or -
when the optional class argument (yyy) is not present, then the statement will enable(+)/disable(-) all messages for the given channel (xxx) on all classes. For example:
WINEDEBUG=+reg,-file
enables all messages on the reg channel and disables all messages on the file channel.
when the optional class argument (yyy) is present, then the statement will enable (+)/disable(-) messages for the given channel (xxx) only on the given class. For example:
WINEDEBUG=trace+reg,warn-file
enables trace messages on the reg channel and disables warning messages on the file channel.
also, the pseudo-channel all is also supported and it has the intuitive semantics:
WINEDEBUG=+all -- enables all debug messages
WINEDEBUG=-all -- disables all debug messages
WINEDEBUG=yyy+all -- enables debug messages for class yyy on all
channels.
WINEDEBUG=yyy-all -- disables debug messages for class yyy on all
channels.
So, for example:
WINEDEBUG=warn-all -- disables all warning messages.
Also, note that at the moment:
the FIXME and ERR classes are enabled by default
the TRACE and WARN classes are disabled by default
To compile out the debugging messages, provide configure with the following options:
--disable-debug -- turns off TRACE, WARN, and FIXME (and DUMP).
--disable-trace -- turns off TRACE only.
This will result in an executable that, when stripped, is about 15%-20% smaller. Note, however, that you will not be able to effectively debug Wine without these messages.
This feature has not been extensively tested--it may subtly break some things.
This new scheme makes certain things more consistent but there is still room for improvement by using a common style of debug messages. Before I continue, let me note that the output format is the following:
yyy:xxx:fff <message>
where:
yyy = the class (fixme, err, warn, trace)
xxx = the channel (atom, win, font, etc)
fff = the function name
these fields are output automatically. All you have to provide is the <message> part.
So here are some ideas:
do not include the name of the function: it is included automatically
if you want to output the parameters of the function, do it as the first thing and include them in parentheses, like this:
TRACE("(%d, %p, ...)\n", par1, par2, ...);
if you want to name a parameter, use = :
TRACE("(fd=%d, file=%s): stub\n", fd, name);
for stubs, you should output a FIXME message. I suggest this style:
FIXME("(%x, %d, ...): stub\n", par1, par2, ...);
try to output one line per message. That is, the format string should contain only one \n and it should always appear at the end of the string.
if the output string needs to be dynamically constructed, render it in memory before outputting it:
char buffer[128] = "";
if (flags & FLAG_A) strcat(buffer, "FLAG_A ");
if (flags & FLAG_B) strcat(buffer, "FLAG_B ");
if (flags & FLAG_C) strcat(buffer, "FLAG_C ");
TRACE("flags = %s\n", buffer);
Most of the time however, it is better to create a helper
function that renders to a temporary buffer:
static const char *dbgstr_flags(int flags)
{
char buffer[128] = "";
if (flags & FLAG_A) strcat(buffer, "FLAG_A ");
if (flags & FLAG_B) strcat(buffer, "FLAG_B ");
if (flags & FLAG_C) strcat(buffer, "FLAG_C ");
return wine_dbg_sprintf("flags = %s\n\n", buffer);
}
...
TRACE("flags = %s\n", dbgstr_flags(flags));
The primary reason to do this is to reverse engineer a hardware device for which you don't have documentation, but can get to work under Wine.
This lot is aimed at parallel port devices, and in particular parallel port scanners which are now so cheap they are virtually being given away. The problem is that few manufactures will release any programming information which prevents drivers being written for Sane, and the traditional technique of using DOSemu to produce the traces does not work as the scanners invariably only have drivers for Windows.
Presuming that you have compiled and installed wine the first thing to do is to enable direct hardware access to your parallel port. To do this edit config (usually in ~/.wine/) and in the ports section add the following two lines
read=0x378,0x379,0x37a,0x37c,0x77a
write=0x378,x379,0x37a,0x37c,0x77a
This adds the necessary access required for SPP/PS2/EPP/ECP parallel port on LPT1. You will need to adjust these number accordingly if your parallel port is on LPT2 or LPT0.
When starting wine use the following command line, where XXXX is the program you need to run in order to access your scanner, and YYYY is the file your trace will be stored in:
WINEDEBUG=+io wine XXXX 2> >(sed 's/^[^:]*:io:[^ ]* //' > YYYY)
You will need large amounts of hard disk space (read hundreds of megabytes if you do a full page scan), and for reasonable performance a really fast processor and lots of RAM.
You will need to postprocess the output into a more manageable format, using the shrink program. First you need to compile the source (which is located at the end of this section):
cc shrink.c -o shrink
Use the shrink program to reduce the physical size of the raw log as follows:
cat log | shrink > log2
The trace has the basic form of
XXXX > YY @ ZZZZ:ZZZZ
where XXXX is the port in hexadecimal being accessed, YY is the data written (or read) from the port, and ZZZZ:ZZZZ is the address in memory of the instruction that accessed the port. The direction of the arrow indicates whether the data was written or read from the port.
> data was written to the port
< data was read from the port
My basic tip for interpreting these logs is to pay close attention to the addresses of the IO instructions. Their grouping and sometimes proximity should reveal the presence of subroutines in the driver. By studying the different versions you should be able to work them out. For example consider the following section of trace from my UMAX Astra 600P
0x378 > 55 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > aa @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > 00 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > 00 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > 00 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
0x378 > 00 @ 0297:01ec
0x37a > 05 @ 0297:01f5
0x379 < 8f @ 0297:01fa
0x37a > 04 @ 0297:0211
As you can see there is a repeating structure starting at address 0297:01ec that consists of four io accesses on the parallel port. Looking at it the first io access writes a changing byte to the data port the second always writes the byte 0x05 to the control port, then a value which always seems to 0x8f is read from the status port at which point a byte 0x04 is written to the control port. By studying this and other sections of the trace we can write a C routine that emulates this, shown below with some macros to make reading/writing on the parallel port easier to read.
#define r_dtr(x) inb(x)
#define r_str(x) inb(x+1)
#define r_ctr(x) inb(x+2)
#define w_dtr(x,y) outb(y, x)
#define w_str(x,y) outb(y, x+1)
#define w_ctr(x,y) outb(y, x+2)
/* Seems to be sending a command byte to the scanner */
int udpp_put(int udpp_base, unsigned char command)
{
int loop, value;
w_dtr(udpp_base, command);
w_ctr(udpp_base, 0x05);
for (loop=0; loop < 10; loop++)
if ((value = r_str(udpp_base)) & 0x80)
{
w_ctr(udpp_base, 0x04);
return value & 0xf8;
}
return (value & 0xf8) | 0x01;
}
For the UMAX Astra 600P only seven such routines exist (well 14 really, seven for SPP and seven for EPP). Whether you choose to disassemble the driver at this point to verify the routines is your own choice. If you do, the address from the trace should help in locating them in the disassembly.
You will probably then find it useful to write a script/perl/C program to analyse the logfile and decode them futher as this can reveal higher level grouping of the low level routines. For example from the logs from my UMAX Astra 600P when decoded further reveal (this is a small snippet)
start:
put: 55 8f
put: aa 8f
put: 00 8f
put: 00 8f
put: 00 8f
put: c2 8f
wait: ff
get: af,87
wait: ff
get: af,87
end: cc
start:
put: 55 8f
put: aa 8f
put: 00 8f
put: 03 8f
put: 05 8f
put: 84 8f
wait: ff
From this it is easy to see that put
routine is often grouped together in five successive calls
sending information to the scanner. Once these are understood
it should be possible to process the logs further to show the
higher level routines in an easy to see format. Once the
highest level format that you can derive from this process is
understood, you then need to produce a series of scans varying
only one parameter between them, so you can discover how to
set the various parameters for the scanner.
The following is the shrink.c program:
/* Copyright David Campbell <campbell@torque.net> */
#include <stdio.h>
#include <string.h>
int main (void)
{
char buff[256], lastline[256] = "";
int count = 0;
while (!feof (stdin))
{
fgets (buff, sizeof (buff), stdin);
if (strcmp (buff, lastline))
{
if (count > 1)
printf ("# Last line repeated %i times #\n", count);
printf ("%s", buff);
strcpy (lastline, buff);
count = 1;
}
else count++;
}
return 0;
}
Some background: On the i386 class of machines, stack entries are usually dword (4 bytes) in size, little-endian. The stack grows downward in memory. The stack pointer, maintained in the esp register, points to the last valid entry; thus, the operation of pushing a value onto the stack involves decrementing esp and then moving the value into the memory pointed to by esp (i.e., push p in assembly resembles *(--esp) = p; in C). Removing (popping) values off the stack is the reverse (i.e., pop p corresponds to p = *(esp++); in C).
In the stdcall calling convention, arguments are
pushed onto the stack right-to-left. For example, the C call
myfunction(40, 20, 70, 30); is expressed in
Intel assembly as:
push 30
push 70
push 20
push 40
call myfunction
The called function is responsible for removing the arguments
off the stack. Thus, before the call to myfunction, the
stack would look like:
[local variable or temporary]
[local variable or temporary]
30
70
20
esp -> 40
After the call returns, it should look like:
[local variable or temporary]
esp -> [local variable or temporary]
To restore the stack to this state, the called function must know how many arguments to remove (which is the number of arguments it takes). This is a problem if the function is undocumented.
One way to attempt to document the number of arguments each function takes is to create a wrapper around that function that detects the stack offset. Essentially, each wrapper assumes that the function will take a large number of arguments. The wrapper copies each of these arguments into its stack, calls the actual function, and then calculates the number of arguments by checking esp before and after the call.
The main problem with this scheme is that the function must actually be called from another program. Many of these functions are seldom used. An attempt was made to aggressively query each function in a given library (ntdll.dll) by passing 64 arguments, all 0, to each function. Unfortunately, Windows NT quickly goes to a blue screen of death, even if the program is run from a non-administrator account.
Another method that has been much more successful is to attempt to
figure out how many arguments each function is removing from the
stack. This instruction, ret hhll (where
hhll is the number of bytes to remove, i.e. the
number of arguments times 4), contains the bytes
0xc2 ll hh in memory. It is a reasonable
assumption that few, if any, functions take more than 16 arguments;
therefore, simply searching for
hh == 0 && ll < 0x40 starting from the
address of a function yields the correct number of arguments most
of the time.
Of course, this is not without errors. ret 00ll is not the only instruction that can have the byte sequence 0xc2 ll 0x0; for example, push 0x000040c2 has the byte sequence 0x68 0xc2 0x40 0x0 0x0, which matches the above. Properly, the utility should look for this sequence only on an instruction boundary; unfortunately, finding instruction boundaries on an i386 requires implementing a full disassembler -- quite a daunting task. Besides, the probability of having such a byte sequence that is not the actual return instruction is fairly low.
Much more troublesome is the non-linear flow of a function. For example, consider the following two functions:
somefunction1:
jmp somefunction1_impl
somefunction2:
ret 0004
somefunction1_impl:
ret 0008
In this case, we would incorrectly detect both
somefunction1 and
somefunction2 as taking only a single
argument, whereas somefunction1 really
takes two arguments.
With these limitations in mind, it is possible to implement more stubs in Wine and, eventually, the functions themselves.
A problem that can happen sometimes is 'it used to work before, now it doesn't anymore...'. Here is a step by step procedure to try to pinpoint when the problem occurred. This is NOT for casual users.
Clone the "Git" repository from winehq. It's more than 90Mb, so you it may take some time with a slow Internet connection.
If you found that something broke between wine-20041019 and wine-20050930 (these are [WWW] release tags). To start regression testing we run:
git bisect start git bisect good wine-20041019 git bisect bad wine-20050930
If you have exact date/time instead of a release you will need to use sha1 IDs from git log.
Having told Git when things were working and when they broke, it will automatically "position" your source tree to the middle. So all you need to do is build the source:
./configure && make clean && make depend && make ./wine 'c:\test.exe'If the version of Wine that Git picked still has the bug, run:
git bisect badand if it does not, run:
git bisect goodWhen you run this command, Git will checkout a new version of Wine for you to rebuild, so repeat this step again. When the regression has been isolated, git will inform you.
To find out what's left to test, try:
git bisect visualize.
When you have found the bad patch and want to go back to the current HEAD run:
git bisect reset
If you find the patch that is the cause of the problem, you have almost won; report about it to Wine Bugzilla or subscribe to wine-devel and post it there. There is a chance that the author will jump in to suggest a fix; or there is always the possibility to look hard at the patch until it is coerced to reveal where is the bug :-)
Deciding what code should be tested next can be a difficult decision. And in any given project, there is always code that isn't tested where bugs could be lurking. This section goes over how to identify these sections using a tool called gcov.
To use gcov on wine, do the following:
In order to activate code coverage in the wine source code, when running make set CFLAGS like so make CFLAGS="-fprofile-arcs -ftest-coverage". Note that this can be done at any directory level. Since compile and run time are significantly increased by these flags, you may want to only use these flags inside a given dll directory.
Run any application or test suite.
Run gcov on the file which you would like to know more about code coverage.
The following is an example situation when using gcov to
determine the coverage of a file could be helpful. We'll use
the dlls/lzexpand/lzexpand_main.c. file.
At one time the code in this file was not fully tested (as it
may still be). For example at the time of this writing, the
function LZOpenFileA had the following
lines in it:
if ((mode&~0x70)!=OF_READ)
return fd;
if (fd==HFILE_ERROR)
return HFILE_ERROR;
cfd=LZInit(fd);
if ((INT)cfd <= 0) return fd;
return cfd;
Currently there are a few tests written to test this function;
however, these tests don't check that everything is correct.
For instance, HFILE_ERROR may be the wrong
error code to return. Using gcov and directed tests, we can
validate the correctness of this line of code. First, we see
what has been tested already by running gcov on the file.
To do this, do the following:
git clone git://source.winehq.org/git/wine.git wine
mkdir build
cd build
../wine/configure
make depend && make CFLAGS="-fprofile-arcs -ftest-coverage"
cd dlls/lxexpand/tests
make test
cd ..
gcov ../../../wine/dlls/lzexpand/lzexpand_main.c
0.00% of 3 lines executed in file ../../../wine/include/wine/unicode.h
Creating unicode.h.gcov.
0.00% of 4 lines executed in file /usr/include/ctype.h
Creating ctype.h.gcov.
0.00% of 6 lines executed in file /usr/include/bits/string2.h
Creating string2.h.gcov.
100.00% of 3 lines executed in file ../../../wine/include/winbase.h
Creating winbase.h.gcov.
50.83% of 240 lines executed in file ../../../wine/dlls/lzexpand/lzexpand_main.c
Creating lzexpand_main.c.gcov.
less lzexpand_main.c.gcov
Note that there is more output, but only output of gcov is
shown. The output file
lzexpand_main.c.gcov looks like this.
9: 545: if ((mode&~0x70)!=OF_READ)
6: 546: return fd;
3: 547: if (fd==HFILE_ERROR)
#####: 548: return HFILE_ERROR;
3: 549: cfd=LZInit(fd);
3: 550: if ((INT)cfd <= 0) return fd;
3: 551: return cfd;
gcov output consists of three components:
the number of times a line was run, the line number, and the
actual text of the line. Note: If a line is optimized out by
the compiler, it will appear as if it was never run. The line
of code which returns HFILE_ERROR is
never executed (and it is highly unlikely that it is optimized
out), so we don't know if it is correct. In order to validate
this line, there are two parts of this process. First we must
write the test. Please see Chapter 5 to
learn more about writing tests. We insert the following lines
into a test case:
INT file;
/* Check for nonexistent file. */
file = LZOpenFile("badfilename_", &test, OF_READ);
ok(file == LZERROR_BADINHANDLE,
"LZOpenFile succeeded on nonexistent file\n");
LZClose(file);
Once we add in this test case, we now want to know if the line
in question is run by this test and works as expected. You
should be in the same directory as you left off in the previous
command example. The only difference is that we have to remove
the *.da files in order to start the
count over (if we leave the files than the number of times the
line is run is just added, e.g. line 545 below would be run 19 times)
and we remove the *.gcov files because
they are out of date and need to be recreated.
rm *.da *.gcov
cd tests
make
make test
cd ..
gcov ../../../wine/dlls/lzexpand/lzexpand_main.c
0.00% of 3 lines executed in file ../../../wine/include/wine/unicode.h
Creating unicode.h.gcov.
0.00% of 4 lines executed in file /usr/include/ctype.h
Creating ctype.h.gcov.
0.00% of 6 lines executed in file /usr/include/bits/string2.h
Creating string2.h.gcov.
100.00% of 3 lines executed in file ../../../wine/include/winbase.h
Creating winbase.h.gcov.
51.67% of 240 lines executed in file ../../../wine/dlls/lzexpand/lzexpand_main.c
Creating lzexpand_main.c.gcov.
less lzexpand_main.c.gcov
Note that there is more output, but only output of gcov is shown. The output file lzexpand_main.c.gcov looks like this.
10: 545: if ((mode&~0x70)!=OF_READ)
6: 546: return fd;
4: 547: if (fd==HFILE_ERROR)
1: 548: return HFILE_ERROR;
3: 549: cfd=LZInit(fd);
3: 550: if ((INT)cfd <= 0) return fd;
3: 551: return cfd;
Based on gcov, we now know that
HFILE_ERROR is returned once. And since
all of our other tests have remain unchanged, we can assume
that the one time it is returned is to satisfy the one case we
added where we check for it. Thus we have validated a line of
code. While this is a cursory example, it demostrates the
potential usefulness of this tool.
For a further in depth description of gcov, the official gcc compiler suite page for gcov is http://gcc.gnu.org/onlinedocs/gcc-3.2.3/gcc/Gcov.html. There is also an excellent article written by Steve Best for Linux Magazine which describes and illustrates this process very well at http://www.linux-mag.com/2003-07/compile_01.html.
This chapter describes the relevant coding practices in Wine, that you should be aware of before doing any serious development in Wine.
Patches are submitted via email to the Wine patches mailing
list, <wine-patches@winehq.org>. Your patch
should include:
A meaningful subject (very short description of patch)
A long (paragraph) description of what was wrong and what is now better. (recommended)
A change log entry (short description of what was changed).
The patch in "Git" format
To generate a patch using Git, first commit it to your local tree.
Each file that you change needs to be updated with git update . If you are adding or removing a file, use git update --add or git update --remove respectively. After updating the index, commit the change using git commit . The commit message will be sent with your patch, and recored in the ChangeLog.
After committing the patch, you can extract it using git format-patch and send it to wine-patches using git imap-send or simply attaching it to you mail manually.
There are a few conventions about coding style that have been adopted over the years of development. The rational for these "rules" is explained for each one.
No HTML mail, since patches should be in-lined and HTML turns the patch into garbage. Also it is considered bad etiquette as it uglifies the message, and is not viewable by many of the subscribers.
Only one change set per patch. Patches should address only one bug/problem at a time. If a lot of changes need to be made then it is preferred to break it into a series of patches. This makes it easier to find regressions.
Tabs are not forbidden but discouraged. A tab is defined as 8 characters and the usual amount of indentation is 4 characters.
C++ style comments are discouraged since some compilers choke on them.
Commenting out a block of code is usually done by enclosing it in #if 0 ... #endif Statements. For example.
/* note about reason for commenting block */ #if 0 code code /* comments */ code #endif
The reason for using this method is that it does not require that you edit comments that may be inside the block of code.
Patches should be in-lined (if you can configure your email client to not wrap lines), or attached as plain text attachments so they can be read inline. This may mean some more work for you. However it allows others to review your patch easily and decreases the chances of it being overlooked or forgotten.
Code is usually limited to 80 columns. This helps prevent mailers mangling patches by line wrap. Also it generally makes code easier to read.
If the patch fixes a bug in Bugzilla please provide a link to the bug in the comments of the patch. This will make it easier for the maintainers of Bugzilla.
Outlook Express is notorious for mangling attachments. Giving the patch a .txt extension and attaching will solve the problem for most mailers including Outlook. Also, there is a way to enable Outlook Express to send .diff attachments.
You need the following two things to make it work.
Make sure that .diff files have \r\n line ends, because if OE detects that there is no \r\n line endings it switches to quoted-printable format attachments.
Using regedit add key "Content Type" with value "text/plain" to the .diff extension under HKEY_CLASSES_ROOT (same as for .txt extension). This tells OE to use Content-Type: text/plain instead of application/octet-stream.
Item #1 is important. After you hit the "Send" button, go to "Outbox" and using "Properties" verify the message source to make sure that the mail has the correct format. You might want to send several test emails to yourself too.
"The basic rules are: no attachments, no MIME crap, no line wrapping, a single patch per mail. Basically if I can't do "cat raw_mail | patch -p0" it's in the wrong format."
(Or, "How do I get Alexandre to apply my patch quickly so I can build on it and it will not go stale?")