jscript: Rename jsheap_t to heap_pool_t.
[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, cpu_arm64;
155
156 static struct cpu*      dbghelp_cpus[] = {&cpu_i386, &cpu_x86_64, &cpu_ppc, &cpu_sparc, &cpu_arm, &cpu_arm64, 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 #elif defined(__aarch64__)
169     &cpu_arm64
170 #else
171 #error define support for your CPU
172 #endif
173     ;
174
175 struct cpu* cpu_find(DWORD machine)
176 {
177     struct cpu** cpu;
178
179     for (cpu = dbghelp_cpus ; *cpu; cpu++)
180     {
181         if (cpu[0]->machine == machine) return cpu[0];
182     }
183     return NULL;
184 }
185
186 /******************************************************************
187  *              SymSetSearchPathW (DBGHELP.@)
188  *
189  */
190 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
191 {
192     struct process* pcs = process_find_by_handle(hProcess);
193
194     if (!pcs) return FALSE;
195     if (!searchPath) return FALSE;
196
197     HeapFree(GetProcessHeap(), 0, pcs->search_path);
198     pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0, 
199                                           (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
200                                 searchPath);
201     return TRUE;
202 }
203
204 /******************************************************************
205  *              SymSetSearchPath (DBGHELP.@)
206  *
207  */
208 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
209 {
210     BOOL        ret = FALSE;
211     unsigned    len;
212     WCHAR*      sp;
213
214     len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
215     if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
216     {
217         MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
218
219         ret = SymSetSearchPathW(hProcess, sp);
220         HeapFree(GetProcessHeap(), 0, sp);
221     }
222     return ret;
223 }
224
225 /***********************************************************************
226  *              SymGetSearchPathW (DBGHELP.@)
227  */
228 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
229                               DWORD SearchPathLength)
230 {
231     struct process* pcs = process_find_by_handle(hProcess);
232     if (!pcs) return FALSE;
233
234     lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
235     return TRUE;
236 }
237
238 /***********************************************************************
239  *              SymGetSearchPath (DBGHELP.@)
240  */
241 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
242                              DWORD SearchPathLength)
243 {
244     WCHAR*      buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
245     BOOL        ret = FALSE;
246
247     if (buffer)
248     {
249         ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
250         if (ret)
251             WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
252                                 szSearchPath, SearchPathLength, NULL, NULL);
253         HeapFree(GetProcessHeap(), 0, buffer);
254     }
255     return ret;
256 }
257
258 /******************************************************************
259  *              invade_process
260  *
261  * SymInitialize helper: loads in dbghelp all known (and loaded modules)
262  * this assumes that hProcess is a handle on a valid process
263  */
264 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
265 {
266     WCHAR       tmp[MAX_PATH];
267     HANDLE      hProcess = user;
268
269     if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base,
270                               tmp, sizeof(tmp) / sizeof(WCHAR)))
271         lstrcpynW(tmp, name, sizeof(tmp) / sizeof(WCHAR));
272
273     SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
274     return TRUE;
275 }
276
277 /******************************************************************
278  *              check_live_target
279  *
280  */
281 static BOOL check_live_target(struct process* pcs)
282 {
283     if (!GetProcessId(pcs->handle)) return FALSE;
284     if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
285     if (!elf_read_wine_loader_dbg_info(pcs))
286         macho_read_wine_loader_dbg_info(pcs);
287     return TRUE;
288 }
289
290 /******************************************************************
291  *              SymInitializeW (DBGHELP.@)
292  *
293  * The initialisation of a dbghelp's context.
294  * Note that hProcess doesn't need to be a valid process handle (except
295  * when fInvadeProcess is TRUE).
296  * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries 
297  * containing PE (and NE) module(s), here's how we handle it:
298  * - we load every module (ELF, NE, PE) passed in SymLoadModule
299  * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
300  *   synchronization: hProcess should be a valid process handle, and we hook
301  *   ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
302  *   our internal ELF modules representation (loading / unloading). This way,
303  *   we'll pair every loaded builtin PE module with its ELF counterpart (and
304  *   access its debug information).
305  * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the 
306  *   hProcess refers to a running process. We use some heuristics here, so YMMV.
307  *   If we detect a live target, then we get the same handling as if
308  *   fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
309  *   we won't be able to make the peering between a builtin PE module and its ELF
310  *   counterpart. Hence we won't be able to provide the requested debug
311  *   information. We'll however be able to load native PE modules (and their
312  *   debug information) without any trouble.
313  * Note also that this scheme can be intertwined with the deferred loading 
314  * mechanism (ie only load the debug information when we actually need it).
315  */
316 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
317 {
318     struct process*     pcs;
319
320     TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
321
322     if (process_find_by_handle(hProcess)){
323         WARN("the symbols for this process have already been initialized!\n");
324
325         /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
326            It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
327            Native still returns TRUE even if the process has already been initialized. */
328         return TRUE;
329     }
330
331     pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
332     if (!pcs) return FALSE;
333
334     pcs->handle = hProcess;
335
336     if (UserSearchPath)
337     {
338         pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,      
339                                               (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
340                                     UserSearchPath);
341     }
342     else
343     {
344         unsigned        size;
345         unsigned        len;
346         static const WCHAR      sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
347         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};
348
349         pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
350         while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
351             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
352         pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
353
354         len = GetEnvironmentVariableW(sym_path, NULL, 0);
355         if (len)
356         {
357             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
358             pcs->search_path[size] = ';';
359             GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
360             size += 1 + len;
361         }
362         len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
363         if (len)
364         {
365             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
366             pcs->search_path[size] = ';';
367             GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
368         }
369     }
370
371     pcs->lmodules = NULL;
372     pcs->dbg_hdr_addr = 0;
373     pcs->next = process_first;
374     process_first = pcs;
375     
376     if (check_live_target(pcs))
377     {
378         if (fInvadeProcess)
379             EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
380         elf_synchronize_module_list(pcs);
381         macho_synchronize_module_list(pcs);
382     }
383     else if (fInvadeProcess)
384     {
385         SymCleanup(hProcess);
386         SetLastError(ERROR_INVALID_PARAMETER);
387         return FALSE;
388     }
389
390     return TRUE;
391 }
392
393 /******************************************************************
394  *              SymInitialize (DBGHELP.@)
395  *
396  *
397  */
398 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
399 {
400     WCHAR*              sp = NULL;
401     BOOL                ret;
402
403     if (UserSearchPath)
404     {
405         unsigned len;
406
407         len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
408         sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
409         MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
410     }
411
412     ret = SymInitializeW(hProcess, sp, fInvadeProcess);
413     HeapFree(GetProcessHeap(), 0, sp);
414     return ret;
415 }
416
417 /******************************************************************
418  *              SymCleanup (DBGHELP.@)
419  *
420  */
421 BOOL WINAPI SymCleanup(HANDLE hProcess)
422 {
423     struct process**    ppcs;
424     struct process*     next;
425
426     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
427     {
428         if ((*ppcs)->handle == hProcess)
429         {
430             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
431
432             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
433             next = (*ppcs)->next;
434             HeapFree(GetProcessHeap(), 0, *ppcs);
435             *ppcs = next;
436             return TRUE;
437         }
438     }
439
440     ERR("this process has not had SymInitialize() called for it!\n");
441     return FALSE;
442 }
443
444 /******************************************************************
445  *              SymSetOptions (DBGHELP.@)
446  *
447  */
448 DWORD WINAPI SymSetOptions(DWORD opts)
449 {
450     struct process* pcs;
451
452     for (pcs = process_first; pcs; pcs = pcs->next)
453     {
454         pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
455     }
456     return dbghelp_options = opts;
457 }
458
459 /******************************************************************
460  *              SymGetOptions (DBGHELP.@)
461  *
462  */
463 DWORD WINAPI SymGetOptions(void)
464 {
465     return dbghelp_options;
466 }
467
468 /******************************************************************
469  *              SymSetParentWindow (DBGHELP.@)
470  *
471  */
472 BOOL WINAPI SymSetParentWindow(HWND hwnd)
473 {
474     /* Save hwnd so it can be used as parent window */
475     FIXME("(%p): stub\n", hwnd);
476     return TRUE;
477 }
478
479 /******************************************************************
480  *              SymSetContext (DBGHELP.@)
481  *
482  */
483 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
484                           PIMAGEHLP_CONTEXT Context)
485 {
486     struct process* pcs = process_find_by_handle(hProcess);
487     if (!pcs) return FALSE;
488
489     if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
490         pcs->ctx_frame.FrameOffset  == StackFrame->FrameOffset  &&
491         pcs->ctx_frame.StackOffset  == StackFrame->StackOffset)
492     {
493         TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
494               wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
495               wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
496               wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
497         pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
498         SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
499         return FALSE;
500     }
501
502     pcs->ctx_frame = *StackFrame;
503     /* MSDN states that Context is not (no longer?) used */
504     return TRUE;
505 }
506
507 /******************************************************************
508  *              reg_cb64to32 (internal)
509  *
510  * Registered callback for converting information from 64 bit to 32 bit
511  */
512 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
513 {
514     struct process*                     pcs = process_find_by_handle(hProcess);
515     void*                               data32;
516     IMAGEHLP_DEFERRED_SYMBOL_LOAD64*    idsl64;
517     IMAGEHLP_DEFERRED_SYMBOL_LOAD       idsl;
518
519     if (!pcs) return FALSE;
520     switch (action)
521     {
522     case CBA_DEBUG_INFO:
523     case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
524     case CBA_SET_OPTIONS:
525     case CBA_SYMBOLS_UNLOADED:
526         data32 = (void*)(DWORD_PTR)data;
527         break;
528     case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
529     case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
530     case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
531     case CBA_DEFERRED_SYMBOL_LOAD_START:
532         idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD_PTR)data;
533         if (!validate_addr64(idsl64->BaseOfImage))
534             return FALSE;
535         idsl.SizeOfStruct = sizeof(idsl);
536         idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
537         idsl.CheckSum = idsl64->CheckSum;
538         idsl.TimeDateStamp = idsl64->TimeDateStamp;
539         memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
540         idsl.Reparse = idsl64->Reparse;
541         data32 = &idsl;
542         break;
543     case CBA_DUPLICATE_SYMBOL:
544     case CBA_EVENT:
545     case CBA_READ_MEMORY:
546     default:
547         FIXME("No mapping for action %u\n", action);
548         return FALSE;
549     }
550     return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
551 }
552
553 /******************************************************************
554  *              pcs_callback (internal)
555  */
556 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
557 {
558     IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
559
560     TRACE("%p %u %p\n", pcs, action, data);
561
562     if (!pcs->reg_cb) return FALSE;
563     if (!pcs->reg_is_unicode)
564     {
565         IMAGEHLP_DEFERRED_SYMBOL_LOADW64*   idslW;
566
567         switch (action)
568         {
569         case CBA_DEBUG_INFO:
570         case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
571         case CBA_SET_OPTIONS:
572         case CBA_SYMBOLS_UNLOADED:
573             break;
574         case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
575         case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
576         case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
577         case CBA_DEFERRED_SYMBOL_LOAD_START:
578             idslW = data;
579             idsl.SizeOfStruct = sizeof(idsl);
580             idsl.BaseOfImage = idslW->BaseOfImage;
581             idsl.CheckSum = idslW->CheckSum;
582             idsl.TimeDateStamp = idslW->TimeDateStamp;
583             WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
584                                 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
585             idsl.Reparse = idslW->Reparse;
586             data = &idsl;
587             break;
588         case CBA_DUPLICATE_SYMBOL:
589         case CBA_EVENT:
590         case CBA_READ_MEMORY:
591         default:
592             FIXME("No mapping for action %u\n", action);
593             return FALSE;
594         }
595     }
596     return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
597 }
598
599 /******************************************************************
600  *              sym_register_cb
601  *
602  * Helper for registering a callback.
603  */
604 static BOOL sym_register_cb(HANDLE hProcess,
605                             PSYMBOL_REGISTERED_CALLBACK64 cb,
606                             PSYMBOL_REGISTERED_CALLBACK cb32,
607                             DWORD64 user, BOOL unicode)
608 {
609     struct process* pcs = process_find_by_handle(hProcess);
610
611     if (!pcs) return FALSE;
612     pcs->reg_cb = cb;
613     pcs->reg_cb32 = cb32;
614     pcs->reg_is_unicode = unicode;
615     pcs->reg_user = user;
616
617     return TRUE;
618 }
619
620 /***********************************************************************
621  *              SymRegisterCallback (DBGHELP.@)
622  */
623 BOOL WINAPI SymRegisterCallback(HANDLE hProcess, 
624                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
625                                 PVOID UserContext)
626 {
627     TRACE("(%p, %p, %p)\n", 
628           hProcess, CallbackFunction, UserContext);
629     return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
630 }
631
632 /***********************************************************************
633  *              SymRegisterCallback64 (DBGHELP.@)
634  */
635 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess, 
636                                   PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
637                                   ULONG64 UserContext)
638 {
639     TRACE("(%p, %p, %s)\n", 
640           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
641     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
642 }
643
644 /***********************************************************************
645  *              SymRegisterCallbackW64 (DBGHELP.@)
646  */
647 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess, 
648                                    PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
649                                    ULONG64 UserContext)
650 {
651     TRACE("(%p, %p, %s)\n", 
652           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
653     return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, TRUE);
654 }
655
656 /* This is imagehlp version not dbghelp !! */
657 static API_VERSION api_version = { 4, 0, 2, 0 };
658
659 /***********************************************************************
660  *           ImagehlpApiVersion (DBGHELP.@)
661  */
662 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
663 {
664     return &api_version;
665 }
666
667 /***********************************************************************
668  *           ImagehlpApiVersionEx (DBGHELP.@)
669  */
670 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
671 {
672     if (!AppVersion) return NULL;
673
674     AppVersion->MajorVersion = api_version.MajorVersion;
675     AppVersion->MinorVersion = api_version.MinorVersion;
676     AppVersion->Revision = api_version.Revision;
677     AppVersion->Reserved = api_version.Reserved;
678
679     return AppVersion;
680 }
681
682 /******************************************************************
683  *              ExtensionApiVersion (DBGHELP.@)
684  */
685 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
686 {
687     static EXT_API_VERSION      eav = {5, 5, 5, 0};
688     return &eav;
689 }
690
691 /******************************************************************
692  *              WinDbgExtensionDllInit (DBGHELP.@)
693  */
694 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
695                                    unsigned short major, unsigned short minor)
696 {
697 }