winebuild: Fixed index in module table for delayed imports.
[wine.git] / dlls / dbghelp / dbghelp.c
blobbd75a60a423add96dfb4861570bcfd163ed69861
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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"
29 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
31 /* TODO
32 * - support for symbols' types is still partly missing
33 * + C++ support
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
38 * Codeview does BTW.
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
44 * func doesn't work)
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
49 * get a full RE
50 * - msc:
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)
54 * + C++ management
55 * - stabs:
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
63 * + C++ management
66 unsigned dbghelp_options = SYMOPT_UNDNAME;
67 HANDLE hMsvcrt = NULL;
69 /***********************************************************************
70 * DllMain (DEBUGHLP.@)
72 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
74 switch (fdwReason)
76 case DLL_PROCESS_ATTACH: break;
77 case DLL_PROCESS_DETACH:
78 if (hMsvcrt) FreeLibrary(hMsvcrt);
79 break;
80 case DLL_THREAD_ATTACH: break;
81 case DLL_THREAD_DETACH: break;
82 default: break;
84 return TRUE;
87 static struct process* process_first /* = NULL */;
89 /******************************************************************
90 * process_find_by_handle
93 struct process* process_find_by_handle(HANDLE hProcess)
95 struct process* p;
97 for (p = process_first; p && p->handle != hProcess; p = p->next);
98 if (!p) SetLastError(ERROR_INVALID_HANDLE);
99 return p;
102 /******************************************************************
103 * validate_addr64 (internal)
106 BOOL validate_addr64(DWORD64 addr)
108 if (addr >> 32)
110 FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
111 SetLastError(ERROR_INVALID_PARAMETER);
112 return FALSE;
114 return TRUE;
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),
130 searchPath);
131 return TRUE;
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);
144 return TRUE;
147 /******************************************************************
148 * invade_process
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)
155 char tmp[MAX_PATH];
156 HANDLE hProcess = (HANDLE)user;
158 if (!GetModuleFileNameExA(hProcess, (HMODULE)base,
159 tmp, sizeof(tmp)))
160 lstrcpynA(tmp, name, sizeof(tmp));
162 SymLoadModule(hProcess, 0, tmp, name, base, size);
163 return TRUE;
166 /******************************************************************
167 * check_live_target
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)
205 struct process* pcs;
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;
217 if (UserSearchPath)
219 pcs->search_path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(UserSearchPath) + 1),
220 UserSearchPath);
222 else
224 unsigned size;
225 unsigned len;
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);
233 if (len)
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);
238 size += 1 + len;
240 len = GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", NULL, 0);
241 if (len)
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);
246 size += 1 + len;
250 pcs->lmodules = NULL;
251 pcs->dbg_hdr_addr = 0;
252 pcs->next = process_first;
253 process_first = pcs;
255 if (check_live_target(pcs))
257 if (fInvadeProcess)
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);
265 return FALSE;
268 return TRUE;
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);
289 *ppcs = next;
290 return TRUE;
293 return FALSE;
296 /******************************************************************
297 * SymSetOptions (DBGHELP.@)
300 DWORD WINAPI SymSetOptions(DWORD opts)
302 struct process* pcs;
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);
328 return TRUE;
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 */
350 return FALSE;
353 pcs->ctx_frame = *StackFrame;
354 /* MSDN states that Context is not (no longer?) used */
355 return TRUE;
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;
367 void* data32;
368 IMAGEHLP_DEFERRED_SYMBOL_LOAD64* idsl64;
369 IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl;
371 switch (action)
373 case CBA_DEBUG_INFO:
374 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
375 case CBA_SET_OPTIONS:
376 case CBA_SYMBOLS_UNLOADED:
377 data32 = (void*)(DWORD)data;
378 break;
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))
385 return FALSE;
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;
392 data32 = &idsl;
393 break;
394 case CBA_DUPLICATE_SYMBOL:
395 case CBA_EVENT:
396 case CBA_READ_MEMORY:
397 default:
398 FIXME("No mapping for action %lu\n", action);
399 return FALSE;
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,
419 PVOID UserContext)
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,
430 ULONG64 UserContext)
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;
440 return TRUE;
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)
451 return &api_version;
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;
466 return AppVersion;
469 /******************************************************************
470 * ExtensionApiVersion (DBGHELP.@)
472 LPEXT_API_VERSION WINAPI ExtensionApiVersion(void)
474 static EXT_API_VERSION eav = {5, 5, 5, 0};
475 return &eav;
478 /******************************************************************
479 * WinDbgExtensionDllInit (DBGHELP.@)
481 void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis,
482 unsigned short major, unsigned short minor)