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