Repository navigation
ntdll iOS: fill missing control registers in a cross-thread GetThread… - #160
Merged
willfaust merged 1 commit intoOct 4, 2026
Merged
Conversation
…Context ## Problem Ghost of Tsushima froze on a loading screen. The log showed `RtlpWaitForCriticalSection ... blocked by <tid>`: the blocking thread was calling `GetThreadContext` on another thread thousands of times, every answer `rip=0 rsp=0 flags=00100002` (no `CONTEXT_CONTROL`), while it held a critical section the main thread was waiting for. ## Cause For a cross-thread `NtGetContextThread` the server sometimes has no native capture of the target (`have_native = 0`) and answers with the integer registers only. The ARM64EC wrapper then hands the x64 caller a context with `Rip = Rsp = 0` and no `CONTEXT_CONTROL`, and a caller that samples another thread's instruction pointer (profilers, job systems, watchdogs) keeps retrying. ## Change `build/ntdll-unix/signal_arm64_ios.c`: when the server reply lacks the control registers (or has `Pc = 0`) and the caller asked for them, `get_context_from_amd64_area` fills them -- and the integer registers, if requested -- from the target's last saved x64 state, `TEB->ChpeV2CpuAreaInfo->ContextAmd64` (written whenever the thread leaves emulated code). The values are returned in the ARM64EC register mapping, so the wrapper's `context_arm_to_x64()` turns them back into x64 registers; X23/X28 are cleared so the wrapper does not substitute an emulator frame's Pc/Sp. Only threads of the calling process are handled (the TEB pointer is read in this address space); nothing changes when the server's answer is complete. The first 8 uses log `[ctx] get handle=...`. ## Evidence Ghost of Tsushima, iPhone 17 Pro Max, iOS 27: the frozen loading screen's log had the blocked critical section and the stream of `rip=0 rsp=0` contexts described above; the change shipped in the fork after that log. ## Notes / risks - **Not confirmed on device.** No later log recorded the freeze again, but none recorded the `[ctx]` line either, so whether this was the fix is unproven. Treat it as a correctness improvement: a stale but real Rip/Rsp instead of zeros. - The saved state is the thread's last exit from emulated code, not a suspended snapshot; a running thread's registers may have moved on (Windows callers are expected to suspend first). - If `ContextAmd64` is empty or `Pc`/`Sp` are 0, the server's answer is returned unchanged (still without `CONTEXT_CONTROL`). - Split out of the D3D12 tessellation topic; unrelated to it. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0189oLHghpaYKLk4f786a6bc Signed-off-by: bahacan16 <190844990+bahacan16@users.noreply.github.com>
willfaust
added a commit
that referenced
this pull request
Oct 4, 2026
…ol-only #160 fills a cross-thread GetThreadContext's missing control registers from the target's saved x64 state. Now: - opt-in, MADEIRA_CTX_CPU_AREA=1 (madeira.cfg or a game's own config): the state is the thread's last exit from emulated code, and a caller that writes the context back (a .NET runtime redirecting a thread) would move the thread back there; - the TEB, CPU area and saved context are read with mach_vm_read_overwrite, so a target that exits meanwhile cannot fault the caller; - only Pc, Sp, Fp and Lr are filled; the integer registers stay the server's, and Cpsr is left alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…Context
Problem
Ghost of Tsushima froze on a loading screen. The log showed
RtlpWaitForCriticalSection ... blocked by <tid>: the blocking thread was callingGetThreadContexton another thread thousands of times, every answerrip=0 rsp=0 flags=00100002(noCONTEXT_CONTROL), while it held a critical section the main thread was waiting for.Cause
For a cross-thread
NtGetContextThreadthe server sometimes has no native capture of the target (have_native = 0) and answers with the integer registers only. The ARM64EC wrapper then hands the x64 caller a context withRip = Rsp = 0and noCONTEXT_CONTROL, and a caller that samples another thread's instruction pointer (profilers, job systems, watchdogs) keeps retrying.Change
build/ntdll-unix/signal_arm64_ios.c: when the server reply lacks the control registers (or hasPc = 0) and the caller asked for them,get_context_from_amd64_areafills them -- and the integer registers, if requested -- from the target's last saved x64 state,TEB->ChpeV2CpuAreaInfo->ContextAmd64(written whenever the thread leaves emulated code). The values are returned in the ARM64EC register mapping, so the wrapper'scontext_arm_to_x64()turns them back into x64 registers; X23/X28 are cleared so the wrapper does not substitute an emulator frame's Pc/Sp. Only threads of the calling process are handled (the TEB pointer is read in this address space); nothing changes when the server's answer is complete. The first 8 uses log[ctx] get handle=....Evidence
Ghost of Tsushima, iPhone 17 Pro Max, iOS 27: the frozen loading screen's log had the blocked critical section and the stream of
rip=0 rsp=0contexts described above; the change shipped in the fork after that log.Notes / risks
[ctx]line either, so whether this was the fix is unproven. Treat it as a correctness improvement: a stale but real Rip/Rsp instead of zeros.ContextAmd64is empty orPc/Spare 0, the server's answer is returned unchanged (still withoutCONTEXT_CONTROL).🤖 Generated with Claude Code
Claude-Session: https://claude.ai/code/session_0189oLHghpaYKLk4f786a6bc