Module: wine Branch: master Commit: 11d6c52f32e4ac31414f1b8580606e61c738c452 URL: https://source.winehq.org/git/wine.git/?a=commit;h=11d6c52f32e4ac31414f1b858...
Author: Alexandre Julliard julliard@winehq.org Date: Thu Jul 1 12:17:29 2021 +0200
ntdll: Use the CPU area to get/set the Wow64 context on x86-64.
Signed-off-by: Alexandre Julliard julliard@winehq.org
---
dlls/ntdll/unix/signal_x86_64.c | 184 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 179 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index f9e82d72190..56dc640e082 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1614,7 +1614,8 @@ void *get_native_context( CONTEXT *context ) */ void *get_wow_context( CONTEXT *context ) { - return NULL; + if (context->SegCs != cs64_sel) return NULL; + return get_cpu_area( IMAGE_FILE_MACHINE_I386 ); }
@@ -1851,10 +1852,97 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) */ NTSTATUS set_thread_wow64_context( HANDLE handle, const void *ctx, ULONG size ) { - BOOL self; + BOOL self = (handle == GetCurrentThread()); + struct syscall_frame *frame = amd64_thread_data()->syscall_frame; + I386_CONTEXT *wow_frame; + const I386_CONTEXT *context = ctx; + DWORD flags = context->ContextFlags & ~CONTEXT_i386;
if (size != sizeof(I386_CONTEXT)) return STATUS_INFO_LENGTH_MISMATCH; - return set_thread_context( handle, ctx, &self, IMAGE_FILE_MACHINE_I386 ); + + /* debug registers require a server call */ + if (self && (flags & CONTEXT_I386_DEBUG_REGISTERS)) + 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) + { + NTSTATUS ret = set_thread_context( handle, context, &self, IMAGE_FILE_MACHINE_I386 ); + if (ret || !self) return ret; + if (flags & CONTEXT_I386_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_I386_DEBUG_REGISTERS)) return ret; + } + + if (!(wow_frame = get_cpu_area( IMAGE_FILE_MACHINE_I386 ))) return STATUS_INVALID_PARAMETER; + + if (flags & CONTEXT_I386_INTEGER) + { + wow_frame->Eax = context->Eax; + wow_frame->Ebx = context->Ebx; + wow_frame->Ecx = context->Ecx; + wow_frame->Edx = context->Edx; + wow_frame->Esi = context->Esi; + wow_frame->Edi = context->Edi; + } + if (flags & CONTEXT_I386_CONTROL) + { + wow_frame->Esp = context->Esp; + wow_frame->Ebp = context->Ebp; + wow_frame->Eip = context->Eip; + wow_frame->EFlags = context->EFlags; + wow_frame->SegCs = cs32_sel; + wow_frame->SegSs = ds64_sel; + } + if (flags & CONTEXT_I386_SEGMENTS) + { + wow_frame->SegDs = ds64_sel; + wow_frame->SegEs = ds64_sel; + wow_frame->SegFs = 0; /* FIXME */ + wow_frame->SegGs = ds64_sel; + } + if (flags & CONTEXT_I386_DEBUG_REGISTERS) + { + wow_frame->Dr0 = context->Dr0; + wow_frame->Dr1 = context->Dr1; + wow_frame->Dr2 = context->Dr2; + wow_frame->Dr3 = context->Dr3; + wow_frame->Dr6 = context->Dr6; + wow_frame->Dr7 = context->Dr7; + } + if (flags & CONTEXT_I386_EXTENDED_REGISTERS) + { + memcpy( &frame->xsave, context->ExtendedRegisters, sizeof(frame->xsave) ); + } + else if (flags & CONTEXT_I386_FLOATING_POINT) + { + fpu_to_fpux( &frame->xsave, &context->FloatSave ); + } + if (flags & CONTEXT_I386_XSTATE) + { + CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); + XSTATE *xs = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); + + if (xs->Mask & XSTATE_MASK_GSSE) + { + frame->xstate.Mask |= XSTATE_MASK_GSSE; + memcpy( &frame->xstate.YmmContext, &xs->YmmContext, sizeof(xs->YmmContext) ); + } + else frame->xstate.Mask &= ~XSTATE_MASK_GSSE; + frame->restore_flags |= CONTEXT_I386_XSTATE; + } + return STATUS_SUCCESS; }
@@ -1863,10 +1951,96 @@ NTSTATUS set_thread_wow64_context( HANDLE handle, const void *ctx, ULONG size ) */ NTSTATUS get_thread_wow64_context( HANDLE handle, void *ctx, ULONG size ) { - BOOL self; + DWORD needed_flags; + struct syscall_frame *frame = amd64_thread_data()->syscall_frame; + I386_CONTEXT *wow_frame, *context = ctx; + BOOL self = (handle == GetCurrentThread());
if (size != sizeof(I386_CONTEXT)) return STATUS_INFO_LENGTH_MISMATCH; - return get_thread_context( handle, ctx, &self, IMAGE_FILE_MACHINE_I386 ); + + needed_flags = context->ContextFlags & ~CONTEXT_i386; + + /* debug registers require a server call */ + if (needed_flags & CONTEXT_I386_DEBUG_REGISTERS) self = FALSE; + + if (!self) + { + NTSTATUS ret = get_thread_context( handle, context, &self, IMAGE_FILE_MACHINE_I386 ); + if (ret || !self) return ret; + /* update the cached version of the debug registers */ + if (needed_flags & CONTEXT_I386_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 (!(needed_flags & ~CONTEXT_I386_DEBUG_REGISTERS)) return ret; + } + + if (!(wow_frame = get_cpu_area( IMAGE_FILE_MACHINE_I386 ))) return STATUS_INVALID_PARAMETER; + + if (needed_flags & CONTEXT_I386_INTEGER) + { + context->Eax = wow_frame->Eax; + context->Ebx = wow_frame->Ebx; + context->Ecx = wow_frame->Ecx; + context->Edx = wow_frame->Edx; + context->Esi = wow_frame->Esi; + context->Edi = wow_frame->Edi; + context->ContextFlags |= CONTEXT_I386_INTEGER; + } + if (needed_flags & CONTEXT_I386_CONTROL) + { + context->Esp = wow_frame->Esp; + context->Ebp = wow_frame->Ebp; + context->Eip = wow_frame->Eip; + context->EFlags = wow_frame->EFlags; + context->SegCs = wow_frame->SegCs; + context->SegSs = wow_frame->SegSs; + context->ContextFlags |= CONTEXT_I386_CONTROL; + } + if (needed_flags & CONTEXT_I386_SEGMENTS) + { + context->SegDs = wow_frame->SegDs; + context->SegEs = wow_frame->SegEs; + context->SegFs = wow_frame->SegFs; + context->SegGs = wow_frame->SegGs; + context->ContextFlags |= CONTEXT_I386_SEGMENTS; + } + if (needed_flags & CONTEXT_I386_EXTENDED_REGISTERS) + { + memcpy( context->ExtendedRegisters, &frame->xsave, sizeof(context->ExtendedRegisters) ); + context->ContextFlags |= CONTEXT_I386_EXTENDED_REGISTERS; + } + if (needed_flags & CONTEXT_I386_FLOATING_POINT) + { + fpux_to_fpu( &context->FloatSave, &frame->xsave ); + context->ContextFlags |= CONTEXT_I386_FLOATING_POINT; + } + if ((needed_flags & CONTEXT_I386_XSTATE) && (cpu_info.ProcessorFeatureBits & CPU_FEATURE_AVX)) + { + CONTEXT_EX *context_ex = (CONTEXT_EX *)(context + 1); + XSTATE *xstate = (XSTATE *)((char *)context_ex + context_ex->XState.Offset); + unsigned int mask; + + if (context_ex->XState.Length < offsetof(XSTATE, YmmContext) || + context_ex->XState.Length > sizeof(XSTATE)) + return STATUS_INVALID_PARAMETER; + + mask = (xstate_compaction_enabled ? xstate->CompactionMask : xstate->Mask) & XSTATE_MASK_GSSE; + xstate->Mask = frame->xstate.Mask & mask; + xstate->CompactionMask = xstate_compaction_enabled ? (0x8000000000000000 | mask) : 0; + memset( xstate->Reserved, 0, sizeof(xstate->Reserved) ); + if (xstate->Mask) + { + if (context_ex->XState.Length < sizeof(XSTATE)) return STATUS_BUFFER_OVERFLOW; + memcpy( &xstate->YmmContext, &frame->xstate.YmmContext, sizeof(xstate->YmmContext) ); + } + } + return STATUS_SUCCESS; }