xref: /reactos/dll/win32/dbghelp/dbghelp.c (revision 682f85ad)
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
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  */
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  */
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  */
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
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 
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 
194 /******************************************************************
195  *		SymSetSearchPathW (DBGHELP.@)
196  *
197  */
198 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
199 {
200     struct process* pcs = process_find_by_handle(hProcess);
201 
202     if (!pcs) return FALSE;
203     if (!searchPath) return FALSE;
204 
205     HeapFree(GetProcessHeap(), 0, pcs->search_path);
206     pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
207                                           (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
208                                 searchPath);
209     return TRUE;
210 }
211 
212 /******************************************************************
213  *		SymSetSearchPath (DBGHELP.@)
214  *
215  */
216 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
217 {
218     BOOL        ret = FALSE;
219     unsigned    len;
220     WCHAR*      sp;
221 
222     len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
223     if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
224     {
225         MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
226 
227         ret = SymSetSearchPathW(hProcess, sp);
228         HeapFree(GetProcessHeap(), 0, sp);
229     }
230     return ret;
231 }
232 
233 /***********************************************************************
234  *		SymGetSearchPathW (DBGHELP.@)
235  */
236 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
237                               DWORD SearchPathLength)
238 {
239     struct process* pcs = process_find_by_handle(hProcess);
240     if (!pcs) return FALSE;
241 
242     lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
243     return TRUE;
244 }
245 
246 /***********************************************************************
247  *		SymGetSearchPath (DBGHELP.@)
248  */
249 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
250                              DWORD SearchPathLength)
251 {
252     WCHAR*      buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
253     BOOL        ret = FALSE;
254 
255     if (buffer)
256     {
257         ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
258         if (ret)
259             WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
260                                 szSearchPath, SearchPathLength, NULL, NULL);
261         HeapFree(GetProcessHeap(), 0, buffer);
262     }
263     return ret;
264 }
265 
266 #ifndef DBGHELP_STATIC_LIB
267 /******************************************************************
268  *		invade_process
269  *
270  * SymInitialize helper: loads in dbghelp all known (and loaded modules)
271  * this assumes that hProcess is a handle on a valid process
272  */
273 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
274 {
275     WCHAR       tmp[MAX_PATH];
276     HANDLE      hProcess = user;
277 
278     if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base, tmp, ARRAY_SIZE(tmp)))
279         lstrcpynW(tmp, name, ARRAY_SIZE(tmp));
280 
281     SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
282     return TRUE;
283 }
284 
285 const WCHAR *process_getenv(const struct process *process, const WCHAR *name)
286 {
287     size_t name_len;
288     const WCHAR *iter;
289 
290     if (!process->environment) return NULL;
291     name_len = lstrlenW(name);
292 
293     for (iter = process->environment; *iter; iter += lstrlenW(iter) + 1)
294     {
295         if (!wcsnicmp(iter, name, name_len) && iter[name_len] == '=')
296             return iter + name_len + 1;
297     }
298 
299     return NULL;
300 }
301 
302 /******************************************************************
303  *		check_live_target
304  *
305  */
306 static BOOL check_live_target(struct process* pcs)
307 {
308     PROCESS_BASIC_INFORMATION pbi;
309     ULONG_PTR base = 0, env = 0;
310 
311     if (!GetProcessId(pcs->handle)) return FALSE;
312     if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
313 
314     if (NtQueryInformationProcess( pcs->handle, ProcessBasicInformation,
315                                    &pbi, sizeof(pbi), NULL ))
316         return FALSE;
317 
318     if (!pcs->is_64bit)
319     {
320         DWORD env32;
321         PEB32 peb32;
322         C_ASSERT(sizeof(void*) != 4 || FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Environment) == 0x48);
323         if (!ReadProcessMemory(pcs->handle, pbi.PebBaseAddress, &peb32, sizeof(peb32), NULL)) return FALSE;
324         base = peb32.Reserved[0];
325         if (read_process_memory(pcs, peb32.ProcessParameters + 0x48, &env32, sizeof(env32))) env = env32;
326     }
327     else
328     {
329         PEB peb;
330         if (!ReadProcessMemory(pcs->handle, pbi.PebBaseAddress, &peb, sizeof(peb), NULL)) return FALSE;
331         base = peb.Reserved[0];
332         ReadProcessMemory(pcs->handle, &peb.ProcessParameters->Environment, &env, sizeof(env), NULL);
333     }
334 
335 #ifdef __REACTOS__
336     /* Wine store their loader base address in peb.reserved[0] and load its symbol from there.
337      * ReactOS does not care about it, we are just happy if we managed to read the value */
338     base = 1;
339 #endif
340 
341     /* read debuggee environment block */
342     if (env)
343     {
344         size_t buf_size = 0, i, last_null = -1;
345         WCHAR *buf = NULL;
346 
347         do
348         {
349             size_t read_size = sysinfo.dwAllocationGranularity - (env & (sysinfo.dwAllocationGranularity - 1));
350             if (buf)
351             {
352                 WCHAR *new_buf;
353                 if (!(new_buf = realloc(buf, buf_size + read_size))) break;
354                 buf = new_buf;
355             }
356             else if(!(buf = malloc(read_size))) break;
357 
358             if (!read_process_memory(pcs, env, (char*)buf + buf_size, read_size)) break;
359             for (i = buf_size / sizeof(WCHAR); i < (buf_size + read_size) / sizeof(WCHAR); i++)
360             {
361                 if (buf[i]) continue;
362                 if (last_null + 1 == i)
363                 {
364                     pcs->environment = realloc(buf, (i + 1) * sizeof(WCHAR));
365                     buf = NULL;
366                     break;
367                 }
368                 last_null = i;
369             }
370             env += read_size;
371             buf_size += read_size;
372         }
373         while (buf);
374         free(buf);
375     }
376 
377     if (!base) return FALSE;
378 
379     TRACE("got debug info address %#lx from PEB %p\n", base, pbi.PebBaseAddress);
380 #ifndef __REACTOS__
381     if (!elf_read_wine_loader_dbg_info(pcs, base) && !macho_read_wine_loader_dbg_info(pcs, base))
382         WARN("couldn't load process debug info at %#lx\n", base);
383 #endif
384     return TRUE;
385 }
386 #endif
387 
388 /******************************************************************
389  *		SymInitializeW (DBGHELP.@)
390  *
391  * The initialisation of a dbghelp's context.
392  * Note that hProcess doesn't need to be a valid process handle (except
393  * when fInvadeProcess is TRUE).
394  * Since we also allow loading ELF (pure) libraries and Wine ELF libraries
395  * containing PE (and NE) module(s), here's how we handle it:
396  * - we load every module (ELF, NE, PE) passed in SymLoadModule
397  * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
398  *   synchronization: hProcess should be a valid process handle, and we hook
399  *   ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
400  *   our internal ELF modules representation (loading / unloading). This way,
401  *   we'll pair every loaded builtin PE module with its ELF counterpart (and
402  *   access its debug information).
403  * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
404  *   hProcess refers to a running process. We use some heuristics here, so YMMV.
405  *   If we detect a live target, then we get the same handling as if
406  *   fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
407  *   we won't be able to make the peering between a builtin PE module and its ELF
408  *   counterpart. Hence we won't be able to provide the requested debug
409  *   information. We'll however be able to load native PE modules (and their
410  *   debug information) without any trouble.
411  * Note also that this scheme can be intertwined with the deferred loading
412  * mechanism (ie only load the debug information when we actually need it).
413  */
414 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
415 {
416     struct process*     pcs;
417     BOOL wow64, child_wow64;
418 
419     TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
420 
421     if (process_find_by_handle(hProcess))
422     {
423         WARN("the symbols for this process have already been initialized!\n");
424 
425         /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
426            It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
427            Native still returns TRUE even if the process has already been initialized. */
428         return TRUE;
429     }
430 
431     IsWow64Process(GetCurrentProcess(), &wow64);
432 
433     if (GetProcessId(hProcess) && !IsWow64Process(hProcess, &child_wow64))
434         return FALSE;
435 
436     pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
437     if (!pcs) return FALSE;
438 
439     pcs->handle = hProcess;
440     pcs->is_64bit = (sizeof(void *) == 8 || wow64) && !child_wow64;
441     pcs->loader = &no_loader_ops; /* platform-specific initialization will override it if loader debug info can be found */
442 
443     if (UserSearchPath)
444     {
445         pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
446                                               (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
447                                     UserSearchPath);
448     }
449     else
450     {
451         unsigned        size;
452         unsigned        len;
453         static const WCHAR      sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
454         static const WCHAR      alt_sym_path[] = {'_','N','T','_','A','L','T','E','R','N','A','T','E','_','S','Y','M','B','O','L','_','P','A','T','H',0};
455 
456         pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
457         while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
458             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
459         pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
460 
461         len = GetEnvironmentVariableW(sym_path, NULL, 0);
462         if (len)
463         {
464             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
465             pcs->search_path[size] = ';';
466             GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
467             size += 1 + len;
468         }
469         len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
470         if (len)
471         {
472             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
473             pcs->search_path[size] = ';';
474             GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
475         }
476     }
477 
478     pcs->lmodules = NULL;
479     pcs->dbg_hdr_addr = 0;
480     pcs->next = process_first;
481     process_first = pcs;
482 
483 #ifndef DBGHELP_STATIC_LIB
484     if (check_live_target(pcs))
485     {
486         if (fInvadeProcess)
487             EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
488         if (pcs->loader) pcs->loader->synchronize_module_list(pcs);
489     }
490     else if (fInvadeProcess)
491 #else
492     if (fInvadeProcess)
493 #endif
494     {
495         SymCleanup(hProcess);
496         SetLastError(ERROR_INVALID_PARAMETER);
497         return FALSE;
498     }
499 
500     return TRUE;
501 }
502 
503 /******************************************************************
504  *		SymInitialize (DBGHELP.@)
505  *
506  *
507  */
508 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
509 {
510     WCHAR*              sp = NULL;
511     BOOL                ret;
512 
513     if (UserSearchPath)
514     {
515         unsigned len;
516 
517         len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
518         sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
519         MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
520     }
521 
522     ret = SymInitializeW(hProcess, sp, fInvadeProcess);
523     HeapFree(GetProcessHeap(), 0, sp);
524     return ret;
525 }
526 
527 /******************************************************************
528  *		SymCleanup (DBGHELP.@)
529  *
530  */
531 BOOL WINAPI SymCleanup(HANDLE hProcess)
532 {
533     struct process**    ppcs;
534     struct process*     next;
535 
536     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
537     {
538         if ((*ppcs)->handle == hProcess)
539         {
540             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
541 
542             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
543             free((*ppcs)->environment);
544             next = (*ppcs)->next;
545             HeapFree(GetProcessHeap(), 0, *ppcs);
546             *ppcs = next;
547             return TRUE;
548         }
549     }
550 
551     ERR("this process has not had SymInitialize() called for it!\n");
552     return FALSE;
553 }
554 
555 /******************************************************************
556  *		SymSetOptions (DBGHELP.@)
557  *
558  */
559 DWORD WINAPI SymSetOptions(DWORD opts)
560 {
561     struct process* pcs;
562 
563     for (pcs = process_first; pcs; pcs = pcs->next)
564     {
565         pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
566     }
567     return dbghelp_options = opts;
568 }
569 
570 /******************************************************************
571  *		SymGetOptions (DBGHELP.@)
572  *
573  */
574 DWORD WINAPI SymGetOptions(void)
575 {
576     return dbghelp_options;
577 }
578 
579 /******************************************************************
580  *		SymSetExtendedOption (DBGHELP.@)
581  *
582  */
583 BOOL WINAPI SymSetExtendedOption(IMAGEHLP_EXTENDED_OPTIONS option, BOOL value)
584 {
585     BOOL old = FALSE;
586 
587     switch(option)
588     {
589         case SYMOPT_EX_WINE_NATIVE_MODULES:
590             old = dbghelp_opt_native;
591             dbghelp_opt_native = value;
592             break;
593         default:
594             FIXME("Unsupported option %d with value %d\n", option, value);
595     }
596 
597     return old;
598 }
599 
600 /******************************************************************
601  *		SymGetExtendedOption (DBGHELP.@)
602  *
603  */
604 BOOL WINAPI SymGetExtendedOption(IMAGEHLP_EXTENDED_OPTIONS option)
605 {
606     switch(option)
607     {
608         case SYMOPT_EX_WINE_NATIVE_MODULES:
609             return dbghelp_opt_native;
610         default:
611             FIXME("Unsupported option %d\n", option);
612     }
613 
614     return FALSE;
615 }
616 
617 /******************************************************************
618  *		SymSetParentWindow (DBGHELP.@)
619  *
620  */
621 BOOL WINAPI SymSetParentWindow(HWND hwnd)
622 {
623     /* Save hwnd so it can be used as parent window */
624     FIXME("(%p): stub\n", hwnd);
625     return TRUE;
626 }
627 
628 /******************************************************************
629  *		SymSetContext (DBGHELP.@)
630  *
631  */
632 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
633                           PIMAGEHLP_CONTEXT Context)
634 {
635     struct process* pcs = process_find_by_handle(hProcess);
636     if (!pcs) return FALSE;
637 
638     if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
639         pcs->ctx_frame.FrameOffset  == StackFrame->FrameOffset  &&
640         pcs->ctx_frame.StackOffset  == StackFrame->StackOffset)
641     {
642         TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
643               wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
644               wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
645               wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
646         pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
647         SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
648         return FALSE;
649     }
650 
651     pcs->ctx_frame = *StackFrame;
652     /* MSDN states that Context is not (no longer?) used */
653     return TRUE;
654 }
655 
656 /******************************************************************
657  *		reg_cb64to32 (internal)
658  *
659  * Registered callback for converting information from 64 bit to 32 bit
660  */
661 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
662 {
663     struct process*                     pcs = process_find_by_handle(hProcess);
664     void*                               data32;
665     IMAGEHLP_DEFERRED_SYMBOL_LOAD64*    idsl64;
666     IMAGEHLP_DEFERRED_SYMBOL_LOAD       idsl;
667 
668     if (!pcs) return FALSE;
669     switch (action)
670     {
671     case CBA_DEBUG_INFO:
672     case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
673     case CBA_SET_OPTIONS:
674     case CBA_SYMBOLS_UNLOADED:
675         data32 = (void*)(DWORD_PTR)data;
676         break;
677     case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
678     case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
679     case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
680     case CBA_DEFERRED_SYMBOL_LOAD_START:
681         idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD_PTR)data;
682         if (!validate_addr64(idsl64->BaseOfImage))
683             return FALSE;
684         idsl.SizeOfStruct = sizeof(idsl);
685         idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
686         idsl.CheckSum = idsl64->CheckSum;
687         idsl.TimeDateStamp = idsl64->TimeDateStamp;
688         memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
689         idsl.Reparse = idsl64->Reparse;
690         data32 = &idsl;
691         break;
692     case CBA_DUPLICATE_SYMBOL:
693     case CBA_EVENT:
694     case CBA_READ_MEMORY:
695     default:
696         FIXME("No mapping for action %u\n", action);
697         return FALSE;
698     }
699     return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
700 }
701 
702 /******************************************************************
703  *		pcs_callback (internal)
704  */
705 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
706 {
707     IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
708 
709     TRACE("%p %u %p\n", pcs, action, data);
710 
711     if (!pcs->reg_cb) return FALSE;
712     if (!pcs->reg_is_unicode)
713     {
714         IMAGEHLP_DEFERRED_SYMBOL_LOADW64*   idslW;
715 
716         switch (action)
717         {
718         case CBA_DEBUG_INFO:
719         case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
720         case CBA_SET_OPTIONS:
721         case CBA_SYMBOLS_UNLOADED:
722             break;
723         case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
724         case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
725         case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
726         case CBA_DEFERRED_SYMBOL_LOAD_START:
727             idslW = data;
728             idsl.SizeOfStruct = sizeof(idsl);
729             idsl.BaseOfImage = idslW->BaseOfImage;
730             idsl.CheckSum = idslW->CheckSum;
731             idsl.TimeDateStamp = idslW->TimeDateStamp;
732             WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
733                                 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
734             idsl.Reparse = idslW->Reparse;
735             data = &idsl;
736             break;
737         case CBA_DUPLICATE_SYMBOL:
738         case CBA_EVENT:
739         case CBA_READ_MEMORY:
740         default:
741             FIXME("No mapping for action %u\n", action);
742             return FALSE;
743         }
744     }
745     return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
746 }
747 
748 /******************************************************************
749  *		sym_register_cb
750  *
751  * Helper for registering a callback.
752  */
753 static BOOL sym_register_cb(HANDLE hProcess,
754                             PSYMBOL_REGISTERED_CALLBACK64 cb,
755                             PSYMBOL_REGISTERED_CALLBACK cb32,
756                             DWORD64 user, BOOL unicode)
757 {
758     struct process* pcs = process_find_by_handle(hProcess);
759 
760     if (!pcs) return FALSE;
761     pcs->reg_cb = cb;
762     pcs->reg_cb32 = cb32;
763     pcs->reg_is_unicode = unicode;
764     pcs->reg_user = user;
765 
766     return TRUE;
767 }
768 
769 /***********************************************************************
770  *		SymRegisterCallback (DBGHELP.@)
771  */
772 BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
773                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
774                                 PVOID UserContext)
775 {
776     TRACE("(%p, %p, %p)\n",
777           hProcess, CallbackFunction, UserContext);
778     return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
779 }
780 
781 /***********************************************************************
782  *		SymRegisterCallback64 (DBGHELP.@)
783  */
784 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess,
785                                   PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
786                                   ULONG64 UserContext)
787 {
788     TRACE("(%p, %p, %s)\n",
789           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
790     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
791 }
792 
793 /***********************************************************************
794  *		SymRegisterCallbackW64 (DBGHELP.@)
795  */
796 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess,
797                                    PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
798                                    ULONG64 UserContext)
799 {
800     TRACE("(%p, %p, %s)\n",
801           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
802     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, TRUE);
803 }
804 
805 /* This is imagehlp version not dbghelp !! */
806 static API_VERSION api_version = { 4, 0, 2, 0 };
807 
808 /***********************************************************************
809  *           ImagehlpApiVersion (DBGHELP.@)
810  */
811 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
812 {
813     return &api_version;
814 }
815 
816 /***********************************************************************
817  *           ImagehlpApiVersionEx (DBGHELP.@)
818  */
819 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
820 {
821     if (!AppVersion) return NULL;
822 
823     AppVersion->MajorVersion = api_version.MajorVersion;
824     AppVersion->MinorVersion = api_version.MinorVersion;
825     AppVersion->Revision = api_version.Revision;
826     AppVersion->Reserved = api_version.Reserved;
827 
828     return AppVersion;
829 }
830 
831 /******************************************************************
832  *		ExtensionApiVersion (DBGHELP.@)
833  */
834 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
835 {
836     static EXT_API_VERSION      eav = {5, 5, 5, 0};
837     return &eav;
838 }
839 
840 /******************************************************************
841  *		WinDbgExtensionDllInit (DBGHELP.@)
842  */
843 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
844                                    unsigned short major, unsigned short minor)
845 {
846 }
847 
848 DWORD calc_crc32(HANDLE handle)
849 {
850     BYTE buffer[8192];
851     DWORD crc = 0;
852     DWORD len;
853 
854     SetFilePointer(handle, 0, 0, FILE_BEGIN);
855     while (ReadFile(handle, buffer, sizeof(buffer), &len, NULL) && len)
856         crc = RtlComputeCrc32(crc, buffer, len);
857     return crc;
858 }
859