/*********************************************************************** * Microsoft (R) Debugging Information Dumper * * Copyright (C) Microsoft Corp 1987-1996. All rights reserved. * * File: cvdump.c * * File Comments: * ***********************************************************************/ #include #include #include #include #include #include #include #include #include #include #undef STRICT #define STRICT #define NOMINMAX // windef.h #define NOHELP #define NOPROFILER #define NOSYSPARAMSINFO #define NONLS // winnls.h #define NOSERVICE // winsvc.h #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include "macimage.h" #include "ppcimage.h" #include "version.h" #include "cvinfo.h" #include "cvdef.h" #include "cvexefmt.h" #include "cvdump.h" // Various definitions #include "cvtdef.h" #ifndef IMAGE_FILE_MACHINE_R10000 #define IMAGE_FILE_MACHINE_R10000 0x168 // MIPS little-endian #endif #define IMAGE_NB_SIGNATURE 0x424E #define MAXNAM 256 int cbSecMod; // # bytes in MODULES section ushort exefile; // Executable file handle ushort Sig; // file signature long lfoBase; // file offset of base bool_t fLinearExe; // TRUE if 32 bit exe bool_t fDbgFile; // TRUE if 32 bit DBG file PMOD ModList; // List of module entries WORD rect; // Type of symbol record char vfDspRect; // true ==> display record type char fSym; char fPub; char fTyp; char fSrc; char fMod; char fHdr; char fNum; char fGPSym; char fSTSym; char fStatics; char fRaw; char fMpc; char fSegMap; char fFileInd; int iModToList; // Which modules to list (0==ALL) char f386; // True if 386 executable uchar Signature[4]; // Version signature long lfaBase; // Base address OMFDirEntry Libraries; // sstLibraries directory entry OMFDirEntry GlobalSym; OMFDirEntry GlobalPub; OMFDirEntry StaticSym; OMFDirEntry GlobalTypes; OMFDirEntry MpcDebugInfo; OMFDirEntry SegMap; OMFDirEntry SegName; OMFDirEntry FileIndex; long cbRec; ulong cSST; ulong cSST06; OMFDirHeader DirHead; long dlfaBase; long lfoDir; char PdbFileName[_MAX_PATH]; char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; OMFDirEntry *pDir; void Fatal( char *msg) { fflush(NULL); fprintf(stderr, "CVDUMP : fatal error : %s\n", msg); if (vfDspRect) { fprintf(stderr, " pos: %lx", _tell(exefile)); fprintf(stderr, " Record type: %02x", rect); } putc('\n', stderr); exit(1); } void LongUsage(void) { fputs("Usage: cvdump [-?] [-l] [-m] [-p] [-s] [-t] [-g] [-u] [-M] [-S] [-q] [-x] [-h] file\n", stderr); fputs("\t-l\tSource lines\n", stderr); fputs("\t-m\tModules\n", stderr); fputs("\t-p\tPublics\n", stderr); fputs("\t-s\tSymbols\n", stderr); fputs("\t-t\tTypes\n", stderr); fputs("\t-h\tHeader (section table)\n", stderr); fputs("\t-g\tGlobal Symbols\n", stderr); fputs("\t-MXXX\tXXX = Module number to dump\n", stderr); fputs("\t-S\tDump static symbols only\n", stderr); fputs("\t-q\tMPC debug info for P-code\n", stderr); fputs("\t-x\tSegment Map\n", stderr); fputs("\t-h\tHeader Dump\n", stderr); fputs("\tfile\tExe file to dump\n", stderr); exit(1); } void Usage(void) { fputs("Usage: cvdump [-?] [-l] [-m] [-p] [-s] [-t] [-g] [-u] [-M] [-S] [-q] [-x] [-h] file\n", stderr); exit(1); } void OpenExe(const char *path) { char outpath[_MAX_PATH]; char fname[_MAX_FNAME]; char ext[_MAX_EXT]; // try to open the exe file strcpy(outpath, path); // Build output file by splitting path and rebuilding with // new extension. _splitpath(outpath, drive, dir, fname, ext); if (ext[0] == '\0') { strcpy(ext, ".exe"); } _makepath(outpath, drive, dir, fname, ext); if ((exefile = _open(outpath, O_RDONLY | O_BINARY)) == -1) { perror(path); exit(1); } } /** CheckSignature - check file signature * * Sig = CheckSignature() * * Entry none * * Exit none * * Return SIG02 if exe has NB02 signature * SIG05 if exe has NB05 signature * SIG06 if exe has NB06 signature * aborts if any other signature */ static const enum SIG MapOrdToSig[] = { SIGOBSOLETE, //0 SIGOBSOLETE, //1 SIG02, SIGOBSOLETE, //3 SIGOBSOLETE, //4 SIG05, SIG06, SIG07, SIG08, SIG09, SIG10, }; ushort CheckSignature(void) { enum SIG retval; int sigOrd; if (_read(exefile, Signature, 4) == 4) { if ((Signature[0] != 'N') || (Signature[1] != 'B')) { Fatal("Unknown executable signature"); } sigOrd = (Signature[2] - '0') * 10 + (Signature[3] - '0'); if (sigOrd > 10) { Fatal("Unknown executable signature"); } retval = MapOrdToSig[sigOrd]; if (retval == SIGOBSOLETE) { Fatal("Obsolete executable signature"); } return((ushort) retval); } Fatal("Unknown executable signature"); } /** ReadNB02 - read file with NB02 signature * * */ void ReadNB02(void) { ushort i; DirEntry Dir; // locate directory, read number of entries, allocate space, read // directory entries and sort into ascending module index order if ((_read(exefile, (char *)&lfoDir, sizeof (long)) != sizeof (long)) || (_lseek(exefile, lfoDir + lfoBase, SEEK_SET) == -1L) || (_read(exefile, (char *)&cSST, 2) != 2)) { Fatal("Invalid executable"); } // reformat directory into local memory assert (cSST <= 0xFFFFL); if ((pDir = (OMFDirEntry *) malloc (((ushort)cSST) * sizeof (OMFDirEntry))) == NULL) { Fatal("Out of memory"); } for (i = 0; i < (ushort)cSST; i++) { if (_read(exefile, (char *)&Dir, sizeof (DirEntry)) != sizeof (DirEntry)) { Fatal("Invalid executable"); } pDir[i].SubSection = Dir.SubSectionType; pDir[i].iMod = Dir.ModuleIndex; pDir[i].lfo = Dir.lfoStart; pDir[i].cb = (ulong)Dir.Size; } } /** ReadNB05 - read file with NB05 signature * * */ void ReadNB05(void) { // locate directory, read number of entries, allocate space, read // directory entries and sort into ascending module index order if ((_read(exefile, (char *)&lfoDir, sizeof (long)) != sizeof (long)) || (_lseek(exefile, lfoDir + lfoBase, SEEK_SET) == -1L) || (_read(exefile, (char *)&DirHead, sizeof (OMFDirHeader)) != sizeof (OMFDirHeader))) { Fatal("Invalid executable"); } cSST = DirHead.cDir; // read directory into local memory to sort, then copy to memory buffer assert ( ( cSST * sizeof ( OMFDirEntry ) ) < UINT_MAX ); if ((pDir = (OMFDirEntry *) malloc((size_t) cSST * sizeof (OMFDirEntry))) == NULL) { Fatal("Out of memory"); } if (readfar(exefile, (char *) pDir, (sizeof (OMFDirEntry) * cSST)) != (sizeof (OMFDirEntry) * cSST)) { Fatal("Invalid executable"); } } /** Get module - find or create module entry in list * * pMod = GetModule(iMod, fAdd) * * Entry iMod = module index * fAdd = TRUE if module to be added to list * * Exit new module structure added if iMod not in list * * Returns pointer to module structure */ PMOD GetModule(ushort index, bool_t fAdd) { PMOD new; PMOD prev; PMOD ptr; prev = NULL; ptr = ModList; // while there are entries left in moduleList while (ptr != NULL) { if (ptr->iMod == index) { return ptr; } else if (ptr->iMod > index) { break; } prev = ptr; ptr = ptr->next; } // create a blank ModuleList entry if (fAdd) { if ((new = (PMOD) malloc (sizeof (modlist))) == NULL) { Fatal("Out of memory"); } memset (new, 0, sizeof (modlist)); new->iMod = index; // do sorted list insertion into ModuleList if (prev == NULL) { ModList = new; } else { prev->next = new; } new->next = ptr; return(new); } Fatal("New module added during ilink"); } void GetSSTMOD(PMOD pMod, long lfoStart, ulong cb) { ushort cbName; if (_lseek(exefile, lfoStart, SEEK_SET) == -1L) { Fatal("Error positioning to SSTMODULE entry"); } // M00BUG - this does not handle multiple segments cbRec = cb; if ((pMod->rgdmc = malloc(sizeof(DMC))) == NULL) { Fatal("Out of memory"); } pMod->rgdmc [ 0 ].sa = WGets(); pMod->rgdmc [ 0 ].ra = (ulong)WGets(); pMod->rgdmc [ 0 ].cb = (ulong)WGets(); pMod->dm_iov = WGets(); pMod->dm_ilib = WGets(); pMod->dm_cSeg = WGets(); if ((pMod->ModName = (char *) malloc(1 + (cbName = Gets()))) == NULL) { Fatal("Out of memory"); } // Read the name and terminate it with null GetBytes(pMod->ModName, (size_t) cbName); pMod->ModName[cbName] = '\0'; pMod->style[0] = 'C'; pMod->style[0] = 'V'; } void GetsstModule(PMOD pMod, long lfoStart, ulong cb) { ushort cbName; OMFSegDesc SegDesc; ushort i; if (_lseek(exefile, lfoStart, SEEK_SET) == -1L) { Fatal("Error positioning to sstModule entry"); } cbRec = cb; // read fixed portion of sstModule table pMod->dm_iov = WGets(); pMod->dm_ilib = WGets(); pMod->dm_cSeg = WGets(); GetBytes(pMod->style, sizeof (pMod->style)); // read per segment information if (pMod->dm_cSeg > 0) { if ((pMod->rgdmc = malloc(sizeof(DMC) * pMod->dm_cSeg)) == NULL) { Fatal("Out of memory"); } for (i = 0; i < pMod->dm_cSeg; i++) { GetBytes((char *) &SegDesc, sizeof (SegDesc)); pMod->rgdmc [ i ].sa = SegDesc.Seg; pMod->rgdmc [ i ].ra = SegDesc.Off; pMod->rgdmc [ i ].cb = SegDesc.cbSeg; } } if ((pMod->ModName = (char *) malloc(1 + (cbName = Gets()))) == NULL) { Fatal("Out of memory"); } // Read the name and terminate it with null GetBytes(pMod->ModName, (size_t) cbName); pMod->ModName[cbName] = '\0'; } BOOL BuildModuleList(void) { ulong i; PMOD module; char * pStr; IMAGE_DOS_HEADER exehdr; IMAGE_FILE_HEADER FileHeader; IMAGE_SEPARATE_DEBUG_HEADER DebugHeader; ULONG ulSignature; ulong offDebugDir = 0; static char szInvalidExe[] = "Invalid executable"; // read exe signature to see if we have a 32 bit linear exe if ((_lseek(exefile, 0L, SEEK_SET) == -1L) || (_read(exefile, &exehdr, sizeof(IMAGE_DOS_HEADER)) != sizeof(IMAGE_DOS_HEADER)) ) { Fatal(szInvalidExe); } if (exehdr.e_magic == IMAGE_SEPARATE_DEBUG_SIGNATURE) { fDbgFile = TRUE; fLinearExe = TRUE; // so other fLinear tests will work _lseek(exefile, 0, SEEK_SET); _read(exefile, &DebugHeader, sizeof(IMAGE_SEPARATE_DEBUG_HEADER)); } else if ((exehdr.e_magic == IMAGE_FILE_MACHINE_R4000) || (exehdr.e_magic == IMAGE_FILE_MACHINE_R10000) || (exehdr.e_magic == IMAGE_FILE_MACHINE_ALPHA)) { // Check if this is a ROM image or an object file. _lseek(exefile, 0, SEEK_SET); _read(exefile, &FileHeader, IMAGE_SIZEOF_FILE_HEADER); if (FileHeader.SizeOfOptionalHeader != IMAGE_SIZEOF_ROM_OPTIONAL_HEADER) { _lseek(exefile, 0, SEEK_SET); return(TRUE); } fLinearExe = TRUE; } else { if (exehdr.e_magic == IMAGE_DOS_SIGNATURE) { // old header // seek to location of new header (if any) if (_lseek(exefile, exehdr.e_lfanew, SEEK_SET) == -1) { Fatal(szInvalidExe); } } else { // seek back to beginning of file, may find new header there _lseek(exefile, 0, SEEK_SET); } if (_read(exefile, &ulSignature, sizeof(ulong)) == sizeof(ulong)) { if (ulSignature == IMAGE_NT_SIGNATURE) { fLinearExe = TRUE; _read(exefile, &FileHeader, sizeof(IMAGE_FILE_HEADER)); } } } if (fLinearExe) { DWORD cDirs; IMAGE_SECTION_HEADER secthdr; IMAGE_DEBUG_DIRECTORY dbgdir; IMAGE_OPTIONAL_HEADER OptionalHeader; if (fDbgFile) { cDirs = DebugHeader.DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY); secthdr.PointerToRawData = sizeof(IMAGE_SEPARATE_DEBUG_HEADER) + (DebugHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + DebugHeader.ExportedNamesSize); offDebugDir = 0; } else { int cObjs = FileHeader.NumberOfSections; if (FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) { Fatal("No Debug Info"); } // Read the optional header. if (_read(exefile, &OptionalHeader, FileHeader.SizeOfOptionalHeader) != FileHeader.SizeOfOptionalHeader) { Fatal(szInvalidExe); } for (; cObjs != 0; cObjs -= 1) { if (_read(exefile, §hdr, IMAGE_SIZEOF_SECTION_HEADER) != IMAGE_SIZEOF_SECTION_HEADER ) { Fatal(szInvalidExe); } if (FileHeader.SizeOfOptionalHeader == IMAGE_SIZEOF_ROM_OPTIONAL_HEADER) { // ROM images always have the debug directory at the beginning of // .rdata. Find it, then count the total (there's a null record // at the end to terminate) if (!strncmp(secthdr.Name, ".rdata", 5)) { offDebugDir = 0; if (_lseek(exefile, secthdr.PointerToRawData, SEEK_SET) == -1L) { Fatal(szInvalidExe); } _read(exefile, &dbgdir, sizeof(IMAGE_DEBUG_DIRECTORY)); cDirs = 0; while (dbgdir.Type != 0) { cDirs ++; if (_read(exefile, &dbgdir, sizeof(IMAGE_DEBUG_DIRECTORY)) != sizeof(IMAGE_DEBUG_DIRECTORY)) { Fatal(szInvalidExe); } } break; } } else { // Standard PE images have a pointer in the data directory. Walk the // section list to find who really have it... if ((OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress >= secthdr.VirtualAddress) && (OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress < secthdr.VirtualAddress + secthdr.SizeOfRawData) ) { offDebugDir = OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress - secthdr.VirtualAddress; cDirs = OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size / sizeof ( IMAGE_DEBUG_DIRECTORY ); break; } } } if (cObjs == 0 || cDirs == 0) { Fatal(szInvalidExe); } } // Now look at the debug information header record for ( ; cDirs != 0; cDirs-- ) { if (_lseek(exefile, secthdr.PointerToRawData + offDebugDir, SEEK_SET) == -1L) { Fatal(szInvalidExe); } if (_read(exefile, &dbgdir, sizeof (IMAGE_DEBUG_DIRECTORY )) != sizeof (IMAGE_DEBUG_DIRECTORY )) { Fatal(szInvalidExe); } if (dbgdir.Type == IMAGE_DEBUG_TYPE_CODEVIEW) break; offDebugDir += sizeof( IMAGE_DEBUG_DIRECTORY ); } if (dbgdir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) { Fatal(szInvalidExe); } lfoBase = dbgdir.PointerToRawData; if (_lseek(exefile, lfoBase, SEEK_SET) == -1L) { Fatal(szInvalidExe); } Sig = CheckSignature(); } else { if (_lseek(exefile, -8L, SEEK_END) == -1L) { Fatal(szInvalidExe); } Sig = CheckSignature(); if ((_read(exefile, (char *)&dlfaBase, sizeof (long)) != sizeof (long)) || (_lseek(exefile, -dlfaBase, SEEK_END) == -1L)) { Fatal("No CodeView info"); } lfoBase = _tell(exefile); if (CheckSignature() != Sig) { Fatal(szInvalidExe); } } // display status of OMF information switch (Sig) { case SIG02: puts("File linked by link 5.20 or earlier linker\n"); break; case SIG05: puts("File linked by link 5.30 and not cvpacked\n"); break; case SIG06: puts("File incrementally linked by ilink 1.30 and not cvpacked\n"); break; case SIG07: puts("File has been cvpacked by QCWIN 1.0\n"); break; case SIG08: puts("File has been cvpacked for C7.0\n"); break; case SIG09: puts("File has been cvpacked for C8.0\n"); break; case SIG10: puts("File is VC++ 2.0+ format\n"); break; default: Fatal("File format not recognized"); break; } // locate first directory, read number of entries, allocate space and // read directory entries switch (Sig) { case SIG02: // Read and sort NB02 directory ReadNB02(); break; case SIG10: ReadNB05(); { // read PDB file name + any padding int i = 0; SIG sig; AGE age; _lseek(exefile, lfoBase + 8, SEEK_SET); // read the signature _read(exefile, &sig, sizeof (unsigned long)); fprintf(stdout, "signature = 0x%8.8x, ", sig); // and the age _read(exefile, &age, sizeof (unsigned long)); fprintf(stdout, "age = 0x%8.8x\n\n", age); do { _read(exefile, &PdbFileName[i], sizeof(char)); } while (PdbFileName[i++] != 0); fprintf(stdout, "Pdb File Name = %s\n", PdbFileName); if (_access(PdbFileName, 0)) { fprintf(stdout, "Unable to open - %s\n", PdbFileName); { char PdbFname[_MAX_FNAME]; char PdbExt[_MAX_EXT]; _splitpath(PdbFileName, NULL, NULL, PdbFname, PdbExt); _makepath(PdbFileName, drive, dir, PdbFname, PdbExt); } if (_access(PdbFileName, 0) == 0) { fprintf(stdout, "Using - %s\n", PdbFileName); } else { PdbFileName[0] = 0; } } } return(FALSE); case SIG05: case SIG06: case SIG07: case SIG08: case SIG09: ReadNB05(); break; } // sweep directory and build module list if (fHdr) { puts("Subsection iMod cb lfo offset"); } for (i = 0; i < cSST; i++) { switch (pDir[i].SubSection) { case SSTMODULE: module = GetModule(pDir[i].iMod, TRUE); module->ModuleSize = pDir[i].cb; module->ModulesAddr = pDir[i].lfo; GetSSTMOD(module, lfoBase + pDir[i].lfo, pDir[i].cb); pStr = "Module"; break; case SSTPUBLIC: module = GetModule(pDir[i].iMod, TRUE); module->PublicSize = pDir[i].cb; module->PublicsAddr = pDir[i].lfo; pStr = "Publics"; break; case SSTTYPES: module = GetModule(pDir[i].iMod, TRUE); module->TypeSize = pDir[i].cb; module->TypesAddr = pDir[i].lfo; pStr = "Types"; break; case SSTSYMBOLS: module = GetModule(pDir[i].iMod, TRUE); module->SymbolSize = pDir[i].cb; module->SymbolsAddr = pDir[i].lfo; pStr = "Symbols"; break; case SSTLIBRARIES: Libraries = pDir[i]; pStr = "Libraries"; break; case SSTSRCLNSEG: case sstSrcLnSeg: module = GetModule(pDir[i].iMod, TRUE); module->SrcLnSize = pDir[i].cb; module->SrcLnAddr = pDir[i].lfo; pStr = "SrcLnSeg"; break; case sstModule: module = GetModule(pDir[i].iMod, TRUE); module->ModuleSize = pDir[i].cb; module->ModulesAddr = pDir[i].lfo; GetsstModule(module, lfoBase + pDir[i].lfo, pDir[i].cb); pStr = "Module"; break; case sstTypes: module = GetModule(pDir[i].iMod, TRUE); module->TypeSize = pDir[i].cb; module->TypesAddr = pDir[i].lfo; pStr = "Types"; break; case sstPreComp: module = GetModule(pDir[i].iMod, TRUE); module->TypeSize = pDir[i].cb; module->TypesAddr = pDir[i].lfo; pStr = "PreComp"; break; case sstPublic: module = GetModule(pDir[i].iMod, TRUE); module->PublicSize = pDir[i].cb; module->PublicsAddr = pDir[i].lfo; pStr = "Publics"; break; case sstPublicSym: module = GetModule(pDir[i].iMod, TRUE); module->PublicSize = pDir[i].cb; module->PublicSymAddr = pDir[i].lfo; pStr = "PublicSym"; break; case sstAlignSym: case sstSymbols: module = GetModule(pDir[i].iMod, TRUE); module->SymbolSize = pDir[i].cb; module->SymbolsAddr = pDir[i].lfo; pStr = "Symbols"; break; case sstSrcModule: module = GetModule(pDir[i].iMod, TRUE); module->SrcLnSize = pDir[i].cb; module->SrcModuleAddr = pDir[i].lfo; pStr = "SrcModule"; break; case sstLibraries: Libraries = pDir[i]; pStr = "Libraries"; break; case sstGlobalSym: GlobalSym = pDir[i]; pStr = "Global Sym"; break; case sstGlobalPub: GlobalPub = pDir[i]; pStr = "Global Pub"; break; case sstGlobalTypes: GlobalTypes = pDir[i]; pStr = "Global Types"; break; case sstMPC: MpcDebugInfo = pDir[i]; pStr = "MPC Debug Info"; break; case sstSegMap: SegMap = pDir[i]; pStr = "SegMap"; break; case sstSegName: SegName = pDir[i]; pStr = "SegName"; break; case sstFileIndex: FileIndex = pDir[i]; pStr = "FileIndex"; break; case sstStaticSym: StaticSym = pDir[i]; pStr = "Static Sym"; break; default: pStr = "Unknown or NYI"; break; } if (fHdr) { printf("%-12s %5d %6ld 0x%08lX 0x%08lX\n", pStr, pDir[i].iMod, pDir[i].cb, pDir[i].lfo, pDir[i].lfo + lfoBase); } } return(FALSE); } void DumpObjFile(void) { IMAGE_FILE_HEADER objFileHdr; IMAGE_SECTION_HEADER secHdr; int i; ULONG offSection; ULONG sig; int fSigSym = FALSE; if (_lseek(exefile, 0, SEEK_SET) == -1) { Fatal("Internal error"); } if (_read(exefile, &objFileHdr, sizeof(objFileHdr)) != sizeof(objFileHdr)) { Fatal("Can't read file header"); } offSection = sizeof(objFileHdr) + objFileHdr.SizeOfOptionalHeader; for (i = 0; i < objFileHdr.NumberOfSections; i++, offSection += sizeof(secHdr)) { if (_lseek(exefile, offSection, SEEK_SET) == -1) { Fatal("Can't seek to section\n"); } if (_read(exefile, &secHdr, sizeof(secHdr)) != sizeof(secHdr)) { Fatal("Error reading section header\n"); } /* * check for various types of sections to dump */ if (fSym) { if (_strnicmp((char *) secHdr.Name, ".debug$S", 8) == 0) { if (_lseek(exefile, secHdr.PointerToRawData, SEEK_SET) == -1) { Fatal("Cannot seek to symbols section"); } if ((!fSigSym) && ((_read(exefile, &sig, sizeof(sig)) != sizeof(sig)) || (sig != 1))) { Fatal("Bad signature on .debug$S section"); } puts("********* SYMBOLS **********\n"); DumpModSymC7( secHdr.SizeOfRawData - sizeof(sig) * !fSigSym ); fSigSym = TRUE; } } if (fTyp) { if ((_strnicmp((char *) secHdr.Name, ".debug$T", 8) == 0) || (_strnicmp((char *) secHdr.Name, ".debug$P", 8) == 0)) { if (_lseek(exefile, secHdr.PointerToRawData, SEEK_SET) == -1) { Fatal("Cannot seek to types section"); } if ((_read(exefile, &sig, sizeof(sig)) != sizeof(sig)) || (sig != CV_SIGNATURE_C7)) { Fatal("Bad signature on .debug$T section"); } puts("********** TYPES ************\n"); DumpModTypC7( secHdr.SizeOfRawData - sizeof(sig) ); } } } } void DumpMod(void) { PMOD pMod; // pointer to module list uchar Name[256]; int ich; unsigned int i = 0; puts("\n\n*** MODULES ***"); for (pMod = ModList; pMod != NULL; pMod = pMod->next) { strcpy (Name, pMod->ModName); printf("%d\t%s", pMod->iMod, Name ); if ((ich = strlen(Name) + 8) < 40 && pMod->dm_cSeg > 0) { while ( ich < 40 ) { printf("\t"); ich += 8; } printf("%04X:%08lX\t%8lX\n", pMod->rgdmc[0].sa, pMod->rgdmc[0].ra, pMod->rgdmc[0].cb); i = 1; } else { printf("\n"); i = 0; } for (; i < pMod->dm_cSeg; i++) { printf("\t\t\t\t\t%04X:%08lX\t%8lX\n", pMod->rgdmc[i].sa, pMod->rgdmc[i].ra, pMod->rgdmc[i].cb); } } } /** DumpLibs - Display libraries section. * * * */ void DumpLibs(void) { char name[256]; int cbName; int i = 0; if (Libraries.lfo != 0) { puts("\n\n*** LIBRARIES subsection ***"); _lseek(exefile, lfoBase + Libraries.lfo, SEEK_SET); cbRec = Libraries.cb; while (cbRec > 0) { // Read the name GetBytes(name, (size_t)(cbName = Gets())); name[cbName] = '\0'; printf("%4d: %s\n", i, name); i++; } } fputc('\n', stdout); } void DumpNumbers(void) { unsigned long cbModule = 0; unsigned long cbSource = 0; unsigned long cbSymbols = 0; unsigned long cbPublics = 0; unsigned long cbTypes = 0; unsigned long cbGlobals = 0; unsigned long cbOther = 0; unsigned long cbTotal = 0; unsigned int isst = 0; for ( isst = 0; isst < cSST; isst++ ) { switch (pDir [ isst ].SubSection) { case sstModule: case SSTMODULE: cbModule += pDir [ isst ].cb; break; case SSTPUBLIC: case sstPublic: case sstPublicSym: case sstGlobalPub: cbPublics += pDir [ isst ].cb; break; case SSTTYPES: case sstTypes: case sstGlobalTypes: cbTypes += pDir [ isst ].cb; break; case SSTSYMBOLS: case sstAlignSym: case sstSymbols: cbSymbols += pDir [ isst ].cb; break; case SSTSRCLNSEG: case sstSrcModule: cbSource += pDir [ isst ].cb; break; case sstGlobalSym: cbGlobals += pDir [ isst ].cb; break; default: cbOther += pDir [ isst ].cb; break; } } cbTotal = cbModule + cbSource + cbSymbols + cbPublics + cbTypes + cbGlobals + cbOther; puts("\n\n*** Statistics ***\n"); printf("Module %10ld %2d%%\n", cbModule, (int) ((cbModule *100)/cbTotal) ); printf("Source Lines %10ld %2d%%\n", cbSource, (int) ((cbSource *100)/cbTotal) ); printf("Symbols %10ld %2d%%\n", cbSymbols, (int) ((cbSymbols*100)/cbTotal) ); printf("Publics %10ld %2d%%\n", cbPublics, (int) ((cbPublics*100)/cbTotal) ); printf("Types %10ld %2d%%\n", cbTypes, (int) ((cbTypes *100)/cbTotal) ); printf("Globals %10ld %2d%%\n", cbGlobals, (int) ((cbGlobals*100)/cbTotal) ); printf("Others %10ld %2d%%\n", cbOther, (int) ((cbOther *100)/cbTotal) ); printf("Total %10ld\n", cbTotal ); } __inline BOOL FDumpPdb() { return((Sig == SIG10) && (PdbFileName[0])); } /** DumpPub - Display PUBLICS section * * * */ void DumpPub(void) { #define MAXPUB (256 + max (sizeof (PUBSYM16), sizeof (PUBSYM32) + 3)) uchar name[MAXPUB]; // Name of public symbol ushort cbName; // Length of name SYMPTR pSym = (SYMPTR)&name; ulong off; _segment seg; ushort type; PMOD pMod; uchar fNeedsTitle = TRUE; for (pMod = ModList; pMod != NULL; pMod = pMod->next) { if ((pMod->PublicSize != 0) && ((iModToList == 0) || ((ushort)iModToList == pMod->iMod))) { if( fNeedsTitle ){ fNeedsTitle = FALSE; puts("\n\n*** PUBLICS ***"); } if (pMod->PublicsAddr != 0) { _lseek(exefile, lfoBase + pMod->PublicsAddr, SEEK_SET); strcpy (name, pMod->ModName); puts(name); cbRec = pMod->PublicSize; while (cbRec) { if (fLinearExe) { off = LGets(); } else { off = (ulong)WGets(); } seg = WGets(); type = WGets(); cbName = Gets(); GetBytes(name, (size_t) cbName); name[cbName] = '\0'; printf("\t%4.4x:%8.8lx %4.4x %s\n", seg, off, type, name); } } else if (pMod->PublicSymAddr != 0) { cbRec = pMod->PublicSize; _lseek(exefile, lfoBase + pMod->PublicSymAddr, SEEK_SET); strcpy (name, pMod->ModName); printf("%s\tsignature = %lx\n", name, LGets()); while (cbRec) { pSym->reclen = WGets(); GetBytes((uchar *) &pSym->rectyp, (size_t) pSym->reclen); switch (pSym->rectyp) { case S_PUB16: cbName = ((PUBPTR16)pSym)->name[0]; ((PUBPTR16)pSym)->name[cbName + 1] = '\0'; printf("\t%4.4x:%4.4x %4.4x %s\n", ((PUBPTR16)pSym)->seg, ((PUBPTR16)pSym)->off, ((PUBPTR16)pSym)->typind, &((PUBPTR16)pSym)->name[1]); break; case S_PUB32: cbName = ((PUBPTR32)pSym)->name[0]; ((PUBPTR32)pSym)->name[cbName + 1] = '\0'; printf("\t%4.4x:%8.8lx %4.4x %s\n", ((PUBPTR32)pSym)->seg, ((PUBPTR32)pSym)->off, ((PUBPTR32)pSym)->typind, &((PUBPTR32)pSym)->name[1]); break; } } } } } } void PrtModName(const char *name, long lfa) { printf(" *** Module %s at %06lx (%ld) ***\n", name, lfa, lfa); } /** DumpSrcLn - dump sstSrcLnSeg table * * DumpSrcLn (pMod) * * Entry pMod = pointer to module * * Exit * * Returns none */ void DumpSrcLn(PMOD pMod) { char name[MAXNAM]; // Source file name ushort cbName; // Length of name ushort cLin; // Count of line numbers ulong loffset; // Code offset ushort lineno; // Line number int i; _lseek(exefile, lfoBase + pMod->SrcLnAddr, SEEK_SET); strcpy (name, pMod->ModName); PrtModName (name, pMod->SrcLnAddr); cbRec = pMod->SrcLnSize; while (cbRec) { cbName = Gets(); GetBytes(name, (size_t) cbName);// Get source name name[cbName] = '\0'; printf("%s:", name); printf("\tsegment = %4d", WGets()); cLin = WGets(); // Get count printf("\tcount = %4d:", cLin); i = 0; while (cLin-- > 0) { // Display the offsets if (i == 0) { fputc('\n', stdout); } // Get next line number lineno = WGets(); loffset = fLinearExe ? LGets(): (ulong) WGets() ; printf("\t%4d %08lx", lineno, loffset); i = (i + 1) % 4; // 4 offsets per output line } fputc('\n', stdout); } fputc('\n', stdout); } /** DumpSrcMod - dump sstSrcModule table * * DumpSrcModd (pMod) * * Entry pMod = pointer to module * * Exit * * Returns none */ void DumpSrcMod(PMOD pMod) { OMFSourceModule *pModTable; // pointer to module file table OMFSourceFile *pFileTable; // pointer to current file table OMFSourceLine *pLineTable; // pointer to current segment table ushort cFile; // number of source files in module ushort iFile; ushort cSeg; // number of segments in source file ushort iSeg; ulong cbSrcLn = 0; // byte count of SrcLnSeg table char *pName; // pointer to file name ushort cbName; // length of file name ushort cPair; // pointer to table of start/end ranges struct range { unsigned long start; unsigned long end; }; struct range *pRangeTable; ushort *pLine; CV_uoff32_t *pOff; ushort i; ushort j; uchar name[256]; ulong loffset; // Code offset ushort lineno; // Line number _lseek(exefile, lfoBase + pMod->SrcModuleAddr, SEEK_SET); strcpy(name, pMod->ModName); PrtModName (name, pMod->SrcModuleAddr); cbRec = pMod->SrcLnSize; if ((pModTable = (OMFSourceModule *) malloc((size_t)cbRec)) == NULL) { Fatal("Out of memory"); } readfar(exefile, (char *) pModTable, cbRec); cFile = pModTable->cFile; cSeg = pModTable->cSeg; printf("\t( files = %d, segs = %d )\n\n", cFile, cSeg ); puts ( "\tContributor Segments:\n"); for ( iSeg = 0; iSeg < pModTable->cSeg; iSeg++ ) { printf("\t\t%04x:%08lx-%08lx\n", *( (ushort *) &pModTable->baseSrcFile [ cFile + cSeg * 2 ] + iSeg ), pModTable->baseSrcFile [ cFile + iSeg * 2 ], pModTable->baseSrcFile [ cFile + iSeg * 2 + 1 ] ); } printf("\n"); // walk each of the OMFSourceFile tables in the module table for (iFile = 0; iFile < cFile; iFile++) { pFileTable = (OMFSourceFile *)(((char *)pModTable) + pModTable->baseSrcFile[iFile]); // walk each of the OMFSourceLine tables in the file cSeg = pFileTable->cSeg; // Get pointer to base of range table. pRangeTable = (struct range *) &(pFileTable->baseSrcLn[cSeg]); // set the length of the file and the pointer to the file name pName = ((char *)pFileTable) + offsetof (OMFSourceFile, baseSrcLn) + 3 * sizeof (ulong) * cSeg; cbName = *((uchar *)pName)++; for (iSeg = 0; iSeg < cSeg; iSeg++) { pLineTable = (OMFSourceLine *)(((char *)pModTable) + pFileTable->baseSrcLn[iSeg]); cPair = pLineTable->cLnOff; // copy file name memcpy (name, pName, cbName); name[cbName] = 0; printf("\t%s:seg = %x, start/end %08lx/%08lx, line/addr pairs = %d\n", name, pLineTable->Seg, pRangeTable[iSeg].start, pRangeTable[iSeg].end, cPair); pOff = (CV_uoff32_t *)(((char *)pLineTable) + offsetof (OMFSourceLine, offset)); pLine = (ushort *)(((char *)pOff) + cPair * sizeof (CV_uoff32_t)); for (i = 0, j = 0; i < cPair; i++) { if (j == 0) { printf("\n"); } lineno = *((ushort *)pLine)++; loffset = *((CV_uoff32_t *)pOff)++; printf("\t%4d %08lx", lineno, loffset); j = (j + 1) % 4; // 4 offsets per output line } printf("\n"); } printf("\n"); } free(pModTable); } /** DumpSrc - display SRCLINES section. * */ void DumpSrc(void) { PMOD pMod; puts("\n\n*** SRCLINES ***"); for (pMod = ModList; pMod != NULL; pMod = pMod->next) { if ((pMod->SrcLnSize != 0) && ((iModToList == 0) || ((ushort)iModToList == pMod->iMod))) { if (pMod->SrcLnAddr != 0) { DumpSrcLn(pMod); } else if (pMod->SrcModuleAddr != 0) { fputs("\n", stdout); DumpSrcMod(pMod); } } } } /** DumpSegMap - dump sstSegMap info * * DumpSegMap ( ) * * Entry * * Exit * * Returns none */ void DumpSegMap(void) { unsigned short iSeg; unsigned short cSegLog; unsigned short cSegEle; unsigned short flags; unsigned short ovlNbr; unsigned short ggr; unsigned short sa; unsigned short iSegName; unsigned short iClassName; unsigned long phyOff; unsigned long cbSeg; if (SegMap.lfo != 0) { puts("\n\n*** SEGMENT MAP subsection ***"); _lseek(exefile, lfoBase + SegMap.lfo, SEEK_SET); cbRec = SegMap.cb; cSegEle = WGets(); cSegLog = WGets(); printf("Seg Flag ovl# ggr sa sgnm clnm phyoff cb\n"); for (iSeg = 0; iSeg < cSegEle; ++iSeg) { flags = WGets(); ovlNbr = WGets(); ggr = WGets(); sa = WGets(); iSegName = WGets(); iClassName = WGets(); phyOff = LGets(); cbSeg = LGets(); printf(" %02x %04x %04x %04x %04x %04x %04x %08lx %08lx\n", iSeg+1, flags, ovlNbr, ggr, sa, iSegName, iClassName, phyOff, cbSeg ); } } fputc('\n', stdout); } /** DumpMpc - dump sstMPC info includeing segment to frame mapping table * * DumpMpc ( ) * * Entry * * Exit * * Returns none */ LOCAL void DumpMpc ( void) { unsigned short iSeg; unsigned short frame; unsigned short cSeg; if (MpcDebugInfo.lfo != 0) { puts("\n\n*** MPC DEBUG INFO subsection ***"); _lseek(exefile, lfoBase + MpcDebugInfo.lfo, SEEK_SET); cbRec = MpcDebugInfo.cb; cSeg = WGets(); for (iSeg = 0; iSeg < cSeg; ++iSeg) { frame = WGets(); printf(" %02x : %04x\n", iSeg+1, frame); } } fputc('\n', stdout); } void DumpFileInd(void) { ushort cMod = 0; ushort cFileRef = 0; ushort *rgiszMod = NULL; ushort *rgcszMod = NULL; ulong *rgulNames = NULL; char *rgchNames = NULL; ulong ichNames = 0; int imod = 0; ulong cchNames = 0; if ( FileIndex.lfo != 0 ) { puts("\n\n*** File Index DEBUG INFO subsection ***\n"); _lseek(exefile, lfoBase + FileIndex.lfo, SEEK_SET); cbRec = FileIndex.cb; cMod = WGets(); cFileRef = WGets(); printf("Modules = %d, Files = %d\n\n", cMod, cFileRef ); rgiszMod = malloc ( sizeof ( ushort ) * cMod ); GetBytes((char *) rgiszMod, sizeof ( ushort ) * cMod ); rgcszMod = malloc ( sizeof ( ushort ) * cMod ); GetBytes((char *) rgcszMod, sizeof ( ushort ) * cMod ); rgulNames = malloc ( sizeof ( ulong ) * cFileRef ); GetBytes((char *) rgulNames, sizeof ( ulong ) * cFileRef ); cchNames = FileIndex.cb - ( sizeof ( ushort ) * 2 + sizeof ( ushort ) * 2 * cMod + // Module list & cfiles sizeof ( ulong ) * cFileRef // String offsets ); assert ( cchNames < UINT_MAX ); rgchNames = malloc ( (size_t)cchNames ); GetBytes(rgchNames, (size_t) cchNames ); for ( imod = 0; imod < (int) cMod; imod++ ) { int isz = 0; if ( rgcszMod [ imod ] != 0 ) { printf("Module #%d: Index = %d, Count = %d\n", imod + 1, rgiszMod [ imod ], rgcszMod [ imod ] ); for ( isz = 0; isz < (int) rgcszMod [ imod ]; isz++ ) { printf(" %8lx", rgulNames [ rgiszMod [ imod ] + isz ] ); if ( isz + 1 % 8 == 0 ) { printf("\n"); } } printf("\n"); } } printf("\n"); ichNames = 0; while ( ichNames + 3 < cchNames ) { char rgch [ 256 ]; memset ( rgch, 0, 256 ); memcpy ( rgch, &rgchNames [ ichNames + 1 ], (size_t) rgchNames [ ichNames ] ); printf("%8lx: %s\n", ichNames, rgch ); ichNames += rgchNames [ ichNames ] + 1; } free(rgiszMod); free(rgcszMod); free(rgulNames); free(rgchNames); } fputc('\n', stdout); } /** ClearModules - clear module entries * * ClearModules() * * Entry none * * Exit module entry pointer and counts reset * * Returns pointer to module structure */ void ClearModules(void) { PMOD pMod = ModList; // while there are entries left in moduleList while (pMod != NULL) { pMod->ModulesAddr = 0; pMod->SymbolsAddr = 0; pMod->TypesAddr = 0; pMod->PublicsAddr = 0; pMod->PublicSymAddr = 0; pMod->SrcLnAddr = 0; pMod->SrcModuleAddr = 0; pMod->ModuleSize = 0; pMod->SymbolSize = 0; pMod->TypeSize = 0; pMod->PublicSize = 0; pMod->SrcLnSize = 0; pMod = pMod->next; } } /** ReadNB06 - read incremental link directory * * */ void ReadNB06(void) { ushort i; PMOD module; char *pStr; // locate next directory, read number of entries, read entries and // replace existing module directory entries. if ((_lseek(exefile, lfoBase + DirHead.lfoNextDir, SEEK_SET) == -1L) || (_read(exefile, (char *)&DirHead, sizeof (OMFDirHeader)) != sizeof (OMFDirHeader))) { Fatal("Invalid incrementally linked executable"); } if (cSST < DirHead.cDir) { Fatal("ilink directory entries exceeds original"); } cSST06 = DirHead.cDir; // read directory entries and replace into existing list if (readfar(exefile, (char *) pDir, (sizeof (OMFDirEntry) * cSST06)) != (sizeof (OMFDirEntry) * cSST06)) { Fatal("Invalid executable"); } for (i = 0; i < (ushort)cSST06; i++) { switch (pDir[i].SubSection) { case sstModule: module = GetModule(pDir[i].iMod, FALSE); module->ModuleSize = pDir[i].cb; module->ModulesAddr = pDir[i].lfo; GetsstModule (module, lfoBase + pDir[i].lfo, pDir[i].cb); pStr = "Module"; break; case sstTypes: module = GetModule(pDir[i].iMod, FALSE); module->TypeSize = pDir[i].cb; module->TypesAddr = pDir[i].lfo; pStr = "Types"; break; case sstPublic: module = GetModule(pDir[i].iMod, FALSE); module->PublicSize = pDir[i].cb; module->PublicsAddr = pDir[i].lfo; pStr = "Publics"; break; case sstPublicSym: module = GetModule(pDir[i].iMod, FALSE); module->PublicSize = pDir[i].cb; module->PublicSymAddr = pDir[i].lfo; pStr = "PublicSym"; break; case sstAlignSym: case sstSymbols: module = GetModule(pDir[i].iMod, FALSE); module->SymbolSize = pDir[i].cb; module->SymbolsAddr = pDir[i].lfo; pStr = "Symbols"; break; case sstSrcModule: module = GetModule(pDir[i].iMod, FALSE); module->SrcLnSize = pDir[i].cb; module->SrcModuleAddr = pDir[i].lfo; pStr = "SrcModule"; break; default: assert (0); // what else is there break; } printf("%-12s %5d %6ld %8ld\n", pStr, pDir[i].iMod, pDir[i].cb, pDir[i].lfo); } } void DumpExeFile(void) /*++ Routine Description: This routine calls the set of routines for dumping the CVInfo from and EXE file. Arguments: None. Return Value: None. --*/ { BOOL fDumpPdb; if (BuildModuleList()) { DumpObjFile(); return; } fDumpPdb = FDumpPdb(); if (!fDumpPdb) { if (fMod) { DumpMod(); DumpLibs(); } if (fNum) { DumpNumbers(); } } if (fPub) { if (fDumpPdb) { DumpPDBPublics(PdbFileName); } else { DumpPub(); if (GlobalPub.cb != 0){ DumpGlobal("Global Publics", &GlobalPub); } } } if (fTyp) { DumpTyp(); if (fDumpPdb) { DumpPDBTypes(PdbFileName); } else if (GlobalTypes.cb != 0){ DumpCom(); } } if (fSym) { if (fDumpPdb) { DumpPDBSyms(PdbFileName); } else { DumpSym(); } } if (fGPSym) { if (fDumpPdb) { DumpPDBGlobals(PdbFileName); } else if (GlobalSym.cb != 0) { DumpGlobal("Compacted Global Symbols", &GlobalSym); } } if (!fDumpPdb) { if (fSTSym && (StaticSym.cb != 0)) { DumpGlobal("Static symbol references", &StaticSym); } if (fSrc) { DumpSrc(); } if (fSegMap) { DumpSegMap(); } if (fMpc) { DumpMpc(); } if (fFileInd) { DumpFileInd(); } while (DirHead.lfoNextDir != 0L) { ClearModules(); ReadNB06(); if (fMod) { DumpMod(); } if (fPub) { DumpPub(); } if (fTyp) { DumpTyp(); } if (fSym) { DumpSym(); } if (fSrc) { DumpSrc(); } } } } void DumpPDBFile ( char * szPdb ) { if (fPub) { DumpPDBPublics(szPdb); } if (fTyp) { DumpPDBTypes(szPdb); } if (fSym) { DumpPDBSyms(szPdb); } if (fGPSym) { DumpPDBGlobals(szPdb); } } int __cdecl main(int argc, char *argv[]) { short magic; // Magic word // print startup microsoft banner and process the arguments printf("Microsoft (R) Symbol and Type OMF Dumper Version %d.%02d.%04d\n" "Copyright (C) 1987-1996 Microsoft Corporation\n\n", rmj, rmm, rup); if (argc < 2) { // Check syntax Usage(); } // Skip argv[0] argv++; argc--; while (argc && **argv == '-') { switch ( (*argv)[1]) { case 'l': // Dump SRCLINES fSrc++; break; case 'f': // Dump File index fFileInd++; break; case 'm': // Dump MODULES fMod++; break; case 'p': // Dump PUBLICS fPub++; break; case 's': // Dump SYMBOLS fSym++; break; case 't': // Dump TYPES fTyp++; break; case 'h': // Dump header (section table) fHdr++; break; case 'g': // Dump GLOBAL fGPSym++; break; case 'i': // Dump Statics fSTSym++; break; case 'q': // Dump MPC/P-code info fMpc++; break; case 'x': // Dump Seg Map fSegMap++; break; case 'M': // Modules to list symbols and types for iModToList = atoi ((*argv) + 2); break; case 'R': // Dump Raw data fRaw++; break; case 'S': // Dump Statics only fStatics++; break; case '?': // Print usage banner LongUsage(); break; case 'n': // Dump size numbers fNum++; break; default: // Syntax error Usage(); } argv++; argc--; } if (!argv[0] || argv[1] || !argc) { Usage(); } OpenExe(*argv); if (!(fPub || fSym || fTyp || fSrc || fMod || fHdr || fGPSym || fSegMap || fMpc || fNum || fFileInd || fSTSym)) { // If no switches, set all options fPub = fSym = fTyp = fSrc = fMod = fHdr = fGPSym = fSegMap = fMpc = fNum = fFileInd = fSTSym = TRUE; } // check to see that it is an exe if (_read(exefile, &magic, 2) != 2) { Fatal("Zero length file input file"); } if ((magic == IMAGE_DOS_SIGNATURE) || (magic == IMAGE_OS2_SIGNATURE) || (magic == IMAGE_NT_SIGNATURE) || (magic == IMAGE_NB_SIGNATURE) || (magic == IMAGE_SEPARATE_DEBUG_SIGNATURE) || (magic == IMAGE_FILE_MACHINE_R4000) || // ROM images for MIPS (magic == IMAGE_FILE_MACHINE_R10000) || // ROM images for MIPS (magic == IMAGE_FILE_MACHINE_ALPHA)) { // ROM images for ALPHA DumpExeFile(); } else if ((magic == IMAGE_FILE_MACHINE_I386) || (magic == IMAGE_FILE_MACHINE_R4000) || (magic == IMAGE_FILE_MACHINE_R10000) || (magic == IMAGE_FILE_MACHINE_ALPHA) || (magic == IMAGE_FILE_MACHINE_POWERPC) || (magic == IMAGE_FILE_MACHINE_M68K) || (magic == IMAGE_FILE_MACHINE_MPPC_601)) { DumpObjFile(); } else if (magic == 'iM') { // Check for a pdb if (_lseek(exefile, 0x28, SEEK_SET) != -1 && _read(exefile, &magic, 2) == 2) { if (magic == 'GJ') { _close ( exefile ); DumpPDBFile ( *argv ); } else { Fatal("Unrecognized input file"); } } else { Fatal("Unrecognized input file"); } } else { Fatal("Unrecognized input file"); } return(0); }