ntdll: Add support for debug registers in exceptions on x86-64.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2017-07-19 17:11:38 +02:00
parent 9e59362ae3
commit a1b563f41c
1 changed files with 96 additions and 18 deletions

View File

@ -310,6 +310,12 @@ static wine_signal_handler handlers[256];
struct amd64_thread_data
{
DWORD_PTR dr0; /* debug registers */
DWORD_PTR dr1;
DWORD_PTR dr2;
DWORD_PTR dr3;
DWORD_PTR dr6;
DWORD_PTR dr7;
void *exit_frame; /* exit frame pointer */
};
@ -1661,7 +1667,7 @@ static inline BOOL is_inside_signal_stack( void *ptr )
*/
static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
{
context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_DEBUG_REGISTERS;
context->Rax = RAX_sig(sigcontext);
context->Rcx = RCX_sig(sigcontext);
context->Rdx = RDX_sig(sigcontext);
@ -1698,6 +1704,12 @@ static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
#else
__asm__("movw %%ss,%0" : "=m" (context->SegSs));
#endif
context->Dr0 = amd64_thread_data()->dr0;
context->Dr1 = amd64_thread_data()->dr1;
context->Dr2 = amd64_thread_data()->dr2;
context->Dr3 = amd64_thread_data()->dr3;
context->Dr6 = amd64_thread_data()->dr6;
context->Dr7 = amd64_thread_data()->dr7;
if (FPU_sig(sigcontext))
{
context->ContextFlags |= CONTEXT_FLOATING_POINT;
@ -1714,6 +1726,12 @@ static void save_context( CONTEXT *context, const ucontext_t *sigcontext )
*/
static void restore_context( const CONTEXT *context, ucontext_t *sigcontext )
{
amd64_thread_data()->dr0 = context->Dr0;
amd64_thread_data()->dr1 = context->Dr1;
amd64_thread_data()->dr2 = context->Dr2;
amd64_thread_data()->dr3 = context->Dr3;
amd64_thread_data()->dr6 = context->Dr6;
amd64_thread_data()->dr7 = context->Dr7;
RAX_sig(sigcontext) = context->Rax;
RCX_sig(sigcontext) = context->Rcx;
RDX_sig(sigcontext) = context->Rdx;
@ -1873,6 +1891,16 @@ __ASM_GLOBAL_FUNC( set_full_cpu_context,
static void set_cpu_context( const CONTEXT *context )
{
DWORD flags = context->ContextFlags & ~CONTEXT_AMD64;
if (flags & CONTEXT_DEBUG_REGISTERS)
{
amd64_thread_data()->dr0 = context->Dr0;
amd64_thread_data()->dr1 = context->Dr1;
amd64_thread_data()->dr2 = context->Dr2;
amd64_thread_data()->dr3 = context->Dr3;
amd64_thread_data()->dr6 = context->Dr6;
amd64_thread_data()->dr7 = context->Dr7;
}
if (flags & CONTEXT_FULL)
{
if (!(flags & CONTEXT_CONTROL))
@ -2081,10 +2109,20 @@ NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
*/
NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context )
{
NTSTATUS ret;
BOOL self;
NTSTATUS ret = STATUS_SUCCESS;
BOOL self = (handle == GetCurrentThread());
/* debug registers require a server call */
if (self && (context->ContextFlags & (CONTEXT_DEBUG_REGISTERS & ~CONTEXT_AMD64)))
self = (amd64_thread_data()->dr0 == context->Dr0 &&
amd64_thread_data()->dr1 == context->Dr1 &&
amd64_thread_data()->dr2 == context->Dr2 &&
amd64_thread_data()->dr3 == context->Dr3 &&
amd64_thread_data()->dr6 == context->Dr6 &&
amd64_thread_data()->dr7 == context->Dr7);
if (!self) ret = set_thread_context( handle, context, &self );
ret = set_thread_context( handle, context, &self );
if (self && ret == STATUS_SUCCESS) set_cpu_context( context );
return ret;
}
@ -2109,12 +2147,25 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
needed_flags &= ~context->ContextFlags;
}
if (self && needed_flags)
if (self)
{
CONTEXT ctx;
RtlCaptureContext( &ctx );
copy_context( context, &ctx, ctx.ContextFlags & needed_flags );
context->ContextFlags |= ctx.ContextFlags & needed_flags;
if (needed_flags)
{
CONTEXT ctx;
RtlCaptureContext( &ctx );
copy_context( context, &ctx, ctx.ContextFlags & needed_flags );
context->ContextFlags |= ctx.ContextFlags & needed_flags;
}
/* update the cached version of the debug registers */
if (context->ContextFlags & (CONTEXT_DEBUG_REGISTERS & ~CONTEXT_AMD64))
{
amd64_thread_data()->dr0 = context->Dr0;
amd64_thread_data()->dr1 = context->Dr1;
amd64_thread_data()->dr2 = context->Dr2;
amd64_thread_data()->dr3 = context->Dr3;
amd64_thread_data()->dr6 = context->Dr6;
amd64_thread_data()->dr7 = context->Dr7;
}
}
return STATUS_SUCCESS;
}
@ -2618,14 +2669,42 @@ static void raise_segv_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
case 3: /* BREAKPOINT_LOAD_SYMBOLS */
case 4: /* BREAKPOINT_UNLOAD_SYMBOLS */
case 5: /* BREAKPOINT_COMMAND_STRING (>= Win2003) */
goto done;
set_cpu_context( context );
}
break;
}
status = raise_exception( rec, context, TRUE );
if (status) raise_status( status, rec );
done:
set_cpu_context( context );
status = NtRaiseException( rec, context, TRUE );
raise_status( status, rec );
}
/**********************************************************************
* raise_trap_exception
*/
static void raise_trap_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
{
NTSTATUS status;
if (rec->ExceptionCode == EXCEPTION_SINGLE_STEP)
{
/* when single stepping can't tell whether this is a hw bp or a
* single step interrupt. try to avoid as much overhead as possible
* and only do a server call if there is any hw bp enabled. */
if( !(context->EFlags & 0x100) || (amd64_thread_data()->dr7 & 0xff) )
{
/* (possible) hardware breakpoint, fetch the debug registers */
DWORD saved_flags = context->ContextFlags;
context->ContextFlags = CONTEXT_DEBUG_REGISTERS;
NtGetContextThread(GetCurrentThread(), context);
context->ContextFlags |= saved_flags; /* restore flags */
}
context->EFlags &= ~0x100; /* clear single-step flag */
}
status = NtRaiseException( rec, context, TRUE );
raise_status( status, rec );
}
@ -2636,9 +2715,8 @@ done:
*/
static void raise_generic_exception( EXCEPTION_RECORD *rec, CONTEXT *context )
{
NTSTATUS status = raise_exception( rec, context, TRUE );
if (status) raise_status( status, rec );
set_cpu_context( context );
NTSTATUS status = NtRaiseException( rec, context, TRUE );
raise_status( status, rec );
}
@ -2747,7 +2825,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
*/
static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
{
EXCEPTION_RECORD *rec = setup_exception( sigcontext, raise_generic_exception );
EXCEPTION_RECORD *rec = setup_exception( sigcontext, raise_trap_exception );
switch (siginfo->si_code)
{