diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index d45785e87..b734a6b19 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -27,6 +27,8 @@ #include "SharedCache.h" #include "ObjC.h" #include +#include +#include #include #include #include @@ -78,19 +80,53 @@ struct SharedCache::State DSCViewState viewState = DSCViewStateUnloaded; }; -static std::recursive_mutex viewStateMutex; -static std::unordered_map> viewStateCache; +struct SharedCache::ViewSpecificState { + std::mutex typeLibraryMutex; + std::unordered_map> typeLibraries; -std::mutex progressMutex; -std::unordered_map progressMap; - -struct ViewSpecificMutexes { std::mutex viewOperationsThatInfluenceMetadataMutex; - std::mutex typeLibraryLookupAndApplicationMutex; + + std::atomic progress; + + std::mutex stateMutex; + std::shared_ptr cachedState; }; -static std::unordered_map viewSpecificMutexes; +std::shared_ptr ViewSpecificStateForId(uint64_t viewIdentifier, bool insertIfNeeded = true) { + static std::mutex viewSpecificStateMutex; + static std::unordered_map> viewSpecificState; + + std::lock_guard lock(viewSpecificStateMutex); + + if (auto it = viewSpecificState.find(viewIdentifier); it != viewSpecificState.end()) { + if (auto statePtr = it->second.lock()) { + return statePtr; + } + } + + if (!insertIfNeeded) { + return nullptr; + } + + auto statePtr = std::make_shared(); + viewSpecificState[viewIdentifier] = statePtr; + + // Prune entries for any views that are no longer in use. + for (auto it = viewSpecificState.begin(); it != viewSpecificState.end(); ) { + if (it->second.expired()) { + it = viewSpecificState.erase(it); + } else { + ++it; + } + } + + return statePtr; +} + +std::shared_ptr ViewSpecificStateForView(Ref view) { + return ViewSpecificStateForId(view->GetFile()->GetSessionId()); +} std::string base_name(std::string const& path) { @@ -220,9 +256,7 @@ void SharedCache::PerformInitialLoad() auto path = m_dscView->GetFile()->GetOriginalFilename(); auto baseFile = MMappedFileAccessor::Open(m_dscView, m_dscView->GetFile()->GetSessionId(), path)->lock(); - progressMutex.lock(); - progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressLoadingCaches; - progressMutex.unlock(); + m_viewSpecificState->progress = LoadProgressLoadingCaches; WillMutateState(); @@ -737,9 +771,8 @@ void SharedCache::PerformInitialLoad() } } baseFile.reset(); - progressMutex.lock(); - progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressLoadingImages; - progressMutex.unlock(); + + m_viewSpecificState->progress = LoadProgressLoadingImages; // We have set up enough metadata to map VM now. @@ -952,9 +985,7 @@ void SharedCache::PerformInitialLoad() m_logger->LogDebug("Finished initial load of Shared Cache"); - progressMutex.lock(); - progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressFinished; - progressMutex.unlock(); + m_viewSpecificState->progress = LoadProgressFinished; } std::shared_ptr SharedCache::GetVMMap(bool mapPages) @@ -983,10 +1014,10 @@ void SharedCache::DeserializeFromRawView() { if (m_dscView->QueryMetadata(SharedCacheMetadataTag)) { - std::unique_lock viewStateCacheLock(viewStateMutex); - if (auto it = viewStateCache.find(m_dscView->GetFile()->GetSessionId()); it != viewStateCache.end()) + std::lock_guard lock(m_viewSpecificState->stateMutex); + if (m_viewSpecificState->cachedState) { - m_state = it->second; + m_state = m_viewSpecificState->cachedState; m_stateIsShared = true; m_metadataValid = true; } @@ -1367,7 +1398,7 @@ void SharedCache::ParseAndApplySlideInfoForFile(std::shared_ptr dscView) : m_dscView(dscView) +SharedCache::SharedCache(BinaryNinja::Ref dscView) : m_dscView(dscView), m_viewSpecificState(ViewSpecificStateForView(dscView)) { if (dscView->GetTypeName() != VIEW_NAME) { @@ -1381,46 +1412,43 @@ SharedCache::SharedCache(BinaryNinja::Ref dscView) : m_ DeserializeFromRawView(); if (!m_metadataValid) return; - if (State().viewState == DSCViewStateUnloaded) + + if (State().viewState != DSCViewStateUnloaded) { + m_viewSpecificState->progress = LoadProgressFinished; + return; + } + + std::unique_lock lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex); + try { + PerformInitialLoad(); + } + catch (...) { - std::unique_lock lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex); - try { - PerformInitialLoad(); - } - catch (...) - { - m_logger->LogError("Failed to perform initial load of Shared Cache"); - } + m_logger->LogError("Failed to perform initial load of Shared Cache"); + } - auto settings = m_dscView->GetLoadSettings(VIEW_NAME); - bool autoLoadLibsystem = true; - if (settings && settings->Contains("loader.dsc.autoLoadLibSystem")) - { - autoLoadLibsystem = settings->Get("loader.dsc.autoLoadLibSystem", m_dscView); - } - if (autoLoadLibsystem) + auto settings = m_dscView->GetLoadSettings(VIEW_NAME); + bool autoLoadLibsystem = true; + if (settings && settings->Contains("loader.dsc.autoLoadLibSystem")) + { + autoLoadLibsystem = settings->Get("loader.dsc.autoLoadLibSystem", m_dscView); + } + if (autoLoadLibsystem) + { + for (const auto& [_, header] : State().headers) { - for (const auto& [_, header] : State().headers) + if (header.installName.find("libsystem_c.dylib") != std::string::npos) { - if (header.installName.find("libsystem_c.dylib") != std::string::npos) - { - lock.unlock(); - m_logger->LogInfo("Loading core libsystem_c.dylib library"); - LoadImageWithInstallName(header.installName, false); - lock.lock(); - break; - } + lock.unlock(); + m_logger->LogInfo("Loading core libsystem_c.dylib library"); + LoadImageWithInstallName(header.installName, false); + break; } } - MutableState().viewState = DSCViewStateLoaded; - SaveToDSCView(); - } - else - { - progressMutex.lock(); - progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressFinished; - progressMutex.unlock(); } + + MutableState().viewState = DSCViewStateLoaded; + SaveToDSCView(); } SharedCache::~SharedCache() { @@ -1536,7 +1564,7 @@ bool SharedCache::LoadImageContainingAddress(uint64_t address, bool skipObjC) bool SharedCache::LoadSectionAtAddress(uint64_t address) { - std::unique_lock lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex); + std::unique_lock lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex); DeserializeFromRawView(); WillMutateState(); @@ -1806,7 +1834,7 @@ bool SharedCache::LoadImageWithInstallName(std::string installName, bool skipObj { auto settings = m_dscView->GetLoadSettings(VIEW_NAME); - std::unique_lock lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex); + std::unique_lock lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex); DeserializeFromRawView(); WillMutateState(); @@ -1880,21 +1908,7 @@ bool SharedCache::LoadImageWithInstallName(std::string installName, bool skipObj return false; } - std::unique_lock typelibLock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].typeLibraryLookupAndApplicationMutex); - auto typeLib = m_dscView->GetTypeLibrary(header.installName); - - if (!typeLib) - { - auto typeLibs = m_dscView->GetDefaultPlatform()->GetTypeLibrariesByName(header.installName); - if (!typeLibs.empty()) - { - typeLib = typeLibs[0]; - m_dscView->AddTypeLibrary(typeLib); - m_logger->LogInfo("shared-cache: adding type library for '%s': %s (%s)", - targetImage->installName.c_str(), typeLib->GetName().c_str(), typeLib->GetGuid().c_str()); - } - } - typelibLock.unlock(); + auto typeLib = TypeLibraryForImage(header.installName); SaveToDSCView(); @@ -2875,7 +2889,7 @@ std::vector>> SharedCache::LoadAllSymbolsAndW { WillMutateState(); - std::unique_lock initialLoadBlock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex); + std::lock_guard initialLoadBlock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex); std::vector>> symbols; for (const auto& img : State().images) @@ -2929,6 +2943,24 @@ std::string SharedCache::SerializedImageHeaderForName(std::string name) return ""; } +Ref SharedCache::TypeLibraryForImage(const std::string& installName) { + std::lock_guard lock(m_viewSpecificState->typeLibraryMutex); + if (auto it = m_viewSpecificState->typeLibraries.find(installName); it != m_viewSpecificState->typeLibraries.end()) { + return it->second; + } + + auto typeLib = m_dscView->GetTypeLibrary(installName); + if (!typeLib) { + auto typeLibs = m_dscView->GetDefaultPlatform()->GetTypeLibrariesByName(installName); + if (!typeLibs.empty()) { + typeLib = typeLibs[0]; + m_dscView->AddTypeLibrary(typeLib); + } + } + + m_viewSpecificState->typeLibraries[installName] = typeLib; + return typeLib; +} void SharedCache::FindSymbolAtAddrAndApplyToAddr( uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis) @@ -2973,18 +3005,7 @@ void SharedCache::FindSymbolAtAddrAndApplyToAddr( } auto exportList = SharedCache::ParseExportTrie(mapping, *header); std::vector>> exportMapping; - std::unique_lock lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].typeLibraryLookupAndApplicationMutex); - auto typeLib = m_dscView->GetTypeLibrary(header->installName); - if (!typeLib) - { - auto typeLibs = m_dscView->GetDefaultPlatform()->GetTypeLibrariesByName(header->installName); - if (!typeLibs.empty()) - { - typeLib = typeLibs[0]; - m_dscView->AddTypeLibrary(typeLib); - } - } - lock.unlock(); + auto typeLib = TypeLibraryForImage(header->installName); id = m_dscView->BeginUndoActions(); m_dscView->BeginBulkModifySymbols(); for (const auto& sym : exportList) @@ -3020,7 +3041,7 @@ void SharedCache::FindSymbolAtAddrAndApplyToAddr( } } { - std::unique_lock _lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex); + std::lock_guard lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex); MutableState().exportInfos[header->textBase] = std::move(exportMapping); } m_dscView->EndBulkModifySymbols(); @@ -3044,8 +3065,8 @@ bool SharedCache::SaveToDSCView() m_state = cachedState; m_stateIsShared = true; - std::unique_lock viewStateCacheLock(viewStateMutex); - viewStateCache[m_dscView->GetFile()->GetSessionId()] = std::move(cachedState); + std::lock_guard lock(m_viewSpecificState->stateMutex); + m_viewSpecificState->cachedState = std::move(cachedState); m_metadataValid = true; @@ -3055,7 +3076,7 @@ bool SharedCache::SaveToDSCView() } std::vector SharedCache::GetMappedRegions() const { - std::unique_lock lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex); + std::lock_guard lock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex); return State().regionsMappedIntoMemory; } @@ -3392,15 +3413,11 @@ extern "C" BNDSCViewLoadProgress BNDSCViewGetLoadProgress(uint64_t sessionID) { - progressMutex.lock(); - if (progressMap.find(sessionID) == progressMap.end()) - { - progressMutex.unlock(); - return LoadProgressNotStarted; + if (auto viewSpecificState = ViewSpecificStateForId(sessionID, false)) { + return viewSpecificState->progress; } - auto progress = progressMap[sessionID]; - progressMutex.unlock(); - return progress; + + return LoadProgressNotStarted; } uint64_t BNDSCViewFastGetBackingCacheCount(BNBinaryView* data) diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h index 3a521894e..1dcc3033e 100644 --- a/view/sharedcache/core/SharedCache.h +++ b/view/sharedcache/core/SharedCache.h @@ -538,6 +538,8 @@ namespace SharedCacheCore { struct State; + struct ViewSpecificState; + private: Ref m_logger; /* VIEW STATE BEGIN -- SERIALIZE ALL OF THIS AND STORE IT IN RAW VIEW */ @@ -552,6 +554,7 @@ namespace SharedCacheCore { bool m_metadataValid = false; /* VIEWSTATE END -- NOTHING PAST THIS IS SERIALIZED */ + std::shared_ptr m_viewSpecificState; /* API VIEW START */ BinaryNinja::Ref m_dscView; @@ -600,6 +603,7 @@ namespace SharedCacheCore { explicit SharedCache(BinaryNinja::Ref rawView); virtual ~SharedCache(); +private: std::optional LoadHeaderForAddress( std::shared_ptr vm, uint64_t address, std::string installName); void InitializeHeader( @@ -609,6 +613,8 @@ namespace SharedCacheCore { std::vector> ParseExportTrie( std::shared_ptr linkeditFile, SharedCacheMachOHeader header); + Ref TypeLibraryForImage(const std::string& installName); + const State& State() const { return *m_state; } struct State& MutableState() { AssertMutable(); return *m_state; }