The aim of this series is to re-implement dbghelp.EnumerateLoadedModules* in order to match Windows' behavior (especially in wow64 environments).
The serie includes: - a bunch of tests to compare 3 different sets of loaded modules: + the load DLL debug events (generated by kernel) + the output of dbghelp.EnumerateLoadedModules(), which is a wrapper around kernel32.EnumProcesModulesEx (yet modifying some information) + the actual list of loaded modules in dbghelp (SymEnumerateModules). => the later list can be populated automatically from the second if requested to dbghelp (this is what is tested). => for the record, winedbg uses the first set to populate the third set. We just test the content of first set, not the population to the third. - the reimplementation of EnumerateLoadedModules to match Window's behavior (especially regarding paths information)
Despite the three sets look similar, they have quite a few differences to be taken care of (especially in wow64 setup).
Note: having the correct paths (esp. in wow64) is important as dbghelp sometimes tries some wild guesses based on the path (like bitness <g>) or (in some other places) don't rely on path information and retries things on its own. The long target is to simplify the module lookup by relying on more solid information.
@julliard: the paths for wow64 modules returned from kernel32 don't match windows behavior: wine returns 32bit system modules from c:\windows\syswow64 whereas windows returns them from c:\windows\system32 (except a couple of exceptions: 32bit ntdll and exe main module). Implementation in dbghelp includes the correction of this. (I opted not to change ntdll at once: testing on windows show that kernel32.EnumProcesModulesEx returns exactly the ldr_data list, and I didn't feel like adding this burden to this patch ;-).
Let me know if you want ntdll to be fixed before this patch.
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/dbghelp/tests/dbghelp.c | 77 +++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 22 deletions(-)
diff --git a/dlls/dbghelp/tests/dbghelp.c b/dlls/dbghelp/tests/dbghelp.c index 276faf0101a..1986917f7d7 100644 --- a/dlls/dbghelp/tests/dbghelp.c +++ b/dlls/dbghelp/tests/dbghelp.c @@ -719,6 +719,7 @@ static void test_loaded_modules(void) break; case PCSKIND_64BIT: case PCSKIND_WOW64: + case PCSKIND_WINE_OLD_WOW64: todo_wine ok(aggregation.count_systemdir > 2 && aggregation.count_wowdir == 1, "Wrong directory aggregation count %u %u\n", aggregation.count_systemdir, aggregation.count_wowdir); @@ -727,10 +728,6 @@ static void test_loaded_modules(void) ok(aggregation.count_systemdir > 2 && aggregation.count_wowdir == 0, "Wrong directory aggregation count %u %u\n", aggregation.count_systemdir, aggregation.count_wowdir); break; - case PCSKIND_WINE_OLD_WOW64: - ok(aggregation.count_systemdir == 1 && aggregation.count_wowdir > 2, "Wrong directory aggregation count %u %u\n", - aggregation.count_systemdir, aggregation.count_wowdir); - break; } }
@@ -762,15 +759,32 @@ static void test_loaded_modules(void) ret = wrapper_EnumerateLoadedModulesW64(pi.hProcess, aggregate_cb, &aggregation); ok(ret, "EnumerateLoadedModulesW64 failed: %lu\n", GetLastError());
- todo_wine - ok(aggregation.count_32bit == 1 && aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n", - aggregation.count_32bit, aggregation.count_64bit); - ok(aggregation.count_exe == 1 && aggregation.count_ntdll == 1, "Wrong kind aggregation count %u %u\n", - aggregation.count_exe, aggregation.count_ntdll); - todo_wine - ok(aggregation.count_systemdir > 2 && aggregation.count_64bit == aggregation.count_systemdir && aggregation.count_wowdir == 1, - "Wrong directory aggregation count %u %u\n", - aggregation.count_systemdir, aggregation.count_wowdir); + switch (get_process_kind(pi.hProcess)) + { + case PCSKIND_ERROR: + break; + case PCSKIND_WINE_OLD_WOW64: + break; + ok(aggregation.count_32bit == 1 && !aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n", + aggregation.count_32bit, aggregation.count_64bit); + ok(aggregation.count_exe == 1 && !aggregation.count_ntdll, "Wrong kind aggregation count %u %u\n", + aggregation.count_exe, aggregation.count_ntdll); + ok(aggregation.count_systemdir == 0 && aggregation.count_wowdir == 1, + "Wrong directory aggregation count %u %u\n", + aggregation.count_systemdir, aggregation.count_wowdir); + break; + default: + todo_wine + ok(aggregation.count_32bit == 1 && aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n", + aggregation.count_32bit, aggregation.count_64bit); + ok(aggregation.count_exe == 1 && aggregation.count_ntdll == 1, "Wrong kind aggregation count %u %u\n", + aggregation.count_exe, aggregation.count_ntdll); + todo_wine + ok(aggregation.count_systemdir > 2 && aggregation.count_64bit == aggregation.count_systemdir && aggregation.count_wowdir == 1, + "Wrong directory aggregation count %u %u\n", + aggregation.count_systemdir, aggregation.count_wowdir); + break; + }
ret = SymRefreshModuleList(pi.hProcess); ok(ret, "SymRefreshModuleList failed: %lu\n", GetLastError()); @@ -803,15 +817,34 @@ static void test_loaded_modules(void) ret = wrapper_EnumerateLoadedModulesW64(pi.hProcess, aggregate_cb, &aggregation2); ok(ret, "EnumerateLoadedModulesW64 failed: %lu\n", GetLastError());
- ok(aggregation2.count_32bit && aggregation2.count_64bit, "Wrong bitness aggregation count %u %u\n", - aggregation2.count_32bit, aggregation2.count_64bit); - todo_wine - ok(aggregation2.count_exe == 2 && aggregation2.count_ntdll == 2, "Wrong kind aggregation count %u %u\n", - aggregation2.count_exe, aggregation2.count_ntdll); - todo_wine - ok(aggregation2.count_systemdir > 2 && aggregation2.count_64bit == aggregation2.count_systemdir && aggregation2.count_wowdir > 2, - "Wrong directory aggregation count %u %u\n", - aggregation2.count_systemdir, aggregation2.count_wowdir); + switch (get_process_kind(pi.hProcess)) + { + case PCSKIND_ERROR: + break; + case PCSKIND_WINE_OLD_WOW64: + todo_wine + ok(aggregation2.count_32bit && !aggregation2.count_64bit, "Wrong bitness aggregation count %u %u\n", + aggregation2.count_32bit, aggregation2.count_64bit); + todo_wine + ok(aggregation2.count_exe == 2 && aggregation2.count_ntdll == 1, "Wrong kind aggregation count %u %u\n", + aggregation2.count_exe, aggregation2.count_ntdll); + todo_wine + ok(aggregation2.count_systemdir == 0 && aggregation2.count_32bit == aggregation2.count_wowdir + 1 && aggregation2.count_wowdir > 2, + "Wrong directory aggregation count %u %u\n", + aggregation2.count_systemdir, aggregation2.count_wowdir); + break; + default: + ok(aggregation2.count_32bit && aggregation2.count_64bit, "Wrong bitness aggregation count %u %u\n", + aggregation2.count_32bit, aggregation2.count_64bit); + todo_wine + ok(aggregation2.count_exe == 2 && aggregation2.count_ntdll == 2, "Wrong kind aggregation count %u %u\n", + aggregation2.count_exe, aggregation2.count_ntdll); + todo_wine + ok(aggregation2.count_systemdir > 2 && aggregation2.count_64bit == aggregation2.count_systemdir && aggregation2.count_wowdir > 2, + "Wrong directory aggregation count %u %u\n", + aggregation2.count_systemdir, aggregation2.count_wowdir); + break; + }
ret = SymRefreshModuleList(pi.hProcess); ok(ret, "SymRefreshModuleList failed: %lu\n", GetLastError());
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/dbghelp/tests/dbghelp.c | 478 +++++++++++++++++++++++++++++++++++ 1 file changed, 478 insertions(+)
diff --git a/dlls/dbghelp/tests/dbghelp.c b/dlls/dbghelp/tests/dbghelp.c index 1986917f7d7..74ba83fd393 100644 --- a/dlls/dbghelp/tests/dbghelp.c +++ b/dlls/dbghelp/tests/dbghelp.c @@ -612,6 +612,12 @@ static enum process_kind get_process_kind(HANDLE process) return pcskind; }
+static const char* process_kind2string(enum process_kind pcskind) +{ + static const char* str[] = {"error", "64bit", "32bit", "wine_old_wow64", "wow64"}; + return (pcskind >= ARRAY_SIZE(str)) ? str[0] : str[pcskind]; +} + struct loaded_module_aggregation { HANDLE proc; @@ -862,6 +868,477 @@ static void test_loaded_modules(void) } }
+static USHORT pcs_get_loaded_module_machine(HANDLE hProc, DWORD64 base) +{ + IMAGE_DOS_HEADER dos; + DWORD signature; + IMAGE_FILE_HEADER fh; + BOOL ret; + + ret = ReadProcessMemory(hProc, (char*)(DWORD_PTR)base, &dos, sizeof(dos), NULL); + ok(ret, "ReadProcessMemory failed: %lu\n", GetLastError()); + ok(dos.e_magic == IMAGE_DOS_SIGNATURE, "Unexpected signature %x\n", dos.e_magic); + ret = ReadProcessMemory(hProc, (char*)(DWORD_PTR)(base + dos.e_lfanew), &signature, sizeof(signature), NULL); + ok(ret, "ReadProcessMemory failed: %lu\n", GetLastError()); + ok(signature == IMAGE_NT_SIGNATURE, "Unexpected signature %lx\n", signature); + ret = ReadProcessMemory(hProc, (char*)(DWORD_PTR)(base + dos.e_lfanew + sizeof(signature) ), &fh, sizeof(fh), NULL); + ok(ret, "ReadProcessMemory failed: %lu\n", GetLastError()); + return fh.Machine; +} + +static void aggregation_push(struct loaded_module_aggregation* aggregation, DWORD64 base, const WCHAR* name, USHORT machine) +{ + BOOL wow64; + + switch (get_machine_bitness(machine)) + { + case 32: aggregation->count_32bit++; break; + case 64: aggregation->count_64bit++; break; + default: break; + } + if (ends_withW(name, L".exe")) + aggregation->count_exe++; + if (ends_withW(name, L"ntdll.dll")) + aggregation->count_ntdll++; + if (!wcsnicmp(name, system_directory, wcslen(system_directory))) + aggregation->count_systemdir++; + if (IsWow64Process(aggregation->proc, &wow64) && wow64 && + !wcsnicmp(name, wow64_directory, wcslen(wow64_directory))) + aggregation->count_wowdir++; +} + +static BOOL CALLBACK aggregation_sym_cb(const WCHAR* name, DWORD64 base, void* usr) +{ + struct loaded_module_aggregation* aggregation = usr; + IMAGEHLP_MODULEW64 module; + BOOL ret; + + module.SizeOfStruct = sizeof(module); + ret = SymGetModuleInfoW64(aggregation->proc, base, &module); + ok(ret, "SymGetModuleInfoW64 failed: %lu\n", GetLastError()); + + aggregation_push(aggregation, base, module.LoadedImageName, module.MachineType); + + return TRUE; +} + +static BOOL CALLBACK aggregate_enum_cb(PCWSTR imagename, DWORD64 base, ULONG sz, PVOID usr) +{ + struct loaded_module_aggregation* aggregation = usr; + + aggregation_push(aggregation, base, imagename, pcs_get_loaded_module_machine(aggregation->proc, base)); + + return TRUE; +} + +static BOOL pcs_fetch_module_name(HANDLE proc, void* str_addr, void* module_base, WCHAR* buffer, unsigned bufsize, BOOL unicode) +{ + BOOL ret = FALSE; + + if (str_addr) + { + void* tgt = NULL; + SIZE_T addr_size; + SIZE_T r; + BOOL is_wow64; + + ret = IsWow64Process(proc, &is_wow64); + ok(ret, "IsWow64Process failed: %lu\n", GetLastError()); + addr_size = is_win64 && is_wow64 ? 4 : sizeof(void*); + + /* note: string is not always present */ + /* assuming little endian CPU */ + ret = ReadProcessMemory(proc, str_addr, &tgt, addr_size, &r) && r == addr_size; + if (ret) + { + if (unicode) + { + ret = ReadProcessMemory(proc, tgt, buffer, bufsize, &r); + if (ret) + buffer[r / sizeof(WCHAR)] = '\0'; + } + else + { + char *tmp = malloc(bufsize); + if (tmp) + { + ret = ReadProcessMemory(proc, tgt, tmp, bufsize, &r); + if (ret) + { + if (!r) tmp[0] = '\0'; + else if (!memchr(tmp, '\0', r)) tmp[r - 1] = '\0'; + MultiByteToWideChar(CP_ACP, 0, tmp, -1, buffer, bufsize); + buffer[bufsize - 1] = '\0'; + } + free(tmp); + } + else ret = FALSE; + } + } + } + if (!ret) + { + WCHAR tmp[128]; + WCHAR drv[3] = {L'A', L':', L'\0'}; + + ret = GetMappedFileNameW(proc, module_base, buffer, bufsize); + ok(ret, "GetMappedFileNameW failed: %lu\n", GetLastError()); + if (!wcsncmp(buffer, L"\??\", 4)) + memmove(buffer, buffer + 4, (wcslen(buffer) + 1 - 4) * sizeof(WCHAR)); + while (drv[0] <= L'Z') + { + if (QueryDosDeviceW(drv, tmp, ARRAY_SIZE(tmp))) + { + size_t len = wcslen(tmp); + if (len >= 2 && !wcsnicmp(buffer, tmp, len)) + { + memmove(buffer + 2, buffer + len, (wcslen(buffer) + 1 - len) * sizeof(WCHAR)); + buffer[0] = drv[0]; + buffer[1] = drv[1]; + break; + } + } + drv[0]++; + } + } + return ret; +} + +static void test_live_modules_proc(WCHAR* exename, BOOL with_32) +{ + WCHAR buffer[MAX_PATH]; + PROCESS_INFORMATION pi = {0}; + STARTUPINFOW si = {0}; + DEBUG_EVENT de; + BOOL ret; + BOOL is_wow64 = FALSE; + struct loaded_module_aggregation aggregation_event = {}; + struct loaded_module_aggregation aggregation_enum = {}; + struct loaded_module_aggregation aggregation_sym = {}; + DWORD old_options = SymGetOptions(); + enum process_kind pcskind; + if (with_32) + SymSetOptions(old_options | SYMOPT_INCLUDE_32BIT_MODULES); + else + SymSetOptions(old_options & ~SYMOPT_INCLUDE_32BIT_MODULES); + + ret = CreateProcessW(NULL, exename, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi); + if (!ret) + { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + skip("Skip wow64 test on non compatible platform\n"); + else + ok(ret, "CreateProcess failed: %lu\n", GetLastError()); + return; + } + + ret = IsWow64Process(pi.hProcess, &is_wow64); + ok(ret, "IsWow64Process failed: %lu\n", GetLastError()); + pcskind = get_process_kind(pi.hProcess); + ok(pcskind != PCSKIND_ERROR, "Unexpected error\n"); + + winetest_push_context("[%u/%u enum:%s %s]", + is_win64 ? 64 : 32, is_wow64 ? 32 : 64, with_32 ? "+32bit" : "default", + process_kind2string(pcskind)); + + memset(&aggregation_event, 0, sizeof(aggregation_event)); + aggregation_event.proc = pi.hProcess; + while (WaitForDebugEvent(&de, 2000)) + { + switch (de.dwDebugEventCode) + { + case CREATE_PROCESS_DEBUG_EVENT: + if (pcs_fetch_module_name(pi.hProcess, de.u.CreateProcessInfo.lpImageName, de.u.CreateProcessInfo.lpBaseOfImage, + buffer, ARRAY_SIZE(buffer), de.u.CreateProcessInfo.fUnicode)) + aggregation_push(&aggregation_event, (ULONG_PTR)de.u.CreateProcessInfo.lpBaseOfImage, buffer, + pcs_get_loaded_module_machine(pi.hProcess, (ULONG_PTR)de.u.CreateProcessInfo.lpBaseOfImage)); + break; + case LOAD_DLL_DEBUG_EVENT: + if (pcs_fetch_module_name(pi.hProcess, de.u.LoadDll.lpImageName, de.u.LoadDll.lpBaseOfDll, buffer, ARRAY_SIZE(buffer), de.u.LoadDll.fUnicode)) + aggregation_push(&aggregation_event, (ULONG_PTR)de.u.LoadDll.lpBaseOfDll, buffer, + pcs_get_loaded_module_machine(pi.hProcess, (ULONG_PTR)de.u.LoadDll.lpBaseOfDll)); + break; + case UNLOAD_DLL_DEBUG_EVENT: + /* FIXME: we should take of updating aggregation_event; as of today, doesn't trigger issue */ + break; + case EXCEPTION_DEBUG_EVENT: + break; + } + ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE); + ok(ret, "ContinueDebugEvent failed: %lu\n", GetLastError()); + } + + memset(&aggregation_enum, 0, sizeof(aggregation_enum)); + aggregation_enum.proc = pi.hProcess; + ret = wrapper_EnumerateLoadedModulesW64(pi.hProcess, aggregate_enum_cb, &aggregation_enum); + ok(ret, "SymEnumerateModulesW64 failed: %lu\n", GetLastError()); + + ret = SymInitialize(pi.hProcess, NULL, TRUE); + ok(ret, "SymInitialize failed: %lu\n", GetLastError()); + +/* .exe ntdll kernel32 kernelbase user32 gdi32 (all are in system or wow64 directory) */ +#define MODCOUNT 6 +/* when in wow64: wow64 wow64cpu wow64win ntdll (64bit) */ +#define MODWOWCOUNT 4 +/* .exe is reported twice in enum */ +#define XTRAEXE (!!with_32) +/* ntdll is reported twice in enum */ +#define XTRANTDLL (is_win64 && is_wow64 && (!!with_32)) + + memset(&aggregation_sym, 0, sizeof(aggregation_sym)); + aggregation_sym.proc = pi.hProcess; + ret = SymEnumerateModulesW64(pi.hProcess, aggregation_sym_cb, &aggregation_sym); + ok(ret, "SymEnumerateModulesW64 failed: %lu\n", GetLastError()); + + if (pcskind == PCSKIND_64BIT) /* 64/64 */ + { + ok(is_win64, "How come?\n"); + ok(aggregation_event.count_exe == 1, "Unexpected event.count_exe %u\n", aggregation_event.count_exe); + ok(aggregation_event.count_32bit == 0, "Unexpected event.count_32bit %u\n", aggregation_event.count_32bit); + ok(aggregation_event.count_64bit >= MODCOUNT, "Unexpected event.count_64bit %u\n", aggregation_event.count_64bit); + ok(aggregation_event.count_systemdir >= MODCOUNT, "Unexpected event.count_systemdir %u\n", aggregation_event.count_systemdir); + ok(aggregation_event.count_wowdir == 0, "Unexpected event.count_wowdir %u\n", aggregation_event.count_wowdir); + ok(aggregation_event.count_ntdll == 1, "Unexpected event.count_ntdll %u\n", aggregation_event.count_ntdll); + + ok(aggregation_enum.count_exe == 1, "Unexpected enum.count_exe %u\n", aggregation_enum.count_exe); + ok(aggregation_enum.count_32bit == 0, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); + ok(aggregation_enum.count_64bit >= MODCOUNT, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); + ok(aggregation_enum.count_systemdir >= MODCOUNT, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); + ok(aggregation_enum.count_wowdir == 0, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); + ok(aggregation_enum.count_ntdll == 1, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll); + + ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); + ok(aggregation_sym.count_32bit == 0, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); + ok(aggregation_sym.count_64bit >= MODCOUNT, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); + ok(aggregation_sym.count_systemdir >= MODCOUNT, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); + ok(aggregation_sym.count_wowdir == 0, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); + ok(aggregation_sym.count_ntdll == 1, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); + } + else if (pcskind == PCSKIND_32BIT) /* 32/32 */ + { + ok(!is_win64, "How come?\n"); + ok(aggregation_event.count_exe == 1, "Unexpected event.count_exe %u\n", aggregation_event.count_exe); + ok(aggregation_event.count_32bit >= MODCOUNT, "Unexpected event.count_32bit %u\n", aggregation_event.count_32bit); + ok(aggregation_event.count_64bit == 0, "Unexpected event.count_64bit %u\n", aggregation_event.count_64bit); + ok(aggregation_event.count_systemdir >= MODCOUNT, "Unexpected event.count_systemdir %u\n", aggregation_event.count_systemdir); + ok(aggregation_event.count_wowdir == 0, "Unexpected event.count_wowdir %u\n", aggregation_event.count_wowdir); + ok(aggregation_event.count_ntdll == 1, "Unexpected event.count_ntdll %u\n", aggregation_event.count_ntdll); + + ok(aggregation_enum.count_exe == 1, "Unexpected enum.count_exe %u\n", aggregation_enum.count_exe); + ok(aggregation_enum.count_32bit >= MODCOUNT, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); + ok(aggregation_enum.count_64bit == 0, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); + ok(aggregation_enum.count_systemdir >= MODCOUNT, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); + ok(aggregation_enum.count_wowdir == 0, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); + ok(aggregation_enum.count_ntdll == 1, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll); + + ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); + ok(aggregation_sym.count_32bit >= MODCOUNT, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); + ok(aggregation_sym.count_64bit == 0, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); + ok(aggregation_sym.count_systemdir >= MODCOUNT, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); + ok(aggregation_sym.count_wowdir == 0, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); + ok(aggregation_sym.count_ntdll == 1, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); + } + else if (is_win64 && pcskind == PCSKIND_WOW64) /* 64/32 */ + { + ok(aggregation_event.count_exe == 1, "Unexpected event.count_exe %u\n", aggregation_event.count_exe); + ok(aggregation_event.count_32bit >= MODCOUNT, "Unexpected event.count_32bit %u\n", aggregation_event.count_32bit); + ok(aggregation_event.count_64bit >= MODWOWCOUNT, "Unexpected event.count_64bit %u\n", aggregation_event.count_64bit); + ok(aggregation_event.count_systemdir >= MODWOWCOUNT, "Unexpected event.count_systemdir %u\n", aggregation_event.count_systemdir); + ok(aggregation_event.count_wowdir >= MODCOUNT, "Unexpected event.count_wowdir %u\n", aggregation_event.count_wowdir); + ok(aggregation_event.count_ntdll == 2, "Unexpected event.count_ntdll %u\n", aggregation_event.count_ntdll); + + todo_wine_if(with_32) + ok(aggregation_enum.count_exe == 1 + XTRAEXE, "Unexpected enum.count_exe %u\n", aggregation_enum.count_exe); + if (with_32) + ok(aggregation_enum.count_32bit >= MODCOUNT, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); + else + todo_wine + ok(aggregation_enum.count_32bit == 1, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); + todo_wine + ok(aggregation_enum.count_64bit >= MODWOWCOUNT, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); + todo_wine + ok(aggregation_enum.count_systemdir >= MODWOWCOUNT, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); + if (with_32) + ok(aggregation_enum.count_wowdir >= MODCOUNT, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); + else + todo_wine + ok(aggregation_enum.count_wowdir == 1, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); + todo_wine_if(with_32) + ok(aggregation_enum.count_ntdll == 1 + XTRANTDLL, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll); + + ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); + if (with_32) + ok(aggregation_sym.count_32bit >= MODCOUNT, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); + else + todo_wine + ok(aggregation_sym.count_32bit == 1, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); + todo_wine + ok(aggregation_sym.count_64bit >= MODWOWCOUNT, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); + todo_wine + ok(aggregation_sym.count_systemdir >= MODWOWCOUNT, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); + if (with_32) + ok(aggregation_sym.count_wowdir >= MODCOUNT, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); + else + todo_wine + ok(aggregation_sym.count_wowdir == 1, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); + todo_wine_if(with_32) + ok(aggregation_sym.count_ntdll == 1 + XTRANTDLL, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); + } + else if (!is_win64 && pcskind == PCSKIND_WOW64) /* 32/32 */ + { + ok(aggregation_event.count_exe == 1, "Unexpected event.count_exe %u\n", aggregation_event.count_exe); + ok(aggregation_event.count_32bit >= MODCOUNT, "Unexpected event.count_32bit %u\n", aggregation_event.count_32bit); + ok(aggregation_event.count_64bit == 0, "Unexpected event.count_64bit %u\n", aggregation_event.count_64bit); + ok(aggregation_event.count_systemdir == 0, "Unexpected event.count_systemdir %u\n", aggregation_event.count_systemdir); + ok(aggregation_event.count_wowdir >= MODCOUNT, "Unexpected event.count_wowdir %u\n", aggregation_event.count_wowdir); + ok(aggregation_event.count_ntdll == 1, "Unexpected event.count_ntdll %u\n", aggregation_event.count_ntdll); + + ok(aggregation_enum.count_exe == 1 + XTRAEXE, "Unexpected enum.count_exe %u\n", aggregation_enum.count_exe); + ok(aggregation_enum.count_32bit >= MODCOUNT, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); + ok(aggregation_enum.count_64bit == 0, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); + /* yes that's different from event! */ + todo_wine + ok(aggregation_enum.count_systemdir >= MODCOUNT - 1, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); + /* .exe */ + todo_wine + ok(aggregation_enum.count_wowdir == 1, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); + ok(aggregation_enum.count_ntdll == 1, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll); + + ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); + ok(aggregation_sym.count_32bit >= MODCOUNT, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); + ok(aggregation_sym.count_64bit == 0, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); + todo_wine + ok(aggregation_sym.count_systemdir >= MODCOUNT - 1, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); + /* .exe */ + todo_wine + ok(aggregation_sym.count_wowdir == 1, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); + ok(aggregation_sym.count_ntdll == 1 + XTRANTDLL, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); + } + else if (is_win64 && pcskind == PCSKIND_WINE_OLD_WOW64) /* 64/32 */ + { + ok(aggregation_event.count_exe == 1, "Unexpected event.count_exe %u\n", aggregation_event.count_exe); + ok(aggregation_event.count_32bit >= MODCOUNT, "Unexpected event.count_32bit %u\n", aggregation_event.count_32bit); + ok(aggregation_event.count_64bit == 0, "Unexpected event.count_64bit %u\n", aggregation_event.count_64bit); + ok(aggregation_event.count_systemdir >= 1, "Unexpected event.count_systemdir %u\n", aggregation_event.count_systemdir); + ok(aggregation_event.count_wowdir >= MODCOUNT - 1, "Unexpected event.count_wowdir %u\n", aggregation_event.count_wowdir); + ok(aggregation_event.count_ntdll == 1, "Unexpected event.count_ntdll %u\n", aggregation_event.count_ntdll); + + todo_wine_if(with_32) + ok(aggregation_enum.count_exe == 1 + XTRAEXE, "Unexpected enum.count_exe %u\n", aggregation_enum.count_exe); + if (with_32) + ok(aggregation_enum.count_32bit >= MODCOUNT, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); + else + todo_wine_if(!with_32) + ok(aggregation_enum.count_32bit <= 1, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); + ok(aggregation_enum.count_64bit == 0, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); + todo_wine + ok(aggregation_enum.count_systemdir == 0, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); + if (with_32) + ok(aggregation_enum.count_wowdir >= MODCOUNT, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); + else + todo_wine_if(!with_32) + ok(aggregation_enum.count_wowdir <= 1, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); + todo_wine_if(!with_32) + ok(aggregation_enum.count_ntdll == XTRANTDLL, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll); + + ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); + if (with_32) + ok(aggregation_sym.count_32bit >= MODCOUNT, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); + else + todo_wine_if(!with_32) + ok(aggregation_sym.count_wowdir <= 1, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); + todo_wine + ok(aggregation_sym.count_64bit == 0, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); + todo_wine + ok(aggregation_sym.count_systemdir == 0, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); + if (with_32) + ok(aggregation_sym.count_wowdir >= MODCOUNT, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); + else + todo_wine_if(!with_32) + ok(aggregation_sym.count_wowdir <= 1, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); + todo_wine_if(!with_32) + ok(aggregation_sym.count_ntdll == XTRANTDLL, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); + } + else if (!is_win64 && pcskind == PCSKIND_WINE_OLD_WOW64) /* 32/32 */ + { + ok(aggregation_event.count_exe == 1, "Unexpected event.count_exe %u\n", aggregation_event.count_exe); + ok(aggregation_event.count_32bit >= MODCOUNT, "Unexpected event.count_32bit %u\n", aggregation_event.count_32bit); + ok(aggregation_event.count_64bit == 0, "Unexpected event.count_64bit %u\n", aggregation_event.count_64bit); + ok(aggregation_event.count_systemdir == 1, "Unexpected event.count_systemdir %u\n", aggregation_event.count_systemdir); + ok(aggregation_event.count_wowdir >= MODCOUNT, "Unexpected event.count_wowdir %u\n", aggregation_event.count_wowdir); + ok(aggregation_event.count_ntdll == 1, "Unexpected event.count_ntdll %u\n", aggregation_event.count_ntdll); + + ok(aggregation_enum.count_exe == 1 + XTRAEXE, "Unexpected enum.count_exe %u\n", aggregation_enum.count_exe); + ok(aggregation_enum.count_32bit >= MODCOUNT, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); + ok(aggregation_enum.count_64bit == 0, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); + todo_wine + ok(aggregation_enum.count_systemdir >= MODCOUNT, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); + /* .exe */ + todo_wine + ok(aggregation_enum.count_wowdir == 1, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); + ok(aggregation_enum.count_ntdll == 1 + XTRANTDLL, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll); + + ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); + ok(aggregation_sym.count_32bit >= MODCOUNT, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); + ok(aggregation_sym.count_64bit == 0, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); + todo_wine + ok(aggregation_sym.count_systemdir >= MODCOUNT, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); + /* .exe */ + todo_wine + ok(aggregation_sym.count_wowdir == 1, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); + ok(aggregation_sym.count_ntdll == 1 + XTRANTDLL, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); + } + else + ok(0, "Unexpected process kind %u\n", pcskind); + + /* main module is enumerated twice in enum when including 32bit modules */ + todo_wine_if(is_win64 && (pcskind == PCSKIND_WOW64 || pcskind == PCSKIND_WINE_OLD_WOW64) && !with_32) + ok(aggregation_sym.count_32bit + XTRAEXE == aggregation_enum.count_32bit, "Different sym/enum count32_bit (%u/%u)\n", + aggregation_sym.count_32bit, aggregation_enum.count_32bit); + todo_wine_if(is_win64 && (pcskind == PCSKIND_WOW64 || pcskind == PCSKIND_WINE_OLD_WOW64)) + ok(aggregation_sym.count_64bit == aggregation_enum.count_64bit, "Different sym/enum count64_bit (%u/%u)\n", + aggregation_sym.count_64bit, aggregation_enum.count_64bit); + ok(aggregation_sym.count_systemdir == aggregation_enum.count_systemdir, "Different sym/enum systemdir (%u/%u)\n", + aggregation_sym.count_systemdir, aggregation_enum.count_systemdir); + todo_wine_if(with_32) + ok(aggregation_sym.count_wowdir + XTRAEXE == aggregation_enum.count_wowdir, "Different sym/enum wowdir (%u/%u)\n", + aggregation_sym.count_wowdir, aggregation_enum.count_wowdir); + todo_wine_if(with_32) + ok(aggregation_sym.count_exe + XTRAEXE == aggregation_enum.count_exe, "Different sym/enum exe (%u/%u)\n", + aggregation_sym.count_exe, aggregation_enum.count_exe); + ok(aggregation_sym.count_ntdll == aggregation_enum.count_ntdll, "Different sym/enum exe (%u/%u)\n", + aggregation_sym.count_ntdll, aggregation_enum.count_ntdll); + +#undef MODCOUNT +#undef MODWOWCOUNT +#undef XTRAEXE +#undef XTRANTDLL + + TerminateProcess(pi.hProcess, 0); + + winetest_pop_context(); + SymSetOptions(old_options); +} + +static void test_live_modules(void) +{ + WCHAR buffer[200]; + + wcscpy(buffer, system_directory); + wcscat(buffer, L"\msinfo32.exe"); + + test_live_modules_proc(buffer, FALSE); + + if (is_win64) + { + wcscpy(buffer, wow64_directory); + wcscat(buffer, L"\msinfo32.exe"); + + test_live_modules_proc(buffer, TRUE); + test_live_modules_proc(buffer, FALSE); + } +} + START_TEST(dbghelp) { BOOL ret; @@ -891,5 +1368,6 @@ START_TEST(dbghelp) { test_modules_overlap(); test_loaded_modules(); + test_live_modules(); } }
From: Eric Pouech eric.pouech@gmail.com
- correctly taking into accoung SYMOPT_INCLUDE_32BIT_MODULES option - working around module paths returned by ntdll which are inconsistent with Windows' behavior - introducting PCSKIND_UNKNOWN (for handling missing API when running on Win8 and Win10 1507)
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/dbghelp/Makefile.in | 2 +- dlls/dbghelp/module.c | 154 +++++++++++++++++++++++++++++------ dlls/dbghelp/tests/dbghelp.c | 101 ++++++++++------------- 3 files changed, 172 insertions(+), 85 deletions(-)
diff --git a/dlls/dbghelp/Makefile.in b/dlls/dbghelp/Makefile.in index 22be2612eeb..74647820126 100644 --- a/dlls/dbghelp/Makefile.in +++ b/dlls/dbghelp/Makefile.in @@ -1,6 +1,6 @@ MODULE = dbghelp.dll IMPORTLIB = dbghelp -IMPORTS = $(ZLIB_PE_LIBS) +IMPORTS = kernelbase $(ZLIB_PE_LIBS) EXTRAINCL = $(ZLIB_PE_CFLAGS) EXTRADEFS = -D_IMAGEHLP_SOURCE_ DELAYIMPORTS = version diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index ee559cfd99e..c60eb650a1d 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -1262,6 +1262,53 @@ BOOL WINAPI EnumerateLoadedModules(HANDLE hProcess, return EnumerateLoadedModulesW64(hProcess, enum_load_modW64_32, &x); }
+static unsigned int load_and_grow_modules(HANDLE process, HMODULE** hmods, unsigned* alloc, DWORD filter) +{ + DWORD needed; + BOOL ret; + + while ((ret = EnumProcessModulesEx(process, *hmods, *alloc * sizeof(HMODULE), + &needed, filter)) && + needed > *alloc * sizeof(HMODULE)) + { + HMODULE* new = HeapReAlloc(GetProcessHeap(), 0, *hmods, (*alloc) * 2 * sizeof(HMODULE)); + if (!new) return FALSE; + *hmods = new; + *alloc *= 2; + } + return ret ? needed / sizeof(HMODULE) : 0; +} + +static BOOL enumerate_modules_with_conversion(HANDLE process, HMODULE* hmods, + unsigned first, unsigned last, const WCHAR* src, const WCHAR* dst, + PENUMLOADED_MODULES_CALLBACKW64 cb, void* usr) +{ + unsigned int i; + WCHAR imagenameW[MAX_PATH]; + MODULEINFO mi; + size_t src_len = src ? wcslen(src) : 0; + size_t dst_len = dst ? wcslen(dst) : 0; + + for (i = first; i < last; i++) + { + if (GetModuleInformation(process, hmods[i], &mi, sizeof(mi)) && + GetModuleFileNameExW(process, hmods[i], imagenameW, ARRAY_SIZE(imagenameW))) + { + size_t len = wcslen(imagenameW); + + /* transform directories if requested */ + if (src && dst && !wcsnicmp(imagenameW, src, src_len) && + (len - src_len + dst_len) + 1 <= ARRAY_SIZE(imagenameW)) + { + memmove(&imagenameW[dst_len], &imagenameW[src_len], (len - src_len) * sizeof(WCHAR)); + memcpy(imagenameW, dst, dst_len * sizeof(WCHAR)); + } + if (!cb(imagenameW, (DWORD_PTR)mi.lpBaseOfDll, mi.SizeOfImage, usr)) return TRUE; + } + } + return FALSE; +} + /****************************************************************** * EnumerateLoadedModulesW64 (DBGHELP.@) * @@ -1271,44 +1318,99 @@ BOOL WINAPI EnumerateLoadedModulesW64(HANDLE hProcess, PVOID UserContext) { HMODULE* hMods; - WCHAR imagenameW[MAX_PATH]; - DWORD i, sz; - MODULEINFO mi; - BOOL wow64; - DWORD filter = LIST_MODULES_DEFAULT; + unsigned alloc = 256, count; + USHORT pcs_machine, native_machine; + BOOL is_self_wow64, is_pcs_wow64, aborted; + WCHAR* sysdir; + WCHAR* wowdir; + size_t sysdir_len, wowdir_len;
- hMods = HeapAlloc(GetProcessHeap(), 0, 256 * sizeof(hMods[0])); - if (!hMods) return FALSE; + /* hProcess might not be a valid process handle */ + if (!IsWow64Process2(hProcess, &pcs_machine, &native_machine)) return FALSE; + if (!IsWow64Process(GetCurrentProcess(), &is_self_wow64)) return FALSE;
- if (sizeof(void*) > sizeof(int) && - IsWow64Process(hProcess, &wow64) && - wow64) - filter = LIST_MODULES_32BIT; + is_pcs_wow64 = (sizeof(void*) > sizeof(int)) && pcs_machine != IMAGE_FILE_MACHINE_UNKNOWN;
- if (!EnumProcessModulesEx(hProcess, hMods, 256 * sizeof(hMods[0]), &sz, filter)) + if (is_pcs_wow64 || is_self_wow64) { - /* hProcess should also be a valid process handle !! */ - HeapFree(GetProcessHeap(), 0, hMods); + sysdir_len = GetSystemDirectoryW(NULL, 0); + wowdir_len = GetSystemWow64Directory2W(NULL, 0, pcs_machine); + + if (!sysdir_len || !wowdir_len || + !(sysdir = HeapAlloc(GetProcessHeap(), 0, (sysdir_len + 1 + wowdir_len + 1) * sizeof(WCHAR)))) + return FALSE; + wowdir = sysdir + sysdir_len + 1; + if (GetSystemDirectoryW(sysdir, sysdir_len) >= sysdir_len) + FIXME("shouldn't happen\n"); + if (GetSystemWow64Directory2W(wowdir, wowdir_len, pcs_machine) >= wowdir_len) + FIXME("shouldn't happen\n"); + wcscat(sysdir, L"\"); + wcscat(wowdir, L"\"); + } + else sysdir = wowdir = NULL; + + hMods = HeapAlloc(GetProcessHeap(), 0, alloc * sizeof(hMods[0])); + if (!hMods) + { + HeapFree(GetProcessHeap(), 0, sysdir); return FALSE; } - if (sz > 256 * sizeof(hMods[0])) + + /* We should for this function: + * - if caller is not wow64 but target process is wow64, report all 32bit + * system modules from wow directory; + * - otherwise, report them from system directory (except for the main exec which + * is still reported from wow directory in 64/32 case) + * + * Windows always expose from EnumProcessModulesEx (under wow64 or not) + * all the system DLLs from system directory. + * + * Wine incorrectly exposes from EnumProcessModulesEx() for a wow64 process + * the 32bit system modules from the wow directory; except for ntdll, which + * is reported from system directory. + * + * Hence: + * - for LIST_MODULES_DEFAULT, convert the erroneous wine image names from + * wow to system directory + * - for LIST_MODULES_32BIT, convert the system to wow directory if caller is + * 64 bit + */ + if ((count = load_and_grow_modules(hProcess, &hMods, &alloc, LIST_MODULES_DEFAULT))) { - hMods = HeapReAlloc(GetProcessHeap(), 0, hMods, sz); - if (!hMods || !EnumProcessModulesEx(hProcess, hMods, sz, &sz, filter)) - return FALSE; + if (pcs_machine != IMAGE_FILE_MACHINE_UNKNOWN) + { + aborted = enumerate_modules_with_conversion(hProcess, hMods, 0, 1, sysdir, wowdir, + EnumLoadedModulesCallback, UserContext); + if (!aborted) + aborted = enumerate_modules_with_conversion(hProcess, hMods, 1, count, wowdir, sysdir, + EnumLoadedModulesCallback, UserContext); + } + else + aborted = enumerate_modules_with_conversion(hProcess, hMods, 0, count, wowdir, sysdir, + EnumLoadedModulesCallback, UserContext); } - sz /= sizeof(HMODULE); - for (i = 0; i < sz; i++) + else aborted = TRUE; + + if (!aborted && is_pcs_wow64 && (dbghelp_options & SYMOPT_INCLUDE_32BIT_MODULES)) { - if (!GetModuleInformation(hProcess, hMods[i], &mi, sizeof(mi)) || - !GetModuleFileNameExW(hProcess, hMods[i], imagenameW, ARRAY_SIZE(imagenameW))) - continue; - EnumLoadedModulesCallback(imagenameW, (DWORD_PTR)mi.lpBaseOfDll, mi.SizeOfImage, - UserContext); + /* expose 32bit system DLLs from syswow64. */ + count = load_and_grow_modules(hProcess, &hMods, &alloc, LIST_MODULES_32BIT); + if (sizeof(void*) > sizeof(int)) + aborted = enumerate_modules_with_conversion(hProcess, hMods, + 0, count, sysdir, wowdir, + EnumLoadedModulesCallback, UserContext); + else + /* potentially main module */ + aborted = enumerate_modules_with_conversion(hProcess, hMods, + 0, count, NULL, NULL, + EnumLoadedModulesCallback, UserContext); + count = 1; /* not to return error */ } + + HeapFree(GetProcessHeap(), 0, sysdir); HeapFree(GetProcessHeap(), 0, hMods);
- return sz != 0 && i == sz; + return count != 0; }
static void dbghelp_str_WtoA(const WCHAR *src, char *dst, int dst_len) diff --git a/dlls/dbghelp/tests/dbghelp.c b/dlls/dbghelp/tests/dbghelp.c index 74ba83fd393..98e1adb2d75 100644 --- a/dlls/dbghelp/tests/dbghelp.c +++ b/dlls/dbghelp/tests/dbghelp.c @@ -565,18 +565,24 @@ enum process_kind PCSKIND_32BIT, /* 32 bit only configuration (Wine, some Win 7...) */ PCSKIND_WINE_OLD_WOW64, /* Wine "old" wow64 configuration */ PCSKIND_WOW64, /* Wine "new" wow64 configuration, and Windows with wow64 support */ + PCSKIND_UNKNOWN, /* can't tell between PCSKIND_32BIT and PCSKIND_64BIT, on some old Windows platforms */ };
static enum process_kind get_process_kind_internal(HANDLE process) { USHORT m1, m2;
- if (!pIsWow64Process2) + if (!pIsWow64Process2) /* only happens on old Win 8 and early win 1064v1507 */ + { BOOL is_wow64; - return is_win64 ? PCSKIND_64BIT : - IsWow64Process(process, &is_wow64) && is_wow64 ? PCSKIND_WOW64 : - PCSKIND_32BIT; + + if (!strcmp(winetest_platform, "wine") || !IsWow64Process(process, &is_wow64)) + return PCSKIND_ERROR; + if (is_wow64) return PCSKIND_WOW64; + if (!is_win64) /* child cannot be 64bit */ return PCSKIND_32BIT; + /* we could guess child bitness by using ReadMemory, but keep it simple for now */ + return PCSKIND_UNKNOWN; } if (!pIsWow64Process2(process, &m1, &m2)) return PCSKIND_ERROR; if (m1 == IMAGE_FILE_MACHINE_UNKNOWN && get_machine_bitness(m2) == 32) return PCSKIND_32BIT; @@ -721,12 +727,12 @@ static void test_loaded_modules(void) switch (get_process_kind(pi.hProcess)) { case PCSKIND_ERROR: + case PCSKIND_UNKNOWN: /* child is 32bit ! */ ok(0, "Unknown process kind\n"); break; case PCSKIND_64BIT: case PCSKIND_WOW64: case PCSKIND_WINE_OLD_WOW64: - todo_wine ok(aggregation.count_systemdir > 2 && aggregation.count_wowdir == 1, "Wrong directory aggregation count %u %u\n", aggregation.count_systemdir, aggregation.count_wowdir); break; @@ -780,12 +786,10 @@ static void test_loaded_modules(void) aggregation.count_systemdir, aggregation.count_wowdir); break; default: - todo_wine ok(aggregation.count_32bit == 1 && aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n", aggregation.count_32bit, aggregation.count_64bit); ok(aggregation.count_exe == 1 && aggregation.count_ntdll == 1, "Wrong kind aggregation count %u %u\n", aggregation.count_exe, aggregation.count_ntdll); - todo_wine ok(aggregation.count_systemdir > 2 && aggregation.count_64bit == aggregation.count_systemdir && aggregation.count_wowdir == 1, "Wrong directory aggregation count %u %u\n", aggregation.count_systemdir, aggregation.count_wowdir); @@ -828,13 +832,10 @@ static void test_loaded_modules(void) case PCSKIND_ERROR: break; case PCSKIND_WINE_OLD_WOW64: - todo_wine ok(aggregation2.count_32bit && !aggregation2.count_64bit, "Wrong bitness aggregation count %u %u\n", aggregation2.count_32bit, aggregation2.count_64bit); - todo_wine ok(aggregation2.count_exe == 2 && aggregation2.count_ntdll == 1, "Wrong kind aggregation count %u %u\n", aggregation2.count_exe, aggregation2.count_ntdll); - todo_wine ok(aggregation2.count_systemdir == 0 && aggregation2.count_32bit == aggregation2.count_wowdir + 1 && aggregation2.count_wowdir > 2, "Wrong directory aggregation count %u %u\n", aggregation2.count_systemdir, aggregation2.count_wowdir); @@ -842,10 +843,8 @@ static void test_loaded_modules(void) default: ok(aggregation2.count_32bit && aggregation2.count_64bit, "Wrong bitness aggregation count %u %u\n", aggregation2.count_32bit, aggregation2.count_64bit); - todo_wine ok(aggregation2.count_exe == 2 && aggregation2.count_ntdll == 2, "Wrong kind aggregation count %u %u\n", aggregation2.count_exe, aggregation2.count_ntdll); - todo_wine ok(aggregation2.count_systemdir > 2 && aggregation2.count_64bit == aggregation2.count_systemdir && aggregation2.count_wowdir > 2, "Wrong directory aggregation count %u %u\n", aggregation2.count_systemdir, aggregation2.count_wowdir); @@ -956,6 +955,13 @@ static BOOL pcs_fetch_module_name(HANDLE proc, void* str_addr, void* module_base ret = ReadProcessMemory(proc, tgt, buffer, bufsize, &r); if (ret) buffer[r / sizeof(WCHAR)] = '\0'; + /* Win11 starts exposing ntdll from the string indirection in DLL load event. + * So we won't fall back to the mapping's filename below, good! + * But the sent DLL name for the 32bit ntdll is now "ntdll32.dll" instead of "ntdll.dll". + * Replace it by "ntdll.dll" so we won't have to worry about it in the rest of the code. + */ + if (broken(!wcscmp(buffer, L"ntdll32.dll"))) + wcscpy(buffer, L"ntdll.dll"); } else { @@ -982,23 +988,27 @@ static BOOL pcs_fetch_module_name(HANDLE proc, void* str_addr, void* module_base WCHAR drv[3] = {L'A', L':', L'\0'};
ret = GetMappedFileNameW(proc, module_base, buffer, bufsize); - ok(ret, "GetMappedFileNameW failed: %lu\n", GetLastError()); - if (!wcsncmp(buffer, L"\??\", 4)) - memmove(buffer, buffer + 4, (wcslen(buffer) + 1 - 4) * sizeof(WCHAR)); - while (drv[0] <= L'Z') + /* Win8 returns this error */ + if (!broken(!ret && GetLastError() == ERROR_FILE_INVALID)) { - if (QueryDosDeviceW(drv, tmp, ARRAY_SIZE(tmp))) + ok(ret, "GetMappedFileNameW failed: %lu\n", GetLastError()); + if (!wcsncmp(buffer, L"\??\", 4)) + memmove(buffer, buffer + 4, (wcslen(buffer) + 1 - 4) * sizeof(WCHAR)); + while (drv[0] <= L'Z') { - size_t len = wcslen(tmp); - if (len >= 2 && !wcsnicmp(buffer, tmp, len)) + if (QueryDosDeviceW(drv, tmp, ARRAY_SIZE(tmp))) { - memmove(buffer + 2, buffer + len, (wcslen(buffer) + 1 - len) * sizeof(WCHAR)); - buffer[0] = drv[0]; - buffer[1] = drv[1]; - break; + size_t len = wcslen(tmp); + if (len >= 2 && !wcsnicmp(buffer, tmp, len)) + { + memmove(buffer + 2, buffer + len, (wcslen(buffer) + 1 - len) * sizeof(WCHAR)); + buffer[0] = drv[0]; + buffer[1] = drv[1]; + break; + } } + drv[0]++; } - drv[0]++; } } return ret; @@ -1036,6 +1046,11 @@ static void test_live_modules_proc(WCHAR* exename, BOOL with_32) ok(ret, "IsWow64Process failed: %lu\n", GetLastError()); pcskind = get_process_kind(pi.hProcess); ok(pcskind != PCSKIND_ERROR, "Unexpected error\n"); + if (pcskind == PCSKIND_UNKNOWN) + { + skip("Skipping modules test on too old Windows\n"); + return; + }
winetest_push_context("[%u/%u enum:%s %s]", is_win64 ? 64 : 32, is_wow64 ? 32 : 64, with_32 ? "+32bit" : "default", @@ -1145,43 +1160,35 @@ static void test_live_modules_proc(WCHAR* exename, BOOL with_32) ok(aggregation_event.count_64bit >= MODWOWCOUNT, "Unexpected event.count_64bit %u\n", aggregation_event.count_64bit); ok(aggregation_event.count_systemdir >= MODWOWCOUNT, "Unexpected event.count_systemdir %u\n", aggregation_event.count_systemdir); ok(aggregation_event.count_wowdir >= MODCOUNT, "Unexpected event.count_wowdir %u\n", aggregation_event.count_wowdir); - ok(aggregation_event.count_ntdll == 2, "Unexpected event.count_ntdll %u\n", aggregation_event.count_ntdll); + ok(aggregation_event.count_ntdll == 2 || broken(aggregation_event.count_ntdll == 3), + /* yes! with Win 864 and Win1064 v 1507... 2 ntdll ain't enuff <g>, we report 3! */ + /* in fact the first ntdll is reported twice (at same address) in two consecutive events */ + "Unexpected event.count_ntdll %u\n", aggregation_event.count_ntdll);
- todo_wine_if(with_32) ok(aggregation_enum.count_exe == 1 + XTRAEXE, "Unexpected enum.count_exe %u\n", aggregation_enum.count_exe); if (with_32) ok(aggregation_enum.count_32bit >= MODCOUNT, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); else - todo_wine ok(aggregation_enum.count_32bit == 1, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); - todo_wine ok(aggregation_enum.count_64bit >= MODWOWCOUNT, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); - todo_wine ok(aggregation_enum.count_systemdir >= MODWOWCOUNT, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); if (with_32) ok(aggregation_enum.count_wowdir >= MODCOUNT, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); else - todo_wine ok(aggregation_enum.count_wowdir == 1, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); - todo_wine_if(with_32) ok(aggregation_enum.count_ntdll == 1 + XTRANTDLL, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll);
ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); if (with_32) ok(aggregation_sym.count_32bit >= MODCOUNT, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); else - todo_wine ok(aggregation_sym.count_32bit == 1, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); - todo_wine ok(aggregation_sym.count_64bit >= MODWOWCOUNT, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); - todo_wine ok(aggregation_sym.count_systemdir >= MODWOWCOUNT, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); if (with_32) ok(aggregation_sym.count_wowdir >= MODCOUNT, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); else - todo_wine ok(aggregation_sym.count_wowdir == 1, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); - todo_wine_if(with_32) ok(aggregation_sym.count_ntdll == 1 + XTRANTDLL, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); } else if (!is_win64 && pcskind == PCSKIND_WOW64) /* 32/32 */ @@ -1197,20 +1204,16 @@ static void test_live_modules_proc(WCHAR* exename, BOOL with_32) ok(aggregation_enum.count_32bit >= MODCOUNT, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); ok(aggregation_enum.count_64bit == 0, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); /* yes that's different from event! */ - todo_wine ok(aggregation_enum.count_systemdir >= MODCOUNT - 1, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); /* .exe */ - todo_wine ok(aggregation_enum.count_wowdir == 1, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); ok(aggregation_enum.count_ntdll == 1, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll);
ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); ok(aggregation_sym.count_32bit >= MODCOUNT, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); ok(aggregation_sym.count_64bit == 0, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); - todo_wine ok(aggregation_sym.count_systemdir >= MODCOUNT - 1, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); /* .exe */ - todo_wine ok(aggregation_sym.count_wowdir == 1, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); ok(aggregation_sym.count_ntdll == 1 + XTRANTDLL, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); } @@ -1223,40 +1226,30 @@ static void test_live_modules_proc(WCHAR* exename, BOOL with_32) ok(aggregation_event.count_wowdir >= MODCOUNT - 1, "Unexpected event.count_wowdir %u\n", aggregation_event.count_wowdir); ok(aggregation_event.count_ntdll == 1, "Unexpected event.count_ntdll %u\n", aggregation_event.count_ntdll);
- todo_wine_if(with_32) ok(aggregation_enum.count_exe == 1 + XTRAEXE, "Unexpected enum.count_exe %u\n", aggregation_enum.count_exe); if (with_32) ok(aggregation_enum.count_32bit >= MODCOUNT, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); else - todo_wine_if(!with_32) ok(aggregation_enum.count_32bit <= 1, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); ok(aggregation_enum.count_64bit == 0, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); - todo_wine ok(aggregation_enum.count_systemdir == 0, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); if (with_32) ok(aggregation_enum.count_wowdir >= MODCOUNT, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); else - todo_wine_if(!with_32) ok(aggregation_enum.count_wowdir <= 1, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); - todo_wine_if(!with_32) ok(aggregation_enum.count_ntdll == XTRANTDLL, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll);
ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); if (with_32) ok(aggregation_sym.count_32bit >= MODCOUNT, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); else - todo_wine_if(!with_32) ok(aggregation_sym.count_wowdir <= 1, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); - todo_wine ok(aggregation_sym.count_64bit == 0, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); - todo_wine ok(aggregation_sym.count_systemdir == 0, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); if (with_32) ok(aggregation_sym.count_wowdir >= MODCOUNT, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); else - todo_wine_if(!with_32) ok(aggregation_sym.count_wowdir <= 1, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); - todo_wine_if(!with_32) ok(aggregation_sym.count_ntdll == XTRANTDLL, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); } else if (!is_win64 && pcskind == PCSKIND_WINE_OLD_WOW64) /* 32/32 */ @@ -1271,20 +1264,16 @@ static void test_live_modules_proc(WCHAR* exename, BOOL with_32) ok(aggregation_enum.count_exe == 1 + XTRAEXE, "Unexpected enum.count_exe %u\n", aggregation_enum.count_exe); ok(aggregation_enum.count_32bit >= MODCOUNT, "Unexpected enum.count_32bit %u\n", aggregation_enum.count_32bit); ok(aggregation_enum.count_64bit == 0, "Unexpected enum.count_64bit %u\n", aggregation_enum.count_64bit); - todo_wine ok(aggregation_enum.count_systemdir >= MODCOUNT, "Unexpected enum.count_systemdir %u\n", aggregation_enum.count_systemdir); /* .exe */ - todo_wine ok(aggregation_enum.count_wowdir == 1, "Unexpected enum.count_wowdir %u\n", aggregation_enum.count_wowdir); ok(aggregation_enum.count_ntdll == 1 + XTRANTDLL, "Unexpected enum.count_ntdll %u\n", aggregation_enum.count_ntdll);
ok(aggregation_sym.count_exe == 1, "Unexpected sym.count_exe %u\n", aggregation_sym.count_exe); ok(aggregation_sym.count_32bit >= MODCOUNT, "Unexpected sym.count_32bit %u\n", aggregation_sym.count_32bit); ok(aggregation_sym.count_64bit == 0, "Unexpected sym.count_64bit %u\n", aggregation_sym.count_64bit); - todo_wine ok(aggregation_sym.count_systemdir >= MODCOUNT, "Unexpected sym.count_systemdir %u\n", aggregation_sym.count_systemdir); /* .exe */ - todo_wine ok(aggregation_sym.count_wowdir == 1, "Unexpected sym.count_wowdir %u\n", aggregation_sym.count_wowdir); ok(aggregation_sym.count_ntdll == 1 + XTRANTDLL, "Unexpected sym.count_ntdll %u\n", aggregation_sym.count_ntdll); } @@ -1292,18 +1281,14 @@ static void test_live_modules_proc(WCHAR* exename, BOOL with_32) ok(0, "Unexpected process kind %u\n", pcskind);
/* main module is enumerated twice in enum when including 32bit modules */ - todo_wine_if(is_win64 && (pcskind == PCSKIND_WOW64 || pcskind == PCSKIND_WINE_OLD_WOW64) && !with_32) ok(aggregation_sym.count_32bit + XTRAEXE == aggregation_enum.count_32bit, "Different sym/enum count32_bit (%u/%u)\n", aggregation_sym.count_32bit, aggregation_enum.count_32bit); - todo_wine_if(is_win64 && (pcskind == PCSKIND_WOW64 || pcskind == PCSKIND_WINE_OLD_WOW64)) ok(aggregation_sym.count_64bit == aggregation_enum.count_64bit, "Different sym/enum count64_bit (%u/%u)\n", aggregation_sym.count_64bit, aggregation_enum.count_64bit); ok(aggregation_sym.count_systemdir == aggregation_enum.count_systemdir, "Different sym/enum systemdir (%u/%u)\n", aggregation_sym.count_systemdir, aggregation_enum.count_systemdir); - todo_wine_if(with_32) ok(aggregation_sym.count_wowdir + XTRAEXE == aggregation_enum.count_wowdir, "Different sym/enum wowdir (%u/%u)\n", aggregation_sym.count_wowdir, aggregation_enum.count_wowdir); - todo_wine_if(with_32) ok(aggregation_sym.count_exe + XTRAEXE == aggregation_enum.count_exe, "Different sym/enum exe (%u/%u)\n", aggregation_sym.count_exe, aggregation_enum.count_exe); ok(aggregation_sym.count_ntdll == aggregation_enum.count_ntdll, "Different sym/enum exe (%u/%u)\n",
the pipeline failures (crypto and device) are not linked to this patch, and ntdll failure has been fixed yesterday by @julliard
@julliard: the paths for wow64 modules returned from kernel32 don't match windows behavior: wine returns 32bit system modules from c:\windows\syswow64 whereas windows returns them from c:\windows\system32 (except a couple of exceptions: 32bit ntdll and exe main module). Implementation in dbghelp includes the correction of this. (I opted not to change ntdll at once: testing on windows show that kernel32.EnumProcesModulesEx returns exactly the ldr_data list, and I didn't feel like adding this burden to this patch ;-).
It would need to be fixed, or dbghelp can return the wrong info for now. We definitely don't want to add complexity to dbghelp to work around ntdll bugs.
On Tue Mar 28 12:04:59 2023 +0000, Alexandre Julliard wrote:
@julliard: the paths for wow64 modules returned from kernel32 don't
match windows behavior: wine returns 32bit system modules from c:\windows\syswow64 whereas windows returns them from c:\windows\system32 (except a couple of exceptions: 32bit ntdll and exe main module). Implementation in dbghelp includes the correction of this. (I opted not to change ntdll at once: testing on windows show that kernel32.EnumProcesModulesEx returns exactly the ldr_data list, and I didn't feel like adding this burden to this patch ;-). It would need to be fixed, or dbghelp can return the wrong info for now. We definitely don't want to add complexity to dbghelp to work around ntdll bugs.
For clarification: do you have use cases where dbghelp could return the wrong info? the only case I can see is if a 32bit module lies in system32 dir, or a 64bit lies in syswow64 dir, but I'm not aware of any. For the record: most of the complexity in dbghelp (ie path rewrite) will be needed anyway (ie dbghelp exposes wow64 modules from syswow directory, hence requiring the rewrite machinery). fixing ntdll will just remove some rewrite cases, but not all of them
(I take your stance as "don't work around bugs, fix them!") I'll give a try to fixing ntdll then.
This merge request was closed by eric pouech.