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
6 * Copyright 2000 John R. Sheets
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.
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.
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
24 #include "wine/port.h"
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
36 #ifdef HAVE_SYS_STAT_H
37 # include <sys/stat.h>
39 #ifdef HAVE_SYS_MMAN_H
48 #include "cvinclude.h"
53 * IMAGE_SEPARATE_DEBUG_HEADER
54 * IMAGE_SECTION_HEADER[]
55 * IMAGE_DEBUG_DIRECTORY[]
57 * debug data (typical example)
58 * - IMAGE_DEBUG_TYPE_MISC
59 * - IMAGE_DEBUG_TYPE_FPO
60 * - IMAGE_DEBUG_TYPE_CODEVIEW
68 * (hdr) IMAGE_SEPARATE_DEBUG_HEADER - .DBG-specific file header; holds info that
69 * applies to the file as a whole, including # of COFF sections, file offsets, etc.
70 * (hdr) IMAGE_SECTION_HEADER - list of COFF sections copied verbatim from .EXE;
71 * although this directory contains file offsets, these offsets are meaningless
72 * in the context of the .DBG file, because only the section headers are copied
73 * to the .DBG file...not the binary data it points to.
74 * (hdr) IMAGE_DEBUG_DIRECTORY - list of different formats of debug info contained in file
75 * (see IMAGE_DEBUG_TYPE_* descriptions below); tells where each section starts
76 * (hdr) OMFSignature (CV) - Contains "NBxx" signature, plus file offset telling how far
77 * into the IMAGE_DEBUG_TYPE_CODEVIEW section the OMFDirHeader and OMFDirEntry's sit
78 * (data) IMAGE_DEBUG_TYPE_MISC - usually holds name of original .EXE file
79 * (data) IMAGE_DEBUG_TYPE_FPO - Frame Pointer Optimization data; used for dealing with
80 * optimized stack frames (optional)
81 * (data) IMAGE_DEBUG_TYPE_CODEVIEW - *** THE GOOD STUFF ***
82 * This block of data contains all the symbol tables, line number info, etc.,
83 * that the Visual C++ debugger needs.
84 * (hdr) OMFDirHeader (CV) -
85 * (hdr) OMFDirEntry (CV) - list of subsections within CodeView debug data section
89 * The .DBG file typically has three arrays of directory entries, which tell
90 * the OS or debugger where in the file to look for the actual data
92 * IMAGE_SECTION_HEADER - number of entries determined by:
93 * (IMAGE_SEPARATE_DEBUG_HEADER.NumberOfSections)
95 * IMAGE_DEBUG_DIRECTORY - number of entries determined by:
96 * (IMAGE_SEPARATE_DEBUG_HEADER.DebugDirectorySize / sizeof (IMAGE_DEBUG_DIRECTORY))
98 * OMFDirEntry - number of entries determined by:
102 extern IMAGE_NT_HEADERS
* PE_nt_headers
;
104 static void* cv_base
/* = 0 */;
106 static int dump_cv_sst_module(OMFDirEntry
* omfde
)
112 module
= PRD(Offset(cv_base
) + omfde
->lfo
, sizeof(OMFModule
));
113 if (!module
) {printf("Can't get the OMF-Module, aborting\n"); return FALSE
;}
115 printf(" olvNumber: %u\n", module
->ovlNumber
);
116 printf(" iLib: %u\n", module
->iLib
);
117 printf(" cSeg: %u\n", module
->cSeg
);
118 printf(" Style: %c%c\n", module
->Style
[0], module
->Style
[1]);
119 printf(" Name: %.*s\n",
120 *(BYTE
*)((char*)(module
+ 1) + sizeof(OMFSegDesc
) * module
->cSeg
),
121 (char*)(module
+ 1) + sizeof(OMFSegDesc
) * module
->cSeg
+ 1);
123 segDesc
= PRD(Offset(module
+ 1), sizeof(OMFSegDesc
) * module
->cSeg
);
124 if (!segDesc
) {printf("Can't get the OMF-SegDesc, aborting\n"); return FALSE
;}
126 for (i
= 0; i
< module
->cSeg
; i
++)
128 printf (" segment #%2d: offset = [0x%8lx], size = [0x%8lx]\n",
129 segDesc
->Seg
, segDesc
->Off
, segDesc
->cbSeg
);
135 static int dump_cv_sst_global_pub(OMFDirEntry
* omfde
)
146 fileoffset
= Offset(cv_base
) + omfde
->lfo
;
147 printf (" GlobalPub section starts at file offset 0x%lx\n", fileoffset
);
148 printf (" Symbol table starts at 0x%lx\n", fileoffset
+ sizeof (OMFSymHash
));
150 printf ("\n ----- Begin Symbol Table -----\n");
151 printf (" (type) (symbol name) (offset) (len) (seg) (ind)\n");
153 header
= PRD(fileoffset
, sizeof(OMFSymHash
));
154 if (!header
) {printf("Can't get OMF-SymHash, aborting\n");return FALSE
;}
156 symbols
= PRD(fileoffset
+ sizeof(OMFSymHash
), header
->cbSymbol
);
157 if (!symbols
) {printf("Can't OMF-SymHash details, aborting\n"); return FALSE
;}
159 /* We don't know how many symbols are in this block of memory...only what
160 * the total size of the block is. Because the symbol's name is tacked
161 * on to the end of the PUBSYM32 struct, each symbol may take up a different
162 * # of bytes. This makes it harder to parse through the symbol table,
163 * since we won't know the exact location of the following symbol until we've
164 * already parsed the current one.
166 for (curpos
= symbols
; curpos
< symbols
+ header
->cbSymbol
; curpos
+= recordlen
)
168 /* Point to the next PUBSYM32 in the table.
170 sym
= (PUBSYM32
*)curpos
;
172 if (sym
->reclen
< sizeof(PUBSYM32
)) break;
174 symlen
= sym
->reclen
- sizeof(PUBSYM32
) + 1;
175 if (symlen
> sizeof(nametmp
)) {printf("\nsqueeze%d\n", symlen
);symlen
= sizeof(nametmp
) - 1;}
177 memcpy(nametmp
, curpos
+ sizeof (PUBSYM32
) + 1, symlen
);
178 nametmp
[symlen
] = '\0';
180 printf (" 0x%04x %-30.30s [0x%8lx] [0x%4x] %d %ld\n",
181 sym
->rectyp
, nametmp
, sym
->off
, sym
->reclen
, sym
->seg
, sym
->typind
);
183 /* The entire record is null-padded to the nearest 4-byte
184 * boundary, so we must do a little extra math to keep things straight.
186 recordlen
= (sym
->reclen
+ 3) & ~3;
192 static int dump_cv_sst_global_sym(OMFDirEntry
* omfde
)
194 /*** NOT YET IMPLEMENTED ***/
198 static int dump_cv_sst_static_sym(OMFDirEntry
* omfde
)
200 /*** NOT YET IMPLEMENTED ***/
204 static int dump_cv_sst_libraries(OMFDirEntry
* omfde
)
206 /*** NOT YET IMPLEMENTED ***/
210 static int dump_cv_sst_global_types(OMFDirEntry
* omfde
)
212 /*** NOT YET IMPLEMENTED ***/
216 static int dump_cv_sst_seg_map(OMFDirEntry
* omfde
)
219 OMFSegMapDesc
* segMapDesc
;
222 segMap
= PRD(Offset(cv_base
) + omfde
->lfo
, sizeof(OMFSegMap
));
223 if (!segMap
) {printf("Can't get SegMap, aborting\n");return FALSE
;}
225 printf(" cSeg: %u\n", segMap
->cSeg
);
226 printf(" cSegLog: %u\n", segMap
->cSegLog
);
228 segMapDesc
= PRD(Offset(segMap
+ 1), segMap
->cSeg
* sizeof(OMFSegDesc
));
229 if (!segMapDesc
) {printf("Can't get SegDescr array, aborting\n");return FALSE
;}
231 for (i
= 0; i
< segMap
->cSeg
; i
++)
233 printf(" SegDescr #%2d\n", i
+ 1);
234 printf(" flags: %04X\n", segMapDesc
[i
].flags
);
235 printf(" ovl: %u\n", segMapDesc
[i
].ovl
);
236 printf(" group: %u\n", segMapDesc
[i
].group
);
237 printf(" frame: %u\n", segMapDesc
[i
].frame
);
238 printf(" iSegName: %u\n", segMapDesc
[i
].iSegName
);
239 printf(" iClassName: %u\n", segMapDesc
[i
].iClassName
);
240 printf(" offset: %lu\n", segMapDesc
[i
].offset
);
241 printf(" cbSeg: %lu\n", segMapDesc
[i
].cbSeg
);
247 static int dump_cv_sst_file_index(OMFDirEntry
* omfde
)
249 /*** NOT YET IMPLEMENTED ***/
253 static int dump_cv_sst_src_module(OMFDirEntry
* omfde
)
257 unsigned long* seg_info_dw
;
258 unsigned short* seg_info_w
;
260 OMFSourceModule
* sourceModule
;
261 OMFSourceFile
* sourceFile
;
262 OMFSourceLine
* sourceLine
;
264 rawdata
= PRD(Offset(cv_base
) + omfde
->lfo
, omfde
->cb
);
265 if (!rawdata
) {printf("Can't get srcModule subsection details, aborting\n");return FALSE
;}
267 /* FIXME: check ptr validity */
268 sourceModule
= (void*)rawdata
;
269 printf (" Module table: Found %d file(s) and %d segment(s)\n",
270 sourceModule
->cFile
, sourceModule
->cSeg
);
271 for (i
= 0; i
< sourceModule
->cFile
; i
++)
273 printf (" File #%2d begins at an offset of 0x%lx in this section\n",
274 i
+ 1, sourceModule
->baseSrcFile
[i
]);
277 /* FIXME: check ptr validity */
278 seg_info_dw
= (void*)((char*)(sourceModule
+ 1) +
279 sizeof(unsigned long) * (sourceModule
->cFile
- 1));
280 seg_info_w
= (unsigned short*)(&seg_info_dw
[sourceModule
->cSeg
* 2]);
281 for (i
= 0; i
< sourceModule
->cSeg
; i
++)
283 printf (" Segment #%2d start = 0x%lx, end = 0x%lx, seg index = %u\n",
284 i
+ 1, seg_info_dw
[i
* 2], seg_info_dw
[(i
* 2) + 1],
287 ofs
= sizeof(OMFSourceModule
) + sizeof(unsigned long) * (sourceModule
->cFile
- 1) +
288 sourceModule
->cSeg
* (2 * sizeof(unsigned long) + sizeof(unsigned short));
289 ofs
= (ofs
+ 3) & ~3;
291 /* the OMFSourceFile is quite unpleasant to use:
293 * unsigned short number of segments
294 * unsigned short reservered
295 * unsigned long baseSrcLn[# segments]
296 * unsigned long offset[2 * #segments]
297 * odd indices are start offsets
298 * even indices are end offsets
299 * unsigned char string length for file name
300 * char file name (length is previous field)
302 /* FIXME: check ptr validity */
303 sourceFile
= (void*)(rawdata
+ ofs
);
304 seg_info_dw
= (void*)((char*)sourceFile
+ 2 * sizeof(unsigned short) +
305 sourceFile
->cSeg
* sizeof(unsigned long));
307 ofs
+= 2 * sizeof(unsigned short) + 3 * sourceFile
->cSeg
* sizeof(unsigned long);
309 printf(" File table: %.*s\n",
310 *(BYTE
*)((char*)sourceModule
+ ofs
), (char*)sourceModule
+ ofs
+ 1);
312 for (i
= 0; i
< sourceFile
->cSeg
; i
++)
314 printf (" Segment #%2d start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
315 i
+ 1, seg_info_dw
[i
* 2], seg_info_dw
[(i
* 2) + 1], sourceFile
->baseSrcLn
[i
]);
317 /* add file name length */
318 ofs
+= *(BYTE
*)((char*)sourceModule
+ ofs
) + 1;
319 ofs
= (ofs
+ 3) & ~3;
321 for (i
= 0; i
< sourceModule
->cSeg
; i
++)
323 sourceLine
= (void*)(rawdata
+ ofs
);
324 seg_info_dw
= (void*)((char*)sourceLine
+ 2 * sizeof(unsigned short));
325 seg_info_w
= (void*)(&seg_info_dw
[sourceLine
->cLnOff
]);
327 printf (" Line table #%2d: Found %d line numbers for segment index %d\n",
328 i
, sourceLine
->cLnOff
, sourceLine
->Seg
);
330 for (j
= 0; j
< sourceLine
->cLnOff
; j
++)
332 printf (" Pair #%2d: offset = [0x%8lx], linenumber = %d\n",
333 j
+ 1, seg_info_dw
[j
], seg_info_w
[j
]);
335 ofs
+= 2 * sizeof(unsigned short) +
336 sourceLine
->cLnOff
* (sizeof(unsigned long) + sizeof(unsigned short));
337 ofs
= (ofs
+ 3) & ~3;
343 static int dump_cv_sst_align_sym(OMFDirEntry
* omfde
)
345 /*** NOT YET IMPLEMENTED ***/
350 static void dump_codeview_all_modules(OMFDirHeader
*omfdh
)
353 OMFDirEntry
*dirEntry
;
356 if (!omfdh
|| !omfdh
->cDir
) return;
358 dirEntry
= PRD(Offset(omfdh
+ 1), omfdh
->cDir
* sizeof(OMFDirEntry
));
359 if (!dirEntry
) {printf("Can't read DirEntry array, aborting\n"); return;}
361 for (i
= 0; i
< omfdh
->cDir
; i
++)
363 switch (dirEntry
[i
].SubSection
)
365 case sstModule
: str
= "sstModule"; break;
366 case sstAlignSym
: str
= "sstAlignSym"; break;
367 case sstSrcModule
: str
= "sstSrcModule"; break;
368 case sstLibraries
: str
= "sstLibraries"; break;
369 case sstGlobalSym
: str
= "sstGlobalSym"; break;
370 case sstGlobalPub
: str
= "sstGlobalPub"; break;
371 case sstGlobalTypes
: str
= "sstGlobalTypes"; break;
372 case sstSegMap
: str
= "sstSegMap"; break;
373 case sstFileIndex
: str
= "sstFileIndex"; break;
374 case sstStaticSym
: str
= "sstStaticSym"; break;
375 default: str
= "<undefined>"; break;
377 printf("Module #%2d (%p)\n", i
+ 1, &dirEntry
[i
]);
378 printf(" SubSection: %04X (%s)\n", dirEntry
[i
].SubSection
, str
);
379 printf(" iMod: %d\n", dirEntry
[i
].iMod
);
380 printf(" lfo: %ld\n", dirEntry
[i
].lfo
);
381 printf(" cb: %lu\n", dirEntry
[i
].cb
);
383 switch (dirEntry
[i
].SubSection
)
385 case sstModule
: dump_cv_sst_module(&dirEntry
[i
]); break;
386 case sstAlignSym
: dump_cv_sst_align_sym(&dirEntry
[i
]); break;
387 case sstSrcModule
: dump_cv_sst_src_module(&dirEntry
[i
]); break;
388 case sstLibraries
: dump_cv_sst_libraries(&dirEntry
[i
]); break;
389 case sstGlobalSym
: dump_cv_sst_global_sym(&dirEntry
[i
]); break;
390 case sstGlobalPub
: dump_cv_sst_global_pub(&dirEntry
[i
]); break;
391 case sstGlobalTypes
: dump_cv_sst_global_types(&dirEntry
[i
]); break;
392 case sstSegMap
: dump_cv_sst_seg_map(&dirEntry
[i
]); break;
393 case sstFileIndex
: dump_cv_sst_file_index(&dirEntry
[i
]); break;
394 case sstStaticSym
: dump_cv_sst_static_sym(&dirEntry
[i
]); break;
395 default: printf("unsupported type %x\n", dirEntry
[i
].SubSection
); break;
403 static void dump_codeview_headers(unsigned long base
, unsigned long len
)
405 OMFDirHeader
*dirHeader
;
406 OMFSignature
*signature
;
407 OMFDirEntry
*dirEntry
;
409 int modulecount
= 0, alignsymcount
= 0, srcmodulecount
= 0, librariescount
= 0;
410 int globalsymcount
= 0, globalpubcount
= 0, globaltypescount
= 0;
411 int segmapcount
= 0, fileindexcount
= 0, staticsymcount
= 0;
413 cv_base
= PRD(base
, len
);
414 if (!cv_base
) {printf("Can't get full debug content, aborting\n");return;}
418 printf(" CodeView Data\n");
420 printf(" Signature: %.4s\n", signature
->Signature
);
421 printf(" Filepos: 0x%08lX\n", signature
->filepos
);
423 if (memcmp(signature
->Signature
, "NB10", 4) == 0)
425 struct {DWORD TimeStamp
; DWORD Dunno
; char Name
[1];}* pdb_data
;
426 pdb_data
= (void*)(signature
+ 1);
428 printf(" TimeStamp: %08lX (%s)\n",
429 pdb_data
->TimeStamp
, get_time_str(pdb_data
->TimeStamp
));
430 printf(" Dunno: %08lX\n", pdb_data
->Dunno
);
431 printf(" Filename: %s\n", pdb_data
->Name
);
435 if (memcmp(signature
->Signature
, "NB09", 4) != 0 && memcmp(signature
->Signature
, "NB11", 4) != 0)
437 printf("Unsupported signature, aborting\n");
441 dirHeader
= PRD(Offset(cv_base
) + signature
->filepos
, sizeof(OMFDirHeader
));
442 if (!dirHeader
) {printf("Can't get debug header, aborting\n"); return;}
444 printf(" Size of header: 0x%4X\n", dirHeader
->cbDirHeader
);
445 printf(" Size per entry: 0x%4X\n", dirHeader
->cbDirEntry
);
446 printf(" # of entries: 0x%8lX (%ld)\n", dirHeader
->cDir
, dirHeader
->cDir
);
447 printf(" Offset to NextDir: 0x%8lX\n", dirHeader
->lfoNextDir
);
448 printf(" Flags: 0x%8lX\n", dirHeader
->flags
);
450 if (!dirHeader
->cDir
) return;
452 dirEntry
= PRD(Offset(dirHeader
+ 1), sizeof(OMFDirEntry
) * dirHeader
->cDir
);
453 if (!dirEntry
) {printf("Can't get DirEntry array, aborting\n");return;}
455 for (i
= 0; i
< dirHeader
->cDir
; i
++)
457 switch (dirEntry
[i
].SubSection
)
459 case sstModule
: modulecount
++; break;
460 case sstAlignSym
: alignsymcount
++; break;
461 case sstSrcModule
: srcmodulecount
++; break;
462 case sstLibraries
: librariescount
++; break;
463 case sstGlobalSym
: globalsymcount
++; break;
464 case sstGlobalPub
: globalpubcount
++; break;
465 case sstGlobalTypes
: globaltypescount
++; break;
466 case sstSegMap
: segmapcount
++; break;
467 case sstFileIndex
: fileindexcount
++; break;
468 case sstStaticSym
: staticsymcount
++; break;
472 /* This one has to be > 0
474 printf ("\nFound: %d sstModule subsections\n", modulecount
);
476 if (alignsymcount
> 0) printf (" %d sstAlignSym subsections\n", alignsymcount
);
477 if (srcmodulecount
> 0) printf (" %d sstSrcModule subsections\n", srcmodulecount
);
478 if (librariescount
> 0) printf (" %d sstLibraries subsections\n", librariescount
);
479 if (globalsymcount
> 0) printf (" %d sstGlobalSym subsections\n", globalsymcount
);
480 if (globalpubcount
> 0) printf (" %d sstGlobalPub subsections\n", globalpubcount
);
481 if (globaltypescount
> 0) printf (" %d sstGlobalTypes subsections\n", globaltypescount
);
482 if (segmapcount
> 0) printf (" %d sstSegMap subsections\n", segmapcount
);
483 if (fileindexcount
> 0) printf (" %d sstFileIndex subsections\n", fileindexcount
);
484 if (staticsymcount
> 0) printf (" %d sstStaticSym subsections\n", staticsymcount
);
486 dump_codeview_all_modules(dirHeader
);
489 static const char* get_coff_name( PIMAGE_SYMBOL coff_sym
, const char* coff_strtab
)
491 static char namebuff
[9];
494 if( coff_sym
->N
.Name
.Short
)
496 memcpy(namebuff
, coff_sym
->N
.ShortName
, 8);
498 nampnt
= &namebuff
[0];
502 nampnt
= coff_strtab
+ coff_sym
->N
.Name
.Long
;
505 if( nampnt
[0] == '_' )
510 void dump_coff(unsigned long coffbase
, unsigned long len
, void* pmt
)
512 PIMAGE_COFF_SYMBOLS_HEADER coff
;
513 PIMAGE_SYMBOL coff_sym
;
514 PIMAGE_SYMBOL coff_symbols
;
515 PIMAGE_LINENUMBER coff_linetab
;
517 IMAGE_SECTION_HEADER
* sectHead
= pmt
;
522 coff
= (PIMAGE_COFF_SYMBOLS_HEADER
)PRD(coffbase
, len
);
524 coff_symbols
= (PIMAGE_SYMBOL
) ((unsigned int) coff
+ coff
->LvaToFirstSymbol
);
525 coff_linetab
= (PIMAGE_LINENUMBER
) ((unsigned int) coff
+ coff
->LvaToFirstLinenumber
);
526 coff_strtab
= (char *) (coff_symbols
+ coff
->NumberOfSymbols
);
528 printf("\nDebug table: COFF format. modbase %p, coffbase %p\n", PRD(0, 0), coff
);
529 printf(" ID | seg:offs [ abs ] | symbol/function name\n");
530 for(i
=0; i
< coff
->NumberOfSymbols
; i
++ )
532 coff_sym
= coff_symbols
+ i
;
533 naux
= coff_sym
->NumberOfAuxSymbols
;
535 if( coff_sym
->StorageClass
== IMAGE_SYM_CLASS_FILE
)
537 printf("file %s\n", (char *) (coff_sym
+ 1));
542 if( (coff_sym
->StorageClass
== IMAGE_SYM_CLASS_STATIC
)
544 && (coff_sym
->SectionNumber
== 1) )
546 DWORD base
= sectHead
[coff_sym
->SectionNumber
- 1].VirtualAddress
;
548 * This is a normal static function when naux == 0.
549 * Just register it. The current file is the correct
550 * one in this instance.
552 nampnt
= get_coff_name( coff_sym
, coff_strtab
);
554 printf("%05d | %02d:%08lx [%08lx] | %s\n", i
, coff_sym
->SectionNumber
- 1, coff_sym
->Value
- base
, coff_sym
->Value
, nampnt
);
559 if( (coff_sym
->StorageClass
== IMAGE_SYM_CLASS_EXTERNAL
)
560 && ISFCN(coff_sym
->Type
)
561 && (coff_sym
->SectionNumber
> 0) )
563 DWORD base
= sectHead
[coff_sym
->SectionNumber
- 1].VirtualAddress
;
565 nampnt
= get_coff_name( coff_sym
, coff_strtab
);
567 /* FIXME: add code to find out the file this symbol belongs to,
569 printf("%05d | %02d:%08lx [%08lx] | %s\n", i
, coff_sym
->SectionNumber
- 1, coff_sym
->Value
- base
, coff_sym
->Value
, nampnt
);
575 * For now, skip past the aux entries.
582 void dump_codeview(unsigned long base
, unsigned long len
)
584 dump_codeview_headers(base
, len
);
587 void dump_frame_pointer_omission(unsigned long base
, unsigned long len
)
589 /* FPO is used to describe nonstandard stack frames */
590 printf("FIXME: FPO (frame pointer omission) debug symbol dumping not implemented yet.\n");