2db3935fe4da9cc25b203a9e4972a6284cc55352
[reactos.git] / modules / rostests / apitests / dbghelp / pdb.c
1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Test for dbghelp PDB functions
5 * PROGRAMMER: Mark Jansen
6 */
7
8 #include <ntstatus.h>
9 #define WIN32_NO_STATUS
10 #include <windows.h>
11 #include <dbghelp.h>
12 #include <cvconst.h> // SymTagXXX
13 #include <stdio.h>
14
15 #include "wine/test.h"
16
17 #define ok_ulonglong(expression, result) \
18 do { \
19 ULONG64 _value = (expression); \
20 ULONG64 _result = (result); \
21 ok(_value == (result), "Wrong value for '%s', expected: " #result " (%s), got: %s\n", \
22 #expression, wine_dbgstr_longlong(_result), wine_dbgstr_longlong(_value)); \
23 } while (0)
24
25
26 // data.c
27 void create_compressed_files();
28 int extract_msvc_exe(char szFile[MAX_PATH]);
29 void cleanup_msvc_exe();
30
31 static HANDLE proc()
32 {
33 return GetCurrentProcess();
34 }
35
36 static BOOL init_sym_imp(const char* file, int line)
37 {
38 if (!SymInitialize(proc(), NULL, FALSE))
39 {
40 DWORD err = GetLastError();
41 ok_(file, line)(0, "Failed to init: 0x%x\n", err);
42 return FALSE;
43 }
44 return TRUE;
45 }
46
47 static void deinit_sym()
48 {
49 SymCleanup(proc());
50 }
51
52 #define init_sym() init_sym_imp(__FILE__, __LINE__)
53
54 #define INIT_PSYM(buff) do { \
55 memset((buff), 0, sizeof((buff))); \
56 ((PSYMBOL_INFO)(buff))->SizeOfStruct = sizeof(SYMBOL_INFO); \
57 ((PSYMBOL_INFO)(buff))->MaxNameLen = MAX_SYM_NAME; \
58 } while (0)
59
60 /* modified copy of function from apitests/apphelp/apitest.c */
61 BOOL get_module_version(
62 _In_ HMODULE mod,
63 _Out_ VS_FIXEDFILEINFO *fileinfo)
64 {
65 BOOL res = FALSE;
66 HRSRC hResInfo;
67 char *errmsg;
68 DWORD dwSize, errcode = 0;
69 UINT uLen;
70 HGLOBAL hResData = 0;
71 LPVOID pRes = NULL;
72 HLOCAL pResCopy = 0;
73 VS_FIXEDFILEINFO *lpFfi;
74
75 if (fileinfo == NULL)
76 {
77 errmsg = "fileinfo is NULL.\n";
78 goto cleanup;
79 }
80
81 hResInfo = FindResource(mod, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
82 if (hResInfo == 0)
83 {
84 errmsg = "FindResource failed";
85 errcode = GetLastError();
86 goto cleanup;
87 }
88
89 dwSize = SizeofResource(mod, hResInfo);
90 if (dwSize == 0)
91 {
92 errmsg = "SizeofResource failed";
93 errcode = GetLastError();
94 goto cleanup;
95 }
96
97 hResData = LoadResource(mod, hResInfo);
98 if (hResData == 0)
99 {
100 errmsg = "LoadResource failed";
101 errcode = GetLastError();
102 goto cleanup;
103 }
104
105 pRes = LockResource(hResData);
106 if (pRes == NULL)
107 {
108 errmsg = "LockResource failed";
109 errcode = GetLastError();
110 goto cleanup;
111 }
112
113 pResCopy = LocalAlloc(LMEM_FIXED, dwSize);
114 if (pResCopy == NULL)
115 {
116 errmsg = "LocalAlloc failed";
117 errcode = GetLastError();
118 goto cleanup;
119 }
120
121 CopyMemory(pResCopy, pRes, dwSize);
122
123 if (VerQueryValueW(pResCopy, L"\\", (LPVOID*)&lpFfi, &uLen))
124 {
125 *fileinfo = *lpFfi;
126 res = TRUE;
127 }
128
129 cleanup:
130 /* cleanup */
131 if (hResData != 0)
132 FreeResource(hResData);
133 if (pResCopy != NULL)
134 LocalFree(pResCopy);
135 /* if it was good */
136 if (res == TRUE)
137 return TRUE;
138 /* failure path */
139 if (errcode == 0)
140 trace("get_module_version - %s.\n", errmsg);
141 else
142 trace("get_module_version - %s (lasterror %d).\n", errmsg, errcode);
143 return FALSE;
144 }
145
146 static VS_FIXEDFILEINFO dbghelpFileVer;
147 static void init_dbghelp_version()
148 {
149 LPAPI_VERSION v;
150 WCHAR filenameW[MAX_PATH + 1];
151 HMODULE hDLL;
152 DWORD fileLen;
153 VS_FIXEDFILEINFO fileInfo;
154
155 memset(&dbghelpFileVer, 0, sizeof(dbghelpFileVer));
156
157 /* get internal file version */
158 v = ImagehlpApiVersion();
159 if (v == NULL)
160 return;
161
162 /* get module file version */
163 hDLL = GetModuleHandleW(L"dbghelp.dll");
164 if (hDLL == 0)
165 {
166 ok(FALSE, "Dbghelp.dll is not loaded!\n");
167 return;
168 }
169 if (!get_module_version(hDLL, &fileInfo))
170 memset(&fileInfo, 0, sizeof(fileInfo));
171 dbghelpFileVer = fileInfo;
172
173 /* get full file path */
174 fileLen = GetModuleFileNameW(hDLL, filenameW, MAX_PATH + 1);
175 if (fileLen == 0)
176 {
177 ok(FALSE, "GetModuleFileNameW for dbghelp.dll failed!\n");
178 return;
179 }
180
181 trace("Using %S\n", filenameW);
182 trace(" API-Version: %hu.%hu.%hu (%hu)\n",
183 v->MajorVersion, v->MinorVersion, v->Revision, v->Reserved);
184
185 trace(" Fileversion: %hu.%hu.%hu.%hu\n",
186 HIWORD(fileInfo.dwProductVersionMS),
187 LOWORD(fileInfo.dwProductVersionMS),
188 HIWORD(fileInfo.dwProductVersionLS),
189 LOWORD(fileInfo.dwProductVersionLS));
190 }
191
192 /* Maybe our dbghelp.dll is too old? */
193 static BOOL can_enumerate(HANDLE hProc, DWORD64 BaseAddress)
194 {
195 IMAGEHLP_MODULE64 ModuleInfo;
196 BOOL Ret;
197
198 memset(&ModuleInfo, 0, sizeof(ModuleInfo));
199 ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
200 Ret = SymGetModuleInfo64(hProc, BaseAddress, &ModuleInfo);
201
202 return Ret && ModuleInfo.SymType == SymPdb;
203 }
204
205
206 static void test_SymFromName(HANDLE hProc, const char* szModuleName)
207 {
208 BOOL Ret;
209 char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
210 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
211
212 DWORD64 BaseAddress;
213 DWORD dwErr;
214
215 if (!init_sym())
216 return;
217
218 SetLastError(ERROR_SUCCESS);
219 BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0);
220 dwErr = GetLastError();
221
222 ok_ulonglong(BaseAddress, 0x600000);
223 ok_hex(dwErr, ERROR_SUCCESS);
224
225 if (!can_enumerate(hProc, BaseAddress))
226 {
227 skip("dbghelp.dll too old or cannot enumerate symbols!\n");
228 }
229 else
230 {
231 INIT_PSYM(buffer);
232 Ret = SymFromName(hProc, "DllMain", pSymbol);
233 ok_int(Ret, TRUE);
234 ok_ulonglong(pSymbol->ModBase, BaseAddress);
235 ok_hex(pSymbol->Flags, 0);
236 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010);
237 ok_hex(pSymbol->Tag, SymTagFunction);
238 ok_str(pSymbol->Name, "DllMain");
239
240 INIT_PSYM(buffer);
241 Ret = SymFromName(hProc, "_DllMain@12", pSymbol);
242 ok_int(Ret, TRUE);
243 ok_ulonglong(pSymbol->ModBase, BaseAddress);
244 ok_hex(pSymbol->Flags, 0x400000); // ??
245 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010);
246 ok_hex(pSymbol->Tag, SymTagPublicSymbol);
247 ok_str(pSymbol->Name, "_DllMain@12");
248
249 INIT_PSYM(buffer);
250 Ret = SymFromName(hProc, "FfsChkdsk", pSymbol);
251 ok_int(Ret, TRUE);
252 ok_ulonglong(pSymbol->ModBase, BaseAddress);
253 ok_hex(pSymbol->Flags, 0);
254 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1040);
255 ok_hex(pSymbol->Tag, SymTagFunction);
256 ok_str(pSymbol->Name, "FfsChkdsk");
257
258 INIT_PSYM(buffer);
259 Ret = SymFromName(hProc, "_FfsChkdsk@24", pSymbol);
260 ok_int(Ret, TRUE);
261 ok_ulonglong(pSymbol->ModBase, BaseAddress);
262 ok_hex(pSymbol->Flags, 0x400000); // ??
263 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1040);
264 ok_hex(pSymbol->Tag, SymTagPublicSymbol);
265 ok_str(pSymbol->Name, "_FfsChkdsk@24");
266
267 INIT_PSYM(buffer);
268 Ret = SymFromName(hProc, "FfsFormat", pSymbol);
269 ok_int(Ret, TRUE);
270 ok_ulonglong(pSymbol->ModBase, BaseAddress);
271 ok_hex(pSymbol->Flags, 0);
272 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1070);
273 ok_hex(pSymbol->Tag, SymTagFunction);
274 ok_str(pSymbol->Name, "FfsFormat");
275
276 INIT_PSYM(buffer);
277 Ret = SymFromName(hProc, "_FfsFormat@24", pSymbol);
278 ok_int(Ret, TRUE);
279 ok_ulonglong(pSymbol->ModBase, BaseAddress);
280 ok_hex(pSymbol->Flags, 0x400000); // ??
281 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1070);
282 ok_hex(pSymbol->Tag, SymTagPublicSymbol);
283 ok_str(pSymbol->Name, "_FfsFormat@24");
284 }
285
286 deinit_sym();
287 }
288
289 static void test_SymFromAddr(HANDLE hProc, const char* szModuleName)
290 {
291 BOOL Ret;
292 char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
293 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
294
295 DWORD64 BaseAddress, Displacement;
296 DWORD dwErr;
297
298 if (!init_sym())
299 return;
300
301 SetLastError(ERROR_SUCCESS);
302 BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0);
303 dwErr = GetLastError();
304
305 ok_ulonglong(BaseAddress, 0x600000);
306 ok_hex(dwErr, ERROR_SUCCESS);
307
308 /* No address found before load address of module */
309 Displacement = 0;
310 INIT_PSYM(buffer);
311 Ret = SymFromAddr(hProc, BaseAddress -1, &Displacement, pSymbol);
312 dwErr = GetLastError();
313 ok_int(Ret, FALSE);
314 ok_hex(dwErr, ERROR_MOD_NOT_FOUND);
315
316 /* Right at the start of the module is recognized as the first symbol found */
317 Displacement = 0;
318 INIT_PSYM(buffer);
319 Ret = SymFromAddr(hProc, BaseAddress, &Displacement, pSymbol);
320 ok_int(Ret, TRUE);
321 ok_ulonglong(Displacement, 0xffffffffffffffff);
322 ok_ulonglong(pSymbol->ModBase, BaseAddress);
323 ok_hex(pSymbol->Flags, 0);
324 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010);
325 ok_hex(pSymbol->Tag, SymTagFunction);
326 ok_str(pSymbol->Name, "DllMain");
327
328 /* The actual first instruction of the function */
329 Displacement = 0;
330 INIT_PSYM(buffer);
331 Ret = SymFromAddr(hProc, BaseAddress + 0x1010, &Displacement, pSymbol);
332 ok_int(Ret, TRUE);
333 ok_ulonglong(Displacement, 0);
334 ok_ulonglong(pSymbol->ModBase, BaseAddress);
335 ok_hex(pSymbol->Flags, 0);
336 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010);
337 ok_hex(pSymbol->Tag, SymTagFunction);
338 ok_str(pSymbol->Name, "DllMain");
339
340 /* The last instruction in the function */
341 Displacement = 0;
342 INIT_PSYM(buffer);
343 Ret = SymFromAddr(hProc, BaseAddress + 0x102D, &Displacement, pSymbol);
344 ok_int(Ret, TRUE);
345 ok_ulonglong(Displacement, 0x1d);
346 ok_ulonglong(pSymbol->ModBase, BaseAddress);
347 ok_hex(pSymbol->Flags, 0);
348 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010);
349 ok_hex(pSymbol->Tag, SymTagFunction);
350 ok_str(pSymbol->Name, "DllMain");
351
352 /* The padding below the function */
353 Displacement = 0;
354 INIT_PSYM(buffer);
355 Ret = SymFromAddr(hProc, BaseAddress + 0x102E, &Displacement, pSymbol);
356 ok_int(Ret, TRUE);
357 ok_ulonglong(Displacement, 0x1e);
358 ok_ulonglong(pSymbol->ModBase, BaseAddress);
359 ok_hex(pSymbol->Flags, 0);
360 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010);
361 ok_hex(pSymbol->Tag, SymTagFunction);
362 ok_str(pSymbol->Name, "DllMain");
363
364 /* One byte before the next function */
365 Displacement = 0;
366 INIT_PSYM(buffer);
367 Ret = SymFromAddr(hProc, BaseAddress + 0x103f, &Displacement, pSymbol);
368 ok_int(Ret, TRUE);
369 ok_ulonglong(Displacement, 0x2f);
370 ok_ulonglong(pSymbol->ModBase, BaseAddress);
371 ok_hex(pSymbol->Flags, 0);
372 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1010);
373 ok_hex(pSymbol->Tag, SymTagFunction);
374 ok_str(pSymbol->Name, "DllMain");
375
376 /* First byte of the next function */
377 Displacement = 0;
378 INIT_PSYM(buffer);
379 Ret = SymFromAddr(hProc, BaseAddress + 0x1040, &Displacement, pSymbol);
380 ok_int(Ret, TRUE);
381 ok_ulonglong(Displacement, 0);
382 ok_ulonglong(pSymbol->ModBase, BaseAddress);
383 ok_hex(pSymbol->Flags, 0);
384 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1040);
385 ok_hex(pSymbol->Tag, SymTagFunction);
386 ok_str(pSymbol->Name, "FfsChkdsk");
387
388 if (!can_enumerate(hProc, BaseAddress))
389 {
390 skip("dbghelp.dll too old or cannot read this symbol!\n");
391 }
392 else
393 {
394 /* .idata */
395 Displacement = 0;
396 INIT_PSYM(buffer);
397 Ret = SymFromAddr(hProc, BaseAddress + 0x2000, &Displacement, pSymbol);
398 ok_int(Ret, TRUE);
399 ok_ulonglong(Displacement, 0);
400 ok_ulonglong(pSymbol->ModBase, BaseAddress);
401 ok_hex(pSymbol->Flags, 0);
402 ok_ulonglong(pSymbol->Address, BaseAddress + 0x2000);
403 ok_hex(pSymbol->Tag, SymTagPublicSymbol);
404 ok_str(pSymbol->Name, "__imp__DbgPrint");
405 }
406
407 deinit_sym();
408 }
409
410 typedef struct _test_context
411 {
412 DWORD64 BaseAddress;
413 SIZE_T Index;
414 } test_context;
415
416 static struct _test_data {
417 DWORD64 AddressOffset;
418 ULONG Size;
419 ULONG Tag;
420 const char* Name;
421 } test_data[] = {
422 /* TODO: Order is based on magic, should find entries based on name, and mark as 'seen' */
423 { 0x1070, 36, SymTagFunction, "FfsFormat" },
424 { 0x1010, 32, SymTagFunction, "DllMain" },
425 { 0x1040, 36, SymTagFunction, "FfsChkdsk" },
426
427 { 0x2100, 0, SymTagPublicSymbol, "__IMPORT_DESCRIPTOR_ntdll" },
428 { 0x109a, 0, SymTagPublicSymbol, "_DbgPrint" },
429 { 0x2004, 0, SymTagPublicSymbol, "\x7fntdll_NULL_THUNK_DATA" },
430 { 0x2000, 0, SymTagPublicSymbol, "__imp__DbgPrint" },
431 { 0x2114, 0, SymTagPublicSymbol, "__NULL_IMPORT_DESCRIPTOR" },
432 };
433
434 static BOOL CALLBACK EnumSymProc(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext)
435 {
436 test_context* ctx = UserContext;
437
438 if (ctx->Index < ARRAYSIZE(test_data))
439 {
440 ok_ulonglong(pSymInfo->ModBase, ctx->BaseAddress);
441 ok_ulonglong(pSymInfo->Address, ctx->BaseAddress + test_data[ctx->Index].AddressOffset);
442 ok_hex(pSymInfo->Tag, test_data[ctx->Index].Tag);
443 ok_str(pSymInfo->Name, test_data[ctx->Index].Name);
444
445 ctx->Index++;
446 }
447 else
448 {
449 ok(0, "Out of bounds (%lu), max is: %i!\n", ctx->Index, ARRAYSIZE(test_data));
450 }
451
452 return TRUE;
453 }
454
455 static void test_SymEnumSymbols(HANDLE hProc, const char* szModuleName)
456 {
457 BOOL Ret;
458 DWORD dwErr;
459
460 test_context ctx;
461
462 if (!init_sym())
463 return;
464
465 ctx.Index = 0;
466 SetLastError(ERROR_SUCCESS);
467 ctx.BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0);
468 dwErr = GetLastError();
469
470 ok_ulonglong(ctx.BaseAddress, 0x600000);
471 ok_hex(dwErr, ERROR_SUCCESS);
472
473 if (!can_enumerate(hProc, ctx.BaseAddress))
474 {
475 skip("dbghelp.dll too old or cannot enumerate symbols!\n");
476 }
477 else
478 {
479 Ret = SymEnumSymbols(hProc, ctx.BaseAddress, NULL, EnumSymProc, &ctx);
480 ok_int(Ret, TRUE);
481 ok_int(ctx.Index, ARRAYSIZE(test_data));
482 }
483
484 deinit_sym();
485 }
486
487 typedef struct _symregcallback_context
488 {
489 UINT idx;
490 BOOL isANSI;
491 } symregcallback_context;
492
493 static struct _symregcallback_test_data {
494 ULONG ActionCode;
495 const char* Name;
496 } symregcallback_test_data[] = {
497 { CBA_DEFERRED_SYMBOL_LOAD_CANCEL },
498 { CBA_DEFERRED_SYMBOL_LOAD_START },
499 { CBA_READ_MEMORY },
500 { CBA_DEFERRED_SYMBOL_LOAD_PARTIAL },
501 { CBA_DEFERRED_SYMBOL_LOAD_COMPLETE }
502 };
503
504 static BOOL CALLBACK SymRegisterCallback64Proc(
505 HANDLE hProcess,
506 ULONG ActionCode,
507 ULONG64 CallbackData,
508 ULONG64 UserContext)
509 {
510 symregcallback_context *ctx;
511 ctx = (symregcallback_context*)(ULONG_PTR)UserContext;
512
513 if (ctx->idx > sizeof(symregcallback_test_data))
514 {
515 ok(FALSE, "SymRegisterCallback64Proc: Too many calls.\n");
516 }
517 else
518 {
519 ok(ActionCode == symregcallback_test_data[ctx->idx].ActionCode,
520 "ActionCode (idx %u) expected %u, got %u\n",
521 ctx->idx, symregcallback_test_data[ctx->idx].ActionCode, ActionCode);
522 }
523 ctx->idx++;
524
525 return FALSE;
526 }
527
528 static void test_SymRegCallback(HANDLE hProc, const char* szModuleName, BOOL testANSI)
529 {
530 BOOL Ret;
531 DWORD dwErr;
532 ULONG64 BaseAddress;
533 symregcallback_context ctx;
534
535 ctx.idx = 0;
536 ctx.isANSI = testANSI;
537
538 if (!init_sym())
539 return;
540
541 if (testANSI)
542 {
543 Ret = SymRegisterCallback64(hProc, SymRegisterCallback64Proc, (ULONG_PTR)&ctx);
544 }
545 else
546 {
547 // dbghelp fileversion 5.2.3790.3959
548 // SymRegisterCallbackW64 crash only happens on real Windows 2003
549 // Fileversion 5.2.3790.3959 is used in Windows 2003.
550 // In ROS there is no crash.
551 // I could not figure out whats wrong.
552 if ((dbghelpFileVer.dwProductVersionMS == MAKELONG(2, 5)) &&
553 (dbghelpFileVer.dwProductVersionLS == MAKELONG(3959, 3790)))
554 {
555 skip("dbghelp.dll ver 5.2.3790.3959 (w2k3), SymRegisterCallbackW64 would crash!\n");
556 return;
557 }
558 Ret = SymRegisterCallbackW64(hProc, SymRegisterCallback64Proc, (ULONG_PTR)&ctx);
559 }
560
561 ok_int(Ret, TRUE);
562 if (!Ret)
563 return;
564
565 SetLastError(ERROR_SUCCESS);
566 BaseAddress = SymLoadModule64(hProc, NULL, szModuleName, NULL, 0x600000, 0);
567 dwErr = GetLastError();
568
569 ok_ulonglong(BaseAddress, 0x600000);
570 ok_hex(dwErr, ERROR_SUCCESS);
571
572 /* this is what we want to test ... we expect 5 calls */
573 ok_int(ctx.idx, 5);
574
575 deinit_sym();
576 }
577
578 START_TEST(pdb)
579 {
580 char szDllName[MAX_PATH];
581 //create_compressed_files();
582
583 DWORD Options = SymGetOptions();
584 Options &= ~(SYMOPT_UNDNAME);
585 //Options |= SYMOPT_DEBUG;
586 SymSetOptions(Options);
587
588 if (!extract_msvc_exe(szDllName))
589 {
590 ok(0, "Failed extracting files\n");
591 return;
592 }
593
594 init_dbghelp_version();
595
596 test_SymFromName(proc(), szDllName);
597 test_SymFromAddr(proc(), szDllName);
598 test_SymEnumSymbols(proc(), szDllName);
599 test_SymRegCallback(proc(), szDllName, TRUE);
600 test_SymRegCallback(proc(), szDllName, FALSE);
601
602 cleanup_msvc_exe();
603
604 }