2 * Functions to read parts of a .DBG file into their respective struct's
4 * Copyright 2000 John R. Sheets
10 * IMAGE_SEPARATE_DEBUG_HEADER
11 * IMAGE_SECTION_HEADER[]
12 * IMAGE_DEBUG_DIRECTORY[]
14 * debug data (typical example)
15 * - IMAGE_DEBUG_TYPE_MISC
16 * - IMAGE_DEBUG_TYPE_FPO
17 * - IMAGE_DEBUG_TYPE_CODEVIEW
25 * (hdr) IMAGE_SEPARATE_DEBUG_HEADER - .DBG-specific file header; holds info that
26 * applies to the file as a whole, including # of COFF sections, file offsets, etc.
27 * (hdr) IMAGE_SECTION_HEADER - list of COFF sections copied verbatim from .EXE;
28 * although this directory contains file offsets, these offsets are meaningless
29 * in the context of the .DBG file, because only the section headers are copied
30 * to the .DBG file...not the binary data it points to.
31 * (hdr) IMAGE_DEBUG_DIRECTORY - list of different formats of debug info contained in file
32 * (see IMAGE_DEBUG_TYPE_* descriptions below); tells where each section starts
33 * (hdr) OMFSignature (CV) - Contains "NBxx" signature, plus file offset telling how far
34 * into the IMAGE_DEBUG_TYPE_CODEVIEW section the OMFDirHeader and OMFDirEntry's sit
35 * (data) IMAGE_DEBUG_TYPE_MISC - usually holds name of original .EXE file
36 * (data) IMAGE_DEBUG_TYPE_FPO - Frame Pointer Optimization data; used for dealing with
37 * optimized stack frames (optional)
38 * (data) IMAGE_DEBUG_TYPE_CODEVIEW - *** THE GOOD STUFF ***
39 * This block of data contains all the symbol tables, line number info, etc.,
40 * that the Visual C++ debugger needs.
41 * (hdr) OMFDirHeader (CV) -
42 * (hdr) OMFDirEntry (CV) - list of subsections within CodeView debug data section
46 * The .DBG file typically has three arrays of directory entries, which tell
47 * the OS or debugger where in the file to look for the actual data
49 * IMAGE_SECTION_HEADER - number of entries determined by:
50 * (IMAGE_SEPARATE_DEBUG_HEADER.NumberOfSections)
52 * IMAGE_DEBUG_DIRECTORY - number of entries determined by:
53 * (IMAGE_SEPARATE_DEBUG_HEADER.DebugDirectorySize / sizeof (IMAGE_DEBUG_DIRECTORY))
55 * OMFDirEntry - number of entries determined by:
66 extern DWORD g_dwStartOfCodeView
;
69 * Extract a generic block of data from debugfile (pass in fileoffset == -1
70 * to avoid the fseek()).
72 int ReadChunk (FILE *debugfile
, void *dest
, int length
, int fileoffset
)
77 fseek (debugfile
, fileoffset
, SEEK_SET
);
79 bytes_read
= fread (dest
, 1, length
, debugfile
);
80 if (bytes_read
< length
)
82 printf ("ERROR: Only able to read %d bytes of %d-byte chunk!\n",
91 * Scan the next two bytes of a file, and see if they correspond to a file
92 * header signature. Don't forget to put the file pointer back where we
95 CVHeaderType
GetHeaderType (FILE *debugfile
)
98 CVHeaderType ret
= CV_NONE
;
100 int oldpos
= ftell (debugfile
);
103 printf (" *** Current file position = %lx\n", ftell (debugfile
));
106 if (!ReadChunk (debugfile
, &hdrtype
, sizeof (WORD
), -1))
108 fseek (debugfile
, oldpos
, SEEK_SET
);
112 if (hdrtype
== 0x5A4D) /* "MZ" */
114 else if (hdrtype
== 0x4550) /* "PE" */
116 else if (hdrtype
== 0x4944) /* "DI" */
119 fseek (debugfile
, oldpos
, SEEK_SET
);
122 printf ("Returning header type = %d [0x%x]\n", ret
, hdrtype
);
123 printf (" *** Current file position = %lx\n", ftell (debugfile
));
130 * Extract the DOS file headers from an executable
132 int ReadDOSFileHeader (FILE *debugfile
, IMAGE_DOS_HEADER
*doshdr
)
136 bytes_read
= fread (doshdr
, 1, sizeof (IMAGE_DOS_HEADER
), debugfile
);
137 if (bytes_read
< sizeof (IMAGE_DOS_HEADER
))
139 printf ("ERROR: Only able to read %d bytes of %d-byte DOS file header!\n",
140 bytes_read
, sizeof (IMAGE_DOS_HEADER
));
144 /* Skip over stub data, if present
146 if (doshdr
->e_lfanew
)
147 fseek (debugfile
, doshdr
->e_lfanew
, SEEK_SET
);
153 * Extract the DOS and NT file headers from an executable
155 int ReadPEFileHeader (FILE *debugfile
, IMAGE_NT_HEADERS
*nthdr
)
159 bytes_read
= fread (nthdr
, 1, sizeof (IMAGE_NT_HEADERS
), debugfile
);
160 if (bytes_read
< sizeof (IMAGE_NT_HEADERS
))
162 printf ("ERROR: Only able to read %d bytes of %d-byte NT file header!\n",
163 bytes_read
, sizeof (IMAGE_NT_HEADERS
));
171 * Extract the DBG file header from debugfile
173 int ReadDBGFileHeader (FILE *debugfile
, IMAGE_SEPARATE_DEBUG_HEADER
*dbghdr
)
177 bytes_read
= fread (dbghdr
, 1, sizeof (IMAGE_SEPARATE_DEBUG_HEADER
), debugfile
);
178 if (bytes_read
< sizeof (IMAGE_SEPARATE_DEBUG_HEADER
))
180 printf ("ERROR: Only able to read %d bytes of %d-byte DBG file header!\n",
181 bytes_read
, sizeof (IMAGE_SEPARATE_DEBUG_HEADER
));
189 * Extract all of the file's COFF section headers into an array of
190 * IMAGE_SECTION_HEADER's. These COFF sections don't really apply to
191 * the .DBG file directly (they contain file offsets into the .EXE file
192 * which don't correspond to anything in the .DBG file). They are
193 * copied verbatim into this .DBG file to help make the debugging process
194 * more robust. By referencing these COFF section headers, the debugger
195 * can still function in the absence of the original .EXE file!
197 * NOTE: Do not bother pre-allocating memory. This function will
198 * allocate it for you. Don't forget to free() it when you're done,
201 int ReadSectionHeaders (FILE *debugfile
, int numsects
, IMAGE_SECTION_HEADER
**secthdrs
)
205 /* Need a double-pointer so we can change the destination of the pointer
206 * and return the new allocation back to the caller.
208 *secthdrs
= calloc (numsects
, sizeof (IMAGE_SECTION_HEADER
));
209 bytes_read
= fread (*secthdrs
, sizeof (IMAGE_SECTION_HEADER
), numsects
, debugfile
);
210 if (bytes_read
< numsects
)
212 printf ("ERROR while reading COFF headers: Only able to "
213 "read %d headers out of %d desired!\n",
214 bytes_read
, sizeof (IMAGE_SECTION_HEADER
));
222 * Load in the debug directory table. This directory describes the various
223 * blocks of debug data that reside at the end of the file (after the COFF
224 * sections), including FPO data, COFF-style debug info, and the CodeView
225 * we are *really* after.
227 int ReadDebugDir (FILE *debugfile
, int numdirs
, IMAGE_DEBUG_DIRECTORY
**debugdirs
)
231 /* Need a double-pointer so we can change the destination of the pointer
232 * and return the new allocation back to the caller.
234 *debugdirs
= calloc (numdirs
, sizeof (IMAGE_DEBUG_DIRECTORY
));
235 bytes_read
= fread (*debugdirs
, sizeof (IMAGE_DEBUG_DIRECTORY
), numdirs
, debugfile
);
236 if (bytes_read
< numdirs
)
238 printf ("ERROR while reading Debug Directory: Only able to "
239 "read %d headers out of %d desired!\n",
240 bytes_read
, numdirs
);
248 * Load in the CodeView-style headers inside the CodeView debug section.
249 * The 'sig' and 'dirhdr' parameters must point to already-allocated
252 int ReadCodeViewHeader (FILE *debugfile
, OMFSignature
*sig
, OMFDirHeader
*dirhdr
)
256 bytes_read
= fread (sig
, 1, sizeof (OMFSignature
), debugfile
);
257 if (bytes_read
< sizeof (OMFSignature
))
259 printf ("ERROR while reading CodeView Header Signature: Only "
260 "able to read %d bytes out of %d desired!\n",
261 bytes_read
, sizeof (OMFSignature
));
265 /* Must perform a massive jump, almost to the end of the file, to find the
266 * CodeView Directory Header (OMFDirHeader), which is immediately followed
267 * by the array of entries (OMFDirEntry). We calculate the jump based on
268 * the beginning of the CodeView debug section (from the CodeView entry in
269 * the IMAGE_DEBUG_DIRECTORY array), with the added offset from OMGSignature.
271 fseek (debugfile
, sig
->filepos
+ g_dwStartOfCodeView
, SEEK_SET
);
272 bytes_read
= fread (dirhdr
, 1, sizeof (OMFDirHeader
), debugfile
);
273 if (bytes_read
< sizeof (OMFDirHeader
))
275 printf ("ERROR while reading CodeView Directory Header: Only "
276 "able to read %d bytes out of %d desired!\n",
277 bytes_read
, sizeof (OMFDirHeader
));
281 /* File pointer is now at first OMGDirEntry, so we can begin reading those now,
282 * with an immediate call to ReadCodeViewDirectory ().
289 * Load in the CodeView directory entries, which each point to a CodeView
290 * subsection (e.g. sstModules, sstGlobalPub). The number of entries in
291 * this table is determined by OMFDirEntry.cDir.
293 * Strangely enough, this particular section comes immediately *after*
294 * the debug data (as opposed to immediately *before* the data as is the
295 * standard with the COFF headers).
297 int ReadCodeViewDirectory (FILE *debugfile
, int entrynum
, OMFDirEntry
**entries
)
301 /* Need a double-pointer so we can change the destination of the pointer
302 * and return the new allocation back to the caller.
304 /* printf ("Allocating space for %d entries\n", entrynum); */
305 *entries
= calloc (entrynum
, sizeof (OMFDirEntry
));
306 /* printf ("Allocated memory at %p (%p)\n", *entries, entries); */
307 bytes_read
= fread (*entries
, sizeof (OMFDirEntry
), entrynum
, debugfile
);
308 if (bytes_read
< entrynum
)
310 printf ("ERROR while reading CodeView Debug Directories: Only "
311 "able to read %d entries out of %d desired!\n",
312 bytes_read
, entrynum
);
320 * Load in the data contents of all CodeView sstModule sub-sections in the file (likely a
321 * large array, as there is one sub-section for every code module... > 100 modules is normal).
322 * 'entrynum' should hold the total number of CV sub-sections, not the number of sstModule
323 * subsections. The function will ignore anything that isn't a sstModule.
325 * NOTE: 'debugfile' must already be pointing to the correct location.
327 int ReadModuleData (FILE *debugfile
, int entrynum
, OMFDirEntry
*entries
,
328 int *module_count
, OMFModuleFull
**modules
)
333 OMFSegDesc
*segarray
;
335 OMFModuleFull
*module
;
338 /* How much of the OMFModuleFull struct can we pull directly from the file?
339 * (Kind of a hack, but not much else we can do...the 'SegInfo' and 'Name'
340 * fields will hold memory pointers, not the actual data from the file.)
342 int module_bytes
= (sizeof (unsigned short) * 3) + (sizeof (char) * 2);
347 /* Find out how many sstModule sub-sections we have in 'entries'
350 for (i
= 0; i
< entrynum
; i
++)
352 if (entries
[i
].SubSection
== sstModule
)
356 /* Need a double-pointer so we can change the destination of the pointer
357 * and return the new allocation back to the caller.
359 *modules
= calloc (*module_count
, sizeof (OMFModuleFull
));
360 for (i
= 0; i
< *module_count
; i
++)
362 /* Convenience pointer to current module
364 module
= &(*modules
)[i
];
366 /* Must extract each OMFModuleFull separately from file, because the 'SegInfo'
367 * and 'Name' fields also require separate allocations; the data for these
368 * fields is interspersed in the file, between OMFModuleFull blocks.
370 bytes_read
= fread (module
, sizeof (char), module_bytes
, debugfile
);
371 if (bytes_read
< module_bytes
)
373 printf ("ERROR while reading CodeView Module Sub-section Data: "
374 "Only able to read %d bytes from entry %d!\n",
379 /* Allocate space for, and grab the entire 'SegInfo' array.
381 segnum
= module
->cSeg
;
382 segarray
= calloc (segnum
, sizeof (OMFSegDesc
));
384 bytes_read
= fread (segarray
, sizeof (OMFSegDesc
), segnum
, debugfile
);
385 if (bytes_read
< segnum
)
387 printf ("ERROR while reading CodeView Module SegInfo Data: "
388 "Only able to read %d segments from module %d!\n",
392 module
->SegInfo
= segarray
;
394 /* Allocate space for the (length-prefixed) 'Name' field.
396 bytes_read
= fread (&namelen
, sizeof (char), 1, debugfile
);
399 printf ("ERROR while reading CodeView Module Name length!\n");
403 /* Read 'Name' field from file. 'Name' must be aligned on a 4-byte
404 * boundary, so we must do a little extra math on the string length.
405 * (NOTE: Must include namelen byte in total padding length, too.)
407 pad
= ((namelen
+ 1) % 4);
409 namelen
+= (4 - pad
);
411 module
->Name
= calloc (namelen
+ 1, sizeof (char));
412 bytes_read
= fread (module
->Name
, sizeof (char), namelen
, debugfile
);
413 if (bytes_read
< namelen
)
415 printf ("ERROR while reading CodeView Module Name: "
416 "Only able to read %d chars from module %d!\n",
420 /* printf ("%s\n", module->Name); */
424 printf ("Done reading %d modules\n", *module_count
);