Martin Storsjo : ntdll: Implement the arm64 machine frame and context unwind opcodes.

Alexandre Julliard julliard at winehq.org
Tue Aug 25 16:16:57 CDT 2020


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

Author: Martin Storsjo <martin at martin.st>
Date:   Tue Aug 25 12:26:28 2020 +0300

ntdll: Implement the arm64 machine frame and context unwind opcodes.

Signed-off-by: Martin Storsjo <martin at martin.st>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/ntdll/signal_arm64.c    | 13 ++++++-
 dlls/ntdll/tests/exception.c | 82 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c
index 5521e915a7..719c71f952 100644
--- a/dlls/ntdll/signal_arm64.c
+++ b/dlls/ntdll/signal_arm64.c
@@ -635,6 +635,15 @@ static void process_unwind_codes( BYTE *ptr, BYTE *end, CONTEXT *context,
             ptr += len;
             continue;
         }
+        else if (*ptr == 0xe9)  /* MSFT_OP_MACHINE_FRAME */
+        {
+            context->Pc = ((DWORD64 *)context->Sp)[1];
+            context->Sp = ((DWORD64 *)context->Sp)[0];
+        }
+        else if (*ptr == 0xea)  /* MSFT_OP_CONTEXT */
+        {
+            memcpy( context, (DWORD64 *)context->Sp, sizeof(CONTEXT) );
+        }
         else
         {
             WARN( "unsupported code %02x\n", *ptr );
@@ -891,13 +900,15 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc,
 
     *handler_data = NULL;
 
+    context->Pc = 0;
     if (func->u.s.Flag)
         handler = unwind_packed_data( base, pc, func, context, ctx_ptr );
     else
         handler = unwind_full_data( base, pc, func, context, handler_data, ctx_ptr );
 
     TRACE( "ret: lr=%lx sp=%lx handler=%p\n", context->u.s.Lr, context->Sp, handler );
-    context->Pc = context->u.s.Lr;
+    if (!context->Pc)
+        context->Pc = context->u.s.Lr;
     context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
     *frame_ret = context->Sp;
     return handler;
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 6c6bb97d02..d53b7b659c 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -4539,12 +4539,94 @@ static void test_virtual_unwind(void)
 #endif
     };
 
+    static const BYTE function_2[] =
+    {
+        0xff, 0x43, 0x00, 0xd1,   /* 00: sub sp, sp, #16 */
+        0x1f, 0x20, 0x03, 0xd5,   /* 04: nop */
+        0xff, 0x43, 0x00, 0xd1,   /* 08: sub sp, sp, #16 */
+        0x1f, 0x20, 0x03, 0xd5,   /* 0c: nop */
+        0xc0, 0x03, 0x5f, 0xd6,   /* 10: ret */
+    };
+
+    static const DWORD unwind_info_2_header =
+        (sizeof(function_2)/4) | /* function length */
+        (0 << 20) | /* X */
+        (0 << 21) | /* E */
+        (0 << 22) | /* epilog */
+        (1 << 27);  /* codes */
+
+    static const BYTE unwind_info_2[] =
+    {
+        DW(unwind_info_2_header),
+
+        UWOP_ALLOC_SMALL(16),   /* sub sp,  sp,  #16 */
+        UWOP_MACHINE_FRAME,
+        UWOP_ALLOC_SMALL(16),   /* sub sp,  sp,  #16 */
+        UWOP_END,
+    };
+
+    /* Partial prologues with the custom frame opcodes (machine frame,
+     * context) behave like there's an off-by-one bug; unwinding from
+     * offset 0, which normally does nothing, executes one opcode if
+     * there's a machine frame or context in the prologue, and for other
+     * offsets, it behaves like unwinding from one instruction further
+     * ahead. So only test the full prologue case. */
+    static const struct results results_2[] =
+    {
+      /* offset  fp    handler  pc      frame offset  registers */
+#if 0
+        { 0x00,  0x00,  0,     ORIG_LR, 0x010, TRUE,  { {-1,-1} }},
+        { 0x04,  0x00,  0,     0x0008,  0x010, FALSE, { {-1,-1} }},
+        { 0x08,  0x00,  0,     0x0018,  0x020, FALSE, { {-1,-1} }},
+#endif
+        { 0x0c,  0x00,  0,     0x0018,  0x020, FALSE, { {-1,-1} }},
+    };
+
+    static const BYTE function_3[] =
+    {
+        0xff, 0x43, 0x00, 0xd1,   /* 00: sub sp, sp, #16 */
+        0x1f, 0x20, 0x03, 0xd5,   /* 04: nop */
+        0xff, 0x43, 0x00, 0xd1,   /* 08: sub sp, sp, #16 */
+        0x1f, 0x20, 0x03, 0xd5,   /* 0c: nop */
+        0xc0, 0x03, 0x5f, 0xd6,   /* 10: ret */
+    };
+
+    static const DWORD unwind_info_3_header =
+        (sizeof(function_3)/4) | /* function length */
+        (0 << 20) | /* X */
+        (0 << 21) | /* E */
+        (0 << 22) | /* epilog */
+        (1 << 27);  /* codes */
+
+    static const BYTE unwind_info_3[] =
+    {
+        DW(unwind_info_3_header),
+
+        UWOP_ALLOC_SMALL(16),   /* sub sp,  sp,  #16 */
+        UWOP_CONTEXT,
+        UWOP_ALLOC_SMALL(16),   /* sub sp,  sp,  #16 */
+        UWOP_END,
+    };
+
+    static const struct results results_3[] =
+    {
+      /* offset  fp    handler  pc      frame offset  registers */
+#if 0
+        { 0x00,  0x00,  0,     ORIG_LR, 0x010, TRUE,  { {-1,-1} }},
+        { 0x04,  0x00,  0 ,    0x0108,  0x110, FALSE, { {x0, 0x08}, {x1, 0x10}, {x2, 0x18}, {x3, 0x20}, {x4, 0x28}, {x5, 0x30}, {x6, 0x38}, {x7, 0x40}, {x8, 0x48}, {x9, 0x50}, {x10, 0x58}, {x11, 0x60}, {x12, 0x68}, {x13, 0x70}, {x14, 0x78}, {x15, 0x80}, {x16, 0x88}, {x17, 0x90}, {x18, 0x98}, {x19, 0xA0}, {x20, 0xA8}, {x21, 0xB0}, {x22, 0xB8}, {x23, 0xC0}, {x24, 0xC8}, {x25, 0xD0}, {x26, 0xD8}, {x27, 0xE0}, {x28, 0xE8}, {x29, 0xF0}, {lr, 0xF8}, {d0, 0x110}, {d1, 0x120}, {d2, 0x130}, {d3, 0x140}, {d4, 0x150}, {d5, 0x160}, {d6, 0x170}, {d7, 0x180}, {d8, 0x190}, {d9, 0x1a0}, {d10, 0x1b0}, {d11, 0x1c0}, {d12, 0x1d0}, {d13, 0x1e0}, {d14, 0x1f0}, {d15, 0x200}, {-1,-1} }},
+        { 0x08,  0x00,  0 ,    0x0118,  0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }},
+#endif
+        { 0x0c,  0x00,  0 ,    0x0118,  0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }},
+    };
+
     static const struct unwind_test tests[] =
     {
 #define TEST(func, unwind, unwind_packed, results) \
         { func, sizeof(func), unwind, unwind_packed ? 0 : sizeof(unwind), results, ARRAY_SIZE(results) }
         TEST(function_0, unwind_info_0, 0, results_0),
         TEST(function_1, unwind_info_1, 1, results_1),
+        TEST(function_2, unwind_info_2, 0, results_2),
+        TEST(function_3, unwind_info_3, 0, results_3),
 #undef TEST
     };
     unsigned int i;




More information about the wine-cvs mailing list