ntdll: Remove support for unwinding ELF dlls on ARM.
This commit is contained in:
parent
654c03d131
commit
2778e45421
1 changed files with 1 additions and 432 deletions
|
@ -256,444 +256,13 @@ static BOOL is_inside_syscall( ucontext_t *sigcontext )
|
|||
(char *)SP_sig(sigcontext) <= (char *)arm_thread_data()->syscall_frame);
|
||||
}
|
||||
|
||||
struct exidx_entry
|
||||
{
|
||||
uint32_t addr;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
static uint32_t prel31_to_abs(const uint32_t *ptr)
|
||||
{
|
||||
uint32_t prel31 = *ptr;
|
||||
uint32_t rel = prel31 | ((prel31 << 1) & 0x80000000);
|
||||
return (uintptr_t)ptr + rel;
|
||||
}
|
||||
|
||||
static uint8_t get_byte(const uint32_t *ptr, int offset, int bytes)
|
||||
{
|
||||
int word = offset >> 2;
|
||||
int byte = offset & 0x3;
|
||||
if (offset >= bytes)
|
||||
return 0xb0; /* finish opcode */
|
||||
return (ptr[word] >> (24 - 8*byte)) & 0xff;
|
||||
}
|
||||
|
||||
static uint32_t get_uleb128(const uint32_t *ptr, int *offset, int bytes)
|
||||
{
|
||||
int shift = 0;
|
||||
uint32_t val = 0;
|
||||
while (1)
|
||||
{
|
||||
uint8_t byte = get_byte(ptr, (*offset)++, bytes);
|
||||
val |= (byte & 0x7f) << shift;
|
||||
if ((byte & 0x80) == 0)
|
||||
break;
|
||||
shift += 7;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pop_regs(CONTEXT *context, uint32_t regs)
|
||||
{
|
||||
int i;
|
||||
DWORD new_sp = 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (regs & (1U << i))
|
||||
{
|
||||
DWORD val = *(DWORD *)context->Sp;
|
||||
if (i != 13)
|
||||
(&context->R0)[i] = val;
|
||||
else
|
||||
new_sp = val;
|
||||
context->Sp += 4;
|
||||
}
|
||||
}
|
||||
if (regs & (1 << 13))
|
||||
context->Sp = new_sp;
|
||||
}
|
||||
|
||||
static void pop_vfp(CONTEXT *context, int first, int last)
|
||||
{
|
||||
int i;
|
||||
for (i = first; i <= last; i++)
|
||||
{
|
||||
context->D[i] = *(ULONGLONG *)context->Sp;
|
||||
context->Sp += 8;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t regmask(int first_bit, int n_bits)
|
||||
{
|
||||
return ((1U << (n_bits + 1)) - 1) << first_bit;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* ehabi_virtual_unwind
|
||||
*/
|
||||
static NTSTATUS ehabi_virtual_unwind( UINT ip, DWORD *frame, CONTEXT *context,
|
||||
const struct exidx_entry *entry,
|
||||
PEXCEPTION_ROUTINE *handler, void **handler_data )
|
||||
{
|
||||
const uint32_t *ptr;
|
||||
const void *lsda = NULL;
|
||||
int compact_inline = 0;
|
||||
int offset = 0;
|
||||
int bytes = 0;
|
||||
int personality;
|
||||
int extra_words;
|
||||
int finish = 0;
|
||||
int set_pc = 0;
|
||||
UINT func_begin = prel31_to_abs(&entry->addr);
|
||||
|
||||
*frame = context->Sp;
|
||||
|
||||
TRACE( "ip %#x function %#x\n", ip, func_begin );
|
||||
|
||||
if (entry->data == 1)
|
||||
{
|
||||
ERR("EXIDX_CANTUNWIND\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
else if (entry->data & 0x80000000)
|
||||
{
|
||||
if ((entry->data & 0x7f000000) != 0)
|
||||
{
|
||||
ERR("compact inline EXIDX must have personality 0\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
ptr = &entry->data;
|
||||
compact_inline = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = (uint32_t *)prel31_to_abs(&entry->data);
|
||||
}
|
||||
|
||||
if ((*ptr & 0x80000000) == 0)
|
||||
{
|
||||
/* Generic */
|
||||
void *personality_func = (void *)prel31_to_abs(ptr);
|
||||
int words = (ptr[1] >> 24) & 0xff;
|
||||
lsda = ptr + 1 + words + 1;
|
||||
|
||||
ERR("generic EHABI unwinding not supported\n");
|
||||
(void)personality_func;
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/* Compact */
|
||||
|
||||
personality = (*ptr >> 24) & 0x0f;
|
||||
switch (personality)
|
||||
{
|
||||
case 0:
|
||||
if (!compact_inline)
|
||||
lsda = ptr + 1;
|
||||
extra_words = 0;
|
||||
offset = 1;
|
||||
break;
|
||||
case 1:
|
||||
extra_words = (*ptr >> 16) & 0xff;
|
||||
lsda = ptr + extra_words + 1;
|
||||
offset = 2;
|
||||
break;
|
||||
case 2:
|
||||
extra_words = (*ptr >> 16) & 0xff;
|
||||
lsda = ptr + extra_words + 1;
|
||||
offset = 2;
|
||||
break;
|
||||
default:
|
||||
ERR("unsupported compact EXIDX personality %d\n", personality);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/* Not inspecting the descriptors */
|
||||
(void)lsda;
|
||||
|
||||
bytes = 4 + 4*extra_words;
|
||||
while (offset < bytes && !finish)
|
||||
{
|
||||
uint8_t byte = get_byte(ptr, offset++, bytes);
|
||||
if ((byte & 0xc0) == 0x00)
|
||||
{
|
||||
/* Increment Sp */
|
||||
context->Sp += (byte & 0x3f) * 4 + 4;
|
||||
}
|
||||
else if ((byte & 0xc0) == 0x40)
|
||||
{
|
||||
/* Decrement Sp */
|
||||
context->Sp -= (byte & 0x3f) * 4 + 4;
|
||||
}
|
||||
else if ((byte & 0xf0) == 0x80)
|
||||
{
|
||||
/* Pop {r4-r15} based on register mask */
|
||||
int regs = ((byte & 0x0f) << 8) | get_byte(ptr, offset++, bytes);
|
||||
if (!regs)
|
||||
{
|
||||
ERR("refuse to unwind\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
regs <<= 4;
|
||||
pop_regs(context, regs);
|
||||
if (regs & (1 << 15))
|
||||
set_pc = 1;
|
||||
}
|
||||
else if ((byte & 0xf0) == 0x90)
|
||||
{
|
||||
/* Restore Sp from other register */
|
||||
int reg = byte & 0x0f;
|
||||
if (reg == 13 || reg == 15)
|
||||
{
|
||||
ERR("reserved opcode\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
context->Sp = (&context->R0)[reg];
|
||||
}
|
||||
else if ((byte & 0xf0) == 0xa0)
|
||||
{
|
||||
/* Pop r4-r(4+n) (+lr) */
|
||||
int n = byte & 0x07;
|
||||
int regs = regmask(4, n);
|
||||
if (byte & 0x08)
|
||||
regs |= 1 << 14;
|
||||
pop_regs(context, regs);
|
||||
}
|
||||
else if (byte == 0xb0)
|
||||
{
|
||||
finish = 1;
|
||||
}
|
||||
else if (byte == 0xb1)
|
||||
{
|
||||
/* Pop {r0-r3} based on register mask */
|
||||
int regs = get_byte(ptr, offset++, bytes);
|
||||
if (regs == 0 || (regs & 0xf0) != 0)
|
||||
{
|
||||
ERR("spare opcode\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
pop_regs(context, regs);
|
||||
}
|
||||
else if (byte == 0xb2)
|
||||
{
|
||||
/* Increment Sp by a larger amount */
|
||||
int imm = get_uleb128(ptr, &offset, bytes);
|
||||
context->Sp += 0x204 + imm * 4;
|
||||
}
|
||||
else if (byte == 0xb3)
|
||||
{
|
||||
/* Pop VFP registers as if saved by FSTMFDX; this opcode
|
||||
* is deprecated. */
|
||||
ERR("FSTMFDX unsupported\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
else if ((byte & 0xfc) == 0xb4)
|
||||
{
|
||||
ERR("spare opcode\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
else if ((byte & 0xf8) == 0xb8)
|
||||
{
|
||||
/* Pop VFP registers as if saved by FSTMFDX; this opcode
|
||||
* is deprecated. */
|
||||
ERR("FSTMFDX unsupported\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
else if ((byte & 0xf8) == 0xc0)
|
||||
{
|
||||
ERR("spare opcode / iWMMX\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
else if ((byte & 0xfe) == 0xc8)
|
||||
{
|
||||
/* Pop VFP registers d(16+ssss)-d(16+ssss+cccc), or
|
||||
* d(0+ssss)-d(0+ssss+cccc) as if saved by VPUSH */
|
||||
int first, last;
|
||||
if ((byte & 0x01) == 0)
|
||||
first = 16;
|
||||
else
|
||||
first = 0;
|
||||
byte = get_byte(ptr, offset++, bytes);
|
||||
first += (byte & 0xf0) >> 4;
|
||||
last = first + (byte & 0x0f);
|
||||
if (last >= 32)
|
||||
{
|
||||
ERR("reserved opcode\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
pop_vfp(context, first, last);
|
||||
}
|
||||
else if ((byte & 0xf8) == 0xc8)
|
||||
{
|
||||
ERR("spare opcode\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
else if ((byte & 0xf8) == 0xd0)
|
||||
{
|
||||
/* Pop VFP registers d8-d(8+n) as if saved by VPUSH */
|
||||
int n = byte & 0x07;
|
||||
pop_vfp(context, 8, 8 + n);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("spare opcode\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
if (offset > bytes)
|
||||
{
|
||||
ERR("truncated opcodes\n");
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
*handler = NULL; /* personality */
|
||||
*handler_data = NULL; /* lsda */
|
||||
|
||||
if (!set_pc)
|
||||
{
|
||||
context->Pc = context->Lr;
|
||||
context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
|
||||
}
|
||||
else context->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
|
||||
|
||||
TRACE( "next function pc=%08lx\n", context->Pc );
|
||||
TRACE(" r0=%08lx r1=%08lx r2=%08lx r3=%08lx\n",
|
||||
context->R0, context->R1, context->R2, context->R3 );
|
||||
TRACE(" r4=%08lx r5=%08lx r6=%08lx r7=%08lx\n",
|
||||
context->R4, context->R5, context->R6, context->R7 );
|
||||
TRACE(" r8=%08lx r9=%08lx r10=%08lx r11=%08lx\n",
|
||||
context->R8, context->R9, context->R10, context->R11 );
|
||||
TRACE(" r12=%08lx sp=%08lx lr=%08lx pc=%08lx\n",
|
||||
context->R12, context->Sp, context->Lr, context->Pc );
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef linux
|
||||
struct iterate_data
|
||||
{
|
||||
ULONG_PTR ip;
|
||||
int failed;
|
||||
struct exidx_entry *entry;
|
||||
};
|
||||
|
||||
static int contains_addr(struct dl_phdr_info *info, const ElfW(Phdr) *phdr, struct iterate_data *data)
|
||||
{
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
return 0;
|
||||
return data->ip >= info->dlpi_addr + phdr->p_vaddr && data->ip < info->dlpi_addr + phdr->p_vaddr + phdr->p_memsz;
|
||||
}
|
||||
|
||||
static int check_exidx(struct dl_phdr_info *info, size_t info_size, void *arg)
|
||||
{
|
||||
struct iterate_data *data = arg;
|
||||
int i;
|
||||
int found_addr;
|
||||
const ElfW(Phdr) *exidx = NULL;
|
||||
struct exidx_entry *begin, *end;
|
||||
|
||||
if (info->dlpi_phnum == 0 || data->ip < info->dlpi_addr || data->failed)
|
||||
return 0;
|
||||
|
||||
found_addr = 0;
|
||||
for (i = 0; i < info->dlpi_phnum; i++)
|
||||
{
|
||||
const ElfW(Phdr) *phdr = &info->dlpi_phdr[i];
|
||||
if (contains_addr(info, phdr, data))
|
||||
found_addr = 1;
|
||||
if (phdr->p_type == PT_ARM_EXIDX)
|
||||
exidx = phdr;
|
||||
}
|
||||
|
||||
if (!found_addr || !exidx)
|
||||
{
|
||||
if (found_addr)
|
||||
{
|
||||
TRACE("found matching address in %s, but no EXIDX\n", info->dlpi_name);
|
||||
data->failed = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
begin = (struct exidx_entry *)(info->dlpi_addr + exidx->p_vaddr);
|
||||
end = (struct exidx_entry *)(info->dlpi_addr + exidx->p_vaddr + exidx->p_memsz);
|
||||
if (data->ip < prel31_to_abs(&begin->addr))
|
||||
{
|
||||
TRACE("%lx before EXIDX start at %x\n", data->ip, prel31_to_abs(&begin->addr));
|
||||
data->failed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (begin + 1 < end)
|
||||
{
|
||||
struct exidx_entry *mid = begin + (end - begin)/2;
|
||||
uint32_t abs_addr = prel31_to_abs(&mid->addr);
|
||||
if (abs_addr > data->ip)
|
||||
{
|
||||
end = mid;
|
||||
}
|
||||
else if (abs_addr < data->ip)
|
||||
{
|
||||
begin = mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
begin = mid;
|
||||
end = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
data->entry = begin;
|
||||
TRACE("found %lx in %s, base %x, entry %p with addr %x (rel %x) data %x\n",
|
||||
data->ip, info->dlpi_name, info->dlpi_addr, begin,
|
||||
prel31_to_abs(&begin->addr),
|
||||
prel31_to_abs(&begin->addr) - info->dlpi_addr, begin->data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct exidx_entry *find_exidx_entry( void *ip )
|
||||
{
|
||||
struct iterate_data data = {};
|
||||
|
||||
data.ip = (ULONG_PTR)ip;
|
||||
data.failed = 0;
|
||||
data.entry = NULL;
|
||||
dl_iterate_phdr(check_exidx, &data);
|
||||
|
||||
return data.entry;
|
||||
}
|
||||
#endif
|
||||
|
||||
/***********************************************************************
|
||||
* unwind_builtin_dll
|
||||
*/
|
||||
NTSTATUS unwind_builtin_dll( void *args )
|
||||
{
|
||||
struct unwind_builtin_dll_params *params = args;
|
||||
DISPATCHER_CONTEXT *dispatch = params->dispatch;
|
||||
CONTEXT *context = params->context;
|
||||
DWORD ip = context->Pc - (dispatch->ControlPcIsUnwound ? 2 : 0);
|
||||
#ifdef linux
|
||||
const struct exidx_entry *entry = find_exidx_entry( (void *)ip );
|
||||
NTSTATUS status;
|
||||
|
||||
if (entry)
|
||||
return ehabi_virtual_unwind( ip, &dispatch->EstablisherFrame, context, entry,
|
||||
&dispatch->LanguageHandler, &dispatch->HandlerData );
|
||||
|
||||
status = context->Pc != context->Lr ?
|
||||
STATUS_SUCCESS : STATUS_INVALID_DISPOSITION;
|
||||
TRACE( "no info found for %lx, %s\n", ip, status == STATUS_SUCCESS ?
|
||||
"assuming leaf function" : "error, stuck" );
|
||||
dispatch->LanguageHandler = NULL;
|
||||
dispatch->EstablisherFrame = context->Sp;
|
||||
context->Pc = context->Lr;
|
||||
context->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
|
||||
return status;
|
||||
#else
|
||||
ERR("ARM EHABI unwinding available, unable to unwind\n");
|
||||
return STATUS_INVALID_DISPOSITION;
|
||||
#endif
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue