diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index 2c4dd2ba3..5c0b0e8c8 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -72,10 +72,11 @@ public: ContextImpl& CTX; bool IsGeneratingCache = false; - uint64_t ComputeCodeMapId(std::string_view Filename, int FD) override; + FEX_CONFIG_OPT(EnableCodeCaching, ENABLECODECACHINGWIP); - void LoadData(Core::InternalThreadState&, std::byte* MappedCacheFile, const ExecutableFileSectionInfo&) override; + uint64_t ComputeCodeMapId(std::string_view Filename, int FD) override; bool SaveData(Core::InternalThreadState&, int TargetFD, const ExecutableFileSectionInfo&, uint64_t SerializedBaseAddress) override; + bool LoadData(Core::InternalThreadState&, std::byte* MappedCacheFile, const ExecutableFileSectionInfo&) override; void InitiateCacheGeneration() override { IsGeneratingCache = true; diff --git a/FEXCore/Source/Interface/Core/CodeCache.cpp b/FEXCore/Source/Interface/Core/CodeCache.cpp index 690a5abb1..54880155c 100644 --- a/FEXCore/Source/Interface/Core/CodeCache.cpp +++ b/FEXCore/Source/Interface/Core/CodeCache.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -227,7 +228,7 @@ uint64_t CodeCache::ComputeCodeMapId(std::string_view Filename, int FD) { } struct CodeCacheHeader { - char Magic[4] = {'F', 'X', 'C', 'C'}; + std::array Magic = ExpectedMagic; uint32_t FormatVersion = 1; char FEXVersion[8] = {}; uint32_t NumBlocks; @@ -236,11 +237,9 @@ struct CodeCacheHeader { uint32_t NumRelocations; uint64_t SerializedBaseAddress; // TODO: Consider including information from LookupCache.BlockLinks -}; -void CodeCache::LoadData(Core::InternalThreadState& Thread, std::byte* MappedCacheFile, const ExecutableFileSectionInfo& GuestRIPLookup) { - // TODO -} + static constexpr std::array ExpectedMagic = {'F', 'X', 'C', 'C'}; +}; template concept OrderedContainer = requires { typename T::key_compare; }; @@ -248,12 +247,13 @@ concept OrderedContainer = requires { typename T::key_compare; }; bool CodeCache::SaveData(Core::InternalThreadState& Thread, int fd, const ExecutableFileSectionInfo& SourceBinary, uint64_t SerializedBaseAddress) { auto CodeBuffer = CTX.GetLatest(); auto& LookupCache = *Thread.LookupCache->Shared; - auto Relocations = Thread.CPUBackend->TakeRelocations(SourceBinary.FileStartVA); // Write file header - CodeCacheHeader header; - memcpy(&header.FEXVersion[0], GIT_SHORT_HASH, strlen(GIT_SHORT_HASH)); + CodeCacheHeader header {}; + constexpr std::string_view git_hash = GIT_SHORT_HASH; + static_assert(git_hash.size() <= sizeof(header.FEXVersion)); + std::ranges::copy(git_hash, header.FEXVersion); header.NumBlocks = LookupCache.BlockList.size(); header.NumCodePages = LookupCache.CodePages.size(); header.CodeBufferSize = CTX.LatestOffset; @@ -314,9 +314,9 @@ bool CodeCache::SaveData(Core::InternalThreadState& Thread, int fd, const Execut // Dump code pages static_assert(OrderedContainer, "Non-deterministic data source"); - for (auto& [Page, Entrypoints] : LookupCache.CodePages) { - static_assert(sizeof(Page) == 8, "Breaking change in code cache data layout"); - ::write(fd, &Page, sizeof(Page)); + for (auto& [PageIndex, Entrypoints] : LookupCache.CodePages) { + uint64_t PageAddr = PageIndex << 12; + ::write(fd, &PageAddr, sizeof(PageAddr)); uint64_t NumEntrypoints = Entrypoints.size(); ::write(fd, &NumEntrypoints, sizeof(NumEntrypoints)); ::write(fd, Entrypoints.data(), Entrypoints.size() * sizeof(Entrypoints[0])); @@ -325,6 +325,160 @@ bool CodeCache::SaveData(Core::InternalThreadState& Thread, int fd, const Execut return true; } +bool CodeCache::LoadData(Core::InternalThreadState& Thread, std::byte* MappedCacheFile, const ExecutableFileSectionInfo& BinarySection) { + if (!EnableCodeCaching) { + return true; + } + + namespace ranges = std::ranges; + + // Read file header + CodeCacheHeader header {}; + ::memcpy(&header, MappedCacheFile, sizeof(header)); + MappedCacheFile += sizeof(header); + + LogMan::Msg::IFmt("Cache load: {:5} blocks; base={:#14x}; off={:#9x}-{:#09x}; {:016x} {}", header.NumBlocks, BinarySection.FileStartVA, + BinarySection.BeginVA - BinarySection.FileStartVA, BinarySection.EndVA - BinarySection.FileStartVA, + BinarySection.FileInfo.FileId, BinarySection.FileInfo.Filename); + + if (!ranges::equal(header.Magic, header.ExpectedMagic)) { + LogMan::Msg::EFmt("Invalid cache file header"); + return false; + } + + char ExpectedVersion[8] = GIT_SHORT_HASH; + ranges::fill(ranges::find(ExpectedVersion, 0), std::end(ExpectedVersion), 0); + if (!ranges::equal(header.FEXVersion, ExpectedVersion)) { + LogMan::Msg::IFmt("Cache generated from old FEX version {}, current is {}; skipping", fmt::join(header.FEXVersion, ""), + fmt::join(ExpectedVersion, "")); + return false; + } + + if (header.NumBlocks == 0) { + // Valid caches are never empty + LogMan::Msg::IFmt("Code cache empty, aborting"); + return false; + } + + // Read guest<->host block mappings + using BlockListEntry = decltype(GuestToHostMap::BlockList)::value_type; + fextl::vector BlockList(header.NumBlocks); + { + for (auto& BlockPtr : BlockList) { + ::memcpy(&BlockPtr.first, MappedCacheFile, sizeof(BlockPtr.first)); + MappedCacheFile += sizeof(BlockPtr.first); + ::memcpy(&BlockPtr.second.HostCode, MappedCacheFile, sizeof(BlockPtr.second.HostCode)); + MappedCacheFile += sizeof(BlockPtr.second.HostCode); + uint64_t NumGuestPages; + ::memcpy(&NumGuestPages, MappedCacheFile, sizeof(NumGuestPages)); + MappedCacheFile += sizeof(NumGuestPages); + + BlockPtr.second.CodePages.resize(NumGuestPages); + ::memcpy(BlockPtr.second.CodePages.data(), MappedCacheFile, std::span {BlockPtr.second.CodePages}.size_bytes()); + MappedCacheFile += std::span {BlockPtr.second.CodePages}.size_bytes(); + } + + // Consistency check: VMA regions at the top and end should belong to the same file + auto [min_val, max_val] = ranges::minmax_element(BlockList, std::less {}, &decltype(BlockList)::value_type::first); + auto MinBound = CTX.SyscallHandler->LookupExecutableFileSection(Thread, min_val->first + BinarySection.FileStartVA); + auto MaxBound = CTX.SyscallHandler->LookupExecutableFileSection(Thread, max_val->first + BinarySection.FileStartVA); + if (&MinBound->FileInfo != &BinarySection.FileInfo || &MaxBound->FileInfo != &BinarySection.FileInfo) { + ERROR_AND_DIE_FMT("Cached blocks offsets {:#x}-{:#x} out of bounds for guest library {} ({:016x} @ {:#x}) while trying to load " + "section {:#x}-{:#x}!", + min_val->first, max_val->first, BinarySection.FileInfo.Filename, BinarySection.FileInfo.FileId, + BinarySection.FileStartVA, BinarySection.BeginVA, BinarySection.EndVA); + } + + // Constrain BlockList to the given ExecutableFileSectionInfo + LOGMAN_THROW_A_FMT(ranges::is_sorted(BlockList, [](auto& a, auto& b) { return a.first < b.first; }), "Expected sorted block list"); + auto begin = ranges::lower_bound(BlockList, BinarySection.BeginVA - BinarySection.FileStartVA, std::less {}, &BlockListEntry::first); + auto end = + ranges::upper_bound(begin, BlockList.end(), BinarySection.EndVA - BinarySection.FileStartVA - 1, std::less {}, &BlockListEntry::first); + BlockList.erase(end, BlockList.end()); + BlockList.erase(BlockList.begin(), begin); + if (BlockList.empty()) { + // Not an error since there is just no data to load + LogMan::Msg::IFmt("No blocks cached in this range, aborting"); + return true; + } + } + + // Read relocations + fextl::vector Relocations(header.NumRelocations, FEXCore::CPU::Relocation::Default()); + ::memcpy(Relocations.data(), MappedCacheFile, Relocations.size() * sizeof(Relocations[0])); + MappedCacheFile += Relocations.size() * sizeof(Relocations[0]); + + // Pad to next page in file, which contains CodeBuffer data + MappedCacheFile = reinterpret_cast(AlignUp(reinterpret_cast(MappedCacheFile), Utils::FEX_PAGE_SIZE)); + + // Prepare CodeBuffer: Page aligned and big enough to hold all cached data + auto Lock = std::unique_lock {CTX.CodeBufferWriteMutex}; + if (auto Prev = Thread.CPUBackend->CheckCodeBufferUpdate()) { + Allocator::VirtualDontNeed(Thread.CallRetStackBase, FEXCore::Core::InternalThreadState::CALLRET_STACK_SIZE); + auto lk = Thread.LookupCache->AcquireWriteLock(); + Thread.LookupCache->ChangeGuestToHostMapping(*Prev, *CTX.GetLatest()->LookupCache, lk); + } + + auto CodeBuffer = CTX.GetLatest(); + LOGMAN_THROW_A_FMT(header.CodeBufferSize <= CodeBuffer->Size, "CodeBuffer too small to load code cache"); + LOGMAN_THROW_A_FMT(reinterpret_cast(CodeBuffer->Ptr) % 0x1000 == 0, "Expected CodeBuffer base to be page-aligned"); + const auto Delta = AlignUp(CTX.LatestOffset, 0x1000) - CTX.LatestOffset; + CTX.LatestOffset += Delta; + + while (CTX.LatestOffset + header.CodeBufferSize > CodeBuffer->Size - Utils::FEX_PAGE_SIZE) { + CTX.ClearCodeCache(&Thread); + CodeBuffer = CTX.GetLatest(); + LogMan::Msg::IFmt("Increased code buffer size to {} MiB for cache load", CodeBuffer->Size / 1024 / 1024); + } + + // Read CodeBuffer data from file. Make sure the destination is page-aligned. + // TODO: Only load the data needed for the selected section + auto CodeBufferRange = std::as_writable_bytes(std::span {CodeBuffer->Ptr, CodeBuffer->Size}).subspan(CTX.LatestOffset, header.CodeBufferSize); + ::memcpy(CodeBufferRange.data(), MappedCacheFile, header.CodeBufferSize); + MappedCacheFile += header.CodeBufferSize; + CTX.LatestOffset += header.CodeBufferSize; + + // Apply FEX relocations + auto Ret = ApplyCodeRelocations(BinarySection.FileStartVA, CodeBufferRange, Relocations, false); + LOGMAN_THROW_A_FMT(Ret == true, "Failed to apply code cache relocations"); + + { + auto& LookupCache = *CodeBuffer->LookupCache; + auto WriteLock = LookupCache.AcquireWriteLock(); + + // Register blocks to LookupCache + for (auto& [Guest, Host] : BlockList) { + for (auto& CodePage : Host.CodePages) { + CodePage += BinarySection.FileStartVA; + } + auto HostCode = reinterpret_cast(Host.HostCode + reinterpret_cast(CodeBufferRange.data())); + LookupCache.AddBlockMapping(Guest + BinarySection.FileStartVA, std::move(Host.CodePages), HostCode, WriteLock); + } + + // Register loaded code ranges + fextl::vector Entrypoints; + for (uint32_t i = 0; i < header.NumCodePages; ++i) { + uint64_t CodePage; + memcpy(&CodePage, MappedCacheFile, sizeof(CodePage)); + MappedCacheFile += sizeof(CodePage); + + uint64_t NumEntrypoints; + memcpy(&NumEntrypoints, MappedCacheFile, sizeof(NumEntrypoints)); + MappedCacheFile += sizeof(NumEntrypoints); + + Entrypoints.resize(NumEntrypoints); + memcpy(Entrypoints.data(), MappedCacheFile, NumEntrypoints * sizeof(Entrypoints[0])); + MappedCacheFile += NumEntrypoints * sizeof(Entrypoints[0]); + + if (LookupCache.AddBlockExecutableRange(Entrypoints, CodePage, FEXCore::Utils::FEX_PAGE_SIZE, WriteLock)) { + CTX.SyscallHandler->MarkGuestExecutableRange(&Thread, CodePage, FEXCore::Utils::FEX_PAGE_SIZE); + } + } + } + + return true; +} + bool CodeCache::ApplyCodeRelocations(uint64_t GuestEntry, std::span Code, std::span EntryRelocations, bool ForStorage) { CPU::Arm64Emitter Emitter(&CTX, Code.data(), Code.size_bytes()); diff --git a/FEXCore/Source/Interface/Core/JIT/Relocations.h b/FEXCore/Source/Interface/Core/JIT/Relocations.h index 734a5908e..9faa8a781 100644 --- a/FEXCore/Source/Interface/Core/JIT/Relocations.h +++ b/FEXCore/Source/Interface/Core/JIT/Relocations.h @@ -74,6 +74,17 @@ struct RelocGuestRIP final { }; union Relocation { + // Clang 16 Can't default-initialize this union + static Relocation Default() { +#if __clang_major__ < 17 + Relocation Ret {.Header {}}; + memset(&Ret, 0, sizeof(Ret)); + return Ret; +#else + return {}; +#endif + } + RelocationHeader Header {}; RelocNamedSymbolLiteral NamedSymbolLiteral; diff --git a/FEXCore/Source/Interface/Core/LookupCache.h b/FEXCore/Source/Interface/Core/LookupCache.h index f906a6466..ab73cb1c3 100644 --- a/FEXCore/Source/Interface/Core/LookupCache.h +++ b/FEXCore/Source/Interface/Core/LookupCache.h @@ -141,7 +141,7 @@ struct GuestToHostMap { BlockLinks->insert({{GuestDestination, HostLink}, delinker}); } - bool AddBlockExecutableRange(const fextl::set& Addresses, uint64_t Start, uint64_t Length, const LookupCacheWriteLockToken&) { + bool AddBlockExecutableRange(const std::ranges::input_range auto& Addresses, uint64_t Start, uint64_t Length, const LookupCacheWriteLockToken&) { bool rv = false; for (auto CurrentPage = Start >> 12, EndPage = (Start + Length - 1) >> 12; CurrentPage <= EndPage; CurrentPage++) { diff --git a/FEXCore/include/FEXCore/Core/CodeCache.h b/FEXCore/include/FEXCore/Core/CodeCache.h index 9e4cce349..7c43e99e4 100644 --- a/FEXCore/include/FEXCore/Core/CodeCache.h +++ b/FEXCore/include/FEXCore/Core/CodeCache.h @@ -46,7 +46,14 @@ struct ExecutableFileSectionInfo { ExecutableFileInfo& FileInfo; // Start address that the file is mapped to. + // NOTE: Since executable files may be mapped multiple times, this can depend on the queried section. uintptr_t FileStartVA; + + // Start address of the section mapping + uintptr_t BeginVA; + + // End address that of the section mapping + uintptr_t EndVA; }; using CodeMapFileId = uint64_t; @@ -175,8 +182,9 @@ public: /** * Loads a code cache from mapped memory and appends it to the current Core state. * TODO: Optionally recompiles all contained code blocks at runtime for validation. + * Returns false if the provided cache file is invalid, and true otherwise. */ - virtual void LoadData(Core::InternalThreadState&, std::byte* MappedCacheFile, const ExecutableFileSectionInfo&) = 0; + virtual bool LoadData(Core::InternalThreadState&, std::byte* MappedCacheFile, const ExecutableFileSectionInfo&) = 0; /** * Bundles the current Core state (CodeBuffer, GuestToHostMapping, ...) to a code cache and writes it to the given file descriptor. diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index 2fdfa6238..c6d1970d5 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -198,6 +198,7 @@ public: FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE); FEX_CONFIG_OPT(SMCChecks, SMCCHECKS); FEX_CONFIG_OPT(NeedsSeccomp, NEEDSSECCOMP); + FEX_CONFIG_OPT(EnableCodeCaching, ENABLECODECACHINGWIP); uint32_t GetHostKernelVersion() const { return HostKernelVersion; @@ -250,8 +251,9 @@ public: fextl::set VolatileInstructions {}; FEXCore::IntervalList VolatileValidRanges {}; }; - std::optional - TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t addr, size_t length, int prot, int flags, int fd, off_t offset); + std::optional TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t addr, size_t length, + int prot, int flags, int fd, off_t offset, + std::optional& CachedSection); void TrackMunmap(FEXCore::Core::InternalThreadState* Thread, void* addr, size_t length); void TrackMremap(FEXCore::Core::InternalThreadState* Thread, uint64_t OldAddress, size_t OldSize, size_t NewSize, int flags, uint64_t NewAddress); void TrackShmat(FEXCore::Core::InternalThreadState* Thread, int shmid, uint64_t shmaddr, int shmflg, uint64_t Length); @@ -322,6 +324,8 @@ public: VMATracking::VMATracking VMATracking; + const uint64_t CodeCacheConfigId = 0; // TODO: Make unique to active configuration + uint64_t read_ldt(FEXCore::Core::CpuStateFrame* Frame, void* ptr, unsigned long bytecount); uint64_t write_ldt(FEXCore::Core::CpuStateFrame* Frame, void* ptr, unsigned long bytecount, bool legacy); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 63de1c98a..179a988fa 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -7,6 +7,7 @@ desc: SMC/MMan Tracking $end_info$ */ +#include #include "Common/FDUtils.h" #include "Common/FEXServerClient.h" #include "Common/FileMappingBaseAddress.h" @@ -171,6 +172,10 @@ void SyscallHandler::InvalidateGuestCodeRange(FEXCore::Core::InternalThreadState InvalidateCodeRangeIfNecessary(Thread, Start, Length); } +static FEXCore::ExecutableFileSectionInfo BuildSectionInfo(const VMATracking::MappedResource& Resource, uint64_t Base, uint64_t Size) { + return FEXCore::ExecutableFileSectionInfo {*Resource.MappedFile, Resource.FirstVMA->Base, Base, Base + Size}; +} + std::optional SyscallHandler::LookupExecutableFileSection(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestAddr) { auto lk = FEXCore::GuardSignalDeferringSection(VMATracking.Mutex, &Thread); @@ -181,7 +186,7 @@ SyscallHandler::LookupExecutableFileSection(FEXCore::Core::InternalThreadState& } auto& [MappingBaseAddr, Entry] = *EntryIt; - return FEXCore::ExecutableFileSectionInfo {*Entry.Resource->MappedFile, Entry.Resource->FirstVMA->Base}; + return BuildSectionInfo(*Entry.Resource, MappingBaseAddr, Entry.Length); } FEXCore::HLE::ExecutableRangeInfo SyscallHandler::QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) { @@ -220,6 +225,8 @@ void* SyscallHandler::GuestMmap(bool Is64Bit, FEXCore::Core::InternalThreadState size_t Size = FEXCore::AlignUp(length, FEXCore::Utils::FEX_PAGE_SIZE); std::optional LateMetadata = std::nullopt; + std::optional CachedSection; + { // NOTE: Frontend calls this with a nullptr Thread during initialization, but // providing this code with a valid Thread object earlier would allow @@ -240,7 +247,7 @@ void* SyscallHandler::GuestMmap(bool Is64Bit, FEXCore::Core::InternalThreadState } } - LateMetadata = TrackMmap(Thread, Result, length, prot, flags, fd, offset); + LateMetadata = TrackMmap(Thread, Result, length, prot, flags, fd, offset, CachedSection); } InvalidateCodeRangeIfNecessary(Thread, Result, Size); @@ -250,6 +257,25 @@ void* SyscallHandler::GuestMmap(bool Is64Bit, FEXCore::Core::InternalThreadState CTX->AddForceTSOInformation(LateMetadata->VolatileValidRanges, std::move(LateMetadata->VolatileInstructions)); } + if (EnableCodeCaching && CachedSection) { + auto CacheFilename = fextl::fmt::format("{}cache/{}-{:016x}", FEX::Config::GetCacheDirectory(), + FEXCore::CodeMap::GetBaseFilename(CachedSection->FileInfo, false), CodeCacheConfigId); + int FD = open(CacheFilename.c_str(), O_RDONLY); + struct stat buf; + if (FD != -1 && (fstat(FD, &buf) == 0)) { + auto CacheFileSize = buf.st_size; + auto MappedCache = (std::byte*)FEXCore::Allocator::mmap(nullptr, CacheFileSize, PROT_READ, MAP_PRIVATE, FD, 0); + LOGMAN_THROW_A_FMT(MappedCache, "Failed to map code cache into memory"); + if (!CTX->GetCodeCache().LoadData(*Thread, MappedCache, *CachedSection)) { + // TODO: Delete this cache file + } + FEXCore::Allocator::munmap(MappedCache, CacheFileSize); + } + if (FD != -1) { + close(FD); + } + } + return reinterpret_cast(Result); } @@ -418,8 +444,9 @@ uint64_t SyscallHandler::GuestShmdt(bool Is64Bit, FEXCore::Core::InternalThreadS } // MMan Tracking -std::optional SyscallHandler::TrackMmap( - FEXCore::Core::InternalThreadState* Thread, uint64_t addr, size_t length, int prot, int flags, int fd, off_t offset) { +std::optional +SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t addr, size_t length, int prot, int flags, int fd, + off_t offset, std::optional& CachedSection) { size_t Size = FEXCore::AlignUp(length, FEXCore::Utils::FEX_PAGE_SIZE); const auto ProtMapping = VMATracking::VMAProt::fromProt(prot); @@ -517,7 +544,18 @@ std::optional SyscallHandler: Resource->Iterator = Iter; } - VMATracking.TrackVMARange(CTX, Resource, addr, offset, Size, VMATracking::VMAFlags::fromFlags(flags), ProtMapping); + VMATracking.TrackVMARange(CTX, Resource, addr, offset, Size, VMATracking::VMAFlags::fromFlags(flags), VMATracking::VMAProt::fromProt(prot)); + + // Load code cache if present. + // FEXServer was requested to generate library caches on program launch. + if (EnableCodeCaching && Resource && Resource->MappedFile && VMATracking::VMAProt::fromProt(prot).Executable) { + if (Thread) { + CachedSection.emplace(BuildSectionInfo(*Resource, addr, Size)); + } else { + // Cache can't be loaded without a thread; skip this for now + } + } + return VolatileMetadata; } diff --git a/Source/Tools/LinuxEmulation/VDSO_Emulation.cpp b/Source/Tools/LinuxEmulation/VDSO_Emulation.cpp index 5ce71a56f..104f374ea 100644 --- a/Source/Tools/LinuxEmulation/VDSO_Emulation.cpp +++ b/Source/Tools/LinuxEmulation/VDSO_Emulation.cpp @@ -774,8 +774,9 @@ void LoadFEXGeneratedCode(bool Is64Bit, VDSOMapping* Mapping, FEX::HLE::SyscallH mprotect(Mapping->X86GeneratedCodePtr, Mapping->X86GeneratedCodeSize, PROT_READ | PROT_EXEC); { auto lk = FEXCore::GuardSignalDeferringSectionWithFallback(Handler->VMATracking.Mutex, nullptr); + std::optional IgnoredSection; FEX::HLE::_SyscallHandler->TrackMmap(nullptr, reinterpret_cast(Mapping->X86GeneratedCodePtr), Mapping->X86GeneratedCodeSize, - PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0, IgnoredSection); } FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(nullptr, reinterpret_cast(Mapping->X86GeneratedCodePtr),