// loadomf.cxx - load // // Copyright 1989-94, Microsoft Corporation // // Purpose: // // 10-Nov-94 BryanT // Merge in NT changes. // Change the load code so we first call the Shell to see // if the symbol load s/b deferred or ignored. // Functions changed: OLStart, OLLoadOmf // New Functions: OLContinue (the part of OLStart that determines) // what type of file we're looking at). // LoadOmfForReal (the part of OLLoadOmf that actually // performs the symbol load) // Replace all the hexg param's with lpexg's. We have it everywhere // it's needed and every function calls LLLock/LLUnlock to get it... // Define UnloadOmf. // // 07-Jan-96 BryanT // #include "shinc.hpp" #pragma hdrstop #include // The exe file information static LSZ lszFName; // file name static LONG lfaBase; // offset of directory info from end of file static ULONG cDir; // number of directory entries static OMFDirEntry *lpdss; // pointer to directory table static OMFDirEntry *lpdssCur; // pointer to current directory entry static LONG lcbFilePos = 0; static WORD csegExe = 0; static PIMAGE_SECTION_HEADER SecHdr; static unsigned int SecCount; static DWORD ImageAlign; static WORD btAlign = 0; // Alignment bit typedef struct _PDB_INFO { SIG sig; AGE age; char sz[_MAX_PATH]; } PDB_INFO; static PDB_INFO pdbInfo; LOCAL SHE CheckSignature (INT , OMFSignature *, PDB_INFO *); LOCAL SHE OLStart (LPEXG); LOCAL BOOL OLMkSegDir (WORD, LPSGD *, LPSGE *, LPEXG); LOCAL SHE OLLoadTypes (LPEXG); LOCAL SHE OLLoadSym (LPEXG); LOCAL SHE OLLoadSrc (LPEXG); LOCAL SHE OLGlobalPubs (LPEXG); LOCAL SHE OLGlobalSym (LPEXG); LOCAL SHE OLStaticSym (LPEXG); LOCAL SHE OLLoadSegMap (LPEXG); LOCAL SHE OLLoadNameIndex (LPEXG); LOCAL LPCH OLRwrSrcMod (OMFSourceModule *); LOCAL BOOL OLLoadHashSubSec (LPGST, LPB); LOCAL SHE NB10LoadOmf (LPEXG, HEXG); LOCAL SHE LoadPdb (LPEXG, PDB_INFO *); LOCAL SHE NB10LoadModules (LPEXG, ULONG*, HEXG); LOCAL VOID LoadSymbols(HPDS, HEXG, BOOL); LOCAL SHE LoadOmfForReal(LPEXG, HEXG); LOCAL SHE LoadFpo(LPEXG, int, PIMAGE_DEBUG_DIRECTORY); LOCAL SHE LoadPdata(LPEXG, int, ULONG, ULONG, ULONG, ULONG, BOOL); LOCAL SHE LoadOmap(LPEXG, int, PIMAGE_DEBUG_DIRECTORY); LOCAL int OLMkModule(LPEXG, HEXG); LOCAL SHE OLValidate(int, void *, LPSTR); #define MAX_SEARCH_PATH_LEN 512 // This is hard-coded name of the registry location where setup will put the // pdb path. This should be changed when we have a general mechanism for the debugger // dlls to get the IDE's root registry key name. static TCHAR szDefaultKeyName[] = "Software\\Microsoft\\Developer\\Build System\\Components\\Platforms\\" #ifdef TARGMAC68K "Macintosh" #elif defined (TARGMACPPC) "Power Macintosh" #elif defined (TARGALPHA) "Win32 (ALPHA)" #elif defined (_MIPS_) "Win32 (MIPS)" #else // This must be x86. "Win32 (x86)" #endif "\\Directories" ; static TCHAR szPdbDirs[] = "Pdb Dirs"; TCHAR rgSearchPath[MAX_SEARCH_PATH_LEN]; BOOL fQueriedRegistry = FALSE; // CFile is a simple helper class which will force its file to be closed // as soon as the CFile object is destructed. class CFile { public: INT m_hfile; CFile() { m_hfile = -1; } void ReInit() { if (m_hfile != -1) { SYClose(m_hfile); m_hfile = -1; } } INT Open(LSZ lszName) { m_hfile = SYOpen(lszName); return(m_hfile); } ~CFile() { if(m_hfile != -1) { SYClose (m_hfile); m_hfile = -1; } } operator INT&() { return m_hfile; } }; VOID LoadDefered( HEXG hexg ) { LoadSymbols(hpdsCur, hexg, TRUE); return; } VOID UnloadDefered( HEXG hexg ) { return; } // OLLoadOmf - load omf information from exe // // error = OLLoadOmf (hexg) // // Entry hexg = handle to executable information struct // // Exit // // Returns An error code suitable for errno. SHE OLLoadOmf( HEXG hexg, VLDCHK *vldChk, DWORD dllLoadAddress ) { SHE sheRet = sheNone; LSZ lszFname = NULL; LPEXG lpexg = (LPEXG) LLLock (hexg); if (lpexg->fOmfLoaded) { return sheNone; } // Query the shell and see if we should load this one now. lszFname = lpexg->lszName; if (!SYGetDefaultShe(lszFname, &sheRet)) { if (lpexg->lszAltName) { lszFname = lpexg->lszAltName; if (!SYGetDefaultShe(lszFname, &sheRet)) { SYGetDefaultShe(NULL, &sheRet); lszFname = lpexg->lszName; } } else { SYGetDefaultShe(NULL, &sheRet); } } // SYGetDefaultShe is expected to return one of the following // values: // // sheSuppressSyms - Don't load, just keep track of the name/start // sheNoSymbols - This module has already been processed and there are no symbols // sheDeferSyms - Defer symbol loading until needed // sheSymbolsConverted - The symbols are already loaded // sheNone - Go ahead and load the symbols now. // Regardless of the load type, save some stuff lpexg->LoadAddress = dllLoadAddress; lpexg->ulTimeStamp = vldChk->TimeDateStamp; lpexg->ulCheckSum = vldChk->CheckSum; lpexg->debugData.she = sheRet; if ((sheRet != sheNone) && (sheRet != sheSymbolsConverted)) { if (sheRet == sheNoSymbols) { lpexg->fOmfMissing = TRUE; } else if (sheRet == sheSuppressSyms) { lpexg->fOmfSkipped = TRUE; } else if ( sheRet == sheDeferSyms) { lpexg->fOmfDefered = TRUE; // ShAddBkgrndSymbolLoad(hexg); } LLUnlock (hexg); return sheRet; } LLUnlock(hexg); // If we made it this far, we must load the symbols LoadSymbols(hpdsCur, hexg, FALSE); return(sheRet); } // LoadSymbols // // Purpose: This function loads a defered module's symbols. After // the symbols are loaded the shell is notified of the completed // module load. // // Input: hpds - Handle to process to load the symbols for // hexg - exg handle for the module to be added // fNotifyShell - Should shell be notified on load. // // Return: None VOID LoadSymbols( HPDS hpds, HEXG hexg, BOOL fNotifyShell ) { SHE sheRet; HEXE hexe; LPEXG lpexg = NULL; LPPDS lppds = NULL; LPEXE lpexe = NULL; LPSTR lpname = NULL; HPDS hpdsLast; // EnterCriticalSection( &CsSymbolLoad ); // find the exe for this exg hexe = NULL; while ((hexe=SHGetNextExe(hexe))) { lpexe = (LPEXE) LLLock(hexe); if ((hexg == lpexe->hexg) && (hpds == lpexe->hpds)) { break; } else { LLUnlock(hexe); } } if (!hexe) { goto done; // didn't find a hexg/hpds match } // lock down the necessary data structures lpexg = (LPEXG) LLLock(hexg); if (!lpexg) { goto done; } lppds = (LPPDS) LLLock(lpexe->hpds); if (!lppds) { goto done; } // mark the module as being loaded lpexg->fOmfLoading = TRUE; // LeaveCriticalSection( &CsSymbolLoad ); // load the symbols (yes, pass both lpexg and hexg. // OlMkModule needs hexg for creating the lpmds) sheRet = LoadOmfForReal(lpexg, hexg); // EnterCriticalSection( &CsSymbolLoad ); switch (sheRet) { case sheNoSymbols: lpexg->fOmfMissing = TRUE; break; case sheSuppressSyms: lpexg->fOmfSkipped = TRUE; break; case sheNone: case sheSymbolsConverted: lpexg->fOmfLoaded = TRUE; break; default: lpexg->fOmfMissing = TRUE; break; } if (fNotifyShell) { // // notify the shell that symbols have been loaded // if (lpexg->lszAltName) { lpname = lpexg->lszAltName; } else { lpname = lpexg->lszName; } // hpdsLast = SHChangeProcess(hpds, TRUE); hpdsLast = SHChangeProcess(hpds); DLoadedSymbols(sheRet, lppds->hpid, lpname); // SHChangeProcess(hpdsLast, FALSE); SHChangeProcess(hpdsLast); } // update the module flags lpexg->fOmfDefered = FALSE; lpexg->fOmfLoading = FALSE; done: // LeaveCriticalSection( &CsSymbolLoad ); // free resources if (lpexe) { if (lppds) { LLUnlock(lpexe->hpds); } LLUnlock(hexe); } if (lpexg) { LLUnlock(hexg); } return; } // LoadOmfForReal // // Purpose: Here's where the symbolic is actually loaded from the image. // // Input: lpexg - The pointer to the exg structure // hexg - The handle of the exg structure // // Return: Standard she error codes. SHE LoadOmfForReal( LPEXG lpexg, HEXG hexg ) { SHE sheRet = sheNone; SHE sheRetSymbols = sheNone; WORD cbMod = 0; ULONG cMod; ULONG iDir; csegExe = 0; __try { // Open and verify the exe. sheRet = sheRetSymbols = OLStart(lpexg); // If there was an error, bail. // (sheNone doesn't mean "no symbols", it means "error None") if ((sheRet != sheNone) && (sheRet != sheSymbolsConverted)) { goto returnhere; } if (lpexg->ppdb) { sheRet = NB10LoadOmf(lpexg, hexg); goto returnhere; } btAlign = (WORD)(lfaBase & 1); lpdssCur = lpdss; iDir = 0; // Load up the module table. // First, count up how many sstModule entries we have. The spec // requires all the sstModules to be before any other. while (iDir < cDir && lpdssCur->SubSection == sstModule) { lpdssCur++; iDir++; } // If there's no modules, there's no sense continuing. if (iDir == 0) { sheRet = sheNoSymbols; goto returnhere; } lpexg->cMod = cMod = iDir; // Allocate the rgMod buffer and load each dir entry in. lpexg->rgMod = (LPMDS)MHAlloc((cMod+2) * sizeof(MDS)); if (lpexg->rgMod == NULL) { sheRet = sheOutOfMemory; goto returnhere; } memset(lpexg->rgMod, 0, sizeof(MDS)*(cMod+2)); lpexg->rgMod[cMod+1].imds = (WORD) -1; // Go through the list of directory entries and process all of the sstModule records. for (iDir = 0, lpdssCur = lpdss; iDir < cMod; iDir += 1, lpdssCur++) { if (!OLMkModule (lpexg, hexg)) { sheRet = sheOutOfMemory; goto returnhere; } } // Set up map of modules. This function is used to create a map // of contributer segments to modules. This is then used when // determining which module is used for an address. lpexg->csgd = csegExe; if (!OLMkSegDir (csegExe, &lpexg->lpsgd, &lpexg->lpsge, lpexg)) { sheRet = sheOutOfMemory; goto returnhere; } // continue through the directory entries for (; iDir < cDir; lpdssCur++, iDir++) { if (lpdssCur->cb == 0) { // if nothing in this entry continue; } switch (lpdssCur->SubSection) { case sstSrcModule: sheRet = OLLoadSrc(lpexg); break; case sstAlignSym: sheRet = OLLoadSym(lpexg); break; case sstGlobalTypes: sheRet = OLLoadTypes(lpexg); break; case sstGlobalPub: sheRet = OLGlobalPubs(lpexg); break; case sstGlobalSym: sheRet = OLGlobalSym(lpexg); break; case sstSegMap: sheRet = OLLoadSegMap(lpexg); break; case sstLibraries: // ignore this table case sstMPC: // until this table is implemented case sstSegName: // until this table is implemented case sstModule: // Handled elsewhere break; case sstFileIndex: sheRet = OLLoadNameIndex(lpexg); break; case sstStaticSym: sheRet = OLStaticSym(lpexg); break; default: sheRet = sheCorruptOmf; break; } if (sheRet == sheCorruptOmf) { sheRet = sheNoSymbols; } } } __except(EXCEPTION_EXECUTE_HANDLER) { sheRet = sheNoSymbols; } returnhere: if (SecHdr) { MHFree(SecHdr); SecHdr = NULL; SecCount = 0; } return sheRet; } LOCAL SHE NB10LoadOmf( LPEXG lpexg, HEXG hexg ) { SHE sheRet = sheNone; WORD cbMod = 0; ULONG ModCnt = 0; btAlign = (WORD)(lfaBase & 1); // we need to allocate a buffer large enough to read the largest module // table entry if ((sheRet = NB10LoadModules (lpexg, &ModCnt, hexg)) != sheNone) { return sheRet; } if (ModCnt == 0L) { // if no symbols found return sheNoSymbols; } lpexg->cMod = ModCnt; if(!DBIOpenGlobals(lpexg->pdbi, &(lpexg->pgsiGlobs)) || !DBIOpenPublics(lpexg->pdbi, &(lpexg->pgsiPubs))) { return sheOutOfMemory; } if((sheRet = OLLoadSegMap(lpexg)) != sheNone || (sheRet = OLLoadNameIndex(lpexg)) != sheNone) { return sheRet; } return sheRet; } // OLOPENDBGFILE // // PURPOSE: // Helper function to open the .dbg file it the debug info from the // original one has been stripped. We might want to make this a callback // implemented by the kernel. // // INPUT: // lpszModName -- name of the .dll being loaded // lpszDbgName -- Buffer to which the path of the .dbg file is written // lpVldChk -- pointer to a validity check structure which is used // to ascertain that we have the correct .dbg file. // OUTPUT: // File handle if a .dbg file was opened succesufully. // -1 otherwise // // EXCEPTIONS: // // IMPLEMENTATION: // Look for the .dbg file in the following order // a) The directory which the dll was found in. // b) Along the _NT_ALT_SYMBOL_PATH environment variable // c) Along the _NT_SYMBOL_PATH environment variable. // d) in the %SystemRoot%\symbols\dll directory HFILE OLOpenDbgFile( LSZ lpszModName, LSZ lpszDbgName, LPVLDCHK lpVldChk ) { INT hFile = -1; TCHAR szDrive[_MAX_CVDRIVE]; TCHAR szDir[_MAX_CVDIR]; TCHAR szName[_MAX_CVFNAME]; TCHAR szExt[_MAX_CVEXT]; TCHAR szSearchPath[MAX_SEARCH_PATH_LEN]; TCHAR szRelName[_MAX_CVFNAME]; // typically something like symbols\dll\foo.dbg TCHAR szTmpDbgName[_MAX_CVPATH]; TCHAR szDbgExt[] = __T(".DBG"); TCHAR szSymbols[] = __T("SYMBOLS\\"); // We will look for the .dbg file along these environment variables in // order. TCHAR * rgszDbgPath[] = { __T("_NT_ALT_SYMBOL_PATH"), __T("_NT_SYMBOL_PATH"), __T("SystemRoot"), }; TCHAR pathSep = __T('\\'); // Separates dir names within the PATH env variable assert(lpszModName != NULL); assert(lpszDbgName != NULL); _tsplitpath(lpszModName, szDrive, szDir, szName, szExt); // First look for the dbg file in the same directory as the .dll _tmakepath(szTmpDbgName, szDrive, szDir, szName, szDbgExt); hFile = (INT) SYOpen(szTmpDbgName); if (hFile == -1) { TCHAR * lpszCurr = szRelName; int i; // create the symbols\ext\file.dbg part of the filename _tcscpy(lpszCurr, szSymbols); lpszCurr += _tcslen(szSymbols); _tcscpy(lpszCurr, (szExt + 1)); lpszCurr += _tcslen(szExt + 1); *lpszCurr++ = pathSep; _tcscpy(lpszCurr, szName); lpszCurr += _tcslen(szName); _tcscpy(lpszCurr, szDbgExt); for (i = 0; i < sizeof(rgszDbgPath)/sizeof(rgszDbgPath[0]); i++) { DWORD cbStr; if ((cbStr = GetEnvironmentVariable(rgszDbgPath[i], szSearchPath, MAX_SEARCH_PATH_LEN)) != 0) { if(cbStr > MAX_SEARCH_PATH_LEN) { assert(FALSE); // Shouldn't happen in nature. return -1 ; } if ((cbStr = SearchPath(szSearchPath, szRelName, NULL, _MAX_CVPATH, szTmpDbgName, NULL)) != 0) { if (cbStr > _MAX_CVPATH) { return -1; } hFile = SYOpen(szTmpDbgName); if (hFile != -1) { break; } } } } } if (hFile != -1) { IMAGE_SEPARATE_DEBUG_HEADER sephdr; // Read the header and do the validity check. Note that it we accept // the checksum not matching if the timestamp matches. This is for // cases where the NT setup program patches dlls at setup time. // the file is re-checksumed but the timedatestamp on the dll and // dbg will still be the same. if (SYReadFar(hFile, (LPB)&sephdr, sizeof(sephdr)) != sizeof(sephdr) || (sephdr.Signature != IMAGE_SEPARATE_DEBUG_SIGNATURE) || ((lpVldChk->CheckSum != sephdr.CheckSum) && (lpVldChk->TimeDateStamp != sephdr.TimeDateStamp) ) ) { SYClose(hFile); hFile = -1; } else { _tcscpy(lpszDbgName, szTmpDbgName); } } return (hFile); } #define cbFileMax (_MAX_CVFNAME + _MAX_CVEXT) // OLStart - get exe debug information // // Purpose: To open the file specified and get the offset to the directory // and get the base that everyone is offset from. // // Entry hexg = handle to exe to get info for // // Exit lfaBase = base offset of debug information // cDir = count of number of directory entries // lpdss = directory entries // // Returns file open status #define UNKNOWN_IMAGE 0 #define DOS_IMAGE 1 #define VCDBG_IMAGE 2 #define WIN16_IMAGE 3 #define PE_IMAGE 4 #define ROM_IMAGE 5 #define NTDBG_IMAGE 6 LOCAL SHE OLStart( LPEXG lpexg ) { SHE sheRet; ULONG DirSize; OMFSignature Signature; OMFDirHeader DirHeader; IMAGE_DOS_HEADER doshdr; // Old format MZ header IMAGE_NT_HEADERS pehdr; IMAGE_ROM_HEADERS romhdr; IMAGE_SEPARATE_DEBUG_HEADER sepHdr; PIMAGE_FILE_HEADER pfile; IMAGE_DEBUG_DIRECTORY dbgDir; IMAGE_DEBUG_DIRECTORY cvDbgDir; DWORD cbData; DWORD dllLoadAddress; DWORD ul; VLDCHK vldChk; LSZ szFName = NULL; char szNewName[_MAX_PATH]; int ImageType = UNKNOWN_IMAGE; DWORD cDebugDir; DWORD offDbgDir; DWORD cObjs; CFile hfile; if (lpexg->lszAltName) { szFName = lpexg->lszAltName; } else { szFName = lpexg->lszName; } // lpexg->lszDebug is the file where we pull the symbolic from. dllLoadAddress = lpexg->LoadAddress; vldChk.TimeDateStamp = lpexg->ulTimeStamp; vldChk.CheckSum = lpexg->ulCheckSum; ImageAlign = 0; hfile.Open(szFName); if (hfile == -1) { retry: if (lpexg->lszDebug) { MHFree(lpexg->lszDebug); lpexg->lszDebug = 0; } hfile = SYFindExeFile(szFName, szNewName, sizeof(szNewName), &vldChk, (PFNVALIDATEEXE)OLValidate); if (hfile == -1) { sheRet = sheFileOpen; goto ReturnHere; } lpexg->lszDebug = _strdup(szNewName); } else { // Assert that the input file is OK. We only get here // when using the file name as passed in from the DM. sheRet = OLValidate(hfile, &vldChk, NULL); if ((sheRet == sheBadCheckSum) || (sheRet == sheBadTimeStamp) || (sheRet == sheNoSymbols)) { hfile.ReInit(); goto retry; } lpexg->lszDebug = _strdup(szFName); } // Now figure out what we're looking at. Here are the possible formats: // 1. Image starts with a DOS MZ header and e_lfanew is zero // - Standard DOS exe. // 2. Image starts with a DOS NE header and e_lfanew is non-zero // - If e_lfanew points to a PE header, this is a PE image // - Otherwise, it's probably a Win16 image. // 3. Image starts with a PE header. // - Image is a PE image built with -stub:none // 4. Image starts with a ROM PE header. // - Image is a ROM image. If characteristics flag // doesn't have IMAGE_FILE_DEBUG_STRIPPED set, the debug // directory is at the start of rdata. // 5. Image starts with a DBG file header // - Image is an NT DBG file (symbols only). // 6. None of the signatures match. // - This may be a Languages DBG file. Seek to the end // of the file and attempt to read the CV signature/offset // from there (a Languages DBG file is made by chopping an // image at the start of the debug data and writing the end // in a new file. In the CV format, the signature/offset at the // end of the file points back to the beginning of the data). if ((SYSeek(hfile, 0, SEEK_SET) == 0) && sizeof(doshdr) == SYReadFar (hfile, (LPB) &doshdr, sizeof(doshdr))) { switch (doshdr.e_magic) { case IMAGE_DOS_SIGNATURE: // This is a DOS NE header. if (doshdr.e_lfanew == 0) { ImageType = DOS_IMAGE; } else { if ((SYSeek(hfile, doshdr.e_lfanew, SEEK_SET) == doshdr.e_lfanew) && (SYReadFar(hfile, (LPB) &pehdr, sizeof(pehdr)) == sizeof(pehdr))) { if (pehdr.Signature == IMAGE_NT_SIGNATURE) { ImageType = PE_IMAGE; ImageAlign = pehdr.OptionalHeader.SectionAlignment; pfile = &pehdr.FileHeader; } else { ImageType = WIN16_IMAGE; } } else { // No luck reading from the image. Must be corrupt. sheRet = sheCorruptOmf; goto ReturnHere; } } break; case IMAGE_NT_SIGNATURE: // This image is a PE image w/o a stub. Read in the header. if ((SYSeek(hfile, 0, SEEK_SET) == 0) && (SYReadFar(hfile, (LPB) &pehdr, sizeof(pehdr)) == sizeof(pehdr))) { ImageType = PE_IMAGE; ImageAlign = pehdr.OptionalHeader.SectionAlignment; pfile = &pehdr.FileHeader; } else { // No luck reading from the image. Must be corrupt. sheRet = sheCorruptOmf; goto ReturnHere; } break; case IMAGE_SEPARATE_DEBUG_SIGNATURE: // This image is an NT DBG file. ImageType = NTDBG_IMAGE; if ((SYSeek(hfile, 0, SEEK_SET) != 0) || (SYReadFar(hfile, (LPB) &sepHdr, sizeof(sepHdr)) != sizeof(sepHdr))) { // No luck reading from the image. Must be corrupt. sheRet = sheCorruptOmf; goto ReturnHere; } // If there's no debug info, we can't continue further. if (sepHdr.DebugDirectorySize / sizeof(dbgDir) == 0) { sheRet = sheNoSymbols; goto ReturnHere; } break; default: // None of the above. See if it's a ROM image. // Note: The only way we think we're working on a ROM image // is if the optional header size is correct. Not really foolproof. if ((SYSeek(hfile, 0, SEEK_SET) == 0) && (SYReadFar(hfile, (LPB) &romhdr, sizeof(romhdr)) == sizeof(romhdr))) { if (romhdr.FileHeader.SizeOfOptionalHeader == IMAGE_SIZEOF_ROM_OPTIONAL_HEADER) { // If we think we have a ROM image, make sure there's // symbolic to look for. if (romhdr.FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) { sheRet = sheNoSymbols; goto ReturnHere; } else { ImageType = ROM_IMAGE; pfile = &romhdr.FileHeader; } } else { ImageType = VCDBG_IMAGE; } } else { // No luck reading from the image. Must be corrupt. sheRet = sheCorruptOmf; goto ReturnHere; } break; } } else { // No luck reading from the image. Must be corrupt. sheRet = sheCorruptOmf; goto ReturnHere; } // Now, we know what kind of image we're looking at. // Either obtain the pointer to the CV debug data (and other // relevant data along the way) or convert whatever we do find // to CV debug data. lpexg->fSymConverted = FALSE; switch (ImageType) { case DOS_IMAGE: case VCDBG_IMAGE: case WIN16_IMAGE: // Easy. Skip to the end and look back. ul = SYSeek (hfile, -((LONG)sizeof (OMFSignature)), SEEK_END); if ((sheRet = CheckSignature (hfile, &Signature, &pdbInfo)) == sheNone) { // seek to the base and read in the new key lfaBase = SYSeek (hfile, -Signature.filepos, SEEK_END); sheRet = CheckSignature(hfile, &Signature, &pdbInfo); cbData = ul - lfaBase; } // If the CV signature is invalid, see if we can convert what we do // have (perhaps a .sym file?) if (sheRet != sheNone) { if (pfConvertSymbolsForImage) { lpexg->lpbData = (LPB) (pfConvertSymbolsForImage)( (HANDLE)(int)hfile, lpexg->lszDebug); } // If no symbols converted, bail. Nothing more we can do. if (lpexg->lpbData == 0) { sheRet = sheNoSymbols; goto ReturnHere; } Signature = *(OMFSignature*)lpexg->lpbData; lpexg->fSymConverted = TRUE; } break; case PE_IMAGE: case ROM_IMAGE: // In both the PE image and ROM image, we're past the FILE // and OPTIONAL header by now. Walk through the section // headers and pick up interesting data. We make a // a copy of the section headers in case we need to // reconstruct the original values for a Lego'd image cObjs = pfile->NumberOfSections; SecCount = pfile->NumberOfSections; ul = SecCount * sizeof(IMAGE_SECTION_HEADER); // Note: SecHdr is free'd by LoadOmfForReal. SecHdr = (PIMAGE_SECTION_HEADER) MHAlloc(ul); if (!SecHdr) { sheRet = sheNoSymbols; goto ReturnHere; } if (SYReadFar(hfile, (LPB) SecHdr, ul) != ul) { sheRet = sheNoSymbols; goto ReturnHere; } if (ImageType == PE_IMAGE) { // look for the .pdata section on RISC platforms if ((pfile->Machine == IMAGE_FILE_MACHINE_ALPHA) || (pfile->Machine == IMAGE_FILE_MACHINE_R4000) || (pfile->Machine == IMAGE_FILE_MACHINE_R10000) || (pfile->Machine == IMAGE_FILE_MACHINE_POWERPC)) { for (ul=0; ul < cObjs; ul++) { if (strcmp((char *) SecHdr[ul].Name, ".pdata") == 0) { LoadPdata(lpexg, hfile, dllLoadAddress, pehdr.OptionalHeader.ImageBase, SecHdr[ul].PointerToRawData, SecHdr[ul].SizeOfRawData, FALSE); break; } } } // If the debug info has been stripped, close this handle // and look for the .dbg file... if (pfile->Characteristics & IMAGE_FILE_DEBUG_STRIPPED){ // The debug info has been stripped from this image. // Close this file handle and look for the .DBG file. hfile.ReInit(); ImageType = UNKNOWN_IMAGE; MHFree(SecHdr); SecHdr = 0; goto retry; } // Find the debug directory and the number of entries in it. // For PE images, walk the section headers looking for the // one that's got the debug directory. for (ul=0; ul < cObjs; ul++) { if ((SecHdr[ul].VirtualAddress <= pehdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) && (pehdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress < SecHdr[ul].VirtualAddress + SecHdr[ul].SizeOfRawData)) { // This calculation really isn't necessary nor is the range test // above. Like ROM images, it s/b at the beginning of .rdata. The // only time it won't be is when a pre NT 1.0 image is split sym'd // creating a new MISC debug entry and relocating the directory // to the DEBUG section... offDbgDir = SecHdr[ul].PointerToRawData + pehdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress - SecHdr[ul].VirtualAddress; cDebugDir = pehdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size / sizeof(IMAGE_DEBUG_DIRECTORY); break; } } } else { // For ROM images, there's much less work to do. We only // need to search for the .rdata section. There's no need // to look for .pdata (it will never exist) or worry about // stripped debug symbolic (that case was already handled above). for (ul=0; ul < cObjs; ul++) { if (!strcmp((char *)SecHdr[ul].Name, ".rdata")) { offDbgDir = SecHdr[ul].PointerToRawData; if (SYSeek(hfile, offDbgDir, SEEK_SET) != (LONG) offDbgDir) { sheRet = sheCorruptOmf; goto ReturnHere; } // The linker stores an empty directory entry for ROM // images to terminate the list. cDebugDir = 0; do { if (SYReadFar(hfile, (LPB) &dbgDir, sizeof(dbgDir)) != sizeof(dbgDir)) { sheRet = sheNoSymbols; goto ReturnHere; } cDebugDir++; } while (dbgDir.Type != 0); break; } } } // Assuming we haven't exhausted the list of section headers, // we should have the debug directory now. if (ul == cObjs) { // We didn't find any CV info. Try converting what we did // find. if (pfConvertSymbolsForImage) { lpexg->lpbData = (LPB)(pfConvertSymbolsForImage)( (HANDLE)(int)hfile, lpexg->lszDebug); } if (lpexg->lpbData == 0) { sheRet = sheNoSymbols; goto ReturnHere; } Signature = *(OMFSignature*)lpexg->lpbData; lpexg->fSymConverted = TRUE; break; } // Now search the debug directory for relevant entries. if (SYSeek(hfile, offDbgDir, SEEK_SET) != (LONG) offDbgDir) { sheRet = sheCorruptOmf; goto ReturnHere; } ZeroMemory(&cvDbgDir, sizeof(cvDbgDir) ); for (ul=0; ul < cDebugDir; ul++) { if (SYReadFar(hfile, (LPB) &dbgDir, sizeof(dbgDir)) != sizeof(dbgDir)) { sheRet = sheCorruptOmf; goto ReturnHere; } if (dbgDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW) { cvDbgDir = dbgDir; continue; } if (dbgDir.Type == IMAGE_DEBUG_TYPE_FPO) { LoadFpo(lpexg, hfile, &dbgDir); } if (dbgDir.Type == IMAGE_DEBUG_TYPE_OMAP_FROM_SRC || dbgDir.Type == IMAGE_DEBUG_TYPE_OMAP_TO_SRC) { LoadOmap(lpexg, hfile, &dbgDir); } } if (cvDbgDir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) { // We didn't find any CV info. Try converting what we did // find. if (pfConvertSymbolsForImage) { lpexg->lpbData = (LPB)(pfConvertSymbolsForImage)( (HANDLE)(int)hfile, lpexg->lszDebug); } if (lpexg->lpbData == 0) { sheRet = sheNoSymbols; goto ReturnHere; } Signature = *(OMFSignature*)lpexg->lpbData; lpexg->fSymConverted = TRUE; } else { // Otherwise, calculate the location/size so we can load it. lfaBase = cvDbgDir.PointerToRawData; cbData = cvDbgDir.SizeOfData; if (SYSeek(hfile, lfaBase, SEEK_SET) != lfaBase) { sheRet = sheCorruptOmf; goto ReturnHere; } if ((sheRet = CheckSignature (hfile, &Signature, &pdbInfo)) != sheNone) { goto ReturnHere; } } break; case NTDBG_IMAGE: SecCount = sepHdr.NumberOfSections; if (sepHdr.SectionAlignment) { ImageAlign = sepHdr.SectionAlignment; } ul = SecCount * sizeof(IMAGE_SECTION_HEADER); // Note: SecHdr is free'd by LoadOmfForReal. SecHdr = (PIMAGE_SECTION_HEADER) MHAlloc(ul); if (!SecHdr) { sheRet = sheNoSymbols; goto ReturnHere; } // Read in the section headers. if (SYReadFar(hfile, (LPB) SecHdr, ul) != ul) { sheRet = sheCorruptOmf; goto ReturnHere; } // Skip over the exported names. SYSeek(hfile, sepHdr.ExportedNamesSize, SEEK_CUR); // Look for the interesting debug data. ZeroMemory(&cvDbgDir, sizeof(cvDbgDir)); for (ul=0; ul < (sepHdr.DebugDirectorySize/sizeof(dbgDir)); ul++) { if (SYReadFar(hfile, (LPB) &dbgDir, sizeof(dbgDir)) != sizeof(dbgDir)) { sheRet = sheCorruptOmf; goto ReturnHere; } if (dbgDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW) { cvDbgDir = dbgDir; continue; } if (dbgDir.Type == IMAGE_DEBUG_TYPE_FPO) { LoadFpo(lpexg, hfile, &dbgDir); } // UNDONE: We can eliminate this load for images // that we've already processed the pdata from the // real image... if (dbgDir.Type == IMAGE_DEBUG_TYPE_EXCEPTION) { LoadPdata(lpexg, hfile, dllLoadAddress, sepHdr.ImageBase, dbgDir.PointerToRawData, dbgDir.SizeOfData, TRUE); } if ((dbgDir.Type == IMAGE_DEBUG_TYPE_OMAP_FROM_SRC) || (dbgDir.Type == IMAGE_DEBUG_TYPE_OMAP_TO_SRC)) { LoadOmap(lpexg, hfile, &dbgDir); } } if (cvDbgDir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) { if (pfConvertSymbolsForImage) { lpexg->lpbData = (LPB)(pfConvertSymbolsForImage)( (HANDLE)(int)hfile, lpexg->lszDebug); } if (lpexg->lpbData == 0) { sheRet = sheNoSymbols; goto ReturnHere; } Signature = *(OMFSignature*)lpexg->lpbData; lpexg->fSymConverted = TRUE; } else { lfaBase = cvDbgDir.PointerToRawData; cbData = cvDbgDir.SizeOfData; if (SYSeek(hfile, lfaBase, SEEK_SET) != lfaBase) { sheRet = sheCorruptOmf; goto ReturnHere; } if ((sheRet = CheckSignature (hfile, &Signature, &pdbInfo)) != sheNone) { goto ReturnHere; } } break; default: // No way we should get here, but assert if we do. assert(FALSE); } // O.K. Everything's loaded. If we're looking at a pdb file, // load it and get out. if ((*(LONG UNALIGNED *)(Signature.Signature)) == '01BN') { // No need to keep the debug image filename any longer since // LoadPdb will store the pdb filename anyway. // UNDONE: If we get the name from the .dbg file, the pdb should // be in the same subdir. Can we optimize this load to account // for it? Should we still clear lszDebug? if (lpexg->lszDebug) { MHFree(lpexg->lszDebug); lpexg->lszDebug = 0; } sheRet = LoadPdb(lpexg, &pdbInfo); } else { // No PDB. // If the symbols weren't synthesized, allocate a buffer and // copy them in... if (!lpexg->fSymConverted) { HANDLE hMap; HANDLE hFileMap; hFileMap = CreateFile(lpexg->lszDebug, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFileMap != INVALID_HANDLE_VALUE) { hMap = CreateFileMapping(hFileMap, NULL, PAGE_WRITECOPY, 0, 0, NULL); if (hMap != NULL) { // Map in the symbolic (only). SYSTEM_INFO si; DWORD dwAllocStart, dwAllocDiff; GetSystemInfo(&si); dwAllocStart = lfaBase & (~(si.dwAllocationGranularity - 1)); dwAllocDiff = lfaBase - dwAllocStart; lpexg->pvSymMappedBase = MapViewOfFile(hMap, FILE_MAP_COPY, 0, dwAllocStart, cbData + dwAllocDiff); if (lpexg->pvSymMappedBase) { lpexg->lpbData = ((BYTE *) lpexg->pvSymMappedBase) + dwAllocDiff; } CloseHandle(hMap); } CloseHandle(hFileMap); } if (lpexg->lpbData == NULL) { // Unable to map the image. Read the whole blob in from disk. lpexg->lpbData = (LPB)MHAlloc(cbData); if (!lpexg->lpbData) { sheRet = sheNoSymbols; goto ReturnHere; } if ((SYSeek (hfile, lfaBase, SEEK_SET) != lfaBase) || (SYReadFar (hfile, lpexg->lpbData, cbData) != cbData)) { // Failed to read in the data... Must be corrupt. MHFree(lpexg->lpbData); lpexg->lpbData = 0; sheRet = sheCorruptOmf; goto ReturnHere; } } } // We now have a pointer to the CV debug data. Setup the // pointers to the CV Directory header and return. LPB lpb; lpexg->ppdb = NULL; lpexg->ptpi = NULL; lpexg->pdbi = NULL; lpb = Signature.filepos + lpexg->lpbData; DirHeader = *(OMFDirHeader *) lpb; cDir = DirHeader.cDir; // check to make sure somebody has not messed with omf structure if (DirHeader.cbDirEntry != sizeof (OMFDirEntry)) { sheRet = sheCorruptOmf; goto ReturnHere; } lpdss = (OMFDirEntry *)(lpb + sizeof(DirHeader)); if (lpexg->fSymConverted) { sheRet = sheSymbolsConverted; goto ReturnHere; } sheRet = sheNone; } ReturnHere: lpexg->debugData.she = sheRet; return sheRet; } SHE LoadFpo( LPEXG lpexg, int hfile, PIMAGE_DEBUG_DIRECTORY dbgDir ) { LONG fpos; fpos = SYTell(hfile); lpexg->fIsRisc = FALSE; if (SYSeek(hfile, dbgDir->PointerToRawData, SEEK_SET) != (LONG) dbgDir->PointerToRawData) { return(sheCorruptOmf); } if(!(lpexg->debugData.lpFpo = (PFPO_DATA) MHAlloc(dbgDir->SizeOfData))) return sheOutOfMemory; SYReadFar(hfile, (LPB) lpexg->debugData.lpFpo, dbgDir->SizeOfData); lpexg->debugData.cRtf = dbgDir->SizeOfData / SIZEOF_RFPO_DATA; SYSeek(hfile, fpos, SEEK_SET); return sheNone; } SHE LoadOmap( LPEXG lpexg, int hfile, PIMAGE_DEBUG_DIRECTORY dbgDir ) { LONG fpos; LPVOID lpOmap; DWORD dwCount; fpos = SYTell(hfile); if (SYSeek(hfile, dbgDir->PointerToRawData, SEEK_SET) != (LONG) dbgDir->PointerToRawData) { return(sheCorruptOmf); } if(!(lpOmap = (LPVOID) MHAlloc(dbgDir->SizeOfData))) return sheOutOfMemory; SYReadFar(hfile, (LPB) lpOmap, dbgDir->SizeOfData); dwCount = dbgDir->SizeOfData / sizeof(OMAP); SYSeek(hfile, fpos, SEEK_SET); if(dbgDir->Type == IMAGE_DEBUG_TYPE_OMAP_FROM_SRC) { lpexg->debugData.lpOmapFrom = (LPOMAP) lpOmap; lpexg->debugData.cOmapFrom = dwCount; } else if(dbgDir->Type == IMAGE_DEBUG_TYPE_OMAP_TO_SRC) { lpexg->debugData.lpOmapTo = (LPOMAP) lpOmap; lpexg->debugData.cOmapTo = dwCount; } else { MHFree(lpOmap); } return sheNone; } SHE LoadPdata( LPEXG lpexg, int hfile, ULONG loadAddress, ULONG imageBase, ULONG start, ULONG size, BOOL fDbgFile ) { ULONG cFunc; LONG diff; ULONG index; PIMAGE_RUNTIME_FUNCTION_ENTRY rf; PIMAGE_RUNTIME_FUNCTION_ENTRY tf; PIMAGE_FUNCTION_ENTRY dbgRf; LONG fpos; lpexg->debugData.lpRtf = NULL; lpexg->debugData.cRtf = 0; if(size == 0) { return sheNone; // No data to read... Just return. } if(fDbgFile) { cFunc = size / sizeof(IMAGE_FUNCTION_ENTRY); diff = 0; } else { cFunc = size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY); diff = loadAddress - imageBase; } lpexg->fIsRisc = TRUE; fpos = SYTell(hfile); if (SYSeek(hfile, start, SEEK_SET) != (LONG) start) { return(sheCorruptOmf); } if(fDbgFile) { if(!(dbgRf = (PIMAGE_FUNCTION_ENTRY) MHAlloc(size))) { return sheOutOfMemory; } SYReadFar(hfile, (LPB)dbgRf, size); size = cFunc * sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY); if(!(tf = rf = (PIMAGE_RUNTIME_FUNCTION_ENTRY) MHAlloc(size))) { MHFree(dbgRf); return sheOutOfMemory; } for(index=0; indexBeginAddress; tf++,index++) { ; } if(index < cFunc) { cFunc = index; size = index * sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY); if(!(rf = (PIMAGE_RUNTIME_FUNCTION_ENTRY) MHRealloc(rf, size))) { return sheOutOfMemory; } } if (diff != 0) { for (index=0; indexdebugData.lpRtf = rf; lpexg->debugData.cRtf = cFunc; SYSeek(hfile, fpos, SEEK_SET); return sheNone; } // CheckSignature - check file signature // // she = CheckSignature (INT hfile, OMFSignature *pSig) // // Entry hfile = handle to file // pSig = location where signature should be written to // ppdb = PDB information. // // Exit none // // Return sheNoSymbols if exe has no signature // sheMustRelink if exe has NB00 to NB06 or NB07 (qcwin) signature // sheNotPacked if exe has NB08 signature // sheNone if exe has NB09 signature // sheFutureSymbols if exe has NB10 to NB99 signature LOCAL SHE CheckSignature( INT hfile, OMFSignature *pSig, PDB_INFO *ppdb ) { UINT uSig; if ((SYReadFar (hfile, (LPB) pSig, sizeof (*pSig)) != sizeof (*pSig)) || (pSig->Signature[0] != 'N') || (pSig->Signature[1] != 'B') || (!isdigit(pSig->Signature[2])) || (!isdigit(pSig->Signature[3]))) { return sheNoSymbols; } switch (*(LONG UNALIGNED *)(pSig->Signature)) { case '50BN': case '60BN': case '70BN': return sheMustRelink; case '80BN': return sheNotPacked; case '01BN': SYReadFar(hfile, (LPB)ppdb, sizeof(PDB_INFO)); case '90BN': return sheNone; default: return sheFutureSymbols; } } // OLMkSegDir - MakeSegment directory // // Entry // // Returns non-zero for success BOOL OLMkSegDir( WORD csgd, LPSGD *lplpsgd, LPSGE *lplpsge, LPEXG lpexg ) { LPSGD lpsgd; LPSGE lpsge = NULL; int *lpisge; int csgc = 0; int isge = 0; int isgd = 0; DWORD iMod; if (!(lpsgd = (LPSGD) MHAlloc (csgd * sizeof (SGD)))) { return FALSE; } if (!(lpisge = (int *) MHAlloc (csgd * sizeof (int)))) { MHFree(lpsgd); return FALSE; } memset(lpsgd, 0, csgd * sizeof(SGD)); memset(lpisge, 0, csgd * sizeof(int)); // Count the number of contributers per segment for (iMod = 1; iMod <= lpexg->cMod; iMod++) { LPMDS lpmds = &lpexg->rgMod[iMod]; int cseg = lpmds->csgc; int iseg = 0; int isegT = 0; for (iseg = 0; iseg < cseg; iseg++) { isegT = lpmds->lpsgc [ iseg ].seg; if (isegT != 0) { lpsgd [ isegT - 1 ].csge += 1; csgc += 1; } } } // Allocate subtable for each all segments lpsge = (LPSGE) MHAlloc (csgc * sizeof (SGE)); if (!lpsge) { MHFree (lpsgd); MHFree (lpisge); return FALSE; } // Set up sgd's with pointers into appropriate places in the sge table isge = 0; for (isgd = 0; isgd < (int) csgd; isgd++) { lpsgd[ isgd ].lpsge = lpsge + isge; isge += lpsgd[ isgd ].csge; } // Fill in the sge table for (iMod = 1; iMod <= lpexg->cMod; iMod += 1) { LPMDS lpmds = &lpexg->rgMod[iMod]; int cseg = lpmds->csgc; int iseg = 0; for (iseg = 0; iseg < cseg; iseg++) { int isgeT = lpmds->lpsgc[ iseg ].seg - 1; if (isgeT != -1) { lpsgd[ isgeT ].lpsge[ lpisge[ isgeT ]].sgc = lpmds->lpsgc[ iseg ]; lpsgd[ isgeT ].lpsge[ lpisge[ isgeT ]].hmod = (HMOD)lpmds; lpisge[ isgeT ] += 1; } } } MHFree (lpisge); *lplpsge = lpsge; *lplpsgd = lpsgd; return TRUE; } // OLMkModule - make module entries for module // // Entry lpexg - Supplies the pointer to the EXG structure for current exe // hexg - Supplies the handle EXG structure // // Returns non-zero for success LOCAL int OLMkModule( LPEXG lpexg, HEXG hexg ) { LSZ lszModName; LPMDS lpmds; LPB lpbName; WORD cbName; WORD i; OMFModule *pMod; // Point to the OMFModule table. This structure describes the name and // segments for the current Module being processed. There is a one to one // correspondance of modules to object files. pMod = (OMFModule *) (lpexg->lpbData + lpdssCur->lfo); // Point to the name field in the module table. This location is variable // and is dependent on the number of contributuer segments for the module. lpbName = ((LPB)pMod) + offsetof (OMFModule, SegInfo[0]) + (sizeof (OMFSegDesc) * pMod->cSeg); cbName = *((LPB)lpbName)++; lszModName = (LPCH) MHAlloc (cbName + 1); memmove(lszModName, lpbName, cbName); *(lszModName + cbName) = 0; lpmds = &lpexg->rgMod[lpdssCur->iMod]; lpmds->imds = lpdssCur->iMod; lpmds->hexg = hexg; lpmds->name = lszModName; // step thru making the module entries // // NOTENOTE -- This can most likely be optimized as the data // is just being copied from the debug data. lpmds->csgc = pMod->cSeg; lpmds->lpsgc = (LPSGC)MHAlloc ( pMod->cSeg * sizeof ( SGC ) ); for ( i = 0; i < pMod->cSeg; i++ ) { if ( pMod->SegInfo[i].Seg > csegExe ) { csegExe = pMod->SegInfo[i].Seg; } lpmds->lpsgc[i].seg = pMod->SegInfo[i].Seg; lpmds->lpsgc[i].off = pMod->SegInfo[i].Off; lpmds->lpsgc[i].cb = pMod->SegInfo[i].cbSeg; } return TRUE; } LOCAL SHE NB10LoadModules( LPEXG lpexg, ULONG* pcMods, HEXG hexg ) { Mod* pmod = NULL; ULONG cMod = 0; IMOD imod; // First count up the number of Mods while (DBIQueryNextMod(lpexg->pdbi, pmod, &pmod) && pmod) { if(!ModQueryImod(pmod, &imod)) return sheCorruptOmf; if(imod > *pcMods) cMod = imod; } *pcMods = cMod; // Got the count. Allocate rgMod. lpexg->rgMod = (LPMDS) MHAlloc((cMod+2) * sizeof(MDS)); if (lpexg->rgMod == NULL) { return sheOutOfMemory; } memset(lpexg->rgMod, 0, sizeof(MDS)*(cMod+2)); lpexg->rgMod[cMod+1].imds = (WORD) -1; // Now fill in the blanks. pmod = NULL; for (; cMod; cMod--) { LPMDS lpmds; LPCH lpchName; CB cbName; DBIQueryNextMod(lpexg->pdbi, pmod, &pmod); if(!ModQueryImod(pmod, &imod)) return sheCorruptOmf; lpmds = &lpexg->rgMod[imod]; lpmds->imds = imod; lpmds->pmod = pmod; lpmds->hexg = hexg; if(!ModQueryName(pmod, NULL, &cbName)) return sheCorruptOmf; lpmds->name = (LSZ) MHAlloc(cbName); if(!lpmds->name) return sheOutOfMemory; if(!ModQueryName(pmod, lpmds->name, &cbName)) return sheCorruptOmf; if(!ModSetPvClient(pmod, lpmds)) return sheCorruptOmf; } return sheNone; } LOCAL BOOL OLLoadHashTable( LPB lpbData, ULONG cbTable, LPSHT lpsht, BOOL fDWordChains ) { WORD ccib = 0; LPUL rgib = NULL; LPUL rgcib = NULL; ULONG cbHeader = 0; LPB lpHashStart = lpbData; memset(lpsht, 0, sizeof(SHT)); ccib = *(WORD *)lpbData; // First, get the hash bucket counts lpbData += 4; // the 2 byte hash count and 2 bytes padding rgib = (LPUL) lpbData; // cbHeader = sizeof (ccib) + sizeof (WORD); // Read the segment table offsets and counts // if (!(rgib = (LPUL) MHAlloc (ccib * sizeof (ULONG)))) { // return FALSE; // } // if (!(rgcib = (LPUL) MHAlloc (ccib * sizeof (ULONG)))) { // MHFree(rgib); // return FALSE; // } // memmove(rgib, lpbData, ccib * sizeof(ULONG)); lpbData += ccib * sizeof(ULONG); rgcib = (LPUL) lpbData; // UNDONE: If/when non-DWORD packing isn't interesting, the // extra allocations above can be elminated and rgcib/rgib // can just be pointes into the lpbData buffer. // if (fDWordChains) { // memmove(rgcib, lpbData, ccib * sizeof (ULONG)); lpbData += ccib * sizeof(ULONG); // } else { // // If not DWORD chains, copy from the end of the word // // array to the end of the DWORD array so later code // // doesn't have to special case it... // // LPW rgw = (LPW) rgcib; // WORD icib = ccib; // // memmove(rgcib, lpbData, ccib * sizeof (WORD)); // lpbData += ccib * sizeof(WORD); // // while (icib > 0) { // icib -= 1; // rgcib [ icib ] = (ULONG) rgw [ icib ]; // } // } // Subtract off what we've processed already. cbTable -= (lpbData - lpHashStart); lpsht->ccib = ccib; lpsht->rgib = rgib; lpsht->rgcib = rgcib; lpsht->lpalm = BuildALM(FALSE, btAlign, lpbData, cbTable, cbAlign); if (lpsht->lpalm == NULL) { // MHFree(rgib); // MHFree(rgcib); return FALSE; } // *lplpbData = lpbData; return TRUE; } LOCAL BOOL OLLoadHashSubSec( LPGST lpgst, LPB lpbData ) { LPB lpbTbl = NULL; OMFSymHash hash; ULONG cbSymbol; BOOL fRet = TRUE; LPSHT lpshtName = &lpgst->shtName; LPSHT lpshtAddr = &lpgst->shtAddr; memset(lpshtAddr, 0, sizeof(SHT)); memset(lpshtName, 0, sizeof(SHT)); hash = *(OMFSymHash *)lpbData; lpbData += sizeof (OMFSymHash); cbSymbol = hash.cbSymbol; lpgst->lpalm = BuildALM(TRUE, btAlign, lpbData, cbSymbol, cbAlign); if (lpgst->lpalm == NULL) { return FALSE; } lpbData += cbSymbol; // if (hash.symhash == 6 || hash.symhash == 10) { if (hash.symhash == 10) { fRet = OLLoadHashTable(lpbData, hash.cbHSym, &lpgst->shtName, hash.symhash == 10); lpgst->shtName.HashIndex = hash.symhash; } lpbData += hash.cbHSym; // if (hash.addrhash == 8 || hash.addrhash == 12) { if (hash.addrhash == 12) { fRet = OLLoadHashTable(lpbData, hash.cbHAddr, &lpgst->shtAddr, hash.addrhash == 12); lpgst->shtAddr.HashIndex = hash.addrhash; } return fRet; } // OLLoadTypes - load compacted types table // // Input: lpexg - Pointer to exg we're working on. // // Returns: - An error code LOCAL SHE OLLoadTypes( LPEXG lpexg ) { LPB pTyp; LPB pTypes; OMFTypeFlags flags; DWORD cType = 0; DWORD *rgitd = NULL; DWORD ibType = 0; pTyp = pTypes = lpexg->lpbData + lpdssCur->lfo; flags = *(OMFTypeFlags *) pTypes; pTypes += sizeof(OMFTypeFlags); cType = *(ULONG *) pTypes; pTypes += sizeof(ULONG); if (!cType) { return sheNone; } // Point to the array of pointers to types rgitd = (DWORD *) pTypes; // Move past them pTypes += cType * sizeof(ULONG); // Read in the type index table ibType = pTypes - pTyp; lpexg->lpalmTypes = BuildALM (FALSE, btAlign, pTypes, lpdssCur->cb - ibType, cbAlignType); if (lpexg->lpalmTypes == NULL) { return sheOutOfMemory; } lpexg->rgitd = rgitd; lpexg->citd = cType; return sheNone; } // OLLoadSym - load symbol information // // error = OLLoadSym (pMod) // // Entry lpexg - Pointer to exg structure to use. // // Returns sheNone if symbols loaded __inline SHE OLLoadSym( LPEXG lpexg ) { // UNDONE: If we run into problems with a stale VC, we'll have to // revert to reading the file on demand. The expectation is that the // mapped I/O code will just work. // lpexg->rgMod[lpdssCur->iMod].symbols = NULL; lpexg->rgMod[lpdssCur->iMod].symbols = lpexg->lpbData + lpdssCur->lfo; lpexg->rgMod[lpdssCur->iMod].cbSymbols = lpdssCur->cb; lpexg->rgMod[lpdssCur->iMod].ulsym = lpdssCur->lfo; return sheNone; } __inline SHE OLLoadSrc( LPEXG lpexg ) { lpexg->rgMod[lpdssCur->iMod].hst = (HST) (lpexg->lpbData + lpdssCur->lfo); return sheNone; } __inline SHE OLGlobalPubs( LPEXG lpexg ) { SHE she = sheNone; if (!OLLoadHashSubSec (&lpexg->gstPublics, lpexg->lpbData + lpdssCur->lfo)) { she = sheOutOfMemory; } return she; } __inline SHE OLGlobalSym( LPEXG lpexg ) { SHE she = sheNone; if (!OLLoadHashSubSec (&lpexg->gstGlobals, lpexg->lpbData + lpdssCur->lfo)) { she = sheOutOfMemory; } return she; } LOCAL SHE OLLoadSegMap( LPEXG lpexg ) { LPB lpb; SHE sheRet = sheNone; if(lpexg->pdbi) { CB cb; // load from the pdb if(!DBIQuerySecMap(lpexg->pdbi, NULL, &cb) || !(lpb = (LPB) MHAlloc (cb))) { sheRet = sheOutOfMemory; } else if(!DBIQuerySecMap(lpexg->pdbi, lpb, &cb)) { MHFree(lpb); lpb = NULL; sheRet = sheOutOfMemory; } } else { lpb = lpexg->lpbData + lpdssCur->lfo; } lpexg->lpgsi = lpb; return sheRet; } LOCAL SHE OLLoadNameIndex( LPEXG lpexg ) { OMFFileIndex * lpefi; WORD cmod = 0; WORD cfile = 0; CB cb; if(lpexg->pdbi) { if(!DBIQueryFileInfo(lpexg->pdbi, 0, &cb)) { return sheNoSymbols; } else if(!(lpefi = (OMFFileIndex *) MHAlloc(cb))) { return sheOutOfMemory; } else if(!DBIQueryFileInfo(lpexg->pdbi, (PB)lpefi, &cb)) { MHFree(lpefi); return sheNoSymbols; } } else { lpefi = (OMFFileIndex *)(lpexg->lpbData + lpdssCur->lfo); cb = (CB)lpdssCur->cb; } cmod = lpefi->cmodules; // Make sure we found as many sstModule entries as we should have. assert(cmod == lpexg->cMod); // lpexg->cmod = cmod; cfile = lpefi->cfilerefs; lpexg->lpefi = (LPB) lpefi; lpexg->rgiulFile = lpefi->modulelist; lpexg->rgculFile = &lpefi->modulelist [cmod]; lpexg->rgichFile = (LPUL) &lpefi->modulelist [cmod * 2]; lpexg->lpchFileNames = (LPCH) &lpefi->modulelist [cmod * 2 + cfile * 2]; lpexg->cbFileNames = (ULONG)(cb - ((LPB)lpexg->lpchFileNames - (LPB)lpefi + 1)); return sheNone; } LOCAL SHE OLStaticSym( LPEXG lpexg ) { SHE she = sheNone; if (!OLLoadHashSubSec (&lpexg->gstStatics, lpexg->lpbData + lpdssCur->lfo)) { she = sheOutOfMemory; } return she; } SHE mpECToShe[] = { sheNone, // EC_OK sheNoSymbols, // EC_USAGE (invalid parameter of call order) sheOutOfMemory, // EC_OUT_OF_MEMORY (-, out of RAM) sheNoSymbols, // EC_FILE_SYSTEM (pdb name, can't write file, out of disk, etc) shePdbNotFound, // EC_NOT_FOUND (PDB file not found) shePdbBadSig, // EC_INVALID_SIG (PDB::OpenValidate() and its clients only) shePdbInvalidAge, // EC_INVALID_AGE (PDB::OpenValidate() and its clients only) sheNoSymbols, // EC_PRECOMP_REQUIRED (obj name, Mod::AddTypes only) sheNoSymbols, // EC_OUT_OF_TI (pdb name, TPI::QueryTiForCVRecord() only) sheNoSymbols, // EC_NOT_IMPLEMENTED sheNoSymbols, // EC_V1_PDB (pdb name, PDB::Open() only) shePdbOldFormat, // EC_FORMAT (accessing pdb with obsolete format) }; // Get the name of the pdb file (OMF name) for the specified exe. If the // LoadPdb hasn't been called on this exe OR it's not NB10, this will return // an empty string! Note: There will only be an lszPdbName if there was // an error loading the pdb VOID LOADDS SHPdbNameFromExe( LSZ lszExe, LSZ lszPdbName, UINT cbMax ) { HEXE hexe; // Zero out the destination memset(lszPdbName, 0, cbMax); // Look up the exe if (hexe = SHGethExeFromName(lszExe)) { HEXG hexg = ((LPEXE)LLLock(hexe))->hexg; LPEXG lpexg = (LPEXG)LLLock(hexg); // Only copy if there's a pdbname if (lpexg->lszPdbName) { _tcsncpy(lszPdbName, lpexg->lszPdbName, cbMax); } // Clean up LLUnlock(hexg); LLUnlock(hexe); } } LOCAL SHE LoadPdb( LPEXG lpexg, PDB_INFO *ppdb ) { EC ec; char szExePath[_MAX_PATH]; char szPDBOut[cbErrMax]; char *pcEndOfPath; BOOL fOpenValidate; assert(lpexg); // figure out the home directory of the exe file - pass that along to // OpenValidate this will direct to dbi to search for it in that // directory if it fails to find the pdb. _fullpath(szExePath, lpexg->lszName, _MAX_PATH); pcEndOfPath = _tcsrchr(szExePath, '\\'); *pcEndOfPath = '\0'; // null terminate it *szPDBOut = '\0'; HKEY hSectionKey = NULL; DWORD nType = REG_SZ; DWORD nSize = sizeof(rgSearchPath); if (!fQueriedRegistry) { RegOpenKeyEx(HKEY_CURRENT_USER, szDefaultKeyName, 0, KEY_READ, &hSectionKey); if ((hSectionKey == NULL) || (RegQueryValueEx(hSectionKey, (char *)szPdbDirs, NULL, &nType, (LPBYTE)rgSearchPath, &nSize) != ERROR_SUCCESS) ) { rgSearchPath[0] = 0; } fQueriedRegistry = TRUE; } fOpenValidate = PDBOpenValidateEx(ppdb->sz, szExePath, rgSearchPath, pdbRead, ppdb->sig, ppdb->age, &ec, szPDBOut, &(lpexg->ppdb)); if (!fOpenValidate) { // Save the name of the pdb with the error if (!(lpexg->lszPdbName = (LSZ)MHAlloc(_tcslen(szPDBOut) + 1))) { return sheOutOfMemory; } _tcscpy(lpexg->lszPdbName, szPDBOut); return mpECToShe[ec]; } // Store the name of the pdb in lszDebug. char *szPdb = PDBQueryPDBName(lpexg->ppdb, (char *)szPDBOut); assert(szPdb); assert(lpexg->lszDebug == NULL); // Save the name of the pdb. if (!(lpexg->lszDebug = (LSZ)MHAlloc(_tcslen(szPDBOut) + 1))) { return sheOutOfMemory; } _tcscpy(lpexg->lszDebug, szPDBOut); if(!PDBOpenTpi(lpexg->ppdb, pdbRead, &(lpexg->ptpi))) { ec = PDBQueryLastError(lpexg->ppdb, NULL); return mpECToShe[ ec ]; } if(!PDBOpenDBI(lpexg->ppdb, pdbRead, lpexg->lszName, &(lpexg->pdbi))) { ec = PDBQueryLastError(lpexg->ppdb, NULL); return mpECToShe[ ec ]; } if (!STABOpen(&(lpexg->pstabUDTSym))) return sheOutOfMemory; return sheNone; } // Routine Description: // // This routine is used to validate that the debug information // in a file matches the debug information requested // // Arguments: // // hFile - Supplies the file handle to be validated // lpv - Supplies a pointer to the information to used in vaidation // // Return Value: // // TRUE if matches and FALSE otherwise LOCAL SHE OLValidate( int hFile, void * lpv, LPSTR lpszErrText ) { VLDCHK * pVldChk = (VLDCHK *) lpv; IMAGE_NT_HEADERS peHdr; IMAGE_DOS_HEADER exeHdr; int fPeExe = FALSE; int fPeDbg = FALSE; IMAGE_SEPARATE_DEBUG_HEADER sepHdr; char rgch[4]; if (lpszErrText) { *lpszErrText = 0; } // Read in a dos exe header if ((SYSeek(hFile, 0, SEEK_SET) != 0) || (SYReadFar( hFile, (LPB) &exeHdr, sizeof(exeHdr)) != sizeof(exeHdr))) { return sheNoSymbols; } // See if it is a dos exe hdr if (exeHdr.e_magic == IMAGE_DOS_SIGNATURE) { if ((SYSeek(hFile, exeHdr.e_lfanew, SEEK_SET) == exeHdr.e_lfanew) && (SYReadFar(hFile, (LPB) &peHdr, sizeof(peHdr)) == sizeof(peHdr))) { if (peHdr.Signature == IMAGE_NT_SIGNATURE) { fPeExe = TRUE; } } } else if (exeHdr.e_magic == IMAGE_NT_SIGNATURE) { fPeExe = TRUE; } else if (exeHdr.e_magic == IMAGE_SEPARATE_DEBUG_SIGNATURE) { fPeDbg = TRUE; } if (fPeExe) { if (peHdr.FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED) { return sheNoSymbols; } if (peHdr.OptionalHeader.CheckSum != pVldChk->CheckSum) { if (lpszErrText) { sprintf(lpszErrText,"*** WARNING: symbols checksum is wrong 0x%08x 0x%08x", peHdr.OptionalHeader.CheckSum,pVldChk->CheckSum); } return sheBadCheckSum; } if ((pVldChk->TimeDateStamp != 0xffffffff) && (peHdr.FileHeader.TimeDateStamp != pVldChk->TimeDateStamp)) { if (lpszErrText) { sprintf(lpszErrText,"*** WARNING: symbols timestamp is wrong 0x%08x 0x%08x", peHdr.FileHeader.TimeDateStamp,pVldChk->TimeDateStamp); } return sheBadTimeStamp; } } else if (fPeDbg) { if ((SYSeek(hFile, 0, SEEK_SET) != 0) || (SYReadFar(hFile, (LPB) &sepHdr, sizeof(sepHdr)) != sizeof(sepHdr))) { return sheNoSymbols; } if (sepHdr.CheckSum != pVldChk->CheckSum) { if (lpszErrText) { sprintf(lpszErrText,"*** WARNING: symbols checksum is wrong 0x%08x 0x%08x", sepHdr.CheckSum,pVldChk->CheckSum); } return sheBadCheckSum; } } else { if ((SYSeek(hFile, -8, SEEK_END) == -1) || (SYReadFar(hFile, (LPB)rgch, sizeof(rgch)) != sizeof(rgch))) { return sheNoSymbols; } if ((rgch[0] != 'N') || (rgch[1] != 'B')) { return sheNoSymbols; } } return sheNone; } BOOL OLUnloadOmf( LPEXG lpexg ) { ULONG i; // Cleanup the Module table; for (i = 0; i < lpexg->cMod; i++) { KillMdsNode(&lpexg->rgMod[i]); } if (lpexg->rgMod) { MHFree(lpexg->rgMod); lpexg->rgMod = NULL; lpexg->cMod = 0; } // module map info if (lpexg->lpsgd) { MHFree(lpexg->lpsgd); lpexg->lpsgd = NULL; lpexg->csgd = 0; } // if (lpexg->lpsge) { MHFree(lpexg->lpsge); lpexg->lpsge = NULL; } if (lpexg->lpbData) { // Depending on how we got the data, free it. if (lpexg->pvSymMappedBase) { // Mapped view of file. UnmapViewOfFile(lpexg->pvSymMappedBase); lpexg->pvSymMappedBase = NULL; } else { if (lpexg->fSymConverted) { // Converted from coff/sym file VirtualFree(lpexg->lpbData, 0, MEM_RELEASE); } else { // Read the blob in from disk MHFree(lpexg->lpbData); } } lpexg->lpbData = NULL; } // OSDebug 4 FPO info if (lpexg->debugData.lpRtf) { MHFree(lpexg->debugData.lpRtf); lpexg->debugData.lpRtf = NULL; } if (lpexg->debugData.lpOmapFrom) { MHFree(lpexg->debugData.lpOmapFrom); lpexg->debugData.lpOmapFrom = NULL; } if (lpexg->debugData.lpOmapTo) { MHFree(lpexg->debugData.lpOmapTo); lpexg->debugData.lpOmapTo = NULL; } if (lpexg->debugData.lpSecStart) { MHFree(lpexg->debugData.lpSecStart); lpexg->debugData.lpSecStart = NULL; } // Segment map info if (lpexg->lpgsi) { if (lpexg->ppdb) { MHFree (lpexg->lpgsi); } lpexg->lpgsi = NULL; } // Source Module information if (lpexg->lpefi) { if (lpexg->ppdb) { MHFree(lpexg->lpefi); } lpexg->lpefi = NULL; } // Type Info array lpexg->citd = 0; lpexg->rgitd = NULL; // Publics, Globals, and Statics KillGst(&lpexg->gstPublics); KillGst(&lpexg->gstGlobals); KillGst(&lpexg->gstStatics); // If there's PDB info, clean up and close if (lpexg->ppdb) { if (lpexg->pgsiPubs) { if (!GSIClose(lpexg->pgsiPubs)) { assert(FALSE); } lpexg->pgsiPubs = 0; } if (lpexg->pgsiGlobs) { if (!GSIClose(lpexg->pgsiGlobs)) { assert(FALSE); } lpexg->pgsiGlobs = 0; } if (lpexg->pdbi) { if (!DBIClose(lpexg->pdbi)) { assert(FALSE); } lpexg->pdbi = 0; } if (lpexg->ptpi) { if (!TypesClose(lpexg->ptpi)) { assert(FALSE); } lpexg->ptpi = 0; } if (lpexg->pstabUDTSym) { STABClose(lpexg->pstabUDTSym); lpexg->pstabUDTSym = 0; } if (!PDBClose(lpexg->ppdb)) { assert(FALSE); } lpexg->ppdb = 0; } lpexg->fOmfLoaded = 0; return TRUE; }