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@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 ); }
Rafał Harabień rafalh92@outlook.com writes:
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.
Some test cases would be nice.
Hello,
I was thinking about adding some test but the problem is that I have to execute 32 bit executable from a 64 bit test and Wine for each architecture is built separately. Do you have any suggestions how such test should be implemented? Is Wine testbot using standard bi-arch build where first wine64 is built and then wine32 with option "--with-wine64"?
Best regards,
Rafał Harabień
On 25.02.2021 23:44, Alexandre Julliard wrote:
Rafał Harabień rafalh92@outlook.com writes:
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.
Some test cases would be nice.