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