atl80: Added AtlComModuleRegisterServer implementation (based on AtlModuleRegisterSer...
[wine] / dlls / dbghelp / dbghelp.c
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 #include "config.h"
22
23 #include "dbghelp_private.h"
24 #include "winerror.h"
25 #include "psapi.h"
26 #include "wine/debug.h"
27 #include "wdbgexts.h"
28 #include "winnls.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
31
32 /* TODO
33  *  - support for symbols' types is still partly missing
34  *      + C++ support
35  *      + we should store the underlying type for an enum in the symt_enum struct
36  *      + for enums, we store the names & values (associated to the enum type), 
37  *        but those values are not directly usable from a debugger (that's why, I
38  *        assume, that we have also to define constants for enum values, as 
39  *        Codeview does BTW.
40  *      + SymEnumTypes should only return *user* defined types (UDT, typedefs...) not
41  *        all the types stored/used in the modules (like char*)
42  *  - SymGetLine{Next|Prev} don't work as expected (they don't seem to work across
43  *    functions, and even across function blocks...). Basically, for *Next* to work
44  *    it requires an address after the prolog of the func (the base address of the 
45  *    func doesn't work)
46  *  - most options (dbghelp_options) are not used (loading lines...)
47  *  - in symbol lookup by name, we don't use RE everywhere we should. Moreover, when
48  *    we're supposed to use RE, it doesn't make use of our hash tables. Therefore,
49  *    we could use hash if name isn't a RE, and fall back to a full search when we
50  *    get a full RE
51  *  - msc:
52  *      + we should add parameters' types to the function's signature
53  *        while processing a function's parameters
54  *      + add support for function-less labels (as MSC seems to define them)
55  *      + C++ management
56  *  - stabs: 
57  *      + when, in a same module, the same definition is used in several compilation
58  *        units, we get several definitions of the same object (especially 
59  *        struct/union). we should find a way not to duplicate them
60  *      + in some cases (dlls/user/dialog16.c DIALOG_GetControl16), the same static
61  *        global variable is defined several times (at different scopes). We are
62  *        getting several of those while looking for a unique symbol. Part of the 
63  *        issue is that we don't give a scope to a static variable inside a function
64  *      + C++ management
65  */
66
67 unsigned   dbghelp_options = SYMOPT_UNDNAME;
68 HANDLE     hMsvcrt = NULL;
69
70 /***********************************************************************
71  *           DllMain (DEBUGHLP.@)
72  */
73 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
74 {
75     switch (fdwReason)
76     {
77     case DLL_PROCESS_ATTACH:    break;
78     case DLL_PROCESS_DETACH:
79         if (hMsvcrt) FreeLibrary(hMsvcrt);
80         break;
81     case DLL_THREAD_ATTACH:     break;
82     case DLL_THREAD_DETACH:     break;
83     default:                    break;
84     }
85     return TRUE;
86 }
87
88 static struct process* process_first /* = NULL */;
89
90 /******************************************************************
91  *              process_find_by_handle
92  *
93  */
94 struct process*    process_find_by_handle(HANDLE hProcess)
95 {
96     struct process* p;
97
98     for (p = process_first; p && p->handle != hProcess; p = p->next);
99     if (!p) SetLastError(ERROR_INVALID_HANDLE);
100     return p;
101 }
102
103 /******************************************************************
104  *             validate_addr64 (internal)
105  *
106  */
107 BOOL validate_addr64(DWORD64 addr)
108 {
109     if (sizeof(void*) == sizeof(int) && (addr >> 32))
110     {
111         FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
112         SetLastError(ERROR_INVALID_PARAMETER);
113         return FALSE;
114     }
115     return TRUE;
116 }
117
118 /******************************************************************
119  *              fetch_buffer
120  *
121  * Ensures process' internal buffer is large enough.
122  */
123 void* fetch_buffer(struct process* pcs, unsigned size)
124 {
125     if (size > pcs->buffer_size)
126     {
127         if (pcs->buffer)
128             pcs->buffer = HeapReAlloc(GetProcessHeap(), 0, pcs->buffer, size);
129         else
130             pcs->buffer = HeapAlloc(GetProcessHeap(), 0, size);
131         pcs->buffer_size = (pcs->buffer) ? size : 0;
132     }
133     return pcs->buffer;
134 }
135
136 const char* wine_dbgstr_addr(const ADDRESS64* addr)
137 {
138     if (!addr) return "(null)";
139     switch (addr->Mode)
140     {
141     case AddrModeFlat:
142         return wine_dbg_sprintf("flat<%s>", wine_dbgstr_longlong(addr->Offset));
143     case AddrMode1616:
144         return wine_dbg_sprintf("1616<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
145     case AddrMode1632:
146         return wine_dbg_sprintf("1632<%04x:%08x>", addr->Segment, (DWORD)addr->Offset);
147     case AddrModeReal:
148         return wine_dbg_sprintf("real<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
149     default:
150         return "unknown";
151     }
152 }
153
154 extern struct cpu       cpu_i386, cpu_x86_64, cpu_ppc, cpu_sparc, cpu_arm;
155
156 static struct cpu*      dbghelp_cpus[] = {&cpu_i386, &cpu_x86_64, &cpu_ppc, &cpu_sparc, &cpu_arm, NULL};
157 struct cpu*             dbghelp_current_cpu =
158 #if defined(__i386__)
159     &cpu_i386
160 #elif defined(__x86_64__)
161     &cpu_x86_64
162 #elif defined(__powerpc__)
163     &cpu_ppc
164 #elif defined(__sparc__)
165     &cpu_sparc
166 #elif defined(__arm__)
167     &cpu_arm
168 #else
169 #error define support for your CPU
170 #endif
171     ;
172
173 struct cpu* cpu_find(DWORD machine)
174 {
175     struct cpu** cpu;
176
177     for (cpu = dbghelp_cpus ; *cpu; cpu++)
178     {
179         if (cpu[0]->machine == machine) return cpu[0];
180     }
181     return NULL;
182 }
183
184 /******************************************************************
185  *              SymSetSearchPathW (DBGHELP.@)
186  *
187  */
188 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
189 {
190     struct process* pcs = process_find_by_handle(hProcess);
191
192     if (!pcs) return FALSE;
193     if (!searchPath) return FALSE;
194
195     HeapFree(GetProcessHeap(), 0, pcs->search_path);
196     pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0, 
197                                           (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
198                                 searchPath);
199     return TRUE;
200 }
201
202 /******************************************************************
203  *              SymSetSearchPath (DBGHELP.@)
204  *
205  */
206 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
207 {
208     BOOL        ret = FALSE;
209     unsigned    len;
210     WCHAR*      sp;
211
212     len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
213     if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
214     {
215         MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
216
217         ret = SymSetSearchPathW(hProcess, sp);
218         HeapFree(GetProcessHeap(), 0, sp);
219     }
220     return ret;
221 }
222
223 /***********************************************************************
224  *              SymGetSearchPathW (DBGHELP.@)
225  */
226 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
227                               DWORD SearchPathLength)
228 {
229     struct process* pcs = process_find_by_handle(hProcess);
230     if (!pcs) return FALSE;
231
232     lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
233     return TRUE;
234 }
235
236 /***********************************************************************
237  *              SymGetSearchPath (DBGHELP.@)
238  */
239 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
240                              DWORD SearchPathLength)
241 {
242     WCHAR*      buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
243     BOOL        ret = FALSE;
244
245     if (buffer)
246     {
247         ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
248         if (ret)
249             WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
250                                 szSearchPath, SearchPathLength, NULL, NULL);
251         HeapFree(GetProcessHeap(), 0, buffer);
252     }
253     return ret;
254 }
255
256 /******************************************************************
257  *              invade_process
258  *
259  * SymInitialize helper: loads in dbghelp all known (and loaded modules)
260  * this assumes that hProcess is a handle on a valid process
261  */
262 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
263 {
264     WCHAR       tmp[MAX_PATH];
265     HANDLE      hProcess = user;
266
267     if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base,
268                               tmp, sizeof(tmp) / sizeof(WCHAR)))
269         lstrcpynW(tmp, name, sizeof(tmp) / sizeof(WCHAR));
270
271     SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
272     return TRUE;
273 }
274
275 /******************************************************************
276  *              check_live_target
277  *
278  */
279 static BOOL check_live_target(struct process* pcs)
280 {
281     if (!GetProcessId(pcs->handle)) return FALSE;
282     if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
283     if (!elf_read_wine_loader_dbg_info(pcs))
284         macho_read_wine_loader_dbg_info(pcs);
285     return TRUE;
286 }
287
288 /******************************************************************
289  *              SymInitializeW (DBGHELP.@)
290  *
291  * The initialisation of a dbghelp's context.
292  * Note that hProcess doesn't need to be a valid process handle (except
293  * when fInvadeProcess is TRUE).
294  * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries 
295  * containing PE (and NE) module(s), here's how we handle it:
296  * - we load every module (ELF, NE, PE) passed in SymLoadModule
297  * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
298  *   synchronization: hProcess should be a valid process handle, and we hook
299  *   ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
300  *   our internal ELF modules representation (loading / unloading). This way,
301  *   we'll pair every loaded builtin PE module with its ELF counterpart (and
302  *   access its debug information).
303  * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the 
304  *   hProcess refers to a running process. We use some heuristics here, so YMMV.
305  *   If we detect a live target, then we get the same handling as if
306  *   fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
307  *   we won't be able to make the peering between a builtin PE module and its ELF
308  *   counterpart. Hence we won't be able to provide the requested debug
309  *   information. We'll however be able to load native PE modules (and their
310  *   debug information) without any trouble.
311  * Note also that this scheme can be intertwined with the deferred loading 
312  * mechanism (ie only load the debug information when we actually need it).
313  */
314 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
315 {
316     struct process*     pcs;
317
318     TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
319
320     if (process_find_by_handle(hProcess)){
321         WARN("the symbols for this process have already been initialized!\n");
322
323         /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
324            It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
325            Native still returns TRUE even if the process has already been initialized. */
326         return TRUE;
327     }
328
329     pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
330     if (!pcs) return FALSE;
331
332     pcs->handle = hProcess;
333
334     if (UserSearchPath)
335     {
336         pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,      
337                                               (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
338                                     UserSearchPath);
339     }
340     else
341     {
342         unsigned        size;
343         unsigned        len;
344         static const WCHAR      sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
345         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};
346
347         pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
348         while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
349             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
350         pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
351
352         len = GetEnvironmentVariableW(sym_path, NULL, 0);
353         if (len)
354         {
355             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
356             pcs->search_path[size] = ';';
357             GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
358             size += 1 + len;
359         }
360         len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
361         if (len)
362         {
363             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
364             pcs->search_path[size] = ';';
365             GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
366         }
367     }
368
369     pcs->lmodules = NULL;
370     pcs->dbg_hdr_addr = 0;
371     pcs->next = process_first;
372     process_first = pcs;
373     
374     if (check_live_target(pcs))
375     {
376         if (fInvadeProcess)
377             EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
378         elf_synchronize_module_list(pcs);
379         macho_synchronize_module_list(pcs);
380     }
381     else if (fInvadeProcess)
382     {
383         SymCleanup(hProcess);
384         SetLastError(ERROR_INVALID_PARAMETER);
385         return FALSE;
386     }
387
388     return TRUE;
389 }
390
391 /******************************************************************
392  *              SymInitialize (DBGHELP.@)
393  *
394  *
395  */
396 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
397 {
398     WCHAR*              sp = NULL;
399     BOOL                ret;
400
401     if (UserSearchPath)
402     {
403         unsigned len;
404
405         len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
406         sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
407         MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
408     }
409
410     ret = SymInitializeW(hProcess, sp, fInvadeProcess);
411     HeapFree(GetProcessHeap(), 0, sp);
412     return ret;
413 }
414
415 /******************************************************************
416  *              SymCleanup (DBGHELP.@)
417  *
418  */
419 BOOL WINAPI SymCleanup(HANDLE hProcess)
420 {
421     struct process**    ppcs;
422     struct process*     next;
423
424     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
425     {
426         if ((*ppcs)->handle == hProcess)
427         {
428             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
429
430             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
431             next = (*ppcs)->next;
432             HeapFree(GetProcessHeap(), 0, *ppcs);
433             *ppcs = next;
434             return TRUE;
435         }
436     }
437
438     ERR("this process has not had SymInitialize() called for it!\n");
439     return FALSE;
440 }
441
442 /******************************************************************
443  *              SymSetOptions (DBGHELP.@)
444  *
445  */
446 DWORD WINAPI SymSetOptions(DWORD opts)
447 {
448     struct process* pcs;
449
450     for (pcs = process_first; pcs; pcs = pcs->next)
451     {
452         pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
453     }
454     return dbghelp_options = opts;
455 }
456
457 /******************************************************************
458  *              SymGetOptions (DBGHELP.@)
459  *
460  */
461 DWORD WINAPI SymGetOptions(void)
462 {
463     return dbghelp_options;
464 }
465
466 /******************************************************************
467  *              SymSetParentWindow (DBGHELP.@)
468  *
469  */
470 BOOL WINAPI SymSetParentWindow(HWND hwnd)
471 {
472     /* Save hwnd so it can be used as parent window */
473     FIXME("(%p): stub\n", hwnd);
474     return TRUE;
475 }
476
477 /******************************************************************
478  *              SymSetContext (DBGHELP.@)
479  *
480  */
481 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
482                           PIMAGEHLP_CONTEXT Context)
483 {
484     struct process* pcs = process_find_by_handle(hProcess);
485     if (!pcs) return FALSE;
486
487     if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
488         pcs->ctx_frame.FrameOffset  == StackFrame->FrameOffset  &&
489         pcs->ctx_frame.StackOffset  == StackFrame->StackOffset)
490     {
491         TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
492               wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
493               wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
494               wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
495         pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
496         SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
497         return FALSE;
498     }
499
500     pcs->ctx_frame = *StackFrame;
501     /* MSDN states that Context is not (no longer?) used */
502     return TRUE;
503 }
504
505 /******************************************************************
506  *              reg_cb64to32 (internal)
507  *
508  * Registered callback for converting information from 64 bit to 32 bit
509  */
510 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
511 {
512     struct process*                     pcs = process_find_by_handle(hProcess);
513     void*                               data32;
514     IMAGEHLP_DEFERRED_SYMBOL_LOAD64*    idsl64;
515     IMAGEHLP_DEFERRED_SYMBOL_LOAD       idsl;
516
517     if (!pcs) return FALSE;
518     switch (action)
519     {
520     case CBA_DEBUG_INFO:
521     case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
522     case CBA_SET_OPTIONS:
523     case CBA_SYMBOLS_UNLOADED:
524         data32 = (void*)(DWORD_PTR)data;
525         break;
526     case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
527     case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
528     case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
529     case CBA_DEFERRED_SYMBOL_LOAD_START:
530         idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD_PTR)data;
531         if (!validate_addr64(idsl64->BaseOfImage))
532             return FALSE;
533         idsl.SizeOfStruct = sizeof(idsl);
534         idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
535         idsl.CheckSum = idsl64->CheckSum;
536         idsl.TimeDateStamp = idsl64->TimeDateStamp;
537         memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
538         idsl.Reparse = idsl64->Reparse;
539         data32 = &idsl;
540         break;
541     case CBA_DUPLICATE_SYMBOL:
542     case CBA_EVENT:
543     case CBA_READ_MEMORY:
544     default:
545         FIXME("No mapping for action %u\n", action);
546         return FALSE;
547     }
548     return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
549 }
550
551 /******************************************************************
552  *              pcs_callback (internal)
553  */
554 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
555 {
556     IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
557
558     TRACE("%p %u %p\n", pcs, action, data);
559
560     if (!pcs->reg_cb) return FALSE;
561     if (!pcs->reg_is_unicode)
562     {
563         IMAGEHLP_DEFERRED_SYMBOL_LOADW64*   idslW;
564
565         switch (action)
566         {
567         case CBA_DEBUG_INFO:
568         case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
569         case CBA_SET_OPTIONS:
570         case CBA_SYMBOLS_UNLOADED:
571             break;
572         case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
573         case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
574         case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
575         case CBA_DEFERRED_SYMBOL_LOAD_START:
576             idslW = data;
577             idsl.SizeOfStruct = sizeof(idsl);
578             idsl.BaseOfImage = idslW->BaseOfImage;
579             idsl.CheckSum = idslW->CheckSum;
580             idsl.TimeDateStamp = idslW->TimeDateStamp;
581             WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
582                                 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
583             idsl.Reparse = idslW->Reparse;
584             data = &idsl;
585             break;
586         case CBA_DUPLICATE_SYMBOL:
587         case CBA_EVENT:
588         case CBA_READ_MEMORY:
589         default:
590             FIXME("No mapping for action %u\n", action);
591             return FALSE;
592         }
593     }
594     return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
595 }
596
597 /******************************************************************
598  *              sym_register_cb
599  *
600  * Helper for registering a callback.
601  */
602 static BOOL sym_register_cb(HANDLE hProcess,
603                             PSYMBOL_REGISTERED_CALLBACK64 cb,
604                             PSYMBOL_REGISTERED_CALLBACK cb32,
605                             DWORD64 user, BOOL unicode)
606 {
607     struct process* pcs = process_find_by_handle(hProcess);
608
609     if (!pcs) return FALSE;
610     pcs->reg_cb = cb;
611     pcs->reg_cb32 = cb32;
612     pcs->reg_is_unicode = unicode;
613     pcs->reg_user = user;
614
615     return TRUE;
616 }
617
618 /***********************************************************************
619  *              SymRegisterCallback (DBGHELP.@)
620  */
621 BOOL WINAPI SymRegisterCallback(HANDLE hProcess, 
622                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
623                                 PVOID UserContext)
624 {
625     TRACE("(%p, %p, %p)\n", 
626           hProcess, CallbackFunction, UserContext);
627     return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
628 }
629
630 /***********************************************************************
631  *              SymRegisterCallback64 (DBGHELP.@)
632  */
633 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess, 
634                                   PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
635                                   ULONG64 UserContext)
636 {
637     TRACE("(%p, %p, %s)\n", 
638           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
639     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
640 }
641
642 /***********************************************************************
643  *              SymRegisterCallbackW64 (DBGHELP.@)
644  */
645 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess, 
646                                    PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
647                                    ULONG64 UserContext)
648 {
649     TRACE("(%p, %p, %s)\n", 
650           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
651     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, TRUE);
652 }
653
654 /* This is imagehlp version not dbghelp !! */
655 static API_VERSION api_version = { 4, 0, 2, 0 };
656
657 /***********************************************************************
658  *           ImagehlpApiVersion (DBGHELP.@)
659  */
660 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
661 {
662     return &api_version;
663 }
664
665 /***********************************************************************
666  *           ImagehlpApiVersionEx (DBGHELP.@)
667  */
668 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
669 {
670     if (!AppVersion) return NULL;
671
672     AppVersion->MajorVersion = api_version.MajorVersion;
673     AppVersion->MinorVersion = api_version.MinorVersion;
674     AppVersion->Revision = api_version.Revision;
675     AppVersion->Reserved = api_version.Reserved;
676
677     return AppVersion;
678 }
679
680 /******************************************************************
681  *              ExtensionApiVersion (DBGHELP.@)
682  */
683 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
684 {
685     static EXT_API_VERSION      eav = {5, 5, 5, 0};
686     return &eav;
687 }
688
689 /******************************************************************
690  *              WinDbgExtensionDllInit (DBGHELP.@)
691  */
692 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
693                                    unsigned short major, unsigned short minor)
694 {
695 }