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
23 #include "wine/test.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
);
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
;
50 thread
= CreateThread(NULL
, 0, stack_walk_thread
, NULL
, 0, NULL
);
52 /* wait for the thread to suspend itself */
56 count
= SuspendThread(thread
);
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
;
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
;
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
)
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];
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
)
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
);
210 mapping
= MapViewOfFile(hMap
, FILE_MAP_READ
, 0, 0, 0);
213 nthdr
= RtlImageNtHeader(mapping
);
214 if (nthdr
!= NULL
) machine
= nthdr
->FileHeader
.Machine
;
215 UnmapViewOfFile(mapping
);
224 static BOOL
skip_too_old_dbghelp(HANDLE proc
, DWORD64 base
)
226 IMAGEHLP_MODULE im0
= {sizeof(im0
)};
229 /* test if get module info succeeds with oldest structure format */
230 ret
= SymGetModuleInfo(proc
, base
, &im0
);
233 skip("Too old dbghelp. Skipping module tests.\n");
234 ret
= SymCleanup(proc
);
235 ok(ret
, "SymCleanup failed: %lu\n", GetLastError());
238 ok(ret
, "SymGetModuleInfo failed: %lu\n", GetLastError());
242 static DWORD
get_module_size(const char* path
)
247 IMAGE_NT_HEADERS
*nthdr
;
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());
266 static BOOL CALLBACK
count_module_cb(const char* name
, DWORD64 base
, void* usr
)
272 static unsigned get_module_count(HANDLE proc
)
277 ret
= SymEnumerateModules64(proc
, count_module_cb
, &count
);
278 ok(ret
, "SymEnumerateModules64 failed: %lu\n", GetLastError());
287 IMAGEHLP_MODULE64 module
;
290 static BOOL CALLBACK
nth_module_cb(const char* name
, DWORD64 base
, void* usr
)
292 struct nth_module
* nth
= usr
;
295 if (nth
->index
--) return TRUE
;
296 nth
->module
.SizeOfStruct
= sizeof(nth
->module
);
297 ret
= SymGetModuleInfo64(nth
->proc
, base
, &nth
->module
);
300 ok(!ret
, "SymGetModuleInfo64 should have failed\n");
301 nth
->module
.BaseOfImage
= base
;
305 ok(ret
, "SymGetModuleInfo64 failed: %lu\n", GetLastError());
306 ok(nth
->module
.BaseOfImage
== base
, "Wrong base\n");
311 static BOOL
test_modules(void)
314 char file_system
[MAX_PATH
];
315 char file_wow64
[MAX_PATH
];
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";
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());
396 static void test_modules_overlap(void)
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
);
414 struct test_module input
;
416 struct test_module outputs
[2];
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());
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
);
472 ok(nth
.index
== -1, "Expecting more modules\n");
473 ok(nth
.module
.BaseOfImage
== tests
[i
].outputs
[j
].base
, "Wrong base\n");
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
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
;
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
);
511 ok(aggregation
->count_exe
&& image_len
>= 4 && !wcscmp(imagename
+ image_len
- 4, L
".exe"),
512 "%ls shouldn't already be loaded\n", imagename
);
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
:
526 case IMAGE_FILE_MACHINE_I386
:
527 case IMAGE_FILE_MACHINE_ARM
:
528 case IMAGE_FILE_MACHINE_ARMNT
:
529 aggregation
->count_32bit
++;
531 case IMAGE_FILE_MACHINE_AMD64
:
532 case IMAGE_FILE_MACHINE_ARM64
:
533 aggregation
->count_64bit
++;
536 ok(0, "Unsupported machine %lx\n", im
.MachineType
);
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
++;
555 static void test_loaded_modules(void)
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());
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
);
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);
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
);
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());
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
);
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);
646 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
647 skip("Skip wow64 test on non compatible platform\n");
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
);
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
);
672 ok(aggregation2
.count_exe
== 2 && aggregation2
.count_ntdll
== 2, "Wrong kind aggregation count %u %u\n",
673 aggregation2
.count_exe
, aggregation2
.count_ntdll
);
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);
684 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
685 skip("Skip wow64 test on non compatible platform\n");
687 ok(ret
, "CreateProcess failed: %lu\n", GetLastError());
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());
706 ret
= SymCleanup(GetCurrentProcess());
707 ok(ret
, "got error %lu\n", GetLastError());
711 test_modules_overlap();
712 test_loaded_modules();