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