Skip to content

Commit 86019f8

Browse files
committed
[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.
1 parent b00d315 commit 86019f8

File tree

6 files changed

+278
-254
lines changed

6 files changed

+278
-254
lines changed

view/sharedcache/core/SharedCache.cpp

Lines changed: 0 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -537,197 +537,3 @@ std::optional<CacheSymbol> SharedCache::GetSymbolWithName(const std::string& nam
537537
return std::nullopt;
538538
return GetSymbolAt(it->second);
539539
}
540-
541-
CacheProcessor::CacheProcessor(Ref<BinaryView> view)
542-
{
543-
m_view = std::move(view);
544-
m_logger = new Logger("SharedCache.Processor", m_view->GetFile()->GetSessionId());
545-
}
546-
547-
bool CacheProcessor::ProcessCache(SharedCache& cache)
548-
{
549-
// If we are in a project, use the project cache processor.
550-
if (m_view->GetFile()->GetProjectFile())
551-
return ProcessProjectCache(cache);
552-
return ProcessFileCache(cache);
553-
}
554-
555-
bool CacheProcessor::ProcessFileCache(SharedCache& cache)
556-
{
557-
// We assume that the binary view location has all the files we need.
558-
// If we ever want to allow users to override the shared cache file location
559-
// we should really make a cache processor constructor with entry file paths.
560-
std::string baseFilePath = m_view->GetFile()->GetOriginalFilename();
561-
std::string baseFileName = BaseFileName(baseFilePath);
562-
563-
// If we don't have an original file path, prompt the user to select one.
564-
if (baseFilePath.empty())
565-
{
566-
if (!GetOpenFileNameInput(baseFilePath, "Please select the base shared cache file"))
567-
{
568-
// User did not give a file, tell them that we will try and scan the file directory.
569-
m_logger->LogWarn("Failed to get original file path, will try and scan the file directory.");
570-
}
571-
// Update so next load we don't need to prompt the user.
572-
// TODO: What happens for a remote project? This will point at what exactly?
573-
m_view->GetFile()->SetOriginalFilename(baseFilePath);
574-
}
575-
576-
// Add this file to the entries
577-
try
578-
{
579-
auto baseEntry = CacheEntry::FromFile(baseFilePath, baseFileName, CacheEntryType::Primary);
580-
if (!baseEntry.has_value())
581-
return false;
582-
583-
// Before we do anything else, add this to the cache so it's available to other entries.
584-
cache.AddEntry(std::move(*baseEntry));
585-
}
586-
catch (const std::exception& e)
587-
{
588-
// Just return false so the view init can continue.
589-
m_logger->LogErrorF("Failed to load base entry {}... {}", baseFileName, e.what());
590-
return false;
591-
}
592-
593-
// Locate all possible related entry files and add them to the cache.
594-
std::filesystem::path basePath = std::filesystem::path(baseFilePath).parent_path();
595-
for (const auto& entry : std::filesystem::directory_iterator(basePath))
596-
{
597-
if (!entry.is_regular_file())
598-
continue;
599-
auto currentFilePath = entry.path().string();
600-
auto currentFileName = BaseFileName(currentFilePath);
601-
// Skip our base file, obviously.
602-
if (currentFilePath == baseFilePath)
603-
continue;
604-
// Filter files that don't contain the base file name i.e. "dyld_shared_cache_arm64e"
605-
if (currentFilePath.find(baseFileName) == std::string::npos)
606-
continue;
607-
// Skip map files, they contain some nice information... we don't use.
608-
if (entry.path().extension() == ".map")
609-
continue;
610-
// Skip bndb files!
611-
if (entry.path().extension() == ".bndb")
612-
continue;
613-
try
614-
{
615-
auto additionalEntry = CacheEntry::FromFile(currentFilePath, currentFileName, CacheEntryType::Secondary);
616-
if (!additionalEntry.has_value())
617-
{
618-
m_logger->LogErrorF("Failed to load entry {}...", currentFileName);
619-
continue;
620-
}
621-
622-
// Add this file as an entry to the cache
623-
cache.AddEntry(std::move(*additionalEntry));
624-
}
625-
catch (const std::exception& e)
626-
{
627-
m_logger->LogErrorF("Failed to load entry {}... {}", currentFileName, e.what());
628-
}
629-
}
630-
631-
return true;
632-
}
633-
634-
bool CacheProcessor::ProcessProjectCache(SharedCache& cache)
635-
{
636-
auto baseProjectFile = m_view->GetFile()->GetProjectFile();
637-
auto baseProjectFileFolder = baseProjectFile->GetFolder();
638-
std::string baseFilePath = baseProjectFile->GetPathOnDisk();
639-
std::string baseFileName = baseProjectFile->GetName();
640-
// This will point to the path on disk for original non-bndb file.
641-
std::string originalFilePath = m_view->GetFile()->GetOriginalFilename();
642-
643-
// If we don't have an original file path, prompt the user to select one.
644-
if (originalFilePath.empty())
645-
{
646-
if (!GetOpenFileNameInput(originalFilePath, "Please select the base shared cache file"))
647-
{
648-
// User did not give a file, tell them that we will try and scan the project directory.
649-
m_logger->LogWarn("Failed to get original file path, will try and scan the project directory.");
650-
}
651-
// Update so next load we don't need to prompt the user.
652-
m_view->GetFile()->SetOriginalFilename(originalFilePath);
653-
}
654-
655-
// Remove the .bndb extension if present ("dyld_shared_cache_arm64e.bndb" => "dyld_shared_cache_arm64e)
656-
// TODO: This is a little annoying, we need to do this because the file accessor we have is separate
657-
// TODO: from the view file accessor. If we either made it so that we can parse from the BNDB file accessor,
658-
// TODO: or... something better than this.
659-
if (baseFileName.find(".bndb") != std::string::npos)
660-
{
661-
baseFileName = baseFileName.substr(0, baseFileName.size() - 5);
662-
// Search for the backing file.
663-
for (const auto& projectFile : baseProjectFile->GetProject()->GetFiles())
664-
{
665-
// Skip files not in the same folder.
666-
auto projectFileFolder = projectFile->GetFolder();
667-
if (baseProjectFileFolder && projectFileFolder)
668-
if (baseProjectFileFolder->GetId() != projectFileFolder->GetId())
669-
continue;
670-
671-
auto projectFilePath = projectFile->GetPathOnDisk();
672-
auto projectFileName = projectFile->GetName();
673-
if (projectFileName == baseFileName || projectFilePath == originalFilePath)
674-
{
675-
// Use the real file instead.
676-
baseFilePath = projectFilePath;
677-
baseFileName = projectFileName;
678-
baseProjectFile = projectFile;
679-
break;
680-
}
681-
}
682-
}
683-
684-
// Add this file to the entries
685-
auto baseEntry = CacheEntry::FromFile(baseFilePath, baseFileName, CacheEntryType::Primary);
686-
if (!baseEntry.has_value())
687-
return false;
688-
689-
// Before we do anything else, add this to the cache so it's available to other entries.
690-
cache.AddEntry(std::move(*baseEntry));
691-
692-
// Enumerate the project files folder to gather the necessary sub caches.
693-
const auto project = baseProjectFile->GetProject();
694-
const auto folder = baseProjectFile->GetFolder();
695-
for (const auto& projectFile : project->GetFiles())
696-
{
697-
auto projectFilePath = projectFile->GetPathOnDisk();
698-
auto projectFileName = projectFile->GetName();
699-
auto currentFolder = projectFile->GetFolder();
700-
// Skip our base project file, obviously.
701-
if (projectFile->GetId() == baseProjectFile->GetId())
702-
continue;
703-
// Filter files that don't contain the base file name i.e. "dyld_shared_cache_arm64e"
704-
if (projectFileName.find(baseFileName) == std::string::npos)
705-
continue;
706-
// Filter out .map files, they contain some nice info for rebasing... that we don't do.
707-
if (projectFileName.find(".map") != std::string::npos)
708-
continue;
709-
// Filter out .bndb files!
710-
if (projectFileName.find(".bndb") != std::string::npos)
711-
continue;
712-
// If both top level, or we are in the same folder as the base project file add it.
713-
if ((!folder && !currentFolder) || (folder && currentFolder))
714-
{
715-
try
716-
{
717-
auto additionalEntry = CacheEntry::FromFile(projectFilePath, projectFileName, CacheEntryType::Secondary);
718-
if (!additionalEntry.has_value())
719-
{
720-
m_logger->LogErrorF("Failed to load entry {}...", projectFileName);
721-
continue;
722-
}
723-
724-
// Add this file as an entry to the cache
725-
cache.AddEntry(std::move(*additionalEntry));
726-
}
727-
catch (const std::exception& e)
728-
{}
729-
}
730-
}
731-
732-
return true;
733-
}

view/sharedcache/core/SharedCache.h

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,22 @@
77
#include "MachO.h"
88
#include "VirtualMemory.h"
99

10-
class SharedCache;
11-
1210
struct CacheSymbol
1311
{
1412
BNSymbolType type;
1513
uint64_t address;
1614
std::string name;
1715

1816
CacheSymbol() = default;
19-
2017
CacheSymbol(BNSymbolType type, uint64_t address, std::string name) :
2118
type(type), address(address), name(std::move(name))
2219
{}
23-
2420
~CacheSymbol() = default;
2521

2622
CacheSymbol(const CacheSymbol& other) = default;
27-
2823
CacheSymbol& operator=(const CacheSymbol& other) = default;
2924

3025
CacheSymbol(CacheSymbol&& other) noexcept = default;
31-
3226
CacheSymbol& operator=(CacheSymbol&& other) noexcept = default;
3327

3428
std::pair<std::string, BinaryNinja::Ref<BinaryNinja::Type>> DemangledName(BinaryNinja::BinaryView& view) const;
@@ -56,15 +50,12 @@ struct CacheRegion
5650
BNSegmentFlag flags;
5751

5852
CacheRegion() = default;
59-
6053
~CacheRegion() = default;
6154

6255
CacheRegion(const CacheRegion& other) = default;
63-
6456
CacheRegion& operator=(const CacheRegion& other) = default;
6557

6658
CacheRegion(CacheRegion&& other) noexcept = default;
67-
6859
CacheRegion& operator=(CacheRegion&& other) noexcept = default;
6960

7061
AddressRange AsAddressRange() const { return {start, start + size}; }
@@ -95,15 +86,12 @@ struct CacheImage
9586
std::shared_ptr<SharedCacheMachOHeader> header;
9687

9788
CacheImage() = default;
98-
9989
~CacheImage() = default;
10090

10191
CacheImage(const CacheImage& other) = default;
102-
10392
CacheImage& operator=(const CacheImage& other) = default;
10493

10594
CacheImage(CacheImage&& other) noexcept = default;
106-
10795
CacheImage& operator=(CacheImage&& other) noexcept = default;
10896

10997
// Get the file name from the path.
@@ -145,11 +133,9 @@ class CacheEntry
145133
std::vector<dyld_cache_mapping_info> mappings, std::vector<std::pair<std::string, dyld_cache_image_info>> images);
146134

147135
CacheEntry() = default;
148-
149136
CacheEntry(const CacheEntry&) = default;
150137

151138
CacheEntry(CacheEntry&&) = default;
152-
153139
CacheEntry& operator=(CacheEntry&&) = default;
154140

155141
// Construct a cache entry from the file on disk.
@@ -213,6 +199,7 @@ class SharedCache
213199
bool AddNonOverlappingRegion(CacheRegion region);
214200

215201
public:
202+
SharedCache() = default;
216203
explicit SharedCache(uint64_t addressSize);
217204

218205
SharedCache(const SharedCache &) = delete;
@@ -269,23 +256,3 @@ class SharedCache
269256

270257
std::optional<CacheSymbol> GetSymbolWithName(const std::string& name);
271258
};
272-
273-
// This constructs a Cache, give it a file path, and it will add all relevant cache entries.
274-
class CacheProcessor
275-
{
276-
BinaryNinja::Ref<BinaryNinja::BinaryView> m_view;
277-
BinaryNinja::Ref<BinaryNinja::Logger> m_logger;
278-
279-
public:
280-
explicit CacheProcessor(BinaryNinja::Ref<BinaryNinja::BinaryView> view);
281-
282-
// Construct a cache from the root file, this will parse the cache header and locate all
283-
// applicable cache entries and add them as well.
284-
bool ProcessCache(SharedCache& cache);
285-
286-
// Process a cache on the file system, this is for when not using a project.
287-
bool ProcessFileCache(SharedCache& cache);
288-
289-
// Process a cache using Binary Ninja's project system.
290-
bool ProcessProjectCache(SharedCache& cache);
291-
};

0 commit comments

Comments
 (0)