ntdll: Handle leaf functions in RtlVirtualUnwind on x86-64.

This commit is contained in:
Alexandre Julliard 2024-02-22 14:54:02 +01:00
parent 18b0473fe9
commit 654c03d131
3 changed files with 53 additions and 31 deletions

View file

@ -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;
}

View file

@ -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;

View file

@ -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);