#include "HeadSpin.h" #include "pfMapFile.h" #include "pfMapFileEntry.h" #include "pfTextFile.h" #include "pfArray.h" #include #include #include #ifdef WIN32 #include extern "C" IMAGE_DOS_HEADER __ImageBase; #endif //----------------------------------------------------------------------------- namespace dev { class MapFile::MapFileImpl { public: long loadAddr; char name[256]; Array segments; Array entries; MapFileImpl( const char* filename ) : loadAddr(0), m_file( filename ), m_err( MapFile::ERROR_NONE ) { m_file.readString( name, sizeof(name) ); char buf[1024]; while ( m_file.readString(buf,sizeof(buf)) ) { if ( !strcmp("Preferred",buf) ) parseLoadAddress(); else if ( !strcmp("Start",buf) ) parseSegments(); else if ( !strcmp("Address",buf) ) parseEntries(); else m_file.skipLine(); } std::sort( segments.begin(), segments.end() ); std::sort( entries.begin(), entries.end() ); } ~MapFileImpl() { } ErrorType error() const { if ( m_err != MapFile::ERROR_NONE ) return m_err; switch ( m_file.error() ) { case TextFile::ERROR_OPEN: return MapFile::ERROR_OPEN; case TextFile::ERROR_READ: return MapFile::ERROR_READ; case TextFile::ERROR_PARSE: return MapFile::ERROR_PARSE; default: return MapFile::ERROR_NONE; } } int line() const { if ( m_err != MapFile::ERROR_NONE ) return m_errLine; return m_file.line(); } private: TextFile m_file; MapFile::ErrorType m_err; int m_errLine; /** * Returns true if the next line is empty. */ bool nextLineEmpty() { m_file.skipLine(); char ch; while ( m_file.peekChar(&ch) && isspace(ch) && ch != '\n' ) m_file.readChar( &ch ); if ( m_file.peekChar(&ch) && ch == '\n' ) return true; return false; } /** * Parses specified string. * Sets error if parsed string doesnt match. */ void parse( const char* str ) { char buf[256]; m_file.readString( buf, sizeof(buf) ); if ( strcmp(str,buf) ) { m_err = MapFile::ERROR_PARSE; m_errLine = m_file.line(); } } /** * Parses specified character. * Sets error if parsed character doesnt match. */ void parse( char ch ) { char ch2; if ( !m_file.readChar(&ch2) || ch2 != ch ) { m_err = MapFile::ERROR_PARSE; m_errLine = m_file.line(); } } /** * Example: * (Preferred) load address is 00400000 */ void parseLoadAddress() { parse( "load" ); parse( "address" ); parse( "is" ); loadAddr = m_file.readHex(); } /** * Example: * (Start) Length Name Class * 0001:00000000 00002c05H .text CODE */ void parseSegments() { parse( "Length" ); parse( "Name" ); parse( "Class" ); m_file.skipWhitespace(); while ( !error() ) { int seg = m_file.readHex(); parse( ':' ); int offs = m_file.readHex(); int len = m_file.readHex(); parse( 'H' ); char buf[256]; m_file.readString( buf, sizeof(buf) ); segments.add( MapFileEntry(seg,offs,len,buf,0) ); // break at empty line if ( nextLineEmpty() ) break; } } /** * Example: * (Address) Publics by Value Rva+Base Lib:Object * 0001:000001a0 ?stackTrace@@YAXXZ 004011a0 f main.obj */ void parseEntries() { parse( "Publics" ); parse( "by" ); parse( "Value" ); parse( "Rva+Base" ); parse( "Lib:Object" ); m_file.skipWhitespace(); while ( !error() ) { int seg = m_file.readHex(); parse( ':' ); int offs = m_file.readHex(); char buf[256]; m_file.readString( buf, sizeof(buf) ); char* entryname = buf; // chop entry name at @@ char* end = strstr( entryname, "@@" ); if ( end ) *end = 0; // skip preceding ?01.. while ( isdigit(*entryname) || *entryname == '?' || *entryname == '$' ) ++entryname; // conv @ -> . for ( char* str = entryname ; *str ; ++str ) if ( *str == '@' ) *str = '.'; // Added 9.5.02 mcn - Reverse the order of the symbols to be more natural if( strlen( entryname ) < 512 ) { static char newName[ 512 ]; char *search; newName[ 0 ] = 0; while( ( search = strrchr( entryname, '.' ) ) != 0 ) { *search = 0; if( newName[ 0 ] != 0 ) strcat( newName, "::" ); strcat( newName, search + 1 ); } if( newName[ 0 ] != 0 ) strcat( newName, "::" ); strcat( newName, entryname ); entryname = newName; } long rvabase = m_file.readHex(); entries.add( MapFileEntry(seg,offs,0,entryname,rvabase) ); // break at empty line if ( nextLineEmpty() ) break; } } }; //----------------------------------------------------------------------------- MapFile::MapFile( const char* filename ) { m_this = TRACKED_NEW MapFileImpl( filename ); } MapFile::~MapFile() { delete m_this; } long MapFile::loadAddress() const { return m_this->loadAddr; } const MapFileEntry& MapFile::getSegment( int i ) const { return m_this->segments[i]; } const MapFileEntry& MapFile::getEntry( int i ) const { return m_this->entries[i]; } int MapFile::segments() const { return m_this->segments.size(); } int MapFile::entries() const { return m_this->entries.size(); } MapFile::ErrorType MapFile::error() const { return m_this->error(); } int MapFile::line() const { return m_this->line(); } int MapFile::findEntry( long addr ) const { #ifdef WIN32 // Cope with Windows ASLR. Note that these operations are not commutative. addr -= (long)&__ImageBase; addr += loadAddress(); #endif // The code before ASLR-proofing tried to compute things by segment. // It didn't work for whatever reason, so here's something simpler // by address. Just be sure to toss anything larger than the // highest address we know about. if (addr == 0 || addr > getEntry(entries() - 1).rvabase()) return -1; for (int i = entries() - 1; i >= 0; --i) { if (getEntry(i).rvabase() <= addr) return i; } return -1; } void MapFile::getModuleMapFilename( char* buffer, int bufferSize ) { int len = 0; buffer[len] = 0; #ifdef WIN32 // get name of the exe/dll len = GetModuleFileName( GetModuleHandle(0), buffer, bufferSize-1 ); buffer[len] = 0; #endif // remove .exe or .dll extension if ( len > 3 && (!strcmp(buffer+len-4,".exe") || !strcmp(buffer+len-4,".EXE") || !strcmp(buffer+len-4,".DLL") || !strcmp(buffer+len-4,".dll")) ) { buffer[len-4] = 0; } // append .map extension if ( (int)strlen(buffer)+4 < bufferSize ) { strcat( buffer, ".map" ); } } } // dev /* * Copyright (c) 2001 Jani Kajala * * Permission to use, copy, modify, distribute and sell this * software and its documentation for any purpose is hereby * granted without fee, provided that the above copyright notice * appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation. * Jani Kajala makes no representations about the suitability * of this software for any purpose. It is provided "as is" * without express or implied warranty. */