This series intent is to let winedbg (and dbghelp) load the 64bit PE modules of a wow64 debuggee.
At this stage, all debug info of all modules (PE and ELF) are properly loaded (and displayed with 'info wow share' command). Breakpoint and backtrace in 64bit code of a wow64 debuggee are not available.
The serie contains: - extension of tests to show that 64bit load dll events are generated for a wow64 debuggee (for a 64bit debugger, not for a 32bit one) - change for adapting filtering of events
I opted for doing it in ntdll and passing the machine of the mapping in request's reply. Please advisde if you'd prefer another approach.
From: Eric Pouech eric.pouech@gmail.com
Showing that load/unload events for a 64bit DLL are generated for a WOW64 process.
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/kernel32/tests/debugger.c | 94 ++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+)
diff --git a/dlls/kernel32/tests/debugger.c b/dlls/kernel32/tests/debugger.c index 513db658222..b9c05a5c052 100644 --- a/dlls/kernel32/tests/debugger.c +++ b/dlls/kernel32/tests/debugger.c @@ -1066,6 +1066,99 @@ static void test_debug_loop(int argc, char **argv) ok(ret, "DeleteFileA failed, last error %#lx.\n", GetLastError()); }
+static void test_debug_loop_wow64(void) +{ + WCHAR buffer[MAX_PATH], *p; + PROCESS_INFORMATION pi; + STARTUPINFOW si; + BOOL ret; + unsigned order = 0, bp_order = 0, bpwx_order = 0, num_ntdll = 0, num_wow64 = 0; + + /* checking conditions for running this test */ + if (GetSystemWow64DirectoryW( buffer, ARRAY_SIZE(buffer) ) && sizeof(void*) > sizeof(int) && pGetMappedFileNameW) + { + wcscat( buffer, L"\notepad.exe" ); + ret = GetFileAttributesW( buffer ) != INVALID_FILE_ATTRIBUTES; + } + else ret = FALSE; + if (!ret) + { + skip("Skipping test on incompatible config\n"); + return; + } + memset( &si, 0, sizeof(si) ); + si.cb = sizeof(si); + ret = CreateProcessW( NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi ); + ok(ret, "CreateProcess failed, last error %#lx.\n", GetLastError()); + + for (;;) + { + DEBUG_EVENT ev; + + ++order; + ret = WaitForDebugEvent( &ev, 2000 ); + if (!ret) break; + + switch (ev.dwDebugEventCode) + { + case CREATE_PROCESS_DEBUG_EVENT: + break; + case LOAD_DLL_DEBUG_EVENT: + if (!pGetMappedFileNameW( pi.hProcess, ev.u.LoadDll.lpBaseOfDll, buffer, ARRAY_SIZE(buffer) )) buffer[0] = L'\0'; + if ((p = wcsrchr( buffer, '\' ))) p++; + else p = buffer; + if (!memcmp( p, L"wow64", 5 * sizeof(WCHAR) )) + { + /* on Win10, wow64cpu's load dll event is received after first exception */ + ok(bpwx_order == 0, "loaddll for wow64 DLLs should appear before exception\n"); + num_wow64++; + } + else if (!wcsicmp( p, L"ntdll.dll" )) + { + ok(bp_order == 0 && bpwx_order == 0, "loaddll on ntdll should appear before exception\n"); + num_ntdll++; + } + break; + case EXCEPTION_DEBUG_EVENT: + if (ev.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) + bp_order = order; + else if (ev.u.Exception.ExceptionRecord.ExceptionCode == STATUS_WX86_BREAKPOINT) + bpwx_order = order; + } + ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE); + ok(ret, "ContinueDebugEvent failed, last error %#lx.\n", GetLastError()); + if (!ret) break; + } + + ret = TerminateProcess( pi.hProcess, 0 ); + ok(ret, "TerminateProcess failed: %lu\n", GetLastError()); + + /* eat up the remaining events */ + for (;;) + { + DEBUG_EVENT ev; + + if (!WaitForDebugEvent( &ev, 2000 )) break; + } + + ret = CloseHandle(pi.hThread); + ok(ret, "CloseHandle failed, last error %#lx.\n", GetLastError()); + ret = CloseHandle(pi.hProcess); + ok(ret, "CloseHandle failed, last error %#lx.\n", GetLastError()); + + todo_wine + { + ok(num_ntdll == 2, "Expecting two ntdll instances\n"); + ok(num_wow64 >= 3, "Expecting more than 3 wow64*.dll\n"); + } + ok(bp_order, "Expecting 1 bp exceptions\n"); + todo_wine + { + ok(bpwx_order, "Expecting 1 bpwx exceptions\n"); + ok(bp_order < bpwx_order, "Out of order bp exceptions\n"); + } +} + static void doChildren(int argc, char **argv) { const char *arguments = "debugger children last"; @@ -2252,6 +2345,7 @@ START_TEST(debugger) test_ExitCode(); test_RemoteDebugger(); test_debug_loop(myARGC, myARGV); + test_debug_loop_wow64(); test_debug_children(myARGV[0], DEBUG_PROCESS, TRUE, FALSE); test_debug_children(myARGV[0], DEBUG_ONLY_THIS_PROCESS, FALSE, FALSE); test_debug_children(myARGV[0], DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS, FALSE, FALSE);
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/kernel32/tests/debugger.c | 7 ++++++- dlls/ntdll/unix/sync.c | 27 +++++++++++++++++++++++++++ include/wine/server_protocol.h | 4 +++- server/debugger.c | 19 +++++++++++++++++++ server/mapping.c | 3 --- server/protocol.def | 2 ++ server/request.h | 4 +++- server/trace.c | 1 + 8 files changed, 61 insertions(+), 6 deletions(-)
diff --git a/dlls/kernel32/tests/debugger.c b/dlls/kernel32/tests/debugger.c index b9c05a5c052..901609df1cd 100644 --- a/dlls/kernel32/tests/debugger.c +++ b/dlls/kernel32/tests/debugger.c @@ -1146,11 +1146,16 @@ static void test_debug_loop_wow64(void) ret = CloseHandle(pi.hProcess); ok(ret, "CloseHandle failed, last error %#lx.\n", GetLastError());
- todo_wine + if (strcmp(winetest_platform, "wine") || num_wow64) /* windows or new wine wow */ { ok(num_ntdll == 2, "Expecting two ntdll instances\n"); ok(num_wow64 >= 3, "Expecting more than 3 wow64*.dll\n"); } + else /* Wine's old wow, or 32/64 bit only configurations */ + { + ok(num_ntdll == 1, "Expecting 1 ntdll instances\n"); + ok(num_wow64 == 0, "Expecting 0 wow64*.dll\n"); + } ok(bp_order, "Expecting 1 bp exceptions\n"); todo_wine { diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 22a3555ba3f..cfcac3735f2 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -1017,6 +1017,26 @@ static NTSTATUS event_data_to_state_change( const debug_event_t *data, DBGUI_WAI return STATUS_INTERNAL_ERROR; }
+/* Don't expose 64bit load/unload dll event for a 32bit caller; helper for NtWaitForDebugEvent */ +static BOOL filter_out_event( HANDLE handle, DBGUI_WAIT_STATE_CHANGE *state, USHORT machine ) +{ + switch (state->NewState) + { + case DbgLoadDllStateChange: + case DbgUnloadDllStateChange: + if (NtCurrentTeb()->WowTebOffset && is_machine_64bit( machine )) + { + if (state->NewState == DbgLoadDllStateChange) + NtClose( state->StateInfo.LoadDll.FileHandle ); + NtDebugContinue( handle, &state->AppClientId, DBG_CONTINUE ); + return TRUE; + } + break; + default: break; + } + return FALSE; +} + /********************************************************************** * NtWaitForDebugEvent (NTDLL.@) */ @@ -1026,6 +1046,7 @@ NTSTATUS WINAPI NtWaitForDebugEvent( HANDLE handle, BOOLEAN alertable, LARGE_INT debug_event_t data; unsigned int ret; BOOL wait = TRUE; + unsigned int machine;
for (;;) { @@ -1039,10 +1060,16 @@ NTSTATUS WINAPI NtWaitForDebugEvent( HANDLE handle, BOOLEAN alertable, LARGE_INT state->NewState = data.code; state->AppClientId.UniqueProcess = ULongToHandle( reply->pid ); state->AppClientId.UniqueThread = ULongToHandle( reply->tid ); + machine = reply->machine; } } SERVER_END_REQ;
+ if (!ret && filter_out_event( handle, state, machine )) + { + wait = TRUE; + continue; + } if (ret != STATUS_PENDING) return ret; if (!wait) return STATUS_TIMEOUT; wait = FALSE; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 472c0ea709d..6d9565033ef 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2117,6 +2117,8 @@ struct wait_debug_event_reply struct reply_header __header; process_id_t pid; thread_id_t tid; + unsigned int machine; + int __pad; /* VARARG(event,debug_event); */ };
@@ -6356,7 +6358,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 758 +#define SERVER_PROTOCOL_VERSION 759
/* ### protocol_version end ### */
diff --git a/server/debugger.c b/server/debugger.c index 48adb244b09..6c97b2b49b1 100644 --- a/server/debugger.c +++ b/server/debugger.c @@ -531,6 +531,24 @@ void debugger_detach( struct process *process, struct debug_obj *debug_obj ) resume_process( process ); }
+static unsigned int get_machine_from_event( struct debug_event *event ) +{ + struct memory_view *view; + const pe_image_info_t *info; + client_ptr_t base; + + switch (event->data.code) + { + case DbgLoadDllStateChange: base = event->data.load_dll.base; break; + case DbgUnloadDllStateChange: base = event->data.unload_dll.base; break; + default: base = 0; break; + } + if (!base || !(view = find_mapped_view( event->sender->process, base )) || + !(info = get_view_image_info( view, &base ))) + return IMAGE_FILE_MACHINE_UNKNOWN; + return info->machine; +} + /* create a debug object */ DECL_HANDLER(create_debug_obj) { @@ -567,6 +585,7 @@ DECL_HANDLER(wait_debug_event) event->sender->process->debug_event = event; reply->pid = get_process_id( event->sender->process ); reply->tid = get_thread_id( event->sender ); + reply->machine = get_machine_from_event( event ); alloc_event_handles( event, current->process ); set_reply_data( &event->data, min( get_reply_max_size(), sizeof(event->data) )); } diff --git a/server/mapping.c b/server/mapping.c index ed81a718bbe..bac3f5d3d02 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -378,10 +378,7 @@ static void set_process_machine( struct process *process, struct memory_view *vi
static int generate_dll_event( struct thread *thread, int code, struct memory_view *view ) { - unsigned short process_machine = thread->process->machine; - if (!(view->flags & SEC_IMAGE)) return 0; - if (process_machine != native_machine && process_machine != view->image.machine) return 0; generate_debug_event( thread, code, view ); return 1; } diff --git a/server/protocol.def b/server/protocol.def index 8c2fbeb4afe..20ac124aab0 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1681,6 +1681,8 @@ struct process_info @REPLY process_id_t pid; /* process id */ thread_id_t tid; /* thread id */ + unsigned int machine; + int __pad; VARARG(event,debug_event); /* debug event data */ @END
diff --git a/server/request.h b/server/request.h index 089af79e199..38f2065f311 100644 --- a/server/request.h +++ b/server/request.h @@ -1153,7 +1153,9 @@ C_ASSERT( FIELD_OFFSET(struct wait_debug_event_request, debug) == 12 ); C_ASSERT( sizeof(struct wait_debug_event_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct wait_debug_event_reply, pid) == 8 ); C_ASSERT( FIELD_OFFSET(struct wait_debug_event_reply, tid) == 12 ); -C_ASSERT( sizeof(struct wait_debug_event_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct wait_debug_event_reply, machine) == 16 ); +C_ASSERT( FIELD_OFFSET(struct wait_debug_event_reply, __pad) == 20 ); +C_ASSERT( sizeof(struct wait_debug_event_reply) == 24 ); C_ASSERT( FIELD_OFFSET(struct queue_exception_event_request, first) == 12 ); C_ASSERT( FIELD_OFFSET(struct queue_exception_event_request, code) == 16 ); C_ASSERT( FIELD_OFFSET(struct queue_exception_event_request, flags) == 20 ); diff --git a/server/trace.c b/server/trace.c index a0076d5449b..39c9a117498 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2246,6 +2246,7 @@ static void dump_wait_debug_event_reply( const struct wait_debug_event_reply *re { fprintf( stderr, " pid=%04x", req->pid ); fprintf( stderr, ", tid=%04x", req->tid ); + fprintf( stderr, ", machine=%08x", req->machine ); dump_varargs_debug_event( ", event=", cur_size ); }
it also needs update from MR!2252; so I'll update it altogether after 2252 is merged