From 6c11d1d745af859cab682c3330bcf391f2521121 Mon Sep 17 00:00:00 2001 From: Martin Storsjo Date: Thu, 28 May 2020 11:14:44 +0300 Subject: [PATCH] ntdll: Implement RtlRestoreContext. Call the consolidate frame callback before resuming. Before calling the callback, fill in ExceptionInformation[10] with the equivalent of dispatch.NonVolatileRegisters. This fixes unwinding of MSVC C++ exceptions in a lot of cases. Signed-off-by: Martin Storsjo Signed-off-by: Alexandre Julliard --- .../api-ms-win-core-rtlsupport-l1-1-0.spec | 2 +- .../api-ms-win-core-rtlsupport-l1-2-0.spec | 2 +- dlls/ntdll/ntdll.spec | 2 +- dlls/ntdll/signal_arm64.c | 134 +++++++++++++++++- 4 files changed, 136 insertions(+), 4 deletions(-) diff --git a/dlls/api-ms-win-core-rtlsupport-l1-1-0/api-ms-win-core-rtlsupport-l1-1-0.spec b/dlls/api-ms-win-core-rtlsupport-l1-1-0/api-ms-win-core-rtlsupport-l1-1-0.spec index 0554127929b..ac166e2c541 100644 --- a/dlls/api-ms-win-core-rtlsupport-l1-1-0/api-ms-win-core-rtlsupport-l1-1-0.spec +++ b/dlls/api-ms-win-core-rtlsupport-l1-1-0/api-ms-win-core-rtlsupport-l1-1-0.spec @@ -9,7 +9,7 @@ @ stdcall -arch=arm,arm64,x86_64 RtlLookupFunctionEntry(long ptr ptr) ntdll.RtlLookupFunctionEntry @ stdcall RtlPcToFileHeader(ptr ptr) ntdll.RtlPcToFileHeader @ stdcall -norelay RtlRaiseException(ptr) ntdll.RtlRaiseException -@ cdecl -arch=x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext +@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext @ stdcall -norelay RtlUnwind(ptr ptr ptr ptr) ntdll.RtlUnwind @ stdcall -arch=arm64,x86_64 RtlUnwindEx(ptr ptr ptr ptr ptr ptr) ntdll.RtlUnwindEx @ stdcall -arch=arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) ntdll.RtlVirtualUnwind diff --git a/dlls/api-ms-win-core-rtlsupport-l1-2-0/api-ms-win-core-rtlsupport-l1-2-0.spec b/dlls/api-ms-win-core-rtlsupport-l1-2-0/api-ms-win-core-rtlsupport-l1-2-0.spec index 35f7dbba728..dd49448348d 100644 --- a/dlls/api-ms-win-core-rtlsupport-l1-2-0/api-ms-win-core-rtlsupport-l1-2-0.spec +++ b/dlls/api-ms-win-core-rtlsupport-l1-2-0/api-ms-win-core-rtlsupport-l1-2-0.spec @@ -10,7 +10,7 @@ @ stdcall -arch=arm,arm64,x86_64 RtlLookupFunctionEntry(long ptr ptr) ntdll.RtlLookupFunctionEntry @ stdcall RtlPcToFileHeader(ptr ptr) ntdll.RtlPcToFileHeader @ stdcall -norelay RtlRaiseException(ptr) ntdll.RtlRaiseException -@ cdecl -arch=x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext +@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) ntdll.RtlRestoreContext @ stdcall -norelay RtlUnwind(ptr ptr ptr ptr) ntdll.RtlUnwind @ stdcall -arch=arm64,x86_64 RtlUnwindEx(ptr ptr ptr ptr ptr ptr) ntdll.RtlUnwindEx @ stdcall -arch=arm64,x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) ntdll.RtlVirtualUnwind diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 6bd0a599a90..42532bd9f1c 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -913,7 +913,7 @@ @ stdcall RtlRemoveVectoredContinueHandler(ptr) @ stdcall RtlRemoveVectoredExceptionHandler(ptr) @ stdcall RtlResetRtlTranslations(ptr) -@ cdecl -arch=x86_64 RtlRestoreContext(ptr ptr) +@ cdecl -arch=arm64,x86_64 RtlRestoreContext(ptr ptr) @ stdcall RtlRestoreLastWin32Error(long) RtlSetLastWin32Error @ stub RtlRevertMemoryStream @ stub RtlRunDecodeUnicodeString diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c index cdffda7a12d..2af1e238cea 100644 --- a/dlls/ntdll/signal_arm64.c +++ b/dlls/ntdll/signal_arm64.c @@ -69,6 +69,29 @@ WINE_DECLARE_DEBUG_CHANNEL(relay); static pthread_key_t teb_key; +/* layering violation: the setjmp buffer is defined in msvcrt, but used by RtlUnwindEx */ +struct MSVCRT_JUMP_BUFFER +{ + unsigned __int64 Frame; + unsigned __int64 Reserved; + unsigned __int64 X19; + unsigned __int64 X20; + unsigned __int64 X21; + unsigned __int64 X22; + unsigned __int64 X23; + unsigned __int64 X24; + unsigned __int64 X25; + unsigned __int64 X26; + unsigned __int64 X27; + unsigned __int64 X28; + unsigned __int64 Fp; + unsigned __int64 Lr; + unsigned __int64 Sp; + unsigned long Fpcr; + unsigned long Fpsr; + double D[8]; +}; + /*********************************************************************** * signal context platform-specific definitions */ @@ -1773,6 +1796,115 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc, return handler; } +/********************************************************************** + * call_consolidate_callback + * + * Wrapper function to call a consolidate callback from a fake frame. + * If the callback executes RtlUnwindEx (like for example done in C++ handlers), + * we have to skip all frames which were already processed. To do that we + * trick the unwinding functions into thinking the call came from somewhere + * else. All CFI instructions are either DW_CFA_def_cfa_expression or + * DW_CFA_expression, and the expressions have the following format: + * + * DW_OP_breg29; sleb128 0x10 | Load x29 + 0x10 + * DW_OP_deref | Get *(x29 + 0x10) == context + * DW_OP_plus_uconst; uleb128 | Add offset to get struct member + * [DW_OP_deref] | Dereference, only for CFA + */ +extern void * WINAPI call_consolidate_callback( CONTEXT *context, + void *(CALLBACK *callback)(EXCEPTION_RECORD *), + EXCEPTION_RECORD *rec, + TEB *teb ); +__ASM_GLOBAL_FUNC( call_consolidate_callback, + "stp x29, x30, [sp, #-0x20]!\n\t" + __ASM_CFI(".cfi_def_cfa_offset 32\n\t") + __ASM_CFI(".cfi_offset 29, -32\n\t") + __ASM_CFI(".cfi_offset 30, -24\n\t") + "mov x29, sp\n\t" + __ASM_CFI(".cfi_def_cfa_register 29\n\t") + "str x0, [sp, 0x10]\n\t" + __ASM_CFI(".cfi_remember_state\n\t") + __ASM_CFI(".cfi_escape 0x0f,0x07,0x8d,0x10,0x06,0x23,0x80,0x02,0x06\n\t") /* CFA */ + __ASM_CFI(".cfi_escape 0x10,0x13,0x06,0x8d,0x10,0x06,0x23,0xa0,0x01\n\t") /* x19 */ + __ASM_CFI(".cfi_escape 0x10,0x14,0x06,0x8d,0x10,0x06,0x23,0xa8,0x01\n\t") /* x20 */ + __ASM_CFI(".cfi_escape 0x10,0x15,0x06,0x8d,0x10,0x06,0x23,0xb0,0x01\n\t") /* x21 */ + __ASM_CFI(".cfi_escape 0x10,0x16,0x06,0x8d,0x10,0x06,0x23,0xb8,0x01\n\t") /* x22 */ + __ASM_CFI(".cfi_escape 0x10,0x17,0x06,0x8d,0x10,0x06,0x23,0xc0,0x01\n\t") /* x23 */ + __ASM_CFI(".cfi_escape 0x10,0x18,0x06,0x8d,0x10,0x06,0x23,0xc8,0x01\n\t") /* x24 */ + __ASM_CFI(".cfi_escape 0x10,0x19,0x06,0x8d,0x10,0x06,0x23,0xd0,0x01\n\t") /* x25 */ + __ASM_CFI(".cfi_escape 0x10,0x1a,0x06,0x8d,0x10,0x06,0x23,0xd8,0x01\n\t") /* x26 */ + __ASM_CFI(".cfi_escape 0x10,0x1b,0x06,0x8d,0x10,0x06,0x23,0xe0,0x01\n\t") /* x27 */ + __ASM_CFI(".cfi_escape 0x10,0x1c,0x06,0x8d,0x10,0x06,0x23,0xe8,0x01\n\t") /* x28 */ + __ASM_CFI(".cfi_escape 0x10,0x1d,0x06,0x8d,0x10,0x06,0x23,0xf0,0x01\n\t") /* x29 */ + __ASM_CFI(".cfi_escape 0x10,0x1e,0x06,0x8d,0x10,0x06,0x23,0xf8,0x01\n\t") /* x30 */ + __ASM_CFI(".cfi_escape 0x10,0x48,0x06,0x8d,0x10,0x06,0x23,0x90,0x03\n\t") /* d8 */ + __ASM_CFI(".cfi_escape 0x10,0x49,0x06,0x8d,0x10,0x06,0x23,0xa0,0x03\n\t") /* d9 */ + __ASM_CFI(".cfi_escape 0x10,0x4a,0x06,0x8d,0x10,0x06,0x23,0xb0,0x03\n\t") /* d10 */ + __ASM_CFI(".cfi_escape 0x10,0x4b,0x06,0x8d,0x10,0x06,0x23,0xc0,0x03\n\t") /* d11 */ + __ASM_CFI(".cfi_escape 0x10,0x4c,0x06,0x8d,0x10,0x06,0x23,0xd0,0x03\n\t") /* d12 */ + __ASM_CFI(".cfi_escape 0x10,0x4d,0x06,0x8d,0x10,0x06,0x23,0xe0,0x03\n\t") /* d13 */ + __ASM_CFI(".cfi_escape 0x10,0x4e,0x06,0x8d,0x10,0x06,0x23,0xf0,0x03\n\t") /* d14 */ + __ASM_CFI(".cfi_escape 0x10,0x4f,0x06,0x8d,0x10,0x06,0x23,0x80,0x04\n\t") /* d15 */ + "mov x0, x2\n\t" + "mov x18, x3\n\t" + "blr x1\n\t" + __ASM_CFI(".cfi_restore_state\n\t") + "ldp x29, x30, [sp], #32\n\t" + __ASM_CFI(".cfi_restore 30\n\t") + __ASM_CFI(".cfi_restore 29\n\t") + __ASM_CFI(".cfi_def_cfa 31, 0\n\t") + "ret") + +/******************************************************************* + * RtlRestoreContext (NTDLL.@) + */ +void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec ) +{ + EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList; + + if (rec && rec->ExceptionCode == STATUS_LONGJUMP && rec->NumberParameters >= 1) + { + struct MSVCRT_JUMP_BUFFER *jmp = (struct MSVCRT_JUMP_BUFFER *)rec->ExceptionInformation[0]; + int i; + + context->u.s.X19 = jmp->X19; + context->u.s.X20 = jmp->X20; + context->u.s.X21 = jmp->X21; + context->u.s.X22 = jmp->X22; + context->u.s.X23 = jmp->X23; + context->u.s.X24 = jmp->X24; + context->u.s.X25 = jmp->X25; + context->u.s.X26 = jmp->X26; + context->u.s.X27 = jmp->X27; + context->u.s.X28 = jmp->X28; + context->u.s.Fp = jmp->Fp; + context->u.s.Lr = jmp->Lr; + context->Sp = jmp->Sp; + context->Fpcr = jmp->Fpcr; + context->Fpsr = jmp->Fpsr; + + for (i = 0; i < 8; i++) + context->V[8+i].D[0] = jmp->D[0]; + } + else if (rec && rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE && rec->NumberParameters >= 1) + { + PVOID (CALLBACK *consolidate)(EXCEPTION_RECORD *) = (void *)rec->ExceptionInformation[0]; + TRACE( "calling consolidate callback %p (rec=%p)\n", consolidate, rec ); + rec->ExceptionInformation[10] = (ULONG_PTR)&context->u.s.X19; + + context->Pc = (ULONG64)call_consolidate_callback( context, consolidate, rec, NtCurrentTeb() ); + } + + /* hack: remove no longer accessible TEB frames */ + while ((ULONG64)teb_frame < context->Sp) + { + TRACE( "removing TEB frame: %p\n", teb_frame ); + teb_frame = __wine_pop_frame( teb_frame ); + } + + TRACE( "returning to %lx stack %lx\n", context->Pc, context->Sp ); + set_cpu_context( context ); +} /******************************************************************* * RtlUnwindEx (NTDLL.@) @@ -1909,7 +2041,7 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec context->u.s.X0 = (ULONG64)retval; context->Pc = (ULONG64)target_ip; - set_cpu_context( context ); + RtlRestoreContext(context, rec); }