| <chapter id="debugger"> | |
| <title>Debugging Wine</title> | |
| <sect1 id="dbg-intro"> | |
| <title>Introduction</title> | |
| <para> | |
| Written by &name-eric-pouech; <email>&email-eric-pouech;</email> | |
| (Last updated: 6/14/2000) | |
| </para> | |
| <para> | |
| (Extracted from <filename>wine/documentation/winedbg</filename>) | |
| </para> | |
| <sect2> | |
| <title>Processes and threads: in underlying OS and in Windows</title> | |
| <para> | |
| 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. | |
| </para> | |
| <para> | |
| Each Windows' thread is implemented as a Unix process (under | |
| Linux using the <function>clone</function> syscall), meaning | |
| that all threads of a same Windows' process share the same | |
| (unix) address space. | |
| </para> | |
| <para> | |
| In the following: | |
| </para> | |
| <itemizedlist> | |
| <listitem> | |
| <para><varname>W-process</varname> means a process in Windows' terminology</para> | |
| </listitem> | |
| <listitem> | |
| <para><varname>U-process</varname> means a process in Unix' terminology</para> | |
| </listitem> | |
| <listitem> | |
| <para><varname>W-thread</varname> means a thread in Windows' terminology</para> | |
| </listitem> | |
| </itemizedlist> | |
| <para> | |
| A <varname>W-process</varname> is made of one or several | |
| <varname>W-threads</varname>. Each | |
| <varname>W-thread</varname> is mapped to one and only one | |
| <varname>U-process</varname>. All | |
| <varname>U-processes</varname> of a same | |
| <varname>W-process</varname> share the same address space. | |
| </para> | |
| <para> | |
| Each Unix process can be identified by two values: | |
| </para> | |
| <itemizedlist> | |
| <listitem> | |
| <para>the Unix process id (<varname>upid</varname> in the following)</para> | |
| </listitem> | |
| <listitem> | |
| <para>the Windows's thread id (<varname>tid</varname>)</para> | |
| </listitem> | |
| </itemizedlist> | |
| <para> | |
| Each Windows' process has also a Windows' process id | |
| (<varname>wpid</varname> in the following). It must be clear | |
| that <varname>upid</varname> and <varname>wpid</varname> are | |
| different and shall not be used instead of the other. | |
| </para> | |
| <para> | |
| <varname>Wpid</varname> and <varname>tid</varname> 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 | |
| <varname>W-process</varname>). | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Wine, debugging and WineDbg</title> | |
| <para> | |
| When talking of debugging in Wine, there are at least two | |
| levels to think of: | |
| </para> | |
| <itemizedlist> | |
| <listitem> | |
| <para>the Windows' debugging API.</para> | |
| </listitem> | |
| <listitem> | |
| <para>the Wine integrated debugger, dubbed | |
| <command>WineDbg</command>.</para> | |
| </listitem> | |
| </itemizedlist> | |
| <para> | |
| Wine implements most of the Windows' debugging API (the | |
| part in KERNEL32, not the one in | |
| <filename>IMAGEHLP.DLL</filename>), and allows any program | |
| (emulated or Winelib) using that API to debug a | |
| <varname>W-process</varname>. | |
| </para> | |
| <para> | |
| <command>WineDbg</command> is a Winelib application making | |
| use of this API to allow debugging both any Wine or Winelib | |
| applications as well as Wine itself (kernel and all DLLs). | |
| </para> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="dbg-modes"> | |
| <title>WineDbg's modes of invocation</title> | |
| <sect2> | |
| <title>Starting a process</title> | |
| <para> | |
| Any application (either a Windows' native executable, or a | |
| Winelib application) can be run through | |
| <command>WineDbg</command>. Command line options and tricks | |
| are the same as for wine: | |
| </para> | |
| <screen> | |
| winedbg telnet.exe | |
| winedbg "hl.exe -windowed" | |
| </screen> | |
| </sect2> | |
| <sect2> | |
| <title>Attaching</title> | |
| <para> | |
| <command>WineDbg</command> can also be launched without any | |
| command line argument: <command>WineDbg</command> is started | |
| without any attached process. You can get a list of running | |
| <varname>W-processes</varname> (and their | |
| <varname>wpid</varname>'s) using the <command>walk | |
| process</command> command, and then, with the | |
| <command>attach</command> command, pick up the | |
| <varname>wpid</varname> of the <varname>W-process</varname> | |
| you want to debug. This is (for now) a neat feature for the | |
| following reasons: | |
| </para> | |
| <itemizedlist> | |
| <listitem> | |
| <para>you can debug an already started application</para> | |
| </listitem> | |
| </itemizedlist> | |
| </sect2> | |
| <sect2 id="dbg-on-exception"> | |
| <title id="dbg-exception-title">On exception</title> | |
| <para> | |
| When something goes wrong, Windows tracks this as an | |
| exception. Exceptions exist for segmentation violation, | |
| stack overflow, division by zero... | |
| </para> | |
| <para> | |
| When an exception occurs, Wine checks if the <varname>W-process</varname> 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. | |
| </para> | |
| <para> | |
| If the <varname>W-process</varname> is not debugged, Wine | |
| tries to launch a debugger. This debugger (normally | |
| <command>WineDbg</command>, see III Configuration for more | |
| details), at startup, attaches to the | |
| <varname>W-process</varname> 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. | |
| </para> | |
| <para> | |
| If <command>WineDbg</command> is the standard debugger, the | |
| <command>pass</command> and <command>cont</command> commands | |
| are the two ways to let the process go further for the | |
| handling of the exception event. | |
| </para> | |
| <para> | |
| 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: | |
| </para> | |
| <variablelist> | |
| <varlistentry> | |
| <term>continue:</term> | |
| <listitem> | |
| <para> | |
| the debugger had the ability to correct what's | |
| generated the exception, and is now able to continue | |
| process execution. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>pass:</term> | |
| <listitem> | |
| <para> | |
| 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. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| <note> | |
| <para> | |
| since some of Wine's code uses exceptions and | |
| <function>try/catch</function> blocks to provide some | |
| functionality, <command>WineDbg</command> can be entered | |
| in such cases with segv exceptions. This happens, for | |
| example, with <function>IsBadReadPtr</function> function. | |
| In that case, the <command>pass</command> command shall be | |
| used, to let the handling of the exception to be done by | |
| the <function>catch</function> block in | |
| <function>IsBadReadPtr</function>. | |
| </para> | |
| </note> | |
| </sect2> | |
| <sect2> | |
| <title>Quitting</title> | |
| <para> | |
| Unfortunately, Windows doesn't provide a detach kind of API, | |
| meaning that once you started debugging a process, you must | |
| do so until the process dies. Killing (or stopping/aborting) | |
| the debugger will also kill the debugged process. This will | |
| be true for any Windows' debugging API compliant debugger, | |
| starting with <command>WineDbg</command>. | |
| </para> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="wine-debugger"> | |
| <title>Using the Wine Debugger</title> | |
| <para> | |
| Written by &name-marcus-meissner; <email>&email-marcus-meissner;</email>, | |
| additions welcome. | |
| </para> | |
| <para> | |
| (Extracted from <filename>wine/documentation/debugging</filename>) | |
| </para> | |
| <para> | |
| 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 <filename>documentation/bugreports</filename> for | |
| information on how to write useful bug reports. | |
| </para> | |
| <sect2> | |
| <title>Crashes</title> | |
| <para> | |
| These usually show up like this: | |
| </para> | |
| <screen> | |
| |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 | |
| </screen> | |
| <para> | |
| 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 | |
| feasible. | |
| </para> | |
| <orderedlist> | |
| <listitem> | |
| <para> | |
| 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 | |
| <emphasis>whole</emphasis> crashdump even if it doesn't | |
| make sense to you. | |
| </para> | |
| <para> | |
| (In this case it is access to an invalid selector, for | |
| <systemitem>%es</systemitem> is <literal>0000</literal>, as | |
| seen in the register dump). | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Determine the cause of the crash. Since this is usually | |
| a primary/secondary reaction to a failed or misbehaving | |
| Wine function, rerun Wine with <parameter>-debugmsg | |
| +relay</parameter> added to the commandline. 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: | |
| </para> | |
| <screen> | |
| |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. | |
| </screen> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| 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 <function>TRACE(<channel>,"(...)\n");</function> at | |
| the beginning of the function. Rerun wine with | |
| <parameter>-debugmsg +xyz,+relay</parameter> added to the | |
| commandline. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Additional information on how to debug using the internal | |
| debugger can be found in | |
| <filename>debugger/README</filename>. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| 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 <parameter>-debugmsg | |
| +all</parameter>, which dumps ALL included debug | |
| information in wine. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| If even that isn't enough, add more debug output for | |
| yourself into the functions you find relevant. See | |
| <filename>documentation/debug-msgs</filename>. You might | |
| also try to run the program in <command>gdb</command> | |
| instead of using the WINE-debugger. If you do that, use | |
| <parameter>handle SIGSEGV nostop noprint</parameter> to | |
| disable the handling of seg faults inside | |
| <command>gdb</command> (needed for Win16). If you don't use | |
| the <parameter>--desktop</parameter> or | |
| <parameter>--managed</parameter> option, start the WINE | |
| process with <parameter>--sync</parameter>, or chances are | |
| good to get X into an unusable state. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| You can also set a breakpoint for that function. Start wine | |
| with the <parameter>--debug</parameter> option added to the | |
| commandline. After loading the executable wine will enter | |
| the internal debugger. Use <parameter>break | |
| KERNEL_LSTRLEN</parameter> (replace by function you want | |
| to debug, CASE IS RELEVANT) to set a breakpoint. Then use | |
| <command>continue</command> 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 <command>continue</command> 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. | |
| </para> | |
| </listitem> | |
| </orderedlist> | |
| </sect2> | |
| <sect2> | |
| <title>Program hangs, nothing happens</title> | |
| <para> | |
| Switch to UNIX shell, get the process-ID using <command>ps -a | | |
| grep wine</command>, and do a <command>kill -HUP | |
| <pid></command> (without the < and >). Wine will | |
| then enter its internal debugger and you can proceed as | |
| explained above. Also, you can use | |
| <parameter>--debug</parameter> switch and then you can get into | |
| internal debugger by pressing | |
| <keycombo><keycap>Ctrl</keycap><keycap>C</keycap></keycombo> in | |
| the terminal where you run Wine. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Program reports an error with a Messagebox</title> | |
| <para> | |
| 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. | |
| </para> | |
| <para> | |
| Since the failure happens usually directly before setting up | |
| the Messagebox you can start wine with | |
| <parameter>--debug</parameter> added to the commandline, set a | |
| breakpoint at <function>MessageBoxA</function> (called by win16 | |
| and win32 programs) and proceed with | |
| <command>continue</command>. With <parameter>--debugmsg | |
| +all</parameter> Wine will now stop directly before setting | |
| up the Messagebox. Proceed as explained above. | |
| </para> | |
| <para> | |
| You can also run wine using <command>wine -debugmsg +relay | |
| program.exe 2>&1 | less -i</command> and in | |
| <command>less</command> search for <quote>MessageBox</quote>. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Disassembling programs:</title> | |
| <para> | |
| You may also try to disassemble the offending program to | |
| check for undocumented features and/or use of them. | |
| </para> | |
| <para> | |
| The best, freely available, disassembler for Win16 programs is | |
| <application>Windows Codeback</application>, archivename | |
| <filename>wcbxxx.zip</filename>, which usually can be found in | |
| the <filename>Cica-Mirror</filename> subdirectory on the WINE | |
| ftpsites. (See <filename>ANNOUNCE</filename>). | |
| </para> | |
| <para> | |
| Disassembling win32 programs is possible using | |
| <application>Windows Disassembler 32</application>, archivename | |
| something like <filename>w32dsm87.zip</filename> (or similar) | |
| on <systemitem class="systemname">ftp.winsite.com</systemitem> | |
| and mirrors. The shareware version does not allow saving of | |
| disassembly listings. You can also use the newer (and in the | |
| full version better) <application>Interactive | |
| Disassembler</application> (IDA) from the ftp sites mentioned | |
| at the end of the document. Understanding disassembled code is | |
| mostly a question of exercise. | |
| </para> | |
| <para> | |
| Most code out there uses standard C function entries (for it | |
| is usually written in C). Win16 function entries usually | |
| look like that: | |
| </para> | |
| <programlisting> | |
| push bp | |
| mov bp, sp | |
| ... function code .. | |
| retf XXXX <--------- XXXX is number of bytes of arguments | |
| </programlisting> | |
| <para> | |
| This is a <function>FAR</function> function with no local | |
| storage. The arguments usually start at | |
| <literal>[bp+6]</literal> with increasing offsets. Note, that | |
| <literal>[bp+6]</literal> belongs to the | |
| <emphasis>rightmost</emphasis> argument, for exported win16 | |
| functions use the PASCAL calling convention. So, if we use | |
| <function>strcmp(a,b)</function> with <parameter>a</parameter> | |
| and <parameter>b</parameter> both 32 bit variables | |
| <parameter>b</parameter> would be at <literal>[bp+6]</literal> | |
| and <parameter>a</parameter> at <literal>[bp+10]</literal>. | |
| </para> | |
| <para> | |
| Most functions make also use of local storage in the stackframe: | |
| </para> | |
| <programlisting> | |
| enter 0086, 00 | |
| ... function code ... | |
| leave | |
| retf XXXX | |
| </programlisting> | |
| <para> | |
| This does mostly the same as above, but also adds | |
| <literal>0x86</literal> bytes of stackstorage, which is | |
| accessed using <literal>[bp-xx]</literal>. Before calling a | |
| function, arguments are pushed on the stack using something | |
| like this: | |
| </para> | |
| <programlisting> | |
| push word ptr [bp-02] <- will be at [bp+8] | |
| push di <- will be at [bp+6] | |
| call KERNEL.LSTRLEN | |
| </programlisting> | |
| <para> | |
| Here first the selector and then the offset to the passed | |
| string are pushed. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Sample debugging session:</title> | |
| <para> | |
| Let's debug the infamous Word <filename>SHARE.EXE</filename> | |
| messagebox: | |
| </para> | |
| <screen> | |
| |marcus@jet $ wine winword.exe | |
| | +---------------------------------------------+ | |
| | | ! You must leave Windows and load SHARE.EXE| | |
| | | before starting Word. | | |
| | +---------------------------------------------+ | |
| </screen> | |
| <screen> | |
| |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. | |
| </screen> | |
| <para> | |
| Whoops, it even detects that something is wrong! | |
| </para> | |
| <screen> | |
| |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 | |
| </screen> | |
| <para> | |
| This fails, since my <medialabel>C:</medialabel> drive is in | |
| this case mounted readonly. | |
| </para> | |
| <screen> | |
| |Ret KERNEL.74: OPENFILE() retval=0xffff ret=060f:09d8 ds=0927 | |
| ^^^^^^ HFILE_ERROR16, yes, it failed. | |
| |Call USER.1: MESSAGEBOX(0x0000,0x09278376"Sie mussen Windows verlassen und SHARE.EXE laden bevor Sie Word starten.",0x00000000,0x1030) ret=060f:084f ds=0927 | |
| </screen> | |
| <para> | |
| And MessageBox'ed. | |
| </para> | |
| <screen> | |
| |Stopped on breakpoint 2 at 0x40189100 (MessageBoxA [msgbox.c:190]) | |
| |190 { <- the sourceline | |
| In 32 bit mode. | |
| Wine-dbg> | |
| </screen> | |
| <para> | |
| The code seems to find a writeable harddisk and tries to create | |
| a file there. To work around this bug, you can define | |
| <medialabel>C:</medialabel> as a networkdrive, which is ignored | |
| by the code above. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Debugging Tips</title> | |
| <para> | |
| Here are some useful debugging tips, added by Andreas Mohr: | |
| </para> | |
| <itemizedlist> | |
| <listitem> | |
| <para> | |
| 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 | |
| <programlisting> | |
| wine --debugmsg +relay program | |
| </programlisting> | |
| to get a listing of the functions the program calls in its start function. | |
| Now you do a | |
| <programlisting> | |
| wine --debug winfile.exe | |
| </programlisting> | |
| </para> | |
| <para> | |
| This way, you get into <command>Wine-dbg</command>. Now you | |
| can set a breakpoint on any function the program calls in | |
| the start function and just type <userinput>c</userinput> | |
| 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. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| 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 <function>MessageBox()</function>. | |
| That's why you should re-run the program with e.g. | |
| <programlisting> | |
| wine --debugmsg +relay <program name> &>relmsg | |
| </programlisting> | |
| Then do a <command>more relmsg</command> and search for the | |
| last occurrence of a call to the string "MESSAGEBOX". This is a line like | |
| <programlisting> | |
| Call USER.1: MESSAGEBOX(0x0000,0x01ff1246 "Runtime error 219 at 0004:1056.",0x00000000,0x1010) ret=01f7:2160 ds=01ff | |
| </programlisting> | |
| In my example the lines before the call to | |
| <function>MessageBox()</function> look like that: | |
| <programlisting> | |
| 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 | |
| </programlisting> | |
| </para> | |
| <para> | |
| I think that the call to <function>MessageBox()</function> | |
| in this example is <emphasis>not</emphasis> 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 | |
| <literal>0x0004:0x1056</literal>. | |
| </para> | |
| <para> | |
| As the segment value of the address is only | |
| <literal>4</literal>, I think that that is only an internal | |
| program value. But the offset address reveals something | |
| quite interesting: Offset <literal>1056</literal> is | |
| <emphasis>very</emphasis> close to the return address of | |
| <function>FREELIBRARY()</function>: | |
| <programlisting> | |
| Call KERNEL.96: FREELIBRARY(0x031f) ret=01cf:105c ds=01ff | |
| ^^^^ | |
| </programlisting> | |
| </para> | |
| <para> | |
| Provided that segment <literal>0x0004</literal> is indeed segment | |
| <literal>0x1cf</literal>, we now we can use IDA (available at | |
| <ulink url="ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip"> | |
| ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip</ulink>) to | |
| disassemble the part that caused the error. We just have to find the address of | |
| the call to <function>FreeLibrary()</function>. 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 <literal>0x1cf</literal>). | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| 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 <function>GetVersion16/32</function> as | |
| those functions are called very often. | |
| </para> | |
| <para> | |
| Then do a <userinput>c</userinput> until you are able to | |
| set this breakpoint without error message. | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Some useful programs: | |
| </para> | |
| <variablelist> | |
| <varlistentry> | |
| <term> | |
| <application>IDA</application>: | |
| <filename> | |
| <ulink url="ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip"> | |
| ftp://ftp.uni-koeln.de/pc/msdos/programming/assembler/ida35bx.zip</ulink> | |
| </filename> | |
| </term> | |
| <listitem> | |
| <para> | |
| <emphasis>Very</emphasis> good DOS disassembler ! It's badly needed | |
| for debugging Wine sometimes. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term> | |
| <application>XRAY</application>: | |
| <filename> | |
| <ulink url="ftp://ftp.th-darmstadt.de/pub/machines/ms-dos/SimTel/msdos/asmutil/xray15.zip"> | |
| ftp://ftp.th-darmstadt.de/pub/machines/ms-dos/SimTel/msdos/asmutil/xray15.zip</ulink> | |
| </filename> | |
| </term> | |
| <listitem> | |
| <para> | |
| Traces DOS calls (Int 21h, DPMI, ...). Use it with | |
| Windows to correct file management problems etc. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term> | |
| <application>pedump</application>: | |
| <filename> | |
| <ulink url="http://oak.oakland.edu/pub/simtelnet/win95/prog/pedump.zip"> | |
| http://oak.oakland.edu/pub/simtelnet/win95/prog/pedump.zip</ulink> | |
| </filename> | |
| </term> | |
| <listitem> | |
| <para> | |
| Dumps the imports and exports of a PE (Portable | |
| Executable) DLL. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| </listitem> | |
| </itemizedlist> | |
| </sect2> | |
| <sect2> | |
| <title>Some basic debugger usages:</title> | |
| <para> | |
| After starting your program with | |
| </para> | |
| <screen> | |
| wine -debug myprog.exe | |
| </screen> | |
| <para> | |
| the program loads and you get a prompt at the program | |
| starting point. Then you can set breakpoints: | |
| </para> | |
| <screen> | |
| b RoutineName (by outine name) OR | |
| b *0x812575 (by address) | |
| </screen> | |
| <para> | |
| Then you hit <command>c</command> (continue) to run the | |
| program. It stops at the breakpoint. You can type | |
| </para> | |
| <screen> | |
| 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) | |
| </screen> | |
| <para> | |
| By hitting <keycap>Enter</keycap>, you repeat the last | |
| command. | |
| </para> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="memory-addresses"> | |
| <title>Useful memory addresses</title> | |
| <para> | |
| Written by &name-andreas-mohr; <email>&email-andreas-mohr;</email> | |
| </para> | |
| <para> | |
| Wine uses several different kinds of memory addresses. | |
| </para> | |
| <variablelist> | |
| <varlistentry> | |
| <term> | |
| Win32/"normal" Wine addresses/Linux: linear addresses. | |
| </term> | |
| <listitem> | |
| <para> | |
| 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). | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term> | |
| Win16 "enhanced mode": segmented addresses. | |
| </term> | |
| <listitem> | |
| <para> | |
| 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. | |
| </para> | |
| <para> | |
| Example: | |
| </para> | |
| <para> | |
| 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. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term> | |
| DOS/Win16 "standard mode" | |
| </term> | |
| <listitem> | |
| <para> | |
| 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. | |
| </para> | |
| <para> | |
| 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 ;-) | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| </sect1> | |
| <sect1 id="dbg-config"> | |
| <title>Configuration</title> | |
| <sect2> | |
| <title>Registry configuration</title> | |
| <para> | |
| The Windows' debugging API uses a registry entry to know | |
| which debugger to invoke when an unhandled exception occurs | |
| (see <link endterm="dbg-exception-title" | |
| linkend="dbg-on-exception"></link> for some details). Two | |
| values in key | |
| </para> | |
| <programlisting> | |
| "MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug" | |
| </programlisting> | |
| <para> | |
| Determine the behavior: | |
| </para> | |
| <variablelist> | |
| <varlistentry> | |
| <term>Debugger:</term> | |
| <listitem> | |
| <para> | |
| this is the command line used to launch the debugger | |
| (it uses two <function>printf</function> formats | |
| (<literal>%ld</literal>) to pass context dependent | |
| information to the debugger). You should put here a | |
| complete path to your debugger | |
| (<command>WineDbg</command> can of course be used, but | |
| any other Windows' debugging API aware debugger will | |
| do). | |
| The path to the debugger you chose to use must be reachable | |
| via a DOS drive in the Wine config file ! | |
| </para> | |
| <para> | |
| You can also set a shell script to launch the debugger. In | |
| this case, you need to be sure that the invocation in | |
| this shell script is of the form: | |
| <programlisting> | |
| WINEPRELOAD=<path_to_winedbg.so> exec wine $* | |
| </programlisting> | |
| (Shell script must use exec, and the debugger .so file must | |
| be preloaded to override the shell script information). | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>Auto:</term> | |
| <listitem> | |
| <para> | |
| 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. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| <para> | |
| A regular Wine registry looks like: | |
| </para> | |
| <programlisting> | |
| [MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug] 957636538 | |
| "Auto"=dword:00000001 | |
| "Debugger"="/usr/local/bin/winedbg %ld %ld" | |
| </programlisting> | |
| <note> | |
| <title>Note 1</title> | |
| <para> | |
| creating this key is mandatory. Not doing so will not | |
| fire the debugger when an exception occurs. | |
| </para> | |
| </note> | |
| <note> | |
| <title>Note 2</title> | |
| <para> | |
| <command>wineinstall</command> 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 | |
| </para> | |
| <programlisting> | |
| [MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug] | |
| </programlisting> | |
| <para> | |
| key before running again <command>wineinstall</command> to | |
| regenerate this key. | |
| </para> | |
| </note> | |
| </sect2> | |
| <sect2> | |
| <title>WineDbg configuration</title> | |
| <para> | |
| <command>WineDbg</command> can be configured thru a number | |
| of options. Those options are stored in the registry, on a | |
| per user basis. The key is (in <emphasis>my</emphasis> registry) | |
| </para> | |
| <programlisting> | |
| [eric\\Software\\Wine\\WineDbg] | |
| </programlisting> | |
| <para> | |
| Those options can be read/written while inside | |
| <command>WineDbg</command>, as part of the debugger | |
| expressions. To refer to one of these options, its name must | |
| be prefixed by a <literal>$</literal> sign. For example, | |
| </para> | |
| <programlisting> | |
| set $BreakAllThreadsStartup = 1 | |
| </programlisting> | |
| <para> | |
| sets the option <varname>BreakAllThreadsStartup</varname> to | |
| <literal>TRUE</literal>. | |
| </para> | |
| <para> | |
| All the options are read from the registry when | |
| <command>WineDbg</command> starts (if no corresponding value | |
| is found, a default value is used), and are written back to | |
| the registry when <command>WineDbg</command> exits (hence, | |
| all modifications to those options are automatically saved | |
| when <command>WineDbg</command> terminates). | |
| </para> | |
| <para> | |
| Here's the list of all options: | |
| </para> | |
| <sect3> | |
| <title>Controlling when the debugger is entered</title> | |
| <variablelist> | |
| <varlistentry> | |
| <term><varname>BreakAllThreadsStartup</varname></term> | |
| <listitem> | |
| <para> | |
| Set to <literal>TRUE</literal> if at all threads | |
| start-up the debugger stops set to | |
| <literal>FALSE</literal> if only at the first thread | |
| startup of a given process the debugger stops. | |
| <literal>FALSE</literal> by default. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term><varname>BreakOnCritSectTimeOut</varname></term> | |
| <listitem> | |
| <para> | |
| Set to <literal>TRUE</literal> if the debugger stops | |
| when a critical section times out (5 minutes); | |
| <literal>TRUE</literal> by default. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term><varname>BreakOnAttach</varname></term> | |
| <listitem> | |
| <para> | |
| Set to <literal>TRUE</literal> if when | |
| <command>WineDbg</command> attaches to an existing | |
| process after an unhandled exception, | |
| <command>WineDbg</command> 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 | |
| <literal>FALSE</literal>. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term><varname>BreakOnFirstChance</varname></term> | |
| <listitem> | |
| <para> | |
| 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 decides either to resume execution (see | |
| <command>WineDbg</command>'s <command>cont</command> | |
| command) or pass the exception up to the exception | |
| handler chain in the program (if it exists) | |
| (<command>WineDbg</command> implements this thru the | |
| <command>pass</command> 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 | |
| <varname>BreakOnFirstChance</varname> exception is | |
| <literal>TRUE</literal>, then winedbg is entered for | |
| both first and last chance execptions (to | |
| <literal>FALSE</literal>, it's only entered for last | |
| chance exceptions). | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term><varname>BreakOnDllLoad</varname></term> | |
| <listitem> | |
| <para> | |
| Set to <literal>TRUE</literal> if the debugger stops | |
| when a DLL is loaded into memory; when the debugger | |
| is invoked after a crash, the DLLs already mapped in | |
| memory will not trigger this break. | |
| <literal>FALSE</literal> by default. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| </sect3> | |
| <sect3> | |
| <title>Output handling</title> | |
| <variablelist> | |
| <varlistentry> | |
| <term><varname>ConChannelMask</varname></term> | |
| <listitem> | |
| <para> | |
| Mask of active debugger output channels on console | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term><varname>StdChannelMask</varname></term> | |
| <listitem> | |
| <para> | |
| Mask of active debugger output channels on <filename>stderr</filename> | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term><varname>UseXTerm</varname></term> | |
| <listitem> | |
| <para> | |
| Set to <literal>TRUE</literal> if the debugger uses | |
| its own <command>xterm</command> window for console | |
| input/output. Set to <literal>FALSE</literal> if | |
| the debugger uses the current Unix console for | |
| input/output | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| <para> | |
| Those last 3 variables are jointly used in two generic ways: | |
| </para> | |
| <orderedlist> | |
| <listitem> | |
| <para>default</para> | |
| <programlisting> | |
| ConChannelMask = DBG_CHN_MESG (1) | |
| StdChannelMask = 0 | |
| UseXTerm = 1 | |
| </programlisting> | |
| <para> | |
| In this case, all input/output goes into a specific | |
| <command>xterm</command> window (but all debug | |
| messages <function>TRACE</function>, | |
| <function>WARN</function>... still goes to tty where | |
| wine is run from). | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| to have all input/output go into the tty where Wine | |
| was started from (to be used in a X11-free | |
| environment) | |
| </para> | |
| <screen> | |
| ConChannelMask = 0 | |
| StdChannelMask = DBG_CHN_MESG (1) | |
| UseXTerm = 1 | |
| </screen> | |
| </listitem> | |
| </orderedlist> | |
| <para> | |
| Those variables also allow, for example for debugging | |
| purposes, to use: | |
| </para> | |
| <screen> | |
| ConChannelMask = 0xfff | |
| StdChannelMask = 0xfff | |
| UseXTerm = 1 | |
| </screen> | |
| <para> | |
| This allows to redirect all <function>WineDbg</function> | |
| output to both tty Wine was started from, and | |
| <command>xterm</command> debugging window. If Wine (or | |
| <command>WineDbg</command>) was started with a redirection | |
| of <filename>stdout</filename> and/or | |
| <filename>stderr</filename> to a file (with for | |
| example >& shell redirect command), you'll get in that | |
| file both outputs. It may be interesting to look in the | |
| relay trace for specific values which the process segv'ed | |
| on. | |
| </para> | |
| </sect3> | |
| <sect3> | |
| <title>Context information</title> | |
| <variablelist> | |
| <varlistentry> | |
| <term><varname>ThreadId</varname></term> | |
| <listitem> | |
| <para>ID of the <varname>W-thread</varname> currently | |
| examined by the debugger</para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term><varname>ProcessId</varname></term> | |
| <listitem> | |
| <para>ID of the <varname>W-thread</varname> currently | |
| examined by the debugger</para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term><registers></term> | |
| <listitem> | |
| <para>All CPU registers are also available</para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| <para> | |
| The <varname>ThreadId</varname> and | |
| <varname>ProcessId</varname> variables can be handy to set | |
| conditional breakpoints on a given thread or process. | |
| </para> | |
| </sect3> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="dbg-commands"> | |
| <title>WineDbg Command Reference</title> | |
| <sect2> | |
| <title>Misc</title> | |
| <screen> | |
| abort aborts the debugger | |
| quit exits the debugger | |
| attach N attach to a W-process (N is its ID). IDs can be | |
| obtained thru walk process command | |
| </screen> | |
| <screen> | |
| help prints some help on the commands | |
| help info prints some help on info commands | |
| </screen> | |
| <screen> | |
| mode 16 switch to 16 bit mode | |
| mode 32 switch to 32 bit mode | |
| </screen> | |
| </sect2> | |
| <sect2> | |
| <title>Flow control</title> | |
| <screen> | |
| cont continue execution until next breakpoint or exception. | |
| pass pass the exception event up to the filter chain. | |
| step continue execution until next C line of code (enters | |
| function call) | |
| next continue execution until next C line of code (doesn't | |
| enter function call) | |
| stepi execute next assembly instruction (enters function | |
| call) | |
| nexti execute next assembly instruction (doesn't enter | |
| function call) | |
| finish do nexti commands until current function is exited | |
| </screen> | |
| <para> | |
| cont, step, next, stepi, nexti can be postfixed by a | |
| number (N), meaning that the command must be executed N | |
| times. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Breakpoints, watch points</title> | |
| <screen> | |
| 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 a 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) | |
| </screen> | |
| <para> | |
| When setting a breakpoint on an <id>, if several symbols with this | |
| <id> exist, the debugger will prompt for the symbol you want to use. | |
| Pick up the one you want from its number. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Stack manipulation</title> | |
| <screen> | |
| bt print calling stack of current thread | |
| 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 | |
| info local prints information on local variables for current | |
| function | |
| </screen> | |
| </sect2> | |
| <sect2> | |
| <title>Directory & source file manipulation</title> | |
| <screen> | |
| show dir | |
| dir <pathname> | |
| dir | |
| symbolfile <module> <pathname> | |
| </screen> | |
| <screen> | |
| list lists 10 source lines from current position | |
| list - lists 10 source lines before 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 | |
| </screen> | |
| <para> | |
| You can specify the end target (to change the 10 lines | |
| value) using the ','. For example: | |
| </para> | |
| <screen> | |
| list 123, 234 lists source lines from line 123 up to line 234 in | |
| current file | |
| list foo.c:1,56 lists source lines from line 1 up to 56 in file foo.c | |
| </screen> | |
| </sect2> | |
| <sect2> | |
| <title>Displaying</title> | |
| <para> | |
| A display is an expression that's evaluated and printed | |
| after the execution of any <command>WineDbg</command> | |
| command. | |
| </para> | |
| <screen> | |
| display lists the active displays | |
| info display (same as above command) | |
| 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 deletes display #N | |
| undisplay N (same as del display) | |
| </screen> | |
| </sect2> | |
| <sect2> | |
| <title>Disassembly</title> | |
| <screen> | |
| disas disassemble from current position | |
| disas <expr> disassemble from address <expr> | |
| disas <expr>,<expr>disassembles code between addresses specified by | |
| the two <expr> | |
| </screen> | |
| </sect2> | |
| <sect2> | |
| <title>Information on Wine's internals</title> | |
| <screen> | |
| info class <id> prints information on Windows's class <id> | |
| walk class lists all Windows' class registered in Wine | |
| info share lists all the dynamic libraries loaded the debugged | |
| program (including .so files, NE and PE DLLs) | |
| info module N prints information on module of handle N | |
| walk module lists all modules loaded by debugged program | |
| info queue N prints information on Wine's queue N | |
| walk queue lists all queues allocated in Wine | |
| info regs prints the value of CPU register | |
| info segment N prints information on segment N | |
| info segment lists all allocated segments | |
| info stack prints the values on top of the stack | |
| info map lists all virtual mappings used by the debugged | |
| program | |
| info wnd N prints information of Window of handle N | |
| walk wnd lists all the window hierarchy starting from the | |
| desktop window | |
| walk wnd N lists all the window hierarchy starting from the | |
| window of handle N | |
| walk process lists all w-processes in Wine session | |
| walk thread lists all w-threads in Wine session | |
| walk modref (no longer avail) | |
| </screen> | |
| </sect2> | |
| <sect2> | |
| <title>Memory (reading, writing, typing)</title> | |
| <screen> | |
| 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> | |
| </screen> | |
| <para> | |
| <filename>/fmt</filename> is either <filename>/<letter></filename> or | |
| <filename>/<count><letter></filename> letter can be | |
| </para> | |
| <screen> | |
| 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 | |
| </screen> | |
| </sect2> | |
| <sect2> | |
| <title>Expressions</title> | |
| <para> | |
| Expressions in Wine Debugger are mostly written in a C form. However, there | |
| are a few discrepancies: | |
| <itemizedlist> | |
| <listitem> | |
| <para> | |
| Identifiers can take a '.' in their names. This allow mainly to access symbols | |
| from different DLLs like USER32.DLL.CreateWindowA | |
| </para> | |
| </listitem> | |
| <listitem> | |
| <para> | |
| Because of previous rule, fields access from a struct must be written as: | |
| <screen> | |
| my_struct . my_field | |
| </screen> | |
| (note the spaces after and before the dot). | |
| </para> | |
| </listitem> | |
| </itemizedlist> | |
| </para> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="dbg-others"> | |
| <title>Other debuggers</title> | |
| <sect2> | |
| <title>Using other Unix debuggers</title> | |
| <para> | |
| You can also use other debuggers (like | |
| <command>gdb</command>), but you must be aware of a few | |
| items: | |
| </para> | |
| <para> | |
| You need to attach the unix debugger to the correct unix | |
| process (representing the correct windows thread) (you can | |
| "guess" it from a <command>ps fax</command> for example: | |
| When running the emulator, usually the first two | |
| <varname>upids</varname> are for the Windows' application | |
| running the desktop, the first thread of the application is | |
| generally the third <varname>upid</varname>; when running a | |
| Winelib program, the first thread of the application is | |
| generally the first <varname>upid</varname>) | |
| </para> | |
| <note> | |
| <para> | |
| Even if latest <command>gdb</command> implements the | |
| notion of threads, it won't work with Wine because the | |
| thread abstraction used for implementing Windows' thread | |
| is not 100% mapped onto the linux posix threads | |
| implementation. It means that you'll have to spawn a | |
| different <command>gdb</command> session for each Windows' | |
| thread you wish to debug. | |
| </para> | |
| </note> | |
| <!-- *** Extra content spliced in from article by Andreas Mohr *** --> | |
| <para> | |
| Following text written by &name-andreas-mohr; <email>&email-andreas-mohr;</email> | |
| </para> | |
| <para> | |
| Here's how to get info about the current execution status of a | |
| certain Wine process: | |
| </para> | |
| <para> | |
| Change into your Wine source dir and enter: | |
| </para> | |
| <screen> | |
| $ gdb wine | |
| </screen> | |
| <para> | |
| Switch to another console and enter <command>ps ax | grep | |
| wine</command> to find all wine processes. Inside | |
| <command>gdb</command>, repeat for all Wine processes: | |
| </para> | |
| <screen> | |
| (gdb) attach <userinput>PID</userinput> | |
| </screen> | |
| <para> | |
| with <userinput>PID</userinput> being the process ID of one of | |
| the Wine processes. Use | |
| </para> | |
| <screen> | |
| (gdb) bt | |
| </screen> | |
| <para> | |
| 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: | |
| </para> | |
| <screen> | |
| (gdb) n | |
| </screen> | |
| <para> | |
| or maybe even | |
| </para> | |
| <screen> | |
| (gdb) b <userinput>SomeFunction</userinput> | |
| </screen> | |
| <para> | |
| and | |
| </para> | |
| <screen> | |
| (gdb) c | |
| </screen> | |
| <para> | |
| to set a breakpoint at a certain function and continue up to | |
| that function. Finally you can enter | |
| </para> | |
| <screen> | |
| (gdb) detach | |
| </screen> | |
| <para> | |
| to detach from the Wine process. | |
| </para> | |
| <!-- *** End of xtra content *** --> | |
| </sect2> | |
| <sect2> | |
| <title>Using other Windows debuggers</title> | |
| <para> | |
| 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. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Main differences between winedbg and regular Unix debuggers</title> | |
| <!-- FIXME: convert this into a table --> | |
| <screen> | |
| +----------------------------------+---------------------------------+ | |
| | WineDbg | gdb | | |
| +----------------------------------+---------------------------------+ | |
| |WineDbg debugs a Windows' process:|gdb debugs a Windows' thread: | | |
| |+ the various threads will be |+ a separate gdb session is | | |
| | handled by the same WineDbg | needed for each thread of | | |
| | session | Windows' process | | |
| |+ a breakpoint will be triggered |+ a breakpoint will be triggered | | |
| | for any thread of the w-process | only for the w-thread debugged | | |
| +----------------------------------+---------------------------------+ | |
| |WineDbg supports debug information|gdb supports debug information | | |
| |from: |from: | | |
| |+ stabs (standard Unix format) |+ stabs (standard Unix format) | | |
| |+ Microsoft's C, CodeView, .DBG | | | |
| +----------------------------------+---------------------------------+ | |
| </screen> | |
| </sect2> | |
| </sect1> | |
| <sect1 id="dbg-limits"> | |
| <title>Limitations</title> | |
| <para> | |
| 16 bit processes are not supported (but calls to 16 bit code | |
| in 32 bit applications are). | |
| </para> | |
| </sect1> | |
| </chapter> | |
| <!-- Keep this comment at the end of the file | |
| Local variables: | |
| mode: sgml | |
| sgml-parent-document:("wine-doc.sgml" "set" "book" "part" "chapter" "") | |
| End: | |
| --> |