This serie is to move Wine's implementation of EnumProcessModules closer to Windows' behavior.
Wine's current implementation is broken for a wow64 process enumerated from a 64bit bit process: - it returns the list of 32bit modules, while Windows returns the main module (32bit) and the 64bit modules (in wow64 process).
This series: - adds tests to demonstrate that discrepancy - fix all caller's to EnumProcessModules in Wine source tree which require the 32bit bit modules (thanks to EnumProcessModulesEx) - fortunately, a couple of callers only require the first (main) module and don't need to be changed. - re-implement EnumProcessModules on top of EnumProcessModulesEx (to mimic Window's results) - shows also that Wine's implementation returns loaded modules from a different directory than Windows. Also tested on Win10, the returned paths from EnumProcessModules+GetModuleFileNameEx are the same as the ones stored in LDR_DATA.
So, there's still more work to do after this serie: - with loader's path for wow64 processes - fix dbghelp to handle the conversions: information from EnumProcessModules, debug events reports from system32, while dbghelp (SymEnumerateModules, SymRefreshModules and the like) report from syswow64! (except ntdll)
From: Eric Pouech eric.pouech@gmail.com
Showing that Wine incorrectly reports, for a Wow64 process, the system DLLs from within the wow64 directory while they should be from system32.
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/psapi/tests/psapi_main.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
diff --git a/dlls/psapi/tests/psapi_main.c b/dlls/psapi/tests/psapi_main.c index a106628a9ed..d1bc0ca63bd 100644 --- a/dlls/psapi/tests/psapi_main.c +++ b/dlls/psapi/tests/psapi_main.c @@ -310,6 +310,23 @@ static void snapshot_check_first_main_module(const struct moduleex_snapshot* sna winetest_pop_context(); }
+static unsigned int snapshot_count_in_dir(const struct moduleex_snapshot* snap, HANDLE proc, const char* dirname) +{ + unsigned int count = 0; + char buffer[128]; + size_t dirname_len = strlen(dirname); + BOOL ret; + int i; + + for (i = 0; i < snap->num_modules; i++) + { + ret = GetModuleFileNameExA(proc, snap->modules[i], buffer, sizeof(buffer)); + ok(ret, "got error %lu\n", GetLastError()); + if (!strncasecmp(buffer, dirname, dirname_len)) count++; + } + return count; +} + static void test_EnumProcessModulesEx(void) { char buffer[200] = "C:\windows\system32\notepad.exe"; @@ -430,6 +447,8 @@ static void test_EnumProcessModulesEx(void)
if (sizeof(void *) == 8) { + unsigned int count; + strcpy(buffer, "C:\windows\syswow64\notepad.exe"); ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); if (ret) @@ -457,6 +476,17 @@ static void test_EnumProcessModulesEx(void) snapshot_check_first_main_module(&snap[2], pi.hProcess, buffer); snapshot_check_first_main_module(&snap[3], pi.hProcess, buffer);
+ ret = GetSystemWow64DirectoryA(buffer, sizeof(buffer)); + ok(ret, "GetSystemWow64DirectoryA failed: %lu\n", GetLastError()); + count = snapshot_count_in_dir(snap, pi.hProcess, buffer); + todo_wine + ok(count <= 1, "Wrong count %u in %s\n", count, buffer); /* notepad can be from system wow64 */ + ret = GetSystemDirectoryA(buffer, sizeof(buffer)); + ok(ret, "GetSystemDirectoryA failed: %lu\n", GetLastError()); + count = snapshot_count_in_dir(snap, pi.hProcess, buffer); + todo_wine + ok(count > 2, "Wrong count %u in %s\n", count, buffer); + /* in fact, this error is only returned when (list & 3 == 0), otherwise the corresponding * list is returned without errors. */
From: Eric Pouech eric.pouech@gmail.com
In order to get 32bit modules for a wow64 process.
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/dbgeng/dbgeng.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/dlls/dbgeng/dbgeng.c b/dlls/dbgeng/dbgeng.c index 3ec510ef296..6d4710163c9 100644 --- a/dlls/dbgeng/dbgeng.c +++ b/dlls/dbgeng/dbgeng.c @@ -134,6 +134,8 @@ static HRESULT debug_target_init_modules_info(struct target_process *target) HMODULE *modules; MODULEINFO info; DWORD needed; + BOOL wow64; + DWORD filter = LIST_MODULES_DEFAULT;
if (target->modules.initialized) return S_OK; @@ -141,8 +143,13 @@ static HRESULT debug_target_init_modules_info(struct target_process *target) if (!target->handle) return E_UNEXPECTED;
+ if (sizeof(void*) > sizeof(int) && + IsWow64Process(target->handle, &wow64) && + wow64) + filter = LIST_MODULES_32BIT; + needed = 0; - EnumProcessModules(target->handle, NULL, 0, &needed); + EnumProcessModulesEx(target->handle, NULL, 0, &needed, filter); if (!needed) return E_FAIL;
@@ -157,7 +164,7 @@ static HRESULT debug_target_init_modules_info(struct target_process *target) return E_OUTOFMEMORY; }
- if (EnumProcessModules(target->handle, modules, count * sizeof(*modules), &needed)) + if (EnumProcessModulesEx(target->handle, modules, count * sizeof(*modules), &needed, filter)) { for (i = 0; i < count; ++i) {
From: Eric Pouech eric.pouech@gmail.com
In order to get 32bit modules for a wow64 process.
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/dbghelp/module.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index c0c47b859f4..5b99b5eea58 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -1273,11 +1273,18 @@ BOOL WINAPI EnumerateLoadedModulesW64(HANDLE hProcess, WCHAR baseW[256], modW[256]; DWORD i, sz; MODULEINFO mi; + BOOL wow64; + DWORD filter = LIST_MODULES_DEFAULT;
hMods = HeapAlloc(GetProcessHeap(), 0, 256 * sizeof(hMods[0])); if (!hMods) return FALSE;
- if (!EnumProcessModules(hProcess, hMods, 256 * sizeof(hMods[0]), &sz)) + if (sizeof(void*) > sizeof(int) && + IsWow64Process(hProcess, &wow64) && + wow64) + filter = LIST_MODULES_32BIT; + + if (!EnumProcessModulesEx(hProcess, hMods, 256 * sizeof(hMods[0]), &sz, filter)) { /* hProcess should also be a valid process handle !! */ HeapFree(GetProcessHeap(), 0, hMods); @@ -1286,7 +1293,7 @@ BOOL WINAPI EnumerateLoadedModulesW64(HANDLE hProcess, if (sz > 256 * sizeof(hMods[0])) { hMods = HeapReAlloc(GetProcessHeap(), 0, hMods, sz); - if (!hMods || !EnumProcessModules(hProcess, hMods, sz, &sz)) + if (!hMods || !EnumProcessModulesEx(hProcess, hMods, sz, &sz, filter)) return FALSE; } sz /= sizeof(HMODULE);
From: Eric Pouech eric.pouech@gmail.com
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/psapi/tests/psapi_main.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-)
diff --git a/dlls/psapi/tests/psapi_main.c b/dlls/psapi/tests/psapi_main.c index d1bc0ca63bd..fc248d35e01 100644 --- a/dlls/psapi/tests/psapi_main.c +++ b/dlls/psapi/tests/psapi_main.c @@ -141,6 +141,7 @@ static void test_EnumProcessModules(void) { MODULEINFO info; char name[40]; + HMODULE hmods[3];
strcpy(buffer, "C:\windows\syswow64\notepad.exe"); ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); @@ -150,26 +151,39 @@ static void test_EnumProcessModules(void) ok(!ret, "wait timed out\n");
SetLastError(0xdeadbeef); - hMod = NULL; - ret = EnumProcessModules(pi.hProcess, &hMod, sizeof(HMODULE), &cbNeeded); + hmods[0] = NULL; + ret = EnumProcessModules(pi.hProcess, hmods, sizeof(hmods), &cbNeeded); ok(ret == 1, "got %ld, error %lu\n", ret, GetLastError()); - ok(!!hMod, "expected non-NULL module\n"); - ok(cbNeeded % sizeof(hMod) == 0, "got %lu\n", cbNeeded); + ok(cbNeeded >= sizeof(HMODULE), "expected at least one module\n"); + ok(!!hmods[0], "expected non-NULL module\n"); + ok(cbNeeded % sizeof(hmods[0]) == 0, "got %lu\n", cbNeeded);
- ret = GetModuleBaseNameA(pi.hProcess, hMod, name, sizeof(name)); + ret = GetModuleBaseNameA(pi.hProcess, hmods[0], name, sizeof(name)); ok(ret, "got error %lu\n", GetLastError()); ok(!strcmp(name, "notepad.exe"), "got %s\n", name);
- ret = GetModuleFileNameExA(pi.hProcess, hMod, name, sizeof(name)); + ret = GetModuleFileNameExA(pi.hProcess, hmods[0], name, sizeof(name)); ok(ret, "got error %lu\n", GetLastError()); ok(!strcmp(name, buffer), "got %s\n", name);
- ret = GetModuleInformation(pi.hProcess, hMod, &info, sizeof(info)); + ret = GetModuleInformation(pi.hProcess, hmods[0], &info, sizeof(info)); ok(ret, "got error %lu\n", GetLastError()); - ok(info.lpBaseOfDll == hMod, "expected %p, got %p\n", hMod, info.lpBaseOfDll); + ok(info.lpBaseOfDll == hmods[0], "expected %p, got %p\n", hmods[0], info.lpBaseOfDll); ok(info.SizeOfImage, "image size was 0\n"); ok(info.EntryPoint >= info.lpBaseOfDll, "got entry point %p\n", info.EntryPoint);
+ /* "old" Wine wow64 will only return main DLL; while windows & multi-arch Wine Wow64 setup + * will return main module, ntdll.dll and one of the wow64*.dll. + */ + todo_wine_if(cbNeeded == sizeof(HMODULE)) + ok(cbNeeded >= 3 * sizeof(HMODULE), "Wrong count of DLLs\n"); + if (cbNeeded >= 3 * sizeof(HMODULE)) + { + ret = GetModuleBaseNameA(pi.hProcess, hmods[2], name, sizeof(name)); + ok(ret, "got error %lu\n", GetLastError()); + todo_wine + ok(strstr(CharLowerA(name), "wow64") != NULL, "third DLL in wow64 should be one of wow*.dll (%s)\n", name); + } TerminateProcess(pi.hProcess, 0); } else
From: Eric Pouech eric.pouech@gmail.com
Note: this patch changes the results of EnumProcessModules for a wow64 target process called from a 64bit process.
It now returns: - main module and all loaded 64bit modules (Wine multi-arch wow64 and Windows) - main module only (Wine "old" wow64). It used to return all the 32bit modules. You now must use EnumProcessModulesEx(..., LIST_MODULES_32BIT) to get that result.
Signed-off-by: Eric Pouech eric.pouech@gmail.com --- dlls/kernelbase/debug.c | 66 +---------------------------------- dlls/psapi/tests/psapi_main.c | 2 -- 2 files changed, 1 insertion(+), 67 deletions(-)
diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index 6d8b9ad4b2c..363734aef0c 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -950,71 +950,7 @@ BOOL WINAPI /* DECLSPEC_HOTPATCH */ EnumPageFilesW( PENUM_PAGE_FILE_CALLBACKW ca BOOL WINAPI DECLSPEC_HOTPATCH EnumProcessModules( HANDLE process, HMODULE *module, DWORD count, DWORD *needed ) { - struct module_iterator iter; - DWORD size = 0; - BOOL target_wow64; - INT ret; - - if (process == GetCurrentProcess()) - { - PPEB_LDR_DATA ldr_data = NtCurrentTeb()->Peb->LdrData; - PLIST_ENTRY head = &ldr_data->InLoadOrderModuleList; - PLIST_ENTRY entry = head->Flink; - - if (count && !module) - { - SetLastError( ERROR_NOACCESS ); - return FALSE; - } - while (entry != head) - { - LDR_DATA_TABLE_ENTRY *ldr = CONTAINING_RECORD( entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks ); - if (count >= sizeof(HMODULE)) - { - *module++ = ldr->DllBase; - count -= sizeof(HMODULE); - } - size += sizeof(HMODULE); - entry = entry->Flink; - } - if (!needed) - { - SetLastError( ERROR_NOACCESS ); - return FALSE; - } - *needed = size; - return TRUE; - } - - if (!IsWow64Process( process, &target_wow64 )) return FALSE; - if (!init_module_iterator( &iter, process, is_win64 && target_wow64 )) return FALSE; - - if (count && !module) - { - SetLastError( ERROR_NOACCESS ); - return FALSE; - } - - while ((ret = module_iterator_next( &iter )) > 0) - { - if (count >= sizeof(HMODULE)) - { - if (sizeof(void *) == 8 && iter.wow64) - *module++ = (HMODULE) (DWORD_PTR)iter.ldr_module32.BaseAddress; - else - *module++ = iter.ldr_module.DllBase; - count -= sizeof(HMODULE); - } - size += sizeof(HMODULE); - } - - if (!needed) - { - SetLastError( ERROR_NOACCESS ); - return FALSE; - } - *needed = size; - return ret == 0; + return EnumProcessModulesEx( process, module, count, needed, LIST_MODULES_DEFAULT ); }
diff --git a/dlls/psapi/tests/psapi_main.c b/dlls/psapi/tests/psapi_main.c index fc248d35e01..227fc52c2ad 100644 --- a/dlls/psapi/tests/psapi_main.c +++ b/dlls/psapi/tests/psapi_main.c @@ -181,7 +181,6 @@ static void test_EnumProcessModules(void) { ret = GetModuleBaseNameA(pi.hProcess, hmods[2], name, sizeof(name)); ok(ret, "got error %lu\n", GetLastError()); - todo_wine ok(strstr(CharLowerA(name), "wow64") != NULL, "third DLL in wow64 should be one of wow*.dll (%s)\n", name); } TerminateProcess(pi.hProcess, 0); @@ -207,7 +206,6 @@ static void test_EnumProcessModules(void) SetLastError(0xdeadbeef); ret = EnumProcessModules(pi.hProcess, &hMod, sizeof(HMODULE), &cbNeeded); ok(!ret, "got %ld\n", ret); - todo_wine ok(GetLastError() == ERROR_PARTIAL_COPY, "got error %lu\n", GetLastError());
TerminateProcess(pi.hProcess, 0);