xref: /reactos/dll/win32/dbghelp/dbghelp.c (revision 4601d948)
1 /*
2  * File dbghelp.c - generic routines (process) for dbghelp DLL
3  *
4  * Copyright (C) 2004, Eric Pouech
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #ifndef DBGHELP_STATIC_LIB
22 
23 #include <unistd.h>
24 #include "dbghelp_private.h"
25 #include "winternl.h"
26 #include "winerror.h"
27 #include "psapi.h"
28 #include "wine/debug.h"
29 #include "wdbgexts.h"
30 #include "winnls.h"
31 #else
32 #include "dbghelp_private.h"
33 #include "wdbgexts.h"
34 #endif
35 
36 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
37 
38 /* TODO
39  *  - support for symbols' types is still partly missing
40  *      + C++ support
41  *      + we should store the underlying type for an enum in the symt_enum struct
42  *      + for enums, we store the names & values (associated to the enum type),
43  *        but those values are not directly usable from a debugger (that's why, I
44  *        assume, that we have also to define constants for enum values, as
45  *        Codeview does BTW.
46  *      + SymEnumTypes should only return *user* defined types (UDT, typedefs...) not
47  *        all the types stored/used in the modules (like char*)
48  *  - SymGetLine{Next|Prev} don't work as expected (they don't seem to work across
49  *    functions, and even across function blocks...). Basically, for *Next* to work
50  *    it requires an address after the prolog of the func (the base address of the
51  *    func doesn't work)
52  *  - most options (dbghelp_options) are not used (loading lines...)
53  *  - in symbol lookup by name, we don't use RE everywhere we should. Moreover, when
54  *    we're supposed to use RE, it doesn't make use of our hash tables. Therefore,
55  *    we could use hash if name isn't a RE, and fall back to a full search when we
56  *    get a full RE
57  *  - msc:
58  *      + we should add parameters' types to the function's signature
59  *        while processing a function's parameters
60  *      + add support for function-less labels (as MSC seems to define them)
61  *      + C++ management
62  *  - stabs:
63  *      + when, in a same module, the same definition is used in several compilation
64  *        units, we get several definitions of the same object (especially
65  *        struct/union). we should find a way not to duplicate them
66  *      + in some cases (dlls/user/dialog16.c DIALOG_GetControl16), the same static
67  *        global variable is defined several times (at different scopes). We are
68  *        getting several of those while looking for a unique symbol. Part of the
69  *        issue is that we don't give a scope to a static variable inside a function
70  *      + C++ management
71  */
72 
73 unsigned   dbghelp_options = SYMOPT_UNDNAME;
74 BOOL       dbghelp_opt_native = FALSE;
75 #ifndef DBGHELP_STATIC_LIB
76 SYSTEM_INFO sysinfo;
77 #endif
78 
79 static struct process* process_first /* = NULL */;
80 
81 #ifndef DBGHELP_STATIC_LIB
DllMain(HINSTANCE instance,DWORD reason,LPVOID reserved)82 BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
83 {
84     switch (reason)
85     {
86     case DLL_PROCESS_ATTACH:
87         GetSystemInfo(&sysinfo);
88         DisableThreadLibraryCalls(instance);
89         break;
90     }
91     return TRUE;
92 }
93 #endif
94 
95 /******************************************************************
96  *		process_find_by_handle
97  *
98  */
process_find_by_handle(HANDLE hProcess)99 struct process*    process_find_by_handle(HANDLE hProcess)
100 {
101     struct process* p;
102 
103     for (p = process_first; p && p->handle != hProcess; p = p->next);
104     if (!p) SetLastError(ERROR_INVALID_HANDLE);
105     return p;
106 }
107 
108 /******************************************************************
109  *             validate_addr64 (internal)
110  *
111  */
validate_addr64(DWORD64 addr)112 BOOL validate_addr64(DWORD64 addr)
113 {
114     if (sizeof(void*) == sizeof(int) && (addr >> 32))
115     {
116         FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
117         SetLastError(ERROR_INVALID_PARAMETER);
118         return FALSE;
119     }
120     return TRUE;
121 }
122 
123 /******************************************************************
124  *		fetch_buffer
125  *
126  * Ensures process' internal buffer is large enough.
127  */
fetch_buffer(struct process * pcs,unsigned size)128 void* fetch_buffer(struct process* pcs, unsigned size)
129 {
130     if (size > pcs->buffer_size)
131     {
132         if (pcs->buffer)
133             pcs->buffer = HeapReAlloc(GetProcessHeap(), 0, pcs->buffer, size);
134         else
135             pcs->buffer = HeapAlloc(GetProcessHeap(), 0, size);
136         pcs->buffer_size = (pcs->buffer) ? size : 0;
137     }
138     return pcs->buffer;
139 }
140 
141 #ifndef DBGHELP_STATIC_LIB
wine_dbgstr_addr(const ADDRESS64 * addr)142 const char* wine_dbgstr_addr(const ADDRESS64* addr)
143 {
144     if (!addr) return "(null)";
145     switch (addr->Mode)
146     {
147     case AddrModeFlat:
148         return wine_dbg_sprintf("flat<%s>", wine_dbgstr_longlong(addr->Offset));
149     case AddrMode1616:
150         return wine_dbg_sprintf("1616<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
151     case AddrMode1632:
152         return wine_dbg_sprintf("1632<%04x:%08x>", addr->Segment, (DWORD)addr->Offset);
153     case AddrModeReal:
154         return wine_dbg_sprintf("real<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
155     default:
156         return "unknown";
157     }
158 }
159 #endif
160 
161 extern struct cpu       cpu_i386, cpu_x86_64, cpu_arm, cpu_arm64;
162 
163 #ifndef DBGHELP_STATIC_LIB
164 static struct cpu*      dbghelp_cpus[] = {&cpu_i386, &cpu_x86_64, &cpu_arm, &cpu_arm64, NULL};
165 #else
166 static struct cpu*      dbghelp_cpus[] = {&cpu_i386, NULL};
167 #endif
168 
169 struct cpu*             dbghelp_current_cpu =
170 #if defined(__i386__) || defined(DBGHELP_STATIC_LIB)
171     &cpu_i386
172 #elif defined(__x86_64__)
173     &cpu_x86_64
174 #elif defined(__arm__)
175     &cpu_arm
176 #elif defined(__aarch64__)
177     &cpu_arm64
178 #else
179 #error define support for your CPU
180 #endif
181     ;
182 
cpu_find(DWORD machine)183 struct cpu* cpu_find(DWORD machine)
184 {
185     struct cpu** cpu;
186 
187     for (cpu = dbghelp_cpus ; *cpu; cpu++)
188     {
189         if (cpu[0]->machine == machine) return cpu[0];
190     }
191     return NULL;
192 }
193 
make_default_search_path(void)194 static WCHAR* make_default_search_path(void)
195 {
196     WCHAR*      search_path;
197     WCHAR*      p;
198     unsigned    sym_path_len;
199     unsigned    alt_sym_path_len;
200 
201     sym_path_len = GetEnvironmentVariableW(L"_NT_SYMBOL_PATH", NULL, 0);
202     alt_sym_path_len = GetEnvironmentVariableW(L"_NT_ALT_SYMBOL_PATH", NULL, 0);
203 
204     /* The default symbol path is ".[;%_NT_SYMBOL_PATH%][;%_NT_ALT_SYMBOL_PATH%]".
205      * If the variables exist, the lengths include a null-terminator. We use that
206      * space for the semicolons, and only add the initial dot and the final null. */
207     search_path = HeapAlloc(GetProcessHeap(), 0,
208                             (1 + sym_path_len + alt_sym_path_len + 1) * sizeof(WCHAR));
209     if (!search_path) return NULL;
210 
211     p = search_path;
212     *p++ = L'.';
213     if (sym_path_len)
214     {
215         *p++ = L';';
216         GetEnvironmentVariableW(L"_NT_SYMBOL_PATH", p, sym_path_len);
217         p += sym_path_len - 1;
218     }
219 
220     if (alt_sym_path_len)
221     {
222         *p++ = L';';
223         GetEnvironmentVariableW(L"_NT_ALT_SYMBOL_PATH", p, alt_sym_path_len);
224         p += alt_sym_path_len - 1;
225     }
226     *p = L'\0';
227 
228     return search_path;
229 }
230 
231 /******************************************************************
232  *		SymSetSearchPathW (DBGHELP.@)
233  *
234  */
SymSetSearchPathW(HANDLE hProcess,PCWSTR searchPath)235 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
236 {
237     struct process* pcs = process_find_by_handle(hProcess);
238     WCHAR*          search_path_buffer;
239 
240     if (!pcs) return FALSE;
241 
242     if (searchPath)
243     {
244         search_path_buffer = HeapAlloc(GetProcessHeap(), 0,
245                                        (lstrlenW(searchPath) + 1) * sizeof(WCHAR));
246         if (!search_path_buffer) return FALSE;
247         lstrcpyW(search_path_buffer, searchPath);
248     }
249     else
250     {
251         search_path_buffer = make_default_search_path();
252         if (!search_path_buffer) return FALSE;
253     }
254     HeapFree(GetProcessHeap(), 0, pcs->search_path);
255     pcs->search_path = search_path_buffer;
256     return TRUE;
257 }
258 
259 /******************************************************************
260  *		SymSetSearchPath (DBGHELP.@)
261  *
262  */
SymSetSearchPath(HANDLE hProcess,PCSTR searchPath)263 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
264 {
265     BOOL        ret = FALSE;
266     unsigned    len;
267     WCHAR*      sp = NULL;
268 
269     if (searchPath)
270     {
271         len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
272         sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
273         if (!sp) return FALSE;
274         MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
275     }
276 
277     ret = SymSetSearchPathW(hProcess, sp);
278 
279     HeapFree(GetProcessHeap(), 0, sp);
280     return ret;
281 }
282 
283 /***********************************************************************
284  *		SymGetSearchPathW (DBGHELP.@)
285  */
SymGetSearchPathW(HANDLE hProcess,PWSTR szSearchPath,DWORD SearchPathLength)286 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
287                               DWORD SearchPathLength)
288 {
289     struct process* pcs = process_find_by_handle(hProcess);
290     if (!pcs) return FALSE;
291 
292     lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
293     return TRUE;
294 }
295 
296 /***********************************************************************
297  *		SymGetSearchPath (DBGHELP.@)
298  */
SymGetSearchPath(HANDLE hProcess,PSTR szSearchPath,DWORD SearchPathLength)299 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
300                              DWORD SearchPathLength)
301 {
302     WCHAR*      buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
303     BOOL        ret = FALSE;
304 
305     if (buffer)
306     {
307         ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
308         if (ret)
309             WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
310                                 szSearchPath, SearchPathLength, NULL, NULL);
311         HeapFree(GetProcessHeap(), 0, buffer);
312     }
313     return ret;
314 }
315 
316 #ifndef DBGHELP_STATIC_LIB
317 /******************************************************************
318  *		invade_process
319  *
320  * SymInitialize helper: loads in dbghelp all known (and loaded modules)
321  * this assumes that hProcess is a handle on a valid process
322  */
process_invade_cb(PCWSTR name,ULONG64 base,ULONG size,PVOID user)323 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
324 {
325     WCHAR       tmp[MAX_PATH];
326     HANDLE      hProcess = user;
327 
328     if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base, tmp, ARRAY_SIZE(tmp)))
329         lstrcpynW(tmp, name, ARRAY_SIZE(tmp));
330 
331     SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
332     return TRUE;
333 }
334 
process_getenv(const struct process * process,const WCHAR * name)335 const WCHAR *process_getenv(const struct process *process, const WCHAR *name)
336 {
337     size_t name_len;
338     const WCHAR *iter;
339 
340     if (!process->environment) return NULL;
341     name_len = lstrlenW(name);
342 
343     for (iter = process->environment; *iter; iter += lstrlenW(iter) + 1)
344     {
345         if (!wcsnicmp(iter, name, name_len) && iter[name_len] == '=')
346             return iter + name_len + 1;
347     }
348 
349     return NULL;
350 }
351 
352 /******************************************************************
353  *		check_live_target
354  *
355  */
check_live_target(struct process * pcs)356 static BOOL check_live_target(struct process* pcs)
357 {
358     PROCESS_BASIC_INFORMATION pbi;
359     ULONG_PTR base = 0, env = 0;
360 
361     if (!GetProcessId(pcs->handle)) return FALSE;
362     if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
363 
364     if (NtQueryInformationProcess( pcs->handle, ProcessBasicInformation,
365                                    &pbi, sizeof(pbi), NULL ))
366         return FALSE;
367 
368     if (!pcs->is_64bit)
369     {
370         DWORD env32;
371         PEB32 peb32;
372         C_ASSERT(sizeof(void*) != 4 || FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Environment) == 0x48);
373         if (!ReadProcessMemory(pcs->handle, pbi.PebBaseAddress, &peb32, sizeof(peb32), NULL)) return FALSE;
374         base = peb32.Reserved[0];
375         if (read_process_memory(pcs, peb32.ProcessParameters + 0x48, &env32, sizeof(env32))) env = env32;
376     }
377     else
378     {
379         PEB peb;
380         if (!ReadProcessMemory(pcs->handle, pbi.PebBaseAddress, &peb, sizeof(peb), NULL)) return FALSE;
381         base = peb.Reserved[0];
382         ReadProcessMemory(pcs->handle, &peb.ProcessParameters->Environment, &env, sizeof(env), NULL);
383     }
384 
385 #ifdef __REACTOS__
386     /* Wine store their loader base address in peb.reserved[0] and load its symbol from there.
387      * ReactOS does not care about it, we are just happy if we managed to read the value */
388     base = 1;
389 #endif
390 
391     /* read debuggee environment block */
392     if (env)
393     {
394         size_t buf_size = 0, i, last_null = -1;
395         WCHAR *buf = NULL;
396 
397         do
398         {
399             size_t read_size = sysinfo.dwAllocationGranularity - (env & (sysinfo.dwAllocationGranularity - 1));
400             if (buf)
401             {
402                 WCHAR *new_buf;
403                 if (!(new_buf = realloc(buf, buf_size + read_size))) break;
404                 buf = new_buf;
405             }
406             else if(!(buf = malloc(read_size))) break;
407 
408             if (!read_process_memory(pcs, env, (char*)buf + buf_size, read_size)) break;
409             for (i = buf_size / sizeof(WCHAR); i < (buf_size + read_size) / sizeof(WCHAR); i++)
410             {
411                 if (buf[i]) continue;
412                 if (last_null + 1 == i)
413                 {
414                     pcs->environment = realloc(buf, (i + 1) * sizeof(WCHAR));
415                     buf = NULL;
416                     break;
417                 }
418                 last_null = i;
419             }
420             env += read_size;
421             buf_size += read_size;
422         }
423         while (buf);
424         free(buf);
425     }
426 
427     if (!base) return FALSE;
428 
429     TRACE("got debug info address %#lx from PEB %p\n", base, pbi.PebBaseAddress);
430 #ifndef __REACTOS__
431     if (!elf_read_wine_loader_dbg_info(pcs, base) && !macho_read_wine_loader_dbg_info(pcs, base))
432         WARN("couldn't load process debug info at %#lx\n", base);
433 #endif
434     return TRUE;
435 }
436 #endif
437 
438 /******************************************************************
439  *		SymInitializeW (DBGHELP.@)
440  *
441  * The initialisation of a dbghelp's context.
442  * Note that hProcess doesn't need to be a valid process handle (except
443  * when fInvadeProcess is TRUE).
444  * Since we also allow loading ELF (pure) libraries and Wine ELF libraries
445  * containing PE (and NE) module(s), here's how we handle it:
446  * - we load every module (ELF, NE, PE) passed in SymLoadModule
447  * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
448  *   synchronization: hProcess should be a valid process handle, and we hook
449  *   ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
450  *   our internal ELF modules representation (loading / unloading). This way,
451  *   we'll pair every loaded builtin PE module with its ELF counterpart (and
452  *   access its debug information).
453  * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
454  *   hProcess refers to a running process. We use some heuristics here, so YMMV.
455  *   If we detect a live target, then we get the same handling as if
456  *   fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
457  *   we won't be able to make the peering between a builtin PE module and its ELF
458  *   counterpart. Hence we won't be able to provide the requested debug
459  *   information. We'll however be able to load native PE modules (and their
460  *   debug information) without any trouble.
461  * Note also that this scheme can be intertwined with the deferred loading
462  * mechanism (ie only load the debug information when we actually need it).
463  */
SymInitializeW(HANDLE hProcess,PCWSTR UserSearchPath,BOOL fInvadeProcess)464 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
465 {
466     struct process*     pcs;
467     BOOL wow64, child_wow64;
468 
469     TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
470 
471     if (process_find_by_handle(hProcess))
472     {
473         WARN("the symbols for this process have already been initialized!\n");
474 
475         /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
476            It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
477            Native still returns TRUE even if the process has already been initialized. */
478         return TRUE;
479     }
480 
481     IsWow64Process(GetCurrentProcess(), &wow64);
482 
483     if (GetProcessId(hProcess) && !IsWow64Process(hProcess, &child_wow64))
484         return FALSE;
485 
486     pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
487     if (!pcs) return FALSE;
488 
489     pcs->handle = hProcess;
490     pcs->is_64bit = (sizeof(void *) == 8 || wow64) && !child_wow64;
491     pcs->loader = &no_loader_ops; /* platform-specific initialization will override it if loader debug info can be found */
492 
493     if (UserSearchPath)
494     {
495         pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
496                                               (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
497                                     UserSearchPath);
498     }
499     else
500     {
501         pcs->search_path = make_default_search_path();
502     }
503 
504     pcs->lmodules = NULL;
505     pcs->dbg_hdr_addr = 0;
506     pcs->next = process_first;
507     process_first = pcs;
508 
509 #ifndef DBGHELP_STATIC_LIB
510     if (check_live_target(pcs))
511     {
512         if (fInvadeProcess)
513             EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
514         if (pcs->loader) pcs->loader->synchronize_module_list(pcs);
515     }
516     else if (fInvadeProcess)
517 #else
518     if (fInvadeProcess)
519 #endif
520     {
521         SymCleanup(hProcess);
522         SetLastError(ERROR_INVALID_PARAMETER);
523         return FALSE;
524     }
525 
526     return TRUE;
527 }
528 
529 /******************************************************************
530  *		SymInitialize (DBGHELP.@)
531  *
532  *
533  */
SymInitialize(HANDLE hProcess,PCSTR UserSearchPath,BOOL fInvadeProcess)534 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
535 {
536     WCHAR*              sp = NULL;
537     BOOL                ret;
538 
539     if (UserSearchPath)
540     {
541         unsigned len;
542 
543         len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
544         sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
545         MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
546     }
547 
548     ret = SymInitializeW(hProcess, sp, fInvadeProcess);
549     HeapFree(GetProcessHeap(), 0, sp);
550     return ret;
551 }
552 
553 /******************************************************************
554  *		SymCleanup (DBGHELP.@)
555  *
556  */
SymCleanup(HANDLE hProcess)557 BOOL WINAPI SymCleanup(HANDLE hProcess)
558 {
559     struct process**    ppcs;
560     struct process*     next;
561 
562     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
563     {
564         if ((*ppcs)->handle == hProcess)
565         {
566             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
567 
568             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
569             free((*ppcs)->environment);
570             next = (*ppcs)->next;
571             HeapFree(GetProcessHeap(), 0, *ppcs);
572             *ppcs = next;
573             return TRUE;
574         }
575     }
576 
577     ERR("this process has not had SymInitialize() called for it!\n");
578     return FALSE;
579 }
580 
581 /******************************************************************
582  *		SymSetOptions (DBGHELP.@)
583  *
584  */
SymSetOptions(DWORD opts)585 DWORD WINAPI SymSetOptions(DWORD opts)
586 {
587     struct process* pcs;
588 
589     for (pcs = process_first; pcs; pcs = pcs->next)
590     {
591         pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
592     }
593     return dbghelp_options = opts;
594 }
595 
596 /******************************************************************
597  *		SymGetOptions (DBGHELP.@)
598  *
599  */
SymGetOptions(void)600 DWORD WINAPI SymGetOptions(void)
601 {
602     return dbghelp_options;
603 }
604 
605 /******************************************************************
606  *		SymSetExtendedOption (DBGHELP.@)
607  *
608  */
SymSetExtendedOption(IMAGEHLP_EXTENDED_OPTIONS option,BOOL value)609 BOOL WINAPI SymSetExtendedOption(IMAGEHLP_EXTENDED_OPTIONS option, BOOL value)
610 {
611     BOOL old = FALSE;
612 
613     switch(option)
614     {
615         case SYMOPT_EX_WINE_NATIVE_MODULES:
616             old = dbghelp_opt_native;
617             dbghelp_opt_native = value;
618             break;
619         default:
620             FIXME("Unsupported option %d with value %d\n", option, value);
621     }
622 
623     return old;
624 }
625 
626 /******************************************************************
627  *		SymGetExtendedOption (DBGHELP.@)
628  *
629  */
SymGetExtendedOption(IMAGEHLP_EXTENDED_OPTIONS option)630 BOOL WINAPI SymGetExtendedOption(IMAGEHLP_EXTENDED_OPTIONS option)
631 {
632     switch(option)
633     {
634         case SYMOPT_EX_WINE_NATIVE_MODULES:
635             return dbghelp_opt_native;
636         default:
637             FIXME("Unsupported option %d\n", option);
638     }
639 
640     return FALSE;
641 }
642 
643 /******************************************************************
644  *		SymSetParentWindow (DBGHELP.@)
645  *
646  */
SymSetParentWindow(HWND hwnd)647 BOOL WINAPI SymSetParentWindow(HWND hwnd)
648 {
649     /* Save hwnd so it can be used as parent window */
650     FIXME("(%p): stub\n", hwnd);
651     return TRUE;
652 }
653 
654 /******************************************************************
655  *		SymSetContext (DBGHELP.@)
656  *
657  */
SymSetContext(HANDLE hProcess,PIMAGEHLP_STACK_FRAME StackFrame,PIMAGEHLP_CONTEXT Context)658 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
659                           PIMAGEHLP_CONTEXT Context)
660 {
661     struct process* pcs = process_find_by_handle(hProcess);
662     if (!pcs) return FALSE;
663 
664     if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
665         pcs->ctx_frame.FrameOffset  == StackFrame->FrameOffset  &&
666         pcs->ctx_frame.StackOffset  == StackFrame->StackOffset)
667     {
668         TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
669               wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
670               wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
671               wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
672         pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
673         SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
674         return FALSE;
675     }
676 
677     pcs->ctx_frame = *StackFrame;
678     /* MSDN states that Context is not (no longer?) used */
679     return TRUE;
680 }
681 
682 /******************************************************************
683  *		reg_cb64to32 (internal)
684  *
685  * Registered callback for converting information from 64 bit to 32 bit
686  */
reg_cb64to32(HANDLE hProcess,ULONG action,ULONG64 data,ULONG64 user)687 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
688 {
689     struct process*                     pcs = process_find_by_handle(hProcess);
690     void*                               data32;
691     IMAGEHLP_DEFERRED_SYMBOL_LOAD64*    idsl64;
692     IMAGEHLP_DEFERRED_SYMBOL_LOAD       idsl;
693 
694     if (!pcs) return FALSE;
695     switch (action)
696     {
697     case CBA_DEBUG_INFO:
698     case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
699     case CBA_SET_OPTIONS:
700     case CBA_SYMBOLS_UNLOADED:
701         data32 = (void*)(DWORD_PTR)data;
702         break;
703     case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
704     case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
705     case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
706     case CBA_DEFERRED_SYMBOL_LOAD_START:
707         idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD_PTR)data;
708         if (!validate_addr64(idsl64->BaseOfImage))
709             return FALSE;
710         idsl.SizeOfStruct = sizeof(idsl);
711         idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
712         idsl.CheckSum = idsl64->CheckSum;
713         idsl.TimeDateStamp = idsl64->TimeDateStamp;
714         memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
715         idsl.Reparse = idsl64->Reparse;
716         data32 = &idsl;
717         break;
718     case CBA_DUPLICATE_SYMBOL:
719     case CBA_EVENT:
720     case CBA_READ_MEMORY:
721     default:
722         FIXME("No mapping for action %u\n", action);
723         return FALSE;
724     }
725     return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
726 }
727 
728 /******************************************************************
729  *		pcs_callback (internal)
730  */
pcs_callback(const struct process * pcs,ULONG action,void * data)731 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
732 {
733     IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
734 
735     TRACE("%p %u %p\n", pcs, action, data);
736 
737     if (!pcs->reg_cb) return FALSE;
738     if (!pcs->reg_is_unicode)
739     {
740         IMAGEHLP_DEFERRED_SYMBOL_LOADW64*   idslW;
741 
742         switch (action)
743         {
744         case CBA_DEBUG_INFO:
745         case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
746         case CBA_SET_OPTIONS:
747         case CBA_SYMBOLS_UNLOADED:
748             break;
749         case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
750         case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
751         case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
752         case CBA_DEFERRED_SYMBOL_LOAD_START:
753             idslW = data;
754             idsl.SizeOfStruct = sizeof(idsl);
755             idsl.BaseOfImage = idslW->BaseOfImage;
756             idsl.CheckSum = idslW->CheckSum;
757             idsl.TimeDateStamp = idslW->TimeDateStamp;
758             WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
759                                 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
760             idsl.Reparse = idslW->Reparse;
761             data = &idsl;
762             break;
763         case CBA_DUPLICATE_SYMBOL:
764         case CBA_EVENT:
765         case CBA_READ_MEMORY:
766         default:
767             FIXME("No mapping for action %u\n", action);
768             return FALSE;
769         }
770     }
771     return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
772 }
773 
774 /******************************************************************
775  *		sym_register_cb
776  *
777  * Helper for registering a callback.
778  */
sym_register_cb(HANDLE hProcess,PSYMBOL_REGISTERED_CALLBACK64 cb,PSYMBOL_REGISTERED_CALLBACK cb32,DWORD64 user,BOOL unicode)779 static BOOL sym_register_cb(HANDLE hProcess,
780                             PSYMBOL_REGISTERED_CALLBACK64 cb,
781                             PSYMBOL_REGISTERED_CALLBACK cb32,
782                             DWORD64 user, BOOL unicode)
783 {
784     struct process* pcs = process_find_by_handle(hProcess);
785 
786     if (!pcs) return FALSE;
787     pcs->reg_cb = cb;
788     pcs->reg_cb32 = cb32;
789     pcs->reg_is_unicode = unicode;
790     pcs->reg_user = user;
791 
792     return TRUE;
793 }
794 
795 /***********************************************************************
796  *		SymRegisterCallback (DBGHELP.@)
797  */
SymRegisterCallback(HANDLE hProcess,PSYMBOL_REGISTERED_CALLBACK CallbackFunction,PVOID UserContext)798 BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
799                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
800                                 PVOID UserContext)
801 {
802     TRACE("(%p, %p, %p)\n",
803           hProcess, CallbackFunction, UserContext);
804     return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
805 }
806 
807 /***********************************************************************
808  *		SymRegisterCallback64 (DBGHELP.@)
809  */
SymRegisterCallback64(HANDLE hProcess,PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,ULONG64 UserContext)810 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess,
811                                   PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
812                                   ULONG64 UserContext)
813 {
814     TRACE("(%p, %p, %s)\n",
815           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
816     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
817 }
818 
819 /***********************************************************************
820  *		SymRegisterCallbackW64 (DBGHELP.@)
821  */
SymRegisterCallbackW64(HANDLE hProcess,PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,ULONG64 UserContext)822 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess,
823                                    PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
824                                    ULONG64 UserContext)
825 {
826     TRACE("(%p, %p, %s)\n",
827           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
828     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, TRUE);
829 }
830 
831 /* This is imagehlp version not dbghelp !! */
832 static API_VERSION api_version = { 4, 0, 2, 0 };
833 
834 /***********************************************************************
835  *           ImagehlpApiVersion (DBGHELP.@)
836  */
ImagehlpApiVersion(VOID)837 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
838 {
839     return &api_version;
840 }
841 
842 /***********************************************************************
843  *           ImagehlpApiVersionEx (DBGHELP.@)
844  */
ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)845 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
846 {
847     if (!AppVersion) return NULL;
848 
849     AppVersion->MajorVersion = api_version.MajorVersion;
850     AppVersion->MinorVersion = api_version.MinorVersion;
851     AppVersion->Revision = api_version.Revision;
852     AppVersion->Reserved = api_version.Reserved;
853 
854     return AppVersion;
855 }
856 
857 /******************************************************************
858  *		ExtensionApiVersion (DBGHELP.@)
859  */
ExtensionApiVersion(void)860 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
861 {
862     static EXT_API_VERSION      eav = {5, 5, 5, 0};
863     return &eav;
864 }
865 
866 /******************************************************************
867  *		WinDbgExtensionDllInit (DBGHELP.@)
868  */
WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,unsigned short major,unsigned short minor)869 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
870                                    unsigned short major, unsigned short minor)
871 {
872 }
873 
calc_crc32(HANDLE handle)874 DWORD calc_crc32(HANDLE handle)
875 {
876     BYTE buffer[8192];
877     DWORD crc = 0;
878     DWORD len;
879 
880     SetFilePointer(handle, 0, 0, FILE_BEGIN);
881     while (ReadFile(handle, buffer, sizeof(buffer), &len, NULL) && len)
882         crc = RtlComputeCrc32(crc, buffer, len);
883     return crc;
884 }
885