/////////////////////////////////////////////////////////////////////////////// // // AddrFromLine.cpp // // Author: Oleg Starodumov // // /////////////////////////////////////////////////////////////////////////////// // // Description: // // This example determines the address that corresponds to the particular // source line (specified by the user). // // This example shows how to: // // * Initialize DbgHelp // * Load symbols for a module or from a .PDB file // * Check what kind of symbols is loaded // * Look up the address that corresponds to the specified line // * Unload symbols // * 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 the list of source lines and corresponding addresses // for the specified source file // * In the list, look up the needed line and display its address // * Unload symbols // * 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 // * Name of the source file // * Line number // /////////////////////////////////////////////////////////////////////////////// // Include files // #include #include #include #include /////////////////////////////////////////////////////////////////////////////// // Directives // #pragma comment( lib, "dbghelp.lib" ) /////////////////////////////////////////////////////////////////////////////// // Declarations // bool GetFileParams( const TCHAR* pFileName, DWORD64& BaseAddr, DWORD& FileSize ); bool GetFileSize( const TCHAR* pFileName, DWORD& FileSize ); void ShowSymbolInfo( DWORD64 ModBase ); bool GetLineAddress( TCHAR* ModName, TCHAR* SrcFileName, ULONG LineNumber, DWORD64& Address ); /////////////////////////////////////////////////////////////////////////////// // main // int _tmain( int argc, const TCHAR* argv[] ) { BOOL bRet = FALSE; // Check command line parameters if( argc < 4 ) { _tprintf( _T("Usage: %s \n"), argv[0] ); return 0; } ULONG LineNumber = 0; if( _stscanf( argv[3], _T("%u"), &LineNumber ) != 1 ) { _tprintf( _T("Usage: %s \n"), argv[0] ); return 0; } if( LineNumber == 0 ) { _tprintf( _T("Line number cannot be 0.\n") ); 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; // SYMOPT_LOAD_LINES option asks DbgHelp to load line information Options |= SYMOPT_LOAD_LINES; // load line information ::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 ); // Obtain and display information about loaded symbols ShowSymbolInfo( ModBase ); // Obtain the address that corresponds to the line const TCHAR* pSrcFileName = argv[2]; _tprintf( _T("Line: %u File: %s \n"), LineNumber, pSrcFileName ); DWORD64 LineAddress = 0; if( !GetLineAddress( NULL, (PTSTR)pSrcFileName, LineNumber, LineAddress ) ) { _tprintf( _T("Line address not found.\n") ); } else { if( LineAddress == 0 ) { _tprintf( _T("The line does not have the corresponding address.\n") ); } else { _tprintf( _T("Address: %I64x \n"), LineAddress ); } } // Unload symbols for the module bRet = ::SymUnloadModule64( GetCurrentProcess(), ModBase ); if( !bRet ) { _tprintf( _T("Error: SymUnloadModule64() failed. Error code: %u \n"), ::GetLastError() ); } } 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 ); } void ShowSymbolInfo( DWORD64 ModBase ) { // Get module information IMAGEHLP_MODULE64 ModuleInfo; memset(&ModuleInfo, 0, sizeof(ModuleInfo) ); ModuleInfo.SizeOfStruct = sizeof(ModuleInfo); BOOL bRet = ::SymGetModuleInfo64( GetCurrentProcess(), ModBase, &ModuleInfo ); if( !bRet ) { _tprintf(_T("Error: SymGetModuleInfo64() failed. Error code: %u \n"), ::GetLastError()); return; } // Display information about symbols // Kind of symbols switch( ModuleInfo.SymType ) { case SymNone: _tprintf( _T("No symbols available for the module.\n") ); break; case SymExport: _tprintf( _T("Loaded symbols: Exports\n") ); break; case SymCoff: _tprintf( _T("Loaded symbols: COFF\n") ); break; case SymCv: _tprintf( _T("Loaded symbols: CodeView\n") ); break; case SymSym: _tprintf( _T("Loaded symbols: SYM\n") ); break; case SymVirtual: _tprintf( _T("Loaded symbols: Virtual\n") ); break; case SymPdb: _tprintf( _T("Loaded symbols: PDB\n") ); break; case SymDia: _tprintf( _T("Loaded symbols: DIA\n") ); break; case SymDeferred: _tprintf( _T("Loaded symbols: Deferred\n") ); // not actually loaded break; default: _tprintf( _T("Loaded symbols: Unknown format.\n") ); break; } // Image name if( _tcslen( ModuleInfo.ImageName ) > 0 ) { _tprintf( _T("Image name: %s \n"), ModuleInfo.ImageName ); } // Loaded image name if( _tcslen( ModuleInfo.LoadedImageName ) > 0 ) { _tprintf( _T("Loaded image name: %s \n"), ModuleInfo.LoadedImageName ); } // Loaded PDB name if( _tcslen( ModuleInfo.LoadedPdbName ) > 0 ) { _tprintf( _T("PDB file name: %s \n"), ModuleInfo.LoadedPdbName ); } // Is debug information unmatched ? // (It can only happen if the debug information is contained // in a separate file (.DBG or .PDB) if( ModuleInfo.PdbUnmatched || ModuleInfo.DbgUnmatched ) { _tprintf( _T("Warning: Unmatched symbols. \n") ); } // Contents // Line numbers available ? _tprintf( _T("Line numbers: %s \n"), ModuleInfo.LineNumbers ? _T("Available") : _T("Not available") ); // Global symbols available ? _tprintf( _T("Global symbols: %s \n"), ModuleInfo.GlobalSymbols ? _T("Available") : _T("Not available") ); // Type information available ? _tprintf( _T("Type information: %s \n"), ModuleInfo.TypeInfo ? _T("Available") : _T("Not available") ); // Source indexing available ? _tprintf( _T("Source indexing: %s \n"), ModuleInfo.SourceIndexed ? _T("Yes") : _T("No") ); // Public symbols available ? _tprintf( _T("Public symbols: %s \n"), ModuleInfo.Publics ? _T("Available") : _T("Not available") ); } bool GetLineAddress( TCHAR* ModName, TCHAR* SrcFileName, ULONG LineNumber, DWORD64& Address ) { // Check preconditions // Note: ModName can be NULL if( SrcFileName == 0 ) return false; if( LineNumber == 0 ) return false; // Obtain the list of line addresses bool bSuccess = true; // Allocate the buffer that will receive the line addresses // Note: The buffer should only be big enough to contain the information // for the needed line ULONG BufSize = LineNumber; DWORD64* pBuf = new DWORD64[ BufSize ]; if( pBuf == 0 ) return false; // Initialize the buffer to contains all zeroes // (it is required because the value of zero allows to distinguish // source lines that do not have an address in the executable) // memset( pBuf, 0, sizeof(DWORD64) * BufSize ); // Obtain the list of line offsets ULONG rv = ::SymGetFileLineOffsets64( GetCurrentProcess(), // Process handle of the current process ModName, // The name of the module (0 -> all modules are searched) SrcFileName, // The name of the source file whose lines we analyze pBuf, // The buffer that receives line addresses BufSize // Size of the buffer (max number of line offsets it can contain) ); if( rv == 0 ) { bSuccess = false; _tprintf( _T("Error: SymGetFileLineOffsets64() failed. Error code: %u \n"), ::GetLastError() ); } else if( rv < BufSize ) { // Wrong line number, probably bSuccess = false; } else { // Save the line address Address = pBuf[LineNumber-1]; } // Delete the buffer delete[] pBuf; // Complete return bSuccess; }