diff --git a/External/FEXCore/Source/Interface/Config/Config.cpp b/External/FEXCore/Source/Interface/Config/Config.cpp index e800bddb6..65b6ce3bc 100644 --- a/External/FEXCore/Source/Interface/Config/Config.cpp +++ b/External/FEXCore/Source/Interface/Config/Config.cpp @@ -34,7 +34,7 @@ namespace FEXCore::Config { CTX->Config.TSOEnabled = Config != 0; break; case FEXCore::Config::CONFIG_SMC_CHECKS: - CTX->Config.SMCChecks = Config != 0; + CTX->Config.SMCChecks = static_cast(Config); break; case FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS: CTX->Config.ABILocalFlags = Config != 0; diff --git a/External/FEXCore/Source/Interface/Context/Context.h b/External/FEXCore/Source/Interface/Context/Context.h index 5b9808968..af314bd03 100644 --- a/External/FEXCore/Source/Interface/Context/Context.h +++ b/External/FEXCore/Source/Interface/Context/Context.h @@ -68,7 +68,7 @@ namespace FEXCore::Context { bool Is64BitMode {true}; bool TSOEnabled {true}; - bool SMCChecks {false}; + FEXCore::Config::ConfigSMCChecks SMCChecks {FEXCore::Config::CONFIG_SMC_MMAN}; bool ABILocalFlags {false}; bool ABINoPF {false}; @@ -209,7 +209,7 @@ namespace FEXCore::Context { void ExecutionThread(FEXCore::Core::InternalThreadState *Thread); void NotifyPause(); - void AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr); + void AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr, uint64_t Start, uint64_t Length); FEXCore::CodeLoader *LocalLoader{}; diff --git a/External/FEXCore/Source/Interface/Core/Core.cpp b/External/FEXCore/Source/Interface/Core/Core.cpp index 5d664fcf2..476f5669c 100644 --- a/External/FEXCore/Source/Interface/Core/Core.cpp +++ b/External/FEXCore/Source/Interface/Core/Core.cpp @@ -607,8 +607,8 @@ namespace FEXCore::Context { return Thread; } - void Context::AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr) { - Thread->LookupCache->AddBlockMapping(Address, Ptr); + void Context::AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr, uint64_t Start, uint64_t Length) { + Thread->LookupCache->AddBlockMapping(Address, Ptr, Start, Length); } void Context::ClearCodeCache(FEXCore::Core::InternalThreadState *Thread, bool AlsoClearIRCache) { @@ -670,7 +670,7 @@ namespace FEXCore::Context { TableInfo = Block.DecodedInstructions[i].TableInfo; DecodedInfo = &Block.DecodedInstructions[i]; - if (Config.SMCChecks) { + if (Config.SMCChecks == FEXCore::Config::CONFIG_SMC_FULL) { auto ExistingCodePtr = reinterpret_cast(Block.Entry + BlockInstructionsLength); auto CodeChanged = Thread->OpDispatcher->_ValidateCode(ExistingCodePtr[0], ExistingCodePtr[1], (uintptr_t)ExistingCodePtr - GuestRIP, DecodedInfo->InstSize); @@ -853,7 +853,8 @@ namespace FEXCore::Context { if (AOTEntry != Mod->end()) { // verify hash - auto hash = fasthash64((void*)(AOTEntry->second.start + file->second.Start - file->second.Offset), AOTEntry->second.len, 0); + auto MappedStart = AOTEntry->second.start + file->second.Start - file->second.Offset; + auto hash = fasthash64((void*)MappedStart, AOTEntry->second.len, 0); if (hash == AOTEntry->second.crc) { IRList = AOTEntry->second.IR; //LogMan::Msg::D("using %s + %lx -> %lx\n", file->second.fileid.c_str(), AOTEntry->first, GuestRIP); @@ -862,7 +863,7 @@ namespace FEXCore::Context { RAData = AOTEntry->second.RAData; DebugData = new FEXCore::Core::DebugData(); - StartAddr = AOTEntry->second.start; + StartAddr = MappedStart; Length = AOTEntry->second.len; GeneratedIR = true; @@ -1117,7 +1118,7 @@ namespace FEXCore::Context { --Thread->CompileBlockReentrantRefCount; // Insert to lookup cache - AddBlockMapping(Thread, GuestRIP, CodePtr); + AddBlockMapping(Thread, GuestRIP, CodePtr, StartAddr, Length); return (uintptr_t)CodePtr; @@ -1177,6 +1178,20 @@ namespace FEXCore::Context { SignalDelegation->UninstallTLSState(Thread); } + void FlushCodeRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length) { + + if (Thread->CTX->Config.SMCChecks == FEXCore::Config::CONFIG_SMC_MMAN) { + auto lower = Thread->LookupCache->CodePages.lower_bound(Start >> 12); + auto upper = Thread->LookupCache->CodePages.upper_bound((Start + Length) >> 12); + + for (auto it = lower; it != upper; it++) { + for (auto Address: it->second) + Context::RemoveCodeEntry(Thread, Address); + it->second.clear(); + } + } + } + void Context::RemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) { Thread->LocalIRCache.erase(GuestRIP); Thread->LookupCache->Erase(GuestRIP); @@ -1248,7 +1263,7 @@ namespace FEXCore::Context { auto fileid = base_filename + "-" + std::to_string(filename_hash) + "-"; // append optimization flags to the fileid - fileid += Config.SMCChecks ? "S" : "s"; + fileid += (Config.SMCChecks == FEXCore::Config::CONFIG_SMC_FULL) ? "S" : "s"; fileid += Config.TSOEnabled ? "T" : "t"; fileid += Config.ABILocalFlags ? "L" : "l"; fileid += Config.ABINoPF ? "p" : "P"; diff --git a/External/FEXCore/Source/Interface/Core/LookupCache.h b/External/FEXCore/Source/Interface/Core/LookupCache.h index 05780db19..835e64554 100644 --- a/External/FEXCore/Source/Interface/Core/LookupCache.h +++ b/External/FEXCore/Source/Interface/Core/LookupCache.h @@ -35,10 +35,16 @@ public: } } - void AddBlockMapping(uint64_t Address, void *HostCode) { + std::map> CodePages; + + void AddBlockMapping(uint64_t Address, void *HostCode, uint64_t Start, uint64_t Length) { auto InsertPoint = BlockList.emplace(Address, (uintptr_t)HostCode); LogMan::Throw::A(InsertPoint.second == true, "Dupplicate block mapping added"); + for (auto CurrentPage = Start >> 12, EndPage = (Start + Length) >> 12; CurrentPage <= EndPage; CurrentPage++) { + CodePages[CurrentPage].push_back(Address); + } + // no need to update L1 or L2, they will get updated on first lookup } @@ -197,6 +203,7 @@ private: } }; + std::map> BlockLinks; std::map BlockList; diff --git a/External/FEXCore/include/FEXCore/Config/Config.h b/External/FEXCore/include/FEXCore/Config/Config.h index 9bae04f5b..040be52b7 100644 --- a/External/FEXCore/include/FEXCore/Config/Config.h +++ b/External/FEXCore/include/FEXCore/Config/Config.h @@ -44,6 +44,12 @@ namespace FEXCore::Config { CONFIG_CUSTOM, }; + enum ConfigSMCChecks { + CONFIG_SMC_NONE, + CONFIG_SMC_MMAN, + CONFIG_SMC_FULL, + }; + void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, uint64_t Config); void SetConfig(FEXCore::Context::Context *CTX, ConfigOption Option, std::string const &Config); uint64_t GetConfig(FEXCore::Context::Context *CTX, ConfigOption Option); diff --git a/External/FEXCore/include/FEXCore/Core/Context.h b/External/FEXCore/include/FEXCore/Core/Context.h index b71f713be..d32e62848 100644 --- a/External/FEXCore/include/FEXCore/Core/Context.h +++ b/External/FEXCore/include/FEXCore/Core/Context.h @@ -238,4 +238,5 @@ namespace FEXCore::Context { void RemoveNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length); void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function(const std::string&)> CacheReader); bool WriteAOTIR(FEXCore::Context::Context *CTX, std::function(const std::string&)> CacheWriter); + void FlushCodeRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length); } diff --git a/Source/Common/ArgumentLoader.cpp b/Source/Common/ArgumentLoader.cpp index a7afa271c..5e6753a15 100644 --- a/Source/Common/ArgumentLoader.cpp +++ b/Source/Common/ArgumentLoader.cpp @@ -66,11 +66,11 @@ namespace FEX::ArgLoader { .help("Number of physical hardware threads to tell the process we have") .set_default(1); - CPUGroup.add_option("--smc-full-checks") + CPUGroup.add_option("--smc-checks") .dest("SMCChecks") - .action("store_true") - .help("Checks code for modification before execution. Slow.") - .set_default(false); + .choices({"none", "mman", "full"}) + .help("Checks code for modification before execution.\n\tnone: No checks\n\tmman: Invalidate on mmap, mprotect, munmap\n\tfull: Validate code before every run (slow)") + .set_default("mman"); CPUGroup.add_option("--unsafe-no-tso") .dest("TSOEnabled") @@ -229,8 +229,13 @@ namespace FEX::ArgLoader { } if (Options.is_set_by_user("SMCChecks")) { - bool SMCChecks = Options.get("SMCChecks"); - Set(FEXCore::Config::ConfigOption::CONFIG_SMC_CHECKS, std::to_string(SMCChecks)); + auto SMCChecks = Options["SMCChecks"]; + if (SMCChecks == "none") + Set(FEXCore::Config::ConfigOption::CONFIG_SMC_CHECKS, "0"); + else if (SMCChecks == "mman") + Set(FEXCore::Config::ConfigOption::CONFIG_SMC_CHECKS, "1"); + else if (SMCChecks == "full") + Set(FEXCore::Config::ConfigOption::CONFIG_SMC_CHECKS, "2"); } if (Options.is_set_by_user("AbiLocalFlags")) { bool AbiLocalFlags = Options.get("AbiLocalFlags"); diff --git a/Source/Tests/ELFLoader.cpp b/Source/Tests/ELFLoader.cpp index 35301692b..7ecaf811e 100644 --- a/Source/Tests/ELFLoader.cpp +++ b/Source/Tests/ELFLoader.cpp @@ -205,7 +205,7 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Config::Value OutputLog{FEXCore::Config::CONFIG_OUTPUTLOG, "stderr"}; FEXCore::Config::Value DumpIR{FEXCore::Config::CONFIG_DUMPIR, "no"}; FEXCore::Config::Value TSOEnabledConfig{FEXCore::Config::CONFIG_TSO_ENABLED, true}; - FEXCore::Config::Value SMCChecksConfig{FEXCore::Config::CONFIG_SMC_CHECKS, false}; + FEXCore::Config::Value SMCChecksConfig{FEXCore::Config::CONFIG_SMC_CHECKS, FEXCore::Config::CONFIG_SMC_MMAN}; FEXCore::Config::Value ABILocalFlags{FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS, false}; FEXCore::Config::Value AbiNoPF{FEXCore::Config::CONFIG_ABI_NO_PF, false}; FEXCore::Config::Value AOTIRCapture{FEXCore::Config::CONFIG_AOTIR_GENERATE, false}; diff --git a/Source/Tests/LinuxSyscalls/x32/Memory.cpp b/Source/Tests/LinuxSyscalls/x32/Memory.cpp index 1d9f62453..523951859 100644 --- a/Source/Tests/LinuxSyscalls/x32/Memory.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Memory.cpp @@ -24,10 +24,13 @@ namespace FEX::HLE::x32 { auto Result = (uint64_t)static_cast(FEX::HLE::_SyscallHandler)->GetAllocator()-> mmap(reinterpret_cast(addr), length, prot,flags, fd, offset); - if (Result != -1 && !(flags & MAP_ANONYMOUS)) { - auto filename = get_fdpath(fd); + if (Result != -1) { + if (!(flags & MAP_ANONYMOUS)) { + auto filename = get_fdpath(fd); - FEXCore::Context::AddNamedRegion(Thread->CTX, Result, length, offset, filename); + FEXCore::Context::AddNamedRegion(Thread->CTX, Result, length, offset, filename); + } + FEXCore::Context::FlushCodeRange(Thread, (uintptr_t)Result, length); } return Result; @@ -37,10 +40,13 @@ namespace FEX::HLE::x32 { auto Result = (uint64_t)static_cast(FEX::HLE::_SyscallHandler)->GetAllocator()-> mmap(reinterpret_cast(addr), length, prot,flags, fd, (uint64_t)pgoffset * 0x1000); - if (Result != -1 && !(flags & MAP_ANONYMOUS)) { - auto filename = get_fdpath(fd); + if (Result != -1) { + if (!(flags & MAP_ANONYMOUS)) { + auto filename = get_fdpath(fd); - FEXCore::Context::AddNamedRegion(Thread->CTX, Result, length, pgoffset * 0x1000, filename); + FEXCore::Context::AddNamedRegion(Thread->CTX, Result, length, pgoffset * 0x1000, filename); + } + FEXCore::Context::FlushCodeRange(Thread, (uintptr_t)Result, length); } return Result; @@ -51,12 +57,16 @@ namespace FEX::HLE::x32 { munmap(addr, length); if (Result != -1) { FEXCore::Context::RemoveNamedRegion(Thread->CTX, (uintptr_t)addr, length); + FEXCore::Context::FlushCodeRange(Thread, (uintptr_t)addr, length); } return Result; }); REGISTER_SYSCALL_IMPL_X32(mprotect, [](FEXCore::Core::InternalThreadState *Thread, void *addr, uint32_t len, int prot) -> uint64_t { uint64_t Result = ::mprotect(addr, len, prot); + if (Result != -1 && prot & PROT_EXEC) { + FEXCore::Context::FlushCodeRange(Thread, (uintptr_t)addr, len); + } SYSCALL_ERRNO(); }); diff --git a/Source/Tests/LinuxSyscalls/x64/Memory.cpp b/Source/Tests/LinuxSyscalls/x64/Memory.cpp index d5e19f061..fcd64627a 100644 --- a/Source/Tests/LinuxSyscalls/x64/Memory.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Memory.cpp @@ -25,6 +25,7 @@ namespace FEX::HLE::x64 { uint64_t Result = ::munmap(addr, length); if (Result != -1) { FEXCore::Context::RemoveNamedRegion(Thread->CTX, (uintptr_t)addr, length); + FEXCore::Context::FlushCodeRange(Thread, (uintptr_t)addr, length); } SYSCALL_ERRNO(); }); @@ -32,10 +33,13 @@ namespace FEX::HLE::x64 { REGISTER_SYSCALL_IMPL_X64(mmap, [](FEXCore::Core::InternalThreadState *Thread, void *addr, size_t length, int prot, int flags, int fd, off_t offset) -> uint64_t { static FEXCore::Config::Value AOTIRLoad(FEXCore::Config::CONFIG_AOTIR_LOAD, false); uint64_t Result = reinterpret_cast(::mmap(addr, length, prot, flags, fd, offset)); - if (Result != -1 && !(flags & MAP_ANONYMOUS)) { - auto filename = get_fdpath(fd); + if (Result != -1) { + if (!(flags & MAP_ANONYMOUS)) { + auto filename = get_fdpath(fd); - FEXCore::Context::AddNamedRegion(Thread->CTX, Result, length, offset, filename); + FEXCore::Context::AddNamedRegion(Thread->CTX, Result, length, offset, filename); + } + FEXCore::Context::FlushCodeRange(Thread, (uintptr_t)Result, length); } SYSCALL_ERRNO(); }); @@ -47,6 +51,9 @@ namespace FEX::HLE::x64 { REGISTER_SYSCALL_IMPL_X64(mprotect, [](FEXCore::Core::InternalThreadState *Thread, void *addr, size_t len, int prot) -> uint64_t { uint64_t Result = ::mprotect(addr, len, prot); + if (Result != -1 && prot & PROT_EXEC) { + FEXCore::Context::FlushCodeRange(Thread, (uintptr_t)addr, len); + } SYSCALL_ERRNO(); }); diff --git a/Source/Tests/TestHarnessRunner.cpp b/Source/Tests/TestHarnessRunner.cpp index 71d62b75b..f0936efe5 100644 --- a/Source/Tests/TestHarnessRunner.cpp +++ b/Source/Tests/TestHarnessRunner.cpp @@ -71,7 +71,7 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Config::Value OutputLog{FEXCore::Config::CONFIG_OUTPUTLOG, "stderr"}; FEXCore::Config::Value DumpIR{FEXCore::Config::CONFIG_DUMPIR, "no"}; FEXCore::Config::Value TSOEnabledConfig{FEXCore::Config::CONFIG_TSO_ENABLED, true}; - FEXCore::Config::Value SMCChecksConfig{FEXCore::Config::CONFIG_SMC_CHECKS, false}; + FEXCore::Config::Value SMCChecksConfig{FEXCore::Config::CONFIG_SMC_CHECKS, FEXCore::Config::CONFIG_SMC_MMAN}; FEXCore::Config::Value ABILocalFlags{FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS, false}; FEXCore::Config::Value AbiNoPF{FEXCore::Config::CONFIG_ABI_NO_PF, false}; diff --git a/Source/Tools/FEXConfig/Main.cpp b/Source/Tools/FEXConfig/Main.cpp index 330886df0..3e92ba989 100644 --- a/Source/Tools/FEXConfig/Main.cpp +++ b/Source/Tools/FEXConfig/Main.cpp @@ -301,11 +301,27 @@ namespace { ConfigChanged = true; } + ImGui::Text("SMC Checks: "); + int SMCChecks = FEXCore::Config::CONFIG_SMC_MMAN; + Value = LoadedConfig->Get(FEXCore::Config::ConfigOption::CONFIG_SMC_CHECKS); - bool SMCChecks = Value.has_value() && **Value == "1"; - if (ImGui::Checkbox("SMC Checks", &SMCChecks)) { - LoadedConfig->EraseSet(FEXCore::Config::ConfigOption::CONFIG_SMC_CHECKS, SMCChecks ? "1" : "0"); - ConfigChanged = true; + if (Value.has_value()) { + if (**Value == "0") { + SMCChecks = FEXCore::Config::CONFIG_SMC_NONE; + } else if (**Value == "1") { + SMCChecks = FEXCore::Config::CONFIG_SMC_MMAN; + } else if (**Value == "2") { + SMCChecks = FEXCore::Config::CONFIG_SMC_FULL; + } + } + + bool SMCChanged = false; + SMCChanged |= ImGui::RadioButton("None", &SMCChecks, FEXCore::Config::CONFIG_SMC_NONE); ImGui::SameLine(); + SMCChanged |= ImGui::RadioButton("MMan", &SMCChecks, FEXCore::Config::CONFIG_SMC_MMAN); ImGui::SameLine(); + SMCChanged |= ImGui::RadioButton("Full", &SMCChecks, FEXCore::Config::CONFIG_SMC_FULL); + + if (SMCChanged) { + LoadedConfig->EraseSet(FEXCore::Config::ConfigOption::CONFIG_SMC_CHECKS, std::to_string(SMCChecks)); } Value = LoadedConfig->Get(FEXCore::Config::ConfigOption::CONFIG_ABI_LOCAL_FLAGS); diff --git a/unittests/ASM/CMakeLists.txt b/unittests/ASM/CMakeLists.txt index a014711b5..9ffa98b43 100644 --- a/unittests/ASM/CMakeLists.txt +++ b/unittests/ASM/CMakeLists.txt @@ -74,7 +74,7 @@ foreach(ASM_SRC ${ASM_SOURCES}) string(REPLACE " " ";" ARGS_LIST ${ARGS}) if (TEST_NAME MATCHES "SelfModifyingCode") - list(APPEND ARGS_LIST "--smc-full-checks") + list(APPEND ARGS_LIST "--smc-checks=full") endif() add_test(NAME ${TEST_NAME}