memory: Implement enhanced memory management system

Add a flexible memory region management system that provides:
- Memory region type classification (System, Graphics, IO, Binary)
- Memory region permission management (executable, writable)
- Binary base address randomization for ASLR
- Dynamic memory mapping capabilities

Credit: boss.smc@citron-emu.org
Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron 2025-03-27 23:25:24 +10:00
parent 1fd5fefcb1
commit ebfc9d8347
5 changed files with 125 additions and 4 deletions

@ -1 +1 @@
Subproject commit bc3a4d9fd9b46729651a3cec4f5226f6272b8684 Subproject commit 0d5b49b80f17bca25e7f9321ad4e671a56f70887

@ -1 +1 @@
Subproject commit 29b35ea4232688c0f42cdff0c10848290760a417 Subproject commit 89d3a6a5ea35d140fe865ed493c89bde777c6a07

2
externals/vcpkg vendored

@ -1 +1 @@
Subproject commit a7d06b3a72d5ec48353bacb84152bd027ee9999b Subproject commit 64f3d3d6201d9cea01d15ea6e793daf0bbcd47c7

View File

@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-FileCopyrightText: 2018 yuzu Emulator Project // SPDX-FileCopyrightText: 2018 yuzu Emulator Project
// SPDX-FileCopyrightText: 2025 Citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
@ -909,7 +910,9 @@ struct Memory::Impl {
#endif #endif
}; };
Memory::Memory(Core::System& system_) : system{system_} { Memory::Memory(Core::System& system_) : system(system_), impl(std::make_unique<Impl>(system_)), gen(rd()) {
// Initialize the random number distribution
dis = std::uniform_int_distribution<u64>(0, std::numeric_limits<u64>::max());
Reset(); Reset();
} }
@ -1151,4 +1154,48 @@ bool Memory::InvalidateSeparateHeap(void* fault_address) {
#endif #endif
} }
Common::ProcessAddress Memory::GenerateRandomBaseAddress() {
u64 random_bits = dis(gen);
return Common::ProcessAddress((random_bits & ~NRO_BASE_ADDRESS_RANDOMIZATION_MASK) |
(random_bits & NRO_BASE_ADDRESS_RANDOMIZATION_MASK));
}
Memory::MemoryRegion* Memory::FindRegion(Common::ProcessAddress address) {
for (auto& entry : memory_regions) {
if (address >= entry.second.start_address &&
address < entry.second.start_address + entry.second.size) {
return &entry.second;
}
}
return nullptr;
}
void Memory::MapMemoryRegion(Common::ProcessAddress start_address, u64 size, MemoryRegionType type,
bool exec, bool write) {
if (start_address + size > EMULATED_MEMORY_SIZE) {
LOG_ERROR(HW_Memory, "Memory mapping exceeds emulated memory boundaries at address {:016X}",
GetInteger(start_address));
return;
}
// Create the memory region
memory_regions[start_address] = MemoryRegion(start_address, size, type, exec, write);
// Map the region in the page table
Common::MemoryPermission perms{};
if (exec) perms |= Common::MemoryPermission::Execute;
if (write) perms |= Common::MemoryPermission::Write;
perms |= Common::MemoryPermission::Read;
// Using the MapMemoryRegion method defined in the Impl struct
impl->MapMemoryRegion(*impl->current_page_table, start_address, size,
Common::PhysicalAddress(GetInteger(start_address)), perms, false);
}
Common::ProcessAddress Memory::MapBinary(u64 size) {
Common::ProcessAddress base_address = GenerateRandomBaseAddress();
MapMemoryRegion(base_address, size, MemoryRegionType::BinaryMemory, true, true);
return base_address;
}
} // namespace Core::Memory } // namespace Core::Memory

View File

@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-FileCopyrightText: 2025 Citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
@ -9,6 +10,8 @@
#include <span> #include <span>
#include <string> #include <string>
#include <vector> #include <vector>
#include <unordered_map>
#include <random>
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/typed_address.h" #include "common/typed_address.h"
@ -43,6 +46,9 @@ constexpr std::size_t CITRON_PAGEBITS = 12;
constexpr u64 CITRON_PAGESIZE = 1ULL << CITRON_PAGEBITS; constexpr u64 CITRON_PAGESIZE = 1ULL << CITRON_PAGEBITS;
constexpr u64 CITRON_PAGEMASK = CITRON_PAGESIZE - 1; constexpr u64 CITRON_PAGEMASK = CITRON_PAGESIZE - 1;
/// Emulated memory size (4GB)
constexpr u64 EMULATED_MEMORY_SIZE = 4ULL * 1024 * 1024 * 1024;
/// Virtual user-space memory regions /// Virtual user-space memory regions
enum : u64 { enum : u64 {
/// TLS (Thread-Local Storage) related. /// TLS (Thread-Local Storage) related.
@ -50,6 +56,18 @@ enum : u64 {
/// Application stack /// Application stack
DEFAULT_STACK_SIZE = 0x100000, DEFAULT_STACK_SIZE = 0x100000,
/// Mask to randomize bits 37-12 for NRO base address
NRO_BASE_ADDRESS_RANDOMIZATION_MASK = 0xFFFFFFFFFFFFF000,
};
/// Types of memory regions in the system
enum class MemoryRegionType {
SystemMemory,
GraphicsMemory,
IOMemory,
BinaryMemory,
Undefined
}; };
/// Central class that handles all memory operations and state. /// Central class that handles all memory operations and state.
@ -64,6 +82,55 @@ public:
Memory(Memory&&) = default; Memory(Memory&&) = default;
Memory& operator=(Memory&&) = delete; Memory& operator=(Memory&&) = delete;
/**
* Structure representing a memory region with its properties
*/
struct MemoryRegion {
Common::ProcessAddress start_address;
u64 size;
std::unique_ptr<u8[]> data;
bool is_mapped;
MemoryRegionType type;
bool is_executable;
bool is_writable;
// Default constructor needed for STL containers
MemoryRegion() : start_address(0), size(0), data(nullptr), is_mapped(false),
type(MemoryRegionType::Undefined), is_executable(false), is_writable(false) {}
MemoryRegion(Common::ProcessAddress start, u64 sz, MemoryRegionType t, bool exec = false, bool write = false)
: start_address(start), size(sz), data(std::make_unique<u8[]>(sz)), is_mapped(false),
type(t), is_executable(exec), is_writable(write) {}
};
/**
* Maps a memory region with the specified properties
*
* @param start_address The starting address of the region
* @param size The size of the region in bytes
* @param type The type of memory region
* @param exec Whether the region is executable
* @param write Whether the region is writable
*/
void MapMemoryRegion(Common::ProcessAddress start_address, u64 size, MemoryRegionType type,
bool exec = false, bool write = false);
/**
* Maps a binary with a randomized base address
*
* @param size The size of the binary in bytes
* @returns The base address where the binary was mapped
*/
Common::ProcessAddress MapBinary(u64 size);
/**
* Finds a memory region containing the specified address
*
* @param address The address to search for
* @returns Pointer to the memory region if found, nullptr otherwise
*/
MemoryRegion* FindRegion(Common::ProcessAddress address);
/** /**
* Resets the state of the Memory system. * Resets the state of the Memory system.
*/ */
@ -497,6 +564,13 @@ private:
struct Impl; struct Impl;
std::unique_ptr<Impl> impl; std::unique_ptr<Impl> impl;
std::unordered_map<Common::ProcessAddress, MemoryRegion> memory_regions;
std::random_device rd;
std::mt19937 gen;
std::uniform_int_distribution<u64> dis;
Common::ProcessAddress GenerateRandomBaseAddress();
}; };
template <typename T, GuestMemoryFlags FLAGS> template <typename T, GuestMemoryFlags FLAGS>