summaryrefslogtreecommitdiff
path: root/view/sharedcache/core/SharedCache.cpp
AgeCommit message (Collapse)Author
2025-04-02[SharedCache] Fix some bugsMason Reed
2025-04-02[SharedCache] Fix some issues with project loadingMason Reed
2025-04-02[SharedCache] Refactor Shared CacheMason Reed
In absence of a better name, this commit refactors the shared cache code.
2025-04-02[SharedCache] Use DefineAutoUserSymbol instead of DefineAutoSymbolMason Reed
This makes it so that we persist the symbols within the regular symbol list in storage
2025-04-02[SharedCache] Make error about invalid metadata a warning, we recover sorta ↵Mason Reed
gracefully now
2025-04-02[SharedCache] Optimize parsing of slide infoMark Rowe
Slide info is parsed and applied on the main thread, below the `SharedCache` constructor, when a shared cache is opened. Time spent applying the slide is time that the main thread is blocked. These changes eliminate some unnecessary overhead so what work remains is dominated by kernel work (paging in data, copying pages when the first modification is made). The changes are: 1. Read slide pointers via `ReadULong` rather than the variable-length `Read` method. The compiler isn't able to eliminate the call to `memcpy` in the variable-length `Read` function, and the function call overhead is noticeable given the small size of the read and number of times it is called. 2. Remove the file member from `MappingInfo`. Slide info is applied for a single file at a time so there's no reason to track the file it belongs to. This removes unnecesssary reference counting on the `std::shared_ptr` that holds the `MMappedFileAccessor`.
2025-04-02[SharedCache] Stop crashing if we don't have a valid dyld in PerformInitialLoadMason Reed
Still need to decide on how we want handle this, because we need multiple files we probably just want to prompt the user for a directory?
2025-04-02[SharedCache] Remove warning which is unlikely to be usefulMason Reed
This warning will happen anytime you load an image with already processed regions.
2025-04-02[ObjC] Create a shared ObjC processor for Macho and DSC viewsWeiN76LQh
Both the Macho and DSC views need to process Objective-C but have separate processor classes. It would appear that the DSC version was largely a copy and paste of the Macho view one, with some modifications. The majority of code overlaps between the 2 so it doesn't make sense to maintain 2 and copy and paste improvements/fixes between them. This commit fixes that by creating a base Objective-C processor that contains the shared code. View specific code is implemented in the respective subclasses for the views. Although there is very little view specific code for each.
2025-04-02[SharedCache] Don't serialize state twiceGlenn Smith
2025-04-02[SharedCache] Make it faster to look up which image contains an addressMark Rowe
`MemoryRegion` now tracks the start address of the image to which it belongs. `SharedCache::HeaderForAddress` uses the existing address range map to quickly find the `MemoryRegion` for a given address, and from there can look up the image via its start address. Some extra logic is added to `CacheInfo::Load` to populate the image start address on `MemoryRegion` when loading from metadata that predates this change. The result is that `HeaderForAddress` is no longer the most expensive part of `FindSymbolAtAddrAndApplyToAddr`.
2025-04-02[SharedCache] Demangle relevant symbolsMason Reed
Use the LLVM demangler to demangle relevant symbols
2025-04-02[SharedCache] Remove some dead code and misc lintsMason Reed
2025-04-02[SharedCache] Don't crash if we have a different metadata versionMason Reed
Also we now initialize m_cacheInfo when calling `DeserializeFromRawView` the responsibility is on the caller to handle
2025-04-02[SharedCache] Warn when loading BNDB with different shared cache metadata ↵Mason Reed
version This does not actually give the user control to exit early, that looks to need core changes.
2025-03-18[SharedCache] Limit sections being added in `SharedCache::InitializeHeader` ↵WeiN76LQh
to the regions being loaded The logic before would default to adding a section unless it was from a region in the `regionsToLoad` argument and its header had already been initialized. However if a user does `Load Section by Address` then there's only one region in `regionsToLoad`. All sections not in that region would be automatically added, even if they had already been or the user hadn't requested them to be loaded.
2025-02-26[SharedCache] Fix loading DSC BNDB from a local project crashingGlenn Smith
Also fixes a BinaryViewRef leak
2025-02-19[SharedCache] Don't capture `this` within lambda passed to ↵Mark Rowe
MMappedFileAccessor::Open Nothing guarantees that the `SharedCache` lives long enough.
2025-02-18[SharedCache] Fix un-updated symbols listMason Reed
There was a second BeginBulkModifySymbols after rebasing the new processing symbols commit
2025-02-17[SharedCache] Fix building on windows due to unresolved extern symbolsMason Reed
This is _probably_ the fix? I am not actually sure I am pushing so the CI can figure it out for me :)
2025-02-17[SharedCache] Split out symbol processing into own functionMason Reed
Split out from https://github.com/WeiN76LQh/binaryninja-api/tree/process-local-symbols
2025-02-17[SharedCache] Define BackingCacheTypeWeiN76LQh
Split out from https://github.com/WeiN76LQh/binaryninja-api/tree/process-local-symbols
2025-02-17[SharedCache] Split state into initial, loaded, and modifiedMark Rowe
The initial state is initialized during `PerformInitialLoad` and is immutable after that point. This required some slight restructuring of how information about memory regions is tracked as that was previously modified as regions were loaded. Memory regions are now stored in a map from their address range to the `MemoryRegion` object. This makes it cheap to look them up by address which is a common operation. The modified state consists of changes since the last save to the `DSCView` / `ViewSpecificState`. This means it is no longer necessary to copy any state when mutating a `SharedCache` instance for the first time. Instead, its data structures start off empty and are populated as images, sections, or symbol information is loaded. The loaded state consists of all modified state that has since been saved. It lives on the `ViewSpecificState`. Saving modified state merges it into the the existing loaded state. This pattern is carried over to the `Metadata` stored on the `DSCView`. The initial state is stored under its own metadata key, and each modified state is stored under a key with an incrementing number. This means each save of the state only needs to serialize the state that changed, rather than reserializing all of the state all of the time. There are two huge benefits from these changes: 1. At no point does `SharedCache` have to copy its in memory state. The basic copy-on-write approach introduced in #6129 reduced how often these copies are made, but they're still frequent and very expensive. 1. At no point does `SharedCache` have to re-serialize state to JSON that it has already serialized. JSON serialization previously added hundreds of milliseconds to any mutating operation on `SharedCache`. As a result, this speeds up the initial load of the shared cache by around 2x and loading of subsequent images improves by about the same. One trade-off is that the serialization / deserialization logic is more complicated. There are two reasons for this: 1. The state is now split across multiple metadata keys and needs to be merged when it is loaded. 2. The in-memory representation uses pointers to identify memory regions. These relationships have to be re-established after the JSON is deserialized. As a future direction it is worth considering whether the logic owned by `SharedCache` could be split in a similar manner to the data. The initial loading of the cache header, loading of images, and handling of symbol information are all mostly independent and work on separate data. If the logic were split into separate classes it would be easier to reason about which data is valid when, and would easily permit concurrent loading of multiple images from the shared library in a thread-safe manner.
2025-02-13[SharedCache] Use StringRef for performanceGlenn Smith
2025-02-12[SharedCache] Remove designator initializationMason Reed
Fixes MSVC C++17 failing to compile, hopefully.
2025-02-12[SharedCache] Fix misc compiler warningsMason Reed
These are from clang so there are still a bunch of warnings on GCC and MSVC
2025-02-12[SharedCache] Always initialize logger firstMason Reed
Potential nullptr deref on invalid view type (see the error log right after the logger init)
2025-02-12[SharedCache] Appease formatting rulezMason Reed
This is a meaningless commit, i'm throwing this in for when someone inevitably calls out the inconsistent "formatting" (what style is this again?)
2025-02-12[SharedCache] Fix performance when loading images from iOS 14 shared cachesMark Rowe
A logic error in `FindSymbolAtAddrAndApplyToAddr` meant that a code path that would attempt to re-use existing symbol definitions in the view would still fall back to searching the exported symbol list. `FindSymbolAtAddrAndApplyToAddr` is updated to return early if a symbol already exists at the target location and the target location is the same as the symbol location. Additionally, in the event a symbol was found in the view at the symbol location, it is applied to the target location and then the function returns. Additionally, `WillMutateState` is moved after these initial checks so it is only called in the codepaths that attempt to locate the symbol amongst the exported symbols. After these changes it is now possible to load QuartzCore from the iOS 14 shared cache in around 70 seconds. Previously it would run for hours without the load completing.
2025-02-12[SharedCache] Don't crash if we open a file with slide version 1Mason Reed
Also add some other misc error reporting and nullptr check
2025-02-12Add Symbol constructor for pre-allocated core namespaceMason Reed
This allows a consumer to bypass the alloc + free of the API NameSpace, removing the need for the consumer to manually construct a symbol for said behavior.
2025-02-12[SharedCache] Rework how file accessors are handledMark Rowe
Previously, `MMappedFileAccessor::Open` attempted to impose a fixed limit on the number of file accessors that were live at one time. This was done because the default file descriptor limit on some platforms is relatively low (256 on macOS). Given that recent iOS shared caches can contain 60+ files it is easy to tie up a large percentage of this limit by opening one or two shared caches. This is problematic as if the limit imposed by `MMappedFileAccessor::Open` is reached, the attempt to access the file will block waiting for another file accessor to be closed. This can lead to a deadlock. This commit makes three changes to this strategy: 1. It attempts to raise the file descriptor limit to 1024. Unix systems support both soft and hard file descriptor limits. The soft file descriptor limit is what is enforced, but a process can explicitly raise the limit to any value below the hard limit if it wishes. 2. The fixed limit on open files accessors is changed to a soft limit. `FileAccessorCache` is introduced to manage the caching of file accessors. It provides a basic LRU cache. Whenever a new file is opened, the cache of open accessors is pruned to stay below the target limit (50% of the soft file descriptor limit). This limiting is primarily done to allow files containing dirty pages to be unmapped if they're no longer being used. LRU isn't the optimal strategy for this, but it is simple to implement and understand. Ideally there'd be some access time component to the cache so files that haven't been accessed can be released. 3. File accessors are cached even for sessions corresponding to views that have been closed. The "Open Selection with Options..." context menu hits this case as a view is created and destroyed as part of populating the options dialog, and the real view is then created in the same session. The most significant benefit of this change is that the logic around opening / closing file accessors is simpler and can no longer deadlock. While working on this I noticed that `SharedCache` opened some files via `MMappedFileAccessor::Open` without specifying a post-open operation to apply slide information. Instead, it would explicitly apply the slide information after opening the file. This works fine so long as the file accessor limit is never hit. If it is hit and the accessor is closed, the next time the file is opened it will not have any slide information applied. This would lead to very confusing bugs.
2025-02-12[SharedCache] Make MetadataSerializable::Load staticMark Rowe
`MetadataSerializable` is updated to allow a subclass to optionally specify the return type of `Load`. If not specified it defaults to the subclass itself. `SharedCache` specifies `std::optional` to represent the case where the serialized metadata is in a format or version it does not recognize. By making `Load` a static member function and having it return a new object it becomes easier to reason about the state of objects when a deserialization failure occurs. It can return `std::optional` to indicate failure and there is no object left in an unknown state. Prior to this, `Load` worked on an existing instance of an object. It was unclear what state the object would be in if a deserialization failure occurred (its original state prior to `Load`? some partially-loaded state? a null state?). The failure itself also had to be communicated out of band.
2025-02-10[SharedCache] Optimizations regarding stl container usage in SharedCache.cppkat
2025-02-10Fix some issues regarding allocations within the sharedcache plugin FFIkat
2025-02-10[SharedCache] Make `m_exportInfos` map's values a `shared_ptr`WeiN76LQh
This avoids expensive copying when returning a value from the map in `SharedCache::GetExportListForHeader`. Additionally it ensures that the value stays alive and at the same location in memory if `m_exportInfos` is modified and requires its storage to be re-allocated. I was unable to use a `unique_ptr` instead of a `shared_ptr` because of copy semantics with `m_exportInfos` in `ViewStateCacheStore`. I don't see things being any worse using `shared_ptr` instead of `unique_ptr` anyway and it means less code changes.
2025-02-10[SharedCache] Only setup undo actions and bulk modify in ↵WeiN76LQh
`SharedCache::FindSymbolAtAddrAndApplyToAddr` if a symbol is found
2025-02-10[SharedCache] Improve the types for `m_exportInfos`WeiN76LQh
This commit changes 2 things; 1. `m_exportInfos` is now a map where its values are also a map rather than a vector of pairs. The reason for this is that `SharedCache::FindSymbolAtAddrAndApplyToAddr` is a hot path which does by far the most accesses to `m_exportInfos`. In that function it must find the correct symbol for a given address so a map lookup will be much quicker than iterating a vector. The other use cases of `m_exportInfos` would prefer a vector but they are executed very infrequently. 2. The symbols are stored in `m_exportInfos` as references to the `Symbol` type. This makes more sense because otherwise there is a lot of time spent converting to and from a `Symbol` type and a pair of `BNSymbolType` + a `std::string`.
2025-02-10[SharedCache] Add parameter to `SharedCache::InitializeHeader` to check if ↵WeiN76LQh
`m_exportInfos` was modified This probably makes more sense than the current solution of using execution of the callback parameter to determine if `m_exportInfos` was modified.
2025-02-10[SharedCache] Use `m_exportInfos` as an export list cacheWeiN76LQh
`SharedCache::ParseExportTrie` is getting called a lot during DSC library loading and analysis. In large part due to the hot path `SharedCache::FindSymbolAtAddrAndApplyToAddr`. Its unnecessary for it to be being called more than once per DSC header as the export list symbol information is stored in `SharedCache::m_exportInfos`. This commit adds the function `SharedCache::GetExportListForHeader`, which will either return the header's list of symbol information cached in `SharedCache::m_exportInfos` or call `SharedCache::ParseExportTrie` and cache the results in `SharedCache::m_exportInfos`. This should also improve the execution time of `SharedCache::LoadAllSymbolsAndWait`. Further improvement here would be to add locking to `SharedCache::GetExportListForHeader` so that races don't result in redundant parsing of the export trie for the same header if multiple threads call `SharedCache::GetExportListForHeader` at the same time for the same header. This only really matters during initial loading because from what I can tell that parses all the export trie's anyway.
2025-01-30[SharedCache] Vision Pro, tvOS, iOS Simulator supportkat
2025-01-27[SharedCache] Optimize `ReadExportNode`Mark Rowe
`ReadExportNode` is called a lot during the initial load of the shared cache and thus impacts how long it takes for the UI to become responsive. This is a collection of optimizations that cut the time spent within `ReadExportNode` by 50%: 1. Pass iterators to `ReadExportNode` rather than a `DataBuffer` + offset. The lack of inlining in `DataBuffer`'s `operator[]` kills performance. Ideally this would have used `std::span`, but that would require bumping the minimum C++ version to C++20. 2. Add `MMappedFileAccessor::ReadSpan` so that `ReadExportNode` can operate directly on the mapped data without first copying it. 3. Removes a call to `GetAnalysisFunctionsForAddress` whose result was unused. 4. Use `std::find` to find the nul at the end of strings rather than assembling the string a character at a time. This avoids repeatedly growing the string. 5. Avoid the usual `Symbol` constructor in favor of `BNCreateSymbol`. The `Symbol` constructor has over head in two forms: 1. It has a `NameSpace` as an argument. Creating / destroying this allocates and deallocates memory We could create a single instance and reuse it for all calls, but... 2. The constructor copies all fields of the `NameSpace` to the heap before calling `BNCreateSymbol` and then deallocates them afterwards. This seems unnecessary, and adds a non-trivial amount of overhead. This can go back to directly constructing the `Symbol` once the constructors are improved.
2025-01-27Port sharedcache view to MemoryRegions, removing the need for the fake Raw ↵kat
view workaround
2025-01-15[SharedCache] Avoid copying strings on each call to VM::MappingAtAddressMark Rowe
`PageMapping` was storing the path to the file it points within. This was causing unnecessary work within `VM::MappingAtAddress` as the `PageMapping`, and thus the path, is copied into the return value. This copying was more expensive than the map lookup. The file path is now stored within a `LazyMappedFileAccessor` class that wraps the `SelfAllocatingWeakPtr`. This means the path is still available via the `PageMapping`, but it does not need to be copied as often. This includes two additional improvements / optimizations while I was touching the code in question: 1. `MMappedFileAccessor::Open` no longer performs two hash lookups when a file accessor already exists. 2. `VM::MapPages` takes the path by const reference to avoid an unnecessary copy.
2025-01-10[SharedCache] Avoid crashing the product whenever bugs occur in ser/deser, ↵kat
fix compilation issue on linux
2025-01-10[SharedCache] Fix handling of relative selectors in macOS shared cachesMark Rowe
Find the relative selector base address in the Objective-C optimization data pointed to by the shared cache header, rather than via `__objc_scoffs`. This is only present on iOS, and not for every iOS version that encodes selectors via direct offsets. This also includes some related improvements: 1. Direct selectors get their own pointer type so they're rendered correctly in the view. 2. Method lists encoded as lists of lists are now handled. 3. The `dyld_cache_header` type added to the view is truncated to the length in the loaded cache. This ensures it is applied to the view. 4. A couple of methods that process method IMPs and selectors are updated to check whether the address is valid before attempting to process them. They would otherwise fail by throwing an exception if they proceed, but checking for validity is quicker and makes exception breakpoints usable.
2025-01-10[SharedCache] Track whether non-image regions are data vs codeMark Rowe
`BackingCache` now tracks the `dyld_cache_mapping_info` for its mappings so it has access to the memory protections for the region. This means it can avoid marking some regions as containing code when they don't, reducing the amount of analysis work that has to be done. Using `dyld_cache_mapping_info` also makes references to mappings easier to understand due to its named fields vs the nested `std::pair`s that were previously in use.
2025-01-10[SharedCache] Cache type libraries in the view-specific stateMark Rowe
They're surprisingly expensive to look up.
2025-01-10[SharedCache] Split view-specific state into a separate structMark Rowe
The existing view-specific state was stored in several global unordered maps. Many of these were accessed without locking, including `viewSpecificMutexes`, which is racy in the face of multiple threads. View-specific state is stored in a new heap-allocated `ViewSpecificState` struct that is reference counted via `std::shared_ptr`. A static map holds a `std::weak_ptr` to each view-specific state, keyed by session id. `SharedCache` retrieves its view-specific state during its constructor. Since `ViewSpecificState` is reference counted it will naturally be deallocated when the last `SharedCache` instance that references it goes away. Its corresponding entry will remain in the static map, though since it only holds a `std::weak_ptr` rather than any state it will not use much memory. The next time view-specific state is retrieved any expired entries will be removed from the map.
2025-01-08[SharedCache] Fix uninitialized `loaded` field for mappings returned by ↵WeiN76LQh
`BNDSCViewGetAllImages`