wined3d: Do not perform a NULL check on riid (Coverity).
[wine/multimedia.git] / dlls / dbghelp / dbghelp.c
blobc47977e97d4c8b4ff8bc7abfb2a5f659be7595b6
1 /*
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
21 #include "config.h"
23 #include "dbghelp_private.h"
24 #include "winerror.h"
25 #include "psapi.h"
26 #include "wine/debug.h"
27 #include "wdbgexts.h"
28 #include "winnls.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
32 /* TODO
33 * - support for symbols' types is still partly missing
34 * + C++ support
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
39 * Codeview does BTW.
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
45 * func doesn't work)
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
50 * get a full RE
51 * - msc:
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)
55 * + C++ management
56 * - stabs:
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
64 * + C++ management
67 unsigned dbghelp_options = SYMOPT_UNDNAME;
68 HANDLE hMsvcrt = NULL;
70 /***********************************************************************
71 * DllMain (DEBUGHLP.@)
73 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
75 switch (fdwReason)
77 case DLL_PROCESS_ATTACH: break;
78 case DLL_PROCESS_DETACH:
79 if (hMsvcrt) FreeLibrary(hMsvcrt);
80 break;
81 case DLL_THREAD_ATTACH: break;
82 case DLL_THREAD_DETACH: break;
83 default: break;
85 return TRUE;
88 static struct process* process_first /* = NULL */;
90 /******************************************************************
91 * process_find_by_handle
94 struct process* process_find_by_handle(HANDLE hProcess)
96 struct process* p;
98 for (p = process_first; p && p->handle != hProcess; p = p->next);
99 if (!p) SetLastError(ERROR_INVALID_HANDLE);
100 return p;
103 /******************************************************************
104 * validate_addr64 (internal)
107 BOOL validate_addr64(DWORD64 addr)
109 if (addr >> 32)
111 FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
112 SetLastError(ERROR_INVALID_PARAMETER);
113 return FALSE;
115 return TRUE;
118 /******************************************************************
119 * fetch_buffer
121 * Ensures process' internal buffer is large enough.
123 void* fetch_buffer(struct process* pcs, unsigned size)
125 if (size > pcs->buffer_size)
127 if (pcs->buffer)
128 pcs->buffer = HeapReAlloc(GetProcessHeap(), 0, pcs->buffer, size);
129 else
130 pcs->buffer = HeapAlloc(GetProcessHeap(), 0, size);
131 pcs->buffer_size = (pcs->buffer) ? size : 0;
133 return pcs->buffer;
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)),
150 searchPath);
151 return TRUE;
154 /******************************************************************
155 * SymSetSearchPath (DBGHELP.@)
158 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath)
160 BOOL ret = FALSE;
161 unsigned len;
162 WCHAR* sp;
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);
172 return ret;
175 /***********************************************************************
176 * SymGetSearchPathW (DBGHELP.@)
178 BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, LPWSTR 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);
185 return TRUE;
188 /***********************************************************************
189 * SymGetSearchPath (DBGHELP.@)
191 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, LPSTR szSearchPath,
192 DWORD SearchPathLength)
194 WCHAR* buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength);
195 BOOL ret = FALSE;
197 if (buffer)
199 ret = SymGetSearchPathW(hProcess, buffer,
200 SearchPathLength * sizeof(WCHAR));
201 if (ret)
202 WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength,
203 szSearchPath, SearchPathLength, NULL, NULL);
204 HeapFree(GetProcessHeap(), 0, buffer);
206 return ret;
209 /******************************************************************
210 * invade_process
212 * SymInitialize helper: loads in dbghelp all known (and loaded modules)
213 * this assumes that hProcess is a handle on a valid process
215 static BOOL WINAPI process_invade_cb(char* name, DWORD base, DWORD size, void* user)
217 char tmp[MAX_PATH];
218 HANDLE hProcess = (HANDLE)user;
220 if (!GetModuleFileNameExA(hProcess, (HMODULE)base,
221 tmp, sizeof(tmp)))
222 lstrcpynA(tmp, name, sizeof(tmp));
224 SymLoadModule(hProcess, 0, tmp, name, base, size);
225 return TRUE;
228 /******************************************************************
229 * check_live_target
232 static BOOL check_live_target(struct process* pcs)
234 if (!GetProcessId(pcs->handle)) return FALSE;
235 if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE;
236 elf_read_wine_loader_dbg_info(pcs);
237 return TRUE;
240 /******************************************************************
241 * SymInitializeW (DBGHELP.@)
243 * The initialisation of a dbghelp's context.
244 * Note that hProcess doesn't need to be a valid process handle (except
245 * when fInvadeProcess is TRUE).
246 * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries
247 * containing PE (and NE) module(s), here's how we handle it:
248 * - we load every module (ELF, NE, PE) passed in SymLoadModule
249 * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
250 * synchronization: hProcess should be a valid process handle, and we hook
251 * ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
252 * our internal ELF modules representation (loading / unloading). This way,
253 * we'll pair every loaded builtin PE module with its ELF counterpart (and
254 * access its debug information).
255 * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the
256 * hProcess refers to a running process. We use some heuristics here, so YMMV.
257 * If we detect a live target, then we get the same handling as if
258 * fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise,
259 * we won't be able to make the peering between a builtin PE module and its ELF
260 * counterpart. Hence we won't be able to provide the requested debug
261 * information. We'll however be able to load native PE modules (and their
262 * debug information) without any trouble.
263 * Note also that this scheme can be intertwined with the deferred loading
264 * mechanism (ie only load the debug information when we actually need it).
266 BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess)
268 struct process* pcs;
270 TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess);
272 if (process_find_by_handle(hProcess))
273 FIXME("what to do ??\n");
275 pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
276 if (!pcs) return FALSE;
278 pcs->handle = hProcess;
280 if (UserSearchPath)
282 pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0,
283 (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)),
284 UserSearchPath);
286 else
288 unsigned size;
289 unsigned len;
290 static const WCHAR sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0};
291 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};
293 pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR));
294 while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len)
295 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR));
296 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR));
298 len = GetEnvironmentVariableW(sym_path, NULL, 0);
299 if (len)
301 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
302 pcs->search_path[size] = ';';
303 GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len);
304 size += 1 + len;
306 len = GetEnvironmentVariableW(alt_sym_path, NULL, 0);
307 if (len)
309 pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR));
310 pcs->search_path[size] = ';';
311 GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len);
312 size += 1 + len;
316 pcs->lmodules = NULL;
317 pcs->dbg_hdr_addr = 0;
318 pcs->next = process_first;
319 process_first = pcs;
321 if (check_live_target(pcs))
323 if (fInvadeProcess)
324 EnumerateLoadedModules(hProcess, process_invade_cb, (void*)hProcess);
325 elf_synchronize_module_list(pcs);
327 else if (fInvadeProcess)
329 SymCleanup(hProcess);
330 SetLastError(ERROR_INVALID_PARAMETER);
331 return FALSE;
334 return TRUE;
337 /******************************************************************
338 * SymInitialize (DBGHELP.@)
342 BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess)
344 WCHAR* sp = NULL;
345 BOOL ret;
347 if (UserSearchPath)
349 unsigned len;
351 len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0);
352 sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
353 MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len);
356 ret = SymInitializeW(hProcess, sp, fInvadeProcess);
357 HeapFree(GetProcessHeap(), 0, sp);
358 return ret;
361 /******************************************************************
362 * SymCleanup (DBGHELP.@)
365 BOOL WINAPI SymCleanup(HANDLE hProcess)
367 struct process** ppcs;
368 struct process* next;
370 for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
372 if ((*ppcs)->handle == hProcess)
374 while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
376 HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
377 next = (*ppcs)->next;
378 HeapFree(GetProcessHeap(), 0, *ppcs);
379 *ppcs = next;
380 return TRUE;
383 return FALSE;
386 /******************************************************************
387 * SymSetOptions (DBGHELP.@)
390 DWORD WINAPI SymSetOptions(DWORD opts)
392 struct process* pcs;
394 for (pcs = process_first; pcs; pcs = pcs->next)
396 pcs_callback(pcs, CBA_SET_OPTIONS, &opts);
398 return dbghelp_options = opts;
401 /******************************************************************
402 * SymGetOptions (DBGHELP.@)
405 DWORD WINAPI SymGetOptions(void)
407 return dbghelp_options;
410 /******************************************************************
411 * SymSetParentWindow (DBGHELP.@)
414 BOOL WINAPI SymSetParentWindow(HWND hwnd)
416 /* Save hwnd so it can be used as parent window */
417 FIXME("(%p): stub\n", hwnd);
418 return TRUE;
421 /******************************************************************
422 * SymSetContext (DBGHELP.@)
425 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
426 PIMAGEHLP_CONTEXT Context)
428 struct process* pcs = process_find_by_handle(hProcess);
429 if (!pcs) return FALSE;
431 if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset &&
432 pcs->ctx_frame.FrameOffset == StackFrame->FrameOffset &&
433 pcs->ctx_frame.StackOffset == StackFrame->StackOffset)
435 TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n",
436 wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset),
437 wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset),
438 wine_dbgstr_longlong(pcs->ctx_frame.StackOffset));
439 pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset;
440 SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */
441 return FALSE;
444 pcs->ctx_frame = *StackFrame;
445 /* MSDN states that Context is not (no longer?) used */
446 return TRUE;
449 /******************************************************************
450 * reg_cb64to32 (internal)
452 * Registered callback for converting information from 64 bit to 32 bit
454 static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user)
456 PSYMBOL_REGISTERED_CALLBACK cb32 = (PSYMBOL_REGISTERED_CALLBACK)(DWORD)(user >> 32);
457 DWORD user32 = (DWORD)user;
458 void* data32;
459 IMAGEHLP_DEFERRED_SYMBOL_LOAD64* idsl64;
460 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl;
462 switch (action)
464 case CBA_DEBUG_INFO:
465 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
466 case CBA_SET_OPTIONS:
467 case CBA_SYMBOLS_UNLOADED:
468 data32 = (void*)(DWORD)data;
469 break;
470 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
471 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
472 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
473 case CBA_DEFERRED_SYMBOL_LOAD_START:
474 idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD)data;
475 if (!validate_addr64(idsl64->BaseOfImage))
476 return FALSE;
477 idsl.SizeOfStruct = sizeof(idsl);
478 idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage;
479 idsl.CheckSum = idsl64->CheckSum;
480 idsl.TimeDateStamp = idsl64->TimeDateStamp;
481 memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName));
482 idsl.Reparse = idsl64->Reparse;
483 data32 = &idsl;
484 break;
485 case CBA_DUPLICATE_SYMBOL:
486 case CBA_EVENT:
487 case CBA_READ_MEMORY:
488 default:
489 FIXME("No mapping for action %u\n", action);
490 return FALSE;
492 return cb32(hProcess, action, (PVOID)data32, (PVOID)user32);
495 /******************************************************************
496 * pcs_callback (internal)
498 BOOL pcs_callback(const struct process* pcs, ULONG action, void* data)
500 TRACE("%p %u %p\n", pcs, action, data);
502 if (!pcs->reg_cb) return FALSE;
503 if (pcs->reg_is_unicode)
505 IMAGEHLP_DEFERRED_SYMBOL_LOAD64* idsl;
506 IMAGEHLP_DEFERRED_SYMBOL_LOADW64 idslW;
508 switch (action)
510 case CBA_DEBUG_INFO:
511 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
512 case CBA_SET_OPTIONS:
513 case CBA_SYMBOLS_UNLOADED:
514 break;
515 case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
516 case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
517 case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
518 case CBA_DEFERRED_SYMBOL_LOAD_START:
519 idsl = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD)data;
520 idslW.SizeOfStruct = sizeof(idslW);
521 idslW.BaseOfImage = idsl->BaseOfImage;
522 idslW.CheckSum = idsl->CheckSum;
523 idslW.TimeDateStamp = idsl->TimeDateStamp;
524 MultiByteToWideChar(CP_ACP, 0, idsl->FileName, -1,
525 idslW.FileName, sizeof(idslW.FileName) / sizeof(WCHAR));
526 idslW.Reparse = idsl->Reparse;
527 data = &idslW;
528 break;
529 case CBA_DUPLICATE_SYMBOL:
530 case CBA_EVENT:
531 case CBA_READ_MEMORY:
532 default:
533 FIXME("No mapping for action %u\n", action);
534 return FALSE;
537 return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user);
540 /******************************************************************
541 * sym_register_cb
543 * Helper for registering a callback.
545 static BOOL sym_register_cb(HANDLE hProcess,
546 PSYMBOL_REGISTERED_CALLBACK64 cb,
547 DWORD64 user, BOOL unicode)
549 struct process* pcs = process_find_by_handle(hProcess);
551 if (!pcs) return FALSE;
552 pcs->reg_cb = cb;
553 pcs->reg_is_unicode = unicode;
554 pcs->reg_user = user;
556 return TRUE;
559 /***********************************************************************
560 * SymRegisterCallback (DBGHELP.@)
562 BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
563 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
564 PVOID UserContext)
566 DWORD64 tmp = ((ULONGLONG)(DWORD)CallbackFunction << 32) | (DWORD)UserContext;
567 TRACE("(%p, %p, %p)\n",
568 hProcess, CallbackFunction, UserContext);
569 return sym_register_cb(hProcess, reg_cb64to32, tmp, FALSE);
572 /***********************************************************************
573 * SymRegisterCallback64 (DBGHELP.@)
575 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess,
576 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
577 ULONG64 UserContext)
579 TRACE("(%p, %p, %s)\n",
580 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
581 return sym_register_cb(hProcess, CallbackFunction, UserContext, FALSE);
584 /***********************************************************************
585 * SymRegisterCallbackW64 (DBGHELP.@)
587 BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess,
588 PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
589 ULONG64 UserContext)
591 TRACE("(%p, %p, %s)\n",
592 hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
593 return sym_register_cb(hProcess, CallbackFunction, UserContext, TRUE);
596 /* This is imagehlp version not dbghelp !! */
597 static API_VERSION api_version = { 4, 0, 2, 0 };
599 /***********************************************************************
600 * ImagehlpApiVersion (DBGHELP.@)
602 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
604 return &api_version;
607 /***********************************************************************
608 * ImagehlpApiVersionEx (DBGHELP.@)
610 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
612 if (!AppVersion) return NULL;
614 AppVersion->MajorVersion = api_version.MajorVersion;
615 AppVersion->MinorVersion = api_version.MinorVersion;
616 AppVersion->Revision = api_version.Revision;
617 AppVersion->Reserved = api_version.Reserved;
619 return AppVersion;
622 /******************************************************************
623 * ExtensionApiVersion (DBGHELP.@)
625 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
627 static EXT_API_VERSION eav = {5, 5, 5, 0};
628 return &eav;
631 /******************************************************************
632 * WinDbgExtensionDllInit (DBGHELP.@)
634 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
635 unsigned short major, unsigned short minor)