Type |
Size |
RVA |
Pointer |
coff |
3984F 00000000 |
2060 |
|
misc |
110 00000000 |
3B8B0 |
Image Name: obj\i386\advapi32.dll |
fpo |
4A20 00000000 |
3B9C0 |
|
cv |
22660 00000000 |
403E0 |
Format: NB09 |
Figure 3 IMAGE_SEPARATE_DEBUG_HEADER Fields
.DBG File |
Executable File Equivalent |
Signature |
N/A (Must be 0x4944 -> "DI") |
Flags |
N/A |
Machine |
IMAGE_FILE_HEADER.Machine |
Characteristics |
IMAGE_FILE_HEADER.Characteristics |
TimeDateStamp |
IMAGE_FILE_HEADER.TimeDateStamp |
CheckSum |
IMAGE_OPTIONAL_HEADER.CheckSum |
ImageBase |
IMAGE_OPTIONAL_HEADER.ImageBase |
SizeOfImage |
IMAGE_OPTIONAL_HEADER.SizeOfImage |
NumberOfSections |
IMAGE_FILE_HEADER.NumberOfSections |
ExportedNamesSize |
N/A |
DebugDirectorySize |
N/A |
SectionAlignment |
IMAGE_OPTIONAL_HEADER.SectionAlignment |
Reserved[2] |
N/A |
Figure 4 IMAGE_DEBUG_DIRECTORY Fields
Field |
Interpretation |
Characteristics |
Always 0, not used |
TimeDateStamp |
Matches TimeDateStamp of executable |
MajorVersion |
Always 0, except for OMAP information |
MinorVersion |
Always 0 |
Type |
IMAGE_DEBUG_TYPE_xxx (see WINNT.H) |
SizeOfData |
Size of debug information |
AddressOfRawData |
0 if debug info in separate file, otherwise relative virtual address |
PointerToRawData |
Offset in .DBG file to debug information |
Figure 5 CoClassSymsDbgFile.cpp
//==================================================
// CoClassSymsDbgFile - Matt Pietrek 1999
// Microsoft Systems Journal, March 1999
// FILE: CoClassSymsDbgFile
//==================================================
#include <windows.h>
#include <stdio.h>
#include <imagehlp.h>
#include <stdlib.h>
#include <stddef.h>
#include "CoClassSymsCallouts.h"
// From Platform SDK, \SAMPLES\SDKTOOLS\IMAGE\INCLUDE\ directory
#include "cvexefmt.h"
//============================================================================
FILE * g_pOutFile = 0;
LOADED_IMAGE g_liExe;
unsigned long g_CVInfoOffset;
unsigned g_cbPublicSymbols = sizeof(OMFSymHash);
unsigned g_lfoGlobalPub = 0;
unsigned g_lfoSstModule = 0;
unsigned g_cbSstModule = 0;
char g_szModuleName[] = { '9', "BobIn2020" }; // Hardcoded length!
//============================================================================
void CalculateCVInfoOffsts( void );
void WriteDBGHeader( FILE * pFile, PIMAGE_NT_HEADERS pNtHdr );
void WriteSectionTable( FILE * pFile, PIMAGE_SECTION_HEADER pSectionHdr,
ULONG nSections );
void WriteDbgDirectory(FILE * pFile, PIMAGE_NT_HEADERS pNtHdr, DWORD cbCVInfo);
unsigned AddPublicSymbolSymbol32( unsigned short seg, unsigned long offset,
char * pszSymbol, FILE * pFile );
unsigned WriteRemainingCVInfo( FILE * pFile );
BOOL WriteSstModule( FILE *pFile );
unsigned WriteSegMap( FILE * pFile, unsigned long fileOffset,
PIMAGE_SECTION_HEADER pSectionHdr, ULONG nSections );
//============================================================================
// First API called by CoClassSyms. Does initial work to prepare for writing
// the .DBG file.
//============================================================================
BOOL __stdcall CoClassSymsBeginSymbolCallouts( PSTR pszExecutable )
{
// Map the executable into memory, since we're going to need all sorts
// of information from it, such as the section table.
if ( !MapAndLoad( pszExecutable, 0, &g_liExe, FALSE, TRUE ) )
{
// Ooops! Something's wrong. Bail out.
printf( "Unable to access or load executable\n" );
return FALSE;
}
// If we get here, the executable looks OK. Synthesize the name of the
// .DBG file we'll be creating by stripping off the existing extension and
// adding ".DBG"
char szExeBaseName[MAX_PATH];
char szDBGFileName[MAX_PATH];
_splitpath( pszExecutable, 0, 0, szExeBaseName, 0 );
sprintf( szDBGFileName, "%s.DBG", szExeBaseName );
// Open the new .DBG file for writing
g_pOutFile = fopen( szDBGFileName, "wb" );
if ( !g_pOutFile )
{
printf( "Error opening output file\n" );
return FALSE;
}
// Figure out where most of the various CV fields and structures will be
// located in the .DBG file. Saves the values away in global variables...
CalculateCVInfoOffsts();
return TRUE;
}
//============================================================================
// API called once for each public symbol. The passed symbol name and address
// is written as an S_PUB32 record to the sstGlobalPub subsection
//============================================================================
BOOL __stdcall CoClassSymsAddSymbol(
unsigned short section,
unsigned long offset,
PSTR pszSymbolName )
{
g_cbPublicSymbols +=
AddPublicSymbolSymbol32( section, offset, pszSymbolName, g_pOutFile );
return TRUE;
}
//============================================================================
// API called when all the public symbols have been written to the .DBG file.
// What remains is to write the rest of the CV info, the .DBG file header
// stuff, and then clean up.
//============================================================================
BOOL __stdcall CoClassSymsSymbolsFinished( void )
{
DWORD cbCVInfo;
cbCVInfo = WriteRemainingCVInfo( g_pOutFile );
WriteDBGHeader( g_pOutFile, g_liExe.FileHeader );
WriteSectionTable( g_pOutFile, g_liExe.Sections, g_liExe.NumberOfSections );
WriteDbgDirectory( g_pOutFile, g_liExe.FileHeader, cbCVInfo );
fclose( g_pOutFile );
UnMapAndLoad( &g_liExe );
return TRUE;
}
//=============================================================================
// Calculates all the offsets to various places in the CV info that we'll
// initially need. Other offsets can only be calculated later, after all the
// public symbols have been processed and added.
//=============================================================================
void CalculateCVInfoOffsts( void )
{
//
// Calculate where the CV symbol table will begin. This will be after:
// IMAGE_SEPARATE_DEBUG_HEADER
// Section table (IMAGE_SECTION_HEADER * nSections)
// IMAGE_DEBUG_DIRECTORY
//
g_CVInfoOffset = sizeof(IMAGE_SEPARATE_DEBUG_HEADER) +
+ (g_liExe.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))
+ sizeof(IMAGE_DEBUG_DIRECTORY);
unsigned long cDir = 3; // An sstModule, an sstGlobalPub and a sstSegMap
// Calculate where the sstModule subsection will go. This is after:
// 'NB09'
// Offset to subsection directory
// Subsection directory header
// Subsection directory entries (3 subsections)
g_lfoSstModule = sizeof(OMFSignature) + sizeof(OMFDirHeader) +
(cDir * sizeof(OMFDirEntry));
// Calculate the size of the sstModule, which is dependent on the number
// of sections in the executable
g_cbSstModule= offsetof(OMFModule,SegInfo)
+ (g_liExe.NumberOfSections * sizeof(OMFSegDesc))
+ sizeof(g_szModuleName);
// Calculate where the sstGlobalPub subsection will go. This is
// immediately after the sstModule subsection
g_lfoGlobalPub = g_lfoSstModule + g_cbSstModule;
}
//=============================================================================
// Writes a public symbol (S_PUB32) record to the next spot in the
// sstGlobalPub area. Called by CoClassSymsAddSymbol, which is the exported
// API for adding public symbols.
//=============================================================================
unsigned AddPublicSymbolSymbol32( unsigned short seg, unsigned long offset,
char * pszSymbol, FILE * pFile )
{
BYTE buffer[512];
PUBSYM32* pPubSym32 = (PUBSYM32*)buffer;
DWORD cbSymbol = lstrlen( pszSymbol );
DWORD realRecordLen = sizeof(PUBSYM32) + cbSymbol;
pPubSym32->reclen = (unsigned short)(realRecordLen - 2);
pPubSym32->rectyp = S_PUB32;
pPubSym32->off = offset;
pPubSym32->seg = seg;
pPubSym32->typind = 0;
pPubSym32->name[0] = (unsigned char)cbSymbol;
lstrcpy( (PSTR)&pPubSym32->name[1], pszSymbol );
fseek( pFile,
g_CVInfoOffset + g_lfoGlobalPub + g_cbPublicSymbols,
SEEK_SET );
fwrite( pPubSym32, realRecordLen, 1, pFile );
return realRecordLen;
}
//=============================================================================
// After all the public symbol (S_PUB32) records have been written, this
// routine writes the header at the beginning of the sstGlobalPub area
//=============================================================================
void WritePublicSymbolsHeader( FILE * pFile )
{
OMFSymHash omfSymHash;
omfSymHash.cbSymbol = g_cbPublicSymbols - sizeof(OMFSymHash);
omfSymHash.symhash = 0; // No symbol or address hash tables...
omfSymHash.addrhash = 0;
omfSymHash.cbHSym = 0;
omfSymHash.cbHAddr = 0;
// Seek to begining of sstGlobalPub to write OMFSymHash record
fseek( pFile, g_CVInfoOffset + g_lfoGlobalPub, SEEK_SET );
fwrite( &omfSymHash, sizeof(omfSymHash), 1, pFile );
}
//=============================================================================
// Writes all CV info, except for the actual public symbols (S_PUB32)
// records. It writes the CV subsection directory, as well as the sstModule
// and sstSegMap blobs.
//=============================================================================
unsigned WriteRemainingCVInfo( FILE * pFile )
{
unsigned long cDir = 3; // An sstModule, an sstGlobalPub and a sstSegMap
unsigned long cbSegMap;
unsigned long lfoSegMap;
lfoSegMap = g_lfoGlobalPub + g_cbPublicSymbols;
WritePublicSymbolsHeader( g_pOutFile );
WriteSstModule( pFile );
cbSegMap = WriteSegMap( pFile, g_CVInfoOffset + lfoSegMap,
g_liExe.Sections, g_liExe.NumberOfSections );
// Restore current position to write rest of dir entries
fseek( pFile, g_CVInfoOffset, SEEK_SET );
//
// Write 'NB09' signature and offset to subsection directory
//
OMFSignature omfsig = { {'N','B','0','9'}, sizeof(omfsig) };
fwrite( &omfsig, sizeof(omfsig), 1, pFile );
//
// Write subsection directory header
//
OMFDirHeader omfdirhdr;
omfdirhdr.cbDirHeader = sizeof(omfdirhdr);
omfdirhdr.cbDirEntry = sizeof(OMFDirEntry);
omfdirhdr.cDir = cDir;
omfdirhdr.lfoNextDir = 0;
omfdirhdr.flags = 0;
fwrite( &omfdirhdr, sizeof(omfdirhdr), 1, pFile );
//
// Write subsection directory entry for the sstModule
//
OMFDirEntry omfdirentry;
omfdirentry.SubSection = sstModule;
omfdirentry.iMod = 1;
omfdirentry.lfo = g_lfoSstModule;
omfdirentry.cb = g_cbSstModule;
fwrite( &omfdirentry, sizeof(omfdirentry), 1, pFile );
//
// Write subsection directory entry for the sstGlobalPub
//
omfdirentry.SubSection = sstGlobalPub;
omfdirentry.iMod = 0xFFFF;
omfdirentry.lfo = g_lfoGlobalPub;
omfdirentry.cb = g_cbPublicSymbols;
fwrite( &omfdirentry, sizeof(omfdirentry), 1, pFile );
//
// Write subsection directory entry for the sstSegMap
//
omfdirentry.SubSection = sstSegMap;
omfdirentry.iMod = 0xFFFF;
omfdirentry.lfo = lfoSegMap;
omfdirentry.cb = cbSegMap;
fwrite( &omfdirentry, sizeof(omfdirentry), 1, pFile );
return lfoSegMap + cbSegMap;
}
//=============================================================================
// Writes the sstModule info, which precedes the sstGlobalPub info in the file
//=============================================================================
BOOL WriteSstModule( FILE *pFile )
{
// Seek to place to write sstModule at
fseek( pFile, g_CVInfoOffset + g_lfoSstModule, SEEK_SET );
//
// Initialize and write the sstModule header
//
OMFModule omfmodule;
omfmodule.ovlNumber = 0;
omfmodule.iLib = 0;
omfmodule.cSeg = (unsigned short)g_liExe.NumberOfSections;
omfmodule.Style[0] = 'C';
omfmodule.Style[1] = 'V';
fwrite( &omfmodule, offsetof(OMFModule,SegInfo), 1, pFile );
//
// Initialize and write an OMFSegDesc for each executable section
//
for ( unsigned i = 0; i < g_liExe.NumberOfSections; i++ )
{
OMFSegDesc omfsegdesc;
omfsegdesc.Seg = i+1;
omfsegdesc.pad = 0;
omfsegdesc.Off = 0;
omfsegdesc.cbSeg = g_liExe.Sections[i].Misc.VirtualSize;
fwrite( &omfsegdesc, sizeof(omfsegdesc), 1, pFile );
}
// Subsection ends with the name of the module
fwrite( g_szModuleName, sizeof(g_szModuleName), 1, pFile );
return TRUE;
}
//=============================================================================
// Writes the sstSegMap info, which follows the sstGlobalPub info in the file
//=============================================================================
unsigned WriteSegMap( FILE * pFile, unsigned long fileOffset,
PIMAGE_SECTION_HEADER pSectionHdr, ULONG nSections )
{
// Seek to place to write sstSegMap at
fseek( pFile, fileOffset, SEEK_SET );
//
// Initialize and write the sstSegMap header
//
OMFSegMap omfSegMap = {(unsigned short)nSections,(unsigned short)nSections};
fwrite( &omfSegMap, sizeof(OMFSegMap), 1, pFile );
//
// Initialize and write an OMFSegMapDesc for each executable section
//
for ( unsigned i = 1; i <= nSections; i++ )
{
OMFSegMapDesc omfSegMapDesc;
omfSegMapDesc.flags = 0;
omfSegMapDesc.ovl = 0;
omfSegMapDesc.group = 0;
omfSegMapDesc.frame = i;
omfSegMapDesc.iSegName = 0xFFFF;
omfSegMapDesc.iClassName = 0xFFFF;
omfSegMapDesc.offset = 0;
omfSegMapDesc.cbSeg = pSectionHdr[i-1].Misc.VirtualSize;
fwrite( &omfSegMapDesc, sizeof(OMFSegMapDesc), 1, pFile );
}
return sizeof(OMFSegMap) + (sizeof(OMFSegMapDesc) * nSections);
}
//============================================================================
// Writes the 'DI' (IMAGE_SEPARATE_DEBUG_HEADER) header at the very beginning
// of the file
//============================================================================
void WriteDBGHeader( FILE * pFile, PIMAGE_NT_HEADERS pNtHdr )
{
fseek( pFile, 0, SEEK_SET );
IMAGE_SEPARATE_DEBUG_HEADER isdh;
isdh.Signature = IMAGE_SEPARATE_DEBUG_SIGNATURE;
isdh.Flags = 0;
isdh.Machine = pNtHdr->FileHeader.Machine;
isdh.Characteristics = pNtHdr->FileHeader.Characteristics;
isdh.TimeDateStamp = pNtHdr->FileHeader.TimeDateStamp;
isdh.CheckSum = pNtHdr->OptionalHeader.CheckSum;
isdh.ImageBase = pNtHdr->OptionalHeader.ImageBase;
isdh.SizeOfImage = pNtHdr->OptionalHeader.SizeOfImage;
isdh.NumberOfSections = pNtHdr->FileHeader.NumberOfSections;
isdh.ExportedNamesSize = 0;
isdh.DebugDirectorySize = sizeof(IMAGE_DEBUG_DIRECTORY);
isdh.SectionAlignment = pNtHdr->OptionalHeader.SectionAlignment;
fwrite( &isdh, sizeof(isdh), 1, pFile );
}
//============================================================================
// Writes the section table that immediately follows the 'DI' header
//============================================================================
void WriteSectionTable( FILE * pFile, PIMAGE_SECTION_HEADER pSectionHdr,
ULONG nSections )
{
// Basically, a block copy of the section table out of the executable image
fwrite( pSectionHdr, sizeof(IMAGE_SECTION_HEADER), nSections, pFile );
}
//============================================================================
// Writes the debug directory that follows the section table
//============================================================================
void WriteDbgDirectory(FILE * pFile, PIMAGE_NT_HEADERS pNtHdr, DWORD cbCVInfo)
{
IMAGE_DEBUG_DIRECTORY idd;
idd.Characteristics = 0;
idd.TimeDateStamp = pNtHdr->FileHeader.TimeDateStamp;
idd.MajorVersion = 0;
idd.MinorVersion = 0;
idd.Type = IMAGE_DEBUG_TYPE_CODEVIEW;
idd.SizeOfData = cbCVInfo;
idd.AddressOfRawData = 0;
idd.PointerToRawData = g_CVInfoOffset;
fwrite( &idd, sizeof(idd), 1, pFile );
}