diff --git a/pykd/dbgclient.cpp b/pykd/dbgclient.cpp index 20c6294..ba96444 100644 --- a/pykd/dbgclient.cpp +++ b/pykd/dbgclient.cpp @@ -12,6 +12,15 @@ DebugClientPtr g_dbgClient( DebugClient::createDbgClient() ); /////////////////////////////////////////////////////////////////////////////////// +DebugClient::DebugClient( IDebugClient4 *client ) + : DbgObject( client ) + , m_symSymbols( new SyntheticSymbols(*m_symbols, *this) ) + , m_internalDbgEventHandler(client, m_symSymbols) +{ +} + +/////////////////////////////////////////////////////////////////////////////////// + DebugClientPtr DebugClient::createDbgClient() { HRESULT hres; @@ -315,4 +324,4 @@ ULONG ptrSize() /////////////////////////////////////////////////////////////////////////////////// -}; // end of namespace pykd \ No newline at end of file +}; // end of namespace pykd diff --git a/pykd/dbgclient.h b/pykd/dbgclient.h index a2aeb9c..cb6db3a 100644 --- a/pykd/dbgclient.h +++ b/pykd/dbgclient.h @@ -14,6 +14,8 @@ #include "pyaux.h" #include "disasm.h" #include "cpureg.h" +#include "inteventhandler.h" +#include "synsymbol.h" ///////////////////////////////////////////////////////////////////////////////// @@ -97,11 +99,11 @@ public: void loadDump( const std::wstring &fileName ); Module loadModuleByName( const std::string &moduleName ) { - return Module( m_client, moduleName ); + return Module( m_client, m_symSymbols, moduleName ); } Module loadModuleByOffset( ULONG64 offset ) { - return Module( m_client, offset ); + return Module( m_client, m_symSymbols, offset ); } DbgExtensionPtr loadExtension( const std::wstring &extPath ) { @@ -191,6 +193,35 @@ public: return m_pyThreadState; } + void addSyntheticSymbol( + ULONG64 addr, + ULONG size, + const std::string &symName + ) + { + return m_symSymbols->add(addr, size, symName); + } + + void delAllSyntheticSymbols() + { + return m_symSymbols->clear(); + } + + ULONG delSyntheticSymbol( + ULONG64 addr + ) + { + return m_symSymbols->remove(addr); + } + + ULONG delSyntheticSymbolsMask( + const std::string &moduleName, + const std::string &symName + ) + { + return m_symSymbols->removeByMask(moduleName, symName); + } + private: template @@ -200,7 +231,10 @@ private: //python::list //loadArray( ULONG64 offset, ULONG count, bool phyAddr ); - DebugClient( IDebugClient4 *client ) : DbgObject( client ) {} + SynSymbolsPtr m_symSymbols; // DebugClient is creator + InternalDbgEventHandler m_internalDbgEventHandler; + + DebugClient( IDebugClient4 *client ); PyThreadStateSaver m_pyThreadState; }; @@ -235,6 +269,38 @@ void setExecutionStatus( ULONG status ); void waitForEvent(); +///////////////////////////////////////////////////////////////////////////////// +// Synthetic symbols global finctions: + +inline void addSyntheticSymbol( + ULONG64 addr, + ULONG size, + const std::string &symName +) +{ + return g_dbgClient->addSyntheticSymbol(addr, size, symName); +} + +inline void delAllSyntheticSymbols() +{ + return g_dbgClient->delAllSyntheticSymbols(); +} + +inline ULONG delSyntheticSymbol( + ULONG64 addr +) +{ + return g_dbgClient->delSyntheticSymbol(addr); +} + +inline ULONG delSyntheticSymbolsMask( + const std::string &moduleName, + const std::string &symName +) +{ + return g_dbgClient->delSyntheticSymbolsMask(moduleName, symName); +} + ///////////////////////////////////////////////////////////////////////////////// template @@ -331,4 +397,4 @@ void changeDebuggerStatus() // //extern dbgClient g_dbgClient; // -///////////////////////////////////////////////////////////////////////////////// \ No newline at end of file +///////////////////////////////////////////////////////////////////////////////// diff --git a/pykd/dbgext.cpp b/pykd/dbgext.cpp index 2f97a68..9c8851a 100644 --- a/pykd/dbgext.cpp +++ b/pykd/dbgext.cpp @@ -234,7 +234,15 @@ BOOST_PYTHON_MODULE( pykd ) .def( "trace", &pykd::DebugClient::changeDebuggerStatus, "Change debugger status to DEBUG_STATUS_STEP_INTO" ) .def( "waitForEvent", &pykd::DebugClient::waitForEvent, - "Wait for events that breaks into the debugger" ); + "Wait for events that breaks into the debugger" ) + .def( "addSynSymbol", &pykd::DebugClient::addSyntheticSymbol, + "Add new synthetic symbol for virtual address" ) + .def( "delAllSynSymbols", &pykd::DebugClient::delAllSyntheticSymbols, + "Delete all synthetic symbol for all modules") + .def( "delSynSymbol", &pykd::DebugClient::delSyntheticSymbol, + "Delete synthetic symbols by virtual address" ) + .def( "delSynSymbolsMask", &pykd::DebugClient::delSyntheticSymbolsMask, + "Delete synthetic symbols by mask of module and symbol name"); python::def( "addr64", &addr64, "Extend address to 64 bits formats" ); @@ -318,6 +326,16 @@ BOOST_PYTHON_MODULE( pykd ) "Read an signed mashine's word wide integer from the target memory" ); python::def( "ptrPtr", &ptrPtr, "Read an pointer value from the target memory" ); + + boost::python::def( "addSynSymbol", &addSyntheticSymbol, + "Add new synthetic symbol for virtual address" ); + boost::python::def( "delAllSynSymbols", &delAllSyntheticSymbols, + "Delete all synthetic symbol for all modules"); + boost::python::def( "delSynSymbol", &delSyntheticSymbol, + "Delete synthetic symbols by virtual address" ); + boost::python::def( "delSynSymbolsMask", &delSyntheticSymbolsMask, + "Delete synthetic symbols by mask of module and symbol name"); + python::def( "loadExt", &pykd::loadExtension, "Load a debuger extension" ); python::def( "loadModule", &loadModuleByName, @@ -610,6 +628,8 @@ BOOST_PYTHON_MODULE( pykd ) // wrapper for standart python exceptions python::register_exception_translator( &PyException::exceptionTranslate ); +#define _DECL_BASE_EXCEPT_STR .def( "__str__", &pykd::DbgException::print ) + // base exception python::class_ dbgExceptionClass( "BaseException", "Pykd base exception class", @@ -618,17 +638,17 @@ BOOST_PYTHON_MODULE( pykd ) .def( python::init( python::args("desc"), "constructor" ) ) .def( "desc", &pykd::DbgException::getDesc, "Get exception description" ) - .def( "__str__", &pykd::DbgException::print); + _DECL_BASE_EXCEPT_STR; pykd::DbgException::setTypeObject( dbgExceptionClass.ptr() ); python::register_exception_translator( &pykd::DbgException::exceptionTranslate ); // DIA exceptions python::class_ > diaException( - "DiaException", "Debug interface access exception", - python::no_init ); + "DiaException", "Debug interface access exception", python::no_init); diaException - .def( "hres", &pyDia::Exception::getRes ); + .def( "hres", &pyDia::Exception::getRes ) + _DECL_BASE_EXCEPT_STR; pyDia::Exception::setTypeObject( diaException.ptr() ); python::register_exception_translator( &pyDia::Exception::exceptionTranslate ); diff --git a/pykd/dbgobj.h b/pykd/dbgobj.h index cdeebbc..4b809c0 100644 --- a/pykd/dbgobj.h +++ b/pykd/dbgobj.h @@ -2,6 +2,7 @@ #include #include "dbgexcept.h" +#include "synsymbol.h" namespace pykd { @@ -11,7 +12,8 @@ class DbgObject { protected: - DbgObject( IDebugClient4 *client ) { + DbgObject( IDebugClient4 *client ) + { m_client = client; @@ -38,7 +40,7 @@ protected: hres = client->QueryInterface( __uuidof(IDebugRegisters2), (void**)&m_registers ); if ( FAILED( hres ) ) - throw DbgException("QueryInterface IDebugDataSpaces failed"); + throw DbgException("QueryInterface IDebugDataSpaces failed"); } virtual ~DbgObject() {}; @@ -50,7 +52,6 @@ protected: CComPtr m_advanced; CComPtr m_dataSpaces; CComPtr m_registers; - }; diff --git a/pykd/inteventhandler.cpp b/pykd/inteventhandler.cpp new file mode 100644 index 0000000..90fd97d --- /dev/null +++ b/pykd/inteventhandler.cpp @@ -0,0 +1,76 @@ + +#include "stdafx.h" + +#include "inteventhandler.h" +#include "dbgexcept.h" + +namespace pykd { + +//////////////////////////////////////////////////////////////////////////////// + +InternalDbgEventHandler::InternalDbgEventHandler( + IDebugClient4 *client, + SynSymbolsPtr synSymbols +) : m_synSymbols(synSymbols) +{ + HRESULT hres = client->CreateClient(&m_client); + if (FAILED(hres)) + throw DbgException("Call IDebugClient::CreateClient failed"); + + m_client->SetEventCallbacks(this); +} + +//////////////////////////////////////////////////////////////////////////////// + +InternalDbgEventHandler::~InternalDbgEventHandler() +{ + m_client->Release(); +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT InternalDbgEventHandler::GetInterestMask( + __out PULONG Mask +) +{ + *Mask = + DEBUG_EVENT_CHANGE_SYMBOL_STATE; + + return S_OK; +} + +/////////////////////////////////////////////////////////////////////////////////// + +HRESULT InternalDbgEventHandler::ChangeSymbolState( + __in ULONG Flags, + __in ULONG64 Argument +) +{ + HRESULT hres = S_OK; + + if (DEBUG_CSS_LOADS & Flags) + hres = symLoaded(Argument); + + return hres; +} + +/////////////////////////////////////////////////////////////////////////////////// + +HRESULT InternalDbgEventHandler::symLoaded( + __in ULONG64 ModuleAddress +) +{ + if (!ModuleAddress) + { + // f.e. is case ".reload /f image.exe", if for image.exe no symbols + m_synSymbols->restoreAll(); + return S_OK; + } + + m_synSymbols->restoreForModule(ModuleAddress); + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +}; // namespace pykd diff --git a/pykd/inteventhandler.h b/pykd/inteventhandler.h new file mode 100644 index 0000000..366b5c0 --- /dev/null +++ b/pykd/inteventhandler.h @@ -0,0 +1,45 @@ +// +// Internal debug event handler +// + +#pragma once + +#include +#include "synsymbol.h" + +namespace pykd { + +class InternalDbgEventHandler : public DebugBaseEventCallbacks { +public: + + InternalDbgEventHandler( + IDebugClient4 *client, + SynSymbolsPtr synSymbols + ); + ~InternalDbgEventHandler(); + +protected: + + // IUnknown impls + STDMETHOD_(ULONG, AddRef)() { return 1; } + STDMETHOD_(ULONG, Release)() { return 1; } + + // IDebugEventCallbacks impls + STDMETHOD(GetInterestMask)( + __out PULONG Mask + ); + + STDMETHOD(ChangeSymbolState)( + __in ULONG Flags, + __in ULONG64 Argument + ); + +private: + + HRESULT symLoaded(__in ULONG64 ModuleAddress); + + IDebugClient *m_client; + SynSymbolsPtr m_synSymbols; +}; + +}; // namespace pykd diff --git a/pykd/module.cpp b/pykd/module.cpp index 52733c9..3d7e0af 100644 --- a/pykd/module.cpp +++ b/pykd/module.cpp @@ -20,7 +20,9 @@ Module loadModuleByOffset( ULONG64 offset ) { /////////////////////////////////////////////////////////////////////////////////// -Module::Module( IDebugClient4 *client, const std::string& moduleName ) : DbgObject( client ) +Module::Module( IDebugClient4 *client, SynSymbolsPtr synSymbols, const std::string& moduleName ) + : DbgObject( client ) + , m_synSymbols(synSymbols) { HRESULT hres; @@ -36,6 +38,8 @@ Module::Module( IDebugClient4 *client, const std::string& moduleName ) : DbgObje throw DbgException( "IDebugSymbol::GetModuleParameters failed" ); m_size = moduleParam.Size; + m_timeDataStamp = moduleParam.TimeDateStamp; + m_checkSumm = moduleParam.Checksum; char imageName[0x100]; @@ -55,7 +59,9 @@ Module::Module( IDebugClient4 *client, const std::string& moduleName ) : DbgObje /////////////////////////////////////////////////////////////////////////////////// -Module::Module( IDebugClient4 *client, ULONG64 offset ) : DbgObject( client ) +Module::Module( IDebugClient4 *client, SynSymbolsPtr synSymbols, ULONG64 offset ) + : DbgObject( client ) + , m_synSymbols(synSymbols) { HRESULT hres; @@ -102,6 +108,8 @@ Module::Module( IDebugClient4 *client, ULONG64 offset ) : DbgObject( client ) throw DbgException( "IDebugSymbol::GetModuleParameters failed" ); m_size = moduleParam.Size; + m_timeDataStamp = moduleParam.TimeDateStamp; + m_checkSumm = moduleParam.Checksum; } /////////////////////////////////////////////////////////////////////////////////// @@ -198,5 +206,18 @@ Module::getTypedVarByAddr( ULONG64 addr ) /////////////////////////////////////////////////////////////////////////////////// +ULONG Module::getRvaByName(const std::string &symName) +{ + try { + pyDia::SymbolPtr sym = getDia()->getChildByName( symName ); + return sym->getRva(); + } + catch (const pyDia::Exception &) { + } + return (ULONG)m_synSymbols->getRvaByName(m_timeDataStamp, m_checkSumm, symName); +} + +/////////////////////////////////////////////////////////////////////////////////// + }; // end of namespace pykd diff --git a/pykd/module.h b/pykd/module.h index 73e3648..f6737f3 100644 --- a/pykd/module.h +++ b/pykd/module.h @@ -6,6 +6,7 @@ #include "diawrapper.h" #include "typeinfo.h" #include "typedvar.h" +#include "synsymbol.h" namespace pykd { @@ -15,9 +16,9 @@ class Module : private DbgObject { public: - Module( IDebugClient4 *client, const std::string& moduleName ); + Module( IDebugClient4 *client, SynSymbolsPtr synSymbols, const std::string& moduleName ); - Module( IDebugClient4 *client, ULONG64 offset ); + Module( IDebugClient4 *client, SynSymbolsPtr synSymbols, ULONG64 offset ); std::string getName() { return m_name; @@ -47,16 +48,12 @@ public: ULONG64 getSymbol( const std::string &symbolname ) { - pyDia::SymbolPtr sym = getDia()->getChildByName( symbolname ); - - return m_base + sym->getRva(); + return m_base + getRvaByName(symbolname); } ULONG getSymbolRva( const std::string &symbolname ) { - pyDia::SymbolPtr sym = getDia()->getChildByName( symbolname ); - - return sym->getRva(); + return getRvaByName(symbolname); } TypeInfoPtr getTypeByName( const std::string &typeName ) { @@ -73,6 +70,8 @@ public: private: + ULONG getRvaByName(const std::string &symName); + pyDia::GlobalScopePtr& getDia() { if (!m_dia) m_dia = pyDia::GlobalScope::loadPdb( getPdbName() ); @@ -84,7 +83,12 @@ private: std::string m_imageName; ULONG64 m_base; ULONG m_size; + pyDia::GlobalScopePtr m_dia; + + ULONG m_timeDataStamp; + ULONG m_checkSumm; + SynSymbolsPtr m_synSymbols; }; /////////////////////////////////////////////////////////////////////////////////// diff --git a/pykd/pykd_2008.vcproj b/pykd/pykd_2008.vcproj index 3bf7832..157b1c9 100644 --- a/pykd/pykd_2008.vcproj +++ b/pykd/pykd_2008.vcproj @@ -1,7 +1,7 @@ + + @@ -445,6 +449,14 @@ /> + + + + @@ -511,6 +523,10 @@ RelativePath=".\intbase.h" > + + @@ -527,6 +543,14 @@ RelativePath=".\stdafx.h" > + + + + diff --git a/pykd/synsymbol.cpp b/pykd/synsymbol.cpp new file mode 100644 index 0000000..75c40db --- /dev/null +++ b/pykd/synsymbol.cpp @@ -0,0 +1,248 @@ + +#include "stdafx.h" + +#include + +#include "dbgmem.h" +#include "dbgexcept.h" +#include "dbgclient.h" +#include "synsymhelpers.h" + +namespace pykd { + + +//////////////////////////////////////////////////////////////////////////////// + +SyntheticSymbols::SyntheticSymbols( + IDebugSymbols3 &symbols, + DebugClient &dbgClient +) : m_symbols(symbols) + , m_dbgClient(dbgClient) +{ + m_allSymbolsLock.reset(new boost::recursive_mutex); +} + +//////////////////////////////////////////////////////////////////////////////// + +void SyntheticSymbols::add( + ULONG64 addr, + ULONG size, + const std::string &symName +) +{ + addr = m_dbgClient.addr64(addr); + + // add new synthetic symbol to debug engine + DEBUG_MODULE_AND_ID dbgModuleAndId = { 0 }; + HRESULT hres = + m_symbols.AddSyntheticSymbol( + addr, + size, + symName.c_str(), + DEBUG_ADDSYNTHSYM_DEFAULT, + &dbgModuleAndId); + if ( FAILED( hres ) ) + throw DbgException(buildExceptDesc("IDebugSymbols3::AddSyntheticSymbol", hres)); + + // add/update symbol for target module (in internal map) + SymbolsScopedLock lock(*m_allSymbolsLock); + + ModSymbols &modSymbols = + m_allSymbols[SynSymHelper(m_symbols).modByBase(dbgModuleAndId.ModuleBase)]; + SymbolData &symData = modSymbols[addr - dbgModuleAndId.ModuleBase]; + + symData.m_name = symName; + symData.m_size = size; +} + +//////////////////////////////////////////////////////////////////////////////// + +ULONG SyntheticSymbols::remove(ULONG64 addr) +{ + addr = m_dbgClient.addr64(addr); + + SynSymHelper synSymHelper(m_symbols); + + // find target module + ULONG64 moduleBase; + SymbolsScopedLock lock(*m_allSymbolsLock); + AllSymbols::iterator itModSymbols = + m_allSymbols.find(synSymHelper.modByOffset(addr, moduleBase)); + if (itModSymbols == m_allSymbols.end()) + return 0; + + // remove symbol from internal map + itModSymbols->second.erase(addr - moduleBase); + + // remove symbol from debug engine + return synSymHelper.removeSyntheticSymbols(addr); +} + +//////////////////////////////////////////////////////////////////////////////// + +ULONG SyntheticSymbols::removeByMask( + const std::string &moduleName, + const std::string &symName +) +{ + SynSymHelper synSymHelper(m_symbols); + + std::vector dbgSymbols; + HRESULT hres = + synSymHelper.getSymbolsByMaks( + moduleName + "!" + symName, + dbgSymbols); + if (FAILED(hres)) + throw DbgException(buildExceptDesc("IDebugSymbols3::GetSymbolEntriesByName", hres)); + + if (dbgSymbols.empty()) + return 0; + + ULONG removed = 0; + + SymbolsScopedLock lock(*m_allSymbolsLock); + for (ULONG i =0; i < dbgSymbols.size(); ++i) + { + DEBUG_SYMBOL_ENTRY dbgSymbolInfo; + HRESULT hres = + m_symbols.GetSymbolEntryInformation( + &dbgSymbols[i], + &dbgSymbolInfo); + if (FAILED(hres)) + continue; + + // try find modules in internal map + AllSymbols::iterator itModSymbols = + m_allSymbols.find(synSymHelper.modByBase(dbgSymbolInfo.ModuleBase)); + if (itModSymbols == m_allSymbols.end()) + continue; + + // try find symbol + ModSymbols::iterator itSymbol = + itModSymbols->second.find(dbgSymbolInfo.Offset - dbgSymbolInfo.ModuleBase); + if (itSymbol == itModSymbols->second.end()) + continue; + + // remove from debug engine + if (synSymHelper.removeSyntheticSymbols(dbgSymbols[i])) + ++removed; + + // remove from internal map + itModSymbols->second.erase(itSymbol); + + if (itModSymbols->second.empty()) + m_allSymbols.erase(itModSymbols); + } + + return removed; +} + +//////////////////////////////////////////////////////////////////////////////// + +void SyntheticSymbols::clear() +{ + SymbolsScopedLock lock(*m_allSymbolsLock); + + // clean symbols from debug engine + forEachLoadedModule( SynSymRemoveAll(m_symbols) ); + + // clean internal map + m_allSymbols.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// + +void SyntheticSymbols::restoreForModule(ULONG64 moduleBase) +{ + SymbolsScopedLock lock(*m_allSymbolsLock); + + SynSymRestore restorer(m_symbols); + AllSymbols::iterator itFoundModule = + m_allSymbols.find( restorer.modByBase(moduleBase) ); + if (itFoundModule == m_allSymbols.end()) + return; + + forEachFromModule(moduleBase, itFoundModule->second, restorer); +} + +//////////////////////////////////////////////////////////////////////////////// + +void SyntheticSymbols::restoreAll() +{ + SymbolsScopedLock lock(*m_allSymbolsLock); + + // clean symbols from debug engine + forEachLoadedModule( SynSymRestore(m_symbols) ); +} + +//////////////////////////////////////////////////////////////////////////////// + +ULONG64 SyntheticSymbols::getRvaByName( + ULONG timeDataStamp, + ULONG checkSumm, + const std::string &symName +) +{ + SymbolsScopedLock lock(*m_allSymbolsLock); + AllSymbols::iterator itFoundModule = + m_allSymbols.find( ModuleId(timeDataStamp, checkSumm) ); + if (itFoundModule == m_allSymbols.end()) + throw DbgException("Synthetic symbol is not found"); + + + ModSymbols::iterator itSynSymbol = itFoundModule->second.begin(); + while (itSynSymbol != itFoundModule->second.end()) + { + if (boost::iequals(symName, itSynSymbol->second.m_name)) + return itSynSymbol->first; + + ++itSynSymbol; + } + throw DbgException("Synthetic symbol is not found"); +} + +//////////////////////////////////////////////////////////////////////////////// + +void SyntheticSymbols::forEachFromModule( + ULONG64 moduleBase, + const ModSymbols &modSymbols, + ISynSymForEach &forEach +) +{ + ModSymbols::const_iterator itSynSymbol = modSymbols.begin(); + while (itSynSymbol != modSymbols.end()) + { + forEach.symbol(moduleBase, itSynSymbol->first, itSynSymbol->second); + ++itSynSymbol; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +void SyntheticSymbols::forEachLoadedModule(ISynSymForEach &forEach) +{ + // for loaded and unloaded modules... + std::vector dbgModules; + if (!SUCCEEDED(forEach.getAllModules(dbgModules)) || dbgModules.empty()) + { + SymbolsScopedLock lock(*m_allSymbolsLock); + m_allSymbols.clear(); + return; + } + + std::vector::iterator itModule = dbgModules.begin(); + while (itModule != dbgModules.end()) + { + // ...do it! + AllSymbols::const_iterator itFoundModule = + m_allSymbols.find( ModuleId(*itModule) ); + if (itFoundModule != m_allSymbols.end()) + forEachFromModule(itModule->Base, itFoundModule->second, forEach); + + ++itModule; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +}; // namespace pykd diff --git a/pykd/synsymbol.h b/pykd/synsymbol.h new file mode 100644 index 0000000..a461631 --- /dev/null +++ b/pykd/synsymbol.h @@ -0,0 +1,144 @@ +// +// Synthetic symbol +// + +#pragma once + +#include "dbgexcept.h" +#include +#include +#include + +namespace pykd { + +//////////////////////////////////////////////////////////////////////////////// + +interface ISynSymForEach; +class DebugClient; + +//////////////////////////////////////////////////////////////////////////////// + +class SyntheticSymbols { +public: + SyntheticSymbols( + IDebugSymbols3 &symbols, + DebugClient &dbgClient + ); + + //////////////////////////////////////////////////////////////////////////////// + // Python API: + + // add new synthetic symbol + void add( + ULONG64 addr, + ULONG size, + const std::string &symName + ); + + // remove synthetic symbols by address + ULONG remove(ULONG64 addr); + + // remove synthetic symbols by module and symbol names + ULONG removeByMask( + const std::string &moduleName, + const std::string &symName + ); + + // remove all synthetic symbols from all modules + void clear(); + + //////////////////////////////////////////////////////////////////////////////// + // Debug event callback manager API: + + void restoreForModule(ULONG64 moduleBase); + void restoreAll(); + + //////////////////////////////////////////////////////////////////////////////// + // Module API: + + ULONG64 getRvaByName( + ULONG timeDataStamp, + ULONG checkSumm, + const std::string &symName + ); + +public: + + // unique module ID + struct ModuleId { + ULONG m_timeDataStamp; + ULONG m_checkSumm; + + ModuleId(ULONG timeDataStamp, ULONG checkSumm) + : m_timeDataStamp(timeDataStamp) + , m_checkSumm(checkSumm) + { + } + + ModuleId(const DEBUG_MODULE_PARAMETERS &dbgModuleParameters) + : m_timeDataStamp(dbgModuleParameters.TimeDateStamp) + , m_checkSumm(dbgModuleParameters.Checksum) + { + } + + bool operator == (const ModuleId &rhs) const + { + return + m_timeDataStamp == rhs.m_timeDataStamp && + m_checkSumm == rhs.m_checkSumm; + } + bool operator < (const ModuleId &rhs) const + { + return + m_timeDataStamp < rhs.m_timeDataStamp && + m_checkSumm < rhs.m_checkSumm; + } + }; + + // symbol data + struct SymbolData { + std::string m_name; + ULONG m_size; + }; + + // synthetic symbols of one module: offset -> name+size + typedef ULONG64 SYM_OFFSET; + typedef std::map ModSymbols; + + // synthetic symbols of all modules + typedef std::map AllSymbols; + +private: + + // lock of static map + typedef boost::shared_ptr SymbolsLock; + typedef boost::recursive_mutex::scoped_lock SymbolsScopedLock; + +private: + + // execute for all symbols from modules + void forEachFromModule( + ULONG64 moduleBase, + const ModSymbols &modSymbols, + ISynSymForEach &forEach + ); + + // execute for all symbols all loaded modules + void forEachLoadedModule(ISynSymForEach &forEach); + +private: + + SymbolsLock m_allSymbolsLock; + AllSymbols m_allSymbols; + + DebugClient &m_dbgClient; + IDebugSymbols3 &m_symbols; +}; + +//////////////////////////////////////////////////////////////////////////////// + +typedef boost::shared_ptr SynSymbolsPtr; + +//////////////////////////////////////////////////////////////////////////////// + +}; // namespace pykd diff --git a/pykd/synsymhelpers.cpp b/pykd/synsymhelpers.cpp new file mode 100644 index 0000000..8f1b067 --- /dev/null +++ b/pykd/synsymhelpers.cpp @@ -0,0 +1,152 @@ + +#include + +#include "dbgexcept.h" +#include "synsymhelpers.h" + +namespace pykd { + +//////////////////////////////////////////////////////////////////////////////// + +bool SynSymHelper::removeSyntheticSymbols( + const DEBUG_MODULE_AND_ID &dbgSymbols +) +{ + return SUCCEEDED( + m_symbols.RemoveSyntheticSymbol( + const_cast(&dbgSymbols) + ) + ); +} + +//////////////////////////////////////////////////////////////////////////////// + +ULONG SynSymHelper::removeSyntheticSymbols( + const std::vector &arrSymbols +) +{ + ULONG countOfRemoved = 0; + for (ULONG i = 0; i < arrSymbols.size(); ++i) + { + if (removeSyntheticSymbols(arrSymbols[i])) + ++countOfRemoved; + } + return countOfRemoved; +} + +//////////////////////////////////////////////////////////////////////////////// + +ULONG SynSymHelper::removeSyntheticSymbols(ULONG64 addr) +{ + ULONG entries = 0; + m_symbols.GetSymbolEntriesByOffset(addr, 0, NULL, NULL, 0, &entries); + if (!entries) + return 0; + + std::vector arrSymbols(entries); + HRESULT hres = m_symbols.GetSymbolEntriesByOffset( + addr, + 0, + &arrSymbols[0], + NULL, + (ULONG)arrSymbols.size(), + NULL); + if (SUCCEEDED(hres)) + return removeSyntheticSymbols(arrSymbols); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// + +SyntheticSymbols::ModuleId SynSymHelper::modByBase(ULONG64 moduleBase) +{ + DEBUG_MODULE_PARAMETERS dbgModuleParameters; + HRESULT hres = + m_symbols.GetModuleParameters( + 1, + &moduleBase, + 0, + &dbgModuleParameters); + if ( FAILED( hres ) ) + throw DbgException(buildExceptDesc("IDebugSymbols3::GetModuleParameters", hres)); + + return SyntheticSymbols::ModuleId(dbgModuleParameters); +} + +//////////////////////////////////////////////////////////////////////////////// + +SyntheticSymbols::ModuleId SynSymHelper::modByOffset( + ULONG64 moduleOffset, + ULONG64 &moduleBase +) +{ + HRESULT hres = + m_symbols.GetModuleByOffset(moduleOffset, 0, NULL, &moduleBase); + if ( FAILED( hres ) ) + throw DbgException(buildExceptDesc("IDebugSymbols3::GetModuleByOffset", hres)); + + return modByBase(moduleBase); +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT SynSymHelper::getAllModules( + std::vector &dbgModules +) +{ + ULONG nLoaded; + ULONG nUnloaded; + HRESULT hres = m_symbols.GetNumberModules(&nLoaded, &nUnloaded); + if (FAILED(hres)) + return hres; + + if (!nLoaded && !nUnloaded) + { + dbgModules.clear(); + return S_OK; + } + + dbgModules.resize(nLoaded + nUnloaded); + return + m_symbols.GetModuleParameters( + nLoaded + nUnloaded, + NULL, + 0, + &dbgModules[0]); +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT SynSymHelper::getSymbolsByMaks( + const std::string &symMask, + std::vector &dbgSymbols +) +{ + ULONG entries = 0; + m_symbols.GetSymbolEntriesByName( + symMask.c_str(), + 0, + NULL, + 0, + &entries); + if (!entries) + { + dbgSymbols.clear(); + return S_OK; + } + + dbgSymbols.resize(entries); + + return + m_symbols.GetSymbolEntriesByName( + symMask.c_str(), + 0, + &dbgSymbols[0], + entries, + NULL); +} + +//////////////////////////////////////////////////////////////////////////////// + +}; // namespace pykd diff --git a/pykd/synsymhelpers.h b/pykd/synsymhelpers.h new file mode 100644 index 0000000..f3e4f48 --- /dev/null +++ b/pykd/synsymhelpers.h @@ -0,0 +1,107 @@ +// +// Synthetic symbol helpers +// + +#pragma once + +#include + +#include "synsymbol.h" + +namespace pykd { + +//////////////////////////////////////////////////////////////////////////////// + +class SynSymHelper { +public: + SynSymHelper(IDebugSymbols3 &symbols) : m_symbols(symbols) {} + + // remove array of synthetic symbols from debug engine + bool removeSyntheticSymbols( + const DEBUG_MODULE_AND_ID &dbgSymbols + ); + ULONG removeSyntheticSymbols( + const std::vector &arrSymbols + ); + ULONG removeSyntheticSymbols(ULONG64 addr); + + // buid ModuleId by base of image + SyntheticSymbols::ModuleId modByBase(ULONG64 moduleBase); + + // buid ModuleId by offset in image + SyntheticSymbols::ModuleId modByOffset( + ULONG64 moduleOffset, + ULONG64 &moduleBase + ); + + // get array of all modules + HRESULT getAllModules(std::vector &dbgModules); + + // get symbols by mask + HRESULT getSymbolsByMaks( + const std::string &symMask, + std::vector &dbgSymbols + ); + +protected: + IDebugSymbols3 &m_symbols; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Enumerator all synthetic symbols for loaded modules +//////////////////////////////////////////////////////////////////////////////// +interface ISynSymForEach : public SynSymHelper { + ISynSymForEach(IDebugSymbols3 &symbols) : SynSymHelper(symbols) {} + + virtual void symbol( + const ULONG64 moduleBase, + SyntheticSymbols::SYM_OFFSET offset, + const SyntheticSymbols::SymbolData &symData + ) = 0; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// Remove symbols from loaded modules +//////////////////////////////////////////////////////////////////////////////// +class SynSymRemoveAll : public ISynSymForEach +{ +public: + SynSymRemoveAll(IDebugSymbols3 &symbols) : ISynSymForEach(symbols) {} + + virtual void symbol( + const ULONG64 moduleBase, + SyntheticSymbols::SYM_OFFSET offset, + const SyntheticSymbols::SymbolData &/*symData*/ + ) override + { + removeSyntheticSymbols(moduleBase + offset); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Restore symbols for loaded modules +//////////////////////////////////////////////////////////////////////////////// +class SynSymRestore : public ISynSymForEach +{ +public: + SynSymRestore(IDebugSymbols3 &symbols) : ISynSymForEach(symbols) {} + + virtual void symbol( + const ULONG64 moduleBase, + SyntheticSymbols::SYM_OFFSET offset, + const SyntheticSymbols::SymbolData &symData + ) override + { + m_symbols.AddSyntheticSymbol( + moduleBase + offset, + symData.m_size, + symData.m_name.c_str(), + DEBUG_ADDSYNTHSYM_DEFAULT, + NULL); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +}; // namespace pykd diff --git a/test/scripts/pykdtest.py b/test/scripts/pykdtest.py index 61793ed..8220b5d 100644 --- a/test/scripts/pykdtest.py +++ b/test/scripts/pykdtest.py @@ -22,6 +22,7 @@ import eventtest import typedvar import memtest import intbase +import synsymtest def getTestSuite( singleName = "" ): if singleName == "": @@ -36,6 +37,7 @@ def getTestSuite( singleName = "" ): unittest.TestLoader().loadTestsFromTestCase( eventtest.EventTest ), unittest.TestLoader().loadTestsFromTestCase( memtest.MemoryTest ), unittest.TestLoader().loadTestsFromTestCase( intbase.IntBaseTest ), + unittest.TestLoader().loadTestsFromTestCase( synsymtest.SynSymTest ) ] ) else: return unittest.TestSuite( unittest.TestLoader().loadTestsFromName( singleName ) ) @@ -58,5 +60,5 @@ if __name__ == "__main__": #suite = getTestSuite( "typedvar.TypedVarTest.testStruct" ) unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run( suite ) - - #a = raw_input("\npress return\n") \ No newline at end of file + + a = raw_input("\npress return\n") \ No newline at end of file diff --git a/test/scripts/synsymtest.py b/test/scripts/synsymtest.py new file mode 100644 index 0000000..ab768cd --- /dev/null +++ b/test/scripts/synsymtest.py @@ -0,0 +1,68 @@ +"""Synthetic symbols tests""" + +import unittest +import target +import pykd + +class SynSymTest(unittest.TestCase): + """Unit tests of synthetic symbols""" + + def testAdd(self): + """Add new synthetic symbol""" + pykd.addSynSymbol( + target.module.offset("FuncWithName0")-1, 1, "synSym1") + self.assertEqual( + target.module.offset("FuncWithName0")-1, + target.module.offset("synSym1")) + + def testDel(self): + """Remove synthetic symbol""" + pykd.addSynSymbol( + target.module.offset("FuncWithName0")-2, 1, "synSym2") + self.assertEqual( + pykd.delSynSymbol( target.module.offset("synSym2") ), 1 ) + + exceptionOccurred = True + try: + target.module.rva("synSym2") + exceptionOccurred = False + except pykd.BaseException: + pass + self.assertTrue(exceptionOccurred) + + def testDelAll(self): + """Remove all synthetic symbols""" + pykd.addSynSymbol( + target.module.offset("FuncWithName0")-3, 1, "synSym3") + pykd.delAllSynSymbols() + + exceptionOccurred = True + try: + target.module.rva("synSym3") + exceptionOccurred = False + except pykd.BaseException: + pass + self.assertTrue(exceptionOccurred) + + def testDelByMask(self): + """Remove synthetic symbol by mask""" + pykd.addSynSymbol( + target.module.offset("FuncWithName0")-4, 1, "synSym4") + self.assertTrue( pykd.delSynSymbolsMask( "*", "synSym4" ) >= 1 ) + + exceptionOccurred = True + try: + target.module.rva("synSym4") + exceptionOccurred = False + except pykd.BaseException: + pass + self.assertTrue(exceptionOccurred) + + def testReload(self): + """Restore synthetic symbols after reload module symbols""" + pykd.addSynSymbol( + target.module.offset("FuncWithName0")-5, 1, "synSym5") + target.module.reload() + self.assertEqual( + target.module.offset("FuncWithName0")-5, + target.module.offset("synSym5")) diff --git a/test/targetapp/targetapp.vcproj b/test/targetapp/targetapp.vcproj index 4f61382..03cd77b 100644 --- a/test/targetapp/targetapp.vcproj +++ b/test/targetapp/targetapp.vcproj @@ -1,7 +1,7 @@ + +