Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntdll/signal_x86_64.c | 5 +- dlls/ntdll/tests/exception.c | 105 ++++++++++++++++++++++++++++------- 2 files changed, 88 insertions(+), 22 deletions(-)
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index 0cbcc177b2e..faf24b93b58 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -815,13 +815,16 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc, /* check if in prolog */ if (pc >= base + function->BeginAddress && pc < base + function->BeginAddress + info->prolog) { + TRACE("inside prolog.\n"); prolog_offset = pc - base - function->BeginAddress; } else { prolog_offset = ~0; - if (is_inside_epilog( (BYTE *)pc, base, function )) + /* Since Win10 1809 epilogue does not have a special treatment in case of zero opcode count. */ + if (info->count && is_inside_epilog( (BYTE *)pc, base, function )) { + TRACE("inside epilog.\n"); interpret_epilog( (BYTE *)pc, context, ctx_ptr ); *frame_ret = frame; return NULL; diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 843c4c3ad8e..1effffd421f 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -1937,6 +1937,7 @@ struct unwind_test const BYTE *unwind_info; const struct results *results; unsigned int nb_results; + const struct results *broken_results; };
enum regs @@ -1961,10 +1962,11 @@ static void call_virtual_unwind( int testnum, const struct unwind_test *test ) CONTEXT context; RUNTIME_FUNCTION runtime_func; KNONVOLATILE_CONTEXT_POINTERS ctx_ptr; - UINT i, j, k; + 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 ); @@ -1993,23 +1995,24 @@ static void call_virtual_unwind( int testnum, const struct unwind_test *test ) data = (void *)0xdeadbeef; handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip, &runtime_func, &context, &data, &frame, &ctx_ptr ); - if (test->results[i].handler) - { - ok( (char *)handler == (char *)code_mem + 0x200, - "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 ); - if (handler) ok( *(DWORD *)data == 0x08070605, - "%u/%u: wrong handler data %p\n", testnum, i, data ); - } + + 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; + + ok( handler == expected_handler || broken( test->broken_results && handler == broken_handler ), + "%u/%u: wrong handler %p/%p\n", testnum, i, handler, expected_handler ); + if (handler) + ok( *(DWORD *)data == 0x08070605, "%u/%u: wrong handler data %p\n", testnum, i, data ); else - { - ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler ); ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data ); - }
- ok( context.Rip == test->results[i].rip, "%u/%u: wrong rip %p/%x\n", - testnum, i, (void *)context.Rip, test->results[i].rip ); - ok( frame == (ULONG64)fake_stack + test->results[i].frame, "%u/%u: wrong frame %p/%p\n", - testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame ); + ok( context.Rip == test->results[i].rip + || broken( test->broken_results && context.Rip == test->broken_results[i].rip ), + "%u/%u: wrong rip %p/%x\n", testnum, i, (void *)context.Rip, test->results[i].rip ); + ok( frame == (ULONG64)fake_stack + test->results[i].frame + || broken( test->broken_results && frame == (ULONG64)fake_stack + test->broken_results[i].frame ), + "%u/%u: wrong frame %p/%p\n", + testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame );
for (j = 0; j < 16; j++) { @@ -2025,15 +2028,40 @@ static void call_virtual_unwind( int testnum, const struct unwind_test *test ) if (test->results[i].regs[k][0] == j) break; }
+ if (test->broken_results) + { + for (broken_k = 0; broken_k < nb_regs; broken_k++) + { + if (test->broken_results[i].regs[broken_k][0] == -1) + { + broken_k = nb_regs; + break; + } + if (test->broken_results[i].regs[broken_k][0] == j) + break; + } + } + else + { + broken_k = k; + } + if (j == rsp) /* rsp is special */ { - ULONG64 expected_rsp; + ULONG64 expected_rsp, broken_rsp;
ok( !ctx_ptr.IntegerContext[j], "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i ); expected_rsp = test->results[i].regs[k][1] < 0 ? -test->results[i].regs[k][1] : (ULONG64)fake_stack + test->results[i].regs[k][1]; - ok( context.Rsp == expected_rsp, + if (test->broken_results) + broken_rsp = test->broken_results[i].regs[k][1] < 0 + ? -test->broken_results[i].regs[k][1] + : (ULONG64)fake_stack + test->broken_results[i].regs[k][1]; + else + broken_rsp = expected_rsp; + + ok( context.Rsp == expected_rsp || broken( context.Rsp == broken_rsp ), "%u/%u: register rsp wrong %p/%p\n", testnum, i, (void *)context.Rsp, (void *)expected_rsp ); continue; @@ -2041,16 +2069,18 @@ static void call_virtual_unwind( int testnum, const struct unwind_test *test )
if (ctx_ptr.IntegerContext[j]) { - ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n", + ok( k < nb_regs || broken( broken_k < nb_regs ), "%u/%u: register %s should not be set to %lx\n", testnum, i, reg_names[j], *(&context.Rax + j) ); - if (k < nb_regs) - ok( *(&context.Rax + j) == test->results[i].regs[k][1], + ok( k == nb_regs || *(&context.Rax + j) == test->results[i].regs[k][1] + || broken( broken_k == nb_regs || *(&context.Rax + j) + == test->broken_results[i].regs[broken_k][1] ), "%u/%u: register %s wrong %p/%x\n", testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] ); } else { - ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] ); + ok( k == nb_regs || broken( broken_k == nb_regs ), "%u/%u: register %s should be set\n", + testnum, i, reg_names[j] ); if (j == rbp) ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n", testnum, i, (void *)context.Rbp ); @@ -2217,12 +2247,45 @@ static void test_virtual_unwind(void) { 0x01, 0x50, TRUE, 0x010, 0x000, { {rsp,-0x028}, {rbp,0x000}, {-1,-1} }}, };
+ static const BYTE function_4[] = + { + 0x55, /* 00: push %rbp */ + 0x5d, /* 01: pop %rbp */ + 0xc3 /* 02: ret */ + }; + + static const BYTE unwind_info_4[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x0, /* prolog size */ + 0, /* opcode count */ + 0, /* frame reg */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results results_4[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, TRUE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + }; + + static const struct results broken_results_4[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }}, + }; + static const struct unwind_test tests[] = { { function_0, sizeof(function_0), unwind_info_0, results_0, ARRAY_SIZE(results_0) }, { function_1, sizeof(function_1), unwind_info_1, results_1, ARRAY_SIZE(results_1) }, { function_2, sizeof(function_2), unwind_info_2, results_2, ARRAY_SIZE(results_2) }, { function_2, sizeof(function_2), unwind_info_3, results_3, ARRAY_SIZE(results_3) }, + + /* Broken before Win10 1809. */ + { function_4, sizeof(function_4), unwind_info_4, results_4, ARRAY_SIZE(results_4), broken_results_4 }, }; unsigned int i;