#include "GPEMap.h"
#include <stdio.h>
#include <malloc.h>
//
// Internal Image Data structures
//
#pragma pack(push,1)
struct ImageDebugDirectory
{
unsigned int m_Characteristics;
unsigned int m_TimeDateStamp;
unsigned short m_MajorVersion;
unsigned short m_MinorVersion;
unsigned int m_Type;
unsigned int m_SizeOfData;
unsigned int m_AddressOfRawData;
unsigned int m_PointerToRawData; // The important bit - pointer to the debug directory's data
// in the PE file
}; //ImageDebugDirectory - Analagous to IMAGE_DEBUG_DIRECTORY with all the unnecessary stuff
//chopped out
struct ImageDosStubHeader
{
unsigned short m_Magic; // DOS signature ('MZ')
unsigned short m_Filler[29]; // Don't need to know this stuff (see MSDN for details)
long m_LFA; // File address of new exe header
}; //ImageDosStubHeader - Analagous to IMAGE_DOS_HEADER, with all the useless crap chopped out
struct ImageDataDirectory
{
unsigned long m_VAddr; // Data virtual offset from m_pMapAddress
unsigned long m_Size; // Size of the data
}; //ImageDataDirectory - Analogous to IMAGE_DATA_DIRECTORY (this is an internal structure in <winnt.h>)
struct ImageNTHeaders
{
unsigned long m_Signature; // PE file signature ('PE\0\0')
unsigned short m_FileHdrFiller[10]; // Don't need to know this shit (see MSDN for details)
unsigned short m_OpFileHdrFiller[48]; // Don't need to know this shit (see MSDN for details)
ImageDataDirectory m_DataDirectory[16]; // PE data directories (resources, debug data, etc.)
// (debug is at offset 144 from m_OpFileHdrFiller - i.e.
// position 6 in this array - see MSDN help for
// IMAGE_DATA_DIRECTORY)
}; //ImageNTHeaders - Analagous to IMAGE_NT_HEADERS in <dbghelp.h>, except with all the unnecessary
//stuff chopped out
//
// Internal CV Data structures
//
struct CVSignature
{
char m_Signature[4]; // CV signature ('NB11' or 'NB09')
unsigned int m_FilePos; // File position (CV base file address)
}; //CVSignature - CodeView type and file position reference
struct CVSubSection
{
unsigned short m_SubSection; // Subsection type (sst...)
unsigned short m_ModuleIndex; // Module index
int m_LFO; // Large file offset of subsection
unsigned int m_SubSectionSize; // Number of bytes in subsection
}; //CVSubSection - CodeView subsection
struct CVDirHeader
{
unsigned short m_DirHeader; // Length of this structure
unsigned short m_DirEntry; // Number of bytes in each directory entry
unsigned int m_NumDirEntries; // Number of directory entries
int m_NextDir; // Offset from the base of next directory (unused)
unsigned int m_Flags; // Status flags
}; //CVDirHeader - Section directory header
struct CVSrcModuleHeader
{
unsigned short m_NumFiles; // Number of source files contained in this module
unsigned short m_NumSegs; // Number of base segments in this module
}; //CVSrcModuleHeader - Source module (.obj) header
struct CVSymTableHeader
{
unsigned short m_SymHashFnc; // Symbol hash function (must be 12)
unsigned short m_AddrHashFnc; // Address hash function (must be 10)
unsigned int m_NumSymBytes; // Number of bytes in the upcoming $$SYMBOL table
unsigned int m_NumSymHashBytes; // Number of bytes in the symbol hash table
unsigned int m_NumAddrHashBytes; // Number of bytes in the address hash table
}; //CVSymTableHeader
struct CVSymData
{
unsigned int m_TypeID; // Type identifier for the symbol
unsigned int m_Offset; // Offset of the symbol
unsigned short m_Segment; // Symbol segment
unsigned char m_NameLen; // Length of the symbol name
char m_Name[512]; // Symbol name (not really 512 characters, but I only
// ever use this structure to reinterpret the data
// pointer, never as a stack or heap object)
}; //CVSymData - variable names in the $$SYMBOL table (S_LDATA32 and S_GDATA32)
struct CVSymProcStart
{
unsigned int m_Parent; //
unsigned int m_End;
unsigned int m_Next;
unsigned int m_ProcLength;
unsigned int m_DbgStart;
unsigned int m_DbgEnd;
unsigned int m_ProcType;
unsigned int m_Offset;
unsigned short m_Segment;
unsigned char m_Flags;
unsigned char m_NameLen;
char m_Name[512];
}; //CVSymProcStart - proc start in a $$SYMBOL table (S_LPROC32 and S_GPROC32)
struct CVSymProcRef
{
unsigned int m_Checksum;
unsigned int m_SymOffset;
unsigned short m_Module;
}; //CVSymProcRef - proc reference in global $$SYMBOL tables (S_PROCREF)
struct CVAddrHashOffset
{
unsigned int m_CurSymOffset;
unsigned int m_CurMemOffset;
}; //CVAddrHashOffset - used in the address sort table in global symbol tables
#pragma pack(pop)
//
// CV Enums
//
enum ModuleType
{
// Module identifiers
eMT_sstModule = 0x120,
eMT_sstTypes = 0x121,
eMT_sstPublic = 0x122,
eMT_sstPublicSym = 0x123,
eMT_sstSymbols = 0x124,
eMT_sstAlignSym = 0x125,
eMT_sstSrcLnSeg = 0x126,
eMT_sstSrcModule = 0x127,
eMT_sstLibraries = 0x128,
eMT_sstGlobalSym = 0x129,
eMT_sstGlobalPub = 0x12a,
eMT_sstGlobalTypes = 0x12b,
eMT_sstMPC = 0x12c,
eMT_sstSegMap = 0x12d,
eMT_sstSegName = 0x12e,
eMT_sstPreComp = 0x12f,
eMT_unused = 0x130,
eMT_sstOffsetMap16 = 0x131,
eMT_sstOffsetMap32 = 0x132,
eMT_sstFileIndex = 0x133,
eMT_sstStaticSym = 0x134,
// Module type range
eMT_lastModule,
eMT_firstModule = eMT_sstModule,
// Maximum module types
eModuleTypes = eMT_lastModule - eMT_firstModule
}; //ModuleType
enum SType
{
// Symbol type identifiers (ripped from MSDN)
S_COMPILE = 0x0001,
S_SSEARCH = 0x0005,
S_END = 0x0006,
S_SKIP = 0x0007,
S_CVRESERVE = 0x0008,
S_OBJNAME = 0x0009,
S_ENDARG = 0x000a,
S_COBOLUDT = 0x000b,
S_MANYREG = 0x000c,
S_RETURN = 0x000d,
S_ENTRYTHIS = 0x000e,
S_REGISTER = 0x1001,
S_CONSTANT = 0x1002,
S_UDT = 0x1003,
S_COBOLUDT2 = 0x1004,
S_MANYREG2 = 0x1005,
S_BPREL32 = 0x1006,
S_LDATA32 = 0x1007,
S_GDATA32 = 0x1008,
S_PUB32 = 0x1009,
S_LPROC32 = 0x100a,
S_GPROC32 = 0x100b,
S_THUNK32 = 0x0206,
S_BLOCK32 = 0x0207,
S_WITH32 = 0x0208,
S_LABEL32 = 0x0209,
S_CEXMODEL32 = 0x020a,
S_VFTTABLE32 = 0x100c,
S_REGREL32 = 0x100d,
S_LTHREAD32 = 0x100e,
S_GTHREAD32 = 0x100f,
S_LPROCMIPS = 0x1010,
S_GPROCMIPS = 0x1011,
S_PROCREF = 0x0400,
S_DATAREF = 0x0401,
S_ALIGN = 0x0402,
S_FIRST = S_COMPILE,
S_LAST = S_ALIGN
};
//
// Global Utility Functions
//
TCHAR * _StringifyOSError(const int BufferSize,TCHAR * pBuffer,const unsigned long ErrCode = -1)
{
// _StringifyOSError fills up to BufferSize character in pBuffer with either the stringified
// version either GetLastError(), or ErrCode if it's not -1
const unsigned long ActErrCode = ErrCode == -1 ? GetLastError() : ErrCode;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
ActErrCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
LPTSTR(pBuffer),
BufferSize,
NULL);
// FormatMessage seems to add newlines to the end of the string buffer. This is unacceptable!
{
const int NewBufLen = strlen(pBuffer);
if (pBuffer[NewBufLen - 1] == _T('\n'))
{
pBuffer[NewBufLen - 1] = _T('\0');
}
if (pBuffer[NewBufLen - 2] == _T('\r'))
{
pBuffer[NewBufLen - 2] = _T('\0');
}
}
return pBuffer;
} //_StringifyOSError
const unsigned char * _GetDataPtr(const unsigned char * & pDataPtr,const unsigned int NumBytes)
{
// _GetDataPtr - Stores pDataPtr, increments pDataPtr by NumByte, then returns the stored pDataPtr
// (useful for parsing the memory mapped file).
const unsigned char * pOldDataPtr = pDataPtr;
pDataPtr += NumBytes;
return pOldDataPtr;
} //_GetDataPtr
template <class T> const T * _GetCastDataPtr(const unsigned char * & pDataPtr,const int Count = 1,T * pDummy = 0)
{
// _GetCastDataPtr - Templated version of _GetDataPtr - Count specifies the number of T's expected at pDataPtr
return (const T*)_GetDataPtr(pDataPtr,sizeof(T) * Count);
} //_GetCastDataPtr
unsigned int _ConvertAddrToMap(const void * pAddr,const unsigned int SystemPageSize)
{
// _ConvertAddrToMap - Convert pAddr to the map file using VirtualQuery
MEMORY_BASIC_INFORMATION tmpMem;
VirtualQuery(pAddr,&tmpMem,sizeof(tmpMem));
// The difference in the allocation base and the base address tells us by how much pAddr was rounded down
unsigned int Mod = (((unsigned int)tmpMem.BaseAddress - (unsigned int)tmpMem.AllocationBase) / SystemPageSize) - 1;
return ((unsigned int)(pAddr) - (unsigned int)tmpMem.BaseAddress) + (Mod * SystemPageSize);
} //_ConvertAddrToMap
//
// GPEMap Methods
//
GPEMap::GPEMap()
{
Init();
const int MaxModuleFilenameLen = 512;
TCHAR ModulePEFile[MaxModuleFilenameLen];
if (GetModuleFileName(0,ModulePEFile,MaxModuleFilenameLen) > 0)
{
// Try to open the pe file and process it
if (OpenPEFile(ModulePEFile) && ProcessPEFile())
{
m_bOK = true;
}
}
else
{
const int OSErrLen = 512;
TCHAR OSErr[OSErrLen];
OutputErrorF(_T("GPEMap::GPEMap - GetModuleFilename failed (OS Error = '%s')"),
_StringifyOSError(OSErrLen,OSErr));
}
} //GPEMap::GPEMap
GPEMap::GPEMap(const TCHAR * pPEFileToMap)
{
Init();
// Try to open the pe file and process it
if (OpenPEFile(pPEFileToMap) && ProcessPEFile())
{
m_bOK = true;
}
} //GPEMap::GPEMap
void GPEMap::Init()
{
// Initialize file mapping members
m_bOK = false;
m_hFile = 0;
m_hMapFile = 0;
m_pMapAddress = 0;
// COFF
m_pCOFFDbgDir = 0;
// CodeView
m_pCVDbgDir = 0;
m_pCVGlobalSym = 0;
m_pCVSrcMods = 0;
m_pCVAlignSym = 0;
m_NumCVSrcMods = 0;
m_NumCVAlignSyms = 0;
// Initialize the system page size
{
SYSTEM_INFO tmpSysInfo;
GetSystemInfo(&tmpSysInfo);
m_SystemPageSize = tmpSysInfo.dwPageSize;
}
} //GPEMap::Init
bool GPEMap::OpenPEFile(const TCHAR * pPEFileToMap)
{
// OpePEFile - Opens pPEFileToMap in a standard file, then creates a memory map of it. The base address
// of the map is stored in m_pMapAddress
// OS error storage
const int OSErrLen = 256;
TCHAR OSErr[OSErrLen];
// Open up the PE file
OFSTRUCT FileInfo;
m_hFile = (HANDLE)OpenFile(pPEFileToMap,&FileInfo,OF_READ);
if (m_hFile != HANDLE(HFILE_ERROR))
{
// Create a file mapping
m_hMapFile = CreateFileMapping(HANDLE(m_hFile), // Current file handle.
NULL, // Default security
PAGE_READONLY, // Read/write permission.
0, // Max. object size
0, // Size of hFile
_T("EXEMap")); // Name of mapping object.
if (m_hMapFile == NULL)
{
OutputErrorF(_T("GPEMap::GPEMap - Failed to open map file (OS Error = '%s')"),
_StringifyOSError(OSErrLen,OSErr));
}
else
{
// Create a view of the file
m_pMapAddress = (unsigned char*)MapViewOfFile(m_hMapFile, // Handle to mapping object
FILE_MAP_READ, // Read/write permission
0, // Max. object size
0, // Size of hFile
0); // Map entire file
if (m_pMapAddress == NULL)
{
OutputErrorF(_T("GPEMap::GPEMap - Could not map view of file (OS Error = '%s')"),
_StringifyOSError(OSErrLen,OSErr));
}
else
{
return true;
}
}
}
else
{
OutputErrorF(_T("GPEMap::GPEMap - Failed to open PE file (OS Error = '%s'"),
_StringifyOSError(OSErrLen,OSErr));
}
return false;
} //GPEMap::OpenPEFile
bool GPEMap::ProcessPEFile()
{
// ProcessPEFile - Sets up precalculated file pointers for quicker symbol lookup. At the moment,
// only the CodeView debug format is supported
// Access the debug directories
unsigned long DataSize;
const unsigned char * pDirEntry;
// Get a pointer to the first debug directory.
{
// Initial structure = a dos stub header, then image nt headers, which contains the pointers
// to the image directories (resources, debug data, import and export tables, and other stuff).
// The debug directories should be at position 6 (offset 144) in this array
const unsigned char * pDataPtr = m_pMapAddress;
const ImageDosStubHeader * pDosHdr = _GetCastDataPtr<ImageDosStubHeader>(pDataPtr);
const ImageNTHeaders * pNT = (ImageNTHeaders*)(m_pMapAddress + pDosHdr->m_LFA);
pDirEntry = m_pMapAddress + pNT->m_DataDirectory[6].m_VAddr;
DataSize = pNT->m_DataDirectory[6].m_Size;
}
// If we got some debug directory data...
if ((pDirEntry != m_pMapAddress) && (DataSize > 0))
{
// ... process it
const ImageDebugDirectory * pDebugDir = (ImageDebugDirectory*)pDirEntry;
const int NumDebugDirs = int(DataSize) / sizeof(ImageDebugDirectory);
for (int i = 0; i < NumDebugDirs; i++, pDebugDir++)
{
switch (pDebugDir->m_Type)
{
// Unprocessed debug directories
case IMAGE_DEBUG_TYPE_UNKNOWN :
case IMAGE_DEBUG_TYPE_FPO :
case IMAGE_DEBUG_TYPE_MISC :
case IMAGE_DEBUG_TYPE_EXCEPTION :
case IMAGE_DEBUG_TYPE_FIXUP :
case IMAGE_DEBUG_TYPE_BORLAND : break;
// Processed debug directories
case IMAGE_DEBUG_TYPE_COFF :
ProcessCOFFDbgDir(pDebugDir); // TODO...
break;
case IMAGE_DEBUG_TYPE_CODEVIEW :
ProcessCVDbgDir(pDebugDir);
break;
};
}
}
return UsingCOFF() || UsingCV();
} //GPEMap::ProcessPEFile
void GPEMap::ClosePEFile()
{
// Close down the file handles and stuff
if (m_pMapAddress)
{
UnmapViewOfFile(m_pMapAddress);
CloseHandle(m_hMapFile);
CloseHandle(m_hFile);
m_pMapAddress = 0;
m_hMapFile = 0;
m_hFile = 0;
}
} //GPEMap::ClosePEFile
void GPEMap::ProcessCVDbgDir(const ImageDebugDirectory * pDebugDir)
{
//assert(UsingCV());
// Verify the CV signature before we do anything else
m_pLFO = m_pMapAddress + pDebugDir->m_PointerToRawData;
m_pCVSig = (const CVSignature *)m_pLFO;
if (!CheckCVSig(*((unsigned int*)&m_pCVSig->m_Signature)))
{
return;
}
const CVDirHeader * pCVDirHeader;
const CVSubSection * pCVSubSections;
// Read in the directory header and subsections
const unsigned char * pDataPtr = m_pLFO + m_pCVSig->m_FilePos;
pCVDirHeader = _GetCastDataPtr<CVDirHeader>(pDataPtr);
pCVSubSections = _GetCastDataPtr<CVSubSection>(pDataPtr);
// Run through and process each of the debug directories
{
const int MaxSrcMods = 1024;
const CVSubSection * SrcModList[MaxSrcMods];
const CVSubSection * AlignSymList[MaxSrcMods];
// Parse each subsection
for (unsigned int i = 0; i < pCVDirHeader->m_NumDirEntries; i++)
{
const CVSubSection * pSubSection = &(pCVSubSections[i]);
switch (ModuleType(pSubSection->m_SubSection))
{
case eMT_sstModule :
case eMT_sstTypes :
case eMT_sstPublic :
case eMT_sstPublicSym :
case eMT_sstSymbols :
case eMT_sstSrcLnSeg :
case eMT_sstLibraries :
case eMT_sstGlobalPub :
case eMT_sstGlobalTypes :
case eMT_sstMPC :
case eMT_sstSegMap :
case eMT_sstSegName :
case eMT_sstPreComp :
case eMT_unused :
case eMT_sstOffsetMap16 :
case eMT_sstOffsetMap32 :
case eMT_sstFileIndex :
case eMT_sstStaticSym : /* Boring */ break;
case eMT_sstAlignSym :
{
// Alignment symbols table - this is somewhat glossed over in the documentation, but
// it seems that for each module, there's a matching one of these that contains all
// the symbol information for it.
AlignSymList[m_NumCVAlignSyms++] = pSubSection;
break;
}; //case eMT_sstAlignSym
case eMT_sstSrcModule :
{
// Module source table - there's one of these per module in PE file
SrcModList[m_NumCVSrcMods++] = pSubSection;
break;
}; //case eMT_sstSrcModule
case eMT_sstGlobalSym :
{
// This is the global symbol lookup table - instead of searching each module for a
// proc address, this table contains an address sort list that allows you to look
// up the address directly in one of the alignment symbol tables. Note that there's
// only one of these per PE file, although there's also a static symbol table and a
// public symbol table (eMT_sstStaticSym and eMT_sstPublic) that are supposed to
// do the same sort of thing.
//assert(m_pCVGlobalSym);
m_pCVGlobalSym = pSubSection;
break;
}; //case eMT_sstGlobalSym
};
}
// Transfer SrcModList and AlignSymList to m_pCVSrcMods and m_pCVAlignSym respectively
{
int i;
m_pCVSrcMods = new const CVSubSection *[m_NumCVSrcMods];
m_pCVAlignSym = new const CVSubSection *[m_NumCVAlignSyms];
for (i = 0; i < m_NumCVSrcMods; m_pCVSrcMods[i] = SrcModList[i],i++);
for (i = 0; i < m_NumCVAlignSyms; m_pCVAlignSym[i]= AlignSymList[i],i++);
}
}
m_pCVDbgDir = pDebugDir;
} //GPEMap::ProcessCVDbgDir
void GPEMap::ProcessCOFFDbgDir(const ImageDebugDirectory * pDebugDir)
{
// TODO...
} //GPEMap::ProcessCOFFDbgDir
void GPEMap::OutputErrorF(const TCHAR * pError,...) const
{
TCHAR FullMsg[1024];
va_list VArgs;
va_start(VArgs,pError);
_vsnprintf(FullMsg,1024,pError,VArgs);
va_end(VArgs);
MessageBox(0,FullMsg,_T("GPEMap Error"),MB_OK);
} //GPEMap::OutputErrorF
bool GPEMap::CheckCVSig(const unsigned int Sig)
{
if ((Sig == (unsigned int)'11BN') ||
(Sig == (unsigned int)'90BN')) // I think NB09 is ok too...
{
return true;
}
if (Sig == (unsigned int)'01BN')
{
// The PDB format is based on a modified CodeView structure that is not publicly available. Please
// mail me at andrewp@redlemon.com if you know anything about it...
OutputErrorF(_T("GPEMap::CheckCVSig - GPEMap does not support external program databases. If you are\n")
_T("using Visual Studio 6.0 then remove the check from 'Use Program Database' in\n")
_T("Project Settings->Link->Customize\n"));
return false;
}
else
{
// (should be ok for unicode)
OutputErrorF(_T("GPEMap::CheckCVSig - Unsupported codeview format ('%c%c%c%c')\n"),
((char*)&Sig)[0],((char*)&Sig)[1],((char*)&Sig)[2],((char*)&Sig)[3]);
}
return false;
} //GPEMap::CheckCVSig
bool GPEMap::GetCVCodeSource(const void * pAddr,GPEMap::CodeSourceData * pData) const
{
// Map pAddr
const unsigned int AddrUInt = _ConvertAddrToMap(pAddr,m_SystemPageSize);
// Find the source information for AddrUInt
if (!CVLookupAddrSrc(AddrUInt,pData))
{
return false;
}
// Find the symbol information for AddrUInt
return CVLookupAddrSym(AddrUInt,pData);
} //GPEMap::GetCVCodeSource
bool GPEMap::CVLookupAddrSym(const unsigned int AddrUInt,CodeSourceData * pData) const
{
if (!m_pCVGlobalSym)
{
return false;
}
const unsigned char * pGlobalSymBase = m_pLFO + m_pCVGlobalSym->m_LFO,
* pDataPtr = pGlobalSymBase;
const CVSymTableHeader * pSymTableHdr = _GetCastDataPtr<CVSymTableHeader>(pDataPtr);
const unsigned char * pSymTable = pDataPtr;
//assert(SymHashFnc == 10);
//assert(AddrHashFnc == 12);
pDataPtr = pDataPtr + (pSymTableHdr->m_NumSymBytes + pSymTableHdr->m_NumSymHashBytes);
const unsigned short NumSegs = *_GetCastDataPtr<unsigned short>(pDataPtr,2 /* (incl. padding) */);
const unsigned int * pSegTable = _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs);
const unsigned int * pOffsetCounts = _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs);
const unsigned int * pOffsetTable = _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs * 2);
pData->m_Fnc[0] = _T('\0');
// TODO : Binary search
for (unsigned int CurSeg = 0; CurSeg < NumSegs; CurSeg++)
{
const CVAddrHashOffset * pCurOff = (CVAddrHashOffset*)pOffsetTable;
for (unsigned int i = 0; i < pOffsetCounts[CurSeg]; i++)
{
if ((AddrUInt >= pCurOff[0].m_CurMemOffset) && (AddrUInt <= pCurOff[1].m_CurMemOffset))
{
const unsigned char * pSymData = pSymTable + pCurOff[0].m_CurSymOffset;
const unsigned short SymLen = *_GetCastDataPtr<unsigned short>(pSymData);
const unsigned short SymType = *_GetCastDataPtr<unsigned short>(pSymData);
switch (SymType)
{
case S_PROCREF :
{
const CVSymProcRef * pProcRef = (CVSymProcRef*)pSymData;
const unsigned char * pModAddr = m_pLFO + m_pCVAlignSym[pProcRef->m_Module-1]->m_LFO;
const CVSymProcStart * pProcData = (CVSymProcStart *)&(pModAddr[pProcRef->m_SymOffset+4]);
_sntprintf(pData->m_Fnc,eMaxFncName,_T("%.*s"),pProcData->m_NameLen,pProcData->m_Name);
return true;
};
// Can also do S_DATAREF for variables (see MSDN documentation and CVSymData for details)
};
// Unhandled symbol type...
return false;
}
pCurOff++;
}
}
return false;
} //GPEMap::CVLookupAddrSym
bool GPEMap::CVLookupAddrSrc(const unsigned int AddrUInt,CodeSourceData * pData) const
{
// Run through the source module subsections
for (int i = 0; i < m_NumCVSrcMods; i++)
{
// Goto the i'th module source section
const unsigned char * pSrcModBase = m_pLFO + m_pCVSrcMods[i]->m_LFO,
* pDataPtr = pSrcModBase;
// Get the module header information and the base source and segment data
const CVSrcModuleHeader * pSrcModHdr = _GetCastDataPtr<CVSrcModuleHeader>(pDataPtr);
const unsigned int * pBaseSrcPtrs = _GetCastDataPtr<unsigned int>(pDataPtr,pSrcModHdr->m_NumFiles);
const unsigned int * pBaseSegPairs = _GetCastDataPtr<unsigned int>(pDataPtr,pSrcModHdr->m_NumSegs * 2);
const unsigned short * pBaseSegs = _GetCastDataPtr<unsigned short>(pDataPtr,pSrcModHdr->m_NumSegs);
// TODO : Quick check of base seg pairs to reject this module fast
bool bInModule = false;
const unsigned int * pCurBasePair = pBaseSegPairs;
for (unsigned int qs = 0; qs < pSrcModHdr->m_NumSegs; qs++)
{
if ((AddrUInt >= pCurBasePair[0]) && (AddrUInt <= pCurBasePair[1]))
{
bInModule = true;
break;
}
pCurBasePair += 2;
}
if (!bInModule)
{
// Address is not in this module... goto the next one
continue;
}
// RUn through the source files
for (unsigned int f = 0; f < pSrcModHdr->m_NumFiles; f++)
{
// Goto the data for this source file
pDataPtr = pSrcModBase + pBaseSrcPtrs[f];
// Get the name, line and segment information for this file
const unsigned short NumSegs = *_GetCastDataPtr<unsigned short>(pDataPtr);
const unsigned short Pad = *_GetCastDataPtr<unsigned short>(pDataPtr);
const unsigned int * pSrcLines = _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs);
const unsigned int * pSegPairs = _GetCastDataPtr<unsigned int>(pDataPtr,NumSegs * 2);
// the documentation lies! FNameLen is only one byte...
const unsigned char FNameLen = *_GetCastDataPtr<unsigned char>(pDataPtr);
const char * pFName = _GetCastDataPtr<char>(pDataPtr,FNameLen);
// Run through the file segments
const unsigned int * pCurPair = pSegPairs;
for (unsigned int s = 0; s < NumSegs; s++)
{
if ((AddrUInt >= pCurPair[0]) && (AddrUInt<= pCurPair[1]))
{
// Found it! Find the line number it appears on
const unsigned char * pTmpDataPtr = pSrcModBase + pSrcLines[s];
const unsigned short Seg = *_GetCastDataPtr<unsigned short>(pTmpDataPtr);
const unsigned short Pair = *_GetCastDataPtr<unsigned short>(pTmpDataPtr);
const unsigned int * pOffsets = _GetCastDataPtr<unsigned int>(pTmpDataPtr,Pair);
const unsigned short * pLines = _GetCastDataPtr<unsigned short>(pTmpDataPtr,Pair);
// Find the matching offset
for (int o = 0; o < Pair; o++)
{
if (pOffsets[o] >= AddrUInt)
{
_sntprintf(pData->m_File,eMaxSourceFileName,_T("%.*s"),FNameLen,pFName);
pData->m_Line = pLines[o == 0 ? o : o-1];
return true;
}
}
return false;
}
pCurPair += 2;
}
}
}
return false;
} //GPEMap::CVLookupAddrSrc
bool GPEMap::GetCodeSource(const void * pAddr,CodeSourceData * pData) const
{
if (!IsOK())
{
return false;
}
//assert(pData);
// Initialize pData
pData->m_File[0] = _T('\0');
pData->m_Line = 0;
pData->m_Fnc[0] = _T('\0');
// Process either using CodeView or COFF
if (UsingCV())
{
return GetCVCodeSource(pAddr,pData);
}
else if (UsingCOFF())
{
// TODO...
}
return false;
} //GPEMap::GetCodeSource
bool GPEMap::GetStackWalkCodeSource(const int NumCalls,
const void * * pStackAddrs,
CodeSourceData * pData) const
{
// Run through the stack address array
for (int i = 0; i < NumCalls; i++)
{
if (pStackAddrs[i])
{
// Find the source file,line and procedure / data name
if (!GetCodeSource(pStackAddrs[i],&(pData[i])))
{
return false;
}
// Helpful...
TCHAR tmpStr[256];
sprintf(tmpStr,_T("%s(%d) : Found in '%s'\n"),pData[i].m_File,pData[i].m_Line,pData[i].m_Fnc);
OutputDebugString(tmpStr);
}
}
return true;
} //GPEMap::GetStackWalkCodeSource
GPEMap::~GPEMap()
{
if (IsOK())
{
ClosePEFile();
// Delete the source module and alignment symbols arrays
if (m_pCVSrcMods)
{
delete []m_pCVSrcMods;
}
if (m_pCVAlignSym)
{
delete []m_pCVAlignSym;
}
}
} //GPEMap::~GPEMap
bool GPEMap::_WalkTheStack(const int NumCallsToCheck,
const int NumCallsToSkip,
void * * pStackAddrs)
{
// Check the parameters
if (!NumCallsToCheck || !pStackAddrs)
{
return false;
}
// Initialize the stack walk variables and clear out the stack address array
int CurStackIn = 0;
int CurSkip = 0;
int ParentEBP;
for (int i = 0; i < NumCallsToCheck; pStackAddrs[i++] = 0);
// Store the parent EBP to begin with
__asm MOV ParentEBP, EBP
do
{
// Get the caller pointer and move on to the caller parent
void * pCaller = *((void**)ParentEBP + 1);
ParentEBP = *((int*)ParentEBP);
// Either skip or store the caller in the stack address array
if (CurSkip < NumCallsToSkip)
{
CurSkip++;
}
else
{
pStackAddrs[CurStackIn++] = pCaller;
}
// Until ParentEBP is null or we've reached the end of the stack address array
} while ((ParentEBP != 0) && (CurStackIn < NumCallsToCheck));
return true;
} //GPEMap::_WalkTheStack
#ifdef _TESTING
//
// TESTING
//
class TestClass
{
public :
void FinalDoStuff(GPEMap * pMap)
{
const int TestStackSize = 7;
void * TestStack[TestStackSize];
GPEMap::CodeSourceData TestStackSource[TestStackSize];
if (GPEMap::_WalkTheStack(TestStackSize,0,TestStack))
{
pMap->GetStackWalkCodeSource(TestStackSize,(const void**)TestStack,TestStackSource);
}
}
static void _PenultimateDoStuff(GPEMap * pMap)
{
TestClass t;
t.FinalDoStuff(pMap);
}
};
void _DoMoreStuff(GPEMap * pMap)
{
TestClass::_PenultimateDoStuff(pMap);
}
void _DoStuff(GPEMap * pMap)
{
_DoMoreStuff(pMap);
}
void _TestGPEMap(const TCHAR * pPEFile)
{
if (pPEFile)
{
GPEMap tmpMap(pPEFile);
_DoStuff(&tmpMap);
}
else
{
GPEMap tmpMap;
_DoStuff(&tmpMap);
}
}
#endif
|