Module: wine Branch: master Commit: 654c03d1317f4b22294109fee7ded2dbbe39645e URL: https://gitlab.winehq.org/wine/wine/-/commit/654c03d1317f4b22294109fee7ded2d...
Author: Alexandre Julliard julliard@winehq.org Date: Thu Feb 22 14:54:02 2024 +0100
ntdll: Handle leaf functions in RtlVirtualUnwind on x86-64.
---
dlls/ntdll/signal_x86_64.c | 32 ++++++++++---------------------- dlls/ntdll/tests/unwind.c | 40 ++++++++++++++++++++++++++++++++-------- dlls/ntdll/unwind.c | 12 +++++++++++- 3 files changed, 53 insertions(+), 31 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index 1303883ecfa..96a2da29201 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -100,22 +100,12 @@ static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEX dispatch->ImageBase = 0; dispatch->ScopeIndex = 0; dispatch->ControlPc = context->Rip; + dispatch->FunctionEntry = RtlLookupFunctionEntry( context->Rip, &dispatch->ImageBase, + dispatch->HistoryTable );
- /* first look for PE exception information */ - - if ((dispatch->FunctionEntry = RtlLookupFunctionEntry( context->Rip, &dispatch->ImageBase, - dispatch->HistoryTable ))) - { - dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, context->Rip, - dispatch->FunctionEntry, context, - &dispatch->HandlerData, &dispatch->EstablisherFrame, - NULL ); - return STATUS_SUCCESS; - } - - /* then look for host system exception information */ - - if (LdrFindEntryForAddress( (void *)context->Rip, &module ) || (module->Flags & LDR_WINE_INTERNAL)) + /* look for host system exception information */ + if (!dispatch->FunctionEntry && + (LdrFindEntryForAddress( (void *)context->Rip, &module ) || (module->Flags & LDR_WINE_INTERNAL))) { struct unwind_builtin_dll_params params = { type, dispatch, context };
@@ -127,14 +117,12 @@ static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEX } if (status != STATUS_UNSUCCESSFUL) return status; } + else WARN( "exception data not found for pc %p\n", (void *)context->Rip );
- /* no exception information, treat as a leaf function */ - - WARN( "exception data not found for pc %p\n", (void *)context->Rip ); - dispatch->EstablisherFrame = context->Rsp; - dispatch->LanguageHandler = NULL; - context->Rip = *(ULONG64 *)context->Rsp; - context->Rsp = context->Rsp + sizeof(ULONG64); + dispatch->LanguageHandler = RtlVirtualUnwind( type, dispatch->ImageBase, context->Rip, + dispatch->FunctionEntry, context, + &dispatch->HandlerData, &dispatch->EstablisherFrame, + NULL ); return STATUS_SUCCESS; }
diff --git a/dlls/ntdll/tests/unwind.c b/dlls/ntdll/tests/unwind.c index 458c368bcc7..a68bda6c748 100644 --- a/dlls/ntdll/tests/unwind.c +++ b/dlls/ntdll/tests/unwind.c @@ -2632,15 +2632,17 @@ static void call_virtual_unwind_x86( int testnum, const struct unwind_test_x86 * UINT i, j, k, broken_k; ULONG64 fake_stack[256]; ULONG64 frame, orig_rip, orig_rbp, unset_reg; - UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8; void *expected_handler, *broken_handler;
memcpy( (char *)code_mem + code_offset, test->function, test->function_size ); - memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size ); - - runtime_func.BeginAddress = code_offset; - runtime_func.EndAddress = code_offset + test->function_size; - runtime_func.UnwindData = unwind_offset; + if (test->unwind_info) + { + UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8; + memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size ); + runtime_func.BeginAddress = code_offset; + runtime_func.EndAddress = code_offset + test->function_size; + runtime_func.UnwindData = unwind_offset; + }
trace( "code: %p stack: %p\n", code_mem, fake_stack );
@@ -2660,8 +2662,10 @@ static void call_virtual_unwind_x86( int testnum, const struct unwind_test_x86 * (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
data = (void *)0xdeadbeef; + if (!test->unwind_info) fake_stack[0] = 0x1234; handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip, - &runtime_func, &context, &data, &frame, &ctx_ptr ); + test->unwind_info ? &runtime_func : NULL, + &context, &data, &frame, &ctx_ptr );
expected_handler = test->results[i].handler ? (char *)code_mem + 0x200 : NULL; broken_handler = test->broken_results && test->broken_results[i].handler ? (char *)code_mem + 0x200 : NULL; @@ -2671,7 +2675,8 @@ static void call_virtual_unwind_x86( int testnum, const struct unwind_test_x86 * if (handler) ok( *(DWORD *)data == 0x08070605, "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data ); else - ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data ); + ok( data == (test->unwind_info ? (void *)0xdeadbeef : NULL), + "%u/%u: handler data set to %p\n", testnum, i, data );
ok( context.Rip == test->results[i].rip || broken( test->broken_results && context.Rip == test->broken_results[i].rip ), @@ -2960,6 +2965,22 @@ static void test_virtual_unwind_x86(void) { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }}, };
+#if 0 + static const BYTE function_5[] = + { + 0x90, /* 00: nop */ + 0x90, /* 01: nop */ + 0xc3 /* 02: ret */ + }; + + static const struct results_x86 results_5[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x00, FALSE, 0x1234, 0x000, { {rsp,0x08}, {-1,-1} }}, + { 0x02, 0x00, FALSE, 0x1234, 0x000, { {rsp,0x08}, {-1,-1} }}, + }; +#endif + static const struct unwind_test_x86 tests[] = { { function_0, sizeof(function_0), unwind_info_0, results_0, ARRAY_SIZE(results_0), broken_results_0 }, @@ -2969,6 +2990,9 @@ static void test_virtual_unwind_x86(void)
/* Broken before Win10 1809. */ { function_4, sizeof(function_4), unwind_info_4, results_4, ARRAY_SIZE(results_4), broken_results_4 }, +#if 0 /* crashes before Win10 21H2 */ + { function_5, sizeof(function_5), NULL, results_5, ARRAY_SIZE(results_5) }, +#endif }; unsigned int i;
diff --git a/dlls/ntdll/unwind.c b/dlls/ntdll/unwind.c index ff68303e553..21a1669f659 100644 --- a/dlls/ntdll/unwind.c +++ b/dlls/ntdll/unwind.c @@ -1838,9 +1838,19 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc, #endif
TRACE( "type %lx rip %I64x rsp %I64x\n", type, pc, context->Rsp ); - if (TRACE_ON(unwind)) dump_unwind_info( base, function );
frame = *frame_ret = context->Rsp; + + if (!function) /* leaf function */ + { + context->Rip = *(ULONG64 *)context->Rsp; + context->Rsp += sizeof(ULONG64); + *data = NULL; + return NULL; + } + + if (TRACE_ON(unwind)) dump_unwind_info( base, function ); + for (;;) { info = (struct UNWIND_INFO *)((char *)base + function->UnwindData);