summaryrefslogtreecommitdiff
path: root/view/sharedcache/core/MappedFile.cpp
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-03-10 11:05:40 -0400
committerMason Reed <mason@vector35.com>2025-04-02 05:36:54 -0400
commit25cc02431b61097b2adfc2fbc493b648b0300c3b (patch)
treea79d9c4f4f67234d3bf9bda413e8608f479a4cc8 /view/sharedcache/core/MappedFile.cpp
parentfa85bf28502286c4821427c5d0ed91a7ed46f8f6 (diff)
[SharedCache] Refactor Shared Cache
In absence of a better name, this commit refactors the shared cache code.
Diffstat (limited to 'view/sharedcache/core/MappedFile.cpp')
-rw-r--r--view/sharedcache/core/MappedFile.cpp193
1 files changed, 193 insertions, 0 deletions
diff --git a/view/sharedcache/core/MappedFile.cpp b/view/sharedcache/core/MappedFile.cpp
new file mode 100644
index 00000000..e0b805bc
--- /dev/null
+++ b/view/sharedcache/core/MappedFile.cpp
@@ -0,0 +1,193 @@
+#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::LogError("mmap failed: %s", 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