Zig's standard library calls RtlEnterCriticalSection and
RtlLeaveCriticalSection on the PEB's FastPebLock before it reads the process
parameters, and stopped at the first call. ntdll's critical section functions
now forward to the guest RTL, which already serves kernel32's (the Rtl
forms answer an NTSTATUS), and the PEB names a free critical section at
FastPebLock (PEB+0x38, PEB+0x1c for 32-bit programs) next to the loader lock.
Assisted-by: Claude Code:claude-opus-5-5
AI-Scope: the RTL exports, the ntdll forwarding rule and the PEB field
(cherry picked from commit bd07c49ed7df6c38fa76c4a27597811eb9b76ea6)
Assisted-by: Claude Code:claude-opus-5-5
AI-Scope: Moved the shared user data clock updates to a single host thread that serves every page, so a host running several guest processes keeps one thread.
KUSER_SHARED_DATA held the tick count, interrupt time and system time of the
moment the process started. Go reads its monotonic and wall clocks straight
from the page, so time stood still in every Go program: restic's scrypt
calibration never saw time pass and asked for parameters too large to use,
and the scheduler never saw a goroutine run long.
The page is now shared with the host, which rewrites the clock fields every
millisecond, in the order KSYSTEM_TIME readers rely on (second high word,
low word, first high word). The tick count counts ticks of 15.625 ms, as the
TickCountMultiplier the page declares says. map_shared can place a view at a
given address for this.
Assisted-by: Claude Code:claude-opus-5-5
AI-Scope: the shared page, its clock updates and the fixed-address view
(cherry picked from commit 9238cbcfb1c5611df67b295b71085176f5ddbebc)