diff options
-rw-r--r-- | src/common/host_memory.cpp | 117 |
1 files changed, 88 insertions, 29 deletions
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index c6d65aab9..9ae384f01 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -1,18 +1,10 @@ #ifdef _WIN32 -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0A00 // Windows 10 - -#include <windows.h> - -#include <boost/icl/separate_interval_set.hpp> - #include <iterator> #include <unordered_map> - -#pragma comment(lib, "mincore.lib") +#include <boost/icl/separate_interval_set.hpp> +#include <windows.h> +#include "common/dynamic_library.h" #elif defined(__linux__) // ^^^ Windows ^^^ vvv Linux vvv @@ -40,38 +32,99 @@ constexpr size_t HugePageSize = 0x200000; #ifdef _WIN32 +// Manually imported for MinGW compatibility +#ifndef MEM_RESERVE_PLACEHOLDER +#define MEM_RESERVE_PLACEHOLDER 0x0004000 +#endif +#ifndef MEM_REPLACE_PLACEHOLDER +#define MEM_REPLACE_PLACEHOLDER 0x00004000 +#endif +#ifndef MEM_COALESCE_PLACEHOLDERS +#define MEM_COALESCE_PLACEHOLDERS 0x00000001 +#endif +#ifndef MEM_PRESERVE_PLACEHOLDER +#define MEM_PRESERVE_PLACEHOLDER 0x00000002 +#endif + +using PFN_CreateFileMapping2 = _Ret_maybenull_ HANDLE(WINAPI*)( + _In_ HANDLE File, _In_opt_ SECURITY_ATTRIBUTES* SecurityAttributes, _In_ ULONG DesiredAccess, + _In_ ULONG PageProtection, _In_ ULONG AllocationAttributes, _In_ ULONG64 MaximumSize, + _In_opt_ PCWSTR Name, + _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters, + _In_ ULONG ParameterCount); + +using PFN_VirtualAlloc2 = _Ret_maybenull_ PVOID(WINAPI*)( + _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, _In_ SIZE_T Size, + _In_ ULONG AllocationType, _In_ ULONG PageProtection, + _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters, + _In_ ULONG ParameterCount); + +using PFN_MapViewOfFile3 = _Ret_maybenull_ PVOID(WINAPI*)( + _In_ HANDLE FileMapping, _In_opt_ HANDLE Process, _In_opt_ PVOID BaseAddress, + _In_ ULONG64 Offset, _In_ SIZE_T ViewSize, _In_ ULONG AllocationType, _In_ ULONG PageProtection, + _Inout_updates_opt_(ParameterCount) MEM_EXTENDED_PARAMETER* ExtendedParameters, + _In_ ULONG ParameterCount); + +using PFN_UnmapViewOfFile2 = BOOL(WINAPI*)(_In_ HANDLE Process, _In_ PVOID BaseAddress, + _In_ ULONG UnmapFlags); + +template <typename T> +static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) { + if (!dll.GetSymbol(name, &pfn)) { + LOG_CRITICAL(HW_Memory, "Failed to load {}", name); + throw std::bad_alloc{}; + } +} + class HostMemory::Impl { public: explicit Impl(size_t backing_size_, size_t virtual_size_) - : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()} { + : backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()}, + kernelbase_dll("Kernelbase") { + if (!kernelbase_dll.IsOpen()) { + LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll"); + throw std::bad_alloc{}; + } + GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2); + GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2); + GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3); + GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2); + // Allocate backing file map backing_handle = - CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, - PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); + pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, + PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0); if (!backing_handle) { + LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory", + backing_size >> 20); throw std::bad_alloc{}; } // Allocate a virtual memory for the backing file map as placeholder - backing_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, backing_size, - MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, - PAGE_NOACCESS, nullptr, 0)); + backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size, + MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, nullptr, 0)); if (!backing_base) { Release(); + LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory", + backing_size >> 20); throw std::bad_alloc{}; } // Map backing placeholder - void* const ret = MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, - MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); + void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, + MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0); if (ret != backing_base) { Release(); + LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20); throw std::bad_alloc{}; } // Allocate virtual address placeholder - virtual_base = static_cast<u8*>(VirtualAlloc2(process, nullptr, virtual_size, - MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, - PAGE_NOACCESS, nullptr, 0)); + virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size, + MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, + PAGE_NOACCESS, nullptr, 0)); if (!virtual_base) { Release(); + LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory", + virtual_size >> 30); throw std::bad_alloc{}; } } @@ -136,8 +189,8 @@ private: void Release() { if (!placeholders.empty()) { for (const auto& placeholder : placeholders) { - if (!UnmapViewOfFile2(process, virtual_base + placeholder.lower(), - MEM_PRESERVE_PLACEHOLDER)) { + if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder.lower(), + MEM_PRESERVE_PLACEHOLDER)) { LOG_CRITICAL(HW_Memory, "Failed to unmap virtual memory placeholder"); } } @@ -149,7 +202,7 @@ private: } } if (backing_base) { - if (!UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) { + if (!pfn_UnmapViewOfFile2(process, backing_base, MEM_PRESERVE_PLACEHOLDER)) { LOG_CRITICAL(HW_Memory, "Failed to unmap backing memory placeholder"); } if (!VirtualFreeEx(process, backing_base, 0, MEM_RELEASE)) { @@ -184,8 +237,8 @@ private: const bool split_left = unmap_begin > placeholder_begin; const bool split_right = unmap_end < placeholder_end; - if (!UnmapViewOfFile2(process, virtual_base + placeholder_begin, - MEM_PRESERVE_PLACEHOLDER)) { + if (!pfn_UnmapViewOfFile2(process, virtual_base + placeholder_begin, + MEM_PRESERVE_PLACEHOLDER)) { LOG_CRITICAL(HW_Memory, "Failed to unmap placeholder"); } // If we have to remap memory regions due to partial unmaps, we are in a data race as @@ -235,8 +288,8 @@ private: } void MapView(size_t virtual_offset, size_t host_offset, size_t length) { - if (!MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset, - length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) { + if (!pfn_MapViewOfFile3(backing_handle, process, virtual_base + virtual_offset, host_offset, + length, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0)) { LOG_CRITICAL(HW_Memory, "Failed to map placeholder"); } } @@ -279,6 +332,12 @@ private: HANDLE process{}; ///< Current process handle HANDLE backing_handle{}; ///< File based backing memory + DynamicLibrary kernelbase_dll; + PFN_CreateFileMapping2 pfn_CreateFileMapping2{}; + PFN_VirtualAlloc2 pfn_VirtualAlloc2{}; + PFN_MapViewOfFile3 pfn_MapViewOfFile3{}; + PFN_UnmapViewOfFile2 pfn_UnmapViewOfFile2{}; + std::mutex placeholder_mutex; ///< Mutex for placeholders boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders std::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset |