wine/dlls/ntdll/signal_arm.c

746 lines
30 KiB
C

/*
* ARM signal handling routines
*
* Copyright 2002 Marcus Meissner, SuSE Linux AG
* Copyright 2010-2013, 2015 André Hentschel
*
* 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
*/
#ifdef __arm__
#include <stdlib.h>
#include <stdarg.h>
#include <setjmp.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"
#include "ntsyscalls.h"
WINE_DEFAULT_DEBUG_CHANNEL(seh);
WINE_DECLARE_DEBUG_CHANNEL(relay);
/* undocumented, copied from the corresponding ARM64 structure */
typedef union _DISPATCHER_CONTEXT_NONVOLREG_ARM
{
BYTE Buffer[8 * sizeof(DWORD) + 8 * sizeof(double)];
struct
{
DWORD GpNvRegs[8];
double FpNvRegs[8];
} DUMMYSTRUCTNAME;
} DISPATCHER_CONTEXT_NONVOLREG_ARM;
/*******************************************************************
* syscalls
*/
#define SYSCALL_ENTRY(id,name,args) __ASM_SYSCALL_FUNC( id, name, args )
ALL_SYSCALLS32
DEFINE_SYSCALL_HELPER32()
#undef SYSCALL_ENTRY
/**************************************************************************
* __chkstk (NTDLL.@)
*
* Incoming r4 contains words to allocate, converting to bytes then return
*/
__ASM_GLOBAL_FUNC( __chkstk, "lsl r4, r4, #2\n\t"
"bx lr" )
/***********************************************************************
* RtlCaptureContext (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( RtlCaptureContext,
"str r1, [r0, #0x8]\n\t" /* context->R1 */
"mov r1, #0x0200000\n\t" /* CONTEXT_ARM */
"add r1, r1, #0x7\n\t" /* CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_FLOATING_POINT */
"str r1, [r0]\n\t" /* context->ContextFlags */
"str SP, [r0, #0x38]\n\t" /* context->Sp */
"str LR, [r0, #0x40]\n\t" /* context->Pc */
"mrs r1, CPSR\n\t"
"bfi r1, lr, #5, #1\n\t" /* Thumb bit */
"str r1, [r0, #0x44]\n\t" /* context->Cpsr */
"mov r1, #0\n\t"
"str r1, [r0, #0x4]\n\t" /* context->R0 */
"str r1, [r0, #0x3c]\n\t" /* context->Lr */
"add r0, #0x0c\n\t"
"stm r0, {r2-r12}\n\t" /* context->R2..R12 */
#ifndef __SOFTFP__
"add r0, #0x44\n\t" /* 0x50 - 0x0c */
"vstm r0, {d0-d15}\n\t" /* context->D0-D15 */
#endif
"bx lr" )
/**********************************************************************
* virtual_unwind
*/
static NTSTATUS virtual_unwind( ULONG type, DISPATCHER_CONTEXT *dispatch, CONTEXT *context )
{
DISPATCHER_CONTEXT_NONVOLREG_ARM *nonvol_regs;
DWORD pc = context->Pc;
dispatch->ScopeIndex = 0;
dispatch->ControlPc = pc;
dispatch->ControlPcIsUnwound = (context->ContextFlags & CONTEXT_UNWOUND_TO_CALL) != 0;
if (dispatch->ControlPcIsUnwound) pc -= 2;
nonvol_regs = (DISPATCHER_CONTEXT_NONVOLREG_ARM *)dispatch->NonVolatileRegisters;
memcpy( nonvol_regs->GpNvRegs, &context->R4, sizeof(nonvol_regs->GpNvRegs) );
memcpy( nonvol_regs->FpNvRegs, &context->D[8], sizeof(nonvol_regs->FpNvRegs) );
dispatch->FunctionEntry = RtlLookupFunctionEntry( pc, (DWORD_PTR *)&dispatch->ImageBase,
dispatch->HistoryTable );
if (RtlVirtualUnwind2( type, dispatch->ImageBase, pc, dispatch->FunctionEntry, context,
NULL, &dispatch->HandlerData, (ULONG_PTR *)&dispatch->EstablisherFrame,
NULL, NULL, NULL, &dispatch->LanguageHandler, 0 ))
{
WARN( "exception data not found for pc %p, lr %p\n", (void *)pc, (void *)context->Lr );
return STATUS_INVALID_DISPOSITION;
}
return STATUS_SUCCESS;
}
/**********************************************************************
* unwind_exception_handler
*
* Handler for exceptions happening while calling an unwind handler.
*/
EXCEPTION_DISPOSITION WINAPI unwind_exception_handler( EXCEPTION_RECORD *record, void *frame,
CONTEXT *context, DISPATCHER_CONTEXT *dispatch )
{
DISPATCHER_CONTEXT *orig_dispatch = ((DISPATCHER_CONTEXT **)frame)[-2];
/* copy the original dispatcher into the current one, except for the TargetIp */
dispatch->ControlPc = orig_dispatch->ControlPc;
dispatch->ImageBase = orig_dispatch->ImageBase;
dispatch->FunctionEntry = orig_dispatch->FunctionEntry;
dispatch->EstablisherFrame = orig_dispatch->EstablisherFrame;
dispatch->LanguageHandler = orig_dispatch->LanguageHandler;
dispatch->HandlerData = orig_dispatch->HandlerData;
dispatch->HistoryTable = orig_dispatch->HistoryTable;
dispatch->ScopeIndex = orig_dispatch->ScopeIndex;
dispatch->ControlPcIsUnwound = orig_dispatch->ControlPcIsUnwound;
*dispatch->ContextRecord = *orig_dispatch->ContextRecord;
memcpy( dispatch->NonVolatileRegisters, orig_dispatch->NonVolatileRegisters,
sizeof(DISPATCHER_CONTEXT_NONVOLREG_ARM) );
TRACE( "detected collided unwind\n" );
return ExceptionCollidedUnwind;
}
/**********************************************************************
* call_unwind_handler
*/
DWORD WINAPI call_unwind_handler( EXCEPTION_RECORD *rec, ULONG_PTR frame,
CONTEXT *context, void *dispatch, PEXCEPTION_ROUTINE handler );
__ASM_GLOBAL_FUNC( call_unwind_handler,
"push {r3,lr}\n\t"
".seh_save_regs {r3,lr}\n\t"
".seh_endprologue\n\t"
".seh_handler unwind_exception_handler, %except\n\t"
"ldr ip, [sp, #8]\n\t" /* handler */
"blx ip\n\t"
"pop {r3,pc}\n\t" )
/***********************************************************************
* call_seh_handler
*/
DWORD WINAPI call_seh_handler( EXCEPTION_RECORD *rec, ULONG_PTR frame,
CONTEXT *context, void *dispatch, PEXCEPTION_ROUTINE handler );
__ASM_GLOBAL_FUNC( call_seh_handler,
"push {r4,lr}\n\t"
".seh_save_regs {r4,lr}\n\t"
".seh_endprologue\n\t"
".seh_handler nested_exception_handler, %except\n\t"
"ldr ip, [sp, #8]\n\t" /* handler */
"blx ip\n\t"
"pop {r4,pc}\n\t" )
/**********************************************************************
* call_seh_handlers
*
* Call the SEH handlers.
*/
NTSTATUS call_seh_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_context )
{
EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
DISPATCHER_CONTEXT_NONVOLREG_ARM nonvol_regs;
UNWIND_HISTORY_TABLE table;
DISPATCHER_CONTEXT dispatch;
CONTEXT context;
NTSTATUS status;
ULONG_PTR frame;
DWORD res;
context = *orig_context;
dispatch.TargetPc = 0;
dispatch.ContextRecord = &context;
dispatch.HistoryTable = &table;
dispatch.NonVolatileRegisters = nonvol_regs.Buffer;
for (;;)
{
status = virtual_unwind( UNW_FLAG_EHANDLER, &dispatch, &context );
if (status != STATUS_SUCCESS) return status;
unwind_done:
if (!dispatch.EstablisherFrame) break;
if (!is_valid_frame( dispatch.EstablisherFrame ))
{
ERR( "invalid frame %lx (%p-%p)\n", dispatch.EstablisherFrame,
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
rec->ExceptionFlags |= EXCEPTION_STACK_INVALID;
break;
}
if (dispatch.LanguageHandler)
{
TRACE( "calling handler %p (rec=%p, frame=%lx context=%p, dispatch=%p)\n",
dispatch.LanguageHandler, rec, dispatch.EstablisherFrame, orig_context, &dispatch );
res = call_seh_handler( rec, dispatch.EstablisherFrame, orig_context,
&dispatch, dispatch.LanguageHandler );
rec->ExceptionFlags &= EXCEPTION_NONCONTINUABLE;
TRACE( "handler at %p returned %lu\n", dispatch.LanguageHandler, res );
switch (res)
{
case ExceptionContinueExecution:
if (rec->ExceptionFlags & EXCEPTION_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION;
return STATUS_SUCCESS;
case ExceptionContinueSearch:
break;
case ExceptionNestedException:
rec->ExceptionFlags |= EXCEPTION_NESTED_CALL;
TRACE( "nested exception\n" );
break;
case ExceptionCollidedUnwind:
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
dispatch.ControlPc, dispatch.FunctionEntry,
&context, (PVOID *)&dispatch.HandlerData, &frame, NULL );
goto unwind_done;
default:
return STATUS_INVALID_DISPOSITION;
}
}
/* hack: call wine handlers registered in the tib list */
else while (is_valid_frame( (ULONG_PTR)teb_frame ) && (DWORD)teb_frame < context.Sp)
{
TRACE( "calling TEB handler %p (rec=%p frame=%p context=%p dispatch=%p) sp=%lx\n",
teb_frame->Handler, rec, teb_frame, orig_context, &dispatch, context.Sp );
res = call_seh_handler( rec, (ULONG_PTR)teb_frame, orig_context,
&dispatch, (PEXCEPTION_ROUTINE)teb_frame->Handler );
TRACE( "TEB handler at %p returned %lu\n", teb_frame->Handler, res );
switch (res)
{
case ExceptionContinueExecution:
if (rec->ExceptionFlags & EXCEPTION_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION;
return STATUS_SUCCESS;
case ExceptionContinueSearch:
break;
case ExceptionNestedException:
rec->ExceptionFlags |= EXCEPTION_NESTED_CALL;
TRACE( "nested exception\n" );
break;
case ExceptionCollidedUnwind:
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
dispatch.ControlPc, dispatch.FunctionEntry,
&context, (PVOID *)&dispatch.HandlerData, &frame, NULL );
teb_frame = teb_frame->Prev;
goto unwind_done;
default:
return STATUS_INVALID_DISPOSITION;
}
teb_frame = teb_frame->Prev;
}
if (context.Sp == (DWORD)NtCurrentTeb()->Tib.StackBase) break;
}
return STATUS_UNHANDLED_EXCEPTION;
}
/*******************************************************************
* KiUserExceptionDispatcher (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( KiUserExceptionDispatcher,
".seh_custom 0xee,0x02\n\t" /* MSFT_OP_CONTEXT */
".seh_endprologue\n\t"
"add r0, sp, #0x1a0\n\t" /* rec (context + 1) */
"mov r1, sp\n\t" /* context */
"bl " __ASM_NAME("dispatch_exception") "\n\t"
"udf #1" )
/*******************************************************************
* KiUserApcDispatcher (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( KiUserApcDispatcher,
".seh_custom 0xee,0x02\n\t" /* MSFT_OP_CONTEXT */
"nop\n\t"
".seh_stackalloc 0x18\n\t"
".seh_endprologue\n\t"
"ldr r0, [sp, #0x04]\n\t" /* arg1 */
"ldr r1, [sp, #0x08]\n\t" /* arg2 */
"ldr r2, [sp, #0x0c]\n\t" /* arg3 */
"ldr ip, [sp]\n\t" /* func */
"blx ip\n\t"
"add r0, sp, #0x18\n\t" /* context */
"ldr r1, [sp, #0x10]\n\t" /* alertable */
"bl " __ASM_NAME("NtContinue") "\n\t"
"udf #1" )
/*******************************************************************
* KiUserCallbackDispatcher (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( KiUserCallbackDispatcher,
".seh_custom 0xee,0x01\n\t" /* MSFT_OP_MACHINE_FRAME */
"nop\n\t"
".seh_save_regs {lr}\n\t"
"nop\n\t"
".seh_stackalloc 0xc\n\t"
".seh_endprologue\n\t"
"ldr r0, [sp]\n\t" /* args */
"ldr r1, [sp, #0x04]\n\t" /* len */
"ldr r2, [sp, #0x08]\n\t" /* id */
"mrc p15, 0, r3, c13, c0, 2\n\t" /* NtCurrentTeb() */
"ldr r3, [r3, 0x30]\n\t" /* peb */
"ldr r3, [r3, 0x2c]\n\t" /* peb->KernelCallbackTable */
"ldr ip, [r3, r2, lsl #2]\n\t"
"blx ip\n\t"
".seh_handler " __ASM_NAME("user_callback_handler") ", %except\n\t"
".globl " __ASM_NAME("KiUserCallbackDispatcherReturn") "\n"
__ASM_NAME("KiUserCallbackDispatcherReturn") ":\n\t"
"mov r2, r0\n\t" /* status */
"mov r1, #0\n\t" /* ret_len */
"mov r0, r1\n\t" /* ret_ptr */
"bl " __ASM_NAME("NtCallbackReturn") "\n\t"
"bl " __ASM_NAME("RtlRaiseStatus") "\n\t"
"udf #1" )
/**********************************************************************
* consolidate_callback
*
* Wrapper function to call a consolidate callback from a fake frame.
* If the callback executes RtlUnwindEx (like for example done in C++ handlers),
* we have to skip all frames which were already processed. To do that we
* trick the unwinding functions into thinking the call came from somewhere
* else.
*/
void WINAPI DECLSPEC_NORETURN consolidate_callback( CONTEXT *context,
void *(CALLBACK *callback)(EXCEPTION_RECORD *),
EXCEPTION_RECORD *rec );
__ASM_GLOBAL_FUNC( consolidate_callback,
"push {r0-r2,lr}\n\t"
".seh_nop\n\t"
"sub sp, sp, #0x1a0\n\t"
".seh_nop\n\t"
"mov r1, r0\n\t"
".seh_nop\n\t"
"mov r0, sp\n\t"
".seh_nop\n\t"
"mov r2, #0x1a0\n\t"
".seh_nop_w\n\t"
"bl memcpy\n\t"
".seh_custom 0xee,0x02\n\t" /* MSFT_OP_CONTEXT */
".seh_endprologue\n\t"
"ldrd r1, r2, [sp, #0x1a4]\n\t"
"mov r0, r2\n\t"
"blx r1\n\t"
"str r0, [sp, #0x40]\n\t" /* context->Pc */
"mov r0, sp\n\t"
"mov r1, #1\n\t"
"b NtContinue" )
/*******************************************************************
* RtlRestoreContext (NTDLL.@)
*/
void CDECL RtlRestoreContext( CONTEXT *context, EXCEPTION_RECORD *rec )
{
EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
if (rec && rec->ExceptionCode == STATUS_LONGJUMP && rec->NumberParameters >= 1)
{
struct _JUMP_BUFFER *jmp = (struct _JUMP_BUFFER *)rec->ExceptionInformation[0];
memcpy( &context->R4, &jmp->R4, 8 * sizeof(DWORD) );
memcpy( &context->D[8], &jmp->D[0], 8 * sizeof(ULONGLONG) );
context->Pc = jmp->Pc;
context->Sp = jmp->Sp;
context->Fpscr = jmp->Fpscr;
}
else if (rec && rec->ExceptionCode == STATUS_UNWIND_CONSOLIDATE && rec->NumberParameters >= 1)
{
PVOID (CALLBACK *consolidate)(EXCEPTION_RECORD *) = (void *)rec->ExceptionInformation[0];
TRACE( "calling consolidate callback %p (rec=%p)\n", consolidate, rec );
consolidate_callback( context, consolidate, rec );
}
/* hack: remove no longer accessible TEB frames */
while (is_valid_frame( (ULONG_PTR)teb_frame ) && (DWORD)teb_frame < context->Sp)
{
TRACE( "removing TEB frame: %p\n", teb_frame );
teb_frame = __wine_pop_frame( teb_frame );
}
TRACE( "returning to %lx stack %lx\n", context->Pc, context->Sp );
NtContinue( context, FALSE );
}
/***********************************************************************
* RtlUnwindEx (NTDLL.@)
*/
void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec,
PVOID retval, CONTEXT *context, UNWIND_HISTORY_TABLE *table )
{
EXCEPTION_REGISTRATION_RECORD *teb_frame = NtCurrentTeb()->Tib.ExceptionList;
DISPATCHER_CONTEXT_NONVOLREG_ARM nonvol_regs;
EXCEPTION_RECORD record;
DISPATCHER_CONTEXT dispatch;
CONTEXT new_context;
NTSTATUS status;
ULONG_PTR frame;
DWORD i, res;
RtlCaptureContext( context );
new_context = *context;
/* build an exception record, if we do not have one */
if (!rec)
{
record.ExceptionCode = STATUS_UNWIND;
record.ExceptionFlags = 0;
record.ExceptionRecord = NULL;
record.ExceptionAddress = (void *)context->Pc;
record.NumberParameters = 0;
rec = &record;
}
rec->ExceptionFlags |= EXCEPTION_UNWINDING | (end_frame ? 0 : EXCEPTION_EXIT_UNWIND);
TRACE( "code=%lx flags=%lx end_frame=%p target_ip=%p\n",
rec->ExceptionCode, rec->ExceptionFlags, end_frame, target_ip );
for (i = 0; i < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); i++)
TRACE( " info[%ld]=%08Ix\n", i, rec->ExceptionInformation[i] );
TRACE_CONTEXT( context );
dispatch.TargetPc = (ULONG_PTR)target_ip;
dispatch.ContextRecord = context;
dispatch.HistoryTable = table;
dispatch.NonVolatileRegisters = nonvol_regs.Buffer;
for (;;)
{
status = virtual_unwind( UNW_FLAG_UHANDLER, &dispatch, &new_context );
if (status != STATUS_SUCCESS) raise_status( status, rec );
unwind_done:
if (!dispatch.EstablisherFrame) break;
if (!is_valid_frame( dispatch.EstablisherFrame ))
{
ERR( "invalid frame %lx (%p-%p)\n", dispatch.EstablisherFrame,
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
rec->ExceptionFlags |= EXCEPTION_STACK_INVALID;
break;
}
if (dispatch.LanguageHandler)
{
if (end_frame && (dispatch.EstablisherFrame > (DWORD)end_frame))
{
ERR( "invalid end frame %lx/%p\n", dispatch.EstablisherFrame, end_frame );
raise_status( STATUS_INVALID_UNWIND_TARGET, rec );
}
if (dispatch.EstablisherFrame == (DWORD)end_frame) rec->ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
TRACE( "calling handler %p (rec=%p, frame=0x%lx context=%p, dispatch=%p)\n",
dispatch.LanguageHandler, rec, dispatch.EstablisherFrame,
dispatch.ContextRecord, &dispatch );
res = call_unwind_handler( rec, dispatch.EstablisherFrame, dispatch.ContextRecord,
&dispatch, dispatch.LanguageHandler );
TRACE( "handler %p returned %lx\n", dispatch.LanguageHandler, res );
switch (res)
{
case ExceptionContinueSearch:
rec->ExceptionFlags &= ~EXCEPTION_COLLIDED_UNWIND;
break;
case ExceptionCollidedUnwind:
new_context = *context;
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
dispatch.ControlPc, dispatch.FunctionEntry,
&new_context, &dispatch.HandlerData, &frame,
NULL );
rec->ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
goto unwind_done;
default:
raise_status( STATUS_INVALID_DISPOSITION, rec );
break;
}
}
else /* hack: call builtin handlers registered in the tib list */
{
while (is_valid_frame( (ULONG_PTR)teb_frame ) &&
(DWORD)teb_frame < new_context.Sp &&
(DWORD)teb_frame < (DWORD)end_frame)
{
TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
teb_frame->Handler, rec, teb_frame, dispatch.ContextRecord, &dispatch );
res = call_unwind_handler( rec, (ULONG_PTR)teb_frame, dispatch.ContextRecord, &dispatch,
(PEXCEPTION_ROUTINE)teb_frame->Handler );
TRACE( "handler at %p returned %lu\n", teb_frame->Handler, res );
teb_frame = __wine_pop_frame( teb_frame );
switch (res)
{
case ExceptionContinueSearch:
rec->ExceptionFlags &= ~EXCEPTION_COLLIDED_UNWIND;
break;
case ExceptionCollidedUnwind:
new_context = *context;
RtlVirtualUnwind( UNW_FLAG_NHANDLER, dispatch.ImageBase,
dispatch.ControlPc, dispatch.FunctionEntry,
&new_context, &dispatch.HandlerData,
&frame, NULL );
rec->ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
goto unwind_done;
default:
raise_status( STATUS_INVALID_DISPOSITION, rec );
break;
}
}
if ((DWORD)teb_frame == (DWORD)end_frame && (DWORD)end_frame < new_context.Sp) break;
}
if (dispatch.EstablisherFrame == (DWORD)end_frame) break;
*context = new_context;
}
if (rec->ExceptionCode != STATUS_UNWIND_CONSOLIDATE)
context->Pc = (DWORD)target_ip;
else if (rec->ExceptionInformation[10] == -1)
rec->ExceptionInformation[10] = (ULONG_PTR)&nonvol_regs;
context->R0 = (DWORD)retval;
RtlRestoreContext(context, rec);
}
/*************************************************************************
* RtlWalkFrameChain (NTDLL.@)
*/
ULONG WINAPI RtlWalkFrameChain( void **buffer, ULONG count, ULONG flags )
{
UNWIND_HISTORY_TABLE table;
RUNTIME_FUNCTION *func;
PEXCEPTION_ROUTINE handler;
ULONG_PTR pc, frame, base;
CONTEXT context;
void *data;
ULONG i, skip = flags >> 8, num_entries = 0;
RtlCaptureContext( &context );
for (i = 0; i < count; i++)
{
pc = context.Pc;
if (context.ContextFlags & CONTEXT_UNWOUND_TO_CALL) pc -= 2;
func = RtlLookupFunctionEntry( pc, &base, &table );
if (RtlVirtualUnwind2( UNW_FLAG_NHANDLER, base, pc, func, &context, NULL,
&data, &frame, NULL, NULL, NULL, &handler, 0 ))
break;
if (!context.Pc) break;
if (!frame || !is_valid_frame( frame )) break;
if (context.Sp == (ULONG_PTR)NtCurrentTeb()->Tib.StackBase) break;
if (i >= skip) buffer[num_entries++] = (void *)context.Pc;
}
return num_entries;
}
/*******************************************************************
* __C_ExecuteExceptionFilter
*/
__ASM_GLOBAL_FUNC( __C_ExecuteExceptionFilter,
"push {r4-r11,lr}\n\t"
".seh_save_regs_w {r4-r11,lr}\n\t"
".seh_endprologue\n\t"
"ldm r3, {r4-r11,lr}\n\t"
"blx r2\n\t"
"pop {r4-r11,pc}\n\t" )
/***********************************************************************
* RtlRaiseException (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( RtlRaiseException,
"push {r0, lr}\n\t"
".seh_save_regs {r0, lr}\n\t"
"sub sp, sp, #0x1a0\n\t" /* sizeof(CONTEXT) */
".seh_stackalloc 0x1a0\n\t"
".seh_endprologue\n\t"
"mov r0, sp\n\t" /* context */
"bl " __ASM_NAME("RtlCaptureContext") "\n\t"
"ldr r0, [sp, #0x1a0]\n\t" /* rec */
"ldr r1, [sp, #0x1a4]\n\t"
"str r1, [sp, #0x3c]\n\t" /* context->Lr */
"str r1, [sp, #0x40]\n\t" /* context->Pc */
"mrs r2, CPSR\n\t"
"bfi r2, r1, #5, #1\n\t" /* Thumb bit */
"str r2, [sp, #0x44]\n\t" /* context->Cpsr */
"str r1, [r0, #12]\n\t" /* rec->ExceptionAddress */
"add r1, sp, #0x1a8\n\t"
"str r1, [sp, #0x38]\n\t" /* context->Sp */
"ldr r1, [sp]\n\t" /* context->ContextFlags */
"orr r1, r1, #0x20000000\n\t" /* CONTEXT_UNWOUND_TO_CALL */
"str r1, [sp]\n\t"
"mov r1, sp\n\t"
"mrc p15, 0, r3, c13, c0, 2\n\t" /* NtCurrentTeb() */
"ldr r3, [r3, #0x30]\n\t" /* peb */
"ldrb r2, [r3, #2]\n\t" /* peb->BeingDebugged */
"cbnz r2, 1f\n\t"
"bl " __ASM_NAME("dispatch_exception") "\n"
"1:\tmov r2, #1\n\t"
"bl " __ASM_NAME("NtRaiseException") "\n\t"
"bl " __ASM_NAME("RtlRaiseStatus") )
/***********************************************************************
* _setjmp (NTDLL.@)
* _setjmpex (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( NTDLL__setjmpex,
".seh_endprologue\n\t"
"stm r0, {r1,r4-r11}\n" /* jmp_buf->Frame,R4..R11 */
"str sp, [r0, #0x24]\n\t" /* jmp_buf->Sp */
"str lr, [r0, #0x28]\n\t" /* jmp_buf->Pc */
"vmrs r2, fpscr\n\t"
"str r2, [r0, #0x2c]\n\t" /* jmp_buf->Fpscr */
"add r0, r0, #0x30\n\t"
"vstm r0, {d8-d15}\n\t" /* jmp_buf->D[0..7] */
"mov r0, #0\n\t"
"bx lr" )
/*******************************************************************
* longjmp (NTDLL.@)
*/
void __cdecl NTDLL_longjmp( _JUMP_BUFFER *buf, int retval )
{
EXCEPTION_RECORD rec;
if (!retval) retval = 1;
rec.ExceptionCode = STATUS_LONGJUMP;
rec.ExceptionFlags = 0;
rec.ExceptionRecord = NULL;
rec.ExceptionAddress = NULL;
rec.NumberParameters = 1;
rec.ExceptionInformation[0] = (DWORD_PTR)buf;
RtlUnwind( (void *)buf->Frame, (void *)buf->Pc, &rec, IntToPtr(retval) );
}
/***********************************************************************
* RtlUserThreadStart (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( RtlUserThreadStart,
"push {r4, lr}\n\t"
".seh_save_regs {r4, lr}\n\t"
".seh_endprologue\n\t"
"mov r2, r1\n\t"
"mov r1, r0\n\t"
"mov r0, #0\n\t"
"ldr ip, 1f\n\t"
"ldr ip, [ip]\n\t"
"blx ip\n\t"
"nop\n"
"1:\t.long " __ASM_NAME("pBaseThreadInitThunk") "\n\t"
".seh_handler " __ASM_NAME("call_unhandled_exception_handler") ", %except" )
/******************************************************************
* LdrInitializeThunk (NTDLL.@)
*/
void WINAPI LdrInitializeThunk( CONTEXT *context, ULONG_PTR unk2, ULONG_PTR unk3, ULONG_PTR unk4 )
{
loader_init( context, (void **)&context->R0 );
TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", (void *)context->R0, (void *)context->R1 );
NtContinue( context, TRUE );
}
/***********************************************************************
* process_breakpoint
*/
__ASM_GLOBAL_FUNC( process_breakpoint,
".seh_endprologue\n\t"
".seh_handler process_breakpoint_handler, %except\n\t"
"udf #0xfe\n\t"
"bx lr\n"
"process_breakpoint_handler:\n\t"
"ldr r0, [r2, #0x40]\n\t" /* context->Pc */
"add r0, r0, #2\n\t"
"str r0, [r2, #0x40]\n\t"
"mov r0, #0\n\t" /* ExceptionContinueExecution */
"bx lr" )
/***********************************************************************
* DbgUiRemoteBreakin (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( DbgUiRemoteBreakin,
".seh_endprologue\n\t"
".seh_handler DbgUiRemoteBreakin_handler, %except\n\t"
"mrc p15, 0, r0, c13, c0, 2\n\t" /* NtCurrentTeb() */
"ldr r0, [r0, #0x30]\n\t" /* NtCurrentTeb()->Peb */
"ldrb r0, [r0, 0x02]\n\t" /* peb->BeingDebugged */
"cbz r0, 1f\n\t"
"bl " __ASM_NAME("DbgBreakPoint") "\n"
"1:\tmov r0, #0\n\t"
"bl " __ASM_NAME("RtlExitUserThread") "\n"
"DbgUiRemoteBreakin_handler:\n\t"
"mov sp, r1\n\t" /* frame */
"b 1b" )
/**********************************************************************
* DbgBreakPoint (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( DbgBreakPoint, "udf #0xfe; bx lr; nop; nop; nop; nop" );
/**********************************************************************
* DbgUserBreakPoint (NTDLL.@)
*/
__ASM_GLOBAL_FUNC( DbgUserBreakPoint, "udf #0xfe; bx lr; nop; nop; nop; nop" );
#endif /* __arm__ */