diff --git a/Source/Tests/FEXLoader.cpp b/Source/Tests/FEXLoader.cpp index 4d56abcbf..d372f424d 100644 --- a/Source/Tests/FEXLoader.cpp +++ b/Source/Tests/FEXLoader.cpp @@ -8,6 +8,7 @@ $end_info$ #include "Common/ArgumentLoader.h" #include "Common/RootFSSetup.h" #include "ELFCodeLoader2.h" +#include "Tests/LinuxSyscalls/LinuxAllocator.h" #include "Tests/LinuxSyscalls/Syscalls.h" #include "Tests/LinuxSyscalls/x32/Syscalls.h" #include "Tests/LinuxSyscalls/x64/Syscalls.h" @@ -477,7 +478,7 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Config::Set(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program).string()); FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0"); - std::unique_ptr Allocator; + std::unique_ptr Allocator; FEXCore::Allocator::PtrCache *Base48Bit{}; if (Loader.Is64BitMode()) { @@ -504,7 +505,12 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Allocator::SetupHooks(); } - Allocator = FEX::HLE::x32::CreateAllocator(Use32BitAllocator); + if (Use32BitAllocator) { + Allocator = FEX::HLE::Create32BitAllocator(); + } + else { + Allocator = FEX::HLE::CreatePassthroughAllocator(); + } if (!Loader.MapMemory([&Allocator](void *addr, size_t length, int prot, int flags, int fd, off_t offset) { return Allocator->mmap(addr, length, prot, flags, fd, offset); diff --git a/Source/Tests/LinuxSyscalls/CMakeLists.txt b/Source/Tests/LinuxSyscalls/CMakeLists.txt index 5f617921d..e4228c300 100644 --- a/Source/Tests/LinuxSyscalls/CMakeLists.txt +++ b/Source/Tests/LinuxSyscalls/CMakeLists.txt @@ -1,8 +1,9 @@ add_compile_options(-fno-operator-names) add_library(LinuxEmulation STATIC - FileManagement.cpp EmulatedFiles/EmulatedFiles.cpp + FileManagement.cpp + LinuxAllocator.cpp SignalDelegator.cpp Syscalls.cpp x32/Syscalls.cpp diff --git a/Source/Tests/LinuxSyscalls/LinuxAllocator.cpp b/Source/Tests/LinuxSyscalls/LinuxAllocator.cpp new file mode 100644 index 000000000..65d5c1afb --- /dev/null +++ b/Source/Tests/LinuxSyscalls/LinuxAllocator.cpp @@ -0,0 +1,592 @@ +#include "Common/MathUtils.h" +#include "Tests/LinuxSyscalls/LinuxAllocator.h" +#include "Tests/LinuxSyscalls/Syscalls.h" + +#include +#include +#include +#include +#include +#include + +#ifndef MREMAP_DONTUNMAP +#define MREMAP_DONTUNMAP 4 +#endif + +namespace FEX::HLE { +class MemAllocator32Bit final : public FEX::HLE::MemAllocator { +private: + static constexpr uint64_t PAGE_SHIFT = 12; + static constexpr uint64_t PAGE_SIZE = 1 << PAGE_SHIFT; + static constexpr uint64_t PAGE_MASK = (1 << PAGE_SHIFT) - 1; + static constexpr uint64_t BASE_KEY = 16; + const uint64_t TOP_KEY = 0xFFFF'F000ULL >> PAGE_SHIFT; + const uint64_t TOP_KEY32BIT = 0x1F'F000ULL >> PAGE_SHIFT; + +public: + MemAllocator32Bit() { + // First 16 pages are taken by the Linux kernel + for (size_t i = 0; i < 16; ++i) { + MappedPages.set(i); + } + // Take the top page as well + MappedPages.set(TOP_KEY); + if (SearchDown) { + LastScanLocation = TOP_KEY; + LastKeyLocation = TOP_KEY; + LastKeyLocation32Bit = TOP_KEY32BIT; + FindPageRangePtr = &MemAllocator32Bit::FindPageRange_TopDown; + } + else { + LastScanLocation = BASE_KEY; + LastKeyLocation = BASE_KEY; + FindPageRangePtr = &MemAllocator32Bit::FindPageRange; + } + } + void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override; + int munmap(void *addr, size_t length) override; + void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override; + uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override; + uint64_t shmdt(const void* shmaddr) override; + static constexpr bool SearchDown = true; + + // PageAddr is a page already shifted to page index + // PagesLength is the number of pages + void SetUsedPages(uint64_t PageAddr, size_t PagesLength) { + // Set the range as mapped + for (size_t i = 0; i < PagesLength; ++i) { + MappedPages.set(PageAddr + i); + } + } + + // PageAddr is a page already shifted to page index + // PagesLength is the number of pages + void SetFreePages(uint64_t PageAddr, size_t PagesLength) { + // Set the range as unused + for (size_t i = 0; i < PagesLength; ++i) { + MappedPages.reset(PageAddr + i); + } + } + +private: + // Set that contains 4k mapped pages + // This is the full 32bit memory range + std::bitset<0x10'0000> MappedPages; + std::map PageToShm{}; + uint64_t LastScanLocation{}; + uint64_t LastKeyLocation{}; + uint64_t LastKeyLocation32Bit{}; + std::mutex AllocMutex{}; + uint64_t FindPageRange(uint64_t Start, size_t Pages) const; + uint64_t FindPageRange_TopDown(uint64_t Start, size_t Pages) const; + using FindHandler = uint64_t(MemAllocator32Bit::*)(uint64_t Start, size_t Pages) const; + FindHandler FindPageRangePtr{}; +}; + +uint64_t MemAllocator32Bit::FindPageRange(uint64_t Start, size_t Pages) const { + // Linear range scan + while (Start != TOP_KEY) { + bool Free = true; + if ((Start + Pages) > TOP_KEY) { + return 0; + } + uint64_t Offset = 0; + for (; Offset < Pages; ++Offset) { + if (MappedPages.test(Start + Offset)) { + Free = false; + break; + } + } + + if (Free) { + return Start; + } + Start += Offset + 1; + } + + return 0; +} + +uint64_t MemAllocator32Bit::FindPageRange_TopDown(uint64_t Start, size_t Pages) const { + // Linear range scan + while (Start >= BASE_KEY && + Start <= TOP_KEY) { + bool Free = true; + + uint64_t Offset = 0; + for (; Offset < Pages; ++Offset) { + if (MappedPages.test(Start - Offset)) { + Free = false; + break; + } + } + + if (Free) { + return Start - Offset; + } + Start -= Offset + 1; + } + + return 0; +} + +void *MemAllocator32Bit::mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { + std::scoped_lock lk{AllocMutex}; + size_t PagesLength = AlignUp(length, PAGE_SIZE) >> PAGE_SHIFT; + + uintptr_t Addr = reinterpret_cast(addr); + uintptr_t PageAddr = Addr >> PAGE_SHIFT; + + bool Fixed = ((flags & MAP_FIXED) || + (flags & MAP_FIXED_NOREPLACE)); + + // Both Addr and length must be page aligned + if (Addr & PAGE_MASK) { + return reinterpret_cast(-EINVAL); + } + + // If we do have an fd then offset must be page aligned + if (fd != -1 && + offset & PAGE_MASK) { + return reinterpret_cast(-EINVAL); + } + + if (Addr + length > std::numeric_limits::max()) { + return reinterpret_cast(-EOVERFLOW); + } + + // Check reserved range + if (Fixed && PageAddr < 16) { + return reinterpret_cast(-EINVAL); + } + + if (!Fixed) { + // If we aren't mapping fixed the ignore the address input + Addr = 0; + PageAddr = 0; + } + + bool Map32Bit = flags & FEX::HLE::X86_64_MAP_32BIT; + + // Find a region that fits our address + if (Addr == 0) { + bool Wrapped = false; + uint64_t BottomPage = Map32Bit && (LastScanLocation >= LastKeyLocation32Bit) ? LastKeyLocation32Bit : LastScanLocation; +restart: + { + // Linear range scan + uint64_t LowerPage = (this->*FindPageRangePtr)(BottomPage, PagesLength); + if (LowerPage == 0) { + // Try again but this time from the start + BottomPage = Map32Bit ? LastKeyLocation32Bit : LastKeyLocation; + LowerPage = (this->*FindPageRangePtr)(BottomPage, PagesLength); + } + + uint64_t UpperPage = LowerPage + PagesLength; + if (LowerPage == 0) { + return reinterpret_cast(-ENOMEM); + } + { + // Try and map the range + void *MappedPtr = ::mmap( + reinterpret_cast(LowerPage<< PAGE_SHIFT), + length, + prot, + flags | MAP_FIXED_NOREPLACE, + fd, + offset); + + if (MappedPtr == MAP_FAILED && + errno != EEXIST) { + return reinterpret_cast(-errno); + } + else if (MappedPtr == MAP_FAILED || + MappedPtr >= reinterpret_cast(TOP_KEY << PAGE_SHIFT)) { + // Handles the case where MAP_FIXED_NOREPLACE failed with MAP_FAILED + // or if the host system's kernel isn't new enough then it returns the wrong pointer + if (MappedPtr >= reinterpret_cast(TOP_KEY << PAGE_SHIFT)) { + // Make sure to munmap this so we don't leak memory + ::munmap(MappedPtr, length); + } + + if (UpperPage == TOP_KEY) { + BottomPage = BASE_KEY; + Wrapped = true; + goto restart; + } + else if (Wrapped && + LowerPage >= LastScanLocation) { + // We linear scanned the entire memory range. Give up + return (void*)(uintptr_t)-errno; + } + else { + // Try again + if (SearchDown) { + BottomPage -= PagesLength; + } + else { + BottomPage += PagesLength; + } + goto restart; + } + } + else { + if (SearchDown) { + LastScanLocation = LowerPage; + } + else { + LastScanLocation = UpperPage; + } + SetUsedPages(LowerPage, PagesLength); + return MappedPtr; + } + } + } + } + else { + void *MappedPtr = ::mmap( + reinterpret_cast(PageAddr << PAGE_SHIFT), + PagesLength << PAGE_SHIFT, + prot, + flags, + fd, + offset); + + if (MappedPtr != MAP_FAILED) { + SetUsedPages(PageAddr, PagesLength); + return MappedPtr; + } + else { + return reinterpret_cast(-errno); + } + } + return 0; +} + +int MemAllocator32Bit::munmap(void *addr, size_t length) { + std::scoped_lock lk{AllocMutex}; + size_t PagesLength = AlignUp(length, PAGE_SIZE) >> PAGE_SHIFT; + + uintptr_t Addr = reinterpret_cast(addr); + uintptr_t PageAddr = Addr >> PAGE_SHIFT; + + uintptr_t PageEnd = PageAddr + PagesLength; + + // Both Addr and length must be page aligned + if (Addr & PAGE_MASK) { + return -EINVAL; + } + + if (length & PAGE_MASK) { + return -EINVAL; + } + + if (Addr + length > std::numeric_limits::max()) { + return -EOVERFLOW; + } + + // Check reserved range + if (PageAddr < 16) { + // Return success for these + return 0; + } + + while (PageAddr != PageEnd) { + // Always pass to munmap, it may be something allocated we aren't tracking + int Result = ::munmap(reinterpret_cast(PageAddr << PAGE_SHIFT), PAGE_SIZE); + if (Result != 0) { + return -errno; + } + + if (MappedPages.test(PageAddr)) { + MappedPages.reset(PageAddr); + } + + ++PageAddr; + } + + return 0; +} + +void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) { + size_t OldPagesLength = AlignUp(old_size, PAGE_SIZE) >> PAGE_SHIFT; + size_t NewPagesLength = AlignUp(new_size, PAGE_SIZE) >> PAGE_SHIFT; + + { + std::scoped_lock lk{AllocMutex}; + if (flags & MREMAP_FIXED) { + void *MappedPtr = ::mremap(old_address, old_size, new_size, flags, new_address); + + if (MappedPtr != MAP_FAILED) { + if (!(flags & MREMAP_DONTUNMAP)) { + // Unmap the old location + uintptr_t OldAddr = reinterpret_cast(old_address); + SetFreePages(OldAddr >> PAGE_SHIFT, OldPagesLength); + } + + // Map the new pages + uintptr_t NewAddr = reinterpret_cast(MappedPtr); + SetUsedPages(NewAddr >> PAGE_SHIFT, NewPagesLength); + } + else { + return reinterpret_cast(-errno); + } + } + else { + uintptr_t OldAddr = reinterpret_cast(old_address); + uintptr_t OldPageAddr = OldAddr >> PAGE_SHIFT; + + if (NewPagesLength < OldPagesLength) { + void *MappedPtr = ::mremap(old_address, old_size, new_size, flags & ~MREMAP_MAYMOVE); + + if (MappedPtr != MAP_FAILED) { + // Clear the pages that we just shrunk + size_t NewPagesLength = AlignUp(new_size, PAGE_SIZE) >> PAGE_SHIFT; + uintptr_t NewPageAddr = reinterpret_cast(MappedPtr) >> PAGE_SHIFT; + SetFreePages(NewPageAddr + NewPagesLength, OldPagesLength - NewPagesLength); + return MappedPtr; + } + else { + return reinterpret_cast(-errno); + } + } + else { + // Scan the region forward from our first region's endd to see if it can be extended + bool CanExtend{true}; + + for (size_t i = OldPagesLength; i < NewPagesLength; ++i) { + if (MappedPages[OldPageAddr + i]) { + CanExtend = false; + break; + } + } + + if (CanExtend) { + void *MappedPtr = ::mremap(old_address, old_size, new_size, flags & ~MREMAP_MAYMOVE); + + if (MappedPtr != MAP_FAILED) { + // Map the new pages + size_t NewPagesLength = AlignUp(new_size, PAGE_SIZE) >> PAGE_SHIFT; + uintptr_t NewAddr = reinterpret_cast(MappedPtr); + SetUsedPages(NewAddr >> PAGE_SHIFT, NewPagesLength); + return MappedPtr; + } + else if (!(flags & MREMAP_MAYMOVE)) { + // We have one more chance if MAYMOVE is specified + return reinterpret_cast(-errno); + } + } + } + } + } + + // Flags can not contain MREMAP_FIXED at this point + // Flags might contain MREMAP_MAYMOVE and/or MREMAP_DONTUNMAP + // New Size is >= old size + + // First, try and allocate a region the size of the new size + void *MappedPtr = this->mmap(nullptr, new_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + std::scoped_lock lk{AllocMutex}; + if (reinterpret_cast(MappedPtr) > -4096) { + // Couldn't find a region that fit our space + return MappedPtr; + } + + // Good news, we found a region + // This will overwrite the previous mmap if it succeeds + MappedPtr = ::mremap(old_address, old_size, new_size, flags | MREMAP_FIXED | MREMAP_MAYMOVE, MappedPtr); + + if (MappedPtr != MAP_FAILED) { + if (!(flags & MREMAP_DONTUNMAP) && + MappedPtr != old_address) { + // If we have both MREMAP_DONTUNMAP not set and the new pointer is at a new location + // Make sure to clear the old mapping + uintptr_t OldAddr = reinterpret_cast(old_address); + SetFreePages(OldAddr >> PAGE_SHIFT , OldPagesLength); + } + + // Map the new pages + size_t NewPagesLength = AlignUp(new_size, PAGE_SIZE) >> PAGE_SHIFT; + uintptr_t NewAddr = reinterpret_cast(MappedPtr); + SetUsedPages(NewAddr >> PAGE_SHIFT, NewPagesLength); + return MappedPtr; + } + + // Failed + return reinterpret_cast(-errno); +} + +uint64_t MemAllocator32Bit::shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) { + std::scoped_lock lk{AllocMutex}; + + if (shmaddr != nullptr) { + // shmaddr must be valid + uint64_t Result = reinterpret_cast(::shmat(shmid, shmaddr, shmflg)); + if (Result != -1) { + uint32_t SmallRet = Result >> 32; + if (!(SmallRet == 0 || + SmallRet == ~0U)) { + LOGMAN_MSG_A("Syscall returning something with data in the upper 32bits! BUG!"); + return -ENOMEM; + } + + uintptr_t NewAddr = reinterpret_cast(Result); + uintptr_t NewPageAddr = NewAddr >> PAGE_SHIFT; + + // Add to the map + PageToShm[NewPageAddr] = shmid; + + *ResultAddress = Result; + + // We must get the shm size and track it + struct shmid_ds buf{}; + + if (shmctl(shmid, IPC_STAT, &buf) == 0) { + // Map the new pages + size_t NewPagesLength = buf.shm_segsz >> PAGE_SHIFT; + SetUsedPages(NewPageAddr, NewPagesLength); + } + + // Zero on working result + Result = 0; + } + else { + Result = -errno; + } + return Result; + } + else { + // We must get the shm size and track it + struct shmid_ds buf{}; + uint64_t PagesLength{}; + + if (shmctl(shmid, IPC_STAT, &buf) == 0) { + PagesLength = AlignUp(buf.shm_segsz, PAGE_SIZE) >> PAGE_SHIFT; + } + else { + return -EINVAL; + } + + bool Wrapped = false; + uint64_t BottomPage = LastScanLocation; +restart: + { + // Linear range scan + uint64_t LowerPage = (this->*FindPageRangePtr)(BottomPage, PagesLength); + if (LowerPage == 0) { + // Try again but this time from the start + BottomPage = LastKeyLocation; + LowerPage = (this->*FindPageRangePtr)(BottomPage, PagesLength); + } + + uint64_t UpperPage = LowerPage + PagesLength; + if (LowerPage == 0) { + return -ENOMEM; + } + { + // Try and map the range + void *MappedPtr = ::shmat( + shmid, + reinterpret_cast(LowerPage << PAGE_SHIFT), + shmflg); + + if (MappedPtr == MAP_FAILED) { + if (UpperPage == TOP_KEY) { + BottomPage = LastKeyLocation; + Wrapped = true; + goto restart; + } + else if (Wrapped && + LowerPage >= LastScanLocation) { + // We linear scanned the entire memory range. Give up + return -errno; + } + else { + // Try again + BottomPage += PagesLength; + goto restart; + } + } + else { + if (SearchDown) { + LastScanLocation = LowerPage; + } + else { + LastScanLocation = UpperPage; + } + // Set the range as mapped + SetUsedPages(LowerPage, PagesLength); + + *ResultAddress = reinterpret_cast(MappedPtr); + + // Add to the map + PageToShm[LowerPage] = shmid; + + // Zero on working result + return 0; + } + } + } + } +} +uint64_t MemAllocator32Bit::shmdt(const void* shmaddr) { + uint32_t AddrPage = reinterpret_cast(shmaddr) >> PAGE_SHIFT; + auto it = PageToShm.find(AddrPage); + + if (it == PageToShm.end()) { + // Page wasn't mapped + return -EINVAL; + } + + uint64_t Result = ::shmdt(shmaddr); + PageToShm.erase(it); + return Result; +} + +class MemAllocatorPassThrough final : public FEX::HLE::MemAllocator { +public: + void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override { + uint64_t Result = (uint64_t)::mmap(addr, length, prot, flags, fd, offset); + if (Result == ~0ULL) { + return reinterpret_cast(-errno); + } + return reinterpret_cast(Result); + } + + int munmap(void *addr, size_t length) override { + uint64_t Result = (uint64_t)::munmap(addr, length); + SYSCALL_ERRNO(); + } + + void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override { + uint64_t Result = (uint64_t)::mremap(old_address, old_size, new_size, flags, new_address); + if (Result == ~0ULL) { + return reinterpret_cast(-errno); + } + return reinterpret_cast(Result); + } + + uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override { + uint64_t Result = (uint64_t)::shmat(shmid, reinterpret_cast(shmaddr), shmflg); + if (Result != ~0ULL) { + *ResultAddress = Result; + Result = 0; + } + SYSCALL_ERRNO(); + } + + uint64_t shmdt(const void* shmaddr) override { + uint64_t Result = ::shmdt(shmaddr); + SYSCALL_ERRNO(); + } +}; + +std::unique_ptr Create32BitAllocator() { + return std::make_unique(); +} + +std::unique_ptr CreatePassthroughAllocator() { + return std::make_unique(); +} + +} diff --git a/Source/Tests/LinuxSyscalls/LinuxAllocator.h b/Source/Tests/LinuxSyscalls/LinuxAllocator.h new file mode 100644 index 000000000..0fa8f0ba2 --- /dev/null +++ b/Source/Tests/LinuxSyscalls/LinuxAllocator.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace FEX::HLE { + constexpr uint32_t X86_64_MAP_32BIT = 0x40; + + class MemAllocator { + public: + virtual ~MemAllocator() = default; + virtual void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) = 0; + virtual int munmap(void *addr, size_t length) = 0; + virtual void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) = 0; + virtual uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) = 0; + virtual uint64_t shmdt(const void* shmaddr) = 0; + }; + + std::unique_ptr Create32BitAllocator(); + std::unique_ptr CreatePassthroughAllocator(); +} diff --git a/Source/Tests/LinuxSyscalls/Syscalls.cpp b/Source/Tests/LinuxSyscalls/Syscalls.cpp index 395b51597..4f703ea7a 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls.cpp @@ -9,6 +9,7 @@ $end_info$ #include "Common/MathUtils.h" #include "Linux/Utils/ELFContainer.h" +#include "Tests/LinuxSyscalls/LinuxAllocator.h" #include "Tests/LinuxSyscalls/Syscalls.h" #include "Tests/LinuxSyscalls/Syscalls/Thread.h" #include "Tests/LinuxSyscalls/x32/Syscalls.h" @@ -589,7 +590,7 @@ SyscallHandler::SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalD FEX::HLE::_SyscallHandler = this; HostKernelVersion = CalculateHostKernelVersion(); GuestKernelVersion = CalculateGuestKernelVersion(); - + Alloc32Handler = FEX::HLE::Create32BitAllocator(); } SyscallHandler::~SyscallHandler() { diff --git a/Source/Tests/LinuxSyscalls/Syscalls.h b/Source/Tests/LinuxSyscalls/Syscalls.h index f617be78a..29b5df469 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.h +++ b/Source/Tests/LinuxSyscalls/Syscalls.h @@ -8,6 +8,7 @@ $end_info$ #pragma once #include "Tests/LinuxSyscalls/FileManagement.h" +#include "Tests/LinuxSyscalls/LinuxAllocator.h" #include #include @@ -143,6 +144,8 @@ public: static uint32_t KernelMinor(uint32_t Version) { return (Version >> 16) & 0xFF; } static uint32_t KernelPatch(uint32_t Version) { return Version & 0xFFFF; } + FEX::HLE::MemAllocator *Get32BitAllocator() { return Alloc32Handler.get(); } + protected: std::vector Definitions{}; std::mutex MMapMutex; @@ -169,6 +172,7 @@ private: void Strace(FEXCore::HLE::SyscallArguments *Args, uint64_t Ret); #endif + std::unique_ptr Alloc32Handler{}; }; uint64_t HandleSyscall(SyscallHandler *Handler, FEXCore::Core::CpuStateFrame *Frame, FEXCore::HLE::SyscallArguments *Args); diff --git a/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp b/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp index ea29b51e7..668dc7cef 100644 --- a/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp @@ -25,580 +25,11 @@ $end_info$ #include #include -#ifndef MREMAP_DONTUNMAP -#define MREMAP_DONTUNMAP 4 -#endif - namespace FEXCore::Context { struct Context; } namespace FEX::HLE::x32 { -class MemAllocator32Bit final : public MemAllocator { -private: - static constexpr uint64_t PAGE_SHIFT = 12; - static constexpr uint64_t PAGE_SIZE = 1 << PAGE_SHIFT; - static constexpr uint64_t PAGE_MASK = (1 << PAGE_SHIFT) - 1; - static constexpr uint64_t BASE_KEY = 16; - const uint64_t TOP_KEY = 0xFFFF'F000ULL >> PAGE_SHIFT; - -public: - MemAllocator32Bit() { - // First 16 pages are taken by the Linux kernel - for (size_t i = 0; i < 16; ++i) { - MappedPages.set(i); - } - // Take the top page as well - MappedPages.set(TOP_KEY); - if (SearchDown) { - LastScanLocation = TOP_KEY; - LastKeyLocation = TOP_KEY; - FindPageRangePtr = &MemAllocator32Bit::FindPageRange_TopDown; - } - else { - LastScanLocation = BASE_KEY; - LastKeyLocation = BASE_KEY; - FindPageRangePtr = &MemAllocator32Bit::FindPageRange; - } - } - void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override; - int munmap(void *addr, size_t length) override; - void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override; - uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override; - uint64_t shmdt(const void* shmaddr) override; - static constexpr bool SearchDown = true; - - // PageAddr is a page already shifted to page index - // PagesLength is the number of pages - void SetUsedPages(uint64_t PageAddr, size_t PagesLength) { - // Set the range as mapped - for (size_t i = 0; i < PagesLength; ++i) { - MappedPages.set(PageAddr + i); - } - } - - // PageAddr is a page already shifted to page index - // PagesLength is the number of pages - void SetFreePages(uint64_t PageAddr, size_t PagesLength) { - // Set the range as unused - for (size_t i = 0; i < PagesLength; ++i) { - MappedPages.reset(PageAddr + i); - } - } - -private: - // Set that contains 4k mapped pages - // This is the full 32bit memory range - std::bitset<0x10'0000> MappedPages; - std::map PageToShm{}; - uint64_t LastScanLocation{}; - uint64_t LastKeyLocation{}; - std::mutex AllocMutex{}; - uint64_t FindPageRange(uint64_t Start, size_t Pages); - uint64_t FindPageRange_TopDown(uint64_t Start, size_t Pages); - using FindHandler = uint64_t(MemAllocator32Bit::*)(uint64_t Start, size_t Pages); - FindHandler FindPageRangePtr{}; -}; - -uint64_t MemAllocator32Bit::FindPageRange(uint64_t Start, size_t Pages) { - // Linear range scan - while (Start != TOP_KEY) { - bool Free = true; - if ((Start + Pages) > TOP_KEY) { - return 0; - } - uint64_t Offset = 0; - for (; Offset < Pages; ++Offset) { - if (MappedPages.test(Start + Offset)) { - Free = false; - break; - } - } - - if (Free) { - return Start; - } - Start += Offset + 1; - } - - return 0; -} - -uint64_t MemAllocator32Bit::FindPageRange_TopDown(uint64_t Start, size_t Pages) { - // Linear range scan - while (Start >= BASE_KEY && - Start <= TOP_KEY) { - bool Free = true; - - uint64_t Offset = 0; - for (; Offset < Pages; ++Offset) { - if (MappedPages.test(Start - Offset)) { - Free = false; - break; - } - } - - if (Free) { - return Start - Offset; - } - Start -= Offset + 1; - } - - return 0; -} - -void *MemAllocator32Bit::mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { - std::scoped_lock lk{AllocMutex}; - size_t PagesLength = AlignUp(length, PAGE_SIZE) >> PAGE_SHIFT; - - uintptr_t Addr = reinterpret_cast(addr); - uintptr_t PageAddr = Addr >> PAGE_SHIFT; - - uintptr_t PageEnd = PageAddr + PagesLength; - - bool Fixed = ((flags & MAP_FIXED) || - (flags & MAP_FIXED_NOREPLACE)); - - // Both Addr and length must be page aligned - if (Addr & PAGE_MASK) { - return reinterpret_cast(-EINVAL); - } - - // If we do have an fd then offset must be page aligned - if (fd != -1 && - offset & PAGE_MASK) { - return reinterpret_cast(-EINVAL); - } - - if (Addr + length > std::numeric_limits::max()) { - return reinterpret_cast(-EOVERFLOW); - } - - // Check reserved range - if (Fixed && PageAddr < 16) { - return reinterpret_cast(-EINVAL); - } - - if (!Fixed) { - // If we aren't mapping fixed the ignore the address input - Addr = 0; - PageAddr = 0; - PageEnd = PagesLength; - } - - // Find a region that fits our address - if (Addr == 0) { - bool Wrapped = false; - uint64_t BottomPage = LastScanLocation; -restart: - { - // Linear range scan - uint64_t LowerPage = (this->*FindPageRangePtr)(BottomPage, PagesLength); - if (LowerPage == 0) { - // Try again but this time from the start - BottomPage = LastKeyLocation; - LowerPage = (this->*FindPageRangePtr)(BottomPage, PagesLength); - } - - uint64_t UpperPage = LowerPage + PagesLength; - if (LowerPage == 0) { - return reinterpret_cast(-ENOMEM); - } - { - // Try and map the range - void *MappedPtr = ::mmap( - reinterpret_cast(LowerPage<< PAGE_SHIFT), - length, - prot, - flags | MAP_FIXED_NOREPLACE, - fd, - offset); - - if (MappedPtr == MAP_FAILED && - errno != EEXIST) { - return reinterpret_cast(-errno); - } - else if (MappedPtr == MAP_FAILED || - MappedPtr >= reinterpret_cast(TOP_KEY << PAGE_SHIFT)) { - // Handles the case where MAP_FIXED_NOREPLACE failed with MAP_FAILED - // or if the host system's kernel isn't new enough then it returns the wrong pointer - if (MappedPtr >= reinterpret_cast(TOP_KEY << PAGE_SHIFT)) { - // Make sure to munmap this so we don't leak memory - ::munmap(MappedPtr, length); - } - - if (UpperPage == TOP_KEY) { - BottomPage = BASE_KEY; - Wrapped = true; - goto restart; - } - else if (Wrapped && - LowerPage >= LastScanLocation) { - // We linear scanned the entire memory range. Give up - return (void*)(uintptr_t)-errno; - } - else { - // Try again - if (SearchDown) { - BottomPage -= PagesLength; - } - else { - BottomPage += PagesLength; - } - goto restart; - } - } - else { - if (SearchDown) { - LastScanLocation = LowerPage; - } - else { - LastScanLocation = UpperPage; - } - SetUsedPages(LowerPage, PagesLength); - return MappedPtr; - } - } - } - } - else { - void *MappedPtr = ::mmap( - reinterpret_cast(PageAddr << PAGE_SHIFT), - PagesLength << PAGE_SHIFT, - prot, - flags, - fd, - offset); - - if (MappedPtr != MAP_FAILED) { - SetUsedPages(PageAddr, PagesLength); - return MappedPtr; - } - else { - return reinterpret_cast(-errno); - } - } - return 0; -} - -int MemAllocator32Bit::munmap(void *addr, size_t length) { - std::scoped_lock lk{AllocMutex}; - size_t PagesLength = AlignUp(length, PAGE_SIZE) >> PAGE_SHIFT; - - uintptr_t Addr = reinterpret_cast(addr); - uintptr_t PageAddr = Addr >> PAGE_SHIFT; - - uintptr_t PageEnd = PageAddr + PagesLength; - - // Both Addr and length must be page aligned - if (Addr & PAGE_MASK) { - return -EINVAL; - } - - if (length & PAGE_MASK) { - return -EINVAL; - } - - if (Addr + length > std::numeric_limits::max()) { - return -EOVERFLOW; - } - - // Check reserved range - if (PageAddr < 16) { - // Return success for these - return 0; - } - - while (PageAddr != PageEnd) { - // Always pass to munmap, it may be something allocated we aren't tracking - int Result = ::munmap(reinterpret_cast(PageAddr << PAGE_SHIFT), PAGE_SIZE); - if (Result != 0) { - return -errno; - } - - if (MappedPages.test(PageAddr)) { - MappedPages.reset(PageAddr); - } - - ++PageAddr; - } - - return 0; -} - -void *MemAllocator32Bit::mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) { - size_t OldPagesLength = AlignUp(old_size, PAGE_SIZE) >> PAGE_SHIFT; - size_t NewPagesLength = AlignUp(new_size, PAGE_SIZE) >> PAGE_SHIFT; - - { - std::scoped_lock lk{AllocMutex}; - if (flags & MREMAP_FIXED) { - void *MappedPtr = ::mremap(old_address, old_size, new_size, flags, new_address); - - if (MappedPtr != MAP_FAILED) { - if (!(flags & MREMAP_DONTUNMAP)) { - // Unmap the old location - uintptr_t OldAddr = reinterpret_cast(old_address); - SetFreePages(OldAddr >> PAGE_SHIFT, OldPagesLength); - } - - // Map the new pages - uintptr_t NewAddr = reinterpret_cast(MappedPtr); - SetUsedPages(NewAddr >> PAGE_SHIFT, NewPagesLength); - } - else { - return reinterpret_cast(-errno); - } - } - else { - uintptr_t OldAddr = reinterpret_cast(old_address); - uintptr_t OldPageAddr = OldAddr >> PAGE_SHIFT; - - if (NewPagesLength < OldPagesLength) { - void *MappedPtr = ::mremap(old_address, old_size, new_size, flags & ~MREMAP_MAYMOVE); - - if (MappedPtr != MAP_FAILED) { - // Clear the pages that we just shrunk - size_t NewPagesLength = AlignUp(new_size, PAGE_SIZE) >> PAGE_SHIFT; - uintptr_t NewPageAddr = reinterpret_cast(MappedPtr) >> PAGE_SHIFT; - SetFreePages(NewPageAddr + NewPagesLength, OldPagesLength - NewPagesLength); - return MappedPtr; - } - else { - return reinterpret_cast(-errno); - } - } - else { - // Scan the region forward from our first region's endd to see if it can be extended - bool CanExtend{true}; - - for (size_t i = OldPagesLength; i < NewPagesLength; ++i) { - if (MappedPages[OldPageAddr + i]) { - CanExtend = false; - break; - } - } - - if (CanExtend) { - void *MappedPtr = ::mremap(old_address, old_size, new_size, flags & ~MREMAP_MAYMOVE); - - if (MappedPtr != MAP_FAILED) { - // Map the new pages - size_t NewPagesLength = AlignUp(new_size, PAGE_SIZE) >> PAGE_SHIFT; - uintptr_t NewAddr = reinterpret_cast(MappedPtr); - SetUsedPages(NewAddr >> PAGE_SHIFT, NewPagesLength); - return MappedPtr; - } - else if (!(flags & MREMAP_MAYMOVE)) { - // We have one more chance if MAYMOVE is specified - return reinterpret_cast(-errno); - } - } - } - } - } - - // Flags can not contain MREMAP_FIXED at this point - // Flags might contain MREMAP_MAYMOVE and/or MREMAP_DONTUNMAP - // New Size is >= old size - - // First, try and allocate a region the size of the new size - void *MappedPtr = this->mmap(nullptr, new_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - std::scoped_lock lk{AllocMutex}; - if (reinterpret_cast(MappedPtr) > -4096) { - // Couldn't find a region that fit our space - return MappedPtr; - } - - // Good news, we found a region - // This will overwrite the previous mmap if it succeeds - MappedPtr = ::mremap(old_address, old_size, new_size, flags | MREMAP_FIXED | MREMAP_MAYMOVE, MappedPtr); - - if (MappedPtr != MAP_FAILED) { - if (!(flags & MREMAP_DONTUNMAP) && - MappedPtr != old_address) { - // If we have both MREMAP_DONTUNMAP not set and the new pointer is at a new location - // Make sure to clear the old mapping - uintptr_t OldAddr = reinterpret_cast(old_address); - SetFreePages(OldAddr >> PAGE_SHIFT , OldPagesLength); - } - - // Map the new pages - size_t NewPagesLength = AlignUp(new_size, PAGE_SIZE) >> PAGE_SHIFT; - uintptr_t NewAddr = reinterpret_cast(MappedPtr); - SetUsedPages(NewAddr >> PAGE_SHIFT, NewPagesLength); - return MappedPtr; - } - - // Failed - return reinterpret_cast(-errno); -} - -uint64_t MemAllocator32Bit::shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) { - std::scoped_lock lk{AllocMutex}; - - if (shmaddr != nullptr) { - // shmaddr must be valid - uint64_t Result = reinterpret_cast(::shmat(shmid, shmaddr, shmflg)); - if (Result != -1) { - uint32_t SmallRet = Result >> 32; - if (!(SmallRet == 0 || - SmallRet == ~0U)) { - LOGMAN_MSG_A("Syscall returning something with data in the upper 32bits! BUG!"); - return -ENOMEM; - } - - uintptr_t NewAddr = reinterpret_cast(Result); - uintptr_t NewPageAddr = NewAddr >> PAGE_SHIFT; - - // Add to the map - PageToShm[NewPageAddr] = shmid; - - *ResultAddress = Result; - - // We must get the shm size and track it - struct shmid_ds buf{}; - - if (shmctl(shmid, IPC_STAT, &buf) == 0) { - // Map the new pages - size_t NewPagesLength = buf.shm_segsz >> PAGE_SHIFT; - SetUsedPages(NewPageAddr, NewPagesLength); - } - - // Zero on working result - Result = 0; - } - else { - Result = -errno; - } - return Result; - } - else { - // We must get the shm size and track it - struct shmid_ds buf{}; - uint64_t PagesLength{}; - - if (shmctl(shmid, IPC_STAT, &buf) == 0) { - PagesLength = AlignUp(buf.shm_segsz, PAGE_SIZE) >> PAGE_SHIFT; - } - else { - return -EINVAL; - } - - bool Wrapped = false; - uint64_t BottomPage = LastScanLocation; -restart: - { - // Linear range scan - uint64_t LowerPage = (this->*FindPageRangePtr)(BottomPage, PagesLength); - if (LowerPage == 0) { - // Try again but this time from the start - BottomPage = LastKeyLocation; - LowerPage = (this->*FindPageRangePtr)(BottomPage, PagesLength); - } - - uint64_t UpperPage = LowerPage + PagesLength; - if (LowerPage == 0) { - return -ENOMEM; - } - { - // Try and map the range - void *MappedPtr = ::shmat( - shmid, - reinterpret_cast(LowerPage << PAGE_SHIFT), - shmflg); - - if (MappedPtr == MAP_FAILED) { - if (UpperPage == TOP_KEY) { - BottomPage = LastKeyLocation; - Wrapped = true; - goto restart; - } - else if (Wrapped && - LowerPage >= LastScanLocation) { - // We linear scanned the entire memory range. Give up - return -errno; - } - else { - // Try again - BottomPage += PagesLength; - goto restart; - } - } - else { - if (SearchDown) { - LastScanLocation = LowerPage; - } - else { - LastScanLocation = UpperPage; - } - // Set the range as mapped - SetUsedPages(LowerPage, PagesLength); - - *ResultAddress = reinterpret_cast(MappedPtr); - - // Add to the map - PageToShm[LowerPage] = shmid; - - // Zero on working result - return 0; - } - } - } - } -} -uint64_t MemAllocator32Bit::shmdt(const void* shmaddr) { - uint32_t AddrPage = reinterpret_cast(shmaddr) >> PAGE_SHIFT; - auto it = PageToShm.find(AddrPage); - - if (it == PageToShm.end()) { - // Page wasn't mapped - return -EINVAL; - } - - uint64_t Result = ::shmdt(shmaddr); - PageToShm.erase(it); - return Result; -} - -class MemAllocatorPassThrough final : public MemAllocator { -public: - void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) override { - uint64_t Result = (uint64_t)::mmap(addr, length, prot, flags, fd, offset); - if (Result == ~0ULL) { - return reinterpret_cast(-errno); - } - return reinterpret_cast(Result); - } - - int munmap(void *addr, size_t length) override { - uint64_t Result = (uint64_t)::munmap(addr, length); - SYSCALL_ERRNO(); - } - - void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) override { - uint64_t Result = (uint64_t)::mremap(old_address, old_size, new_size, flags, new_address); - if (Result == ~0ULL) { - return reinterpret_cast(-errno); - } - return reinterpret_cast(Result); - } - - uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) override { - uint64_t Result = (uint64_t)::shmat(shmid, reinterpret_cast(shmaddr), shmflg); - if (Result != ~0ULL) { - *ResultAddress = Result; - Result = 0; - } - SYSCALL_ERRNO(); - } - - uint64_t shmdt(const void* shmaddr) override { - uint64_t Result = ::shmdt(shmaddr); - SYSCALL_ERRNO(); - } -}; - void RegisterEpoll(FEX::HLE::SyscallHandler *const Handler); void RegisterFD(); void RegisterFS(); @@ -738,15 +169,6 @@ public: #endif } - std::unique_ptr CreateAllocator(bool Use32BitAllocator) { - if (Use32BitAllocator) { - return std::make_unique(); - } - else { - return std::make_unique(); - } - } - std::unique_ptr CreateHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation, std::unique_ptr Allocator) { return std::make_unique(ctx, _SignalDelegation, std::move(Allocator)); } diff --git a/Source/Tests/LinuxSyscalls/x32/Syscalls.h b/Source/Tests/LinuxSyscalls/x32/Syscalls.h index 28d085220..925fdeee7 100644 --- a/Source/Tests/LinuxSyscalls/x32/Syscalls.h +++ b/Source/Tests/LinuxSyscalls/x32/Syscalls.h @@ -29,28 +29,17 @@ class SignalDelegator; namespace FEX::HLE::x32 { #include "SyscallsEnum.h" -class MemAllocator { -public: - virtual ~MemAllocator() = default; - virtual void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) = 0; - virtual int munmap(void *addr, size_t length) = 0; - virtual void *mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) = 0; - virtual uint64_t shmat(int shmid, const void* shmaddr, int shmflg, uint32_t *ResultAddress) = 0; - virtual uint64_t shmdt(const void* shmaddr) = 0; -}; - class x32SyscallHandler final : public FEX::HLE::SyscallHandler { public: x32SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation, std::unique_ptr Allocator); - FEX::HLE::x32::MemAllocator *GetAllocator() { return AllocHandler.get(); } + FEX::HLE::MemAllocator *GetAllocator() { return AllocHandler.get(); } private: void RegisterSyscallHandlers(); std::unique_ptr AllocHandler{}; }; -std::unique_ptr CreateAllocator(bool Use32BitAllocator); std::unique_ptr CreateHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation, std::unique_ptr Allocator); diff --git a/Source/Tests/LinuxSyscalls/x64/Memory.cpp b/Source/Tests/LinuxSyscalls/x64/Memory.cpp index 288591384..5c61693ac 100644 --- a/Source/Tests/LinuxSyscalls/x64/Memory.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Memory.cpp @@ -4,6 +4,7 @@ tags: LinuxSyscalls|syscalls-x86-64 $end_info$ */ +#include "Tests/LinuxSyscalls/LinuxAllocator.h" #include "Tests/LinuxSyscalls/Syscalls.h" #include "Tests/LinuxSyscalls/x64/Syscalls.h" #include @@ -29,7 +30,20 @@ static std::string get_fdpath(int fd) namespace FEX::HLE::x64 { void RegisterMemory(FEX::HLE::SyscallHandler *const Handler) { REGISTER_SYSCALL_IMPL_X64(munmap, [](FEXCore::Core::CpuStateFrame *Frame, void *addr, size_t length) -> uint64_t { - uint64_t Result = FEXCore::Allocator::munmap(addr, length); + uint64_t Result{}; + if (addr < (void*)0x1'0000'0000ULL) { + Result = (uint64_t)static_cast(FEX::HLE::_SyscallHandler)->Get32BitAllocator()-> + munmap(addr, length); + return Result; + + if (Result < -4096) { + errno = -Result; + Result = -1; + } + } + else { + Result = FEXCore::Allocator::munmap(addr, length); + } auto Thread = Frame->Thread; if (Result != -1) { @@ -42,31 +56,24 @@ namespace FEX::HLE::x64 { REGISTER_SYSCALL_IMPL_X64(mmap, [](FEXCore::Core::CpuStateFrame *Frame, void *addr, size_t length, int prot, int flags, int fd, off_t offset) -> uint64_t { static FEX_CONFIG_OPT(AOTIRLoad, AOTIRLOAD); -#ifdef _M_ARM_64 - constexpr uint32_t X86_64_MAP_32BIT = 0x40; + uint64_t Result{}; - // Handle MAP_32BIT without a full allocator - // Set address to a low address to force the kernel to allocate bottom up - bool Map32Bit = (flags & X86_64_MAP_32BIT) && - !(flags & (MAP_FIXED | MAP_FIXED_NOREPLACE)); + bool Map32Bit = flags & FEX::HLE::X86_64_MAP_32BIT; if (Map32Bit) { - addr = reinterpret_cast(0x1'0000); - // Remove the flag - flags &= ~X86_64_MAP_32BIT; + Result = (uint64_t)static_cast(FEX::HLE::_SyscallHandler)->Get32BitAllocator()-> + mmap(reinterpret_cast(addr), length, prot,flags, fd, offset); + if (Result < -4096) { + errno = -Result; + Result = -1; + } + } + else + { + Result = reinterpret_cast(FEXCore::Allocator::mmap(addr, length, prot, flags, fd, offset)); } -#endif - uint64_t Result = reinterpret_cast(FEXCore::Allocator::mmap(addr, length, prot, flags, fd, offset)); auto Thread = Frame->Thread; if (Result != -1) { -#ifdef _M_ARM_64 - if (Map32Bit) { - if ((Result + length) >= 0x1'0000'0000ULL) { - FEXCore::Allocator::munmap(reinterpret_cast(Result), length); - return -ENOMEM; - } - } -#endif if (!(flags & MAP_ANONYMOUS)) { auto filename = get_fdpath(fd); diff --git a/Source/Tests/TestHarnessRunner.cpp b/Source/Tests/TestHarnessRunner.cpp index a7266b72a..318c8d262 100644 --- a/Source/Tests/TestHarnessRunner.cpp +++ b/Source/Tests/TestHarnessRunner.cpp @@ -8,6 +8,7 @@ $end_info$ #include "Common/ArgumentLoader.h" #include "CommonCore/HostFactory.h" #include "HarnessHelpers.h" +#include "Tests/LinuxSyscalls/LinuxAllocator.h" #include "Tests/LinuxSyscalls/Syscalls.h" #include "Tests/LinuxSyscalls/x32/Syscalls.h" #include "Tests/LinuxSyscalls/x64/Syscalls.h" @@ -88,7 +89,7 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Context::InitializeContext(CTX); - std::unique_ptr Allocator; + std::unique_ptr Allocator; if (Loader.Is64BitMode()) { if (!Loader.MapMemory([](void *addr, size_t length, int prot, int flags, int fd, off_t offset) { @@ -106,7 +107,12 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Allocator::SetupHooks(); } - Allocator = FEX::HLE::x32::CreateAllocator(KernelVersion < FEX::HLE::SyscallHandler::KernelVersion(4, 17)); + if (KernelVersion < FEX::HLE::SyscallHandler::KernelVersion(4, 17)) { + Allocator = FEX::HLE::Create32BitAllocator(); + } + else { + Allocator = FEX::HLE::CreatePassthroughAllocator(); + } if (!Loader.MapMemory([&Allocator](void *addr, size_t length, int prot, int flags, int fd, off_t offset) { return Allocator->mmap(addr, length, prot, flags, fd, offset);