ntdll: Handle x86_64 signals on a separate signal stack.
This commit is contained in:
parent
873e027e01
commit
a19db6dc11
|
@ -188,6 +188,10 @@ enum i386_trap_code
|
|||
TRAP_x86_CACHEFLT = 19 /* Cache flush exception */
|
||||
};
|
||||
|
||||
static const size_t teb_size = 0x2000; /* we reserve two pages for the TEB */
|
||||
static size_t signal_stack_size;
|
||||
|
||||
typedef void (*raise_func)( EXCEPTION_RECORD *rec, CONTEXT *context );
|
||||
typedef int (*wine_signal_handler)(unsigned int sig);
|
||||
|
||||
static wine_signal_handler handlers[256];
|
||||
|
@ -357,6 +361,16 @@ static inline int dispatch_signal(unsigned int sig)
|
|||
return handlers[sig](sig);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* get_signal_stack
|
||||
*
|
||||
* Get the base of the signal stack for the current thread.
|
||||
*/
|
||||
static inline void *get_signal_stack(void)
|
||||
{
|
||||
return (char *)NtCurrentTeb() + teb_size;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* save_context
|
||||
*
|
||||
|
@ -666,6 +680,108 @@ NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
|
|||
}
|
||||
|
||||
|
||||
extern void raise_func_trampoline( EXCEPTION_RECORD *rec, CONTEXT *context, raise_func func );
|
||||
__ASM_GLOBAL_FUNC( raise_func_trampoline,
|
||||
".cfi_signal_frame\n\t"
|
||||
".cfi_def_cfa %rbp,144\n\t" /* red zone + rip + rbp */
|
||||
".cfi_rel_offset %rip,8\n\t"
|
||||
".cfi_rel_offset %rbp,0\n\t"
|
||||
"call *%rdx\n\t"
|
||||
"int $3")
|
||||
|
||||
/***********************************************************************
|
||||
* setup_exception
|
||||
*
|
||||
* Setup a proper stack frame for the raise function, and modify the
|
||||
* sigcontext so that the return from the signal handler will call
|
||||
* the raise function.
|
||||
*/
|
||||
static EXCEPTION_RECORD *setup_exception( ucontext_t *sigcontext, raise_func func )
|
||||
{
|
||||
struct stack_layout
|
||||
{
|
||||
CONTEXT context;
|
||||
EXCEPTION_RECORD rec;
|
||||
ULONG64 rbp;
|
||||
ULONG64 rip;
|
||||
ULONG64 red_zone[16];
|
||||
} *stack;
|
||||
ULONG64 *rsp_ptr;
|
||||
DWORD exception_code = 0;
|
||||
|
||||
stack = (struct stack_layout *)(RSP_sig(sigcontext) & ~15);
|
||||
|
||||
/* stack sanity checks */
|
||||
|
||||
if ((char *)stack >= (char *)get_signal_stack() &&
|
||||
(char *)stack < (char *)get_signal_stack() + signal_stack_size)
|
||||
{
|
||||
ERR( "nested exception on signal stack in thread %04x eip %016lx esp %016lx stack %p-%p\n",
|
||||
GetCurrentThreadId(), RIP_sig(sigcontext), RSP_sig(sigcontext),
|
||||
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
|
||||
abort_thread(1);
|
||||
}
|
||||
|
||||
if (stack - 1 > stack || /* check for overflow in subtraction */
|
||||
(char *)stack <= (char *)NtCurrentTeb()->DeallocationStack ||
|
||||
(char *)stack > (char *)NtCurrentTeb()->Tib.StackBase)
|
||||
{
|
||||
WARN( "exception outside of stack limits in thread %04x eip %016lx esp %016lx stack %p-%p\n",
|
||||
GetCurrentThreadId(), RIP_sig(sigcontext), RSP_sig(sigcontext),
|
||||
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
|
||||
}
|
||||
else if ((char *)(stack - 1) < (char *)NtCurrentTeb()->DeallocationStack + 4096)
|
||||
{
|
||||
/* stack overflow on last page, unrecoverable */
|
||||
UINT diff = (char *)NtCurrentTeb()->DeallocationStack + 4096 - (char *)(stack - 1);
|
||||
ERR( "stack overflow %u bytes in thread %04x eip %016lx esp %016lx stack %p-%p-%p\n",
|
||||
diff, GetCurrentThreadId(), RIP_sig(sigcontext),
|
||||
RSP_sig(sigcontext), NtCurrentTeb()->DeallocationStack,
|
||||
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
|
||||
abort_thread(1);
|
||||
}
|
||||
else if ((char *)(stack - 1) < (char *)NtCurrentTeb()->Tib.StackLimit)
|
||||
{
|
||||
/* stack access below stack limit, may be recoverable */
|
||||
if (virtual_handle_stack_fault( stack - 1 )) exception_code = EXCEPTION_STACK_OVERFLOW;
|
||||
else
|
||||
{
|
||||
UINT diff = (char *)NtCurrentTeb()->Tib.StackLimit - (char *)(stack - 1);
|
||||
ERR( "stack overflow %u bytes in thread %04x eip %016lx esp %016lx stack %p-%p-%p\n",
|
||||
diff, GetCurrentThreadId(), RIP_sig(sigcontext),
|
||||
RSP_sig(sigcontext), NtCurrentTeb()->DeallocationStack,
|
||||
NtCurrentTeb()->Tib.StackLimit, NtCurrentTeb()->Tib.StackBase );
|
||||
abort_thread(1);
|
||||
}
|
||||
}
|
||||
|
||||
stack--; /* push the stack_layout structure */
|
||||
stack->rec.ExceptionRecord = NULL;
|
||||
stack->rec.ExceptionCode = exception_code;
|
||||
stack->rec.ExceptionFlags = EXCEPTION_CONTINUABLE;
|
||||
stack->rec.ExceptionAddress = (void *)RIP_sig(sigcontext);
|
||||
stack->rec.NumberParameters = 0;
|
||||
save_context( &stack->context, sigcontext );
|
||||
|
||||
/* store return address and %rbp without aligning, so that the offset is fixed */
|
||||
rsp_ptr = (ULONG64 *)RSP_sig(sigcontext) - 16;
|
||||
*(--rsp_ptr) = RIP_sig(sigcontext);
|
||||
*(--rsp_ptr) = RBP_sig(sigcontext);
|
||||
|
||||
/* now modify the sigcontext to return to the raise function */
|
||||
RIP_sig(sigcontext) = (ULONG_PTR)raise_func_trampoline;
|
||||
RDI_sig(sigcontext) = (ULONG_PTR)&stack->rec;
|
||||
RSI_sig(sigcontext) = (ULONG_PTR)&stack->context;
|
||||
RDX_sig(sigcontext) = (ULONG_PTR)func;
|
||||
RBP_sig(sigcontext) = (ULONG_PTR)rsp_ptr;
|
||||
RSP_sig(sigcontext) = (ULONG_PTR)stack;
|
||||
/* clear single-step, direction, and align check flag */
|
||||
EFL_sig(sigcontext) &= ~(0x100|0x400|0x40000);
|
||||
|
||||
return &stack->rec;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* find_function_info
|
||||
*/
|
||||
|
@ -856,6 +972,41 @@ static NTSTATUS raise_exception( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL f
|
|||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* raise_segv_exception
|
||||
*/
|
||||
static void raise_segv_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
switch(rec->ExceptionCode)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
if (rec->NumberParameters == 2)
|
||||
{
|
||||
if (!(rec->ExceptionCode = virtual_handle_fault( (void *)rec->ExceptionInformation[1],
|
||||
rec->ExceptionInformation[0] )))
|
||||
set_cpu_context( context );
|
||||
}
|
||||
break;
|
||||
}
|
||||
status = raise_exception( rec, context, TRUE );
|
||||
if (status) raise_status( status, rec );
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* raise_generic_exception
|
||||
*
|
||||
* Generic raise function for exceptions that don't need special treatment.
|
||||
*/
|
||||
static void raise_generic_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
|
||||
{
|
||||
NTSTATUS status = raise_exception( rec, context, TRUE );
|
||||
if (status) raise_status( status, rec );
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* segv_handler
|
||||
*
|
||||
|
@ -863,48 +1014,37 @@ static NTSTATUS raise_exception( EXCEPTION_RECORD *rec, CONTEXT *context, BOOL f
|
|||
*/
|
||||
static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
||||
{
|
||||
EXCEPTION_RECORD rec;
|
||||
CONTEXT context;
|
||||
NTSTATUS status;
|
||||
EXCEPTION_RECORD *rec = setup_exception( sigcontext, raise_segv_exception );
|
||||
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;
|
||||
rec->ExceptionCode = EXCEPTION_INT_OVERFLOW;
|
||||
break;
|
||||
case TRAP_x86_BOUND: /* Bound range exception */
|
||||
rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
|
||||
rec->ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
|
||||
break;
|
||||
case TRAP_x86_PRIVINFLT: /* Invalid opcode exception */
|
||||
rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
|
||||
rec->ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
|
||||
break;
|
||||
case TRAP_x86_STKFLT: /* Stack fault */
|
||||
rec.ExceptionCode = EXCEPTION_STACK_OVERFLOW;
|
||||
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;
|
||||
rec->ExceptionCode = ERROR_sig(ucontext) ? EXCEPTION_ACCESS_VIOLATION : EXCEPTION_PRIV_INSTRUCTION;
|
||||
rec->ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
|
||||
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;
|
||||
rec->ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
|
||||
rec->NumberParameters = 2;
|
||||
rec->ExceptionInformation[0] = (ERROR_sig(ucontext) & 2) != 0;
|
||||
rec->ExceptionInformation[1] = (ULONG_PTR)siginfo->si_addr;
|
||||
break;
|
||||
case TRAP_x86_ALIGNFLT: /* Alignment check exception */
|
||||
rec.ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT;
|
||||
rec->ExceptionCode = EXCEPTION_DATATYPE_MISALIGNMENT;
|
||||
break;
|
||||
default:
|
||||
ERR( "Got unexpected trap %ld\n", TRAP_sig(ucontext) );
|
||||
|
@ -915,14 +1055,9 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
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;
|
||||
rec->ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
|
||||
break;
|
||||
}
|
||||
|
||||
status = raise_exception( &rec, &context, TRUE );
|
||||
if (status) raise_status( status, &rec );
|
||||
done:
|
||||
restore_context( &context, ucontext );
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
|
@ -932,34 +1067,20 @@ done:
|
|||
*/
|
||||
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;
|
||||
EXCEPTION_RECORD *rec = setup_exception( sigcontext, raise_generic_exception );
|
||||
|
||||
switch (siginfo->si_code)
|
||||
{
|
||||
case TRAP_TRACE: /* Single-step exception */
|
||||
rec.ExceptionCode = EXCEPTION_SINGLE_STEP;
|
||||
EFL_sig(ucontext) &= ~0x100; /* clear single-step flag */
|
||||
rec->ExceptionCode = EXCEPTION_SINGLE_STEP;
|
||||
break;
|
||||
case TRAP_BRKPT: /* Breakpoint exception */
|
||||
rec.ExceptionAddress = (char *)rec.ExceptionAddress - 1; /* back up over the int3 instruction */
|
||||
rec->ExceptionAddress = (char *)rec->ExceptionAddress - 1; /* back up over the int3 instruction */
|
||||
/* fall through */
|
||||
default:
|
||||
rec.ExceptionCode = EXCEPTION_BREAKPOINT;
|
||||
rec->ExceptionCode = EXCEPTION_BREAKPOINT;
|
||||
break;
|
||||
}
|
||||
|
||||
status = raise_exception( &rec, &context, TRUE );
|
||||
if (status) raise_status( status, &rec );
|
||||
restore_context( &context, ucontext );
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
|
@ -967,50 +1088,38 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
*
|
||||
* Handler for SIGFPE.
|
||||
*/
|
||||
static void fpe_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
||||
static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
||||
{
|
||||
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;
|
||||
EXCEPTION_RECORD *rec = setup_exception( sigcontext, raise_generic_exception );
|
||||
|
||||
switch (siginfo->si_code)
|
||||
{
|
||||
case FPE_FLTSUB:
|
||||
rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
|
||||
rec->ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
|
||||
break;
|
||||
case FPE_INTDIV:
|
||||
rec.ExceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
|
||||
rec->ExceptionCode = EXCEPTION_INT_DIVIDE_BY_ZERO;
|
||||
break;
|
||||
case FPE_INTOVF:
|
||||
rec.ExceptionCode = EXCEPTION_INT_OVERFLOW;
|
||||
rec->ExceptionCode = EXCEPTION_INT_OVERFLOW;
|
||||
break;
|
||||
case FPE_FLTDIV:
|
||||
rec.ExceptionCode = EXCEPTION_FLT_DIVIDE_BY_ZERO;
|
||||
rec->ExceptionCode = EXCEPTION_FLT_DIVIDE_BY_ZERO;
|
||||
break;
|
||||
case FPE_FLTOVF:
|
||||
rec.ExceptionCode = EXCEPTION_FLT_OVERFLOW;
|
||||
rec->ExceptionCode = EXCEPTION_FLT_OVERFLOW;
|
||||
break;
|
||||
case FPE_FLTUND:
|
||||
rec.ExceptionCode = EXCEPTION_FLT_UNDERFLOW;
|
||||
rec->ExceptionCode = EXCEPTION_FLT_UNDERFLOW;
|
||||
break;
|
||||
case FPE_FLTRES:
|
||||
rec.ExceptionCode = EXCEPTION_FLT_INEXACT_RESULT;
|
||||
rec->ExceptionCode = EXCEPTION_FLT_INEXACT_RESULT;
|
||||
break;
|
||||
case FPE_FLTINV:
|
||||
default:
|
||||
rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
|
||||
rec->ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
|
||||
break;
|
||||
}
|
||||
|
||||
status = raise_exception( &rec, &context, TRUE );
|
||||
if (status) raise_status( status, &rec );
|
||||
restore_context( &context, ucontext );
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
|
@ -1018,23 +1127,12 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
|||
*
|
||||
* Handler for SIGINT.
|
||||
*/
|
||||
static void int_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
||||
static void int_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
||||
{
|
||||
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 );
|
||||
EXCEPTION_RECORD *rec = setup_exception( sigcontext, raise_generic_exception );
|
||||
rec->ExceptionCode = CONTROL_C_EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1044,21 +1142,11 @@ static void int_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
|||
*
|
||||
* Handler for SIGABRT.
|
||||
*/
|
||||
static void abrt_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
||||
static void abrt_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
||||
{
|
||||
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 );
|
||||
EXCEPTION_RECORD *rec = setup_exception( sigcontext, raise_generic_exception );
|
||||
rec->ExceptionCode = EXCEPTION_WINE_ASSERTION;
|
||||
rec->ExceptionFlags = EH_NONCONTINUABLE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1096,8 +1184,15 @@ static void usr1_handler( int signal, siginfo_t *siginfo, void *ucontext )
|
|||
*/
|
||||
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 */
|
||||
assert( sizeof(TEB) <= teb_size );
|
||||
if (!signal_stack_size)
|
||||
{
|
||||
size_t size = 8192, min_size = teb_size + max( MINSIGSTKSZ, 8192 );
|
||||
/* find the first power of two not smaller than min_size */
|
||||
while (size < min_size) size *= 2;
|
||||
signal_stack_size = size - teb_size;
|
||||
}
|
||||
return signal_stack_size + teb_size;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1118,11 +1213,18 @@ int CDECL __wine_set_signal_handler(unsigned int sig, wine_signal_handler wsh)
|
|||
*/
|
||||
void signal_init_thread( TEB *teb )
|
||||
{
|
||||
stack_t ss;
|
||||
|
||||
#ifdef __linux__
|
||||
arch_prctl( ARCH_SET_GS, teb );
|
||||
#else
|
||||
# error Please define setting %gs for your architecture
|
||||
#endif
|
||||
|
||||
ss.ss_sp = (char *)teb + teb_size;
|
||||
ss.ss_size = signal_stack_size;
|
||||
ss.ss_flags = 0;
|
||||
if (sigaltstack(&ss, NULL) == -1) perror( "sigaltstack" );
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
|
@ -1133,7 +1235,7 @@ 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_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||
|
||||
sig_act.sa_sigaction = int_handler;
|
||||
if (sigaction( SIGINT, &sig_act, NULL ) == -1) goto error;
|
||||
|
|
Loading…
Reference in New Issue