On Sat Jul 5 00:27:21 2025 +0000, Yuxuan Shui wrote:
Thanks. So this is the loop I am using to unwind:
for (frames = 0; frames < 16; frames++) { func = RtlLookupFunctionEntry(context.Rip, &base, &table); if (RtlVirtualUnwind(UNW_FLAG_NHANDLER, base, context.Rip, func, &context, &data, &frame, NULL)) break; if (!context.Rip) break; if (!frame) break; WINE_ERR("%d: %p\n", frames, (void *)context.Rip); if (context.Rip == (DWORD64)unwind_target || frame == (DWORD64)target_frame) { WINE_ERR("found\n"); TRACE_CONTEXT(&context); break; } } if (frames >= 16) WINE_ERR("not found\n");
and looks like nothing except rbp is saved. here's a register print out:
during unwind: rip=00007ff684951a25 rsp=000000ce835ff8b0 rbp=000000ce835ffa20 eflags=00000202 rax=00007ffc18854a00 rbx=0000000000000000 rcx=000000ce835ff080 rdx=0000000000000082 rsi=0000000000000000 rdi=0000000000000000 r8=0000000000000000 r9=0000000000000000 r10=0000000000000000 r11=000000ce835ff598 r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 mxcsr=00001f80 before unwind rip=00007ff684951a16 rsp=000000ce835ff8b0 rbp=000000ce835ffa20 eflags=00000202 rax=00007ffc18854a00 rbx=00000000deadbeaf rcx=00007ff684957040 rdx=00000000deadbeaf rsi=00000000deadbeaf rdi=00000000deadbeaf r8=00000000deadbeaf r9=00000000deadbeaf r10=00000000deadbeaf r11=00000000deadbeaf r12=00000000deadbeaf r13=00000000deadbeaf r14=00000000deadbeaf r15=0000000000000000 mxcsr=00001f80
if i make wine **save all non-volatile registers**, this is the output i got:
during unwind: rip=0000000140001a25 rsp=00007ffffe2ffe00 rbp=00007ffffe2fff70 eflags=00000202 rax=00006ffffd2bf91c rbx=00000000deadbeaf rcx=00007ffffe2ff0b0 rdx=0000000000000082 rsi=00007ffffe0ff138 rdi=00007ffffe2ffd20 r8=0000000000000000 r9=0000000000000000 r10=00000001400010c1 r11=0000000000000202 r12=00000000deadbeaf r13=00000000deadbeaf r14=00000000deadbeaf r15=0000000000000000 mxcsr=00001fa0
given that windows disallow unwinding through a user callback, it's not surprising they chose to not restore registers before calling user callback.
I'd say it is a bit unexpected, but that's what test are for. Probably a test which confirms that would be great to have with the patch. You can still avoid passing additional parameter in original patch and just get rbp from frame, also avoid moving that register back and forth and read / restore just once.