Paul Gofman : ntdll: Support UWOP_PUSH_MACHFRAME opcode in RtlVirtualUnwind() on x64.

Alexandre Julliard julliard at winehq.org
Thu Aug 13 15:11:51 CDT 2020


Module: wine
Branch: master
Commit: 76556bc5885b45f88ca436fb2f04cdf79bc29af0
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=76556bc5885b45f88ca436fb2f04cdf79bc29af0

Author: Paul Gofman <pgofman at codeweavers.com>
Date:   Wed Aug 12 23:19:54 2020 +0300

ntdll: Support UWOP_PUSH_MACHFRAME opcode in RtlVirtualUnwind() on x64.

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/ntdll/signal_x86_64.c   | 28 ++++++++++++++++++---
 dlls/ntdll/tests/exception.c | 60 +++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 81 insertions(+), 7 deletions(-)

diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index 36a7b03380..3ed8952689 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -792,6 +792,7 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc,
     ULONG64 frame, off;
     struct UNWIND_INFO *info;
     unsigned int i, prolog_offset;
+    BOOL mach_frame = FALSE;
 
     TRACE( "type %x rip %lx rsp %lx\n", type, pc, context->Rsp );
     if (TRACE_ON(seh)) dump_unwind_info( base, function );
@@ -864,7 +865,23 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc,
                 set_float_reg( context, ctx_ptr, info->opcodes[i].info, (M128A *)off );
                 break;
             case UWOP_PUSH_MACHFRAME:
-                FIXME( "PUSH_MACHFRAME %u\n", info->opcodes[i].info );
+                if (info->flags & UNW_FLAG_CHAININFO)
+                {
+                    FIXME("PUSH_MACHFRAME with chained unwind info.\n");
+                    break;
+                }
+                if (i + get_opcode_size(info->opcodes[i]) < info->count )
+                {
+                    FIXME("PUSH_MACHFRAME is not the last opcode.\n");
+                    break;
+                }
+
+                if (info->opcodes[i].info)
+                    context->Rsp += 0x8;
+
+                context->Rip = *(ULONG64 *)context->Rsp;
+                context->Rsp = *(ULONG64 *)(context->Rsp + 24);
+                mach_frame = TRUE;
                 break;
             case UWOP_EPILOG:
                 if (info->version == 2)
@@ -879,9 +896,12 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc,
         function = &handler_data->chain;  /* restart with the chained info */
     }
 
-    /* now pop return address */
-    context->Rip = *(ULONG64 *)context->Rsp;
-    context->Rsp += sizeof(ULONG64);
+    if (!mach_frame)
+    {
+        /* now pop return address */
+        context->Rip = *(ULONG64 *)context->Rsp;
+        context->Rsp += sizeof(ULONG64);
+    }
 
     if (!(info->flags & type)) return NULL;  /* no matching handler */
     if (prolog_offset != ~0) return NULL;  /* inside prolog */
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index d5b8040857..f74ddde0f2 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -2021,11 +2021,15 @@ static void call_virtual_unwind( int testnum, const struct unwind_test *test )
 
             if (j == rsp)  /* rsp is special */
             {
+                ULONG64 expected_rsp;
+
                 ok( !ctx_ptr.IntegerContext[j],
                     "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
-                ok( context.Rsp == (ULONG64)fake_stack + test->results[i].regs[k][1],
+                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,
                     "%u/%u: register rsp wrong %p/%p\n",
-                    testnum, i, (void *)context.Rsp, (char *)fake_stack + test->results[i].regs[k][1] );
+                    testnum, i, (void *)context.Rsp, (void *)expected_rsp );
                 continue;
             }
 
@@ -2159,10 +2163,60 @@ static void test_virtual_unwind(void)
         { 0x16,  0x50,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
     };
 
+    static const BYTE function_2[] =
+    {
+        0x55,                     /* 00: push %rbp */
+        0x90, 0x90,               /* 01: nop; nop */
+        0x5d,                     /* 03: pop %rbp */
+        0xc3                      /* 04: ret */
+     };
+
+    static const BYTE unwind_info_2[] =
+    {
+        1 | (UNW_FLAG_EHANDLER << 3),  /* version + flags */
+        0x0,                           /* prolog size */
+        2,                             /* opcode count */
+        0,                             /* frame reg */
+
+        0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
+        0x00, UWOP(PUSH_MACHFRAME, 0), /* 00 */
+
+        0x00, 0x02, 0x00, 0x00,  /* handler */
+        0x05, 0x06, 0x07, 0x08,  /* data */
+    };
+
+    static const struct results results_2[] =
+    {
+      /* offset  rbp   handler  rip   frame   registers */
+        { 0x01,  0x50,  TRUE, 0x008, 0x000, { {rsp,-0x020}, {rbp,0x000}, {-1,-1} }},
+    };
+
+    static const BYTE unwind_info_3[] =
+    {
+        1 | (UNW_FLAG_EHANDLER << 3),  /* version + flags */
+        0x0,                           /* prolog size */
+        2,                             /* opcode count */
+        0,                             /* frame reg */
+
+        0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
+        0x00, UWOP(PUSH_MACHFRAME, 1), /* 00 */
+
+        0x00, 0x02, 0x00, 0x00,  /* handler */
+        0x05, 0x06, 0x07, 0x08,  /* data */
+    };
+
+    static const struct results results_3[] =
+    {
+      /* offset  rbp   handler  rip   frame   registers */
+        { 0x01,  0x50,  TRUE, 0x010, 0x000, { {rsp,-0x028}, {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_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) },
     };
     unsigned int i;
 




More information about the wine-cvs mailing list