1 This is the core of the Wine debugger. The reverse assember
2 was stolen from Mach more or less intact. It turns out that there are
3 two variables that are set differently if you are reverse assembling
4 16 bit code, and on the whole it seems to work.
8 The internal debugger has *tons* more capability than it did before.
9 I have enclosed some examples that show usage at the end of this file.
12 1) Ability of debugger to read debug information from wine executable
13 *and* from Win32 executables. Local variable and line number information is
14 also read and processed.
16 2) The internal debugger is capable of 'stepping' to the next
17 line number, just like gdb. Examples of the commands are:
29 All of these should be exactly like how gdb does things.
31 3) The internal debugger now has a sense of what source file and line
32 number a given PC is at. New commands to support this are just like gdb,
39 there are a variety of formats of arguments for the list command. All
40 permutations supported by gdb should also be supported.
42 4) The internal debugger knows about datatypes of various objects,
43 for both Win32 *and* the debugging information in the wine executable itself.
44 I have enclosed an example of how this works at the end.
46 5) There are more ways the 'b' command can be used to set breakpoints.
54 I don't think this covers all of the permutations that gdb accepts (this should
55 be cleaned up someday so that all possibilities are acceptable).
57 6) The 'print' and 'x' commands should behave more or less exactly
58 as they do under gdb. The difference is that the way the data is presented
59 will be slightly different, but the content should be fundamentally the same.
61 7) The internal debugger now supports conditional breakpoints, and
62 automatic display expressions. An example is at the end of this file. The
63 syntax and usage should be identical to that of gdb.
65 8) Type casts can be made from within the debugger, but they currently
66 don't work with typedef'ed types. They only work with builtin types and
67 named structures unions, etc. The problem is that internally we don't always
68 record the typedefed names of structures, so we have no guarantee that we
69 would know what each type is. This can be fixed, of course - it just takes
70 more memory. Note that in some cases, typedefed structures could be cast
71 using '(struct typedfname)' instead of '(typedfname)'. Technically this
72 isn't quite correct, but if and when the rest of this stuff gets fixed,
73 this would need to get corrected too.
77 If it weren't for the fact that gdb doesn't grok the Win32 debug
78 information, you could just use gdb. The internal debugger should be able
79 to read and use debugging information for both Win32 and also for the
80 Wine executable, making it possible to debug the combination of the two
81 together as if it were one large (very large) entity.
83 LIMITATIONS AND DIFFERENCES FROM GDB:
85 You cannot set a breakpoint by file and line number as you can
86 with gdb. Adding support for this wouldn't be all that tough, I guess, but
87 it would be a nuisance. You can set a breakpoint given a function and
88 line number, however. An example would be 'b main:2993'. It turns out
89 that the way the internal data structures are arranged it is a whole lot
90 easier to do things in this way than it would be to try and get the
91 source:line type of breakpoint working, but it would probably be worth it
94 Getting stack traces through Wine itself can be a bit tricky.
95 This is because by default the thing is built with optimization
96 enabled, and as a result sometimes functions don't get frames, and
97 lots of variables are optimized into registers. You can turn off
98 optimization for a few key source files if it will help you.
100 Memory consumption is getting to be a real problem. I think 32Mb is
101 no longer sufficient to debug wine - 48 or 64 is probably a whole lot better.
102 Unfortunately I cannot shut down X to save memory :-).
104 *************************************************************************
107 Here is an example of how I tracked down a bug in Wine. The program
108 is something that just maps and dumps the contents of a Win32 executable.
109 It was dying for some reason.
111 Start the first time through.
113 bash$ ls -l dumpexe.exe
114 -rw-rw-r-- 1 eric devel 168448 Jan 4 13:51 dumpexe.exe
115 bash$ ./wine -debug './dumpexe.exe -symbol ./dumpexe.exe'
116 Warning: invalid dir 'e:\test' in path, deleting it.
117 Win32 task 'W32SXXXX': Breakpoint 1 at 0x081a3450
118 Loading symbols from ELF file ./wine...
119 Loading symbols from ELF file /usr/X11R6/lib/libXpm.so.4.6...
120 Loading symbols from ELF file /usr/X11R6/lib/libSM.so.6.0...
121 Loading symbols from ELF file /usr/X11R6/lib/libICE.so.6.0...
122 Loading symbols from ELF file /usr/X11R6/lib/libXext.so.6.0...
123 Loading symbols from ELF file /usr/X11R6/lib/libX11.so.6.0...
124 Loading symbols from ELF file /lib/libm.so.5.0.5...
125 Loading symbols from ELF file /lib/libc.so.5.2.18...
126 Loading symbols from ELF file /lib/ld-linux.so.1...
127 Loading symbols from Win32 file ./dumpexe.exe...
128 Stopped on breakpoint 1 at 0x081a3450 (_mainCRTStartup)
130 *** Invalid address 0x414c5ff8 (
\7fKERNEL32_NULL_THUNK_DATA+0x3930ee6c)
131 0x081a3450 (_mainCRTStartup): movl %fs:0,%eax
133 Breakpoint 2 at 0x081a0078 (DumpFile+0x9 [dumpexe.c:2723])
135 Dump File: ./dumpexe.exe
136 Stopped on breakpoint 2 at 0x081a0078 (DumpFile+0x9 [dumpexe.c:2723])
137 Enter path to file dumpexe.c: ../de
138 2723 HANDLE hFile = NULL;
139 0x081a0078 (DumpFile+0x9 [dumpexe.c:2723]): movl $0x0,0xfffffff4(%ebp)
141 2723 HANDLE hFile = NULL;
142 2724 HANDLE hMap = NULL;
143 2725 PSTR lpMap = NULL;
144 2726 DWORD dwFileSize = 0;
145 2727 DWORD dwFileSizeHigh = 0;
147 2729 PIMAGE_DOS_HEADER lpImageDOS = NULL;
148 2730 PIMAGE_FILE_HEADER lpImageFile = NULL;
149 2731 PIMAGE_NT_HEADERS lpImageNT = NULL;
153 2747 dwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
154 0x081a00ea (DumpFile+0x7b [dumpexe.c:2747]): leal 0xfffffff0(%ebp),%eax
156 2749 && (GetLastError() != NO_ERROR) )
\r
157 0x081a00fb (DumpFile+0x8c [dumpexe.c:2749]): cmpl $-1,0xffffffe8(%ebp)
158 Wine-dbg>x/d dwFileSize
162 2758 PAGE_READONLY, 0, 0, (LPSTR) NULL);
163 0x081a0124 (DumpFile+0xb5 [dumpexe.c:2758]): pushl $0x0
167 2751 Fatal("Cannot get size of file %s", lpFileName);
173 2757 hMap = CreateFileMapping(hFile, (LPSECURITY_ATTRIBUTES) NULL,
174 2758 PAGE_READONLY, 0, 0, (LPSTR) NULL);
175 2759 if( hMap == NULL )
178 2759 if( hMap == NULL )
179 0x081a013b (DumpFile+0xcc [dumpexe.c:2759]): cmpl $0,0xfffffffc(%ebp)
183 2767 lpMap = (LPSTR) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
184 0x081a0156 (DumpFile+0xe7 [dumpexe.c:2767]): pushl $0x0
186 2768 if( lpMap == NULL )
187 0x081a016b (DumpFile+0xfc [dumpexe.c:2768]): cmpl $0,0xffffffe0(%ebp)
192 Wine-dbg> x/10x 0x40007000
194 0x40007000 (
\7fKERNEL32_NULL_THUNK_DATA+0x37e4fe74): *** Invalid address 0x40007000 (
\7fKERNEL32_NULL_THUNK_DATA+0x37e4fe74)
198 *******************************************************************
199 The first time through, we find that MapViewOfFile isn't mapping the file
200 correctly into the virtual address space. Try running again, and step into
201 MapViewOfFile to figure out what went wrong.
202 *******************************************************************
205 bash$ ./wine -debug './dumpexe.exe -symbol ./dumpexe.exe'
206 Warning: invalid dir 'e:\test' in path, deleting it.
207 Win32 task 'W32SXXXX': Breakpoint 1 at 0x081a3450
208 Loading symbols from ELF file ./wine...
209 Loading symbols from ELF file /usr/X11R6/lib/libXpm.so.4.6...
210 Loading symbols from ELF file /usr/X11R6/lib/libSM.so.6.0...
211 Loading symbols from ELF file /usr/X11R6/lib/libICE.so.6.0...
212 Loading symbols from ELF file /usr/X11R6/lib/libXext.so.6.0...
213 Loading symbols from ELF file /usr/X11R6/lib/libX11.so.6.0...
214 Loading symbols from ELF file /lib/libm.so.5.0.5...
215 Loading symbols from ELF file /lib/libc.so.5.2.18...
216 Loading symbols from ELF file /lib/ld-linux.so.1...
217 Loading symbols from Win32 file ./dumpexe.exe...
218 Stopped on breakpoint 1 at 0x081a3450 (_mainCRTStartup)
220 *** Invalid address 0x414c5ff8 (
\7fKERNEL32_NULL_THUNK_DATA+0x3930ee6c)
221 0x081a3450 (_mainCRTStartup): movl %fs:0,%eax
222 Wine-dbg>b DumpFile:2767
223 Breakpoint 2 at 0x081a0156 (DumpFile+0xe7 [dumpexe.c:2767])
225 Dump File: ./dumpexe.exe
\r
226 Stopped on breakpoint 2 at 0x081a0156 (DumpFile+0xe7 [dumpexe.c:2767])
227 Enter path to file dumpexe.c: ../de
228 2767 lpMap = (LPSTR) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
229 0x081a0156 (DumpFile+0xe7 [dumpexe.c:2767]): pushl $0x0
231 390 0385 stdcall MapViewOfFile(long long long long long) MapViewOfFile
232 0x080d793c (KERNEL32_385 [kernel32.spec:390]): pushl %ebp
234 223 if (!debugging_relay) return;
235 0x080c83dc (RELAY_DebugCallFrom32+0xc [relay.c:223]): cmpw $0,0x644a
238 0x080c848e (RELAY_DebugCallFrom32+0xbe [relay.c:244]): leal 0xfffffff4(%ebp),%esp
240 103 return MapViewOfFileEx(handle,access,offhi,offlo,size,0);
241 0x080911a4 (MapViewOfFile+0x14 [file.c:103]): pushl $0x0
243 113 FILEMAP_OBJECT *fmap = (FILEMAP_OBJECT*)handle;
244 0x080911cf (MapViewOfFileEx+0xf [file.c:113]): movl 0x8(%ebp),%esi
246 115 if (!size) size = fmap->size;
247 0x080911d2 (MapViewOfFileEx+0x12 [file.c:115]): testl %ebx,%ebx
250 115 if (!size) size = fmap->size;
251 116 if (!size) size = 1;
252 117 return mmap ((caddr_t)st, size, fmap->prot,
253 118 MAP_ANON|MAP_PRIVATE,
254 119 FILE_GetUnixHandle(fmap->hfile),
258 123 /***********************************************************************
259 124 * UnmapViewOfFile (KERNEL32.385)
264 116 if (!size) size = 1;
265 0x080911d9 (MapViewOfFileEx+0x19 [file.c:116]): testl %ebx,%ebx
269 117 return mmap ((caddr_t)st, size, fmap->prot,
270 0x080911e2 (MapViewOfFileEx+0x22 [file.c:117]): pushl %eax
274 MapViewOfFileEx:handle == 0x08e48c90
275 MapViewOfFileEx:access == 0x00000004
276 MapViewOfFileEx:offhi == 0x00000000
277 MapViewOfFileEx:offlo == 0x00000000
278 MapViewOfFileEx:size == 0x00000000
279 MapViewOfFileEx:st == 0x00000000
280 MapViewOfFileEx:offlo optimized into register $eax
281 MapViewOfFileEx:size optimized into register $ebx
282 MapViewOfFileEx:st optimized into register $edi
283 MapViewOfFileEx:fmap optimized into register $esi
289 =>0 0x080911e2 (MapViewOfFileEx+0x22 [file.c:117])
290 1 0x080911b0 (MapViewOfFile+0x20(handle=0x8e48c90, access=0x4, offhi=0x0, offlo=0x0, size=0x0) [file.c:104])
291 2 0x08104ab5 (CallFrom32_stdcall_5+0x25 [callfrom32.s])
292 3 0x081a0168 (DumpFile+0xf9(lpFileName=0x414c61ed) [dumpexe.c:2767])
293 4 0x081a0c35 (main+0x410(argc=0x3, argv=0x414c61cc) [dumpexe.c:3078])
294 5 0x081a3514 (_mainCRTStartup+0xc4)
295 6 0x0810549f (Code_Start+0x13 [callto32.s])
296 7 0x0802fdac (TASK_CallToStart+0x8c [task.c:373])
300 *******************************************************************
301 Notice that you can step through the thunks into our own transfer
302 routines. You will notice that the source line displays as something
305 390 0385 stdcall MapViewOfFile(long long long long long) MapViewOfFile
307 This is just the source line from the spec file that caused the transfer
308 routine to be generated. From this you can step again, and you step
309 into the relay logging code - keep stepping and you eventually step into
310 the actual function that does the dirty work.
312 At this point an examination of the source to the Win32 program
313 and an examination of the source to win32/file.s showed where the problem
314 was. When you specify 0 for the size of the object in CreateFileMapping,
315 it is supposed to use the entire size of the file as the size of the
316 object. Instead we were just blindly copying the number over.
318 *******************************************************************
321 Breakpoint 1 at 0x080108c0 (main [dbgmain.c:213])
322 Wine-dbg>print breakpoints[1]
323 {addr={type=0x08043000, seg=0, off=134285504}, addrlen=' ', opcode='U', enabled=1, skipcount=0, in_use=1}
325 Wine-dbg> print breakpoints[1].enabled
327 Wine-dbg>set breakpoints[0].enabled = 0
328 Wine-dbg>print breakpoints[0].enabled
331 Wine-dbg>print type_hash_table[1]->type
334 Wine-dbg>print type_hash_table[1]
336 Wine-dbg>print *type_hash_table[1]
337 print *type_hash_table[1]
\r
338 {type=STRUCT, next=0x00000000, name="LOGPALETTE", un={basic={basic_type=8, output_format=" V
\1d\b M
\1d\b", 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}}}
341 *******************************************************************
343 This example shows how you can print out various data structures.
344 Note that enumerated types are displayed in the symbolic form, and strings
345 are displayed in the expected manner.
347 You can use the set command to set more or less anything. Note
348 however that you cannot use enumerated types on the RHS of the expression.
350 *******************************************************************
359 2991 for( i = 1; i < argc; i++ )
361 2993 if( strncmp(argv[i], "-dos", sizeof("-dos") - 1) == 0 )
363 2995 DmpCtrl.bDumpDOSHeader = TRUE;
366 Breakpoint 3 at 0x081a8861 (main+0x3c [dumpexe.c:2993])
367 Wine-dbg>condition 3 i == 2
369 Stopped on breakpoint 3 at 0x081a8861 (main+0x3c [dumpexe.c:2993])
370 2993 if( strncmp(argv[i], "-dos", sizeof("-dos") - 1) == 0 )
371 0x081a8861 (main+0x3c [dumpexe.c:2993]): pushl $0x4
374 Wine-dbg>print argv[i]
377 *******************************************************************
379 This example shows how to use conditional breakpoints.
380 Here is another one that demonstrates another cool feature
381 conditional breakpoints that involve a function call:
383 condition 3 strcmp(argv[i], "./dumpexe.exe") == 0
385 *******************************************************************
394 2991 for( i = 1; i < argc; i++ )
396 2993 if( strncmp(argv[i], "-dos", sizeof("-dos") - 1) == 0 )
398 2995 DmpCtrl.bDumpDOSHeader = TRUE;
401 Breakpoint 3 at 0x081a8861 (main+0x3c [dumpexe.c:2993])
402 Wine-dbg>condition 3 strcmp(argv[i], "./dumpexe.exe") == 0
405 1: y 0x081ab450 (_mainCRTStartup)
406 2: y 0x081a882e (main+0x9 [dumpexe.c:2986])
407 3: y 0x081a8861 (main+0x3c [dumpexe.c:2993])
408 stop when ( strcmp(( argv[i] ), "./dumpexe.exe") == 0 )
410 Stopped on breakpoint 3 at 0x081a8861 (main+0x3c [dumpexe.c:2993])
411 2993 if( strncmp(argv[i], "-dos", sizeof("-dos") - 1) == 0 )
412 0x081a8861 (main+0x3c [dumpexe.c:2993]): pushl $0x4
415 Wine-dbg>print argv[i]