[Memory/CPU] UWP: Support separate code execution and write memory, FromApp functions + other Windows memory fixes
This commit is contained in:
@@ -29,7 +29,7 @@ class Win32MappedMemory : public MappedMemory {
|
||||
if (data_) {
|
||||
UnmapViewOfFile(data_);
|
||||
}
|
||||
if (mapping_handle != INVALID_HANDLE_VALUE) {
|
||||
if (mapping_handle) {
|
||||
CloseHandle(mapping_handle);
|
||||
}
|
||||
if (file_handle != INVALID_HANDLE_VALUE) {
|
||||
@@ -42,9 +42,9 @@ class Win32MappedMemory : public MappedMemory {
|
||||
UnmapViewOfFile(data_);
|
||||
data_ = nullptr;
|
||||
}
|
||||
if (mapping_handle != INVALID_HANDLE_VALUE) {
|
||||
if (mapping_handle) {
|
||||
CloseHandle(mapping_handle);
|
||||
mapping_handle = INVALID_HANDLE_VALUE;
|
||||
mapping_handle = nullptr;
|
||||
}
|
||||
if (file_handle != INVALID_HANDLE_VALUE) {
|
||||
if (truncate_size) {
|
||||
@@ -65,8 +65,13 @@ class Win32MappedMemory : public MappedMemory {
|
||||
size_t aligned_length = length + (offset - aligned_offset);
|
||||
|
||||
UnmapViewOfFile(data_);
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
data_ = MapViewOfFile(mapping_handle, view_access_, aligned_offset >> 32,
|
||||
aligned_offset & 0xFFFFFFFF, aligned_length);
|
||||
#else
|
||||
data_ = MapViewOfFileFromApp(mapping_handle, ULONG(view_access_),
|
||||
ULONG64(aligned_offset), aligned_length);
|
||||
#endif
|
||||
if (!data_) {
|
||||
return false;
|
||||
}
|
||||
@@ -84,7 +89,7 @@ class Win32MappedMemory : public MappedMemory {
|
||||
}
|
||||
|
||||
HANDLE file_handle = INVALID_HANDLE_VALUE;
|
||||
HANDLE mapping_handle = INVALID_HANDLE_VALUE;
|
||||
HANDLE mapping_handle = nullptr;
|
||||
DWORD view_access_ = 0;
|
||||
};
|
||||
|
||||
@@ -129,16 +134,28 @@ std::unique_ptr<MappedMemory> MappedMemory::Open(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mm->mapping_handle = CreateFileMapping(mm->file_handle, nullptr,
|
||||
mapping_protect, aligned_length >> 32,
|
||||
aligned_length & 0xFFFFFFFF, nullptr);
|
||||
if (mm->mapping_handle == INVALID_HANDLE_VALUE) {
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
mm->mapping_handle = CreateFileMapping(
|
||||
mm->file_handle, nullptr, mapping_protect, DWORD(aligned_length >> 32),
|
||||
DWORD(aligned_length), nullptr);
|
||||
#else
|
||||
mm->mapping_handle =
|
||||
CreateFileMappingFromApp(mm->file_handle, nullptr, ULONG(mapping_protect),
|
||||
ULONG64(aligned_length), nullptr);
|
||||
#endif
|
||||
if (!mm->mapping_handle) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
mm->data_ = reinterpret_cast<uint8_t*>(MapViewOfFile(
|
||||
mm->mapping_handle, view_access, static_cast<DWORD>(aligned_offset >> 32),
|
||||
static_cast<DWORD>(aligned_offset & 0xFFFFFFFF), aligned_length));
|
||||
mm->mapping_handle, view_access, DWORD(aligned_offset >> 32),
|
||||
DWORD(aligned_offset), aligned_length));
|
||||
#else
|
||||
mm->data_ = reinterpret_cast<uint8_t*>(
|
||||
MapViewOfFileFromApp(mm->mapping_handle, ULONG(view_access),
|
||||
ULONG64(aligned_offset), aligned_length));
|
||||
#endif
|
||||
if (!mm->data_) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -203,8 +220,8 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||
class Chunk {
|
||||
public:
|
||||
explicit Chunk(size_t capacity)
|
||||
: file_handle_(0),
|
||||
mapping_handle_(0),
|
||||
: file_handle_(INVALID_HANDLE_VALUE),
|
||||
mapping_handle_(nullptr),
|
||||
data_(nullptr),
|
||||
offset_(0),
|
||||
capacity_(capacity),
|
||||
@@ -217,7 +234,7 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||
if (mapping_handle_) {
|
||||
CloseHandle(mapping_handle_);
|
||||
}
|
||||
if (file_handle_) {
|
||||
if (file_handle_ != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(file_handle_);
|
||||
}
|
||||
}
|
||||
@@ -231,13 +248,19 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||
|
||||
file_handle_ = CreateFile(path.c_str(), file_access, file_share, nullptr,
|
||||
create_mode, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (!file_handle_) {
|
||||
if (file_handle_ == INVALID_HANDLE_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
mapping_handle_ =
|
||||
CreateFileMapping(file_handle_, nullptr, mapping_protect, 0,
|
||||
static_cast<DWORD>(capacity_), nullptr);
|
||||
CreateFileMapping(file_handle_, nullptr, mapping_protect,
|
||||
DWORD(capacity_ >> 32), DWORD(capacity_), nullptr);
|
||||
#else
|
||||
mapping_handle_ = CreateFileMappingFromApp(file_handle_, nullptr,
|
||||
ULONG(mapping_protect),
|
||||
ULONG64(capacity_), nullptr);
|
||||
#endif
|
||||
if (!mapping_handle_) {
|
||||
return false;
|
||||
}
|
||||
@@ -247,10 +270,32 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||
if (low_address_space) {
|
||||
bool successful = false;
|
||||
data_ = reinterpret_cast<uint8_t*>(0x10000000);
|
||||
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
HANDLE process = GetCurrentProcess();
|
||||
#endif
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
if (MapViewOfFileEx(mapping_handle_, view_access, 0, 0, capacity_,
|
||||
data_)) {
|
||||
successful = true;
|
||||
}
|
||||
#else
|
||||
// VirtualAlloc2FromApp and MapViewOfFile3FromApp were added in
|
||||
// 10.0.17134.0.
|
||||
// https://docs.microsoft.com/en-us/uwp/win32-and-com/win32-apis
|
||||
if (VirtualAlloc2FromApp(process, data_, capacity_,
|
||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||
PAGE_NOACCESS, nullptr, 0)) {
|
||||
if (MapViewOfFile3FromApp(mapping_handle_, process, data_, 0,
|
||||
capacity_, MEM_REPLACE_PLACEHOLDER,
|
||||
ULONG(mapping_protect), nullptr, 0)) {
|
||||
successful = true;
|
||||
} else {
|
||||
VirtualFree(data_, capacity_, MEM_RELEASE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (successful) {
|
||||
break;
|
||||
}
|
||||
data_ += capacity_;
|
||||
@@ -261,8 +306,13 @@ class Win32ChunkedMappedMemoryWriter : public ChunkedMappedMemoryWriter {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
data_ = reinterpret_cast<uint8_t*>(
|
||||
MapViewOfFile(mapping_handle_, view_access, 0, 0, capacity_));
|
||||
#else
|
||||
data_ = reinterpret_cast<uint8_t*>(MapViewOfFileFromApp(
|
||||
mapping_handle_, ULONG(view_access), 0, capacity_));
|
||||
#endif
|
||||
}
|
||||
if (!data_) {
|
||||
return false;
|
||||
|
||||
@@ -8,11 +8,26 @@
|
||||
*/
|
||||
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
DEFINE_bool(
|
||||
writable_executable_memory, true,
|
||||
"Allow mapping memory with both write and execute access, for simulating "
|
||||
"behavior on platforms where that's not supported",
|
||||
"Memory");
|
||||
|
||||
namespace xe {
|
||||
namespace memory {
|
||||
|
||||
bool IsWritableExecutableMemoryPreferred() {
|
||||
return IsWritableExecutableMemorySupported() &&
|
||||
cvars::writable_executable_memory;
|
||||
}
|
||||
|
||||
} // namespace memory
|
||||
|
||||
// TODO(benvanik): fancy AVX versions.
|
||||
// https://github.com/gnuradio/volk/blob/master/kernels/volk/volk_16u_byteswap.h
|
||||
|
||||
@@ -35,6 +35,7 @@ enum class PageAccess {
|
||||
kNoAccess = 0,
|
||||
kReadOnly = 1 << 0,
|
||||
kReadWrite = kReadOnly | 1 << 1,
|
||||
kExecuteReadOnly = kReadOnly | 1 << 2,
|
||||
kExecuteReadWrite = kReadWrite | 1 << 2,
|
||||
};
|
||||
|
||||
@@ -49,6 +50,16 @@ enum class DeallocationType {
|
||||
kDecommit = 1 << 1,
|
||||
};
|
||||
|
||||
// Whether the host allows the pages to be allocated or mapped with
|
||||
// PageAccess::kExecuteReadWrite - if not, separate mappings backed by the same
|
||||
// memory-mapped file must be used to write to executable pages.
|
||||
bool IsWritableExecutableMemorySupported();
|
||||
|
||||
// Whether PageAccess::kExecuteReadWrite is a supported and preferred way of
|
||||
// writing executable memory, useful for simulating how Xenia would work without
|
||||
// writable executable memory on a system with it.
|
||||
bool IsWritableExecutableMemoryPreferred();
|
||||
|
||||
// Allocates a block of memory at the given page-aligned base address.
|
||||
// Fails if the memory is not available.
|
||||
// Specify nullptr for base_address to leave it up to the system.
|
||||
|
||||
@@ -39,6 +39,8 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
|
||||
return PROT_READ;
|
||||
case PageAccess::kReadWrite:
|
||||
return PROT_READ | PROT_WRITE;
|
||||
case PageAccess::kExecuteReadOnly:
|
||||
return PROT_READ | PROT_EXEC;
|
||||
case PageAccess::kExecuteReadWrite:
|
||||
return PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
default:
|
||||
@@ -47,6 +49,8 @@ uint32_t ToPosixProtectFlags(PageAccess access) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsWritableExecutableMemorySupported() { return true; }
|
||||
|
||||
void* AllocFixed(void* base_address, size_t length,
|
||||
AllocationType allocation_type, PageAccess access) {
|
||||
// mmap does not support reserve / commit, so ignore allocation_type.
|
||||
@@ -112,6 +116,7 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
|
||||
oflag = 0;
|
||||
break;
|
||||
case PageAccess::kReadOnly:
|
||||
case PageAccess::kExecuteReadOnly:
|
||||
oflag = O_RDONLY;
|
||||
break;
|
||||
case PageAccess::kReadWrite:
|
||||
|
||||
@@ -42,6 +42,8 @@ DWORD ToWin32ProtectFlags(PageAccess access) {
|
||||
return PAGE_READONLY;
|
||||
case PageAccess::kReadWrite:
|
||||
return PAGE_READWRITE;
|
||||
case PageAccess::kExecuteReadOnly:
|
||||
return PAGE_EXECUTE_READ;
|
||||
case PageAccess::kExecuteReadWrite:
|
||||
return PAGE_EXECUTE_READWRITE;
|
||||
default:
|
||||
@@ -63,6 +65,8 @@ PageAccess ToXeniaProtectFlags(DWORD access) {
|
||||
return PageAccess::kReadOnly;
|
||||
case PAGE_READWRITE:
|
||||
return PageAccess::kReadWrite;
|
||||
case PAGE_EXECUTE_READ:
|
||||
return PageAccess::kExecuteReadOnly;
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
return PageAccess::kExecuteReadWrite;
|
||||
default:
|
||||
@@ -70,6 +74,17 @@ PageAccess ToXeniaProtectFlags(DWORD access) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsWritableExecutableMemorySupported() {
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
return true;
|
||||
#else
|
||||
// To test FromApp functions on desktop, replace
|
||||
// WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) with 0 in the #ifs and
|
||||
// link to WindowsApp.lib.
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* AllocFixed(void* base_address, size_t length,
|
||||
AllocationType allocation_type, PageAccess access) {
|
||||
DWORD alloc_type = 0;
|
||||
@@ -88,7 +103,12 @@ void* AllocFixed(void* base_address, size_t length,
|
||||
break;
|
||||
}
|
||||
DWORD protect = ToWin32ProtectFlags(access);
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
return VirtualAlloc(base_address, length, alloc_type, protect);
|
||||
#else
|
||||
return VirtualAllocFromApp(base_address, length, ULONG(alloc_type),
|
||||
ULONG(protect));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DeallocFixed(void* base_address, size_t length,
|
||||
@@ -115,13 +135,19 @@ bool Protect(void* base_address, size_t length, PageAccess access,
|
||||
*out_old_access = PageAccess::kNoAccess;
|
||||
}
|
||||
DWORD new_protect = ToWin32ProtectFlags(access);
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
DWORD old_protect = 0;
|
||||
BOOL result = VirtualProtect(base_address, length, new_protect, &old_protect);
|
||||
#else
|
||||
ULONG old_protect = 0;
|
||||
BOOL result = VirtualProtectFromApp(base_address, length, ULONG(new_protect),
|
||||
&old_protect);
|
||||
#endif
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
if (out_old_access) {
|
||||
*out_old_access = ToXeniaProtectFlags(old_protect);
|
||||
*out_old_access = ToXeniaProtectFlags(DWORD(old_protect));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -148,9 +174,14 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
|
||||
DWORD protect =
|
||||
ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE);
|
||||
auto full_path = "Local" / path;
|
||||
return CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, protect,
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
return CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, protect,
|
||||
static_cast<DWORD>(length >> 32),
|
||||
static_cast<DWORD>(length), full_path.c_str());
|
||||
#else
|
||||
return CreateFileMappingFromApp(INVALID_HANDLE_VALUE, nullptr, ULONG(protect),
|
||||
ULONG64(length), full_path.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void CloseFileMappingHandle(FileMappingHandle handle,
|
||||
@@ -160,6 +191,7 @@ void CloseFileMappingHandle(FileMappingHandle handle,
|
||||
|
||||
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
||||
PageAccess access, size_t file_offset) {
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
DWORD target_address_low = static_cast<DWORD>(file_offset);
|
||||
DWORD target_address_high = static_cast<DWORD>(file_offset >> 32);
|
||||
DWORD file_access = 0;
|
||||
@@ -170,6 +202,9 @@ void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
||||
case PageAccess::kReadWrite:
|
||||
file_access = FILE_MAP_ALL_ACCESS;
|
||||
break;
|
||||
case PageAccess::kExecuteReadOnly:
|
||||
file_access = FILE_MAP_READ | FILE_MAP_EXECUTE;
|
||||
break;
|
||||
case PageAccess::kExecuteReadWrite:
|
||||
file_access = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE;
|
||||
break;
|
||||
@@ -180,6 +215,25 @@ void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
||||
}
|
||||
return MapViewOfFileEx(handle, file_access, target_address_high,
|
||||
target_address_low, length, base_address);
|
||||
#else
|
||||
// VirtualAlloc2FromApp and MapViewOfFile3FromApp were added in 10.0.17134.0.
|
||||
// https://docs.microsoft.com/en-us/uwp/win32-and-com/win32-apis
|
||||
HANDLE process = GetCurrentProcess();
|
||||
void* placeholder = VirtualAlloc2FromApp(
|
||||
process, base_address, length, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||
PAGE_NOACCESS, nullptr, 0);
|
||||
if (!placeholder) {
|
||||
return nullptr;
|
||||
}
|
||||
void* mapping = MapViewOfFile3FromApp(
|
||||
handle, process, placeholder, ULONG64(file_offset), length,
|
||||
MEM_REPLACE_PLACEHOLDER, ULONG(ToWin32ProtectFlags(access)), nullptr, 0);
|
||||
if (!mapping) {
|
||||
VirtualFree(placeholder, length, MEM_RELEASE);
|
||||
return nullptr;
|
||||
}
|
||||
return mapping;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UnmapFileView(FileMappingHandle handle, void* base_address,
|
||||
|
||||
Reference in New Issue
Block a user