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
20 #define WIN32_NO_STATUS
25 #include "wine/test.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
);
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
;
56 thread
= CreateThread(NULL
, 0, stack_walk_thread
, NULL
, 0, NULL
);
58 /* wait for the thread to suspend itself */
62 count
= SuspendThread(thread
);
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
;
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
;
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
)
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];
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
)
215 case IMAGE_FILE_MACHINE_I386
:
216 case IMAGE_FILE_MACHINE_ARM
:
217 case IMAGE_FILE_MACHINE_ARMNT
:
219 case IMAGE_FILE_MACHINE_AMD64
:
220 case IMAGE_FILE_MACHINE_ARM64
:
223 ok(0, "Unsupported machine %x\n", machine
);
228 static USHORT
get_module_machine(const char* path
)
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
);
241 mapping
= MapViewOfFile(hMap
, FILE_MAP_READ
, 0, 0, 0);
244 nthdr
= RtlImageNtHeader(mapping
);
245 if (nthdr
!= NULL
) machine
= nthdr
->FileHeader
.Machine
;
246 UnmapViewOfFile(mapping
);
255 static BOOL
skip_too_old_dbghelp(HANDLE proc
, DWORD64 base
)
257 IMAGEHLP_MODULE im0
= {sizeof(im0
)};
260 /* test if get module info succeeds with oldest structure format */
261 ret
= SymGetModuleInfo(proc
, base
, &im0
);
264 skip("Too old dbghelp. Skipping module tests.\n");
265 ret
= SymCleanup(proc
);
266 ok(ret
, "SymCleanup failed: %lu\n", GetLastError());
269 ok(ret
, "SymGetModuleInfo failed: %lu\n", GetLastError());
273 static DWORD
get_module_size(const char* path
)
278 IMAGE_NT_HEADERS
*nthdr
;
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());
297 static BOOL CALLBACK
count_module_cb(const char* name
, DWORD64 base
, void* usr
)
303 static unsigned get_module_count(HANDLE proc
)
308 ret
= SymEnumerateModules64(proc
, count_module_cb
, &count
);
309 ok(ret
, "SymEnumerateModules64 failed: %lu\n", GetLastError());
318 IMAGEHLP_MODULE64 module
;
321 static BOOL CALLBACK
nth_module_cb(const char* name
, DWORD64 base
, void* usr
)
323 struct nth_module
* nth
= usr
;
326 if (nth
->index
--) return TRUE
;
327 nth
->module
.SizeOfStruct
= sizeof(nth
->module
);
328 ret
= SymGetModuleInfo64(nth
->proc
, base
, &nth
->module
);
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
;
337 ok(ret
, "SymGetModuleInfo64 failed: %lu\n", GetLastError());
338 ok(nth
->module
.BaseOfImage
== base
, "Wrong base\n");
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
)
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
)
361 static BOOL
test_modules(void)
364 char file_system
[MAX_PATH
];
365 char file_wow64
[MAX_PATH
];
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";
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());
449 static void test_modules_overlap(void)
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
);
459 IMAGEHLP_SYMBOL64
* sym
= (void*)buffer
;
470 struct test_module input
;
472 struct test_module outputs
[2];
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());
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
);
531 ok(nth
.index
== -1, "Expecting more modules\n");
532 ok(nth
.module
.BaseOfImage
== tests
[i
].outputs
[j
].base
, "Wrong base\n");
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
);
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());
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
)
574 if (!pIsWow64Process2
) /* only happens on old Win 8 and early win 1064v1507 */
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
;
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
;
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
);
617 struct loaded_module_aggregation
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
;
634 memset(&im
, 0, sizeof(im
));
635 im
.SizeOfStruct
= sizeof(im
);
637 ret
= SymGetModuleInfoW64(aggregation
->proc
, base
, &im
);
639 ok(aggregation
->count_exe
&& ends_withW(imagename
, L
".exe"),
640 "%ls shouldn't already be loaded\n", imagename
);
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;
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
++;
671 static void test_loaded_modules(void)
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());
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
);
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
))
720 ok(0, "Unknown process kind\n");
725 ok(aggregation
.count_systemdir
> 2 && aggregation
.count_wowdir
== 1, "Wrong directory aggregation count %u %u\n",
726 aggregation
.count_systemdir
, aggregation
.count_wowdir
);
729 ok(aggregation
.count_systemdir
> 2 && aggregation
.count_wowdir
== 0, "Wrong directory aggregation count %u %u\n",
730 aggregation
.count_systemdir
, aggregation
.count_wowdir
);
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
);
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);
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
);
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());
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
);
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);
785 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
786 skip("Skip wow64 test on non compatible platform\n");
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
);
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
);
811 ok(aggregation2
.count_exe
== 2 && aggregation2
.count_ntdll
== 2, "Wrong kind aggregation count %u %u\n",
812 aggregation2
.count_exe
, aggregation2
.count_ntdll
);
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);
826 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
827 skip("Skip wow64 test on non compatible platform\n");
829 ok(ret
, "CreateProcess failed: %lu\n", GetLastError());
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());
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';
861 test_modules_overlap();
862 test_loaded_modules();