- implement SymUnDName and UndecorateSymbolName on top of
[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  *      + funcargtype:s are (partly) wrong: they should be a specific struct (like
34  *        typedef) pointing to the actual type (and not a direct access)
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  *  - most options (dbghelp_options) are not used (loading lines...)
41  *  - in symbol lookup by name, we don't use RE everywhere we should. Moreover, when
42  *    we're supposed to use RE, it doesn't make use of our hash tables. Therefore,
43  *    we could use hash if name isn't a RE, and fall back to a full search when we
44  *    get a full RE
45  *  - msc:
46  *      + we should add parameters' types to the function's signature
47  *        while processing a function's parameters
48  *      + get rid of MSC reading FIXME:s (lots of types are not defined)
49  *      + C++ management
50  *  - stabs: 
51  *      + when, in a same module, the same definition is used in several compilation
52  *        units, we get several definitions of the same object (especially 
53  *        struct/union). we should find a way not to duplicate them
54  *      + in some cases (dlls/user/dialog16.c DIALOG_GetControl16), the same static
55  *        global variable is defined several times (at different scopes). We are
56  *        getting several of those while looking for a unique symbol. Part of the 
57  *        issue is that we don't give a scope to a static variable inside a function
58  *      + C++ management
59  *  - implement the callback notification mechanism
60  */
61
62 unsigned   dbghelp_options = SYMOPT_UNDNAME;
63 HANDLE     hMsvcrt = NULL;
64
65 /***********************************************************************
66  *           DllMain (DEBUGHLP.@)
67  */
68 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
69 {
70     switch (fdwReason)
71     {
72     case DLL_PROCESS_ATTACH:    break;
73     case DLL_PROCESS_DETACH:
74         if (hMsvcrt) FreeLibrary(hMsvcrt);
75         break;
76     case DLL_THREAD_ATTACH:     break;
77     case DLL_THREAD_DETACH:     break;
78     default:                    break;
79     }
80     return TRUE;
81 }
82
83 static struct process* process_first /* = NULL */;
84
85 /******************************************************************
86  *              process_find_by_handle
87  *
88  */
89 struct process*    process_find_by_handle(HANDLE hProcess)
90 {
91     struct process* p;
92
93     for (p = process_first; p && p->handle != hProcess; p = p->next);
94     if (!p) SetLastError(ERROR_INVALID_HANDLE);
95     return p;
96 }
97
98 /******************************************************************
99  *              SymSetSearchPath (DBGHELP.@)
100  *
101  */
102 BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PSTR searchPath)
103 {
104     struct process* pcs = process_find_by_handle(hProcess);
105
106     if (!pcs) return FALSE;
107     if (!searchPath) return FALSE;
108
109     HeapFree(GetProcessHeap(), 0, pcs->search_path);
110     pcs->search_path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(searchPath) + 1),
111                               searchPath);
112     return TRUE;
113 }
114
115 /***********************************************************************
116  *              SymGetSearchPath (DBGHELP.@)
117  */
118 BOOL WINAPI SymGetSearchPath(HANDLE hProcess, LPSTR szSearchPath, 
119                              DWORD SearchPathLength)
120 {
121     struct process* pcs = process_find_by_handle(hProcess);
122     if (!pcs) return FALSE;
123
124     strncpy(szSearchPath, pcs->search_path, SearchPathLength);
125     szSearchPath[SearchPathLength - 1] = '\0';
126     return TRUE;
127 }
128
129 /******************************************************************
130  *              invade_process
131  *
132  * SymInitialize helper: loads in dbghelp all known (and loaded modules)
133  * this assumes that hProcess is a handle on a valid process
134  */
135 static BOOL process_invade(HANDLE hProcess)
136 {
137     HMODULE     hMods[256];
138     char        img[256];
139     DWORD       i, sz;
140     MODULEINFO  mi;
141
142     if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &sz))
143         return FALSE; /* FIXME should grow hMods */
144     
145     for (i = 0; i < sz / sizeof(HMODULE); i++)
146     {
147         if (!GetModuleInformation(hProcess, hMods[i], &mi, sizeof(mi)) ||
148             !GetModuleFileNameExA(hProcess, hMods[i], img, sizeof(img)) ||
149             !SymLoadModule(hProcess, 0, img, NULL, (DWORD)mi.lpBaseOfDll, mi.SizeOfImage))
150             return FALSE;
151     }
152
153     return sz != 0;
154 }
155
156 /******************************************************************
157  *              SymInitialize (DBGHELP.@)
158  *
159  * The initialisation of a dbghelp's context.
160  * Note that hProcess doesn't need to be a valid process handle (except
161  * when fInvadeProcess is TRUE).
162  * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries 
163  * containing PE (and NE) module(s), here's how we handle it:
164  * - we load every module (ELF, NE, PE) passed in SymLoadModule
165  * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
166  *   synchronization: hProcess should be a valid process handle, and we hook
167  *   ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
168  *   our internal ELF modules representation (loading / unloading). This way,
169  *   we'll pair every loaded builtin PE module with its ELF counterpart (and
170  *   access its debug information).
171  * - if fInvadeProcess (in SymInitialize) is FALSE, we won't be able to
172  *   make the peering between a builtin PE module and its ELF counterpart, hence
173  *   we won't be able to provide the requested debug information. We'll
174  *   however be able to load native PE modules (and their debug information)
175  *   without any trouble.
176  * Note also that this scheme can be intertwined with the deferred loading 
177  * mechanism (ie only load the debug information when we actually need it).
178  */
179 BOOL WINAPI SymInitialize(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess)
180 {
181     struct process*     pcs;
182
183     TRACE("(%p %s %u)\n", hProcess, debugstr_a(UserSearchPath), fInvadeProcess);
184
185     if (process_find_by_handle(hProcess))
186         FIXME("what to do ??\n");
187
188     pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
189     if (!pcs) return FALSE;
190
191     pcs->handle = hProcess;
192
193     if (UserSearchPath)
194     {
195         pcs->search_path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(UserSearchPath) + 1), 
196                                   UserSearchPath);
197     }
198     else
199     {
200         unsigned        size;
201         unsigned        len;
202
203         pcs->search_path = HeapAlloc(GetProcessHeap(), 0, len = MAX_PATH);
204         while ((size = GetCurrentDirectoryA(len, pcs->search_path)) >= len)
205             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, len *= 2);
206         pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1);
207
208         len = GetEnvironmentVariableA("_NT_SYMBOL_PATH", NULL, 0);
209         if (len)
210         {
211             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1 + len + 1);
212             pcs->search_path[size] = ';';
213             GetEnvironmentVariableA("_NT_SYMBOL_PATH", pcs->search_path + size + 1, len);
214             size += 1 + len;
215         }
216         len = GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", NULL, 0);
217         if (len)
218         {
219             pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1 + len + 1);
220             pcs->search_path[size] = ';';
221             GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", pcs->search_path + size + 1, len);
222             size += 1 + len;
223         }
224     }
225
226     pcs->lmodules = NULL;
227     pcs->dbg_hdr_addr = 0;
228     pcs->next = process_first;
229     process_first = pcs;
230
231     if (fInvadeProcess)
232     {
233         if (!elf_read_wine_loader_dbg_info(pcs))
234         {
235             SymCleanup(hProcess);
236             return FALSE;
237         }
238         process_invade(hProcess);
239         elf_synchronize_module_list(pcs);
240     }
241     return TRUE;
242 }
243
244 /******************************************************************
245  *              SymCleanup (DBGHELP.@)
246  *
247  */
248 BOOL WINAPI SymCleanup(HANDLE hProcess)
249 {
250     struct process**    ppcs;
251     struct process*     next;
252
253     for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
254     {
255         if ((*ppcs)->handle == hProcess)
256         {
257             while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
258
259             HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
260             next = (*ppcs)->next;
261             HeapFree(GetProcessHeap(), 0, *ppcs);
262             *ppcs = next;
263             return TRUE;
264         }
265     }
266     return FALSE;
267 }
268
269 /******************************************************************
270  *              SymSetOptions (DBGHELP.@)
271  *
272  */
273 DWORD WINAPI SymSetOptions(DWORD opts)
274 {
275     return dbghelp_options = opts;
276 }
277
278 /******************************************************************
279  *              SymGetOptions (DBGHELP.@)
280  *
281  */
282 DWORD WINAPI SymGetOptions(void)
283 {
284     return dbghelp_options;
285 }
286
287 /******************************************************************
288  *              SymSetContext (DBGHELP.@)
289  *
290  */
291 BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
292                           PIMAGEHLP_CONTEXT Context)
293 {
294     struct process* pcs = process_find_by_handle(hProcess);
295     if (!pcs) return FALSE;
296
297     pcs->ctx_frame = *StackFrame;
298     /* MSDN states that Context is not (no longer?) used */
299     return TRUE;
300 }
301
302 /***********************************************************************
303  *              SymRegisterCallback (DBGHELP.@)
304  */
305 BOOL WINAPI SymRegisterCallback(HANDLE hProcess, 
306                                 PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
307                                 PVOID UserContext)
308 {
309     FIXME("(%p, %p, %p): stub\n", hProcess, CallbackFunction, UserContext);
310     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
311     return FALSE;
312 }
313
314 /* This is imagehlp version not dbghelp !! */
315 static API_VERSION api_version = { 4, 0, 2, 0 };
316
317 /***********************************************************************
318  *           ImagehlpApiVersion (DBGHELP.@)
319  */
320 LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
321 {
322     return &api_version;
323 }
324
325 /***********************************************************************
326  *           ImagehlpApiVersionEx (DBGHELP.@)
327  */
328 LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
329 {
330     if (!AppVersion) return NULL;
331
332     AppVersion->MajorVersion = api_version.MajorVersion;
333     AppVersion->MinorVersion = api_version.MinorVersion;
334     AppVersion->Revision = api_version.Revision;
335     AppVersion->Reserved = api_version.Reserved;
336
337     return AppVersion;
338 }