From c67ffb82a8064a60e5c3bbae3f2ed5e8c7f2b4f6 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 11 Mar 2026 16:40:07 +0100 Subject: [PATCH] Core: Support reporting blocks that are uncacheable due to unhandled ELF relocations --- FEXCore/Source/Interface/Context/Context.h | 1 + FEXCore/Source/Interface/Core/Core.cpp | 4 ++++ FEXCore/Source/Interface/Core/Frontend.cpp | 16 +++++++++++++++- FEXCore/Source/Interface/Core/Frontend.h | 1 + FEXCore/include/FEXCore/Core/CodeCache.h | 7 ++++++- FEXCore/include/FEXCore/Core/Context.h | 1 + Source/Tools/CommonTools/Linux/Utils/ELFParser.h | 6 ++++++ Source/Tools/FEXOfflineCompiler/Main.cpp | 6 ++++++ .../LinuxSyscalls/SyscallsSMCTracking.cpp | 12 ++++++++++++ 9 files changed, 52 insertions(+), 2 deletions(-) diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index 8b59a8e1a..fd6fe5b55 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -127,6 +127,7 @@ public: void ExecuteThread(FEXCore::Core::InternalThreadState* Thread) override; + bool CheckIfBlockIsCacheable(FEXCore::Core::InternalThreadState&, uint64_t GuestRIP, uint64_t MaxInst) override; void CompileRIP(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP) override; void CompileRIPCount(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP, uint64_t MaxInst) override; diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 549d0c8b9..25c342771 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -502,6 +502,10 @@ static void IRDumper(FEXCore::Core::InternalThreadState* Thread, IR::IREmitter* fextl::fmt::print(FD, "IR-ShouldDump-{} 0x{:x}:\n{}\n@@@@@\n", NewIR.PostRA() ? "post" : "pre", GuestRIP, out.str()); }; +bool ContextImpl::CheckIfBlockIsCacheable(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestRIP, uint64_t MaxInst) { + return Thread.FrontendDecoder->CheckIfCacheable(Thread, reinterpret_cast(GuestRIP), GuestRIP, MaxInst); +} + ContextImpl::GenerateIRResult ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP, bool ExtendedDebugInfo, uint64_t MaxInst) { FEXCORE_PROFILE_SCOPED("GenerateIR"); diff --git a/FEXCore/Source/Interface/Core/Frontend.cpp b/FEXCore/Source/Interface/Core/Frontend.cpp index b1614f245..a265ab22e 100644 --- a/FEXCore/Source/Interface/Core/Frontend.cpp +++ b/FEXCore/Source/Interface/Core/Frontend.cpp @@ -1348,6 +1348,13 @@ const uint8_t* Decoder::AdjustAddrForSpecialRegion(const uint8_t* _InstStream, u return _InstStream - EntryPoint + RIP; } +bool Decoder::CheckIfCacheable(FEXCore::Core::InternalThreadState& Thread, const uint8_t* InstStream, uint64_t PC, uint64_t MaxInst) { + DecodeInstructionsAtEntry(&Thread, InstStream, PC, MaxInst); + bool Uncacheable = HitBadRelocation; + DelayedDisownBuffer(); + return !Uncacheable; +} + void Decoder::DecodeInstructionsAtEntry(FEXCore::Core::InternalThreadState* Thread, const uint8_t* _InstStream, uint64_t PC, uint64_t MaxInst) { FEXCORE_PROFILE_SCOPED("DecodeInstructions"); BlockInfo.TotalInstructionCount = 0; @@ -1465,6 +1472,13 @@ void Decoder::DecodeInstructionsAtEntry(FEXCore::Core::InternalThreadState* Thre } BlockIt->BlockStatus = DecodeInstruction(OpAddress); + if (HitBadRelocation) { + BlockInfo.TotalInstructionCount = 0; + BlockInfo.Blocks = {*BlockIt}; + BlockInfo.EntryPoints.clear(); + BlockInfo.CodePages.clear(); + return; + } uint64_t OpEndAddress = OpAddress + DecodeInst->InstSize; DecodedMinAddress = std::min(DecodedMinAddress, OpAddress); @@ -1483,7 +1497,7 @@ void Decoder::DecodeInstructionsAtEntry(FEXCore::Core::InternalThreadState* Thre // Can not continue this block at all on invalid instruction if (BlockIt->BlockStatus != DecodedBlockStatus::SUCCESS) [[unlikely]] { - if (!EntryBlock) { + if (!EntryBlock && BlockIt->BlockStatus != DecodedBlockStatus::BAD_RELOCATION) { // In multiblock configurations, we can early terminate any non-entrypoint blocks with the expectation that this won't get hit. // Improves compile-times. // Just need to undo additions that this block decoding has caused. diff --git a/FEXCore/Source/Interface/Core/Frontend.h b/FEXCore/Source/Interface/Core/Frontend.h index 2771e6c2d..6fad347bd 100644 --- a/FEXCore/Source/Interface/Core/Frontend.h +++ b/FEXCore/Source/Interface/Core/Frontend.h @@ -54,6 +54,7 @@ public: }; Decoder(FEXCore::Core::InternalThreadState* Thread); + bool CheckIfCacheable(FEXCore::Core::InternalThreadState&, const uint8_t* InstStream, uint64_t PC, uint64_t MaxInst); void DecodeInstructionsAtEntry(FEXCore::Core::InternalThreadState* Thread, const uint8_t* InstStream, uint64_t PC, uint64_t MaxInst); const DecodedBlockInformation* GetDecodedBlockInfo() const { diff --git a/FEXCore/include/FEXCore/Core/CodeCache.h b/FEXCore/include/FEXCore/Core/CodeCache.h index 5e5caa140..21d408721 100644 --- a/FEXCore/include/FEXCore/Core/CodeCache.h +++ b/FEXCore/include/FEXCore/Core/CodeCache.h @@ -27,7 +27,12 @@ namespace HLE { struct SourcecodeMap; } // namespace HLE -enum class GuestRelocationType : uint32_t { Rel32, Rel64 }; +enum class GuestRelocationType : uint32_t { + Rel32, + Rel64, + // Skip blocks containing this relocation + Skip, +}; // Generic information associated with an executable file. struct ExecutableFileInfo { diff --git a/FEXCore/include/FEXCore/Core/Context.h b/FEXCore/include/FEXCore/Core/Context.h index be1b5c1cb..7b84f15f9 100644 --- a/FEXCore/include/FEXCore/Core/Context.h +++ b/FEXCore/include/FEXCore/Core/Context.h @@ -73,6 +73,7 @@ public: */ FEX_DEFAULT_VISIBILITY virtual void ExecuteThread(FEXCore::Core::InternalThreadState* Thread) = 0; + FEX_DEFAULT_VISIBILITY virtual bool CheckIfBlockIsCacheable(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestRIP, uint64_t MaxInst) = 0; FEX_DEFAULT_VISIBILITY virtual void CompileRIP(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP) = 0; FEX_DEFAULT_VISIBILITY virtual void CompileRIPCount(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP, uint64_t MaxInst) = 0; diff --git a/Source/Tools/CommonTools/Linux/Utils/ELFParser.h b/Source/Tools/CommonTools/Linux/Utils/ELFParser.h index 4fda8aaa7..978a2d5ea 100644 --- a/Source/Tools/CommonTools/Linux/Utils/ELFParser.h +++ b/Source/Tools/CommonTools/Linux/Utils/ELFParser.h @@ -461,6 +461,12 @@ private: static std::optional ClassifyRelocation32(uint32_t Type) { if (Type == R_386_RELATIVE || Type == R_386_32) { return FEXCore::GuestRelocationType::Rel32; + } else if (Type == R_386_PC32) { + // Currently not handled + return FEXCore::GuestRelocationType::Skip; + } else if (Type == R_386_TLS_TPOFF) { + // Currently not handled + return FEXCore::GuestRelocationType::Skip; } return std::nullopt; } diff --git a/Source/Tools/FEXOfflineCompiler/Main.cpp b/Source/Tools/FEXOfflineCompiler/Main.cpp index 8b720d9bb..f9290985d 100644 --- a/Source/Tools/FEXOfflineCompiler/Main.cpp +++ b/Source/Tools/FEXOfflineCompiler/Main.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -201,7 +202,12 @@ static std::optional GenerateSingleCache(FEXCore::ExecutableFileInf std::vector> LoaderMem; fmt::print(stderr, "Compiling code...\n"); + FEX_CONFIG_OPT(MaxInst, MAXINST); for (auto Addr : BlockList) { + if (!CTX->CheckIfBlockIsCacheable(*Thread, Addr + SyscallHandler->VAFileStart, MaxInst)) { + continue; + } + CTX->CompileRIP(Thread, Addr + SyscallHandler->VAFileStart); } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 5b5c7bf94..167c1cf5a 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -555,6 +555,18 @@ SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t a Resource->ProgramHeaders = std::move(ELFResult.ProgramHeaders); Resource->MappedFile->Relocations = std::move(ELFResult.Relocations); Resource->RequiresDelayedCacheLoad = ELFResult.HasCodeRelocations; + + // GuestRelocationType::Skip indicates to FEXOfflineCompiler that + // any blocks covered by the relocation may not be cached. + // At runtime, we can safely drop these relocations. + for (auto it = Resource->MappedFile->Relocations.begin(); it != Resource->MappedFile->Relocations.end();) { + if (it->second == FEXCore::GuestRelocationType::Skip) { + it = Resource->MappedFile->Relocations.erase(it); + } else { + ++it; + } + } + LOGMAN_THROW_A_FMT(Resource->ProgramHeaders.empty() || offset == 0, "Expected file offset 0 for the first mapping of an ELF " "file"); }