/////////////////////////////////////////////////////////////////////////////// // // LocalsFromAddr.cpp // // Author: Oleg Starodumov // // /////////////////////////////////////////////////////////////////////////////// // // Description: // // This example looks up a function by address, then enumerates local variables // and parameters of the function and displays some simple information about them. // // This example shows how to: // // * Define _NO_CVCONST_H to be able to use various non-default declarations // from DbgHelp.h (e.g. SymTagEnum enumeration) // * Initialize DbgHelp // * Load symbols for a module or from a .PDB file // * Check what kind of symbols is loaded // * Look up a function by address (supplied by the user) // * Enumerate local variables and parameters of a function // * Display simple information about local variables and parameters // * Deinitialize DbgHelp // // Actions: // // * Enable debug option // * Initialize DbgHelp // * If symbols should be loaded from a .PDB file, determine its size // * Load symbols // * Obtain and display information about loaded symbols // * Look up a function by address // * Enumerate local variables and parameters of the function // * Display simple information about every local variable or parameter // * Deinitialize DbgHelp // // Command line parameters: // // * Path to the module you want to load symbols for, // or to a .PDB file to load the symbols from // * Address of the function // /////////////////////////////////////////////////////////////////////////////// // Include files // #include #include // Now we have to define _NO_CVCONST_H to be able to access // various declarations from DbgHelp.h, which are not available by default #define _NO_CVCONST_H #include #include #include /////////////////////////////////////////////////////////////////////////////// // Important types missing in DbgHelp.h // #ifdef _NO_CVCONST_H // CV_HREG_e, originally from CVCONST.H in DIA SDK typedef enum CV_HREG_e { // Only a limited number of registers included here CV_REG_EAX = 17, CV_REG_ECX = 18, CV_REG_EDX = 19, CV_REG_EBX = 20, CV_REG_ESP = 21, CV_REG_EBP = 22, CV_REG_ESI = 23, CV_REG_EDI = 24, } CV_HREG_e; #endif // _NO_CVCONST_H /////////////////////////////////////////////////////////////////////////////// // Directives // #pragma comment( lib, "dbghelp.lib" ) /////////////////////////////////////////////////////////////////////////////// // Declarations // bool GetFileParams( const TCHAR* pFileName, DWORD64& BaseAddr, DWORD& FileSize ); bool GetFileSize( const TCHAR* pFileName, DWORD& FileSize ); BOOL CALLBACK MyEnumSymbolsCallback( SYMBOL_INFO* pSymInfo, ULONG SymbolSize, PVOID UserContext ); void ShowSymbolDetails( SYMBOL_INFO& SymInfo ); const TCHAR* TagStr( ULONG Tag ); LPCTSTR RegisterStr( ULONG RegCode ); /////////////////////////////////////////////////////////////////////////////// // CSymbolInfoPackage class declaration // // Wrapper for SYMBOL_INFO_PACKAGE structure // struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE { CSymbolInfoPackage() { si.SizeOfStruct = sizeof(SYMBOL_INFO); si.MaxNameLen = sizeof(name); } }; /////////////////////////////////////////////////////////////////////////////// // main // int _tmain( int argc, const TCHAR* argv[] ) { BOOL bRet = FALSE; // Check command line parameters if( argc < 3 ) { _tprintf( _T("Usage: %s \n"), argv[0] ); return 0; } DWORD64 SymAddr = 0; if( _stscanf( argv[2], _T("%I64x"), &SymAddr ) != 1 ) { _tprintf( _T("Usage: %s \n"), argv[0] ); return 0; } // Set options DWORD Options = SymGetOptions(); // SYMOPT_DEBUG option asks DbgHelp to print additional troubleshooting // messages to debug output - use the debugger's Debug Output window // to view the messages Options |= SYMOPT_DEBUG; ::SymSetOptions( Options ); // Initialize DbgHelp and load symbols for all modules of the current process bRet = ::SymInitialize ( GetCurrentProcess(), // Process handle of the current process NULL, // No user-defined search path -> use default FALSE // Do not load symbols for modules in the current process ); if( !bRet ) { _tprintf(_T("Error: SymInitialize() failed. Error code: %u \n"), ::GetLastError()); return 0; } do { // Determine the base address and the file size const TCHAR* pFileName = argv[1]; DWORD64 BaseAddr = 0; DWORD FileSize = 0; if( !GetFileParams( pFileName, BaseAddr, FileSize ) ) { _tprintf( _T("Error: Cannot obtain file parameters (internal error).\n") ); break; } // Load symbols for the module _tprintf( _T("Loading symbols for: %s ... \n"), pFileName ); DWORD64 ModBase = ::SymLoadModule64 ( GetCurrentProcess(), // Process handle of the current process NULL, // Handle to the module's image file (not needed) pFileName, // Path/name of the file NULL, // User-defined short name of the module (it can be NULL) BaseAddr, // Base address of the module (cannot be NULL if .PDB file is used, otherwise it can be NULL) FileSize // Size of the file (cannot be NULL if .PDB file is used, otherwise it can be NULL) ); if( ModBase == 0 ) { _tprintf(_T("Error: SymLoadModule64() failed. Error code: %u \n"), ::GetLastError()); break; } _tprintf( _T("Load address: %I64x \n"), ModBase ); // Look up symbol by address _tprintf( _T("Looking for symbol at address %I64x ... \n"), SymAddr ); CSymbolInfoPackage sip; // it contains SYMBOL_INFO structure plus additional // space for the name of the symbol DWORD64 Displacement = 0; bRet = ::SymFromAddr( GetCurrentProcess(), // Process handle of the current process SymAddr, // Symbol address &Displacement, // Address of the variable that will receive the displacement &sip.si // Address of the SYMBOL_INFO structure (inside "sip" object) ); if( !bRet ) { _tprintf( _T("Error: SymFromAddr() failed. Error code: %u \n"), ::GetLastError() ); break; } // Symbol found. Is it a function ? if( sip.si.Tag != SymTagFunction ) { // No, it is not a function _tprintf( _T("No function found at the given address.\n") ); break; } // Enumerate local variables of the function // Set the context to the user-specified address IMAGEHLP_STACK_FRAME sf; sf.InstructionOffset = SymAddr; bRet = ::SymSetContext( GetCurrentProcess(), // Process handle of the current process &sf, // The context 0 // Not used ); if( !bRet ) { _tprintf( _T("Error: SymSetContext() failed. Error code: %u \n"), ::GetLastError() ); break; } // Enumerate local variables int NumLocals = 0; // the number of local variables found bRet = ::SymEnumSymbols( GetCurrentProcess(), // Process handle of the current process 0, // 0 -> SymEnumSymbols will use the context set with SymSetContext 0, // Mask must also be 0 to use the context MyEnumSymbolsCallback, // The callback function &NumLocals // User-defined context ); if( !bRet ) { _tprintf( _T("Error: SymEnumSymbols() failed. Error code: %u \n"), ::GetLastError() ); break; } if( NumLocals == 0 ) { _tprintf( _T("The function does not have parameters and local variables.\n") ); } } while( 0 ); // Deinitialize DbgHelp bRet = ::SymCleanup( GetCurrentProcess() ); if( !bRet ) { _tprintf(_T("Error: SymCleanup() failed. Error code: %u \n"), ::GetLastError()); return 0; } // Complete return 0; } /////////////////////////////////////////////////////////////////////////////// // Functions // bool GetFileParams( const TCHAR* pFileName, DWORD64& BaseAddr, DWORD& FileSize ) { // Check parameters if( pFileName == 0 ) { return false; } // Determine the extension of the file TCHAR szFileExt[_MAX_EXT] = {0}; _tsplitpath( pFileName, NULL, NULL, NULL, szFileExt ); // Is it .PDB file ? if( _tcsicmp( szFileExt, _T(".PDB") ) == 0 ) { // Yes, it is a .PDB file // Determine its size, and use a dummy base address BaseAddr = 0x10000000; // it can be any non-zero value, but if we load symbols // from more than one file, memory regions specified // for different files should not overlap // (region is "base address + file size") if( !GetFileSize( pFileName, FileSize ) ) { return false; } } else { // It is not a .PDB file // Base address and file size can be 0 BaseAddr = 0; FileSize = 0; } // Complete return true; } bool GetFileSize( const TCHAR* pFileName, DWORD& FileSize ) { // Check parameters if( pFileName == 0 ) { return false; } // Open the file HANDLE hFile = ::CreateFile( pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if( hFile == INVALID_HANDLE_VALUE ) { _tprintf( _T("CreateFile() failed. Error: %u \n"), ::GetLastError() ); return false; } // Obtain the size of the file FileSize = ::GetFileSize( hFile, NULL ); if( FileSize == INVALID_FILE_SIZE ) { _tprintf( _T("GetFileSize() failed. Error: %u \n"), ::GetLastError() ); // and continue ... } // Close the file if( !::CloseHandle( hFile ) ) { _tprintf( _T("CloseHandle() failed. Error: %u \n"), ::GetLastError() ); // and continue ... } // Complete return ( FileSize != INVALID_FILE_SIZE ); } BOOL CALLBACK MyEnumSymbolsCallback( SYMBOL_INFO* pSymInfo, ULONG SymbolSize, PVOID UserContext ) { if( pSymInfo != 0 ) { // Increase the counter of found local variables and parameters if( UserContext != 0 ) *(int*)UserContext += 1; // Display information about the symbol ShowSymbolDetails( *pSymInfo ); } return TRUE; // Continue enumeration } void ShowSymbolDetails( SYMBOL_INFO& SymInfo ) { // Is it parameter or a local variable ? if( SymInfo.Flags & SYMFLAG_PARAMETER ) { // Parameter _tprintf( _T("Parameter: %s "), SymInfo.Name ); } else if( SymInfo.Flags & SYMFLAG_LOCAL ) { // Local variable _tprintf( _T("Local: %s "), SymInfo.Name ); } else { // Something else ? _tprintf( _T("Unknown: %s "), SymInfo.Name ); } // Register _tprintf( _T("%s"), RegisterStr(SymInfo.Register) ); // Offset long Offset = (long)SymInfo.Address; if( Offset >= 0 ) _tprintf( _T("+%x "), Offset ); else _tprintf( _T("-%x "), (UINT_MAX - Offset + 1) ); // Size _tprintf( _T("Size: %u "), SymInfo.Size ); _tprintf( _T("\n") ); } const TCHAR* TagStr( ULONG Tag ) { switch( Tag ) { case SymTagNull: return _T("Null"); case SymTagExe: return _T("Exe"); case SymTagCompiland: return _T("Compiland"); case SymTagCompilandDetails: return _T("CompilandDetails"); case SymTagCompilandEnv: return _T("CompilandEnv"); case SymTagFunction: return _T("Function"); case SymTagBlock: return _T("Block"); case SymTagData: return _T("Data"); case SymTagAnnotation: return _T("Annotation"); case SymTagLabel: return _T("Label"); case SymTagPublicSymbol: return _T("PublicSymbol"); case SymTagUDT: return _T("UDT"); case SymTagEnum: return _T("Enum"); case SymTagFunctionType: return _T("FunctionType"); case SymTagPointerType: return _T("PointerType"); case SymTagArrayType: return _T("ArrayType"); case SymTagBaseType: return _T("BaseType"); case SymTagTypedef: return _T("Typedef"); case SymTagBaseClass: return _T("BaseClass"); case SymTagFriend: return _T("Friend"); case SymTagFunctionArgType: return _T("FunctionArgType"); case SymTagFuncDebugStart: return _T("FuncDebugStart"); case SymTagFuncDebugEnd: return _T("FuncDebugEnd"); case SymTagUsingNamespace: return _T("UsingNamespace"); case SymTagVTableShape: return _T("VTableShape"); case SymTagVTable: return _T("VTable"); case SymTagCustom: return _T("Custom"); case SymTagThunk: return _T("Thunk"); case SymTagCustomType: return _T("CustomType"); case SymTagManagedType: return _T("ManagedType"); case SymTagDimension: return _T("Dimension"); default: return _T("Unknown"); } return _T(""); } LPCTSTR RegisterStr( ULONG RegCode ) { switch( RegCode ) { case CV_REG_EAX: return _T("EAX"); case CV_REG_ECX: return _T("ECX"); case CV_REG_EDX: return _T("EDX"); case CV_REG_EBX: return _T("EBX"); case CV_REG_ESP: return _T("ESP"); case CV_REG_EBP: return _T("EBP"); case CV_REG_ESI: return _T("ESI"); case CV_REG_EDI: return _T("EDI"); default: return _T("UNKNOWN"); } }