/*********************************************************************** * Microsoft (R) 32-Bit Incremental Linker * * Copyright (C) Microsoft Corp 1992-95. All rights reserved. * * File: dbinsp.cpp * * File Comments: * * Inspects & dumps incremental database files. * ***********************************************************************/ #include "link.h" #if DBG // macros #define FileWarning(file, str) printf("%s : %s\n", file, str) // statics static PIMAGE pimage; static void *pvIncr; static INT FileHandle; static DWORD FileLen; static char *SzDbFile; static BOOL fSymbols; static BOOL fHashTable; static BOOL fSectionMap; static BOOL fLibMap; static BOOL fHeaders; // function prototypes extern void IncrInitImage(PPIMAGE); void ProcessDbInspSwitches(void); void DbInsp(PARGUMENT_LIST); BOOL FVerifyAndDumpHeader(const char *); BOOL FReadInFile(void); void DumpIncrDb(const char *); BOOL DumpIncrImageHeaders(PIMAGE, const char *); void DumpIncrImgFileHdr(PIMAGE_FILE_HEADER); void DumpIncrImgOptHdr(PIMAGE_OPTIONAL_HEADER); void DumpIncrPGRP(PGRP); void DumpIncrPSEC(PSEC); void DumpIncrPLIB(PLIB); void DumpIncrPMOD(PMOD); void DumpIncrPCON(PCON); void DumpIncrSectionMap(PSECS, const char *); void DumpIncrLibMap(PLIB, const char *); void DumpIncrHashTable(PHT, void *); void DumpIncrSymbolTable(PST); // functions void DbInspUsage(void) { if (fNeedBanner) { PrintBanner(); } puts("usage: DBINSP [options] [files]\n\n" " options:\n\n" " /ALL\n" " /HASH\n" " /HEADERS\n" " /LIBMAP\n" " /NOLOGO\n" " /OUT:filename\n" " /SECMAP\n" " /SYMBOLS"); fflush(stdout); exit(USAGE); } MainFunc DbInspMain(int Argc, char *Argv[]) /*++ Routine Description: Dumps an incremental database in human readable form. Arguments: Argc - Standard C argument count. Argv - Standard C argument strings. Return Value: 0 Dump was successful. !0 Dumper error index. --*/ { WORD i; PARGUMENT_LIST parg; if (Argc < 2) { DbInspUsage(); } ParseCommandLine(Argc, Argv, NULL); ProcessDbInspSwitches(); if (fNeedBanner) { PrintBanner(); } for(i = 0, parg = ObjectFilenameArguments.First; i < ObjectFilenameArguments.Count; i++, parg = parg->Next) { DbInsp(parg); } FileCloseAll(); fclose(InfoStream); return (0); } void ProcessDbInspSwitches ( void ) /*++ Routine Description: Process incr db inspector switches. Arguments: None. Return Value: None. --*/ { PARGUMENT_LIST parg; WORD i; for (i=0,parg=SwitchArguments.First; iNext, i++) { if (!strcmp(parg->OriginalName, "?")) { DbInspUsage(); assert(FALSE); // doesn't return } if (!_strnicmp(parg->OriginalName, "out:", 4)) { if (*(parg->OriginalName+4)) { szInfoFilename = parg->OriginalName+4; if (!(InfoStream = fopen(szInfoFilename, "wt"))) { Fatal(NULL, CANTOPENFILE, szInfoFilename); } } continue; } if (!_stricmp(parg->OriginalName, "nologo")) { fNeedBanner = FALSE; continue; } if (!_stricmp(parg->OriginalName, "symbols")) { fSymbols = TRUE; continue; } if (!_stricmp(parg->OriginalName, "all")) { fSymbols = TRUE; fHashTable = TRUE; fSectionMap = TRUE; fLibMap = TRUE; fHeaders = TRUE; continue; } if (!_stricmp(parg->OriginalName, "hash")) { fHashTable = TRUE; continue; } if (!_stricmp(parg->OriginalName, "secmap")) { fSectionMap = TRUE; continue; } if (!_stricmp(parg->OriginalName, "libmap")) { fLibMap = TRUE; continue; } if (!_stricmp(parg->OriginalName, "headers")) { fHeaders = TRUE; continue; } Warning(NULL, WARN_UNKNOWN_SWITCH, parg->OriginalName); } // end for } void DbInsp ( PARGUMENT_LIST parg ) /*++ Routine Description: Verifies and dumps contents of incremental database. Arguments: parg - ptr to argument. Return Value: None. --*/ { fprintf(InfoStream, "\nInspecting file %s\n", parg->OriginalName); // Set the incr db filename szIncrDbFilename = parg->OriginalName; // Peek at the header if (!FVerifyAndDumpHeader(parg->OriginalName)) { return; } // Read in the entire file if (!FReadInFile()) { return; } // verify symbol table (ptrs are valid only after reading it in the entire image) if (!FValidPtrInfo((DWORD) pvIncr, (DWORD) FileLen, (DWORD) pimage->pst->blkStringTable.pb, pimage->pst->blkStringTable.cb)) { FileWarning(parg->OriginalName, "invalid pointer to string table"); return; } if (Align(sizeof(DWORD), ((DWORD) pimage->pst->blkStringTable.pb + pimage->pst->blkStringTable.cb - (DWORD) pvIncr)) != FileLen) { FileWarning(parg->OriginalName, "garbage beyond string table"); return; } // Dump the rest of the stuff DumpIncrDb(parg->OriginalName); // Close the incr db file FileClose(FileIncrDbHandle, TRUE); } BOOL FVerifyAndDumpHeader ( const char *szFile ) /*++ Routine Description: Reads in just the header and verifies it & dumps interesting info. Arguments: szFile - name of the incr db file. Return Value: None. --*/ { struct _stat statfile; IMAGE image; // stat the file if (_stat(szFile, &statfile) == -1) { Fatal(NULL, CANTOPENFILE, szFile); } FileLen = statfile.st_size; if (FileLen < sizeof(IMAGE)) { FileWarning(szFile, "file too small to be incr db"); return(FALSE); } FileHandle = FileOpen(szFile, O_RDONLY | O_BINARY, 0); FileRead(FileHandle, &image, sizeof(IMAGE)); FileClose(FileHandle, TRUE); // verify the incr db signature if (strcmp(image.Sig, INCDB_SIGNATURE)) { FileWarning(szFile, "invalid incr db signature found"); return(FALSE); } // verify the version numbers if (image.MajVersNum != INCDB_MAJVERSNUM) { FileWarning(szFile, "invalid version number found"); return(FALSE); } pvIncr = image.pvBase; if (!DumpIncrImageHeaders(&image, szFile)) { return(FALSE); } return(TRUE); } BOOL FReadInFile(void) /*++ Routine Description: Reads in file onto private heap. Arguments: szFile - name of file. Return Value: None. --*/ { DWORD dwErr = 0; // create a private heap to load the image if (CreateHeap(pvIncr, FileLen, FALSE, &dwErr) != pvIncr) { puts("failed to map ILK file"); return(FALSE); } pimage = (PIMAGE) pvIncr; IncrInitImage(&pimage); return(TRUE); } BOOL DumpIncrImageHeaders ( PIMAGE pimg, const char *szFile ) /*++ Routine Description: Dumps out the various headers. Arguments: pimg - pointer to IMAGE struct szFile - name of file for error reporting Return Value: TRUE if everything is valid. --*/ { BYTE Sig[32]; BOOL fValid = TRUE; fprintf(InfoStream, "\nIMAGE HEADER VALUES\n"); strcpy((char *) Sig, pimg->Sig); Sig[26]='\0'; fprintf(InfoStream, " Signature: %s", Sig); fprintf(InfoStream, " MajVersNum: 0x%.4x\n", pimg->MajVersNum); fprintf(InfoStream, " MinVersNum: 0x%.4x\n", pimg->MinVersNum); fprintf(InfoStream, " Heap Base: 0x%.8lx\n",pvIncr); fprintf(InfoStream, " secs.psecHead: 0x%.8lx\n", pimg->secs.psecHead); fprintf(InfoStream, " libs.plibHead: 0x%.8lx\n", pimg->libs.plibHead); fprintf(InfoStream, " libs.fNoDefaultLibs: %c\n", pimg->libs.fNoDefaultLibs ? '1':'0'); fprintf(InfoStream, " plibCmdLineObjs: 0x%.8lx\n",pimg->plibCmdLineObjs); fprintf(InfoStream, " pst: 0x%.8lx\n",pimg->pst); if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)pimg->secs.psecHead, 0)) { FileWarning(szFile, "invalid pointer to section list"); fValid = FALSE; } if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)pimg->libs.plibHead, 0)) { FileWarning(szFile, "invalid pointer to lib list"); fValid = FALSE; } if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)pimg->plibCmdLineObjs, 0)) { FileWarning(szFile, "invalid pointer to cmdline objs lib"); fValid = FALSE; } if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)pimg->pst, 0)) { FileWarning(szFile, "invalid pointer to symbol table"); fValid = FALSE; } DumpIncrImgFileHdr(&pimg->ImgFileHdr); DumpIncrImgOptHdr(&pimg->ImgOptHdr); return(fValid); } void DumpIncrImgFileHdr ( PIMAGE_FILE_HEADER pImgFileHdr ) /*++ Routine Description: Dumps out the IMAGE_FILE_HEADER. Arguments: pImgFileHdr - pointer to IMAGE_FILE_HEADER struct Return Value: None. --*/ { if (!fHeaders) { return; } fprintf(InfoStream, "\nIMAGE FILE HEADER VALUES\n"); fprintf(InfoStream, " Machine: 0x%.4x\n", pImgFileHdr->Machine); fprintf(InfoStream, " NumOfSec: 0x%.4x\n", pImgFileHdr->NumberOfSections); fprintf(InfoStream, " TimeStamp: %s", ctime((time_t *)&pImgFileHdr->TimeDateStamp)); fprintf(InfoStream, " PtrToSymTbl: 0x%.8lx\n", pImgFileHdr->PointerToSymbolTable); fprintf(InfoStream, " NumOfSyms: 0x%.8lx\n", pImgFileHdr->NumberOfSymbols); fprintf(InfoStream, " SizeOfOptHdr: 0x%.4x\n", pImgFileHdr->SizeOfOptionalHeader); fprintf(InfoStream, " Character.: 0x%.4x\n", pImgFileHdr->Characteristics); } static const char * const SubsystemName[] = { "Unknown", "Native", "Windows GUI", "Windows CUI", "Posix CUI", "MMOSA", }; static const char * const DirectoryEntryName[] = { "Export", "Import", "Resource", "Exception", "Security", "Base Relocation", "Debug", "Description", "Special", "Thread Storage", NULL }; void DumpIncrImgOptHdr ( PIMAGE_OPTIONAL_HEADER pImgOptHdr ) /*++ Routine Description: Dumps out the IMAGE_OPTIONAL_HEADER. Code taken from dump.c yikes! Arguments: pImgFileHdr - pointer to IMAGE_OPTIONAL_HEADER struct Return Value: None. --*/ { WORD i, j; char version[30]; if (!fHeaders) { return; } fprintf(InfoStream, "\nIMAGE OPTIONAL FILE HEADER VALUES\n"); fprintf(InfoStream, "% 8hX magic #\n", pImgOptHdr->Magic); j = (WORD) sprintf(version, "%d.%d", pImgOptHdr->MajorLinkerVersion, pImgOptHdr->MinorLinkerVersion); if (j > 8) { j = 8; } for (j = (WORD) (8-j); j; j--) { fputc(' ', InfoStream); } fprintf(InfoStream, "%s linker version\n% 8lX size of code\n% 8lX size of initialized data\n% 8lX size of uninitialized data\n% 8lX address of entry point\n% 8lX base of code\n% 8lX base of data\n", version, pImgOptHdr->SizeOfCode, pImgOptHdr->SizeOfInitializedData, pImgOptHdr->SizeOfUninitializedData, pImgOptHdr->AddressOfEntryPoint, pImgOptHdr->BaseOfCode, pImgOptHdr->BaseOfData); switch (pImgOptHdr->Subsystem) { case IMAGE_SUBSYSTEM_MMOSA : i = 5; break; case IMAGE_SUBSYSTEM_POSIX_CUI : i = 4; break; case IMAGE_SUBSYSTEM_WINDOWS_CUI : i = 3; break; case IMAGE_SUBSYSTEM_WINDOWS_GUI : i = 2; break; case IMAGE_SUBSYSTEM_NATIVE : i = 1; break; default : i = 0; } fprintf(InfoStream, " ----- new -----\n% 8lX image base\n% 8lX section alignment\n% 8lX file alignment\n% 8hX subsystem (%s)\n", pImgOptHdr->ImageBase, pImgOptHdr->SectionAlignment, pImgOptHdr->FileAlignment, pImgOptHdr->Subsystem, SubsystemName[i]); j = (WORD) sprintf(version, "%hX.%hX", pImgOptHdr->MajorOperatingSystemVersion, pImgOptHdr->MinorOperatingSystemVersion); if (j > 8) { j = 8; } for (j = (WORD) (8-j); j; j--) { fputc(' ', InfoStream); } fprintf(InfoStream, "%s operating system version\n", version); j = (WORD) sprintf(version, "%hX.%hX", pImgOptHdr->MajorImageVersion, pImgOptHdr->MinorImageVersion); if (j > 8) { j = 8; } for (j = (WORD) (8-j); j; j--) { fputc(' ', InfoStream); } fprintf(InfoStream, "%s image version\n", version); j = (WORD) sprintf(version, "%hX.%hX", pImgOptHdr->MajorSubsystemVersion, pImgOptHdr->MinorSubsystemVersion); if (j > 8) { j = 8; } for (j = (WORD) (8-j); j; j--) { fputc(' ', InfoStream); } fprintf(InfoStream, "%s subsystem version\n% 8lX size of image\n% 8lX size of headers\n% 8lX checksum\n", version, pImgOptHdr->SizeOfImage, pImgOptHdr->SizeOfHeaders, pImgOptHdr->CheckSum); fprintf(InfoStream, "% 8lX size of stack reserve\n% 8lX size of stack commit\n% 8lX size of heap reserve\n% 8lX size of heap commit\n%", pImgOptHdr->SizeOfStackReserve, pImgOptHdr->SizeOfStackCommit, pImgOptHdr->SizeOfHeapReserve, pImgOptHdr->SizeOfHeapCommit); for (i=0; iDataDirectory[i].VirtualAddress, pImgOptHdr->DataDirectory[i].Size, DirectoryEntryName[i] ); } fputc('\n', InfoStream); } void DumpIncrDb ( const char *szFile ) /*++ Routine Description: Dumps the contents of the incr db file. Arguments: szFile - name of the file to dump. Return Value: None. --*/ { DumpIncrSymbolTable(pimage->pst); DumpIncrHashTable(pimage->pst->pht, &pimage->pst->blkStringTable); DumpIncrSectionMap(&pimage->secs, szFile); DumpIncrLibMap(pimage->libs.plibHead, szFile); } void DumpIncrSectionMap( PSECS psecs, const char *szFile ) /*++ Routine Description: Dump the image map. Arguments: None. Return Value: None. --*/ { ENM_SEC enm_sec; ENM_GRP enm_grp; ENM_DST enm_dst; CHAR buf[128]; if (!fSectionMap) { return; } fprintf(InfoStream, "\nSECTION MAP DUMP\n"); InitEnmSec(&enm_sec, psecs); while (FNextEnmSec(&enm_sec)) { if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)enm_sec.psec, sizeof(SEC))) { sprintf(buf, "invalid ptr to SEC 0x%lx found\n",enm_sec.psec); FileWarning(szFile, buf); goto InvalidSectionMap; } DumpIncrPSEC(enm_sec.psec); InitEnmGrp(&enm_grp, enm_sec.psec); while (FNextEnmGrp(&enm_grp)) { if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)enm_grp.pgrp, sizeof(GRP))) { sprintf(buf, "invalid ptr to GRP 0x%lx found\n",enm_grp.pgrp); FileWarning(szFile, buf); goto InvalidSectionMap; } DumpIncrPGRP(enm_grp.pgrp); InitEnmDst(&enm_dst, enm_grp.pgrp); while (FNextEnmDst(&enm_dst)) { if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)enm_dst.pcon, sizeof(CON))) { sprintf(buf, "invalid ptr to CON 0x%lx found\n",enm_dst.pcon); FileWarning(szFile, buf); goto InvalidSectionMap; } DumpIncrPCON(enm_dst.pcon); } } } InvalidSectionMap: fprintf(InfoStream, "\n"); } void DumpIncrLibMap( PLIB plibHead, const char *szFile ) /*++ Routine Description: Dump the driver map. Arguments: None. Return Value: None. --*/ { ENM_LIB enm_lib; ENM_MOD enm_mod; ENM_SRC enm_src; CHAR buf[128]; if (!fLibMap) { return; } fprintf(InfoStream, "\nLIBRARY MAP OF IMAGE\n"); InitEnmLib(&enm_lib, plibHead); while (FNextEnmLib(&enm_lib)) { if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)enm_lib.plib, sizeof(LIB))) { sprintf(buf, "invalid ptr to LIB 0x%lx found\n",enm_lib.plib); FileWarning(szFile, buf); goto InvalidLibMap; } DumpIncrPLIB(enm_lib.plib); InitEnmMod(&enm_mod, enm_lib.plib); while (FNextEnmMod(&enm_mod)) { if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)enm_mod.pmod, sizeof(MOD))) { sprintf(buf, "invalid ptr to MOD 0x%lx found\n",enm_mod.pmod); FileWarning(szFile, buf); goto InvalidLibMap; } DumpIncrPMOD(enm_mod.pmod); InitEnmSrc(&enm_src, enm_mod.pmod); while (FNextEnmSrc(&enm_src)) { if (!FValidPtrInfo((DWORD) pvIncr, (DWORD)FileLen, (DWORD)enm_src.pcon, sizeof(CON))) { sprintf(buf, "invalid ptr to CON 0x%lx found\n",enm_src.pcon); FileWarning(szFile, buf); goto InvalidLibMap; } DumpIncrPCON(enm_src.pcon); } } } InvalidLibMap: fprintf(InfoStream, "\n"); } void DumpIncrPSEC( PSEC psec) /*++ Routine Description: Dump an image section. Arguments: psec - section to dump. Return Value: None. --*/ { assert(psec); fprintf(InfoStream, "\n==========\n"); fprintf(InfoStream, "section=%.8s, isec=%.4x\n", psec->szName, psec->isec); fprintf(InfoStream, "rva= %.8lX ", psec->rva); fprintf(InfoStream, "foPad= %.8lx ", psec->foPad); fprintf(InfoStream, "cbRawData= %.8lx ", psec->cbRawData); fprintf(InfoStream, "foRawData= %.8lx\n", psec->foRawData); fprintf(InfoStream, "foLinenum= %.8lx ", psec->foLinenum); fprintf(InfoStream, "flags= %.8lx ", psec->flags); fprintf(InfoStream, "cLinenum= %.4x\n", psec->cLinenum); fflush(InfoStream); } void DumpIncrPGRP( PGRP pgrp) /*++ Routine Description: Dump an image group. Arguments: pgrp - group to dump. Return Value: None. --*/ { fprintf(InfoStream, "\n----------\n"); fprintf(InfoStream, "\n group=%s\n", pgrp->szName); fflush(InfoStream); } void DumpIncrPLIB( PLIB plib) /*++ Routine Description: Dump a library. Arguments: plib - library to dump. Return Value: None. --*/ { fprintf(InfoStream, "\n==========\n"); fprintf(InfoStream, "library=%s\n", plib->szName); fprintf(InfoStream, "foIntMemST=%.8lx ", plib->foIntMemSymTab); fprintf(InfoStream, "csymIntMem=%.8lx ", plib->csymIntMem); fprintf(InfoStream, "flags= %.8lx\n", plib->flags); fprintf(InfoStream, "TimeStamp= %s", ctime((time_t *)&plib->TimeStamp)); fflush(InfoStream); } void DumpIncrPMOD( PMOD pmod) /*++ Routine Description: Dump a module. Arguments: pmod - module to dump. Return Value: None. --*/ { fprintf(InfoStream, "\n----------\n"); fprintf(InfoStream, " module=%s, ", SzOrigFilePMOD(pmod)); if (FIsLibPMOD(pmod)) { fprintf(InfoStream, "foMember=%.8lx\n", pmod->foMember); } else { fprintf(InfoStream, "szNameMod=%s\n", pmod->szNameMod); } fprintf(InfoStream, "foSymTable=%.8lx ", pmod->foSymbolTable); fprintf(InfoStream, "csymbols= %.8lx ", pmod->csymbols); fprintf(InfoStream, "cbOptHdr= %.8lx\n", pmod->cbOptHdr); fprintf(InfoStream, "flags= %.8lx ", pmod->flags); fprintf(InfoStream, "ccon= %.8lx ", pmod->ccon); fprintf(InfoStream, "icon= %.8lx ", pmod->icon); fprintf(InfoStream, "TimeStamp= (%.8lx) %s", pmod->TimeStamp, ctime((time_t *)&pmod->TimeStamp)); fprintf(InfoStream, "cbFile= %.8lx ", pmod->cbFile); fprintf(InfoStream, "HdrTime= %.8lx\n", pmod->HdrTimeStamp); fflush(InfoStream); } void DumpIncrPCON( PCON pcon) /*++ Routine Description: Dump a contribution. Arguments: pcon - contribution to dump. Return Value: None. --*/ { fprintf(InfoStream, "\n contributor: flags=%.8lx, rva=%.8lx, module=%s\n", pcon->flags, pcon->rva, SzObjNamePCON(pcon)); fprintf(InfoStream, "cbRawData= %.8lx ", pcon->cbRawData); fprintf(InfoStream, "foRawDataD=%.8lx ", pcon->foRawDataDest); fprintf(InfoStream, "chksum =%.8lx ", pcon->chksumComdat); fprintf(InfoStream, "selComdat= %.4x\n", pcon->selComdat); fprintf(InfoStream, "cbPad = %.4x\n", pcon->cbPad); fflush(InfoStream); } void DumpIncrHashTable( PHT pht, void *pvBlk) /*++ Routine Description: Dump a hash table. Arguments: pht - hast table pvBlk - ptr to string table Return Value: None. --*/ { PELEMENT pelement; DWORD ibucket; assert(pht); if (!fHashTable) { return; } fprintf(InfoStream, "\nHASH TABLE DUMP\n"); for (ibucket = 0; ibucket < pht->cbuckets; ibucket++) { assert(ibucket / pht->celementInChunk < pht->cchunkInDir); pelement = pht->rgpchunk[ibucket / pht->celementInChunk]-> rgpelement[ibucket % pht->celementInChunk]; fprintf(InfoStream, "bucket = %u\n", ibucket); while (pelement) { fprintf(InfoStream, " %s\n", pht->SzFromPv(pelement->pv, pvBlk)); pelement = pelement->pelementNext; } } fprintf(InfoStream, "\n"); } void DumpIncrSymbolTable ( PST pst ) /*++ Routine Description: Dump the symbol table. Arguments: pst - symbol table Return Value: None. --*/ { PPEXTERNAL rgpexternal; DWORD cexternal; DWORD i; if (!fSymbols) { return; } fprintf(InfoStream, "\nSYMBOL TABLE DUMP\n"); // put them out sorted by name rgpexternal = RgpexternalByName(pst); cexternal = Cexternal(pst); for (i = 0; i < cexternal; i++) { PEXTERNAL pext; char *szSym; const char *szType; pext = rgpexternal[i]; szSym = SzOutputSymbolName(SzNamePext(pext, pst), TRUE); if (!pext->pcon || (pext->Flags & EXTERN_IGNORE)) { fprintf(InfoStream, "%s off=%.8lx flags=%.8lx %s name=%s\n", pext->Offset ? "THUNK" : " ", pext->Offset, pext->Flags, "IGNR", szSym); continue; } if (pext->pcon && !FIsLibPCON(pext->pcon)) { if (ISFCN(pext->ImageSymbol.Type)) { szType = "FUNC"; } else { szType = "DATA"; } } else { szType = " "; } fprintf(InfoStream, "%s off=%.8lx flags=%.8lx %s name=%s\n", pext->Offset ? "THUNK" : " ", pext->Offset, pext->Flags, szType, szSym); if (szSym != SzNamePext(pext, pst)) { free(szSym); } } } #endif // DBG