| /* | |
| * Made after: | |
| * CVDump - Parses through a Visual Studio .DBG file in CodeView 4 format | |
| * and dumps the info to STDOUT in a human-readable format | |
| * | |
| * Copyright 2000 John R. Sheets | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <stdlib.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #ifdef HAVE_UNISTD_H | |
| # include <unistd.h> | |
| #endif | |
| #include <time.h> | |
| #ifdef HAVE_SYS_TYPES_H | |
| # include <sys/types.h> | |
| #endif | |
| #ifdef HAVE_SYS_STAT_H | |
| # include <sys/stat.h> | |
| #endif | |
| #ifdef HAVE_SYS_MMAN_H | |
| #include <sys/mman.h> | |
| #endif | |
| #include <fcntl.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winedump.h" | |
| #include "wine/mscvpdb.h" | |
| /* | |
| * .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) | |
| */ | |
| extern const IMAGE_NT_HEADERS* PE_nt_headers; | |
| static const void* cv_base /* = 0 */; | |
| static int dump_cv_sst_module(const OMFDirEntry* omfde) | |
| { | |
| const OMFModule* module; | |
| const OMFSegDesc* segDesc; | |
| int i; | |
| module = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFModule)); | |
| if (!module) {printf("Can't get the OMF-Module, aborting\n"); return FALSE;} | |
| printf(" olvNumber: %u\n", module->ovlNumber); | |
| printf(" iLib: %u\n", module->iLib); | |
| printf(" cSeg: %u\n", module->cSeg); | |
| printf(" Style: %c%c\n", module->Style[0], module->Style[1]); | |
| printf(" Name: %.*s\n", | |
| *(const BYTE*)((const char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg), | |
| (const char*)(module + 1) + sizeof(OMFSegDesc) * module->cSeg + 1); | |
| segDesc = PRD(Offset(module + 1), sizeof(OMFSegDesc) * module->cSeg); | |
| if (!segDesc) {printf("Can't get the OMF-SegDesc, aborting\n"); return FALSE;} | |
| for (i = 0; i < module->cSeg; i++) | |
| { | |
| printf (" segment #%2d: offset = [0x%8x], size = [0x%8x]\n", | |
| segDesc->Seg, segDesc->Off, segDesc->cbSeg); | |
| segDesc++; | |
| } | |
| return TRUE; | |
| } | |
| static int dump_cv_sst_global_pub(const OMFDirEntry* omfde) | |
| { | |
| long fileoffset; | |
| const OMFSymHash* header; | |
| const BYTE* symbols; | |
| fileoffset = Offset(cv_base) + omfde->lfo; | |
| printf (" GlobalPub section starts at file offset 0x%lx\n", fileoffset); | |
| printf (" Symbol table starts at 0x%lx\n", fileoffset + sizeof (OMFSymHash)); | |
| printf ("\n ----- Begin Symbol Table -----\n"); | |
| header = PRD(fileoffset, sizeof(OMFSymHash)); | |
| if (!header) {printf("Can't get OMF-SymHash, aborting\n");return FALSE;} | |
| symbols = PRD(fileoffset + sizeof(OMFSymHash), header->cbSymbol); | |
| if (!symbols) {printf("Can't OMF-SymHash details, aborting\n"); return FALSE;} | |
| codeview_dump_symbols(symbols, header->cbSymbol); | |
| return TRUE; | |
| } | |
| static int dump_cv_sst_global_sym(const OMFDirEntry* omfde) | |
| { | |
| /*** NOT YET IMPLEMENTED ***/ | |
| return TRUE; | |
| } | |
| static int dump_cv_sst_static_sym(const OMFDirEntry* omfde) | |
| { | |
| /*** NOT YET IMPLEMENTED ***/ | |
| return TRUE; | |
| } | |
| static int dump_cv_sst_libraries(const OMFDirEntry* omfde) | |
| { | |
| /*** NOT YET IMPLEMENTED ***/ | |
| return TRUE; | |
| } | |
| static int dump_cv_sst_global_types(const OMFDirEntry* omfde) | |
| { | |
| long fileoffset; | |
| const OMFGlobalTypes*types; | |
| const BYTE* data; | |
| unsigned sz; | |
| fileoffset = Offset(cv_base) + omfde->lfo; | |
| printf (" GlobalTypes section starts at file offset 0x%lx\n", fileoffset); | |
| printf ("\n ----- Begin Global Types Table -----\n"); | |
| types = PRD(fileoffset, sizeof(OMFGlobalTypes)); | |
| if (!types) {printf("Can't get OMF-GlobalTypes, aborting\n");return FALSE;} | |
| sz = omfde->cb - sizeof(OMFGlobalTypes) - sizeof(DWORD) * types->cTypes; | |
| data = PRD(fileoffset + sizeof(OMFGlobalTypes) + sizeof(DWORD) * types->cTypes, sz); | |
| if (!data) {printf("Can't OMF-SymHash details, aborting\n"); return FALSE;} | |
| /* doc says: | |
| * - for NB07 & NB08 (that we don't support yet), offsets are from types | |
| * - for NB09, offsets are from data | |
| * For now, we only support the latter | |
| */ | |
| codeview_dump_types_from_offsets(data, (const DWORD*)(types + 1), types->cTypes); | |
| return TRUE; | |
| } | |
| static int dump_cv_sst_seg_map(const OMFDirEntry* omfde) | |
| { | |
| const OMFSegMap* segMap; | |
| const OMFSegMapDesc* segMapDesc; | |
| int i; | |
| segMap = PRD(Offset(cv_base) + omfde->lfo, sizeof(OMFSegMap)); | |
| if (!segMap) {printf("Can't get SegMap, aborting\n");return FALSE;} | |
| printf(" cSeg: %u\n", segMap->cSeg); | |
| printf(" cSegLog: %u\n", segMap->cSegLog); | |
| segMapDesc = PRD(Offset(segMap + 1), segMap->cSeg * sizeof(OMFSegDesc)); | |
| if (!segMapDesc) {printf("Can't get SegDescr array, aborting\n");return FALSE;} | |
| for (i = 0; i < segMap->cSeg; i++) | |
| { | |
| printf(" SegDescr #%2d\n", i + 1); | |
| printf(" flags: %04X\n", segMapDesc[i].flags); | |
| printf(" ovl: %u\n", segMapDesc[i].ovl); | |
| printf(" group: %u\n", segMapDesc[i].group); | |
| printf(" frame: %u\n", segMapDesc[i].frame); | |
| printf(" iSegName: %u\n", segMapDesc[i].iSegName); | |
| printf(" iClassName: %u\n", segMapDesc[i].iClassName); | |
| printf(" offset: %lu\n", segMapDesc[i].offset); | |
| printf(" cbSeg: %lu\n", segMapDesc[i].cbSeg); | |
| } | |
| return TRUE; | |
| } | |
| static int dump_cv_sst_file_index(const OMFDirEntry* omfde) | |
| { | |
| /*** NOT YET IMPLEMENTED ***/ | |
| return TRUE; | |
| } | |
| static int dump_cv_sst_src_module(const OMFDirEntry* omfde) | |
| { | |
| int i, j; | |
| const BYTE* rawdata; | |
| const unsigned long* seg_info_dw; | |
| const unsigned short* seg_info_w; | |
| unsigned ofs; | |
| const OMFSourceModule* sourceModule; | |
| const OMFSourceFile* sourceFile; | |
| const OMFSourceLine* sourceLine; | |
| rawdata = PRD(Offset(cv_base) + omfde->lfo, omfde->cb); | |
| if (!rawdata) {printf("Can't get srcModule subsection details, aborting\n");return FALSE;} | |
| /* FIXME: check ptr validity */ | |
| sourceModule = (const void*)rawdata; | |
| printf (" Module table: Found %d file(s) and %d segment(s)\n", | |
| sourceModule->cFile, sourceModule->cSeg); | |
| for (i = 0; i < sourceModule->cFile; i++) | |
| { | |
| printf (" File #%2d begins at an offset of 0x%lx in this section\n", | |
| i + 1, sourceModule->baseSrcFile[i]); | |
| } | |
| /* FIXME: check ptr validity */ | |
| seg_info_dw = (const void*)((const char*)(sourceModule + 1) + | |
| sizeof(unsigned long) * (sourceModule->cFile - 1)); | |
| seg_info_w = (const unsigned short*)(&seg_info_dw[sourceModule->cSeg * 2]); | |
| for (i = 0; i < sourceModule->cSeg; i++) | |
| { | |
| printf (" Segment #%2d start = 0x%lx, end = 0x%lx, seg index = %u\n", | |
| i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1], | |
| seg_info_w[i]); | |
| } | |
| ofs = sizeof(OMFSourceModule) + sizeof(unsigned long) * (sourceModule->cFile - 1) + | |
| sourceModule->cSeg * (2 * sizeof(unsigned long) + sizeof(unsigned short)); | |
| ofs = (ofs + 3) & ~3; | |
| /* the OMFSourceFile is quite unpleasant to use: | |
| * we have first: | |
| * unsigned short number of segments | |
| * unsigned short reserved | |
| * unsigned long baseSrcLn[# segments] | |
| * unsigned long offset[2 * #segments] | |
| * odd indices are start offsets | |
| * even indices are end offsets | |
| * unsigned char string length for file name | |
| * char file name (length is previous field) | |
| */ | |
| /* FIXME: check ptr validity */ | |
| sourceFile = (const void*)(rawdata + ofs); | |
| seg_info_dw = (const void*)((const char*)sourceFile + 2 * sizeof(unsigned short) + | |
| sourceFile->cSeg * sizeof(unsigned long)); | |
| ofs += 2 * sizeof(unsigned short) + 3 * sourceFile->cSeg * sizeof(unsigned long); | |
| printf(" File table: %.*s\n", | |
| *(const BYTE*)((const char*)sourceModule + ofs), (const char*)sourceModule + ofs + 1); | |
| for (i = 0; i < sourceFile->cSeg; i++) | |
| { | |
| printf (" Segment #%2d start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", | |
| i + 1, seg_info_dw[i * 2], seg_info_dw[(i * 2) + 1], sourceFile->baseSrcLn[i]); | |
| } | |
| /* add file name length */ | |
| ofs += *(const BYTE*)((const char*)sourceModule + ofs) + 1; | |
| ofs = (ofs + 3) & ~3; | |
| for (i = 0; i < sourceModule->cSeg; i++) | |
| { | |
| sourceLine = (const void*)(rawdata + ofs); | |
| seg_info_dw = (const void*)((const char*)sourceLine + 2 * sizeof(unsigned short)); | |
| seg_info_w = (const void*)(&seg_info_dw[sourceLine->cLnOff]); | |
| printf (" Line table #%2d: Found %d line numbers for segment index %d\n", | |
| i, sourceLine->cLnOff, sourceLine->Seg); | |
| for (j = 0; j < sourceLine->cLnOff; j++) | |
| { | |
| printf (" Pair #%2d: offset = [0x%8lx], linenumber = %d\n", | |
| j + 1, seg_info_dw[j], seg_info_w[j]); | |
| } | |
| ofs += 2 * sizeof(unsigned short) + | |
| sourceLine->cLnOff * (sizeof(unsigned long) + sizeof(unsigned short)); | |
| ofs = (ofs + 3) & ~3; | |
| } | |
| return TRUE; | |
| } | |
| static int dump_cv_sst_align_sym(const OMFDirEntry* omfde) | |
| { | |
| const char* rawdata = PRD(Offset(cv_base) + omfde->lfo, omfde->cb); | |
| if (!rawdata) {printf("Can't get srcAlignSym subsection details, aborting\n");return FALSE;} | |
| if (omfde->cb < sizeof(DWORD)) return TRUE; | |
| codeview_dump_symbols(rawdata + sizeof(DWORD), omfde->cb - sizeof(DWORD)); | |
| return TRUE; | |
| } | |
| static void dump_codeview_all_modules(const OMFDirHeader *omfdh) | |
| { | |
| unsigned i; | |
| const OMFDirEntry* dirEntry; | |
| const char* str; | |
| if (!omfdh || !omfdh->cDir) return; | |
| dirEntry = PRD(Offset(omfdh + 1), omfdh->cDir * sizeof(OMFDirEntry)); | |
| if (!dirEntry) {printf("Can't read DirEntry array, aborting\n"); return;} | |
| for (i = 0; i < omfdh->cDir; i++) | |
| { | |
| switch (dirEntry[i].SubSection) | |
| { | |
| case sstModule: str = "sstModule"; break; | |
| case sstAlignSym: str = "sstAlignSym"; break; | |
| case sstSrcModule: str = "sstSrcModule"; break; | |
| case sstLibraries: str = "sstLibraries"; break; | |
| case sstGlobalSym: str = "sstGlobalSym"; break; | |
| case sstGlobalPub: str = "sstGlobalPub"; break; | |
| case sstGlobalTypes: str = "sstGlobalTypes"; break; | |
| case sstSegMap: str = "sstSegMap"; break; | |
| case sstFileIndex: str = "sstFileIndex"; break; | |
| case sstStaticSym: str = "sstStaticSym"; break; | |
| default: str = "<undefined>"; break; | |
| } | |
| printf("Module #%2d (%p)\n", i + 1, &dirEntry[i]); | |
| printf(" SubSection: %04X (%s)\n", dirEntry[i].SubSection, str); | |
| printf(" iMod: %d\n", dirEntry[i].iMod); | |
| printf(" lfo: %d\n", dirEntry[i].lfo); | |
| printf(" cb: %u\n", dirEntry[i].cb); | |
| switch (dirEntry[i].SubSection) | |
| { | |
| case sstModule: dump_cv_sst_module(&dirEntry[i]); break; | |
| case sstAlignSym: dump_cv_sst_align_sym(&dirEntry[i]); break; | |
| case sstSrcModule: dump_cv_sst_src_module(&dirEntry[i]); break; | |
| case sstLibraries: dump_cv_sst_libraries(&dirEntry[i]); break; | |
| case sstGlobalSym: dump_cv_sst_global_sym(&dirEntry[i]); break; | |
| case sstGlobalPub: dump_cv_sst_global_pub(&dirEntry[i]); break; | |
| case sstGlobalTypes: dump_cv_sst_global_types(&dirEntry[i]); break; | |
| case sstSegMap: dump_cv_sst_seg_map(&dirEntry[i]); break; | |
| case sstFileIndex: dump_cv_sst_file_index(&dirEntry[i]); break; | |
| case sstStaticSym: dump_cv_sst_static_sym(&dirEntry[i]); break; | |
| default: printf("unsupported type %x\n", dirEntry[i].SubSection); break; | |
| } | |
| printf("\n"); | |
| } | |
| return; | |
| } | |
| static void dump_codeview_headers(unsigned long base, unsigned long len) | |
| { | |
| const OMFDirHeader* dirHeader; | |
| const char* signature; | |
| const OMFDirEntry* dirEntry; | |
| const OMFSignature* sig; | |
| unsigned i; | |
| int modulecount = 0, alignsymcount = 0, srcmodulecount = 0, librariescount = 0; | |
| int globalsymcount = 0, globalpubcount = 0, globaltypescount = 0; | |
| int segmapcount = 0, fileindexcount = 0, staticsymcount = 0; | |
| cv_base = PRD(base, len); | |
| if (!cv_base) {printf("Can't get full debug content, aborting\n");return;} | |
| signature = cv_base; | |
| printf(" CodeView Data\n"); | |
| printf(" Signature: %.4s\n", signature); | |
| if (memcmp(signature, "NB10", 4) == 0) | |
| { | |
| const CODEVIEW_PDB_DATA* pdb_data; | |
| pdb_data = (const void *)cv_base; | |
| printf(" Filepos: 0x%08lX\n", pdb_data->filepos); | |
| printf(" TimeStamp: %08X (%s)\n", | |
| pdb_data->timestamp, get_time_str(pdb_data->timestamp)); | |
| printf(" Age: %08X\n", pdb_data->age); | |
| printf(" Filename: %s\n", pdb_data->name); | |
| return; | |
| } | |
| if (memcmp(signature, "RSDS", 4) == 0) | |
| { | |
| const OMFSignatureRSDS* rsds_data; | |
| rsds_data = (const void *)cv_base; | |
| printf(" Guid: %s\n", get_guid_str(&rsds_data->guid)); | |
| printf(" Age: %08X\n", rsds_data->age); | |
| printf(" Filename: %s\n", rsds_data->name); | |
| return; | |
| } | |
| if (memcmp(signature, "NB09", 4) != 0 && memcmp(signature, "NB11", 4) != 0) | |
| { | |
| printf("Unsupported signature (%.4s), aborting\n", signature); | |
| return; | |
| } | |
| sig = cv_base; | |
| printf(" Filepos: 0x%08lX\n", sig->filepos); | |
| dirHeader = PRD(Offset(cv_base) + sig->filepos, sizeof(OMFDirHeader)); | |
| if (!dirHeader) {printf("Can't get debug header, aborting\n"); return;} | |
| printf(" Size of header: 0x%4X\n", dirHeader->cbDirHeader); | |
| printf(" Size per entry: 0x%4X\n", dirHeader->cbDirEntry); | |
| printf(" # of entries: 0x%8X (%d)\n", dirHeader->cDir, dirHeader->cDir); | |
| printf(" Offset to NextDir: 0x%8X\n", dirHeader->lfoNextDir); | |
| printf(" Flags: 0x%8X\n", dirHeader->flags); | |
| if (!dirHeader->cDir) return; | |
| dirEntry = PRD(Offset(dirHeader + 1), sizeof(OMFDirEntry) * dirHeader->cDir); | |
| if (!dirEntry) {printf("Can't get DirEntry array, aborting\n");return;} | |
| for (i = 0; i < dirHeader->cDir; i++) | |
| { | |
| switch (dirEntry[i].SubSection) | |
| { | |
| case sstModule: modulecount++; break; | |
| case sstAlignSym: alignsymcount++; break; | |
| case sstSrcModule: srcmodulecount++; break; | |
| case sstLibraries: librariescount++; break; | |
| case sstGlobalSym: globalsymcount++; break; | |
| case sstGlobalPub: globalpubcount++; break; | |
| case sstGlobalTypes: globaltypescount++; break; | |
| case sstSegMap: segmapcount++; break; | |
| case sstFileIndex: fileindexcount++; break; | |
| case sstStaticSym: staticsymcount++; break; | |
| } | |
| } | |
| /* This one has to be > 0 | |
| */ | |
| printf ("\nFound: %d sstModule subsections\n", modulecount); | |
| if (alignsymcount > 0) printf (" %d sstAlignSym subsections\n", alignsymcount); | |
| if (srcmodulecount > 0) printf (" %d sstSrcModule subsections\n", srcmodulecount); | |
| if (librariescount > 0) printf (" %d sstLibraries subsections\n", librariescount); | |
| if (globalsymcount > 0) printf (" %d sstGlobalSym subsections\n", globalsymcount); | |
| if (globalpubcount > 0) printf (" %d sstGlobalPub subsections\n", globalpubcount); | |
| if (globaltypescount > 0) printf (" %d sstGlobalTypes subsections\n", globaltypescount); | |
| if (segmapcount > 0) printf (" %d sstSegMap subsections\n", segmapcount); | |
| if (fileindexcount > 0) printf (" %d sstFileIndex subsections\n", fileindexcount); | |
| if (staticsymcount > 0) printf (" %d sstStaticSym subsections\n", staticsymcount); | |
| dump_codeview_all_modules(dirHeader); | |
| } | |
| static const char *get_coff_name( const IMAGE_SYMBOL *coff_sym, const char *coff_strtab ) | |
| { | |
| static char namebuff[9]; | |
| const char* nampnt; | |
| if( coff_sym->N.Name.Short ) | |
| { | |
| memcpy(namebuff, coff_sym->N.ShortName, 8); | |
| namebuff[8] = '\0'; | |
| nampnt = &namebuff[0]; | |
| } | |
| else | |
| { | |
| nampnt = coff_strtab + coff_sym->N.Name.Long; | |
| } | |
| if( nampnt[0] == '_' ) | |
| nampnt++; | |
| return nampnt; | |
| } | |
| static const char* storage_class(BYTE sc) | |
| { | |
| static char tmp[7]; | |
| switch (sc) | |
| { | |
| case IMAGE_SYM_CLASS_STATIC: return "static"; | |
| case IMAGE_SYM_CLASS_EXTERNAL: return "extrnl"; | |
| case IMAGE_SYM_CLASS_LABEL: return "label "; | |
| } | |
| sprintf(tmp, "#%d", sc); | |
| return tmp; | |
| } | |
| void dump_coff_symbol_table(const IMAGE_SYMBOL *coff_symbols, unsigned num_sym, | |
| const IMAGE_SECTION_HEADER *sectHead) | |
| { | |
| const IMAGE_SYMBOL *coff_sym; | |
| const char *coff_strtab = (const char *) (coff_symbols + num_sym); | |
| unsigned int i; | |
| const char *nampnt; | |
| int naux; | |
| printf("\nDebug table: COFF format.\n"); | |
| printf(" ID | seg:offs [ abs ] | Kind | symbol/function name\n"); | |
| for (i=0; i < num_sym; i++) | |
| { | |
| coff_sym = coff_symbols + i; | |
| naux = coff_sym->NumberOfAuxSymbols; | |
| switch (coff_sym->StorageClass) | |
| { | |
| case IMAGE_SYM_CLASS_FILE: | |
| printf("file %s\n", (const char *) (coff_sym + 1)); | |
| break; | |
| case IMAGE_SYM_CLASS_STATIC: | |
| case IMAGE_SYM_CLASS_EXTERNAL: | |
| case IMAGE_SYM_CLASS_LABEL: | |
| if (coff_sym->SectionNumber > 0) | |
| { | |
| DWORD base = sectHead[coff_sym->SectionNumber - 1].VirtualAddress; | |
| nampnt = get_coff_name( coff_sym, coff_strtab ); | |
| printf("%05d | %02d:%08x [%08x] | %s | %s\n", | |
| i, coff_sym->SectionNumber - 1, coff_sym->Value, | |
| base + coff_sym->Value, | |
| storage_class(coff_sym->StorageClass), nampnt); | |
| } | |
| break; | |
| default: | |
| printf("%05d | %s\n", i, storage_class(coff_sym->StorageClass)); | |
| } | |
| /* | |
| * For now, skip past the aux entries. | |
| */ | |
| i += naux; | |
| } | |
| } | |
| void dump_coff(unsigned long coffbase, unsigned long len, const void* pmt) | |
| { | |
| const IMAGE_COFF_SYMBOLS_HEADER *coff = PRD(coffbase, len); | |
| const IMAGE_SYMBOL *coff_symbols = | |
| (const IMAGE_SYMBOL *) ((const char *)coff + coff->LvaToFirstSymbol); | |
| const IMAGE_SECTION_HEADER *sectHead = pmt; | |
| dump_coff_symbol_table(coff_symbols, coff->NumberOfSymbols, sectHead); | |
| } | |
| void dump_codeview(unsigned long base, unsigned long len) | |
| { | |
| dump_codeview_headers(base, len); | |
| } | |
| void dump_frame_pointer_omission(unsigned long base, unsigned long len) | |
| { | |
| const FPO_DATA* fpo; | |
| const FPO_DATA* last; | |
| const char* x; | |
| /* FPO is used to describe nonstandard stack frames */ | |
| printf("Range #loc #pmt Prlg #reg Info\n" | |
| "-----------------+----+----+----+----+------------\n"); | |
| fpo = PRD(base, len); | |
| if (!fpo) {printf("Couldn't get FPO blob\n"); return;} | |
| last = (const FPO_DATA*)((const char*)fpo + len); | |
| while (fpo < last && fpo->ulOffStart) | |
| { | |
| switch (fpo->cbFrame) | |
| { | |
| case FRAME_FPO: x = "FRAME_FPO"; break; | |
| case FRAME_NONFPO: x = "FRAME_NONFPO"; break; | |
| case FRAME_TRAP: x = "FRAME_TRAP"; break; | |
| case FRAME_TSS: x = "case FRAME_TSS"; break; | |
| default: x = NULL; break; | |
| } | |
| printf("%08x-%08x %4u %4u %4u %4u %s%s%s\n", | |
| fpo->ulOffStart, fpo->ulOffStart + fpo->cbProcSize, | |
| fpo->cdwLocals, fpo->cdwParams, fpo->cbProlog, fpo->cbRegs, | |
| x, fpo->fHasSEH ? " SEH" : "", fpo->fUseBP ? " UseBP" : ""); | |
| fpo++; | |
| } | |
| } | |
| struct stab_nlist | |
| { | |
| union | |
| { | |
| char* n_name; | |
| struct stab_nlist* n_next; | |
| long n_strx; | |
| } n_un; | |
| unsigned char n_type; | |
| char n_other; | |
| short n_desc; | |
| unsigned long n_value; | |
| }; | |
| static const char * const stabs_defs[] = { | |
| NULL,NULL,NULL,NULL, /* 00 */ | |
| NULL,NULL,NULL,NULL, /* 08 */ | |
| NULL,NULL,NULL,NULL, /* 10 */ | |
| NULL,NULL,NULL,NULL, /* 18 */ | |
| "GSYM","FNAME","FUN","STSYM", /* 20 */ | |
| "LCSYM","MAIN","ROSYM","PC", /* 28 */ | |
| NULL,"NSYMS","NOMAP",NULL, /* 30 */ | |
| "OBJ",NULL,"OPT",NULL, /* 38 */ | |
| "RSYM","M2C","SLINE","DSLINE", /* 40 */ | |
| "BSLINE","DEFD","FLINE",NULL, /* 48 */ | |
| "EHDECL",NULL,"CATCH",NULL, /* 50 */ | |
| NULL,NULL,NULL,NULL, /* 58 */ | |
| "SSYM","ENDM","SO",NULL, /* 60 */ | |
| NULL,NULL,NULL,NULL, /* 68 */ | |
| NULL,NULL,NULL,NULL, /* 70 */ | |
| NULL,NULL,NULL,NULL, /* 78 */ | |
| "LSYM","BINCL","SOL",NULL, /* 80 */ | |
| NULL,NULL,NULL,NULL, /* 88 */ | |
| NULL,NULL,NULL,NULL, /* 90 */ | |
| NULL,NULL,NULL,NULL, /* 98 */ | |
| "PSYM","EINCL","ENTRY",NULL, /* a0 */ | |
| NULL,NULL,NULL,NULL, /* a8 */ | |
| NULL,NULL,NULL,NULL, /* b0 */ | |
| NULL,NULL,NULL,NULL, /* b8 */ | |
| "LBRAC","EXCL","SCOPE",NULL, /* c0 */ | |
| NULL,NULL,NULL,NULL, /* c8 */ | |
| NULL,NULL,NULL,NULL, /* d0 */ | |
| NULL,NULL,NULL,NULL, /* d8 */ | |
| "RBRAC","BCOMM","ECOMM",NULL, /* e0 */ | |
| "ECOML","WITH",NULL,NULL, /* e8 */ | |
| "NBTEXT","NBDATA","NBBSS","NBSTS", /* f0 */ | |
| "NBLCS",NULL,NULL,NULL /* f8 */ | |
| }; | |
| void dump_stabs(const void* pv_stabs, unsigned szstabs, const char* stabstr, unsigned szstr) | |
| { | |
| int i; | |
| int nstab; | |
| const char* ptr; | |
| char* stabbuff; | |
| unsigned int stabbufflen; | |
| const struct stab_nlist* stab_ptr = pv_stabs; | |
| const char* strs_end; | |
| char n_buffer[16]; | |
| nstab = szstabs / sizeof(struct stab_nlist); | |
| strs_end = stabstr + szstr; | |
| /* | |
| * Allocate a buffer into which we can build stab strings for cases | |
| * where the stab is continued over multiple lines. | |
| */ | |
| stabbufflen = 65536; | |
| stabbuff = malloc(stabbufflen); | |
| stabbuff[0] = '\0'; | |
| printf("#Sym n_type n_othr n_desc n_value n_strx String\n"); | |
| for (i = 0; i < nstab; i++, stab_ptr++) | |
| { | |
| ptr = stabstr + stab_ptr->n_un.n_strx; | |
| if ((ptr > strs_end) || (ptr + strlen(ptr) > strs_end)) | |
| { | |
| ptr = "[[*** bad string ***]]"; | |
| } | |
| else if (ptr[strlen(ptr) - 1] == '\\') | |
| { | |
| /* | |
| * Indicates continuation. Append this to the buffer, and go onto the | |
| * next record. Repeat the process until we find a stab without the | |
| * '/' character, as this indicates we have the whole thing. | |
| */ | |
| unsigned len = strlen(ptr); | |
| if (strlen(stabbuff) + len > stabbufflen) | |
| { | |
| stabbufflen += 65536; | |
| stabbuff = realloc(stabbuff, stabbufflen); | |
| } | |
| strncat(stabbuff, ptr, len - 1); | |
| continue; | |
| } | |
| else if (stabbuff[0] != '\0') | |
| { | |
| strcat(stabbuff, ptr); | |
| ptr = stabbuff; | |
| } | |
| if ((stab_ptr->n_type & 1) || !stabs_defs[stab_ptr->n_type / 2]) | |
| sprintf(n_buffer, "<0x%02x>", stab_ptr->n_type); | |
| else | |
| sprintf(n_buffer, "%-6s", stabs_defs[stab_ptr->n_type / 2]); | |
| printf("%4d %s %-8x % 6d %-8lx %-6lx %s\n", | |
| i, n_buffer, stab_ptr->n_other, stab_ptr->n_desc, stab_ptr->n_value, | |
| stab_ptr->n_un.n_strx, ptr); | |
| } | |
| free(stabbuff); | |
| } |