Skip to content

Commit 2e85527

Browse files
committed
[SharedCache] Add a named symbol map
Fixes #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.
1 parent 41450c3 commit 2e85527

File tree

7 files changed

+68
-26
lines changed

7 files changed

+68
-26
lines changed

view/sharedcache/api/python/sharedcache.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ def get_symbol_at(self, address: int) -> Optional[CacheSymbol]:
214214
sccore.BNSharedCacheFreeSymbol(api_symbol)
215215
return symbol
216216

217+
def get_symbol_with_name(self, name: str) -> Optional[CacheSymbol]:
218+
api_symbol = sccore.BNSharedCacheSymbol()
219+
if not sccore.BNSharedCacheControllerGetSymbolWithName(self.handle, name, api_symbol):
220+
return None
221+
symbol = symbol_from_api(api_symbol)
222+
sccore.BNSharedCacheFreeSymbol(api_symbol)
223+
return symbol
224+
217225
@property
218226
def regions(self) -> [CacheRegion]:
219227
count = ctypes.c_ulonglong()

view/sharedcache/core/SharedCache.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,10 @@ void SharedCache::AddSymbol(CacheSymbol symbol)
201201
m_symbols.insert({symbol.address, std::move(symbol)});
202202
}
203203

204-
void SharedCache::AddSymbols(std::vector<CacheSymbol> symbols)
204+
void SharedCache::AddSymbols(std::vector<CacheSymbol>&& symbols)
205205
{
206-
for (auto& symbol : symbols)
207-
m_symbols.insert({symbol.address, std::move(symbol)});
206+
for (auto&& symbol : symbols)
207+
m_symbols.emplace(symbol.address, std::move(symbol));
208208
}
209209

210210
CacheEntryId SharedCache::AddEntry(CacheEntry entry)
@@ -403,6 +403,14 @@ void SharedCache::ProcessEntrySlideInfo(const CacheEntry& entry)
403403
slideInfoProcessor.ProcessEntry(*m_vm, entry);
404404
}
405405

406+
void SharedCache::ProcessSymbols()
407+
{
408+
// Populate the named symbols from the regular symbols map.
409+
m_namedSymbols.reserve(m_symbols.size());
410+
for (const auto& [address, symbol] : m_symbols)
411+
m_namedSymbols.emplace(symbol.name, address);
412+
}
413+
406414
std::optional<CacheEntry> SharedCache::GetEntryContaining(const uint64_t address) const
407415
{
408416
for (const auto& [_, entry] : m_entries)
@@ -482,10 +490,10 @@ std::optional<CacheSymbol> SharedCache::GetSymbolAt(uint64_t address) const
482490

483491
std::optional<CacheSymbol> SharedCache::GetSymbolWithName(const std::string& name) const
484492
{
485-
for (const auto& [address, symbol] : m_symbols)
486-
if (symbol.name == name)
487-
return symbol;
488-
return std::nullopt;
493+
const auto it = m_namedSymbols.find(name);
494+
if (it == m_namedSymbols.end())
495+
return std::nullopt;
496+
return GetSymbolAt(it->second);
489497
}
490498

491499
CacheProcessor::CacheProcessor(Ref<BinaryView> view)

view/sharedcache/core/SharedCache.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,11 @@ class SharedCache
199199
AddressRangeMap<CacheRegion> m_regions {};
200200
// Describes the images of the cache.
201201
std::unordered_map<uint64_t, CacheImage> m_images {};
202-
// All the symbols for this cache. Both mapped and unmapped (not in the view).
202+
// All the external symbols for this cache. Both mapped and unmapped (not in the view).
203203
std::unordered_map<uint64_t, CacheSymbol> m_symbols {};
204+
// Quickly lookup a symbol by name, populated by `FinalizeSymbols`.
205+
// `m_namedSymbols` is modified in a worker thread spawned by view init so we must not get a symbol until its populated.
206+
std::unordered_map<std::string, uint64_t> m_namedSymbols {};
204207

205208
bool ProcessEntryImage(const std::string& path, const dyld_cache_image_info& info);
206209

@@ -211,12 +214,19 @@ class SharedCache
211214
public:
212215
explicit SharedCache(uint64_t addressSize);
213216

217+
SharedCache(const SharedCache &) = delete;
218+
SharedCache &operator=(const SharedCache &) = delete;
219+
220+
SharedCache(SharedCache &&) noexcept = default;
221+
SharedCache &operator=(SharedCache &&) noexcept = default;
222+
214223
uint64_t GetBaseAddress() const { return m_baseAddress; }
215224
std::shared_ptr<VirtualMemory> GetVirtualMemory() { return m_vm; }
216225
const std::unordered_map<CacheEntryId, CacheEntry>& GetEntries() const { return m_entries; }
217226
const AddressRangeMap<CacheRegion>& GetRegions() const { return m_regions; }
218227
const std::unordered_map<uint64_t, CacheImage>& GetImages() const { return m_images; }
219228
const std::unordered_map<uint64_t, CacheSymbol>& GetSymbols() const { return m_symbols; }
229+
const std::unordered_map<std::string, uint64_t>& GetNamedSymbols() const { return m_namedSymbols; }
220230

221231
void AddImage(CacheImage image);
222232

@@ -225,7 +235,7 @@ class SharedCache
225235

226236
void AddSymbol(CacheSymbol symbol);
227237

228-
void AddSymbols(std::vector<CacheSymbol> symbols);
238+
void AddSymbols(std::vector<CacheSymbol>&& symbols);
229239

230240
// Adds the cache entry and populates the virtual memory using the mapping information.
231241
// After being added the entry is read only, there is nothing that can modify it.
@@ -237,6 +247,9 @@ class SharedCache
237247

238248
void ProcessEntrySlideInfo(const CacheEntry& entry);
239249

250+
// Construct the named symbols lookup map for use with `GetSymbolWithName`.
251+
void ProcessSymbols();
252+
240253
std::optional<CacheEntry> GetEntryContaining(uint64_t address) const;
241254

242255
std::optional<CacheEntry> GetEntryWithImage(const CacheImage& image) const;

view/sharedcache/core/SharedCacheController.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void RegisterSharedCacheControllerDestructor()
5454
BNRegisterObjectDestructionCallbacks(&callbacks);
5555
}
5656

57-
SharedCacheController::SharedCacheController(SharedCache cache, Ref<Logger> logger) : m_cache(std::move(cache))
57+
SharedCacheController::SharedCacheController(SharedCache&& cache, Ref<Logger> logger) : m_cache(std::move(cache))
5858
{
5959
INIT_DSC_API_OBJECT();
6060
m_logger = std::move(logger);
@@ -65,7 +65,7 @@ SharedCacheController::SharedCacheController(SharedCache cache, Ref<Logger> logg
6565
m_regionFilter = std::regex(".*LINKEDIT.*");
6666
}
6767

68-
DSCRef<SharedCacheController> SharedCacheController::Initialize(BinaryView& view, SharedCache cache)
68+
DSCRef<SharedCacheController> SharedCacheController::Initialize(BinaryView& view, SharedCache&& cache)
6969
{
7070
auto id = GetViewIdFromView(view);
7171
std::unique_lock<std::shared_mutex> lock(GlobalControllersMutex);

view/sharedcache/core/SharedCacheController.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ namespace BinaryNinja::DSC {
3535
bool m_processObjC;
3636
bool m_processCFStrings;
3737

38-
explicit SharedCacheController(SharedCache cache, Ref<Logger> logger);
38+
explicit SharedCacheController(SharedCache&& cache, Ref<Logger> logger);
3939

4040
public:
4141
// Initialize the DSCacheView, this should be called from the view initialize function only!
42-
static DSCRef<SharedCacheController> Initialize(BinaryView& view, SharedCache cache);
42+
static DSCRef<SharedCacheController> Initialize(BinaryView& view, SharedCache&& cache);
4343

4444
// NOTE: This will not create one if it does not exist. To create one for the view call `Initialize`.
4545
static DSCRef<SharedCacheController> FromView(BinaryView& view);

view/sharedcache/core/SharedCacheView.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,19 @@ bool SharedCacheView::Init()
842842

843843
auto cacheController = SharedCacheController::Initialize(*this, std::move(sharedCache));
844844

845+
{
846+
// Load up all the symbols into the named symbols lookup map.
847+
// NOTE: We do this on a separate thread as image & region loading does not consult this.
848+
WorkerPriorityEnqueue([logger, cacheController]() {
849+
auto& sharedCache = cacheController->GetCache();
850+
auto startTime = std::chrono::high_resolution_clock::now();
851+
sharedCache.ProcessSymbols();
852+
auto endTime = std::chrono::high_resolution_clock::now();
853+
std::chrono::duration<double> elapsed = endTime - startTime;
854+
logger->LogInfo("Processing %zu symbols took %.3f seconds (separate thread)", sharedCache.GetSymbols().size(), elapsed.count());
855+
});
856+
}
857+
845858
// Users can adjust which images are loaded by default using the `loader.dsc.autoLoadPattern` setting.
846859
std::string autoLoadPattern = ".*libsystem_c.dylib";
847860
if (settings && settings->Contains("loader.dsc.autoLoadPattern"))

view/sharedcache/core/ffi.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ extern "C"
195195
bool BNSharedCacheControllerGetRegionAt(
196196
BNSharedCacheController* controller, uint64_t address, BNSharedCacheRegion* outRegion)
197197
{
198-
auto region = controller->object->GetCache().GetRegionAt(address);
198+
const auto region = controller->object->GetCache().GetRegionAt(address);
199199
if (!region)
200200
return false;
201201
*outRegion = RegionToApi(*region);
@@ -205,7 +205,7 @@ extern "C"
205205
bool BNSharedCacheControllerGetRegionContaining(
206206
BNSharedCacheController* controller, uint64_t address, BNSharedCacheRegion* outRegion)
207207
{
208-
auto region = controller->object->GetCache().GetRegionContaining(address);
208+
const auto region = controller->object->GetCache().GetRegionContaining(address);
209209
if (!region)
210210
return false;
211211
*outRegion = RegionToApi(*region);
@@ -214,7 +214,7 @@ extern "C"
214214

215215
BNSharedCacheRegion* BNSharedCacheControllerGetRegions(BNSharedCacheController* controller, size_t* count)
216216
{
217-
auto regions = controller->object->GetCache().GetRegions();
217+
const auto& regions = controller->object->GetCache().GetRegions();
218218
*count = regions.size();
219219
BNSharedCacheRegion* apiRegions = new BNSharedCacheRegion[*count];
220220
int idx = 0;
@@ -225,7 +225,7 @@ extern "C"
225225

226226
BNSharedCacheRegion* BNSharedCacheControllerGetLoadedRegions(BNSharedCacheController* controller, size_t* count)
227227
{
228-
auto loadedRegionStarts = controller->object->GetLoadedRegions();
228+
const auto& loadedRegionStarts = controller->object->GetLoadedRegions();
229229

230230
// TODO: This translation should likely exist in the core cache controller class?
231231
std::vector<CacheRegion> loadedRegions;
@@ -271,7 +271,7 @@ extern "C"
271271
bool BNSharedCacheControllerGetImageAt(
272272
BNSharedCacheController* controller, uint64_t address, BNSharedCacheImage* outImage)
273273
{
274-
auto image = controller->object->GetCache().GetImageAt(address);
274+
const auto image = controller->object->GetCache().GetImageAt(address);
275275
if (!image)
276276
return false;
277277
*outImage = ImageToApi(*image);
@@ -281,7 +281,7 @@ extern "C"
281281
bool BNSharedCacheControllerGetImageContaining(
282282
BNSharedCacheController* controller, uint64_t address, BNSharedCacheImage* outImage)
283283
{
284-
auto image = controller->object->GetCache().GetImageContaining(address);
284+
const auto image = controller->object->GetCache().GetImageContaining(address);
285285
if (!image)
286286
return false;
287287
*outImage = ImageToApi(*image);
@@ -291,7 +291,7 @@ extern "C"
291291
bool BNSharedCacheControllerGetImageWithName(
292292
BNSharedCacheController* controller, const char* name, BNSharedCacheImage* outImage)
293293
{
294-
auto image = controller->object->GetCache().GetImageWithName(name);
294+
const auto image = controller->object->GetCache().GetImageWithName(name);
295295
if (!image)
296296
return false;
297297
*outImage = ImageToApi(*image);
@@ -317,7 +317,7 @@ extern "C"
317317

318318
BNSharedCacheImage* BNSharedCacheControllerGetImages(BNSharedCacheController* controller, size_t* count)
319319
{
320-
auto images = controller->object->GetCache().GetImages();
320+
const auto& images = controller->object->GetCache().GetImages();
321321
*count = images.size();
322322
BNSharedCacheImage* apiImages = new BNSharedCacheImage[*count];
323323
size_t idx = 0;
@@ -328,7 +328,7 @@ extern "C"
328328

329329
BNSharedCacheImage* BNSharedCacheControllerGetLoadedImages(BNSharedCacheController* controller, size_t* count)
330330
{
331-
auto loadedImageStarts = controller->object->GetLoadedImages();
331+
const auto& loadedImageStarts = controller->object->GetLoadedImages();
332332

333333
// TODO: This translation should likely exist in the core cache controller class?
334334
std::vector<CacheImage> loadedImages;
@@ -362,7 +362,7 @@ extern "C"
362362
bool BNSharedCacheControllerGetSymbolAt(
363363
BNSharedCacheController* controller, uint64_t address, BNSharedCacheSymbol* outSymbol)
364364
{
365-
auto symbol = controller->object->GetCache().GetSymbolAt(address);
365+
const auto symbol = controller->object->GetCache().GetSymbolAt(address);
366366
if (!symbol)
367367
return false;
368368
*outSymbol = SymbolToApi(*symbol);
@@ -372,7 +372,7 @@ extern "C"
372372
bool BNSharedCacheControllerGetSymbolWithName(
373373
BNSharedCacheController* controller, const char* name, BNSharedCacheSymbol* outSymbol)
374374
{
375-
auto symbol = controller->object->GetCache().GetSymbolWithName(name);
375+
const auto symbol = controller->object->GetCache().GetSymbolWithName(name);
376376
if (!symbol)
377377
return false;
378378
*outSymbol = SymbolToApi(*symbol);
@@ -381,7 +381,7 @@ extern "C"
381381

382382
BNSharedCacheSymbol* BNSharedCacheControllerGetSymbols(BNSharedCacheController* controller, size_t* count)
383383
{
384-
auto symbols = controller->object->GetCache().GetSymbols();
384+
const auto& symbols = controller->object->GetCache().GetSymbols();
385385
*count = symbols.size();
386386
BNSharedCacheSymbol* apiSymbols = new BNSharedCacheSymbol[*count];
387387
size_t idx = 0;
@@ -405,7 +405,7 @@ extern "C"
405405

406406
BNSharedCacheEntry* BNSharedCacheControllerGetEntries(BNSharedCacheController* controller, size_t* count)
407407
{
408-
auto entries = controller->object->GetCache().GetEntries();
408+
const auto& entries = controller->object->GetCache().GetEntries();
409409
*count = entries.size();
410410
BNSharedCacheEntry* apiEntries = new BNSharedCacheEntry[*count];
411411
size_t idx = 0;

0 commit comments

Comments
 (0)