mirror of https://git.citron-emu.org/citron/emu
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:
parent
1fd5fefcb1
commit
ebfc9d8347
|
@ -1 +1 @@
|
||||||
Subproject commit bc3a4d9fd9b46729651a3cec4f5226f6272b8684
|
Subproject commit 0d5b49b80f17bca25e7f9321ad4e671a56f70887
|
|
@ -1 +1 @@
|
||||||
Subproject commit 29b35ea4232688c0f42cdff0c10848290760a417
|
Subproject commit 89d3a6a5ea35d140fe865ed493c89bde777c6a07
|
|
@ -1 +1 @@
|
||||||
Subproject commit a7d06b3a72d5ec48353bacb84152bd027ee9999b
|
Subproject commit 64f3d3d6201d9cea01d15ea6e793daf0bbcd47c7
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue