Copyright © Microsoft Corporation. This document is an archived reproduction of a version originally published by Microsoft. It may have slight formatting modifications for consistency and to improve readability. |
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 ); }