[PATCH] ntdll: Allow getting/setting x86_64 context of x86 processes in wine64

Rafał Harabień rafalh92 at outlook.com
Wed Feb 24 16:49:44 CST 2021


WoW64 process has two separate contexts:
- 32 bit context used most of the time (e.g. by application code)
- 64 bit context used by system when it quits x86 emulation and jumps to
  the kernel code
A notable exception are debug registers - their state is shared. Some
debuggers make use of that fact and sets/gets debug registers of 32 bit
processes using 64 bit thread context.

This change adds support for setting and getting debug registers using
64 bit thread context. Getting other registers is allowed too and will
return values from 32 bit thread context.

This change fixes hardware breakpoints in IDA 7.0 disassembler (64 bit app)
when debugging 32 bit applications.

Signed-off-by: Rafał Harabień <rafalh92 at outlook.com>
---
 dlls/ntdll/unix/signal_x86_64.c | 62 +++++++++++++++++++++++++++++++++
 server/thread.c                 | 24 +++++++++++--
 2 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c
index 22a5d34e06c..4c1aa1bfc74 100644
--- a/dlls/ntdll/unix/signal_x86_64.c
+++ b/dlls/ntdll/unix/signal_x86_64.c
@@ -1709,6 +1709,68 @@ NTSTATUS context_to_server( context_t *to, const CONTEXT *from )
  */
 NTSTATUS context_from_server( CONTEXT *to, const context_t *from )
 {
+    if (from->cpu == CPU_x86)
+    {
+        // WoW64 process has two separate contexts: one 32 bit used when executing normal user code and one 64 bit
+        // used by WoW64 internals for talking with the kernel. Debug registers are shared between those contexts
+        // and some debuggers make use of that fact so handle getting them here. Other registers could be set to
+        // anything but to keep things sane lets copy them from 32 bit context.
+        to->ContextFlags = CONTEXT_AMD64;
+        if (from->flags & SERVER_CTX_CONTROL)
+        {
+            to->ContextFlags |= CONTEXT_CONTROL;
+            to->Rbp    = from->ctl.i386_regs.ebp;
+            to->Rip    = from->ctl.i386_regs.eip;
+            to->Rsp    = from->ctl.i386_regs.esp;
+            to->SegCs  = from->ctl.i386_regs.cs;
+            to->SegSs  = from->ctl.i386_regs.ss;
+            to->EFlags = from->ctl.i386_regs.eflags;
+        }
+
+        if (from->flags & SERVER_CTX_INTEGER)
+        {
+            to->ContextFlags |= CONTEXT_INTEGER;
+            to->Rax = from->integer.i386_regs.eax;
+            to->Rcx = from->integer.i386_regs.ecx;
+            to->Rdx = from->integer.i386_regs.edx;
+            to->Rbx = from->integer.i386_regs.ebx;
+            to->Rsi = from->integer.i386_regs.esi;
+            to->Rdi = from->integer.i386_regs.edi;
+            to->R8  = 0;
+            to->R9  = 0;
+            to->R10 = 0;
+            to->R11 = 0;
+            to->R12 = 0;
+            to->R13 = 0;
+            to->R14 = 0;
+            to->R15 = 0;
+        }
+        if (from->flags & SERVER_CTX_SEGMENTS)
+        {
+            to->ContextFlags |= CONTEXT_SEGMENTS;
+            to->SegDs = from->seg.i386_regs.ds;
+            to->SegEs = from->seg.i386_regs.es;
+            to->SegFs = from->seg.i386_regs.fs;
+            to->SegGs = from->seg.i386_regs.gs;
+        }
+        if (from->flags & SERVER_CTX_FLOATING_POINT)
+        {
+            to->ContextFlags |= CONTEXT_FLOATING_POINT;
+            memset(&to->u.FltSave, 0, sizeof(to->u.FltSave));
+        }
+        if (from->flags & SERVER_CTX_DEBUG_REGISTERS)
+        {
+            to->ContextFlags |= CONTEXT_DEBUG_REGISTERS;
+            to->Dr0 = from->debug.i386_regs.dr0;
+            to->Dr1 = from->debug.i386_regs.dr1;
+            to->Dr2 = from->debug.i386_regs.dr2;
+            to->Dr3 = from->debug.i386_regs.dr3;
+            to->Dr6 = from->debug.i386_regs.dr6;
+            to->Dr7 = from->debug.i386_regs.dr7;
+        }
+        return STATUS_SUCCESS;
+    }
+
     if (from->cpu != CPU_x86_64) return STATUS_INVALID_PARAMETER;
 
     to->ContextFlags = CONTEXT_AMD64 | (to->ContextFlags & 0x40);
diff --git a/server/thread.c b/server/thread.c
index 3cee717e169..99e82d113fa 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -1887,8 +1887,7 @@ DECL_HANDLER(set_thread_context)
     reply->self = (thread == current);
 
     if (thread->state == TERMINATED) set_error( STATUS_UNSUCCESSFUL );
-    else if (context->cpu != thread->process->cpu) set_error( STATUS_INVALID_PARAMETER );
-    else
+    else if (context->cpu == thread->process->cpu)
     {
         unsigned int system_flags = get_context_system_regs(context->cpu) & context->flags;
 
@@ -1900,6 +1899,27 @@ DECL_HANDLER(set_thread_context)
             thread->context->regs.flags |= context->flags;
         }
     }
+    else if (context->cpu == CPU_x86_64 && thread->process->cpu == CPU_x86)
+    {
+        // WoW64 process has two separate contexts: one 32 bit used when executing normal user code and one 64 bit
+        // used by WoW64 internals for talking with the kernel. Debug registers are shared between those contexts
+        // and some debuggers make use of that fact so handle setting them here.
+        unsigned int system_flags = get_context_system_regs( context->cpu ) & context->flags;
+        if (system_flags)
+        {
+            set_thread_context( thread, context, system_flags );
+            if (thread->context && !get_error())
+            {
+                thread->context->regs.debug.i386_regs.dr0 = context->debug.x86_64_regs.dr0;
+                thread->context->regs.debug.i386_regs.dr1 = context->debug.x86_64_regs.dr1;
+                thread->context->regs.debug.i386_regs.dr2 = context->debug.x86_64_regs.dr2;
+                thread->context->regs.debug.i386_regs.dr3 = context->debug.x86_64_regs.dr3;
+                thread->context->regs.debug.i386_regs.dr6 = context->debug.x86_64_regs.dr6;
+                thread->context->regs.debug.i386_regs.dr7 = context->debug.x86_64_regs.dr7;
+            }
+        }
+    }
+    else set_error( STATUS_INVALID_PARAMETER );
 
     release_object( thread );
 }
-- 
2.25.1




More information about the wine-devel mailing list