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
23 #include "dbghelp_private.h"
26 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
33 * - support for symbols' types is still partly missing
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
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
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
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)
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
67 unsigned dbghelp_options = SYMOPT_UNDNAME;
68 HANDLE hMsvcrt = NULL;
70 /***********************************************************************
71 * DllMain (DEBUGHLP.@)
73 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
77 case DLL_PROCESS_ATTACH: break;
78 case DLL_PROCESS_DETACH:
79 if (hMsvcrt) FreeLibrary(hMsvcrt);
81 case DLL_THREAD_ATTACH: break;
82 case DLL_THREAD_DETACH: break;
88 static struct process* process_first /* = NULL */;
90 /******************************************************************
91 * process_find_by_handle
94 struct process* process_find_by_handle(HANDLE hProcess)
98 for (p = process_first; p && p->handle != hProcess; p = p->next);
99 if (!p) SetLastError(ERROR_INVALID_HANDLE);
103 /******************************************************************
104 * validate_addr64 (internal)
107 BOOL validate_addr64(DWORD64 addr)
109 if (sizeof(void*) == sizeof(int) && (addr >> 32))
111 FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
112 SetLastError(ERROR_INVALID_PARAMETER);
118 /******************************************************************
121 * Ensures process' internal buffer is large enough.
123 void* fetch_buffer(struct process* pcs, unsigned size)
125 if (size > pcs->buffer_size)
128 pcs->buffer = HeapReAlloc(GetProcessHeap(), 0, pcs->buffer, size);
130 pcs->buffer = HeapAlloc(GetProcessHeap(), 0, size);
131 pcs->buffer_size = (pcs->buffer) ? size : 0;
136 const char* wine_dbgstr_addr(const ADDRESS64* addr)
138 if (!addr) return "(null)";
142 return wine_dbg_sprintf("flat<%s>", wine_dbgstr_longlong(addr->Offset));
144 return wine_dbg_sprintf("1616<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
146 return wine_dbg_sprintf("1632<%04x:%08x>", addr->Segment, (DWORD)addr->Offset);
148 return wine_dbg_sprintf("real<%04x:%04x>", addr->Segment, (DWORD)addr->Offset);
154 extern struct cpu cpu_i386, cpu_x86_64, cpu_ppc, cpu_sparc, cpu_arm, cpu_arm64;
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__)
160 #elif defined(__x86_64__)
162 #elif defined(__powerpc__)
164 #elif defined(__sparc__)
166 #elif defined(__arm__)
168 #elif defined(__aarch64__)
171 #error define support for your CPU
175 struct cpu* cpu_find(DWORD machine)
179 for (cpu = dbghelp_cpus ; *cpu; cpu++)
181 if (cpu[0]->machine == machine) return cpu[0];
186 /******************************************************************
187 * SymSetSearchPathW (DBGHELP.@)
190 BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath)
192 struct process* pcs = process_find_by_handle(hProcess);
194 if (!pcs) return FALSE;
195 if (!searchPath) return FALSE;
197 HeapFree(GetProcessHeap(), 0, pcs->search_path);
198 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
199 (lstrlenW(searchPath) + 1) * sizeof(WCHAR)),
204 /******************************************************************
205 * SymSetSearchPath (DBGHELP.@)
208 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
214 len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0);
215 if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
217 MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len);
219 ret = SymSetSearchPathW(hProcess, sp);
220 HeapFree(GetProcessHeap(), 0, sp);
225 /***********************************************************************
226 * SymGetSearchPathW (DBGHELP.@)
228 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath,
229 DWORD SearchPathLength)
231 struct process* pcs = process_find_by_handle(hProcess);
232 if (!pcs) return FALSE;
234 lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength);
238 /***********************************************************************
239 * SymGetSearchPath (DBGHELP.@)
241 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath,
242 DWORD SearchPathLength)
244 WCHAR* buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR));
249 ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength);
251 WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
252 szSearchPath, SearchPathLength, NULL, NULL);
253 HeapFree(GetProcessHeap(), 0, buffer);
258 /******************************************************************
261 * SymInitialize helper: loads in dbghelp all known (and loaded modules)
262 * this assumes that hProcess is a handle on a valid process
264 static BOOL WINAPI process_invade_cb(PCWSTR name, ULONG64 base, ULONG size, PVOID user)
267 HANDLE hProcess = user;
269 if (!GetModuleFileNameExW(hProcess, (HMODULE)(DWORD_PTR)base,
270 tmp, sizeof(tmp) / sizeof(WCHAR)))
271 lstrcpynW(tmp, name, sizeof(tmp) / sizeof(WCHAR));
273 SymLoadModuleExW(hProcess, 0, tmp, name, base, size, NULL, 0);
277 /******************************************************************
281 static BOOL check_live_target(struct process* pcs)
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);
290 /******************************************************************
291 * SymInitializeW (DBGHELP.@)
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).
316 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
320 TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
322 if (process_find_by_handle(hProcess)){
323 WARN("the symbols for this process have already been initialized!\n");
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. */
331 pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
332 if (!pcs) return FALSE;
334 pcs->handle = hProcess;
338 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
339 (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
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};
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));
354 len = GetEnvironmentVariableW(sym_path, NULL, 0);
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);
362 len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
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);
371 pcs->lmodules = NULL;
372 pcs->dbg_hdr_addr = 0;
373 pcs->next = process_first;
376 if (check_live_target(pcs))
379 EnumerateLoadedModulesW64(hProcess, process_invade_cb, hProcess);
380 elf_synchronize_module_list(pcs);
381 macho_synchronize_module_list(pcs);
383 else if (fInvadeProcess)
385 SymCleanup(hProcess);
386 SetLastError(ERROR_INVALID_PARAMETER);
393 /******************************************************************
394 * SymInitialize (DBGHELP.@)
398 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
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);
412 ret = SymInitializeW(hProcess, sp, fInvadeProcess);
413 HeapFree(GetProcessHeap(), 0, sp);
417 /******************************************************************
418 * SymCleanup (DBGHELP.@)
421 BOOL WINAPI SymCleanup(HANDLE hProcess)
423 struct process** ppcs;
424 struct process* next;
426 for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
428 if ((*ppcs)->handle == hProcess)
430 while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
432 HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
433 next = (*ppcs)->next;
434 HeapFree(GetProcessHeap(), 0, *ppcs);
440 ERR("this process has not had SymInitialize() called for it!\n");
444 /******************************************************************
445 * SymSetOptions (DBGHELP.@)
448 DWORD WINAPI SymSetOptions(DWORD opts)
452 for (pcs = process_first; pcs; pcs = pcs->next)
454 pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
456 return dbghelp_options = opts;
459 /******************************************************************
460 * SymGetOptions (DBGHELP.@)
463 DWORD WINAPI SymGetOptions(void)
465 return dbghelp_options;
468 /******************************************************************
469 * SymSetParentWindow (DBGHELP.@)
472 BOOL WINAPI SymSetParentWindow(HWND hwnd)
474 /* Save hwnd so it can be used as parent window */
475 FIXME("(%p): stub\n", hwnd);
479 /******************************************************************
480 * SymSetContext (DBGHELP.@)
483 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
484 PIMAGEHLP_CONTEXT Context)
486 struct process* pcs = process_find_by_handle(hProcess);
487 if (!pcs) return FALSE;
489 if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
490 pcs->ctx_frame.FrameOffset == StackFrame->FrameOffset &&
491 pcs->ctx_frame.StackOffset == StackFrame->StackOffset)
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 */
502 pcs->ctx_frame = *StackFrame;
503 /* MSDN states that Context is not (no longer?) used */
507 /******************************************************************
508 * reg_cb64to32 (internal)
510 * Registered callback for converting information from 64 bit to 32 bit
512 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
514 struct process* pcs = process_find_by_handle(hProcess);
516 IMAGEHLP_DEFERRED_SYMBOL_LOAD64* idsl64;
517 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl;
519 if (!pcs) return FALSE;
523 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
524 case CBA_SET_OPTIONS:
525 case CBA_SYMBOLS_UNLOADED:
526 data32 = (void*)(DWORD_PTR)data;
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))
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;
543 case CBA_DUPLICATE_SYMBOL:
545 case CBA_READ_MEMORY:
547 FIXME("No mapping for action %u\n", action);
550 return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user);
553 /******************************************************************
554 * pcs_callback (internal)
556 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
558 IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
560 TRACE("%p %u %p\n", pcs, action, data);
562 if (!pcs->reg_cb) return FALSE;
563 if (!pcs->reg_is_unicode)
565 IMAGEHLP_DEFERRED_SYMBOL_LOADW64* idslW;
570 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
571 case CBA_SET_OPTIONS:
572 case CBA_SYMBOLS_UNLOADED:
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:
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;
588 case CBA_DUPLICATE_SYMBOL:
590 case CBA_READ_MEMORY:
592 FIXME("No mapping for action %u\n", action);
596 return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
599 /******************************************************************
602 * Helper for registering a callback.
604 static BOOL sym_register_cb(HANDLE hProcess,
605 PSYMBOL_REGISTERED_CALLBACK64 cb,
606 PSYMBOL_REGISTERED_CALLBACK cb32,
607 DWORD64 user, BOOL unicode)
609 struct process* pcs = process_find_by_handle(hProcess);
611 if (!pcs) return FALSE;
613 pcs->reg_cb32 = cb32;
614 pcs->reg_is_unicode = unicode;
615 pcs->reg_user = user;
620 /***********************************************************************
621 * SymRegisterCallback (DBGHELP.@)
623 BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
624 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
627 TRACE("(%p, %p, %p)\n",
628 hProcess, CallbackFunction, UserContext);
629 return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE);
632 /***********************************************************************
633 * SymRegisterCallback64 (DBGHELP.@)
635 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess,
636 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
639 TRACE("(%p, %p, %s)\n",
640 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
641 return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, FALSE);
644 /***********************************************************************
645 * SymRegisterCallbackW64 (DBGHELP.@)
647 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess,
648 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
651 TRACE("(%p, %p, %s)\n",
652 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
653 return sym_register_cb(hProcess, CallbackFunction, NULL, UserContext, TRUE);
656 /* This is imagehlp version not dbghelp !! */
657 static API_VERSION api_version = { 4, 0, 2, 0 };
659 /***********************************************************************
660 * ImagehlpApiVersion (DBGHELP.@)
662 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
667 /***********************************************************************
668 * ImagehlpApiVersionEx (DBGHELP.@)
670 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
672 if (!AppVersion) return NULL;
674 AppVersion->MajorVersion = api_version.MajorVersion;
675 AppVersion->MinorVersion = api_version.MinorVersion;
676 AppVersion->Revision = api_version.Revision;
677 AppVersion->Reserved = api_version.Reserved;
682 /******************************************************************
683 * ExtensionApiVersion (DBGHELP.@)
685 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
687 static EXT_API_VERSION eav = {5, 5, 5, 0};
691 /******************************************************************
692 * WinDbgExtensionDllInit (DBGHELP.@)
694 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
695 unsigned short major, unsigned short minor)