summaryrefslogtreecommitdiff
path: root/view/sharedcache
diff options
context:
space:
mode:
authorMark Rowe <mark@vector35.com>2026-02-23 23:26:27 -0800
committerMark Rowe <mark@vector35.com>2026-02-24 14:24:54 -0800
commita72b98ed5a357bb380d81f07f3f36946aa729bb2 (patch)
treeca48b3607721fa2c109b7989ae4b911bab89ea6d /view/sharedcache
parent3eda43f185a0411538745a99e251122e6a9192e0 (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.cpp95
-rw-r--r--view/sharedcache/core/FileAccessorCache.h76
-rw-r--r--view/sharedcache/core/MachO.cpp4
-rw-r--r--view/sharedcache/core/MappedFile.cpp193
-rw-r--r--view/sharedcache/core/MappedFile.h86
-rw-r--r--view/sharedcache/core/MappedFileAccessor.cpp111
-rw-r--r--view/sharedcache/core/MappedFileAccessor.h103
-rw-r--r--view/sharedcache/core/MappedFileRegion.cpp213
-rw-r--r--view/sharedcache/core/MappedFileRegion.h72
-rw-r--r--view/sharedcache/core/SharedCache.cpp69
-rw-r--r--view/sharedcache/core/SharedCache.h7
-rw-r--r--view/sharedcache/core/SharedCacheBuilder.cpp3
-rw-r--r--view/sharedcache/core/SharedCacheController.cpp8
-rw-r--r--view/sharedcache/core/SharedCacheController.h1
-rw-r--r--view/sharedcache/core/SharedCacheView.cpp12
-rw-r--r--view/sharedcache/core/SlideInfo.cpp71
-rw-r--r--view/sharedcache/core/SlideInfo.h6
-rw-r--r--view/sharedcache/core/VirtualMemory.cpp199
-rw-r--r--view/sharedcache/core/VirtualMemory.h65
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);