1679 lines
57 KiB
C
1679 lines
57 KiB
C
/*
|
|
* x86-64 signal handling routines
|
|
*
|
|
* Copyright 1999, 2005 Alexandre Julliard
|
|
*
|
|
* 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 __x86_64__
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <assert.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
# include <sys/param.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_SIGNAL_H
|
|
# include <sys/signal.h>
|
|
#endif
|
|
|
|
#define NONAMELESSUNION
|
|
#define NONAMELESSSTRUCT
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "windef.h"
|
|
#include "winternl.h"
|
|
#include "wine/library.h"
|
|
#include "wine/exception.h"
|
|
#include "ntdll_misc.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(seh);
|
|
|
|
struct _DISPATCHER_CONTEXT;
|
|
|
|
typedef LONG (WINAPI *PC_LANGUAGE_EXCEPTION_HANDLER)( EXCEPTION_POINTERS *ptrs, ULONG64 frame );
|
|
typedef EXCEPTION_DISPOSITION (WINAPI *PEXCEPTION_ROUTINE)( EXCEPTION_RECORD *rec,
|
|
ULONG64 frame,
|
|
CONTEXT *context,
|
|
struct _DISPATCHER_CONTEXT *dispatch );
|
|
|
|
typedef struct _DISPATCHER_CONTEXT
|
|
{
|
|
ULONG64 ControlPc;
|
|
ULONG64 ImageBase;
|
|
PRUNTIME_FUNCTION FunctionEntry;
|
|
ULONG64 EstablisherFrame;
|
|
ULONG64 TargetIp;
|
|
PCONTEXT ContextRecord;
|
|
PEXCEPTION_ROUTINE LanguageHandler;
|
|
PVOID HandlerData;
|
|
PUNWIND_HISTORY_TABLE HistoryTable;
|
|
ULONG ScopeIndex;
|
|
} DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT;
|
|
|
|
typedef struct _SCOPE_TABLE
|
|
{
|
|
ULONG Count;
|
|
struct
|
|
{
|
|
ULONG BeginAddress;
|
|
ULONG EndAddress;
|
|
ULONG HandlerAddress;
|
|
ULONG JumpTarget;
|
|
} ScopeRecord[1];
|
|
} SCOPE_TABLE, *PSCOPE_TABLE;
|
|
|
|
|
|
/***********************************************************************
|
|
* signal context platform-specific definitions
|
|
*/
|
|
#ifdef linux
|
|
|
|
#include <asm/prctl.h>
|
|
extern int arch_prctl(int func, void *ptr);
|
|
|
|
#define RAX_sig(context) ((context)->uc_mcontext.gregs[REG_RAX])
|
|
#define RBX_sig(context) ((context)->uc_mcontext.gregs[REG_RBX])
|
|
#define RCX_sig(context) ((context)->uc_mcontext.gregs[REG_RCX])
|
|
#define RDX_sig(context) ((context)->uc_mcontext.gregs[REG_RDX])
|
|
#define RSI_sig(context) ((context)->uc_mcontext.gregs[REG_RSI])
|
|
#define RDI_sig(context) ((context)->uc_mcontext.gregs[REG_RDI])
|
|
#define RBP_sig(context) ((context)->uc_mcontext.gregs[REG_RBP])
|
|
#define R8_sig(context) ((context)->uc_mcontext.gregs[REG_R8])
|
|
#define R9_sig(context) ((context)->uc_mcontext.gregs[REG_R9])
|
|
#define R10_sig(context) ((context)->uc_mcontext.gregs[REG_R10])
|
|
#define R11_sig(context) ((context)->uc_mcontext.gregs[REG_R11])
|
|
#define R12_sig(context) ((context)->uc_mcontext.gregs[REG_R12])
|
|
#define R13_sig(context) ((context)->uc_mcontext.gregs[REG_R13])
|
|
#define R14_sig(context) ((context)->uc_mcontext.gregs[REG_R14])
|
|
#define R15_sig(context) ((context)->uc_mcontext.gregs[REG_R15])
|
|
|
|
#define CS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 0))
|
|
#define GS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 1))
|
|
#define FS_sig(context) (*((WORD *)&(context)->uc_mcontext.gregs[REG_CSGSFS] + 2))
|
|
|
|
#define RSP_sig(context) ((context)->uc_mcontext.gregs[REG_RSP])
|
|
#define RIP_sig(context) ((context)->uc_mcontext.gregs[REG_RIP])
|
|
#define EFL_sig(context) ((context)->uc_mcontext.gregs[REG_EFL])
|
|
#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
|
|
#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
|
|
|
|
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.fpregs))
|
|
|
|
#endif /* linux */
|
|
|
|
#if defined(__NetBSD__)
|
|
# include <sys/ucontext.h>
|
|
# include <sys/types.h>
|
|
# include <signal.h>
|
|
|
|
#define RAX_sig(context) ((context)->uc_mcontext.__gregs[_REG_RAX])
|
|
#define RBX_sig(context) ((context)->uc_mcontext.__gregs[_REG_RBX])
|
|
#define RCX_sig(context) ((context)->uc_mcontext.__gregs[_REG_RCX])
|
|
#define RDX_sig(context) ((context)->uc_mcontext.__gregs[_REG_RDX])
|
|
#define RSI_sig(context) ((context)->uc_mcontext.__gregs[_REG_RSI])
|
|
#define RDI_sig(context) ((context)->uc_mcontext.__gregs[_REG_RDI])
|
|
#define RBP_sig(context) ((context)->uc_mcontext.__gregs[_REG_RBP])
|
|
#define R8_sig(context) ((context)->uc_mcontext.__gregs[_REG_R8])
|
|
#define R9_sig(context) ((context)->uc_mcontext.__gregs[_REG_R9])
|
|
#define R10_sig(context) ((context)->uc_mcontext.__gregs[_REG_R10])
|
|
#define R11_sig(context) ((context)->uc_mcontext.__gregs[_REG_R11])
|
|
#define R12_sig(context) ((context)->uc_mcontext.__gregs[_REG_R12])
|
|
#define R13_sig(context) ((context)->uc_mcontext.__gregs[_REG_R13])
|
|
#define R14_sig(context) ((context)->uc_mcontext.__gregs[_REG_R14])
|
|
#define R15_sig(context) ((context)->uc_mcontext.__gregs[_REG_R15])
|
|
|
|
#define CS_sig(context) ((context)->uc_mcontext.__gregs[_REG_CS])
|
|
#define DS_sig(context) ((context)->uc_mcontext.__gregs[_REG_DS])
|
|
#define ES_sig(context) ((context)->uc_mcontext.__gregs[_REG_ES])
|
|
#define FS_sig(context) ((context)->uc_mcontext.__gregs[_REG_FS])
|
|
#define GS_sig(context) ((context)->uc_mcontext.__gregs[_REG_GS])
|
|
#define SS_sig(context) ((context)->uc_mcontext.__gregs[_REG_SS])
|
|
|
|
#define EFL_sig(context) ((context)->uc_mcontext.__gregs[_REG_RFL])
|
|
|
|
#define RIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext.__gregs[_REG_RIP]))
|
|
#define RSP_sig(context) (*((unsigned long*)&(context)->uc_mcontext.__gregs[_REG_URSP]))
|
|
|
|
#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
|
|
#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
|
|
|
|
#define FPU_sig(context) ((XMM_SAVE_AREA32 *)((context)->uc_mcontext.__fpregs))
|
|
#endif /* __NetBSD__ */
|
|
|
|
enum i386_trap_code
|
|
{
|
|
TRAP_x86_UNKNOWN = -1, /* Unknown fault (TRAP_sig not defined) */
|
|
TRAP_x86_DIVIDE = 0, /* Division by zero exception */
|
|
TRAP_x86_TRCTRAP = 1, /* Single-step exception */
|
|
TRAP_x86_NMI = 2, /* NMI interrupt */
|
|
TRAP_x86_BPTFLT = 3, /* Breakpoint exception */
|
|
TRAP_x86_OFLOW = 4, /* Overflow exception */
|
|
TRAP_x86_BOUND = 5, /* Bound range exception */
|
|
TRAP_x86_PRIVINFLT = 6, /* Invalid opcode exception */
|
|
TRAP_x86_DNA = 7, /* Device not available exception */
|
|
TRAP_x86_DOUBLEFLT = 8, /* Double fault exception */
|
|
TRAP_x86_FPOPFLT = 9, /* Coprocessor segment overrun */
|
|
TRAP_x86_TSSFLT = 10, /* Invalid TSS exception */
|
|
TRAP_x86_SEGNPFLT = 11, /* Segment not present exception */
|
|
TRAP_x86_STKFLT = 12, /* Stack fault */
|
|
TRAP_x86_PROTFLT = 13, /* General protection fault */
|
|
TRAP_x86_PAGEFLT = 14, /* Page fault */
|
|
TRAP_x86_ARITHTRAP = 16, /* Floating point exception */
|
|
TRAP_x86_ALIGNFLT = 17, /* Alignment check exception */
|
|
TRAP_x86_MCHK = 18, /* Machine check exception */
|
|
TRAP_x86_CACHEFLT = 19 /* Cache flush exception */
|
|
};
|
|
|
|
typedef int (*wine_signal_handler)(unsigned int sig);
|
|
|
|
static wine_signal_handler handlers[256];
|
|
|
|
/* definitions for 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_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 %x-%x\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;
|
|
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;
|
|
|
|
TRACE( "scope table at %p\n", table );
|
|
for (i = 0; i < table->Count; i++)
|
|
TRACE( " %u: %lx-%lx handler %lx target %lx\n", i,
|
|
base + table->ScopeRecord[i].BeginAddress,
|
|
base + table->ScopeRecord[i].EndAddress,
|
|
base + table->ScopeRecord[i].HandlerAddress,
|
|
base + table->ScopeRecord[i].JumpTarget );
|
|
}
|
|
|
|
/***********************************************************************
|
|
* dispatch_signal
|
|
*/
|
|
static inline int dispatch_signal(unsigned int sig)
|
|
{
|
|
if (handlers[sig] == NULL) return 0;
|
|
return handlers[sig](sig);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* save_context
|
|
*
|
|
* Set the register values from a sigcontext.
|
|
*/
|
|
static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
|
|
{
|
|
context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
|
|
context->Rax = RAX_sig(sigcontext);
|
|
context->Rcx = RCX_sig(sigcontext);
|
|
context->Rdx = RDX_sig(sigcontext);
|
|
context->Rbx = RBX_sig(sigcontext);
|
|
context->Rsp = RSP_sig(sigcontext);
|
|
context->Rbp = RBP_sig(sigcontext);
|
|
context->Rsi = RSI_sig(sigcontext);
|
|
context->Rdi = RDI_sig(sigcontext);
|
|
context->R8 = R8_sig(sigcontext);
|
|
context->R9 = R9_sig(sigcontext);
|
|
context->R10 = R10_sig(sigcontext);
|
|
context->R11 = R11_sig(sigcontext);
|
|
context->R12 = R12_sig(sigcontext);
|
|
context->R13 = R13_sig(sigcontext);
|
|
context->R14 = R14_sig(sigcontext);
|
|
context->R15 = R15_sig(sigcontext);
|
|
context->Rip = RIP_sig(sigcontext);
|
|
context->SegCs = CS_sig(sigcontext);
|
|
context->SegFs = FS_sig(sigcontext);
|
|
context->SegGs = GS_sig(sigcontext);
|
|
context->EFlags = EFL_sig(sigcontext);
|
|
#ifdef DS_sig
|
|
context->SegDs = DS_sig(sigcontext);
|
|
#else
|
|
__asm__("movw %%ds,%0" : "=m" (context->SegDs));
|
|
#endif
|
|
#ifdef ES_sig
|
|
context->SegEs = ES_sig(sigcontext);
|
|
#else
|
|
__asm__("movw %%es,%0" : "=m" (context->SegEs));
|
|
#endif
|
|
#ifdef SS_sig
|
|
context->SegSs = SS_sig(sigcontext);
|
|
#else
|
|
__asm__("movw %%ss,%0" : "=m" (context->SegSs));
|
|
#endif
|
|
context->MxCsr = 0; /* FIXME */
|
|
if (FPU_sig(sigcontext))
|
|
{
|
|
context->ContextFlags |= CONTEXT_FLOATING_POINT;
|
|
context->u.FltSave = *FPU_sig(sigcontext);
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* restore_context
|
|
*
|
|
* Build a sigcontext from the register values.
|
|
*/
|
|
static void restore_context( const CONTEXT *context, ucontext_t *sigcontext )
|
|
{
|
|
RAX_sig(sigcontext) = context->Rax;
|
|
RCX_sig(sigcontext) = context->Rcx;
|
|
RDX_sig(sigcontext) = context->Rdx;
|
|
RBX_sig(sigcontext) = context->Rbx;
|
|
RSP_sig(sigcontext) = context->Rsp;
|
|
RBP_sig(sigcontext) = context->Rbp;
|
|
RSI_sig(sigcontext) = context->Rsi;
|
|
RDI_sig(sigcontext) = context->Rdi;
|
|
R8_sig(sigcontext) = context->R8;
|
|
R9_sig(sigcontext) = context->R9;
|
|
R10_sig(sigcontext) = context->R10;
|
|
R11_sig(sigcontext) = context->R11;
|
|
R12_sig(sigcontext) = context->R12;
|
|
R13_sig(sigcontext) = context->R13;
|
|
R14_sig(sigcontext) = context->R14;
|
|
R15_sig(sigcontext) = context->R15;
|
|
RIP_sig(sigcontext) = context->Rip;
|
|
CS_sig(sigcontext) = context->SegCs;
|
|
FS_sig(sigcontext) = context->SegFs;
|
|
GS_sig(sigcontext) = context->SegGs;
|
|
EFL_sig(sigcontext) = context->EFlags;
|
|
#ifdef DS_sig
|
|
DS_sig(sigcontext) = context->SegDs;
|
|
#endif
|
|
#ifdef ES_sig
|
|
ES_sig(sigcontext) = context->SegEs;
|
|
#endif
|
|
#ifdef SS_sig
|
|
SS_sig(sigcontext) = context->SegSs;
|
|
#endif
|
|
if (FPU_sig(sigcontext)) *FPU_sig(sigcontext) = context->u.FltSave;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* RtlCaptureContext (NTDLL.@)
|
|
*/
|
|
void WINAPI __regs_RtlCaptureContext( CONTEXT *context, CONTEXT *regs )
|
|
{
|
|
*context = *regs;
|
|
}
|
|
DEFINE_REGS_ENTRYPOINT( RtlCaptureContext, 1 )
|
|
|
|
|
|
/***********************************************************************
|
|
* set_cpu_context
|
|
*
|
|
* Set the new CPU context.
|
|
*/
|
|
void set_cpu_context( const CONTEXT *context )
|
|
{
|
|
extern void CDECL __wine_restore_regs( const CONTEXT * ) DECLSPEC_NORETURN;
|
|
__wine_restore_regs( context );
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* copy_context
|
|
*
|
|
* Copy a register context according to the flags.
|
|
*/
|
|
void copy_context( CONTEXT *to, const CONTEXT *from, DWORD flags )
|
|
{
|
|
flags &= ~CONTEXT_AMD64; /* get rid of CPU id */
|
|
if (flags & CONTEXT_CONTROL)
|
|
{
|
|
to->Rbp = from->Rbp;
|
|
to->Rip = from->Rip;
|
|
to->Rsp = from->Rsp;
|
|
to->SegCs = from->SegCs;
|
|
to->SegSs = from->SegSs;
|
|
to->EFlags = from->EFlags;
|
|
to->MxCsr = from->MxCsr;
|
|
}
|
|
if (flags & CONTEXT_INTEGER)
|
|
{
|
|
to->Rax = from->Rax;
|
|
to->Rcx = from->Rcx;
|
|
to->Rdx = from->Rdx;
|
|
to->Rbx = from->Rbx;
|
|
to->Rsi = from->Rsi;
|
|
to->Rdi = from->Rdi;
|
|
to->R8 = from->R8;
|
|
to->R9 = from->R9;
|
|
to->R10 = from->R10;
|
|
to->R11 = from->R11;
|
|
to->R12 = from->R12;
|
|
to->R13 = from->R13;
|
|
to->R14 = from->R14;
|
|
to->R15 = from->R15;
|
|
}
|
|
if (flags & CONTEXT_SEGMENTS)
|
|
{
|
|
to->SegDs = from->SegDs;
|
|
to->SegEs = from->SegEs;
|
|
to->SegFs = from->SegFs;
|
|
to->SegGs = from->SegGs;
|
|
}
|
|
if (flags & CONTEXT_FLOATING_POINT)
|
|
{
|
|
to->u.FltSave = from->u.FltSave;
|
|
}
|
|
if (flags & CONTEXT_DEBUG_REGISTERS)
|
|
{
|
|
to->Dr0 = from->Dr0;
|
|
to->Dr1 = from->Dr1;
|
|
to->Dr2 = from->Dr2;
|
|
to->Dr3 = from->Dr3;
|
|
to->Dr6 = from->Dr6;
|
|
to->Dr7 = from->Dr7;
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* context_to_server
|
|
*
|
|
* Convert a register context to the server format.
|
|
*/
|
|
NTSTATUS context_to_server( context_t *to, const CONTEXT *from )
|
|
{
|
|
DWORD flags = from->ContextFlags & ~CONTEXT_AMD64; /* get rid of CPU id */
|
|
|
|
memset( to, 0, sizeof(*to) );
|
|
to->cpu = CPU_x86_64;
|
|
|
|
if (flags & CONTEXT_CONTROL)
|
|
{
|
|
to->flags |= SERVER_CTX_CONTROL;
|
|
to->ctl.x86_64_regs.rbp = from->Rbp;
|
|
to->ctl.x86_64_regs.rip = from->Rip;
|
|
to->ctl.x86_64_regs.rsp = from->Rsp;
|
|
to->ctl.x86_64_regs.cs = from->SegCs;
|
|
to->ctl.x86_64_regs.ss = from->SegSs;
|
|
to->ctl.x86_64_regs.flags = from->EFlags;
|
|
to->ctl.x86_64_regs.mxcsr = from->MxCsr;
|
|
}
|
|
if (flags & CONTEXT_INTEGER)
|
|
{
|
|
to->flags |= SERVER_CTX_INTEGER;
|
|
to->integer.x86_64_regs.rax = from->Rax;
|
|
to->integer.x86_64_regs.rcx = from->Rcx;
|
|
to->integer.x86_64_regs.rdx = from->Rdx;
|
|
to->integer.x86_64_regs.rbx = from->Rbx;
|
|
to->integer.x86_64_regs.rsi = from->Rsi;
|
|
to->integer.x86_64_regs.rdi = from->Rdi;
|
|
to->integer.x86_64_regs.r8 = from->R8;
|
|
to->integer.x86_64_regs.r9 = from->R9;
|
|
to->integer.x86_64_regs.r10 = from->R10;
|
|
to->integer.x86_64_regs.r11 = from->R11;
|
|
to->integer.x86_64_regs.r12 = from->R12;
|
|
to->integer.x86_64_regs.r13 = from->R13;
|
|
to->integer.x86_64_regs.r14 = from->R14;
|
|
to->integer.x86_64_regs.r15 = from->R15;
|
|
}
|
|
if (flags & CONTEXT_SEGMENTS)
|
|
{
|
|
to->flags |= SERVER_CTX_SEGMENTS;
|
|
to->seg.x86_64_regs.ds = from->SegDs;
|
|
to->seg.x86_64_regs.es = from->SegEs;
|
|
to->seg.x86_64_regs.fs = from->SegFs;
|
|
to->seg.x86_64_regs.gs = from->SegGs;
|
|
}
|
|
if (flags & CONTEXT_FLOATING_POINT)
|
|
{
|
|
to->flags |= SERVER_CTX_FLOATING_POINT;
|
|
memcpy( to->fp.x86_64_regs.fpregs, &from->u.FltSave, sizeof(to->fp.x86_64_regs.fpregs) );
|
|
}
|
|
if (flags & CONTEXT_DEBUG_REGISTERS)
|
|
{
|
|
to->flags |= SERVER_CTX_DEBUG_REGISTERS;
|
|
to->debug.x86_64_regs.dr0 = from->Dr0;
|
|
to->debug.x86_64_regs.dr1 = from->Dr1;
|
|
to->debug.x86_64_regs.dr2 = from->Dr2;
|
|
to->debug.x86_64_regs.dr3 = from->Dr3;
|
|
to->debug.x86_64_regs.dr6 = from->Dr6;
|
|
to->debug.x86_64_regs.dr7 = from->Dr7;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* context_from_server
|
|
*
|
|
* Convert a register context from the server format.
|
|
*/
|
|
NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
|
|
{
|
|
if (from->cpu != CPU_x86_64) return STATUS_INVALID_PARAMETER;
|
|
|
|
to->ContextFlags = CONTEXT_AMD64;
|
|
if (from->flags & SERVER_CTX_CONTROL)
|
|
{
|
|
to->ContextFlags |= CONTEXT_CONTROL;
|
|
to->Rbp = from->ctl.x86_64_regs.rbp;
|
|
to->Rip = from->ctl.x86_64_regs.rip;
|
|
to->Rsp = from->ctl.x86_64_regs.rsp;
|
|
to->SegCs = from->ctl.x86_64_regs.cs;
|
|
to->SegSs = from->ctl.x86_64_regs.ss;
|
|
to->EFlags = from->ctl.x86_64_regs.flags;
|
|
to->MxCsr = from->ctl.x86_64_regs.mxcsr;
|
|
}
|
|
|
|
if (from->flags & SERVER_CTX_INTEGER)
|
|
{
|
|
to->ContextFlags |= CONTEXT_INTEGER;
|
|
to->Rax = from->integer.x86_64_regs.rax;
|
|
to->Rcx = from->integer.x86_64_regs.rcx;
|
|
to->Rdx = from->integer.x86_64_regs.rdx;
|
|
to->Rbx = from->integer.x86_64_regs.rbx;
|
|
to->Rsi = from->integer.x86_64_regs.rsi;
|
|
to->Rdi = from->integer.x86_64_regs.rdi;
|
|
to->R8 = from->integer.x86_64_regs.r8;
|
|
to->R9 = from->integer.x86_64_regs.r9;
|
|
to->R10 = from->integer.x86_64_regs.r10;
|
|
to->R11 = from->integer.x86_64_regs.r11;
|
|
to->R12 = from->integer.x86_64_regs.r12;
|
|
to->R13 = from->integer.x86_64_regs.r13;
|
|
to->R14 = from->integer.x86_64_regs.r14;
|
|
to->R15 = from->integer.x86_64_regs.r15;
|
|
}
|
|
if (from->flags & SERVER_CTX_SEGMENTS)
|
|
{
|
|
to->ContextFlags |= CONTEXT_SEGMENTS;
|
|
to->SegDs = from->seg.x86_64_regs.ds;
|
|
to->SegEs = from->seg.x86_64_regs.es;
|
|
to->SegFs = from->seg.x86_64_regs.fs;
|
|
to->SegGs = from->seg.x86_64_regs.gs;
|
|
}
|
|
if (from->flags & SERVER_CTX_FLOATING_POINT)
|
|
{
|
|
to->ContextFlags |= CONTEXT_FLOATING_POINT;
|
|
memcpy( &to->u.FltSave, from->fp.x86_64_regs.fpregs, sizeof(from->fp.x86_64_regs.fpregs) );
|
|
}
|
|
if (from->flags & SERVER_CTX_DEBUG_REGISTERS)
|
|
{
|
|
to->ContextFlags |= CONTEXT_DEBUG_REGISTERS;
|
|
to->Dr0 = from->debug.x86_64_regs.dr0;
|
|
to->Dr1 = from->debug.x86_64_regs.dr1;
|
|
to->Dr2 = from->debug.x86_64_regs.dr2;
|
|
to->Dr3 = from->debug.x86_64_regs.dr3;
|
|
to->Dr6 = from->debug.x86_64_regs.dr6;
|
|
to->Dr7 = from->debug.x86_64_regs.dr7;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* find_function_info
|
|
*/
|
|
static RUNTIME_FUNCTION *find_function_info( ULONG64 pc, HMODULE module,
|
|
RUNTIME_FUNCTION *func, ULONG size )
|
|
{
|
|
int min = 0;
|
|
int max = size/sizeof(*func) - 1;
|
|
|
|
while (min <= max)
|
|
{
|
|
int pos = (min + max) / 2;
|
|
if ((char *)pc < (char *)module + func[pos].BeginAddress) max = pos - 1;
|
|
else if ((char *)pc >= (char *)module + func[pos].EndAddress) min = pos + 1;
|
|
else
|
|
{
|
|
func += pos;
|
|
while (func->UnwindData & 1) /* follow chained entry */
|
|
func = (RUNTIME_FUNCTION *)((char *)module + (func->UnwindData & ~1));
|
|
return func;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* call_stack_handlers
|
|
*
|
|
* Call the stack handlers chain.
|
|
*/
|
|
static NTSTATUS call_stack_handlers( EXCEPTION_RECORD *rec, CONTEXT *orig_context )
|
|
{
|
|
EXCEPTION_POINTERS ptrs;
|
|
UNWIND_HISTORY_TABLE table;
|
|
ULONG64 frame;
|
|
RUNTIME_FUNCTION *dir, *info;
|
|
PEXCEPTION_ROUTINE handler;
|
|
DISPATCHER_CONTEXT dispatch;
|
|
CONTEXT context, new_context;
|
|
LDR_MODULE *module;
|
|
DWORD size;
|
|
|
|
context = *orig_context;
|
|
for (;;)
|
|
{
|
|
/* FIXME: should use the history table to make things faster */
|
|
|
|
if (LdrFindEntryForAddress( (void *)context.Rip, &module ))
|
|
{
|
|
ERR( "no module found for rip %p, can't dispatch exception\n", (void *)context.Rip );
|
|
break;
|
|
}
|
|
if (!(dir = RtlImageDirectoryEntryToData( module->BaseAddress, TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size )))
|
|
{
|
|
ERR( "module %s doesn't contain exception data, can't dispatch exception\n",
|
|
debugstr_w(module->BaseDllName.Buffer) );
|
|
break;
|
|
}
|
|
if (!(info = find_function_info( context.Rip, module->BaseAddress, dir, size )))
|
|
{
|
|
/* leaf function */
|
|
context.Rip = *(ULONG64 *)context.Rsp;
|
|
context.Rsp += sizeof(ULONG64);
|
|
continue;
|
|
}
|
|
|
|
new_context = context;
|
|
|
|
handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)module->BaseAddress, context.Rip,
|
|
info, &new_context, &dispatch.HandlerData, &frame, NULL );
|
|
|
|
if ((frame & 7) ||
|
|
frame < (ULONG64)NtCurrentTeb()->Tib.StackLimit ||
|
|
frame >= (ULONG64)NtCurrentTeb()->Tib.StackBase)
|
|
{
|
|
ERR( "invalid frame %lx\n", frame );
|
|
rec->ExceptionFlags |= EH_STACK_INVALID;
|
|
break;
|
|
}
|
|
|
|
if (handler)
|
|
{
|
|
dispatch.ControlPc = context.Rip;
|
|
dispatch.ImageBase = (ULONG64)module->BaseAddress;
|
|
dispatch.FunctionEntry = info;
|
|
dispatch.EstablisherFrame = frame;
|
|
dispatch.TargetIp = 0; /* FIXME */
|
|
dispatch.ContextRecord = &context;
|
|
dispatch.LanguageHandler = handler;
|
|
dispatch.HistoryTable = &table;
|
|
dispatch.ScopeIndex = 0; /* FIXME */
|
|
|
|
TRACE( "calling handler %p (rec=%p, frame=%lx context=%p, dispatch=%p)\n",
|
|
handler, rec, frame, &context, &dispatch );
|
|
|
|
switch( handler( rec, frame, &context, &dispatch ))
|
|
{
|
|
case ExceptionContinueExecution:
|
|
if (rec->ExceptionFlags & EH_NONCONTINUABLE) return STATUS_NONCONTINUABLE_EXCEPTION;
|
|
*orig_context = context;
|
|
return STATUS_SUCCESS;
|
|
case ExceptionContinueSearch:
|
|
break;
|
|
case ExceptionNestedException:
|
|
break;
|
|
default:
|
|
return STATUS_INVALID_DISPOSITION;
|
|
}
|
|
}
|
|
context = new_context;
|
|
}
|
|
|
|
/* hack: call unhandled exception filter directly */
|
|
ptrs.ExceptionRecord = rec;
|
|
ptrs.ContextRecord = orig_context;
|
|
unhandled_exception_filter( &ptrs );
|
|
return STATUS_UNHANDLED_EXCEPTION;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* raise_exception
|
|
*
|
|
* Implementation of NtRaiseException.
|
|
*/
|
|
static NTSTATUS raise_exception( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
if (first_chance)
|
|
{
|
|
DWORD c;
|
|
|
|
TRACE( "code=%x flags=%x addr=%p ip=%lx tid=%04x\n",
|
|
rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
|
|
context->Rip, GetCurrentThreadId() );
|
|
for (c = 0; c < min( EXCEPTION_MAXIMUM_PARAMETERS, rec->NumberParameters ); c++)
|
|
TRACE( " info[%d]=%08lx\n", c, rec->ExceptionInformation[c] );
|
|
if (rec->ExceptionCode == EXCEPTION_WINE_STUB)
|
|
{
|
|
if (rec->ExceptionInformation[1] >> 16)
|
|
MESSAGE( "wine: Call from %p to unimplemented function %s.%s, aborting\n",
|
|
rec->ExceptionAddress,
|
|
(char*)rec->ExceptionInformation[0], (char*)rec->ExceptionInformation[1] );
|
|
else
|
|
MESSAGE( "wine: Call from %p to unimplemented function %s.%ld, aborting\n",
|
|
rec->ExceptionAddress,
|
|
(char*)rec->ExceptionInformation[0], rec->ExceptionInformation[1] );
|
|
}
|
|
else
|
|
{
|
|
TRACE(" rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx\n",
|
|
context->Rax, context->Rbx, context->Rcx, context->Rdx );
|
|
TRACE(" rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
|
|
context->Rsi, context->Rdi, context->Rbp, context->Rsp );
|
|
TRACE(" r8=%016lx r9=%016lx r10=%016lx r11=%016lx\n",
|
|
context->R8, context->R9, context->R10, context->R11 );
|
|
TRACE(" r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
|
|
context->R12, context->R13, context->R14, context->R15 );
|
|
}
|
|
status = send_debug_event( rec, TRUE, context );
|
|
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
|
|
return STATUS_SUCCESS;
|
|
|
|
if (call_vectored_handlers( rec, context ) == EXCEPTION_CONTINUE_EXECUTION)
|
|
return STATUS_SUCCESS;
|
|
|
|
if ((status = call_stack_handlers( rec, context )) != STATUS_UNHANDLED_EXCEPTION)
|
|
return status;
|
|
}
|
|
|
|
/* last chance exception */
|
|
|
|
status = send_debug_event( rec, FALSE, context );
|
|
if (status != DBG_CONTINUE)
|
|
{
|
|
if (rec->ExceptionFlags & EH_STACK_INVALID)
|
|
ERR("Exception frame is not in stack limits => unable to dispatch exception.\n");
|
|
else if (rec->ExceptionCode == STATUS_NONCONTINUABLE_EXCEPTION)
|
|
ERR("Process attempted to continue execution after noncontinuable exception.\n");
|
|
else
|
|
ERR("Unhandled exception code %x flags %x addr %p\n",
|
|
rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
|
|
NtTerminateProcess( NtCurrentProcess(), rec->ExceptionCode );
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* segv_handler
|
|
*
|
|
* Handler for SIGSEGV and related errors.
|
|
*/
|
|
static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
EXCEPTION_RECORD rec;
|
|
CONTEXT context;
|
|
NTSTATUS status;
|
|
ucontext_t *ucontext = sigcontext;
|
|
|
|
save_context( &context, ucontext );
|
|
|
|
rec.ExceptionRecord = NULL;
|
|
rec.ExceptionFlags = EXCEPTION_CONTINUABLE;
|
|
rec.ExceptionAddress = (LPVOID)context.Rip;
|
|
rec.NumberParameters = 0;
|
|
|
|
switch(TRAP_sig(ucontext))
|
|
{
|
|
case TRAP_x86_OFLOW: /* Overflow exception */
|
|
rec.ExceptionCode = EXCEPTION_INT_OVERFLOW;
|
|
break;
|
|
case TRAP_x86_BOUND: /* Bound range exception */
|
|
rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
|
|
break;
|
|
case TRAP_x86_PRIVINFLT: /* Invalid opcode exception */
|
|
rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
|
|
break;
|
|
case TRAP_x86_STKFLT: /* Stack fault */
|
|
rec.ExceptionCode = EXCEPTION_STACK_OVERFLOW;
|
|
break;
|
|
case TRAP_x86_SEGNPFLT: /* Segment not present exception */
|
|
case TRAP_x86_PROTFLT: /* General protection fault */
|
|
case TRAP_x86_UNKNOWN: /* Unknown fault code */
|
|
rec.ExceptionCode = ERROR_sig(ucontext) ? EXCEPTION_ACCESS_VIOLATION
|
|
: EXCEPTION_PRIV_INSTRUCTION;
|
|
break;
|
|
case TRAP_x86_PAGEFLT: /* Page fault */
|
|
rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
|
|
rec.NumberParameters = 2;
|
|
rec.ExceptionInformation[0] = (ERROR_sig(ucontext) & 2) != 0;
|
|
rec.ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr;
|
|
if (!(rec.ExceptionCode = virtual_handle_fault( siginfo->si_addr, rec.ExceptionInformation[0] )))
|
|
goto done;
|
|
break;
|
|
case TRAP_x86_ALIGNFLT: /* Alignment check exception */
|
|
rec.ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT;
|
|
break;
|
|
default:
|
|
ERR( "Got unexpected trap %ld\n", TRAP_sig(ucontext) );
|
|
/* fall through */
|
|
case TRAP_x86_NMI: /* NMI interrupt */
|
|
case TRAP_x86_DNA: /* Device not available exception */
|
|
case TRAP_x86_DOUBLEFLT: /* Double fault exception */
|
|
case TRAP_x86_TSSFLT: /* Invalid TSS exception */
|
|
case TRAP_x86_MCHK: /* Machine check exception */
|
|
case TRAP_x86_CACHEFLT: /* Cache flush exception */
|
|
rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
|
|
break;
|
|
}
|
|
|
|
status = raise_exception( &rec, &context, TRUE );
|
|
if (status) raise_status( status, &rec );
|
|
done:
|
|
restore_context( &context, ucontext );
|
|
}
|
|
|
|
/**********************************************************************
|
|
* trap_handler
|
|
*
|
|
* Handler for SIGTRAP.
|
|
*/
|
|
static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|
{
|
|
EXCEPTION_RECORD rec;
|
|
CONTEXT context;
|
|
NTSTATUS status;
|
|
ucontext_t *ucontext = sigcontext;
|
|
|
|
save_context( &context, ucontext );
|
|
rec.ExceptionFlags = EXCEPTION_CONTINUABLE;
|
|
rec.ExceptionRecord = NULL;
|
|
rec.ExceptionAddress = (LPVOID)context.Rip;
|
|
rec.NumberParameters = 0;
|
|
|
|
switch (siginfo->si_code)
|
|
{
|
|
case TRAP_TRACE: /* Single-step exception */
|
|
rec.ExceptionCode = EXCEPTION_SINGLE_STEP;
|
|
EFL_sig(ucontext) &= ~0x100; /* clear single-step flag */
|
|
break;
|
|
case TRAP_BRKPT: /* Breakpoint exception */
|
|
rec.ExceptionAddress = (char *)rec.ExceptionAddress - 1; /* back up over the int3 instruction */
|
|
/* fall through */
|
|
default:
|
|
rec.ExceptionCode = EXCEPTION_BREAKPOINT;
|
|
break;
|
|
}
|
|
|
|
status = raise_exception( &rec, &context, TRUE );
|
|
if (status) raise_status( status, &rec );
|
|
restore_context( &context, ucontext );
|
|
}
|
|
|
|
/**********************************************************************
|
|
* fpe_handler
|
|
*
|
|
* Handler for SIGFPE.
|
|
*/
|
|
static void fpe_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
|
{
|
|
EXCEPTION_RECORD rec;
|
|
CONTEXT context;
|
|
NTSTATUS status;
|
|
|
|
save_context( &context, ucontext );
|
|
rec.ExceptionFlags = EXCEPTION_CONTINUABLE;
|
|
rec.ExceptionRecord = NULL;
|
|
rec.ExceptionAddress = (LPVOID)context.Rip;
|
|
rec.NumberParameters = 0;
|
|
|
|
switch (siginfo->si_code)
|
|
{
|
|
case FPE_FLTSUB:
|
|
rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
|
|
break;
|
|
case FPE_INTDIV:
|
|
rec.ExceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
|
|
break;
|
|
case FPE_INTOVF:
|
|
rec.ExceptionCode = EXCEPTION_INT_OVERFLOW;
|
|
break;
|
|
case FPE_FLTDIV:
|
|
rec.ExceptionCode = EXCEPTION_FLT_DIVIDE_BY_ZERO;
|
|
break;
|
|
case FPE_FLTOVF:
|
|
rec.ExceptionCode = EXCEPTION_FLT_OVERFLOW;
|
|
break;
|
|
case FPE_FLTUND:
|
|
rec.ExceptionCode = EXCEPTION_FLT_UNDERFLOW;
|
|
break;
|
|
case FPE_FLTRES:
|
|
rec.ExceptionCode = EXCEPTION_FLT_INEXACT_RESULT;
|
|
break;
|
|
case FPE_FLTINV:
|
|
default:
|
|
rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
|
|
break;
|
|
}
|
|
|
|
status = raise_exception( &rec, &context, TRUE );
|
|
if (status) raise_status( status, &rec );
|
|
restore_context( &context, ucontext );
|
|
}
|
|
|
|
/**********************************************************************
|
|
* int_handler
|
|
*
|
|
* Handler for SIGINT.
|
|
*/
|
|
static void int_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
|
{
|
|
if (!dispatch_signal(SIGINT))
|
|
{
|
|
EXCEPTION_RECORD rec;
|
|
CONTEXT context;
|
|
NTSTATUS status;
|
|
|
|
save_context( &context, ucontext );
|
|
rec.ExceptionCode = CONTROL_C_EXIT;
|
|
rec.ExceptionFlags = EXCEPTION_CONTINUABLE;
|
|
rec.ExceptionRecord = NULL;
|
|
rec.ExceptionAddress = (LPVOID)context.Rip;
|
|
rec.NumberParameters = 0;
|
|
status = raise_exception( &rec, &context, TRUE );
|
|
if (status) raise_status( status, &rec );
|
|
restore_context( &context, ucontext );
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* abrt_handler
|
|
*
|
|
* Handler for SIGABRT.
|
|
*/
|
|
static void abrt_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
|
{
|
|
EXCEPTION_RECORD rec;
|
|
CONTEXT context;
|
|
NTSTATUS status;
|
|
|
|
save_context( &context, ucontext );
|
|
rec.ExceptionCode = EXCEPTION_WINE_ASSERTION;
|
|
rec.ExceptionFlags = EH_NONCONTINUABLE;
|
|
rec.ExceptionRecord = NULL;
|
|
rec.ExceptionAddress = (LPVOID)context.Rip;
|
|
rec.NumberParameters = 0;
|
|
status = raise_exception( &rec, &context, TRUE );
|
|
if (status) raise_status( status, &rec );
|
|
restore_context( &context, ucontext );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* quit_handler
|
|
*
|
|
* Handler for SIGQUIT.
|
|
*/
|
|
static void quit_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
|
{
|
|
abort_thread(0);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* usr1_handler
|
|
*
|
|
* Handler for SIGUSR1, used to signal a thread that it got suspended.
|
|
*/
|
|
static void usr1_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
|
{
|
|
CONTEXT context;
|
|
|
|
save_context( &context, ucontext );
|
|
wait_suspend( &context );
|
|
restore_context( &context, ucontext );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* get_signal_stack_total_size
|
|
*
|
|
* Retrieve the size to allocate for the signal stack, including the TEB at the bottom.
|
|
* Must be a power of two.
|
|
*/
|
|
size_t get_signal_stack_total_size(void)
|
|
{
|
|
assert( sizeof(TEB) <= 2*getpagesize() );
|
|
return 2*getpagesize(); /* this is just for the TEB, we don't need a signal stack */
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* __wine_set_signal_handler (NTDLL.@)
|
|
*/
|
|
int CDECL __wine_set_signal_handler(unsigned int sig, wine_signal_handler wsh)
|
|
{
|
|
if (sig > sizeof(handlers) / sizeof(handlers[0])) return -1;
|
|
if (handlers[sig] != NULL) return -2;
|
|
handlers[sig] = wsh;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* signal_init_thread
|
|
*/
|
|
void signal_init_thread( TEB *teb )
|
|
{
|
|
#ifdef __linux__
|
|
arch_prctl( ARCH_SET_GS, teb );
|
|
#else
|
|
# error Please define setting %gs for your architecture
|
|
#endif
|
|
}
|
|
|
|
/**********************************************************************
|
|
* signal_init_process
|
|
*/
|
|
void signal_init_process(void)
|
|
{
|
|
struct sigaction sig_act;
|
|
|
|
sig_act.sa_mask = server_block_set;
|
|
sig_act.sa_flags = SA_RESTART | SA_SIGINFO;
|
|
|
|
sig_act.sa_sigaction = int_handler;
|
|
if (sigaction( SIGINT, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = fpe_handler;
|
|
if (sigaction( SIGFPE, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = abrt_handler;
|
|
if (sigaction( SIGABRT, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = quit_handler;
|
|
if (sigaction( SIGQUIT, &sig_act, NULL ) == -1) goto error;
|
|
sig_act.sa_sigaction = usr1_handler;
|
|
if (sigaction( SIGUSR1, &sig_act, NULL ) == -1) goto error;
|
|
|
|
sig_act.sa_sigaction = segv_handler;
|
|
if (sigaction( SIGSEGV, &sig_act, NULL ) == -1) goto error;
|
|
if (sigaction( SIGILL, &sig_act, NULL ) == -1) goto error;
|
|
#ifdef SIGBUS
|
|
if (sigaction( SIGBUS, &sig_act, NULL ) == -1) goto error;
|
|
#endif
|
|
|
|
#ifdef SIGTRAP
|
|
sig_act.sa_sigaction = trap_handler;
|
|
if (sigaction( SIGTRAP, &sig_act, NULL ) == -1) goto error;
|
|
#endif
|
|
return;
|
|
|
|
error:
|
|
perror("sigaction");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* RtlLookupFunctionEntry (NTDLL.@)
|
|
*/
|
|
PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG64 pc, ULONG64 *base, UNWIND_HISTORY_TABLE *table )
|
|
{
|
|
LDR_MODULE *module;
|
|
RUNTIME_FUNCTION *func;
|
|
ULONG size;
|
|
|
|
/* FIXME: should use the history table to make things faster */
|
|
|
|
if (LdrFindEntryForAddress( (void *)pc, &module ))
|
|
{
|
|
WARN( "module not found for %lx\n", pc );
|
|
return NULL;
|
|
}
|
|
if (!(func = RtlImageDirectoryEntryToData( module->BaseAddress, TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size )))
|
|
{
|
|
WARN( "no exception table found in module %p pc %lx\n", module->BaseAddress, pc );
|
|
return NULL;
|
|
}
|
|
func = find_function_info( pc, module->BaseAddress, func, size );
|
|
if (func) *base = (ULONG64)module->BaseAddress;
|
|
return func;
|
|
}
|
|
|
|
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->u2.IntegerContext[reg] = &context->Rax + reg;
|
|
}
|
|
|
|
static void set_float_reg( CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr, int reg, M128A val )
|
|
{
|
|
*(&context->u.s.Xmm0 + reg) = val;
|
|
if (ctx_ptr) ctx_ptr->u1.FloatingContext[reg] = &context->u.s.Xmm0 + reg;
|
|
}
|
|
|
|
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:
|
|
return 2;
|
|
case UWOP_SAVE_NONVOL_FAR:
|
|
case UWOP_SAVE_XMM128_FAR:
|
|
return 3;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static BOOL is_inside_epilog( BYTE *pc )
|
|
{
|
|
/* add or lea must be the first instruction, and it must have a rex.W prefix */
|
|
if ((pc[0] & 0xf8) == 0x48)
|
|
{
|
|
switch (pc[1])
|
|
{
|
|
case 0x81: /* add $nnnn,%rsp */
|
|
if (pc[0] == 0x48 && pc[2] == 0xc4)
|
|
{
|
|
pc += 7;
|
|
break;
|
|
}
|
|
return FALSE;
|
|
case 0x83: /* add $n,%rsp */
|
|
if (pc[0] == 0x48 && pc[2] == 0xc4)
|
|
{
|
|
pc += 4;
|
|
break;
|
|
}
|
|
return FALSE;
|
|
case 0x8d: /* lea n(reg),%rsp */
|
|
if (pc[0] & 0x06) return FALSE; /* rex.RX must be cleared */
|
|
if (((pc[2] >> 3) & 7) != 4) return FALSE; /* dest reg mus be %rsp */
|
|
if ((pc[2] & 7) == 4) return FALSE; /* no SIB byte allowed */
|
|
if ((pc[2] >> 6) == 1) /* 8-bit offset */
|
|
{
|
|
pc += 4;
|
|
break;
|
|
}
|
|
if ((pc[2] >> 6) == 2) /* 32-bit offset */
|
|
{
|
|
pc += 7;
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* now check for various pop instructions */
|
|
|
|
for (;;)
|
|
{
|
|
BYTE rex = 0;
|
|
|
|
if ((*pc & 0xf0) == 0x40) rex = *pc++ & 0x0f; /* rex prefix */
|
|
|
|
switch (*pc)
|
|
{
|
|
case 0x58: /* pop %rax/%r8 */
|
|
case 0x59: /* pop %rcx/%r9 */
|
|
case 0x5a: /* pop %rdx/%r10 */
|
|
case 0x5b: /* pop %rbx/%r11 */
|
|
case 0x5c: /* pop %rsp/%r12 */
|
|
case 0x5d: /* pop %rbp/%r13 */
|
|
case 0x5e: /* pop %rsi/%r14 */
|
|
case 0x5f: /* pop %rdi/%r15 */
|
|
pc++;
|
|
continue;
|
|
case 0xc2: /* ret $nn */
|
|
case 0xc3: /* ret */
|
|
return TRUE;
|
|
/* FIXME: add various jump instructions */
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* execute a function epilog, which must have been validated with is_inside_epilog() */
|
|
static void interpret_epilog( BYTE *pc, CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *ctx_ptr )
|
|
{
|
|
for (;;)
|
|
{
|
|
BYTE rex = 0;
|
|
|
|
if ((*pc & 0xf0) == 0x40) rex = *pc++ & 0x0f; /* rex prefix */
|
|
|
|
switch (*pc)
|
|
{
|
|
case 0x58: /* pop %rax/r8 */
|
|
case 0x59: /* pop %rcx/r9 */
|
|
case 0x5a: /* pop %rdx/r10 */
|
|
case 0x5b: /* pop %rbx/r11 */
|
|
case 0x5c: /* pop %rsp/r12 */
|
|
case 0x5d: /* pop %rbp/r13 */
|
|
case 0x5e: /* pop %rsi/r14 */
|
|
case 0x5f: /* pop %rdi/r15 */
|
|
set_int_reg( context, ctx_ptr, *pc - 0x58 + (rex & 1) * 8, *(ULONG64 *)context->Rsp );
|
|
context->Rsp += sizeof(ULONG64);
|
|
pc++;
|
|
continue;
|
|
case 0x81: /* add $nnnn,%rsp */
|
|
context->Rsp += *(LONG *)(pc + 2);
|
|
pc += 2 + sizeof(LONG);
|
|
continue;
|
|
case 0x83: /* add $n,%rsp */
|
|
context->Rsp += (signed char)pc[2];
|
|
pc += 3;
|
|
continue;
|
|
case 0x8d:
|
|
if ((pc[1] >> 6) == 1) /* lea n(reg),%rsp */
|
|
{
|
|
context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + (signed char)pc[2];
|
|
pc += 3;
|
|
}
|
|
else /* lea nnnn(reg),%rsp */
|
|
{
|
|
context->Rsp = get_int_reg( context, (pc[1] & 7) + (rex & 1) * 8 ) + *(LONG *)(pc + 2);
|
|
pc += 2 + sizeof(LONG);
|
|
}
|
|
continue;
|
|
case 0xc2: /* ret $nn */
|
|
context->Rip = *(ULONG64 *)context->Rsp;
|
|
context->Rsp += sizeof(ULONG64) + *(WORD *)(pc + 1);
|
|
return;
|
|
case 0xc3: /* ret */
|
|
context->Rip = *(ULONG64 *)context->Rsp;
|
|
context->Rsp += sizeof(ULONG64);
|
|
return;
|
|
/* FIXME: add various jump instructions */
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
* RtlVirtualUnwind (NTDLL.@)
|
|
*/
|
|
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;
|
|
|
|
TRACE( "type %x rip %lx rsp %lx\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)
|
|
{
|
|
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)
|
|
{
|
|
prolog_offset = pc - base - function->BeginAddress;
|
|
}
|
|
else
|
|
{
|
|
prolog_offset = ~0;
|
|
if (is_inside_epilog( (BYTE *)pc ))
|
|
{
|
|
interpret_epilog( (BYTE *)pc, context, ctx_ptr );
|
|
*frame_ret = frame;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < info->count; i += get_opcode_size(info->opcodes[i]))
|
|
{
|
|
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:
|
|
FIXME( "PUSH_MACHFRAME %u\n", info->opcodes[i].info );
|
|
break;
|
|
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 */
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* RtlUnwindEx (NTDLL.@)
|
|
*/
|
|
void WINAPI RtlUnwindEx( ULONG64 end_frame, ULONG64 target_ip, EXCEPTION_RECORD *rec,
|
|
ULONG64 retval, CONTEXT *context, UNWIND_HISTORY_TABLE *table )
|
|
{
|
|
EXCEPTION_RECORD record;
|
|
ULONG64 frame;
|
|
RUNTIME_FUNCTION *dir, *info;
|
|
PEXCEPTION_ROUTINE handler;
|
|
DISPATCHER_CONTEXT dispatch;
|
|
CONTEXT new_context;
|
|
LDR_MODULE *module;
|
|
DWORD size;
|
|
|
|
/* 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->Rip;
|
|
record.NumberParameters = 0;
|
|
rec = &record;
|
|
}
|
|
|
|
rec->ExceptionFlags |= EH_UNWINDING | (end_frame ? 0 : EH_EXIT_UNWIND);
|
|
|
|
FIXME( "code=%x flags=%x end_frame=%lx target_ip=%lx\n",
|
|
rec->ExceptionCode, rec->ExceptionFlags, end_frame, target_ip );
|
|
|
|
frame = context->Rsp;
|
|
while (frame != end_frame)
|
|
{
|
|
/* FIXME: should use the history table to make things faster */
|
|
|
|
if (LdrFindEntryForAddress( (void *)context->Rip, &module ))
|
|
{
|
|
ERR( "no module found for rip %p, can't unwind exception\n", (void *)context->Rip );
|
|
raise_status( STATUS_BAD_FUNCTION_TABLE, rec );
|
|
}
|
|
if (!(dir = RtlImageDirectoryEntryToData( module->BaseAddress, TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size )))
|
|
{
|
|
ERR( "module %s doesn't contain exception data, can't unwind exception\n",
|
|
debugstr_w(module->BaseDllName.Buffer) );
|
|
raise_status( STATUS_BAD_FUNCTION_TABLE, rec );
|
|
}
|
|
if (!(info = find_function_info( context->Rip, module->BaseAddress, dir, size )))
|
|
{
|
|
/* leaf function */
|
|
context->Rip = *(ULONG64 *)context->Rsp;
|
|
context->Rsp += sizeof(ULONG64);
|
|
continue;
|
|
}
|
|
|
|
new_context = *context;
|
|
|
|
handler = RtlVirtualUnwind( UNW_FLAG_UHANDLER, (ULONG64)module->BaseAddress, context->Rip,
|
|
info, &new_context, &dispatch.HandlerData, &frame, NULL );
|
|
|
|
if ((frame & 7) ||
|
|
frame < (ULONG64)NtCurrentTeb()->Tib.StackLimit ||
|
|
frame >= (ULONG64)NtCurrentTeb()->Tib.StackBase)
|
|
{
|
|
ERR( "invalid frame %lx\n", frame );
|
|
raise_status( STATUS_BAD_STACK, rec );
|
|
}
|
|
|
|
if (end_frame && (frame > end_frame))
|
|
{
|
|
ERR( "invalid frame %lx/%lx\n", frame, end_frame );
|
|
raise_status( STATUS_INVALID_UNWIND_TARGET, rec );
|
|
}
|
|
|
|
if (handler)
|
|
{
|
|
dispatch.ControlPc = context->Rip;
|
|
dispatch.ImageBase = (ULONG64)module->BaseAddress;
|
|
dispatch.FunctionEntry = info;
|
|
dispatch.EstablisherFrame = frame;
|
|
dispatch.TargetIp = target_ip;
|
|
dispatch.ContextRecord = context;
|
|
dispatch.LanguageHandler = handler;
|
|
dispatch.HistoryTable = table;
|
|
dispatch.ScopeIndex = 0; /* FIXME */
|
|
|
|
TRACE( "calling handler %p (rec=%p, frame=%lx context=%p, dispatch=%p)\n",
|
|
handler, rec, frame, context, &dispatch );
|
|
|
|
switch( handler( rec, frame, context, &dispatch ))
|
|
{
|
|
case ExceptionContinueSearch:
|
|
break;
|
|
case ExceptionCollidedUnwind:
|
|
FIXME( "ExceptionCollidedUnwind not supported yet\n" );
|
|
break;
|
|
default:
|
|
raise_status( STATUS_INVALID_DISPOSITION, rec );
|
|
break;
|
|
}
|
|
}
|
|
*context = new_context;
|
|
}
|
|
context->Rax = retval;
|
|
context->Rip = target_ip;
|
|
TRACE( "returning to %lx stack %lx\n", context->Rip, context->Rsp );
|
|
set_cpu_context( context );
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* RtlUnwind (NTDLL.@)
|
|
*/
|
|
void WINAPI __regs_RtlUnwind( ULONG64 frame, ULONG64 target_ip, EXCEPTION_RECORD *rec,
|
|
ULONG64 retval, CONTEXT *context )
|
|
{
|
|
RtlUnwindEx( frame, target_ip, rec, retval, context, NULL );
|
|
}
|
|
DEFINE_REGS_ENTRYPOINT( RtlUnwind, 4 )
|
|
|
|
|
|
/*******************************************************************
|
|
* __C_specific_handler (NTDLL.@)
|
|
*/
|
|
EXCEPTION_DISPOSITION WINAPI __C_specific_handler( EXCEPTION_RECORD *rec,
|
|
ULONG64 frame,
|
|
CONTEXT *context,
|
|
struct _DISPATCHER_CONTEXT *dispatch )
|
|
{
|
|
SCOPE_TABLE *table = dispatch->HandlerData;
|
|
ULONG i;
|
|
|
|
TRACE( "%p %lx %p %p\n", rec, frame, context, dispatch );
|
|
if (TRACE_ON(seh)) dump_scope_table( dispatch->ImageBase, table );
|
|
|
|
if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) /* FIXME */
|
|
return ExceptionContinueSearch;
|
|
|
|
for (i = 0; i < table->Count; i++)
|
|
{
|
|
if (context->Rip >= dispatch->ImageBase + table->ScopeRecord[i].BeginAddress &&
|
|
context->Rip < dispatch->ImageBase + table->ScopeRecord[i].EndAddress)
|
|
{
|
|
if (!table->ScopeRecord[i].JumpTarget) continue;
|
|
if (table->ScopeRecord[i].HandlerAddress != EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
EXCEPTION_POINTERS ptrs;
|
|
PC_LANGUAGE_EXCEPTION_HANDLER filter;
|
|
|
|
filter = (PC_LANGUAGE_EXCEPTION_HANDLER)(dispatch->ImageBase + table->ScopeRecord[i].HandlerAddress);
|
|
ptrs.ExceptionRecord = rec;
|
|
ptrs.ContextRecord = context;
|
|
TRACE( "calling filter %p ptrs %p frame %lx\n", filter, &ptrs, frame );
|
|
switch (filter( &ptrs, frame ))
|
|
{
|
|
case EXCEPTION_EXECUTE_HANDLER:
|
|
break;
|
|
case EXCEPTION_CONTINUE_SEARCH:
|
|
continue;
|
|
case EXCEPTION_CONTINUE_EXECUTION:
|
|
return ExceptionContinueExecution;
|
|
}
|
|
}
|
|
TRACE( "unwinding to target %lx\n", dispatch->ImageBase + table->ScopeRecord[i].JumpTarget );
|
|
RtlUnwindEx( frame, dispatch->ImageBase + table->ScopeRecord[i].JumpTarget,
|
|
rec, 0, context, dispatch->HistoryTable );
|
|
}
|
|
}
|
|
return ExceptionContinueSearch;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
* NtRaiseException (NTDLL.@)
|
|
*/
|
|
NTSTATUS WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance )
|
|
{
|
|
NTSTATUS status = raise_exception( rec, context, first_chance );
|
|
if (status == STATUS_SUCCESS) NtSetContextThread( GetCurrentThread(), context );
|
|
return status;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* RtlRaiseException (NTDLL.@)
|
|
*/
|
|
void WINAPI __regs_RtlRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context )
|
|
{
|
|
NTSTATUS status;
|
|
|
|
rec->ExceptionAddress = (void *)context->Rip;
|
|
status = raise_exception( rec, context, TRUE );
|
|
if (status != STATUS_SUCCESS) raise_status( status, rec );
|
|
}
|
|
DEFINE_REGS_ENTRYPOINT( RtlRaiseException, 1 )
|
|
|
|
|
|
/**********************************************************************
|
|
* __wine_enter_vm86 (NTDLL.@)
|
|
*/
|
|
void __wine_enter_vm86( CONTEXT *context )
|
|
{
|
|
MESSAGE("vm86 mode not supported on this platform\n");
|
|
}
|
|
|
|
/**********************************************************************
|
|
* DbgBreakPoint (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( DbgBreakPoint, "int $3; ret")
|
|
|
|
/**********************************************************************
|
|
* DbgUserBreakPoint (NTDLL.@)
|
|
*/
|
|
__ASM_GLOBAL_FUNC( DbgUserBreakPoint, "int $3; ret")
|
|
|
|
#endif /* __x86_64__ */
|