diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index fe1d27f57..e40600952 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -314,7 +314,7 @@ void HandleImageMap(uint64_t Address) { if (VolatileMetadata) { LoadImageVolatileMetadata(Address); } - InvalidationTracker->HandleImageMap(Address); + InvalidationTracker->HandleImageMap(ModuleName, Address); } } // namespace @@ -716,7 +716,7 @@ bool ResetToConsistentStateImpl(EXCEPTION_RECORD* Exception, CONTEXT* GuestConte } std::scoped_lock Lock(ThreadCreationMutex); - if (InvalidationTracker && InvalidationTracker->HandleRWXAccessViolation(FaultAddress)) { + if (InvalidationTracker && InvalidationTracker->HandleRWXAccessViolation(Thread, NativeContext->Pc, FaultAddress)) { FEXCORE_PROFILE_INSTANT_INCREMENT(Thread, AccumulatedSMCCount, 1); if (CTX->IsAddressInCodeBuffer(Thread, NativeContext->Pc) && !CTX->IsCurrentBlockSingleInst(CPUArea.ThreadState()) && CTX->IsAddressInCurrentBlock(Thread, FaultAddress & FEXCore::Utils::FEX_PAGE_MASK, FEXCore::Utils::FEX_PAGE_SIZE)) { diff --git a/Source/Windows/Common/InvalidationTracker.cpp b/Source/Windows/Common/InvalidationTracker.cpp index d3e6f3c5c..4a3477aa2 100644 --- a/Source/Windows/Common/InvalidationTracker.cpp +++ b/Source/Windows/Common/InvalidationTracker.cpp @@ -64,23 +64,34 @@ void InvalidationTracker::HandleMemoryProtectionNotification(uint64_t Address, u } } -void InvalidationTracker::HandleImageMap(uint64_t Address) { +void InvalidationTracker::HandleImageMap(std::string_view Name, uint64_t Address) { auto* Nt = RtlImageNtHeader(reinterpret_cast(Address)); auto* SectionsBegin = IMAGE_FIRST_SECTION(Nt); auto* SectionsEnd = SectionsBegin + Nt->FileHeader.NumberOfSections; + uint64_t LastExecutableSectionEnd = 0; for (auto* Section = SectionsBegin; Section != SectionsEnd; Section++) { if (Section->Characteristics & IMAGE_SCN_MEM_EXECUTE) { std::unique_lock Lock(IntervalsLock); uint64_t SectionBase = Address + Section->VirtualAddress; - XIntervals.Insert({SectionBase, SectionBase + Section->Misc.VirtualSize}); + uint64_t SectionEnd = SectionBase + Section->Misc.VirtualSize; + XIntervals.Insert({SectionBase, SectionEnd}); + LastExecutableSectionEnd = std::max(LastExecutableSectionEnd, SectionEnd); if (Section->Characteristics & IMAGE_SCN_MEM_WRITE) { LogMan::Msg::DFmt("Add image SMC interval: {:X} - {:X}", SectionBase, SectionBase + Section->Misc.VirtualSize); RWXIntervals.Insert({SectionBase, SectionBase + Section->Misc.VirtualSize}); } } } + + FEX_CONFIG_OPT(MonoHacks, MONOHACKS); + if (MonoHacks && (Name == "mono-2.0-bdwgc.dll" || Name == "mono.dll")) { + CTX.MarkMonoDetected(); + MonoBackpatcherDetectionPending = true; + MonoBase = Address; + MonoEnd = LastExecutableSectionEnd; + } } InvalidationTracker::InvalidateContainingSectionResult InvalidationTracker::InvalidateContainingSection(uint64_t Address, bool Free) { @@ -162,7 +173,7 @@ void InvalidationTracker::ReprotectRWXIntervals(uint64_t Address, uint64_t Size) } while (Address < End); } -bool InvalidationTracker::HandleRWXAccessViolation(uint64_t FaultAddress) { +bool InvalidationTracker::HandleRWXAccessViolation(FEXCore::Core::InternalThreadState* Thread, uint64_t HostPc, uint64_t FaultAddress) { const bool NeedsInvalidate = [&](uint64_t Address) { std::shared_lock Lock(IntervalsLock); const bool Enclosed = RWXIntervals.Query(Address).Enclosed; @@ -179,12 +190,15 @@ bool InvalidationTracker::HandleRWXAccessViolation(uint64_t FaultAddress) { }(FaultAddress); if (NeedsInvalidate) { - // IntervalsLock cannot be held during invalidation - std::scoped_lock Lock(CTX.GetCodeInvalidationMutex()); - FEXCore::Context::InvalidatedEntryAccumulator Accumulator; - for (auto Thread : Threads) { - CTX.InvalidateGuestCodeRange(Thread.second, Accumulator, FaultAddress & FEXCore::Utils::FEX_PAGE_MASK, FEXCore::Utils::FEX_PAGE_SIZE); + { + // IntervalsLock cannot be held during invalidation + std::scoped_lock Lock(CTX.GetCodeInvalidationMutex()); + FEXCore::Context::InvalidatedEntryAccumulator Accumulator; + for (auto Thread : Threads) { + CTX.InvalidateGuestCodeRange(Thread.second, Accumulator, FaultAddress & FEXCore::Utils::FEX_PAGE_MASK, FEXCore::Utils::FEX_PAGE_SIZE); + } } + DetectMonoBackpatcherBlock(Thread, HostPc); return true; } return false; @@ -205,6 +219,35 @@ FEXCore::HLE::ExecutableRangeInfo InvalidationTracker::QueryExecutableRange(uint return {XResult.Interval.Offset, XResult.Interval.End - XResult.Interval.Offset, false}; } +void InvalidationTracker::DetectMonoBackpatcherBlock(FEXCore::Core::InternalThreadState* Thread, uint64_t HostPc) { + if (!MonoBackpatcherDetectionPending) { + return; + } + + if (!CTX.IsAddressInCodeBuffer(Thread, HostPc)) { + return; + } + + uint64_t RIP = CTX.RestoreRIPFromHostPC(Thread, HostPc); + if (!RIP || RIP < MonoBase || RIP >= MonoEnd) { + return; + } + + static constexpr uint8_t XChgOp = 0x87; + if (*reinterpret_cast(RIP) != XChgOp && *reinterpret_cast(RIP + 1) != XChgOp) { + return; + } + + uint64_t BlockEntry = CTX.GetGuestBlockEntry(Thread); + LogMan::Msg::DFmt("Detected mono backpatcher at: {:X}", BlockEntry); + DisableSMCDetection(); + { + std::scoped_lock CodeLock(CTX.GetCodeInvalidationMutex()); + CTX.MarkMonoBackpatcherBlock(BlockEntry); + } + InvalidateAlignedInterval(BlockEntry, FEXCore::Utils::FEX_PAGE_SIZE, false); +} + void InvalidationTracker::DisableSMCDetection() { std::unique_lock Lock(IntervalsLock); SMCDetectionDisabled = true; diff --git a/Source/Windows/Common/InvalidationTracker.h b/Source/Windows/Common/InvalidationTracker.h index 44bba0d47..39fc382a7 100644 --- a/Source/Windows/Common/InvalidationTracker.h +++ b/Source/Windows/Common/InvalidationTracker.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace FEXCore::Core { struct InternalThreadState; @@ -22,7 +23,7 @@ class InvalidationTracker { public: InvalidationTracker(FEXCore::Context::Context& CTX, const std::unordered_map& Threads); void HandleMemoryProtectionNotification(uint64_t Address, uint64_t Size, ULONG Prot); - void HandleImageMap(uint64_t Address); + void HandleImageMap(std::string_view Name, uint64_t Address); struct InvalidateContainingSectionResult { uint64_t SectionStart; uint64_t SectionSize; @@ -30,10 +31,11 @@ public: InvalidateContainingSectionResult InvalidateContainingSection(uint64_t Address, bool Free); void InvalidateAlignedInterval(uint64_t Address, uint64_t Size, bool Free); void ReprotectRWXIntervals(uint64_t Address, uint64_t Size); - bool HandleRWXAccessViolation(uint64_t FaultAddress); + bool HandleRWXAccessViolation(FEXCore::Core::InternalThreadState* Thread, uint64_t HostPC, uint64_t FaultAddress); FEXCore::HLE::ExecutableRangeInfo QueryExecutableRange(uint64_t Address); private: + void DetectMonoBackpatcherBlock(FEXCore::Core::InternalThreadState* Thread, uint64_t HostPC); void DisableSMCDetection(); FEXCore::IntervalList XIntervals; @@ -42,5 +44,9 @@ private: FEXCore::Context::Context& CTX; const std::unordered_map& Threads; bool SMCDetectionDisabled {false}; // Protected by IntervalsLock + + bool MonoBackpatcherDetectionPending {false}; + uint64_t MonoBase {0}; + uint64_t MonoEnd {0}; }; } // namespace FEX::Windows diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index 8e84194a9..bf7debaf8 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -161,7 +161,7 @@ bool IsAddressInJit(uint64_t Address) { void HandleImageMap(uint64_t Address) { fextl::string ModuleName = FEX::Windows::GetSectionFilePath(Address); LogMan::Msg::DFmt("Load module {}: {:X}", ModuleName, Address); - InvalidationTracker->HandleImageMap(Address); + InvalidationTracker->HandleImageMap(ModuleName, Address); } } // namespace @@ -799,7 +799,7 @@ bool BTCpuResetToConsistentStateImpl(EXCEPTION_POINTERS* Ptrs) { if (Thread) { std::scoped_lock Lock(ThreadCreationMutex); FEXCORE_PROFILE_INSTANT_INCREMENT(Thread, AccumulatedSMCCount, 1); - if (InvalidationTracker->HandleRWXAccessViolation(FaultAddress)) { + if (InvalidationTracker->HandleRWXAccessViolation(Thread, Context->Pc, FaultAddress)) { if (CTX->IsAddressInCodeBuffer(Thread, Context->Pc) && !CTX->IsCurrentBlockSingleInst(Thread) && CTX->IsAddressInCurrentBlock(Thread, FaultAddress & FEXCore::Utils::FEX_PAGE_MASK, FEXCore::Utils::FEX_PAGE_SIZE)) { Context::ReconstructThreadState(Context);