FEX-Emu / FEX

A fast usermode x86 and x86-64 emulator for Arm64 Linux
https://fex-emu.com
MIT License
2.09k stars 115 forks source link

Ubisoft Connect requires EFLAGS.TF support #3590

Open Sonicadvance1 opened 4 months ago

Sonicadvance1 commented 4 months ago

What a nightmare.

Sonicadvance1 commented 4 months ago

Implemented a hack for EFLAGS.TF. Signal handler entry needs to clear EFLAGS to a known good state when a TF happens (Maybe with other signals as well?) UPC then continues forward and tries to ptrace itself which results in a new error message.

Error at hooking API "LdrFindResource_U"
Dumping first 32 bytes:
<bytes>

Ptrace issue has been known since #1335

Sonicadvance1 commented 4 months ago
diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp
index 9db0e7c52..6e6784a13 100644
--- a/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp
+++ b/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp
@@ -732,6 +732,31 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, const FEXCore
          offsetof(FEXCore::Core::InternalThreadState, InterruptFaultPage) - offsetof(FEXCore::Core::InternalThreadState, BaseFrameState));
   }

+  ldrb(TMP1, STATE, offsetof(FEXCore::Core::CPUState, flags[X86State::RFLAG_TF_LOC]));
+
+  ARMEmitter::SingleUseForwardLabel TFCheck{};
+  cbz(ARMEmitter::Size::i64Bit, TMP1, &TFCheck);
+
+  // Do TFCheck.
+  ///< XXX: Correct?
+  Core::CpuStateFrame::SynchronousFaultDataStruct State = {
+    .FaultToTopAndGeneratedException = 1,
+    .Signal = SIGTRAP,
+    .TrapNo = 0,
+    .si_code = 0,
+    .err_code = 0,
+  };
+
+  uint64_t Constant {};
+  memcpy(&Constant, &State, sizeof(State));
+
+  LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r1, Constant);
+  str(ARMEmitter::XReg::x1, STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData));
+  ldr(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGTRAP));
+  br(TMP1);
+
+  Bind(&TFCheck);
+
   // LOGMAN_THROW_A_FMT(RAData->HasFullRA(), "Arm64 JIT only works with RA");

   SpillSlots = RAData->SpillSlots();
diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp
index 588fc77d5..eb37cf6d7 100644
--- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp
+++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator.cpp
@@ -412,7 +412,9 @@ void SignalDelegator::RestoreFrame_x64(FEXCore::Core::InternalThreadState* Threa

     Frame->State.rip = guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_RIP];
     // XXX: Full context setting
-    CTX->SetFlagsFromCompactedEFLAGS(Thread, guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_EFL]);
+    uint32_t eflags = guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_EFL];
+    eflags &= ~(1U << FEXCore::X86State::RFLAG_TF_LOC);
+    CTX->SetFlagsFromCompactedEFLAGS(Thread, eflags);

 #define COPY_REG(x) Frame->State.gregs[FEXCore::X86State::REG_##x] = guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_##x];
     COPY_REG(R8);
@@ -479,7 +481,9 @@ void SignalDelegator::RestoreFrame_ia32(FEXCore::Core::InternalThreadState* Thre
     ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));

     // XXX: Full context setting
-    CTX->SetFlagsFromCompactedEFLAGS(Thread, guest_uctx->sc.flags);
+    uint32_t eflags = guest_uctx->sc.flags;
+    eflags &= ~(1U << FEXCore::X86State::RFLAG_TF_LOC);
+    CTX->SetFlagsFromCompactedEFLAGS(Thread, eflags);

     Frame->State.rip = guest_uctx->sc.ip;
     Frame->State.cs_idx = guest_uctx->sc.cs;
@@ -558,7 +562,9 @@ void SignalDelegator::RestoreRTFrame_ia32(FEXCore::Core::InternalThreadState* Th
     ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));

     // XXX: Full context setting
-    CTX->SetFlagsFromCompactedEFLAGS(Thread, guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EFL]);
+    uint32_t eflags = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EFL];
+    eflags &= ~(1U << FEXCore::X86State::RFLAG_TF_LOC);
+    CTX->SetFlagsFromCompactedEFLAGS(Thread, eflags);

     Frame->State.rip = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EIP];
     Frame->State.cs_idx = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_CS];
@@ -1201,6 +1207,7 @@ bool SignalDelegator::HandleDispatcherGuestSignal(FEXCore::Core::InternalThreadS

   Frame->State.rip = reinterpret_cast<uint64_t>(GuestAction->sigaction_handler.sigaction);
   Frame->State.gregs[FEXCore::X86State::REG_RSP] = NewGuestSP;
+  Frame->State.flags[FEXCore::X86State::RFLAG_TF_LOC] = 0;

   // The guest starts its signal frame with a zero initialized FPU
   // Set that up now. Little bit costly but it's a requirement

For the hack.