Added regedit unit test, a couple minor changes to regedit.
[wine] / tools / winedump / debug.c
1 /*
2  * Made after:
3  *      CVDump - Parses through a Visual Studio .DBG file in CodeView 4 format
4  *      and dumps the info to STDOUT in a human-readable format
5  *
6  *      Copyright 2000 John R. Sheets
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef HAVE_SYS_MMAN_H
33 #include <sys/mman.h>
34 #endif
35 #include <fcntl.h>
36
37 #include "winnt.h"
38 #include "winedump.h"
39 #include "pe.h"
40 #include "cvinclude.h"
41
42 /*
43  * .DBG File Layout:
44  *
45  * IMAGE_SEPARATE_DEBUG_HEADER
46  * IMAGE_SECTION_HEADER[]
47  * IMAGE_DEBUG_DIRECTORY[]
48  * OMFSignature
49  * debug data (typical example)
50  *   - IMAGE_DEBUG_TYPE_MISC
51  *   - IMAGE_DEBUG_TYPE_FPO
52  *   - IMAGE_DEBUG_TYPE_CODEVIEW
53  * OMFDirHeader
54  * OMFDirEntry[]
55  */
56
57 /*
58  * Descriptions:
59  *
60  * (hdr)  IMAGE_SEPARATE_DEBUG_HEADER - .DBG-specific file header; holds info that
61  *        applies to the file as a whole, including # of COFF sections, file offsets, etc.
62  * (hdr)  IMAGE_SECTION_HEADER - list of COFF sections copied verbatim from .EXE;
63  *        although this directory contains file offsets, these offsets are meaningless
64  *        in the context of the .DBG file, because only the section headers are copied
65  *        to the .DBG file...not the binary data it points to.
66  * (hdr)  IMAGE_DEBUG_DIRECTORY - list of different formats of debug info contained in file
67  *        (see IMAGE_DEBUG_TYPE_* descriptions below); tells where each section starts
68  * (hdr)  OMFSignature (CV) - Contains "NBxx" signature, plus file offset telling how far
69  *        into the IMAGE_DEBUG_TYPE_CODEVIEW section the OMFDirHeader and OMFDirEntry's sit
70  * (data) IMAGE_DEBUG_TYPE_MISC - usually holds name of original .EXE file
71  * (data) IMAGE_DEBUG_TYPE_FPO - Frame Pointer Optimization data; used for dealing with
72  *        optimized stack frames (optional)
73  * (data) IMAGE_DEBUG_TYPE_CODEVIEW - *** THE GOOD STUFF ***
74  *        This block of data contains all the symbol tables, line number info, etc.,
75  *        that the Visual C++ debugger needs.
76  * (hdr)  OMFDirHeader (CV) -
77  * (hdr)  OMFDirEntry (CV) - list of subsections within CodeView debug data section
78  */
79
80 /*
81  * The .DBG file typically has three arrays of directory entries, which tell
82  * the OS or debugger where in the file to look for the actual data
83  *
84  * IMAGE_SECTION_HEADER - number of entries determined by:
85  *    (IMAGE_SEPARATE_DEBUG_HEADER.NumberOfSections)
86  *
87  * IMAGE_DEBUG_DIRECTORY - number of entries determined by:
88  *    (IMAGE_SEPARATE_DEBUG_HEADER.DebugDirectorySize / sizeof (IMAGE_DEBUG_DIRECTORY))
89  *
90  * OMFDirEntry - number of entries determined by:
91  *    (OMFDirHeader.cDir)
92  */
93
94 static  void*           cv_base /* = 0 */;
95
96 static int dump_cv_sst_module(OMFDirEntry* omfde)
97 {
98     OMFModule*  module;
99     OMFSegDesc* segDesc;
100     int         i;
101
102     module = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFModule));
103     if (!module) {printf("Can't get the OMF-Module, aborting\n"); return FALSE;}
104
105     printf("    olvNumber:          %u\n", module->ovlNumber);
106     printf("    iLib:               %u\n", module->iLib);
107     printf("    cSeg:               %u\n", module->cSeg);
108     printf("    Style:              %c%c\n", module->Style[0], module->Style[1]);
109     printf("    Name:               %.*s\n",
110            *(BYTE*)((char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg),
111            (char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg + 1);
112
113     segDesc = PRD(Offset(module + 1), sizeof(OMFSegDesc) * module->cSeg);
114     if (!segDesc) {printf("Can't get the OMF-SegDesc, aborting\n"); return FALSE;}
115
116     for (i = 0; i < module->cSeg; i++)
117     {
118         printf ("      segment #%2d: offset = [0x%8lx], size = [0x%8lx]\n",
119                 segDesc->Seg, segDesc->Off, segDesc->cbSeg);
120         segDesc++;
121     }
122     return TRUE;
123 }
124
125 static int dump_cv_sst_global_pub(OMFDirEntry* omfde)
126 {
127     long        fileoffset;
128     OMFSymHash* header;
129     BYTE*       symbols;
130     BYTE*       curpos;
131     PUBSYM32*   sym;
132     unsigned    symlen;
133     int         recordlen;
134     char        nametmp[256];
135
136     fileoffset = Offset(cv_base) + omfde->lfo;
137     printf ("    GlobalPub section starts at file offset 0x%lx\n", fileoffset);
138     printf ("    Symbol table starts at 0x%lx\n", fileoffset + sizeof (OMFSymHash));
139
140     printf ("\n                           ----- Begin Symbol Table -----\n");
141     printf ("      (type)      (symbol name)                 (offset)      (len) (seg) (ind)\n");
142
143     header = PRD(fileoffset, sizeof(OMFSymHash));
144     if (!header) {printf("Can't get OMF-SymHash, aborting\n");return FALSE;}
145
146     symbols = PRD(fileoffset + sizeof(OMFSymHash), header->cbSymbol);
147     if (!symbols) {printf("Can't OMF-SymHash details, aborting\n"); return FALSE;}
148
149     /* We don't know how many symbols are in this block of memory...only what
150      * the total size of the block is.  Because the symbol's name is tacked
151      * on to the end of the PUBSYM32 struct, each symbol may take up a different
152      * # of bytes.  This makes it harder to parse through the symbol table,
153      * since we won't know the exact location of the following symbol until we've
154      * already parsed the current one.
155      */
156     for (curpos = symbols; curpos < symbols + header->cbSymbol; curpos += recordlen)
157     {
158         /* Point to the next PUBSYM32 in the table.
159          */
160         sym = (PUBSYM32*)curpos;
161
162         if (sym->reclen < sizeof(PUBSYM32)) break;
163
164         symlen = sym->reclen - sizeof(PUBSYM32) + 1;
165         if (symlen > sizeof(nametmp)) {printf("\nsqueeze%d\n", symlen);symlen = sizeof(nametmp) - 1;}
166
167         memcpy(nametmp, curpos + sizeof (PUBSYM32) + 1, symlen);
168         nametmp[symlen] = '\0';
169
170         printf ("      0x%04x  %-30.30s  [0x%8lx]  [0x%4x]  %d     %ld\n",
171                 sym->rectyp, nametmp, sym->off, sym->reclen, sym->seg, sym->typind);
172
173         /* The entire record is null-padded to the nearest 4-byte
174          * boundary, so we must do a little extra math to keep things straight.
175          */
176         recordlen = (sym->reclen + 3) & ~3;
177     }
178
179     return TRUE;
180 }
181
182 static int dump_cv_sst_global_sym(OMFDirEntry* omfde)
183 {
184     /*** NOT YET IMPLEMENTED ***/
185     return TRUE;
186 }
187
188 static int dump_cv_sst_static_sym(OMFDirEntry* omfde)
189 {
190     /*** NOT YET IMPLEMENTED ***/
191     return TRUE;
192 }
193
194 static int dump_cv_sst_libraries(OMFDirEntry* omfde)
195 {
196     /*** NOT YET IMPLEMENTED ***/
197     return TRUE;
198 }
199
200 static int dump_cv_sst_global_types(OMFDirEntry* omfde)
201 {
202     /*** NOT YET IMPLEMENTED ***/
203     return TRUE;
204 }
205
206 static int dump_cv_sst_seg_map(OMFDirEntry* omfde)
207 {
208     OMFSegMap*          segMap;
209     OMFSegMapDesc*      segMapDesc;
210     int         i;
211
212     segMap = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFSegMap));
213     if (!segMap) {printf("Can't get SegMap, aborting\n");return FALSE;}
214
215     printf("    cSeg:          %u\n", segMap->cSeg);
216     printf("    cSegLog:       %u\n", segMap->cSegLog);
217
218     segMapDesc = PRD(Offset(segMap + 1), segMap->cSeg * sizeof(OMFSegDesc));
219     if (!segMapDesc) {printf("Can't get SegDescr array, aborting\n");return FALSE;}
220
221     for (i = 0; i < segMap->cSeg; i++)
222     {
223         printf("    SegDescr #%2d\n", i + 1);
224         printf("      flags:         %04X\n", segMapDesc[i].flags);
225         printf("      ovl:           %u\n", segMapDesc[i].ovl);
226         printf("      group:         %u\n", segMapDesc[i].group);
227         printf("      frame:         %u\n", segMapDesc[i].frame);
228         printf("      iSegName:      %u\n", segMapDesc[i].iSegName);
229         printf("      iClassName:    %u\n", segMapDesc[i].iClassName);
230         printf("      offset:        %lu\n", segMapDesc[i].offset);
231         printf("      cbSeg:         %lu\n", segMapDesc[i].cbSeg);
232     }
233
234     return TRUE;
235 }
236
237 static int dump_cv_sst_file_index(OMFDirEntry* omfde)
238 {
239     /*** NOT YET IMPLEMENTED ***/
240     return TRUE;
241 }
242
243 static int dump_cv_sst_src_module(OMFDirEntry* omfde)
244 {
245     int                 i, j;
246     BYTE*               rawdata;
247     unsigned long*      seg_info_dw;
248     unsigned short*     seg_info_w;
249     unsigned            ofs;
250     OMFSourceModule*    sourceModule;
251     OMFSourceFile*      sourceFile;
252     OMFSourceLine*      sourceLine;
253
254     rawdata = PRD(Offset(cv_base) + omfde->lfo, omfde->cb);
255     if (!rawdata) {printf("Can't get srcModule subsection details, aborting\n");return FALSE;}
256
257     /* FIXME: check ptr validity */
258     sourceModule = (void*)rawdata;
259     printf ("    Module table: Found %d file(s) and %d segment(s)\n",
260             sourceModule->cFile, sourceModule->cSeg);
261     for (i = 0; i < sourceModule->cFile; i++)
262     {
263         printf ("      File #%2d begins at an offset of 0x%lx in this section\n",
264                 i + 1, sourceModule->baseSrcFile[i]);
265     }
266
267     /* FIXME: check ptr validity */
268     seg_info_dw = (void*)((char*)(sourceModule + 1) +
269                           sizeof(unsigned long) * (sourceModule->cFile - 1));
270     seg_info_w = (unsigned short*)(&seg_info_dw[sourceModule->cSeg * 2]);
271     for (i = 0; i <  sourceModule->cSeg; i++)
272     {
273         printf ("      Segment #%2d start = 0x%lx, end = 0x%lx, seg index = %u\n",
274                 i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1],
275                 seg_info_w[i]);
276     }
277     ofs = sizeof(OMFSourceModule) + sizeof(unsigned long) * (sourceModule->cFile - 1) +
278         sourceModule->cSeg * (2 * sizeof(unsigned long) + sizeof(unsigned short));
279     ofs = (ofs + 3) & ~3;
280
281     /* the OMFSourceFile is quite unpleasant to use:
282      * we have first:
283      *  unsigned short  number of segments
284      *  unsigned short  reservered
285      *  unsigned long   baseSrcLn[# segments]
286      *  unsigned long   offset[2 * #segments]
287      *                          odd indices are start offsets
288      *                          even indices are end offsets
289      *  unsigned char   string length for file name
290      *  char            file name (length is previous field)
291      */
292     /* FIXME: check ptr validity */
293     sourceFile = (void*)(rawdata + ofs);
294     seg_info_dw = (void*)((char*)sourceFile + 2 * sizeof(unsigned short) +
295                           sourceFile->cSeg * sizeof(unsigned long));
296
297     ofs += 2 * sizeof(unsigned short) + 3 * sourceFile->cSeg * sizeof(unsigned long);
298
299     printf("    File table: %.*s\n",
300            *(BYTE*)((char*)sourceModule + ofs), (char*)sourceModule + ofs + 1);
301
302     for (i = 0; i < sourceFile->cSeg; i++)
303     {
304         printf ("      Segment #%2d start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
305                 i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1], sourceFile->baseSrcLn[i]);
306     }
307     /* add file name length */
308     ofs += *(BYTE*)((char*)sourceModule + ofs) + 1;
309     ofs = (ofs + 3) & ~3;
310
311     for (i = 0; i < sourceModule->cSeg; i++)
312     {
313         sourceLine = (void*)(rawdata + ofs);
314         seg_info_dw = (void*)((char*)sourceLine + 2 * sizeof(unsigned short));
315         seg_info_w = (void*)(&seg_info_dw[sourceLine->cLnOff]);
316
317         printf ("    Line table #%2d: Found %d line numbers for segment index %d\n",
318                 i, sourceLine->cLnOff, sourceLine->Seg);
319
320         for (j = 0; j < sourceLine->cLnOff; j++)
321         {
322             printf ("      Pair #%2d: offset = [0x%8lx], linenumber = %d\n",
323                     j + 1, seg_info_dw[j], seg_info_w[j]);
324         }
325         ofs += 2 * sizeof(unsigned short) +
326             sourceLine->cLnOff * (sizeof(unsigned long) + sizeof(unsigned short));
327         ofs = (ofs + 3) & ~3;
328     }
329
330     return TRUE;
331 }
332
333 static int dump_cv_sst_align_sym(OMFDirEntry* omfde)
334 {
335     /*** NOT YET IMPLEMENTED ***/
336
337     return TRUE;
338 }
339
340 static void dump_codeview_all_modules(OMFDirHeader *omfdh)
341 {
342     unsigned            i;
343     OMFDirEntry        *dirEntry;
344     const char*         str;
345
346     if (!omfdh || !omfdh->cDir) return;
347
348     dirEntry = PRD(Offset(omfdh + 1), omfdh->cDir * sizeof(OMFDirEntry));
349     if (!dirEntry) {printf("Can't read DirEntry array, aborting\n"); return;}
350
351     for (i = 0; i < omfdh->cDir; i++)
352     {
353         switch (dirEntry[i].SubSection)
354         {
355         case sstModule:         str = "sstModule";      break;
356         case sstAlignSym:       str = "sstAlignSym";    break;
357         case sstSrcModule:      str = "sstSrcModule";   break;
358         case sstLibraries:      str = "sstLibraries";   break;
359         case sstGlobalSym:      str = "sstGlobalSym";   break;
360         case sstGlobalPub:      str = "sstGlobalPub";   break;
361         case sstGlobalTypes:    str = "sstGlobalTypes"; break;
362         case sstSegMap:         str = "sstSegMap";      break;
363         case sstFileIndex:      str = "sstFileIndex";   break;
364         case sstStaticSym:      str = "sstStaticSym";   break;
365         default:                str = "<undefined>";    break;
366         }
367         printf("Module #%2d (%p)\n", i + 1, &dirEntry[i]);
368         printf("  SubSection:            %04X (%s)\n", dirEntry[i].SubSection, str);
369         printf("  iMod:                  %d\n", dirEntry[i].iMod);
370         printf("  lfo:                   %ld\n", dirEntry[i].lfo);
371         printf("  cb:                    %lu\n", dirEntry[i].cb);
372
373         switch (dirEntry[i].SubSection)
374         {
375         case sstModule:         dump_cv_sst_module(&dirEntry[i]);       break;
376         case sstAlignSym:       dump_cv_sst_align_sym(&dirEntry[i]);    break;
377         case sstSrcModule:      dump_cv_sst_src_module(&dirEntry[i]);   break;
378         case sstLibraries:      dump_cv_sst_libraries(&dirEntry[i]);    break;
379         case sstGlobalSym:      dump_cv_sst_global_sym(&dirEntry[i]);   break;
380         case sstGlobalPub:      dump_cv_sst_global_pub(&dirEntry[i]);   break;
381         case sstGlobalTypes:    dump_cv_sst_global_types(&dirEntry[i]); break;
382         case sstSegMap:         dump_cv_sst_seg_map(&dirEntry[i]);      break;
383         case sstFileIndex:      dump_cv_sst_file_index(&dirEntry[i]);   break;
384         case sstStaticSym:      dump_cv_sst_static_sym(&dirEntry[i]);   break;
385         default:                printf("unsupported type %x\n", dirEntry[i].SubSection); break;
386         }
387         printf("\n");
388     }
389
390     return;
391 }
392
393 static void dump_codeview_headers(unsigned long base, unsigned long len)
394 {
395     OMFDirHeader        *dirHeader;
396     OMFSignature        *signature;
397     OMFDirEntry         *dirEntry;
398     unsigned            i;
399     int modulecount = 0, alignsymcount = 0, srcmodulecount = 0, librariescount = 0;
400     int globalsymcount = 0, globalpubcount = 0, globaltypescount = 0;
401     int segmapcount = 0, fileindexcount = 0, staticsymcount = 0;
402
403     cv_base = PRD(base, len);
404     if (!cv_base) {printf("Can't get full debug content, aborting\n");return;}
405
406     signature = cv_base;
407
408     printf("    CodeView Data\n");
409
410     printf("      Signature:         %.4s\n", signature->Signature);
411     printf("      Filepos:           0x%08lX\n", signature->filepos);
412
413     if (memcmp(signature->Signature, "NB10", 4) == 0)
414     {
415         struct {DWORD TimeStamp; DWORD  Dunno; char Name[1];}* pdb_data;
416         pdb_data = (void*)(signature + 1);
417
418         printf("        TimeStamp:            %08lX (%s)\n",
419                pdb_data->TimeStamp, get_time_str(pdb_data->TimeStamp));
420         printf("        Dunno:                %08lX\n", pdb_data->Dunno);
421         printf("        Filename:             %s\n", pdb_data->Name);
422         return;
423     }
424
425     if (memcmp(signature->Signature, "NB09", 4) != 0 && memcmp(signature->Signature, "NB11", 4) != 0)
426     {
427         printf("Unsupported signature, aborting\n");
428         return;
429     }
430
431     dirHeader = PRD(Offset(cv_base) + signature->filepos, sizeof(OMFDirHeader));
432     if (!dirHeader) {printf("Can't get debug header, aborting\n"); return;}
433
434     printf("      Size of header:    0x%4X\n", dirHeader->cbDirHeader);
435     printf("      Size per entry:    0x%4X\n", dirHeader->cbDirEntry);
436     printf("      # of entries:      0x%8lX (%ld)\n", dirHeader->cDir, dirHeader->cDir);
437     printf("      Offset to NextDir: 0x%8lX\n", dirHeader->lfoNextDir);
438     printf("      Flags:             0x%8lX\n", dirHeader->flags);
439
440     if (!dirHeader->cDir) return;
441
442     dirEntry = PRD(Offset(dirHeader + 1), sizeof(OMFDirEntry) * dirHeader->cDir);
443     if (!dirEntry) {printf("Can't get DirEntry array, aborting\n");return;}
444
445     for (i = 0; i < dirHeader->cDir; i++)
446     {
447         switch (dirEntry[i].SubSection)
448         {
449         case sstModule:         modulecount++;          break;
450         case sstAlignSym:       alignsymcount++;        break;
451         case sstSrcModule:      srcmodulecount++;       break;
452         case sstLibraries:      librariescount++;       break;
453         case sstGlobalSym:      globalsymcount++;       break;
454         case sstGlobalPub:      globalpubcount++;       break;
455         case sstGlobalTypes:    globaltypescount++;     break;
456         case sstSegMap:         segmapcount++;          break;
457         case sstFileIndex:      fileindexcount++;       break;
458         case sstStaticSym:      staticsymcount++;       break;
459         }
460     }
461
462     /* This one has to be > 0
463      */
464     printf ("\nFound: %d sstModule subsections\n", modulecount);
465
466     if (alignsymcount > 0)    printf ("       %d sstAlignSym subsections\n", alignsymcount);
467     if (srcmodulecount > 0)   printf ("       %d sstSrcModule subsections\n", srcmodulecount);
468     if (librariescount > 0)   printf ("       %d sstLibraries subsections\n", librariescount);
469     if (globalsymcount > 0)   printf ("       %d sstGlobalSym subsections\n", globalsymcount);
470     if (globalpubcount > 0)   printf ("       %d sstGlobalPub subsections\n", globalpubcount);
471     if (globaltypescount > 0) printf ("       %d sstGlobalTypes subsections\n", globaltypescount);
472     if (segmapcount > 0)      printf ("       %d sstSegMap subsections\n", segmapcount);
473     if (fileindexcount > 0)   printf ("       %d sstFileIndex subsections\n", fileindexcount);
474     if (staticsymcount > 0)   printf ("       %d sstStaticSym subsections\n", staticsymcount);
475
476     dump_codeview_all_modules(dirHeader);
477 }
478
479 void    dump_codeview(unsigned long base, unsigned long len)
480 {
481     dump_codeview_headers(base, len);
482 }
483