ntdll: Add support for some function epilogs in RtlVirtualUnwind.
This commit is contained in:
parent
fb819d6491
commit
1d4747c35f
|
@ -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]))
|
||||
|
|
|
@ -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[] =
|
||||
|
|
Loading…
Reference in New Issue