From 8d7b8729f106c653433cce6510d39ab98989db5b Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Tue, 12 Jul 2022 16:58:40 -0700 Subject: [PATCH 1/2] FEXCore: Support synchronizing RIP on block entry through config If an application is doing long jumps on exceptions then we need to ensure that RIP is synchronized at least to block entry for some amount of safety. Due to our block linking which doesn't ensure that RIP is synchronized on block entry, our exception handling wouldn't see a RIP change, thus not going down the path that we jump to Dispatcher loop top on RIP change. Burn a couple of instructions on block entry to ensure that RIP synchronized but only on config. --- .../FEXCore/Source/Interface/Config/Config.json.in | 10 ++++++++++ External/FEXCore/Source/Interface/Context/Context.h | 1 + External/FEXCore/Source/Interface/Core/Core.cpp | 11 +++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/External/FEXCore/Source/Interface/Config/Config.json.in b/External/FEXCore/Source/Interface/Config/Config.json.in index 466938439..9ebbaf42b 100644 --- a/External/FEXCore/Source/Interface/Config/Config.json.in +++ b/External/FEXCore/Source/Interface/Config/Config.json.in @@ -309,6 +309,16 @@ "Forces a process to stall out on initialization", "Useful for a process that keeps restarting and doesn't work" ] + }, + "x86dec_SynchronizeRIPOnAllBlocks": { + "Type": "bool", + "Default": "false", + "Desc": [ + "An application that uses try-catch or longjump extensively needs the ability to do context aware state flushing", + "In the case of FEX's block-linking, it won't always ensure that RIP is synchronized.", + "If an exception occurs and RIP isn't synchronized, then FEX's exception stack restore may not long jump as expected", + "Can be useful for Wine applications that rely on stack unwinding" + ] } }, "Misc": { diff --git a/External/FEXCore/Source/Interface/Context/Context.h b/External/FEXCore/Source/Interface/Context/Context.h index 766ae0953..e2027e397 100644 --- a/External/FEXCore/Source/Interface/Context/Context.h +++ b/External/FEXCore/Source/Interface/Context/Context.h @@ -113,6 +113,7 @@ namespace FEXCore::Context { FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO); FEX_CONFIG_OPT(CacheObjectCodeCompilation, CACHEOBJECTCODECOMPILATION); FEX_CONFIG_OPT(x87ReducedPrecision, X87REDUCEDPRECISION); + FEX_CONFIG_OPT(x86dec_SynchronizeRIPOnAllBlocks, X86DEC_SYNCHRONIZERIPONALLBLOCKS); } Config; FEXCore::HostFeatures HostFeatures; diff --git a/External/FEXCore/Source/Interface/Core/Core.cpp b/External/FEXCore/Source/Interface/Core/Core.cpp index 73a5fff76..405407761 100644 --- a/External/FEXCore/Source/Interface/Core/Core.cpp +++ b/External/FEXCore/Source/Interface/Core/Core.cpp @@ -762,6 +762,13 @@ namespace FEXCore::Context { // Reset any block-specific state Thread->OpDispatcher->StartNewBlock(); + if (Config.x86dec_SynchronizeRIPOnAllBlocks) { + // Ensure the RIP is synchronized to the context on block entry. + // In the case of block linking, the RIP may not have synchronized. + auto NewRIP = Thread->OpDispatcher->_EntrypointOffset(Block.Entry - GuestRIP, GPRSize); + Thread->OpDispatcher->_StoreContext(GPRSize, IR::GPRClass, NewRIP, offsetof(FEXCore::Core::CPUState, rip)); + } + uint64_t InstsInBlock = Block.NumInstructions; for (size_t i = 0; i < InstsInBlock; ++i) { @@ -775,7 +782,7 @@ namespace FEXCore::Context { if (ExtendedDebugInfo) { Thread->OpDispatcher->_GuestOpcode(Block.Entry + BlockInstructionsLength - GuestRIP); } - + if (Config.SMCChecks == FEXCore::Config::CONFIG_SMC_FULL) { auto ExistingCodePtr = reinterpret_cast(Block.Entry + BlockInstructionsLength); @@ -1292,4 +1299,4 @@ namespace FEXCore::Context { Thread->FrontendDecoder->SetExternalBranches(ExternalBranches); Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress); } -} \ No newline at end of file +} From c6a557abe2e669fe11cc4c988df4dbeed14285a8 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Tue, 12 Jul 2022 17:02:43 -0700 Subject: [PATCH 2/2] AppConfig: Add VC redistributable configs These require the previous commit's RIP block synchronization. This is due to the fact that these rely on exceptions to occur on try-catch blocks. With us ensuring RIP synchronization on block entry, this solves the problem of these crashing before doing anything. Wine 6.x didn't require this, but 7.x does. Fixes Proton Experimental hangs when it is installing the VC runtimes. --- Data/AppConfig/VC_redist.x64.exe.json | 5 +++++ Data/AppConfig/VC_redist.x86.exe.json | 5 +++++ Data/AppConfig/vcredist_x64.exe.json | 5 +++++ Data/AppConfig/vcredist_x86.exe.json | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 Data/AppConfig/VC_redist.x64.exe.json create mode 100644 Data/AppConfig/VC_redist.x86.exe.json create mode 100644 Data/AppConfig/vcredist_x64.exe.json create mode 100644 Data/AppConfig/vcredist_x86.exe.json diff --git a/Data/AppConfig/VC_redist.x64.exe.json b/Data/AppConfig/VC_redist.x64.exe.json new file mode 100644 index 000000000..95f5d5ee6 --- /dev/null +++ b/Data/AppConfig/VC_redist.x64.exe.json @@ -0,0 +1,5 @@ +{ + "Config": { + "x86dec_SynchronizeRIPOnAllBlocks": "1" + } +} diff --git a/Data/AppConfig/VC_redist.x86.exe.json b/Data/AppConfig/VC_redist.x86.exe.json new file mode 100644 index 000000000..95f5d5ee6 --- /dev/null +++ b/Data/AppConfig/VC_redist.x86.exe.json @@ -0,0 +1,5 @@ +{ + "Config": { + "x86dec_SynchronizeRIPOnAllBlocks": "1" + } +} diff --git a/Data/AppConfig/vcredist_x64.exe.json b/Data/AppConfig/vcredist_x64.exe.json new file mode 100644 index 000000000..95f5d5ee6 --- /dev/null +++ b/Data/AppConfig/vcredist_x64.exe.json @@ -0,0 +1,5 @@ +{ + "Config": { + "x86dec_SynchronizeRIPOnAllBlocks": "1" + } +} diff --git a/Data/AppConfig/vcredist_x86.exe.json b/Data/AppConfig/vcredist_x86.exe.json new file mode 100644 index 000000000..95f5d5ee6 --- /dev/null +++ b/Data/AppConfig/vcredist_x86.exe.json @@ -0,0 +1,5 @@ +{ + "Config": { + "x86dec_SynchronizeRIPOnAllBlocks": "1" + } +}