Added CSIDL_MYVIDEO|MYPICTURES|MYMUSIC to _SHRegisterUserShellFolders.
[wine] / dlls / dbghelp / stack.c
1 /*
2  * Stack walking
3  *
4  * Copyright 1995 Alexandre Julliard
5  * Copyright 1996 Eric Youngdale
6  * Copyright 1999 Ove Kåven
7  * Copyright 2004 Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32 #include "dbghelp_private.h"
33 #include "winreg.h"
34 #include "winternl.h"
35 #include "wine/winbase16.h"
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
39
40 enum st_mode {stm_start, stm_32bit, stm_16bit, stm_done};
41
42 static const char* wine_dbgstr_addr(const ADDRESS* addr)
43 {
44     if (!addr) return "(null)";
45     switch (addr->Mode)
46     {
47     case AddrModeFlat:
48         return wine_dbg_sprintf("flat<%08lx>", addr->Offset);
49     case AddrMode1616:
50         return wine_dbg_sprintf("1616<%04x:%04lx>", addr->Segment, addr->Offset);
51     case AddrMode1632:
52         return wine_dbg_sprintf("1632<%04x:%08lx>", addr->Segment, addr->Offset);
53     case AddrModeReal:
54         return wine_dbg_sprintf("real<%04x:%04lx>", addr->Segment, addr->Offset);
55     default:
56         return "unknown";
57     }
58 }
59
60 static BOOL CALLBACK read_mem(HANDLE hProcess, DWORD addr, void* buffer,
61                               DWORD size, LPDWORD nread)
62 {
63     return ReadProcessMemory(hProcess, (void*)addr, buffer, size, nread);
64 }
65
66 /* indexes in Reserved array */
67 #define __CurrentMode     0
68 #define __CurrentSwitch   1
69 #define __NextSwitch      2
70
71 #define curr_mode   (frame->Reserved[__CurrentMode])
72 #define curr_switch (frame->Reserved[__CurrentSwitch])
73 #define next_switch (frame->Reserved[__NextSwitch])
74
75 /***********************************************************************
76  *              StackWalk (DBGHELP.@)
77  */
78 BOOL WINAPI StackWalk(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
79                       LPSTACKFRAME frame, LPVOID ctx,
80                       PREAD_PROCESS_MEMORY_ROUTINE f_read_mem,
81                       PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
82                       PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
83                       PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr)
84 {
85     STACK32FRAME        frame32;
86     STACK16FRAME        frame16;
87     char                ch;
88     ADDRESS             tmp;
89     DWORD               p;
90     WORD                val;
91     BOOL                do_switch;
92
93     TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p)\n",
94           MachineType, hProcess, hThread, frame, ctx,
95           f_read_mem, FunctionTableAccessRoutine,
96           GetModuleBaseRoutine, f_xlat_adr);
97
98     if (MachineType != IMAGE_FILE_MACHINE_I386)
99     {
100         SetLastError(ERROR_INVALID_PARAMETER);
101         return FALSE;
102     }
103
104     /* sanity check */
105     if (curr_mode >= stm_done) return FALSE;
106
107     /* sigh... MS isn't even consistent in the func prototypes */
108     if (!f_read_mem) f_read_mem = read_mem;
109     if (!f_xlat_adr) f_xlat_adr = addr_to_linear;
110
111     TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
112           wine_dbgstr_addr(&frame->AddrPC), 
113           wine_dbgstr_addr(&frame->AddrFrame),
114           wine_dbgstr_addr(&frame->AddrReturn),
115           wine_dbgstr_addr(&frame->AddrStack), 
116           curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
117           curr_switch, next_switch);
118
119     if (curr_mode == stm_start)
120     {
121         THREAD_BASIC_INFORMATION info;
122
123         if ((frame->AddrPC.Mode == AddrModeFlat) &&
124             (frame->AddrFrame.Mode != AddrModeFlat))
125         {
126             WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
127             goto done_err;
128         }
129
130         /* Init done */
131         curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ? 
132             stm_32bit : stm_16bit;
133
134         /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
135          * address space
136          */
137         if (NtQueryInformationThread(hThread, ThreadBasicInformation, &info,    
138                                      sizeof(info), NULL) == STATUS_SUCCESS)
139         {
140             curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, WOW32Reserved);
141             if (!f_read_mem(hProcess, curr_switch, &next_switch,
142                         sizeof(next_switch), NULL))
143             {
144                 WARN("Can't read TEB:WOW32Reserved\n");
145                 goto done_err;
146             }
147             if (curr_mode == stm_16bit)
148             {
149                 if (!f_read_mem(hProcess, next_switch, &frame32,
150                                 sizeof(frame32), NULL))
151                 {
152                     WARN("Bad stack frame 0x%08lx\n", next_switch);
153                     goto done_err;
154                 }
155                 curr_switch = (DWORD)frame32.frame16;
156                 tmp.Mode    = AddrMode1616;
157                 tmp.Segment = SELECTOROF(curr_switch);
158                 tmp.Offset  = OFFSETOF(curr_switch);
159                 if (!f_read_mem(hProcess, f_xlat_adr(hProcess, hThread, &tmp),
160                                 &ch, sizeof(ch), NULL))
161                     curr_switch = 0xFFFFFFFF;
162             }
163             else
164             {
165                 tmp.Mode    = AddrMode1616;
166                 tmp.Segment = SELECTOROF(next_switch);
167                 tmp.Offset  = OFFSETOF(next_switch);
168                 p = f_xlat_adr(hProcess, hThread, &tmp);
169                 if (!f_read_mem(hProcess, p, &frame16, sizeof(frame16), NULL))
170                 {
171                     WARN("Bad stack frame 0x%08lx\n", p);
172                     goto done_err;
173                 }
174                 curr_switch = (DWORD)frame16.frame32;
175
176                 if (!f_read_mem(hProcess, curr_switch, &ch, sizeof(ch), NULL))
177                     curr_switch = 0xFFFFFFFF;
178             }
179         }
180         else
181             /* FIXME: this will allow to work when we're not attached to a live target, 
182              * but the 16 <=> 32 switch facility won't be available.
183              */
184             curr_switch = 0;
185         frame->AddrReturn.Mode = frame->AddrStack.Mode = (curr_mode == stm_16bit) ? AddrMode1616 : AddrModeFlat;
186         /* don't set up AddrStack on first call. Either the caller has set it up, or
187          * we will get it in the next frame
188          */
189     }
190     else
191     {
192         if (frame->AddrFrame.Offset == 0) goto done_err;
193         if (frame->AddrFrame.Mode == AddrModeFlat)
194         {
195             assert(curr_mode == stm_32bit);
196             do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
197         }
198         else
199         {
200             assert(curr_mode == stm_16bit);
201             do_switch = curr_switch && 
202                 frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
203                 frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
204         }
205            
206         if (do_switch)
207         {
208             if (curr_mode == stm_16bit)
209             {
210                 if (!f_read_mem(hProcess, next_switch, &frame32, 
211                                 sizeof(frame32), NULL))
212                 {
213                     WARN("Bad stack frame 0x%08lx\n", next_switch);
214                     goto done_err;
215                 }
216
217                 frame->AddrPC.Mode        = AddrModeFlat;
218                 frame->AddrPC.Segment     = 0;
219                 frame->AddrPC.Offset      = frame32.retaddr;
220                 frame->AddrFrame.Mode     = AddrModeFlat;
221                 frame->AddrFrame.Segment  = 0;
222                 frame->AddrFrame.Offset   = frame32.ebp;
223
224                 frame->AddrStack.Mode     = AddrModeFlat;
225                 frame->AddrStack.Segment  = 0;
226                 frame->AddrReturn.Mode    = AddrModeFlat;
227                 frame->AddrReturn.Segment = 0;
228
229                 next_switch = curr_switch;
230                 tmp.Mode    = AddrMode1616;
231                 tmp.Segment = SELECTOROF(next_switch);
232                 tmp.Offset  = OFFSETOF(next_switch);
233                 p = f_xlat_adr(hProcess, hThread, &tmp);
234
235                 if (!f_read_mem(hProcess, p, &frame16, sizeof(frame16), NULL))
236                 {
237                     WARN("Bad stack frame 0x%08lx\n", p);
238                     goto done_err;
239                 }
240                 curr_switch = (DWORD)frame16.frame32;
241                 curr_mode = stm_32bit;
242                 if (!f_read_mem(hProcess, curr_switch, &ch, sizeof(ch), NULL))
243                     curr_switch = 0;
244             }
245             else
246             {
247                 tmp.Mode    = AddrMode1616;
248                 tmp.Segment = SELECTOROF(next_switch);
249                 tmp.Offset  = OFFSETOF(next_switch);
250                 p = f_xlat_adr(hProcess, hThread, &tmp);
251
252                 if (!f_read_mem(hProcess, p, &frame16, sizeof(frame16), NULL))
253                 {
254                     WARN("Bad stack frame 0x%08lx\n", p);
255                     goto done_err;
256                 }
257
258                 TRACE("Got a 16 bit stack switch:"
259                       "\n\tframe32: %08lx"
260                       "\n\tedx:%08lx ecx:%08lx ebp:%08lx"
261                       "\n\tds:%04x es:%04x fs:%04x gs:%04x"
262                       "\n\tcall_from_ip:%08lx module_cs:%04lx relay=%08lx"
263                       "\n\tentry_ip:%04x entry_point:%08lx"
264                       "\n\tbp:%04x ip:%04x cs:%04x\n",
265                       (unsigned long)frame16.frame32,
266                       frame16.edx, frame16.ecx, frame16.ebp,
267                       frame16.ds, frame16.es, frame16.fs, frame16.gs,
268                       frame16.callfrom_ip, frame16.module_cs, frame16.relay,
269                       frame16.entry_ip, frame16.entry_point,
270                       frame16.bp, frame16.ip, frame16.cs);
271
272                       
273                 frame->AddrPC.Mode       = AddrMode1616;
274                 frame->AddrPC.Segment    = frame16.cs;
275                 frame->AddrPC.Offset     = frame16.ip;
276
277                 frame->AddrFrame.Mode    = AddrMode1616;
278                 frame->AddrFrame.Segment = SELECTOROF(next_switch);
279                 frame->AddrFrame.Offset  = frame16.bp;
280
281                 frame->AddrStack.Mode    = AddrMode1616;
282                 frame->AddrStack.Segment = SELECTOROF(next_switch);
283
284                 frame->AddrReturn.Mode    = AddrMode1616;
285                 frame->AddrReturn.Segment = frame16.cs;
286
287                 next_switch = curr_switch;
288                 if (!f_read_mem(hProcess, next_switch, &frame32, sizeof(frame32), NULL))
289                 {
290                     WARN("Bad stack frame 0x%08lx\n", next_switch);
291                     goto done_err;
292                 }
293                 curr_switch = (DWORD)frame32.frame16;
294                 tmp.Mode    = AddrMode1616;
295                 tmp.Segment = SELECTOROF(curr_switch);
296                 tmp.Offset  = OFFSETOF(curr_switch);
297
298                 if (!f_read_mem(hProcess, f_xlat_adr(hProcess, hThread, &tmp),
299                                 &ch, sizeof(ch), NULL))
300                     curr_switch = 0;
301                 curr_mode = stm_16bit;
302             }
303         }
304         else
305         {
306             frame->AddrPC = frame->AddrReturn;
307             if (curr_mode == stm_16bit)
308             {
309                 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(WORD);
310                 /* "pop up" previous BP value */
311                 if (!f_read_mem(hProcess, 
312                                 f_xlat_adr(hProcess, hThread, &frame->AddrFrame),
313                                 &val, sizeof(WORD), NULL))
314                     goto done_err;
315                 frame->AddrFrame.Offset = val;
316             }
317             else
318             {
319                 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(DWORD);
320                 /* "pop up" previous EBP value */
321                 if (!f_read_mem(hProcess, frame->AddrFrame.Offset, 
322                                 &frame->AddrFrame.Offset, sizeof(DWORD), NULL))
323                     goto done_err;
324             }
325         }
326     }
327
328     if (curr_mode == stm_16bit)
329     {
330         int     i;
331
332         p = f_xlat_adr(hProcess, hThread, &frame->AddrFrame);
333         if (!f_read_mem(hProcess, p + sizeof(WORD), &val, sizeof(WORD), NULL))
334             goto done_err;
335         frame->AddrReturn.Offset = val;
336         /* get potential cs if a far call was used */
337         if (!f_read_mem(hProcess, p + 2 * sizeof(WORD), 
338                         &val, sizeof(WORD), NULL))
339             goto done_err;
340         if (frame->AddrFrame.Offset & 1)
341             frame->AddrReturn.Segment = val; /* far call assumed */
342         else
343         {
344             /* not explicitly marked as far call, 
345              * but check whether it could be anyway
346              */
347             if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
348             {
349                 LDT_ENTRY       le;
350
351                 if (GetThreadSelectorEntry(hThread, val, &le) &&
352                     (le.HighWord.Bits.Type & 0x08)) /* code segment */
353                 {
354                     /* it is very uncommon to push a code segment cs as
355                      * a parameter, so this should work in most cases 
356                      */
357                     frame->AddrReturn.Segment = val;
358                 }
359             }
360         }
361         frame->AddrFrame.Offset &= ~1;
362         /* we "pop" parameters as 16 bit entities... of course, this won't
363          * work if the parameter is in fact bigger than 16bit, but
364          * there's no way to know that here
365          */
366         for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
367         {
368             f_read_mem(hProcess, p + (2 + i) * sizeof(WORD), 
369                        &val, sizeof(val), NULL);
370             frame->Params[i] = val;
371         }
372     }
373     else
374     {
375         if (!f_read_mem(hProcess, 
376                         frame->AddrFrame.Offset + sizeof(DWORD),
377                         &frame->AddrReturn.Offset, sizeof(DWORD), NULL))
378         {
379             WARN("Cannot read new frame offset %08lx\n", frame->AddrFrame.Offset + sizeof(DWORD));
380             goto done_err;
381         }
382         f_read_mem(hProcess, 
383                    frame->AddrFrame.Offset + 2 * sizeof(DWORD), 
384                    frame->Params, sizeof(frame->Params), NULL);
385     }
386
387     frame->Far = FALSE;
388     frame->Virtual = FALSE;
389
390     TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
391           wine_dbgstr_addr(&frame->AddrPC), 
392           wine_dbgstr_addr(&frame->AddrFrame),
393           wine_dbgstr_addr(&frame->AddrReturn),
394           wine_dbgstr_addr(&frame->AddrStack), 
395           curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
396           curr_switch, next_switch);
397
398     return TRUE;
399 done_err:
400     curr_mode = stm_done;
401     return FALSE;
402 }
403
404 /***********************************************************************
405  *              StackWalk64 (DBGHELP.@)
406  */
407 BOOL WINAPI StackWalk64(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
408                         LPSTACKFRAME64 frame, LPVOID ctx,
409                         PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem,
410                         PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
411                         PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
412                         PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr)
413 {
414     FIXME("(%ld, %p, %p, %p, %p, %p, %p, %p, %p) - stub!\n",
415           MachineType, hProcess, hThread, frame, ctx,
416           f_read_mem, FunctionTableAccessRoutine,
417           GetModuleBaseRoutine, f_xlat_adr);
418     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
419     return FALSE;
420 }
421
422 /******************************************************************
423  *              SymRegisterFunctionEntryCallback (DBGHELP.@)
424  *
425  *
426  */
427 BOOL WINAPI SymRegisterFunctionEntryCallback(HANDLE hProc,
428                                              PSYMBOL_FUNCENTRY_CALLBACK cb, PVOID user)
429 {
430     FIXME("(%p %p %p): stub!\n", hProc, cb, user);
431     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
432     return FALSE;
433 }
434
435 /******************************************************************
436  *              SymRegisterFunctionEntryCallback64 (DBGHELP.@)
437  *
438  *
439  */
440 BOOL WINAPI SymRegisterFunctionEntryCallback64(HANDLE hProc,
441                                                PSYMBOL_FUNCENTRY_CALLBACK64 cb,
442                                                ULONG64 user)
443 {
444     FIXME("(%p %p %s): stub!\n", hProc, cb, wine_dbgstr_longlong(user));
445     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
446     return FALSE;
447 }