ntdll: Move exception unwinding code to a separate file.
This commit is contained in:
parent
d88f92eb4e
commit
a5a253820f
6 changed files with 1686 additions and 1793 deletions
|
@ -66,6 +66,7 @@ SOURCES = \
|
|||
unix/tape.c \
|
||||
unix/thread.c \
|
||||
unix/virtual.c \
|
||||
unwind.c \
|
||||
version.c \
|
||||
version.rc \
|
||||
wcstring.c
|
||||
|
|
|
@ -586,589 +586,6 @@ __ASM_GLOBAL_FUNC( KiUserCallbackDispatcher,
|
|||
"udf #1" )
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Definitions for Win32 unwind tables
|
||||
*/
|
||||
|
||||
struct unwind_info
|
||||
{
|
||||
DWORD function_length : 18;
|
||||
DWORD version : 2;
|
||||
DWORD x : 1;
|
||||
DWORD e : 1;
|
||||
DWORD f : 1;
|
||||
DWORD epilog : 5;
|
||||
DWORD codes : 4;
|
||||
};
|
||||
|
||||
struct unwind_info_ext
|
||||
{
|
||||
WORD epilog;
|
||||
BYTE codes;
|
||||
BYTE reserved;
|
||||
};
|
||||
|
||||
struct unwind_info_epilog
|
||||
{
|
||||
DWORD offset : 18;
|
||||
DWORD res : 2;
|
||||
DWORD cond : 4;
|
||||
DWORD index : 8;
|
||||
};
|
||||
|
||||
static const BYTE unwind_code_len[256] =
|
||||
{
|
||||
/* 00 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* 20 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* 80 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
/* a0 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
/* c0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* e0 */ 1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,4,3,4,1,1,1,1,1
|
||||
};
|
||||
|
||||
static const BYTE unwind_instr_len[256] =
|
||||
{
|
||||
/* 00 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
/* 20 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
/* 40 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
/* 60 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
/* 80 */ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
/* a0 */ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
/* c0 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,
|
||||
/* e0 */ 4,4,4,4,4,4,4,4,4,4,4,4,2,2,0,4,0,0,0,0,0,4,4,2,2,4,4,2,4,2,4,0
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* get_sequence_len
|
||||
*/
|
||||
static unsigned int get_sequence_len( BYTE *ptr, BYTE *end, int include_end )
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
|
||||
while (ptr < end)
|
||||
{
|
||||
if (*ptr >= 0xfd)
|
||||
{
|
||||
if (*ptr <= 0xfe && include_end)
|
||||
ret += unwind_instr_len[*ptr];
|
||||
break;
|
||||
}
|
||||
ret += unwind_instr_len[*ptr];
|
||||
ptr += unwind_code_len[*ptr];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* pop_regs_mask
|
||||
*/
|
||||
static void pop_regs_mask( int mask, CONTEXT *context,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= 12; i++)
|
||||
{
|
||||
if (!(mask & (1 << i))) continue;
|
||||
if (ptrs && i >= 4 && i <= 11) (&ptrs->R4)[i - 4] = (DWORD *)context->Sp;
|
||||
if (i >= 4) (&context->R0)[i] = *(DWORD *)context->Sp;
|
||||
context->Sp += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* pop_regs_range
|
||||
*/
|
||||
static void pop_regs_range( int last, CONTEXT *context,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
int i;
|
||||
for (i = 4; i <= last; i++)
|
||||
{
|
||||
if (ptrs) (&ptrs->R4)[i - 4] = (DWORD *)context->Sp;
|
||||
(&context->R0)[i] = *(DWORD *)context->Sp;
|
||||
context->Sp += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* pop_lr
|
||||
*/
|
||||
static void pop_lr( int increment, CONTEXT *context,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
if (ptrs) ptrs->Lr = (DWORD *)context->Sp;
|
||||
context->Lr = *(DWORD *)context->Sp;
|
||||
context->Sp += increment;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* pop_fpregs_range
|
||||
*/
|
||||
static void pop_fpregs_range( int first, int last, CONTEXT *context,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
int i;
|
||||
for (i = first; i <= last; i++)
|
||||
{
|
||||
if (ptrs && i >= 8 && i <= 15) (&ptrs->D8)[i - 8] = (ULONGLONG *)context->Sp;
|
||||
context->D[i] = *(ULONGLONG *)context->Sp;
|
||||
context->Sp += 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* ms_opcode
|
||||
*/
|
||||
static void ms_opcode( BYTE opcode, CONTEXT *context,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case 1: /* MSFT_OP_MACHINE_FRAME */
|
||||
context->Pc = ((DWORD *)context->Sp)[1];
|
||||
context->Sp = ((DWORD *)context->Sp)[0];
|
||||
context->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
|
||||
break;
|
||||
case 2: /* MSFT_OP_CONTEXT */
|
||||
{
|
||||
int i;
|
||||
CONTEXT *src = (CONTEXT *)context->Sp;
|
||||
|
||||
*context = *src;
|
||||
if (!ptrs) break;
|
||||
for (i = 0; i < 8; i++) (&ptrs->R4)[i] = &src->R4 + i;
|
||||
ptrs->Lr = &src->Lr;
|
||||
for (i = 0; i < 8; i++) (&ptrs->D8)[i] = &src->D[i + 8];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
WARN( "unsupported code %02x\n", opcode );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* process_unwind_codes
|
||||
*/
|
||||
static void process_unwind_codes( BYTE *ptr, BYTE *end, CONTEXT *context,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ptrs, int skip )
|
||||
{
|
||||
unsigned int val, len;
|
||||
unsigned int i;
|
||||
|
||||
/* skip codes */
|
||||
while (ptr < end && skip)
|
||||
{
|
||||
if (*ptr >= 0xfd) break;
|
||||
skip -= unwind_instr_len[*ptr];
|
||||
ptr += unwind_code_len[*ptr];
|
||||
}
|
||||
|
||||
while (ptr < end)
|
||||
{
|
||||
len = unwind_code_len[*ptr];
|
||||
if (ptr + len > end) break;
|
||||
val = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
val = (val << 8) | ptr[i];
|
||||
|
||||
if (*ptr <= 0x7f) /* add sp, sp, #x */
|
||||
context->Sp += 4 * (val & 0x7f);
|
||||
else if (*ptr <= 0xbf) /* pop {r0-r12,lr} */
|
||||
{
|
||||
pop_regs_mask( val & 0x1fff, context, ptrs );
|
||||
if (val & 0x2000)
|
||||
pop_lr( 4, context, ptrs );
|
||||
}
|
||||
else if (*ptr <= 0xcf) /* mov sp, rX */
|
||||
context->Sp = (&context->R0)[val & 0x0f];
|
||||
else if (*ptr <= 0xd7) /* pop {r4-rX,lr} */
|
||||
{
|
||||
pop_regs_range( (val & 0x03) + 4, context, ptrs );
|
||||
if (val & 0x04)
|
||||
pop_lr( 4, context, ptrs );
|
||||
}
|
||||
else if (*ptr <= 0xdf) /* pop {r4-rX,lr} */
|
||||
{
|
||||
pop_regs_range( (val & 0x03) + 8, context, ptrs );
|
||||
if (val & 0x04)
|
||||
pop_lr( 4, context, ptrs );
|
||||
}
|
||||
else if (*ptr <= 0xe7) /* vpop {d8-dX} */
|
||||
pop_fpregs_range( 8, (val & 0x07) + 8, context, ptrs );
|
||||
else if (*ptr <= 0xeb) /* add sp, sp, #x */
|
||||
context->Sp += 4 * (val & 0x3ff);
|
||||
else if (*ptr <= 0xed) /* pop {r0-r12,lr} */
|
||||
{
|
||||
pop_regs_mask( val & 0xff, context, ptrs );
|
||||
if (val & 0x100)
|
||||
pop_lr( 4, context, ptrs );
|
||||
}
|
||||
else if (*ptr <= 0xee) /* Microsoft-specific 0x00-0x0f, Available 0x10-0xff */
|
||||
ms_opcode( val & 0xff, context, ptrs );
|
||||
else if (*ptr <= 0xef && ((val & 0xff) <= 0x0f)) /* ldr lr, [sp], #x */
|
||||
pop_lr( 4 * (val & 0x0f), context, ptrs );
|
||||
else if (*ptr <= 0xf4) /* Available */
|
||||
WARN( "unsupported code %02x\n", *ptr );
|
||||
else if (*ptr <= 0xf5) /* vpop {dS-dE} */
|
||||
pop_fpregs_range( (val & 0xf0) >> 4, (val & 0x0f), context, ptrs );
|
||||
else if (*ptr <= 0xf6) /* vpop {dS-dE} */
|
||||
pop_fpregs_range( ((val & 0xf0) >> 4) + 16, (val & 0x0f) + 16, context, ptrs );
|
||||
else if (*ptr == 0xf7 || *ptr == 0xf9) /* add sp, sp, #x */
|
||||
context->Sp += 4 * (val & 0xffff);
|
||||
else if (*ptr == 0xf8 || *ptr == 0xfa) /* add sp, sp, #x */
|
||||
context->Sp += 4 * (val & 0xffffff);
|
||||
else if (*ptr <= 0xfc) /* nop */
|
||||
/* nop */ ;
|
||||
else /* end */
|
||||
break;
|
||||
|
||||
ptr += len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* unwind_packed_data
|
||||
*/
|
||||
static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION *func,
|
||||
CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
int i, pos = 0;
|
||||
int pf = 0, ef = 0, fpoffset = 0, stack = func->StackAdjust;
|
||||
int prologue_regmask = 0;
|
||||
int epilogue_regmask = 0;
|
||||
unsigned int offset, len;
|
||||
BYTE prologue[10], *prologue_end, epilogue[20], *epilogue_end;
|
||||
|
||||
TRACE( "function %lx-%lx: len=%#x flag=%x ret=%u H=%u reg=%u R=%u L=%u C=%u stackadjust=%x\n",
|
||||
base + func->BeginAddress, base + func->BeginAddress + func->FunctionLength * 2,
|
||||
func->FunctionLength, func->Flag, func->Ret,
|
||||
func->H, func->Reg, func->R, func->L, func->C, func->StackAdjust );
|
||||
|
||||
offset = (pc - base) - func->BeginAddress;
|
||||
if (func->StackAdjust >= 0x03f4)
|
||||
{
|
||||
pf = func->StackAdjust & 0x04;
|
||||
ef = func->StackAdjust & 0x08;
|
||||
stack = (func->StackAdjust & 3) + 1;
|
||||
}
|
||||
|
||||
if (!func->R || pf)
|
||||
{
|
||||
int first = 4, last = func->Reg + 4;
|
||||
if (pf)
|
||||
{
|
||||
first = (~func->StackAdjust) & 3;
|
||||
if (func->R)
|
||||
last = 3;
|
||||
}
|
||||
for (i = first; i <= last; i++)
|
||||
prologue_regmask |= 1 << i;
|
||||
fpoffset = last + 1 - first;
|
||||
}
|
||||
|
||||
if (!func->R || ef)
|
||||
{
|
||||
int first = 4, last = func->Reg + 4;
|
||||
if (ef)
|
||||
{
|
||||
first = (~func->StackAdjust) & 3;
|
||||
if (func->R)
|
||||
last = 3;
|
||||
}
|
||||
for (i = first; i <= last; i++)
|
||||
epilogue_regmask |= 1 << i;
|
||||
}
|
||||
|
||||
if (func->C)
|
||||
{
|
||||
prologue_regmask |= 1 << 11;
|
||||
epilogue_regmask |= 1 << 11;
|
||||
}
|
||||
|
||||
if (func->L)
|
||||
{
|
||||
prologue_regmask |= 1 << 14; /* lr */
|
||||
if (func->Ret != 0)
|
||||
epilogue_regmask |= 1 << 14; /* lr */
|
||||
else if (!func->H)
|
||||
epilogue_regmask |= 1 << 15; /* pc */
|
||||
}
|
||||
|
||||
/* Synthesize prologue opcodes */
|
||||
if (stack && !pf)
|
||||
{
|
||||
if (stack <= 0x7f)
|
||||
{
|
||||
prologue[pos++] = stack; /* sub sp, sp, #x */
|
||||
}
|
||||
else
|
||||
{
|
||||
prologue[pos++] = 0xe8 | (stack >> 8); /* sub.w sp, sp, #x */
|
||||
prologue[pos++] = stack & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if (func->R && func->Reg != 7)
|
||||
prologue[pos++] = 0xe0 | func->Reg; /* vpush {d8-dX} */
|
||||
|
||||
if (func->C && fpoffset == 0)
|
||||
prologue[pos++] = 0xfb; /* mov r11, sp - handled as nop16 */
|
||||
else if (func->C)
|
||||
prologue[pos++] = 0xfc; /* add r11, sp, #x - handled as nop32 */
|
||||
|
||||
if (prologue_regmask & 0xf00) /* r8-r11 set */
|
||||
{
|
||||
int bitmask = prologue_regmask & 0x1fff;
|
||||
if (prologue_regmask & (1 << 14)) /* lr */
|
||||
bitmask |= 0x2000;
|
||||
prologue[pos++] = 0x80 | (bitmask >> 8); /* push.w {r0-r12,lr} */
|
||||
prologue[pos++] = bitmask & 0xff;
|
||||
}
|
||||
else if (prologue_regmask) /* r0-r7, lr set */
|
||||
{
|
||||
int bitmask = prologue_regmask & 0xff;
|
||||
if (prologue_regmask & (1 << 14)) /* lr */
|
||||
bitmask |= 0x100;
|
||||
prologue[pos++] = 0xec | (bitmask >> 8); /* push {r0-r7,lr} */
|
||||
prologue[pos++] = bitmask & 0xff;
|
||||
}
|
||||
|
||||
if (func->H)
|
||||
prologue[pos++] = 0x04; /* push {r0-r3} - handled as sub sp, sp, #16 */
|
||||
|
||||
prologue[pos++] = 0xff; /* end */
|
||||
prologue_end = &prologue[pos];
|
||||
|
||||
/* Synthesize epilogue opcodes */
|
||||
pos = 0;
|
||||
if (stack && !ef)
|
||||
{
|
||||
if (stack <= 0x7f)
|
||||
{
|
||||
epilogue[pos++] = stack; /* sub sp, sp, #x */
|
||||
}
|
||||
else
|
||||
{
|
||||
epilogue[pos++] = 0xe8 | (stack >> 8); /* sub.w sp, sp, #x */
|
||||
epilogue[pos++] = stack & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if (func->R && func->Reg != 7)
|
||||
epilogue[pos++] = 0xe0 | func->Reg; /* vpush {d8-dX} */
|
||||
|
||||
if (epilogue_regmask & 0x7f00) /* r8-r11, lr set */
|
||||
{
|
||||
int bitmask = epilogue_regmask & 0x1fff;
|
||||
if (epilogue_regmask & (3 << 14)) /* lr or pc */
|
||||
bitmask |= 0x2000;
|
||||
epilogue[pos++] = 0x80 | (bitmask >> 8); /* push.w {r0-r12,lr} */
|
||||
epilogue[pos++] = bitmask & 0xff;
|
||||
}
|
||||
else if (epilogue_regmask) /* r0-r7, pc set */
|
||||
{
|
||||
int bitmask = epilogue_regmask & 0xff;
|
||||
if (epilogue_regmask & (1 << 15)) /* pc */
|
||||
bitmask |= 0x100; /* lr */
|
||||
epilogue[pos++] = 0xec | (bitmask >> 8); /* push {r0-r7,lr} */
|
||||
epilogue[pos++] = bitmask & 0xff;
|
||||
}
|
||||
|
||||
if (func->H && !(func->L && func->Ret == 0))
|
||||
epilogue[pos++] = 0x04; /* add sp, sp, #16 */
|
||||
else if (func->H && (func->L && func->Ret == 0))
|
||||
{
|
||||
epilogue[pos++] = 0xef; /* ldr lr, [sp], #20 */
|
||||
epilogue[pos++] = 5;
|
||||
}
|
||||
|
||||
if (func->Ret == 1)
|
||||
epilogue[pos++] = 0xfd; /* bx lr */
|
||||
else if (func->Ret == 2)
|
||||
epilogue[pos++] = 0xfe; /* b address */
|
||||
else
|
||||
epilogue[pos++] = 0xff; /* end */
|
||||
epilogue_end = &epilogue[pos];
|
||||
|
||||
if (func->Flag == 1 && offset < 4 * (prologue_end - prologue)) {
|
||||
/* Check prologue */
|
||||
len = get_sequence_len( prologue, prologue_end, 0 );
|
||||
if (offset < len)
|
||||
{
|
||||
process_unwind_codes( prologue, prologue_end, context, ptrs, len - offset );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (func->Ret != 3 && 2 * func->FunctionLength - offset <= 4 * (epilogue_end - epilogue)) {
|
||||
/* Check epilogue */
|
||||
len = get_sequence_len( epilogue, epilogue_end, 1 );
|
||||
if (offset >= 2 * func->FunctionLength - len)
|
||||
{
|
||||
process_unwind_codes( epilogue, epilogue_end, context, ptrs, offset - (2 * func->FunctionLength - len) );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute full prologue */
|
||||
process_unwind_codes( prologue, prologue_end, context, ptrs, 0 );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* unwind_full_data
|
||||
*/
|
||||
static void *unwind_full_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION *func,
|
||||
CONTEXT *context, PVOID *handler_data, KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
struct unwind_info *info;
|
||||
struct unwind_info_epilog *info_epilog;
|
||||
unsigned int i, codes, epilogs, len, offset;
|
||||
void *data;
|
||||
BYTE *end;
|
||||
|
||||
info = (struct unwind_info *)((char *)base + func->UnwindData);
|
||||
data = info + 1;
|
||||
epilogs = info->epilog;
|
||||
codes = info->codes;
|
||||
if (!codes && !epilogs)
|
||||
{
|
||||
struct unwind_info_ext *infoex = data;
|
||||
codes = infoex->codes;
|
||||
epilogs = infoex->epilog;
|
||||
data = infoex + 1;
|
||||
}
|
||||
info_epilog = data;
|
||||
if (!info->e) data = info_epilog + epilogs;
|
||||
|
||||
offset = (pc - base) - func->BeginAddress;
|
||||
end = (BYTE *)data + codes * 4;
|
||||
|
||||
TRACE( "function %lx-%lx: len=%#x ver=%u X=%u E=%u F=%u epilogs=%u codes=%u\n",
|
||||
base + func->BeginAddress, base + func->BeginAddress + info->function_length * 2,
|
||||
info->function_length, info->version, info->x, info->e, info->f, epilogs, codes * 4 );
|
||||
|
||||
/* check for prolog */
|
||||
if (offset < codes * 4 * 4 && !info->f)
|
||||
{
|
||||
len = get_sequence_len( data, end, 0 );
|
||||
if (offset < len)
|
||||
{
|
||||
process_unwind_codes( data, end, context, ptrs, len - offset );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for epilog */
|
||||
if (!info->e)
|
||||
{
|
||||
for (i = 0; i < epilogs; i++)
|
||||
{
|
||||
/* TODO: Currently not checking epilogue conditions. */
|
||||
if (offset < 2 * info_epilog[i].offset) break;
|
||||
if (offset - 2 * info_epilog[i].offset < (codes * 4 - info_epilog[i].index) * 4)
|
||||
{
|
||||
BYTE *ptr = (BYTE *)data + info_epilog[i].index;
|
||||
len = get_sequence_len( ptr, end, 1 );
|
||||
if (offset <= 2 * info_epilog[i].offset + len)
|
||||
{
|
||||
process_unwind_codes( ptr, end, context, ptrs, offset - 2 * info_epilog[i].offset );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (2 * info->function_length - offset <= (codes * 4 - epilogs) * 4)
|
||||
{
|
||||
BYTE *ptr = (BYTE *)data + epilogs;
|
||||
len = get_sequence_len( ptr, end, 1 );
|
||||
if (offset >= 2 * info->function_length - len)
|
||||
{
|
||||
process_unwind_codes( ptr, end, context, ptrs, offset - (2 * info->function_length - len) );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
process_unwind_codes( data, end, context, ptrs, 0 );
|
||||
|
||||
/* get handler since we are inside the main code */
|
||||
if (info->x)
|
||||
{
|
||||
DWORD *handler_rva = (DWORD *)data + codes;
|
||||
*handler_data = handler_rva + 1;
|
||||
return (char *)base + *handler_rva;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* find_function_info
|
||||
*/
|
||||
static RUNTIME_FUNCTION *find_function_info( ULONG_PTR pc, ULONG_PTR base,
|
||||
RUNTIME_FUNCTION *func, ULONG size )
|
||||
{
|
||||
int min = 0;
|
||||
int max = size - 1;
|
||||
|
||||
while (min <= max)
|
||||
{
|
||||
int pos = (min + max) / 2;
|
||||
ULONG_PTR start = base + (func[pos].BeginAddress & ~1);
|
||||
|
||||
if (pc >= start)
|
||||
{
|
||||
struct unwind_info *info = (struct unwind_info *)(base + func[pos].UnwindData);
|
||||
if (pc < start + 2 * (func[pos].Flag ? func[pos].FunctionLength : info->function_length))
|
||||
return func + pos;
|
||||
min = pos + 1;
|
||||
}
|
||||
else max = pos - 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* RtlVirtualUnwind (NTDLL.@)
|
||||
*/
|
||||
PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc,
|
||||
RUNTIME_FUNCTION *func, CONTEXT *context,
|
||||
PVOID *handler_data, ULONG_PTR *frame_ret,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
|
||||
{
|
||||
void *handler;
|
||||
|
||||
TRACE( "type %lx pc %Ix sp %lx func %lx\n", type, pc, context->Sp, base + func->BeginAddress );
|
||||
|
||||
*handler_data = NULL;
|
||||
|
||||
context->Pc = 0;
|
||||
if (func->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: pc=%lx lr=%lx sp=%lx handler=%p\n", context->Pc, context->Lr, context->Sp, handler );
|
||||
if (!context->Pc)
|
||||
{
|
||||
context->Pc = context->Lr;
|
||||
context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
|
||||
}
|
||||
*frame_ret = context->Sp;
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlLookupFunctionTable (NTDLL.@)
|
||||
|
@ -1183,49 +600,6 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionTable( ULONG_PTR pc, ULONG_PTR *base,
|
|||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlLookupFunctionEntry (NTDLL.@)
|
||||
*/
|
||||
PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG_PTR pc, ULONG_PTR *base,
|
||||
UNWIND_HISTORY_TABLE *table )
|
||||
{
|
||||
RUNTIME_FUNCTION *func;
|
||||
ULONG_PTR dynbase;
|
||||
ULONG size;
|
||||
|
||||
if ((func = RtlLookupFunctionTable( pc, base, &size )))
|
||||
return find_function_info( pc, *base, func, size / sizeof(*func));
|
||||
|
||||
if ((func = lookup_dynamic_function_table( pc, &dynbase, &size )))
|
||||
{
|
||||
RUNTIME_FUNCTION *ret = find_function_info( pc, dynbase, func, size );
|
||||
if (ret) *base = dynbase;
|
||||
return ret;
|
||||
}
|
||||
|
||||
*base = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlAddFunctionTable (NTDLL.@)
|
||||
*/
|
||||
BOOLEAN CDECL RtlAddFunctionTable( RUNTIME_FUNCTION *table, DWORD count, ULONG_PTR base )
|
||||
{
|
||||
ULONG_PTR end = base;
|
||||
void *ret;
|
||||
|
||||
if (count)
|
||||
{
|
||||
RUNTIME_FUNCTION *func = table + count - 1;
|
||||
struct unwind_info *info = (struct unwind_info *)(base + func->UnwindData);
|
||||
end += func->BeginAddress + 2 * (func->Flag ? func->FunctionLength : info->function_length);
|
||||
}
|
||||
return !RtlAddGrowableFunctionTable( &ret, table, count, 0, base, end );
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* call_consolidate_callback
|
||||
*
|
||||
|
|
|
@ -579,499 +579,6 @@ __ASM_GLOBAL_FUNC( KiUserCallbackDispatcher,
|
|||
"brk #1" )
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Definitions for Win32 unwind tables
|
||||
*/
|
||||
|
||||
struct unwind_info_ext
|
||||
{
|
||||
WORD epilog;
|
||||
BYTE codes;
|
||||
BYTE reserved;
|
||||
};
|
||||
|
||||
struct unwind_info_epilog
|
||||
{
|
||||
DWORD offset : 18;
|
||||
DWORD res : 4;
|
||||
DWORD index : 10;
|
||||
};
|
||||
|
||||
static const BYTE unwind_code_len[256] =
|
||||
{
|
||||
/* 00 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* 20 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* a0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
/* c0 */ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
/* e0 */ 4,1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* get_sequence_len
|
||||
*/
|
||||
static unsigned int get_sequence_len( BYTE *ptr, BYTE *end )
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
|
||||
while (ptr < end)
|
||||
{
|
||||
if (*ptr == 0xe4 || *ptr == 0xe5) break;
|
||||
ptr += unwind_code_len[*ptr];
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* restore_regs
|
||||
*/
|
||||
static void restore_regs( int reg, int count, int pos, CONTEXT *context,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
int i, offset = max( 0, pos );
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (ptrs && reg + i >= 19) (&ptrs->X19)[reg + i - 19] = (DWORD64 *)context->Sp + i + offset;
|
||||
context->X[reg + i] = ((DWORD64 *)context->Sp)[i + offset];
|
||||
}
|
||||
if (pos < 0) context->Sp += -8 * pos;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* restore_fpregs
|
||||
*/
|
||||
static void restore_fpregs( int reg, int count, int pos, CONTEXT *context,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
int i, offset = max( 0, pos );
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (ptrs && reg + i >= 8) (&ptrs->D8)[reg + i - 8] = (DWORD64 *)context->Sp + i + offset;
|
||||
context->V[reg + i].D[0] = ((double *)context->Sp)[i + offset];
|
||||
}
|
||||
if (pos < 0) context->Sp += -8 * pos;
|
||||
}
|
||||
|
||||
|
||||
static void do_pac_auth( CONTEXT *context )
|
||||
{
|
||||
register DWORD64 x17 __asm__( "x17" ) = context->Lr;
|
||||
register DWORD64 x16 __asm__( "x16" ) = context->Sp;
|
||||
|
||||
/* This is the autib1716 instruction. The hint instruction is used here
|
||||
* as gcc does not assemble autib1716 for pre armv8.3a targets. For
|
||||
* pre-armv8.3a targets, this is just treated as a hint instruction, which
|
||||
* is ignored. */
|
||||
__asm__( "hint 0xe" : "+r"(x17) : "r"(x16) );
|
||||
|
||||
context->Lr = x17;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* process_unwind_codes
|
||||
*/
|
||||
static void process_unwind_codes( BYTE *ptr, BYTE *end, CONTEXT *context,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ptrs, int skip )
|
||||
{
|
||||
unsigned int val, len, save_next = 2;
|
||||
|
||||
/* skip codes */
|
||||
while (ptr < end && skip)
|
||||
{
|
||||
if (*ptr == 0xe4 || *ptr == 0xe5) break;
|
||||
ptr += unwind_code_len[*ptr];
|
||||
skip--;
|
||||
}
|
||||
|
||||
while (ptr < end)
|
||||
{
|
||||
if ((len = unwind_code_len[*ptr]) > 1)
|
||||
{
|
||||
if (ptr + len > end) break;
|
||||
val = ptr[0] * 0x100 + ptr[1];
|
||||
}
|
||||
else val = *ptr;
|
||||
|
||||
if (*ptr < 0x20) /* alloc_s */
|
||||
context->Sp += 16 * (val & 0x1f);
|
||||
else if (*ptr < 0x40) /* save_r19r20_x */
|
||||
restore_regs( 19, save_next, -(val & 0x1f), context, ptrs );
|
||||
else if (*ptr < 0x80) /* save_fplr */
|
||||
restore_regs( 29, 2, val & 0x3f, context, ptrs );
|
||||
else if (*ptr < 0xc0) /* save_fplr_x */
|
||||
restore_regs( 29, 2, -(val & 0x3f) - 1, context, ptrs );
|
||||
else if (*ptr < 0xc8) /* alloc_m */
|
||||
context->Sp += 16 * (val & 0x7ff);
|
||||
else if (*ptr < 0xcc) /* save_regp */
|
||||
restore_regs( 19 + ((val >> 6) & 0xf), save_next, val & 0x3f, context, ptrs );
|
||||
else if (*ptr < 0xd0) /* save_regp_x */
|
||||
restore_regs( 19 + ((val >> 6) & 0xf), save_next, -(val & 0x3f) - 1, context, ptrs );
|
||||
else if (*ptr < 0xd4) /* save_reg */
|
||||
restore_regs( 19 + ((val >> 6) & 0xf), 1, val & 0x3f, context, ptrs );
|
||||
else if (*ptr < 0xd6) /* save_reg_x */
|
||||
restore_regs( 19 + ((val >> 5) & 0xf), 1, -(val & 0x1f) - 1, context, ptrs );
|
||||
else if (*ptr < 0xd8) /* save_lrpair */
|
||||
{
|
||||
restore_regs( 19 + 2 * ((val >> 6) & 0x7), 1, val & 0x3f, context, ptrs );
|
||||
restore_regs( 30, 1, (val & 0x3f) + 1, context, ptrs );
|
||||
}
|
||||
else if (*ptr < 0xda) /* save_fregp */
|
||||
restore_fpregs( 8 + ((val >> 6) & 0x7), save_next, val & 0x3f, context, ptrs );
|
||||
else if (*ptr < 0xdc) /* save_fregp_x */
|
||||
restore_fpregs( 8 + ((val >> 6) & 0x7), save_next, -(val & 0x3f) - 1, context, ptrs );
|
||||
else if (*ptr < 0xde) /* save_freg */
|
||||
restore_fpregs( 8 + ((val >> 6) & 0x7), 1, val & 0x3f, context, ptrs );
|
||||
else if (*ptr == 0xde) /* save_freg_x */
|
||||
restore_fpregs( 8 + ((val >> 5) & 0x7), 1, -(val & 0x3f) - 1, context, ptrs );
|
||||
else if (*ptr == 0xe0) /* alloc_l */
|
||||
context->Sp += 16 * ((ptr[1] << 16) + (ptr[2] << 8) + ptr[3]);
|
||||
else if (*ptr == 0xe1) /* set_fp */
|
||||
context->Sp = context->Fp;
|
||||
else if (*ptr == 0xe2) /* add_fp */
|
||||
context->Sp = context->Fp - 8 * (val & 0xff);
|
||||
else if (*ptr == 0xe3) /* nop */
|
||||
/* nop */ ;
|
||||
else if (*ptr == 0xe4) /* end */
|
||||
break;
|
||||
else if (*ptr == 0xe5) /* end_c */
|
||||
break;
|
||||
else if (*ptr == 0xe6) /* save_next */
|
||||
{
|
||||
save_next += 2;
|
||||
ptr += len;
|
||||
continue;
|
||||
}
|
||||
else if (*ptr == 0xe9) /* MSFT_OP_MACHINE_FRAME */
|
||||
{
|
||||
context->Pc = ((DWORD64 *)context->Sp)[1];
|
||||
context->Sp = ((DWORD64 *)context->Sp)[0];
|
||||
context->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
|
||||
}
|
||||
else if (*ptr == 0xea) /* MSFT_OP_CONTEXT */
|
||||
{
|
||||
memcpy( context, (DWORD64 *)context->Sp, sizeof(CONTEXT) );
|
||||
}
|
||||
else if (*ptr == 0xfc) /* pac_sign_lr */
|
||||
{
|
||||
do_pac_auth( context );
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN( "unsupported code %02x\n", *ptr );
|
||||
return;
|
||||
}
|
||||
save_next = 2;
|
||||
ptr += len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* unwind_packed_data
|
||||
*/
|
||||
static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION *func,
|
||||
CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
int i;
|
||||
unsigned int len, offset, skip = 0;
|
||||
unsigned int int_size = func->RegI * 8, fp_size = func->RegF * 8, regsave, local_size;
|
||||
unsigned int int_regs, fp_regs, saved_regs, local_size_regs;
|
||||
|
||||
TRACE( "function %I64x-%I64x: len=%#x flag=%x regF=%u regI=%u H=%u CR=%u frame=%x\n",
|
||||
base + func->BeginAddress, base + func->BeginAddress + func->FunctionLength * 4,
|
||||
func->FunctionLength, func->Flag, func->RegF, func->RegI, func->H, func->CR, func->FrameSize );
|
||||
|
||||
if (func->CR == 1) int_size += 8;
|
||||
if (func->RegF) fp_size += 8;
|
||||
|
||||
regsave = ((int_size + fp_size + 8 * 8 * func->H) + 0xf) & ~0xf;
|
||||
local_size = func->FrameSize * 16 - regsave;
|
||||
|
||||
int_regs = int_size / 8;
|
||||
fp_regs = fp_size / 8;
|
||||
saved_regs = regsave / 8;
|
||||
local_size_regs = local_size / 8;
|
||||
|
||||
/* check for prolog/epilog */
|
||||
if (func->Flag == 1)
|
||||
{
|
||||
offset = ((pc - base) - func->BeginAddress) / 4;
|
||||
if (offset < 17 || offset >= func->FunctionLength - 15)
|
||||
{
|
||||
len = (int_size + 8) / 16 + (fp_size + 8) / 16;
|
||||
switch (func->CR)
|
||||
{
|
||||
case 2:
|
||||
len++; /* pacibsp */
|
||||
/* fall through */
|
||||
case 3:
|
||||
len++; /* mov x29,sp */
|
||||
len++; /* stp x29,lr,[sp,0] */
|
||||
if (local_size <= 512) break;
|
||||
/* fall through */
|
||||
case 0:
|
||||
case 1:
|
||||
if (local_size) len++; /* sub sp,sp,#local_size */
|
||||
if (local_size > 4088) len++; /* sub sp,sp,#4088 */
|
||||
break;
|
||||
}
|
||||
len += 4 * func->H;
|
||||
if (offset < len) /* prolog */
|
||||
{
|
||||
skip = len - offset;
|
||||
}
|
||||
else if (offset >= func->FunctionLength - (len + 1)) /* epilog */
|
||||
{
|
||||
skip = offset - (func->FunctionLength - (len + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
if (func->CR == 3 || func->CR == 2)
|
||||
{
|
||||
DWORD64 *fp = (DWORD64 *) context->Fp; /* X[29] */
|
||||
context->Sp = context->Fp;
|
||||
context->X[29] = fp[0];
|
||||
context->X[30] = fp[1];
|
||||
}
|
||||
context->Sp += local_size;
|
||||
if (fp_size) restore_fpregs( 8, fp_regs, int_regs, context, ptrs );
|
||||
if (func->CR == 1) restore_regs( 30, 1, int_regs - 1, context, ptrs );
|
||||
restore_regs( 19, func->RegI, -saved_regs, context, ptrs );
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
|
||||
switch (func->CR)
|
||||
{
|
||||
case 3:
|
||||
case 2:
|
||||
/* mov x29,sp */
|
||||
if (pos++ >= skip) context->Sp = context->Fp;
|
||||
if (local_size <= 512)
|
||||
{
|
||||
/* stp x29,lr,[sp,-#local_size]! */
|
||||
if (pos++ >= skip) restore_regs( 29, 2, -local_size_regs, context, ptrs );
|
||||
break;
|
||||
}
|
||||
/* stp x29,lr,[sp,0] */
|
||||
if (pos++ >= skip) restore_regs( 29, 2, 0, context, ptrs );
|
||||
/* fall through */
|
||||
case 0:
|
||||
case 1:
|
||||
if (!local_size) break;
|
||||
/* sub sp,sp,#local_size */
|
||||
if (pos++ >= skip) context->Sp += (local_size - 1) % 4088 + 1;
|
||||
if (local_size > 4088 && pos++ >= skip) context->Sp += 4088;
|
||||
break;
|
||||
}
|
||||
|
||||
if (func->H) pos += 4;
|
||||
|
||||
if (fp_size)
|
||||
{
|
||||
if (func->RegF % 2 == 0 && pos++ >= skip)
|
||||
/* str d%u,[sp,#fp_size] */
|
||||
restore_fpregs( 8 + func->RegF, 1, int_regs + fp_regs - 1, context, ptrs );
|
||||
for (i = (func->RegF + 1) / 2 - 1; i >= 0; i--)
|
||||
{
|
||||
if (pos++ < skip) continue;
|
||||
if (!i && !int_size)
|
||||
/* stp d8,d9,[sp,-#regsave]! */
|
||||
restore_fpregs( 8, 2, -saved_regs, context, ptrs );
|
||||
else
|
||||
/* stp dn,dn+1,[sp,#offset] */
|
||||
restore_fpregs( 8 + 2 * i, 2, int_regs + 2 * i, context, ptrs );
|
||||
}
|
||||
}
|
||||
|
||||
if (func->RegI % 2)
|
||||
{
|
||||
if (pos++ >= skip)
|
||||
{
|
||||
/* stp xn,lr,[sp,#offset] */
|
||||
if (func->CR == 1) restore_regs( 30, 1, int_regs - 1, context, ptrs );
|
||||
/* str xn,[sp,#offset] */
|
||||
restore_regs( 18 + func->RegI, 1,
|
||||
(func->RegI > 1) ? func->RegI - 1 : -saved_regs,
|
||||
context, ptrs );
|
||||
}
|
||||
}
|
||||
else if (func->CR == 1)
|
||||
{
|
||||
/* str lr,[sp,#offset] */
|
||||
if (pos++ >= skip) restore_regs( 30, 1, func->RegI ? int_regs - 1 : -saved_regs, context, ptrs );
|
||||
}
|
||||
|
||||
for (i = func->RegI / 2 - 1; i >= 0; i--)
|
||||
{
|
||||
if (pos++ < skip) continue;
|
||||
if (i)
|
||||
/* stp xn,xn+1,[sp,#offset] */
|
||||
restore_regs( 19 + 2 * i, 2, 2 * i, context, ptrs );
|
||||
else
|
||||
/* stp x19,x20,[sp,-#regsave]! */
|
||||
restore_regs( 19, 2, -saved_regs, context, ptrs );
|
||||
}
|
||||
}
|
||||
if (func->CR == 2) do_pac_auth( context );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* unwind_full_data
|
||||
*/
|
||||
static void *unwind_full_data( ULONG_PTR base, ULONG_PTR pc, RUNTIME_FUNCTION *func,
|
||||
CONTEXT *context, PVOID *handler_data, KNONVOLATILE_CONTEXT_POINTERS *ptrs )
|
||||
{
|
||||
IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *info;
|
||||
struct unwind_info_epilog *info_epilog;
|
||||
unsigned int i, codes, epilogs, len, offset;
|
||||
void *data;
|
||||
BYTE *end;
|
||||
|
||||
info = (IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *)((char *)base + func->UnwindData);
|
||||
data = info + 1;
|
||||
epilogs = info->EpilogCount;
|
||||
codes = info->CodeWords;
|
||||
if (!codes && !epilogs)
|
||||
{
|
||||
struct unwind_info_ext *infoex = data;
|
||||
codes = infoex->codes;
|
||||
epilogs = infoex->epilog;
|
||||
data = infoex + 1;
|
||||
}
|
||||
info_epilog = data;
|
||||
if (!info->EpilogInHeader) data = info_epilog + epilogs;
|
||||
|
||||
offset = ((pc - base) - func->BeginAddress) / 4;
|
||||
end = (BYTE *)data + codes * 4;
|
||||
|
||||
TRACE( "function %I64x-%I64x: len=%#x ver=%u X=%u E=%u epilogs=%u codes=%u\n",
|
||||
base + func->BeginAddress, base + func->BeginAddress + info->FunctionLength * 4,
|
||||
info->FunctionLength, info->Version, info->ExceptionDataPresent, info->EpilogInHeader,
|
||||
epilogs, codes * 4 );
|
||||
|
||||
/* check for prolog */
|
||||
if (offset < codes * 4)
|
||||
{
|
||||
len = get_sequence_len( data, end );
|
||||
if (offset < len)
|
||||
{
|
||||
process_unwind_codes( data, end, context, ptrs, len - offset );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* check for epilog */
|
||||
if (!info->EpilogInHeader)
|
||||
{
|
||||
for (i = 0; i < epilogs; i++)
|
||||
{
|
||||
if (offset < info_epilog[i].offset) break;
|
||||
if (offset - info_epilog[i].offset < codes * 4 - info_epilog[i].index)
|
||||
{
|
||||
BYTE *ptr = (BYTE *)data + info_epilog[i].index;
|
||||
len = get_sequence_len( ptr, end );
|
||||
if (offset <= info_epilog[i].offset + len)
|
||||
{
|
||||
process_unwind_codes( ptr, end, context, ptrs, offset - info_epilog[i].offset );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (info->FunctionLength - offset <= codes * 4 - epilogs)
|
||||
{
|
||||
BYTE *ptr = (BYTE *)data + epilogs;
|
||||
len = get_sequence_len( ptr, end ) + 1;
|
||||
if (offset >= info->FunctionLength - len)
|
||||
{
|
||||
process_unwind_codes( ptr, end, context, ptrs, offset - (info->FunctionLength - len) );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
process_unwind_codes( data, end, context, ptrs, 0 );
|
||||
|
||||
/* get handler since we are inside the main code */
|
||||
if (info->ExceptionDataPresent)
|
||||
{
|
||||
DWORD *handler_rva = (DWORD *)data + codes;
|
||||
*handler_data = handler_rva + 1;
|
||||
return (char *)base + *handler_rva;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* find_function_info
|
||||
*/
|
||||
static RUNTIME_FUNCTION *find_function_info( ULONG_PTR pc, ULONG_PTR base,
|
||||
RUNTIME_FUNCTION *func, ULONG size )
|
||||
{
|
||||
int min = 0;
|
||||
int max = size - 1;
|
||||
|
||||
while (min <= max)
|
||||
{
|
||||
int pos = (min + max) / 2;
|
||||
ULONG_PTR start = base + func[pos].BeginAddress;
|
||||
|
||||
if (pc >= start)
|
||||
{
|
||||
ULONG len = func[pos].Flag ? func[pos].FunctionLength :
|
||||
((IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *)(base + func[pos].UnwindData))->FunctionLength;
|
||||
if (pc < start + 4 * len) return func + pos;
|
||||
min = pos + 1;
|
||||
}
|
||||
else max = pos - 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlVirtualUnwind (NTDLL.@)
|
||||
*/
|
||||
PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc,
|
||||
RUNTIME_FUNCTION *func, CONTEXT *context,
|
||||
PVOID *handler_data, ULONG_PTR *frame_ret,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
|
||||
{
|
||||
void *handler;
|
||||
|
||||
TRACE( "type %lx pc %I64x sp %I64x func %I64x\n", type, pc, context->Sp, base + func->BeginAddress );
|
||||
|
||||
*handler_data = NULL;
|
||||
|
||||
context->Pc = 0;
|
||||
if (func->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: pc=%I64x lr=%I64x sp=%I64x handler=%p\n", context->Pc, context->Lr, context->Sp, handler );
|
||||
if (!context->Pc)
|
||||
{
|
||||
context->Pc = context->Lr;
|
||||
context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
|
||||
}
|
||||
*frame_ret = context->Sp;
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlLookupFunctionTable (NTDLL.@)
|
||||
*/
|
||||
|
@ -1085,50 +592,6 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionTable( ULONG_PTR pc, ULONG_PTR *base,
|
|||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlLookupFunctionEntry (NTDLL.@)
|
||||
*/
|
||||
PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG_PTR pc, ULONG_PTR *base,
|
||||
UNWIND_HISTORY_TABLE *table )
|
||||
{
|
||||
RUNTIME_FUNCTION *func;
|
||||
ULONG_PTR dynbase;
|
||||
ULONG size;
|
||||
|
||||
if ((func = RtlLookupFunctionTable( pc, base, &size )))
|
||||
return find_function_info( pc, *base, func, size / sizeof(*func));
|
||||
|
||||
if ((func = lookup_dynamic_function_table( pc, &dynbase, &size )))
|
||||
{
|
||||
RUNTIME_FUNCTION *ret = find_function_info( pc, dynbase, func, size );
|
||||
if (ret) *base = dynbase;
|
||||
return ret;
|
||||
}
|
||||
|
||||
*base = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlAddFunctionTable (NTDLL.@)
|
||||
*/
|
||||
BOOLEAN CDECL RtlAddFunctionTable( RUNTIME_FUNCTION *table, DWORD count, ULONG_PTR base )
|
||||
{
|
||||
ULONG_PTR end = base;
|
||||
void *ret;
|
||||
|
||||
if (count)
|
||||
{
|
||||
RUNTIME_FUNCTION *func = table + count - 1;
|
||||
ULONG len = func->Flag ? func->FunctionLength :
|
||||
((IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *)(base + func->UnwindData))->FunctionLength;
|
||||
end += func->BeginAddress + 4 * len;
|
||||
}
|
||||
return !RtlAddGrowableFunctionTable( &ret, table, count, 0, base, end );
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* call_consolidate_callback
|
||||
*
|
||||
|
|
|
@ -229,52 +229,6 @@ static void context_arm_to_x64( CONTEXT *ctx, const ARM64_NT_CONTEXT *arm_ctx )
|
|||
memcpy( ec_ctx->V, arm_ctx->V, sizeof(ec_ctx->V) );
|
||||
}
|
||||
|
||||
static RUNTIME_FUNCTION *find_function_info( ULONG_PTR pc, ULONG_PTR base,
|
||||
RUNTIME_FUNCTION *func, ULONG size )
|
||||
{
|
||||
int min = 0;
|
||||
int max = size - 1;
|
||||
|
||||
while (min <= max)
|
||||
{
|
||||
int pos = (min + max) / 2;
|
||||
if (pc < base + func[pos].BeginAddress) max = pos - 1;
|
||||
else if (pc >= base + func[pos].EndAddress) min = pos + 1;
|
||||
else
|
||||
{
|
||||
func += pos;
|
||||
while (func->UnwindData & 1) /* follow chained entry */
|
||||
func = (RUNTIME_FUNCTION *)(base + (func->UnwindData & ~1));
|
||||
return func;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static ARM64_RUNTIME_FUNCTION *find_function_info_arm64( ULONG_PTR pc, ULONG_PTR base,
|
||||
ARM64_RUNTIME_FUNCTION *func, ULONG size )
|
||||
{
|
||||
int min = 0;
|
||||
int max = size - 1;
|
||||
|
||||
while (min <= max)
|
||||
{
|
||||
int pos = (min + max) / 2;
|
||||
ULONG_PTR start = base + func[pos].BeginAddress;
|
||||
|
||||
if (pc >= start)
|
||||
{
|
||||
ULONG len = func[pos].Flag ? func[pos].FunctionLength :
|
||||
((IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA *)(base + func[pos].UnwindData))->FunctionLength;
|
||||
if (pc < start + 4 * len) return func + pos;
|
||||
min = pos + 1;
|
||||
}
|
||||
else max = pos - 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* syscalls
|
||||
|
@ -1857,60 +1811,6 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionTable( ULONG_PTR pc, ULONG_PTR *base,
|
|||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlLookupFunctionEntry (NTDLL.@)
|
||||
*/
|
||||
PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG_PTR pc, ULONG_PTR *base,
|
||||
UNWIND_HISTORY_TABLE *table )
|
||||
{
|
||||
RUNTIME_FUNCTION *func;
|
||||
ULONG_PTR dynbase;
|
||||
ULONG size;
|
||||
|
||||
if ((func = RtlLookupFunctionTable( pc, base, &size )))
|
||||
{
|
||||
if (RtlIsEcCode( (void *)pc ))
|
||||
{
|
||||
ARM64_RUNTIME_FUNCTION *arm64func = (ARM64_RUNTIME_FUNCTION *)func;
|
||||
size /= sizeof(*arm64func);
|
||||
return (RUNTIME_FUNCTION *)find_function_info_arm64( pc, *base, arm64func, size );
|
||||
}
|
||||
return find_function_info( pc, *base, func, size / sizeof(*func));
|
||||
}
|
||||
|
||||
if ((func = lookup_dynamic_function_table( pc, &dynbase, &size )))
|
||||
{
|
||||
RUNTIME_FUNCTION *ret;
|
||||
|
||||
if (RtlIsEcCode( (void *)pc ))
|
||||
ret = (RUNTIME_FUNCTION *)find_function_info_arm64( pc, dynbase,
|
||||
(ARM64_RUNTIME_FUNCTION *)func, size );
|
||||
else
|
||||
ret = find_function_info( pc, dynbase, func, size );
|
||||
|
||||
if (ret) *base = dynbase;
|
||||
return ret;
|
||||
}
|
||||
|
||||
*base = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlAddFunctionTable (NTDLL.@)
|
||||
*/
|
||||
BOOLEAN CDECL RtlAddFunctionTable( RUNTIME_FUNCTION *table, DWORD count, ULONG_PTR base )
|
||||
{
|
||||
ULONG_PTR end = base;
|
||||
void *ret;
|
||||
|
||||
if (count) end += table[count - 1].EndAddress;
|
||||
|
||||
return !RtlAddGrowableFunctionTable( &ret, table, count, 0, base, end );
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* RtlUnwindEx (NTDLL.@)
|
||||
*/
|
||||
|
|
|
@ -77,165 +77,6 @@ ALL_SYSCALLS64
|
|||
#undef SYSCALL_ENTRY
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Definitions for Win32 unwind tables
|
||||
*/
|
||||
|
||||
union handler_data
|
||||
{
|
||||
RUNTIME_FUNCTION chain;
|
||||
ULONG handler;
|
||||
};
|
||||
|
||||
struct opcode
|
||||
{
|
||||
BYTE offset;
|
||||
BYTE code : 4;
|
||||
BYTE info : 4;
|
||||
};
|
||||
|
||||
struct UNWIND_INFO
|
||||
{
|
||||
BYTE version : 3;
|
||||
BYTE flags : 5;
|
||||
BYTE prolog;
|
||||
BYTE count;
|
||||
BYTE frame_reg : 4;
|
||||
BYTE frame_offset : 4;
|
||||
struct opcode opcodes[1]; /* info->count entries */
|
||||
/* followed by handler_data */
|
||||
};
|
||||
|
||||
#define UWOP_PUSH_NONVOL 0
|
||||
#define UWOP_ALLOC_LARGE 1
|
||||
#define UWOP_ALLOC_SMALL 2
|
||||
#define UWOP_SET_FPREG 3
|
||||
#define UWOP_SAVE_NONVOL 4
|
||||
#define UWOP_SAVE_NONVOL_FAR 5
|
||||
#define UWOP_EPILOG 6
|
||||
#define UWOP_SAVE_XMM128 8
|
||||
#define UWOP_SAVE_XMM128_FAR 9
|
||||
#define UWOP_PUSH_MACHFRAME 10
|
||||
|
||||
static void dump_unwind_info( ULONG64 base, RUNTIME_FUNCTION *function )
|
||||
{
|
||||
static const char * const reg_names[16] =
|
||||
{ "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
|
||||
|
||||
union handler_data *handler_data;
|
||||
struct UNWIND_INFO *info;
|
||||
unsigned int i, count;
|
||||
|
||||
TRACE( "**** func %lx-%lx\n", function->BeginAddress, function->EndAddress );
|
||||
for (;;)
|
||||
{
|
||||
if (function->UnwindData & 1)
|
||||
{
|
||||
RUNTIME_FUNCTION *next = (RUNTIME_FUNCTION *)((char *)base + (function->UnwindData & ~1));
|
||||
TRACE( "unwind info for function %p-%p chained to function %p-%p\n",
|
||||
(char *)base + function->BeginAddress, (char *)base + function->EndAddress,
|
||||
(char *)base + next->BeginAddress, (char *)base + next->EndAddress );
|
||||
function = next;
|
||||
continue;
|
||||
}
|
||||
info = (struct UNWIND_INFO *)((char *)base + function->UnwindData);
|
||||
|
||||
TRACE( "unwind info at %p flags %x prolog 0x%x bytes function %p-%p\n",
|
||||
info, info->flags, info->prolog,
|
||||
(char *)base + function->BeginAddress, (char *)base + function->EndAddress );
|
||||
|
||||
if (info->frame_reg)
|
||||
TRACE( " frame register %s offset 0x%x(%%rsp)\n",
|
||||
reg_names[info->frame_reg], info->frame_offset * 16 );
|
||||
|
||||
for (i = 0; i < info->count; i++)
|
||||
{
|
||||
TRACE( " 0x%x: ", info->opcodes[i].offset );
|
||||
switch (info->opcodes[i].code)
|
||||
{
|
||||
case UWOP_PUSH_NONVOL:
|
||||
TRACE( "pushq %%%s\n", reg_names[info->opcodes[i].info] );
|
||||
break;
|
||||
case UWOP_ALLOC_LARGE:
|
||||
if (info->opcodes[i].info)
|
||||
{
|
||||
count = *(DWORD *)&info->opcodes[i+1];
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = *(USHORT *)&info->opcodes[i+1] * 8;
|
||||
i++;
|
||||
}
|
||||
TRACE( "subq $0x%x,%%rsp\n", count );
|
||||
break;
|
||||
case UWOP_ALLOC_SMALL:
|
||||
count = (info->opcodes[i].info + 1) * 8;
|
||||
TRACE( "subq $0x%x,%%rsp\n", count );
|
||||
break;
|
||||
case UWOP_SET_FPREG:
|
||||
TRACE( "leaq 0x%x(%%rsp),%s\n",
|
||||
info->frame_offset * 16, reg_names[info->frame_reg] );
|
||||
break;
|
||||
case UWOP_SAVE_NONVOL:
|
||||
count = *(USHORT *)&info->opcodes[i+1] * 8;
|
||||
TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count );
|
||||
i++;
|
||||
break;
|
||||
case UWOP_SAVE_NONVOL_FAR:
|
||||
count = *(DWORD *)&info->opcodes[i+1];
|
||||
TRACE( "movq %%%s,0x%x(%%rsp)\n", reg_names[info->opcodes[i].info], count );
|
||||
i += 2;
|
||||
break;
|
||||
case UWOP_SAVE_XMM128:
|
||||
count = *(USHORT *)&info->opcodes[i+1] * 16;
|
||||
TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count );
|
||||
i++;
|
||||
break;
|
||||
case UWOP_SAVE_XMM128_FAR:
|
||||
count = *(DWORD *)&info->opcodes[i+1];
|
||||
TRACE( "movaps %%xmm%u,0x%x(%%rsp)\n", info->opcodes[i].info, count );
|
||||
i += 2;
|
||||
break;
|
||||
case UWOP_PUSH_MACHFRAME:
|
||||
TRACE( "PUSH_MACHFRAME %u\n", info->opcodes[i].info );
|
||||
break;
|
||||
case UWOP_EPILOG:
|
||||
if (info->version == 2)
|
||||
{
|
||||
unsigned int offset;
|
||||
if (info->opcodes[i].info)
|
||||
offset = info->opcodes[i].offset;
|
||||
else
|
||||
offset = (info->opcodes[i+1].info << 8) + info->opcodes[i+1].offset;
|
||||
TRACE("epilog %p-%p\n", (char *)base + function->EndAddress - offset,
|
||||
(char *)base + function->EndAddress - offset + info->opcodes[i].offset );
|
||||
i += 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
FIXME( "unknown code %u\n", info->opcodes[i].code );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handler_data = (union handler_data *)&info->opcodes[(info->count + 1) & ~1];
|
||||
if (info->flags & UNW_FLAG_CHAININFO)
|
||||
{
|
||||
TRACE( " chained to function %p-%p\n",
|
||||
(char *)base + handler_data->chain.BeginAddress,
|
||||
(char *)base + handler_data->chain.EndAddress );
|
||||
function = &handler_data->chain;
|
||||
continue;
|
||||
}
|
||||
if (info->flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
|
||||
TRACE( " handler %p data at %p\n",
|
||||
(char *)base + handler_data->handler, &handler_data->handler + 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_scope_table( ULONG64 base, const SCOPE_TABLE *table )
|
||||
{
|
||||
unsigned int i;
|
||||
|
@ -249,28 +90,6 @@ static void dump_scope_table( ULONG64 base, const SCOPE_TABLE *table )
|
|||
(char *)base + table->ScopeRecord[i].JumpTarget );
|
||||
}
|
||||
|
||||
static RUNTIME_FUNCTION *find_function_info( ULONG_PTR pc, ULONG_PTR base,
|
||||
RUNTIME_FUNCTION *func, ULONG size )
|
||||
{
|
||||
int min = 0;
|
||||
int max = size - 1;
|
||||
|
||||
while (min <= max)
|
||||
{
|
||||
int pos = (min + max) / 2;
|
||||
if (pc < base + func[pos].BeginAddress) max = pos - 1;
|
||||
else if (pc >= base + func[pos].EndAddress) min = pos + 1;
|
||||
else
|
||||
{
|
||||
func += pos;
|
||||
while (func->UnwindData & 1) /* follow chained entry */
|
||||
func = (RUNTIME_FUNCTION *)(base + (func->UnwindData & ~1));
|
||||
return func;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* virtual_unwind
|
||||
|
@ -731,316 +550,6 @@ BOOLEAN WINAPI RtlIsEcCode( const void *ptr )
|
|||
}
|
||||
|
||||
|
||||
static ULONG64 get_int_reg( CONTEXT *context, int reg )
|
||||
{
|
||||
return *(&context->Rax + reg);
|
||||
}
|
||||
|
||||
static void set_int_reg( CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr, int reg, ULONG64 *val )
|
||||
{
|
||||
*(&context->Rax + reg) = *val;
|
||||
if (ctx_ptr) ctx_ptr->IntegerContext[reg] = val;
|
||||
}
|
||||
|
||||
static void set_float_reg( CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr, int reg, M128A *val )
|
||||
{
|
||||
/* Use a memcpy() to avoid issues if val is misaligned. */
|
||||
memcpy(&context->Xmm0 + reg, val, sizeof(*val));
|
||||
if (ctx_ptr) ctx_ptr->FloatingContext[reg] = val;
|
||||
}
|
||||
|
||||
static int get_opcode_size( struct opcode op )
|
||||
{
|
||||
switch (op.code)
|
||||
{
|
||||
case UWOP_ALLOC_LARGE:
|
||||
return 2 + (op.info != 0);
|
||||
case UWOP_SAVE_NONVOL:
|
||||
case UWOP_SAVE_XMM128:
|
||||
case UWOP_EPILOG:
|
||||
return 2;
|
||||
case UWOP_SAVE_NONVOL_FAR:
|
||||
case UWOP_SAVE_XMM128_FAR:
|
||||
return 3;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL is_inside_epilog( BYTE *pc, ULONG64 base, const RUNTIME_FUNCTION *function )
|
||||
{
|
||||
/* 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 (;;)
|
||||
{
|
||||
if ((*pc & 0xf0) == 0x40) pc++; /* 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;
|
||||
case 0xe9: /* jmp nnnn */
|
||||
pc += 5 + *(LONG *)(pc + 1);
|
||||
if (pc - (BYTE *)base >= function->BeginAddress && pc - (BYTE *)base < function->EndAddress)
|
||||
continue;
|
||||
break;
|
||||
case 0xeb: /* jmp n */
|
||||
pc += 2 + (signed char)pc[1];
|
||||
if (pc - (BYTE *)base >= function->BeginAddress && pc - (BYTE *)base < function->EndAddress)
|
||||
continue;
|
||||
break;
|
||||
case 0xf3: /* rep; ret (for amd64 prediction bug) */
|
||||
return pc[1] == 0xc3;
|
||||
}
|
||||
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 */
|
||||
case 0xf3: /* rep; ret */
|
||||
context->Rip = *(ULONG64 *)context->Rsp;
|
||||
context->Rsp += sizeof(ULONG64);
|
||||
return;
|
||||
case 0xe9: /* jmp nnnn */
|
||||
pc += 5 + *(LONG *)(pc + 1);
|
||||
continue;
|
||||
case 0xeb: /* jmp n */
|
||||
pc += 2 + (signed char)pc[1];
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* RtlVirtualUnwind (NTDLL.@)
|
||||
*/
|
||||
PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG64 base, ULONG64 pc,
|
||||
RUNTIME_FUNCTION *function, CONTEXT *context,
|
||||
PVOID *data, ULONG64 *frame_ret,
|
||||
KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
|
||||
{
|
||||
union handler_data *handler_data;
|
||||
ULONG64 frame, off;
|
||||
struct UNWIND_INFO *info;
|
||||
unsigned int i, prolog_offset;
|
||||
BOOL mach_frame = FALSE;
|
||||
|
||||
TRACE( "type %lx rip %I64x rsp %I64x\n", type, pc, context->Rsp );
|
||||
if (TRACE_ON(seh)) dump_unwind_info( base, function );
|
||||
|
||||
frame = *frame_ret = context->Rsp;
|
||||
for (;;)
|
||||
{
|
||||
info = (struct UNWIND_INFO *)((char *)base + function->UnwindData);
|
||||
handler_data = (union handler_data *)&info->opcodes[(info->count + 1) & ~1];
|
||||
|
||||
if (info->version != 1 && info->version != 2)
|
||||
{
|
||||
FIXME( "unknown unwind info version %u at %p\n", info->version, info );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (info->frame_reg)
|
||||
frame = get_int_reg( context, info->frame_reg ) - info->frame_offset * 16;
|
||||
|
||||
/* check if in prolog */
|
||||
if (pc >= base + function->BeginAddress && pc < base + function->BeginAddress + info->prolog)
|
||||
{
|
||||
TRACE("inside prolog.\n");
|
||||
prolog_offset = pc - base - function->BeginAddress;
|
||||
}
|
||||
else
|
||||
{
|
||||
prolog_offset = ~0;
|
||||
/* Since Win10 1809 epilogue does not have a special treatment in case of zero opcode count. */
|
||||
if (info->count && is_inside_epilog( (BYTE *)pc, base, function ))
|
||||
{
|
||||
TRACE("inside epilog.\n");
|
||||
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]))
|
||||
{
|
||||
if (prolog_offset < info->opcodes[i].offset) continue; /* skip it */
|
||||
|
||||
switch (info->opcodes[i].code)
|
||||
{
|
||||
case UWOP_PUSH_NONVOL: /* pushq %reg */
|
||||
set_int_reg( context, ctx_ptr, info->opcodes[i].info, (ULONG64 *)context->Rsp );
|
||||
context->Rsp += sizeof(ULONG64);
|
||||
break;
|
||||
case UWOP_ALLOC_LARGE: /* subq $nn,%rsp */
|
||||
if (info->opcodes[i].info) context->Rsp += *(DWORD *)&info->opcodes[i+1];
|
||||
else context->Rsp += *(USHORT *)&info->opcodes[i+1] * 8;
|
||||
break;
|
||||
case UWOP_ALLOC_SMALL: /* subq $n,%rsp */
|
||||
context->Rsp += (info->opcodes[i].info + 1) * 8;
|
||||
break;
|
||||
case UWOP_SET_FPREG: /* leaq nn(%rsp),%framereg */
|
||||
context->Rsp = *frame_ret = frame;
|
||||
break;
|
||||
case UWOP_SAVE_NONVOL: /* movq %reg,n(%rsp) */
|
||||
off = frame + *(USHORT *)&info->opcodes[i+1] * 8;
|
||||
set_int_reg( context, ctx_ptr, info->opcodes[i].info, (ULONG64 *)off );
|
||||
break;
|
||||
case UWOP_SAVE_NONVOL_FAR: /* movq %reg,nn(%rsp) */
|
||||
off = frame + *(DWORD *)&info->opcodes[i+1];
|
||||
set_int_reg( context, ctx_ptr, info->opcodes[i].info, (ULONG64 *)off );
|
||||
break;
|
||||
case UWOP_SAVE_XMM128: /* movaps %xmmreg,n(%rsp) */
|
||||
off = frame + *(USHORT *)&info->opcodes[i+1] * 16;
|
||||
set_float_reg( context, ctx_ptr, info->opcodes[i].info, (M128A *)off );
|
||||
break;
|
||||
case UWOP_SAVE_XMM128_FAR: /* movaps %xmmreg,nn(%rsp) */
|
||||
off = frame + *(DWORD *)&info->opcodes[i+1];
|
||||
set_float_reg( context, ctx_ptr, info->opcodes[i].info, (M128A *)off );
|
||||
break;
|
||||
case UWOP_PUSH_MACHFRAME:
|
||||
if (info->flags & UNW_FLAG_CHAININFO)
|
||||
{
|
||||
FIXME("PUSH_MACHFRAME with chained unwind info.\n");
|
||||
break;
|
||||
}
|
||||
if (i + get_opcode_size(info->opcodes[i]) < info->count )
|
||||
{
|
||||
FIXME("PUSH_MACHFRAME is not the last opcode.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (info->opcodes[i].info)
|
||||
context->Rsp += 0x8;
|
||||
|
||||
context->Rip = *(ULONG64 *)context->Rsp;
|
||||
context->Rsp = *(ULONG64 *)(context->Rsp + 24);
|
||||
mach_frame = TRUE;
|
||||
break;
|
||||
case UWOP_EPILOG:
|
||||
if (info->version == 2)
|
||||
break; /* nothing to do */
|
||||
default:
|
||||
FIXME( "unknown code %u\n", info->opcodes[i].code );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(info->flags & UNW_FLAG_CHAININFO)) break;
|
||||
function = &handler_data->chain; /* restart with the chained info */
|
||||
}
|
||||
|
||||
if (!mach_frame)
|
||||
{
|
||||
/* now pop return address */
|
||||
context->Rip = *(ULONG64 *)context->Rsp;
|
||||
context->Rsp += sizeof(ULONG64);
|
||||
}
|
||||
|
||||
if (!(info->flags & type)) return NULL; /* no matching handler */
|
||||
if (prolog_offset != ~0) return NULL; /* inside prolog */
|
||||
|
||||
*data = &handler_data->handler + 1;
|
||||
return (char *)base + handler_data->handler;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlLookupFunctionTable (NTDLL.@)
|
||||
*/
|
||||
|
@ -1054,45 +563,6 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionTable( ULONG_PTR pc, ULONG_PTR *base,
|
|||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlLookupFunctionEntry (NTDLL.@)
|
||||
*/
|
||||
PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG_PTR pc, ULONG_PTR *base,
|
||||
UNWIND_HISTORY_TABLE *table )
|
||||
{
|
||||
RUNTIME_FUNCTION *func;
|
||||
ULONG_PTR dynbase;
|
||||
ULONG size;
|
||||
|
||||
if ((func = RtlLookupFunctionTable( pc, base, &size )))
|
||||
return find_function_info( pc, *base, func, size / sizeof(*func));
|
||||
|
||||
if ((func = lookup_dynamic_function_table( pc, &dynbase, &size )))
|
||||
{
|
||||
RUNTIME_FUNCTION *ret = find_function_info( pc, dynbase, func, size );
|
||||
if (ret) *base = dynbase;
|
||||
return ret;
|
||||
}
|
||||
|
||||
*base = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* RtlAddFunctionTable (NTDLL.@)
|
||||
*/
|
||||
BOOLEAN CDECL RtlAddFunctionTable( RUNTIME_FUNCTION *table, DWORD count, ULONG_PTR base )
|
||||
{
|
||||
ULONG_PTR end = base;
|
||||
void *ret;
|
||||
|
||||
if (count) end += table[count - 1].EndAddress;
|
||||
|
||||
return !RtlAddGrowableFunctionTable( &ret, table, count, 0, base, end );
|
||||
}
|
||||
|
||||
|
||||
struct unwind_exception_frame
|
||||
{
|
||||
EXCEPTION_REGISTRATION_RECORD frame;
|
||||
|
|
1685
dlls/ntdll/unwind.c
Normal file
1685
dlls/ntdll/unwind.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue