mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-04-06 06:55:09 +00:00
rx/mem: cross platform implementation
Some checks are pending
Formatting check / formatting-check (push) Waiting to run
Build RPCSX / build-linux (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.1-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.2-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.4-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.5-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv9-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv9.1-a) (push) Waiting to run
Build RPCSX / build-android (x86_64, x86-64) (push) Waiting to run
Some checks are pending
Formatting check / formatting-check (push) Waiting to run
Build RPCSX / build-linux (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.1-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.2-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.4-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.5-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv9-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv9.1-a) (push) Waiting to run
Build RPCSX / build-android (x86_64, x86-64) (push) Waiting to run
fixed compilation errors avoid memfd_create usage on android
This commit is contained in:
parent
0bc167ea87
commit
3ffece2d77
10 changed files with 293 additions and 163 deletions
|
|
@ -2,21 +2,11 @@
|
|||
|
||||
#include "AddressRange.hpp"
|
||||
#include "EnumBitSet.hpp"
|
||||
#include "mem.hpp"
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
namespace rx {
|
||||
enum class Protection {
|
||||
R,
|
||||
W,
|
||||
X,
|
||||
|
||||
bitset_last = X
|
||||
};
|
||||
|
||||
std::errc reserveVirtualSpace(rx::AddressRange range);
|
||||
std::errc releaseVirtualSpace(rx::AddressRange range, std::size_t alignment);
|
||||
|
||||
class Mappable {
|
||||
#ifdef _WIN32
|
||||
using NativeHandle = void *;
|
||||
|
|
@ -46,7 +36,8 @@ public:
|
|||
static std::pair<Mappable, std::errc> CreateMemory(std::size_t size);
|
||||
static std::pair<Mappable, std::errc> CreateSwap(std::size_t size);
|
||||
std::errc map(rx::AddressRange virtualRange, std::size_t offset,
|
||||
rx::EnumBitSet<Protection> protection, std::size_t alignment);
|
||||
rx::EnumBitSet<mem::Protection> protection,
|
||||
std::size_t alignment);
|
||||
|
||||
[[nodiscard]] NativeHandle release() {
|
||||
return std::exchange(m_handle, kInvalidHandle);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "AddressRange.hpp"
|
||||
#include "EnumBitSet.hpp"
|
||||
#include <cstddef>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
namespace rx::mem {
|
||||
enum class Protection {
|
||||
R,
|
||||
W,
|
||||
X,
|
||||
|
||||
bitset_last = X
|
||||
};
|
||||
|
||||
struct VirtualQueryEntry : rx::AddressRange {
|
||||
rx::EnumBitSet<Protection> flags{};
|
||||
|
||||
VirtualQueryEntry() = default;
|
||||
VirtualQueryEntry(rx::AddressRange range, rx::EnumBitSet<Protection> prot)
|
||||
: AddressRange(range), flags(prot) {}
|
||||
};
|
||||
|
||||
extern const std::size_t pageSize;
|
||||
void *map(void *address, std::size_t size, int prot, int flags, int fd = -1,
|
||||
std::ptrdiff_t offset = 0);
|
||||
void *reserve(std::size_t size);
|
||||
bool reserve(void *address, std::size_t size);
|
||||
bool protect(void *address, std::size_t size, int prot);
|
||||
bool unmap(void *address, std::size_t size);
|
||||
void printStats();
|
||||
std::errc reserve(rx::AddressRange range);
|
||||
std::errc release(rx::AddressRange range, std::size_t alignment);
|
||||
std::errc protect(rx::AddressRange range, rx::EnumBitSet<Protection> prot);
|
||||
std::vector<VirtualQueryEntry> query(rx::AddressRange range);
|
||||
} // namespace rx::mem
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "Mappable.hpp"
|
||||
#include "mem.hpp"
|
||||
#include <system_error>
|
||||
|
||||
#ifndef _WIN32
|
||||
|
|
@ -13,59 +14,10 @@
|
|||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
std::errc rx::reserveVirtualSpace(rx::AddressRange range) {
|
||||
auto pointer = std::bit_cast<void *>(range.beginAddress());
|
||||
|
||||
#ifdef _WIN32
|
||||
auto reservation = VirtualAlloc2(nullptr, pointer, range.size(),
|
||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||
PAGE_NOACCESS, nullptr, 0);
|
||||
|
||||
if (reservation == nullptr) {
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
#else
|
||||
#ifdef MAP_FIXED_NOREPLACE
|
||||
static constexpr auto kMapFixedNoReplace = MAP_FIXED_NOREPLACE;
|
||||
#else
|
||||
static constexpr auto kMapFixedNoReplace = MAP_FIXED;
|
||||
#ifdef ANDROID
|
||||
#include "format-base.hpp"
|
||||
#endif
|
||||
|
||||
auto reservation = ::mmap(pointer, range.size(), PROT_NONE,
|
||||
MAP_ANON | kMapFixedNoReplace | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (reservation == MAP_FAILED) {
|
||||
return std::errc{errno};
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
std::errc rx::releaseVirtualSpace(rx::AddressRange range,
|
||||
[[maybe_unused]] std::size_t alignment) {
|
||||
#ifdef _WIN32
|
||||
// simple and stupid implementation
|
||||
for (std::uintptr_t address = range.beginAddress();
|
||||
address < range.endAddress(); address += alignment) {
|
||||
auto pointer = std::bit_cast<void *>(address);
|
||||
if (!UnmapViewOfFileEx(pointer, MEM_PRESERVE_PLACEHOLDER)) {
|
||||
VirtualFree(pointer, alignment, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER);
|
||||
}
|
||||
}
|
||||
#else
|
||||
auto pointer = std::bit_cast<void *>(range.beginAddress());
|
||||
|
||||
auto reservation = ::mmap(pointer, range.size(), PROT_NONE,
|
||||
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (!reservation || reservation != pointer) {
|
||||
return std::errc{errno};
|
||||
}
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<rx::Mappable, std::errc>
|
||||
rx::Mappable::CreateMemory(std::size_t size) {
|
||||
rx::Mappable result;
|
||||
|
|
@ -80,12 +32,22 @@ rx::Mappable::CreateMemory(std::size_t size) {
|
|||
}
|
||||
|
||||
result.m_handle = handle;
|
||||
#else
|
||||
#ifdef ANDROID
|
||||
auto name = rx::format("/{}-{:x}", (void *)&result, size);
|
||||
auto fd = ::shm_open(name.c_str(), O_CREAT | O_TRUNC, 0666);
|
||||
#else
|
||||
auto fd = ::memfd_create("", 0);
|
||||
#endif
|
||||
|
||||
if (fd < 0) {
|
||||
return {{}, std::errc{errno}};
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
::shm_unlink(name.c_str());
|
||||
#endif
|
||||
|
||||
result.m_handle = fd;
|
||||
|
||||
if (::ftruncate(fd, size) < 0) {
|
||||
|
|
@ -119,7 +81,7 @@ std::pair<rx::Mappable, std::errc> rx::Mappable::CreateSwap(std::size_t size) {
|
|||
}
|
||||
|
||||
std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset,
|
||||
rx::EnumBitSet<Protection> protection,
|
||||
rx::EnumBitSet<mem::Protection> protection,
|
||||
[[maybe_unused]] std::size_t alignment) {
|
||||
#ifdef _WIN32
|
||||
static const DWORD protTable[] = {
|
||||
|
|
@ -133,11 +95,11 @@ std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset,
|
|||
PAGE_EXECUTE_READWRITE, // XRW
|
||||
};
|
||||
|
||||
auto prot =
|
||||
protTable[(protection & (Protection::R | Protection::W | Protection::X))
|
||||
.toUnderlying()];
|
||||
auto prot = protTable[(protection & (mem::Protection::R | mem::Protection::W |
|
||||
mem::Protection::X))
|
||||
.toUnderlying()];
|
||||
|
||||
releaseVirtualSpace(virtualRange, alignment);
|
||||
mem::release(virtualRange, alignment);
|
||||
|
||||
for (std::uintptr_t address = virtualRange.beginAddress();
|
||||
address < virtualRange.endAddress();
|
||||
|
|
@ -152,18 +114,16 @@ std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset,
|
|||
return std::errc::invalid_argument;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
#else
|
||||
int prot = 0;
|
||||
|
||||
if (protection & Protection::R) {
|
||||
if (protection & mem::Protection::R) {
|
||||
prot |= PROT_READ;
|
||||
}
|
||||
if (protection & Protection::W) {
|
||||
if (protection & mem::Protection::W) {
|
||||
prot |= PROT_READ | PROT_WRITE;
|
||||
}
|
||||
if (protection & Protection::X) {
|
||||
if (protection & mem::Protection::X) {
|
||||
prot |= PROT_EXEC;
|
||||
}
|
||||
|
||||
|
|
@ -175,9 +135,9 @@ std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset,
|
|||
if (result == MAP_FAILED) {
|
||||
return std::errc{errno};
|
||||
}
|
||||
#endif
|
||||
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
void rx::Mappable::destroy() {
|
||||
|
|
|
|||
243
rx/src/mem.cpp
243
rx/src/mem.cpp
|
|
@ -1,50 +1,205 @@
|
|||
#include "mem.hpp"
|
||||
#include "print.hpp"
|
||||
|
||||
#ifdef __linux__
|
||||
#include "die.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NTDDI_VERSION NTDDI_WIN10_NI
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <cstdio>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern const std::size_t rx::mem::pageSize = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
void *rx::mem::map(void *address, std::size_t size, int prot, int flags, int fd,
|
||||
std::ptrdiff_t offset) {
|
||||
return ::mmap(address, size, prot, flags, fd, offset);
|
||||
}
|
||||
|
||||
void *rx::mem::reserve(std::size_t size) {
|
||||
return map(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS);
|
||||
}
|
||||
|
||||
bool rx::mem::reserve(void *address, std::size_t size) {
|
||||
return map(address, size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) != MAP_FAILED;
|
||||
}
|
||||
|
||||
bool rx::mem::protect(void *address, std::size_t size, int prot) {
|
||||
return ::mprotect(address, size, prot) == 0;
|
||||
}
|
||||
|
||||
bool rx::mem::unmap(void *address, std::size_t size) {
|
||||
return ::munmap(address, size) == 0;
|
||||
}
|
||||
|
||||
void rx::mem::printStats() {
|
||||
FILE *maps = fopen("/proc/self/maps", "r");
|
||||
|
||||
if (!maps) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *line = nullptr;
|
||||
std::size_t size = 0;
|
||||
while (getline(&line, &size, maps) > 0) {
|
||||
rx::print("{}", line);
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(maps);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
const std::size_t rx::mem::pageSize = [] {
|
||||
SYSTEM_INFO info;
|
||||
::GetSystemInfo(&info);
|
||||
return info.dwPageSize;
|
||||
}();
|
||||
#else
|
||||
const std::size_t rx::mem::pageSize = sysconf(_SC_PAGE_SIZE);
|
||||
#endif
|
||||
|
||||
std::errc rx::mem::reserve(rx::AddressRange range) {
|
||||
auto pointer = std::bit_cast<void *>(range.beginAddress());
|
||||
|
||||
#ifdef _WIN32
|
||||
auto reservation = VirtualAlloc2(nullptr, pointer, range.size(),
|
||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||
PAGE_NOACCESS, nullptr, 0);
|
||||
|
||||
if (reservation == nullptr) {
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
#else
|
||||
#ifdef MAP_FIXED_NOREPLACE
|
||||
static constexpr auto kMapFixedNoReplace = MAP_FIXED_NOREPLACE;
|
||||
#else
|
||||
static constexpr auto kMapFixedNoReplace = MAP_FIXED;
|
||||
#endif
|
||||
|
||||
auto reservation = ::mmap(pointer, range.size(), PROT_NONE,
|
||||
MAP_ANON | kMapFixedNoReplace | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (reservation == MAP_FAILED) {
|
||||
return std::errc{errno};
|
||||
}
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
std::errc rx::mem::release(rx::AddressRange range,
|
||||
[[maybe_unused]] std::size_t alignment) {
|
||||
#ifdef _WIN32
|
||||
// simple and stupid implementation
|
||||
for (std::uintptr_t address = range.beginAddress();
|
||||
address < range.endAddress(); address += alignment) {
|
||||
auto pointer = std::bit_cast<void *>(address);
|
||||
if (!UnmapViewOfFileEx(pointer, MEM_PRESERVE_PLACEHOLDER)) {
|
||||
VirtualFree(pointer, alignment, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER);
|
||||
}
|
||||
}
|
||||
#else
|
||||
auto pointer = std::bit_cast<void *>(range.beginAddress());
|
||||
|
||||
auto reservation = ::mmap(pointer, range.size(), PROT_NONE,
|
||||
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (!reservation || reservation != pointer) {
|
||||
return std::errc{errno};
|
||||
}
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::errc rx::mem::protect(rx::AddressRange range,
|
||||
rx::EnumBitSet<Protection> prot) {
|
||||
auto pointer = std::bit_cast<void *>(range.beginAddress());
|
||||
#ifdef _WIN32
|
||||
static const DWORD protTable[] = {
|
||||
PAGE_NOACCESS, // 0
|
||||
PAGE_READONLY, // R
|
||||
PAGE_EXECUTE_READWRITE, // W
|
||||
PAGE_EXECUTE_READWRITE, // RW
|
||||
PAGE_EXECUTE, // X
|
||||
PAGE_EXECUTE_READWRITE, // XR
|
||||
PAGE_EXECUTE_READWRITE, // XW
|
||||
PAGE_EXECUTE_READWRITE, // XRW
|
||||
};
|
||||
|
||||
auto rawProt =
|
||||
(prot & (mem::Protection::R | mem::Protection::W | mem::Protection::X))
|
||||
.toUnderlying();
|
||||
auto wProt = protTable[rawProt];
|
||||
|
||||
if (!VirtualProtect(pointer, range.size(), wProt, nullptr)) {
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
#else
|
||||
if (::mprotect(pointer, range.size(), prot.toUnderlying())) {
|
||||
return std::errc{errno};
|
||||
}
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<rx::mem::VirtualQueryEntry> rx::mem::query(rx::AddressRange range) {
|
||||
std::vector<VirtualQueryEntry> result;
|
||||
|
||||
#ifdef _WIN32
|
||||
std::uintptr_t address = range.beginAddress();
|
||||
while (address < range.endAddress()) {
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
if (!VirtualQuery((void *)address, &info, sizeof(info))) {
|
||||
rx::die("VirtualQuery: failed, address = {:x}", address);
|
||||
}
|
||||
|
||||
auto region = rx::AddressRange::fromBeginSize(
|
||||
(std::uintptr_t)info.BaseAddress, info.RegionSize)
|
||||
.intersection(range);
|
||||
|
||||
address = region.endAddress();
|
||||
|
||||
if (info.State == MEM_FREE || !region.isValid() ||
|
||||
!range.contains(region)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rx::EnumBitSet<Protection> flags = {};
|
||||
|
||||
switch (info.AllocationProtect & 0xff) {
|
||||
case PAGE_NOACCESS:
|
||||
break;
|
||||
case PAGE_READONLY:
|
||||
flags = Protection::R;
|
||||
break;
|
||||
case PAGE_READWRITE:
|
||||
case PAGE_WRITECOPY:
|
||||
flags = Protection::R | Protection::W;
|
||||
break;
|
||||
case PAGE_EXECUTE:
|
||||
flags = Protection::X;
|
||||
break;
|
||||
case PAGE_EXECUTE_READ:
|
||||
flags = Protection::X | Protection::R;
|
||||
break;
|
||||
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
flags = Protection::X | Protection::W | Protection::R;
|
||||
break;
|
||||
}
|
||||
|
||||
result.emplace_back(region, flags);
|
||||
}
|
||||
#elif defined(__linux)
|
||||
char buf[1024];
|
||||
auto maps = std::fopen("/proc/self/maps", "r");
|
||||
|
||||
while (std::fgets(buf, sizeof(buf), maps)) {
|
||||
std::uint64_t beginAddress;
|
||||
std::uint64_t endAddress;
|
||||
char flagChars[5];
|
||||
|
||||
std::sscanf(buf, "%lx-%lx %4s", &beginAddress, &endAddress, flagChars);
|
||||
|
||||
auto region = rx::AddressRange::fromBeginEnd(beginAddress, endAddress)
|
||||
.intersection(range);
|
||||
|
||||
if (!region.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (region.beginAddress() >= range.beginAddress()) {
|
||||
break;
|
||||
}
|
||||
|
||||
rx::EnumBitSet<Protection> flags = {};
|
||||
|
||||
if (flagChars[0] == 'r') {
|
||||
flags |= Protection::R;
|
||||
}
|
||||
|
||||
if (flagChars[1] == 'w') {
|
||||
flags |= Protection::W;
|
||||
}
|
||||
|
||||
if (flagChars[2] == 'x') {
|
||||
flags |= Protection::X;
|
||||
}
|
||||
|
||||
result.emplace_back(region, flags);
|
||||
}
|
||||
|
||||
std::fclose(maps);
|
||||
#elif defined(__APPLE__)
|
||||
// FIXME: use mach_vm_region_info?
|
||||
// workaround: assume all pages are not used
|
||||
#else
|
||||
#error "Not implemented"
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue