Introduce a variable "elemdesc" to eliminate some long common
[wine] / dlls / dbghelp / dbghelp.c
1 /*
2  * File dbghelp.c - generic routines (process) for dbghelp DLL
3  *
4  * Copyright (C) 2004, Eric Pouech
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #include "dbghelp_private.h"
24 #include "winerror.h"
25 #include "psapi.h"
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
29
30 /* TODO
31  *  - support for symbols' types is still partly missing
32  *      + C++ support
33  *      + we should store the underlying type for an enum in the symt_enum struct
34  *      + for enums, we store the names & values (associated to the enum type), 
35  *        but those values are not directly usable from a debugger (that's why, I
36  *        assume, that we have also to define constants for enum values, as 
37  *        Codeview does BTW.
38  *      + SymEnumTypes should only return *user* defined types (UDT, typedefs...) not
39  *        all the types stored/used in the modules (like char*)
40  *  - SymGetLine{Next|Prev} don't work as expected (they don't seem to work across
41  *    functions, and even across function blocks...). Basically, for *Next* to work
42  *    it requires an address after the prolog of the func (the base address of the 
43  *    func doesn't work)
44  *  - most options (dbghelp_options) are not used (loading lines...)
45  *  - in symbol lookup by name, we don't use RE everywhere we should. Moreover, when
46  *    we're supposed to use RE, it doesn't make use of our hash tables. Therefore,
47  *    we could use hash if name isn't a RE, and fall back to a full search when we
48  *    get a full RE
49  *  - msc:
50  *      + we should add parameters' types to the function's signature
51  *        while processing a function's parameters
52  *      + add support for function-less labels (as MSC seems to define them)
53  *      + C++ management
54  *  - stabs: 
55  *      + when, in a same module, the same definition is used in several compilation
56  *        units, we get several definitions of the same object (especially 
57  *        struct/union). we should find a way not to duplicate them
58  *      + in some cases (dlls/user/dialog16.c DIALOG_GetControl16), the same static
59  *        global variable is defined several times (at different scopes). We are
60  *        getting several of those while looking for a unique symbol. Part of the 
61  *        issue is that we don't give a scope to a static variable inside a function
62  *      + C++ management
63  *  - implement the callback notification mechanism
64  */
65
66 unsigned   dbghelp_options = SYMOPT_UNDNAME;
67 HANDLE     hMsvcrt = NULL;
68
69 /***********************************************************************
70  *           DllMain (DEBUGHLP.@)
71  */
72 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
73 {
74     switch (fdwReason)
75     {
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;
83     }
84     return TRUE;
85 }
86
87 static struct process* process_first /* = NULL */;
88
89 /******************************************************************
90  *              process_find_by_handle
91  *
92  */
93 struct process*    process_find_by_handle(HANDLE hProcess)
94 {
95     struct process* p;
96
97     for (p = process_first; p && p->handle != hProcess; p = p->next);
98     if (!p) SetLastError(ERROR_INVALID_HANDLE);
99     return p;
100 }
101
102 /******************************************************************
103  *             validate_addr64 (internal)
104  *
105  */
106 BOOL validate_addr64(DWORD64 addr)
107 {
108     if (addr >> 32)
109     {
110         FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
111         SetLastError(ERROR_INVALID_PARAMETER);
112         return FALSE;
113     }
114     return TRUE;
115 }
116
117 /******************************************************************
118  *              SymSetSearchPath (DBGHELP.@)
119  *
120  */
121 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PSTR searchPath)
122 {
123     struct process* pcs = process_find_by_handle(hProcess);
124
125     if (!pcs) return FALSE;
126     if (!searchPath) return FALSE;
127
128     HeapFree(GetProcessHeap(), 0, pcs->search_path);
129     pcs->search_path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(searchPath) + 1),
130                               searchPath);
131     return TRUE;
132 }
133
134 /***********************************************************************
135  *              SymGetSearchPath (DBGHELP.@)
136  */
137 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, LPSTR szSearchPath, 
138                              DWORD SearchPathLength)
139 {
140     struct process* pcs = process_find_by_handle(hProcess);
141     if (!pcs) return FALSE;
142
143     lstrcpynA(szSearchPath, pcs->search_path, SearchPathLength);
144     return TRUE;
145 }
146
147 /******************************************************************
148  *              invade_process
149  *
150  * SymInitialize helper: loads in dbghelp all known (and loaded modules)
151  * this assumes that hProcess is a handle on a valid process
152  */
153 static BOOL WINAPI process_invade_cb(char* name, DWORD base, DWORD size, void* user)
154 {
155     char        tmp[MAX_PATH];
156     HANDLE      hProcess = (HANDLE)user;
157
158     if (!GetModuleFileNameExA(hProcess, (HMODULE)base, 
159                               tmp, sizeof(tmp)))
160         lstrcpynA(tmp, name, sizeof(tmp));
161
162     SymLoadModule(hProcess, 0, tmp, name, base, size);
163     return TRUE;
164 }
165
166 /******************************************************************
167  *              SymInitialize (DBGHELP.@)
168  *
169  * The initialisation of a dbghelp's context.
170  * Note that hProcess doesn't need to be a valid process handle (except
171  * when fInvadeProcess is TRUE).
172  * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries 
173  * containing PE (and NE) module(s), here's how we handle it:
174  * - we load every module (ELF, NE, PE) passed in SymLoadModule
175  * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
176  *   synchronization: hProcess should be a valid process handle, and we hook
177  *   ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
178  *   our internal ELF modules representation (loading / unloading). This way,
179  *   we'll pair every loaded builtin PE module with its ELF counterpart (and
180  *   access its debug information).
181  * - if fInvadeProcess (in SymInitialize) is FALSE, we won't be able to
182  *   make the peering between a builtin PE module and its ELF counterpart, hence
183  *   we won't be able to provide the requested debug information. We'll
184  *   however be able to load native PE modules (and their debug information)
185  *   without any trouble.
186  * Note also that this scheme can be intertwined with the deferred loading 
187  * mechanism (ie only load the debug information when we actually need it).
188  */
189 BOOL WINAPI SymInitialize(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess)
190 {
191     struct process*     pcs;
192
193     TRACE("(%p %s %u)\n", hProcess, debugstr_a(UserSearchPath), fInvadeProcess);
194
195     if (process_find_by_handle(hProcess))
196         FIXME("what to do ??\n");
197
198     pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
199     if (!pcs) return FALSE;
200
201     pcs->handle = hProcess;
202
203     if (UserSearchPath)
204     {
205         pcs->search_path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(UserSearchPath) + 1), 
206                                   UserSearchPath);
207     }
208     else
209     {
210         unsigned        size;
211         unsigned        len;
212
213         pcs->search_path = HeapAlloc(GetProcessHeap(), 0, len = MAX_PATH);
214         while ((size = GetCurrentDirectoryA(len, pcs->search_path)) >= len)
215             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, len *= 2);
216         pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1);
217
218         len = GetEnvironmentVariableA("_NT_SYMBOL_PATH", NULL, 0);
219         if (len)
220         {
221             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1 + len + 1);
222             pcs->search_path[size] = ';';
223             GetEnvironmentVariableA("_NT_SYMBOL_PATH", pcs->search_path + size + 1, len);
224             size += 1 + len;
225         }
226         len = GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", NULL, 0);
227         if (len)
228         {
229             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1 + len + 1);
230             pcs->search_path[size] = ';';
231             GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", pcs->search_path + size + 1, len);
232             size += 1 + len;
233         }
234     }
235
236     pcs->lmodules = NULL;
237     pcs->dbg_hdr_addr = 0;
238     pcs->next = process_first;
239     process_first = pcs;
240
241     if (fInvadeProcess)
242     {
243         if (!elf_read_wine_loader_dbg_info(pcs))
244         {
245             SymCleanup(hProcess);
246             return FALSE;
247         }
248         EnumerateLoadedModules(hProcess, process_invade_cb, (void*)hProcess);
249         elf_synchronize_module_list(pcs);
250     }
251
252     return TRUE;
253 }
254
255 /******************************************************************
256  *              SymCleanup (DBGHELP.@)
257  *
258  */
259 BOOL WINAPI SymCleanup(HANDLE hProcess)
260 {
261     struct process**    ppcs;
262     struct process*     next;
263
264     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
265     {
266         if ((*ppcs)->handle == hProcess)
267         {
268             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
269
270             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
271             next = (*ppcs)->next;
272             HeapFree(GetProcessHeap(), 0, *ppcs);
273             *ppcs = next;
274             return TRUE;
275         }
276     }
277     return FALSE;
278 }
279
280 /******************************************************************
281  *              SymSetOptions (DBGHELP.@)
282  *
283  */
284 DWORD WINAPI SymSetOptions(DWORD opts)
285 {
286     return dbghelp_options = opts;
287 }
288
289 /******************************************************************
290  *              SymGetOptions (DBGHELP.@)
291  *
292  */
293 DWORD WINAPI SymGetOptions(void)
294 {
295     return dbghelp_options;
296 }
297
298 /******************************************************************
299  *              SymSetParentWindow (DBGHELP.@)
300  *
301  */
302 BOOL WINAPI SymSetParentWindow(HWND hwnd)
303 {
304     /* Save hwnd so it can be used as parent window */
305     FIXME("(%p): stub\n", hwnd);
306     return TRUE;
307 }
308
309 /******************************************************************
310  *              SymSetContext (DBGHELP.@)
311  *
312  */
313 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
314                           PIMAGEHLP_CONTEXT Context)
315 {
316     struct process* pcs = process_find_by_handle(hProcess);
317     if (!pcs) return FALSE;
318
319     pcs->ctx_frame = *StackFrame;
320     /* MSDN states that Context is not (no longer?) used */
321     return TRUE;
322 }
323
324 /***********************************************************************
325  *              SymRegisterCallback (DBGHELP.@)
326  */
327 BOOL WINAPI SymRegisterCallback(HANDLE hProcess, 
328                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
329                                 PVOID UserContext)
330 {
331     FIXME("(%p, %p, %p): stub\n", hProcess, CallbackFunction, UserContext);
332     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
333     return FALSE;
334 }
335
336 /***********************************************************************
337  *              SymRegisterCallback64 (DBGHELP.@)
338  */
339 BOOL WINAPI SymRegisterCallback64(HANDLE hProcess, 
340                                   PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction,
341                                   ULONG64 UserContext)
342 {
343     FIXME("(%p, %p, %s): stub\n", 
344           hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext));
345     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
346     return FALSE;
347 }
348
349 /* This is imagehlp version not dbghelp !! */
350 static API_VERSION api_version = { 4, 0, 2, 0 };
351
352 /***********************************************************************
353  *           ImagehlpApiVersion (DBGHELP.@)
354  */
355 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
356 {
357     return &api_version;
358 }
359
360 /***********************************************************************
361  *           ImagehlpApiVersionEx (DBGHELP.@)
362  */
363 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
364 {
365     if (!AppVersion) return NULL;
366
367     AppVersion->MajorVersion = api_version.MajorVersion;
368     AppVersion->MinorVersion = api_version.MinorVersion;
369     AppVersion->Revision = api_version.Revision;
370     AppVersion->Reserved = api_version.Reserved;
371
372     return AppVersion;
373 }