Alexandre Julliard : ntdll: Add support for some function epilogs in RtlVirtualUnwind.

Alexandre Julliard julliard at winehq.org
Wed May 20 08:29:34 CDT 2009


Module: wine
Branch: master
Commit: 1d4747c35fe5627375c98ab0cb55ec1e4e0eab4e
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=1d4747c35fe5627375c98ab0cb55ec1e4e0eab4e

Author: Alexandre Julliard <julliard at winehq.org>
Date:   Wed May 20 12:31:28 2009 +0200

ntdll: Add support for some function epilogs in RtlVirtualUnwind.

---

 dlls/ntdll/signal_x86_64.c   |  132 +++++++++++++++++++++++++++++++++++++++++-
 dlls/ntdll/tests/exception.c |   10 +++
 2 files changed, 141 insertions(+), 1 deletions(-)

diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index c1210e9..bf78685 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -1060,6 +1060,131 @@ static int get_opcode_size( struct opcode op )
     }
 }
 
+static BOOL is_inside_epilog( BYTE *pc )
+{
+    /* add or lea must be the first instruction, and it must have a rex.W prefix */
+    if ((pc[0] & 0xf8) == 0x48)
+    {
+        switch (pc[1])
+        {
+        case 0x81: /* add $nnnn,%rsp */
+            if (pc[0] == 0x48 && pc[2] == 0xc4)
+            {
+                pc += 7;
+                break;
+            }
+            return FALSE;
+        case 0x83: /* add $n,%rsp */
+            if (pc[0] == 0x48 && pc[2] == 0xc4)
+            {
+                pc += 4;
+                break;
+            }
+            return FALSE;
+        case 0x8d: /* lea n(reg),%rsp */
+            if (pc[0] & 0x06) return FALSE;  /* rex.RX must be cleared */
+            if (((pc[2] >> 3) & 7) != 4) return FALSE;  /* dest reg mus be %rsp */
+            if ((pc[2] & 7) == 4) return FALSE;  /* no SIB byte allowed */
+            if ((pc[2] >> 6) == 1)  /* 8-bit offset */
+            {
+                pc += 4;
+                break;
+            }
+            if ((pc[2] >> 6) == 2)  /* 32-bit offset */
+            {
+                pc += 7;
+                break;
+            }
+            return FALSE;
+        }
+    }
+
+    /* now check for various pop instructions */
+
+    for (;;)
+    {
+        BYTE rex = 0;
+
+        if ((*pc & 0xf0) == 0x40) rex = *pc++ & 0x0f;  /* rex prefix */
+
+        switch (*pc)
+        {
+        case 0x58: /* pop %rax/%r8 */
+        case 0x59: /* pop %rcx/%r9 */
+        case 0x5a: /* pop %rdx/%r10 */
+        case 0x5b: /* pop %rbx/%r11 */
+        case 0x5c: /* pop %rsp/%r12 */
+        case 0x5d: /* pop %rbp/%r13 */
+        case 0x5e: /* pop %rsi/%r14 */
+        case 0x5f: /* pop %rdi/%r15 */
+            pc++;
+            continue;
+        case 0xc2: /* ret $nn */
+        case 0xc3: /* ret */
+            return TRUE;
+        /* FIXME: add various jump instructions */
+        }
+        return FALSE;
+    }
+}
+
+/* execute a function epilog, which must have been validated with is_inside_epilog() */
+static void interpret_epilog( BYTE *pc, CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
+{
+    for (;;)
+    {
+        BYTE rex = 0;
+
+        if ((*pc & 0xf0) == 0x40) rex = *pc++ & 0x0f;  /* rex prefix */
+
+        switch (*pc)
+        {
+        case 0x58: /* pop %rax/r8 */
+        case 0x59: /* pop %rcx/r9 */
+        case 0x5a: /* pop %rdx/r10 */
+        case 0x5b: /* pop %rbx/r11 */
+        case 0x5c: /* pop %rsp/r12 */
+        case 0x5d: /* pop %rbp/r13 */
+        case 0x5e: /* pop %rsi/r14 */
+        case 0x5f: /* pop %rdi/r15 */
+            set_int_reg( context, ctx_ptr, *pc - 0x58 + (rex & 1) * 8, *(ULONG64 *)context->Rsp );
+            context->Rsp += sizeof(ULONG64);
+            pc++;
+            continue;
+        case 0x81: /* add $nnnn,%rsp */
+            context->Rsp += *(LONG *)(pc + 2);
+            pc += 2 + sizeof(LONG);
+            continue;
+        case 0x83: /* add $n,%rsp */
+            context->Rsp += (signed char)pc[2];
+            pc += 3;
+            continue;
+        case 0x8d:
+            if ((pc[1] >> 6) == 1)  /* lea n(reg),%rsp */
+            {
+                context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + (signed char)pc[2];
+                pc += 3;
+            }
+            else  /* lea nnnn(reg),%rsp */
+            {
+                context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + *(LONG *)(pc + 2);
+                pc += 2 + sizeof(LONG);
+            }
+            continue;
+        case 0xc2: /* ret $nn */
+            context->Rip = *(ULONG64 *)context->Rsp;
+            context->Rsp += sizeof(ULONG64) + *(WORD *)(pc + 1);
+            return;
+        case 0xc3: /* ret */
+            context->Rip = *(ULONG64 *)context->Rsp;
+            context->Rsp += sizeof(ULONG64);
+            return;
+        /* FIXME: add various jump instructions */
+        }
+        return;
+    }
+}
+
 /**********************************************************************
  *              RtlVirtualUnwind   (NTDLL.@)
  */
@@ -1100,7 +1225,12 @@ PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc,
         else
         {
             prolog_offset = ~0;
-            /* FIXME: check for function epilog */
+            if (is_inside_epilog( (BYTE *)pc ))
+            {
+                interpret_epilog( (BYTE *)pc, context, ctx_ptr );
+                *frame_ret = frame;
+                return NULL;
+            }
         }
 
         for (i = 0; i < info->count; i += get_opcode_size(info->opcodes[i]))
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index c3cc797..ff4125f 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -1116,6 +1116,9 @@ static void test_virtual_unwind(void)
         { 0x1c,  0x40,  TRUE,  0x128, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
         { 0x1d,  0x40,  TRUE,  0x128, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
         { 0x24,  0x40,  TRUE,  0x128, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
+        { 0x2b,  0x40,  FALSE, 0x128, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
+        { 0x32,  0x40,  FALSE, 0x008, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
+        { 0x33,  0x40,  FALSE, 0x000, { {rsp,0x008}, {-1,-1}}},
     };
 
 
@@ -1165,6 +1168,13 @@ static void test_virtual_unwind(void)
         { 0x04,  0x50,  FALSE, 0x020, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
         { 0x06,  0x50,  FALSE, 0x028, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
         { 0x0a,  0x50,  TRUE,  0x058, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
+        { 0x0c,  0x50,  FALSE, 0x058, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
+        { 0x10,  0x50,  FALSE, 0x028, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
+        { 0x12,  0x50,  FALSE, 0x020, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
+        { 0x13,  0x50,  FALSE, 0x018, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
+        { 0x14,  0x50,  FALSE, 0x010, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
+        { 0x15,  0x50,  FALSE, 0x008, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
+        { 0x16,  0x50,  FALSE, 0x000, { {rsp,0x008}, {-1,-1} }},
     };
 
     static const struct unwind_test tests[] =




More information about the wine-cvs mailing list