mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-01 12:45:56 +00:00
106 lines
3.9 KiB
C++
106 lines
3.9 KiB
C++
#include "prx/libc/include/GuestArena.hpp"
|
|
#include <crt_externs.h>
|
|
#include <mach/mach.h>
|
|
#include <mach/mach_vm.h>
|
|
#include <pthread.h>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
extern "C" [[noreturn]] void Aps5StartGuest_nid_no_patch(void (*entry)(void*, void*), void* block);
|
|
extern "C" void Aps5RunOnMainThread_nid_no_patch(void (*function)(void*), void* context);
|
|
|
|
namespace {
|
|
|
|
constexpr std::uintptr_t MappingAlignment = 0x10000;
|
|
constexpr std::size_t MappingBytes = 0x10000;
|
|
constexpr std::size_t NeighbourBytes = std::size_t {1} << 20;
|
|
|
|
std::uintptr_t originalStack = 0;
|
|
std::string originalArgument;
|
|
bool originalInArena = false;
|
|
mach_vm_address_t neighbour = 0;
|
|
|
|
bool RegionOf(mach_vm_address_t address, mach_vm_address_t& begin, mach_vm_address_t& end, std::uint32_t& tag) {
|
|
mach_vm_address_t base = address;
|
|
mach_vm_size_t size = 0;
|
|
natural_t depth = 0;
|
|
vm_region_submap_info_data_64_t info {};
|
|
mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
|
|
if (mach_vm_region_recurse(mach_task_self(), &base, &size, &depth, reinterpret_cast<vm_region_recurse_info_t>(&info), &count) != KERN_SUCCESS || base > address) return false;
|
|
begin = base;
|
|
end = base + size;
|
|
tag = info.user_tag;
|
|
return true;
|
|
}
|
|
|
|
mach_vm_address_t MapStackNeighbour(std::uintptr_t stack) {
|
|
mach_vm_address_t low = 0, end = 0;
|
|
std::uint32_t tag = 0;
|
|
if (!RegionOf(stack, low, end, tag) || tag != VM_MEMORY_STACK) return 0;
|
|
for (;;) {
|
|
mach_vm_address_t begin = 0, previousEnd = 0;
|
|
if (!RegionOf(low - 1, begin, previousEnd, tag) || tag != VM_MEMORY_STACK || previousEnd != low) break;
|
|
low = begin;
|
|
}
|
|
mach_vm_address_t at = low - NeighbourBytes;
|
|
if (mach_vm_deallocate(mach_task_self(), at, NeighbourBytes) != KERN_SUCCESS) return 0;
|
|
if (mach_vm_allocate(mach_task_self(), &at, NeighbourBytes, VM_MAKE_TAG(VM_MEMORY_STACK)) != KERN_SUCCESS) return 0;
|
|
return at;
|
|
}
|
|
|
|
void Require(bool value, const char* what) {
|
|
if (value) return;
|
|
std::fprintf(stderr, "check failed: %s\n", what);
|
|
std::exit(1);
|
|
}
|
|
|
|
bool InArena(std::uintptr_t address) {
|
|
std::uintptr_t base = 0;
|
|
std::size_t bytes = 0;
|
|
GuestArena::GuestArenaRange_nid_postfix(&base, &bytes);
|
|
return address >= base && address < base + bytes;
|
|
}
|
|
|
|
void CheckMainThread(void*) {
|
|
int local = 0;
|
|
Require(pthread_main_np() != 0, "the check runs on the main thread");
|
|
const auto address = reinterpret_cast<std::uintptr_t>(&local);
|
|
if (originalInArena) {
|
|
Require(!InArena(address), "the main thread stack is outside the guest arena");
|
|
Require(address != originalStack, "the main thread stack moved");
|
|
const auto old = reinterpret_cast<const void*>(originalStack & ~(MappingAlignment - 1));
|
|
try {
|
|
GuestArena::GuestArenaMarkUsed_nid_postfix(old, MappingBytes);
|
|
} catch (const std::runtime_error&) {
|
|
Require(false, "a fixed guest mapping fits where the main thread stack was");
|
|
}
|
|
GuestArena::GuestArenaRelease_nid_postfix(old, MappingBytes);
|
|
}
|
|
if (neighbour != 0) {
|
|
mach_vm_address_t begin = 0, end = 0;
|
|
std::uint32_t tag = 0;
|
|
Require(RegionOf(neighbour, begin, end, tag) && tag == VM_MEMORY_STACK, "a stack region next to the main stack survived the move");
|
|
}
|
|
Require(originalArgument == (*_NSGetArgv())[0], "argv survived the move");
|
|
Require(std::getenv("PATH") != nullptr, "the environment survived the move");
|
|
}
|
|
|
|
void Guest(void*, void*) {
|
|
Aps5RunOnMainThread_nid_no_patch(CheckMainThread, nullptr);
|
|
std::exit(0);
|
|
}
|
|
|
|
}
|
|
|
|
int main(int, char** argv) {
|
|
int local = 0;
|
|
originalStack = reinterpret_cast<std::uintptr_t>(&local);
|
|
originalInArena = InArena(originalStack);
|
|
originalArgument = argv[0];
|
|
if (originalInArena) neighbour = MapStackNeighbour(originalStack);
|
|
Aps5StartGuest_nid_no_patch(Guest, nullptr);
|
|
}
|