summaryrefslogtreecommitdiff
path: root/view/sharedcache
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-04-10 00:53:06 -0400
committerMason Reed <mason@vector35.com>2025-04-10 14:38:09 -0400
commit86019f825db516d8d5349c271f702dfe69b5fead (patch)
treeec91007fa0c857bfd54bb3d1d78ff55daf083928 /view/sharedcache
parentb00d315524610d8c66af27619e87648bd5d63942 (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.
Diffstat (limited to 'view/sharedcache')
-rw-r--r--view/sharedcache/core/SharedCache.cpp194
-rw-r--r--view/sharedcache/core/SharedCache.h35
-rw-r--r--view/sharedcache/core/SharedCacheBuilder.cpp104
-rw-r--r--view/sharedcache/core/SharedCacheBuilder.h44
-rw-r--r--view/sharedcache/core/SharedCacheView.cpp151
-rw-r--r--view/sharedcache/core/SharedCacheView.h4
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();
};