Added cvdump tool to dump CodeView symbol information.
This commit is contained in:
parent
cb98a6ed7a
commit
e61d33bfd1
|
@ -92,7 +92,7 @@ INSTALLSUBDIRS = $(DLLDIR) $(DOCSUBDIRS) $(INCSUBDIRS)
|
||||||
LINTSUBDIRS = $(LIBSUBDIRS) $(DLLDIR) $(EMUSUBDIRS) $(DOCSUBDIRS)
|
LINTSUBDIRS = $(LIBSUBDIRS) $(DLLDIR) $(EMUSUBDIRS) $(DOCSUBDIRS)
|
||||||
|
|
||||||
# Extra sub-directories to clean
|
# Extra sub-directories to clean
|
||||||
CLEANSUBDIRS = dlls include include/bitmaps include/wine
|
CLEANSUBDIRS = dlls include include/bitmaps include/wine tools/cvdump
|
||||||
|
|
||||||
LIBOBJS = \
|
LIBOBJS = \
|
||||||
controls/controls.o \
|
controls/controls.o \
|
||||||
|
|
|
@ -6314,6 +6314,7 @@ resources/Makefile
|
||||||
scheduler/Makefile
|
scheduler/Makefile
|
||||||
server/Makefile
|
server/Makefile
|
||||||
tools/Makefile
|
tools/Makefile
|
||||||
|
tools/cvdump/Makefile
|
||||||
tools/wrc/Makefile
|
tools/wrc/Makefile
|
||||||
tsx11/Makefile
|
tsx11/Makefile
|
||||||
win32/Makefile
|
win32/Makefile
|
||||||
|
@ -6537,6 +6538,7 @@ resources/Makefile
|
||||||
scheduler/Makefile
|
scheduler/Makefile
|
||||||
server/Makefile
|
server/Makefile
|
||||||
tools/Makefile
|
tools/Makefile
|
||||||
|
tools/cvdump/Makefile
|
||||||
tools/wrc/Makefile
|
tools/wrc/Makefile
|
||||||
tsx11/Makefile
|
tsx11/Makefile
|
||||||
win32/Makefile
|
win32/Makefile
|
||||||
|
|
|
@ -1056,6 +1056,7 @@ resources/Makefile
|
||||||
scheduler/Makefile
|
scheduler/Makefile
|
||||||
server/Makefile
|
server/Makefile
|
||||||
tools/Makefile
|
tools/Makefile
|
||||||
|
tools/cvdump/Makefile
|
||||||
tools/wrc/Makefile
|
tools/wrc/Makefile
|
||||||
tsx11/Makefile
|
tsx11/Makefile
|
||||||
win32/Makefile
|
win32/Makefile
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Makefile
|
||||||
|
cvdump
|
|
@ -0,0 +1,18 @@
|
||||||
|
TOPSRCDIR = @top_srcdir@
|
||||||
|
TOPOBJDIR = ../..
|
||||||
|
SRCDIR = @srcdir@
|
||||||
|
VPATH = @srcdir@
|
||||||
|
|
||||||
|
PROGRAMS = cvdump@PROGEXT@
|
||||||
|
MODULE = none
|
||||||
|
|
||||||
|
C_SRCS = cvcrunch.c cvdump.c cvload.c
|
||||||
|
|
||||||
|
all: $(PROGRAMS)
|
||||||
|
|
||||||
|
@MAKE_RULES@
|
||||||
|
|
||||||
|
cvdump@PROGEXT@: $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) -o cvdump@PROGEXT@ $(OBJS)
|
||||||
|
|
||||||
|
### Dependencies:
|
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
* Functions to process in-memory arrays of CodeView data sections
|
||||||
|
* (currently only contains sstSrcModule).
|
||||||
|
*
|
||||||
|
* Copyright 2000 John R. Sheets
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* FIXME - Change to cvprint.c */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "peexe.h"
|
||||||
|
#include "cvinclude.h"
|
||||||
|
|
||||||
|
/************************ sstSrcModule ************************/
|
||||||
|
|
||||||
|
/* Print out stuff in OMFSourceModule block. Rather than using the supplied
|
||||||
|
* OMFSourceModule struct, we'll extract each piece of data separately from
|
||||||
|
* the block of memory (rawdata). This struct (and the others used in
|
||||||
|
* sstSrcModule sections) is pretty useless. We can't use sizeof() on it
|
||||||
|
* because it contains the first element of the file offset array (i.e. baseSrcFile),
|
||||||
|
* which we need to parse separately anyway. See below for problems with the
|
||||||
|
* other structs.
|
||||||
|
*
|
||||||
|
* The contents of this section look like this (the first two fields are
|
||||||
|
* already extracted and passed in as parameters):
|
||||||
|
*
|
||||||
|
* unsigned short cFile
|
||||||
|
* unsigned short cSeg
|
||||||
|
* unsigned long baseSrcFile[cFile]
|
||||||
|
* unsigned long segarray[cSeg * 2]
|
||||||
|
* unsigned short segindexarray[cSeg]
|
||||||
|
*/
|
||||||
|
int PrintSrcModuleInfo (BYTE* rawdata, short *filecount, short *segcount)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int datalen;
|
||||||
|
|
||||||
|
unsigned short cFile;
|
||||||
|
unsigned short cSeg;
|
||||||
|
unsigned long *baseSrcFile;
|
||||||
|
unsigned long *segarray;
|
||||||
|
unsigned short *segindexarray;
|
||||||
|
|
||||||
|
/* Get all our pointers straightened out
|
||||||
|
*/
|
||||||
|
cFile = *(short*)rawdata;
|
||||||
|
cSeg = *(short*)(rawdata + 2);
|
||||||
|
baseSrcFile = (long*)(rawdata + 4);
|
||||||
|
segarray = &baseSrcFile[cFile];
|
||||||
|
segindexarray = (short*)(&segarray[cSeg * 2]);
|
||||||
|
|
||||||
|
/* Pass # of segments and files back to calling function
|
||||||
|
*/
|
||||||
|
*filecount = cFile;
|
||||||
|
*segcount = cSeg;
|
||||||
|
|
||||||
|
printf ("\n Module table: Found %d file(s) and %d segment(s)\n", cFile, cSeg);
|
||||||
|
for (i = 0; i < cFile; i++)
|
||||||
|
{
|
||||||
|
printf (" File #%d begins at an offset of 0x%lx in this section\n",
|
||||||
|
i + 1, baseSrcFile[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < cSeg; i++)
|
||||||
|
{
|
||||||
|
printf (" Segment #%d start = 0x%lx, end = 0x%lx, seg index = %d\n",
|
||||||
|
i + 1, segarray[i * 2], segarray[(i * 2) + 1], segindexarray[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the total length of the data (in bytes) that we used, so
|
||||||
|
* we'll know how far to jump ahead for the next part of the sstSrcModule.
|
||||||
|
*/
|
||||||
|
datalen = ((BYTE*)(&segindexarray[cSeg]) - rawdata);
|
||||||
|
/* printf ("datalen before padding = %d\n", datalen); */
|
||||||
|
if (datalen % 4)
|
||||||
|
datalen += 4 - (datalen % 4);
|
||||||
|
/* printf ("datalen after padding = %d\n", datalen); */
|
||||||
|
|
||||||
|
return datalen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print out the contents of a OMFSourceFile block. Unfortunately, the official
|
||||||
|
* version of this struct (probably quite outdated) claims that the 'cFName' field
|
||||||
|
* is a short. Based on experimentation with MSVC 5.0 .DBG files, this field is
|
||||||
|
* quite clearly only a single byte. Yet another reason to do it all by hand
|
||||||
|
* and avoid the "official" structs.
|
||||||
|
*
|
||||||
|
* The contents of this section look like this (the first field is
|
||||||
|
* pre-extracted, and 'pad' is ignored):
|
||||||
|
*
|
||||||
|
* unsigned short cSeg
|
||||||
|
* unsigned short pad
|
||||||
|
* unsigned long baseSrcLn[cSeg]
|
||||||
|
* unsigned long segarray[cSeg * 2]
|
||||||
|
* char cFName
|
||||||
|
* char Name[cFName]
|
||||||
|
*/
|
||||||
|
int PrintSrcModuleFileInfo (BYTE* rawdata)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int datalen;
|
||||||
|
|
||||||
|
unsigned short cSeg;
|
||||||
|
unsigned long *baseSrcLn;
|
||||||
|
unsigned long *segarray;
|
||||||
|
unsigned char cFName;
|
||||||
|
char Name[256];
|
||||||
|
|
||||||
|
/* Get all our pointers straightened out
|
||||||
|
*/
|
||||||
|
cSeg = *(short*)(rawdata);
|
||||||
|
/* Skip the 'pad' field */
|
||||||
|
baseSrcLn = (long*)(rawdata + 4);
|
||||||
|
segarray = &baseSrcLn[cSeg];
|
||||||
|
cFName = *((char*)&segarray[cSeg * 2]);
|
||||||
|
snprintf (Name, cFName + 1, "%s", (char*)&segarray[cSeg * 2] + 1);
|
||||||
|
|
||||||
|
/* printf ("cSeg = %d\n", cSeg); */
|
||||||
|
printf ("\n File table: '%s'\n", Name);
|
||||||
|
|
||||||
|
for (i = 0; i < cSeg; i++)
|
||||||
|
{
|
||||||
|
printf (" Segment #%d start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
|
||||||
|
i + 1, segarray[i * 2], segarray[(i * 2) + 1], baseSrcLn[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the total length of the data (in bytes) that we used, so
|
||||||
|
* we'll know how far to jump ahead for the next part of the sstSrcModule.
|
||||||
|
*/
|
||||||
|
datalen = ((BYTE*)(&segarray[cSeg * 2]) + cFName + 1 - rawdata);
|
||||||
|
/* printf ("datalen before padding = %d\n", datalen); */
|
||||||
|
if (datalen % 4)
|
||||||
|
datalen += 4 - (datalen % 4);
|
||||||
|
/* printf ("datalen after padding = %d\n", datalen); */
|
||||||
|
|
||||||
|
return datalen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print out the contents of a OMFSourceLine block. The contents of this section
|
||||||
|
* look like this:
|
||||||
|
*
|
||||||
|
* unsigned short Seg
|
||||||
|
* unsigned short cPair
|
||||||
|
* unsigned long offset[cPair]
|
||||||
|
* unsigned long linenumber[cPair]
|
||||||
|
*/
|
||||||
|
int PrintSrcModuleLineInfo (BYTE* rawdata, int tablecount)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int datalen;
|
||||||
|
|
||||||
|
unsigned short Seg;
|
||||||
|
unsigned short cPair;
|
||||||
|
unsigned long *offset;
|
||||||
|
unsigned short *linenumber;
|
||||||
|
|
||||||
|
Seg = *(short*)rawdata;
|
||||||
|
cPair = *(short*)(rawdata + 2);
|
||||||
|
offset = (long*)(rawdata + 4);
|
||||||
|
linenumber = (short*)&offset[cPair];
|
||||||
|
|
||||||
|
printf ("\n Line table #%d: Found %d line numbers for segment index %d\n",
|
||||||
|
tablecount, cPair, Seg);
|
||||||
|
|
||||||
|
for (i = 0; i < cPair; i++)
|
||||||
|
{
|
||||||
|
printf (" Pair #%2d: offset = [0x%8lx], linenumber = %d\n",
|
||||||
|
i + 1, offset[i], linenumber[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the total length of the data (in bytes) that we used, so
|
||||||
|
* we'll know how far to jump ahead for the next part of the sstSrcModule.
|
||||||
|
*/
|
||||||
|
datalen = ((BYTE*)(&linenumber[cPair]) - rawdata);
|
||||||
|
/* printf ("datalen before padding = %d\n", datalen); */
|
||||||
|
if (datalen % 4)
|
||||||
|
datalen += 4 - (datalen % 4);
|
||||||
|
/* printf ("datalen after padding = %d\n", datalen); */
|
||||||
|
|
||||||
|
return datalen;
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Includes for cvdump tool.
|
||||||
|
*
|
||||||
|
* Copyright 2000 John R. Sheets
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* #define VERBOSE */
|
||||||
|
|
||||||
|
#include "peexe.h"
|
||||||
|
#include "neexe.h"
|
||||||
|
#include "cvinclude.h"
|
||||||
|
|
||||||
|
typedef enum { CV_NONE, CV_DOS, CV_NT, CV_DBG } CVHeaderType;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function Prototypes
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* From cvload.c */
|
||||||
|
CVHeaderType GetHeaderType (FILE *debugfile);
|
||||||
|
int ReadDOSFileHeader (FILE *debugfile, IMAGE_DOS_HEADER *doshdr);
|
||||||
|
int ReadPEFileHeader (FILE *debugfile, IMAGE_NT_HEADERS *nthdr);
|
||||||
|
int ReadDBGFileHeader (FILE *debugfile, IMAGE_SEPARATE_DEBUG_HEADER *dbghdr);
|
||||||
|
|
||||||
|
int ReadSectionHeaders (FILE *debugfile, int numsects, IMAGE_SECTION_HEADER **secthdrs);
|
||||||
|
int ReadDebugDir (FILE *debugfile, int numdirs, IMAGE_DEBUG_DIRECTORY **debugdirs);
|
||||||
|
int ReadCodeViewHeader (FILE *debugfile, OMFSignature *sig, OMFDirHeader *dirhdr);
|
||||||
|
int ReadCodeViewDirectory (FILE *debugfile, int entrynum, OMFDirEntry **entries);
|
||||||
|
int ReadModuleData (FILE *debugfile, int entrynum, OMFDirEntry *entries,
|
||||||
|
int *module_count, OMFModuleFull **modules);
|
||||||
|
int ReadChunk (FILE *debugfile, void *dest, int length, int fileoffset);
|
||||||
|
|
||||||
|
/* From cvprint.c */
|
||||||
|
int PrintSrcModuleInfo (BYTE* rawdata, short *filecount, short *segcount);
|
||||||
|
int PrintSrcModuleFileInfo (BYTE* rawdata);
|
||||||
|
int PrintSrcModuleLineInfo (BYTE* rawdata, int tablecount);
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* CodeView 4 Debug format - declarations
|
||||||
|
*
|
||||||
|
* (based on cvinfo.h and cvexefmt.h from the Win32 SDK)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define sstModule 0x120
|
||||||
|
#define sstAlignSym 0x125
|
||||||
|
#define sstSrcModule 0x127
|
||||||
|
#define sstLibraries 0x128
|
||||||
|
#define sstGlobalSym 0x129
|
||||||
|
#define sstGlobalPub 0x12a
|
||||||
|
#define sstGlobalTypes 0x12b
|
||||||
|
#define sstSegMap 0x12d
|
||||||
|
#define sstFileIndex 0x133
|
||||||
|
#define sstStaticSym 0x134
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* Old, crusty value */
|
||||||
|
#define S_PUB32 0x0203
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define S_PUB32 0x1009
|
||||||
|
|
||||||
|
#include "pshpack1.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CodeView headers
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct OMFSignature
|
||||||
|
{
|
||||||
|
char Signature[4];
|
||||||
|
long filepos;
|
||||||
|
} OMFSignature;
|
||||||
|
|
||||||
|
typedef struct OMFDirHeader
|
||||||
|
{
|
||||||
|
unsigned short cbDirHeader;
|
||||||
|
unsigned short cbDirEntry;
|
||||||
|
unsigned long cDir;
|
||||||
|
long lfoNextDir;
|
||||||
|
unsigned long flags;
|
||||||
|
} OMFDirHeader;
|
||||||
|
|
||||||
|
typedef struct OMFDirEntry
|
||||||
|
{
|
||||||
|
unsigned short SubSection;
|
||||||
|
unsigned short iMod;
|
||||||
|
long lfo;
|
||||||
|
unsigned long cb;
|
||||||
|
} OMFDirEntry;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sstModule subsection
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct OMFSegDesc
|
||||||
|
{
|
||||||
|
unsigned short Seg;
|
||||||
|
unsigned short pad;
|
||||||
|
unsigned long Off;
|
||||||
|
unsigned long cbSeg;
|
||||||
|
} OMFSegDesc;
|
||||||
|
|
||||||
|
/* Must chop off the OMFSegDesc* field, because we need to write this
|
||||||
|
* struct out to a file. If we write the whole struct out, we'll end up
|
||||||
|
* with (*OMFSegDesc), not (OMFSegDesc). See OMFModuleFull.
|
||||||
|
*/
|
||||||
|
typedef struct OMFModule
|
||||||
|
{
|
||||||
|
unsigned short ovlNumber;
|
||||||
|
unsigned short iLib;
|
||||||
|
unsigned short cSeg;
|
||||||
|
char Style[2];
|
||||||
|
} OMFModule;
|
||||||
|
|
||||||
|
/* Full version, with memory pointers, too. Use OMFModule for writing out to
|
||||||
|
* a file, and OMFModuleFull for reading. If offsetof() were available, we
|
||||||
|
* could use that instead.
|
||||||
|
*/
|
||||||
|
typedef struct OMFModuleFull
|
||||||
|
{
|
||||||
|
unsigned short ovlNumber;
|
||||||
|
unsigned short iLib;
|
||||||
|
unsigned short cSeg;
|
||||||
|
char Style[2];
|
||||||
|
OMFSegDesc *SegInfo;
|
||||||
|
char *Name;
|
||||||
|
} OMFModuleFull;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sstGlobalPub section
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Header for symbol table.
|
||||||
|
*/
|
||||||
|
typedef struct OMFSymHash
|
||||||
|
{
|
||||||
|
unsigned short symhash;
|
||||||
|
unsigned short addrhash;
|
||||||
|
unsigned long cbSymbol;
|
||||||
|
unsigned long cbHSym;
|
||||||
|
unsigned long cbHAddr;
|
||||||
|
} OMFSymHash;
|
||||||
|
|
||||||
|
/* Symbol table entry.
|
||||||
|
*/
|
||||||
|
typedef struct DATASYM32
|
||||||
|
{
|
||||||
|
unsigned short reclen;
|
||||||
|
unsigned short rectyp;
|
||||||
|
unsigned long typind;
|
||||||
|
unsigned long off;
|
||||||
|
unsigned short seg;
|
||||||
|
} DATASYM32;
|
||||||
|
typedef DATASYM32 PUBSYM32;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sstSegMap section
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct OMFSegMapDesc
|
||||||
|
{
|
||||||
|
unsigned short flags;
|
||||||
|
unsigned short ovl;
|
||||||
|
unsigned short group;
|
||||||
|
unsigned short frame;
|
||||||
|
unsigned short iSegName;
|
||||||
|
unsigned short iClassName;
|
||||||
|
unsigned long offset;
|
||||||
|
unsigned long cbSeg;
|
||||||
|
} OMFSegMapDesc;
|
||||||
|
|
||||||
|
typedef struct OMFSegMap
|
||||||
|
{
|
||||||
|
unsigned short cSeg;
|
||||||
|
unsigned short cSegLog;
|
||||||
|
OMFSegMapDesc rgDesc[0];
|
||||||
|
} OMFSegMap;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sstSrcModule section
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct OMFSourceLine
|
||||||
|
{
|
||||||
|
unsigned short Seg;
|
||||||
|
unsigned short cLnOff;
|
||||||
|
unsigned long offset[1];
|
||||||
|
unsigned short lineNbr[1];
|
||||||
|
} OMFSourceLine;
|
||||||
|
|
||||||
|
typedef struct OMFSourceFile
|
||||||
|
{
|
||||||
|
unsigned short cSeg;
|
||||||
|
unsigned short reserved;
|
||||||
|
unsigned long baseSrcLn[1];
|
||||||
|
unsigned short cFName;
|
||||||
|
char Name;
|
||||||
|
} OMFSourceFile;
|
||||||
|
|
||||||
|
typedef struct OMFSourceModule
|
||||||
|
{
|
||||||
|
unsigned short cFile;
|
||||||
|
unsigned short cSeg;
|
||||||
|
unsigned long baseSrcFile[1];
|
||||||
|
} OMFSourceModule;
|
||||||
|
|
|
@ -0,0 +1,430 @@
|
||||||
|
/*
|
||||||
|
* Functions to read parts of a .DBG file into their respective struct's
|
||||||
|
*
|
||||||
|
* Copyright 2000 John R. Sheets
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* .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)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "cvdump.h"
|
||||||
|
|
||||||
|
extern DWORD g_dwStartOfCodeView;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract a generic block of data from debugfile (pass in fileoffset == -1
|
||||||
|
* to avoid the fseek()).
|
||||||
|
*/
|
||||||
|
int ReadChunk (FILE *debugfile, void *dest, int length, int fileoffset)
|
||||||
|
{
|
||||||
|
size_t bytes_read;
|
||||||
|
|
||||||
|
if (fileoffset >= 0)
|
||||||
|
fseek (debugfile, fileoffset, SEEK_SET);
|
||||||
|
|
||||||
|
bytes_read = fread (dest, 1, length, debugfile);
|
||||||
|
if (bytes_read < length)
|
||||||
|
{
|
||||||
|
printf ("ERROR: Only able to read %d bytes of %d-byte chunk!\n",
|
||||||
|
bytes_read, length);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan the next two bytes of a file, and see if they correspond to a file
|
||||||
|
* header signature. Don't forget to put the file pointer back where we
|
||||||
|
* found it...
|
||||||
|
*/
|
||||||
|
CVHeaderType GetHeaderType (FILE *debugfile)
|
||||||
|
{
|
||||||
|
WORD hdrtype;
|
||||||
|
CVHeaderType ret = CV_NONE;
|
||||||
|
|
||||||
|
int oldpos = ftell (debugfile);
|
||||||
|
|
||||||
|
#ifdef VERBOSE
|
||||||
|
printf (" *** Current file position = %lx\n", ftell (debugfile));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!ReadChunk (debugfile, &hdrtype, sizeof (WORD), -1))
|
||||||
|
{
|
||||||
|
fseek (debugfile, oldpos, SEEK_SET);
|
||||||
|
return CV_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdrtype == 0x5A4D) /* "MZ" */
|
||||||
|
ret = CV_DOS;
|
||||||
|
else if (hdrtype == 0x4550) /* "PE" */
|
||||||
|
ret = CV_NT;
|
||||||
|
else if (hdrtype == 0x4944) /* "DI" */
|
||||||
|
ret = CV_DBG;
|
||||||
|
|
||||||
|
fseek (debugfile, oldpos, SEEK_SET);
|
||||||
|
|
||||||
|
#ifdef VERBOSE
|
||||||
|
printf ("Returning header type = %d [0x%x]\n", ret, hdrtype);
|
||||||
|
printf (" *** Current file position = %lx\n", ftell (debugfile));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract the DOS file headers from an executable
|
||||||
|
*/
|
||||||
|
int ReadDOSFileHeader (FILE *debugfile, IMAGE_DOS_HEADER *doshdr)
|
||||||
|
{
|
||||||
|
size_t bytes_read;
|
||||||
|
|
||||||
|
bytes_read = fread (doshdr, 1, sizeof (IMAGE_DOS_HEADER), debugfile);
|
||||||
|
if (bytes_read < sizeof (IMAGE_DOS_HEADER))
|
||||||
|
{
|
||||||
|
printf ("ERROR: Only able to read %d bytes of %d-byte DOS file header!\n",
|
||||||
|
bytes_read, sizeof (IMAGE_DOS_HEADER));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip over stub data, if present
|
||||||
|
*/
|
||||||
|
if (doshdr->e_lfanew)
|
||||||
|
fseek (debugfile, doshdr->e_lfanew, SEEK_SET);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract the DOS and NT file headers from an executable
|
||||||
|
*/
|
||||||
|
int ReadPEFileHeader (FILE *debugfile, IMAGE_NT_HEADERS *nthdr)
|
||||||
|
{
|
||||||
|
size_t bytes_read;
|
||||||
|
|
||||||
|
bytes_read = fread (nthdr, 1, sizeof (IMAGE_NT_HEADERS), debugfile);
|
||||||
|
if (bytes_read < sizeof (IMAGE_NT_HEADERS))
|
||||||
|
{
|
||||||
|
printf ("ERROR: Only able to read %d bytes of %d-byte NT file header!\n",
|
||||||
|
bytes_read, sizeof (IMAGE_NT_HEADERS));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract the DBG file header from debugfile
|
||||||
|
*/
|
||||||
|
int ReadDBGFileHeader (FILE *debugfile, IMAGE_SEPARATE_DEBUG_HEADER *dbghdr)
|
||||||
|
{
|
||||||
|
size_t bytes_read;
|
||||||
|
|
||||||
|
bytes_read = fread (dbghdr, 1, sizeof (IMAGE_SEPARATE_DEBUG_HEADER), debugfile);
|
||||||
|
if (bytes_read < sizeof (IMAGE_SEPARATE_DEBUG_HEADER))
|
||||||
|
{
|
||||||
|
printf ("ERROR: Only able to read %d bytes of %d-byte DBG file header!\n",
|
||||||
|
bytes_read, sizeof (IMAGE_SEPARATE_DEBUG_HEADER));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract all of the file's COFF section headers into an array of
|
||||||
|
* IMAGE_SECTION_HEADER's. These COFF sections don't really apply to
|
||||||
|
* the .DBG file directly (they contain file offsets into the .EXE file
|
||||||
|
* which don't correspond to anything in the .DBG file). They are
|
||||||
|
* copied verbatim into this .DBG file to help make the debugging process
|
||||||
|
* more robust. By referencing these COFF section headers, the debugger
|
||||||
|
* can still function in the absence of the original .EXE file!
|
||||||
|
*
|
||||||
|
* NOTE: Do not bother pre-allocating memory. This function will
|
||||||
|
* allocate it for you. Don't forget to free() it when you're done,
|
||||||
|
* though.
|
||||||
|
*/
|
||||||
|
int ReadSectionHeaders (FILE *debugfile, int numsects, IMAGE_SECTION_HEADER **secthdrs)
|
||||||
|
{
|
||||||
|
size_t bytes_read;
|
||||||
|
|
||||||
|
/* Need a double-pointer so we can change the destination of the pointer
|
||||||
|
* and return the new allocation back to the caller.
|
||||||
|
*/
|
||||||
|
*secthdrs = calloc (numsects, sizeof (IMAGE_SECTION_HEADER));
|
||||||
|
bytes_read = fread (*secthdrs, sizeof (IMAGE_SECTION_HEADER), numsects, debugfile);
|
||||||
|
if (bytes_read < numsects)
|
||||||
|
{
|
||||||
|
printf ("ERROR while reading COFF headers: Only able to "
|
||||||
|
"read %d headers out of %d desired!\n",
|
||||||
|
bytes_read, sizeof (IMAGE_SECTION_HEADER));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load in the debug directory table. This directory describes the various
|
||||||
|
* blocks of debug data that reside at the end of the file (after the COFF
|
||||||
|
* sections), including FPO data, COFF-style debug info, and the CodeView
|
||||||
|
* we are *really* after.
|
||||||
|
*/
|
||||||
|
int ReadDebugDir (FILE *debugfile, int numdirs, IMAGE_DEBUG_DIRECTORY **debugdirs)
|
||||||
|
{
|
||||||
|
size_t bytes_read;
|
||||||
|
|
||||||
|
/* Need a double-pointer so we can change the destination of the pointer
|
||||||
|
* and return the new allocation back to the caller.
|
||||||
|
*/
|
||||||
|
*debugdirs = calloc (numdirs, sizeof (IMAGE_DEBUG_DIRECTORY));
|
||||||
|
bytes_read = fread (*debugdirs, sizeof (IMAGE_DEBUG_DIRECTORY), numdirs, debugfile);
|
||||||
|
if (bytes_read < numdirs)
|
||||||
|
{
|
||||||
|
printf ("ERROR while reading Debug Directory: Only able to "
|
||||||
|
"read %d headers out of %d desired!\n",
|
||||||
|
bytes_read, numdirs);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load in the CodeView-style headers inside the CodeView debug section.
|
||||||
|
* The 'sig' and 'dirhdr' parameters must point to already-allocated
|
||||||
|
* data structures.
|
||||||
|
*/
|
||||||
|
int ReadCodeViewHeader (FILE *debugfile, OMFSignature *sig, OMFDirHeader *dirhdr)
|
||||||
|
{
|
||||||
|
size_t bytes_read;
|
||||||
|
|
||||||
|
bytes_read = fread (sig, 1, sizeof (OMFSignature), debugfile);
|
||||||
|
if (bytes_read < sizeof (OMFSignature))
|
||||||
|
{
|
||||||
|
printf ("ERROR while reading CodeView Header Signature: Only "
|
||||||
|
"able to read %d bytes out of %d desired!\n",
|
||||||
|
bytes_read, sizeof (OMFSignature));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Must perform a massive jump, almost to the end of the file, to find the
|
||||||
|
* CodeView Directory Header (OMFDirHeader), which is immediately followed
|
||||||
|
* by the array of entries (OMFDirEntry). We calculate the jump based on
|
||||||
|
* the beginning of the CodeView debug section (from the CodeView entry in
|
||||||
|
* the IMAGE_DEBUG_DIRECTORY array), with the added offset from OMGSignature.
|
||||||
|
*/
|
||||||
|
fseek (debugfile, sig->filepos + g_dwStartOfCodeView, SEEK_SET);
|
||||||
|
bytes_read = fread (dirhdr, 1, sizeof (OMFDirHeader), debugfile);
|
||||||
|
if (bytes_read < sizeof (OMFDirHeader))
|
||||||
|
{
|
||||||
|
printf ("ERROR while reading CodeView Directory Header: Only "
|
||||||
|
"able to read %d bytes out of %d desired!\n",
|
||||||
|
bytes_read, sizeof (OMFDirHeader));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* File pointer is now at first OMGDirEntry, so we can begin reading those now,
|
||||||
|
* with an immediate call to ReadCodeViewDirectory ().
|
||||||
|
*/
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load in the CodeView directory entries, which each point to a CodeView
|
||||||
|
* subsection (e.g. sstModules, sstGlobalPub). The number of entries in
|
||||||
|
* this table is determined by OMFDirEntry.cDir.
|
||||||
|
*
|
||||||
|
* Strangely enough, this particular section comes immediately *after*
|
||||||
|
* the debug data (as opposed to immediately *before* the data as is the
|
||||||
|
* standard with the COFF headers).
|
||||||
|
*/
|
||||||
|
int ReadCodeViewDirectory (FILE *debugfile, int entrynum, OMFDirEntry **entries)
|
||||||
|
{
|
||||||
|
size_t bytes_read;
|
||||||
|
|
||||||
|
/* Need a double-pointer so we can change the destination of the pointer
|
||||||
|
* and return the new allocation back to the caller.
|
||||||
|
*/
|
||||||
|
/* printf ("Allocating space for %d entries\n", entrynum); */
|
||||||
|
*entries = calloc (entrynum, sizeof (OMFDirEntry));
|
||||||
|
/* printf ("Allocated memory at %p (%p)\n", *entries, entries); */
|
||||||
|
bytes_read = fread (*entries, sizeof (OMFDirEntry), entrynum, debugfile);
|
||||||
|
if (bytes_read < entrynum)
|
||||||
|
{
|
||||||
|
printf ("ERROR while reading CodeView Debug Directories: Only "
|
||||||
|
"able to read %d entries out of %d desired!\n",
|
||||||
|
bytes_read, entrynum);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load in the data contents of all CodeView sstModule sub-sections in the file (likely a
|
||||||
|
* large array, as there is one sub-section for every module... > 100 modules is normal).
|
||||||
|
* 'entrynum' should hold the total number of CV sub-sections, not the number of sstModule
|
||||||
|
* subsections. The function will ignore anything that isn't a sstModule.
|
||||||
|
*
|
||||||
|
* NOTE: 'debugfile' must already be pointing to the correct location.
|
||||||
|
*/
|
||||||
|
int ReadModuleData (FILE *debugfile, int entrynum, OMFDirEntry *entries,
|
||||||
|
int *module_count, OMFModuleFull **modules)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int segnum;
|
||||||
|
size_t bytes_read;
|
||||||
|
OMFSegDesc *segarray;
|
||||||
|
char namelen;
|
||||||
|
OMFModuleFull *module;
|
||||||
|
int pad;
|
||||||
|
|
||||||
|
/* How much of the OMFModuleFull struct can we pull directly from the file?
|
||||||
|
* (Kind of a hack, but not much else we can do...the 'SegInfo' and 'Name'
|
||||||
|
* fields will hold memory pointers, not the actual data from the file.)
|
||||||
|
*/
|
||||||
|
int module_bytes = (sizeof (unsigned short) * 3) + (sizeof (char) * 2);
|
||||||
|
|
||||||
|
if (entries == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Find out how many sstModule sub-sections we have in 'entries'
|
||||||
|
*/
|
||||||
|
*module_count = 0;
|
||||||
|
for (i = 0; i < entrynum; i++)
|
||||||
|
{
|
||||||
|
if (entries[i].SubSection == sstModule)
|
||||||
|
(*module_count)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Need a double-pointer so we can change the destination of the pointer
|
||||||
|
* and return the new allocation back to the caller.
|
||||||
|
*/
|
||||||
|
*modules = calloc (*module_count, sizeof (OMFModuleFull));
|
||||||
|
for (i = 0; i < *module_count; i++)
|
||||||
|
{
|
||||||
|
/* Convenience pointer to current module
|
||||||
|
*/
|
||||||
|
module = &(*modules)[i];
|
||||||
|
|
||||||
|
/* Must extract each OMFModuleFull separately from file, because the 'SegInfo'
|
||||||
|
* and 'Name' fields also require separate allocations; the data for these
|
||||||
|
* fields is interspersed in the file, between OMFModuleFull blocks.
|
||||||
|
*/
|
||||||
|
bytes_read = fread (module, sizeof (char), module_bytes, debugfile);
|
||||||
|
if (bytes_read < module_bytes)
|
||||||
|
{
|
||||||
|
printf ("ERROR while reading CodeView Module Sub-section Data: "
|
||||||
|
"Only able to read %d bytes from entry %d!\n",
|
||||||
|
bytes_read, i);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate space for, and grab the entire 'SegInfo' array.
|
||||||
|
*/
|
||||||
|
segnum = module->cSeg;
|
||||||
|
segarray = calloc (segnum, sizeof (OMFSegDesc));
|
||||||
|
|
||||||
|
bytes_read = fread (segarray, sizeof (OMFSegDesc), segnum, debugfile);
|
||||||
|
if (bytes_read < segnum)
|
||||||
|
{
|
||||||
|
printf ("ERROR while reading CodeView Module SegInfo Data: "
|
||||||
|
"Only able to read %d segments from module %d!\n",
|
||||||
|
bytes_read, i);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
module->SegInfo = segarray;
|
||||||
|
|
||||||
|
/* Allocate space for the (length-prefixed) 'Name' field.
|
||||||
|
*/
|
||||||
|
bytes_read = fread (&namelen, sizeof (char), 1, debugfile);
|
||||||
|
if (bytes_read < 1)
|
||||||
|
{
|
||||||
|
printf ("ERROR while reading CodeView Module Name length!\n");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read 'Name' field from file. 'Name' must be aligned on a 4-byte
|
||||||
|
* boundary, so we must do a little extra math on the string length.
|
||||||
|
* (NOTE: Must include namelen byte in total padding length, too.)
|
||||||
|
*/
|
||||||
|
pad = ((namelen + 1) % 4);
|
||||||
|
if (pad)
|
||||||
|
namelen += (4 - pad);
|
||||||
|
|
||||||
|
module->Name = calloc (namelen, sizeof (char) + 1);
|
||||||
|
bytes_read = fread (module->Name, sizeof (char), namelen, debugfile);
|
||||||
|
if (bytes_read < namelen)
|
||||||
|
{
|
||||||
|
printf ("ERROR while reading CodeView Module Name: "
|
||||||
|
"Only able to read %d chars from module %d!\n",
|
||||||
|
bytes_read, i);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* printf ("%s\n", module->Name); */
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VERBOSE
|
||||||
|
printf ("Done reading %d modules\n", *module_count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue