1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Test for dbghelp rsym functions
5 * COPYRIGHT: Copyright 2017-2019 Mark Jansen (mark.jansen@reactos.org)
6 *
7 * These tests are based on the PDB tests.
8 */
9
10 #include <ntstatus.h>
11 #define WIN32_NO_STATUS
12 #include <windows.h>
13 #include <dbghelp.h>
14 #include <cvconst.h> // SymTagXXX
15 #include <stdio.h>
16
17 #include "wine/test.h"
18
19 #define ok_ulonglong(expression, result) \
20 do { \
21 ULONG64 _value = (expression); \
22 ULONG64 _result = (result); \
23 ok(_value == _result, "Wrong value for '%s', expected: " #result " (%s), got: %s\n", \
24 #expression, wine_dbgstr_longlong(_result), wine_dbgstr_longlong(_value)); \
25 } while (0)
26
27 #define ok_ulonglong_(file, line, expression, result) \
28 do { \
29 ULONG64 _value = (expression); \
30 ULONG64 _result = (result); \
31 ok_(file, line)(_value == _result, "Wrong value for '%s', expected: " #result " (%s), got: %s\n", \
32 #expression, wine_dbgstr_longlong(_result), wine_dbgstr_longlong(_value)); \
33 } while (0)
34
35
36 // data.c
37 void dump_rsym(const char* filename);
38 int extract_gcc_dll(char szFile[MAX_PATH]);
39 void cleanup_gcc_dll();
40
proc()41 static HANDLE proc()
42 {
43 return GetCurrentProcess();
44 }
45
init_sym_imp(BOOL fInvadeProcess,const char * file,int line)46 static BOOL init_sym_imp(BOOL fInvadeProcess, const char* file, int line)
47 {
48 if (!SymInitialize(proc(), NULL, fInvadeProcess))
49 {
50 DWORD err = GetLastError();
51 ok_(file, line)(0, "Failed to init: 0x%x\n", err);
52 return FALSE;
53 }
54 return TRUE;
55 }
56
deinit_sym()57 static void deinit_sym()
58 {
59 SymCleanup(proc());
60 }
61
62 #define init_sym(fInvadeProcess) init_sym_imp(fInvadeProcess, __FILE__, __LINE__)
63
64 #define INIT_PSYM(buff) do { \
65 memset((buff), 0, sizeof((buff))); \
66 ((PSYMBOL_INFO)(buff))->SizeOfStruct = sizeof(SYMBOL_INFO); \
67 ((PSYMBOL_INFO)(buff))->MaxNameLen = MAX_SYM_NAME; \
68 } while (0)
69
supports_rsym(HANDLE hProc,DWORD64 BaseAddress)70 static BOOL supports_rsym(HANDLE hProc, DWORD64 BaseAddress)
71 {
72 IMAGEHLP_MODULE64 ModuleInfo;
73 BOOL Ret;
74
75 memset(&ModuleInfo, 0, sizeof(ModuleInfo));
76 ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
77 Ret = SymGetModuleInfo64(hProc, BaseAddress, &ModuleInfo);
78
79 return Ret &&
80 ModuleInfo.SymType == SymDia &&
81 ModuleInfo.CVSig == ('R' | ('S' << 8) | ('Y' << 16) | ('M' << 24));
82 }
83
84
test_SymFromName(HANDLE hProc,DWORD64 BaseAddress)85 static void test_SymFromName(HANDLE hProc, DWORD64 BaseAddress)
86 {
87 BOOL Ret;
88 char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
89 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
90
91 if (!supports_rsym(hProc, BaseAddress))
92 {
93 skip("dbghelp.dll cannot parse rsym\n");
94 }
95 else
96 {
97 INIT_PSYM(buffer);
98 Ret = SymFromName(hProc, "DllMain", pSymbol);
99 ok_int(Ret, TRUE);
100 ok_ulonglong(pSymbol->ModBase, BaseAddress);
101 ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION);
102 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1000);
103 ok_hex(pSymbol->Tag, SymTagFunction);
104 ok_str(pSymbol->Name, "DllMain");
105
106 INIT_PSYM(buffer);
107 Ret = SymFromName(hProc, "FfsChkdsk", pSymbol);
108 ok_int(Ret, TRUE);
109 ok_ulonglong(pSymbol->ModBase, BaseAddress);
110 ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION);
111 ok_ulonglong(pSymbol->Address, BaseAddress + 0x103F);
112 ok_hex(pSymbol->Tag, SymTagFunction);
113 ok_str(pSymbol->Name, "FfsChkdsk");
114
115 INIT_PSYM(buffer);
116 Ret = SymFromName(hProc, "FfsFormat", pSymbol);
117 ok_int(Ret, TRUE);
118 ok_ulonglong(pSymbol->ModBase, BaseAddress);
119 ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION);
120 ok_ulonglong(pSymbol->Address, BaseAddress + 0x100C);
121 ok_hex(pSymbol->Tag, SymTagFunction);
122 ok_str(pSymbol->Name, "FfsFormat");
123 }
124 }
125
test_SymFromAddr(HANDLE hProc,DWORD64 BaseAddress)126 static void test_SymFromAddr(HANDLE hProc, DWORD64 BaseAddress)
127 {
128 BOOL Ret;
129 char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
130 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
131
132 DWORD64 Displacement;
133 DWORD dwErr;
134
135 if (!supports_rsym(hProc, BaseAddress))
136 {
137 skip("dbghelp.dll cannot parse rsym\n");
138 }
139 else
140 {
141 /* No address found before load address of module */
142 Displacement = 0;
143 INIT_PSYM(buffer);
144 Ret = SymFromAddr(hProc, BaseAddress -1, &Displacement, pSymbol);
145 dwErr = GetLastError();
146 ok_int(Ret, FALSE);
147 ok_hex(dwErr, ERROR_MOD_NOT_FOUND);
148
149 /* Right at the start of the module is recognized as the first symbol found */
150 Displacement = 0;
151 INIT_PSYM(buffer);
152 Ret = SymFromAddr(hProc, BaseAddress, &Displacement, pSymbol);
153 /* Our dbghelp.dll does not recognize this yet */
154 todo_if(!Ret)
155 {
156 ok_int(Ret, TRUE);
157 ok_ulonglong(Displacement, 0xffffffffffffffff);
158 ok_ulonglong(pSymbol->ModBase, BaseAddress);
159 ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION);
160 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1000);
161 ok_hex(pSymbol->Tag, SymTagFunction);
162 ok_str(pSymbol->Name, "DllMain");
163 }
164
165 /* The actual first instruction of the function */
166 Displacement = 0;
167 INIT_PSYM(buffer);
168 Ret = SymFromAddr(hProc, BaseAddress + 0x1000, &Displacement, pSymbol);
169 ok_int(Ret, TRUE);
170 ok_ulonglong(Displacement, 0);
171 ok_ulonglong(pSymbol->ModBase, BaseAddress);
172 ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION);
173 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1000);
174 ok_hex(pSymbol->Tag, SymTagFunction);
175 ok_str(pSymbol->Name, "DllMain");
176
177 /* The last instruction in the function */
178 Displacement = 0;
179 INIT_PSYM(buffer);
180 Ret = SymFromAddr(hProc, BaseAddress + 0x1009, &Displacement, pSymbol);
181 ok_int(Ret, TRUE);
182 ok_ulonglong(Displacement, 0x9);
183 ok_ulonglong(pSymbol->ModBase, BaseAddress);
184 ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION);
185 ok_ulonglong(pSymbol->Address, BaseAddress + 0x1000);
186 ok_hex(pSymbol->Tag, SymTagFunction);
187 ok_str(pSymbol->Name, "DllMain");
188
189 /* First byte of the next function */
190 Displacement = 0;
191 INIT_PSYM(buffer);
192 Ret = SymFromAddr(hProc, BaseAddress + 0x103F, &Displacement, pSymbol);
193 ok_int(Ret, TRUE);
194 ok_ulonglong(Displacement, 0);
195 ok_ulonglong(pSymbol->ModBase, BaseAddress);
196 ok_hex(pSymbol->Flags, SYMFLAG_FUNCTION);
197 ok_ulonglong(pSymbol->Address, BaseAddress + 0x103F);
198 ok_hex(pSymbol->Tag, SymTagFunction);
199 ok_str(pSymbol->Name, "FfsChkdsk");
200
201 /* .idata */
202 Displacement = 0;
203 INIT_PSYM(buffer);
204 Ret = SymFromAddr(hProc, BaseAddress + 0x4000, &Displacement, pSymbol);
205 ok_int(Ret, TRUE);
206 ok_ulonglong(Displacement, 0);
207 ok_ulonglong(pSymbol->ModBase, BaseAddress);
208 ok_hex(pSymbol->Flags, SYMFLAG_EXPORT);
209 ok_ulonglong(pSymbol->Address, BaseAddress + 0x4000);
210 ok_hex(pSymbol->Tag, SymTagPublicSymbol);
211 ok_str(pSymbol->Name, "_head_dll_ntdll_libntdll_a");
212 }
213 }
214
215 typedef struct _test_context
216 {
217 DWORD64 BaseAddress;
218 SIZE_T Index;
219 } test_context;
220
221 static struct _test_data {
222 DWORD64 AddressOffset;
223 ULONG Size;
224 ULONG Tag;
225 const char* Name;
226 int Line;
227 } test_data[] = {
228
229 /* TODO: Order is based on magic, should find entries based on name, and mark as 'seen' */
230 { 0x107c, 0, SymTagPublicSymbol, "__CTOR_LIST__", __LINE__ },
231 { 0x2074, 0, SymTagPublicSymbol, "__RUNTIME_PSEUDO_RELOC_LIST_END__", __LINE__ },
232 { 0x1000, 12, SymTagPublicSymbol, "EntryPoint", __LINE__ },
233 { 0x100c, 51, SymTagFunction, "FfsFormat", __LINE__ },
234 { 0x4030, 0, SymTagPublicSymbol, "_imp__DbgPrint", __LINE__ },
235 { 0x1084, 0, SymTagPublicSymbol, "__DTOR_LIST__", __LINE__ },
236 { 0x103f, 53, SymTagFunction, "FfsChkdsk", __LINE__ },
237 { 0x2074, 0, SymTagPublicSymbol, "_rt_psrelocs_end", __LINE__ },
238 { 0x103f, 53, SymTagPublicSymbol, "ChkdskEx", __LINE__ },
239 { 0x4048, 0, SymTagPublicSymbol, "_dll_ntdll_libntdll_a_iname", __LINE__ },
240
241
242
243 { 0x2074, 0, SymTagPublicSymbol, "_rt_psrelocs_start", __LINE__ },
244 { 0x1000, 12, SymTagFunction, "DllMain", __LINE__ },
245 { 0x100c, 0, SymTagPublicSymbol, "FormatEx", __LINE__ },
246 { 0x1074, 0, SymTagPublicSymbol, "DbgPrint", __LINE__ },
247 { 0x68900000, 0, SymTagPublicSymbol, "__ImageBase", __LINE__ },
248 { 0x68902074, 0, SymTagPublicSymbol, "__RUNTIME_PSEUDO_RELOC_LIST__", __LINE__ },
249 { 0x4000, 0, SymTagPublicSymbol, "_head_dll_ntdll_libntdll_a", __LINE__ },
250 };
251
EnumSymProc(PSYMBOL_INFO pSymInfo,ULONG SymbolSize,PVOID UserContext)252 static BOOL CALLBACK EnumSymProc(PSYMBOL_INFO pSymInfo, ULONG SymbolSize, PVOID UserContext)
253 {
254 test_context* ctx = UserContext;
255
256 if (ctx->Index < ARRAYSIZE(test_data))
257 {
258 ok_ulonglong_(__FILE__, test_data[ctx->Index].Line, pSymInfo->ModBase, ctx->BaseAddress);
259 if (test_data[ctx->Index].AddressOffset > 0x100000)
260 ok_ulonglong_(__FILE__, test_data[ctx->Index].Line, pSymInfo->Address, test_data[ctx->Index].AddressOffset);
261 else
262 ok_ulonglong_(__FILE__, test_data[ctx->Index].Line, pSymInfo->Address, ctx->BaseAddress + test_data[ctx->Index].AddressOffset);
263 ok_hex_(__FILE__, test_data[ctx->Index].Line, pSymInfo->Tag, test_data[ctx->Index].Tag);
264 ok_str_(__FILE__, test_data[ctx->Index].Line, pSymInfo->Name, test_data[ctx->Index].Name);
265
266 ctx->Index++;
267 }
268 else
269 {
270 ok(0, "Out of bounds (%lu), max is: %i!\n", ctx->Index, ARRAYSIZE(test_data));
271 }
272
273 return TRUE;
274 }
275
test_SymEnumSymbols(HANDLE hProc,DWORD64 BaseAddress)276 static void test_SymEnumSymbols(HANDLE hProc, DWORD64 BaseAddress)
277 {
278 BOOL Ret;
279 test_context ctx;
280
281 ctx.Index = 0;
282 ctx.BaseAddress = BaseAddress;
283
284 if (!supports_rsym(hProc, ctx.BaseAddress))
285 {
286 skip("dbghelp.dll cannot parse rsym\n");
287 }
288 else
289 {
290 Ret = SymEnumSymbols(hProc, ctx.BaseAddress, NULL, EnumSymProc, &ctx);
291 ok_int(Ret, TRUE);
292 ok_int(ctx.Index, ARRAYSIZE(test_data));
293 }
294 }
295
296
START_TEST(rsym)297 START_TEST(rsym)
298 {
299 char szDllName[MAX_PATH];
300 #ifdef _M_IX86
301 HMODULE hMod;
302 #endif
303 DWORD64 BaseAddress;
304 DWORD dwErr, Options;
305
306 Options = SymGetOptions();
307 Options &= ~(SYMOPT_UNDNAME);
308 //Options |= SYMOPT_DEBUG;
309 SymSetOptions(Options);
310
311 if (!extract_gcc_dll(szDllName))
312 {
313 ok(0, "Failed extracting files\n");
314 return;
315 }
316
317 if (init_sym(FALSE))
318 {
319 SetLastError(ERROR_SUCCESS);
320 BaseAddress = SymLoadModule64(proc(), NULL, szDllName, NULL, 0x600000, 0);
321 dwErr = GetLastError();
322
323 ok_ulonglong(BaseAddress, 0x600000);
324 ok(dwErr == ERROR_SUCCESS || dwErr == ERROR_FILE_NOT_FOUND, "Got 0x%x\n", dwErr);
325
326 if (BaseAddress == 0x600000)
327 {
328 trace("Module loaded by SymLoadModule64\n");
329 test_SymFromName(proc(), BaseAddress);
330 test_SymFromAddr(proc(), BaseAddress);
331 test_SymEnumSymbols(proc(), BaseAddress);
332 }
333
334 deinit_sym();
335 }
336
337 #ifdef _M_IX86
338 hMod = LoadLibraryA(szDllName);
339 if (hMod)
340 {
341 BaseAddress = (DWORD64)(DWORD_PTR)hMod;
342 /* Invade process */
343 if (init_sym(TRUE))
344 {
345 trace("Module loaded by LoadLibraryA\n");
346 test_SymFromName(proc(), BaseAddress);
347 test_SymFromAddr(proc(), BaseAddress);
348 test_SymEnumSymbols(proc(), BaseAddress);
349
350 deinit_sym();
351 }
352
353 FreeLibrary(hMod);
354 }
355 #endif
356 cleanup_gcc_dll();
357 }
358