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