size_t GetSymbolNameFromAddress(void* address, char* symbolName, size_t size, size_t* offsetBytes) { if (size) *symbolName = 0; if (offsetBytes) *offsetBytes = 0; __try { ULONG_ADDR dwOffset = 0; union { IMAGEHLP_SYMBOL sym; char symNameBuffer[sizeof(IMAGEHLP_SYMBOL) + MAX_SYM_NAME]; } u; u.sym.SizeOfStruct = sizeof(u.sym); u.sym.MaxNameLength = sizeof(u.symNameBuffer) - sizeof(u.sym); if (!SymGetSymFromAddr(GetSymHandle(), CheckAddress(address), &dwOffset, &u.sym)) { return 0; } else { const char* sourceName = u.sym.Name; char undName[1024]; if (UnDecorateSymbolName(u.sym.Name, undName, sizeof(undName), UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS)) { sourceName = undName; } else if (SymUnDName(&u.sym, undName, sizeof(undName))) { sourceName = undName; } if (offsetBytes) { *offsetBytes = dwOffset; } if (size) { strncpy(symbolName, sourceName, size)[size - 1] = 0; } return strlen(sourceName); } } __except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(GetExceptionCode()); } return 0; }
void GetFunctionInfo(DWORD64 dw64Address, FILE * fw) { DWORD64 dw64Displacement = 0; char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; PSYMBOL_INFO pSym = (PSYMBOL_INFO)buffer; pSym->SizeOfStruct = sizeof(SYMBOL_INFO); pSym->MaxNameLen = MAX_SYM_NAME; if(SymFromAddr(GetCurrentProcess(), dw64Address, &dw64Displacement, pSym) == TRUE) { // We have decorated name, try to make it readable if(UnDecorateSymbolName(pSym->Name, sDebugBuf, 512, UNDNAME_COMPLETE | UNDNAME_NO_THISTYPE | UNDNAME_NO_SPECIAL_SYMS | UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS) > 0) { // We have readable name, write it fprintf(fw, "%s\n", sDebugBuf); return; } } // We don't found any info, write '?' fprintf(fw, "?\n"); }
GCallStack::GCallStack(HANDLE hThread, CONTEXT& c) { if (!_bLockInit) // only init the single instance of the CRITICAL_SECTION 1 time for the many instances of GCallStack { InitializeCriticalSection(&_DbgHelpLock); _bLockInit = true; } DWORD imageType = IMAGE_FILE_MACHINE_I386; HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId()); int frameNum = 0; // counts walked frames int MAX_STACK_FRAMES = 7777; // in C# the maximum stack frames imposed by the language is 1000. Arbitrary limit to guarantee no condition of infinate walking in corrupted memory. DWORD offsetFromLine; // tells us line number in the source file #if defined(_LINUX64) || defined(_WIN64) || defined(_IOS) unsigned __int64 offsetFromSymbol; // tells us how far from the symbol we were #else DWORD offsetFromSymbol; // tells us how far from the symbol we were #endif DWORD symOptions; // symbol handler settings IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN ); GString strStackName(MAXNAMELEN + 512); // undecorated method/function name + Source file and line number IMAGEHLP_MODULE Module; IMAGEHLP_LINE Line; STACKFRAME64 s; // in/out stackframe memset( &s, '\0', sizeof s ); // note: converted code from [std::string symSearchPath] to [GString symSearchPath] so it will compile with the _UNICODE build flag - 8/18/2014 GString symSearchPath; #ifdef _UNICODE wchar_t *tt = 0, *p; tt = new wchar_t[TTBUFLEN]; #else char *tt = 0, *p; tt = new char[TTBUFLEN]; #endif // build symbol search path from: symSearchPath = ""; // current directory if (GetCurrentDirectory(TTBUFLEN, tt)) symSearchPath << tt << "; "; // dir with executable if ( GetModuleFileName( 0, tt, TTBUFLEN ) ) { #ifdef _UNICODE for (p = tt + wcslen(tt) - 1; p >= tt; --p) #else for (p = tt + strlen(tt) - 1; p >= tt; --p) // VC6 does not have a _tcsclen() and we still support VC6 #endif { // locate the rightmost path separator if ( *p == '\\' || *p == '/' || *p == ':' ) break; } // if we found one, p is pointing at it; if not, tt only contains an exe name (no path), and p points before its first byte if ( p != tt ) // path sep found? { if ( *p == ':' ) // we leave colons in place ++ p; *p = '\0'; // eliminate the exe name and last path sep symSearchPath << tt << "; "; } } // environment variable _NT_SYMBOL_PATH GString g("_NT_SYMBOL_PATH"); if (GetEnvironmentVariable(g, tt, TTBUFLEN)) symSearchPath << tt << "; "; // environment variable _NT_ALTERNATE_SYMBOL_PATH g = "_NT_ALTERNATE_SYMBOL_PATH"; if (GetEnvironmentVariable(g, tt, TTBUFLEN)) symSearchPath << tt << "; "; // environment variable SYSTEMROOT g = "SYSTEMROOT"; if (GetEnvironmentVariable(g, tt, TTBUFLEN)) symSearchPath << tt << "; "; if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 ); // 8/20/2014 note: In older Windows API's SymInitialize()'s 2nd argument was not defined as "const char *", it was only "char *" // Although "const" was not defined, the API call is "const" in behavior. In newer versions of the Windows API this has been fixed. // In newer versions - SymInitialize's 2nd argument may resolve to either "const char *" OR "const wchar_t *", and in those builds the // GString has a default conversion to the correct string type, however in the older build configurations, GString does not (and should not) // know how to resolve to a "char *" by default, so in that case the preprocessor directive isolates the code needed to convert to "char *" #if defined(_MSC_VER) && _MSC_VER <= 1200 if (!SymInitialize(hProcess, symSearchPath.Buf(), false)) // symSearchPath == (char *) #else if (!SymInitialize(hProcess, symSearchPath, true)) // symSearchPath == (const char *) --OR-- (const wchar_t *) depending on the _UNICODE preprocessor definition #endif { goto tagCleanUp; } symOptions = SymGetOptions(); symOptions |= SYMOPT_LOAD_LINES; symOptions &= ~SYMOPT_UNDNAME; SymSetOptions( symOptions ); enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() ); // init STACKFRAME for first call, definitions found in ImageHlp.h #ifdef _M_IX86 imageType = IMAGE_FILE_MACHINE_I386; s.AddrPC.Offset = c.Eip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Ebp; s.AddrFrame.Mode = AddrModeFlat; s.AddrStack.Offset = c.Esp; s.AddrStack.Mode = AddrModeFlat; #elif _M_X64 imageType = IMAGE_FILE_MACHINE_AMD64; s.AddrPC.Offset = c.Rip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Rsp; s.AddrFrame.Mode = AddrModeFlat; s.AddrStack.Offset = c.Rsp; s.AddrStack.Mode = AddrModeFlat; #elif _M_IA64 imageType = IMAGE_FILE_MACHINE_IA64; s.AddrPC.Offset = c.StIIP; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.IntSp; s.AddrFrame.Mode = AddrModeFlat; s.AddrBStore.Offset = c.RsBSP; s.AddrBStore.Mode = AddrModeFlat; s.AddrStack.Offset = c.IntSp; s.AddrStack.Mode = AddrModeFlat; #endif memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN ); pSym->SizeOfStruct = IMGSYMLEN; pSym->MaxNameLength = MAXNAMELEN; memset( &Line, '\0', sizeof Line ); Line.SizeOfStruct = sizeof Line; memset( &Module, '\0', sizeof Module ); Module.SizeOfStruct = sizeof Module; offsetFromSymbol = 0; // DbgHelp is single threaded, so acquire a lock. EnterCriticalSection(&_DbgHelpLock); while ( frameNum < MAX_STACK_FRAMES ) { // get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase()) // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can // assume that either you are done, or that the stack is so hosed that the next deeper frame could not be found. #ifdef _WIN64 if (!StackWalk64(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) #else if (!StackWalk(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL)) #endif break; // Maybe it failed, maybe we have finished walking the stack if ( s.AddrPC.Offset != 0 ) { // Most likely a valid stack rame // show procedure info if ( ! SymGetSymFromAddr64( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) ) { break; } else { // UnDecorateSymbolName() to get the Class::Method or function() name in tyhe callstack strStackName.Empty(); UnDecorateSymbolName(pSym->Name, strStackName._str, MAXNAMELEN, UNDNAME_COMPLETE); strStackName.SetLength(strlen(strStackName._str)); // SymGetLineFromAddr() to get the source.cpp and the line number IMAGEHLP_LINE64 Line; if (SymGetLineFromAddr64(hProcess, s.AddrPC.Offset, &offsetFromLine, &Line) != FALSE) { GString g(Line.FileName); // Line.FileName conains the "c:\Full\Path\TO\Source.cpp" // Builds string "Foo::Bar[Source.cpp]@777" strStackName << "[" << g.StartingAt(g.ReverseFind("\\") + 1) << "]@" << Line.LineNumber; } // add the GString to the GStringList, do not add frame 0 because it will always be GException::GSeception where we divided by 0 if (frameNum > 0) _stk += strStackName; } } else { // base reached SetLastError(0); break; } ++frameNum; } LeaveCriticalSection(&_DbgHelpLock); // de-init symbol handler etc. (SymCleanup()) SymCleanup( hProcess ); free( pSym ); tagCleanUp:; delete [] tt; CloseHandle(hProcess); }
/** * Loads the elements of the call stack in a list * @param exceptionInfos are useful information on the exception */ void LoadCallStack(EXCEPTION_POINTERS* exceptionInfos, HANDLE &hProcess, std::list<std::string> &callStack, std::list<std::string> &fileStack) { STACKFRAME64 tempStackFrame; CONTEXT context = *(exceptionInfos->ContextRecord); memset( &tempStackFrame, 0, sizeof(STACKFRAME64) ); DWORD machineType; #ifdef _M_IX86 machineType = IMAGE_FILE_MACHINE_I386; tempStackFrame.AddrPC.Offset = context.Eip; tempStackFrame.AddrPC.Mode = AddrModeFlat; tempStackFrame.AddrStack.Offset = context.Esp; tempStackFrame.AddrStack.Mode = AddrModeFlat; tempStackFrame.AddrFrame.Offset = context.Ebp; tempStackFrame.AddrFrame.Mode = AddrModeFlat; #elif _M_X64 machineType = IMAGE_FILE_MACHINE_AMD64; tempStackFrame.AddrPC.Offset = context.Rip; tempStackFrame.AddrPC.Mode = AddrModeFlat; tempStackFrame.AddrFrame.Offset = context.Rsp; tempStackFrame.AddrFrame.Mode = AddrModeFlat; tempStackFrame.AddrStack.Offset = context.Rsp; tempStackFrame.AddrStack.Mode = AddrModeFlat; #elif _M_IA64 machineType = IMAGE_FILE_MACHINE_IA64; tempStackFrame.AddrPC.Offset = context.StIIP; tempStackFrame.AddrPC.Mode = AddrModeFlat; tempStackFrame.AddrFrame.Offset = context.IntSp; tempStackFrame.AddrFrame.Mode = AddrModeFlat; tempStackFrame.AddrBStore.Offset = context.RsBSP; tempStackFrame.AddrBStore.Mode = AddrModeFlat; tempStackFrame.AddrStack.Offset = context.IntSp; tempStackFrame.AddrStack.Mode = AddrModeFlat; #else #error "Platform not supported!" #endif ULONG64 buffer[(sizeof(SYMBOL_INFO) + nbChar*sizeof(TCHAR) + sizeof(ULONG64) + 1) / sizeof(ULONG64)]; PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer); PSTR undecoratedName = (PSTR)malloc(sizeof(TCHAR) * nbChar); pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = nbChar; DWORD lineDisplacement; IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) }; while(StackWalk64(machineType, hProcess, GetCurrentThread(), &tempStackFrame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { // Sanity stack check if(tempStackFrame.AddrPC.Offset == 0) { break; } DWORD64 symDisplacement = 0; // Try to get the symbol name if(SymFromAddr(hProcess, tempStackFrame.AddrPC.Offset, &symDisplacement, pSymbol)) { UnDecorateSymbolName(pSymbol->Name, undecoratedName, MAX_SYM_NAME, UNDNAME_COMPLETE); callStack.push_back(std::string((char*)undecoratedName) + "+" + ::boost::lexical_cast<std::string>(symDisplacement)); if(SymGetLineFromAddr64(hProcess, tempStackFrame.AddrPC.Offset, &lineDisplacement, &lineInfo)) { fileStack.push_back(std::string(lineInfo.FileName) + "\tl:" + ::boost::lexical_cast<std::string>(lineInfo.LineNumber)); } else { fileStack.push_back("No info"); } } else { } } free(undecoratedName); }
//----------------------------------------------------------------------------// static void dumpBacktrace(size_t frames) { #if defined(_DEBUG) || defined(DEBUG) #if defined(_MSC_VER) SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES); if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) return; HANDLE thread = GetCurrentThread(); CONTEXT context; RtlCaptureContext(&context); STACKFRAME64 stackframe; ZeroMemory(&stackframe, sizeof(stackframe)); stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrStack.Mode = AddrModeFlat; stackframe.AddrFrame.Mode = AddrModeFlat; #if _M_IX86 stackframe.AddrPC.Offset = context.Eip; stackframe.AddrStack.Offset = context.Esp; stackframe.AddrFrame.Offset = context.Ebp; DWORD machine_arch = IMAGE_FILE_MACHINE_I386; #elif _M_X64 stackframe.AddrPC.Offset = context.Rip; stackframe.AddrStack.Offset = context.Rsp; stackframe.AddrFrame.Offset = context.Rbp; DWORD machine_arch = IMAGE_FILE_MACHINE_AMD64; #endif char symbol_buffer[1024]; ZeroMemory(symbol_buffer, sizeof(symbol_buffer)); PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(symbol_buffer); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = sizeof(symbol_buffer) - sizeof(SYMBOL_INFO); Logger& logger(Logger::getSingleton()); logger.logEvent("========== Start of Backtrace ==========", Errors); size_t frame_no = 0; while (StackWalk64(machine_arch, GetCurrentProcess(), thread, &stackframe, &context, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0) && stackframe.AddrPC.Offset) { symbol->Address = stackframe.AddrPC.Offset; DWORD64 displacement = 0; char signature[256]; if (SymFromAddr(GetCurrentProcess(), symbol->Address, &displacement, symbol)) UnDecorateSymbolName(symbol->Name, signature, sizeof(signature), UNDNAME_COMPLETE); else sprintf_s(signature, sizeof(signature), "%p", symbol->Address); IMAGEHLP_MODULE64 modinfo; modinfo.SizeOfStruct = sizeof(modinfo); const BOOL have_image_name = SymGetModuleInfo64(GetCurrentProcess(), symbol->Address, &modinfo); char outstr[512]; sprintf_s(outstr, sizeof(outstr), "#%d %s +%#llx (%s)", frame_no, signature, displacement, (have_image_name ? modinfo.LoadedImageName : "????")); logger.logEvent(outstr, Errors); if (++frame_no >= frames) break; if (!stackframe.AddrReturn.Offset) break; } logger.logEvent("========== End of Backtrace ==========", Errors); SymCleanup(GetCurrentProcess()); #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__HAIKU__) void* buffer[frames]; const int received = backtrace(&buffer[0], frames); Logger& logger(Logger::getSingleton()); logger.logEvent("========== Start of Backtrace ==========", Errors); for (int i = 0; i < received; ++i) { char outstr[512]; Dl_info info; if (dladdr(buffer[i], &info)) { if (!info.dli_sname) snprintf(outstr, 512, "#%d %p (%s)", i, buffer[i], info.dli_fname); else { ptrdiff_t offset = static_cast<char*>(buffer[i]) - static_cast<char*>(info.dli_saddr); int demangle_result = 0; char* demangle_name = abi::__cxa_demangle(info.dli_sname, 0, 0, &demangle_result); snprintf(outstr, 512, "#%d %s +%#tx (%s)", i, demangle_name ? demangle_name : info.dli_sname, offset, info.dli_fname); std::free(demangle_name); } } else snprintf(outstr, 512, "#%d --- error ---", i); logger.logEvent(outstr, Errors); } logger.logEvent("========== End of Backtrace ==========", Errors); #endif #endif }
static BOOL ResolveSymbol(HANDLE hProcess, DWORD dwAddress, SYMBOL_INFO &siSymbol) { BOOL fRetval = TRUE; siSymbol.dwAddress = dwAddress; union { CHAR rgchSymbol[sizeof(IMAGEHLP_SYMBOL) + 255]; IMAGEHLP_SYMBOL sym; }; CHAR szUndec[256]; CHAR szWithOffset[256]; LPSTR pszSymbol = NULL; IMAGEHLP_MODULE mi; memset(&siSymbol, 0, sizeof(SYMBOL_INFO)); mi.SizeOfStruct = sizeof(IMAGEHLP_MODULE); if (!SymGetModuleInfo(hProcess, dwAddress, &mi)) lstrcpyA(siSymbol.szModule, "<no module>"); else { LPSTR pszModule = strchr(mi.ImageName, '\\'); if (pszModule == NULL) pszModule = mi.ImageName; else pszModule++; lstrcpynA(siSymbol.szModule, pszModule, _countof(siSymbol.szModule)); lstrcatA(siSymbol.szModule, "! "); } __try { sym.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); sym.Address = dwAddress; sym.MaxNameLength = 255; if (SymGetSymFromAddr(hProcess, dwAddress, &(siSymbol.dwOffset), &sym)) { pszSymbol = sym.Name; if (UnDecorateSymbolName(sym.Name, szUndec, _countof(szUndec), UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS)) { pszSymbol = szUndec; } else if (SymUnDName(&sym, szUndec, _countof(szUndec))) { pszSymbol = szUndec; } if (siSymbol.dwOffset != 0) { wsprintfA(szWithOffset, "%s + %d bytes", pszSymbol, siSymbol.dwOffset); pszSymbol = szWithOffset; } } else pszSymbol = "<no symbol>"; } __except (EXCEPTION_EXECUTE_HANDLER) { pszSymbol = "<EX: no symbol>"; siSymbol.dwOffset = dwAddress - mi.BaseOfImage; } lstrcpynA(siSymbol.szSymbol, pszSymbol, _countof(siSymbol.szSymbol)); return fRetval; }
int main(int argc, char **argv) { BOOL bRet; DWORD dwRet; bool debug = false; char *szModule = nullptr; bool functions = false; bool demangle = false; bool pretty = false; while (1) { int opt = getopt(argc, argv, "?CDe:fHp"); switch (opt) { case 'C': demangle = true; break; case 'D': debug = true; break; case 'e': szModule = optarg; break; case 'f': functions = true; break; case 'H': usage(argv[0]); return EXIT_SUCCESS; case 'p': pretty = true; break; case '?': fprintf(stderr, "error: invalid option `%c`\n", optopt); /* pass-through */ default: usage(argv[0]); return EXIT_FAILURE; case -1: break; } if (opt == -1) { break; } } if (szModule == nullptr) { usage(argv[0]); return EXIT_FAILURE; } // Load the module HMODULE hModule = nullptr; #ifdef _WIN64 hModule = LoadLibraryExA(szModule, NULL, LOAD_LIBRARY_AS_DATAFILE); #endif if (!hModule) { hModule = LoadLibraryExA(szModule, NULL, DONT_RESOLVE_DLL_REFERENCES); } if (!hModule) { fprintf(stderr, "error: failed to load %s\n", szModule); return EXIT_FAILURE; } DWORD dwSymOptions = SymGetOptions(); dwSymOptions |= SYMOPT_LOAD_LINES; #ifndef NDEBUG dwSymOptions |= SYMOPT_DEBUG; #endif // We can get more information by calling UnDecorateSymbolName() ourselves. dwSymOptions &= ~SYMOPT_UNDNAME; SymSetOptions(dwSymOptions); HANDLE hProcess = GetCurrentProcess(); bRet = InitializeSym(hProcess, FALSE); assert(bRet); if (debug) { SymRegisterCallback64(hProcess, &callback, 0); } dwRet = SymLoadModuleEx(hProcess, NULL, szModule, NULL, (DWORD64)(UINT_PTR)hModule, 0, NULL, 0); if (!dwRet) { fprintf(stderr, "warning: failed to load module symbols\n"); } if (!GetModuleHandleA("symsrv.dll")) { fprintf(stderr, "warning: symbol server not loaded\n"); } while (optind < argc) { const char *arg = argv[optind++]; DWORD64 dwRelAddr; if (arg[0] == '0' && arg[1] == 'x') { sscanf(&arg[2], "%08" PRIX64, &dwRelAddr); } else { dwRelAddr = atol(arg); } UINT_PTR dwAddr = (UINT_PTR)hModule + dwRelAddr; if (functions) { struct { SYMBOL_INFO Symbol; CHAR Name[512]; } sym; char UnDecoratedName[512]; const char *function = "??"; ZeroMemory(&sym, sizeof sym); sym.Symbol.SizeOfStruct = sizeof sym.Symbol; sym.Symbol.MaxNameLen = sizeof sym.Symbol.Name + sizeof sym.Name; DWORD64 dwSymDisplacement = 0; bRet = SymFromAddr(hProcess, dwAddr, &dwSymDisplacement, &sym.Symbol); if (bRet) { function = sym.Symbol.Name; if (demangle) { if (UnDecorateSymbolName( sym.Symbol.Name, UnDecoratedName, sizeof UnDecoratedName, UNDNAME_COMPLETE)) { function = UnDecoratedName; } } } fputs(function, stdout); fputs(pretty ? " at " : "\n", stdout); } IMAGEHLP_LINE64 line; ZeroMemory(&line, sizeof line); line.SizeOfStruct = sizeof line; DWORD dwLineDisplacement = 0; bRet = SymGetLineFromAddr64(hProcess, dwAddr, &dwLineDisplacement, &line); if (bRet) { fprintf(stdout, "%s:%lu\n", line.FileName, line.LineNumber); } else { fputs("??:?\n", stdout); } fflush(stdout); } SymCleanup(hProcess); FreeLibrary(hModule); return 0; }
void CDebugMgr::RecordException(PEXCEPTION_POINTERS pExceptionInfo) { PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; // First print information about the type of fault m_pchBuffer += wsprintf(m_pchBuffer, "Exception code: %08X\n", pExceptionRecord->ExceptionCode); // Now print information about where the fault occured TCHAR szFaultingModule[MAX_PATH]; DWORD section, offset; FindLogicalAddress( pExceptionRecord->ExceptionAddress, szFaultingModule, sizeof( szFaultingModule ), section, offset ); m_pchBuffer += wsprintf(m_pchBuffer,"Fault address: %08X %02X:%08X %s", pExceptionRecord->ExceptionAddress, section, offset, szFaultingModule ); TCHAR szMap[MAX_PATH] = ""; wsprintf(szMap, szFaultingModule); wsprintf(&szMap[lstrlen(szMap)-3], "MAP"); TCHAR szSymbol[256] = "<unknown>"; TCHAR szUnmangledSymbol[1024] = "<unknown>"; TCHAR szObject[128] = "<unknown>"; if ( m_dwOptions & DEBUGMGR_RESOLVESYMBOLS ) { if ( FindSymbol(szMap, section, offset, szSymbol, szObject) ) { if ( m_dwOptions & DEBUGMGR_UNMANGLESYMBOLS && *szSymbol == '?' ) { UnDecorateSymbolName(szSymbol, szUnmangledSymbol, 1024, UNDNAME_COMPLETE); } else { wsprintf(szUnmangledSymbol, szSymbol); } m_pchBuffer += wsprintf(m_pchBuffer, " in function %s in object file %s", szUnmangledSymbol, szObject); if ( m_dwOptions & DEBUGMGR_RESOLVESOURCELINE ) { TCHAR szSource[256]; TCHAR szLine[256]; if ( FindSourceLine(szMap, szObject, section, offset, szSource, szLine) ) { m_pchBuffer += wsprintf(m_pchBuffer, " at Line %s of %s\n", szLine, szSource); } else { m_pchBuffer += wsprintf(m_pchBuffer, " at Line ??? of ???\n"); } } else { m_pchBuffer += wsprintf(m_pchBuffer, "\n\n"); } } else { m_pchBuffer += wsprintf(m_pchBuffer, "\n"); } } else { m_pchBuffer += wsprintf(m_pchBuffer, "\n"); } PCONTEXT pCtx = pExceptionInfo->ContextRecord; // Show the registers m_pchBuffer += wsprintf(m_pchBuffer, "EAX:%08X\nEBX:%08X\nECX:%08X\nEDX:%08X\nESI:%08X\nEDI:%08X\n", pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, pCtx->Esi, pCtx->Edi ); m_pchBuffer += wsprintf(m_pchBuffer,"CS:EIP:%04X:%08X\n", pCtx->SegCs, pCtx->Eip ); m_pchBuffer += wsprintf(m_pchBuffer,"SS:ESP:%04X:%08X EBP:%08X\n", pCtx->SegSs, pCtx->Esp, pCtx->Ebp ); m_pchBuffer += wsprintf(m_pchBuffer,"DS:%04X ES:%04X FS:%04X GS:%04X\n", pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); m_pchBuffer += wsprintf(m_pchBuffer,"Flags:%08X\n", pCtx->EFlags ); }
/** stdcall: * C Naming - Name prefixed by a '_', followed by the name, then an '@', followed by number of bytes in arguments. i.e. foo(int a, double b) = _foo@12 * C++ Naming - __stdcall * Args - Arguments are passed on the stack. * Cleanup - Callee cleans up the stack before returning cdecl: * C Naming - Name prefixed by a '_' * C++ Naming - __cdecl in demangled name * Args - Arguments are passed on the stack. * Cleanup - Caller cleans up the stack after the return fastcall: * C Naming - Name prefixed by a '@', followed by the func name, then another '@', followed by the number of bytes in the arguments. i.e. foo(double a, int b, int c, int d) = @foo@20 * C++ Naming - __fastcall in the mangled name * Args - First two arguments that are less than DWORD size are passed in ECX & EDX * Cleanup - Callee cleans up the stack before returning thiscall: * C Naming - NA * C++ Naming - __thiscall in the demangled name * 'this' parameter is passed in ECX, others are passed in the stack * Cleanup Callee cleans up the stack before returning **/ callType func_instance::getCallingConvention() { std::cerr << "symtab name (c++): " << symTabName() << std::endl; //const char *name = symTabName().c_str(); const int buffer_size = 1024; char buffer[buffer_size]; int pos; if (callingConv != unknown_call) return callingConv; if (symTabName().empty()) { assert(0); //Umm... return unknown_call; } switch(symTabName()[0]) { case '?': //C++ Encoded symbol. Everything is stored in the C++ name // mangling scheme UnDecorateSymbolName(symTabName().c_str(), buffer, buffer_size, UNDNAME_NO_ARGUMENTS | UNDNAME_NO_FUNCTION_RETURNS); printf("undecorated name: %s\n", buffer); if (strstr(buffer, "__thiscall")) { callingConv = thiscall_call; return callingConv; } if (strstr(buffer, "__fastcall")) { callingConv = fastcall_call; return callingConv; } if (strstr(buffer, "__stdcall")) { callingConv = stdcall_call; return callingConv; } if (strstr(buffer, "__cdecl")) { callingConv = cdecl_call; return callingConv; } break; case '_': //Check for stdcall or cdecl pos = symTabName().find('@'); if (pos != std::string::npos) { callingConv = stdcall_call; return callingConv; } else { callingConv = cdecl_call; return callingConv; } break; case '@': //Should be a fast call pos = symTabName().find('@'); if (pos != std::string::npos) { callingConv = fastcall_call; return callingConv; } break; } //We have no idea what this call is. We probably got an undecorated // name. If the function doesn't clean up it's own stack (doesn't // have a ret #) instruction, then it must be a cdecl call, as that's // the only type that doesn't clean its own stack. //If the function is part of a class, then it's most likely a thiscall, // although that could be incorrect for a static function. //Otherwise let's guess that it's a stdcall. if (!ifunc()->cleansOwnStack()) { callingConv = cdecl_call; } else if (symTabName().find("::") != std::string::npos) { callingConv = thiscall_call; } else { callingConv = stdcall_call; } return callingConv; }
void mrpt::system::getCallStackBackTrace(TCallStackBackTrace& out_bt) { out_bt.backtrace_levels.clear(); const unsigned int framesToSkip = 1; // skip *this* function from the backtrace const unsigned int framesToCapture = 64; #ifdef _WIN32 void* backTrace[framesToCapture]{}; SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); const HANDLE hProcess = GetCurrentProcess(); if (!SymInitialize( hProcess, nullptr /* UserSearchPath */, TRUE /*fInvadeProcess*/)) { std::cerr << "[mrpt::system::getCallStackBackTrace] Error in SymInitialize()!" << std::endl; return; } char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; const USHORT nFrames = CaptureStackBackTrace( framesToSkip, framesToCapture, backTrace, nullptr); for (unsigned int i = 0; i < nFrames; i++) { TCallStackEntry cse; cse.address = backTrace[i]; if (!SymFromAddr(hProcess, (DWORD64)cse.address, nullptr, pSymbol)) { cse.symbolName = "???"; cse.symbolNameOriginal = "???"; out_bt.backtrace_levels.emplace_back(cse); continue; } SYMBOL_INFO& si = *pSymbol; cse.symbolNameOriginal = si.Name; char undecorated_name[1024]; if (!UnDecorateSymbolName( si.Name, undecorated_name, sizeof(undecorated_name), UNDNAME_COMPLETE)) { cse.symbolName = cse.symbolNameOriginal; } else { cse.symbolName = std::string(undecorated_name); } out_bt.backtrace_levels.emplace_back(cse); } #else // Based on: https://gist.github.com/fmela/591333 void* callstack[framesToCapture]; const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]); int nFrames = ::backtrace(callstack, nMaxFrames); char** symbols = ::backtrace_symbols(callstack, nFrames); for (int i = (int)framesToSkip; i < nFrames; i++) { TCallStackEntry cse; cse.address = callstack[i]; Dl_info info; if (dladdr(callstack[i], &info) && info.dli_sname) { char* demangled = nullptr; int status = -1; if (info.dli_sname[0] == '_') { demangled = abi::__cxa_demangle( info.dli_sname, nullptr, nullptr, &status); } cse.symbolNameOriginal = info.dli_sname == nullptr ? symbols[i] : info.dli_sname; cse.symbolName = status == 0 ? std::string(demangled) : cse.symbolNameOriginal; free(demangled); } out_bt.backtrace_levels.emplace_back(cse); } free(symbols); #endif }
void write_stack_trace(PCONTEXT pContext, TextOutputStream& outputStream) { HANDLE m_hProcess = GetCurrentProcess(); DWORD dwMachineType = 0; CONTEXT context = *pContext; // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag if ( !SymInitialize( m_hProcess, (PSTR)environment_get_app_path(), TRUE ) ) { return; } STACKFRAME sf; memset( &sf, 0, sizeof(sf) ); #ifdef _M_IX86 // Initialize the STACKFRAME structure for the first call. This is only // necessary for Intel CPUs, and isn't mentioned in the documentation. sf.AddrPC.Offset = context.Eip; sf.AddrPC.Mode = AddrModeFlat; sf.AddrStack.Offset = context.Esp; sf.AddrStack.Mode = AddrModeFlat; sf.AddrFrame.Offset = context.Ebp; sf.AddrFrame.Mode = AddrModeFlat; dwMachineType = IMAGE_FILE_MACHINE_I386; #endif while ( 1 ) { // Get the next stack frame if ( ! StackWalk( dwMachineType, m_hProcess, GetCurrentThread(), &sf, &context, 0, SymFunctionTableAccess, SymGetModuleBase, 0 ) ) break; if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure break; // the frame is OK. Bail if not. // Get the name of the function for this stack frame entry BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + MAX_SYM_NAME ]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; DWORD64 symDisplacement = 0; // Displacement of the input address, // relative to the start of the symbol IMAGEHLP_MODULE module = { sizeof(IMAGEHLP_MODULE) }; if(SymGetModuleInfo(m_hProcess, sf.AddrPC.Offset, &module)) { outputStream << module.ModuleName << "!"; if ( SymFromAddr(m_hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol)) { char undecoratedName[MAX_SYM_NAME]; UnDecorateSymbolName(pSymbol->Name, undecoratedName, MAX_SYM_NAME, UNDNAME_COMPLETE); outputStream << undecoratedName; outputStream << "("; // Use SymSetContext to get just the locals/params for this frame IMAGEHLP_STACK_FRAME imagehlpStackFrame; imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset; SymSetContext( m_hProcess, &imagehlpStackFrame, 0 ); // Enumerate the locals/parameters EnumerateSymbolsContext context(sf, outputStream); SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &context ); outputStream << ")"; outputStream << " + " << Offset(reinterpret_cast<void*>(symDisplacement)); // Get the source line for this stack frame entry IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) }; DWORD dwLineDisplacement; if ( SymGetLineFromAddr( m_hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo ) ) { outputStream << " " << lineInfo.FileName << " line " << Unsigned(lineInfo.LineNumber); } } else { outputStream << Address(reinterpret_cast<void*>(sf.AddrPC.Offset)); } } outputStream << "\n"; } SymCleanup(m_hProcess); return; }
GCallStack::GCallStack(HANDLE hThread, CONTEXT& c) { DWORD imageType = IMAGE_FILE_MACHINE_I386; HANDLE hProcess = GetCurrentProcess(); int frameNum; // counts walked frames #if defined(_LINUX64) || defined(_WIN64) || defined(_IOS) unsigned __int64 offsetFromSymbol; // tells us how far from the symbol we were #else DWORD offsetFromSymbol; // tells us how far from the symbol we were #endif DWORD symOptions; // symbol handler settings IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN ); char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans IMAGEHLP_MODULE Module; IMAGEHLP_LINE Line; std::string symSearchPath; char *tt = 0, *p; STACKFRAME s; // in/out stackframe memset( &s, '\0', sizeof s ); tt = new char[TTBUFLEN]; // build symbol search path from: symSearchPath = ""; // current directory if ( GetCurrentDirectory( TTBUFLEN, tt ) ) symSearchPath += tt + std::string( ";" ); // dir with executable if ( GetModuleFileName( 0, tt, TTBUFLEN ) ) { for ( p = tt + strlen( tt ) - 1; p >= tt; -- p ) { // locate the rightmost path separator if ( *p == '\\' || *p == '/' || *p == ':' ) break; } // if we found one, p is pointing at it; if not, tt only contains // an exe name (no path), and p points before its first byte if ( p != tt ) // path sep found? { if ( *p == ':' ) // we leave colons in place ++ p; *p = '\0'; // eliminate the exe name and last path sep symSearchPath += tt + std::string( ";" ); } } // environment variable _NT_SYMBOL_PATH if ( GetEnvironmentVariable( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) ) symSearchPath += tt + std::string( ";" ); // environment variable _NT_ALTERNATE_SYMBOL_PATH if ( GetEnvironmentVariable( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) ) symSearchPath += tt + std::string( ";" ); // environment variable SYSTEMROOT if ( GetEnvironmentVariable( "SYSTEMROOT", tt, TTBUFLEN ) ) symSearchPath += tt + std::string( ";" ); if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 ); // why oh why does SymInitialize() want a writeable string? strncpy( tt, symSearchPath.c_str(), TTBUFLEN ); tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator // init symbol handler stuff (SymInitialize()) if ( ! SymInitialize( hProcess, tt, false ) ) { goto tagCleanUp; } symOptions = SymGetOptions(); symOptions |= SYMOPT_LOAD_LINES; symOptions &= ~SYMOPT_UNDNAME; SymSetOptions( symOptions ); // SymSetOptions() enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() ); // init STACKFRAME for first call // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway, #ifndef _WIN64 s.AddrPC.Offset = c.Eip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Ebp; s.AddrFrame.Mode = AddrModeFlat; #endif memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN ); pSym->SizeOfStruct = IMGSYMLEN; pSym->MaxNameLength = MAXNAMELEN; memset( &Line, '\0', sizeof Line ); Line.SizeOfStruct = sizeof Line; memset( &Module, '\0', sizeof Module ); Module.SizeOfStruct = sizeof Module; offsetFromSymbol = 0; for ( frameNum = 0; ;) { // get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase()) // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can // assume that either you are done, or that the stack is so hosed that the next // deeper frame could not be found. if ( ! StackWalk( imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL ) ) break; if ( s.AddrPC.Offset != 0 ) { // we seem to have a valid PC // show procedure info (SymGetSymFromAddr()) if ( ! SymGetSymFromAddr( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) ) { break; } else { // UnDecorateSymbolName() UnDecorateSymbolName( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE ); if (!frameNum && strstr(undFullName, "Exception")) continue; _stk += undFullName; } } // we seem to have a valid PC // no return address means no deeper stackframe if ( s.AddrReturn.Offset == 0 ) { // avoid misunderstandings in the printf() following the loop SetLastError( 0 ); break; } ++frameNum; } // de-init symbol handler etc. (SymCleanup()) SymCleanup( hProcess ); free( pSym ); tagCleanUp:; delete [] tt; }
void StackDumper::ShowStackCore( HANDLE hThread, CONTEXT& c ) { // This makes this code custom for 32-bit windows. There is a technique to find out what // machine type we are running on, but this should do us for a good while. DWORD imageType = IMAGE_FILE_MACHINE_I386; HANDLE hProcess = GetCurrentProcess(); int frameNum; // counts walked frames DWORD offsetFromSymbol; // tells us how far from the symbol we were DWORD symOptions; // symbol handler settings IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN ); IMAGEHLP_MODULE Module; IMAGEHLP_LINE Line; StrApp strSearchPath; // path to search for symbol tables (I think...JT) achar *tt = 0; STACKFRAME s; // in/out stackframe memset( &s, '\0', sizeof s ); tt = new achar[TTBUFLEN]; if (!tt) return; // Build symbol search path. // Add current directory if (::GetCurrentDirectory( TTBUFLEN, tt ) ) AppendToStaWithSep(strSearchPath, tt); // Add directory containing executable or DLL we are running in. if (::GetModuleFileName( 0, tt, TTBUFLEN ) ) { StrUni stuPath = tt; // convert to Unicode if necessary, allows use of wchars const OLECHAR * pchPath = stuPath.Chars(); const OLECHAR * pch; for (pch = pchPath + wcslen(pchPath) - 1; pch >= pchPath; -- pch ) { // locate the rightmost path separator if ( *pch == L'\\' || *pch == L'/' || *pch == L':' ) break; } // if we found one, p is pointing at it; if not, tt only contains // an exe name (no path), and p points before its first byte if ( pch != pchPath ) // path sep found? { if ( *pch == L':' ) // we leave colons in place ++ pch; if (strSearchPath.Length()) strSearchPath.Append(";"); strSearchPath.Append(pchPath, (pch - pchPath)); } } // environment variable _NT_SYMBOL_PATH if (::GetEnvironmentVariable( _T("_NT_SYMBOL_PATH"), tt, TTBUFLEN )) AppendToStaWithSep(strSearchPath, tt); // environment variable _NT_ALTERNATE_SYMBOL_PATH if (::GetEnvironmentVariable( _T("_NT_ALTERNATE_SYMBOL_PATH"), tt, TTBUFLEN )) AppendToStaWithSep(strSearchPath, tt); // environment variable SYSTEMROOT if (::GetEnvironmentVariable( _T("SYSTEMROOT"), tt, TTBUFLEN )) AppendToStaWithSep(strSearchPath, tt); // Why oh why does SymInitialize() want a writeable string? Surely it doesn't modify it... // The doc clearly says it is an [in] parameter. // Also, there is not a wide character version of this function! StrAnsi staT(strSearchPath); if ( !::SymInitialize( hProcess, const_cast<char *>(staT.Chars()), false ) ) goto LCleanup; // SymGetOptions() symOptions = SymGetOptions(); symOptions |= SYMOPT_LOAD_LINES; symOptions &= ~SYMOPT_UNDNAME; SymSetOptions( symOptions ); // SymSetOptions() // Enumerate modules and tell imagehlp.dll about them. // On NT, this is not necessary, but it won't hurt. EnumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() ); // init STACKFRAME for first call // Notes: AddrModeFlat is just an assumption. I hate VDM debugging. // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway, // and good riddance. s.AddrPC.Offset = c.Eip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Ebp; s.AddrFrame.Mode = AddrModeFlat; memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN ); pSym->SizeOfStruct = IMGSYMLEN; pSym->MaxNameLength = MAXNAMELEN; memset( &Line, '\0', sizeof Line ); Line.SizeOfStruct = sizeof Line; memset( &Module, '\0', sizeof Module ); Module.SizeOfStruct = sizeof Module; offsetFromSymbol = 0; if (!m_pstaDump) { try { m_pstaDump = NewObj StrAnsiBufHuge; } catch (...) { goto LCleanup; } } // If the stack dump gets too big, we remove some entries from near the // middle, and insert a marker. This counts the characters up to the // end of the marker. int ichEndLowHalf; ichEndLowHalf = 0; m_pstaDump->FormatAppend( "\r\n--# FV EIP----- RetAddr- FramePtr StackPtr Symbol\r\n" ); // EberhardB: a stack of 1.000 frames should be enough in most cases; limiting it // prevents a mysterious infinite(?) loop on our build machine. for ( frameNum = 0; frameNum < 1000; ++ frameNum ) { // get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase()) // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can // assume that either you are done, or that the stack is so hosed that the next // deeper frame could not be found. if ( ! StackWalk( imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL ) ) break; // display its contents m_pstaDump->FormatAppend( "%3d %c%c %08x %08x %08x %08x ", frameNum, s.Far? 'F': '.', s.Virtual? 'V': '.', s.AddrPC.Offset, s.AddrReturn.Offset, s.AddrFrame.Offset, s.AddrStack.Offset ); if ( s.AddrPC.Offset == 0 ) { m_pstaDump->Append( "(-nosymbols- PC == 0)\r\n" ); } else { // we seem to have a valid PC char undName[MAXNAMELEN]; // undecorated name //char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans // show procedure info (SymGetSymFromAddr()) if ( ! SymGetSymFromAddr( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) ) { if ( gle != 487 ) m_pstaDump->FormatAppend( "SymGetSymFromAddr(): gle = %u\r\n", gle ); } else { UnDecorateSymbolName( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY ); //UnDecorateSymbolName( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE ); m_pstaDump->Append( undName ); //if ( offsetFromSymbol != 0 ) // m_pstaDump->FormatAppend( " %+d bytes", offsetFromSymbol ); //m_pstaDump->FormatAppend( "\r\n Sig: %s\r\n", pSym->Name ); //m_pstaDump->FormatAppend( "\r\n Decl: %s\r\n", undFullName ); } // show line number info, NT5.0-method (SymGetLineFromAddr()). If we can't get this function, // or it doesn't work, leave out line number info. if (! g_pfnSymGetLineFromAddr) { StrApp staModName("IMAGEHLP.DLL"); g_pfnSymGetLineFromAddr = (PFNSYMGETLINEFROMADDR) GetProcAddress( GetModuleHandle(staModName.Chars()), "SymGetLineFromAddr"); } if (! g_pfnSymGetLineFromAddr || ! g_pfnSymGetLineFromAddr( hProcess, s.AddrPC.Offset, &offsetFromSymbol, &Line ) ) { if ( g_pfnSymGetLineFromAddr && gle != 487 ) // apparently a magic number indicating not in symbol file. m_pstaDump->FormatAppend( "SymGetLineFromAddr(): gle = %u\r\n", gle ); else m_pstaDump->FormatAppend( " (no line # avail)\r\n"); } else { m_pstaDump->FormatAppend( " %s(%u)\r\n", Line.FileName, Line.LineNumber ); } #ifdef JT_20010626_WantModuleInfo // If we want this info adapt the printf and _snprintf in the following. // show module info (SymGetModuleInfo()) if ( ! SymGetModuleInfo( hProcess, s.AddrPC.Offset, &Module ) ) { m_pstaDump->FormatAppend( "SymGetModuleInfo): gle = %u\r\n", gle ); } else { // got module info OK m_pstaDump->FormatAppend( " Mod: %s[%s], base: 0x%x\r\n Sym: type: ", Module.ModuleName, Module.ImageName, Module.BaseOfImage ); switch ( Module.SymType ) { case SymNone: m_pstaDump->FormatAppend( "-nosymbols-"); break; case SymCoff: m_pstaDump->FormatAppend( "COFF"); break; case SymCv: m_pstaDump->FormatAppend( "CV"); break; case SymPdb: m_pstaDump->FormatAppend( "PDB"); break; case SymExport: m_pstaDump->FormatAppend( "-exported-"); break; case SymDeferred: m_pstaDump->FormatAppend( "-deferred-"); break; case SymSym: m_pstaDump->FormatAppend( "SYM"); break; default: m_pstaDump->FormatAppend( "symtype=%d", (long) Module.SymType); break; } m_pstaDump->FormatAppend( ", file: %s\r\n", Module.LoadedImageName); } // got module info OK #endif // JT_20010626_WantModuleInfo // We don't want to return more than about 10K of info (enough for an email). // This also serves to make sure there's enough room in the buffer for more. // The idea is that we'd like to keep both the top and bottom of the stack. // So we delete frames from the middle until we have less than 10K. if (m_pstaDump->Length() > MAXDUMPLEN) { if (!ichEndLowHalf) { static char * pszGap = "\r\n\r\n\r\n******************Frames skipped here***************\r\n\r\n\r\n"; int cchGap = strlen(pszGap); ichEndLowHalf = FindStartOfFrame(MAXDUMPLEN / 2); // Overwrite some of what's there with the gap marker. The incomplete // frame will be part of what gets deleted. m_pstaDump->Replace(ichEndLowHalf, ichEndLowHalf + cchGap, pszGap, cchGap); ichEndLowHalf += cchGap; } int cchLeave = m_pstaDump->Length(); int ichKeep = ichEndLowHalf; while (cchLeave > MAXDUMPLEN) { int ichKeepT = FindStartOfFrame(ichKeep + 1); cchLeave -= ichKeepT - ichKeep; ichKeep = ichKeepT; } m_pstaDump->Replace(ichEndLowHalf, ichKeep, (char *)NULL, 0); } } // we seem to have a valid PC // no return address means no deeper stackframe if ( s.AddrReturn.Offset == 0 ) { // avoid misunderstandings in the printf() following the loop SetLastError( 0 ); break; } } // for ( frameNum ) if ( gle != 0 ) printf( "\r\nStackWalk(): gle = %u\r\n", gle ); LCleanup: ResumeThread( hThread ); // de-init symbol handler etc. SymCleanup( hProcess ); free( pSym ); delete [] tt; #ifdef DEBUG ::OutputDebugStringA(m_pstaDump->Chars()); #endif }
void PrintBacktrace(PCONTEXT context) { HANDLE hProcess = GetCurrentProcess(); HANDLE hThread = GetCurrentThread(); char appDir[MAX_PATH + 1]; GetModuleFileName(nullptr, appDir, sizeof(appDir)); char* end = strrchr(appDir, PATH_SEPARATOR); if (end) *end = '\0'; SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS); if (!SymInitialize(hProcess, appDir, TRUE)) { warn("Could not obtain detailed exception information: SymInitialize failed"); return; } const int MAX_NAMELEN = 1024; IMAGEHLP_SYMBOL64* sym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_NAMELEN); memset(sym, 0, sizeof(IMAGEHLP_SYMBOL64) + MAX_NAMELEN); sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); sym->MaxNameLength = MAX_NAMELEN; IMAGEHLP_LINE64 ilLine; memset(&ilLine, 0, sizeof(ilLine)); ilLine.SizeOfStruct = sizeof(ilLine); STACKFRAME64 sfStackFrame; memset(&sfStackFrame, 0, sizeof(sfStackFrame)); DWORD imageType; #ifdef _M_IX86 imageType = IMAGE_FILE_MACHINE_I386; sfStackFrame.AddrPC.Offset = context->Eip; sfStackFrame.AddrPC.Mode = AddrModeFlat; sfStackFrame.AddrFrame.Offset = context->Ebp; sfStackFrame.AddrFrame.Mode = AddrModeFlat; sfStackFrame.AddrStack.Offset = context->Esp; sfStackFrame.AddrStack.Mode = AddrModeFlat; #elif _M_X64 imageType = IMAGE_FILE_MACHINE_AMD64; sfStackFrame.AddrPC.Offset = context->Rip; sfStackFrame.AddrPC.Mode = AddrModeFlat; sfStackFrame.AddrFrame.Offset = context->Rsp; sfStackFrame.AddrFrame.Mode = AddrModeFlat; sfStackFrame.AddrStack.Offset = context->Rsp; sfStackFrame.AddrStack.Mode = AddrModeFlat; #else warn("Could not obtain detailed exception information: platform not supported"); return; #endif for (int frameNum = 0; ; frameNum++) { if (frameNum > 1000) { warn("Endless stack, abort tracing"); return; } if (!StackWalk64(imageType, hProcess, hThread, &sfStackFrame, context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) { warn("Could not obtain detailed exception information: StackWalk64 failed"); return; } DWORD64 dwAddr = sfStackFrame.AddrPC.Offset; BString<1024> symName; BString<1024> srcFileName; int lineNumber = 0; DWORD64 dwSymbolDisplacement; if (SymGetSymFromAddr64(hProcess, dwAddr, &dwSymbolDisplacement, sym)) { UnDecorateSymbolName(sym->Name, symName, symName.Capacity(), UNDNAME_COMPLETE); symName[sizeof(symName) - 1] = '\0'; } else { symName = "<symbol not available>"; } DWORD dwLineDisplacement; if (SymGetLineFromAddr64(hProcess, dwAddr, &dwLineDisplacement, &ilLine)) { lineNumber = ilLine.LineNumber; char* useFileName = ilLine.FileName; char* root = strstr(useFileName, "\\daemon\\"); if (root) { useFileName = root; } srcFileName = useFileName; } else { srcFileName = "<filename not available>"; } info("%s (%i) : %s", *srcFileName, lineNumber, *symName); if (sfStackFrame.AddrReturn.Offset == 0) { break; } } }
bool CMapFile::LoadFile(LPCTSTR szFilePath, FuncInfoList& listFuncDatas) { HANDLE hFile = ::CreateFile(szFilePath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); if(hFile == INVALID_HANDLE_VALUE) return false; const int pointerLength = 8; bool result = false; for(;;) { // Read File DWORD dwSize = ::GetFileSize(hFile, NULL); LPSTR pFileData = (LPSTR)::malloc(dwSize + 1); LPSTR pFileEnd = pFileData + dwSize; DWORD dwRead; if(!::ReadFile(hFile, (LPVOID)pFileData, dwSize, &dwRead, NULL) || dwRead != dwSize) break; pFileData[dwSize - 1] = 0; // Find begin of function datas LPSTR pLine = strstr(pFileData, "Lib:Object"); if(pLine == NULL) break; // Replace \r\n with \0 for(DWORD i=0; i<dwSize; ++ i) { if(pFileData[i] == '\r' || pFileData[i] == '\n') pFileData[i] = 0; } char buffer[1024]; while((pLine = NextLine(pLine, pFileEnd)) != NULL) { stFuncData data; data.nFuncSize = 0; data.strFuncSize = "0"; if(strstr(pLine, "entry point at") != NULL) { result = true; break; } // 0005:000002b0 __imp___CRT_RTC_INITW 004182b0 MSVCRTD:MSVCR90D.dll // Section No data.nSectionNo = atoi(pLine); pLine = strchr(pLine, ':'); if(pLine == NULL) break; // FuncName pLine = strtok(pLine, " "); if(pLine == NULL) break; pLine = (LPSTR)strtok(NULL, " "); if(pLine == NULL) break; data.strFuncName = pLine; if(data.strFuncName == _T("__TI2PAD")) printf(""); // DecodedFuncName #ifdef UnDecorateSymbolName #undef UnDecorateSymbolName #endif // UnDecorateSymbolName if(UnDecorateSymbolName(pLine, buffer, _countof(buffer), UNDNAME_NO_LEADING_UNDERSCORES | UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_FUNCTION_RETURNS | UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ALLOCATION_LANGUAGE | UNDNAME_NO_THISTYPE | UNDNAME_NO_ACCESS_SPECIFIERS | UNDNAME_NO_THROW_SIGNATURES | UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_RETURN_UDT_MODEL | UNDNAME_32_BIT_DECODE | UNDNAME_NAME_ONLY)) { data.strFuncName = buffer; } // RVA pLine = strtok(NULL, " "); if(pLine == NULL) break; // FuncAddr sscanf(pLine, "%x", &data.pFuncAddr); if(listFuncDatas.GetSize() > 0) { stFuncData& lastFuncData = listFuncDatas[listFuncDatas.GetSize() - 1]; if(lastFuncData.nSectionNo == data.nSectionNo && lastFuncData.nFuncSize <= 0) { lastFuncData.nFuncSize = data.pFuncAddr - lastFuncData.pFuncAddr; lastFuncData.strFuncSize.Format(_T("%d"), lastFuncData.nFuncSize); } } // find last token LPSTR lastToken = pLine; while(pLine != NULL) { lastToken = pLine; pLine = strtok(NULL, " "); } pLine = lastToken; // Module:Obj LPCSTR pos = strchr(pLine, ':'); if(pos == NULL) { data.strObjName = pLine; } else { CString tmp(pLine, pos - pLine); data.strModuleName = tmp; data.strObjName = pos + 1; } listFuncDatas.Add(data); } break; } ::CloseHandle(hFile); return result; }
//************************************************************************* // Method: GetStackInfo // Description: Gets the call stack for the specified thread // // Parameters: // hThread - the handle to the target thread // threadContext - the context of the target thread // // Return Value: (FrameInfo *) An array containing stack trace data //************************************************************************* SiUtils::SiArray <FrameInfo *> StackWalker::GetStackInfo(HANDLE hThread, CONTEXT &threadContext) { //Clear the frame array of any previous data frameArray.Clear(); DWORD imageType = IMAGE_FILE_MACHINE_I386; HANDLE hProcess = GetCurrentProcess(); int frameNum; // counts walked frames DWORD offsetFromSymbol; // tells us how far from the symbol we were DWORD symOptions; // symbol handler settings IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN ); char undName[MAXNAMELEN]; // undecorated name char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans IMAGEHLP_MODULE Module; IMAGEHLP_LINE Line; std::string symSearchPath; char *tt = 0, *p; STACKFRAME s; // in/out stackframe memset( &s, '\0', sizeof s ); tt = new char[TTBUFLEN]; // build symbol search path from: symSearchPath = ""; // current directory if (GetCurrentDirectory(TTBUFLEN, tt )) symSearchPath += tt + std::string( ";" ); // dir with executable if (GetModuleFileName(0, tt, TTBUFLEN)) { for ( p = tt + strlen( tt ) - 1; p >= tt; -- p ) { // locate the rightmost path separator if ( *p == '\\' || *p == '/' || *p == ':' ) break; } if ( p != tt ) { if ( *p == ':' ) // we leave colons in place ++ p; *p = '\0'; // eliminate the exe name and last path sep symSearchPath += tt + std::string( ";" ); } } // environment variable _NT_SYMBOL_PATH if ( GetEnvironmentVariable( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) ) symSearchPath += tt + std::string( ";" ); // environment variable _NT_ALTERNATE_SYMBOL_PATH if ( GetEnvironmentVariable( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) ) symSearchPath += tt + std::string( ";" ); // environment variable SYSTEMROOT if ( GetEnvironmentVariable( "SYSTEMROOT", tt, TTBUFLEN ) ) symSearchPath += tt + std::string( ";" ); if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 ); strncpy( tt, symSearchPath.c_str(), TTBUFLEN ); tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator // init symbol handler stuff (SymInitialize()) if (!SymInitialize(hProcess, tt, false )) goto cleanup; // SymGetOptions() symOptions = SymGetOptions(); symOptions |= SYMOPT_LOAD_LINES; symOptions &= ~SYMOPT_UNDNAME; SymSetOptions( symOptions ); // Enumerate modules and tell imagehlp.dll about them. enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() ); // init STACKFRAME for first call // Notes: AddrModeFlat is just an assumption. I hate VDM debugging. // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway, // and good riddance. s.AddrPC.Offset = threadContext.Eip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = threadContext.Ebp; s.AddrFrame.Mode = AddrModeFlat; memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN ); pSym->SizeOfStruct = IMGSYMLEN; pSym->MaxNameLength = MAXNAMELEN; memset( &Line, '\0', sizeof Line ); Line.SizeOfStruct = sizeof Line; memset( &Module, '\0', sizeof Module ); Module.SizeOfStruct = sizeof Module; offsetFromSymbol = 0; for ( frameNum = 0; ; ++ frameNum ) { FrameInfo * frameInfo = new FrameInfo(); if (!StackWalk( imageType, hProcess, hThread, &s, &threadContext, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL ) ) break; frameInfo->frameNumber = frameNum; frameInfo->IsWOWFarCall = s.Far; frameInfo->IsVirtualFrame = s.Virtual; frameInfo->Eip = s.AddrPC.Offset; //if 0, the no symbols frameInfo->ReturnAddr = s.AddrReturn.Offset; frameInfo->FramePtr = s.AddrFrame.Offset; frameInfo->StackPtr = s.AddrStack.Offset; if (s.AddrPC.Offset == 0) { //printf( "(-nosymbols- PC == 0)\n" ); } else { // we seem to have a valid PC // show procedure info (SymGetSymFromAddr()) if ( ! SymGetSymFromAddr( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) ) { /*if ( GetLastError() != 487 ) printf( "SymGetSymFromAddr(): GetLastError() = %lu\n", GetLastError() );*/ } else { // UnDecorateSymbolName() UnDecorateSymbolName( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY ); UnDecorateSymbolName( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE ); strcpy (frameInfo->undecoratedName, undName); strcpy (frameInfo->undecoratedFullName, undFullName); strcpy (frameInfo->signature, pSym->Name ); frameInfo->offsetFromSymbol = offsetFromSymbol; } // show module info (SymGetModuleInfo()) if (SymGetModuleInfo( hProcess, s.AddrPC.Offset, &Module ) ) { strcpy (frameInfo->ModuleName, Module.ModuleName); strcpy (frameInfo->ImageName, Module.ImageName); frameInfo->BaseOfImage = Module.BaseOfImage; } } // we seem to have a valid PC // no return address means no deeper stackframe if ( s.AddrReturn.Offset == 0 ) { // avoid misunderstandings in the printf() following the loop SetLastError( 0 ); break; } this->frameArray.Add (frameInfo); } // for ( frameNum ) cleanup: // de-init symbol handler etc. (SymCleanup()) SymCleanup( hProcess ); free( pSym ); delete [] tt; return frameArray; }
// Get function prototype and parameter info from ip address and stack address static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol ) { BOOL ret = FALSE; DWORD dwSymSize = 10000; TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?"); CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; LPTSTR lpszParamSep = nullptr; LPTSTR lpszParsed = lpszUnDSymbol; PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); ::ZeroMemory( pSym, dwSymSize ); pSym->SizeOfStruct = dwSymSize; pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL); // Set the default to unknown _tcscpy( lpszSymbol, _T("?") ); // Get symbol info for IP DWORD64 dwDisp = 0; if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) ) { // Make the symbol readable for humans UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, UNDNAME_COMPLETE | UNDNAME_NO_THISTYPE | UNDNAME_NO_SPECIAL_SYMS | UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS ); // Symbol information is ANSI string PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol ); // I am just smarter than the symbol file :) if ( _tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0 ) _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)")); else if ( _tcscmp(lpszUnDSymbol, _T("_main")) == 0 ) _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)")); else if ( _tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0 ) _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()")); else if ( _tcscmp(lpszUnDSymbol, _T("_wmain")) == 0 ) _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)")); else if ( _tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0 ) _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()")); lpszSymbol[0] = _T('\0'); // Let's go through the stack, and modify the function prototype, and insert the actual // parameter values from the stack if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == nullptr && _tcsstr( lpszUnDSymbol, _T("()") ) == nullptr) { ULONG index = 0; for ( ; ; index++ ) { lpszParamSep = _tcschr( lpszParsed, _T(',') ); if ( lpszParamSep == nullptr ) break; *lpszParamSep = _T('\0'); _tcscat( lpszSymbol, lpszParsed ); _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) ); lpszParsed = lpszParamSep + 1; } lpszParamSep = _tcschr( lpszParsed, _T(')') ); if ( lpszParamSep != nullptr ) { *lpszParamSep = _T('\0'); _tcscat( lpszSymbol, lpszParsed ); _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) ); lpszParsed = lpszParamSep + 1; } } _tcscat( lpszSymbol, lpszParsed ); ret = TRUE; } GlobalFree( pSym ); return ret; }
void CDebugMgr::RecordStack(const CONTEXT* pContext) { m_pchBuffer += wsprintf(m_pchBuffer,"\nCall stack:\n" ); m_pchBuffer += wsprintf(m_pchBuffer,"Address Frame Logical addr Module\n" ); DWORD pc = pContext->Eip; PDWORD pFrame, pPrevFrame; pFrame = (PDWORD)pContext->Ebp; do { TCHAR szModule[MAX_PATH] = ""; DWORD section = 0, offset = 0; FindLogicalAddress((PVOID)pc, szModule,sizeof(szModule),section,offset ); m_pchBuffer += wsprintf(m_pchBuffer,"%08X %08X %04X:%08X %s", pc, pFrame, section, offset, szModule ); // See if we need to spit out symbol/source/line info if ( m_dwOptions & DEBUGMGR_RESOLVESYMBOLS ) { // Generate the name of the map file TCHAR szMap[MAX_PATH] = ""; wsprintf(szMap, szModule); wsprintf(&szMap[lstrlen(szMap)-3], "MAP"); TCHAR szSymbol[256] = "<unknown>"; TCHAR szUnmangledSymbol[1024] = "<unknown>"; TCHAR szObject[128] = "<unknown>"; if ( FindSymbol(szMap, section, offset, szSymbol, szObject) ) { if ( m_dwOptions & DEBUGMGR_UNMANGLESYMBOLS && *szSymbol == '?' ) { UnDecorateSymbolName(szSymbol, szUnmangledSymbol, 1024, UNDNAME_COMPLETE); } else { wsprintf(szUnmangledSymbol, szSymbol); } } m_pchBuffer += wsprintf(m_pchBuffer, " in function %s in object file %s", szUnmangledSymbol, szObject); if ( m_dwOptions & DEBUGMGR_RESOLVESOURCELINE ) { TCHAR szSource[256]; TCHAR szLine[256]; if ( FindSourceLine(szMap, szObject, section, offset, szSource, szLine) ) { m_pchBuffer += wsprintf(m_pchBuffer, " at Line %s of %s\n", szLine, szSource); } else { m_pchBuffer += wsprintf(m_pchBuffer, " at Line ??? of ???\n"); } } else { m_pchBuffer += wsprintf(m_pchBuffer, "\n\n"); } } else { m_pchBuffer += wsprintf(m_pchBuffer, "\n"); } // Go on to the next stack frame pc = pFrame[1]; pPrevFrame = pFrame; pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack if ( (DWORD)pFrame & 3 ) // Frame pointer must be aligned on a break; // DWORD boundary. Bail if not so. if ( pFrame <= pPrevFrame ) break; // Can two DWORDs be read from the supposed frame address? if ( IsBadWritePtr(pFrame, sizeof(PVOID)*2) ) break; } while ( 1 ); }