summaryrefslogtreecommitdiff
path: root/view/sharedcache
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-04-06 20:00:00 -0400
committerMason Reed <mason@vector35.com>2025-04-06 20:00:37 -0400
commitc22d54c5d95f4d23629484579414387b0c7503ee (patch)
tree933a58c8ed3518f507f5bd4d496364411273f5b9 /view/sharedcache
parent4f22a944d77f784b53ee3a37d9f1966ede1c98a4 (diff)
[SharedCache] Replace write log with callback registration for reapplying slide info
This improves performance by ~10x with the only real issue being a copy of the cache entry existing for each weak ref. Also fixes - Some old TODO's that are no longer relevant - Some function signatures not being const - FileAccessorCache not evicting enough accessors to fit under an adjusted cache size - Added a warning if we are over the global cache size in view initialization - Logger name not having a `.` in `SlideInfoProcessor::SlideInfoProcessor` - Re-added the accessor dirty check before applying slide info to fix https://github.com/Vector35/binaryninja-api/issues/6570
Diffstat (limited to 'view/sharedcache')
-rw-r--r--view/sharedcache/core/FileAccessorCache.cpp37
-rw-r--r--view/sharedcache/core/FileAccessorCache.h36
-rw-r--r--view/sharedcache/core/MappedFileAccessor.cpp21
-rw-r--r--view/sharedcache/core/MappedFileAccessor.h20
-rw-r--r--view/sharedcache/core/SharedCache.cpp36
-rw-r--r--view/sharedcache/core/SharedCache.h7
-rw-r--r--view/sharedcache/core/SharedCacheView.cpp7
-rw-r--r--view/sharedcache/core/SlideInfo.cpp109
-rw-r--r--view/sharedcache/core/SlideInfo.h6
-rw-r--r--view/sharedcache/core/VirtualMemory.cpp10
-rw-r--r--view/sharedcache/core/VirtualMemory.h5
11 files changed, 137 insertions, 157 deletions
diff --git a/view/sharedcache/core/FileAccessorCache.cpp b/view/sharedcache/core/FileAccessorCache.cpp
index e281ec5f..5fa965b3 100644
--- a/view/sharedcache/core/FileAccessorCache.cpp
+++ b/view/sharedcache/core/FileAccessorCache.cpp
@@ -32,7 +32,6 @@ FileAccessorCache& FileAccessorCache::Global()
return cache;
}
-
WeakFileAccessor FileAccessorCache::Open(const std::string& filePath)
{
const auto id = GetCacheAccessorID(filePath);
@@ -51,7 +50,7 @@ WeakFileAccessor FileAccessorCache::Open(const std::string& filePath)
}
// Evict if we are going to go above the limit.
- if (m_cache.size() >= m_cacheSize)
+ while (m_cache.size() >= m_cacheSize)
EvictLastUsed();
// Create a new file accessor and add it to the cache.
@@ -69,20 +68,6 @@ WeakFileAccessor FileAccessorCache::Open(const std::string& filePath)
return WeakFileAccessor(sharedAccessor, filePath);
}
-void FileAccessorWriteLog::AddPointer(const size_t address, const size_t pointer)
-{
- // TODO: A sharded map here would be better. see: rust dashmap
- std::unique_lock<std::shared_mutex> lock(m_persistedMutex);
- m_persistedPointers.emplace_back(address, pointer);
-}
-
-void FileAccessorWriteLog::ApplyWrites(MappedFileAccessor& accessor)
-{
- std::shared_lock<std::shared_mutex> lock(m_persistedMutex);
- for (const auto& [address, pointer] : m_persistedPointers)
- accessor.WritePointer(address, pointer);
-}
-
std::shared_ptr<MappedFileAccessor> WeakFileAccessor::lock()
{
auto sharedPtr = m_weakPtr.lock();
@@ -93,20 +78,12 @@ std::shared_ptr<MappedFileAccessor> WeakFileAccessor::lock()
m_weakPtr = FileAccessorCache::Global().Open(m_filePath).m_weakPtr;
sharedPtr = m_weakPtr.lock();
- // Apply any previously written pointers back.
- if (sharedPtr)
- m_writeLog->ApplyWrites(*sharedPtr);
+ // Call the function registered with `RegisterReviveCallback`.
+ // TODO: This races if two functions cannot acquire and revive the same file at the same time.
+ // TODO: This will be called twice.
+ if (m_reviveCallback.has_value())
+ (*m_reviveCallback)(*sharedPtr);
}
return sharedPtr;
-}
-
-void WeakFileAccessor::WritePointer(const size_t address, const size_t pointer)
-{
- // Persist the pointer after the file accessor is revived.
- m_writeLog->AddPointer(address, pointer);
-
- // And then actually apply the written pointer...
- if (auto sharedPtr = m_weakPtr.lock())
- sharedPtr->WritePointer(address, pointer);
-}
+} \ No newline at end of file
diff --git a/view/sharedcache/core/FileAccessorCache.h b/view/sharedcache/core/FileAccessorCache.h
index 0ef6595a..811aea78 100644
--- a/view/sharedcache/core/FileAccessorCache.h
+++ b/view/sharedcache/core/FileAccessorCache.h
@@ -37,48 +37,36 @@ public:
// Adjust the cache size limit.
// This will NOT evict current cache entries, as they are already available.
// Any subsequent call to `Open` will assume this cache size, evicting until the size is equal to the cache size.
- void SetCacheSize(uint64_t size) { m_cacheSize = size; };
-};
-
-// Write log to be used in conjunction with `WeakFileAccessor` to re-apply written data to a "revived" file.
-struct FileAccessorWriteLog
-{
- // To persist writes to a file accessor being revived (within the lock() function)
- // we keep a list of writes that will be re-applied in the lock function.
- std::shared_mutex m_persistedMutex;
- std::vector<std::pair<size_t, uint64_t>> m_persistedPointers;
-
- FileAccessorWriteLog() = default;
+ void SetCacheSize(const uint64_t size) { m_cacheSize = size; };
- // Add the pointer to the persisted pointers.
- void AddPointer(size_t address, size_t pointer);
-
- // Apply all logged writes to the given accessor.
- void ApplyWrites(MappedFileAccessor& accessor);
+ size_t GetCacheSize() const { return m_cacheSize; }
};
class WeakFileAccessor
{
+ using ReviveCallback = std::function<void(MappedFileAccessor&)>;
+
// Weak pointer to the mapped file accessor, once this is expired we will re-open.
std::weak_ptr<MappedFileAccessor> m_weakPtr;
// File path for re-opening if needed
std::string m_filePath;
// Used to re-add writes once the file accessor is "revived".
- std::shared_ptr<FileAccessorWriteLog> m_writeLog;
+ std::optional<ReviveCallback> m_reviveCallback;
// TODO: Store a weak_ptr/shared_ptr to FileAccessorCache? That way we dont access Global()
// TODO: Only need to do the above if we want multiple caches.
public:
explicit WeakFileAccessor(std::weak_ptr<MappedFileAccessor> weakPtr, std::string filePath) :
- m_weakPtr(std::move(weakPtr)), m_filePath(std::move(filePath)),
- m_writeLog(std::make_shared<FileAccessorWriteLog>())
+ m_weakPtr(std::move(weakPtr)), m_filePath(std::move(filePath))
{}
- std::shared_ptr<MappedFileAccessor> lock();
+ // Register the function to be called once the file accessor is revived, this is typically
+ // used to re-apply writes such as from slide info.
+ void RegisterReviveCallback(const ReviveCallback& callback) {
+ m_reviveCallback = callback;
+ }
- // Persists the written pointer within this weak file accessor.
- // This works as we expect the weak file accessor to be stored per virtual memory region.
- void WritePointer(size_t address, size_t pointer);
+ std::shared_ptr<MappedFileAccessor> lock();
};
diff --git a/view/sharedcache/core/MappedFileAccessor.cpp b/view/sharedcache/core/MappedFileAccessor.cpp
index 3c9bf1e2..4f9f99a8 100644
--- a/view/sharedcache/core/MappedFileAccessor.cpp
+++ b/view/sharedcache/core/MappedFileAccessor.cpp
@@ -1,6 +1,5 @@
#include "MappedFileAccessor.h"
-
std::shared_ptr<MappedFileAccessor> MappedFileAccessor::Open(const std::string& filePath)
{
auto file = MappedFile::OpenFile(filePath);
@@ -40,48 +39,48 @@ std::string MappedFileAccessor::ReadNullTermString(size_t address, const size_t
return str;
}
-uint8_t MappedFileAccessor::ReadUInt8(size_t address)
+uint8_t MappedFileAccessor::ReadUInt8(size_t address) const
{
return Read<uint8_t>(address);
}
-int8_t MappedFileAccessor::ReadInt8(size_t address)
+int8_t MappedFileAccessor::ReadInt8(size_t address) const
{
return Read<int8_t>(address);
}
-uint16_t MappedFileAccessor::ReadUInt16(size_t address)
+uint16_t MappedFileAccessor::ReadUInt16(size_t address) const
{
return Read<uint16_t>(address);
}
-int16_t MappedFileAccessor::ReadInt16(size_t address)
+int16_t MappedFileAccessor::ReadInt16(size_t address) const
{
return Read<int16_t>(address);
}
-uint32_t MappedFileAccessor::ReadUInt32(size_t address)
+uint32_t MappedFileAccessor::ReadUInt32(size_t address) const
{
return Read<uint32_t>(address);
}
-int32_t MappedFileAccessor::ReadInt32(size_t address)
+int32_t MappedFileAccessor::ReadInt32(size_t address) const
{
return Read<int32_t>(address);
}
-uint64_t MappedFileAccessor::ReadUInt64(size_t address)
+uint64_t MappedFileAccessor::ReadUInt64(size_t address) const
{
return Read<uint64_t>(address);
}
-int64_t MappedFileAccessor::ReadInt64(size_t address)
+int64_t MappedFileAccessor::ReadInt64(size_t address) const
{
return Read<int64_t>(address);
}
-BinaryNinja::DataBuffer MappedFileAccessor::ReadBuffer(size_t addr, size_t length)
+BinaryNinja::DataBuffer MappedFileAccessor::ReadBuffer(size_t addr, size_t length) const
{
if (addr + length > Length())
throw UnmappedAccessException(addr + length, Length());
@@ -104,7 +103,7 @@ void MappedFileAccessor::Read(void* dest, size_t addr, size_t length) const
}
template <typename T>
-T MappedFileAccessor::Read(size_t address)
+T MappedFileAccessor::Read(size_t address) const
{
T result;
Read(&result, address, sizeof(T));
diff --git a/view/sharedcache/core/MappedFileAccessor.h b/view/sharedcache/core/MappedFileAccessor.h
index 2bbf4ba9..a63d8506 100644
--- a/view/sharedcache/core/MappedFileAccessor.h
+++ b/view/sharedcache/core/MappedFileAccessor.h
@@ -70,23 +70,23 @@ public:
std::string ReadNullTermString(size_t address, size_t maxLength = -1) const;
- uint8_t ReadUInt8(size_t address);
+ uint8_t ReadUInt8(size_t address) const;
- int8_t ReadInt8(size_t address);
+ int8_t ReadInt8(size_t address) const;
- uint16_t ReadUInt16(size_t address);
+ uint16_t ReadUInt16(size_t address) const;
- int16_t ReadInt16(size_t address);
+ int16_t ReadInt16(size_t address) const;
- uint32_t ReadUInt32(size_t address);
+ uint32_t ReadUInt32(size_t address) const;
- int32_t ReadInt32(size_t address);
+ int32_t ReadInt32(size_t address) const;
- uint64_t ReadUInt64(size_t address);
+ uint64_t ReadUInt64(size_t address) const;
- int64_t ReadInt64(size_t address);
+ int64_t ReadInt64(size_t address) const;
- BinaryNinja::DataBuffer ReadBuffer(size_t addr, size_t length);
+ BinaryNinja::DataBuffer ReadBuffer(size_t addr, size_t length) const;
// Returns a range of pointers within the mapped memory region corresponding to
// {addr, length}.
@@ -99,5 +99,5 @@ public:
void Read(void* dest, size_t addr, size_t length) const;
template <typename T>
- T Read(size_t address);
+ T Read(size_t address) const;
};
diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp
index 9bbe51e4..c6950a84 100644
--- a/view/sharedcache/core/SharedCache.cpp
+++ b/view/sharedcache/core/SharedCache.cpp
@@ -397,10 +397,42 @@ void SharedCache::ProcessEntryRegions(const CacheEntry& entry)
}
}
-void SharedCache::ProcessEntrySlideInfo(const CacheEntry& entry)
+void SharedCache::ProcessEntrySlideInfo(const CacheEntry& entry) const
{
auto slideInfoProcessor = SlideInfoProcessor(GetBaseAddress());
- slideInfoProcessor.ProcessEntry(*m_vm, entry);
+
+ // This will be set for every associated `VirtualMemoryRegion` so that any accesses though the VM will be always be slid.
+ // NOTE: This MUST be called on the CacheEntry object owned by SharedCache, otherwise persistence through the `SharedCacheController` will not occur.
+ // NOTE: This will keep a copy of a processor in the `WeakFileAccessor` until that object is destroyed (likely view destruction).
+ // NOTE: This will keep a copy of the cache entry in the `WeakFileAccessor` until that object is destroyed (likely view destruction).
+ auto reviveCallback = [slideInfoProcessor, entry](MappedFileAccessor& revivedAccessor) {
+ slideInfoProcessor.ProcessEntry(revivedAccessor, entry);
+ };
+
+ // Use the current entry accessor, don't register the callback for this one as we want calls through the VM to be slid only.
+ // Actually process the slide info for this entry, everything else besides this is to support revived file accessors.
+ auto slideMappings = slideInfoProcessor.ProcessEntry(*entry.GetAccessor().lock(), entry);
+
+ // Register the revive callback for all virtual memory regions that have been slid.
+ // The reason we don't just set this on the entry accessor is that accessor is not consulted for anything really after
+ // this point, everything else will be going through the virtual memory, and because the callback is on the weak accessor
+ // reference and not the file accessor cache itself this matters.
+ auto vm = GetVirtualMemory();
+ for (const auto& mapping : slideMappings)
+ {
+ // Because the mapping address is a file offset for us to consult the virtual memory we must first call `GetMappedAddress`.
+ if (auto mappedMappingAddr = entry.GetMappedAddress(mapping.address))
+ {
+ if (auto vmRegion = vm->GetRegionAtAddress(*mappedMappingAddr))
+ {
+ // Ok we have the virtual memory region, lets register the callback on its accessor.
+ vmRegion->fileAccessor.RegisterReviveCallback(reviveCallback);
+ continue;
+ }
+ }
+
+ LogWarn("Failed to register revive callback for slide mapping %llx in entry '%s'", mapping.address, entry.GetFileName().c_str());
+ }
}
void SharedCache::ProcessSymbols()
diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h
index 3b9e9fe1..3f2caed5 100644
--- a/view/sharedcache/core/SharedCache.h
+++ b/view/sharedcache/core/SharedCache.h
@@ -134,9 +134,8 @@ class CacheEntry
// Mappings tell us _where_ to map the regions within the flat address space.
// Without this we wouldn't know where the entry is supposed to exist in the address space.
std::vector<dyld_cache_mapping_info> m_mappings {};
- // TODO: We really should remove this methinks.
- // TODO: Storing this here is basically useless? IDK
// Mapping of image path to image info, used within ProcessImagesAndRegions to add them to the cache.
+ // Also used to retrieve the image dependencies.
std::unordered_map<std::string, dyld_cache_image_info> m_images {};
public:
@@ -221,7 +220,7 @@ public:
SharedCache &operator=(SharedCache &&) noexcept = default;
uint64_t GetBaseAddress() const { return m_baseAddress; }
- std::shared_ptr<VirtualMemory> GetVirtualMemory() { return m_vm; }
+ std::shared_ptr<VirtualMemory> GetVirtualMemory() const { 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; }
@@ -245,7 +244,7 @@ public:
void ProcessEntryRegions(const CacheEntry& entry);
- void ProcessEntrySlideInfo(const CacheEntry& entry);
+ void ProcessEntrySlideInfo(const CacheEntry& entry) const;
// Construct the named symbols lookup map for use with `GetSymbolWithName`.
void ProcessSymbols();
diff --git a/view/sharedcache/core/SharedCacheView.cpp b/view/sharedcache/core/SharedCacheView.cpp
index a3e494e2..00caddd2 100644
--- a/view/sharedcache/core/SharedCacheView.cpp
+++ b/view/sharedcache/core/SharedCacheView.cpp
@@ -787,7 +787,8 @@ bool SharedCacheView::Init()
bool result = cacheProcessor.ProcessCache(sharedCache);
auto endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = endTime - startTime;
- logger->LogInfo("Processing %zu entries took %.3f seconds", sharedCache.GetEntries().size(), elapsed.count());
+ auto entryCount = sharedCache.GetEntries().size();
+ logger->LogInfo("Processing %zu entries took %.3f seconds", entryCount, elapsed.count());
if (!result)
{
@@ -800,6 +801,10 @@ bool SharedCacheView::Init()
// Skip all the other shared cache stuff.
return true;
}
+
+ // If we can't store all of our files for this cache in the accessor cache we might run into issues, warn the user.
+ if (entryCount > FileAccessorCache::Global().GetCacheSize())
+ logger->LogWarn("Cache contains more entries than the allowed number of opened file handles, this may impact reliability.");
}
{
diff --git a/view/sharedcache/core/SlideInfo.cpp b/view/sharedcache/core/SlideInfo.cpp
index 3ab83d33..9fcb88bd 100644
--- a/view/sharedcache/core/SlideInfo.cpp
+++ b/view/sharedcache/core/SlideInfo.cpp
@@ -6,11 +6,11 @@
SlideInfoProcessor::SlideInfoProcessor(uint64_t baseAddress)
{
- m_logger = new BinaryNinja::Logger("SlideInfoProcessor");
+ m_logger = new BinaryNinja::Logger("SlideInfo.Processor");
m_baseAddress = baseAddress;
}
-void ApplySlideInfoV5(VirtualMemory& vm, const SlideMappingInfo& mapping)
+void ApplySlideInfoV5(MappedFileAccessor& accessor, const SlideMappingInfo& mapping)
{
uint64_t pageStartsOffset = mapping.address + sizeof(dyld_cache_slide_info_v5);
uint64_t pageStartCount = mapping.slideInfoV5.page_starts_count;
@@ -19,7 +19,7 @@ void ApplySlideInfoV5(VirtualMemory& vm, const SlideMappingInfo& mapping)
auto cursor = pageStartsOffset;
for (size_t i = 0; i < pageStartCount; i++)
{
- uint16_t delta = vm.ReadUInt16(cursor);
+ uint16_t delta = accessor.ReadUInt16(cursor);
cursor += sizeof(uint16_t);
if (delta == DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE)
continue;
@@ -29,23 +29,23 @@ void ApplySlideInfoV5(VirtualMemory& vm, const SlideMappingInfo& mapping)
do
{
loc += delta * sizeof(dyld_cache_slide_pointer5);
- dyld_cache_slide_pointer5 slideInfo = {vm.ReadUInt64(loc)};
+ dyld_cache_slide_pointer5 slideInfo = {accessor.ReadUInt64(loc)};
delta = slideInfo.regular.next;
if (slideInfo.auth.auth)
{
uint64_t value = mapping.slideInfoV5.value_add + slideInfo.auth.runtimeOffset;
- vm.WritePointer(loc, value);
+ accessor.WritePointer(loc, value);
}
else
{
uint64_t value = mapping.slideInfoV5.value_add + slideInfo.regular.runtimeOffset;
- vm.WritePointer(loc, value);
+ accessor.WritePointer(loc, value);
}
} while (delta != 0);
}
}
-void ApplySlideInfoV3(VirtualMemory& vm, const SlideMappingInfo& mapping)
+void ApplySlideInfoV3(MappedFileAccessor& accessor, const SlideMappingInfo& mapping)
{
uint64_t pageStartsOffset = mapping.address + sizeof(dyld_cache_slide_info_v3);
uint64_t pageStartCount = mapping.slideInfoV3.page_starts_count;
@@ -54,7 +54,7 @@ void ApplySlideInfoV3(VirtualMemory& vm, const SlideMappingInfo& mapping)
auto cursor = pageStartsOffset;
for (size_t i = 0; i < pageStartCount; i++)
{
- uint16_t delta = vm.ReadUInt16(cursor);
+ uint16_t delta = accessor.ReadUInt16(cursor);
cursor += sizeof(uint16_t);
if (delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE)
continue;
@@ -64,14 +64,14 @@ void ApplySlideInfoV3(VirtualMemory& vm, const SlideMappingInfo& mapping)
do
{
loc += delta * sizeof(dyld_cache_slide_pointer3);
- dyld_cache_slide_pointer3 slideInfo = {vm.ReadUInt64(loc)};
+ dyld_cache_slide_pointer3 slideInfo = {accessor.ReadUInt64(loc)};
delta = slideInfo.plain.offsetToNextPointer;
if (slideInfo.auth.authenticated)
{
uint64_t value = slideInfo.auth.offsetFromSharedCacheBase;
value += mapping.slideInfoV3.auth_value_add;
- vm.WritePointer(loc, value);
+ accessor.WritePointer(loc, value);
}
else
{
@@ -79,13 +79,13 @@ void ApplySlideInfoV3(VirtualMemory& vm, const SlideMappingInfo& mapping)
uint64_t top8Bits = value51 & 0x0007F80000000000;
uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFF;
uint64_t value = (uint64_t)top8Bits << 13 | bottom43Bits;
- vm.WritePointer(loc, value);
+ accessor.WritePointer(loc, value);
}
} while (delta != 0);
}
}
-void ApplySlideInfoV2(VirtualMemory& vm, const SlideMappingInfo& mapping)
+void ApplySlideInfoV2(MappedFileAccessor& accessor, const SlideMappingInfo& mapping)
{
auto rebaseChain = [&](const dyld_cache_slide_info_v2& slideInfo, uint64_t pageContent, uint16_t startOffset) {
// TODO: This is always zero?
@@ -103,7 +103,7 @@ void ApplySlideInfoV2(VirtualMemory& vm, const SlideMappingInfo& mapping)
while (delta != 0)
{
uint64_t loc = pageContent + pageOffset;
- uintptr_t rawValue = vm.ReadUInt64(loc);
+ uintptr_t rawValue = accessor.ReadUInt64(loc);
delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
uintptr_t value = (rawValue & valueMask);
if (value != 0)
@@ -114,7 +114,7 @@ void ApplySlideInfoV2(VirtualMemory& vm, const SlideMappingInfo& mapping)
value += slideAmount;
}
pageOffset += delta;
- vm.WritePointer(loc, value);
+ accessor.WritePointer(loc, value);
}
};
@@ -126,7 +126,7 @@ void ApplySlideInfoV2(VirtualMemory& vm, const SlideMappingInfo& mapping)
auto cursor = pageStartsOffset;
for (size_t i = 0; i < pageStartCount; i++)
{
- uint16_t start = vm.ReadUInt16(cursor);
+ uint16_t start = accessor.ReadUInt16(cursor);
cursor += sizeof(uint16_t);
if (start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE)
continue;
@@ -138,7 +138,7 @@ void ApplySlideInfoV2(VirtualMemory& vm, const SlideMappingInfo& mapping)
do
{
uint64_t extraCursor = extrasOffset + (j * sizeof(uint16_t));
- auto extra = vm.ReadUInt16(extraCursor);
+ auto extra = accessor.ReadUInt16(extraCursor);
uint16_t aStart = extra;
uint64_t page = mapping.mappingInfo.fileOffset + (pageSize * i);
uint16_t pageStartOffset = (aStart & 0x3FFF) * 4;
@@ -156,20 +156,15 @@ void ApplySlideInfoV2(VirtualMemory& vm, const SlideMappingInfo& mapping)
}
}
-std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(VirtualMemory& vm, const CacheEntry& entry) const
+std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(const MappedFileAccessor& accessor, const CacheEntry& entry) const
{
auto& baseHeader = entry.GetHeader();
// Handle legacy, single mapping slide info.
if (baseHeader.slideInfoOffsetUnused)
{
- auto slideInfoAddress = entry.GetMappedAddress(baseHeader.slideInfoOffsetUnused);
- if (!slideInfoAddress.has_value())
- {
- m_logger->LogError("Unmapped slide info address %llx", slideInfoAddress);
- return {};
- }
- auto slideInfoVersion = vm.ReadUInt32(*slideInfoAddress);
+ auto slideInfoAddress = baseHeader.slideInfoOffsetUnused;
+ auto slideInfoVersion = accessor.ReadUInt32(slideInfoAddress);
if (slideInfoVersion != 2 && slideInfoVersion != 3)
{
m_logger->LogError("Unsupported slide info version %d", slideInfoVersion);
@@ -177,24 +172,15 @@ std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(VirtualMemory& v
}
SlideMappingInfo singleMapping = {};
- singleMapping.address = *slideInfoAddress;
-
- auto mappingAddress = entry.GetMappedAddress(baseHeader.mappingOffset + sizeof(dyld_cache_mapping_info));
- if (!mappingAddress.has_value())
- {
- m_logger->LogError("Unmapped mapping address %llx", mappingAddress);
- return {};
- }
- vm.Read(&singleMapping.mappingInfo, *mappingAddress, sizeof(dyld_cache_mapping_info));
+ singleMapping.address = slideInfoAddress;
singleMapping.slideInfoVersion = slideInfoVersion;
+
+ auto mappingAddress = baseHeader.mappingOffset + sizeof(dyld_cache_mapping_info);
+ accessor.Read(&singleMapping.mappingInfo, mappingAddress, sizeof(dyld_cache_mapping_info));
if (singleMapping.slideInfoVersion == 2)
- vm.Read(&singleMapping.slideInfoV2, *slideInfoAddress, sizeof(dyld_cache_slide_info_v2));
+ accessor.Read(&singleMapping.slideInfoV2, slideInfoAddress, sizeof(dyld_cache_slide_info_v2));
else if (singleMapping.slideInfoVersion == 3)
- vm.Read(&singleMapping.slideInfoV3, *slideInfoAddress, sizeof(dyld_cache_slide_info_v3));
-
- // Adjust the file offset to mapped, we are expecting the consumer to just take it in as mapped.
- // if we did not make it mapped, then we would need to do all this weirdness in every consumer (each slide info version)
- singleMapping.mappingInfo.fileOffset = *entry.GetMappedAddress(singleMapping.mappingInfo.fileOffset);
+ accessor.Read(&singleMapping.slideInfoV3, slideInfoAddress, sizeof(dyld_cache_slide_info_v3));
return {singleMapping};
}
@@ -203,36 +189,29 @@ std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(VirtualMemory& v
for (auto i = 0; i < baseHeader.mappingWithSlideCount; i++)
{
dyld_cache_mapping_and_slide_info mappingAndSlideInfo = {};
- auto mappingAndSlideInfoAddress =
- entry.GetMappedAddress(baseHeader.mappingWithSlideOffset + (i * sizeof(dyld_cache_mapping_and_slide_info)));
- if (!mappingAndSlideInfoAddress.has_value())
- {
- m_logger->LogError("Unmapped mapping and slide info address %llx", mappingAndSlideInfoAddress);
- continue;
- }
-
- vm.Read(&mappingAndSlideInfo, *mappingAndSlideInfoAddress, sizeof(dyld_cache_mapping_and_slide_info));
+ auto mappingAndSlideInfoAddress = baseHeader.mappingWithSlideOffset + (i * sizeof(dyld_cache_mapping_and_slide_info));
+ accessor.Read(&mappingAndSlideInfo, mappingAndSlideInfoAddress, sizeof(dyld_cache_mapping_and_slide_info));
if (mappingAndSlideInfo.size == 0 || mappingAndSlideInfo.slideInfoFileOffset == 0)
continue;
SlideMappingInfo map = {};
- map.address = *entry.GetMappedAddress(mappingAndSlideInfo.slideInfoFileOffset);
- map.slideInfoVersion = vm.ReadUInt32(map.address);
+ map.address = mappingAndSlideInfo.slideInfoFileOffset;
+ map.slideInfoVersion = accessor.ReadUInt32(map.address);
map.mappingInfo.address = mappingAndSlideInfo.address;
map.mappingInfo.size = mappingAndSlideInfo.size;
- map.mappingInfo.fileOffset = *entry.GetMappedAddress(mappingAndSlideInfo.fileOffset);
+ map.mappingInfo.fileOffset = mappingAndSlideInfo.fileOffset;
if (map.slideInfoVersion == 2)
{
- vm.Read(&map.slideInfoV2, map.address, sizeof(dyld_cache_slide_info_v2));
+ accessor.Read(&map.slideInfoV2, map.address, sizeof(dyld_cache_slide_info_v2));
}
else if (map.slideInfoVersion == 3)
{
- vm.Read(&map.slideInfoV3, map.address, sizeof(dyld_cache_slide_info_v3));
+ accessor.Read(&map.slideInfoV3, map.address, sizeof(dyld_cache_slide_info_v3));
map.slideInfoV3.auth_value_add = m_baseAddress;
}
else if (map.slideInfoVersion == 5)
{
- vm.Read(&map.slideInfoV5, map.address, sizeof(dyld_cache_slide_info_v5));
+ accessor.Read(&map.slideInfoV5, map.address, sizeof(dyld_cache_slide_info_v5));
map.slideInfoV5.value_add = m_baseAddress;
}
else
@@ -251,21 +230,28 @@ std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(VirtualMemory& v
return mappings;
}
-void SlideInfoProcessor::ApplyMappings(VirtualMemory& vm, const std::vector<SlideMappingInfo>& mappings)
+void SlideInfoProcessor::ApplyMappings(MappedFileAccessor& accessor, const std::vector<SlideMappingInfo>& mappings) const
{
+ // TODO: HACK: to prevent applying slide info twice we use check to see if the accessor has been written to
+ // TODO: prior to this function, because this is currently the ONLY place where writes happen this is safe
+ // TODO: but if we add more places that write to the accessor than we will need to likely need to be more specific
+ // TODO: and/or have the backing file accessor store some additional state.
+ if (accessor.IsDirty())
+ return;
+
// Apply the slide information to the mapped file.
for (const auto& mapping : mappings)
{
switch (mapping.slideInfoVersion)
{
case 2:
- ApplySlideInfoV2(vm, mapping);
+ ApplySlideInfoV2(accessor, mapping);
break;
case 3:
- ApplySlideInfoV3(vm, mapping);
+ ApplySlideInfoV3(accessor, mapping);
break;
case 5:
- ApplySlideInfoV5(vm, mapping);
+ ApplySlideInfoV5(accessor, mapping);
break;
default:
m_logger->LogError(
@@ -275,16 +261,19 @@ void SlideInfoProcessor::ApplyMappings(VirtualMemory& vm, const std::vector<Slid
}
}
-void SlideInfoProcessor::ProcessEntry(VirtualMemory& vm, const CacheEntry& entry)
+std::vector<SlideMappingInfo> SlideInfoProcessor::ProcessEntry(MappedFileAccessor& accessor, const CacheEntry& entry) const
{
try
{
- ApplyMappings(vm, ReadEntryInfo(vm, entry));
+ auto slideMappings = ReadEntryInfo(accessor, entry);
+ ApplyMappings(accessor, slideMappings);
+ return slideMappings;
}
catch (const std::exception& e)
{
// Just log an error, we technically can continue as if the slide info is not applied for a given entry it does
// not necessarily mean we cannot do analysis on others.
m_logger->LogError("Error processing slide info for entry `%s`: %s", entry.GetFileName().c_str(), e.what());
+ return {};
}
}
diff --git a/view/sharedcache/core/SlideInfo.h b/view/sharedcache/core/SlideInfo.h
index bc165589..75134985 100644
--- a/view/sharedcache/core/SlideInfo.h
+++ b/view/sharedcache/core/SlideInfo.h
@@ -33,10 +33,10 @@ class SlideInfoProcessor
public:
explicit SlideInfoProcessor(uint64_t baseAddress);
- std::vector<SlideMappingInfo> ReadEntryInfo(VirtualMemory& vm, const CacheEntry& entry) const;
+ std::vector<SlideMappingInfo> ReadEntryInfo(const MappedFileAccessor& accessor, const CacheEntry& entry) const;
// Write the slide information back to the entries memory mapped regions.
- void ApplyMappings(VirtualMemory& vm, const std::vector<SlideMappingInfo>& mappings);
+ void ApplyMappings(MappedFileAccessor& accessor, const std::vector<SlideMappingInfo>& mappings) const;
- void ProcessEntry(VirtualMemory& vm, const CacheEntry& entry);
+ std::vector<SlideMappingInfo> ProcessEntry(MappedFileAccessor& accessor, const CacheEntry& entry) const;
};
diff --git a/view/sharedcache/core/VirtualMemory.cpp b/view/sharedcache/core/VirtualMemory.cpp
index b55ddefe..60ed1481 100644
--- a/view/sharedcache/core/VirtualMemory.cpp
+++ b/view/sharedcache/core/VirtualMemory.cpp
@@ -1,9 +1,5 @@
#include "VirtualMemory.h"
-// TODO: Add back the ability to do relocations on this stuff.
-// TODO: ^ the above is currently handled only by the consumers of the VirtualMemory
-// TODO: however we might still want to have this be done here as well for persistence.
-
void VirtualMemory::MapRegion(WeakFileAccessor fileAccessor, AddressRange mappedRange, uint64_t fileOffset)
{
// Create a new VirtualMemoryRegion object
@@ -56,9 +52,7 @@ void VirtualMemory::WritePointer(size_t address, size_t pointer)
auto region = GetRegionAtAddress(address, offset);
if (!region.has_value())
throw UnmappedRegionException(address);
- // Unique to the `VirtualMemory::WritePointer` we actually have an interface on the WeakFileAccessor to use.
- // this is the mechanism for persisting our written pointers.
- region->fileAccessor.WritePointer(offset, pointer);
+ region->fileAccessor.lock()->WritePointer(offset, pointer);
}
std::string VirtualMemory::ReadCString(uint64_t address)
@@ -277,7 +271,7 @@ uint8_t VirtualMemoryReader::ReadUInt8(uint64_t address)
int8_t VirtualMemoryReader::ReadInt8()
{
- return ReadUInt8(m_cursor);
+ return ReadInt8(m_cursor);
}
int8_t VirtualMemoryReader::ReadInt8(uint64_t address)
diff --git a/view/sharedcache/core/VirtualMemory.h b/view/sharedcache/core/VirtualMemory.h
index 43a9acb4..2edc6ef3 100644
--- a/view/sharedcache/core/VirtualMemory.h
+++ b/view/sharedcache/core/VirtualMemory.h
@@ -55,10 +55,7 @@ public:
bool IsAddressMapped(uint64_t address);
- // TODO: Bulk pointer writes here would alleviate a lot of the time spent in the slide info processor.
- // Write a pointer at a given address. This pointer will be persisted
- // for a given `VirtualMemoryRegion` region, unlike using the MappedFileAccessor directly.
- // The persistence is provided through the WeakFileAccessor itself and thus is unique to the construction.
+ // Write a pointer at a given address. This pointer is never persisted when a file accessor is closed.
void WritePointer(size_t address, size_t pointer);
std::string ReadCString(uint64_t address);