From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/unwind.c | 47 ++++++++++++++++++++++++++++++++++++++- dlls/ntdll/unwind.c | 10 ++++++++- 2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/tests/unwind.c b/dlls/ntdll/tests/unwind.c index 87f4ca92131..9049fca50fd 100644 --- a/dlls/ntdll/tests/unwind.c +++ b/dlls/ntdll/tests/unwind.c @@ -3122,6 +3122,16 @@ static void test_virtual_unwind_x86(void) 0xc3, /* 08: ret */ };
+ BYTE function_tail_jump_ff[] = + { + 0x55, /* 00: push %rbp */ + 0x90, /* 01: nop */ + 0x5d, /* 02: pop %rbp */ + 0x48, 0xff, 0x25, /* 03: space for rex prefix and 0xff jump opcode */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc3, /* ret */ + }; + static const BYTE unwind_info_6[] = { 1, /* version + flags */ @@ -3166,7 +3176,42 @@ static void test_virtual_unwind_x86(void) { function_6_2, sizeof(function_6_2), unwind_info_6, results_6_body, ARRAY_SIZE(results_6_body) }, { function_6_3, sizeof(function_6_3), unwind_info_6, results_6_epilogue, ARRAY_SIZE(results_6_epilogue) }, }; - unsigned int i; + + struct unwind_test_x86 jump_test = { function_tail_jump_ff, sizeof(function_tail_jump_ff), unwind_info_6, + results_6_epilogue, ARRAY_SIZE(results_6_epilogue) }; + + unsigned int i, rex_prefix, ind; + + for (rex_prefix = 0; rex_prefix <= 8; ++rex_prefix) + { + for (i = 0; i < 256; ++i) + { + unsigned int ext; + int exp; + ind = 3; + if (rex_prefix) function_tail_jump_ff[ind++] = 0x40 | rex_prefix; + function_tail_jump_ff[ind++] = 0xff; + function_tail_jump_ff[ind++] = i; + ext = (i >> 3) & 0x7; + memset( function_tail_jump_ff + ind, 0, sizeof(function_tail_jump_ff) - ind ); + if (((rex_prefix == 8 || !rex_prefix) && i == 0x25) + || (rex_prefix == 8 && ext == 4)) + { + jump_test.results = results_6_epilogue; + jump_test.nb_results = ARRAY_SIZE(results_6_epilogue); + exp = 1; + } + else + { + jump_test.results = results_6_body; + jump_test.nb_results = ARRAY_SIZE(results_6_body); + exp = 0; + } + winetest_push_context( "rex %#x, byte %#x, exp %d", rex_prefix, i, exp ); + call_virtual_unwind_x86( 0, &jump_test ); + winetest_pop_context(); + } + }
for (i = 0; i < ARRAY_SIZE(tests); i++) call_virtual_unwind_x86( i, &tests[i] ); diff --git a/dlls/ntdll/unwind.c b/dlls/ntdll/unwind.c index f4bdcf3512b..1884c256929 100644 --- a/dlls/ntdll/unwind.c +++ b/dlls/ntdll/unwind.c @@ -1845,6 +1845,8 @@ static int get_opcode_size( struct opcode op )
static BOOL is_inside_epilog( BYTE *pc, ULONG64 base, const RUNTIME_FUNCTION *function ) { + BYTE rex; + /* add or lea must be the first instruction, and it must have a rex.W prefix */ if ((pc[0] & 0xf8) == 0x48) { @@ -1886,7 +1888,8 @@ static BOOL is_inside_epilog( BYTE *pc, ULONG64 base, const RUNTIME_FUNCTION *fu
for (;;) { - if ((*pc & 0xf0) == 0x40) pc++; /* rex prefix */ + rex = 0; + if ((*pc & 0xf0) == 0x40) rex |= *pc++; /* rex prefix */
switch (*pc) { @@ -1911,6 +1914,10 @@ static BOOL is_inside_epilog( BYTE *pc, ULONG64 base, const RUNTIME_FUNCTION *fu return !(pc - (BYTE *)base >= function->BeginAddress && pc - (BYTE *)base < function->EndAddress); case 0xf3: /* rep; ret (for amd64 prediction bug) */ return pc[1] == 0xc3; + case 0xff: /* jmp */ + if (rex && rex != 0x48) return FALSE; + if (pc[1] == 0x25) return TRUE; + return rex && ((pc[1] >> 3) & 7) == 4; } return FALSE; } @@ -1967,6 +1974,7 @@ static void interpret_epilog( BYTE *pc, CONTEXT *context, KNONVOLATILE_CONTEXT_P case 0xeb: /* jmp n */ case 0xc3: /* ret */ case 0xf3: /* rep; ret */ + case 0xff: /* ret */ context->Rip = *(ULONG64 *)context->Rsp; context->Rsp += sizeof(ULONG64); return;