123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- This is the core of the Wine debugger. The reverse assembler
- was stolen from Mach more or less intact. It turns out that there are
- two variables that are set differently if you are reverse assembling
- 16 bit code, and on the whole it seems to work.
- NEWS:
- The internal debugger has *tons* more capability than it did before.
- I have enclosed some examples that show usage at the end of this file.
- New features include:
- 1) Ability of debugger to read debug information from wine executable
- *and* from Win32 executables. Local variable and line number information is
- also read and processed.
- 2) The internal debugger is capable of 'stepping' to the next
- line number, just like gdb. Examples of the commands are:
- step
- stepi
- si
- step 3
- si 5
- next
- nexti
- cont 4
- finish
- All of these should be exactly like how gdb does things.
- 3) The internal debugger now has a sense of what source file and line
- number a given PC is at. New commands to support this are just like gdb,
- and include:
- list
- dir
- show dir
- there are a variety of formats of arguments for the list command. All
- permutations supported by gdb should also be supported.
- 4) The internal debugger knows about datatypes of various objects,
- for both Win32 *and* the debugging information in the wine executable itself.
- I have enclosed an example of how this works at the end.
- 5) There are more ways the 'b' command can be used to set breakpoints.
- Examples are:
- b *0x8190000
- b 1100
- b Usage
- b
- I don't think this covers all of the permutations that gdb accepts (this should
- be cleaned up someday so that all possibilities are acceptable).
- 6) The 'print' and 'x' commands should behave more or less exactly
- as they do under gdb. The difference is that the way the data is presented
- will be slightly different, but the content should be fundamentally the same.
- 7) The internal debugger now supports conditional breakpoints, and
- automatic display expressions. An example is at the end of this file. The
- syntax and usage should be identical to that of gdb.
- 8) Type casts can be made from within the debugger, but they currently
- don't work with typedef'ed types. They only work with builtin types and
- named structures unions, etc. The problem is that internally we don't always
- record the typedefed names of structures, so we have no guarantee that we
- would know what each type is. This can be fixed, of course - it just takes
- more memory. Note that in some cases, typedefed structures could be cast
- using '(struct typedfname)' instead of '(typedfname)'. Technically this
- isn't quite correct, but if and when the rest of this stuff gets fixed,
- this would need to get corrected too.
- NOTES:
- If it weren't for the fact that gdb doesn't grok the Win32 debug
- information, you could just use gdb. The internal debugger should be able
- to read and use debugging information for both Win32 and also for the
- Wine executable, making it possible to debug the combination of the two
- together as if it were one large (very large) entity.
- LIMITATIONS AND DIFFERENCES FROM GDB:
- You cannot set a breakpoint by file and line number as you can
- with gdb. Adding support for this wouldn't be all that tough, I guess, but
- it would be a nuisance. You can set a breakpoint given a function and
- line number, however. An example would be 'b main:2993'. It turns out
- that the way the internal data structures are arranged it is a whole lot
- easier to do things in this way than it would be to try and get the
- source:line type of breakpoint working, but it would probably be worth it
- to try.
- Getting stack traces through Wine itself can be a bit tricky.
- This is because by default the thing is built with optimization
- enabled, and as a result sometimes functions don't get frames, and
- lots of variables are optimized into registers. You can turn off
- optimization for a few key source files if it will help you.
- Memory consumption is getting to be a real problem. I think 32Mb is
- no longer sufficient to debug wine - 48 or 64 is probably a whole lot better.
- Unfortunately I cannot shut down X to save memory :-).
- *************************************************************************
- EXAMPLES:
- Here is an example of how I tracked down a bug in Wine. The program
- is something that just maps and dumps the contents of a Win32 executable.
- It was dying for some reason.
- Note that this example is rather old and does not necessarily use current
- syntax !
- Start the first time through.
- bash$ ls -l dumpexe.exe
- -rw-rw-r-- 1 eric devel 168448 Jan 4 13:51 dumpexe.exe
- bash$ ./wine -debug './dumpexe.exe -symbol ./dumpexe.exe'
- Warning: invalid dir 'e:\test' in path, deleting it.
- Win32 task 'W32SXXXX': Breakpoint 1 at 0x081a3450
- Loading symbols from ELF file ./wine...
- Loading symbols from ELF file /usr/X11R6/lib/libXpm.so.4.6...
- Loading symbols from ELF file /usr/X11R6/lib/libSM.so.6.0...
- Loading symbols from ELF file /usr/X11R6/lib/libICE.so.6.0...
- Loading symbols from ELF file /usr/X11R6/lib/libXext.so.6.0...
- Loading symbols from ELF file /usr/X11R6/lib/libX11.so.6.0...
- Loading symbols from ELF file /lib/libm.so.5.0.5...
- Loading symbols from ELF file /lib/libc.so.5.2.18...
- Loading symbols from ELF file /lib/ld-linux.so.1...
- Loading symbols from Win32 file ./dumpexe.exe...
- Stopped on breakpoint 1 at 0x081a3450 (_mainCRTStartup)
- In 32 bit mode.
- *** Invalid address 0x414c5ff8 (KERNEL32_NULL_THUNK_DATA+0x3930ee6c)
- 0x081a3450 (_mainCRTStartup): movl %fs:0,%eax
- Wine-dbg>b DumpFile
- Breakpoint 2 at 0x081a0078 (DumpFile+0x9 [dumpexe.c:2723])
- Wine-dbg>c
- Dump File: ./dumpexe.exe
- Stopped on breakpoint 2 at 0x081a0078 (DumpFile+0x9 [dumpexe.c:2723])
- Enter path to file dumpexe.c: ../de
- 2723 HANDLE hFile = NULL;
- 0x081a0078 (DumpFile+0x9 [dumpexe.c:2723]): movl $0x0,0xfffffff4(%ebp)
- Wine-dbg>list
- 2723 HANDLE hFile = NULL;
- 2724 HANDLE hMap = NULL;
- 2725 PSTR lpMap = NULL;
- 2726 DWORD dwFileSize = 0;
- 2727 DWORD dwFileSizeHigh = 0;
- 2728
- 2729 PIMAGE_DOS_HEADER lpImageDOS = NULL;
- 2730 PIMAGE_FILE_HEADER lpImageFile = NULL;
- 2731 PIMAGE_NT_HEADERS lpImageNT = NULL;
- 2732
- 2733 /*
- Wine-dbg>n 10
- 2747 dwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
- 0x081a00ea (DumpFile+0x7b [dumpexe.c:2747]): leal 0xfffffff0(%ebp),%eax
- Wine-dbg>n
- 2749 && (GetLastError() != NO_ERROR) )
- 0x081a00fb (DumpFile+0x8c [dumpexe.c:2749]): cmpl $-1,0xffffffe8(%ebp)
- Wine-dbg>x/d dwFileSize
- x/d dwFileSize
- 168448
- Wine-dbg>n
- 2758 PAGE_READONLY, 0, 0, (LPSTR) NULL);
- 0x081a0124 (DumpFile+0xb5 [dumpexe.c:2758]): pushl $0x0
- Wine-dbg>list 2750
- list 2750
- 2750 {
- 2751 Fatal("Cannot get size of file %s", lpFileName);
- 2752 }
- 2753
- 2754 /*
- 2755 * map the file
- 2756 */
- 2757 hMap = CreateFileMapping(hFile, (LPSECURITY_ATTRIBUTES) NULL,
- 2758 PAGE_READONLY, 0, 0, (LPSTR) NULL);
- 2759 if( hMap == NULL )
- 2760 {
- Wine-dbg>n
- 2759 if( hMap == NULL )
- 0x081a013b (DumpFile+0xcc [dumpexe.c:2759]): cmpl $0,0xfffffffc(%ebp)
- Wine-dbg>x hMap
- 08e48c30
- Wine-dbg>n
- 2767 lpMap = (LPSTR) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
- 0x081a0156 (DumpFile+0xe7 [dumpexe.c:2767]): pushl $0x0
- Wine-dbg>n
- 2768 if( lpMap == NULL )
- 0x081a016b (DumpFile+0xfc [dumpexe.c:2768]): cmpl $0,0xffffffe0(%ebp)
- Wine-dbg>print lpMap
- 0x414c5f40
- Wine-dbg>x lpMap
- 40007000
- Wine-dbg> x/10x 0x40007000
- x/10x 0x40007000
- 0x40007000 (KERNEL32_NULL_THUNK_DATA+0x37e4fe74): *** Invalid address 0x40007000 (KERNEL32_NULL_THUNK_DATA+0x37e4fe74)
- Wine-dbg>quit
- $
- *******************************************************************
- The first time through, we find that MapViewOfFile isn't mapping the file
- correctly into the virtual address space. Try running again, and step into
- MapViewOfFile to figure out what went wrong.
- *******************************************************************
- bash$ ./wine -debug './dumpexe.exe -symbol ./dumpexe.exe'
- Warning: invalid dir 'e:\test' in path, deleting it.
- Win32 task 'W32SXXXX': Breakpoint 1 at 0x081a3450
- Loading symbols from ELF file ./wine...
- Loading symbols from ELF file /usr/X11R6/lib/libXpm.so.4.6...
- Loading symbols from ELF file /usr/X11R6/lib/libSM.so.6.0...
- Loading symbols from ELF file /usr/X11R6/lib/libICE.so.6.0...
- Loading symbols from ELF file /usr/X11R6/lib/libXext.so.6.0...
- Loading symbols from ELF file /usr/X11R6/lib/libX11.so.6.0...
- Loading symbols from ELF file /lib/libm.so.5.0.5...
- Loading symbols from ELF file /lib/libc.so.5.2.18...
- Loading symbols from ELF file /lib/ld-linux.so.1...
- Loading symbols from Win32 file ./dumpexe.exe...
- Stopped on breakpoint 1 at 0x081a3450 (_mainCRTStartup)
- In 32 bit mode.
- *** Invalid address 0x414c5ff8 (KERNEL32_NULL_THUNK_DATA+0x3930ee6c)
- 0x081a3450 (_mainCRTStartup): movl %fs:0,%eax
- Wine-dbg>b DumpFile:2767
- Breakpoint 2 at 0x081a0156 (DumpFile+0xe7 [dumpexe.c:2767])
- Wine-dbg>c
- Dump File: ./dumpexe.exe
- Stopped on breakpoint 2 at 0x081a0156 (DumpFile+0xe7 [dumpexe.c:2767])
- Enter path to file dumpexe.c: ../de
- 2767 lpMap = (LPSTR) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
- 0x081a0156 (DumpFile+0xe7 [dumpexe.c:2767]): pushl $0x0
- Wine-dbg>step
- 390 0385 stdcall MapViewOfFile(long long long long long) MapViewOfFile
- 0x080d793c (KERNEL32_385 [kernel32.spec:390]): pushl %ebp
- Wine-dbg>step
- 223 if (!debugging_relay) return;
- 0x080c83dc (RELAY_DebugCallFrom32+0xc [relay.c:223]): cmpw $0,0x644a
- Wine-dbg>
- 244 }
- 0x080c848e (RELAY_DebugCallFrom32+0xbe [relay.c:244]): leal 0xfffffff4(%ebp),%esp
- Wine-dbg>
- 103 return MapViewOfFileEx(handle,access,offhi,offlo,size,0);
- 0x080911a4 (MapViewOfFile+0x14 [file.c:103]): pushl $0x0
- Wine-dbg>
- 113 FILEMAP_OBJECT *fmap = (FILEMAP_OBJECT*)handle;
- 0x080911cf (MapViewOfFileEx+0xf [file.c:113]): movl 0x8(%ebp),%esi
- Wine-dbg>n
- 115 if (!size) size = fmap->size;
- 0x080911d2 (MapViewOfFileEx+0x12 [file.c:115]): testl %ebx,%ebx
- Wine-dbg>list
- list
- 115 if (!size) size = fmap->size;
- 116 if (!size) size = 1;
- 117 return mmap ((caddr_t)st, size, fmap->prot,
- 118 MAP_ANON|MAP_PRIVATE,
- 119 FILE_GetUnixHandle(fmap->hfile),
- 120 offlo);
- 121 }
- 122
- 123 /***********************************************************************
- 124 * UnmapViewOfFile (KERNEL32.385)
- 125 */
- Wine-dbg>x size
- 00000000
- Wine-dbg>n
- 116 if (!size) size = 1;
- 0x080911d9 (MapViewOfFileEx+0x19 [file.c:116]): testl %ebx,%ebx
- Wine-dbg>x size
- 00000000
- Wine-dbg>n
- 117 return mmap ((caddr_t)st, size, fmap->prot,
- 0x080911e2 (MapViewOfFileEx+0x22 [file.c:117]): pushl %eax
- Wine-dbg>x size
- 00000000
- Wine-dbg>info local
- MapViewOfFileEx:handle == 0x08e48c90
- MapViewOfFileEx:access == 0x00000004
- MapViewOfFileEx:offhi == 0x00000000
- MapViewOfFileEx:offlo == 0x00000000
- MapViewOfFileEx:size == 0x00000000
- MapViewOfFileEx:st == 0x00000000
- MapViewOfFileEx:offlo optimized into register $eax
- MapViewOfFileEx:size optimized into register $ebx
- MapViewOfFileEx:st optimized into register $edi
- MapViewOfFileEx:fmap optimized into register $esi
- Wine-dbg>print $ebx
- 0x0001
- Wine-dbg>bt
- bt
- Backtrace:
- =>0 0x080911e2 (MapViewOfFileEx+0x22 [file.c:117])
- 1 0x080911b0 (MapViewOfFile+0x20(handle=0x8e48c90, access=0x4, offhi=0x0, offlo=0x0, size=0x0) [file.c:104])
- 2 0x08104ab5 (CallFrom32_stdcall_5+0x25 [callfrom32.s])
- 3 0x081a0168 (DumpFile+0xf9(lpFileName=0x414c61ed) [dumpexe.c:2767])
- 4 0x081a0c35 (main+0x410(argc=0x3, argv=0x414c61cc) [dumpexe.c:3078])
- 5 0x081a3514 (_mainCRTStartup+0xc4)
- 6 0x0810549f (Code_Start+0x13 [callto32.s])
- 7 0x0802fdac (TASK_CallToStart+0x8c [task.c:373])
- Wine-dbg>
- *******************************************************************
- Notice that you can step through the thunks into our own transfer
- routines. You will notice that the source line displays as something
- like:
- 390 0385 stdcall MapViewOfFile(long long long long long) MapViewOfFile
- This is just the source line from the spec file that caused the transfer
- routine to be generated. From this you can step again, and you step
- into the relay logging code - keep stepping and you eventually step into
- the actual function that does the dirty work.
- At this point an examination of the source to the Win32 program
- and an examination of the source to win32/file.s showed where the problem
- was. When you specify 0 for the size of the object in CreateFileMapping,
- it is supposed to use the entire size of the file as the size of the
- object. Instead we were just blindly copying the number over.
- *******************************************************************
- Wine-dbg>b main
- Breakpoint 1 at 0x080108c0 (main [dbgmain.c:213])
- Wine-dbg>print breakpoints[1]
- {addr={type=0x08043000, seg=0, off=134285504}, addrlen=' ', opcode='U', enabled=1, skipcount=0, in_use=1}
- Wine-dbg> print breakpoints[1].enabled
- 1
- Wine-dbg>set breakpoints[0].enabled = 0
- Wine-dbg>print breakpoints[0].enabled
- 0
- Wine-dbg>print type_hash_table[1]->type
- STRUCT
- Wine-dbg>print type_hash_table[1]
- 0x08072020
- Wine-dbg>print *type_hash_table[1]
- print *type_hash_table[1]
- {type=STRUCT, next=0x00000000, name="LOGPALETTE", un={basic={basic_type=8, output_format=" V M", basic_size=-128, b_signed=0}, bitfield={bitoff=8, nbits=0, basetype=0x081d56c0}, pointer={pointsto=0x00000008}, funct={rettype=0x00000008}, array={start=8, end=136140480, basictype=0x08043e80}, structure={size=8, members=0x081d56c0}, enumeration={members=0x00000008}}}
- Wine-dbg>
- *******************************************************************
- This example shows how you can print out various data structures.
- Note that enumerated types are displayed in the symbolic form, and strings
- are displayed in the expected manner.
- You can use the set command to set more or less anything. Note
- however that you cannot use enumerated types on the RHS of the expression.
- *******************************************************************
- Wine-dbg>list
- 2986 if( argc <= 1 )
- 2987 {
- 2988 Usage(argv[0]);
- 2989 }
- 2990
- 2991 for( i = 1; i < argc; i++ )
- 2992 {
- 2993 if( strncmp(argv[i], "-dos", sizeof("-dos") - 1) == 0 )
- 2994 {
- 2995 DmpCtrl.bDumpDOSHeader = TRUE;
- 2996 }
- Wine-dbg>b 2993
- Breakpoint 3 at 0x081a8861 (main+0x3c [dumpexe.c:2993])
- Wine-dbg>condition 3 i == 2
- Wine-dbg>c
- Stopped on breakpoint 3 at 0x081a8861 (main+0x3c [dumpexe.c:2993])
- 2993 if( strncmp(argv[i], "-dos", sizeof("-dos") - 1) == 0 )
- 0x081a8861 (main+0x3c [dumpexe.c:2993]): pushl $0x4
- Wine-dbg>print i
- 2
- Wine-dbg>print argv[i]
- "./dumpexe.exe"
- *******************************************************************
- This example shows how to use conditional breakpoints.
- Here is another one that demonstrates another cool feature
- conditional breakpoints that involve a function call:
- condition 3 strcmp(argv[i], "./dumpexe.exe") == 0
- *******************************************************************
- Wine-dbg>list
- 2986 if( argc <= 1 )
- 2987 {
- 2988 Usage(argv[0]);
- 2989 }
- 2990
- 2991 for( i = 1; i < argc; i++ )
- 2992 {
- 2993 if( strncmp(argv[i], "-dos", sizeof("-dos") - 1) == 0 )
- 2994 {
- 2995 DmpCtrl.bDumpDOSHeader = TRUE;
- 2996 }
- Wine-dbg>b 2993
- Breakpoint 3 at 0x081a8861 (main+0x3c [dumpexe.c:2993])
- Wine-dbg>condition 3 strcmp(argv[i], "./dumpexe.exe") == 0
- Wine-dbg>info break
- Breakpoints:
- 1: y 0x081ab450 (_mainCRTStartup)
- 2: y 0x081a882e (main+0x9 [dumpexe.c:2986])
- 3: y 0x081a8861 (main+0x3c [dumpexe.c:2993])
- stop when ( strcmp(( argv[i] ), "./dumpexe.exe") == 0 )
- Wine-dbg>c
- Stopped on breakpoint 3 at 0x081a8861 (main+0x3c [dumpexe.c:2993])
- 2993 if( strncmp(argv[i], "-dos", sizeof("-dos") - 1) == 0 )
- 0x081a8861 (main+0x3c [dumpexe.c:2993]): pushl $0x4
- Wine-dbg>print i
- 2
- Wine-dbg>print argv[i]
- "./dumpexe.exe"
- Wine-dbg>
|