ntdll: Use the CPU area to get/set the Wow64 context on x86-64.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2021-07-01 12:17:29 +02:00
parent 4acecd8619
commit 11d6c52f32
1 changed files with 179 additions and 5 deletions

View File

@ -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;
}