diff options
| author | Mason Reed <mason@vector35.com> | 2025-04-10 00:53:06 -0400 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2025-04-10 14:38:09 -0400 |
| commit | 86019f825db516d8d5349c271f702dfe69b5fead (patch) | |
| tree | ec91007fa0c857bfd54bb3d1d78ff55daf083928 | |
| parent | b00d315524610d8c66af27619e87648bd5d63942 (diff) | |
[SharedCache] Make the shared cache file lookup more straightforward
We already had separated this part out into the `CacheProcessor` but its more like a builder than anything, so this refactors that out into a `SharedCacheBuilder`.
- Separate out the `CacheProcessor` and simplify its implementation
- Move more implementation specific stuff to the shared cache view
- Prepare for more validation of the shared cache to detect irregular or incomplete shared cache information
- Deduplicate a lot of file vs. project file lookup stuff
- Stop copying all images when getting the number of images to log
- Allow user to select another directory to scan for shared cache files, might make this a warning instead to prevent users from relying on this behavior
We will likely follow this commit up soon with better open behavior, maybe open with options can specify a list of cache entry files? Shouldn't be too much effort aside from making the UI modal for that.
| -rw-r--r-- | view/sharedcache/core/SharedCache.cpp | 194 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCache.h | 35 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCacheBuilder.cpp | 104 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCacheBuilder.h | 44 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCacheView.cpp | 151 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCacheView.h | 4 |
6 files changed, 278 insertions, 254 deletions
diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index 2e902056..717ec6aa 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -537,197 +537,3 @@ std::optional<CacheSymbol> SharedCache::GetSymbolWithName(const std::string& nam return std::nullopt; return GetSymbolAt(it->second); } - -CacheProcessor::CacheProcessor(Ref<BinaryView> view) -{ - m_view = std::move(view); - m_logger = new Logger("SharedCache.Processor", m_view->GetFile()->GetSessionId()); -} - -bool CacheProcessor::ProcessCache(SharedCache& cache) -{ - // If we are in a project, use the project cache processor. - if (m_view->GetFile()->GetProjectFile()) - return ProcessProjectCache(cache); - return ProcessFileCache(cache); -} - -bool CacheProcessor::ProcessFileCache(SharedCache& cache) -{ - // We assume that the binary view location has all the files we need. - // If we ever want to allow users to override the shared cache file location - // we should really make a cache processor constructor with entry file paths. - std::string baseFilePath = m_view->GetFile()->GetOriginalFilename(); - std::string baseFileName = BaseFileName(baseFilePath); - - // If we don't have an original file path, prompt the user to select one. - if (baseFilePath.empty()) - { - if (!GetOpenFileNameInput(baseFilePath, "Please select the base shared cache file")) - { - // User did not give a file, tell them that we will try and scan the file directory. - m_logger->LogWarn("Failed to get original file path, will try and scan the file directory."); - } - // Update so next load we don't need to prompt the user. - // TODO: What happens for a remote project? This will point at what exactly? - m_view->GetFile()->SetOriginalFilename(baseFilePath); - } - - // Add this file to the entries - try - { - auto baseEntry = CacheEntry::FromFile(baseFilePath, baseFileName, CacheEntryType::Primary); - if (!baseEntry.has_value()) - return false; - - // Before we do anything else, add this to the cache so it's available to other entries. - cache.AddEntry(std::move(*baseEntry)); - } - catch (const std::exception& e) - { - // Just return false so the view init can continue. - m_logger->LogErrorF("Failed to load base entry {}... {}", baseFileName, e.what()); - return false; - } - - // Locate all possible related entry files and add them to the cache. - std::filesystem::path basePath = std::filesystem::path(baseFilePath).parent_path(); - for (const auto& entry : std::filesystem::directory_iterator(basePath)) - { - if (!entry.is_regular_file()) - continue; - auto currentFilePath = entry.path().string(); - auto currentFileName = BaseFileName(currentFilePath); - // Skip our base file, obviously. - if (currentFilePath == baseFilePath) - continue; - // Filter files that don't contain the base file name i.e. "dyld_shared_cache_arm64e" - if (currentFilePath.find(baseFileName) == std::string::npos) - continue; - // Skip map files, they contain some nice information... we don't use. - if (entry.path().extension() == ".map") - continue; - // Skip bndb files! - if (entry.path().extension() == ".bndb") - continue; - try - { - auto additionalEntry = CacheEntry::FromFile(currentFilePath, currentFileName, CacheEntryType::Secondary); - if (!additionalEntry.has_value()) - { - m_logger->LogErrorF("Failed to load entry {}...", currentFileName); - continue; - } - - // Add this file as an entry to the cache - cache.AddEntry(std::move(*additionalEntry)); - } - catch (const std::exception& e) - { - m_logger->LogErrorF("Failed to load entry {}... {}", currentFileName, e.what()); - } - } - - return true; -} - -bool CacheProcessor::ProcessProjectCache(SharedCache& cache) -{ - auto baseProjectFile = m_view->GetFile()->GetProjectFile(); - auto baseProjectFileFolder = baseProjectFile->GetFolder(); - std::string baseFilePath = baseProjectFile->GetPathOnDisk(); - std::string baseFileName = baseProjectFile->GetName(); - // This will point to the path on disk for original non-bndb file. - std::string originalFilePath = m_view->GetFile()->GetOriginalFilename(); - - // If we don't have an original file path, prompt the user to select one. - if (originalFilePath.empty()) - { - if (!GetOpenFileNameInput(originalFilePath, "Please select the base shared cache file")) - { - // User did not give a file, tell them that we will try and scan the project directory. - m_logger->LogWarn("Failed to get original file path, will try and scan the project directory."); - } - // Update so next load we don't need to prompt the user. - m_view->GetFile()->SetOriginalFilename(originalFilePath); - } - - // Remove the .bndb extension if present ("dyld_shared_cache_arm64e.bndb" => "dyld_shared_cache_arm64e) - // TODO: This is a little annoying, we need to do this because the file accessor we have is separate - // TODO: from the view file accessor. If we either made it so that we can parse from the BNDB file accessor, - // TODO: or... something better than this. - if (baseFileName.find(".bndb") != std::string::npos) - { - baseFileName = baseFileName.substr(0, baseFileName.size() - 5); - // Search for the backing file. - for (const auto& projectFile : baseProjectFile->GetProject()->GetFiles()) - { - // Skip files not in the same folder. - auto projectFileFolder = projectFile->GetFolder(); - if (baseProjectFileFolder && projectFileFolder) - if (baseProjectFileFolder->GetId() != projectFileFolder->GetId()) - continue; - - auto projectFilePath = projectFile->GetPathOnDisk(); - auto projectFileName = projectFile->GetName(); - if (projectFileName == baseFileName || projectFilePath == originalFilePath) - { - // Use the real file instead. - baseFilePath = projectFilePath; - baseFileName = projectFileName; - baseProjectFile = projectFile; - break; - } - } - } - - // Add this file to the entries - auto baseEntry = CacheEntry::FromFile(baseFilePath, baseFileName, CacheEntryType::Primary); - if (!baseEntry.has_value()) - return false; - - // Before we do anything else, add this to the cache so it's available to other entries. - cache.AddEntry(std::move(*baseEntry)); - - // Enumerate the project files folder to gather the necessary sub caches. - const auto project = baseProjectFile->GetProject(); - const auto folder = baseProjectFile->GetFolder(); - for (const auto& projectFile : project->GetFiles()) - { - auto projectFilePath = projectFile->GetPathOnDisk(); - auto projectFileName = projectFile->GetName(); - auto currentFolder = projectFile->GetFolder(); - // Skip our base project file, obviously. - if (projectFile->GetId() == baseProjectFile->GetId()) - continue; - // Filter files that don't contain the base file name i.e. "dyld_shared_cache_arm64e" - if (projectFileName.find(baseFileName) == std::string::npos) - continue; - // Filter out .map files, they contain some nice info for rebasing... that we don't do. - if (projectFileName.find(".map") != std::string::npos) - continue; - // Filter out .bndb files! - if (projectFileName.find(".bndb") != std::string::npos) - continue; - // If both top level, or we are in the same folder as the base project file add it. - if ((!folder && !currentFolder) || (folder && currentFolder)) - { - try - { - auto additionalEntry = CacheEntry::FromFile(projectFilePath, projectFileName, CacheEntryType::Secondary); - if (!additionalEntry.has_value()) - { - m_logger->LogErrorF("Failed to load entry {}...", projectFileName); - continue; - } - - // Add this file as an entry to the cache - cache.AddEntry(std::move(*additionalEntry)); - } - catch (const std::exception& e) - {} - } - } - - return true; -} diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h index 0bf04776..8aea0362 100644 --- a/view/sharedcache/core/SharedCache.h +++ b/view/sharedcache/core/SharedCache.h @@ -7,8 +7,6 @@ #include "MachO.h" #include "VirtualMemory.h" -class SharedCache; - struct CacheSymbol { BNSymbolType type; @@ -16,19 +14,15 @@ struct CacheSymbol std::string name; CacheSymbol() = default; - CacheSymbol(BNSymbolType type, uint64_t address, std::string name) : type(type), address(address), name(std::move(name)) {} - ~CacheSymbol() = default; CacheSymbol(const CacheSymbol& other) = default; - CacheSymbol& operator=(const CacheSymbol& other) = default; CacheSymbol(CacheSymbol&& other) noexcept = default; - CacheSymbol& operator=(CacheSymbol&& other) noexcept = default; std::pair<std::string, BinaryNinja::Ref<BinaryNinja::Type>> DemangledName(BinaryNinja::BinaryView& view) const; @@ -56,15 +50,12 @@ struct CacheRegion BNSegmentFlag flags; CacheRegion() = default; - ~CacheRegion() = default; CacheRegion(const CacheRegion& other) = default; - CacheRegion& operator=(const CacheRegion& other) = default; CacheRegion(CacheRegion&& other) noexcept = default; - CacheRegion& operator=(CacheRegion&& other) noexcept = default; AddressRange AsAddressRange() const { return {start, start + size}; } @@ -95,15 +86,12 @@ struct CacheImage std::shared_ptr<SharedCacheMachOHeader> header; CacheImage() = default; - ~CacheImage() = default; CacheImage(const CacheImage& other) = default; - CacheImage& operator=(const CacheImage& other) = default; CacheImage(CacheImage&& other) noexcept = default; - CacheImage& operator=(CacheImage&& other) noexcept = default; // Get the file name from the path. @@ -145,11 +133,9 @@ public: std::vector<dyld_cache_mapping_info> mappings, std::vector<std::pair<std::string, dyld_cache_image_info>> images); CacheEntry() = default; - CacheEntry(const CacheEntry&) = default; CacheEntry(CacheEntry&&) = default; - CacheEntry& operator=(CacheEntry&&) = default; // Construct a cache entry from the file on disk. @@ -213,6 +199,7 @@ class SharedCache bool AddNonOverlappingRegion(CacheRegion region); public: + SharedCache() = default; explicit SharedCache(uint64_t addressSize); SharedCache(const SharedCache &) = delete; @@ -269,23 +256,3 @@ public: std::optional<CacheSymbol> GetSymbolWithName(const std::string& name); }; - -// This constructs a Cache, give it a file path, and it will add all relevant cache entries. -class CacheProcessor -{ - BinaryNinja::Ref<BinaryNinja::BinaryView> m_view; - BinaryNinja::Ref<BinaryNinja::Logger> m_logger; - -public: - explicit CacheProcessor(BinaryNinja::Ref<BinaryNinja::BinaryView> view); - - // Construct a cache from the root file, this will parse the cache header and locate all - // applicable cache entries and add them as well. - bool ProcessCache(SharedCache& cache); - - // Process a cache on the file system, this is for when not using a project. - bool ProcessFileCache(SharedCache& cache); - - // Process a cache using Binary Ninja's project system. - bool ProcessProjectCache(SharedCache& cache); -}; diff --git a/view/sharedcache/core/SharedCacheBuilder.cpp b/view/sharedcache/core/SharedCacheBuilder.cpp new file mode 100644 index 00000000..5c3ea0d8 --- /dev/null +++ b/view/sharedcache/core/SharedCacheBuilder.cpp @@ -0,0 +1,104 @@ +#include <filesystem> +#include "SharedCacheBuilder.h" + +using namespace BinaryNinja; + +SharedCacheBuilder::SharedCacheBuilder(Ref<BinaryView> view) +{ + m_view = std::move(view); + m_logger = new Logger("SharedCache.Builder", m_view->GetFile()->GetSessionId()); + m_primaryFileName = BaseFileName(m_view->GetFile()->GetOriginalFilename()); + m_cache = SharedCache(m_view->GetAddressSize()); + m_processedFiles = {}; +} + +SharedCache SharedCacheBuilder::Finalize() +{ + // Reset the state to the builder to reuse. + m_processedFiles = {}; + return std::move(m_cache); +} + +bool SharedCacheBuilder::AddFile( + const std::string& filePath, const std::string& fileName, const CacheEntryType cacheType) +{ + // Skip already processed files. + if (auto [_, inserted] = m_processedFiles.insert(filePath); !inserted) + return false; + // We only want to process files containing the base file name. + if (fileName.find(m_primaryFileName) == std::string::npos) + return false; + // Skip map files, they contain some nice information... we don't use. + if (fileName.find(".map") != std::string::npos) + return false; + // Skip bndb files! + if (fileName.find(".bndb") != std::string::npos) + return false; + + try + { + if (auto entry = CacheEntry::FromFile(filePath, fileName, cacheType)) + { + m_cache.AddEntry(std::move(*entry)); + return true; + } + } + catch (const std::exception& e) + { + // Just return false so the view init can continue. + m_logger->LogErrorF("Failed to add file {}... {}", fileName, e.what()); + } + + return false; +} + +size_t SharedCacheBuilder::AddDirectory(const std::string& directoryPath) +{ + // Filters then attempts to process a single directory entry as a shared cache file. + auto processDirEntry = [&](const std::filesystem::directory_entry& entry) { + const auto currentFilePath = entry.path().string(); + const auto currentFileName = BaseFileName(currentFilePath); + + // Skip non-files. + if (!entry.is_regular_file()) + return false; + + // Ok, we are now _sure_ that this file _might_ be a part of the cache, lets try and process it! + return AddFile(currentFilePath, currentFileName, CacheEntryType::Secondary); + }; + + // TODO: This is ugly. + size_t added = 0; + // Locate all possible related entry files and add them to the cache. + for (const auto& entry : std::filesystem::directory_iterator(directoryPath)) + if (processDirEntry(entry)) + added++; + return added; +} + +size_t SharedCacheBuilder::AddProjectFolder(Ref<ProjectFolder> folder) +{ + auto processProjectFile = [&](const ProjectFile& file) { + const auto currentFilePath = file.GetPathOnDisk(); + const auto currentFileName = file.GetName(); + + // Skip files not in the folder. + if (const auto currentFolder = file.GetFolder(); folder) + if (currentFolder->GetId() != folder->GetId()) + return false; + + // Ok, we are now _sure_ that this file _might_ be a part of the cache, lets try and process it! + return AddFile(currentFilePath, currentFileName, CacheEntryType::Secondary); + }; + + auto viewProjectFile = m_view->GetFile()->GetProjectFile(); + if (!viewProjectFile) + return 0; + + auto project = viewProjectFile->GetProject(); + size_t added = 0; + for (const auto& projectFile : project->GetFiles()) + if (processProjectFile(*projectFile)) + added++; + return added; +} diff --git a/view/sharedcache/core/SharedCacheBuilder.h b/view/sharedcache/core/SharedCacheBuilder.h new file mode 100644 index 00000000..4a3ed1c4 --- /dev/null +++ b/view/sharedcache/core/SharedCacheBuilder.h @@ -0,0 +1,44 @@ +#pragma once + +#include "binaryninjaapi.h" +#include "SharedCache.h" + +// This constructs a Cache, give it a file path, and it will add all relevant cache entries. +class SharedCacheBuilder +{ + BinaryNinja::Ref<BinaryNinja::BinaryView> m_view; + BinaryNinja::Ref<BinaryNinja::Logger> m_logger; + + // When we call `AddFile` it will start populating this caches entries. + // This cache is what is returned via `Finalize`. + SharedCache m_cache; + + // List of already processedFiles so we skip adding them again. + std::set<std::string> m_processedFiles; + // The base file name (i.e. "dyld_shared_cache_arm64e"), this is used to filter out non-relevant files. + std::string m_primaryFileName; + +public: + explicit SharedCacheBuilder(BinaryNinja::Ref<BinaryNinja::BinaryView> view); + + SharedCache& GetCache() { return m_cache; }; + std::set<std::string> GetProcessedFiles() { return m_processedFiles; }; + std::string GetPrimaryFileName() { return m_primaryFileName; }; + + // Set the base file name used when filtering in `AddFile`. + void SetPrimaryFileName(const std::string& baseFileName) { m_primaryFileName = baseFileName; }; + + // Returns a shared cache that is ready for processing, this should include all the required shared cache entries. + SharedCache Finalize(); + + // Tries to add the file to the shared cache, if the file has already been processed or is not valid + // then false will be returned, true if the file was added to the shared cache. A file can only be added once. + bool AddFile( + const std::string& filePath, const std::string& fileName, CacheEntryType cacheType = CacheEntryType::Secondary); + + // Process a directory on the file system. + size_t AddDirectory(const std::string& directoryPath); + + // Process a directory in a project. + size_t AddProjectFolder(BinaryNinja::Ref<BinaryNinja::ProjectFolder> folder); +}; diff --git a/view/sharedcache/core/SharedCacheView.cpp b/view/sharedcache/core/SharedCacheView.cpp index 478ce2d6..35c30446 100644 --- a/view/sharedcache/core/SharedCacheView.cpp +++ b/view/sharedcache/core/SharedCacheView.cpp @@ -1,8 +1,11 @@ #include "SharedCacheView.h" #include <regex> +#include <filesystem> + #include "SharedCacheController.h" #include "FileAccessorCache.h" #include "MappedFileAccessor.h" +#include "SharedCacheBuilder.h" using namespace BinaryNinja; using namespace BinaryNinja::DSC; @@ -166,7 +169,7 @@ bool SharedCacheView::Init() std::string os; std::string arch; - auto logger = new Logger("SharedCache.View", GetFile()->GetSessionId()); + m_logger = new Logger("SharedCache.View", GetFile()->GetSessionId()); uint32_t platform; GetParentView()->Read(&platform, 0xd8, 4); @@ -194,7 +197,7 @@ bool SharedCacheView::Init() case DSCPlatformWatchOSSimulator: case DSCPlatformBridgeOS: default: - logger->LogError("Unknown platform: %d", platform); + m_logger->LogError("Unknown platform: %d", platform); return false; } @@ -209,7 +212,7 @@ bool SharedCacheView::Init() } else { - logger->LogError("Unknown magic: %s", magic); + m_logger->LogError("Unknown magic: %s", magic); return false; } @@ -335,7 +338,7 @@ bool SharedCacheView::Init() if (!err.empty() || !headerType.type) { - logger->LogError("Failed to parse header type: %s", err.c_str()); + m_logger->LogError("Failed to parse header type: %s", err.c_str()); return false; } @@ -758,14 +761,14 @@ bool SharedCacheView::Init() GetParentView()->Read(&basePointer, 16, 4); if (basePointer == 0) { - logger->LogError("Failed to read base pointer"); + m_logger->LogError("Failed to read base pointer"); return false; } uint64_t primaryBase = 0; GetParentView()->Read(&primaryBase, basePointer, 8); if (primaryBase == 0) { - logger->LogError("Failed to read primary base at 0x%llx", basePointer); + m_logger->LogError("Failed to read primary base at 0x%llx", basePointer); return false; } @@ -790,30 +793,121 @@ bool SharedCacheView::Init() return true; } - auto sharedCache = SharedCache(GetAddressSize()); + return InitController(); +} + +// Get the file path and file name for the primary cache entry. +// We need this to support BNDB and projects. +std::optional<std::pair<std::string, std::string>> GetPrimaryFileInfo(BinaryView& view) +{ + auto viewFile = view.GetFile(); + // Add the primary file, for a regular view this is the original path. + auto primaryFilePath = viewFile->GetOriginalFilename(); + + // If we don't have an original file path, prompt the user to select one. + if (primaryFilePath.empty()) + { + if (!GetOpenFileNameInput(primaryFilePath, "Please select the primary shared cache file")) + return std::nullopt; + // Update so next load we don't need to prompt the user. + viewFile->SetOriginalFilename(primaryFilePath); + } + + // The primary file name for which we will use when adding files. + // NOTE: For projects the file name here is a UUID, we will need to traverse the project to get the name. + auto primaryFileName = BaseFileName(primaryFilePath); + + // If we are a project file, we need to grab the actual name of the file. + if (auto primaryProjectFile = viewFile->GetProjectFile()) + { + primaryFileName = primaryProjectFile->GetName(); + + // If we are a BNDB in a project, we need to change the `primaryFilePath` as well. + // Because our shared cache processing only works through our mapped file accessor we need to original + // file to map in, this can be relaxed if we ever support Binary Ninja file accessors. + if (primaryFileName.find(".bndb") != std::string::npos) + { + primaryFileName = primaryFileName.substr(0, primaryFileName.size() - 5); + for (const auto& pj : primaryProjectFile->GetProject()->GetFiles()) + { + auto projectFilePath = pj->GetPathOnDisk(); + auto projectFileName = pj->GetName(); + if (projectFileName == primaryFileName || projectFilePath == primaryFilePath) + { + primaryFilePath = projectFilePath; + primaryFileName = projectFileName; + break; + } + } + } + } + + return {{primaryFilePath, primaryFileName}}; +} + +bool SharedCacheView::InitController() +{ + auto primaryFileInfo = GetPrimaryFileInfo(*this); + if (!primaryFileInfo.has_value()) + return false; + auto [primaryFilePath, primaryFileName] = primaryFileInfo.value(); + auto primaryFileDir = std::filesystem::path(primaryFilePath).parent_path(); + + // OK, we have the primary shared cache file, now let's add the entries. + auto sharedCacheBuilder = SharedCacheBuilder(this); + sharedCacheBuilder.SetPrimaryFileName(primaryFileName); + + // Add the primary file. If we fail log alert that the primary cache file is invalid. + // We process the primary cache entry first as it might be consulted in the processing of later entries. + if (!sharedCacheBuilder.AddFile(primaryFilePath, primaryFileName, CacheEntryType::Primary)) + { + m_logger->LogAlertF("Failed to add primary cache file: '{}'", primaryFileName); + return false; + } { - // Add up all the cache entries using the cache processor. // After this we should have all the mappings available as well. - CacheProcessor cacheProcessor = CacheProcessor(this); auto startTime = std::chrono::high_resolution_clock::now(); - bool result = cacheProcessor.ProcessCache(sharedCache); + sharedCacheBuilder.AddDirectory(primaryFileDir); + if (auto projectFile = GetFile()->GetProjectFile()) + sharedCacheBuilder.AddProjectFolder(projectFile->GetFolder()); + auto entryCount = sharedCacheBuilder.GetCache().GetEntries().size(); auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> elapsed = endTime - startTime; - auto entryCount = sharedCache.GetEntries().size(); - logger->LogInfo("Processing %zu entries took %.3f seconds", entryCount, elapsed.count()); - - if (!result) - { - // Oh no, we failed to process the cache, this likely means the primary on-disk file was not able to be found. - logger->LogError("Failed to process cache, likely missing cache files."); - } + m_logger->LogInfo("Processing %zu entries took %.3f seconds", entryCount, elapsed.count()); // If we can't store all of our files for this cache in the accessor cache we might run into issues, warn the user. if (entryCount > FileAccessorCache::Global().GetCacheSize()) - logger->LogWarn("Cache contains more entries than the allowed number of opened file handles, this may impact reliability."); + m_logger->LogWarn("Cache contains more entries than the allowed number of opened file handles, this may impact reliability."); + + // If we have less entries than the primary header subcache array count than let the user add another directory. + const auto& cacheEntries = sharedCacheBuilder.GetCache().GetEntries(); + for (const auto& [_, entry] : cacheEntries) + { + if (entry.GetType() != CacheEntryType::Primary) + continue; + + auto requiredCount = entry.GetHeader().subCacheArrayCount; + if (requiredCount <= entryCount) + continue; + + m_logger->LogWarnF("Opening with {} entries when shared cache header says there are {}, likely missing files, prompting user to select a directory containing the rest.", entryCount, requiredCount); + // We don't have enough entries, prompt the user to select a directory with the rest. + std::string supplementaryDir; + if (GetDirectoryNameInput(supplementaryDir, "Directory with associated shared cache files")) + { + auto additionalEntries = sharedCacheBuilder.AddDirectory(primaryFileDir); + m_logger->LogInfoF("Processed an additional {} entries...", additionalEntries); + entryCount += additionalEntries; + // If we are still below the count, just let the user know and continue. + if (entryCount < requiredCount) + m_logger->LogWarnF("Provided entry files still below the reported entry count in the shared cache header. Some functionality may be lost."); + } + } } + auto sharedCache = sharedCacheBuilder.Finalize(); + { // Write all the slide info pointers to the virtual memory. // This should be done before any other work begins, as the backing data will be altered by this process. @@ -822,7 +916,7 @@ bool SharedCacheView::Init() sharedCache.ProcessEntrySlideInfo(entry); auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> elapsed = endTime - startTime; - logger->LogInfo("Processing slide info took %.3f seconds", elapsed.count()); + m_logger->LogInfo("Processing slide info took %.3f seconds", elapsed.count()); } { @@ -834,11 +928,11 @@ bool SharedCacheView::Init() sharedCache.ProcessEntryImages(entry); auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> elapsed = endTime - startTime; - auto images = sharedCache.GetImages(); - logger->LogInfo("Processing %zu images took %.3f seconds", images.size(), elapsed.count()); + const auto& images = sharedCache.GetImages(); + m_logger->LogInfo("Processing %zu images took %.3f seconds", images.size(), elapsed.count()); // Warn if we found no images, and provide the likely explanation if (images.empty()) - logger->LogWarn("Failed to process any images, likely missing cache files."); + m_logger->LogWarn("Failed to process any images, likely missing cache files."); } { @@ -849,12 +943,16 @@ bool SharedCacheView::Init() sharedCache.ProcessEntryRegions(entry); auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> elapsed = endTime - startTime; - logger->LogInfo("Processing %zu regions took %.3f seconds", sharedCache.GetRegions().size(), elapsed.count()); + m_logger->LogInfo("Processing %zu regions took %.3f seconds", sharedCache.GetRegions().size(), elapsed.count()); } + // TODO: Here we should have all the regions and what not for the virtual memory populated, I think it might be a good idea + // TODO: To verify the regions are here and pointing at valid file accessor mappings, to make diagnosing issues quicker. + auto cacheController = SharedCacheController::Initialize(*this, std::move(sharedCache)); { + auto logger = m_logger; // Load up all the symbols into the named symbols lookup map. // NOTE: We do this on a separate thread as image & region loading does not consult this. WorkerPriorityEnqueue([logger, cacheController]() { @@ -867,11 +965,12 @@ bool SharedCacheView::Init() }); } + Ref<Settings> settings = GetLoadSettings(GetTypeName()); // Users can adjust which images are loaded by default using the `loader.dsc.autoLoadPattern` setting. std::string autoLoadPattern = ".*libsystem_c.dylib"; if (settings && settings->Contains("loader.dsc.autoLoadPattern")) autoLoadPattern = settings->Get<std::string>("loader.dsc.autoLoadPattern", this); - logger->LogDebug("Loading images using pattern: %s", autoLoadPattern.c_str()); + m_logger->LogDebug("Loading images using pattern: %s", autoLoadPattern.c_str()); { // Load all images that match the `autoLoadPattern`. @@ -884,7 +983,7 @@ bool SharedCacheView::Init() ++loadedImages; auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> elapsed = endTime - startTime; - logger->LogInfo("Automatically loading %zu images took %.3f seconds", loadedImages, elapsed.count()); + m_logger->LogInfo("Automatically loading %zu images took %.3f seconds", loadedImages, elapsed.count()); } return true; diff --git a/view/sharedcache/core/SharedCacheView.h b/view/sharedcache/core/SharedCacheView.h index 1ead0b33..0ae43368 100644 --- a/view/sharedcache/core/SharedCacheView.h +++ b/view/sharedcache/core/SharedCacheView.h @@ -11,6 +11,7 @@ class SharedCacheView : public BinaryNinja::BinaryView { bool m_parseOnly; + BinaryNinja::Ref<BinaryNinja::Logger> m_logger; public: SharedCacheView(const std::string& typeName, BinaryView* data, bool parseOnly = false); @@ -18,6 +19,9 @@ public: ~SharedCacheView() override = default; bool Init() override; + + // Initialized the shared cache controller for this view. This is what allows us to load images and regions. + bool InitController(); }; |
