From 2654be08d514b1aa279bd3957e2964c5814f551d Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 11 Jan 2006 20:20:32 +0100 Subject: [PATCH] ntdll: Handle NtSetContextThread on the client side (as far as possible) when setting the context of the current thread. --- dlls/ntdll/ntdll_misc.h | 1 + dlls/ntdll/signal_i386.c | 41 +++++++++++--------- dlls/ntdll/signal_powerpc.c | 11 ++++++ dlls/ntdll/signal_sparc.c | 11 ++++++ dlls/ntdll/signal_x86_64.c | 11 ++++++ dlls/ntdll/thread.c | 70 ++++++++++++++++++++-------------- include/wine/server_protocol.h | 3 +- server/context_i386.c | 5 +-- server/protocol.def | 2 + server/thread.c | 1 + server/trace.c | 7 +++- 11 files changed, 113 insertions(+), 50 deletions(-) diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 322a2799a80..0df5b2192e7 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -32,6 +32,7 @@ /* exceptions */ extern void wait_suspend( CONTEXT *context ); extern void WINAPI __regs_RtlRaiseException( PEXCEPTION_RECORD, PCONTEXT ); +extern void set_cpu_context( const CONTEXT *context ); /* debug helper */ extern LPCSTR debugstr_us( const UNICODE_STRING *str ); diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c index d11e37dabeb..be33458ccb2 100644 --- a/dlls/ntdll/signal_i386.c +++ b/dlls/ntdll/signal_i386.c @@ -722,13 +722,13 @@ inline static void save_fpu( CONTEXT *context, const SIGCONTEXT *sigcontext ) * * Restore the FPU context to a sigcontext. */ -inline static void restore_fpu( CONTEXT *context ) +inline static void restore_fpu( const CONTEXT *context ) { + FLOATING_SAVE_AREA float_status = context->FloatSave; /* reset the current interrupt status */ - context->FloatSave.StatusWord &= context->FloatSave.ControlWord | 0xffffff80; + float_status.StatusWord &= float_status.ControlWord | 0xffffff80; #ifdef __GNUC__ - /* avoid nested exceptions */ - __asm__ __volatile__( "frstor %0; fwait" : : "m" (context->FloatSave) ); + __asm__ __volatile__( "frstor %0; fwait" : : "m" (float_status) ); #endif /* __GNUC__ */ } @@ -799,12 +799,19 @@ inline static void restore_context( const CONTEXT *context, SIGCONTEXT *sigconte * * Set the new CPU context. */ -inline static void DECLSPEC_NORETURN set_cpu_context( CONTEXT *context ) +void set_cpu_context( const CONTEXT *context ) { DWORD flags = context->ContextFlags & ~CONTEXT_i386; if (flags & CONTEXT_FLOATING_POINT) restore_fpu( context ); - __wine_call_from_32_restore_regs( context ); + + if (flags & CONTEXT_FULL) + { + if ((flags & CONTEXT_FULL) != (CONTEXT_FULL & ~CONTEXT_i386)) + FIXME( "setting partial context (%lx) not supported\n", flags ); + else + __wine_call_from_32_restore_regs( context ); + } } @@ -991,7 +998,7 @@ static inline DWORD get_fpu_code( const CONTEXT *context ) /********************************************************************** * raise_segv_exception */ -static void WINAPI DECLSPEC_NORETURN raise_segv_exception( EXCEPTION_RECORD *rec, CONTEXT *context ) +static void WINAPI raise_segv_exception( EXCEPTION_RECORD *rec, CONTEXT *context ) { switch(rec->ExceptionCode) { @@ -1011,14 +1018,14 @@ static void WINAPI DECLSPEC_NORETURN raise_segv_exception( EXCEPTION_RECORD *rec } __regs_RtlRaiseException( rec, context ); done: - set_cpu_context( context ); + NtSetContextThread( GetCurrentThread(), context ); } /********************************************************************** * raise_trap_exception */ -static void WINAPI DECLSPEC_NORETURN raise_trap_exception( EXCEPTION_RECORD *rec, CONTEXT *context ) +static void WINAPI raise_trap_exception( EXCEPTION_RECORD *rec, CONTEXT *context ) { DWORD dr0, dr1, dr2, dr3, dr6, dr7; @@ -1037,6 +1044,7 @@ static void WINAPI DECLSPEC_NORETURN raise_trap_exception( EXCEPTION_RECORD *rec * shall return a breakpoint, not a single step exception */ if (!(context->Dr6 & 0xf)) rec->ExceptionCode = EXCEPTION_BREAKPOINT; + context->ContextFlags |= CONTEXT_FULL; /* restore flags */ } } @@ -1049,15 +1057,14 @@ static void WINAPI DECLSPEC_NORETURN raise_trap_exception( EXCEPTION_RECORD *rec __regs_RtlRaiseException( rec, context ); + context->ContextFlags = CONTEXT_FULL; if (dr0 != context->Dr0 || dr1 != context->Dr1 || dr2 != context->Dr2 || dr3 != context->Dr3 || dr6 != context->Dr6 || dr7 != context->Dr7) { /* the debug registers have changed, set the new values */ - context->ContextFlags = CONTEXT_DEBUG_REGISTERS; - NtSetContextThread(GetCurrentThread(), context); + context->ContextFlags |= CONTEXT_DEBUG_REGISTERS; } - context->ContextFlags = CONTEXT_FULL; /* restore flags */ - set_cpu_context( context ); + NtSetContextThread( GetCurrentThread(), context ); } @@ -1066,10 +1073,10 @@ static void WINAPI DECLSPEC_NORETURN raise_trap_exception( EXCEPTION_RECORD *rec * * Generic raise function for exceptions that don't need special treatment. */ -static void WINAPI DECLSPEC_NORETURN raise_exception( EXCEPTION_RECORD *rec, CONTEXT *context ) +static void WINAPI raise_exception( EXCEPTION_RECORD *rec, CONTEXT *context ) { __regs_RtlRaiseException( rec, context ); - set_cpu_context( context ); + NtSetContextThread( GetCurrentThread(), context ); } @@ -1077,7 +1084,7 @@ static void WINAPI DECLSPEC_NORETURN raise_exception( EXCEPTION_RECORD *rec, CON /********************************************************************** * raise_vm86_sti_exception */ -static void WINAPI DECLSPEC_NORETURN raise_vm86_sti_exception( EXCEPTION_RECORD *rec, CONTEXT *context ) +static void WINAPI raise_vm86_sti_exception( EXCEPTION_RECORD *rec, CONTEXT *context ) { /* merge_vm86_pending_flags merges the vm86_pending flag in safely */ NtCurrentTeb()->vm86_pending |= VIP_MASK; @@ -1101,7 +1108,7 @@ static void WINAPI DECLSPEC_NORETURN raise_vm86_sti_exception( EXCEPTION_RECORD __regs_RtlRaiseException( rec, context ); } done: - set_cpu_context( context ); + NtSetContextThread( GetCurrentThread(), context ); } diff --git a/dlls/ntdll/signal_powerpc.c b/dlls/ntdll/signal_powerpc.c index d60075ccb02..50d48d47866 100644 --- a/dlls/ntdll/signal_powerpc.c +++ b/dlls/ntdll/signal_powerpc.c @@ -268,6 +268,17 @@ inline static void restore_fpu( CONTEXT *context, const SIGCONTEXT *sigcontext ) } +/*********************************************************************** + * set_cpu_context + * + * Set the new CPU context. + */ +void set_cpu_context( const CONTEXT *context ) +{ + FIXME("not implemented\n"); +} + + /********************************************************************** * get_fpu_code * diff --git a/dlls/ntdll/signal_sparc.c b/dlls/ntdll/signal_sparc.c index ef5e760c29d..4ae33403d12 100644 --- a/dlls/ntdll/signal_sparc.c +++ b/dlls/ntdll/signal_sparc.c @@ -148,6 +148,17 @@ static void restore_fpu( CONTEXT *context, ucontext_t *ucontext ) } +/*********************************************************************** + * set_cpu_context + * + * Set the new CPU context. + */ +void set_cpu_context( const CONTEXT *context ) +{ + FIXME("not implemented\n"); +} + + /********************************************************************** * segv_handler * diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c index 271e0a981dd..020b0dea528 100644 --- a/dlls/ntdll/signal_x86_64.c +++ b/dlls/ntdll/signal_x86_64.c @@ -196,6 +196,17 @@ static void restore_context( const CONTEXT *context, SIGCONTEXT *sigcontext ) } +/*********************************************************************** + * set_cpu_context + * + * Set the new CPU context. + */ +void set_cpu_context( const CONTEXT *context ) +{ + FIXME("not implemented\n"); +} + + /********************************************************************** * segv_handler * diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index a267acd2f5f..9a2e2bd80f2 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -491,41 +491,55 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) { NTSTATUS ret; DWORD dummy, i; + BOOL self = FALSE; - SERVER_START_REQ( set_thread_context ) - { - req->handle = handle; - req->flags = context->ContextFlags; - req->suspend = 0; - wine_server_add_data( req, context, sizeof(*context) ); - ret = wine_server_call( req ); - } - SERVER_END_REQ; +#ifdef __i386__ + /* on i386 debug registers always require a server call */ + self = ((handle == GetCurrentThread()) && + !(context->ContextFlags & (CONTEXT_DEBUG_REGISTERS & ~CONTEXT_i386))); +#endif - if (ret == STATUS_PENDING) + if (!self) { - if (NtSuspendThread( handle, &dummy ) == STATUS_SUCCESS) + SERVER_START_REQ( set_thread_context ) { - for (i = 0; i < 100; i++) - { - SERVER_START_REQ( set_thread_context ) - { - req->handle = handle; - req->flags = context->ContextFlags; - req->suspend = 0; - wine_server_add_data( req, context, sizeof(*context) ); - ret = wine_server_call( req ); - } - SERVER_END_REQ; - if (ret != STATUS_PENDING) break; - NtYieldExecution(); - } - NtResumeThread( handle, &dummy ); + req->handle = handle; + req->flags = context->ContextFlags; + req->suspend = 0; + wine_server_add_data( req, context, sizeof(*context) ); + ret = wine_server_call( req ); + self = reply->self; } + SERVER_END_REQ; + + if (ret == STATUS_PENDING) + { + if (NtSuspendThread( handle, &dummy ) == STATUS_SUCCESS) + { + for (i = 0; i < 100; i++) + { + SERVER_START_REQ( set_thread_context ) + { + req->handle = handle; + req->flags = context->ContextFlags; + req->suspend = 0; + wine_server_add_data( req, context, sizeof(*context) ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + if (ret != STATUS_PENDING) break; + NtYieldExecution(); + } + NtResumeThread( handle, &dummy ); + } + if (ret == STATUS_PENDING) ret = STATUS_ACCESS_DENIED; + } + + if (ret) return ret; } - if (ret == STATUS_PENDING) ret = STATUS_ACCESS_DENIED; - return ret; + if (self) set_cpu_context( context ); + return STATUS_SUCCESS; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index ec12eae6a39..5f2ad9ea4a0 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2006,6 +2006,7 @@ struct set_thread_context_request struct set_thread_context_reply { struct reply_header __header; + int self; }; @@ -4347,6 +4348,6 @@ union generic_reply struct query_symlink_reply query_symlink_reply; }; -#define SERVER_PROTOCOL_VERSION 218 +#define SERVER_PROTOCOL_VERSION 219 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/context_i386.c b/server/context_i386.c index 1d9048211a8..7f1bb440238 100644 --- a/server/context_i386.c +++ b/server/context_i386.c @@ -604,10 +604,9 @@ void set_thread_context( struct thread *thread, const CONTEXT *context, unsigned flags &= ~CONTEXT_i386; /* get rid of CPU id */ if (thread->context) /* thread is inside an exception event or suspended */ - { copy_context( thread->context, context, flags ); - flags &= CONTEXT_DEBUG_REGISTERS; - } + + flags &= CONTEXT_DEBUG_REGISTERS; /* the other registers are handled on the client side */ if (flags && suspend_for_ptrace( thread )) { diff --git a/server/protocol.def b/server/protocol.def index 65d18b9e18d..55aef190841 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1442,6 +1442,8 @@ enum char_info_mode unsigned int flags; /* context flags */ int suspend; /* if setting context during suspend */ VARARG(context,context); /* thread context */ +@REPLY + int self; /* was it a handle to the current thread? */ @END diff --git a/server/thread.c b/server/thread.c index 507eed56a39..3ffbf8538fe 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1132,6 +1132,7 @@ DECL_HANDLER(set_thread_context) { set_thread_context( thread, get_req_data(), req->flags ); } + reply->self = (thread == current); release_object( thread ); } diff --git a/server/trace.c b/server/trace.c index cb9fe822b76..49ff25e81dc 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1908,6 +1908,11 @@ static void dump_set_thread_context_request( const struct set_thread_context_req dump_varargs_context( cur_size ); } +static void dump_set_thread_context_reply( const struct set_thread_context_reply *req ) +{ + fprintf( stderr, " self=%d", req->self ); +} + static void dump_get_selector_entry_request( const struct get_selector_entry_request *req ) { fprintf( stderr, " handle=%p,", req->handle ); @@ -3527,7 +3532,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_cancel_timer_reply, (dump_func)dump_get_timer_info_reply, (dump_func)dump_get_thread_context_reply, - (dump_func)0, + (dump_func)dump_set_thread_context_reply, (dump_func)dump_get_selector_entry_reply, (dump_func)dump_add_atom_reply, (dump_func)0,