Files
FEX-Emu--FEX/Source/Windows/Common/InvalidationTracker.h
T
Billy Laws 8e9f593c39 Windows: Handle NtReadFile calls to RX protected RWX memory
Handling this safely requires blocking all compilation for the duration
of the read, as fault-based tracking doesn't work when wine's unix side
itself is the one performing the write into the RWX region (we cannot
catch unix faults).

Fixes a startup crash in Persona 5.
2026-01-05 02:34:00 +00:00

66 lines
2.6 KiB
C++

// SPDX-License-Identifier: MIT
#pragma once
#include <FEXCore/Utils/IntervalList.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <mutex>
#include <shared_mutex>
#include <unordered_map>
#include <string_view>
namespace FEXCore::Core {
struct InternalThreadState;
}
namespace FEXCore::Context {
class Context;
}
namespace FEX::Windows {
/**
* @brief Handles SMC and regular code invalidation
*/
class InvalidationTracker {
public:
InvalidationTracker(FEXCore::Context::Context& CTX, const std::unordered_map<DWORD, FEXCore::Core::InternalThreadState*>& Threads);
void HandleMemoryProtectionNotification(uint64_t Address, uint64_t Size, ULONG Prot);
void HandleImageMap(std::string_view Name, uint64_t Address);
struct InvalidateContainingSectionResult {
uint64_t SectionStart;
uint64_t SectionSize;
};
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(FEXCore::Core::InternalThreadState* Thread, uint64_t HostPC, uint64_t FaultAddress);
// Unprotects any RWX intervals in the input interval and invalidates code
// NOTE: CodeInvalidationMutex must be locked when calling this, and if true is returned, kept locked until the write ends.
bool BeginUntrackedWriteLocked(uint64_t Address, uint64_t Size);
FEXCore::HLE::ExecutableRangeInfo QueryExecutableRange(uint64_t Address);
private:
void DetectMonoBackpatcherBlock(FEXCore::Core::InternalThreadState* Thread, uint64_t HostPC);
void DisableSMCDetection();
void InvalidateIntervalInternal(uint64_t Address, uint64_t Size);
// NOTE: This assumed CodeInvalidationMutex is locked by the caller
void InvalidateIntervalInternalLocked(uint64_t Address, uint64_t Size);
// NOTE: If ForWriteLocked is true then this assumes CodeInvalidationMutex is locked by the caller,
// and any code in the range will be invalidated before protection as RWX, otherwise protects as RX if false.
bool ProtectRWXIntervalsInternal(uint64_t Address, uint64_t Size, bool ForWriteLocked);
FEXCore::IntervalList<uint64_t> XIntervals;
FEXCore::IntervalList<uint64_t> RWXIntervals;
std::shared_mutex IntervalsLock;
FEXCore::Context::Context& CTX;
const std::unordered_map<DWORD, FEXCore::Core::InternalThreadState*>& Threads;
bool SMCDetectionDisabled {false}; // Protected by IntervalsLock
bool MonoBackpatcherDetectionPending {false};
uint64_t MonoBase {0};
uint64_t MonoEnd {0};
};
} // namespace FEX::Windows