| /* |
| * 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 code 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 + 1, sizeof (char)); |
| 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; |
| } |
| |
| |