diff options
Diffstat (limited to 'view/sharedcache')
| -rw-r--r-- | view/sharedcache/core/DSCView.cpp | 2 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCache.cpp | 68 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCache.h | 6 | ||||
| -rw-r--r-- | view/sharedcache/core/VM.cpp | 201 | ||||
| -rw-r--r-- | view/sharedcache/core/VM.h | 110 |
5 files changed, 209 insertions, 178 deletions
diff --git a/view/sharedcache/core/DSCView.cpp b/view/sharedcache/core/DSCView.cpp index c0b7b349..974e3c4e 100644 --- a/view/sharedcache/core/DSCView.cpp +++ b/view/sharedcache/core/DSCView.cpp @@ -28,8 +28,6 @@ DSCView::DSCView(const std::string& typeName, BinaryView* data, bool parseOnly) DSCView::~DSCView() { - if (!m_parseOnly) - MMappedFileAccessor::CloseAll(GetFile()->GetSessionId()); } enum DSCPlatform { diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index bfc06db9..224097f5 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -215,7 +215,7 @@ uint64_t SharedCache::FastGetBackingCacheCount(BinaryNinja::Ref<BinaryNinja::Bin { std::shared_ptr<MMappedFileAccessor> baseFile; try { - baseFile = MMappedFileAccessor::Open(dscView, dscView->GetFile()->GetSessionId(), dscView->GetFile()->GetOriginalFilename())->lock(); + baseFile = MapFileWithoutApplyingSlide(dscView->GetFile()->GetOriginalFilename()); } catch (...){ LogError("Shared Cache preload: Failed to open file %s", dscView->GetFile()->GetOriginalFilename().c_str()); @@ -278,7 +278,7 @@ void SharedCache::PerformInitialLoad() { m_logger->LogInfo("Performing initial load of Shared Cache"); auto path = m_dscView->GetFile()->GetOriginalFilename(); - auto baseFile = MMappedFileAccessor::Open(m_dscView, m_dscView->GetFile()->GetSessionId(), path)->lock(); + auto baseFile = MapFileWithoutApplyingSlide(path); m_viewSpecificState->progress = LoadProgressLoadingCaches; @@ -363,7 +363,7 @@ void SharedCache::PerformInitialLoad() for (auto address : addresses) { i++; - auto vm = GetVMMap(true); + auto vm = GetVMMap(); auto machoHeader = SharedCache::LoadHeaderForAddress(vm, address, "dyld_shared_cache_branch_islands_" + std::to_string(i)); if (machoHeader) { @@ -451,7 +451,7 @@ void SharedCache::PerformInitialLoad() subCachePath = path + "." + entry.fileExtension; subCacheFilename = mainFileName + "." + entry.fileExtension; } - auto subCacheFile = MMappedFileAccessor::Open(m_dscView, m_dscView->GetFile()->GetSessionId(), subCachePath)->lock(); + auto subCacheFile = MapFileWithoutApplyingSlide(subCachePath); dyld_cache_header subCacheHeader {}; uint64_t headerSize = subCacheFile->ReadUInt32(16); @@ -537,7 +537,7 @@ void SharedCache::PerformInitialLoad() { auto subCachePath = path + "." + std::to_string(i); auto subCacheFilename = mainFileName + "." + std::to_string(i); - auto subCacheFile = MMappedFileAccessor::Open(m_dscView, m_dscView->GetFile()->GetSessionId(), subCachePath)->lock(); + auto subCacheFile = MapFileWithoutApplyingSlide(subCachePath); dyld_cache_header subCacheHeader {}; uint64_t headerSize = subCacheFile->ReadUInt32(16); @@ -582,7 +582,7 @@ void SharedCache::PerformInitialLoad() // Load .symbols subcache try { auto subCachePath = path + ".symbols"; - auto subCacheFile = MMappedFileAccessor::Open(m_dscView, m_dscView->GetFile()->GetSessionId(), subCachePath)->lock(); + auto subCacheFile = MapFileWithoutApplyingSlide(subCachePath); dyld_cache_header subCacheHeader {}; uint64_t headerSize = subCacheFile->ReadUInt32(16); @@ -679,7 +679,7 @@ void SharedCache::PerformInitialLoad() subCacheFilename = mainFileName + "." + entry.fileExtension; } - auto subCacheFile = MMappedFileAccessor::Open(m_dscView, m_dscView->GetFile()->GetSessionId(), subCachePath)->lock(); + auto subCacheFile = MapFileWithoutApplyingSlide(subCachePath); dyld_cache_header subCacheHeader {}; uint64_t headerSize = subCacheFile->ReadUInt32(16); @@ -738,7 +738,7 @@ void SharedCache::PerformInitialLoad() try { auto subCachePath = path + ".symbols"; - auto subCacheFile = MMappedFileAccessor::Open(m_dscView, m_dscView->GetFile()->GetSessionId(), subCachePath)->lock(); + auto subCacheFile = MapFileWithoutApplyingSlide(subCachePath); dyld_cache_header subCacheHeader {}; uint64_t headerSize = subCacheFile->ReadUInt32(16); if (subCacheFile->ReadUInt32(16) > sizeof(dyld_cache_header)) @@ -774,7 +774,7 @@ void SharedCache::PerformInitialLoad() // We have set up enough metadata to map VM now. - auto vm = GetVMMap(true); + auto vm = GetVMMap(); if (!vm) { m_logger->LogError("Failed to map VM pages for Shared Cache on initial load, this is fatal."); @@ -973,21 +973,16 @@ void SharedCache::PerformInitialLoad() m_viewSpecificState->progress = LoadProgressFinished; } -std::shared_ptr<VM> SharedCache::GetVMMap(bool mapPages) +std::shared_ptr<VM> SharedCache::GetVMMap() { std::shared_ptr<VM> vm = std::make_shared<VM>(0x1000); - if (mapPages) - { - for (const auto& cache : State().backingCaches) - { - for (const auto& mapping : cache.mappings) - { - vm->MapPages(m_dscView, m_dscView->GetFile()->GetSessionId(), mapping.address, mapping.fileOffset, mapping.size, cache.path, - [this, vm=vm](std::shared_ptr<MMappedFileAccessor> mmap){ - ParseAndApplySlideInfoForFile(mmap); - }); - } + for (const auto& cache : State().backingCaches) { + for (const auto& mapping : cache.mappings) { + vm->MapPages(m_dscView, m_dscView->GetFile()->GetSessionId(), mapping.address, mapping.fileOffset, mapping.size, cache.path, + [this, vm=vm](std::shared_ptr<MMappedFileAccessor> mmap){ + ParseAndApplySlideInfoForFile(mmap); + }); } } @@ -1565,7 +1560,6 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address) } m_logger->LogInfo("Loading stub island %s @ 0x%llx", stubIsland.prettyName.c_str(), stubIsland.start); auto targetFile = vm->MappingAtAddress(stubIsland.start).first.fileAccessor->lock(); - ParseAndApplySlideInfoForFile(targetFile); auto reader = VMReader(vm); auto buff = reader.ReadBuffer(stubIsland.start, stubIsland.size); m_dscView->GetMemoryMap()->AddDataMemoryRegion(stubIsland.prettyName, stubIsland.start, buff, SegmentReadable | SegmentExecutable); @@ -1594,7 +1588,6 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address) } m_logger->LogInfo("Loading dyld data %s", dyldData.prettyName.c_str()); auto targetFile = vm->MappingAtAddress(dyldData.start).first.fileAccessor->lock(); - ParseAndApplySlideInfoForFile(targetFile); auto reader = VMReader(vm); auto buff = reader.ReadBuffer(dyldData.start, dyldData.size); m_dscView->GetMemoryMap()->AddDataMemoryRegion(dyldData.prettyName, dyldData.start, buff, SegmentReadable); @@ -1623,7 +1616,6 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address) } m_logger->LogInfo("Loading non-image region %s", region.prettyName.c_str()); auto targetFile = vm->MappingAtAddress(region.start).first.fileAccessor->lock(); - ParseAndApplySlideInfoForFile(targetFile); auto reader = VMReader(vm); auto buff = reader.ReadBuffer(region.start, region.size); m_dscView->GetMemoryMap()->AddDataMemoryRegion(region.prettyName, region.start, buff, region.flags); @@ -1652,7 +1644,6 @@ bool SharedCache::LoadSectionAtAddress(uint64_t address) m_logger->LogDebug("Partial loading image %s", targetHeader.installName.c_str()); auto targetFile = vm->MappingAtAddress(targetSegment->start).first.fileAccessor->lock(); - ParseAndApplySlideInfoForFile(targetFile); auto buff = reader.ReadBuffer(targetSegment->start, targetSegment->size); m_dscView->GetMemoryMap()->AddDataMemoryRegion(targetSegment->prettyName, targetSegment->start, buff, targetSegment->flags); @@ -1803,8 +1794,6 @@ bool SharedCache::LoadImageWithInstallName(std::string installName, bool skipObj } auto targetFile = vm->MappingAtAddress(region.start).first.fileAccessor->lock(); - ParseAndApplySlideInfoForFile(targetFile); - auto buff = reader.ReadBuffer(region.start, region.size); region.loaded = true; @@ -2847,12 +2836,7 @@ std::vector<std::pair<std::string, Ref<Symbol>>> SharedCache::LoadAllSymbolsAndW auto header = HeaderForAddress(img.headerLocation); auto exportList = GetExportListForHeader(*header, [&]() { try { - auto mapping = MMappedFileAccessor::Open( - m_dscView, - m_dscView->GetFile()->GetSessionId(), - header->exportTriePath - )->lock(); - return mapping; + return MapFile(header->exportTriePath); } catch (...) { @@ -2970,7 +2954,7 @@ void SharedCache::FindSymbolAtAddrAndApplyToAddr( auto exportList = GetExportListForHeader(*header, [&]() { try { - return MMappedFileAccessor::Open(m_dscView, m_dscView->GetFile()->GetSessionId(), header->exportTriePath)->lock(); + return MapFile(header->exportTriePath); } catch (...) { @@ -3307,7 +3291,7 @@ extern "C" if (cache->object) { try { - auto vm = cache->object->GetVMMap(true); + auto vm = cache->object->GetVMMap(); auto viewImageHeaders = cache->object->AllImageHeaders(); *count = viewImageHeaders.size(); BNDSCImage* images = new BNDSCImage[viewImageHeaders.size()]; @@ -3386,7 +3370,7 @@ extern "C" BNDSCMemoryUsageInfo BNDSCViewGetMemoryUsageInfo() { BNDSCMemoryUsageInfo info; - info.mmapRefs = mmapCount.load(); + info.mmapRefs = MMapCount(); info.sharedCacheRefs = sharedCacheReferences.load(); return info; } @@ -3695,4 +3679,16 @@ size_t SharedCache::GetObjCRelativeMethodBaseAddress(const VMReader& reader) con return 0; } +std::shared_ptr<MMappedFileAccessor> SharedCache::MapFile(const std::string& path) +{ + return MMappedFileAccessor:: + Open(m_dscView, m_dscView->GetFile()->GetSessionId(), path, [this](std::shared_ptr<MMappedFileAccessor> mmap) { + ParseAndApplySlideInfoForFile(mmap); + })->lock(); +} + +std::shared_ptr<MMappedFileAccessor> SharedCache::MapFileWithoutApplyingSlide(const std::string& path) { + return std::make_shared<MMappedFileAccessor>(path); +} + } // namespace SharedCacheCore diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h index 1b3f8261..1ecfd885 100644 --- a/view/sharedcache/core/SharedCache.h +++ b/view/sharedcache/core/SharedCache.h @@ -588,13 +588,14 @@ namespace SharedCacheCore { void DeserializeFromRawView(); public: - std::shared_ptr<VM> GetVMMap(bool mapPages = true); + std::shared_ptr<VM> GetVMMap(); static SharedCache* GetFromDSCView(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView); static uint64_t FastGetBackingCacheCount(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView); bool SaveToDSCView(); void ParseAndApplySlideInfoForFile(std::shared_ptr<MMappedFileAccessor> file); + std::optional<uint64_t> GetImageStart(std::string installName); std::optional<SharedCacheMachOHeader> HeaderForAddress(uint64_t); bool LoadImageWithInstallName(std::string installName, bool skipObjC); @@ -655,6 +656,9 @@ private: // Must be called before first access to `MutableState()` after the state // is loaded from the cache. Can safely be called multiple times. void WillMutateState(); + + std::shared_ptr<MMappedFileAccessor> MapFile(const std::string& path); + static std::shared_ptr<MMappedFileAccessor> MapFileWithoutApplyingSlide(const std::string& path); }; } diff --git a/view/sharedcache/core/VM.cpp b/view/sharedcache/core/VM.cpp index 4a3acf66..deab7b0f 100644 --- a/view/sharedcache/core/VM.cpp +++ b/view/sharedcache/core/VM.cpp @@ -50,17 +50,15 @@ #include <sys/resource.h> #endif -void VMShutdown() -{ - std::unique_lock<std::mutex> lock2(fileAccessorsMutex); - std::unique_lock<std::mutex> lock(fileAccessorDequeMutex); +static std::atomic<uint64_t> mmapCount = 0; - // This will trigger the deallocation logic for these. - // It is background threaded to avoid a deadlock on exit. - fileAccessorReferenceHolder.clear(); - fileAccessors.clear(); +uint64_t MMapCount() +{ + return mmapCount; } +void VMShutdown() {} + std::string ResolveFilePath(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView, const std::string& path) { @@ -203,92 +201,121 @@ void MMAP::Unmap() #endif } +FileAccessorCache& FileAccessorCache::Shared() +{ + static FileAccessorCache& shared = *new FileAccessorCache; + return shared; +}; + +FileAccessorCache::FileAccessorCache() = default; -std::shared_ptr<LazyMappedFileAccessor> MMappedFileAccessor::Open(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView, const uint64_t sessionID, const std::string &path, std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine) +void FileAccessorCache::SetCacheSize(uint64_t cacheSize) { - std::scoped_lock<std::mutex> lock(fileAccessorsMutex); - if (auto it = fileAccessors.find(path); it != fileAccessors.end()) { - return it->second; + m_cacheSize = cacheSize; + EvictFromCacheIfNeeded(); +} + +void FileAccessorCache::RecordAccess(const std::string& path) +{ + auto it = m_accessors.find(path); + if (it == m_accessors.end()) { + return; + } + + // Move the entry for `path` to the end of `m_leastRecentlyOpened`. + m_leastRecentlyOpened.splice(m_leastRecentlyOpened.end(), m_leastRecentlyOpened, it->second.second); +} + +void FileAccessorCache::EvictFromCacheIfNeeded() +{ + std::lock_guard lock(m_mutex); + while (m_accessors.size() > m_cacheSize) { + m_accessors.erase(m_leastRecentlyOpened.front()); + m_leastRecentlyOpened.pop_front(); } +} - auto fileAcccessor = std::make_shared<LazyMappedFileAccessor>( - path, - // Allocator logic for the SelfAllocatingWeakPtr - [path=path, sessionID=sessionID, dscView](){ - std::unique_lock<std::mutex> _lock(fileAccessorDequeMutex); +std::shared_ptr<LazyMappedFileAccessor> FileAccessorCache::OpenLazily(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView, + const uint64_t sessionID, const std::string& path, + std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine) +{ + std::lock_guard lock(m_mutex); + if (auto it = m_lazyAccessors.find(path); it != m_lazyAccessors.end()) { + RecordAccess(path); + return it->second; + } - // Iterate through held references and start removing them until we can get a file pointer - // FIXME: This could clear all currently used file pointers and still not get one. FIX! - // We should probably use a condition variable here to wait for a file pointer to be released!!! - for (auto& [_, fileAccessorDeque] : fileAccessorReferenceHolder) - { - if (fileAccessorSemaphore.try_acquire()) - break; - fileAccessorDeque.pop_front(); + auto accessor = std::make_shared<LazyMappedFileAccessor>(path, + [=, dscView = std::move(dscView), postAllocationRoutine = std::move(postAllocationRoutine)]( + const std::string& path) { + auto accessor = Open(dscView, sessionID, path); + if (postAllocationRoutine) { + postAllocationRoutine(accessor); } - - mmapCount++; - _lock.unlock(); - auto accessor = std::shared_ptr<MMappedFileAccessor>(new MMappedFileAccessor(ResolveFilePath(dscView, path)), [](MMappedFileAccessor* accessor){ - // worker thread or we can deadlock on exit here. - BinaryNinja::WorkerEnqueue([accessor](){ - fileAccessorSemaphore.release(); - mmapCount--; - if (fileAccessors.count(accessor->m_path)) - { - std::scoped_lock<std::mutex> lock(fileAccessorsMutex); - fileAccessors.erase(accessor->m_path); - } - delete accessor; - }, "MMappedFileAccessor Destructor"); - }); - _lock.lock(); - // If some background thread has managed to try and open a file when the BV was already closed, - // we can still give them the file they want so they dont crash, but as soon as they let go it's gone. - if (!blockedSessionIDs.count(sessionID)) - fileAccessorReferenceHolder[sessionID].push_back(accessor); return accessor; - }, - [postAllocationRoutine=postAllocationRoutine](std::shared_ptr<MMappedFileAccessor> accessor){ - if (postAllocationRoutine) - postAllocationRoutine(std::move(accessor)); }); - fileAccessors.insert_or_assign(path, fileAcccessor); - return fileAcccessor; + + m_lazyAccessors.insert_or_assign(path, accessor); + return accessor; } +std::shared_ptr<MMappedFileAccessor> FileAccessorCache::Open( + BinaryNinja::Ref<BinaryNinja::BinaryView> dscView, const uint64_t sessionID, const std::string& path) +{ + EvictFromCacheIfNeeded(); + + mmapCount++; + auto accessor = std::shared_ptr<MMappedFileAccessor>(new MMappedFileAccessor(ResolveFilePath(dscView, path)), + [this](MMappedFileAccessor* accessor) { Close(accessor); }); + + std::lock_guard lock(m_mutex); + auto [it, inserted] = m_accessors.insert({path, {accessor, m_leastRecentlyOpened.end()}}); + if (inserted) { + m_leastRecentlyOpened.push_back(path); + it->second.second = std::prev(it->second.second); + } else { + RecordAccess(path); + } -void MMappedFileAccessor::CloseAll(const uint64_t sessionID) + return accessor; +} + +void FileAccessorCache::Close(MMappedFileAccessor* accessor) { - blockedSessionIDs.insert(sessionID); - if (fileAccessorReferenceHolder.count(sessionID) == 0) - return; - fileAccessorReferenceHolder.erase(sessionID); + mmapCount--; + delete accessor; } +std::shared_ptr<LazyMappedFileAccessor> MMappedFileAccessor::Open(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView, + const uint64_t sessionID, const std::string& path, + std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine) +{ + return FileAccessorCache::Shared().OpenLazily(dscView, sessionID, path, std::move(postAllocationRoutine)); +} void MMappedFileAccessor::InitialVMSetup() { - // check for BN_SHAREDCACHE_FP_MAX - // if it exists, set maxFPLimit to that value - maxFPLimit = 0; - if (auto env = getenv("BN_SHAREDCACHE_FP_MAX"); env) - { - // FIXME behav on 0 here is unintuitive, '0123' will interpret as octal and be 83 according to manpage. meh. - maxFPLimit = strtoull(env, nullptr, 0); - if (maxFPLimit < 10) + static std::once_flag once; + std::call_once(once, []{ + // check for BN_SHAREDCACHE_FP_MAX + // if it exists, set maxFPLimit to that value + unsigned long long maxFPLimit = 0; + if (auto env = getenv("BN_SHAREDCACHE_FP_MAX"); env) { - BinaryNinja::LogWarn("BN_SHAREDCACHE_FP_MAX set to below 10. A value of at least 10 is recommended for performant analysis on SharedCache Binaries."); + // FIXME behav on 0 here is unintuitive, '0123' will interpret as octal and be 83 according to manpage. meh. + maxFPLimit = strtoull(env, nullptr, 0); + if (maxFPLimit < 10) + { + BinaryNinja::LogWarn("BN_SHAREDCACHE_FP_MAX set to below 10. A value of at least 10 is recommended for performant analysis on SharedCache Binaries."); + } + if (maxFPLimit == 0) + { + BinaryNinja::LogError("BN_SHAREDCACHE_FP_MAX set to 0. Adjusting to 1"); + maxFPLimit = 1; + } } - if (maxFPLimit == 0) + else { - BinaryNinja::LogError("BN_SHAREDCACHE_FP_MAX set to 0. Adjusting to 1"); - maxFPLimit = 1; - } - } - else - { - if (maxFPLimit < 10) { #ifdef _MSC_VER // It is not _super_ clear what the max file pointer limit is on windows, // but to my understanding, we are using the windows API to map files, @@ -297,15 +324,30 @@ void MMappedFileAccessor::InitialVMSetup() // parallelize sharedcache processing on in terms of FP usage concerns maxFPLimit = 0x1000000; #else - // unix in comparison will likely have a very small limit, especially mac, necessitating all of this consideration + // The soft file descriptor limit on Linux and Mac is a lot lower than + // on Windows (1024 for Linux, 256 for Mac). Recent iOS shared caches + // have 60+ files which may not leave much headroom if a user opens + // more than one at a time. Attempt to increase the file descriptor + // limit to 1024, and limit ourselves to caching half of them as a + // memory vs performance trade-off (closing and re-opening a file + // requires parsing and applying the slide information again). + constexpr rlim_t TargetFileDescriptorLimit = 1024; struct rlimit rlim; getrlimit(RLIMIT_NOFILE, &rlim); + unsigned long long previousLimit = rlim.rlim_cur; + if (rlim.rlim_cur < TargetFileDescriptorLimit) { + rlim.rlim_cur = std::min(TargetFileDescriptorLimit, rlim.rlim_max); + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { + perror("setrlimit(RLIMIT_NOFILE)"); + rlim.rlim_cur = previousLimit; + } + } maxFPLimit = rlim.rlim_cur / 2; #endif } - } - BinaryNinja::LogInfo("Shared Cache processing initialized with a max file pointer limit of 0x%llx", maxFPLimit); - fileAccessorSemaphore.set_count(maxFPLimit); + BinaryNinja::LogInfo("Shared Cache processing initialized with a max file descriptor limit of %lld", maxFPLimit); + FileAccessorCache::Shared().SetCacheSize(maxFPLimit); + }); } @@ -486,7 +528,8 @@ void VM::MapPages(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView, uint64_t se throw MappingPageAlignmentException(); } - auto accessor = MMappedFileAccessor::Open(std::move(dscView), sessionID, filePath, postAllocationRoutine); + auto accessor = + MMappedFileAccessor::Open(std::move(dscView), sessionID, filePath, std::move(postAllocationRoutine)); auto [it, inserted] = m_map.insert_or_assign({vm_address, vm_address + size}, PageMapping(std::move(accessor), fileoff)); if (m_safe && !inserted) { diff --git a/view/sharedcache/core/VM.h b/view/sharedcache/core/VM.h index dd4f3842..402df196 100644 --- a/view/sharedcache/core/VM.h +++ b/view/sharedcache/core/VM.h @@ -4,62 +4,26 @@ #ifndef SHAREDCACHE_VM_H #define SHAREDCACHE_VM_H + #include <binaryninjaapi.h> -#include <condition_variable> + +#include <list> +#include <mutex> +#include <unordered_map> void VMShutdown(); std::string ResolveFilePath(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView, const std::string& path); -class counting_semaphore { -public: - explicit counting_semaphore(int count = 0) : count_(count) {} - - void release(int update = 1) { - std::unique_lock<std::mutex> lock(mutex_); - count_ += update; - cv_.notify_all(); - } - - void acquire() { - std::unique_lock<std::mutex> lock(mutex_); - cv_.wait(lock, [this]() { return count_ > 0; }); - --count_; - } - - bool try_acquire() { - std::unique_lock<std::mutex> lock(mutex_); - if (count_ > 0) { - --count_; - return true; - } - return false; - } - - void set_count(int new_count) { - std::unique_lock<std::mutex> lock(mutex_); - count_ = new_count; - cv_.notify_all(); - } - -private: - std::mutex mutex_; - std::condition_variable cv_; - int count_; -}; - - template <typename T> class SelfAllocatingWeakPtr { public: - SelfAllocatingWeakPtr(std::function<std::shared_ptr<T>()> allocator, std::function<void(std::shared_ptr<T>)> postAlloc) - : allocator(allocator), postAlloc(postAlloc) {} + SelfAllocatingWeakPtr(std::function<std::shared_ptr<T>()> allocator) : allocator(allocator) {} std::shared_ptr<T> lock() { std::shared_ptr<T> sharedPtr = weakPtr.lock(); if (!sharedPtr) { sharedPtr = allocator(); - postAlloc(sharedPtr); weakPtr = sharedPtr; } return sharedPtr; @@ -71,8 +35,7 @@ public: private: std::weak_ptr<T> weakPtr; // Weak reference to the object - std::function<std::shared_ptr<T>()> allocator; // Function to recreate the object - std::function<void(std::shared_ptr<T>)> postAlloc; // Function to call after the object is allocated + std::function<std::shared_ptr<T>()> allocator; // Function to recreate the object }; @@ -106,26 +69,20 @@ class MMAP { class LazyMappedFileAccessor : public SelfAllocatingWeakPtr<MMappedFileAccessor> { public: - LazyMappedFileAccessor(std::string filePath, std::function<std::shared_ptr<MMappedFileAccessor>()> allocator, - std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAlloc) - : SelfAllocatingWeakPtr(std::move(allocator), std::move(postAlloc)), m_filePath(std::move(filePath)) { - } + LazyMappedFileAccessor( + std::string filePath, std::function<std::shared_ptr<MMappedFileAccessor>(const std::string&)> allocator) : + SelfAllocatingWeakPtr([this, allocator = std::move(allocator)] { return allocator(m_filePath); }), + m_filePath(std::move(filePath)) + {} + ~LazyMappedFileAccessor() = default; - std::string_view filePath() const { return m_filePath; } + std::string_view filePath() const { return m_filePath; } private: std::string m_filePath; }; -static uint64_t maxFPLimit; -static std::mutex fileAccessorDequeMutex; -static std::unordered_map<uint64_t, std::deque<std::shared_ptr<MMappedFileAccessor>>> fileAccessorReferenceHolder; -static std::set<uint64_t> blockedSessionIDs; -static std::mutex fileAccessorsMutex; -static std::unordered_map<std::string, std::shared_ptr<LazyMappedFileAccessor>> fileAccessors; -static counting_semaphore fileAccessorSemaphore(0); - -static std::atomic<uint64_t> mmapCount = 0; +uint64_t MMapCount(); class MMappedFileAccessor { std::string m_path; @@ -142,9 +99,9 @@ public: static void InitialVMSetup(); - std::string Path() const { return m_path; }; + const std::string& Path() const { return m_path; }; - size_t Length() const { return m_mmap.len; }; + size_t Length() const { return m_mmap.len; }; void *Data() const { return m_mmap._mmap; }; @@ -201,6 +158,39 @@ public: T Read(size_t address); }; +class FileAccessorCache +{ +public: + static FileAccessorCache& Shared(); + + std::shared_ptr<LazyMappedFileAccessor> OpenLazily(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView, + const uint64_t sessionID, const std::string& path, + std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine); + + void SetCacheSize(uint64_t size); + +private: + FileAccessorCache(); + + std::shared_ptr<MMappedFileAccessor> Open( + BinaryNinja::Ref<BinaryNinja::BinaryView> dscView, const uint64_t sessionID, const std::string& path); + + void Close(MMappedFileAccessor* accessor); + + void RecordAccess(const std::string& path); + void EvictFromCacheIfNeeded(); + + std::mutex m_mutex; + std::unordered_map<std::string, std::shared_ptr<LazyMappedFileAccessor>> m_lazyAccessors; + + // Ordered from least recently opened (front) to most recently opened (end). + std::list<std::string> m_leastRecentlyOpened; + std::unordered_map<std::string, std::pair<std::shared_ptr<MMappedFileAccessor>, std::list<std::string>::iterator>> + m_accessors; + + uint64_t m_cacheSize = 8; +}; + struct PageMapping { std::shared_ptr<LazyMappedFileAccessor> fileAccessor; size_t fileOffset; |
