diff options
| author | Mason Reed <mason@vector35.com> | 2025-04-06 20:00:00 -0400 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2025-04-06 20:00:37 -0400 |
| commit | c22d54c5d95f4d23629484579414387b0c7503ee (patch) | |
| tree | 933a58c8ed3518f507f5bd4d496364411273f5b9 /view/sharedcache | |
| parent | 4f22a944d77f784b53ee3a37d9f1966ede1c98a4 (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.cpp | 37 | ||||
| -rw-r--r-- | view/sharedcache/core/FileAccessorCache.h | 36 | ||||
| -rw-r--r-- | view/sharedcache/core/MappedFileAccessor.cpp | 21 | ||||
| -rw-r--r-- | view/sharedcache/core/MappedFileAccessor.h | 20 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCache.cpp | 36 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCache.h | 7 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCacheView.cpp | 7 | ||||
| -rw-r--r-- | view/sharedcache/core/SlideInfo.cpp | 109 | ||||
| -rw-r--r-- | view/sharedcache/core/SlideInfo.h | 6 | ||||
| -rw-r--r-- | view/sharedcache/core/VirtualMemory.cpp | 10 | ||||
| -rw-r--r-- | view/sharedcache/core/VirtualMemory.h | 5 |
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); |
