From e762e1b716f904f7f82820a146a346f2e296fc53 Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 10 Jun 2026 18:03:36 +0000 Subject: [PATCH 1/2] Flush instruction cache after (un)hooking so the ARM64 emulator re-JITs MinHook flushes only the 5 patched bytes; under the x86-on-ARM64 emulator (xtajit) that can leave a stale JIT translation of the modified prologue and trap with a non-continuable breakpoint. Flush the whole prologue and the trampoline on install, and the restored prologue on teardown, so the emulator re-translates them. No-op on native x86. Co-Authored-By: Claude Opus 4.8 (1M context) --- source/D3D9Hooks.cpp | 13 +++++++++++++ source/dll_main.cpp | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/source/D3D9Hooks.cpp b/source/D3D9Hooks.cpp index 9fe89c3..7b9cfda 100644 --- a/source/D3D9Hooks.cpp +++ b/source/D3D9Hooks.cpp @@ -32,6 +32,10 @@ namespace { // --- IDirect3D*Texture9 (IUnknown layout) --------------------------- constexpr int kResource_Release = 2; + // Bytes to flush from a hook target / trampoline so the x86-on-ARM64 emulator re-JITs + // it. Covers MinHook's max prologue patch and its 64-byte trampoline slot. + constexpr SIZE_T kFlushSpan = 64; + void** GetVTable(void* com_object) { return *static_cast(com_object); @@ -98,6 +102,13 @@ namespace { Warning("D3D9Hooks: MH_EnableHook failed for %p\n", target); return false; } + // Under the x86-on-ARM64 emulator, patched code is only re-translated when its + // instruction cache is flushed. MinHook flushes just the 5 patched bytes, which can + // leave a stale/guarded JIT block and trap; flush the whole prologue and the freshly + // built trampoline so the emulator re-JITs both. No-op on native x86. + const HANDLE proc = GetCurrentProcess(); + FlushInstructionCache(proc, target, kFlushSpan); + if (original && *original) FlushInstructionCache(proc, *original, kFlushSpan); g_hooked_targets.push_back(target); return true; } @@ -361,9 +372,11 @@ void RemoveAllD3D9Hooks() // Signal first: a detour reached after this (e.g. a surviving patch) falls through. g_unhooked = true; + const HANDLE proc = GetCurrentProcess(); for (void* target : g_hooked_targets) { MH_DisableHook(target); MH_RemoveHook(target); + FlushInstructionCache(proc, target, kFlushSpan); // re-JIT the restored prologue under emulation } g_hooked_targets.clear(); diff --git a/source/dll_main.cpp b/source/dll_main.cpp index d68bf3e..d359b72 100644 --- a/source/dll_main.cpp +++ b/source/dll_main.cpp @@ -252,6 +252,10 @@ void InitInstance(HINSTANCE hModule) if (GetProcAddress_fn) { MH_CreateHook(GetProcAddress_fn, OnGetProcAddress, (void**)&GetProcAddress_ret); MH_EnableHook(GetProcAddress_fn); + // Re-JIT the patched prologue + trampoline under the x86-on-ARM64 emulator (no-op native). + const HANDLE proc = GetCurrentProcess(); + FlushInstructionCache(proc, (void*)GetProcAddress_fn, 64); + if (GetProcAddress_ret) FlushInstructionCache(proc, (void*)GetProcAddress_ret, 64); } } From f420bbd50a445d96317e4c4b26878e4323b9db0e Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 10 Jun 2026 18:05:51 +0000 Subject: [PATCH 2/2] Bump version to 1.9.0.3 Co-Authored-By: Claude Opus 4.8 (1M context) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64f4f5f..c0c21eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ endif() set(VERSION_MAJOR 1) set(VERSION_MINOR 9) set(VERSION_PATCH 0) -set(VERSION_TWEAK 2) +set(VERSION_TWEAK 3) set(VERSION_RC "${CMAKE_CURRENT_BINARY_DIR}/version.rc") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in" "${VERSION_RC}" @ONLY)