[PATCH] ntdll: Don't mind epilog in RtlVirtualUnwind() in case of zero op count on x64.

Paul Gofman pgofman at codeweavers.com
Fri Aug 14 05:10:44 CDT 2020


Signed-off-by: Paul Gofman <pgofman at 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;
 
-- 
2.26.2




More information about the wine-devel mailing list