/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/pore/poreve/porevesrc/hookmanager.C $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* COPYRIGHT International Business Machines Corp. 2012,2014 */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ // $Id: hookmanager.C,v 1.17 2013/11/27 20:04:36 thi Exp $ /// \file hookmanager.C /// \brief A portable symbol table and hook execution facility #include #include #include "hookmanager.H" #ifndef __HOSTBOOT_MODULE #include #endif using namespace vsbe; using namespace fapi; #ifndef ULL /// The printf() checker for 64-bit GCC throws a warning if a uint64_t is /// printed as %llx - I have no idea why, but by casting them to (unsigned /// long long) the warning goes away. #define ULL(x) ((unsigned long long)(x)) #endif fapi::ReturnCode vsbe::hookOk; HookManager* HookManager::s_instance = 0; //////////////////////////////////////////////////////////////////////////// // PoreAddressComparison //////////////////////////////////////////////////////////////////////////// bool PoreAddressComparison::operator()(PoreAddress const& i_lhs, PoreAddress const& i_rhs) const { return (i_lhs.iv_memorySpace < i_rhs.iv_memorySpace) || (i_lhs.iv_offset < i_rhs.iv_offset); } //////////////////////////////////////////////////////////////////////////// // CharPointerComparison //////////////////////////////////////////////////////////////////////////// bool CharPointerComparison::operator()(char const* i_lhs, char const* i_rhs) const { return strcmp(i_lhs, i_rhs) < 0; } //////////////////////////////////////////////////////////////////////////// // HookManager //////////////////////////////////////////////////////////////////////////// ////////////////////////////// Creators ////////////////////////////// HookManager::HookManager() : iv_error(HOOK_OK) { } /// \todo This destructor leaks memory; We need to add code to delete all of /// the dynamic objects. HookManager::~HookManager() { #ifndef __HOSTBOOT_MODULE std::vector::size_type i; int rc; for (i = 0; i < iv_dllHandles.size(); i++) { rc = ::dlclose(iv_dllHandles[i]); FAPI_DBG("%d <-- dlclose(%p)", rc, iv_dllHandles[i]); if (rc != 0) { FAPI_ERR("%s:%d: dlclose(%p) (iv_dllHandles[%d]) " "failed with the error below\n%s", __FILE__, __LINE__, iv_dllHandles[i], i, ::dlerror()); } } #endif // __HOSTBOOT_MODULE } ///////////////////////////// Accessors ////////////////////////////// fapi::ReturnCode HookManager::runInstructionHook(const PoreAddress& i_address, const uint32_t i_hook, const uint64_t i_parameter, Pore& io_pore, const fapi::Target& i_target) { InstructionHookMap::iterator ihmi; fapi::ReturnCode rc; ihmi = instance()->iv_instructionHookMap.find(i_hook); if (ihmi == instance()->iv_instructionHookMap.end()) { rc = FAPI_RC_SUCCESS; } else { rc = (ihmi->second)(i_address, i_hook, i_parameter, io_pore, i_target); } return rc; } fapi::ReturnCode HookManager::runReadHooks(const PoreAddress& i_address, Pore& io_pore, const fapi::Target& i_target) { return instance()->runHooks(HOOK_READ_INTERACTIVE, HOOK_READ_EXTRACTED, i_address, io_pore, i_target); } fapi::ReturnCode HookManager::runWriteHooks(const PoreAddress& i_address, Pore& io_pore, const fapi::Target& i_target) { return instance()->runHooks(HOOK_WRITE_INTERACTIVE, HOOK_WRITE_EXTRACTED, i_address, io_pore, i_target); } fapi::ReturnCode HookManager::runFetchHooks(const PoreAddress& i_address, Pore& io_pore, const fapi::Target& i_target) { return instance()->runHooks(HOOK_FETCH_INTERACTIVE, HOOK_FETCH_EXTRACTED, i_address, io_pore, i_target); } HookError HookManager::findGlobalSymbol(const char* i_symbol, bool& o_found, GlobalSymbolInfo& io_info) { GlobalSymbolMap::iterator gsmi; o_found = false; if (!instance()->iv_error) { gsmi = instance()->iv_globalSymbolMap.find(i_symbol); if (gsmi != instance()->iv_globalSymbolMap.end()) { o_found = true; io_info = *(gsmi->second); } } return instance()->iv_error; } HookError HookManager::globalSymbolList(GlobalSymbolList& io_symbols, const char* i_types) { GlobalSymbolMap::iterator gsmi; if (!instance()->iv_error) { for (gsmi = instance()->iv_globalSymbolMap.begin(); gsmi != instance()->iv_globalSymbolMap.end(); gsmi++) { if ((i_types == 0) || (strchr(i_types, gsmi->second->iv_type) != 0)) { io_symbols.push_back(*gsmi); } } } return instance()->iv_error; } void HookManager::report(const int i_options) { InstructionHookMap::iterator ihmi; HookedAddressMap::iterator hami; HookedFileMap::iterator hfmi; GlobalSymbolMap::iterator gsmi; Hook* hook; const HookTable* table; size_t entry; char type; ExtractedHook* exHook; if (i_options != 0) { FAPI_INF(""); FAPI_INF("------------------------------------------------------"); FAPI_INF("-- HookManager @ %p", instance()); FAPI_INF("------------------------------------------------------"); if (i_options & HM_REPORT_HOOKED_ADDRESS_MAP) { FAPI_INF(""); FAPI_INF("--- Hooked Address Map : %zu unique addresses ---", instance()->iv_hookedAddressMap.size()); FAPI_INF(""); for (hami = instance()->iv_hookedAddressMap.begin(); hami != instance()->iv_hookedAddressMap.end(); hami++) { for (hook = hami->second; hook != 0; hook = hook->iv_next) { switch (hook->iv_type) { case HOOK_READ_INTERACTIVE: case HOOK_WRITE_INTERACTIVE: case HOOK_FETCH_INTERACTIVE: switch (hook->iv_type) { case HOOK_READ_INTERACTIVE: type = 'r'; break; case HOOK_WRITE_INTERACTIVE: type = 'w'; break; case HOOK_FETCH_INTERACTIVE: type = 'x'; break; default: type = '?'; break; // For GCC -Wall } FAPI_INF("%04x:%08x %c %p", hami->first.iv_memorySpace, hami->first.iv_offset, type, hook->iv_hook); break; case HOOK_READ_EXTRACTED: case HOOK_WRITE_EXTRACTED: case HOOK_FETCH_EXTRACTED: switch (hook->iv_type) { case HOOK_READ_EXTRACTED: type = 'r'; break; case HOOK_WRITE_EXTRACTED: type = 'w'; break; case HOOK_FETCH_EXTRACTED: type = 'x'; break; default: type = '?'; break; // For GCC -Wall } exHook = (ExtractedHook*)(hook->iv_hook); FAPI_INF("%04x:%08x %8zu %s", hami->first.iv_memorySpace, hami->first.iv_offset, exHook->iv_index, exHook->iv_file); break; default: break; // For GCC -Wall } } } } if (i_options & HM_REPORT_HOOK_TABLES) { FAPI_INF(""); FAPI_INF("--- Hook Tables : %zu hooked files---", instance()->iv_hookedFileMap.size()); for (hfmi = instance()->iv_hookedFileMap.begin(); hfmi != instance()->iv_hookedFileMap.end(); hfmi++) { FAPI_INF(""); FAPI_INF("%s", hfmi->first); table = hfmi->second; for (entry = 0; entry < table->iv_entries; entry++) { FAPI_INF("%8zu %p", entry, table->iv_hooks[entry]); } } } if (i_options & HM_REPORT_INSTRUCTION_HOOK_MAP) { FAPI_INF(""); FAPI_INF("--- Instruction Hook Map ---"); FAPI_INF(""); for (ihmi = instance()->iv_instructionHookMap.begin(); ihmi != instance()->iv_instructionHookMap.end(); ihmi++) { FAPI_INF("%06x %p", ihmi->first, ihmi->second); } } if (i_options & HM_REPORT_GLOBAL_SYMBOL_MAP) { FAPI_INF(""); FAPI_INF("--- Global Symbol Map ---"); FAPI_INF(""); for (gsmi = instance()->iv_globalSymbolMap.begin(); gsmi != instance()->iv_globalSymbolMap.end(); gsmi++) { FAPI_INF("%04x:%08x %c %s", gsmi->second->iv_address.iv_memorySpace, gsmi->second->iv_address.iv_offset, gsmi->second->iv_type, gsmi->first); } } FAPI_INF(""); FAPI_INF("------------------------------------------------------"); FAPI_INF(""); } } //////////////////////////// Manipulators //////////////////////////// void* HookManager::dlopen(const char* i_fileName, int i_flags) { void* handle = 0; #ifndef __HOSTBOOT_MODULE handle = ::dlopen(i_fileName, i_flags); FAPI_DBG("%p <-- dlopen(%s, %d)", handle, i_fileName, i_flags); if (handle != 0) { instance()->iv_dllHandles.push_back(handle); } #endif // __HOSTBOOT_MODULE return handle; } HookError HookManager::registerInstructionHook(const uint32_t i_index, HookInstructionHook i_hookRoutine) { InstructionHookMap::iterator ihmi; if (!instance()->iv_error) { ihmi = instance()->iv_instructionHookMap.find(i_index); if (ihmi == instance()->iv_instructionHookMap.end()) { instance()->iv_instructionHookMap[i_index] = i_hookRoutine; } else { if (ihmi->second != i_hookRoutine) { FAPI_ERR("%s : Static hook collision for index : %u", __FUNCTION__, i_index); instance()->iv_error = HOOK_STATIC_COLLISION; } } } return instance()->iv_error; } HookError HookManager::registerHookTable(const char* i_file, const HookTable* i_table) { if (!instance()->iv_error) { if (instance()->iv_hookedFileMap.find(i_file) != instance()->iv_hookedFileMap.end()) { FAPI_ERR("%s : File name collision : %s", __FUNCTION__, i_file); instance()->iv_error = HOOK_FILE_NAME_COLLISION; } else { instance()->iv_hookedFileMap[i_file] = i_table; } } return instance()->iv_error; } HookError HookManager::registerHook(const PoreAddress& i_address, Hook* io_hook) { HookedAddressMap::iterator hami; Hook* hook; if (!instance()->iv_error) { hami = instance()->iv_hookedAddressMap.find(i_address); if (hami != instance()->iv_hookedAddressMap.end()) { for (hook = hami->second; hook->iv_next != 0; hook = hook->iv_next); hook->iv_next = io_hook; } else { instance()->iv_hookedAddressMap[i_address] = io_hook; } io_hook->iv_next = 0; } return instance()->iv_error; } HookError HookManager::registerGlobalSymbol(const char* i_symbol, const GlobalSymbolInfo* i_info) { if (!instance()->iv_error) { if (instance()->iv_globalSymbolMap.find(i_symbol) != instance()->iv_globalSymbolMap.end()) { FAPI_ERR("%s : Multiply defined symbol : %s", __FUNCTION__, i_symbol); instance()->iv_error = HOOK_MULTIPLY_DEFINED_SYMBOL; } else { instance()->iv_globalSymbolMap[i_symbol] = i_info; } } return instance()->iv_error; } HookError HookManager::addInteractiveHook(const PoreAddress& i_address, const HookType i_type, const AddressBasedHook i_hookRoutine) { Hook* hook = new Hook(); HookError rc; instance()->iv_error = HOOK_OK; if (hook == 0) { rc = HOOK_MEMORY_ALLOCATION_FAILED; } else { switch (i_type) { case HOOK_READ_INTERACTIVE: case HOOK_WRITE_INTERACTIVE: case HOOK_FETCH_INTERACTIVE: rc = HOOK_OK; hook->iv_type = i_type; hook->iv_hook = (void*)i_hookRoutine; registerHook(i_address, hook); break; default: delete hook; rc = HOOK_ILLEGAL_TYPE; } } instance()->iv_error = rc; return rc; } HookError HookManager::deleteInteractiveHooks(const PoreAddress& i_address, const HookType i_type, const AddressBasedHook i_hookRoutine) { HookedAddressMap::iterator hami; Hook **last, *hook, *next; HookError rc; bool deleted = false; switch (i_type) { case HOOK_READ_INTERACTIVE: case HOOK_WRITE_INTERACTIVE: case HOOK_FETCH_INTERACTIVE: hami = instance()->iv_hookedAddressMap.find(i_address); if (hami != instance()->iv_hookedAddressMap.end()) { for (last = &(hami->second), hook = hami->second; hook != 0; hook = next) { next = hook->iv_next; if ((hook->iv_type == i_type) && ((i_hookRoutine == 0) || (hook->iv_hook) == i_hookRoutine)) { delete hook; deleted = true; *last = next; } else { last = &(hook->iv_next); } } } if ((i_hookRoutine == 0) || deleted) { if (hami->second == 0) { instance()->iv_hookedAddressMap.erase(hami); } rc = HOOK_OK; } else { rc = HOOK_INTERACTIVE_DELETE_FAILED; } break; default: rc = HOOK_ILLEGAL_TYPE; } instance()->iv_error = rc; return rc; } void HookManager::clearError() { instance()->iv_error = HOOK_OK; } ////////////////////////// Implementation //////////////////////////// // This routine checks to see if the i_address is mapped in the table, and if // so, looks for a hook of the indicated type associated with that address. // It is possible that the search may beningnly fail even though the address is // mapped, e.g. we may be looking for a read hook but the hook mapped to the // address is a write hook. We may want to consider a more efficient structure // (multi-key map?) to avoid this. fapi::ReturnCode HookManager::runHooks(const HookType i_interactiveType, const HookType i_extractedType, const PoreAddress& i_address, Pore& io_pore, const fapi::Target& i_target) { HookedAddressMap::iterator hami; Hook* hook; ExtractedHook *exHook; HookedFileMap::iterator hfmi; const HookTable* table; fapi::ReturnCode rc; hami = instance()->iv_hookedAddressMap.find(i_address); if (hami != instance()->iv_hookedAddressMap.end()) { for (hook = hami->second; (hook != 0) && rc.ok(); hook = hook->iv_next) { if (hook->iv_type == i_interactiveType) { rc = ((AddressBasedHook)(hook->iv_hook))(i_address, i_interactiveType, io_pore, i_target); } else if (hook->iv_type == i_extractedType) { exHook = (ExtractedHook*)(hook->iv_hook); hfmi = instance()->iv_hookedFileMap.find(exHook->iv_file); if (hfmi == instance()->iv_hookedFileMap.end()) { FAPI_ERR("%s : Address %04x:%08x is hooked from " "file '%s', but no HookTable can be found " "for the file.", __FUNCTION__, i_address.iv_memorySpace, i_address.iv_offset, exHook->iv_file); instance()->iv_error = HOOK_TABLE_MISSING; FAPI_SET_HWP_ERROR(rc, RC_POREVE_HOOKMANAGER_INCONSISTENCY); } else { table = hfmi->second; if (exHook->iv_index > table->iv_entries) { FAPI_ERR("%s : Address %04x:%08x is hooked from " "file '%s' at index %zu, " "but the index exceeds " "the number of hooks indexed for the " "file (%zu).", __FUNCTION__, i_address.iv_memorySpace, i_address.iv_offset, hfmi->first, exHook->iv_index, table->iv_entries); instance()->iv_error = HOOK_INDEX_FAILURE; FAPI_SET_HWP_ERROR(rc, RC_POREVE_HOOKMANAGER_INCONSISTENCY); } else { rc = (table->iv_hooks[exHook->iv_index]) (i_address, i_extractedType, io_pore, i_target); } } } if (!rc.ok()) break; } } return rc; } //////////////////////////////////////////////////////////////////////////// // HookInitializer //////////////////////////////////////////////////////////////////////////// HookInitializer::HookInitializer(HookManagerInitializer i_function) { i_function(); } HookInitializer::~HookInitializer() { }