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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "dbghelp_private.h"
26 #include "wine/debug.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
32 * - support for symbols' types is still partly missing
34 * + we should store the underlying type for an enum in the symt_enum struct
35 * + for enums, we store the names & values (associated to the enum type),
36 * but those values are not directly usable from a debugger (that's why, I
37 * assume, that we have also to define constants for enum values, as
39 * + SymEnumTypes should only return *user* defined types (UDT, typedefs...) not
40 * all the types stored/used in the modules (like char*)
41 * - SymGetLine{Next|Prev} don't work as expected (they don't seem to work across
42 * functions, and even across function blocks...). Basically, for *Next* to work
43 * it requires an address after the prolog of the func (the base address of the
45 * - most options (dbghelp_options) are not used (loading lines...)
46 * - in symbol lookup by name, we don't use RE everywhere we should. Moreover, when
47 * we're supposed to use RE, it doesn't make use of our hash tables. Therefore,
48 * we could use hash if name isn't a RE, and fall back to a full search when we
51 * + we should add parameters' types to the function's signature
52 * while processing a function's parameters
53 * + add support for function-less labels (as MSC seems to define them)
56 * + when, in a same module, the same definition is used in several compilation
57 * units, we get several definitions of the same object (especially
58 * struct/union). we should find a way not to duplicate them
59 * + in some cases (dlls/user/dialog16.c DIALOG_GetControl16), the same static
60 * global variable is defined several times (at different scopes). We are
61 * getting several of those while looking for a unique symbol. Part of the
62 * issue is that we don't give a scope to a static variable inside a function
66 unsigned dbghelp_options
= SYMOPT_UNDNAME
;
67 HANDLE hMsvcrt
= NULL
;
69 /***********************************************************************
70 * DllMain (DEBUGHLP.@)
72 BOOL WINAPI
DllMain(HINSTANCE hinstDLL
, DWORD fdwReason
, LPVOID lpvReserved
)
76 case DLL_PROCESS_ATTACH
: break;
77 case DLL_PROCESS_DETACH
:
78 if (hMsvcrt
) FreeLibrary(hMsvcrt
);
80 case DLL_THREAD_ATTACH
: break;
81 case DLL_THREAD_DETACH
: break;
87 static struct process
* process_first
/* = NULL */;
89 /******************************************************************
90 * process_find_by_handle
93 struct process
* process_find_by_handle(HANDLE hProcess
)
97 for (p
= process_first
; p
&& p
->handle
!= hProcess
; p
= p
->next
);
98 if (!p
) SetLastError(ERROR_INVALID_HANDLE
);
102 /******************************************************************
103 * validate_addr64 (internal)
106 BOOL
validate_addr64(DWORD64 addr
)
110 FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr
));
111 SetLastError(ERROR_INVALID_PARAMETER
);
117 /******************************************************************
118 * SymSetSearchPath (DBGHELP.@)
121 BOOL WINAPI
SymSetSearchPath(HANDLE hProcess
, PCSTR searchPath
)
123 struct process
* pcs
= process_find_by_handle(hProcess
);
125 if (!pcs
) return FALSE
;
126 if (!searchPath
) return FALSE
;
128 HeapFree(GetProcessHeap(), 0, pcs
->search_path
);
129 pcs
->search_path
= strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(searchPath
) + 1),
134 /***********************************************************************
135 * SymGetSearchPath (DBGHELP.@)
137 BOOL WINAPI
SymGetSearchPath(HANDLE hProcess
, LPSTR szSearchPath
,
138 DWORD SearchPathLength
)
140 struct process
* pcs
= process_find_by_handle(hProcess
);
141 if (!pcs
) return FALSE
;
143 lstrcpynA(szSearchPath
, pcs
->search_path
, SearchPathLength
);
147 /******************************************************************
150 * SymInitialize helper: loads in dbghelp all known (and loaded modules)
151 * this assumes that hProcess is a handle on a valid process
153 static BOOL WINAPI
process_invade_cb(char* name
, DWORD base
, DWORD size
, void* user
)
156 HANDLE hProcess
= (HANDLE
)user
;
158 if (!GetModuleFileNameExA(hProcess
, (HMODULE
)base
,
160 lstrcpynA(tmp
, name
, sizeof(tmp
));
162 SymLoadModule(hProcess
, 0, tmp
, name
, base
, size
);
166 /******************************************************************
170 static BOOL
check_live_target(struct process
* pcs
)
172 if (!GetProcessId(pcs
->handle
)) return FALSE
;
173 if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL
, 0)) return FALSE
;
174 return elf_read_wine_loader_dbg_info(pcs
);
177 /******************************************************************
178 * SymInitialize (DBGHELP.@)
180 * The initialisation of a dbghelp's context.
181 * Note that hProcess doesn't need to be a valid process handle (except
182 * when fInvadeProcess is TRUE).
183 * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries
184 * containing PE (and NE) module(s), here's how we handle it:
185 * - we load every module (ELF, NE, PE) passed in SymLoadModule
186 * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
187 * synchronization: hProcess should be a valid process handle, and we hook
188 * ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
189 * our internal ELF modules representation (loading / unloading). This way,
190 * we'll pair every loaded builtin PE module with its ELF counterpart (and
191 * access its debug information).
192 * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
193 * hProcess refers to a running process. We use some heuristics here, so YMMV.
194 * If we detect a live target, then we get the same handling as if
195 * fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
196 * we won't be able to make the peering between a builtin PE module and its ELF
197 * counterpart. Hence we won't be able to provide the requested debug
198 * information. We'll however be able to load native PE modules (and their
199 * debug information) without any trouble.
200 * Note also that this scheme can be intertwined with the deferred loading
201 * mechanism (ie only load the debug information when we actually need it).
203 BOOL WINAPI
SymInitialize(HANDLE hProcess
, PCSTR UserSearchPath
, BOOL fInvadeProcess
)
207 TRACE("(%p %s %u)\n", hProcess
, debugstr_a(UserSearchPath
), fInvadeProcess
);
209 if (process_find_by_handle(hProcess
))
210 FIXME("what to do ??\n");
212 pcs
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*pcs
));
213 if (!pcs
) return FALSE
;
215 pcs
->handle
= hProcess
;
219 pcs
->search_path
= strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(UserSearchPath
) + 1),
227 pcs
->search_path
= HeapAlloc(GetProcessHeap(), 0, len
= MAX_PATH
);
228 while ((size
= GetCurrentDirectoryA(len
, pcs
->search_path
)) >= len
)
229 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, len
*= 2);
230 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, size
+ 1);
232 len
= GetEnvironmentVariableA("_NT_SYMBOL_PATH", NULL
, 0);
235 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, size
+ 1 + len
+ 1);
236 pcs
->search_path
[size
] = ';';
237 GetEnvironmentVariableA("_NT_SYMBOL_PATH", pcs
->search_path
+ size
+ 1, len
);
240 len
= GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", NULL
, 0);
243 pcs
->search_path
= HeapReAlloc(GetProcessHeap(), 0, pcs
->search_path
, size
+ 1 + len
+ 1);
244 pcs
->search_path
[size
] = ';';
245 GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", pcs
->search_path
+ size
+ 1, len
);
250 pcs
->lmodules
= NULL
;
251 pcs
->dbg_hdr_addr
= 0;
252 pcs
->next
= process_first
;
255 if (check_live_target(pcs
))
258 EnumerateLoadedModules(hProcess
, process_invade_cb
, (void*)hProcess
);
259 elf_synchronize_module_list(pcs
);
261 else if (fInvadeProcess
)
263 SymCleanup(hProcess
);
264 SetLastError(ERROR_INVALID_PARAMETER
);
271 /******************************************************************
272 * SymCleanup (DBGHELP.@)
275 BOOL WINAPI
SymCleanup(HANDLE hProcess
)
277 struct process
** ppcs
;
278 struct process
* next
;
280 for (ppcs
= &process_first
; *ppcs
; ppcs
= &(*ppcs
)->next
)
282 if ((*ppcs
)->handle
== hProcess
)
284 while ((*ppcs
)->lmodules
) module_remove(*ppcs
, (*ppcs
)->lmodules
);
286 HeapFree(GetProcessHeap(), 0, (*ppcs
)->search_path
);
287 next
= (*ppcs
)->next
;
288 HeapFree(GetProcessHeap(), 0, *ppcs
);
296 /******************************************************************
297 * SymSetOptions (DBGHELP.@)
300 DWORD WINAPI
SymSetOptions(DWORD opts
)
304 for (pcs
= process_first
; pcs
; pcs
= pcs
->next
)
306 pcs_callback(pcs
, CBA_SET_OPTIONS
, &opts
);
308 return dbghelp_options
= opts
;
311 /******************************************************************
312 * SymGetOptions (DBGHELP.@)
315 DWORD WINAPI
SymGetOptions(void)
317 return dbghelp_options
;
320 /******************************************************************
321 * SymSetParentWindow (DBGHELP.@)
324 BOOL WINAPI
SymSetParentWindow(HWND hwnd
)
326 /* Save hwnd so it can be used as parent window */
327 FIXME("(%p): stub\n", hwnd
);
331 /******************************************************************
332 * SymSetContext (DBGHELP.@)
335 BOOL WINAPI
SymSetContext(HANDLE hProcess
, PIMAGEHLP_STACK_FRAME StackFrame
,
336 PIMAGEHLP_CONTEXT Context
)
338 struct process
* pcs
= process_find_by_handle(hProcess
);
339 if (!pcs
) return FALSE
;
341 if (pcs
->ctx_frame
.ReturnOffset
== StackFrame
->ReturnOffset
&&
342 pcs
->ctx_frame
.FrameOffset
== StackFrame
->FrameOffset
&&
343 pcs
->ctx_frame
.StackOffset
== StackFrame
->StackOffset
)
345 TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
346 wine_dbgstr_longlong(pcs
->ctx_frame
.ReturnOffset
),
347 wine_dbgstr_longlong(pcs
->ctx_frame
.FrameOffset
),
348 wine_dbgstr_longlong(pcs
->ctx_frame
.StackOffset
));
349 SetLastError(ERROR_ACCESS_DENIED
); /* latest MSDN says ERROR_SUCCESS */
353 pcs
->ctx_frame
= *StackFrame
;
354 /* MSDN states that Context is not (no longer?) used */
358 /******************************************************************
359 * reg_cb64to32 (internal)
361 * Registered callback for converting information from 64 bit to 32 bit
363 static BOOL CALLBACK
reg_cb64to32(HANDLE hProcess
, ULONG action
, ULONG64 data
, ULONG64 user
)
365 PSYMBOL_REGISTERED_CALLBACK cb32
= (PSYMBOL_REGISTERED_CALLBACK
)(DWORD
)(user
>> 32);
366 DWORD user32
= (DWORD
)user
;
368 IMAGEHLP_DEFERRED_SYMBOL_LOAD64
* idsl64
;
369 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl
;
374 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL
:
375 case CBA_SET_OPTIONS
:
376 case CBA_SYMBOLS_UNLOADED
:
377 data32
= (void*)(DWORD
)data
;
379 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE
:
380 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE
:
381 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL
:
382 case CBA_DEFERRED_SYMBOL_LOAD_START
:
383 idsl64
= (IMAGEHLP_DEFERRED_SYMBOL_LOAD64
*)(DWORD
)data
;
384 if (!validate_addr64(idsl64
->BaseOfImage
))
386 idsl
.SizeOfStruct
= sizeof(idsl
);
387 idsl
.BaseOfImage
= (DWORD
)idsl64
->BaseOfImage
;
388 idsl
.CheckSum
= idsl64
->CheckSum
;
389 idsl
.TimeDateStamp
= idsl64
->TimeDateStamp
;
390 memcpy(idsl
.FileName
, idsl64
->FileName
, sizeof(idsl
.FileName
));
391 idsl
.Reparse
= idsl64
->Reparse
;
394 case CBA_DUPLICATE_SYMBOL
:
396 case CBA_READ_MEMORY
:
398 FIXME("No mapping for action %lu\n", action
);
401 return cb32(hProcess
, action
, (PVOID
)data32
, (PVOID
)user32
);
404 /******************************************************************
405 * pcs_callback (internal)
407 BOOL
pcs_callback(const struct process
* pcs
, ULONG action
, void* data
)
409 TRACE("%p %lu %p\n", pcs
, action
, data
);
410 if (!pcs
->reg_cb
) return FALSE
;
411 return pcs
->reg_cb(pcs
->handle
, action
, (ULONG64
)(DWORD_PTR
)data
, pcs
->reg_user
);
414 /***********************************************************************
415 * SymRegisterCallback (DBGHELP.@)
417 BOOL WINAPI
SymRegisterCallback(HANDLE hProcess
,
418 PSYMBOL_REGISTERED_CALLBACK CallbackFunction
,
421 DWORD64 tmp
= ((ULONGLONG
)(DWORD
)CallbackFunction
<< 32) | (DWORD
)UserContext
;
422 return SymRegisterCallback64(hProcess
, reg_cb64to32
, tmp
);
425 /***********************************************************************
426 * SymRegisterCallback64 (DBGHELP.@)
428 BOOL WINAPI
SymRegisterCallback64(HANDLE hProcess
,
429 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction
,
432 struct process
* pcs
= process_find_by_handle(hProcess
);
434 TRACE("(%p, %p, %s)\n",
435 hProcess
, CallbackFunction
, wine_dbgstr_longlong(UserContext
));
436 if (!pcs
) return FALSE
;
437 pcs
->reg_cb
= CallbackFunction
;
438 pcs
->reg_user
= UserContext
;
443 /* This is imagehlp version not dbghelp !! */
444 static API_VERSION api_version
= { 4, 0, 2, 0 };
446 /***********************************************************************
447 * ImagehlpApiVersion (DBGHELP.@)
449 LPAPI_VERSION WINAPI
ImagehlpApiVersion(VOID
)
454 /***********************************************************************
455 * ImagehlpApiVersionEx (DBGHELP.@)
457 LPAPI_VERSION WINAPI
ImagehlpApiVersionEx(LPAPI_VERSION AppVersion
)
459 if (!AppVersion
) return NULL
;
461 AppVersion
->MajorVersion
= api_version
.MajorVersion
;
462 AppVersion
->MinorVersion
= api_version
.MinorVersion
;
463 AppVersion
->Revision
= api_version
.Revision
;
464 AppVersion
->Reserved
= api_version
.Reserved
;
469 /******************************************************************
470 * ExtensionApiVersion (DBGHELP.@)
472 LPEXT_API_VERSION WINAPI
ExtensionApiVersion(void)
474 static EXT_API_VERSION eav
= {5, 5, 5, 0};
478 /******************************************************************
479 * WinDbgExtensionDllInit (DBGHELP.@)
481 void WINAPI
WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis
,
482 unsigned short major
, unsigned short minor
)