summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-04-04 23:45:06 -0400
committerMason Reed <mason@vector35.com>2025-04-06 20:00:37 -0400
commit2e855275732aed00486ca100f4151f4074d9949e (patch)
tree24916cf3ffe405706c18d34808d26ef2de893139
parent41450c3df2be33b817c864fb71ca38d76d81d2ab (diff)
[SharedCache] Add a named symbol map
Fixes https://github.com/Vector35/binaryninja-api/issues/6561 - Also tightens the SharedCache class to move only, to prevent accidental copies. - Also removes some extra copies in FFI when should pass by ref - Also adds `get_symbol_with_name` to python API The current named symbol map is populated in a worker thread spawned in the view init. This is because populating the map can take about 1 second. If we are fine with another 1 second added to the view init time then we can add it serially but I don't think this way is _that_ bad, no analysis consults this, however a user might add a workflow that would be racing this. So we need to add a mutex.
-rw-r--r--view/sharedcache/api/python/sharedcache.py8
-rw-r--r--view/sharedcache/core/SharedCache.cpp22
-rw-r--r--view/sharedcache/core/SharedCache.h17
-rw-r--r--view/sharedcache/core/SharedCacheController.cpp4
-rw-r--r--view/sharedcache/core/SharedCacheController.h4
-rw-r--r--view/sharedcache/core/SharedCacheView.cpp13
-rw-r--r--view/sharedcache/core/ffi.cpp26
7 files changed, 68 insertions, 26 deletions
diff --git a/view/sharedcache/api/python/sharedcache.py b/view/sharedcache/api/python/sharedcache.py
index e250b95b..dff9bcc3 100644
--- a/view/sharedcache/api/python/sharedcache.py
+++ b/view/sharedcache/api/python/sharedcache.py
@@ -214,6 +214,14 @@ class SharedCacheController:
sccore.BNSharedCacheFreeSymbol(api_symbol)
return symbol
+ def get_symbol_with_name(self, name: str) -> Optional[CacheSymbol]:
+ api_symbol = sccore.BNSharedCacheSymbol()
+ if not sccore.BNSharedCacheControllerGetSymbolWithName(self.handle, name, api_symbol):
+ return None
+ symbol = symbol_from_api(api_symbol)
+ sccore.BNSharedCacheFreeSymbol(api_symbol)
+ return symbol
+
@property
def regions(self) -> [CacheRegion]:
count = ctypes.c_ulonglong()
diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp
index 65d5aec1..9bbe51e4 100644
--- a/view/sharedcache/core/SharedCache.cpp
+++ b/view/sharedcache/core/SharedCache.cpp
@@ -201,10 +201,10 @@ void SharedCache::AddSymbol(CacheSymbol symbol)
m_symbols.insert({symbol.address, std::move(symbol)});
}
-void SharedCache::AddSymbols(std::vector<CacheSymbol> symbols)
+void SharedCache::AddSymbols(std::vector<CacheSymbol>&& symbols)
{
- for (auto& symbol : symbols)
- m_symbols.insert({symbol.address, std::move(symbol)});
+ for (auto&& symbol : symbols)
+ m_symbols.emplace(symbol.address, std::move(symbol));
}
CacheEntryId SharedCache::AddEntry(CacheEntry entry)
@@ -403,6 +403,14 @@ void SharedCache::ProcessEntrySlideInfo(const CacheEntry& entry)
slideInfoProcessor.ProcessEntry(*m_vm, entry);
}
+void SharedCache::ProcessSymbols()
+{
+ // Populate the named symbols from the regular symbols map.
+ m_namedSymbols.reserve(m_symbols.size());
+ for (const auto& [address, symbol] : m_symbols)
+ m_namedSymbols.emplace(symbol.name, address);
+}
+
std::optional<CacheEntry> SharedCache::GetEntryContaining(const uint64_t address) const
{
for (const auto& [_, entry] : m_entries)
@@ -482,10 +490,10 @@ std::optional<CacheSymbol> SharedCache::GetSymbolAt(uint64_t address) const
std::optional<CacheSymbol> SharedCache::GetSymbolWithName(const std::string& name) const
{
- for (const auto& [address, symbol] : m_symbols)
- if (symbol.name == name)
- return symbol;
- return std::nullopt;
+ const auto it = m_namedSymbols.find(name);
+ if (it == m_namedSymbols.end())
+ return std::nullopt;
+ return GetSymbolAt(it->second);
}
CacheProcessor::CacheProcessor(Ref<BinaryView> view)
diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h
index 674e00c3..3b9e9fe1 100644
--- a/view/sharedcache/core/SharedCache.h
+++ b/view/sharedcache/core/SharedCache.h
@@ -199,8 +199,11 @@ class SharedCache
AddressRangeMap<CacheRegion> m_regions {};
// Describes the images of the cache.
std::unordered_map<uint64_t, CacheImage> m_images {};
- // All the symbols for this cache. Both mapped and unmapped (not in the view).
+ // All the external symbols for this cache. Both mapped and unmapped (not in the view).
std::unordered_map<uint64_t, CacheSymbol> m_symbols {};
+ // Quickly lookup a symbol by name, populated by `FinalizeSymbols`.
+ // `m_namedSymbols` is modified in a worker thread spawned by view init so we must not get a symbol until its populated.
+ std::unordered_map<std::string, uint64_t> m_namedSymbols {};
bool ProcessEntryImage(const std::string& path, const dyld_cache_image_info& info);
@@ -211,12 +214,19 @@ class SharedCache
public:
explicit SharedCache(uint64_t addressSize);
+ SharedCache(const SharedCache &) = delete;
+ SharedCache &operator=(const SharedCache &) = delete;
+
+ SharedCache(SharedCache &&) noexcept = default;
+ SharedCache &operator=(SharedCache &&) noexcept = default;
+
uint64_t GetBaseAddress() const { return m_baseAddress; }
std::shared_ptr<VirtualMemory> GetVirtualMemory() { return m_vm; }
const std::unordered_map<CacheEntryId, CacheEntry>& GetEntries() const { return m_entries; }
const AddressRangeMap<CacheRegion>& GetRegions() const { return m_regions; }
const std::unordered_map<uint64_t, CacheImage>& GetImages() const { return m_images; }
const std::unordered_map<uint64_t, CacheSymbol>& GetSymbols() const { return m_symbols; }
+ const std::unordered_map<std::string, uint64_t>& GetNamedSymbols() const { return m_namedSymbols; }
void AddImage(CacheImage image);
@@ -225,7 +235,7 @@ public:
void AddSymbol(CacheSymbol symbol);
- void AddSymbols(std::vector<CacheSymbol> symbols);
+ void AddSymbols(std::vector<CacheSymbol>&& symbols);
// Adds the cache entry and populates the virtual memory using the mapping information.
// After being added the entry is read only, there is nothing that can modify it.
@@ -237,6 +247,9 @@ public:
void ProcessEntrySlideInfo(const CacheEntry& entry);
+ // Construct the named symbols lookup map for use with `GetSymbolWithName`.
+ void ProcessSymbols();
+
std::optional<CacheEntry> GetEntryContaining(uint64_t address) const;
std::optional<CacheEntry> GetEntryWithImage(const CacheImage& image) const;
diff --git a/view/sharedcache/core/SharedCacheController.cpp b/view/sharedcache/core/SharedCacheController.cpp
index 2dabefb5..678f4b59 100644
--- a/view/sharedcache/core/SharedCacheController.cpp
+++ b/view/sharedcache/core/SharedCacheController.cpp
@@ -54,7 +54,7 @@ void RegisterSharedCacheControllerDestructor()
BNRegisterObjectDestructionCallbacks(&callbacks);
}
-SharedCacheController::SharedCacheController(SharedCache cache, Ref<Logger> logger) : m_cache(std::move(cache))
+SharedCacheController::SharedCacheController(SharedCache&& cache, Ref<Logger> logger) : m_cache(std::move(cache))
{
INIT_DSC_API_OBJECT();
m_logger = std::move(logger);
@@ -65,7 +65,7 @@ SharedCacheController::SharedCacheController(SharedCache cache, Ref<Logger> logg
m_regionFilter = std::regex(".*LINKEDIT.*");
}
-DSCRef<SharedCacheController> SharedCacheController::Initialize(BinaryView& view, SharedCache cache)
+DSCRef<SharedCacheController> SharedCacheController::Initialize(BinaryView& view, SharedCache&& cache)
{
auto id = GetViewIdFromView(view);
std::unique_lock<std::shared_mutex> lock(GlobalControllersMutex);
diff --git a/view/sharedcache/core/SharedCacheController.h b/view/sharedcache/core/SharedCacheController.h
index caf61e41..3a6c76dc 100644
--- a/view/sharedcache/core/SharedCacheController.h
+++ b/view/sharedcache/core/SharedCacheController.h
@@ -35,11 +35,11 @@ namespace BinaryNinja::DSC {
bool m_processObjC;
bool m_processCFStrings;
- explicit SharedCacheController(SharedCache cache, Ref<Logger> logger);
+ explicit SharedCacheController(SharedCache&& cache, Ref<Logger> logger);
public:
// Initialize the DSCacheView, this should be called from the view initialize function only!
- static DSCRef<SharedCacheController> Initialize(BinaryView& view, SharedCache cache);
+ static DSCRef<SharedCacheController> Initialize(BinaryView& view, SharedCache&& cache);
// NOTE: This will not create one if it does not exist. To create one for the view call `Initialize`.
static DSCRef<SharedCacheController> FromView(BinaryView& view);
diff --git a/view/sharedcache/core/SharedCacheView.cpp b/view/sharedcache/core/SharedCacheView.cpp
index d07d8614..a3e494e2 100644
--- a/view/sharedcache/core/SharedCacheView.cpp
+++ b/view/sharedcache/core/SharedCacheView.cpp
@@ -842,6 +842,19 @@ bool SharedCacheView::Init()
auto cacheController = SharedCacheController::Initialize(*this, std::move(sharedCache));
+ {
+ // 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]() {
+ auto& sharedCache = cacheController->GetCache();
+ auto startTime = std::chrono::high_resolution_clock::now();
+ sharedCache.ProcessSymbols();
+ auto endTime = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double> elapsed = endTime - startTime;
+ logger->LogInfo("Processing %zu symbols took %.3f seconds (separate thread)", sharedCache.GetSymbols().size(), elapsed.count());
+ });
+ }
+
// 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"))
diff --git a/view/sharedcache/core/ffi.cpp b/view/sharedcache/core/ffi.cpp
index 0b37196f..e4e92acd 100644
--- a/view/sharedcache/core/ffi.cpp
+++ b/view/sharedcache/core/ffi.cpp
@@ -195,7 +195,7 @@ extern "C"
bool BNSharedCacheControllerGetRegionAt(
BNSharedCacheController* controller, uint64_t address, BNSharedCacheRegion* outRegion)
{
- auto region = controller->object->GetCache().GetRegionAt(address);
+ const auto region = controller->object->GetCache().GetRegionAt(address);
if (!region)
return false;
*outRegion = RegionToApi(*region);
@@ -205,7 +205,7 @@ extern "C"
bool BNSharedCacheControllerGetRegionContaining(
BNSharedCacheController* controller, uint64_t address, BNSharedCacheRegion* outRegion)
{
- auto region = controller->object->GetCache().GetRegionContaining(address);
+ const auto region = controller->object->GetCache().GetRegionContaining(address);
if (!region)
return false;
*outRegion = RegionToApi(*region);
@@ -214,7 +214,7 @@ extern "C"
BNSharedCacheRegion* BNSharedCacheControllerGetRegions(BNSharedCacheController* controller, size_t* count)
{
- auto regions = controller->object->GetCache().GetRegions();
+ const auto& regions = controller->object->GetCache().GetRegions();
*count = regions.size();
BNSharedCacheRegion* apiRegions = new BNSharedCacheRegion[*count];
int idx = 0;
@@ -225,7 +225,7 @@ extern "C"
BNSharedCacheRegion* BNSharedCacheControllerGetLoadedRegions(BNSharedCacheController* controller, size_t* count)
{
- auto loadedRegionStarts = controller->object->GetLoadedRegions();
+ const auto& loadedRegionStarts = controller->object->GetLoadedRegions();
// TODO: This translation should likely exist in the core cache controller class?
std::vector<CacheRegion> loadedRegions;
@@ -271,7 +271,7 @@ extern "C"
bool BNSharedCacheControllerGetImageAt(
BNSharedCacheController* controller, uint64_t address, BNSharedCacheImage* outImage)
{
- auto image = controller->object->GetCache().GetImageAt(address);
+ const auto image = controller->object->GetCache().GetImageAt(address);
if (!image)
return false;
*outImage = ImageToApi(*image);
@@ -281,7 +281,7 @@ extern "C"
bool BNSharedCacheControllerGetImageContaining(
BNSharedCacheController* controller, uint64_t address, BNSharedCacheImage* outImage)
{
- auto image = controller->object->GetCache().GetImageContaining(address);
+ const auto image = controller->object->GetCache().GetImageContaining(address);
if (!image)
return false;
*outImage = ImageToApi(*image);
@@ -291,7 +291,7 @@ extern "C"
bool BNSharedCacheControllerGetImageWithName(
BNSharedCacheController* controller, const char* name, BNSharedCacheImage* outImage)
{
- auto image = controller->object->GetCache().GetImageWithName(name);
+ const auto image = controller->object->GetCache().GetImageWithName(name);
if (!image)
return false;
*outImage = ImageToApi(*image);
@@ -317,7 +317,7 @@ extern "C"
BNSharedCacheImage* BNSharedCacheControllerGetImages(BNSharedCacheController* controller, size_t* count)
{
- auto images = controller->object->GetCache().GetImages();
+ const auto& images = controller->object->GetCache().GetImages();
*count = images.size();
BNSharedCacheImage* apiImages = new BNSharedCacheImage[*count];
size_t idx = 0;
@@ -328,7 +328,7 @@ extern "C"
BNSharedCacheImage* BNSharedCacheControllerGetLoadedImages(BNSharedCacheController* controller, size_t* count)
{
- auto loadedImageStarts = controller->object->GetLoadedImages();
+ const auto& loadedImageStarts = controller->object->GetLoadedImages();
// TODO: This translation should likely exist in the core cache controller class?
std::vector<CacheImage> loadedImages;
@@ -362,7 +362,7 @@ extern "C"
bool BNSharedCacheControllerGetSymbolAt(
BNSharedCacheController* controller, uint64_t address, BNSharedCacheSymbol* outSymbol)
{
- auto symbol = controller->object->GetCache().GetSymbolAt(address);
+ const auto symbol = controller->object->GetCache().GetSymbolAt(address);
if (!symbol)
return false;
*outSymbol = SymbolToApi(*symbol);
@@ -372,7 +372,7 @@ extern "C"
bool BNSharedCacheControllerGetSymbolWithName(
BNSharedCacheController* controller, const char* name, BNSharedCacheSymbol* outSymbol)
{
- auto symbol = controller->object->GetCache().GetSymbolWithName(name);
+ const auto symbol = controller->object->GetCache().GetSymbolWithName(name);
if (!symbol)
return false;
*outSymbol = SymbolToApi(*symbol);
@@ -381,7 +381,7 @@ extern "C"
BNSharedCacheSymbol* BNSharedCacheControllerGetSymbols(BNSharedCacheController* controller, size_t* count)
{
- auto symbols = controller->object->GetCache().GetSymbols();
+ const auto& symbols = controller->object->GetCache().GetSymbols();
*count = symbols.size();
BNSharedCacheSymbol* apiSymbols = new BNSharedCacheSymbol[*count];
size_t idx = 0;
@@ -405,7 +405,7 @@ extern "C"
BNSharedCacheEntry* BNSharedCacheControllerGetEntries(BNSharedCacheController* controller, size_t* count)
{
- auto entries = controller->object->GetCache().GetEntries();
+ const auto& entries = controller->object->GetCache().GetEntries();
*count = entries.size();
BNSharedCacheEntry* apiEntries = new BNSharedCacheEntry[*count];
size_t idx = 0;