#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #else #include #include #endif #include #include #include "jit_allocator.hpp" namespace RSP { namespace JIT { #ifdef IOS // iOS/tvOS is 64bit but does not allow an infinite amount of VA space static constexpr bool huge_va = false; #else static constexpr bool huge_va = std::numeric_limits::max() > 0x100000000ull; #endif // On 64-bit systems, we will never allocate more than one block, this is important since we must ensure that // relative jumps are reachable in 32-bits. // We won't actually allocate 1 GB on 64-bit, but just reserve VA space for it, which we have basically an infinite amount of. static constexpr size_t block_size = huge_va ? (1024 * 1024 * 1024) : (2 * 1024 * 1024); Allocator::~Allocator() { #ifdef _WIN32 for (auto &block : blocks) VirtualFree(block.code, 0, MEM_RELEASE); #else for (auto &block : blocks) munmap(block.code, block.size); #endif } static size_t align_page(size_t offset) { #if defined(__APPLE__) && defined(__aarch64__) size_t pagesize = sysconf(_SC_PAGESIZE) - 1; #else size_t pagesize = 4095; #endif return (offset + pagesize) & ~size_t(pagesize); } static bool commit_read_write(void *ptr, size_t size) { #ifdef _WIN32 return VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) == ptr; #else return mprotect(ptr, size, PROT_READ | PROT_WRITE) == 0; #endif } static bool commit_execute(void *ptr, size_t size) { #ifdef _WIN32 DWORD old_protect; return VirtualProtect(ptr, align_page(size), PAGE_EXECUTE, &old_protect) != 0; #else return mprotect(ptr, size, PROT_EXEC) == 0; #endif } bool Allocator::commit_code(void *code, size_t size) { return commit_execute(code, size); } void *Allocator::allocate_code(size_t size) { size = align_page(size); if (blocks.empty()) blocks.push_back(reserve_block(std::max(size, block_size))); auto *block = &blocks.back(); if (!block->code) return nullptr; block->offset = align_page(block->offset); if (block->offset + size > block->size) block = nullptr; if (!block) { if (huge_va) abort(); blocks.push_back(reserve_block(std::max(size, block_size))); block = &blocks.back(); } if (!block || !block->code) return nullptr; void *ret = block->code + block->offset; block->offset += size; if (!commit_read_write(ret, size)) return nullptr; return ret; } Allocator::Block Allocator::reserve_block(size_t size) { Block block; #ifdef _WIN32 block.code = static_cast(VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE)); block.size = size; return block; #else block.code = static_cast(mmap(nullptr, size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); block.size = size; return block; #endif } } }