mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
fix(libc): reserve the guest arena at its fixed range around what Windows already placed in it
The arena is always [0x200000000, 0x7200000000). Every free, allocation-granularity-aligned piece of the range is reserved as a placeholder; what lies between the pieces (thread stacks and other reservations Windows made before libc starts, unaligned slivers) and the system-reserved range are holes, used ranges a release never frees. A failed query or reservation of the range throws. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
bc51b337ad
commit
17eefbbbd9
1 file changed
+27
-17
@@ -8,6 +8,8 @@
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@@ -16,12 +18,10 @@
|
||||
namespace GuestArena {
|
||||
namespace {
|
||||
|
||||
constexpr std::uintptr_t PreferredBase = 0x0000000200000000ull;
|
||||
constexpr std::uintptr_t ArenaStart = 0x0000000200000000ull;
|
||||
constexpr std::uintptr_t SystemReservedStart = 0x00000007FFFFC000ull;
|
||||
constexpr std::uintptr_t SystemReservedEnd = 0x0000001000000000ull;
|
||||
constexpr std::size_t MaximumSize = 0x0000007000000000ull;
|
||||
constexpr std::size_t MinimumSize = 0x0000001000000000ull;
|
||||
constexpr std::uintptr_t MapAreaEnd = 0x000000FC00000000ull;
|
||||
constexpr std::size_t ArenaSize = 0x0000007000000000ull;
|
||||
|
||||
std::uintptr_t alignUp(std::uintptr_t value, std::size_t alignment) {
|
||||
return (value + alignment - 1) & ~(static_cast<std::uintptr_t>(alignment) - 1);
|
||||
@@ -83,30 +83,40 @@ public:
|
||||
if (rangeStart < start) _used.emplace(rangeStart, start);
|
||||
if (rangeEnd > end) _used.emplace(end, rangeEnd);
|
||||
}
|
||||
for (const auto& [holeStart, holeEnd] : _holes) {
|
||||
if (holeStart < end && start < holeEnd) _used.emplace(holeStart, holeEnd);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Arena() {
|
||||
#ifdef _WIN32
|
||||
// Windows places other reservations randomly, so take the lowest base and largest size that fit.
|
||||
for (std::uintptr_t base = PreferredBase; base + MinimumSize <= MapAreaEnd; base += MinimumSize) {
|
||||
for (std::size_t size = MaximumSize; size >= MinimumSize; size /= 2) {
|
||||
if (base + size > MapAreaEnd) continue;
|
||||
_writeWatched = std::getenv("APS5_NO_WRITE_WATCH") == nullptr;
|
||||
void* reserved = WindowsMappings::Get().Reserve(reinterpret_cast<void*>(base), size);
|
||||
if (!reserved) continue;
|
||||
_base = reinterpret_cast<std::uintptr_t>(reserved);
|
||||
_end = _base + size;
|
||||
if (_base < SystemReservedEnd && SystemReservedStart < _end) _used.emplace(std::max(_base, SystemReservedStart), std::min(_end, SystemReservedEnd));
|
||||
return;
|
||||
}
|
||||
_writeWatched = std::getenv("APS5_NO_WRITE_WATCH") == nullptr;
|
||||
SYSTEM_INFO system{};
|
||||
GetSystemInfo(&system);
|
||||
const std::uintptr_t granularity = system.dwAllocationGranularity;
|
||||
const std::uintptr_t end = ArenaStart + ArenaSize;
|
||||
for (std::uintptr_t cursor = ArenaStart; cursor < end;) {
|
||||
MEMORY_BASIC_INFORMATION info{};
|
||||
if (VirtualQuery(reinterpret_cast<const void*>(cursor), &info, sizeof(info)) == 0) throw std::system_error(static_cast<int>(GetLastError()), std::system_category(), "query the guest arena range");
|
||||
const auto regionEnd = std::min(end, reinterpret_cast<std::uintptr_t>(info.BaseAddress) + info.RegionSize);
|
||||
const auto first = info.State == MEM_FREE ? std::min(regionEnd, alignUp(cursor, granularity)) : regionEnd;
|
||||
const auto last = std::max(first, regionEnd & ~(granularity - 1));
|
||||
if (first < last && WindowsMappings::Get().Reserve(reinterpret_cast<void*>(first), last - first) == nullptr) throw std::system_error(static_cast<int>(GetLastError()), std::system_category(), "reserve the guest arena range");
|
||||
if (cursor < first) _holes.emplace_back(cursor, first);
|
||||
if (last < regionEnd) _holes.emplace_back(last, regionEnd);
|
||||
cursor = regionEnd;
|
||||
}
|
||||
std::fprintf(stderr, "[memory] guest arena unavailable below 0x%llx\n", static_cast<unsigned long long>(MapAreaEnd));
|
||||
_holes.emplace_back(SystemReservedStart, SystemReservedEnd);
|
||||
for (const auto& [holeStart, holeEnd] : _holes) _used.emplace(holeStart, holeEnd);
|
||||
_base = ArenaStart;
|
||||
_end = end;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::mutex _lock;
|
||||
std::map<std::uintptr_t, std::uintptr_t> _used;
|
||||
std::vector<std::pair<std::uintptr_t, std::uintptr_t>> _holes;
|
||||
std::uintptr_t _base = 0;
|
||||
std::uintptr_t _end = 0;
|
||||
bool _writeWatched = false;
|
||||
|
||||
Reference in new issue
Block a user