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