wine/tools/winedump/debug.c
2006-05-23 14:11:13 +02:00

591 lines
20 KiB
C

/*
* Made after:
* CVDump - Parses through a Visual Studio .DBG file in CodeView 4 format
* and dumps the info to STDOUT in a human-readable format
*
* Copyright 2000 John R. Sheets
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "wine/port.h"
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <time.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <fcntl.h>
#include "windef.h"
#include "winbase.h"
#include "winedump.h"
#include "pe.h"
#include "cvinclude.h"
/*
* .DBG File Layout:
*
* IMAGE_SEPARATE_DEBUG_HEADER
* IMAGE_SECTION_HEADER[]
* IMAGE_DEBUG_DIRECTORY[]
* OMFSignature
* debug data (typical example)
* - IMAGE_DEBUG_TYPE_MISC
* - IMAGE_DEBUG_TYPE_FPO
* - IMAGE_DEBUG_TYPE_CODEVIEW
* OMFDirHeader
* OMFDirEntry[]
*/
/*
* Descriptions:
*
* (hdr) IMAGE_SEPARATE_DEBUG_HEADER - .DBG-specific file header; holds info that
* applies to the file as a whole, including # of COFF sections, file offsets, etc.
* (hdr) IMAGE_SECTION_HEADER - list of COFF sections copied verbatim from .EXE;
* although this directory contains file offsets, these offsets are meaningless
* in the context of the .DBG file, because only the section headers are copied
* to the .DBG file...not the binary data it points to.
* (hdr) IMAGE_DEBUG_DIRECTORY - list of different formats of debug info contained in file
* (see IMAGE_DEBUG_TYPE_* descriptions below); tells where each section starts
* (hdr) OMFSignature (CV) - Contains "NBxx" signature, plus file offset telling how far
* into the IMAGE_DEBUG_TYPE_CODEVIEW section the OMFDirHeader and OMFDirEntry's sit
* (data) IMAGE_DEBUG_TYPE_MISC - usually holds name of original .EXE file
* (data) IMAGE_DEBUG_TYPE_FPO - Frame Pointer Optimization data; used for dealing with
* optimized stack frames (optional)
* (data) IMAGE_DEBUG_TYPE_CODEVIEW - *** THE GOOD STUFF ***
* This block of data contains all the symbol tables, line number info, etc.,
* that the Visual C++ debugger needs.
* (hdr) OMFDirHeader (CV) -
* (hdr) OMFDirEntry (CV) - list of subsections within CodeView debug data section
*/
/*
* The .DBG file typically has three arrays of directory entries, which tell
* the OS or debugger where in the file to look for the actual data
*
* IMAGE_SECTION_HEADER - number of entries determined by:
* (IMAGE_SEPARATE_DEBUG_HEADER.NumberOfSections)
*
* IMAGE_DEBUG_DIRECTORY - number of entries determined by:
* (IMAGE_SEPARATE_DEBUG_HEADER.DebugDirectorySize / sizeof (IMAGE_DEBUG_DIRECTORY))
*
* OMFDirEntry - number of entries determined by:
* (OMFDirHeader.cDir)
*/
extern const IMAGE_NT_HEADERS* PE_nt_headers;
static const void* cv_base /* = 0 */;
static int dump_cv_sst_module(const OMFDirEntry* omfde)
{
const OMFModule* module;
const OMFSegDesc* segDesc;
int i;
module = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFModule));
if (!module) {printf("Can't get the OMF-Module, aborting\n"); return FALSE;}
printf(" olvNumber: %u\n", module->ovlNumber);
printf(" iLib: %u\n", module->iLib);
printf(" cSeg: %u\n", module->cSeg);
printf(" Style: %c%c\n", module->Style[0], module->Style[1]);
printf(" Name: %.*s\n",
*(BYTE*)((char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg),
(char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg + 1);
segDesc = PRD(Offset(module + 1), sizeof(OMFSegDesc) * module->cSeg);
if (!segDesc) {printf("Can't get the OMF-SegDesc, aborting\n"); return FALSE;}
for (i = 0; i < module->cSeg; i++)
{
printf (" segment #%2d: offset = [0x%8lx], size = [0x%8lx]\n",
segDesc->Seg, segDesc->Off, segDesc->cbSeg);
segDesc++;
}
return TRUE;
}
static int dump_cv_sst_global_pub(const OMFDirEntry* omfde)
{
long fileoffset;
const OMFSymHash* header;
const BYTE* symbols;
const BYTE* curpos;
const PUBSYM32* sym;
unsigned symlen;
int recordlen;
char nametmp[256];
fileoffset = Offset(cv_base) + omfde->lfo;
printf (" GlobalPub section starts at file offset 0x%lx\n", fileoffset);
printf (" Symbol table starts at 0x%lx\n", fileoffset + sizeof (OMFSymHash));
printf ("\n ----- Begin Symbol Table -----\n");
printf (" (type) (symbol name) (offset) (len) (seg) (ind)\n");
header = PRD(fileoffset, sizeof(OMFSymHash));
if (!header) {printf("Can't get OMF-SymHash, aborting\n");return FALSE;}
symbols = PRD(fileoffset + sizeof(OMFSymHash), header->cbSymbol);
if (!symbols) {printf("Can't OMF-SymHash details, aborting\n"); return FALSE;}
/* We don't know how many symbols are in this block of memory...only what
* the total size of the block is. Because the symbol's name is tacked
* on to the end of the PUBSYM32 struct, each symbol may take up a different
* # of bytes. This makes it harder to parse through the symbol table,
* since we won't know the exact location of the following symbol until we've
* already parsed the current one.
*/
for (curpos = symbols; curpos < symbols + header->cbSymbol; curpos += recordlen)
{
/* Point to the next PUBSYM32 in the table.
*/
sym = (PUBSYM32*)curpos;
if (sym->reclen < sizeof(PUBSYM32)) break;
symlen = sym->reclen - sizeof(PUBSYM32) + 1;
if (symlen > sizeof(nametmp)) {printf("\nsqueeze%d\n", symlen);symlen = sizeof(nametmp) - 1;}
memcpy(nametmp, curpos + sizeof (PUBSYM32) + 1, symlen);
nametmp[symlen] = '\0';
printf (" 0x%04x %-30.30s [0x%8lx] [0x%4x] %d %ld\n",
sym->rectyp, nametmp, sym->off, sym->reclen, sym->seg, sym->typind);
/* The entire record is null-padded to the nearest 4-byte
* boundary, so we must do a little extra math to keep things straight.
*/
recordlen = (sym->reclen + 3) & ~3;
}
return TRUE;
}
static int dump_cv_sst_global_sym(const OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_static_sym(const OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_libraries(const OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_global_types(const OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_seg_map(const OMFDirEntry* omfde)
{
const OMFSegMap* segMap;
const OMFSegMapDesc* segMapDesc;
int i;
segMap = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFSegMap));
if (!segMap) {printf("Can't get SegMap, aborting\n");return FALSE;}
printf(" cSeg: %u\n", segMap->cSeg);
printf(" cSegLog: %u\n", segMap->cSegLog);
segMapDesc = PRD(Offset(segMap + 1), segMap->cSeg * sizeof(OMFSegDesc));
if (!segMapDesc) {printf("Can't get SegDescr array, aborting\n");return FALSE;}
for (i = 0; i < segMap->cSeg; i++)
{
printf(" SegDescr #%2d\n", i + 1);
printf(" flags: %04X\n", segMapDesc[i].flags);
printf(" ovl: %u\n", segMapDesc[i].ovl);
printf(" group: %u\n", segMapDesc[i].group);
printf(" frame: %u\n", segMapDesc[i].frame);
printf(" iSegName: %u\n", segMapDesc[i].iSegName);
printf(" iClassName: %u\n", segMapDesc[i].iClassName);
printf(" offset: %lu\n", segMapDesc[i].offset);
printf(" cbSeg: %lu\n", segMapDesc[i].cbSeg);
}
return TRUE;
}
static int dump_cv_sst_file_index(const OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static int dump_cv_sst_src_module(const OMFDirEntry* omfde)
{
int i, j;
const BYTE* rawdata;
const unsigned long* seg_info_dw;
const unsigned short* seg_info_w;
unsigned ofs;
const OMFSourceModule* sourceModule;
const OMFSourceFile* sourceFile;
const OMFSourceLine* sourceLine;
rawdata = PRD(Offset(cv_base) + omfde->lfo, omfde->cb);
if (!rawdata) {printf("Can't get srcModule subsection details, aborting\n");return FALSE;}
/* FIXME: check ptr validity */
sourceModule = (void*)rawdata;
printf (" Module table: Found %d file(s) and %d segment(s)\n",
sourceModule->cFile, sourceModule->cSeg);
for (i = 0; i < sourceModule->cFile; i++)
{
printf (" File #%2d begins at an offset of 0x%lx in this section\n",
i + 1, sourceModule->baseSrcFile[i]);
}
/* FIXME: check ptr validity */
seg_info_dw = (void*)((char*)(sourceModule + 1) +
sizeof(unsigned long) * (sourceModule->cFile - 1));
seg_info_w = (unsigned short*)(&seg_info_dw[sourceModule->cSeg * 2]);
for (i = 0; i < sourceModule->cSeg; i++)
{
printf (" Segment #%2d start = 0x%lx, end = 0x%lx, seg index = %u\n",
i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1],
seg_info_w[i]);
}
ofs = sizeof(OMFSourceModule) + sizeof(unsigned long) * (sourceModule->cFile - 1) +
sourceModule->cSeg * (2 * sizeof(unsigned long) + sizeof(unsigned short));
ofs = (ofs + 3) & ~3;
/* the OMFSourceFile is quite unpleasant to use:
* we have first:
* unsigned short number of segments
* unsigned short reservered
* unsigned long baseSrcLn[# segments]
* unsigned long offset[2 * #segments]
* odd indices are start offsets
* even indices are end offsets
* unsigned char string length for file name
* char file name (length is previous field)
*/
/* FIXME: check ptr validity */
sourceFile = (void*)(rawdata + ofs);
seg_info_dw = (void*)((char*)sourceFile + 2 * sizeof(unsigned short) +
sourceFile->cSeg * sizeof(unsigned long));
ofs += 2 * sizeof(unsigned short) + 3 * sourceFile->cSeg * sizeof(unsigned long);
printf(" File table: %.*s\n",
*(BYTE*)((char*)sourceModule + ofs), (char*)sourceModule + ofs + 1);
for (i = 0; i < sourceFile->cSeg; i++)
{
printf (" Segment #%2d start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1], sourceFile->baseSrcLn[i]);
}
/* add file name length */
ofs += *(BYTE*)((char*)sourceModule + ofs) + 1;
ofs = (ofs + 3) & ~3;
for (i = 0; i < sourceModule->cSeg; i++)
{
sourceLine = (void*)(rawdata + ofs);
seg_info_dw = (void*)((char*)sourceLine + 2 * sizeof(unsigned short));
seg_info_w = (void*)(&seg_info_dw[sourceLine->cLnOff]);
printf (" Line table #%2d: Found %d line numbers for segment index %d\n",
i, sourceLine->cLnOff, sourceLine->Seg);
for (j = 0; j < sourceLine->cLnOff; j++)
{
printf (" Pair #%2d: offset = [0x%8lx], linenumber = %d\n",
j + 1, seg_info_dw[j], seg_info_w[j]);
}
ofs += 2 * sizeof(unsigned short) +
sourceLine->cLnOff * (sizeof(unsigned long) + sizeof(unsigned short));
ofs = (ofs + 3) & ~3;
}
return TRUE;
}
static int dump_cv_sst_align_sym(const OMFDirEntry* omfde)
{
/*** NOT YET IMPLEMENTED ***/
return TRUE;
}
static void dump_codeview_all_modules(const OMFDirHeader *omfdh)
{
unsigned i;
const OMFDirEntry* dirEntry;
const char* str;
if (!omfdh || !omfdh->cDir) return;
dirEntry = PRD(Offset(omfdh + 1), omfdh->cDir * sizeof(OMFDirEntry));
if (!dirEntry) {printf("Can't read DirEntry array, aborting\n"); return;}
for (i = 0; i < omfdh->cDir; i++)
{
switch (dirEntry[i].SubSection)
{
case sstModule: str = "sstModule"; break;
case sstAlignSym: str = "sstAlignSym"; break;
case sstSrcModule: str = "sstSrcModule"; break;
case sstLibraries: str = "sstLibraries"; break;
case sstGlobalSym: str = "sstGlobalSym"; break;
case sstGlobalPub: str = "sstGlobalPub"; break;
case sstGlobalTypes: str = "sstGlobalTypes"; break;
case sstSegMap: str = "sstSegMap"; break;
case sstFileIndex: str = "sstFileIndex"; break;
case sstStaticSym: str = "sstStaticSym"; break;
default: str = "<undefined>"; break;
}
printf("Module #%2d (%p)\n", i + 1, &dirEntry[i]);
printf(" SubSection: %04X (%s)\n", dirEntry[i].SubSection, str);
printf(" iMod: %d\n", dirEntry[i].iMod);
printf(" lfo: %ld\n", dirEntry[i].lfo);
printf(" cb: %lu\n", dirEntry[i].cb);
switch (dirEntry[i].SubSection)
{
case sstModule: dump_cv_sst_module(&dirEntry[i]); break;
case sstAlignSym: dump_cv_sst_align_sym(&dirEntry[i]); break;
case sstSrcModule: dump_cv_sst_src_module(&dirEntry[i]); break;
case sstLibraries: dump_cv_sst_libraries(&dirEntry[i]); break;
case sstGlobalSym: dump_cv_sst_global_sym(&dirEntry[i]); break;
case sstGlobalPub: dump_cv_sst_global_pub(&dirEntry[i]); break;
case sstGlobalTypes: dump_cv_sst_global_types(&dirEntry[i]); break;
case sstSegMap: dump_cv_sst_seg_map(&dirEntry[i]); break;
case sstFileIndex: dump_cv_sst_file_index(&dirEntry[i]); break;
case sstStaticSym: dump_cv_sst_static_sym(&dirEntry[i]); break;
default: printf("unsupported type %x\n", dirEntry[i].SubSection); break;
}
printf("\n");
}
return;
}
static void dump_codeview_headers(unsigned long base, unsigned long len)
{
const OMFDirHeader* dirHeader;
const OMFSignature* signature;
const OMFDirEntry* dirEntry;
unsigned i;
int modulecount = 0, alignsymcount = 0, srcmodulecount = 0, librariescount = 0;
int globalsymcount = 0, globalpubcount = 0, globaltypescount = 0;
int segmapcount = 0, fileindexcount = 0, staticsymcount = 0;
cv_base = PRD(base, len);
if (!cv_base) {printf("Can't get full debug content, aborting\n");return;}
signature = cv_base;
printf(" CodeView Data\n");
printf(" Signature: %.4s\n", signature->Signature);
printf(" Filepos: 0x%08lX\n", signature->filepos);
if (memcmp(signature->Signature, "NB10", 4) == 0)
{
struct {DWORD TimeStamp; DWORD Dunno; char Name[1];}* pdb_data;
pdb_data = (void*)(signature + 1);
printf(" TimeStamp: %08lX (%s)\n",
pdb_data->TimeStamp, get_time_str(pdb_data->TimeStamp));
printf(" Dunno: %08lX\n", pdb_data->Dunno);
printf(" Filename: %s\n", pdb_data->Name);
return;
}
if (memcmp(signature->Signature, "NB09", 4) != 0 && memcmp(signature->Signature, "NB11", 4) != 0)
{
printf("Unsupported signature, aborting\n");
return;
}
dirHeader = PRD(Offset(cv_base) + signature->filepos, sizeof(OMFDirHeader));
if (!dirHeader) {printf("Can't get debug header, aborting\n"); return;}
printf(" Size of header: 0x%4X\n", dirHeader->cbDirHeader);
printf(" Size per entry: 0x%4X\n", dirHeader->cbDirEntry);
printf(" # of entries: 0x%8lX (%ld)\n", dirHeader->cDir, dirHeader->cDir);
printf(" Offset to NextDir: 0x%8lX\n", dirHeader->lfoNextDir);
printf(" Flags: 0x%8lX\n", dirHeader->flags);
if (!dirHeader->cDir) return;
dirEntry = PRD(Offset(dirHeader + 1), sizeof(OMFDirEntry) * dirHeader->cDir);
if (!dirEntry) {printf("Can't get DirEntry array, aborting\n");return;}
for (i = 0; i < dirHeader->cDir; i++)
{
switch (dirEntry[i].SubSection)
{
case sstModule: modulecount++; break;
case sstAlignSym: alignsymcount++; break;
case sstSrcModule: srcmodulecount++; break;
case sstLibraries: librariescount++; break;
case sstGlobalSym: globalsymcount++; break;
case sstGlobalPub: globalpubcount++; break;
case sstGlobalTypes: globaltypescount++; break;
case sstSegMap: segmapcount++; break;
case sstFileIndex: fileindexcount++; break;
case sstStaticSym: staticsymcount++; break;
}
}
/* This one has to be > 0
*/
printf ("\nFound: %d sstModule subsections\n", modulecount);
if (alignsymcount > 0) printf (" %d sstAlignSym subsections\n", alignsymcount);
if (srcmodulecount > 0) printf (" %d sstSrcModule subsections\n", srcmodulecount);
if (librariescount > 0) printf (" %d sstLibraries subsections\n", librariescount);
if (globalsymcount > 0) printf (" %d sstGlobalSym subsections\n", globalsymcount);
if (globalpubcount > 0) printf (" %d sstGlobalPub subsections\n", globalpubcount);
if (globaltypescount > 0) printf (" %d sstGlobalTypes subsections\n", globaltypescount);
if (segmapcount > 0) printf (" %d sstSegMap subsections\n", segmapcount);
if (fileindexcount > 0) printf (" %d sstFileIndex subsections\n", fileindexcount);
if (staticsymcount > 0) printf (" %d sstStaticSym subsections\n", staticsymcount);
dump_codeview_all_modules(dirHeader);
}
static const char* get_coff_name( PIMAGE_SYMBOL coff_sym, const char* coff_strtab )
{
static char namebuff[9];
const char* nampnt;
if( coff_sym->N.Name.Short )
{
memcpy(namebuff, coff_sym->N.ShortName, 8);
namebuff[8] = '\0';
nampnt = &namebuff[0];
}
else
{
nampnt = coff_strtab + coff_sym->N.Name.Long;
}
if( nampnt[0] == '_' )
nampnt++;
return nampnt;
}
void dump_coff(unsigned long coffbase, unsigned long len, const void* pmt)
{
PIMAGE_COFF_SYMBOLS_HEADER coff;
PIMAGE_SYMBOL coff_sym;
PIMAGE_SYMBOL coff_symbols;
PIMAGE_LINENUMBER coff_linetab;
char * coff_strtab;
const IMAGE_SECTION_HEADER * sectHead = pmt;
unsigned int i;
const char * nampnt;
int naux;
coff = (PIMAGE_COFF_SYMBOLS_HEADER)PRD(coffbase, len);
coff_symbols = (PIMAGE_SYMBOL) ((char *)coff + coff->LvaToFirstSymbol);
coff_linetab = (PIMAGE_LINENUMBER) ((char *)coff + coff->LvaToFirstLinenumber);
coff_strtab = (char *) (coff_symbols + coff->NumberOfSymbols);
printf("\nDebug table: COFF format. modbase %p, coffbase %p\n", PRD(0, 0), coff);
printf(" ID | seg:offs [ abs ] | symbol/function name\n");
for(i=0; i < coff->NumberOfSymbols; i++ )
{
coff_sym = coff_symbols + i;
naux = coff_sym->NumberOfAuxSymbols;
if( coff_sym->StorageClass == IMAGE_SYM_CLASS_FILE )
{
printf("file %s\n", (char *) (coff_sym + 1));
i += naux;
continue;
}
if( (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC)
&& (naux == 0)
&& (coff_sym->SectionNumber == 1) )
{
DWORD base = sectHead[coff_sym->SectionNumber - 1].VirtualAddress;
/*
* This is a normal static function when naux == 0.
* Just register it. The current file is the correct
* one in this instance.
*/
nampnt = get_coff_name( coff_sym, coff_strtab );
printf("%05d | %02d:%08lx [%08lx] | %s\n", i, coff_sym->SectionNumber - 1, coff_sym->Value - base, coff_sym->Value, nampnt);
i += naux;
continue;
}
if( (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL)
&& ISFCN(coff_sym->Type)
&& (coff_sym->SectionNumber > 0) )
{
DWORD base = sectHead[coff_sym->SectionNumber - 1].VirtualAddress;
nampnt = get_coff_name( coff_sym, coff_strtab );
/* FIXME: add code to find out the file this symbol belongs to,
* see winedbg */
printf("%05d | %02d:%08lx [%08lx] | %s\n", i, coff_sym->SectionNumber - 1, coff_sym->Value - base, coff_sym->Value, nampnt);
i += naux;
continue;
}
/*
* For now, skip past the aux entries.
*/
i += naux;
}
}
void dump_codeview(unsigned long base, unsigned long len)
{
dump_codeview_headers(base, len);
}
void dump_frame_pointer_omission(unsigned long base, unsigned long len)
{
/* FPO is used to describe nonstandard stack frames */
printf("FIXME: FPO (frame pointer omission) debug symbol dumping not implemented yet.\n");
}