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
;
69 static struct process
* process_first
/* = NULL */;
71 /******************************************************************
72 * process_find_by_handle
75 struct process
* process_find_by_handle(HANDLE hProcess
)
79 for (p
= process_first
; p
&& p
->handle
!= hProcess
; p
= p
->next
);
80 if (!p
) SetLastError(ERROR_INVALID_HANDLE
);
84 /******************************************************************
85 * validate_addr64 (internal)
88 BOOL
validate_addr64(DWORD64 addr
)
90 if (sizeof(void*) == sizeof(int) && (addr
>> 32))
92 FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr
));
93 SetLastError(ERROR_INVALID_PARAMETER
);
99 /******************************************************************
102 * Ensures process' internal buffer is large enough.
104 void* fetch_buffer(struct process
* pcs
, unsigned size
)
106 if (size
> pcs
->buffer_size
)
109 pcs
->buffer
= HeapReAlloc(GetProcessHeap(), 0, pcs
->buffer
, size
);
111 pcs
->buffer
= HeapAlloc(GetProcessHeap(), 0, size
);
112 pcs
->buffer_size
= (pcs
->buffer
) ? size
: 0;
117 const char* wine_dbgstr_addr(const ADDRESS64
* addr
)
119 if (!addr
) return "(null)";
123 return wine_dbg_sprintf("flat<%s>", wine_dbgstr_longlong(addr
->Offset
));
125 return wine_dbg_sprintf("1616<%04x:%04x>", addr
->Segment
, (DWORD
)addr
->Offset
);
127 return wine_dbg_sprintf("1632<%04x:%08x>", addr
->Segment
, (DWORD
)addr
->Offset
);
129 return wine_dbg_sprintf("real<%04x:%04x>", addr
->Segment
, (DWORD
)addr
->Offset
);
135 extern struct cpu cpu_i386
, cpu_x86_64
, cpu_ppc
, cpu_arm
, cpu_arm64
;
137 static struct cpu
* dbghelp_cpus
[] = {&cpu_i386
, &cpu_x86_64
, &cpu_ppc
, &cpu_arm
, &cpu_arm64
, NULL
};
138 struct cpu
* dbghelp_current_cpu
=
139 #if defined(__i386__)
141 #elif defined(__x86_64__)
143 #elif defined(__powerpc__)
145 #elif defined(__arm__)
147 #elif defined(__aarch64__)
150 #error define support for your CPU
154 struct cpu
* cpu_find(DWORD machine
)
158 for (cpu
= dbghelp_cpus
; *cpu
; cpu
++)
160 if (cpu
[0]->machine
== machine
) return cpu
[0];
165 /******************************************************************
166 * SymSetSearchPathW (DBGHELP.@)
169 BOOL WINAPI
SymSetSearchPathW(HANDLE hProcess
, PCWSTR searchPath
)
171 struct process
* pcs
= process_find_by_handle(hProcess
);
173 if (!pcs
) return FALSE
;
174 if (!searchPath
) return FALSE
;
176 HeapFree(GetProcessHeap(), 0, pcs
->search_path
);
177 pcs
->search_path
= lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
178 (lstrlenW(searchPath
) + 1) * sizeof(WCHAR
)),
183 /******************************************************************
184 * SymSetSearchPath (DBGHELP.@)
187 BOOL WINAPI
SymSetSearchPath(HANDLE hProcess
, PCSTR searchPath
)
193 len
= MultiByteToWideChar(CP_ACP
, 0, searchPath
, -1, NULL
, 0);
194 if ((sp
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
))))
196 MultiByteToWideChar(CP_ACP
, 0, searchPath
, -1, sp
, len
);
198 ret
= SymSetSearchPathW(hProcess
, sp
);
199 HeapFree(GetProcessHeap(), 0, sp
);
204 /***********************************************************************
205 * SymGetSearchPathW (DBGHELP.@)
207 BOOL WINAPI
SymGetSearchPathW(HANDLE hProcess
, PWSTR szSearchPath
,
208 DWORD SearchPathLength
)
210 struct process
* pcs
= process_find_by_handle(hProcess
);
211 if (!pcs
) return FALSE
;
213 lstrcpynW(szSearchPath
, pcs
->search_path
, SearchPathLength
);
217 /***********************************************************************
218 * SymGetSearchPath (DBGHELP.@)
220 BOOL WINAPI
SymGetSearchPath(HANDLE hProcess
, PSTR szSearchPath
,
221 DWORD SearchPathLength
)
223 WCHAR
* buffer
= HeapAlloc(GetProcessHeap(), 0, SearchPathLength
* sizeof(WCHAR
));
228 ret
= SymGetSearchPathW(hProcess
, buffer
, SearchPathLength
);
230 WideCharToMultiByte(CP_ACP
, 0, buffer
, SearchPathLength
,
231 szSearchPath
, SearchPathLength
, NULL
, NULL
);
232 HeapFree(GetProcessHeap(), 0, buffer
);
237 /******************************************************************
240 * SymInitialize helper: loads in dbghelp all known (and loaded modules)
241 * this assumes that hProcess is a handle on a valid process
243 static BOOL WINAPI
process_invade_cb(PCWSTR name
, ULONG64 base
, ULONG size
, PVOID user
)
246 HANDLE hProcess
= user
;
248 if (!GetModuleFileNameExW(hProcess
, (HMODULE
)(DWORD_PTR
)base
,
249 tmp
, sizeof(tmp
) / sizeof(WCHAR
)))
250 lstrcpynW(tmp
, name
, sizeof(tmp
) / sizeof(WCHAR
));
252 SymLoadModuleExW(hProcess
, 0, tmp
, name
, base
, size
, NULL
, 0);
256 /******************************************************************
260 static BOOL
check_live_target(struct process
* pcs
)
262 if (!GetProcessId(pcs
->handle
)) return FALSE
;
263 if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL
, 0)) return FALSE
;
264 if (!elf_read_wine_loader_dbg_info(pcs
))
265 macho_read_wine_loader_dbg_info(pcs
);
269 /******************************************************************
270 * SymInitializeW (DBGHELP.@)
272 * The initialisation of a dbghelp's context.
273 * Note that hProcess doesn't need to be a valid process handle (except
274 * when fInvadeProcess is TRUE).
275 * Since we also allow loading ELF (pure) libraries and Wine ELF libraries
276 * containing PE (and NE) module(s), here's how we handle it:
277 * - we load every module (ELF, NE, PE) passed in SymLoadModule
278 * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
279 * synchronization: hProcess should be a valid process handle, and we hook
280 * ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
281 * our internal ELF modules representation (loading / unloading). This way,
282 * we'll pair every loaded builtin PE module with its ELF counterpart (and
283 * access its debug information).
284 * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
285 * hProcess refers to a running process. We use some heuristics here, so YMMV.
286 * If we detect a live target, then we get the same handling as if
287 * fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
288 * we won't be able to make the peering between a builtin PE module and its ELF
289 * counterpart. Hence we won't be able to provide the requested debug
290 * information. We'll however be able to load native PE modules (and their
291 * debug information) without any trouble.
292 * Note also that this scheme can be intertwined with the deferred loading
293 * mechanism (ie only load the debug information when we actually need it).
295 BOOL WINAPI
SymInitializeW(HANDLE hProcess
, PCWSTR UserSearchPath
, BOOL fInvadeProcess
)
299 TRACE("(%p %s %u)\n", hProcess
, debugstr_w(UserSearchPath
), fInvadeProcess
);
301 if (process_find_by_handle(hProcess
)){
302 WARN("the symbols for this process have already been initialized!\n");
304 /* MSDN says to only call this function once unless SymCleanup() has been called since the last call.
305 It also says to call SymRefreshModuleList() instead if you just want the module list refreshed.
306 Native still returns TRUE even if the process has already been initialized. */
310 pcs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pcs
));
311 if (!pcs
) return FALSE
;
313 pcs
->handle
= hProcess
;
317 pcs
->search_path
= lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
318 (lstrlenW(UserSearchPath
) + 1) * sizeof(WCHAR
)),
325 static const WCHAR sym_path
[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
326 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};
328 pcs
->search_path
= HeapAlloc(GetProcessHeap(), 0, (len
= MAX_PATH
) * sizeof(WCHAR
));
329 while ((size
= GetCurrentDirectoryW(len
, pcs
->search_path
)) >= len
)
330 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (len
*= 2) * sizeof(WCHAR
));
331 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (size
+ 1) * sizeof(WCHAR
));
333 len
= GetEnvironmentVariableW(sym_path
, NULL
, 0);
336 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (size
+ 1 + len
+ 1) * sizeof(WCHAR
));
337 pcs
->search_path
[size
] = ';';
338 GetEnvironmentVariableW(sym_path
, pcs
->search_path
+ size
+ 1, len
);
341 len
= GetEnvironmentVariableW(alt_sym_path
, NULL
, 0);
344 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, (size
+ 1 + len
+ 1) * sizeof(WCHAR
));
345 pcs
->search_path
[size
] = ';';
346 GetEnvironmentVariableW(alt_sym_path
, pcs
->search_path
+ size
+ 1, len
);
350 pcs
->lmodules
= NULL
;
351 pcs
->dbg_hdr_addr
= 0;
352 pcs
->next
= process_first
;
355 if (check_live_target(pcs
))
358 EnumerateLoadedModulesW64(hProcess
, process_invade_cb
, hProcess
);
359 elf_synchronize_module_list(pcs
);
360 macho_synchronize_module_list(pcs
);
362 else if (fInvadeProcess
)
364 SymCleanup(hProcess
);
365 SetLastError(ERROR_INVALID_PARAMETER
);
372 /******************************************************************
373 * SymInitialize (DBGHELP.@)
377 BOOL WINAPI
SymInitialize(HANDLE hProcess
, PCSTR UserSearchPath
, BOOL fInvadeProcess
)
386 len
= MultiByteToWideChar(CP_ACP
, 0, UserSearchPath
, -1, NULL
, 0);
387 sp
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
388 MultiByteToWideChar(CP_ACP
, 0, UserSearchPath
, -1, sp
, len
);
391 ret
= SymInitializeW(hProcess
, sp
, fInvadeProcess
);
392 HeapFree(GetProcessHeap(), 0, sp
);
396 /******************************************************************
397 * SymCleanup (DBGHELP.@)
400 BOOL WINAPI
SymCleanup(HANDLE hProcess
)
402 struct process
** ppcs
;
403 struct process
* next
;
405 for (ppcs
= &process_first
; *ppcs
; ppcs
= &(*ppcs
)->next
)
407 if ((*ppcs
)->handle
== hProcess
)
409 while ((*ppcs
)->lmodules
) module_remove(*ppcs
, (*ppcs
)->lmodules
);
411 HeapFree(GetProcessHeap(), 0, (*ppcs
)->search_path
);
412 next
= (*ppcs
)->next
;
413 HeapFree(GetProcessHeap(), 0, *ppcs
);
419 ERR("this process has not had SymInitialize() called for it!\n");
423 /******************************************************************
424 * SymSetOptions (DBGHELP.@)
427 DWORD WINAPI
SymSetOptions(DWORD opts
)
431 for (pcs
= process_first
; pcs
; pcs
= pcs
->next
)
433 pcs_callback(pcs
, CBA_SET_OPTIONS
, &opts
);
435 return dbghelp_options
= opts
;
438 /******************************************************************
439 * SymGetOptions (DBGHELP.@)
442 DWORD WINAPI
SymGetOptions(void)
444 return dbghelp_options
;
447 /******************************************************************
448 * SymSetParentWindow (DBGHELP.@)
451 BOOL WINAPI
SymSetParentWindow(HWND hwnd
)
453 /* Save hwnd so it can be used as parent window */
454 FIXME("(%p): stub\n", hwnd
);
458 /******************************************************************
459 * SymSetContext (DBGHELP.@)
462 BOOL WINAPI
SymSetContext(HANDLE hProcess
, PIMAGEHLP_STACK_FRAME StackFrame
,
463 PIMAGEHLP_CONTEXT Context
)
465 struct process
* pcs
= process_find_by_handle(hProcess
);
466 if (!pcs
) return FALSE
;
468 if (pcs
->ctx_frame
.ReturnOffset
== StackFrame
->ReturnOffset
&&
469 pcs
->ctx_frame
.FrameOffset
== StackFrame
->FrameOffset
&&
470 pcs
->ctx_frame
.StackOffset
== StackFrame
->StackOffset
)
472 TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
473 wine_dbgstr_longlong(pcs
->ctx_frame
.ReturnOffset
),
474 wine_dbgstr_longlong(pcs
->ctx_frame
.FrameOffset
),
475 wine_dbgstr_longlong(pcs
->ctx_frame
.StackOffset
));
476 pcs
->ctx_frame
.InstructionOffset
= StackFrame
->InstructionOffset
;
477 SetLastError(ERROR_ACCESS_DENIED
); /* latest MSDN says ERROR_SUCCESS */
481 pcs
->ctx_frame
= *StackFrame
;
482 /* MSDN states that Context is not (no longer?) used */
486 /******************************************************************
487 * reg_cb64to32 (internal)
489 * Registered callback for converting information from 64 bit to 32 bit
491 static BOOL CALLBACK
reg_cb64to32(HANDLE hProcess
, ULONG action
, ULONG64 data
, ULONG64 user
)
493 struct process
* pcs
= process_find_by_handle(hProcess
);
495 IMAGEHLP_DEFERRED_SYMBOL_LOAD64
* idsl64
;
496 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl
;
498 if (!pcs
) return FALSE
;
502 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL
:
503 case CBA_SET_OPTIONS
:
504 case CBA_SYMBOLS_UNLOADED
:
505 data32
= (void*)(DWORD_PTR
)data
;
507 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE
:
508 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE
:
509 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL
:
510 case CBA_DEFERRED_SYMBOL_LOAD_START
:
511 idsl64
= (IMAGEHLP_DEFERRED_SYMBOL_LOAD64
*)(DWORD_PTR
)data
;
512 if (!validate_addr64(idsl64
->BaseOfImage
))
514 idsl
.SizeOfStruct
= sizeof(idsl
);
515 idsl
.BaseOfImage
= (DWORD
)idsl64
->BaseOfImage
;
516 idsl
.CheckSum
= idsl64
->CheckSum
;
517 idsl
.TimeDateStamp
= idsl64
->TimeDateStamp
;
518 memcpy(idsl
.FileName
, idsl64
->FileName
, sizeof(idsl
.FileName
));
519 idsl
.Reparse
= idsl64
->Reparse
;
522 case CBA_DUPLICATE_SYMBOL
:
524 case CBA_READ_MEMORY
:
526 FIXME("No mapping for action %u\n", action
);
529 return pcs
->reg_cb32(hProcess
, action
, data32
, (PVOID
)(DWORD_PTR
)user
);
532 /******************************************************************
533 * pcs_callback (internal)
535 BOOL
pcs_callback(const struct process
* pcs
, ULONG action
, void* data
)
537 IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl
;
539 TRACE("%p %u %p\n", pcs
, action
, data
);
541 if (!pcs
->reg_cb
) return FALSE
;
542 if (!pcs
->reg_is_unicode
)
544 IMAGEHLP_DEFERRED_SYMBOL_LOADW64
* idslW
;
549 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL
:
550 case CBA_SET_OPTIONS
:
551 case CBA_SYMBOLS_UNLOADED
:
553 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE
:
554 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE
:
555 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL
:
556 case CBA_DEFERRED_SYMBOL_LOAD_START
:
558 idsl
.SizeOfStruct
= sizeof(idsl
);
559 idsl
.BaseOfImage
= idslW
->BaseOfImage
;
560 idsl
.CheckSum
= idslW
->CheckSum
;
561 idsl
.TimeDateStamp
= idslW
->TimeDateStamp
;
562 WideCharToMultiByte(CP_ACP
, 0, idslW
->FileName
, -1,
563 idsl
.FileName
, sizeof(idsl
.FileName
), NULL
, NULL
);
564 idsl
.Reparse
= idslW
->Reparse
;
567 case CBA_DUPLICATE_SYMBOL
:
569 case CBA_READ_MEMORY
:
571 FIXME("No mapping for action %u\n", action
);
575 return pcs
->reg_cb(pcs
->handle
, action
, (ULONG64
)(DWORD_PTR
)data
, pcs
->reg_user
);
578 /******************************************************************
581 * Helper for registering a callback.
583 static BOOL
sym_register_cb(HANDLE hProcess
,
584 PSYMBOL_REGISTERED_CALLBACK64 cb
,
585 PSYMBOL_REGISTERED_CALLBACK cb32
,
586 DWORD64 user
, BOOL unicode
)
588 struct process
* pcs
= process_find_by_handle(hProcess
);
590 if (!pcs
) return FALSE
;
592 pcs
->reg_cb32
= cb32
;
593 pcs
->reg_is_unicode
= unicode
;
594 pcs
->reg_user
= user
;
599 /***********************************************************************
600 * SymRegisterCallback (DBGHELP.@)
602 BOOL WINAPI
SymRegisterCallback(HANDLE hProcess
,
603 PSYMBOL_REGISTERED_CALLBACK CallbackFunction
,
606 TRACE("(%p, %p, %p)\n",
607 hProcess
, CallbackFunction
, UserContext
);
608 return sym_register_cb(hProcess
, reg_cb64to32
, CallbackFunction
, (DWORD_PTR
)UserContext
, FALSE
);
611 /***********************************************************************
612 * SymRegisterCallback64 (DBGHELP.@)
614 BOOL WINAPI
SymRegisterCallback64(HANDLE hProcess
,
615 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction
,
618 TRACE("(%p, %p, %s)\n",
619 hProcess
, CallbackFunction
, wine_dbgstr_longlong(UserContext
));
620 return sym_register_cb(hProcess
, CallbackFunction
, NULL
, UserContext
, FALSE
);
623 /***********************************************************************
624 * SymRegisterCallbackW64 (DBGHELP.@)
626 BOOL WINAPI
SymRegisterCallbackW64(HANDLE hProcess
,
627 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction
,
630 TRACE("(%p, %p, %s)\n",
631 hProcess
, CallbackFunction
, wine_dbgstr_longlong(UserContext
));
632 return sym_register_cb(hProcess
, CallbackFunction
, NULL
, UserContext
, TRUE
);
635 /* This is imagehlp version not dbghelp !! */
636 static API_VERSION api_version
= { 4, 0, 2, 0 };
638 /***********************************************************************
639 * ImagehlpApiVersion (DBGHELP.@)
641 LPAPI_VERSION WINAPI
ImagehlpApiVersion(VOID
)
646 /***********************************************************************
647 * ImagehlpApiVersionEx (DBGHELP.@)
649 LPAPI_VERSION WINAPI
ImagehlpApiVersionEx(LPAPI_VERSION AppVersion
)
651 if (!AppVersion
) return NULL
;
653 AppVersion
->MajorVersion
= api_version
.MajorVersion
;
654 AppVersion
->MinorVersion
= api_version
.MinorVersion
;
655 AppVersion
->Revision
= api_version
.Revision
;
656 AppVersion
->Reserved
= api_version
.Reserved
;
661 /******************************************************************
662 * ExtensionApiVersion (DBGHELP.@)
664 LPEXT_API_VERSION WINAPI
ExtensionApiVersion(void)
666 static EXT_API_VERSION eav
= {5, 5, 5, 0};
670 /******************************************************************
671 * WinDbgExtensionDllInit (DBGHELP.@)
673 void WINAPI
WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis
,
674 unsigned short major
, unsigned short minor
)