/******************************************************************** Copyright (c) 1999 Microsoft Corporation Module Name: symres.cpp Abstract: Symbol translator main file implements symbol translator function in DLL takes a callstack entry as input, and resolves symbol name Revision History: Brijesh Krishnaswami (brijeshk) - 04/29/99 - Created ********************************************************************/ #include #include #include #include #include "symdef.h" #include // for trace output to include filename #ifdef THIS_FILE #undef THIS_FILE #endif static char __szTraceSourceFile[]=__FILE__; #define THIS_FILE __szTraceSourceFile #define TRACE_ID SYMRESMAINID // global variable that stores thread local storage index DWORD g_dwTlsIndex; // location of sym files repository and log file WCHAR g_szwSymDir[MAX_PATH]; WCHAR g_szwLogFile[MAX_PATH]; // strip off path and extension from filename void SplitExtension( LPWSTR szwFullname, // [in] full name LPWSTR szwName, // [out] name part LPWSTR szwExt // [out] extension part ) { LPWSTR plast = NULL; LPWSTR pfront = NULL; TraceFunctEnter("SplitExtension"); if (pfront = wcsrchr(szwFullname, L'\\')) { pfront++; lstrcpyW(szwName,pfront); } else { lstrcpyW(szwName,szwFullname); } if (plast = wcsrchr(szwName, L'.')) { *plast = L'\0'; plast++; lstrcpyW(szwExt,plast); } else { lstrcpyW(szwExt, L""); } TraceFunctLeave(); } // undecorate symbol name void UndecorateSymbol( LPTSTR szSymbol // [in] [out] function name undecorated in place ) { TCHAR szTemp[MAX_PATH]; PIMAGEHLP_SYMBOL pihsym; DWORD dwSize; TraceFunctEnter("UndecorateSymbol"); dwSize = sizeof(IMAGEHLP_SYMBOL)+MAX_PATH; pihsym = (IMAGEHLP_SYMBOL *) new BYTE[dwSize]; if (pihsym) { pihsym->SizeOfStruct = dwSize; pihsym->Address = 0; pihsym->Flags = 0; pihsym->MaxNameLength = MAX_PATH; lstrcpy(pihsym->Name,szSymbol); SymUnDName(pihsym,szTemp,MAX_PATH); lstrcpy(szSymbol,szTemp); delete [] pihsym; } else { ErrorTrace(TRACE_ID, "Cannot allocate memory"); } TraceFunctLeave(); } // select file from list of open files, or open and add to list // maintain files in usage order, least recently used at end of list OPENFILE* // pointer to open file info GetFile( LPWSTR szwModule // [in] name of file ) { OPENFILE* pFile = NULL; OPENFILE* pLast = NULL; MAPDEF map; DWORD dwCread; std::list * pOpenFilesList = NULL; std::list::iterator it; TCHAR szTarget[MAX_PATH + MAX_PATH]; TraceFunctEnter("GetFile"); // get file list pointer from thread local storage pOpenFilesList = (std::list *) TlsGetValue(g_dwTlsIndex); if (NO_ERROR != GetLastError() || !pOpenFilesList) { ErrorTrace(TRACE_ID, "Error reading TLS"); goto exit; } // search open list to see if file is already open it = pOpenFilesList->begin(); while (it != pOpenFilesList->end()) { if (!lstrcmpiW((*it)->szwName,szwModule)) { // move the file to the beginning of list // so that the LRU file is at the end pFile = *it; pOpenFilesList->erase(it); pOpenFilesList->push_front(pFile); break; } it++; } if (it == pOpenFilesList->end()) // not open, so open and store handle { pFile = new OPENFILE; if (!pFile) { ErrorTrace(TRACE_ID, "Cannot allocate memory"); goto exit; } // open SYM file pFile->hfFile = CreateFileW(szwModule, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == pFile->hfFile) { ErrorTrace(TRACE_ID,"Error opening file %ls",szwModule); delete pFile; pFile = NULL; goto exit; } // copy filename and version into pFile node lstrcpyW(pFile->szwName, szwModule); // read map definition ReadFile(pFile->hfFile, &map, sizeof(MAPDEF)-1, &dwCread, NULL); if (dwCread != sizeof(MAPDEF)-1) { ErrorTrace(TRACE_ID, "Error reading file"); delete pFile; pFile = NULL; goto exit; } pFile->ulFirstSeg = map.md_spseg*16; pFile->nSeg = map.md_cseg; pFile->psCurSymDefPtrs = NULL; pOpenFilesList->push_front(pFile); } // maintain at most MAXOPENFILES open files if (pOpenFilesList->size() > MAXOPENFILES) { // close last file in list pLast = pOpenFilesList->back(); if (pLast) { CloseHandle(pLast->hfFile); if (pLast->psCurSymDefPtrs) { delete [] pLast->psCurSymDefPtrs; pLast->psCurSymDefPtrs = NULL; } delete pLast; pOpenFilesList->pop_back(); } else // something is amiss here { FatalTrace(TRACE_ID,"Error reading open files list"); goto exit; } } exit: TraceFunctLeave(); return pFile; } // read segment defintion for dwSection ULONG // return offset of segment definition, 0 if failed GetSegDef( OPENFILE* pFile, // [in] pointer to open file info DWORD dwSection, // [in] section number SEGDEF* pSeg // [out] pointer to segment definition ) { ULONG ulCurSeg = pFile->ulFirstSeg; int iSectionIndex = 0; DWORD dwCread; TraceFunctEnter("GetSegDef"); // step through segments while (iSectionIndex < pFile->nSeg) { // go to segment beginning if (SetFilePointer(pFile->hfFile, ulCurSeg, NULL, FILE_BEGIN) == 0xFFFFFFFF) { ErrorTrace(TRACE_ID, "Cannot set file pointer"); ulCurSeg = 0; break; } // read seg defn if (!ReadFile(pFile->hfFile, pSeg, sizeof(SEGDEF)-1, &dwCread, NULL)) { ErrorTrace(TRACE_ID, "Cannot read segment definition"); ulCurSeg = 0; break; } iSectionIndex++; if (iSectionIndex == dwSection) // gotcha { break; } // go to next segment definition ulCurSeg = pSeg->gd_spsegnext*16; } // found our section and it's non-empty? if (iSectionIndex != dwSection || !pSeg->gd_csym) // no { ulCurSeg = 0; } TraceFunctLeave(); return ulCurSeg; } // parse sym file to resolve address void GetNameFromAddr( LPWSTR szwModule, // [in] name of symbol file DWORD dwSection, // [in] section part of address to resolve UINT_PTR offset, // [in] offset part of address to resolve LPWSTR szwFuncName // [out] resolved function name, ) // "" otherwise { SEGDEF seg; DWORD dwSymAddr; LPTSTR szMapName; LPTSTR szSegName; TCHAR szSymName[MAX_NAME+1]; TCHAR szPrevName[MAX_NAME+1]; TCHAR sztFuncName[MAX_NAME+1]; int i; int j; int nNameLen; DWORD dwCread; int iSectionIndex; int nToRead; unsigned char cName; ULONG ulCurSeg; ULONG ulSymNameOffset; ULONG ulPrevNameOffset; OPENFILE* pFile = NULL; HANDLE hfFile; FILE* fDump = NULL; BOOL fSuccess = FALSE; HANDLE hfLogFile = NULL; DWORD dwWritten; TCHAR szWrite[MAX_PATH + 50]; DWORD dwArrayOffset; DWORD dwSymOffset; TraceFunctEnter("GetNameFromAddr"); // be pessimistic lstrcpy(sztFuncName,TEXT("")); // get file from open list, or open file pFile = GetFile(szwModule); if (!pFile) { ErrorTrace(TRACE_ID, "Error opening file"); goto exit; } hfFile = pFile->hfFile; // for easy access if (!(ulCurSeg = GetSegDef(pFile,dwSection,&seg))) { ErrorTrace(TRACE_ID, "Cannot find section"); goto exit; } // have we already read in the symbol definition offsets for this section? if (dwSection != pFile->dwCurSection || !pFile->psCurSymDefPtrs) // no { // free up previously read symdef pointers if (pFile->psCurSymDefPtrs) { delete [] pFile->psCurSymDefPtrs; pFile->psCurSymDefPtrs = NULL; } // big symbols? if (seg.gd_type & MSF_BIGSYMDEF) { dwArrayOffset = seg.gd_psymoff * 16; pFile->psCurSymDefPtrs = new BYTE[seg.gd_csym*3]; } else { dwArrayOffset = seg.gd_psymoff; pFile->psCurSymDefPtrs = new BYTE[seg.gd_csym*2]; } if (!pFile->psCurSymDefPtrs) { ErrorTrace(TRACE_ID, "Cannot allocate memory"); goto exit; } if (SetFilePointer(hfFile, ulCurSeg + dwArrayOffset, NULL, FILE_BEGIN) == 0xFFFFFFFF) { ErrorTrace(TRACE_ID, "Cannot set file pointer"); delete [] pFile->psCurSymDefPtrs; pFile->psCurSymDefPtrs = NULL; goto exit; } // read symbol definition pointers array if (!ReadFile(hfFile, pFile->psCurSymDefPtrs, seg.gd_csym * ((seg.gd_type & MSF_BIGSYMDEF)?3:2), &dwCread, NULL)) { ErrorTrace(TRACE_ID, "Cannot read sym pointers array"); delete [] pFile->psCurSymDefPtrs; pFile->psCurSymDefPtrs = NULL; goto exit; } // save this section pFile->dwCurSection = dwSection; } // read symbols for (i = 0; i < seg.gd_csym; i++) { // go to offset of sym defintion if (seg.gd_type & MSF_BIGSYMDEF) { dwSymOffset = pFile->psCurSymDefPtrs[i*3+0] + pFile->psCurSymDefPtrs[i*3+1]*256 + pFile->psCurSymDefPtrs[i*3+2]*65536; } else { dwSymOffset = pFile->psCurSymDefPtrs[i*2+0] + pFile->psCurSymDefPtrs[i*2+1]*256; } if (SetFilePointer(hfFile, ulCurSeg + dwSymOffset, NULL, FILE_BEGIN) == 0xFFFFFFFF) { ErrorTrace(TRACE_ID, "Cannot set file pointer"); goto exit; } // read symbol address DWORD if (!ReadFile(hfFile,&dwSymAddr,sizeof(DWORD),&dwCread,NULL)) { ErrorTrace(TRACE_ID, "Cannot read symbol definition"); goto exit; } // symbol address is 1 word or two? nToRead = sizeof(SHORT) + ((seg.gd_type & MSF_32BITSYMS) * sizeof(SHORT)); // calculate offset of symbol name ulSymNameOffset = ulCurSeg + dwSymOffset + nToRead; // use just lower word of address if 16-bit symbol if (!(seg.gd_type & MSF_32BITSYMS)) { dwSymAddr = dwSymAddr & 0x0000FFFF; } // do we have our function? // if current address is greater than offset, then since we are // traversing in the increasing order of addresses, the previous // symbol must be our quarry if (dwSymAddr > offset) break; // store previous name offset ulPrevNameOffset = ulSymNameOffset; } // did we get our function? // BUGBUG: cannot resolve the last symbol in a section, because we don't know // the size of the function code // if offset > dwSymAddr of last symbol, then we cannot decide if offset belonged // to last symbol or was beyond it - so assume if (i < seg.gd_csym) { // go to name offset if (SetFilePointer(hfFile, ulPrevNameOffset, NULL, FILE_BEGIN) == 0xFFFFFFFF) { ErrorTrace(TRACE_ID, "Error setting file pointer"); goto exit; } // read length of name if (!ReadFile(hfFile,&cName,sizeof(TCHAR),&dwCread,NULL)) { ErrorTrace(TRACE_ID, "Error reading length of name"); goto exit; } nNameLen = (int) cName; // read symbol name if (!ReadFile(hfFile,sztFuncName,nNameLen,&dwCread,NULL)) { ErrorTrace(TRACE_ID, "Error reading name"); goto exit; } sztFuncName[nNameLen] = TCHAR('\0'); UndecorateSymbol(sztFuncName); fSuccess = TRUE; } exit: if (!MultiByteToWideChar(CP_ACP, 0, sztFuncName, -1, szwFuncName, MAX_PATH)) { lstrcpyW(szwFuncName, L""); } // log unresolved symbols to log file (filename read from registry) // file write operation is not wrapped in a mutex // for speed considerations if (!fSuccess) { hfLogFile = CreateFileW(g_szwLogFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE != hfLogFile) { wsprintf(szWrite, "\n%ls %04X:%08X", szwModule, dwSection, offset); if (SetFilePointer(hfLogFile, 0, NULL, FILE_END) != 0xFFFFFFFF) { WriteFile(hfLogFile, szWrite, lstrlen(szWrite), &dwWritten, NULL); } CloseHandle(hfLogFile); } } TraceFunctLeave(); } // cleanup memory void Cleanup() { std::list* pOpenFilesList = NULL; std::list::iterator it; TraceFunctEnter("Cleanup"); pOpenFilesList = (std::list *) TlsGetValue(g_dwTlsIndex); if (!pOpenFilesList) { goto exit; } for (it = pOpenFilesList->begin(); it != pOpenFilesList->end(); it++) { if (*it) { if ((*it)->psCurSymDefPtrs) { delete [] (*it)->psCurSymDefPtrs; } if ((*it)->hfFile && (*it)->hfFile != INVALID_HANDLE_VALUE) { CloseHandle((*it)->hfFile); } delete *it; } } delete pOpenFilesList; exit: TraceFunctLeave(); } BOOL HandleProcessAttach() { std::list * pOpenFilesList = NULL; BOOL fRc = FALSE; DWORD dwType; DWORD dwSize; ULONG lResult; HKEY hKey; TraceFunctEnter("HandleProcessAttach"); // allocate thread local storage if ((g_dwTlsIndex = TlsAlloc()) == 0xFFFFFFFF) { ErrorTrace(TRACE_ID, "Cannot get TLS index"); goto exit; } // create a new list of open sym files pOpenFilesList = new std::list; if (!pOpenFilesList) { ErrorTrace(TRACE_ID, "Out of memory"); goto exit; } // store pointer to list in TLS if (!TlsSetValue(g_dwTlsIndex, (PVOID) pOpenFilesList)) { ErrorTrace(TRACE_ID, "Cannot write to TLS"); delete pOpenFilesList; pOpenFilesList = NULL; goto exit; } lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\PCHealth\\Symbols"), 0, KEY_QUERY_VALUE, &hKey); // read symbol files location and name of log file from registry if(lResult == ERROR_SUCCESS) { dwSize = MAX_PATH; RegQueryValueExW(hKey, L"SymDir", NULL, NULL, (LPBYTE) g_szwSymDir, &dwSize); dwSize = MAX_PATH; RegQueryValueExW(hKey, L"LogFile", NULL, NULL, (LPBYTE) g_szwLogFile, &dwSize); RegCloseKey(hKey); fRc = TRUE; } exit: TraceFunctLeave(); return fRc; } // Dll entry point // allocates TLS // initializes list of open sym files - one list per client thread // cleans up after itself BOOL APIENTRY DllMain( HANDLE hDll, // [in] handle to Dll DWORD dwReason, // [in] why DllMain is called LPVOID lpReserved // [in] ignored ) { BOOL fRc = TRUE; std::list* pOpenFilesList = NULL; switch (dwReason) { case DLL_PROCESS_ATTACH: // initial thread of process that loaded us fRc = HandleProcessAttach(); break; case DLL_THREAD_ATTACH: // if list already exists, do nothing if (TlsGetValue(g_dwTlsIndex)) { break; } // create new list for every new thread pOpenFilesList = new std::list; if (!pOpenFilesList) { ErrorTrace(TRACE_ID, "Out of memory"); fRc = FALSE; break; } // store pointer to list in TLS if (!TlsSetValue(g_dwTlsIndex, (PVOID) pOpenFilesList)) { ErrorTrace(TRACE_ID, "Cannot write to TLS"); delete pOpenFilesList; pOpenFilesList = NULL; fRc = FALSE; } break; case DLL_THREAD_DETACH: Cleanup(); break; case DLL_PROCESS_DETACH: // free TLS TlsFree(g_dwTlsIndex); break; default: break; } return fRc; } // exported function // called by clients to resolve a single callstack entry extern "C" void APIENTRY ResolveSymbols( LPWSTR szwFilename, LPWSTR szwVersion, DWORD dwSection, UINT_PTR Offset, LPWSTR szwFuncName ) { WCHAR szwName[MAX_PATH] = L""; WCHAR szwSymFile[MAX_PATH+MAX_PATH] = L""; WCHAR szwExt[MAX_PATH] = L""; TraceFunctEnter("ResolveSymbols"); // sanity check if (!szwFilename || !szwVersion) { ErrorTrace(TRACE_ID, "No module name/version"); goto exit; } // get sym file name SplitExtension(szwFilename, szwName, szwExt); wsprintfW(szwSymFile, L"%s\\%s\\%s_%s_%s.SYM", g_szwSymDir, szwName, szwName, szwExt, szwVersion); // resolve symbol name GetNameFromAddr(szwSymFile, dwSection, Offset, szwFuncName); exit: TraceFunctLeave(); }