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
)
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 /******************************************************************
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
)),
154 /******************************************************************
155 * SymSetSearchPath (DBGHELP.@)
158 BOOL WINAPI
SymSetSearchPath(HANDLE hProcess
, PCSTR searchPath
)
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
);
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
);
188 /***********************************************************************
189 * SymGetSearchPath (DBGHELP.@)
191 BOOL WINAPI
SymGetSearchPath(HANDLE hProcess
, PSTR szSearchPath
,
192 DWORD SearchPathLength
)
194 WCHAR
* buffer
= HeapAlloc(GetProcessHeap(), 0, SearchPathLength
* sizeof(WCHAR
));
199 ret
= SymGetSearchPathW(hProcess
, buffer
, SearchPathLength
);
201 WideCharToMultiByte(CP_ACP
, 0, buffer
, SearchPathLength
,
202 szSearchPath
, SearchPathLength
, NULL
, NULL
);
203 HeapFree(GetProcessHeap(), 0, buffer
);
208 /******************************************************************
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
)
217 HANDLE hProcess
= user
;
219 if (!GetModuleFileNameExA(hProcess
, (HMODULE
)base
,
221 lstrcpynA(tmp
, name
, sizeof(tmp
));
223 SymLoadModule(hProcess
, 0, tmp
, name
, base
, size
);
227 /******************************************************************
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 elf_read_wine_loader_dbg_info(pcs
);
239 /******************************************************************
240 * SymInitializeW (DBGHELP.@)
242 * The initialisation of a dbghelp's context.
243 * Note that hProcess doesn't need to be a valid process handle (except
244 * when fInvadeProcess is TRUE).
245 * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries
246 * containing PE (and NE) module(s), here's how we handle it:
247 * - we load every module (ELF, NE, PE) passed in SymLoadModule
248 * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
249 * synchronization: hProcess should be a valid process handle, and we hook
250 * ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
251 * our internal ELF modules representation (loading / unloading). This way,
252 * we'll pair every loaded builtin PE module with its ELF counterpart (and
253 * access its debug information).
254 * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
255 * hProcess refers to a running process. We use some heuristics here, so YMMV.
256 * If we detect a live target, then we get the same handling as if
257 * fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
258 * we won't be able to make the peering between a builtin PE module and its ELF
259 * counterpart. Hence we won't be able to provide the requested debug
260 * information. We'll however be able to load native PE modules (and their
261 * debug information) without any trouble.
262 * Note also that this scheme can be intertwined with the deferred loading
263 * mechanism (ie only load the debug information when we actually need it).
265 BOOL WINAPI
SymInitializeW(HANDLE hProcess
, PCWSTR UserSearchPath
, BOOL fInvadeProcess
)
269 TRACE("(%p %s %u)\n", hProcess
, debugstr_w(UserSearchPath
), fInvadeProcess
);
271 if (process_find_by_handle(hProcess
))
272 FIXME("what to do ??\n");
274 pcs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pcs
));
275 if (!pcs
) return FALSE
;
277 pcs
->handle
= hProcess
;
281 pcs
->search_path
= lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
282 (lstrlenW(UserSearchPath
) + 1) * sizeof(WCHAR
)),
289 static const WCHAR sym_path
[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
290 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};
292 pcs
->search_path
= HeapAlloc(GetProcessHeap(), 0, (len
= MAX_PATH
) * sizeof(WCHAR
));
293 while ((size
= GetCurrentDirectoryW(len
, pcs
->search_path
)) >= len
)
294 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (len
*= 2) * sizeof(WCHAR
));
295 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (size
+ 1) * sizeof(WCHAR
));
297 len
= GetEnvironmentVariableW(sym_path
, NULL
, 0);
300 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (size
+ 1 + len
+ 1) * sizeof(WCHAR
));
301 pcs
->search_path
[size
] = ';';
302 GetEnvironmentVariableW(sym_path
, pcs
->search_path
+ size
+ 1, len
);
305 len
= GetEnvironmentVariableW(alt_sym_path
, NULL
, 0);
308 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (size
+ 1 + len
+ 1) * sizeof(WCHAR
));
309 pcs
->search_path
[size
] = ';';
310 GetEnvironmentVariableW(alt_sym_path
, pcs
->search_path
+ size
+ 1, len
);
315 pcs
->lmodules
= NULL
;
316 pcs
->dbg_hdr_addr
= 0;
317 pcs
->next
= process_first
;
320 if (check_live_target(pcs
))
323 EnumerateLoadedModules(hProcess
, process_invade_cb
, hProcess
);
324 elf_synchronize_module_list(pcs
);
326 else if (fInvadeProcess
)
328 SymCleanup(hProcess
);
329 SetLastError(ERROR_INVALID_PARAMETER
);
336 /******************************************************************
337 * SymInitialize (DBGHELP.@)
341 BOOL WINAPI
SymInitialize(HANDLE hProcess
, PCSTR UserSearchPath
, BOOL fInvadeProcess
)
350 len
= MultiByteToWideChar(CP_ACP
, 0, UserSearchPath
, -1, NULL
, 0);
351 sp
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
352 MultiByteToWideChar(CP_ACP
, 0, UserSearchPath
, -1, sp
, len
);
355 ret
= SymInitializeW(hProcess
, sp
, fInvadeProcess
);
356 HeapFree(GetProcessHeap(), 0, sp
);
360 /******************************************************************
361 * SymCleanup (DBGHELP.@)
364 BOOL WINAPI
SymCleanup(HANDLE hProcess
)
366 struct process
** ppcs
;
367 struct process
* next
;
369 for (ppcs
= &process_first
; *ppcs
; ppcs
= &(*ppcs
)->next
)
371 if ((*ppcs
)->handle
== hProcess
)
373 while ((*ppcs
)->lmodules
) module_remove(*ppcs
, (*ppcs
)->lmodules
);
375 HeapFree(GetProcessHeap(), 0, (*ppcs
)->search_path
);
376 next
= (*ppcs
)->next
;
377 HeapFree(GetProcessHeap(), 0, *ppcs
);
385 /******************************************************************
386 * SymSetOptions (DBGHELP.@)
389 DWORD WINAPI
SymSetOptions(DWORD opts
)
393 for (pcs
= process_first
; pcs
; pcs
= pcs
->next
)
395 pcs_callback(pcs
, CBA_SET_OPTIONS
, &opts
);
397 return dbghelp_options
= opts
;
400 /******************************************************************
401 * SymGetOptions (DBGHELP.@)
404 DWORD WINAPI
SymGetOptions(void)
406 return dbghelp_options
;
409 /******************************************************************
410 * SymSetParentWindow (DBGHELP.@)
413 BOOL WINAPI
SymSetParentWindow(HWND hwnd
)
415 /* Save hwnd so it can be used as parent window */
416 FIXME("(%p): stub\n", hwnd
);
420 /******************************************************************
421 * SymSetContext (DBGHELP.@)
424 BOOL WINAPI
SymSetContext(HANDLE hProcess
, PIMAGEHLP_STACK_FRAME StackFrame
,
425 PIMAGEHLP_CONTEXT Context
)
427 struct process
* pcs
= process_find_by_handle(hProcess
);
428 if (!pcs
) return FALSE
;
430 if (pcs
->ctx_frame
.ReturnOffset
== StackFrame
->ReturnOffset
&&
431 pcs
->ctx_frame
.FrameOffset
== StackFrame
->FrameOffset
&&
432 pcs
->ctx_frame
.StackOffset
== StackFrame
->StackOffset
)
434 TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
435 wine_dbgstr_longlong(pcs
->ctx_frame
.ReturnOffset
),
436 wine_dbgstr_longlong(pcs
->ctx_frame
.FrameOffset
),
437 wine_dbgstr_longlong(pcs
->ctx_frame
.StackOffset
));
438 pcs
->ctx_frame
.InstructionOffset
= StackFrame
->InstructionOffset
;
439 SetLastError(ERROR_ACCESS_DENIED
); /* latest MSDN says ERROR_SUCCESS */
443 pcs
->ctx_frame
= *StackFrame
;
444 /* MSDN states that Context is not (no longer?) used */
448 /******************************************************************
449 * reg_cb64to32 (internal)
451 * Registered callback for converting information from 64 bit to 32 bit
453 static BOOL CALLBACK
reg_cb64to32(HANDLE hProcess
, ULONG action
, ULONG64 data
, ULONG64 user
)
455 PSYMBOL_REGISTERED_CALLBACK cb32
= (PSYMBOL_REGISTERED_CALLBACK
)(DWORD
)(user
>> 32);
456 DWORD user32
= (DWORD
)user
;
458 IMAGEHLP_DEFERRED_SYMBOL_LOAD64
* idsl64
;
459 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl
;
464 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL
:
465 case CBA_SET_OPTIONS
:
466 case CBA_SYMBOLS_UNLOADED
:
467 data32
= (void*)(DWORD
)data
;
469 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE
:
470 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE
:
471 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL
:
472 case CBA_DEFERRED_SYMBOL_LOAD_START
:
473 idsl64
= (IMAGEHLP_DEFERRED_SYMBOL_LOAD64
*)(DWORD
)data
;
474 if (!validate_addr64(idsl64
->BaseOfImage
))
476 idsl
.SizeOfStruct
= sizeof(idsl
);
477 idsl
.BaseOfImage
= (DWORD
)idsl64
->BaseOfImage
;
478 idsl
.CheckSum
= idsl64
->CheckSum
;
479 idsl
.TimeDateStamp
= idsl64
->TimeDateStamp
;
480 memcpy(idsl
.FileName
, idsl64
->FileName
, sizeof(idsl
.FileName
));
481 idsl
.Reparse
= idsl64
->Reparse
;
484 case CBA_DUPLICATE_SYMBOL
:
486 case CBA_READ_MEMORY
:
488 FIXME("No mapping for action %u\n", action
);
491 return cb32(hProcess
, action
, data32
, (PVOID
)user32
);
494 /******************************************************************
495 * pcs_callback (internal)
497 BOOL
pcs_callback(const struct process
* pcs
, ULONG action
, void* data
)
499 TRACE("%p %u %p\n", pcs
, action
, data
);
501 if (!pcs
->reg_cb
) return FALSE
;
502 if (!pcs
->reg_is_unicode
)
504 IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl
;
505 IMAGEHLP_DEFERRED_SYMBOL_LOADW64
* idslW
;
510 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL
:
511 case CBA_SET_OPTIONS
:
512 case CBA_SYMBOLS_UNLOADED
:
514 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE
:
515 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE
:
516 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL
:
517 case CBA_DEFERRED_SYMBOL_LOAD_START
:
518 idslW
= (IMAGEHLP_DEFERRED_SYMBOL_LOADW64
*)(DWORD
)data
;
519 idsl
.SizeOfStruct
= sizeof(idsl
);
520 idsl
.BaseOfImage
= idslW
->BaseOfImage
;
521 idsl
.CheckSum
= idslW
->CheckSum
;
522 idsl
.TimeDateStamp
= idslW
->TimeDateStamp
;
523 WideCharToMultiByte(CP_ACP
, 0, idslW
->FileName
, -1,
524 idsl
.FileName
, sizeof(idsl
.FileName
), NULL
, NULL
);
525 idsl
.Reparse
= idslW
->Reparse
;
528 case CBA_DUPLICATE_SYMBOL
:
530 case CBA_READ_MEMORY
:
532 FIXME("No mapping for action %u\n", action
);
536 return pcs
->reg_cb(pcs
->handle
, action
, (ULONG64
)(DWORD_PTR
)data
, pcs
->reg_user
);
539 /******************************************************************
542 * Helper for registering a callback.
544 static BOOL
sym_register_cb(HANDLE hProcess
,
545 PSYMBOL_REGISTERED_CALLBACK64 cb
,
546 DWORD64 user
, BOOL unicode
)
548 struct process
* pcs
= process_find_by_handle(hProcess
);
550 if (!pcs
) return FALSE
;
552 pcs
->reg_is_unicode
= unicode
;
553 pcs
->reg_user
= user
;
558 /***********************************************************************
559 * SymRegisterCallback (DBGHELP.@)
561 BOOL WINAPI
SymRegisterCallback(HANDLE hProcess
,
562 PSYMBOL_REGISTERED_CALLBACK CallbackFunction
,
565 DWORD64 tmp
= ((ULONGLONG
)(DWORD
)CallbackFunction
<< 32) | (DWORD
)UserContext
;
566 TRACE("(%p, %p, %p)\n",
567 hProcess
, CallbackFunction
, UserContext
);
568 return sym_register_cb(hProcess
, reg_cb64to32
, tmp
, FALSE
);
571 /***********************************************************************
572 * SymRegisterCallback64 (DBGHELP.@)
574 BOOL WINAPI
SymRegisterCallback64(HANDLE hProcess
,
575 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction
,
578 TRACE("(%p, %p, %s)\n",
579 hProcess
, CallbackFunction
, wine_dbgstr_longlong(UserContext
));
580 return sym_register_cb(hProcess
, CallbackFunction
, UserContext
, FALSE
);
583 /***********************************************************************
584 * SymRegisterCallbackW64 (DBGHELP.@)
586 BOOL WINAPI
SymRegisterCallbackW64(HANDLE hProcess
,
587 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction
,
590 TRACE("(%p, %p, %s)\n",
591 hProcess
, CallbackFunction
, wine_dbgstr_longlong(UserContext
));
592 return sym_register_cb(hProcess
, CallbackFunction
, UserContext
, TRUE
);
595 /* This is imagehlp version not dbghelp !! */
596 static API_VERSION api_version
= { 4, 0, 2, 0 };
598 /***********************************************************************
599 * ImagehlpApiVersion (DBGHELP.@)
601 LPAPI_VERSION WINAPI
ImagehlpApiVersion(VOID
)
606 /***********************************************************************
607 * ImagehlpApiVersionEx (DBGHELP.@)
609 LPAPI_VERSION WINAPI
ImagehlpApiVersionEx(LPAPI_VERSION AppVersion
)
611 if (!AppVersion
) return NULL
;
613 AppVersion
->MajorVersion
= api_version
.MajorVersion
;
614 AppVersion
->MinorVersion
= api_version
.MinorVersion
;
615 AppVersion
->Revision
= api_version
.Revision
;
616 AppVersion
->Reserved
= api_version
.Reserved
;
621 /******************************************************************
622 * ExtensionApiVersion (DBGHELP.@)
624 LPEXT_API_VERSION WINAPI
ExtensionApiVersion(void)
626 static EXT_API_VERSION eav
= {5, 5, 5, 0};
630 /******************************************************************
631 * WinDbgExtensionDllInit (DBGHELP.@)
633 void WINAPI
WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis
,
634 unsigned short major
, unsigned short minor
)