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 WARN("the symbols for this process have already been initialized!\n");
274 /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
275 It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
276 Native still returns TRUE even if the process has already been initialized. */
280 pcs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pcs
));
281 if (!pcs
) return FALSE
;
283 pcs
->handle
= hProcess
;
287 pcs
->search_path
= lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
288 (lstrlenW(UserSearchPath
) + 1) * sizeof(WCHAR
)),
295 static const WCHAR sym_path
[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
296 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};
298 pcs
->search_path
= HeapAlloc(GetProcessHeap(), 0, (len
= MAX_PATH
) * sizeof(WCHAR
));
299 while ((size
= GetCurrentDirectoryW(len
, pcs
->search_path
)) >= len
)
300 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (len
*= 2) * sizeof(WCHAR
));
301 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (size
+ 1) * sizeof(WCHAR
));
303 len
= GetEnvironmentVariableW(sym_path
, NULL
, 0);
306 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (size
+ 1 + len
+ 1) * sizeof(WCHAR
));
307 pcs
->search_path
[size
] = ';';
308 GetEnvironmentVariableW(sym_path
, pcs
->search_path
+ size
+ 1, len
);
311 len
= GetEnvironmentVariableW(alt_sym_path
, NULL
, 0);
314 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (size
+ 1 + len
+ 1) * sizeof(WCHAR
));
315 pcs
->search_path
[size
] = ';';
316 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
;
326 if (check_live_target(pcs
))
329 EnumerateLoadedModules(hProcess
, process_invade_cb
, hProcess
);
330 elf_synchronize_module_list(pcs
);
332 else if (fInvadeProcess
)
334 SymCleanup(hProcess
);
335 SetLastError(ERROR_INVALID_PARAMETER
);
342 /******************************************************************
343 * SymInitialize (DBGHELP.@)
347 BOOL WINAPI
SymInitialize(HANDLE hProcess
, PCSTR UserSearchPath
, BOOL fInvadeProcess
)
356 len
= MultiByteToWideChar(CP_ACP
, 0, UserSearchPath
, -1, NULL
, 0);
357 sp
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
358 MultiByteToWideChar(CP_ACP
, 0, UserSearchPath
, -1, sp
, len
);
361 ret
= SymInitializeW(hProcess
, sp
, fInvadeProcess
);
362 HeapFree(GetProcessHeap(), 0, sp
);
366 /******************************************************************
367 * SymCleanup (DBGHELP.@)
370 BOOL WINAPI
SymCleanup(HANDLE hProcess
)
372 struct process
** ppcs
;
373 struct process
* next
;
375 for (ppcs
= &process_first
; *ppcs
; ppcs
= &(*ppcs
)->next
)
377 if ((*ppcs
)->handle
== hProcess
)
379 while ((*ppcs
)->lmodules
) module_remove(*ppcs
, (*ppcs
)->lmodules
);
381 HeapFree(GetProcessHeap(), 0, (*ppcs
)->search_path
);
382 next
= (*ppcs
)->next
;
383 HeapFree(GetProcessHeap(), 0, *ppcs
);
389 ERR("this process has not had SymInitialize() called for it!\n");
393 /******************************************************************
394 * SymSetOptions (DBGHELP.@)
397 DWORD WINAPI
SymSetOptions(DWORD opts
)
401 for (pcs
= process_first
; pcs
; pcs
= pcs
->next
)
403 pcs_callback(pcs
, CBA_SET_OPTIONS
, &opts
);
405 return dbghelp_options
= opts
;
408 /******************************************************************
409 * SymGetOptions (DBGHELP.@)
412 DWORD WINAPI
SymGetOptions(void)
414 return dbghelp_options
;
417 /******************************************************************
418 * SymSetParentWindow (DBGHELP.@)
421 BOOL WINAPI
SymSetParentWindow(HWND hwnd
)
423 /* Save hwnd so it can be used as parent window */
424 FIXME("(%p): stub\n", hwnd
);
428 /******************************************************************
429 * SymSetContext (DBGHELP.@)
432 BOOL WINAPI
SymSetContext(HANDLE hProcess
, PIMAGEHLP_STACK_FRAME StackFrame
,
433 PIMAGEHLP_CONTEXT Context
)
435 struct process
* pcs
= process_find_by_handle(hProcess
);
436 if (!pcs
) return FALSE
;
438 if (pcs
->ctx_frame
.ReturnOffset
== StackFrame
->ReturnOffset
&&
439 pcs
->ctx_frame
.FrameOffset
== StackFrame
->FrameOffset
&&
440 pcs
->ctx_frame
.StackOffset
== StackFrame
->StackOffset
)
442 TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
443 wine_dbgstr_longlong(pcs
->ctx_frame
.ReturnOffset
),
444 wine_dbgstr_longlong(pcs
->ctx_frame
.FrameOffset
),
445 wine_dbgstr_longlong(pcs
->ctx_frame
.StackOffset
));
446 pcs
->ctx_frame
.InstructionOffset
= StackFrame
->InstructionOffset
;
447 SetLastError(ERROR_ACCESS_DENIED
); /* latest MSDN says ERROR_SUCCESS */
451 pcs
->ctx_frame
= *StackFrame
;
452 /* MSDN states that Context is not (no longer?) used */
456 /******************************************************************
457 * reg_cb64to32 (internal)
459 * Registered callback for converting information from 64 bit to 32 bit
461 static BOOL CALLBACK
reg_cb64to32(HANDLE hProcess
, ULONG action
, ULONG64 data
, ULONG64 user
)
463 PSYMBOL_REGISTERED_CALLBACK cb32
= (PSYMBOL_REGISTERED_CALLBACK
)(DWORD
)(user
>> 32);
464 DWORD user32
= (DWORD
)user
;
466 IMAGEHLP_DEFERRED_SYMBOL_LOAD64
* idsl64
;
467 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl
;
472 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL
:
473 case CBA_SET_OPTIONS
:
474 case CBA_SYMBOLS_UNLOADED
:
475 data32
= (void*)(DWORD
)data
;
477 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE
:
478 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE
:
479 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL
:
480 case CBA_DEFERRED_SYMBOL_LOAD_START
:
481 idsl64
= (IMAGEHLP_DEFERRED_SYMBOL_LOAD64
*)(DWORD
)data
;
482 if (!validate_addr64(idsl64
->BaseOfImage
))
484 idsl
.SizeOfStruct
= sizeof(idsl
);
485 idsl
.BaseOfImage
= (DWORD
)idsl64
->BaseOfImage
;
486 idsl
.CheckSum
= idsl64
->CheckSum
;
487 idsl
.TimeDateStamp
= idsl64
->TimeDateStamp
;
488 memcpy(idsl
.FileName
, idsl64
->FileName
, sizeof(idsl
.FileName
));
489 idsl
.Reparse
= idsl64
->Reparse
;
492 case CBA_DUPLICATE_SYMBOL
:
494 case CBA_READ_MEMORY
:
496 FIXME("No mapping for action %u\n", action
);
499 return cb32(hProcess
, action
, data32
, (PVOID
)user32
);
502 /******************************************************************
503 * pcs_callback (internal)
505 BOOL
pcs_callback(const struct process
* pcs
, ULONG action
, void* data
)
507 TRACE("%p %u %p\n", pcs
, action
, data
);
509 if (!pcs
->reg_cb
) return FALSE
;
510 if (!pcs
->reg_is_unicode
)
512 IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl
;
513 IMAGEHLP_DEFERRED_SYMBOL_LOADW64
* idslW
;
518 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL
:
519 case CBA_SET_OPTIONS
:
520 case CBA_SYMBOLS_UNLOADED
:
522 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE
:
523 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE
:
524 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL
:
525 case CBA_DEFERRED_SYMBOL_LOAD_START
:
526 idslW
= (IMAGEHLP_DEFERRED_SYMBOL_LOADW64
*)(DWORD
)data
;
527 idsl
.SizeOfStruct
= sizeof(idsl
);
528 idsl
.BaseOfImage
= idslW
->BaseOfImage
;
529 idsl
.CheckSum
= idslW
->CheckSum
;
530 idsl
.TimeDateStamp
= idslW
->TimeDateStamp
;
531 WideCharToMultiByte(CP_ACP
, 0, idslW
->FileName
, -1,
532 idsl
.FileName
, sizeof(idsl
.FileName
), NULL
, NULL
);
533 idsl
.Reparse
= idslW
->Reparse
;
536 case CBA_DUPLICATE_SYMBOL
:
538 case CBA_READ_MEMORY
:
540 FIXME("No mapping for action %u\n", action
);
544 return pcs
->reg_cb(pcs
->handle
, action
, (ULONG64
)(DWORD_PTR
)data
, pcs
->reg_user
);
547 /******************************************************************
550 * Helper for registering a callback.
552 static BOOL
sym_register_cb(HANDLE hProcess
,
553 PSYMBOL_REGISTERED_CALLBACK64 cb
,
554 DWORD64 user
, BOOL unicode
)
556 struct process
* pcs
= process_find_by_handle(hProcess
);
558 if (!pcs
) return FALSE
;
560 pcs
->reg_is_unicode
= unicode
;
561 pcs
->reg_user
= user
;
566 /***********************************************************************
567 * SymRegisterCallback (DBGHELP.@)
569 BOOL WINAPI
SymRegisterCallback(HANDLE hProcess
,
570 PSYMBOL_REGISTERED_CALLBACK CallbackFunction
,
573 DWORD64 tmp
= ((ULONGLONG
)(DWORD
)CallbackFunction
<< 32) | (DWORD
)UserContext
;
574 TRACE("(%p, %p, %p)\n",
575 hProcess
, CallbackFunction
, UserContext
);
576 return sym_register_cb(hProcess
, reg_cb64to32
, tmp
, FALSE
);
579 /***********************************************************************
580 * SymRegisterCallback64 (DBGHELP.@)
582 BOOL WINAPI
SymRegisterCallback64(HANDLE hProcess
,
583 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction
,
586 TRACE("(%p, %p, %s)\n",
587 hProcess
, CallbackFunction
, wine_dbgstr_longlong(UserContext
));
588 return sym_register_cb(hProcess
, CallbackFunction
, UserContext
, FALSE
);
591 /***********************************************************************
592 * SymRegisterCallbackW64 (DBGHELP.@)
594 BOOL WINAPI
SymRegisterCallbackW64(HANDLE hProcess
,
595 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction
,
598 TRACE("(%p, %p, %s)\n",
599 hProcess
, CallbackFunction
, wine_dbgstr_longlong(UserContext
));
600 return sym_register_cb(hProcess
, CallbackFunction
, UserContext
, TRUE
);
603 /* This is imagehlp version not dbghelp !! */
604 static API_VERSION api_version
= { 4, 0, 2, 0 };
606 /***********************************************************************
607 * ImagehlpApiVersion (DBGHELP.@)
609 LPAPI_VERSION WINAPI
ImagehlpApiVersion(VOID
)
614 /***********************************************************************
615 * ImagehlpApiVersionEx (DBGHELP.@)
617 LPAPI_VERSION WINAPI
ImagehlpApiVersionEx(LPAPI_VERSION AppVersion
)
619 if (!AppVersion
) return NULL
;
621 AppVersion
->MajorVersion
= api_version
.MajorVersion
;
622 AppVersion
->MinorVersion
= api_version
.MinorVersion
;
623 AppVersion
->Revision
= api_version
.Revision
;
624 AppVersion
->Reserved
= api_version
.Reserved
;
629 /******************************************************************
630 * ExtensionApiVersion (DBGHELP.@)
632 LPEXT_API_VERSION WINAPI
ExtensionApiVersion(void)
634 static EXT_API_VERSION eav
= {5, 5, 5, 0};
638 /******************************************************************
639 * WinDbgExtensionDllInit (DBGHELP.@)
641 void WINAPI
WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis
,
642 unsigned short major
, unsigned short minor
)