dbghelp/tests: Fix process kind detection on old Windows machines.
[wine.git] / dlls / dbghelp / tests / dbghelp.c
blobf45516c288261d5391e5f391bb86f05b4812843d
1 /*
2 * Copyright 2018 Zebediah Figura
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "ntstatus.h"
20 #define WIN32_NO_STATUS
21 #include "windows.h"
22 #include "psapi.h"
23 #include "verrsrc.h"
24 #include "dbghelp.h"
25 #include "wine/test.h"
26 #include "winternl.h"
28 static const BOOL is_win64 = sizeof(void*) > sizeof(int);
29 static WCHAR system_directory[MAX_PATH];
30 static WCHAR wow64_directory[MAX_PATH];
32 static BOOL (*WINAPI pIsWow64Process2)(HANDLE, USHORT*, USHORT*);
34 #if defined(__i386__) || defined(__x86_64__)
36 static DWORD CALLBACK stack_walk_thread(void *arg)
38 DWORD count = SuspendThread(GetCurrentThread());
39 ok(!count, "got %ld\n", count);
40 return 0;
43 static void test_stack_walk(void)
45 char si_buf[sizeof(SYMBOL_INFO) + 200];
46 SYMBOL_INFO *si = (SYMBOL_INFO *)si_buf;
47 STACKFRAME64 frame = {{0}}, frame0;
48 BOOL found_our_frame = FALSE;
49 DWORD machine;
50 HANDLE thread;
51 DWORD64 disp;
52 CONTEXT ctx;
53 DWORD count;
54 BOOL ret;
56 thread = CreateThread(NULL, 0, stack_walk_thread, NULL, 0, NULL);
58 /* wait for the thread to suspend itself */
61 Sleep(50);
62 count = SuspendThread(thread);
63 ResumeThread(thread);
65 while (!count);
67 ctx.ContextFlags = CONTEXT_CONTROL;
68 ret = GetThreadContext(thread, &ctx);
69 ok(ret, "got error %u\n", ret);
71 frame.AddrPC.Mode = AddrModeFlat;
72 frame.AddrFrame.Mode = AddrModeFlat;
73 frame.AddrStack.Mode = AddrModeFlat;
75 #ifdef __i386__
76 machine = IMAGE_FILE_MACHINE_I386;
78 frame.AddrPC.Segment = ctx.SegCs;
79 frame.AddrPC.Offset = ctx.Eip;
80 frame.AddrFrame.Segment = ctx.SegSs;
81 frame.AddrFrame.Offset = ctx.Ebp;
82 frame.AddrStack.Segment = ctx.SegSs;
83 frame.AddrStack.Offset = ctx.Esp;
84 #elif defined(__x86_64__)
85 machine = IMAGE_FILE_MACHINE_AMD64;
87 frame.AddrPC.Segment = ctx.SegCs;
88 frame.AddrPC.Offset = ctx.Rip;
89 frame.AddrFrame.Segment = ctx.SegSs;
90 frame.AddrFrame.Offset = ctx.Rbp;
91 frame.AddrStack.Segment = ctx.SegSs;
92 frame.AddrStack.Offset = ctx.Rsp;
93 #endif
94 frame0 = frame;
96 /* first invocation just calculates the return address */
97 ret = StackWalk64(machine, GetCurrentProcess(), thread, &frame, &ctx, NULL,
98 SymFunctionTableAccess64, SymGetModuleBase64, NULL);
99 ok(ret, "StackWalk64() failed: %lu\n", GetLastError());
100 ok(frame.AddrPC.Offset == frame0.AddrPC.Offset, "expected %s, got %s\n",
101 wine_dbgstr_longlong(frame0.AddrPC.Offset),
102 wine_dbgstr_longlong(frame.AddrPC.Offset));
103 ok(frame.AddrStack.Offset == frame0.AddrStack.Offset, "expected %s, got %s\n",
104 wine_dbgstr_longlong(frame0.AddrStack.Offset),
105 wine_dbgstr_longlong(frame.AddrStack.Offset));
106 ok(frame.AddrReturn.Offset && frame.AddrReturn.Offset != frame.AddrPC.Offset,
107 "got bad return address %s\n", wine_dbgstr_longlong(frame.AddrReturn.Offset));
109 while (frame.AddrReturn.Offset)
111 char *addr;
113 ret = StackWalk64(machine, GetCurrentProcess(), thread, &frame, &ctx, NULL,
114 SymFunctionTableAccess64, SymGetModuleBase64, NULL);
115 ok(ret, "StackWalk64() failed: %lu\n", GetLastError());
117 addr = (void *)(DWORD_PTR)frame.AddrPC.Offset;
119 if (!found_our_frame && addr > (char *)stack_walk_thread && addr < (char *)stack_walk_thread + 0x100)
121 found_our_frame = TRUE;
123 si->SizeOfStruct = sizeof(SYMBOL_INFO);
124 si->MaxNameLen = 200;
125 if (SymFromAddr(GetCurrentProcess(), frame.AddrPC.Offset, &disp, si))
126 ok(!strcmp(si->Name, "stack_walk_thread"), "got wrong name %s\n", si->Name);
130 ret = StackWalk64(machine, GetCurrentProcess(), thread, &frame, &ctx, NULL,
131 SymFunctionTableAccess64, SymGetModuleBase64, NULL);
132 ok(!ret, "StackWalk64() should have failed\n");
134 ok(found_our_frame, "didn't find stack_walk_thread frame\n");
137 #else /* __i386__ || __x86_64__ */
139 static void test_stack_walk(void)
143 #endif /* __i386__ || __x86_64__ */
145 static void test_search_path(void)
147 char search_path[128];
148 BOOL ret;
150 /* The default symbol path is ".[;%_NT_SYMBOL_PATH%][;%_NT_ALT_SYMBOL_PATH%]".
151 * We unset both variables earlier so should simply get "." */
152 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
153 ok(ret == TRUE, "ret = %d\n", ret);
154 ok(!strcmp(search_path, "."), "Got search path '%s', expected '.'\n", search_path);
156 /* Set an arbitrary search path */
157 ret = SymSetSearchPath(GetCurrentProcess(), "W:\\");
158 ok(ret == TRUE, "ret = %d\n", ret);
159 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
160 ok(ret == TRUE, "ret = %d\n", ret);
161 ok(!strcmp(search_path, "W:\\"), "Got search path '%s', expected 'W:\\'\n", search_path);
163 /* Setting to NULL resets to the default */
164 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
165 ok(ret == TRUE, "ret = %d\n", ret);
166 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
167 ok(ret == TRUE, "ret = %d\n", ret);
168 ok(!strcmp(search_path, "."), "Got search path '%s', expected '.'\n", search_path);
170 /* With _NT_SYMBOL_PATH */
171 SetEnvironmentVariableA("_NT_SYMBOL_PATH", "X:\\");
172 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
173 ok(ret == TRUE, "ret = %d\n", ret);
174 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
175 ok(ret == TRUE, "ret = %d\n", ret);
176 ok(!strcmp(search_path, ".;X:\\"), "Got search path '%s', expected '.;X:\\'\n", search_path);
178 /* With both _NT_SYMBOL_PATH and _NT_ALT_SYMBOL_PATH */
179 SetEnvironmentVariableA("_NT_ALT_SYMBOL_PATH", "Y:\\");
180 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
181 ok(ret == TRUE, "ret = %d\n", ret);
182 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
183 ok(ret == TRUE, "ret = %d\n", ret);
184 ok(!strcmp(search_path, ".;X:\\;Y:\\"), "Got search path '%s', expected '.;X:\\;Y:\\'\n", search_path);
186 /* With just _NT_ALT_SYMBOL_PATH */
187 SetEnvironmentVariableA("_NT_SYMBOL_PATH", NULL);
188 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
189 ok(ret == TRUE, "ret = %d\n", ret);
190 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
191 ok(ret == TRUE, "ret = %d\n", ret);
192 ok(!strcmp(search_path, ".;Y:\\"), "Got search path '%s', expected '.;Y:\\'\n", search_path);
194 /* Restore original search path */
195 SetEnvironmentVariableA("_NT_ALT_SYMBOL_PATH", NULL);
196 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
197 ok(ret == TRUE, "ret = %d\n", ret);
198 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
199 ok(ret == TRUE, "ret = %d\n", ret);
200 ok(!strcmp(search_path, "."), "Got search path '%s', expected '.'\n", search_path);
203 static BOOL ends_withW(const WCHAR* str, const WCHAR* suffix)
205 size_t strlen = wcslen(str);
206 size_t sfxlen = wcslen(suffix);
208 return strlen >= sfxlen && !wcsicmp(str + strlen - sfxlen, suffix);
211 static unsigned get_machine_bitness(USHORT machine)
213 switch (machine)
215 case IMAGE_FILE_MACHINE_I386:
216 case IMAGE_FILE_MACHINE_ARM:
217 case IMAGE_FILE_MACHINE_ARMNT:
218 return 32;
219 case IMAGE_FILE_MACHINE_AMD64:
220 case IMAGE_FILE_MACHINE_ARM64:
221 return 64;
222 default:
223 ok(0, "Unsupported machine %x\n", machine);
224 return 0;
228 static USHORT get_module_machine(const char* path)
230 HANDLE hFile, hMap;
231 void* mapping;
232 IMAGE_NT_HEADERS *nthdr;
233 USHORT machine = IMAGE_FILE_MACHINE_UNKNOWN;
235 hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
236 if (hFile != INVALID_HANDLE_VALUE)
238 hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
239 if (hMap != NULL)
241 mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
242 if (mapping != NULL)
244 nthdr = RtlImageNtHeader(mapping);
245 if (nthdr != NULL) machine = nthdr->FileHeader.Machine;
246 UnmapViewOfFile(mapping);
248 CloseHandle(hMap);
250 CloseHandle(hFile);
252 return machine;
255 static BOOL skip_too_old_dbghelp(HANDLE proc, DWORD64 base)
257 IMAGEHLP_MODULE im0 = {sizeof(im0)};
258 BOOL ret;
260 /* test if get module info succeeds with oldest structure format */
261 ret = SymGetModuleInfo(proc, base, &im0);
262 if (ret)
264 skip("Too old dbghelp. Skipping module tests.\n");
265 ret = SymCleanup(proc);
266 ok(ret, "SymCleanup failed: %lu\n", GetLastError());
267 return TRUE;
269 ok(ret, "SymGetModuleInfo failed: %lu\n", GetLastError());
270 return FALSE;
273 static DWORD get_module_size(const char* path)
275 BOOL ret;
276 HANDLE hFile, hMap;
277 void* mapping;
278 IMAGE_NT_HEADERS *nthdr;
279 DWORD size;
281 hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
282 ok(hFile != INVALID_HANDLE_VALUE, "Couldn't open file %s (%lu)\n", path, GetLastError());
283 hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
284 ok(hMap != NULL, "Couldn't create map (%lu)\n", GetLastError());
285 mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
286 ok(mapping != NULL, "Couldn't map (%lu)\n", GetLastError());
287 nthdr = RtlImageNtHeader(mapping);
288 ok(nthdr != NULL, "Cannot get NT headers out of %s\n", path);
289 size = nthdr ? nthdr->OptionalHeader.SizeOfImage : 0;
290 ret = UnmapViewOfFile(mapping);
291 ok(ret, "Couldn't unmap (%lu)\n", GetLastError());
292 CloseHandle(hMap);
293 CloseHandle(hFile);
294 return size;
297 static BOOL CALLBACK count_module_cb(const char* name, DWORD64 base, void* usr)
299 (*(unsigned*)usr)++;
300 return TRUE;
303 static unsigned get_module_count(HANDLE proc)
305 unsigned count = 0;
306 BOOL ret;
308 ret = SymEnumerateModules64(proc, count_module_cb, &count);
309 ok(ret, "SymEnumerateModules64 failed: %lu\n", GetLastError());
310 return count;
313 struct nth_module
315 HANDLE proc;
316 unsigned int index;
317 BOOL could_fail;
318 IMAGEHLP_MODULE64 module;
321 static BOOL CALLBACK nth_module_cb(const char* name, DWORD64 base, void* usr)
323 struct nth_module* nth = usr;
324 BOOL ret;
326 if (nth->index--) return TRUE;
327 nth->module.SizeOfStruct = sizeof(nth->module);
328 ret = SymGetModuleInfo64(nth->proc, base, &nth->module);
329 if (nth->could_fail)
331 /* Windows11 succeeds into loading the overlapped module */
332 ok(!ret || broken(base == nth->module.BaseOfImage), "SymGetModuleInfo64 should have failed\n");
333 nth->module.BaseOfImage = base;
335 else
337 ok(ret, "SymGetModuleInfo64 failed: %lu\n", GetLastError());
338 ok(nth->module.BaseOfImage == base, "Wrong base\n");
340 return FALSE;
343 /* wrapper around EnumerateLoadedModuleW64 which sometimes fails for unknown reasons on Win11,
344 * with STATUS_INFO_LENGTH_MISMATCH as GetLastError()!
346 static BOOL wrapper_EnumerateLoadedModulesW64(HANDLE proc, PENUMLOADED_MODULES_CALLBACKW64 cb, void* usr)
348 BOOL ret;
349 int retry;
351 for (retry = !strcmp(winetest_platform, "wine") ? 1 : 5; retry >= 0; retry--)
353 ret = EnumerateLoadedModulesW64(proc, cb, usr);
354 if (ret || GetLastError() != STATUS_INFO_LENGTH_MISMATCH)
355 break;
356 Sleep(10);
358 return ret;
361 static BOOL test_modules(void)
363 BOOL ret;
364 char file_system[MAX_PATH];
365 char file_wow64[MAX_PATH];
366 DWORD64 base;
367 const DWORD64 base1 = 0x00010000;
368 const DWORD64 base2 = 0x08010000;
369 IMAGEHLP_MODULEW64 im;
370 USHORT machine_wow, machine2;
371 HANDLE dummy = (HANDLE)(ULONG_PTR)0xcafef00d;
372 const char* target_dll = "c:\\windows\\system32\\kernel32.dll";
373 unsigned count;
375 im.SizeOfStruct = sizeof(im);
377 /* can sym load an exec of different bitness even if 32Bit flag not set */
379 SymSetOptions(SymGetOptions() & ~SYMOPT_INCLUDE_32BIT_MODULES);
380 ret = SymInitialize(GetCurrentProcess(), 0, FALSE);
381 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
383 GetSystemWow64DirectoryA(file_wow64, MAX_PATH);
384 strcat(file_wow64, "\\msinfo32.exe");
386 /* not always present */
387 machine_wow = get_module_machine(file_wow64);
388 if (machine_wow != IMAGE_FILE_MACHINE_UNKNOWN)
390 base = SymLoadModule(GetCurrentProcess(), NULL, file_wow64, NULL, base2, 0);
391 ok(base == base2, "SymLoadModule failed: %lu\n", GetLastError());
392 ret = SymGetModuleInfoW64(GetCurrentProcess(), base2, &im);
393 if (!ret && skip_too_old_dbghelp(GetCurrentProcess(), base2)) return FALSE;
394 ok(ret, "SymGetModuleInfoW64 failed: %lu\n", GetLastError());
395 ok(im.BaseOfImage == base2, "Wrong base address\n");
396 ok(im.MachineType == machine_wow, "Wrong machine %lx (expecting %u)\n", im.MachineType, machine_wow);
399 GetSystemDirectoryA(file_system, MAX_PATH);
400 strcat(file_system, "\\msinfo32.exe");
402 base = SymLoadModule(GetCurrentProcess(), NULL, file_system, NULL, base1, 0);
403 ok(base == base1, "SymLoadModule failed: %lu\n", GetLastError());
404 ret = SymGetModuleInfoW64(GetCurrentProcess(), base1, &im);
405 if (!ret && skip_too_old_dbghelp(GetCurrentProcess(), base1)) return FALSE;
406 ok(ret, "SymGetModuleInfoW64 failed: %lu\n", GetLastError());
407 ok(im.BaseOfImage == base1, "Wrong base address\n");
408 machine2 = get_module_machine(file_system);
409 ok(machine2 != IMAGE_FILE_MACHINE_UNKNOWN, "Unexpected machine %u\n", machine2);
410 ok(im.MachineType == machine2, "Wrong machine %lx (expecting %u)\n", im.MachineType, machine2);
412 /* still can access first module after loading second */
413 if (machine_wow != IMAGE_FILE_MACHINE_UNKNOWN)
415 ret = SymGetModuleInfoW64(GetCurrentProcess(), base2, &im);
416 ok(ret, "SymGetModuleInfoW64 failed: %lu\n", GetLastError());
417 ok(im.BaseOfImage == base2, "Wrong base address\n");
418 ok(im.MachineType == machine_wow, "Wrong machine %lx (expecting %u)\n", im.MachineType, machine_wow);
421 ret = SymCleanup(GetCurrentProcess());
422 ok(ret, "SymCleanup failed: %lu\n", GetLastError());
424 ret = SymInitialize(dummy, NULL, FALSE);
425 ok(ret, "got error %lu\n", GetLastError());
427 ret = SymRefreshModuleList(dummy);
428 ok(!ret, "SymRefreshModuleList should have failed\n");
430 count = get_module_count(dummy);
431 ok(count == 0, "Unexpected count (%u instead of 0)\n", count);
433 /* loading with 0 size succeeds */
434 base = SymLoadModuleEx(dummy, NULL, target_dll, NULL, base1, 0, NULL, 0);
435 ok(base == base1, "SymLoadModuleEx failed: %lu\n", GetLastError());
437 ret = SymUnloadModule64(dummy, base1);
438 ok(ret, "SymUnloadModule64 failed: %lu\n", GetLastError());
440 count = get_module_count(dummy);
441 ok(count == 0, "Unexpected count (%u instead of 0)\n", count);
443 ret = SymCleanup(dummy);
444 ok(ret, "SymCleanup failed: %lu\n", GetLastError());
446 return TRUE;
449 static void test_modules_overlap(void)
451 BOOL ret;
452 DWORD64 base;
453 const DWORD64 base1 = 0x00010000;
454 HANDLE dummy = (HANDLE)(ULONG_PTR)0xcafef00d;
455 const char* target1_dll = "c:\\windows\\system32\\kernel32.dll";
456 const char* target2_dll = "c:\\windows\\system32\\winmm.dll";
457 DWORD imsize = get_module_size(target1_dll);
458 char buffer[512];
459 IMAGEHLP_SYMBOL64* sym = (void*)buffer;
461 int i, j;
462 struct test_module
464 DWORD64 base;
465 DWORD size;
466 const char* name;
468 const struct test
470 struct test_module input;
471 DWORD error_code;
472 struct test_module outputs[2];
474 tests[] =
476 /* cases where first module is left "untouched" and second not loaded */
477 {{base1, 0, target1_dll}, ERROR_SUCCESS, {{base1, imsize, "kernel32"},{0}}},
478 {{base1, imsize, target1_dll}, ERROR_SUCCESS, {{base1, imsize, "kernel32"},{0}}},
479 {{base1, imsize / 2, target1_dll}, ERROR_SUCCESS, {{base1, imsize, "kernel32"},{0}}},
480 {{base1, imsize * 2, target1_dll}, ERROR_SUCCESS, {{base1, imsize, "kernel32"},{0}}},
482 /* cases where first module is unloaded and replaced by second module */
483 {{base1 + imsize / 2, imsize, target1_dll}, ~0, {{base1 + imsize / 2, imsize, "kernel32"},{0}}},
484 {{base1 + imsize / 3, imsize / 3, target1_dll}, ~0, {{base1 + imsize / 3, imsize / 3, "kernel32"},{0}}},
485 {{base1 + imsize / 2, imsize, target2_dll}, ~0, {{base1 + imsize / 2, imsize, "winmm"},{0}}},
486 {{base1 + imsize / 3, imsize / 3, target2_dll}, ~0, {{base1 + imsize / 3, imsize / 3, "winmm"},{0}}},
488 /* cases where second module is actually loaded */
489 {{base1 + imsize, imsize, target1_dll}, ~0, {{base1, imsize, "kernel32"}, {base1 + imsize, imsize, "kernel32"}}},
490 {{base1 - imsize / 2, imsize, target1_dll}, ~0, {{base1, imsize, "kernel32"}, {base1 - imsize / 2, imsize, NULL}}},
491 /* we mark with a NULL modulename the cases where the module is loaded, but isn't visible
492 * (SymGetModuleInfo fails in callback) as it's base address is inside the first loaded module.
494 {{base1 + imsize, imsize, target2_dll}, ~0, {{base1, imsize, "kernel32"}, {base1 + imsize, imsize, "winmm"}}},
495 {{base1 - imsize / 2, imsize, target2_dll}, ~0, {{base1, imsize, "kernel32"}, {base1 - imsize / 2, imsize, NULL}}},
498 ok(imsize, "Cannot get image size\n");
500 for (i = 0; i < ARRAY_SIZE(tests); i++)
502 ret = SymInitialize(dummy, NULL, FALSE);
503 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
505 base = SymLoadModuleEx(dummy, NULL, target1_dll, NULL, base1, 0, NULL, 0);
506 ok(base == base1, "SymLoadModuleEx failed: %lu\n", GetLastError());
507 ret = SymAddSymbol(dummy, base1, "winetest_symbol_virtual", base1 + (3 * imsize) / 4, 13, 0);
508 ok(ret, "SymAddSymbol failed: %lu\n", GetLastError());
510 base = SymLoadModuleEx(dummy, NULL, tests[i].input.name, NULL, tests[i].input.base, tests[i].input.size, NULL, 0);
511 if (tests[i].error_code != ~0)
513 ok(base == 0, "SymLoadModuleEx should have failed\n");
514 ok(GetLastError() == tests[i].error_code, "Wrong error %lu\n", GetLastError());
516 else
518 ok(base == tests[i].input.base, "SymLoadModuleEx failed: %lu\n", GetLastError());
520 for (j = 0; j < ARRAY_SIZE(tests[i].outputs); j++)
522 struct nth_module nth = {dummy, j, !tests[i].outputs[j].name, {0}};
524 ret = SymEnumerateModules64(dummy, nth_module_cb, &nth);
525 ok(ret, "SymEnumerateModules64 failed: %lu\n", GetLastError());
526 if (!tests[i].outputs[j].base)
528 ok(nth.index != -1, "Got more modules than expected %d, %d\n", nth.index, j);
529 break;
531 ok(nth.index == -1, "Expecting more modules\n");
532 ok(nth.module.BaseOfImage == tests[i].outputs[j].base, "Wrong base\n");
533 if (!nth.could_fail)
535 ok(nth.module.ImageSize == tests[i].outputs[j].size, "Wrong size\n");
536 ok(!strcasecmp(nth.module.ModuleName, tests[i].outputs[j].name), "Wrong name\n");
539 memset(sym, 0, sizeof(*sym));
540 sym->SizeOfStruct = sizeof(*sym);
541 sym->MaxNameLength = sizeof(buffer) - sizeof(*sym);
542 ret = SymGetSymFromName64(dummy, "winetest_symbol_virtual", sym);
543 if (tests[i].error_code == ERROR_SUCCESS || tests[i].outputs[1].base)
545 /* first module is not unloaded, so our added symbol should be present, with right attributes */
546 ok(ret, "SymGetSymFromName64 has failed: %lu (%d, %d)\n", GetLastError(), i, j);
547 ok(sym->Address == base1 + (3 * imsize) / 4, "Unexpected size %lu\n", sym->Size);
548 ok(sym->Size == 13, "Unexpected size %lu\n", sym->Size);
549 ok(sym->Flags & SYMFLAG_VIRTUAL, "Unexpected flag %lx\n", sym->Flags);
551 else
553 /* the first module is really unloaded, the our added symbol has disappead */
554 ok(!ret, "SymGetSymFromName64 should have failed %lu\n", GetLastError());
556 ret = SymCleanup(dummy);
557 ok(ret, "SymCleanup failed: %lu\n", GetLastError());
561 enum process_kind
563 PCSKIND_ERROR,
564 PCSKIND_64BIT, /* 64 bit process */
565 PCSKIND_32BIT, /* 32 bit only configuration (Wine, some Win 7...) */
566 PCSKIND_WINE_OLD_WOW64, /* Wine "old" wow64 configuration */
567 PCSKIND_WOW64, /* Wine "new" wow64 configuration, and Windows with wow64 support */
570 static enum process_kind get_process_kind_internal(HANDLE process)
572 USHORT m1, m2;
574 if (!pIsWow64Process2) /* only happens on old Win 8 and early win 1064v1507 */
576 BOOL is_wow64;
578 if (!strcmp(winetest_platform, "wine") || !IsWow64Process(process, &is_wow64))
579 return PCSKIND_ERROR;
580 if (is_wow64) return PCSKIND_WOW64;
581 return is_win64 ? PCSKIND_64BIT : PCSKIND_32BIT;
583 if (!pIsWow64Process2(process, &m1, &m2)) return PCSKIND_ERROR;
584 if (m1 == IMAGE_FILE_MACHINE_UNKNOWN && get_machine_bitness(m2) == 32) return PCSKIND_32BIT;
585 if (m1 == IMAGE_FILE_MACHINE_UNKNOWN && get_machine_bitness(m2) == 64) return PCSKIND_64BIT;
586 if (get_machine_bitness(m1) == 32 && get_machine_bitness(m2) == 64)
588 enum process_kind pcskind = PCSKIND_WOW64;
589 if (!strcmp(winetest_platform, "wine"))
591 PROCESS_BASIC_INFORMATION pbi;
592 PEB32 peb32;
593 const char* peb_addr;
595 if (NtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL))
596 return PCSKIND_ERROR;
598 peb_addr = (const char*)pbi.PebBaseAddress;
599 if (is_win64) peb_addr += 0x1000;
600 if (!ReadProcessMemory(process, peb_addr, &peb32, sizeof(peb32), NULL)) return PCSKIND_ERROR;
601 if (*(const DWORD*)((const char*)&peb32 + 0x460 /* CloudFileFlags */))
602 pcskind = PCSKIND_WINE_OLD_WOW64;
604 return pcskind;
606 return PCSKIND_ERROR;
609 static enum process_kind get_process_kind(HANDLE process)
611 DWORD gle = GetLastError();
612 enum process_kind pcskind = get_process_kind_internal(process);
613 SetLastError(gle);
614 return pcskind;
617 struct loaded_module_aggregation
619 HANDLE proc;
620 unsigned int count_32bit;
621 unsigned int count_64bit;
622 unsigned int count_exe;
623 unsigned int count_ntdll;
624 unsigned int count_systemdir;
625 unsigned int count_wowdir;
628 static BOOL CALLBACK aggregate_cb(PCWSTR imagename, DWORD64 base, ULONG sz, PVOID usr)
630 struct loaded_module_aggregation* aggregation = usr;
631 IMAGEHLP_MODULEW64 im;
632 BOOL ret, wow64;
634 memset(&im, 0, sizeof(im));
635 im.SizeOfStruct = sizeof(im);
637 ret = SymGetModuleInfoW64(aggregation->proc, base, &im);
638 if (ret)
639 ok(aggregation->count_exe && ends_withW(imagename, L".exe"),
640 "%ls shouldn't already be loaded\n", imagename);
641 else
643 ok(!ret, "Module %ls shouldn't be loaded\n", imagename);
644 ret = SymLoadModuleExW(aggregation->proc, NULL, imagename, NULL, base, sz, NULL, 0);
645 ok(ret || broken(GetLastError() == ERROR_SUCCESS) /* Win10/64 v1607 return this on bcryptPrimitives.DLL */,
646 "SymLoadModuleExW failed on %ls: %lu\n", imagename, GetLastError());
647 ret = SymGetModuleInfoW64(aggregation->proc, base, &im);
648 ok(ret, "SymGetModuleInfoW64 failed: %lu\n", GetLastError());
651 switch (get_machine_bitness(im.MachineType))
653 case 32: aggregation->count_32bit++; break;
654 case 64: aggregation->count_64bit++; break;
655 default: break;
657 if (ends_withW(imagename, L".exe"))
658 aggregation->count_exe++;
659 if (!wcsicmp(im.ModuleName, L"ntdll"))
660 aggregation->count_ntdll++;
661 if (!wcsnicmp(imagename, system_directory, wcslen(system_directory)))
662 aggregation->count_systemdir++;
663 if (IsWow64Process(aggregation->proc, &wow64) && wow64 &&
664 !wcsnicmp(imagename, wow64_directory, wcslen(wow64_directory)))
665 aggregation->count_wowdir++;
667 return TRUE;
671 static void test_loaded_modules(void)
673 BOOL ret;
674 char buffer[200];
675 PROCESS_INFORMATION pi = {0};
676 STARTUPINFOA si = {0};
677 struct loaded_module_aggregation aggregation = {0};
679 ret = GetSystemDirectoryA(buffer, sizeof(buffer));
680 ok(ret, "got error %lu\n", GetLastError());
681 strcat(buffer, "\\msinfo32.exe");
683 /* testing with child process of different machines */
684 ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
685 ok(ret, "CreateProcess failed: %lu\n", GetLastError());
687 ret = WaitForInputIdle(pi.hProcess, 5000);
688 ok(!ret, "wait timed out\n");
690 ret = SymInitialize(pi.hProcess, NULL, FALSE);
691 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
692 memset(&aggregation, 0, sizeof(aggregation));
693 aggregation.proc = pi.hProcess;
695 ret = wrapper_EnumerateLoadedModulesW64(pi.hProcess, aggregate_cb, &aggregation);
696 ok(ret, "EnumerateLoadedModulesW64 failed: %lu\n", GetLastError());
698 if (is_win64)
700 ok(!aggregation.count_32bit && aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n",
701 aggregation.count_32bit, aggregation.count_64bit);
702 ok(aggregation.count_exe == 1 && aggregation.count_ntdll == 1, "Wrong kind aggregation count %u %u\n",
703 aggregation.count_exe, aggregation.count_ntdll);
704 ok(aggregation.count_systemdir > 2 && !aggregation.count_wowdir, "Wrong directory aggregation count %u %u\n",
705 aggregation.count_systemdir, aggregation.count_wowdir);
707 else
709 BOOL is_wow64;
710 ret = IsWow64Process(pi.hProcess, &is_wow64);
711 ok(ret, "IsWow64Process failed: %lu\n", GetLastError());
713 ok(aggregation.count_32bit && !aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n",
714 aggregation.count_32bit, aggregation.count_64bit);
715 ok(aggregation.count_exe == 1 && aggregation.count_ntdll == 1, "Wrong kind aggregation count %u %u\n",
716 aggregation.count_exe, aggregation.count_ntdll);
717 switch (get_process_kind(pi.hProcess))
719 case PCSKIND_ERROR:
720 ok(0, "Unknown process kind\n");
721 break;
722 case PCSKIND_64BIT:
723 case PCSKIND_WOW64:
724 todo_wine
725 ok(aggregation.count_systemdir > 2 && aggregation.count_wowdir == 1, "Wrong directory aggregation count %u %u\n",
726 aggregation.count_systemdir, aggregation.count_wowdir);
727 break;
728 case PCSKIND_32BIT:
729 ok(aggregation.count_systemdir > 2 && aggregation.count_wowdir == 0, "Wrong directory aggregation count %u %u\n",
730 aggregation.count_systemdir, aggregation.count_wowdir);
731 break;
732 case PCSKIND_WINE_OLD_WOW64:
733 ok(aggregation.count_systemdir == 1 && aggregation.count_wowdir > 2, "Wrong directory aggregation count %u %u\n",
734 aggregation.count_systemdir, aggregation.count_wowdir);
735 break;
739 ret = SymRefreshModuleList(pi.hProcess);
740 ok(ret || broken(GetLastError() == STATUS_PARTIAL_COPY /* Win11 in some cases */), "SymRefreshModuleList failed: %lu\n", GetLastError());
742 SymCleanup(pi.hProcess);
743 TerminateProcess(pi.hProcess, 0);
745 if (is_win64)
747 ret = GetSystemWow64DirectoryA(buffer, sizeof(buffer));
748 ok(ret, "got error %lu\n", GetLastError());
749 strcat(buffer, "\\msinfo32.exe");
751 SymSetOptions(SymGetOptions() & ~SYMOPT_INCLUDE_32BIT_MODULES);
753 ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
754 if (ret)
756 ret = WaitForInputIdle(pi.hProcess, 5000);
757 ok(!ret, "wait timed out\n");
759 ret = SymInitialize(pi.hProcess, NULL, FALSE);
760 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
761 memset(&aggregation, 0, sizeof(aggregation));
762 aggregation.proc = pi.hProcess;
764 ret = wrapper_EnumerateLoadedModulesW64(pi.hProcess, aggregate_cb, &aggregation);
765 ok(ret, "EnumerateLoadedModulesW64 failed: %lu\n", GetLastError());
767 todo_wine
768 ok(aggregation.count_32bit == 1 && aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n",
769 aggregation.count_32bit, aggregation.count_64bit);
770 ok(aggregation.count_exe == 1 && aggregation.count_ntdll == 1, "Wrong kind aggregation count %u %u\n",
771 aggregation.count_exe, aggregation.count_ntdll);
772 todo_wine
773 ok(aggregation.count_systemdir > 2 && aggregation.count_64bit == aggregation.count_systemdir && aggregation.count_wowdir == 1,
774 "Wrong directory aggregation count %u %u\n",
775 aggregation.count_systemdir, aggregation.count_wowdir);
777 ret = SymRefreshModuleList(pi.hProcess);
778 ok(ret, "SymRefreshModuleList failed: %lu\n", GetLastError());
780 SymCleanup(pi.hProcess);
781 TerminateProcess(pi.hProcess, 0);
783 else
785 if (GetLastError() == ERROR_FILE_NOT_FOUND)
786 skip("Skip wow64 test on non compatible platform\n");
787 else
788 ok(ret, "CreateProcess failed: %lu\n", GetLastError());
791 SymSetOptions(SymGetOptions() | SYMOPT_INCLUDE_32BIT_MODULES);
793 ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
794 if (ret)
796 struct loaded_module_aggregation aggregation2 = {0};
798 ret = WaitForInputIdle(pi.hProcess, 5000);
799 ok(!ret, "wait timed out\n");
801 ret = SymInitialize(pi.hProcess, NULL, FALSE);
802 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
803 memset(&aggregation2, 0, sizeof(aggregation2));
804 aggregation2.proc = pi.hProcess;
805 ret = wrapper_EnumerateLoadedModulesW64(pi.hProcess, aggregate_cb, &aggregation2);
806 ok(ret, "EnumerateLoadedModulesW64 failed: %lu\n", GetLastError());
808 ok(aggregation2.count_32bit && aggregation2.count_64bit, "Wrong bitness aggregation count %u %u\n",
809 aggregation2.count_32bit, aggregation2.count_64bit);
810 todo_wine
811 ok(aggregation2.count_exe == 2 && aggregation2.count_ntdll == 2, "Wrong kind aggregation count %u %u\n",
812 aggregation2.count_exe, aggregation2.count_ntdll);
813 todo_wine
814 ok(aggregation2.count_systemdir > 2 && aggregation2.count_64bit == aggregation2.count_systemdir && aggregation2.count_wowdir > 2,
815 "Wrong directory aggregation count %u %u\n",
816 aggregation2.count_systemdir, aggregation2.count_wowdir);
818 ret = SymRefreshModuleList(pi.hProcess);
819 ok(ret, "SymRefreshModuleList failed: %lu\n", GetLastError());
821 SymCleanup(pi.hProcess);
822 TerminateProcess(pi.hProcess, 0);
824 else
826 if (GetLastError() == ERROR_FILE_NOT_FOUND)
827 skip("Skip wow64 test on non compatible platform\n");
828 else
829 ok(ret, "CreateProcess failed: %lu\n", GetLastError());
834 START_TEST(dbghelp)
836 BOOL ret;
838 /* Don't let the user's environment influence our symbol path */
839 SetEnvironmentVariableA("_NT_SYMBOL_PATH", NULL);
840 SetEnvironmentVariableA("_NT_ALT_SYMBOL_PATH", NULL);
842 pIsWow64Process2 = (void*)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process2");
844 ret = SymInitialize(GetCurrentProcess(), NULL, TRUE);
845 ok(ret, "got error %lu\n", GetLastError());
847 test_stack_walk();
848 test_search_path();
850 ret = SymCleanup(GetCurrentProcess());
851 ok(ret, "got error %lu\n", GetLastError());
853 ret = GetSystemDirectoryW(system_directory, ARRAY_SIZE(system_directory));
854 ok(ret, "GetSystemDirectoryW failed: %lu\n", GetLastError());
855 /* failure happens on a 32bit only wine setup */
856 if (!GetSystemWow64DirectoryW(wow64_directory, ARRAY_SIZE(wow64_directory)))
857 wow64_directory[0] = L'\0';
859 if (test_modules())
861 test_modules_overlap();
862 test_loaded_modules();