summaryrefslogtreecommitdiff
path: root/view/sharedcache/core
diff options
context:
space:
mode:
authorMark Rowe <mrowe@bdash.net.nz>2024-11-13 18:40:37 -0800
committerkat <kat@vector35.com>2024-12-10 10:39:14 -0500
commit8024cbe3eb257c39d17e77b1f445ceb28d02b1ed (patch)
tree87674bbeb28a78c57c3031fc20861a5949a0177a /view/sharedcache/core
parent90af5bdc87977c4c39ed7594f882dfad41756fb5 (diff)
[SharedCache] Use basic copy-on-write for viewStateCache
Copying the state from the cache into a new `SharedCache` object is done with a global lock held and is so expensive that it results in much of the shared cache analysis running on a single thread, with others blocked waiting to acquire the lock. The cache now holds a `std::shared_ptr` to the state. New `SharedCache` objects take a reference to the cached state and only create their own copy of it the first time they perform an operation that would mutate it. The cached copy is never mutated, only replaced, so there is no danger of modifying the state out from under a `SharedCache` object. Since the copy happens at first mutation, it is performed without any global locks held. This avoids blocking other threads. This cuts the initial load time of a macOS shared cache from 3 minutes to 70 seconds, and cuts the time taken to load and analyze AppKit from multiple hours to around 14 minutes.
Diffstat (limited to 'view/sharedcache/core')
-rw-r--r--view/sharedcache/core/SharedCache.cpp448
-rw-r--r--view/sharedcache/core/SharedCache.h54
2 files changed, 273 insertions, 229 deletions
diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp
index c1ff902e..f76ee1ad 100644
--- a/view/sharedcache/core/SharedCache.cpp
+++ b/view/sharedcache/core/SharedCache.cpp
@@ -53,30 +53,33 @@ int count_trailing_zeros(uint64_t value) {
}
#endif
-struct ViewStateCacheStore {
- SharedCache::SharedCacheFormat m_cacheFormat;
+struct SharedCache::State
+{
+ std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>
+ exportInfos;
+ std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>
+ symbolInfos;
- DSCViewState m_viewState;
+ std::unordered_map<std::string, uint64_t> imageStarts;
+ std::unordered_map<uint64_t, SharedCacheMachOHeader> headers;
- std::unordered_map<std::string, uint64_t> m_imageStarts;
- std::unordered_map<uint64_t, SharedCacheMachOHeader> m_headers;
+ std::vector<CacheImage> images;
- std::vector<CacheImage> m_images;
- std::vector<MemoryRegion> m_regionsMappedIntoMemory;
+ std::vector<MemoryRegion> regionsMappedIntoMemory;
- std::vector<BackingCache> m_backingCaches;
- std::vector<MemoryRegion> m_stubIslandRegions; // TODO honestly both of these should be refactored into nonImageRegions. :p
- std::vector<MemoryRegion> m_dyldDataRegions;
- std::vector<MemoryRegion> m_nonImageRegions;
+ std::vector<BackingCache> backingCaches;
- std::string m_baseFilePath;
+ std::vector<MemoryRegion> stubIslandRegions; // TODO honestly both of these should be refactored into nonImageRegions. :p
+ std::vector<MemoryRegion> dyldDataRegions;
+ std::vector<MemoryRegion> nonImageRegions;
- std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>> m_exportInfos;
- std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>> m_symbolInfos;
+ std::string baseFilePath;
+ SharedCacheFormat cacheFormat;
+ DSCViewState viewState = DSCViewStateUnloaded;
};
static std::recursive_mutex viewStateMutex;
-static std::unordered_map<uint64_t, ViewStateCacheStore> viewStateCache;
+static std::unordered_map<uint64_t, std::shared_ptr<struct SharedCache::State>> viewStateCache;
std::mutex progressMutex;
std::unordered_map<uint64_t, BNDSCViewLoadProgress> progressMap;
@@ -221,7 +224,9 @@ void SharedCache::PerformInitialLoad()
progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressLoadingCaches;
progressMutex.unlock();
- m_baseFilePath = path;
+ WillMutateState();
+
+ MutableState().baseFilePath = path;
DataBuffer sig = baseFile->ReadBuffer(0, 4);
if (sig.GetLength() != 4)
@@ -230,14 +235,14 @@ void SharedCache::PerformInitialLoad()
if (strncmp(magic, "dyld", 4) != 0)
abort();
- m_cacheFormat = RegularCacheFormat;
+ MutableState().cacheFormat = RegularCacheFormat;
dyld_cache_header primaryCacheHeader {};
size_t header_size = baseFile->ReadUInt32(16);
baseFile->Read(&primaryCacheHeader, 0, std::min(header_size, sizeof(dyld_cache_header)));
if (primaryCacheHeader.imagesCountOld != 0)
- m_cacheFormat = RegularCacheFormat;
+ MutableState().cacheFormat = RegularCacheFormat;
size_t subCacheOff = offsetof(struct dyld_cache_header, subCacheArrayOffset);
size_t headerEnd = primaryCacheHeader.mappingOffset;
@@ -246,15 +251,15 @@ void SharedCache::PerformInitialLoad()
if (primaryCacheHeader.cacheType != 2)
{
if (std::filesystem::exists(ResolveFilePath(m_dscView, baseFile->Path() + ".01")))
- m_cacheFormat = LargeCacheFormat;
+ MutableState().cacheFormat = LargeCacheFormat;
else
- m_cacheFormat = SplitCacheFormat;
+ MutableState().cacheFormat = SplitCacheFormat;
}
else
- m_cacheFormat = iOS16CacheFormat;
+ MutableState().cacheFormat = iOS16CacheFormat;
}
- switch (m_cacheFormat)
+ switch (State().cacheFormat)
{
case RegularCacheFormat:
{
@@ -272,7 +277,7 @@ void SharedCache::PerformInitialLoad()
mapRawToAddrAndSize.second.second = mapping.size;
cache.mappings.push_back(mapRawToAddrAndSize);
}
- m_backingCaches.push_back(cache);
+ MutableState().backingCaches.push_back(std::move(cache));
dyld_cache_image_info img {};
@@ -280,7 +285,7 @@ void SharedCache::PerformInitialLoad()
{
baseFile->Read(&img, primaryCacheHeader.imagesOffsetOld + (i * sizeof(img)), sizeof(img));
auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
- m_imageStarts[iname] = img.address;
+ MutableState().imageStarts[iname] = img.address;
}
m_logger->LogInfo("Found %d images in the shared cache", primaryCacheHeader.imagesCountOld);
@@ -312,7 +317,7 @@ void SharedCache::PerformInitialLoad()
std::string segNameStr = std::string(segName);
stubIslandRegion.prettyName = "dyld_shared_cache_branch_islands_" + std::to_string(i) + "::" + segNameStr;
stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
- m_stubIslandRegions.push_back(stubIslandRegion);
+ MutableState().stubIslandRegions.push_back(std::move(stubIslandRegion));
}
}
}
@@ -338,9 +343,9 @@ void SharedCache::PerformInitialLoad()
mapRawToAddrAndSize.first = mapping.fileOffset;
mapRawToAddrAndSize.second.first = mapping.address;
mapRawToAddrAndSize.second.second = mapping.size;
- cache.mappings.push_back(mapRawToAddrAndSize);
+ cache.mappings.push_back(std::move(mapRawToAddrAndSize));
}
- m_backingCaches.push_back(cache);
+ MutableState().backingCaches.push_back(std::move(cache));
dyld_cache_image_info img {};
@@ -348,7 +353,7 @@ void SharedCache::PerformInitialLoad()
{
baseFile->Read(&img, primaryCacheHeader.imagesOffset + (i * sizeof(img)), sizeof(img));
auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
- m_imageStarts[iname] = img.address;
+ MutableState().imageStarts[iname] = img.address;
}
if (primaryCacheHeader.branchPoolsCount)
@@ -356,7 +361,8 @@ void SharedCache::PerformInitialLoad()
std::vector<uint64_t> pool {};
for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
{
- m_imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] = baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
+ MutableState().imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] =
+ baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
}
}
std::string mainFileName = base_name(path);
@@ -413,7 +419,7 @@ void SharedCache::PerformInitialLoad()
mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
mapRawToAddrAndSize.second.first = subCacheMapping.address;
mapRawToAddrAndSize.second.second = subCacheMapping.size;
- subCache.mappings.push_back(mapRawToAddrAndSize);
+ subCache.mappings.push_back(std::move(mapRawToAddrAndSize));
}
if (subCacheHeader.mappingCount == 1 && subCacheHeader.imagesCountOld == 0 && subCacheHeader.imagesCount == 0
@@ -427,10 +433,10 @@ void SharedCache::PerformInitialLoad()
stubIslandRegion.size = size;
stubIslandRegion.prettyName = subCacheFilename + "::_stubs";
stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
- m_stubIslandRegions.push_back(stubIslandRegion);
+ MutableState().stubIslandRegions.push_back(std::move(stubIslandRegion));
}
- m_backingCaches.push_back(subCache);
+ MutableState().backingCaches.push_back(std::move(subCache));
}
break;
}
@@ -449,9 +455,9 @@ void SharedCache::PerformInitialLoad()
mapRawToAddrAndSize.first = mapping.fileOffset;
mapRawToAddrAndSize.second.first = mapping.address;
mapRawToAddrAndSize.second.second = mapping.size;
- cache.mappings.push_back(mapRawToAddrAndSize);
+ cache.mappings.push_back(std::move(mapRawToAddrAndSize));
}
- m_backingCaches.push_back(cache);
+ MutableState().backingCaches.push_back(std::move(cache));
dyld_cache_image_info img {};
@@ -459,7 +465,7 @@ void SharedCache::PerformInitialLoad()
{
baseFile->Read(&img, primaryCacheHeader.imagesOffset + (i * sizeof(img)), sizeof(img));
auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
- m_imageStarts[iname] = img.address;
+ MutableState().imageStarts[iname] = img.address;
}
if (primaryCacheHeader.branchPoolsCount)
@@ -467,7 +473,8 @@ void SharedCache::PerformInitialLoad()
std::vector<uint64_t> pool {};
for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
{
- m_imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] = baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
+ MutableState().imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] =
+ baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
}
}
@@ -508,10 +515,10 @@ void SharedCache::PerformInitialLoad()
mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
mapRawToAddrAndSize.second.first = subCacheMapping.address;
mapRawToAddrAndSize.second.second = subCacheMapping.size;
- subCache.mappings.push_back(mapRawToAddrAndSize);
+ subCache.mappings.push_back(std::move(mapRawToAddrAndSize));
}
- m_backingCaches.push_back(subCache);
+ MutableState().backingCaches.push_back(std::move(subCache));
if (subCacheHeader.mappingCount == 1 && subCacheHeader.imagesCountOld == 0 && subCacheHeader.imagesCount == 0
&& subCacheHeader.imagesTextOffset == 0)
@@ -524,7 +531,7 @@ void SharedCache::PerformInitialLoad()
stubIslandRegion.size = size;
stubIslandRegion.prettyName = subCacheFilename + "::_stubs";
stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
- m_stubIslandRegions.push_back(stubIslandRegion);
+ MutableState().stubIslandRegions.push_back(std::move(stubIslandRegion));
}
}
@@ -554,10 +561,10 @@ void SharedCache::PerformInitialLoad()
mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
mapRawToAddrAndSize.second.first = subCacheMapping.address;
mapRawToAddrAndSize.second.second = subCacheMapping.size;
- subCache.mappings.push_back(mapRawToAddrAndSize);
+ subCache.mappings.push_back(std::move(mapRawToAddrAndSize));
}
- m_backingCaches.push_back(subCache);
+ MutableState().backingCaches.push_back(std::move(subCache));
break;
}
case iOS16CacheFormat:
@@ -575,10 +582,10 @@ void SharedCache::PerformInitialLoad()
mapRawToAddrAndSize.first = mapping.fileOffset;
mapRawToAddrAndSize.second.first = mapping.address;
mapRawToAddrAndSize.second.second = mapping.size;
- cache.mappings.push_back(mapRawToAddrAndSize);
+ cache.mappings.push_back(std::move(mapRawToAddrAndSize));
}
- m_backingCaches.push_back(cache);
+ MutableState().backingCaches.push_back(std::move(cache));
dyld_cache_image_info img {};
@@ -586,7 +593,7 @@ void SharedCache::PerformInitialLoad()
{
baseFile->Read(&img, primaryCacheHeader.imagesOffset + (i * sizeof(img)), sizeof(img));
auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
- m_imageStarts[iname] = img.address;
+ MutableState().imageStarts[iname] = img.address;
}
if (primaryCacheHeader.branchPoolsCount)
@@ -594,7 +601,8 @@ void SharedCache::PerformInitialLoad()
std::vector<uint64_t> pool {};
for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
{
- m_imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] = baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
+ MutableState().imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] =
+ baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
}
}
@@ -657,7 +665,7 @@ void SharedCache::PerformInitialLoad()
mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
mapRawToAddrAndSize.second.first = subCacheMapping.address;
mapRawToAddrAndSize.second.second = subCacheMapping.size;
- subCache.mappings.push_back(mapRawToAddrAndSize);
+ subCache.mappings.push_back(std::move(mapRawToAddrAndSize));
if (subCachePath.find(".dylddata") != std::string::npos)
{
@@ -669,11 +677,11 @@ void SharedCache::PerformInitialLoad()
dyldDataRegion.size = size;
dyldDataRegion.prettyName = subCacheFilename + "::_data" + std::to_string(j);
dyldDataRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable);
- m_dyldDataRegions.push_back(dyldDataRegion);
+ MutableState().dyldDataRegions.push_back(std::move(dyldDataRegion));
}
}
- m_backingCaches.push_back(subCache);
+ MutableState().backingCaches.push_back(std::move(subCache));
if (subCacheHeader.mappingCount == 1 && subCacheHeader.imagesCountOld == 0 && subCacheHeader.imagesCount == 0
&& subCacheHeader.imagesTextOffset == 0)
@@ -686,7 +694,7 @@ void SharedCache::PerformInitialLoad()
stubIslandRegion.size = size;
stubIslandRegion.prettyName = subCacheFilename + "::_stubs";
stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
- m_stubIslandRegions.push_back(stubIslandRegion);
+ MutableState().stubIslandRegions.push_back(std::move(stubIslandRegion));
}
}
@@ -718,10 +726,10 @@ void SharedCache::PerformInitialLoad()
mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
mapRawToAddrAndSize.second.first = subCacheMapping.address;
mapRawToAddrAndSize.second.second = subCacheMapping.size;
- subCache.mappings.push_back(mapRawToAddrAndSize);
+ subCache.mappings.push_back(std::move(mapRawToAddrAndSize));
}
- m_backingCaches.push_back(subCache);
+ MutableState().backingCaches.push_back(std::move(subCache));
}
catch (...)
{}
@@ -741,7 +749,7 @@ void SharedCache::PerformInitialLoad()
m_logger->LogError("Failed to map VM pages for Shared Cache on initial load, this is fatal.");
return;
}
- for (const auto &start : m_imageStarts)
+ for (const auto& start : State().imageStarts)
{
try {
auto imageHeader = SharedCache::LoadHeaderForAddress(vm, start.second, start.first);
@@ -752,7 +760,7 @@ void SharedCache::PerformInitialLoad()
auto mapping = vm->MappingAtAddress(imageHeader->linkeditSegment.vmaddr);
imageHeader->exportTriePath = mapping.first.filePath;
}
- m_headers[start.second] = imageHeader.value();
+ MutableState().headers[start.second] = imageHeader.value();
CacheImage image;
image.installName = start.first;
image.headerLocation = start.second;
@@ -781,14 +789,14 @@ void SharedCache::PerformInitialLoad()
// if we're positive we have an entry point for some reason, force the segment
// executable. this helps with kernel images.
- for (auto &entryPoint: imageHeader->m_entryPoints)
+ for (auto &entryPoint : imageHeader->m_entryPoints)
if (segment.vmaddr <= entryPoint && (entryPoint < (segment.vmaddr + segment.filesize)))
flags |= SegmentExecutable;
sectionRegion.flags = (BNSegmentFlag)flags;
image.regions.push_back(sectionRegion);
}
- m_images.push_back(image);
+ MutableState().images.push_back(image);
}
else
{
@@ -801,9 +809,9 @@ void SharedCache::PerformInitialLoad()
}
}
- m_logger->LogInfo("Loaded %d Mach-O headers", m_headers.size());
+ m_logger->LogInfo("Loaded %d Mach-O headers", State().headers.size());
- for (const auto& cache : m_backingCaches)
+ for (const auto& cache : State().backingCaches)
{
size_t i = 0;
for (const auto& mapping : cache.mappings)
@@ -814,15 +822,15 @@ void SharedCache::PerformInitialLoad()
region.prettyName = base_name(cache.path) + "::" + std::to_string(i);
// FIXME flags!!! BackingCache.mapping needs refactored to store this information!
region.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
- m_nonImageRegions.push_back(region);
+ MutableState().nonImageRegions.push_back(std::move(region));
i++;
}
}
// Iterate through each Mach-O header
- if (!m_dyldDataRegions.empty())
+ if (!State().dyldDataRegions.empty())
{
- for (const auto& [headerKey, header] : m_headers)
+ for (const auto& [headerKey, header] : State().headers)
{
// Iterate through each segment of the header
for (const auto& segment : header.segments)
@@ -831,7 +839,7 @@ void SharedCache::PerformInitialLoad()
uint64_t segmentEnd = segmentStart + segment.vmsize;
// Iterate through each region in m_dyldDataRegions
- for (auto it = m_dyldDataRegions.begin(); it != m_dyldDataRegions.end();)
+ for (auto it = State().dyldDataRegions.begin(); it != State().dyldDataRegions.end();)
{
uint64_t regionStart = it->start;
uint64_t regionSize = it->size;
@@ -862,12 +870,12 @@ void SharedCache::PerformInitialLoad()
}
// Erase the original region
- it = m_dyldDataRegions.erase(it);
+ it = MutableState().dyldDataRegions.erase(it);
// Insert the new regions (if any)
for (const auto& newRegion : newRegions)
{
- it = m_dyldDataRegions.insert(it, newRegion);
+ it = MutableState().dyldDataRegions.insert(it, newRegion);
++it; // Move iterator to the next position
}
}
@@ -881,9 +889,9 @@ void SharedCache::PerformInitialLoad()
}
// Iterate through each Mach-O header
- if (!m_nonImageRegions.empty())
+ if (!State().nonImageRegions.empty())
{
- for (const auto& [headerKey, header] : m_headers)
+ for (const auto& [headerKey, header] : State().headers)
{
// Iterate through each segment of the header
for (const auto& segment : header.segments)
@@ -892,7 +900,7 @@ void SharedCache::PerformInitialLoad()
uint64_t segmentEnd = segmentStart + segment.vmsize;
// Iterate through each region in m_dyldDataRegions
- for (auto it = m_nonImageRegions.begin(); it != m_nonImageRegions.end();)
+ for (auto it = State().nonImageRegions.begin(); it != State().nonImageRegions.end();)
{
uint64_t regionStart = it->start;
uint64_t regionSize = it->size;
@@ -923,12 +931,12 @@ void SharedCache::PerformInitialLoad()
}
// Erase the original region
- it = m_nonImageRegions.erase(it);
+ it = MutableState().nonImageRegions.erase(it);
// Insert the new regions (if any)
for (const auto& newRegion : newRegions)
{
- it = m_nonImageRegions.insert(it, newRegion);
+ it = MutableState().nonImageRegions.insert(it, newRegion);
++it; // Move iterator to the next position
}
}
@@ -955,7 +963,7 @@ std::shared_ptr<VM> SharedCache::GetVMMap(bool mapPages)
if (mapPages)
{
- for (const auto& cache : m_backingCaches)
+ for (const auto& cache : State().backingCaches)
{
for (const auto& mapping : cache.mappings)
{
@@ -976,22 +984,10 @@ void SharedCache::DeserializeFromRawView()
if (m_dscView->QueryMetadata(SharedCacheMetadataTag))
{
std::unique_lock<std::recursive_mutex> viewStateCacheLock(viewStateMutex);
- if (viewStateCache.find(m_dscView->GetFile()->GetSessionId()) != viewStateCache.end())
+ if (auto it = viewStateCache.find(m_dscView->GetFile()->GetSessionId()); it != viewStateCache.end())
{
- auto c = viewStateCache[m_dscView->GetFile()->GetSessionId()];
- m_imageStarts = c.m_imageStarts;
- m_cacheFormat = c.m_cacheFormat;
- m_backingCaches = c.m_backingCaches;
- m_viewState = c.m_viewState;
- m_headers = c.m_headers;
- m_images = c.m_images;
- m_regionsMappedIntoMemory = c.m_regionsMappedIntoMemory;
- m_stubIslandRegions = c.m_stubIslandRegions;
- m_dyldDataRegions = c.m_dyldDataRegions;
- m_nonImageRegions = c.m_nonImageRegions;
- m_baseFilePath = c.m_baseFilePath;
- m_exportInfos = c.m_exportInfos;
- m_symbolInfos = c.m_symbolInfos;
+ m_state = it->second;
+ m_stateIsShared = true;
m_metadataValid = true;
}
else
@@ -1001,14 +997,16 @@ void SharedCache::DeserializeFromRawView()
if (!m_metadataValid)
{
m_logger->LogError("Failed to deserialize Shared Cache metadata");
- m_viewState = DSCViewStateUnloaded;
+ WillMutateState();
+ MutableState().viewState = DSCViewStateUnloaded;
}
}
else
{
m_metadataValid = true;
- m_viewState = DSCViewStateUnloaded;
- m_images.clear(); // fixme ??
+ WillMutateState();
+ MutableState().viewState = DSCViewStateUnloaded;
+ MutableState().images.clear(); // fixme ??
}
}
@@ -1025,12 +1023,14 @@ void SharedCache::ParseAndApplySlideInfoForFile(std::shared_ptr<MMappedFileAcces
{
if (file->SlideInfoWasApplied())
return;
+
+ WillMutateState();
std::vector<std::pair<uint64_t, uint64_t>> rewrites;
dyld_cache_header baseHeader;
file->Read(&baseHeader, 0, sizeof(dyld_cache_header));
uint64_t base = UINT64_MAX;
- for (const auto& backingCache : m_backingCaches)
+ for (const auto& backingCache : State().backingCaches)
{
for (const auto& mapping : backingCache.mappings)
{
@@ -1361,42 +1361,39 @@ SharedCache::SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView) : m_
DeserializeFromRawView();
if (!m_metadataValid)
return;
- if (m_viewState == DSCViewStateUnloaded)
+ if (State().viewState == DSCViewStateUnloaded)
{
- if (m_viewState == DSCViewStateUnloaded)
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ try {
+ PerformInitialLoad();
+ }
+ catch (...)
{
- std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
- try {
- PerformInitialLoad();
- }
- catch (...)
- {
- m_logger->LogError("Failed to perform initial load of Shared Cache");
- }
+ m_logger->LogError("Failed to perform initial load of Shared Cache");
+ }
- auto settings = m_dscView->GetLoadSettings(VIEW_NAME);
- bool autoLoadLibsystem = true;
- if (settings && settings->Contains("loader.dsc.autoLoadLibSystem"))
- {
- autoLoadLibsystem = settings->Get<bool>("loader.dsc.autoLoadLibSystem", m_dscView);
- }
- if (autoLoadLibsystem)
+ auto settings = m_dscView->GetLoadSettings(VIEW_NAME);
+ bool autoLoadLibsystem = true;
+ if (settings && settings->Contains("loader.dsc.autoLoadLibSystem"))
+ {
+ autoLoadLibsystem = settings->Get<bool>("loader.dsc.autoLoadLibSystem", m_dscView);
+ }
+ if (autoLoadLibsystem)
+ {
+ for (const auto& [_, header] : State().headers)
{
- for (const auto& [_, header] : m_headers)
+ if (header.installName.find("libsystem_c.dylib") != std::string::npos)
{
- if (header.installName.find("libsystem_c.dylib") != std::string::npos)
- {
- lock.unlock();
- m_logger->LogInfo("Loading core libsystem_c.dylib library");
- LoadImageWithInstallName(header.installName);
- lock.lock();
- break;
- }
+ lock.unlock();
+ m_logger->LogInfo("Loading core libsystem_c.dylib library");
+ LoadImageWithInstallName(header.installName);
+ lock.lock();
+ break;
}
}
- m_viewState = DSCViewStateLoaded;
- SaveToDSCView();
}
+ MutableState().viewState = DSCViewStateLoaded;
+ SaveToDSCView();
}
else
{
@@ -1426,7 +1423,7 @@ SharedCache* SharedCache::GetFromDSCView(BinaryNinja::Ref<BinaryNinja::BinaryVie
std::optional<uint64_t> SharedCache::GetImageStart(std::string installName)
{
- for (const auto& [name, start] : m_imageStarts)
+ for (const auto& [name, start] : State().imageStarts)
{
if (name == installName)
{
@@ -1441,7 +1438,7 @@ std::optional<SharedCacheMachOHeader> SharedCache::HeaderForAddress(uint64_t add
// We _could_ mark each page with the image start? :grimacing emoji:
// But that'd require mapping pages :grimacing emoji: :grimacing emoji:
// There's not really any other hacks that could make this faster, that I can think of...
- for (const auto& [start, header] : m_headers)
+ for (const auto& [start, header] : State().headers)
{
for (const auto& segment : header.segments)
{
@@ -1456,21 +1453,21 @@ std::optional<SharedCacheMachOHeader> SharedCache::HeaderForAddress(uint64_t add
std::string SharedCache::NameForAddress(uint64_t address)
{
- for (const auto& stubIsland : m_stubIslandRegions)
+ for (const auto& stubIsland : State().stubIslandRegions)
{
if (stubIsland.start <= address && stubIsland.start + stubIsland.size > address)
{
return stubIsland.prettyName;
}
}
- for (const auto& dyldData : m_dyldDataRegions)
+ for (const auto& dyldData : State().dyldDataRegions)
{
if (dyldData.start <= address && dyldData.start + dyldData.size > address)
{
return dyldData.prettyName;
}
}
- for (const auto& nonImageRegion : m_nonImageRegions)
+ for (const auto& nonImageRegion : State().nonImageRegions)
{
if (nonImageRegion.start <= address && nonImageRegion.start + nonImageRegion.size > address)
{
@@ -1504,7 +1501,7 @@ std::string SharedCache::ImageNameForAddress(uint64_t address)
bool SharedCache::LoadImageContainingAddress(uint64_t address)
{
- for (const auto& [start, header] : m_headers)
+ for (const auto& [start, header] : State().headers)
{
for (const auto& segment : header.segments)
{
@@ -1522,6 +1519,8 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
{
std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
DeserializeFromRawView();
+ WillMutateState();
+
auto vm = GetVMMap();
if (!vm)
{
@@ -1533,13 +1532,13 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
CacheImage* targetImage = nullptr;
MemoryRegion* targetSegment = nullptr;
- for (auto& image : m_images)
+ for (auto& image : MutableState().images)
{
for (auto& region : image.regions)
{
if (region.start <= address && region.start + region.size > address)
{
- targetHeader = m_headers[image.headerLocation];
+ targetHeader = MutableState().headers[image.headerLocation];
targetImage = &image;
targetSegment = &region;
break;
@@ -1550,7 +1549,7 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
}
if (!targetSegment)
{
- for (auto& stubIsland : m_stubIslandRegions)
+ for (auto& stubIsland : MutableState().stubIslandRegions)
{
if (stubIsland.start <= address && stubIsland.start + stubIsland.size > address)
{
@@ -1579,7 +1578,7 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
stubIsland.rawViewOffsetIfLoaded = rawViewEnd;
- m_regionsMappedIntoMemory.push_back(stubIsland);
+ MutableState().regionsMappedIntoMemory.push_back(stubIsland);
SaveToDSCView();
@@ -1590,7 +1589,7 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
}
}
- for (auto& dyldData : m_dyldDataRegions)
+ for (auto& dyldData : MutableState().dyldDataRegions)
{
if (dyldData.start <= address && dyldData.start + dyldData.size > address)
{
@@ -1618,7 +1617,7 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
dyldData.loaded = true;
dyldData.rawViewOffsetIfLoaded = rawViewEnd;
- m_regionsMappedIntoMemory.push_back(dyldData);
+ MutableState().regionsMappedIntoMemory.push_back(dyldData);
SaveToDSCView();
@@ -1629,7 +1628,7 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
}
}
- for (auto& region : m_nonImageRegions)
+ for (auto& region : MutableState().nonImageRegions)
{
if (region.start <= address && region.start + region.size > address)
{
@@ -1656,7 +1655,7 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
region.loaded = true;
region.rawViewOffsetIfLoaded = rawViewEnd;
- m_regionsMappedIntoMemory.push_back(region);
+ MutableState().regionsMappedIntoMemory.push_back(region);
SaveToDSCView();
@@ -1692,7 +1691,7 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address)
targetSegment->loaded = true;
targetSegment->rawViewOffsetIfLoaded = rawViewEnd;
- m_regionsMappedIntoMemory.push_back(*targetSegment);
+ MutableState().regionsMappedIntoMemory.push_back(*targetSegment);
SaveToDSCView();
@@ -1716,12 +1715,14 @@ bool SharedCache::LoadImageWithInstallName(std::string installName)
std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
DeserializeFromRawView();
+ WillMutateState();
+
m_logger->LogInfo("Loading image %s", installName.c_str());
auto vm = GetVMMap();
CacheImage* targetImage = nullptr;
- for (auto& cacheImage : m_images)
+ for (auto& cacheImage : MutableState().images)
{
if (cacheImage.installName == installName)
{
@@ -1729,11 +1730,15 @@ bool SharedCache::LoadImageWithInstallName(std::string installName)
break;
}
}
-
- auto header = m_headers[targetImage->headerLocation];
+ auto it = State().headers.find(targetImage->headerLocation);
+ if (it == State().headers.end())
+ {
+ return false;
+ }
+ const auto& header = it->second;
auto id = m_dscView->BeginUndoActions();
- m_viewState = DSCViewStateLoadedWithImages;
+ MutableState().viewState = DSCViewStateLoadedWithImages;
auto reader = VMReader(vm);
reader.Seek(targetImage->headerLocation);
@@ -1766,7 +1771,7 @@ bool SharedCache::LoadImageWithInstallName(std::string installName)
region.loaded = true;
region.rawViewOffsetIfLoaded = rawViewEnd;
- m_regionsMappedIntoMemory.push_back(region);
+ MutableState().regionsMappedIntoMemory.push_back(region);
m_dscView->GetParentView()->AddAutoSegment(rawViewEnd, region.size, rawViewEnd, region.size, region.flags);
m_dscView->AddUserSegment(region.start, region.size, rawViewEnd, region.size, region.flags);
@@ -2256,6 +2261,7 @@ std::optional<SharedCacheMachOHeader> SharedCache::LoadHeaderForAddress(std::sha
void SharedCache::InitializeHeader(
Ref<BinaryView> view, VM* vm, SharedCacheMachOHeader header, std::vector<MemoryRegion*> regionsToLoad)
{
+ WillMutateState();
Ref<Settings> settings = view->GetLoadSettings(VIEW_NAME);
bool applyFunctionStarts = true;
@@ -2625,7 +2631,7 @@ void SharedCache::InitializeHeader(
view->DefineAutoSymbol(symbolObj);
symbolInfos.push_back({sym.n_value, {type, symbol}});
}
- m_symbolInfos[header.textBase] = symbolInfos;
+ MutableState().symbolInfos[header.textBase] = symbolInfos;
}
if (header.exportTriePresent && header.linkeditPresent && vm->AddressIsMapped(header.linkeditSegment.vmaddr))
@@ -2671,7 +2677,7 @@ void SharedCache::InitializeHeader(
else
view->DefineAutoSymbol(symbol);
}
- m_exportInfos[header.textBase] = exportMapping;
+ MutableState().exportInfos[header.textBase] = std::move(exportMapping);
}
view->EndBulkModifySymbols();
@@ -2778,7 +2784,7 @@ std::vector<Ref<Symbol>> SharedCache::ParseExportTrie(std::shared_ptr<MMappedFil
std::vector<std::string> SharedCache::GetAvailableImages()
{
std::vector<std::string> installNames;
- for (const auto& header : m_headers)
+ for (const auto& header : State().headers)
{
installNames.push_back(header.second.installName);
}
@@ -2788,10 +2794,12 @@ std::vector<std::string> SharedCache::GetAvailableImages()
std::vector<std::pair<std::string, Ref<Symbol>>> SharedCache::LoadAllSymbolsAndWait()
{
+ WillMutateState();
+
std::unique_lock<std::mutex> initialLoadBlock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
std::vector<std::pair<std::string, Ref<Symbol>>> symbols;
- for (const auto& img : m_images)
+ for (const auto& img : State().images)
{
auto header = HeaderForAddress(img.headerLocation);
std::shared_ptr<MMappedFileAccessor> mapping;
@@ -2810,7 +2818,7 @@ std::vector<std::pair<std::string, Ref<Symbol>>> SharedCache::LoadAllSymbolsAndW
exportMapping.push_back({sym->GetAddress(), {sym->GetType(), sym->GetRawName()}});
symbols.push_back({img.installName, sym});
}
- m_exportInfos[header->textBase] = exportMapping;
+ MutableState().exportInfos[header->textBase] = std::move(exportMapping);
}
SaveToDSCView();
@@ -2832,17 +2840,22 @@ std::string SharedCache::SerializedImageHeaderForAddress(uint64_t address)
std::string SharedCache::SerializedImageHeaderForName(std::string name)
{
- auto header = HeaderForAddress(m_imageStarts[name]);
- if (header)
+ if (auto it = State().imageStarts.find(name); it != State().imageStarts.end())
{
- return header->AsString();
+ if (auto header = HeaderForAddress(it->second))
+ {
+ return header->AsString();
+ }
}
return "";
}
-void SharedCache::FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis)
+void SharedCache::FindSymbolAtAddrAndApplyToAddr(
+ uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis)
{
+ WillMutateState();
+
std::string prefix = "";
if (symbolLocation != targetLocation)
prefix = "j_";
@@ -2929,7 +2942,7 @@ void SharedCache::FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64
}
{
std::unique_lock<std::mutex> _lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
- m_exportInfos[header->textBase] = exportMapping;
+ MutableState().exportInfos[header->textBase] = std::move(exportMapping);
}
m_dscView->EndBulkModifySymbols();
m_dscView->ForgetUndoActions(id);
@@ -2944,22 +2957,16 @@ bool SharedCache::SaveToDSCView()
auto data = AsMetadata();
m_dscView->StoreMetadata(SharedCacheMetadataTag, data);
m_dscView->GetParentView()->GetParentView()->StoreMetadata(SharedCacheMetadataTag, data);
+
+ // By moving our state the to cache we can avoid creating a copy in the case
+ // that no further mutations are made to `this`. If we're not done being mutated,
+ // the data will be copied on the first mutation.
+ auto cachedState = std::make_shared<struct State>(std::move(*m_state));
+ m_state = cachedState;
+ m_stateIsShared = true;
+
std::unique_lock<std::recursive_mutex> viewStateCacheLock(viewStateMutex);
- ViewStateCacheStore c;
- c.m_imageStarts = m_imageStarts;
- c.m_cacheFormat = m_cacheFormat;
- c.m_backingCaches = m_backingCaches;
- c.m_viewState = m_viewState;
- c.m_headers = m_headers;
- c.m_images = m_images;
- c.m_regionsMappedIntoMemory = m_regionsMappedIntoMemory;
- c.m_stubIslandRegions = m_stubIslandRegions;
- c.m_dyldDataRegions = m_dyldDataRegions;
- c.m_nonImageRegions = m_nonImageRegions;
- c.m_baseFilePath = m_baseFilePath;
- c.m_exportInfos = m_exportInfos;
- c.m_symbolInfos = m_symbolInfos;
- viewStateCache[m_dscView->GetFile()->GetSessionId()] = c;
+ viewStateCache[m_dscView->GetFile()->GetSessionId()] = std::move(cachedState);
m_metadataValid = true;
@@ -2970,7 +2977,7 @@ bool SharedCache::SaveToDSCView()
std::vector<MemoryRegion> SharedCache::GetMappedRegions() const
{
std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
- return m_regionsMappedIntoMemory;
+ return State().regionsMappedIntoMemory;
}
extern "C"
@@ -3116,7 +3123,7 @@ extern "C"
{
if (cache->object)
{
- return (BNDSCViewState)cache->object->State();
+ return (BNDSCViewState)cache->object->ViewState();
}
return BNDSCViewState::Unloaded;
@@ -3323,14 +3330,14 @@ void SharedCache::Store(SerializationContext& context) const
{
Serialize(context, "metadataVersion", METADATA_VERSION);
- MSS(m_viewState);
- MSS_CAST(m_cacheFormat, uint8_t);
- MSS(m_imageStarts);
- MSS(m_baseFilePath);
+ Serialize(context, "m_viewState", State().viewState);
+ Serialize(context, "m_cacheFormat", State().cacheFormat);
+ Serialize(context, "m_imageStarts", State().imageStarts);
+ Serialize(context, "m_baseFilePath", State().baseFilePath);
Serialize(context, "headers");
context.writer.StartArray();
- for (auto& [k, v] : m_headers)
+ for (auto& [k, v] : State().headers)
{
context.writer.StartObject();
v.Store(context);
@@ -3340,7 +3347,7 @@ void SharedCache::Store(SerializationContext& context) const
Serialize(context, "exportInfos");
context.writer.StartArray();
- for (const auto& pair1 : m_exportInfos)
+ for (const auto& pair1 : State().exportInfos)
{
context.writer.StartObject();
Serialize(context, "key", pair1.first);
@@ -3359,12 +3366,12 @@ void SharedCache::Store(SerializationContext& context) const
}
context.writer.EndArray();
- Serialize(context, "backingCaches", m_backingCaches);
- Serialize(context, "stubIslands", m_stubIslandRegions);
- Serialize(context, "images", m_images);
- Serialize(context, "regionsMappedIntoMemory", m_regionsMappedIntoMemory);
- Serialize(context, "dyldDataSections", m_dyldDataRegions);
- Serialize(context, "nonImageRegions", m_nonImageRegions);
+ Serialize(context, "backingCaches", State().backingCaches);
+ Serialize(context, "stubIslands", State().stubIslandRegions);
+ Serialize(context, "images", State().images);
+ Serialize(context, "regionsMappedIntoMemory", State().regionsMappedIntoMemory);
+ Serialize(context, "dyldDataSections", State().dyldDataRegions);
+ Serialize(context, "nonImageRegions", State().nonImageRegions);
}
void SharedCache::Load(DeserializationContext& context)
@@ -3382,18 +3389,23 @@ void SharedCache::Load(DeserializationContext& context)
m_logger->LogError("Shared Cache metadata version missing");
return;
}
- m_viewState = MSL_CAST(m_viewState, uint8_t, DSCViewState);
- m_cacheFormat = MSL_CAST(m_cacheFormat, uint8_t, SharedCacheFormat);
- m_headers.clear();
+
+ m_stateIsShared = false;
+ m_state = std::make_shared<struct SharedCache::State>();
+
+ MutableState().viewState = static_cast<DSCViewState>(context.load<uint8_t>("m_viewState"));
+ MutableState().cacheFormat = static_cast<SharedCacheFormat>(context.load<uint8_t>("m_cacheFormat"));
+
for (auto& startAndHeader : context.doc["headers"].GetArray())
{
SharedCacheMachOHeader header;
header.LoadFromValue(startAndHeader);
- m_headers[header.textBase] = header;
+ MutableState().headers[header.textBase] = std::move(header);
}
- MSL(m_imageStarts);
- MSL(m_baseFilePath);
- m_exportInfos.clear();
+
+ Deserialize(context, "m_imageStarts", MutableState().imageStarts);
+ Deserialize(context, "m_baseFilePath", MutableState().baseFilePath);
+
for (const auto& obj1 : context.doc["exportInfos"].GetArray())
{
std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> innerVec;
@@ -3404,9 +3416,9 @@ void SharedCache::Load(DeserializationContext& context)
innerVec.push_back({obj2["key"].GetUint64(), innerPair});
}
- m_exportInfos[obj1["key"].GetUint64()] = innerVec;
+ MutableState().exportInfos[obj1["key"].GetUint64()] = std::move(innerVec);
}
- m_symbolInfos.clear();
+
for (auto& symbolInfo : context.doc["symbolInfos"].GetArray())
{
std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> symbolInfoVec;
@@ -3415,52 +3427,94 @@ void SharedCache::Load(DeserializationContext& context)
symbolInfoVec.push_back({symbolInfoPair[0].GetUint64(),
{(BNSymbolType)symbolInfoPair[1].GetUint(), symbolInfoPair[2].GetString()}});
}
- m_symbolInfos[symbolInfo[0].GetUint64()] = std::move(symbolInfoVec);
+ MutableState().symbolInfos[symbolInfo[0].GetUint64()] = std::move(symbolInfoVec);
}
- m_backingCaches.clear();
+
for (auto& bcV : context.doc["backingCaches"].GetArray())
{
BackingCache bc;
bc.LoadFromValue(bcV);
- m_backingCaches.push_back(std::move(bc));
+ MutableState().backingCaches.push_back(std::move(bc));
}
- m_images.clear();
+
for (auto& imgV : context.doc["images"].GetArray())
{
CacheImage img;
img.LoadFromValue(imgV);
- m_images.push_back(std::move(img));
+ MutableState().images.push_back(std::move(img));
}
- m_regionsMappedIntoMemory.clear();
+
for (auto& rV : context.doc["regionsMappedIntoMemory"].GetArray())
{
MemoryRegion r;
r.LoadFromValue(rV);
- m_regionsMappedIntoMemory.push_back(std::move(r));
+ MutableState().regionsMappedIntoMemory.push_back(std::move(r));
}
- m_stubIslandRegions.clear();
+
for (auto& siV : context.doc["stubIslands"].GetArray())
{
MemoryRegion si;
si.LoadFromValue(siV);
- m_stubIslandRegions.push_back(std::move(si));
+ MutableState().stubIslandRegions.push_back(std::move(si));
}
- m_dyldDataRegions.clear();
+
for (auto& siV : context.doc["dyldDataSections"].GetArray())
{
MemoryRegion si;
si.LoadFromValue(siV);
- m_dyldDataRegions.push_back(std::move(si));
+ MutableState().dyldDataRegions.push_back(std::move(si));
}
- m_nonImageRegions.clear();
+
for (auto& siV : context.doc["nonImageRegions"].GetArray())
{
MemoryRegion si;
si.LoadFromValue(siV);
- m_nonImageRegions.push_back(std::move(si));
+ MutableState().nonImageRegions.push_back(std::move(si));
}
m_metadataValid = true;
}
+__attribute__((always_inline)) void SharedCache::AssertMutable() const
+{
+ if (m_stateIsShared)
+ {
+ abort();
+ }
+}
+
+void SharedCache::WillMutateState()
+{
+ if (!m_state)
+ {
+ m_state = std::make_shared<struct State>();
+ }
+ else if (m_stateIsShared)
+ {
+ m_state = std::make_shared<struct State>(*m_state);
+ }
+ m_stateIsShared = false;
+}
+
+
+const std::vector<BackingCache>& SharedCache::BackingCaches() const
+{
+ return State().backingCaches;
+}
+
+DSCViewState SharedCache::ViewState() const
+{
+ return State().viewState;
+}
+
+const std::unordered_map<std::string, uint64_t>& SharedCache::AllImageStarts() const
+{
+ return State().imageStarts;
+}
+
+const std::unordered_map<uint64_t, SharedCacheMachOHeader>& SharedCache::AllImageHeaders() const
+{
+ return State().headers;
+}
+
} // namespace SharedCacheCore
diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h
index 4444f847..ac24b529 100644
--- a/view/sharedcache/core/SharedCache.h
+++ b/view/sharedcache/core/SharedCache.h
@@ -530,37 +530,21 @@ namespace SharedCacheCore {
void Store(SerializationContext& context) const;
void Load(DeserializationContext& context);
+ struct State;
+
private:
Ref<Logger> m_logger;
/* VIEW STATE BEGIN -- SERIALIZE ALL OF THIS AND STORE IT IN RAW VIEW */
// Updated as the view is loaded further, more images are added, etc
- DSCViewState m_viewState = DSCViewStateUnloaded;
- std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>
- m_exportInfos;
- std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>
- m_symbolInfos;
- // ---
+ // NOTE: Access via `State()` or `MutableState()` below.
+ // `WillMutateState()` must be called before the first access to `MutableState()`.
+ std::shared_ptr<State> m_state;
+ bool m_stateIsShared = false;
// Serialized once by PerformInitialLoad and available after m_viewState == Loaded
bool m_metadataValid = false;
- std::string m_baseFilePath;
- SharedCacheFormat m_cacheFormat;
-
- std::unordered_map<std::string, uint64_t> m_imageStarts;
- std::unordered_map<uint64_t, SharedCacheMachOHeader> m_headers;
-
- std::vector<CacheImage> m_images;
-
- std::vector<MemoryRegion> m_regionsMappedIntoMemory;
-
- std::vector<BackingCache> m_backingCaches;
-
- std::vector<MemoryRegion> m_stubIslandRegions;
- std::vector<MemoryRegion> m_dyldDataRegions;
- std::vector<MemoryRegion> m_nonImageRegions;
-
/* VIEWSTATE END -- NOTHING PAST THIS IS SERIALIZED */
/* API VIEW START */
@@ -592,20 +576,17 @@ namespace SharedCacheCore {
std::vector<std::pair<std::string, Ref<Symbol>>> LoadAllSymbolsAndWait();
- std::unordered_map<std::string, uint64_t> AllImageStarts() const { return m_imageStarts; }
- std::unordered_map<uint64_t, SharedCacheMachOHeader> AllImageHeaders() const { return m_headers; }
+ const std::unordered_map<std::string, uint64_t>& AllImageStarts() const;
+ const std::unordered_map<uint64_t, SharedCacheMachOHeader>& AllImageHeaders() const;
std::string SerializedImageHeaderForAddress(uint64_t address);
std::string SerializedImageHeaderForName(std::string name);
void FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis);
- std::vector<BackingCache> BackingCaches() const {
+ const std::vector<BackingCache>& BackingCaches() const;
- return m_backingCaches;
- }
-
- DSCViewState State() const { return m_viewState; }
+ DSCViewState ViewState() const;
explicit SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> rawView);
virtual ~SharedCache();
@@ -614,12 +595,21 @@ namespace SharedCacheCore {
std::shared_ptr<VM> vm, uint64_t address, std::string installName);
void InitializeHeader(
Ref<BinaryView> view, VM* vm, SharedCacheMachOHeader header, std::vector<MemoryRegion*> regionsToLoad);
- void ReadExportNode(std::vector<Ref<Symbol>>& symbolList, SharedCacheMachOHeader& header, DataBuffer& buffer, uint64_t textBase,
- const std::string& currentText, size_t cursor, uint32_t endGuard);
+ void ReadExportNode(std::vector<Ref<Symbol>>& symbolList, SharedCacheMachOHeader& header, DataBuffer& buffer,
+ uint64_t textBase, const std::string& currentText, size_t cursor, uint32_t endGuard);
std::vector<Ref<Symbol>> ParseExportTrie(
std::shared_ptr<MMappedFileAccessor> linkeditFile, SharedCacheMachOHeader header);
- };
+ const State& State() const { return *m_state; }
+ struct State& MutableState() { AssertMutable(); return *m_state; }
+
+ void AssertMutable() const;
+
+ // Ensures that the state is uniquely owned, copying it if it is not.
+ // Must be called before first access to `MutableState()` after the state
+ // is loaded from the cache. Can safely be called multiple times.
+ void WillMutateState();
+ };
}