diff options
| author | Mark Rowe <mark@vector35.com> | 2026-02-23 23:26:27 -0800 |
|---|---|---|
| committer | Mark Rowe <mark@vector35.com> | 2026-02-24 14:24:54 -0800 |
| commit | a72b98ed5a357bb380d81f07f3f36946aa729bb2 (patch) | |
| tree | ca48b3607721fa2c109b7989ae4b911bab89ea6d /view/sharedcache | |
| parent | 3eda43f185a0411538745a99e251122e6a9192e0 (diff) | |
[DSC] Simplify file mapping to fix intermittent unrelocated pointers
The `FileAccessorCache`'s LRU eviction could discard a file's mapped
data after slide info had already been applied to it. Future accesses to
the file produced a fresh mapping, but failed to reapply the slide info.
This could result in pointers not correctly being slid, such as in
https://github.com/Vector35/binaryninja-api/issues/7689.
The LRU cache existed to stay within OS file descriptor limits, since
the old `MappedFile` held its fd open for the lifetime of the mapping.
There's no real reason for it to hold the file descriptor open like
this. Closing it after `mmap` is sufficient to avoid the file descriptor
limits.
`MappedFileRegion` replaces the combination of `FileAccessorCache`,
`WeakFileAccessor`, and `MappedFileAccessor`. It closes the fd
immediately after mmap, so all files can stay mapped without consuming
descriptors, making the cache unnecessary.
`MappedFileRegion` is owned directly by the `CacheEntry` for its full
lifetime. Slide info is applied exactly once to each `MappedFileRegion`.
Diffstat (limited to 'view/sharedcache')
| -rw-r--r-- | view/sharedcache/core/FileAccessorCache.cpp | 95 | ||||
| -rw-r--r-- | view/sharedcache/core/FileAccessorCache.h | 76 | ||||
| -rw-r--r-- | view/sharedcache/core/MachO.cpp | 4 | ||||
| -rw-r--r-- | view/sharedcache/core/MappedFile.cpp | 193 | ||||
| -rw-r--r-- | view/sharedcache/core/MappedFile.h | 86 | ||||
| -rw-r--r-- | view/sharedcache/core/MappedFileAccessor.cpp | 111 | ||||
| -rw-r--r-- | view/sharedcache/core/MappedFileAccessor.h | 103 | ||||
| -rw-r--r-- | view/sharedcache/core/MappedFileRegion.cpp | 213 | ||||
| -rw-r--r-- | view/sharedcache/core/MappedFileRegion.h | 72 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCache.cpp | 69 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCache.h | 7 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCacheBuilder.cpp | 3 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCacheController.cpp | 8 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCacheController.h | 1 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCacheView.cpp | 12 | ||||
| -rw-r--r-- | view/sharedcache/core/SlideInfo.cpp | 71 | ||||
| -rw-r--r-- | view/sharedcache/core/SlideInfo.h | 6 | ||||
| -rw-r--r-- | view/sharedcache/core/VirtualMemory.cpp | 199 | ||||
| -rw-r--r-- | view/sharedcache/core/VirtualMemory.h | 65 |
19 files changed, 432 insertions, 962 deletions
diff --git a/view/sharedcache/core/FileAccessorCache.cpp b/view/sharedcache/core/FileAccessorCache.cpp deleted file mode 100644 index 28aab15d..00000000 --- a/view/sharedcache/core/FileAccessorCache.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "FileAccessorCache.h" - -#include <cassert> - -CacheAccessorID GetCacheAccessorID(const std::string& filePath) -{ - constexpr std::hash<std::string> hasher; - return static_cast<CacheAccessorID>(hasher(filePath)); -} - -FileAccessorCache::FileAccessorCache(size_t cacheSize) -{ - m_cacheSize = cacheSize; - m_accessors = {}; -} - -void FileAccessorCache::EvictLastUsed() -{ - if (m_cache.empty()) - return; - // Evict the least recently used element. - const auto lruID = m_cache.front(); - m_cache.pop_front(); - // Ensure the least recently used ID actually exists in the accessors map. - assert(m_accessors.find(lruID) != m_accessors.end() && "Evicting non-existent ID from accessors map"); - m_accessors.erase(lruID); -} - -FileAccessorCache& FileAccessorCache::Global() -{ - static FileAccessorCache cache {}; - return cache; -} - -WeakFileAccessor FileAccessorCache::Open(const std::string& filePath) -{ - const auto id = GetCacheAccessorID(filePath); - std::unique_lock lock(m_mutex); - - // Check if the file is already in the cache. - if (const auto it = m_accessors.find(id); it != m_accessors.end()) - { - // Move the accessed ID to the back so we keep it in the cache. - auto pos = std::find(m_cache.begin(), m_cache.end(), id); - if (pos != m_cache.end()) - m_cache.erase(pos); - m_cache.push_back(id); - - return WeakFileAccessor(it->second, filePath); - } - - // Evict if we are going to go above the limit. - while (m_cache.size() >= m_cacheSize) - EvictLastUsed(); - - // Create a new file accessor and add it to the cache. - auto accessor = MappedFileAccessor::Open(filePath); - if (accessor == nullptr) - { - // We failed to open the file, we must throw hard! - // TODO: Make this mechanism more thought out... - throw std::runtime_error("Failed to open file: " + filePath); - } - auto sharedAccessor = std::make_shared<MappedFileAccessor>(std::move(*accessor)); - m_accessors.insert_or_assign(id, sharedAccessor); - m_cache.push_back(id); - - return WeakFileAccessor(sharedAccessor, filePath); -} - -void FileAccessorCache::RemoveAccessor(const CacheAccessorID id) -{ - std::unique_lock lock(m_mutex); - m_accessors.erase(id); -} - -std::shared_ptr<MappedFileAccessor> WeakFileAccessor::lock() -{ - auto sharedPtr = m_weakPtr.lock(); - if (!sharedPtr) - { - // This will revive other weak pointers to the same shared ptr. - // Update the weak pointer to the newly created shared instance - m_weakPtr = FileAccessorCache::Global().Open(m_filePath).m_weakPtr; - sharedPtr = m_weakPtr.lock(); - - // 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; -} diff --git a/view/sharedcache/core/FileAccessorCache.h b/view/sharedcache/core/FileAccessorCache.h deleted file mode 100644 index 46de6fcc..00000000 --- a/view/sharedcache/core/FileAccessorCache.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include <shared_mutex> - -#include "MappedFileAccessor.h" - -typedef uint32_t CacheAccessorID; - -// TODO: We might want to make this more than just the path, for example -// TODO: We might want to make it unique to a view session (session id). -// Get a unique entry id for the given file path. -CacheAccessorID GetCacheAccessorID(const std::string& filePath); - -class WeakFileAccessor; - -class FileAccessorCache -{ - size_t m_cacheSize; - std::mutex m_mutex; - // NOTE: If we end up wanting to handle 1000's of files we should consider std::list. - std::deque<CacheAccessorID> m_cache; - std::unordered_map<CacheAccessorID, std::shared_ptr<MappedFileAccessor>> m_accessors; - - explicit FileAccessorCache(size_t cacheSize = 8); - - void EvictLastUsed(); - -public: - static FileAccessorCache& Global(); - - // Get a weak reference to a file accessor, the reference at this point is alive. - // The reference is always alive at this point either because it is in the cache or it has been inserted in. - // Subsequent calls to this might kill the backing file accessor resulting in the weak ref recreating the file - // accessor and inserting itself back into its related cache. - WeakFileAccessor Open(const std::string& filePath); - - void RemoveAccessor(CacheAccessorID id); - - // 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(const uint64_t size) { m_cacheSize = size; }; - - size_t GetCacheSize() const { return m_cacheSize; } - - size_t GetCacheCount() const { return m_accessors.size(); } -}; - -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::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)) - {} - - // 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; - } - - std::shared_ptr<MappedFileAccessor> lock(); -}; diff --git a/view/sharedcache/core/MachO.cpp b/view/sharedcache/core/MachO.cpp index f3f9ec6c..7afcbd8d 100644 --- a/view/sharedcache/core/MachO.cpp +++ b/view/sharedcache/core/MachO.cpp @@ -621,7 +621,9 @@ std::vector<CacheSymbol> SharedCacheMachOHeader::ReadExportSymbolTrie(VirtualMem return {}; std::vector<CacheSymbol> symbols = {}; try { - auto [begin, end] = vm.ReadSpan(GetLinkEditFileBase() + exportTrie.dataoff, exportTrie.datasize); + auto trieSpan = vm.ReadSpan(GetLinkEditFileBase() + exportTrie.dataoff, exportTrie.datasize); + const uint8_t *begin = trieSpan.data(); + const uint8_t *end = begin + trieSpan.size(); const uint8_t *cursor = begin; struct Node diff --git a/view/sharedcache/core/MappedFile.cpp b/view/sharedcache/core/MappedFile.cpp deleted file mode 100644 index 21bda4f8..00000000 --- a/view/sharedcache/core/MappedFile.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#ifdef _MSC_VER - #include <windows.h> -#else - #include <sys/mman.h> - #include <fcntl.h> - #include <cstdlib> - #include <sys/resource.h> -#endif - -#include "MappedFile.h" -#include "binaryninjaapi.h" - -#ifndef _MSC_VER -uint64_t AdjustFileDescriptorLimit() -{ - // 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). - uint64_t maxFPLimit = 1024; - - // check for BN_SHAREDCACHE_FP_MAX - // if it exists, set maxFPLimit to that value - 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) - { - 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; - } - } - - rlimit rlim {}; - getrlimit(RLIMIT_NOFILE, &rlim); - uint64_t previousLimit = rlim.rlim_cur; - uint64_t targetLimit = std::min(maxFPLimit, rlim.rlim_max); - if (rlim.rlim_cur < targetLimit) - { - rlim.rlim_cur = targetLimit; - if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) - { - perror("setrlimit(RLIMIT_NOFILE)"); - rlim.rlim_cur = previousLimit; - } - } - - maxFPLimit = rlim.rlim_cur / 2; - return maxFPLimit; -} - -MappedFile::~MappedFile() -{ - Unmap(); - if (fd) - fclose(fd); -} - -std::optional<MappedFile> MappedFile::OpenFile(const std::string& path) -{ - MappedFile file; - file._mmap = nullptr; - file.fd = fopen(path.c_str(), "r"); - if (file.fd == nullptr) - return std::nullopt; - - fseek(file.fd, 0L, SEEK_END); - file.len = ftell(file.fd); - fseek(file.fd, 0L, SEEK_SET); - - return file; -} - -MapStatus MappedFile::Map() -{ - if (_mmap) - return MapStatus::Success; - - void* result = mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(fd), 0u); - if (result == MAP_FAILED) - { - BinaryNinja::LogErrorF("mmap failed: {}", strerror(errno)); // Use errno to log the reason - return MapStatus::Error; - } - _mmap = static_cast<uint8_t*>(result); - - return MapStatus::Success; -} - -MapStatus MappedFile::Unmap() -{ - if (_mmap) - { - munmap(_mmap, len); - _mmap = nullptr; - } - return MapStatus::Success; -} -#else -uint64_t AdjustFileDescriptorLimit() -{ - return 0x1000000; -} - -MappedFile::~MappedFile() -{ - Unmap(); - if (hFile) - CloseHandle(hFile); -} - -std::optional<MappedFile> MappedFile::OpenFile(const std::string& path) -{ - MappedFile file; - file._mmap = nullptr; - file.hFile = CreateFile(path.c_str(), // file name - GENERIC_READ, // desired access (read-only) - FILE_SHARE_READ, // share mode - NULL, // security attributes - OPEN_EXISTING, // creation disposition - FILE_ATTRIBUTE_NORMAL, // flags and attributes - NULL); // template file - - if (file.hFile == INVALID_HANDLE_VALUE) - return std::nullopt; - - LARGE_INTEGER fileSize; - if (!GetFileSizeEx(file.hFile, &fileSize)) - { - CloseHandle(file.hFile); - return std::nullopt; - } - file.len = static_cast<size_t>(fileSize.QuadPart); - - return file; -} - -MapStatus MappedFile::Map() -{ - if (_mmap) - return MapStatus::Success; - - HANDLE hMapping = CreateFileMapping(hFile, // file handle - NULL, // security attributes - PAGE_WRITECOPY, // protection - 0, // maximum size (high-order DWORD) - 0, // maximum size (low-order DWORD) - NULL); // name of the mapping object - - if (hMapping == NULL) - { - CloseHandle(hFile); - return MapStatus::Error; - } - - _mmap = static_cast<uint8_t*>(MapViewOfFile(hMapping, // handle to the file mapping object - FILE_MAP_COPY, // desired access - 0, // file offset (high-order DWORD) - 0, // file offset (low-order DWORD) - 0)); // number of bytes to map (0 = entire file) - - if (_mmap == nullptr) - { - CloseHandle(hMapping); - CloseHandle(hFile); - return MapStatus::Error; - } - - CloseHandle(hMapping); - CloseHandle(hFile); - - return MapStatus::Success; -} - -MapStatus MappedFile::Unmap() -{ - if (_mmap) - { - UnmapViewOfFile(_mmap); - } - return MapStatus::Success; -} -#endif diff --git a/view/sharedcache/core/MappedFile.h b/view/sharedcache/core/MappedFile.h deleted file mode 100644 index ced79f52..00000000 --- a/view/sharedcache/core/MappedFile.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include <string> -#include <cstdint> -#include <cstdio> -#include <optional> - -// Call this when initializing the plugin so that the process file descriptor limit is raised. -uint64_t AdjustFileDescriptorLimit(); - -enum MapStatus : int -{ - Success, - // TODO: Split this error out into more contextual errors. - Error, -}; - -struct MappedFile -{ - uint8_t* _mmap = nullptr; - size_t len = 0; - -#ifdef _MSC_VER - HANDLE hFile = INVALID_HANDLE_VALUE; -#else - FILE* fd = nullptr; -#endif - - MappedFile() = default; - - ~MappedFile(); - - MappedFile(const MappedFile&) = delete; - - MappedFile& operator=(const MappedFile&) = delete; - - MappedFile(MappedFile&& other) noexcept : _mmap(other._mmap), len(other.len) - { -#ifdef _MSC_VER - hFile = other.hFile; - // Don't close the hFile in the move. - other.hFile = nullptr; -#else - fd = other.fd; - // Don't close the fd in the move. - other.fd = nullptr; -#endif - other._mmap = nullptr; - } - - // I hate C++ - MappedFile& operator=(MappedFile&& other) noexcept - { - if (this != &other) - { - Unmap(); -#ifdef _MSC_VER - if (hFile != nullptr) - { - CloseHandle(hFile); - } - hFile = other.hFile; - // Don't close the hFile in the move. - other.hFile = nullptr; -#else - if (fd != nullptr) - { - fclose(fd); - } - fd = other.fd; - // Don't close the fd in the move. - other.fd = nullptr; -#endif - len = other.len; - _mmap = other._mmap; - other._mmap = nullptr; - } - return *this; - } - - static std::optional<MappedFile> OpenFile(const std::string& path); - - MapStatus Map(); - - MapStatus Unmap(); -}; diff --git a/view/sharedcache/core/MappedFileAccessor.cpp b/view/sharedcache/core/MappedFileAccessor.cpp deleted file mode 100644 index 4f9f99a8..00000000 --- a/view/sharedcache/core/MappedFileAccessor.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "MappedFileAccessor.h" - -std::shared_ptr<MappedFileAccessor> MappedFileAccessor::Open(const std::string& filePath) -{ - auto file = MappedFile::OpenFile(filePath); - if (!file.has_value()) - return nullptr; - if (file->Map() != MapStatus::Success) - return nullptr; - return std::make_shared<MappedFileAccessor>(std::move(*file)); -} - -// TODO: Will obviously not work on 32bit binaries, need to make WritePointer64 and 32 equiv. -void MappedFileAccessor::WritePointer(size_t address, size_t pointer) -{ - if (address + sizeof(size_t*) > Length()) - throw UnmappedAccessException(address + sizeof(size_t*), Length()); - m_dirty = true; - *reinterpret_cast<size_t*>(&m_file._mmap[address]) = pointer; -} - -std::string MappedFileAccessor::ReadNullTermString(size_t address, const size_t maxLength) const -{ - if (address > Length()) - return ""; - // If we are not given a maxLength (i.e. -1) than we will set the max address to the length of the file. - const size_t maxAddr = (maxLength != -1) ? std::min(address + maxLength, Length()) : Length(); - // Read a null-terminated string manually to avoid errors related to string length on Linux. - std::string str; - str.reserve(140); - for (size_t currAddr = address; currAddr < maxAddr; ++currAddr) - { - char c = m_file._mmap[currAddr]; - if (c == '\0') - break; - str += c; - } - str.shrink_to_fit(); - return str; -} - -uint8_t MappedFileAccessor::ReadUInt8(size_t address) const -{ - return Read<uint8_t>(address); -} - -int8_t MappedFileAccessor::ReadInt8(size_t address) const -{ - return Read<int8_t>(address); -} - -uint16_t MappedFileAccessor::ReadUInt16(size_t address) const -{ - return Read<uint16_t>(address); -} - -int16_t MappedFileAccessor::ReadInt16(size_t address) const -{ - return Read<int16_t>(address); -} - - -uint32_t MappedFileAccessor::ReadUInt32(size_t address) const -{ - return Read<uint32_t>(address); -} - -int32_t MappedFileAccessor::ReadInt32(size_t address) const -{ - return Read<int32_t>(address); -} - -uint64_t MappedFileAccessor::ReadUInt64(size_t address) const -{ - return Read<uint64_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) const -{ - if (addr + length > Length()) - throw UnmappedAccessException(addr + length, Length()); - return {&m_file._mmap[addr], length}; -} - -std::pair<const uint8_t*, const uint8_t*> MappedFileAccessor::ReadSpan(size_t addr, size_t length) -{ - if (addr + length > Length()) - throw UnmappedAccessException(addr + length, Length()); - const uint8_t* data = &m_file._mmap[addr]; - return {data, data + length}; -} - -void MappedFileAccessor::Read(void* dest, size_t addr, size_t length) const -{ - if (addr + length > Length()) - throw UnmappedAccessException(addr + length, Length()); - memcpy(dest, &m_file._mmap[addr], length); -} - -template <typename T> -T MappedFileAccessor::Read(size_t address) const -{ - T result; - Read(&result, address, sizeof(T)); - return result; -} diff --git a/view/sharedcache/core/MappedFileAccessor.h b/view/sharedcache/core/MappedFileAccessor.h deleted file mode 100644 index 35d2d5e7..00000000 --- a/view/sharedcache/core/MappedFileAccessor.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include "binaryninjaapi.h" -#include "MappedFile.h" - -#include <cstdint> -#include <list> - -class UnmappedAccessException : public std::exception -{ - uint64_t m_address; - uint64_t m_fileLen; - -public: - explicit UnmappedAccessException(uint64_t address, uint64_t fileLength) : m_address(address), m_fileLen(fileLength) - {} - - virtual const char* what() const throw() - { - thread_local std::string message; - message = - fmt::format("Tried to access unmapped address {0:x} for file with length of {1:x}", m_address, m_fileLen); - return message.c_str(); - } -}; - -// std::enable_shared_from_this allows weak pointers to "revive" the shared pointer in the FileAccessorCache. -class MappedFileAccessor : public std::enable_shared_from_this<MappedFileAccessor> -{ - MappedFile m_file; - bool m_dirty = false; - -public: - explicit MappedFileAccessor(MappedFile file) : m_file(std::move(file)) {} - - ~MappedFileAccessor() = default; - - MappedFileAccessor(const MappedFileAccessor&) = delete; - - MappedFileAccessor& operator=(const MappedFileAccessor&) = delete; - - MappedFileAccessor(MappedFileAccessor&&) noexcept = default; - - MappedFileAccessor& operator=(MappedFileAccessor&&) noexcept = default; - - static std::shared_ptr<MappedFileAccessor> Open(const std::string& filePath); - - size_t Length() const { return m_file.len; }; - - void* Data() const { return m_file._mmap; }; - - bool IsDirty() const { return m_dirty; } - - /** - * Writes to files are implemented for performance reasons and should be treated with utmost care - * - * They _MAY_ disappear as _soon_ as you release the lock on the file. - * They may also NOT disappear for the lifetime of the application. - * - * The former is more likely to occur when concurrent DSC processing is happening. The latter is the typical - * scenario. - * - * This is used explicitly for slide information in a locked scope and _NOTHING_ else. It should probably not be - * used for anything else. - * - * \param address The address to write the pointer to - * \param pointer The pointer to be written - */ - void WritePointer(size_t address, size_t pointer); - - std::string ReadNullTermString(size_t address, size_t maxLength = -1) const; - - uint8_t ReadUInt8(size_t address) const; - - int8_t ReadInt8(size_t address) const; - - uint16_t ReadUInt16(size_t address) const; - - int16_t ReadInt16(size_t address) const; - - uint32_t ReadUInt32(size_t address) const; - - int32_t ReadInt32(size_t address) const; - - uint64_t ReadUInt64(size_t address) const; - - int64_t ReadInt64(size_t address) const; - - BinaryNinja::DataBuffer ReadBuffer(size_t addr, size_t length) const; - - // Returns a range of pointers within the mapped memory region corresponding to - // {addr, length}. - // WARNING: The pointers returned by this method is only valid for the lifetime - // of this file accessor. - // TODO: This should use std::span<const uint8_t> once the minimum supported - // C++ version supports it. - std::pair<const uint8_t*, const uint8_t*> ReadSpan(size_t addr, size_t length); - - void Read(void* dest, size_t addr, size_t length) const; - - template <typename T> - T Read(size_t address) const; -}; diff --git a/view/sharedcache/core/MappedFileRegion.cpp b/view/sharedcache/core/MappedFileRegion.cpp new file mode 100644 index 00000000..8c130610 --- /dev/null +++ b/view/sharedcache/core/MappedFileRegion.cpp @@ -0,0 +1,213 @@ +#include "MappedFileRegion.h" + +#ifdef _MSC_VER +#include <windows.h> +#else +#include <errno.h> +#include <string.h> +#include <sys/mman.h> +#endif + + +MappedFileRegion::MappedFileRegion(PrivateTag, uint8_t* data, size_t length, std::string path) + : m_data(data), m_length(length), m_path(std::move(path)) +{} + +#ifndef _MSC_VER + +std::shared_ptr<MappedFileRegion> MappedFileRegion::Open(const std::string& path) +{ + std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "r"), fclose); + if (!fd) + return nullptr; + + fseek(fd.get(), 0L, SEEK_END); + long fileLen = ftell(fd.get()); + if (fileLen <= 0) + { + if (fileLen < 0) + BinaryNinja::LogErrorF("ftell failed for '{}': {}", path, strerror(errno)); + else + BinaryNinja::LogErrorF("Cannot mmap empty file '{}'", path); + return nullptr; + } + auto length = static_cast<size_t>(fileLen); + void* result = mmap(nullptr, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(fd.get()), 0); + + if (result == MAP_FAILED) + { + BinaryNinja::LogErrorF("mmap failed for '{}': {}", path, strerror(errno)); + return nullptr; + } + + return std::make_shared<MappedFileRegion>(PrivateTag{}, static_cast<uint8_t*>(result), length, path); +} + +MappedFileRegion::~MappedFileRegion() +{ + if (m_data) + { + munmap(m_data, m_length); + m_data = nullptr; + } +} + +#else // _MSC_VER + +std::shared_ptr<MappedFileRegion> MappedFileRegion::Open(const std::string& path) +{ + HANDLE hFile = CreateFile(path.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + return nullptr; + + LARGE_INTEGER fileSize; + if (!GetFileSizeEx(hFile, &fileSize)) + { + CloseHandle(hFile); + return nullptr; + } + auto length = static_cast<size_t>(fileSize.QuadPart); + + HANDLE hMapping = CreateFileMapping(hFile, + NULL, + PAGE_WRITECOPY, + 0, + 0, + NULL); + + if (hMapping == NULL) + { + BinaryNinja::LogErrorF("CreateFileMapping failed for '{}': error {}", path, GetLastError()); + CloseHandle(hFile); + return nullptr; + } + + auto* data = static_cast<uint8_t*>(MapViewOfFile(hMapping, + FILE_MAP_COPY, + 0, + 0, + 0)); + + // Save the error before CloseHandle potentially overwrites it. + DWORD mapViewError = (data == nullptr) ? GetLastError() : 0; + + CloseHandle(hMapping); + CloseHandle(hFile); + + if (!data) + { + BinaryNinja::LogErrorF("MapViewOfFile failed for '{}': error {}", path, mapViewError); + return nullptr; + } + + return std::make_shared<MappedFileRegion>(PrivateTag{}, data, length, path); +} + +MappedFileRegion::~MappedFileRegion() +{ + if (m_data) + { + UnmapViewOfFile(m_data); + m_data = nullptr; + } +} + +#endif // _MSC_VER + +void MappedFileRegion::WriteUInt64(size_t offset, uint64_t value) +{ + if (sizeof(uint64_t) > m_length || offset > m_length - sizeof(uint64_t)) + throw UnmappedAccessException(offset, m_length); + memcpy(&m_data[offset], &value, sizeof(uint64_t)); +} + +uint8_t MappedFileRegion::ReadUInt8(size_t offset) const +{ + return Read<uint8_t>(offset); +} + +int8_t MappedFileRegion::ReadInt8(size_t offset) const +{ + return Read<int8_t>(offset); +} + +uint16_t MappedFileRegion::ReadUInt16(size_t offset) const +{ + return Read<uint16_t>(offset); +} + +int16_t MappedFileRegion::ReadInt16(size_t offset) const +{ + return Read<int16_t>(offset); +} + +uint32_t MappedFileRegion::ReadUInt32(size_t offset) const +{ + return Read<uint32_t>(offset); +} + +int32_t MappedFileRegion::ReadInt32(size_t offset) const +{ + return Read<int32_t>(offset); +} + +uint64_t MappedFileRegion::ReadUInt64(size_t offset) const +{ + return Read<uint64_t>(offset); +} + +int64_t MappedFileRegion::ReadInt64(size_t offset) const +{ + return Read<int64_t>(offset); +} + +std::string MappedFileRegion::ReadNullTermString(size_t offset, const size_t maxLen) const +{ + if (offset >= m_length) + return ""; + + const size_t remaining = m_length - offset; + const size_t limit = (maxLen != static_cast<size_t>(-1)) ? std::min(maxLen, remaining) : remaining; + const auto* begin = reinterpret_cast<const char*>(m_data + offset); + const auto* end = begin + limit; + const auto* nul = std::find(begin, end, '\0'); + return {begin, nul}; +} + +BinaryNinja::DataBuffer MappedFileRegion::ReadBuffer(size_t offset, size_t length) const +{ + if (length > m_length || offset > m_length - length) + throw UnmappedAccessException(offset, m_length); + return {&m_data[offset], length}; +} + +std::span<const uint8_t> MappedFileRegion::ReadSpan(size_t offset, size_t length) const +{ + if (length > m_length || offset > m_length - length) + throw UnmappedAccessException(offset, m_length); + return {&m_data[offset], length}; +} + +void MappedFileRegion::Read(void* dest, size_t offset, size_t length) const +{ + if (length > m_length || offset > m_length - length) + throw UnmappedAccessException(offset, m_length); + memcpy(dest, &m_data[offset], length); +} + +template <typename T> +T MappedFileRegion::Read(size_t offset) const +{ + if (sizeof(T) > m_length || offset > m_length - sizeof(T)) + throw UnmappedAccessException(offset, m_length); + T result; + memcpy(&result, &m_data[offset], sizeof(T)); + return result; +} diff --git a/view/sharedcache/core/MappedFileRegion.h b/view/sharedcache/core/MappedFileRegion.h new file mode 100644 index 00000000..7cbf2100 --- /dev/null +++ b/view/sharedcache/core/MappedFileRegion.h @@ -0,0 +1,72 @@ +#pragma once + +#include "binaryninjaapi.h" + +#include <memory> +#include <mutex> +#include <span> +#include <stdint.h> +#include <string> + +class UnmappedAccessException : public std::runtime_error +{ +public: + UnmappedAccessException(uint64_t offset, uint64_t fileLength) + : std::runtime_error(fmt::format("Tried to access offset {:#x} in file of length {:#x}", offset, fileLength)) + {} +}; + +// A memory-mapped region of a file. Owns the mmap for its entire lifetime. +// Data is mapped using MAP_PRIVATE, so modifications to the data are not persisted to disk. +class MappedFileRegion +{ + uint8_t* m_data = nullptr; + size_t m_length = 0; + std::string m_path; + std::once_flag m_slidOnce; + + MappedFileRegion(const MappedFileRegion&) = delete; + MappedFileRegion& operator=(const MappedFileRegion&) = delete; + MappedFileRegion(MappedFileRegion&&) = delete; + MappedFileRegion& operator=(MappedFileRegion&&) = delete; + + struct PrivateTag {}; + +public: + MappedFileRegion(PrivateTag, uint8_t* data, size_t length, std::string path); + ~MappedFileRegion(); + + // Opens file, mmaps with MAP_PRIVATE, closes fd immediately. + // Returns nullptr on failure. + static std::shared_ptr<MappedFileRegion> Open(const std::string& path); + + const std::string& Path() const { return m_path; } + size_t Length() const { return m_length; } + + // Run `fn` exactly once. All callers block until the work is complete. + template <typename F> + void SlideOnce(F&& fn) { std::call_once(m_slidOnce, std::forward<F>(fn)); } + + // Typed reads -- bounds-checked with overflow-safe checks: + // if (sizeof(T) > m_length || offset > m_length - sizeof(T)) + uint8_t ReadUInt8(size_t offset) const; + int8_t ReadInt8(size_t offset) const; + uint16_t ReadUInt16(size_t offset) const; + int16_t ReadInt16(size_t offset) const; + uint32_t ReadUInt32(size_t offset) const; + int32_t ReadInt32(size_t offset) const; + uint64_t ReadUInt64(size_t offset) const; + int64_t ReadInt64(size_t offset) const; + + std::string ReadNullTermString(size_t offset, size_t maxLen = -1) const; + void Read(void* dest, size_t offset, size_t length) const; + BinaryNinja::DataBuffer ReadBuffer(size_t offset, size_t length) const; + std::span<const uint8_t> ReadSpan(size_t offset, size_t length) const; + + // Write. This is not persisted to disk. Used for applying slides only. + void WriteUInt64(size_t offset, uint64_t value); + +private: + template <typename T> + T Read(size_t offset) const; +}; diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index 09093ba5..f9761968 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -33,7 +33,8 @@ std::vector<std::string> CacheImage::GetDependencies() const } CacheEntry::CacheEntry(std::string filePath, std::string fileName, CacheEntryType type, dyld_cache_header header, - std::vector<dyld_cache_mapping_info>&& mappings, std::vector<std::pair<std::string, dyld_cache_image_info>>&& images) + std::vector<dyld_cache_mapping_info> mappings, std::vector<std::pair<std::string, dyld_cache_image_info>> images, + std::shared_ptr<MappedFileRegion> file) { m_filePath = std::move(filePath); m_fileName = std::move(fileName); @@ -41,21 +42,24 @@ CacheEntry::CacheEntry(std::string filePath, std::string fileName, CacheEntryTyp m_header = header; m_mappings = std::move(mappings); m_images = std::move(images); + m_file = std::move(file); } CacheEntry CacheEntry::FromFile(const std::string& filePath, const std::string& fileName, CacheEntryType type) { - auto file = FileAccessorCache::Global().Open(filePath).lock(); + auto file = MappedFileRegion::Open(filePath); + if (!file) + throw std::runtime_error(fmt::format("Failed to open cache file: '{}'", filePath)); // TODO: Pull this out into another function so we can do IsValidDSCFile or something. // We first want to make sure that the base file is dyld. // All entries must start with "dyld". + if (file->Length() < 4) + throw std::runtime_error(fmt::format("File too small to be a shared cache: '{}'", filePath)); DataBuffer sig = file->ReadBuffer(0, 4); - if (sig.GetLength() != 4) - throw std::runtime_error("File is empty!"); const char* magic = static_cast<char*>(sig.GetData()); if (strncmp(magic, "dyld", 4) != 0) - throw std::runtime_error("File does not start with `dyld`!"); + throw std::runtime_error(fmt::format("File does not start with 'dyld': '{}'", filePath)); // Read the header, this _should_ be compatible with all known DSC formats. // Mason: the above is not true! https://github.com/Vector35/binaryninja-api/issues/6073 @@ -74,7 +78,7 @@ CacheEntry CacheEntry::FromFile(const std::string& filePath, const std::string& // Cancel adding the entry if we have an invalid mapping. if (currentMapping.fileOffset + currentMapping.size > file->Length()) - throw std::runtime_error("Invalid mapping in shared cache entry"); + throw std::runtime_error(fmt::format("Invalid mapping in shared cache entry: '{}'", filePath)); // TODO: Check initProt to make sure its in the range of expected values. @@ -139,12 +143,7 @@ CacheEntry CacheEntry::FromFile(const std::string& filePath, const std::string& images.emplace_back(imageName, branchIslandImg); } - return {filePath, fileName, type, header, std::move(mappings), std::move(images)}; -} - -WeakFileAccessor CacheEntry::GetAccessor() const -{ - return FileAccessorCache::Global().Open(m_filePath); + return CacheEntry{filePath, fileName, type, header, std::move(mappings), std::move(images), std::move(file)}; } std::optional<uint64_t> CacheEntry::GetHeaderAddress() const @@ -226,8 +225,7 @@ void SharedCache::AddSymbols(std::vector<CacheSymbol>&& symbols) void SharedCache::AddEntry(CacheEntry entry) { - // Get the file accessor to associate with the virtual memory region. - auto fileAccessor = FileAccessorCache::Global().Open(entry.GetFilePath()); + const auto& file = entry.GetFile(); if (entry.GetType() == CacheEntryType::Symbols) { @@ -236,7 +234,7 @@ void SharedCache::AddEntry(CacheEntry entry) // This is necessary due to code that processes symbols being written in terms of a `VirtualMemory` // rather than something more generic. m_localSymbolsVM = std::make_shared<VirtualMemory>(m_vm->GetAddressSize()); - m_localSymbolsVM->MapRegion(fileAccessor, {0, fileAccessor.lock()->Length()}, 0); + m_localSymbolsVM->MapRegion(m_localSymbolsEntry->GetFile(), {0, m_localSymbolsEntry->GetFile()->Length()}, 0); return; } @@ -245,7 +243,7 @@ void SharedCache::AddEntry(CacheEntry entry) const auto& mappings = entry.GetMappings(); for (const auto& mapping : mappings) { - m_vm->MapRegion(fileAccessor, {mapping.address, mapping.address + mapping.size}, mapping.fileOffset); + m_vm->MapRegion(file, {mapping.address, mapping.address + mapping.size}, mapping.fileOffset); // Recalculate the base address. if (mapping.address < m_baseAddress || m_baseAddress == 0) @@ -421,40 +419,11 @@ void SharedCache::ProcessEntryRegions(const CacheEntry& entry) void SharedCache::ProcessEntrySlideInfo(const CacheEntry& entry) const { - auto slideInfoProcessor = SlideInfoProcessor(GetBaseAddress()); - - // 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; - } - } - - LogWarnF("Failed to register revive callback for slide mapping {:#x} in entry {:?}", mapping.address, entry.GetFileName().c_str()); - } + const auto& file = entry.GetFile(); + file->SlideOnce([&] { + SlideInfoProcessor processor(GetBaseAddress()); + processor.ProcessEntry(*file, entry); + }); } void SharedCache::ProcessSymbols() diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h index 010dff10..8ecd8f1d 100644 --- a/view/sharedcache/core/SharedCache.h +++ b/view/sharedcache/core/SharedCache.h @@ -1,5 +1,6 @@ #pragma once +#include <shared_mutex> #include <vector> #include <Dyld.h> @@ -127,10 +128,12 @@ class CacheEntry // Mapping of image path to image info, used within ProcessImagesAndRegions to add them to the cache. // Also used to retrieve the image dependencies. std::vector<std::pair<std::string, dyld_cache_image_info>> m_images {}; + std::shared_ptr<MappedFileRegion> m_file; public: CacheEntry(std::string filePath, std::string fileName, CacheEntryType type, dyld_cache_header header, - std::vector<dyld_cache_mapping_info>&& mappings, std::vector<std::pair<std::string, dyld_cache_image_info>>&& images); + std::vector<dyld_cache_mapping_info> mappings, std::vector<std::pair<std::string, dyld_cache_image_info>> images, + std::shared_ptr<MappedFileRegion> file); CacheEntry() = default; CacheEntry(const CacheEntry&) = default; @@ -141,7 +144,7 @@ public: // Construct a cache entry from the file on disk. static CacheEntry FromFile(const std::string& filePath, const std::string& fileName, CacheEntryType type); - WeakFileAccessor GetAccessor() const; + const std::shared_ptr<MappedFileRegion>& GetFile() const { return m_file; } // Get the headers virtual address. // This is useful if you need to read relative to the start of the entry file. diff --git a/view/sharedcache/core/SharedCacheBuilder.cpp b/view/sharedcache/core/SharedCacheBuilder.cpp index 15b4b7ac..639b5bc4 100644 --- a/view/sharedcache/core/SharedCacheBuilder.cpp +++ b/view/sharedcache/core/SharedCacheBuilder.cpp @@ -43,8 +43,7 @@ bool SharedCacheBuilder::AddFile( try { - auto entry = CacheEntry::FromFile(filePath, fileName, cacheType); - m_cache.AddEntry(std::move(entry)); + m_cache.AddEntry(CacheEntry::FromFile(filePath, fileName, cacheType)); } catch (const std::exception& e) { diff --git a/view/sharedcache/core/SharedCacheController.cpp b/view/sharedcache/core/SharedCacheController.cpp index d300017e..8886748f 100644 --- a/view/sharedcache/core/SharedCacheController.cpp +++ b/view/sharedcache/core/SharedCacheController.cpp @@ -39,14 +39,6 @@ void DeleteController(const FileMetadata& file) LogWarnF("Deleting SharedCacheController for view {:#x}, but there are still {} references", id, controller->m_refs.load()); - // Go through the file accessor cache and remove the entries we reference. - auto& fileAccessorCache = FileAccessorCache::Global(); - for (const auto& entry : controller->GetCache().GetEntries()) - { - auto accessorId = GetCacheAccessorID(entry.GetFilePath()); - fileAccessorCache.RemoveAccessor(accessorId); - } - controllers.erase(it); LogDebugF("Deleted SharedCacheController for view {:?}", file.GetFilename().c_str()); } diff --git a/view/sharedcache/core/SharedCacheController.h b/view/sharedcache/core/SharedCacheController.h index cb381cbd..ff859321 100644 --- a/view/sharedcache/core/SharedCacheController.h +++ b/view/sharedcache/core/SharedCacheController.h @@ -1,6 +1,7 @@ #pragma once #include <regex> +#include <shared_mutex> #include "SharedCache.h" #include "refcountobject.h" diff --git a/view/sharedcache/core/SharedCacheView.cpp b/view/sharedcache/core/SharedCacheView.cpp index dac39278..746bd7eb 100644 --- a/view/sharedcache/core/SharedCacheView.cpp +++ b/view/sharedcache/core/SharedCacheView.cpp @@ -3,8 +3,6 @@ #include <filesystem> #include "SharedCacheController.h" -#include "FileAccessorCache.h" -#include "MappedFileAccessor.h" #include "SharedCacheBuilder.h" using namespace BinaryNinja; @@ -17,12 +15,6 @@ SharedCacheViewType::SharedCacheViewType() : BinaryViewType(VIEW_NAME, VIEW_NAME // We register all our one-shot stuff here, such as the object destructor. void SharedCacheViewType::Register() { - auto fdLimit = AdjustFileDescriptorLimit(); - LogDebugF("Shared Cache processing initialized with a max file descriptor limit of {}", fdLimit); - - // Adjust the global accessor cache to the fdlimit. - FileAccessorCache::Global().SetCacheSize(fdLimit); - RegisterSharedCacheControllerDestructor(); static SharedCacheViewType type; @@ -874,10 +866,6 @@ bool SharedCacheView::InitController() std::chrono::duration<double> elapsed = endTime - startTime; m_logger->LogInfoF("Processing {} entries took {:.3f} seconds", totalEntries, elapsed.count()); - // 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 (totalEntries > FileAccessorCache::Global().GetCacheSize()) - m_logger->LogWarn("Cache contains more entries than the allowed number of opened file handles, this may impact reliability."); - // Verify that we are not missing any entries that were stored in the metadata. // If we are that means we should alert the user that a previously associated cache entry is missing. std::set<std::string> missingCacheEntries = m_secondaryFileNames; diff --git a/view/sharedcache/core/SlideInfo.cpp b/view/sharedcache/core/SlideInfo.cpp index bb265cde..9d793d3a 100644 --- a/view/sharedcache/core/SlideInfo.cpp +++ b/view/sharedcache/core/SlideInfo.cpp @@ -10,7 +10,7 @@ SlideInfoProcessor::SlideInfoProcessor(uint64_t baseAddress) m_baseAddress = baseAddress; } -void ApplySlideInfoV5(MappedFileAccessor& accessor, const SlideMappingInfo& mapping) +void ApplySlideInfoV5(MappedFileRegion& file, 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(MappedFileAccessor& accessor, const SlideMappingInfo& mapp auto cursor = pageStartsOffset; for (size_t i = 0; i < pageStartCount; i++) { - uint16_t delta = accessor.ReadUInt16(cursor); + uint16_t delta = file.ReadUInt16(cursor); cursor += sizeof(uint16_t); if (delta == DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE) continue; @@ -29,23 +29,23 @@ void ApplySlideInfoV5(MappedFileAccessor& accessor, const SlideMappingInfo& mapp do { loc += delta * sizeof(dyld_cache_slide_pointer5); - dyld_cache_slide_pointer5 slideInfo = {accessor.ReadUInt64(loc)}; + dyld_cache_slide_pointer5 slideInfo = {file.ReadUInt64(loc)}; delta = slideInfo.regular.next; if (slideInfo.auth.auth) { uint64_t value = mapping.slideInfoV5.value_add + slideInfo.auth.runtimeOffset; - accessor.WritePointer(loc, value); + file.WriteUInt64(loc, value); } else { uint64_t value = mapping.slideInfoV5.value_add + slideInfo.regular.runtimeOffset; - accessor.WritePointer(loc, value); + file.WriteUInt64(loc, value); } } while (delta != 0); } } -void ApplySlideInfoV3(MappedFileAccessor& accessor, const SlideMappingInfo& mapping) +void ApplySlideInfoV3(MappedFileRegion& file, 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(MappedFileAccessor& accessor, const SlideMappingInfo& mapp auto cursor = pageStartsOffset; for (size_t i = 0; i < pageStartCount; i++) { - uint16_t delta = accessor.ReadUInt16(cursor); + uint16_t delta = file.ReadUInt16(cursor); cursor += sizeof(uint16_t); if (delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE) continue; @@ -64,14 +64,14 @@ void ApplySlideInfoV3(MappedFileAccessor& accessor, const SlideMappingInfo& mapp do { loc += delta * sizeof(dyld_cache_slide_pointer3); - dyld_cache_slide_pointer3 slideInfo = {accessor.ReadUInt64(loc)}; + dyld_cache_slide_pointer3 slideInfo = {file.ReadUInt64(loc)}; delta = slideInfo.plain.offsetToNextPointer; if (slideInfo.auth.authenticated) { uint64_t value = slideInfo.auth.offsetFromSharedCacheBase; value += mapping.slideInfoV3.auth_value_add; - accessor.WritePointer(loc, value); + file.WriteUInt64(loc, value); } else { @@ -79,13 +79,13 @@ void ApplySlideInfoV3(MappedFileAccessor& accessor, const SlideMappingInfo& mapp uint64_t top8Bits = value51 & 0x0007F80000000000; uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFF; uint64_t value = (uint64_t)top8Bits << 13 | bottom43Bits; - accessor.WritePointer(loc, value); + file.WriteUInt64(loc, value); } } while (delta != 0); } } -void ApplySlideInfoV2(MappedFileAccessor& accessor, const SlideMappingInfo& mapping) +void ApplySlideInfoV2(MappedFileRegion& file, 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(MappedFileAccessor& accessor, const SlideMappingInfo& mapp while (delta != 0) { uint64_t loc = pageContent + pageOffset; - uintptr_t rawValue = accessor.ReadUInt64(loc); + uintptr_t rawValue = file.ReadUInt64(loc); delta = (uint32_t)((rawValue & deltaMask) >> deltaShift); uintptr_t value = (rawValue & valueMask); if (value != 0) @@ -114,7 +114,7 @@ void ApplySlideInfoV2(MappedFileAccessor& accessor, const SlideMappingInfo& mapp value += slideAmount; } pageOffset += delta; - accessor.WritePointer(loc, value); + file.WriteUInt64(loc, value); } }; @@ -126,7 +126,7 @@ void ApplySlideInfoV2(MappedFileAccessor& accessor, const SlideMappingInfo& mapp auto cursor = pageStartsOffset; for (size_t i = 0; i < pageStartCount; i++) { - uint16_t start = accessor.ReadUInt16(cursor); + uint16_t start = file.ReadUInt16(cursor); cursor += sizeof(uint16_t); if (start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) continue; @@ -138,7 +138,7 @@ void ApplySlideInfoV2(MappedFileAccessor& accessor, const SlideMappingInfo& mapp do { uint64_t extraCursor = extrasOffset + (j * sizeof(uint16_t)); - auto extra = accessor.ReadUInt16(extraCursor); + auto extra = file.ReadUInt16(extraCursor); uint16_t aStart = extra; uint64_t page = mapping.mappingInfo.fileOffset + (pageSize * i); uint16_t pageStartOffset = (aStart & 0x3FFF) * 4; @@ -156,7 +156,7 @@ void ApplySlideInfoV2(MappedFileAccessor& accessor, const SlideMappingInfo& mapp } } -std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(const MappedFileAccessor& accessor, const CacheEntry& entry) const +std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(const MappedFileRegion& file, const CacheEntry& entry) const { const auto& baseHeader = entry.GetHeader(); @@ -164,7 +164,7 @@ std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(const MappedFile if (baseHeader.slideInfoOffsetUnused) { auto slideInfoAddress = baseHeader.slideInfoOffsetUnused; - auto slideInfoVersion = accessor.ReadUInt32(slideInfoAddress); + auto slideInfoVersion = file.ReadUInt32(slideInfoAddress); if (slideInfoVersion != 2 && slideInfoVersion != 3) { m_logger->LogErrorF("Unsupported slide info version {}", slideInfoVersion); @@ -176,11 +176,11 @@ std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(const MappedFile singleMapping.slideInfoVersion = slideInfoVersion; auto mappingAddress = baseHeader.mappingOffset + sizeof(dyld_cache_mapping_info); - accessor.Read(&singleMapping.mappingInfo, mappingAddress, sizeof(dyld_cache_mapping_info)); + file.Read(&singleMapping.mappingInfo, mappingAddress, sizeof(dyld_cache_mapping_info)); if (singleMapping.slideInfoVersion == 2) - accessor.Read(&singleMapping.slideInfoV2, slideInfoAddress, sizeof(dyld_cache_slide_info_v2)); + file.Read(&singleMapping.slideInfoV2, slideInfoAddress, sizeof(dyld_cache_slide_info_v2)); else if (singleMapping.slideInfoVersion == 3) - accessor.Read(&singleMapping.slideInfoV3, slideInfoAddress, sizeof(dyld_cache_slide_info_v3)); + file.Read(&singleMapping.slideInfoV3, slideInfoAddress, sizeof(dyld_cache_slide_info_v3)); return {singleMapping}; } @@ -190,28 +190,28 @@ std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(const MappedFile { dyld_cache_mapping_and_slide_info mappingAndSlideInfo = {}; auto mappingAndSlideInfoAddress = baseHeader.mappingWithSlideOffset + (i * sizeof(dyld_cache_mapping_and_slide_info)); - accessor.Read(&mappingAndSlideInfo, mappingAndSlideInfoAddress, sizeof(dyld_cache_mapping_and_slide_info)); + file.Read(&mappingAndSlideInfo, mappingAndSlideInfoAddress, sizeof(dyld_cache_mapping_and_slide_info)); if (mappingAndSlideInfo.size == 0 || mappingAndSlideInfo.slideInfoFileOffset == 0) continue; SlideMappingInfo map = {}; map.address = mappingAndSlideInfo.slideInfoFileOffset; - map.slideInfoVersion = accessor.ReadUInt32(map.address); + map.slideInfoVersion = file.ReadUInt32(map.address); map.mappingInfo.address = mappingAndSlideInfo.address; map.mappingInfo.size = mappingAndSlideInfo.size; map.mappingInfo.fileOffset = mappingAndSlideInfo.fileOffset; if (map.slideInfoVersion == 2) { - accessor.Read(&map.slideInfoV2, map.address, sizeof(dyld_cache_slide_info_v2)); + file.Read(&map.slideInfoV2, map.address, sizeof(dyld_cache_slide_info_v2)); } else if (map.slideInfoVersion == 3) { - accessor.Read(&map.slideInfoV3, map.address, sizeof(dyld_cache_slide_info_v3)); + file.Read(&map.slideInfoV3, map.address, sizeof(dyld_cache_slide_info_v3)); map.slideInfoV3.auth_value_add = m_baseAddress; } else if (map.slideInfoVersion == 5) { - accessor.Read(&map.slideInfoV5, map.address, sizeof(dyld_cache_slide_info_v5)); + file.Read(&map.slideInfoV5, map.address, sizeof(dyld_cache_slide_info_v5)); map.slideInfoV5.value_add = m_baseAddress; } else @@ -231,28 +231,21 @@ std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(const MappedFile return mappings; } -void SlideInfoProcessor::ApplyMappings(MappedFileAccessor& accessor, const std::vector<SlideMappingInfo>& mappings) const +void SlideInfoProcessor::ApplyMappings(MappedFileRegion& file, 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(accessor, mapping); + ApplySlideInfoV2(file, mapping); break; case 3: - ApplySlideInfoV3(accessor, mapping); + ApplySlideInfoV3(file, mapping); break; case 5: - ApplySlideInfoV5(accessor, mapping); + ApplySlideInfoV5(file, mapping); break; default: m_logger->LogError( @@ -262,12 +255,12 @@ void SlideInfoProcessor::ApplyMappings(MappedFileAccessor& accessor, const std:: } } -std::vector<SlideMappingInfo> SlideInfoProcessor::ProcessEntry(MappedFileAccessor& accessor, const CacheEntry& entry) const +std::vector<SlideMappingInfo> SlideInfoProcessor::ProcessEntry(MappedFileRegion& file, const CacheEntry& entry) const { try { - auto slideMappings = ReadEntryInfo(accessor, entry); - ApplyMappings(accessor, slideMappings); + auto slideMappings = ReadEntryInfo(file, entry); + ApplyMappings(file, slideMappings); return slideMappings; } catch (const std::exception& e) diff --git a/view/sharedcache/core/SlideInfo.h b/view/sharedcache/core/SlideInfo.h index 75134985..fda5ea4d 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(const MappedFileAccessor& accessor, const CacheEntry& entry) const; + std::vector<SlideMappingInfo> ReadEntryInfo(const MappedFileRegion& file, const CacheEntry& entry) const; // Write the slide information back to the entries memory mapped regions. - void ApplyMappings(MappedFileAccessor& accessor, const std::vector<SlideMappingInfo>& mappings) const; + void ApplyMappings(MappedFileRegion& file, const std::vector<SlideMappingInfo>& mappings) const; - std::vector<SlideMappingInfo> ProcessEntry(MappedFileAccessor& accessor, const CacheEntry& entry) const; + std::vector<SlideMappingInfo> ProcessEntry(MappedFileRegion& file, const CacheEntry& entry) const; }; diff --git a/view/sharedcache/core/VirtualMemory.cpp b/view/sharedcache/core/VirtualMemory.cpp index 475d0430..3bc57282 100644 --- a/view/sharedcache/core/VirtualMemory.cpp +++ b/view/sharedcache/core/VirtualMemory.cpp @@ -1,25 +1,22 @@ #include "VirtualMemory.h" -void VirtualMemory::MapRegion(WeakFileAccessor fileAccessor, AddressRange mappedRange, uint64_t fileOffset) +void VirtualMemory::MapRegion(std::shared_ptr<MappedFileRegion> file, AddressRange mappedRange, uint64_t fileOffset) { - // Create a new VirtualMemoryRegion object - VirtualMemoryRegion region(fileOffset, std::move(fileAccessor)); + VirtualMemoryRegion region(fileOffset, std::move(file)); // TODO: How to handle overlapping regions? for (const auto& [existingRange, existingRegion] : m_regions) { if (existingRange.Overlaps(mappedRange)) { - // Handle overlapping regions, e.g., throw an exception or skip the mapping BinaryNinja::LogErrorF("Overlapping memory region {:#x}", existingRange.start); } } - // Insert the region into the map - m_regions.insert_or_assign(mappedRange, region); + m_regions.insert_or_assign(mappedRange, std::move(region)); } -std::optional<VirtualMemoryRegion> VirtualMemory::GetRegionAtAddress(uint64_t address, uint64_t& addressOffset) +const VirtualMemoryRegion* VirtualMemory::FindRegionAtAddress(uint64_t address, uint64_t& addressOffset) const { if (const auto& it = m_regions.find(address); it != m_regions.end()) { @@ -27,35 +24,26 @@ std::optional<VirtualMemoryRegion> VirtualMemory::GetRegionAtAddress(uint64_t ad // be multiple in newer caches) This is relevant for reading out the data in the rest of this file. The second // item in the returned pair is the offset of `address` within the file. const auto& range = it->first; - auto mapping = it->second; + const auto& mapping = it->second; addressOffset = mapping.fileOffset + (address - range.start); - return mapping; + return &mapping; } - return std::nullopt; + return nullptr; } -std::optional<VirtualMemoryRegion> VirtualMemory::GetRegionAtAddress(uint64_t address) +const VirtualMemoryRegion* VirtualMemory::FindRegionAtAddress(uint64_t address) const { uint64_t offset; - return GetRegionAtAddress(address, offset); + return FindRegionAtAddress(address, offset); } -bool VirtualMemory::IsAddressMapped(uint64_t address) +bool VirtualMemory::IsAddressMapped(uint64_t address) const { return m_regions.find(address) != m_regions.end(); } -void VirtualMemory::WritePointer(uint64_t address, size_t pointer) -{ - uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) - throw UnmappedRegionException(address); - region->fileAccessor.lock()->WritePointer(offset, pointer); -} - -uint64_t VirtualMemory::ReadPointer(uint64_t address) +uint64_t VirtualMemory::ReadPointer(uint64_t address) const { switch (m_addressSize) { @@ -70,112 +58,112 @@ uint64_t VirtualMemory::ReadPointer(uint64_t address) } } -std::string VirtualMemory::ReadCString(uint64_t address) +std::string VirtualMemory::ReadCString(uint64_t address) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadNullTermString(offset); + return region->file->ReadNullTermString(offset); } -uint8_t VirtualMemory::ReadUInt8(uint64_t address) +uint8_t VirtualMemory::ReadUInt8(uint64_t address) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadUInt8(offset); + return region->file->ReadUInt8(offset); } -int8_t VirtualMemory::ReadInt8(uint64_t address) +int8_t VirtualMemory::ReadInt8(uint64_t address) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadInt8(offset); + return region->file->ReadInt8(offset); } -uint16_t VirtualMemory::ReadUInt16(uint64_t address) +uint16_t VirtualMemory::ReadUInt16(uint64_t address) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadUInt16(offset); + return region->file->ReadUInt16(offset); } -int16_t VirtualMemory::ReadInt16(uint64_t address) +int16_t VirtualMemory::ReadInt16(uint64_t address) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadInt16(offset); + return region->file->ReadInt16(offset); } -uint32_t VirtualMemory::ReadUInt32(uint64_t address) +uint32_t VirtualMemory::ReadUInt32(uint64_t address) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadUInt32(offset); + return region->file->ReadUInt32(offset); } -int32_t VirtualMemory::ReadInt32(uint64_t address) +int32_t VirtualMemory::ReadInt32(uint64_t address) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadInt32(offset); + return region->file->ReadInt32(offset); } -uint64_t VirtualMemory::ReadUInt64(uint64_t address) +uint64_t VirtualMemory::ReadUInt64(uint64_t address) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadUInt64(offset); + return region->file->ReadUInt64(offset); } -int64_t VirtualMemory::ReadInt64(uint64_t address) +int64_t VirtualMemory::ReadInt64(uint64_t address) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadInt64(offset); + return region->file->ReadInt64(offset); } -BinaryNinja::DataBuffer VirtualMemory::ReadBuffer(uint64_t address, size_t length) +BinaryNinja::DataBuffer VirtualMemory::ReadBuffer(uint64_t address, size_t length) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadBuffer(offset, length); + return region->file->ReadBuffer(offset, length); } -std::pair<const uint8_t*, const uint8_t*> VirtualMemory::ReadSpan(uint64_t address, size_t length) +std::span<const uint8_t> VirtualMemory::ReadSpan(uint64_t address, size_t length) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - return region->fileAccessor.lock()->ReadSpan(offset, length); + return region->file->ReadSpan(offset, length); } -void VirtualMemory::Read(void* dest, uint64_t address, size_t length) +void VirtualMemory::Read(void* dest, uint64_t address, size_t length) const { uint64_t offset; - auto region = GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); - region->fileAccessor.lock()->Read(dest, offset, length); + region->file->Read(dest, offset, length); } VirtualMemoryReader::VirtualMemoryReader(std::shared_ptr<VirtualMemory> memory) @@ -187,74 +175,11 @@ VirtualMemoryReader::VirtualMemoryReader(std::shared_ptr<VirtualMemory> memory) std::string VirtualMemoryReader::ReadCString(uint64_t address, size_t maxLength) { uint64_t offset; - auto region = m_memory->GetRegionAtAddress(address, offset); - if (!region.has_value()) + auto* region = m_memory->FindRegionAtAddress(address, offset); + if (!region) throw UnmappedRegionException(address); // TODO: Advance cursor? - return region->fileAccessor.lock()->ReadNullTermString(offset, maxLength); -} - -uint64_t VirtualMemoryReader::ReadULEB128(size_t cursorLimit) -{ - uint64_t result = 0; - int bit = 0; - uint64_t offset; - auto mapping = m_memory->GetRegionAtAddress(m_cursor, offset); - auto fileLimit = offset + (cursorLimit - m_cursor); - auto fa = mapping->fileAccessor.lock(); - auto* fileBuff = (uint8_t*)fa->Data(); - do - { - if (offset >= fileLimit) - return -1; - uint64_t slice = ((uint64_t*)&((fileBuff)[offset]))[0] & 0x7f; - if (bit > 63) - return -1; - else - { - result |= (slice << bit); - bit += 7; - } - } while (((uint64_t*)&(fileBuff[offset++]))[0] & 0x80); - // TODO: There has got to be a better way to prevent this... - fa->Data(); // prevent deallocation of the fileAccessor as we're operating on the raw data buffer - return result; -} - -int64_t VirtualMemoryReader::ReadSLEB128(size_t cursorLimit) -{ - constexpr size_t BYTE_SIZE = 7; // Number of bits in each SLEB128 byte - constexpr size_t INT64_BITS = 64; // Total number of bits in an int64_t - - int64_t value = 0; - size_t shift = 0; - uint64_t offset; - - // Retrieve associated memory region and the file buffer - auto mapping = m_memory->GetRegionAtAddress(m_cursor, offset); - auto fileLimit = offset + (cursorLimit - m_cursor); - auto fileAccessor = mapping->fileAccessor.lock(); - auto* fileBuffer = static_cast<uint8_t*>(fileAccessor->Data()); - - // Loop through the SLEB128 encoded bytes - while (offset < fileLimit) - { - uint8_t currentByte = fileBuffer[offset++]; - value |= (static_cast<int64_t>(currentByte & 0x7F) << shift); - shift += BYTE_SIZE; - - if ((currentByte & 0x80) == 0) // If MSB is not set, we're done - break; - } - - // Properly sign-extend the value according to its size - value = (value << (INT64_BITS - shift)) >> (INT64_BITS - shift); - - // TODO: There has got to be a better way to prevent this... - // Prevent deallocation of the fileAccessor - fileAccessor->Data(); - - return value; + return region->file->ReadNullTermString(offset, maxLength); } uint64_t VirtualMemoryReader::ReadPointer() diff --git a/view/sharedcache/core/VirtualMemory.h b/view/sharedcache/core/VirtualMemory.h index a490fa24..0cd23a4c 100644 --- a/view/sharedcache/core/VirtualMemory.h +++ b/view/sharedcache/core/VirtualMemory.h @@ -1,6 +1,5 @@ #pragma once -#include "FileAccessorCache.h" -#include "MappedFileAccessor.h" +#include "MappedFileRegion.h" #include "Utility.h" class UnmappedRegionException : public std::exception @@ -22,21 +21,10 @@ public: struct VirtualMemoryRegion { uint64_t fileOffset; - // Access the memory regions contents through this. - // NOTE: Any read through this should be seeked to `fileOffset` - WeakFileAccessor fileAccessor; + std::shared_ptr<MappedFileRegion> file; - VirtualMemoryRegion(uint64_t offset, WeakFileAccessor accessor) - : fileOffset(offset), fileAccessor(std::move(accessor)) {} - - - VirtualMemoryRegion(const VirtualMemoryRegion&) = default; - - VirtualMemoryRegion& operator=(const VirtualMemoryRegion&) = default; - - VirtualMemoryRegion(VirtualMemoryRegion&&) = default; - - VirtualMemoryRegion& operator=(VirtualMemoryRegion&&) = default; + VirtualMemoryRegion(uint64_t offset, std::shared_ptr<MappedFileRegion> f) + : fileOffset(offset), file(std::move(f)) {} }; // Contains information to handle mapping of multiple mapped files into a single memory space. @@ -44,7 +32,6 @@ struct VirtualMemoryRegion // and map them into Binary Ninja. class VirtualMemory { - std::shared_mutex m_regionMutex; AddressRangeMap<VirtualMemoryRegion> m_regions; uint64_t m_addressSize = 8; @@ -53,45 +40,39 @@ public: uint64_t GetAddressSize() const { return m_addressSize; } - // At no point do we ever store a strong pointer to a file accessor, that is the job of the `FileAccessorCache`. - void MapRegion(WeakFileAccessor fileAccessor, AddressRange mappedRange, uint64_t fileOffset); + void MapRegion(std::shared_ptr<MappedFileRegion> file, AddressRange mappedRange, uint64_t fileOffset); - // Returns the region in virtual memory, along with the offset into that region where the address is located. - // Using the regions file accessor and the address offset you can read a regions content. - std::optional<VirtualMemoryRegion> GetRegionAtAddress(uint64_t address, uint64_t& addressOffset); + const VirtualMemoryRegion* FindRegionAtAddress(uint64_t address, uint64_t& addressOffset) const; - std::optional<VirtualMemoryRegion> GetRegionAtAddress(uint64_t address); + const VirtualMemoryRegion* FindRegionAtAddress(uint64_t address) const; - bool IsAddressMapped(uint64_t address); + bool IsAddressMapped(uint64_t address) const; - // Write a pointer at a given address. This pointer is never persisted when a file accessor is closed. - void WritePointer(uint64_t address, size_t pointer); + uint64_t ReadPointer(uint64_t address) const; - uint64_t ReadPointer(uint64_t address); + std::string ReadCString(uint64_t address) const; - std::string ReadCString(uint64_t address); + uint8_t ReadUInt8(uint64_t address) const; - uint8_t ReadUInt8(uint64_t address); + int8_t ReadInt8(uint64_t address) const; - int8_t ReadInt8(uint64_t address); + uint16_t ReadUInt16(uint64_t address) const; - uint16_t ReadUInt16(uint64_t address); + int16_t ReadInt16(uint64_t address) const; - int16_t ReadInt16(uint64_t address); + uint32_t ReadUInt32(uint64_t address) const; - uint32_t ReadUInt32(uint64_t address); + int32_t ReadInt32(uint64_t address) const; - int32_t ReadInt32(uint64_t address); + uint64_t ReadUInt64(uint64_t address) const; - uint64_t ReadUInt64(uint64_t address); + int64_t ReadInt64(uint64_t address) const; - int64_t ReadInt64(uint64_t address); + BinaryNinja::DataBuffer ReadBuffer(uint64_t address, size_t length) const; - BinaryNinja::DataBuffer ReadBuffer(uint64_t address, size_t length); + std::span<const uint8_t> ReadSpan(uint64_t address, size_t length) const; - std::pair<const uint8_t*, const uint8_t*> ReadSpan(uint64_t address, size_t length); - - void Read(void* dest, uint64_t address, size_t length); + void Read(void* dest, uint64_t address, size_t length) const; }; class VirtualMemoryReader @@ -115,10 +96,6 @@ public: std::string ReadCString(uint64_t address, size_t maxLength = -1); - uint64_t ReadULEB128(size_t cursorLimit); - - int64_t ReadSLEB128(size_t cursorLimit); - uint64_t ReadPointer(); uint64_t ReadPointer(uint64_t address); |
