push 5b1efc32b5a8acb1d5b5e60584746392dd0c436e
[wine/hacks.git] / dlls / dbghelp / dbghelp.c
blobb501425d52232bcea47b26e1fb55c9aa21739c24
1 /*
2 * File dbghelp.c - generic routines (process) for dbghelp DLL
4 * Copyright (C) 2004, Eric Pouech
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.
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.
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
21 #include "config.h"
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"
30 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
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
67 unsigned dbghelp_options = SYMOPT_UNDNAME;
68 HANDLE hMsvcrt = NULL;
70 /***********************************************************************
71 * DllMain (DEBUGHLP.@)
73 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
75 switch (fdwReason)
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;
85 return TRUE;
88 static struct process* process_first /* = NULL */;
90 /******************************************************************
91 * process_find_by_handle
94 struct process* process_find_by_handle(HANDLE hProcess)
96 struct process* p;
98 for (p = process_first; p && p->handle != hProcess; p = p->next);
99 if (!p) SetLastError(ERROR_INVALID_HANDLE);
100 return p;
103 /******************************************************************
104 * validate_addr64 (internal)
107 BOOL validate_addr64(DWORD64 addr)
109 if (addr >> 32)
111 FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
112 SetLastError(ERROR_INVALID_PARAMETER);
113 return FALSE;
115 return TRUE;
118 /******************************************************************
119 * fetch_buffer
121 * Ensures process' internal buffer is large enough.
123 void* fetch_buffer(struct process* pcs, unsigned size)
125 if (size > pcs->buffer_size)
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;
133 return pcs->buffer;
136 /******************************************************************
137 * SymSetSearchPathW (DBGHELP.@)
140 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
142 struct process* pcs = process_find_by_handle(hProcess);
144 if (!pcs) return FALSE;
145 if (!searchPath) return FALSE;
147 HeapFree(GetProcessHeap(), 0, pcs->search_path);
148 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
149 (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
150 searchPath);
151 return TRUE;
154 /******************************************************************
155 * SymSetSearchPath (DBGHELP.@)
158 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
160 BOOL ret = FALSE;
161 unsigned len;
162 WCHAR* sp;
164 len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
165 if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
167 MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
169 ret = SymSetSearchPathW(hProcess, sp);
170 HeapFree(GetProcessHeap(), 0, sp);
172 return ret;
175 /***********************************************************************
176 * SymGetSearchPathW (DBGHELP.@)
178 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
179 DWORD SearchPathLength)
181 struct process* pcs = process_find_by_handle(hProcess);
182 if (!pcs) return FALSE;
184 lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
185 return TRUE;
188 /***********************************************************************
189 * SymGetSearchPath (DBGHELP.@)
191 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
192 DWORD SearchPathLength)
194 WCHAR* buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
195 BOOL ret = FALSE;
197 if (buffer)
199 ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
200 if (ret)
201 WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
202 szSearchPath, SearchPathLength, NULL, NULL);
203 HeapFree(GetProcessHeap(), 0, buffer);
205 return ret;
208 /******************************************************************
209 * invade_process
211 * SymInitialize helper: loads in dbghelp all known (and loaded modules)
212 * this assumes that hProcess is a handle on a valid process
214 static BOOL WINAPI process_invade_cb(PCSTR name, ULONG base, ULONG size, PVOID user)
216 char tmp[MAX_PATH];
217 HANDLE hProcess = user;
219 if (!GetModuleFileNameExA(hProcess, (HMODULE)base,
220 tmp, sizeof(tmp)))
221 lstrcpynA(tmp, name, sizeof(tmp));
223 SymLoadModule(hProcess, 0, tmp, name, base, size);
224 return TRUE;
227 /******************************************************************
228 * check_live_target
231 static BOOL check_live_target(struct process* pcs)
233 if (!GetProcessId(pcs->handle)) return FALSE;
234 if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
235 if (!elf_read_wine_loader_dbg_info(pcs))
236 macho_read_wine_loader_dbg_info(pcs);
237 return TRUE;
240 /******************************************************************
241 * SymInitializeW (DBGHELP.@)
243 * The initialisation of a dbghelp's context.
244 * Note that hProcess doesn't need to be a valid process handle (except
245 * when fInvadeProcess is TRUE).
246 * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries
247 * containing PE (and NE) module(s), here's how we handle it:
248 * - we load every module (ELF, NE, PE) passed in SymLoadModule
249 * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
250 * synchronization: hProcess should be a valid process handle, and we hook
251 * ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
252 * our internal ELF modules representation (loading / unloading). This way,
253 * we'll pair every loaded builtin PE module with its ELF counterpart (and
254 * access its debug information).
255 * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
256 * hProcess refers to a running process. We use some heuristics here, so YMMV.
257 * If we detect a live target, then we get the same handling as if
258 * fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
259 * we won't be able to make the peering between a builtin PE module and its ELF
260 * counterpart. Hence we won't be able to provide the requested debug
261 * information. We'll however be able to load native PE modules (and their
262 * debug information) without any trouble.
263 * Note also that this scheme can be intertwined with the deferred loading
264 * mechanism (ie only load the debug information when we actually need it).
266 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
268 struct process* pcs;
270 TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
272 if (process_find_by_handle(hProcess)){
273 WARN("the symbols for this process have already been initialized!\n");
275 /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
276 It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
277 Native still returns TRUE even if the process has already been initialized. */
278 return TRUE;
281 pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
282 if (!pcs) return FALSE;
284 pcs->handle = hProcess;
286 if (UserSearchPath)
288 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
289 (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
290 UserSearchPath);
292 else
294 unsigned size;
295 unsigned len;
296 static const WCHAR sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
297 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};
299 pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
300 while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
301 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
302 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
304 len = GetEnvironmentVariableW(sym_path, NULL, 0);
305 if (len)
307 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
308 pcs->search_path[size] = ';';
309 GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
310 size += 1 + len;
312 len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
313 if (len)
315 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
316 pcs->search_path[size] = ';';
317 GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
321 pcs->lmodules = NULL;
322 pcs->dbg_hdr_addr = 0;
323 pcs->next = process_first;
324 process_first = pcs;
326 if (check_live_target(pcs))
328 if (fInvadeProcess)
329 EnumerateLoadedModules(hProcess, process_invade_cb, hProcess);
330 elf_synchronize_module_list(pcs);
331 macho_synchronize_module_list(pcs);
333 else if (fInvadeProcess)
335 SymCleanup(hProcess);
336 SetLastError(ERROR_INVALID_PARAMETER);
337 return FALSE;
340 return TRUE;
343 /******************************************************************
344 * SymInitialize (DBGHELP.@)
348 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
350 WCHAR* sp = NULL;
351 BOOL ret;
353 if (UserSearchPath)
355 unsigned len;
357 len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
358 sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
359 MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
362 ret = SymInitializeW(hProcess, sp, fInvadeProcess);
363 HeapFree(GetProcessHeap(), 0, sp);
364 return ret;
367 /******************************************************************
368 * SymCleanup (DBGHELP.@)
371 BOOL WINAPI SymCleanup(HANDLE hProcess)
373 struct process** ppcs;
374 struct process* next;
376 for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
378 if ((*ppcs)->handle == hProcess)
380 while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
382 HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
383 next = (*ppcs)->next;
384 HeapFree(GetProcessHeap(), 0, *ppcs);
385 *ppcs = next;
386 return TRUE;
390 ERR("this process has not had SymInitialize() called for it!\n");
391 return FALSE;
394 /******************************************************************
395 * SymSetOptions (DBGHELP.@)
398 DWORD WINAPI SymSetOptions(DWORD opts)
400 struct process* pcs;
402 for (pcs = process_first; pcs; pcs = pcs->next)
404 pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
406 return dbghelp_options = opts;
409 /******************************************************************
410 * SymGetOptions (DBGHELP.@)
413 DWORD WINAPI SymGetOptions(void)
415 return dbghelp_options;
418 /******************************************************************
419 * SymSetParentWindow (DBGHELP.@)
422 BOOL WINAPI SymSetParentWindow(HWND hwnd)
424 /* Save hwnd so it can be used as parent window */
425 FIXME("(%p): stub\n", hwnd);
426 return TRUE;
429 /******************************************************************
430 * SymSetContext (DBGHELP.@)
433 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
434 PIMAGEHLP_CONTEXT Context)
436 struct process* pcs = process_find_by_handle(hProcess);
437 if (!pcs) return FALSE;
439 if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
440 pcs->ctx_frame.FrameOffset == StackFrame->FrameOffset &&
441 pcs->ctx_frame.StackOffset == StackFrame->StackOffset)
443 TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
444 wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
445 wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
446 wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
447 pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
448 SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
449 return FALSE;
452 pcs->ctx_frame = *StackFrame;
453 /* MSDN states that Context is not (no longer?) used */
454 return TRUE;
457 /******************************************************************
458 * reg_cb64to32 (internal)
460 * Registered callback for converting information from 64 bit to 32 bit
462 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
464 PSYMBOL_REGISTERED_CALLBACK cb32 = (PSYMBOL_REGISTERED_CALLBACK)(DWORD)(user >> 32);
465 DWORD user32 = (DWORD)user;
466 void* data32;
467 IMAGEHLP_DEFERRED_SYMBOL_LOAD64* idsl64;
468 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl;
470 switch (action)
472 case CBA_DEBUG_INFO:
473 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
474 case CBA_SET_OPTIONS:
475 case CBA_SYMBOLS_UNLOADED:
476 data32 = (void*)(DWORD)data;
477 break;
478 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
479 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
480 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
481 case CBA_DEFERRED_SYMBOL_LOAD_START:
482 idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD)data;
483 if (!validate_addr64(idsl64->BaseOfImage))
484 return FALSE;
485 idsl.SizeOfStruct = sizeof(idsl);
486 idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
487 idsl.CheckSum = idsl64->CheckSum;
488 idsl.TimeDateStamp = idsl64->TimeDateStamp;
489 memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
490 idsl.Reparse = idsl64->Reparse;
491 data32 = &idsl;
492 break;
493 case CBA_DUPLICATE_SYMBOL:
494 case CBA_EVENT:
495 case CBA_READ_MEMORY:
496 default:
497 FIXME("No mapping for action %u\n", action);
498 return FALSE;
500 return cb32(hProcess, action, data32, (PVOID)user32);
503 /******************************************************************
504 * pcs_callback (internal)
506 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
508 TRACE("%p %u %p\n", pcs, action, data);
510 if (!pcs->reg_cb) return FALSE;
511 if (!pcs->reg_is_unicode)
513 IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
514 IMAGEHLP_DEFERRED_SYMBOL_LOADW64* idslW;
516 switch (action)
518 case CBA_DEBUG_INFO:
519 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
520 case CBA_SET_OPTIONS:
521 case CBA_SYMBOLS_UNLOADED:
522 break;
523 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
524 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
525 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
526 case CBA_DEFERRED_SYMBOL_LOAD_START:
527 idslW = data;
528 idsl.SizeOfStruct = sizeof(idsl);
529 idsl.BaseOfImage = idslW->BaseOfImage;
530 idsl.CheckSum = idslW->CheckSum;
531 idsl.TimeDateStamp = idslW->TimeDateStamp;
532 WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1,
533 idsl.FileName, sizeof(idsl.FileName), NULL, NULL);
534 idsl.Reparse = idslW->Reparse;
535 data = &idsl;
536 break;
537 case CBA_DUPLICATE_SYMBOL:
538 case CBA_EVENT:
539 case CBA_READ_MEMORY:
540 default:
541 FIXME("No mapping for action %u\n", action);
542 return FALSE;
545 return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
548 /******************************************************************
549 * sym_register_cb
551 * Helper for registering a callback.
553 static BOOL sym_register_cb(HANDLE hProcess,
554 PSYMBOL_REGISTERED_CALLBACK64 cb,
555 DWORD64 user, BOOL unicode)
557 struct process* pcs = process_find_by_handle(hProcess);
559 if (!pcs) return FALSE;
560 pcs->reg_cb = cb;
561 pcs->reg_is_unicode = unicode;
562 pcs->reg_user = user;
564 return TRUE;
567 /***********************************************************************
568 * SymRegisterCallback (DBGHELP.@)
570 BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
571 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
572 PVOID UserContext)
574 DWORD64 tmp = ((ULONGLONG)(DWORD)CallbackFunction << 32) | (DWORD)UserContext;
575 TRACE("(%p, %p, %p)\n",
576 hProcess, CallbackFunction, UserContext);
577 return sym_register_cb(hProcess, reg_cb64to32, tmp, FALSE);
580 /***********************************************************************
581 * SymRegisterCallback64 (DBGHELP.@)
583 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess,
584 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
585 ULONG64 UserContext)
587 TRACE("(%p, %p, %s)\n",
588 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
589 return sym_register_cb(hProcess, CallbackFunction, UserContext, FALSE);
592 /***********************************************************************
593 * SymRegisterCallbackW64 (DBGHELP.@)
595 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess,
596 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
597 ULONG64 UserContext)
599 TRACE("(%p, %p, %s)\n",
600 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
601 return sym_register_cb(hProcess, CallbackFunction, UserContext, TRUE);
604 /* This is imagehlp version not dbghelp !! */
605 static API_VERSION api_version = { 4, 0, 2, 0 };
607 /***********************************************************************
608 * ImagehlpApiVersion (DBGHELP.@)
610 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
612 return &api_version;
615 /***********************************************************************
616 * ImagehlpApiVersionEx (DBGHELP.@)
618 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
620 if (!AppVersion) return NULL;
622 AppVersion->MajorVersion = api_version.MajorVersion;
623 AppVersion->MinorVersion = api_version.MinorVersion;
624 AppVersion->Revision = api_version.Revision;
625 AppVersion->Reserved = api_version.Reserved;
627 return AppVersion;
630 /******************************************************************
631 * ExtensionApiVersion (DBGHELP.@)
633 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
635 static EXT_API_VERSION eav = {5, 5, 5, 0};
636 return &eav;
639 /******************************************************************
640 * WinDbgExtensionDllInit (DBGHELP.@)
642 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
643 unsigned short major, unsigned short minor)