Commit a5a25382 authored by Alexandre Julliard's avatar Alexandre Julliard

ntdll: Move exception unwinding code to a separate file.

parent d88f92eb
......@@ -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.@)
......@@ -1184,49 +601,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
*
* Wrapper function to call a consolidate callback from a fake frame.
......
......@@ -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.@)
*/
......@@ -1086,50 +593,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
*
* Wrapper function to call a consolidate callback from a fake frame.
......
......@@ -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;
......
/*
* Exception unwinding
*
* Copyright 1999, 2005, 2019, 2024 Alexandre Julliard
* Copyright 2021 Martin Storsjö
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __i386__
#include <stdlib.h>
#include <stdarg.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"
#include "wine/exception.h"
#include "ntdll_misc.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(unwind);
/***********************************************************************
* ARM64 support
*/
#if defined(__aarch64__) || defined(__arm64ec__)
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
};
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;
}
static void restore_regs( int reg, int count, int pos, ARM64_NT_CONTEXT *context,
KNONVOLATILE_CONTEXT_POINTERS_ARM64 *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;
}
static void restore_fpregs( int reg, int count, int pos, ARM64_NT_CONTEXT *context,
KNONVOLATILE_CONTEXT_POINTERS_ARM64 *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( ARM64_NT_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;
}
static void process_unwind_codes( BYTE *ptr, BYTE *end, ARM64_NT_CONTEXT *context,
KNONVOLATILE_CONTEXT_POINTERS_ARM64 *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;
}
}
static void *unwind_packed_data( ULONG_PTR base, ULONG_PTR pc, ARM64_RUNTIME_FUNCTION *func,
ARM64_NT_CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS_ARM64 *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;
}
static void *unwind_full_data( ULONG_PTR base, ULONG_PTR pc, ARM64_RUNTIME_FUNCTION *func,
ARM64_NT_CONTEXT *context, PVOID *handler_data,
KNONVOLATILE_CONTEXT_POINTERS_ARM64 *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;
}
static ARM64_RUNTIME_FUNCTION *find_function_info_arm64( ULONG_PTR pc, ULONG_PTR base,
ARM64_RUNTIME_FUNCTION *func, ULONG count )
{
int min = 0;
int max = count - 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;
}
#ifdef __arm64ec__
#define RtlVirtualUnwind RtlVirtualUnwind_arm64
#define RtlLookupFunctionEntry RtlLookupFunctionEntry_arm64
#endif
/**********************************************************************
* RtlVirtualUnwind (NTDLL.@)
*/
PVOID WINAPI RtlVirtualUnwind( ULONG type, ULONG_PTR base, ULONG_PTR pc,
ARM64_RUNTIME_FUNCTION *func, ARM64_NT_CONTEXT *context,
PVOID *handler_data, ULONG_PTR *frame_ret,
KNONVOLATILE_CONTEXT_POINTERS_ARM64 *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;
}
/**********************************************************************
* RtlLookupFunctionEntry (NTDLL.@)
*/
PARM64_RUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG_PTR pc, ULONG_PTR *base,
UNWIND_HISTORY_TABLE *table )
{
ARM64_RUNTIME_FUNCTION *func;
ULONG_PTR dynbase;
ULONG size;
if ((func = (ARM64_RUNTIME_FUNCTION *)RtlLookupFunctionTable( pc, base, &size )))
return find_function_info_arm64( pc, *base, func, size / sizeof(*func));
if ((func = (ARM64_RUNTIME_FUNCTION *)lookup_dynamic_function_table( pc, &dynbase, &size )))
{
ARM64_RUNTIME_FUNCTION *ret = find_function_info_arm64( pc, dynbase, func, size );
if (ret) *base = dynbase;
return ret;
}
*base = 0;
return NULL;
}
/**********************************************************************
* RtlAddFunctionTable (NTDLL.@)
*/
#ifndef __arm64ec__
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 );
}
#else
#undef RtlVirtualUnwind
#undef RtlLookupFunctionEntry
#endif
#endif /* __aarch64__ */
/***********************************************************************
* ARM support
*/
#ifdef __arm__
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
};
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;
}
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;
}
}
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;
}
}
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;
}
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;
}
}
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;
}
}
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;
}
}
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;
}
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;
}
static RUNTIME_FUNCTION *find_function_info( ULONG_PTR pc, ULONG_PTR base,
RUNTIME_FUNCTION *func, ULONG count )
{
int min = 0;
int max = count - 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;
}
/**********************************************************************
* 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 );
}
#endif /* __arm__ */
/***********************************************************************
* x86-64 support
*/
#ifdef __x86_64__
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 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;
}
}
static RUNTIME_FUNCTION *find_function_info( ULONG_PTR pc, ULONG_PTR base,
RUNTIME_FUNCTION *func, ULONG count )
{
int min = 0;
int max = count - 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;
}
/**********************************************************************
* 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(unwind)) 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;
}
/**********************************************************************
* RtlLookupFunctionEntry (NTDLL.@)
*/
PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG_PTR pc, ULONG_PTR *base,
UNWIND_HISTORY_TABLE *table )
{
RUNTIME_FUNCTION *func;
ULONG_PTR dynbase;
ULONG size;
#ifdef __arm64ec__
if (RtlIsEcCode( (void *)pc ))
return (RUNTIME_FUNCTION *)RtlLookupFunctionEntry_arm64( pc, base, table );
#endif
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 );
}
#endif /* __x86_64__ */
#endif /* !__i386__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment