dbghelp: Unload overlapping modules in SymLoadModule*().
[wine.git] / dlls / dbghelp / tests / dbghelp.c
blob83805f3ed10a73ef55914c83b3313a606c8b24bd
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 "windows.h"
20 #include "psapi.h"
21 #include "verrsrc.h"
22 #include "dbghelp.h"
23 #include "wine/test.h"
24 #include "winternl.h"
26 static const BOOL is_win64 = sizeof(void*) > sizeof(int);
28 #if defined(__i386__) || defined(__x86_64__)
30 static DWORD CALLBACK stack_walk_thread(void *arg)
32 DWORD count = SuspendThread(GetCurrentThread());
33 ok(!count, "got %ld\n", count);
34 return 0;
37 static void test_stack_walk(void)
39 char si_buf[sizeof(SYMBOL_INFO) + 200];
40 SYMBOL_INFO *si = (SYMBOL_INFO *)si_buf;
41 STACKFRAME64 frame = {{0}}, frame0;
42 BOOL found_our_frame = FALSE;
43 DWORD machine;
44 HANDLE thread;
45 DWORD64 disp;
46 CONTEXT ctx;
47 DWORD count;
48 BOOL ret;
50 thread = CreateThread(NULL, 0, stack_walk_thread, NULL, 0, NULL);
52 /* wait for the thread to suspend itself */
55 Sleep(50);
56 count = SuspendThread(thread);
57 ResumeThread(thread);
59 while (!count);
61 ctx.ContextFlags = CONTEXT_CONTROL;
62 ret = GetThreadContext(thread, &ctx);
63 ok(ret, "got error %u\n", ret);
65 frame.AddrPC.Mode = AddrModeFlat;
66 frame.AddrFrame.Mode = AddrModeFlat;
67 frame.AddrStack.Mode = AddrModeFlat;
69 #ifdef __i386__
70 machine = IMAGE_FILE_MACHINE_I386;
72 frame.AddrPC.Segment = ctx.SegCs;
73 frame.AddrPC.Offset = ctx.Eip;
74 frame.AddrFrame.Segment = ctx.SegSs;
75 frame.AddrFrame.Offset = ctx.Ebp;
76 frame.AddrStack.Segment = ctx.SegSs;
77 frame.AddrStack.Offset = ctx.Esp;
78 #elif defined(__x86_64__)
79 machine = IMAGE_FILE_MACHINE_AMD64;
81 frame.AddrPC.Segment = ctx.SegCs;
82 frame.AddrPC.Offset = ctx.Rip;
83 frame.AddrFrame.Segment = ctx.SegSs;
84 frame.AddrFrame.Offset = ctx.Rbp;
85 frame.AddrStack.Segment = ctx.SegSs;
86 frame.AddrStack.Offset = ctx.Rsp;
87 #endif
88 frame0 = frame;
90 /* first invocation just calculates the return address */
91 ret = StackWalk64(machine, GetCurrentProcess(), thread, &frame, &ctx, NULL,
92 SymFunctionTableAccess64, SymGetModuleBase64, NULL);
93 ok(ret, "StackWalk64() failed: %lu\n", GetLastError());
94 ok(frame.AddrPC.Offset == frame0.AddrPC.Offset, "expected %s, got %s\n",
95 wine_dbgstr_longlong(frame0.AddrPC.Offset),
96 wine_dbgstr_longlong(frame.AddrPC.Offset));
97 ok(frame.AddrStack.Offset == frame0.AddrStack.Offset, "expected %s, got %s\n",
98 wine_dbgstr_longlong(frame0.AddrStack.Offset),
99 wine_dbgstr_longlong(frame.AddrStack.Offset));
100 ok(frame.AddrReturn.Offset && frame.AddrReturn.Offset != frame.AddrPC.Offset,
101 "got bad return address %s\n", wine_dbgstr_longlong(frame.AddrReturn.Offset));
103 while (frame.AddrReturn.Offset)
105 char *addr;
107 ret = StackWalk64(machine, GetCurrentProcess(), thread, &frame, &ctx, NULL,
108 SymFunctionTableAccess64, SymGetModuleBase64, NULL);
109 ok(ret, "StackWalk64() failed: %lu\n", GetLastError());
111 addr = (void *)(DWORD_PTR)frame.AddrPC.Offset;
113 if (!found_our_frame && addr > (char *)stack_walk_thread && addr < (char *)stack_walk_thread + 0x100)
115 found_our_frame = TRUE;
117 si->SizeOfStruct = sizeof(SYMBOL_INFO);
118 si->MaxNameLen = 200;
119 if (SymFromAddr(GetCurrentProcess(), frame.AddrPC.Offset, &disp, si))
120 ok(!strcmp(si->Name, "stack_walk_thread"), "got wrong name %s\n", si->Name);
124 ret = StackWalk64(machine, GetCurrentProcess(), thread, &frame, &ctx, NULL,
125 SymFunctionTableAccess64, SymGetModuleBase64, NULL);
126 ok(!ret, "StackWalk64() should have failed\n");
128 ok(found_our_frame, "didn't find stack_walk_thread frame\n");
131 #else /* __i386__ || __x86_64__ */
133 static void test_stack_walk(void)
137 #endif /* __i386__ || __x86_64__ */
139 static void test_search_path(void)
141 char search_path[128];
142 BOOL ret;
144 /* The default symbol path is ".[;%_NT_SYMBOL_PATH%][;%_NT_ALT_SYMBOL_PATH%]".
145 * We unset both variables earlier so should simply get "." */
146 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
147 ok(ret == TRUE, "ret = %d\n", ret);
148 ok(!strcmp(search_path, "."), "Got search path '%s', expected '.'\n", search_path);
150 /* Set an arbitrary search path */
151 ret = SymSetSearchPath(GetCurrentProcess(), "W:\\");
152 ok(ret == TRUE, "ret = %d\n", ret);
153 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
154 ok(ret == TRUE, "ret = %d\n", ret);
155 ok(!strcmp(search_path, "W:\\"), "Got search path '%s', expected 'W:\\'\n", search_path);
157 /* Setting to NULL resets to the default */
158 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
159 ok(ret == TRUE, "ret = %d\n", ret);
160 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
161 ok(ret == TRUE, "ret = %d\n", ret);
162 ok(!strcmp(search_path, "."), "Got search path '%s', expected '.'\n", search_path);
164 /* With _NT_SYMBOL_PATH */
165 SetEnvironmentVariableA("_NT_SYMBOL_PATH", "X:\\");
166 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
167 ok(ret == TRUE, "ret = %d\n", ret);
168 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
169 ok(ret == TRUE, "ret = %d\n", ret);
170 ok(!strcmp(search_path, ".;X:\\"), "Got search path '%s', expected '.;X:\\'\n", search_path);
172 /* With both _NT_SYMBOL_PATH and _NT_ALT_SYMBOL_PATH */
173 SetEnvironmentVariableA("_NT_ALT_SYMBOL_PATH", "Y:\\");
174 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
175 ok(ret == TRUE, "ret = %d\n", ret);
176 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
177 ok(ret == TRUE, "ret = %d\n", ret);
178 ok(!strcmp(search_path, ".;X:\\;Y:\\"), "Got search path '%s', expected '.;X:\\;Y:\\'\n", search_path);
180 /* With just _NT_ALT_SYMBOL_PATH */
181 SetEnvironmentVariableA("_NT_SYMBOL_PATH", NULL);
182 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
183 ok(ret == TRUE, "ret = %d\n", ret);
184 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
185 ok(ret == TRUE, "ret = %d\n", ret);
186 ok(!strcmp(search_path, ".;Y:\\"), "Got search path '%s', expected '.;Y:\\'\n", search_path);
188 /* Restore original search path */
189 SetEnvironmentVariableA("_NT_ALT_SYMBOL_PATH", NULL);
190 ret = SymSetSearchPath(GetCurrentProcess(), NULL);
191 ok(ret == TRUE, "ret = %d\n", ret);
192 ret = SymGetSearchPath(GetCurrentProcess(), search_path, ARRAY_SIZE(search_path));
193 ok(ret == TRUE, "ret = %d\n", ret);
194 ok(!strcmp(search_path, "."), "Got search path '%s', expected '.'\n", search_path);
197 static USHORT get_module_machine(const char* path)
199 HANDLE hFile, hMap;
200 void* mapping;
201 IMAGE_NT_HEADERS *nthdr;
202 USHORT machine = IMAGE_FILE_MACHINE_UNKNOWN;
204 hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
205 if (hFile != INVALID_HANDLE_VALUE)
207 hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
208 if (hMap != NULL)
210 mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
211 if (mapping != NULL)
213 nthdr = RtlImageNtHeader(mapping);
214 if (nthdr != NULL) machine = nthdr->FileHeader.Machine;
215 UnmapViewOfFile(mapping);
217 CloseHandle(hMap);
219 CloseHandle(hFile);
221 return machine;
224 static BOOL skip_too_old_dbghelp(HANDLE proc, DWORD64 base)
226 IMAGEHLP_MODULE im0 = {sizeof(im0)};
227 BOOL ret;
229 /* test if get module info succeeds with oldest structure format */
230 ret = SymGetModuleInfo(proc, base, &im0);
231 if (ret)
233 skip("Too old dbghelp. Skipping module tests.\n");
234 ret = SymCleanup(proc);
235 ok(ret, "SymCleanup failed: %lu\n", GetLastError());
236 return TRUE;
238 ok(ret, "SymGetModuleInfo failed: %lu\n", GetLastError());
239 return FALSE;
242 static DWORD get_module_size(const char* path)
244 BOOL ret;
245 HANDLE hFile, hMap;
246 void* mapping;
247 IMAGE_NT_HEADERS *nthdr;
248 DWORD size;
250 hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
251 ok(hFile != INVALID_HANDLE_VALUE, "Couldn't open file %s (%lu)\n", path, GetLastError());
252 hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
253 ok(hMap != NULL, "Couldn't create map (%lu)\n", GetLastError());
254 mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
255 ok(mapping != NULL, "Couldn't map (%lu)\n", GetLastError());
256 nthdr = RtlImageNtHeader(mapping);
257 ok(nthdr != NULL, "Cannot get NT headers out of %s\n", path);
258 size = nthdr ? nthdr->OptionalHeader.SizeOfImage : 0;
259 ret = UnmapViewOfFile(mapping);
260 ok(ret, "Couldn't unmap (%lu)\n", GetLastError());
261 CloseHandle(hMap);
262 CloseHandle(hFile);
263 return size;
266 static BOOL CALLBACK count_module_cb(const char* name, DWORD64 base, void* usr)
268 (*(unsigned*)usr)++;
269 return TRUE;
272 static unsigned get_module_count(HANDLE proc)
274 unsigned count = 0;
275 BOOL ret;
277 ret = SymEnumerateModules64(proc, count_module_cb, &count);
278 ok(ret, "SymEnumerateModules64 failed: %lu\n", GetLastError());
279 return count;
282 struct nth_module
284 HANDLE proc;
285 unsigned int index;
286 BOOL will_fail;
287 IMAGEHLP_MODULE64 module;
290 static BOOL CALLBACK nth_module_cb(const char* name, DWORD64 base, void* usr)
292 struct nth_module* nth = usr;
293 BOOL ret;
295 if (nth->index--) return TRUE;
296 nth->module.SizeOfStruct = sizeof(nth->module);
297 ret = SymGetModuleInfo64(nth->proc, base, &nth->module);
298 if (nth->will_fail)
300 ok(!ret, "SymGetModuleInfo64 should have failed\n");
301 nth->module.BaseOfImage = base;
303 else
305 ok(ret, "SymGetModuleInfo64 failed: %lu\n", GetLastError());
306 ok(nth->module.BaseOfImage == base, "Wrong base\n");
308 return FALSE;
311 static BOOL test_modules(void)
313 BOOL ret;
314 char file_system[MAX_PATH];
315 char file_wow64[MAX_PATH];
316 DWORD64 base;
317 const DWORD64 base1 = 0x00010000;
318 const DWORD64 base2 = 0x08010000;
319 IMAGEHLP_MODULEW64 im;
320 USHORT machine_wow, machine2;
321 HANDLE dummy = (HANDLE)(ULONG_PTR)0xcafef00d;
322 const char* target_dll = "c:\\windows\\system32\\kernel32.dll";
323 unsigned count;
325 im.SizeOfStruct = sizeof(im);
327 /* can sym load an exec of different bitness even if 32Bit flag not set */
329 SymSetOptions(SymGetOptions() & ~SYMOPT_INCLUDE_32BIT_MODULES);
330 ret = SymInitialize(GetCurrentProcess(), 0, FALSE);
331 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
333 GetSystemWow64DirectoryA(file_wow64, MAX_PATH);
334 strcat(file_wow64, "\\notepad.exe");
336 /* not always present */
337 machine_wow = get_module_machine(file_wow64);
338 if (machine_wow != IMAGE_FILE_MACHINE_UNKNOWN)
340 base = SymLoadModule(GetCurrentProcess(), NULL, file_wow64, NULL, base2, 0);
341 ok(base == base2, "SymLoadModule failed: %lu\n", GetLastError());
342 ret = SymGetModuleInfoW64(GetCurrentProcess(), base2, &im);
343 if (!ret && skip_too_old_dbghelp(GetCurrentProcess(), base2)) return FALSE;
344 ok(ret, "SymGetModuleInfoW64 failed: %lu\n", GetLastError());
345 ok(im.BaseOfImage == base2, "Wrong base address\n");
346 ok(im.MachineType == machine_wow, "Wrong machine %lx (expecting %u)\n", im.MachineType, machine_wow);
349 GetSystemDirectoryA(file_system, MAX_PATH);
350 strcat(file_system, "\\notepad.exe");
352 base = SymLoadModule(GetCurrentProcess(), NULL, file_system, NULL, base1, 0);
353 ok(base == base1, "SymLoadModule failed: %lu\n", GetLastError());
354 ret = SymGetModuleInfoW64(GetCurrentProcess(), base1, &im);
355 if (!ret && skip_too_old_dbghelp(GetCurrentProcess(), base1)) return FALSE;
356 ok(ret, "SymGetModuleInfoW64 failed: %lu\n", GetLastError());
357 ok(im.BaseOfImage == base1, "Wrong base address\n");
358 machine2 = get_module_machine(file_system);
359 ok(machine2 != IMAGE_FILE_MACHINE_UNKNOWN, "Unexpected machine %u\n", machine2);
360 ok(im.MachineType == machine2, "Wrong machine %lx (expecting %u)\n", im.MachineType, machine2);
362 /* still can access first module after loading second */
363 if (machine_wow != IMAGE_FILE_MACHINE_UNKNOWN)
365 ret = SymGetModuleInfoW64(GetCurrentProcess(), base2, &im);
366 ok(ret, "SymGetModuleInfoW64 failed: %lu\n", GetLastError());
367 ok(im.BaseOfImage == base2, "Wrong base address\n");
368 ok(im.MachineType == machine_wow, "Wrong machine %lx (expecting %u)\n", im.MachineType, machine_wow);
371 ret = SymCleanup(GetCurrentProcess());
372 ok(ret, "SymCleanup failed: %lu\n", GetLastError());
374 ret = SymInitialize(dummy, NULL, FALSE);
375 ok(ret, "got error %lu\n", GetLastError());
377 count = get_module_count(dummy);
378 ok(count == 0, "Unexpected count (%u instead of 0)\n", count);
380 /* loading with 0 size succeeds */
381 base = SymLoadModuleEx(dummy, NULL, target_dll, NULL, base1, 0, NULL, 0);
382 ok(base == base1, "SymLoadModuleEx failed: %lu\n", GetLastError());
384 ret = SymUnloadModule64(dummy, base1);
385 ok(ret, "SymUnloadModule64 failed: %lu\n", GetLastError());
387 count = get_module_count(dummy);
388 ok(count == 0, "Unexpected count (%u instead of 0)\n", count);
390 ret = SymCleanup(dummy);
391 ok(ret, "SymCleanup failed: %lu\n", GetLastError());
393 return TRUE;
396 static void test_modules_overlap(void)
398 BOOL ret;
399 DWORD64 base;
400 const DWORD64 base1 = 0x00010000;
401 HANDLE dummy = (HANDLE)(ULONG_PTR)0xcafef00d;
402 const char* target1_dll = "c:\\windows\\system32\\kernel32.dll";
403 const char* target2_dll = "c:\\windows\\system32\\winmm.dll";
404 DWORD imsize = get_module_size(target1_dll);
405 int i, j;
406 struct test_module
408 DWORD64 base;
409 DWORD size;
410 const char* name;
412 const struct test
414 struct test_module input;
415 DWORD error_code;
416 struct test_module outputs[2];
418 tests[] =
420 /* cases where first module is left "untouched" and second not loaded */
421 {{base1, 0, target1_dll}, ERROR_SUCCESS, {{base1, imsize, "kernel32"},{0}}},
422 {{base1, imsize, target1_dll}, ERROR_SUCCESS, {{base1, imsize, "kernel32"},{0}}},
423 {{base1, imsize / 2, target1_dll}, ERROR_SUCCESS, {{base1, imsize, "kernel32"},{0}}},
424 {{base1, imsize * 2, target1_dll}, ERROR_SUCCESS, {{base1, imsize, "kernel32"},{0}}},
426 /* cases where first module is unloaded and replaced by second module */
427 {{base1 + imsize / 2, imsize, target1_dll}, ~0, {{base1 + imsize / 2, imsize, "kernel32"},{0}}},
428 {{base1 + imsize / 3, imsize / 3, target1_dll}, ~0, {{base1 + imsize / 3, imsize / 3, "kernel32"},{0}}},
429 {{base1 + imsize / 2, imsize, target2_dll}, ~0, {{base1 + imsize / 2, imsize, "winmm"},{0}}},
430 {{base1 + imsize / 3, imsize / 3, target2_dll}, ~0, {{base1 + imsize / 3, imsize / 3, "winmm"},{0}}},
432 /* cases where second module is actually loaded */
433 {{base1 + imsize, imsize, target1_dll}, ~0, {{base1, imsize, "kernel32"}, {base1 + imsize, imsize, "kernel32"}}},
434 {{base1 - imsize / 2, imsize, target1_dll}, ~0, {{base1, imsize, "kernel32"}, {base1 - imsize / 2, imsize, NULL}}},
435 /* we mark with a NULL modulename the cases where the module is loaded, but isn't visible
436 * (SymGetModuleInfo fails in callback) as it's base address is inside the first loaded module.
438 {{base1 + imsize, imsize, target2_dll}, ~0, {{base1, imsize, "kernel32"}, {base1 + imsize, imsize, "winmm"}}},
439 {{base1 - imsize / 2, imsize, target2_dll}, ~0, {{base1, imsize, "kernel32"}, {base1 - imsize / 2, imsize, NULL}}},
442 ok(imsize, "Cannot get image size\n");
444 for (i = 0; i < ARRAY_SIZE(tests); i++)
446 ret = SymInitialize(dummy, NULL, FALSE);
447 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
449 base = SymLoadModuleEx(dummy, NULL, target1_dll, NULL, base1, 0, NULL, 0);
450 ok(base == base1, "SymLoadModuleEx failed: %lu\n", GetLastError());
451 base = SymLoadModuleEx(dummy, NULL, tests[i].input.name, NULL, tests[i].input.base, tests[i].input.size, NULL, 0);
452 if (tests[i].error_code != ~0)
454 ok(base == 0, "SymLoadModuleEx should have failed\n");
455 ok(GetLastError() == tests[i].error_code, "Wrong error %lu\n", GetLastError());
457 else
459 ok(base == tests[i].input.base, "SymLoadModuleEx failed: %lu\n", GetLastError());
461 for (j = 0; j < ARRAY_SIZE(tests[i].outputs); j++)
463 struct nth_module nth = {dummy, j, !tests[i].outputs[j].name, {0}};
465 ret = SymEnumerateModules64(dummy, nth_module_cb, &nth);
466 ok(ret, "SymEnumerateModules64 failed: %lu\n", GetLastError());
467 if (!tests[i].outputs[j].base)
469 ok(nth.index != -1, "Got more modules than expected %d, %d\n", nth.index, j);
470 break;
472 ok(nth.index == -1, "Expecting more modules\n");
473 ok(nth.module.BaseOfImage == tests[i].outputs[j].base, "Wrong base\n");
474 if (!nth.will_fail)
476 ok(nth.module.ImageSize == tests[i].outputs[j].size, "Wrong size\n");
477 ok(!strcasecmp(nth.module.ModuleName, tests[i].outputs[j].name), "Wrong name\n");
480 ret = SymCleanup(dummy);
481 ok(ret, "SymCleanup failed: %lu\n", GetLastError());
485 struct loaded_module_aggregation
487 HANDLE proc;
488 unsigned int count_32bit;
489 unsigned int count_64bit;
490 unsigned int count_exe;
491 unsigned int count_ntdll;
492 unsigned int count_systemdir;
493 unsigned int count_wowdir;
496 static BOOL CALLBACK aggregate_cb(PCWSTR imagename, DWORD64 base, ULONG sz, PVOID usr)
498 struct loaded_module_aggregation* aggregation = usr;
499 IMAGEHLP_MODULEW64 im;
500 size_t image_len;
501 BOOL ret, wow64;
502 WCHAR buffer[MAX_PATH];
504 memset(&im, 0, sizeof(im));
505 im.SizeOfStruct = sizeof(im);
507 image_len = wcslen(imagename);
509 ret = SymGetModuleInfoW64(aggregation->proc, base, &im);
510 if (ret)
511 ok(aggregation->count_exe && image_len >= 4 && !wcscmp(imagename + image_len - 4, L".exe"),
512 "%ls shouldn't already be loaded\n", imagename);
513 else
515 ok(!ret, "Module %ls shouldn't be loaded\n", imagename);
516 ret = SymLoadModuleExW(aggregation->proc, NULL, imagename, NULL, base, sz, NULL, 0);
517 ok(ret, "SymLoadModuleExW failed on %ls: %lu\n", imagename, GetLastError());
518 ret = SymGetModuleInfoW64(aggregation->proc, base, &im);
519 ok(ret, "SymGetModuleInfoW64 failed: %lu\n", GetLastError());
522 switch (im.MachineType)
524 case IMAGE_FILE_MACHINE_UNKNOWN:
525 break;
526 case IMAGE_FILE_MACHINE_I386:
527 case IMAGE_FILE_MACHINE_ARM:
528 case IMAGE_FILE_MACHINE_ARMNT:
529 aggregation->count_32bit++;
530 break;
531 case IMAGE_FILE_MACHINE_AMD64:
532 case IMAGE_FILE_MACHINE_ARM64:
533 aggregation->count_64bit++;
534 break;
535 default:
536 ok(0, "Unsupported machine %lx\n", im.MachineType);
537 break;
539 if (image_len >= 4 && !wcsicmp(imagename + image_len - 4, L".exe"))
540 aggregation->count_exe++;
541 if (!wcsicmp(im.ModuleName, L"ntdll"))
542 aggregation->count_ntdll++;
543 if (GetSystemDirectoryW(buffer, ARRAY_SIZE(buffer)) &&
544 !wcsnicmp(imagename, buffer, wcslen(buffer)))
545 aggregation->count_systemdir++;
546 if (is_win64 && IsWow64Process(aggregation->proc, &wow64) && wow64 &&
547 GetSystemWow64DirectoryW(buffer, ARRAY_SIZE(buffer)) &&
548 !wcsnicmp(imagename, buffer, wcslen(buffer)))
549 aggregation->count_wowdir++;
551 return TRUE;
555 static void test_loaded_modules(void)
557 BOOL ret;
558 char buffer[200];
559 PROCESS_INFORMATION pi = {0};
560 STARTUPINFOA si = {0};
561 struct loaded_module_aggregation aggregation = {0};
563 ret = GetSystemDirectoryA(buffer, sizeof(buffer));
564 ok(ret, "got error %lu\n", GetLastError());
565 strcat(buffer, "\\notepad.exe");
567 /* testing with child process of different machines */
568 ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
569 ok(ret, "CreateProcess failed: %lu\n", GetLastError());
571 ret = WaitForInputIdle(pi.hProcess, 5000);
572 ok(!ret, "wait timed out\n");
574 ret = SymInitialize(pi.hProcess, NULL, FALSE);
575 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
576 memset(&aggregation, 0, sizeof(aggregation));
577 aggregation.proc = pi.hProcess;
579 ret = EnumerateLoadedModulesW64(pi.hProcess, aggregate_cb, &aggregation);
580 ok(ret, "EnumerateLoadedModulesW64 failed: %lu\n", GetLastError());
582 if (is_win64)
584 ok(!aggregation.count_32bit && aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n",
585 aggregation.count_32bit, aggregation.count_64bit);
586 ok(aggregation.count_exe == 1 && aggregation.count_ntdll == 1, "Wrong kind aggregation count %u %u\n",
587 aggregation.count_exe, aggregation.count_ntdll);
588 ok(aggregation.count_systemdir > 2 && !aggregation.count_wowdir, "Wrong directory aggregation count %u %u\n",
589 aggregation.count_systemdir, aggregation.count_wowdir);
591 else
593 BOOL is_wow64;
594 ret = IsWow64Process(pi.hProcess, &is_wow64);
595 ok(ret, "IsWow64Process failed: %lu\n", GetLastError());
597 ok(aggregation.count_32bit && !aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n",
598 aggregation.count_32bit, aggregation.count_64bit);
599 ok(aggregation.count_exe == 1 && aggregation.count_ntdll == 1, "Wrong kind aggregation count %u %u\n",
600 aggregation.count_exe, aggregation.count_ntdll);
601 todo_wine_if(is_wow64)
602 ok(aggregation.count_systemdir > 2 && !aggregation.count_wowdir, "Wrong directory aggregation count %u %u\n",
603 aggregation.count_systemdir, aggregation.count_wowdir);
606 SymCleanup(pi.hProcess);
607 TerminateProcess(pi.hProcess, 0);
609 if (is_win64)
611 ret = GetSystemWow64DirectoryA(buffer, sizeof(buffer));
612 ok(ret, "got error %lu\n", GetLastError());
613 strcat(buffer, "\\notepad.exe");
615 SymSetOptions(SymGetOptions() & ~SYMOPT_INCLUDE_32BIT_MODULES);
617 ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
618 if (ret)
620 ret = WaitForInputIdle(pi.hProcess, 5000);
621 ok(!ret, "wait timed out\n");
623 ret = SymInitialize(pi.hProcess, NULL, FALSE);
624 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
625 memset(&aggregation, 0, sizeof(aggregation));
626 aggregation.proc = pi.hProcess;
628 ret = EnumerateLoadedModulesW64(pi.hProcess, aggregate_cb, &aggregation);
629 ok(ret, "EnumerateLoadedModulesW64 failed: %lu\n", GetLastError());
631 todo_wine
632 ok(aggregation.count_32bit == 1 && aggregation.count_64bit, "Wrong bitness aggregation count %u %u\n",
633 aggregation.count_32bit, aggregation.count_64bit);
634 ok(aggregation.count_exe == 1 && aggregation.count_ntdll == 1, "Wrong kind aggregation count %u %u\n",
635 aggregation.count_exe, aggregation.count_ntdll);
636 todo_wine
637 ok(aggregation.count_systemdir > 2 && aggregation.count_64bit == aggregation.count_systemdir && aggregation.count_wowdir == 1,
638 "Wrong directory aggregation count %u %u\n",
639 aggregation.count_systemdir, aggregation.count_wowdir);
641 SymCleanup(pi.hProcess);
642 TerminateProcess(pi.hProcess, 0);
644 else
646 if (GetLastError() == ERROR_FILE_NOT_FOUND)
647 skip("Skip wow64 test on non compatible platform\n");
648 else
649 ok(ret, "CreateProcess failed: %lu\n", GetLastError());
652 SymSetOptions(SymGetOptions() | SYMOPT_INCLUDE_32BIT_MODULES);
654 ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
655 if (ret)
657 struct loaded_module_aggregation aggregation2 = {0};
659 ret = WaitForInputIdle(pi.hProcess, 5000);
660 ok(!ret, "wait timed out\n");
662 ret = SymInitialize(pi.hProcess, NULL, FALSE);
663 ok(ret, "SymInitialize failed: %lu\n", GetLastError());
664 memset(&aggregation2, 0, sizeof(aggregation2));
665 aggregation2.proc = pi.hProcess;
666 ret = EnumerateLoadedModulesW64(pi.hProcess, aggregate_cb, &aggregation2);
667 ok(ret, "EnumerateLoadedModulesW64 failed: %lu\n", GetLastError());
669 ok(aggregation2.count_32bit && aggregation2.count_64bit, "Wrong bitness aggregation count %u %u\n",
670 aggregation2.count_32bit, aggregation2.count_64bit);
671 todo_wine
672 ok(aggregation2.count_exe == 2 && aggregation2.count_ntdll == 2, "Wrong kind aggregation count %u %u\n",
673 aggregation2.count_exe, aggregation2.count_ntdll);
674 todo_wine
675 ok(aggregation2.count_systemdir > 2 && aggregation2.count_64bit == aggregation2.count_systemdir && aggregation2.count_wowdir > 2,
676 "Wrong directory aggregation count %u %u\n",
677 aggregation2.count_systemdir, aggregation2.count_wowdir);
679 SymCleanup(pi.hProcess);
680 TerminateProcess(pi.hProcess, 0);
682 else
684 if (GetLastError() == ERROR_FILE_NOT_FOUND)
685 skip("Skip wow64 test on non compatible platform\n");
686 else
687 ok(ret, "CreateProcess failed: %lu\n", GetLastError());
692 START_TEST(dbghelp)
694 BOOL ret;
696 /* Don't let the user's environment influence our symbol path */
697 SetEnvironmentVariableA("_NT_SYMBOL_PATH", NULL);
698 SetEnvironmentVariableA("_NT_ALT_SYMBOL_PATH", NULL);
700 ret = SymInitialize(GetCurrentProcess(), NULL, TRUE);
701 ok(ret, "got error %lu\n", GetLastError());
703 test_stack_walk();
704 test_search_path();
706 ret = SymCleanup(GetCurrentProcess());
707 ok(ret, "got error %lu\n", GetLastError());
709 if (test_modules())
711 test_modules_overlap();
712 test_loaded_modules();