ShellExecuteEx, ExtractIconEx, SHFileOperation, SHGetFileInfo,
[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 "dbghelp_private.h"
31 #include "winreg.h"
32 #include "ntstatus.h"
33 #include "thread.h" /* FIXME: must be included before winternl.h */
34 #include "winternl.h"
35 #include "wine/debug.h"
36 #include "stackframe.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 /* indexes in Reserved array */
61 #define __CurrentMode     0
62 #define __CurrentSwitch   1
63 #define __NextSwitch      2
64
65 #define curr_mode   (frame->Reserved[__CurrentMode])
66 #define curr_switch (frame->Reserved[__CurrentSwitch])
67 #define next_switch (frame->Reserved[__NextSwitch])
68
69 /***********************************************************************
70  *              StackWalk (DBGHELP.@)
71  */
72 BOOL WINAPI StackWalk(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
73                       LPSTACKFRAME frame, LPVOID ctx,
74                       PREAD_PROCESS_MEMORY_ROUTINE f_read_mem,
75                       PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
76                       PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
77                       PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr)
78 {
79     STACK32FRAME        frame32;
80     STACK16FRAME        frame16;
81     char                ch;
82     ADDRESS             tmp;
83     DWORD               p;
84     WORD                val;
85     BOOL                do_switch;
86
87     TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p)\n",
88           MachineType, hProcess, hThread, frame, ctx,
89           f_read_mem, FunctionTableAccessRoutine,
90           GetModuleBaseRoutine, f_xlat_adr);
91
92     if (MachineType != IMAGE_FILE_MACHINE_I386)
93     {
94         SetLastError(ERROR_INVALID_PARAMETER);
95         return FALSE;
96     }
97
98     /* sanity check */
99     if (curr_mode >= stm_done) return FALSE;
100
101     if (!f_read_mem) f_read_mem = ReadProcessMemory;
102     if (!f_xlat_adr) f_xlat_adr = addr_to_linear;
103
104     TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
105           wine_dbgstr_addr(&frame->AddrPC), 
106           wine_dbgstr_addr(&frame->AddrFrame),
107           wine_dbgstr_addr(&frame->AddrReturn),
108           wine_dbgstr_addr(&frame->AddrStack), 
109           curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
110           curr_switch, next_switch);
111
112     if (curr_mode == stm_start)
113     {
114         THREAD_BASIC_INFORMATION info;
115
116         if ((frame->AddrPC.Mode == AddrModeFlat) &&
117             (frame->AddrFrame.Mode != AddrModeFlat))
118         {
119             WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
120             goto done_err;
121         }
122
123         /* Init done */
124         curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ? 
125             stm_32bit : stm_16bit;
126
127         /* cur_switch holds address of curr_stack's field in TEB in debuggee
128          * address space
129          */
130         if (NtQueryInformationThread(hThread, ThreadBasicInformation, &info,    
131                                      sizeof(info), NULL) != STATUS_SUCCESS)
132             goto done_err;
133         curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, cur_stack);
134         if (!f_read_mem(hProcess, (void*)curr_switch, &next_switch, 
135                         sizeof(next_switch), NULL))
136         {
137             WARN("Can't read TEB:cur_stack\n");
138             goto done_err;
139         }
140         if (curr_mode == stm_16bit)
141         {
142             if (!f_read_mem(hProcess, (void*)next_switch, &frame32, 
143                             sizeof(frame32), NULL))
144             {
145                 WARN("Bad stack frame 0x%08lx\n", next_switch);
146                 goto done_err;
147             }
148             curr_switch = (DWORD)frame32.frame16;
149             tmp.Mode    = AddrMode1616;
150             tmp.Segment = SELECTOROF(curr_switch);
151             tmp.Offset  = OFFSETOF(curr_switch);
152             if (!f_read_mem(hProcess, (void*)f_xlat_adr(hProcess, hThread, &tmp),
153                             &ch, sizeof(ch), NULL))
154                 curr_switch = 0xFFFFFFFF;
155             frame->AddrReturn.Mode = frame->AddrStack.Mode = AddrMode1616;
156         }
157         else
158         {
159             tmp.Mode    = AddrMode1616;
160             tmp.Segment = SELECTOROF(next_switch);
161             tmp.Offset  = OFFSETOF(next_switch);
162             p = f_xlat_adr(hProcess, hThread, &tmp);
163             if (!f_read_mem(hProcess, (void*)p, &frame16, sizeof(frame16), NULL))
164             {
165                 WARN("Bad stack frame 0x%08lx\n", p);
166                 goto done_err;
167             }
168             curr_switch = (DWORD)frame16.frame32;
169
170             if (!f_read_mem(hProcess, (void*)curr_switch, &ch, sizeof(ch), NULL))
171                 curr_switch = 0xFFFFFFFF;
172             frame->AddrReturn.Mode = frame->AddrStack.Mode = AddrModeFlat;
173         }
174         /* don't set up AddrStack on first call. Either the caller has set it up, or
175          * we will get it in the next frame
176          */
177     }
178     else
179     {
180         if (frame->AddrFrame.Offset == 0) goto done_err;
181         if (frame->AddrFrame.Mode == AddrModeFlat)
182         {
183             assert(curr_mode == stm_32bit);
184             do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
185         }
186         else
187         {
188             assert(curr_mode == stm_16bit);
189             do_switch = OFFSETOF(curr_switch) && 
190                 frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
191                 frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
192         }
193            
194         if (do_switch)
195         {
196             if (curr_mode == stm_16bit)
197             {
198                 if (!f_read_mem(hProcess, (void*)next_switch, &frame32, 
199                                 sizeof(frame32), NULL))
200                 {
201                     WARN("Bad stack frame 0x%08lx\n", next_switch);
202                     goto done_err;
203                 }
204
205                 frame->AddrPC.Mode        = AddrModeFlat;
206                 frame->AddrPC.Segment     = 0;
207                 frame->AddrPC.Offset      = frame32.retaddr;
208                 frame->AddrFrame.Mode     = AddrModeFlat;
209                 frame->AddrFrame.Segment  = 0;
210                 frame->AddrFrame.Offset   = frame32.ebp;
211
212                 frame->AddrStack.Mode     = AddrModeFlat;
213                 frame->AddrStack.Segment  = 0;
214                 frame->AddrReturn.Mode    = AddrModeFlat;
215                 frame->AddrReturn.Segment = 0;
216
217                 next_switch = curr_switch;
218                 tmp.Mode    = AddrMode1616;
219                 tmp.Segment = SELECTOROF(next_switch);
220                 tmp.Offset  = OFFSETOF(next_switch);
221                 p = f_xlat_adr(hProcess, hThread, &tmp);
222
223                 if (!f_read_mem(hProcess, (void*)p, &frame16, sizeof(frame16), NULL))
224                 {
225                     WARN("Bad stack frame 0x%08lx\n", p);
226                     goto done_err;
227                 }
228                 curr_switch = (DWORD)frame16.frame32;
229                 curr_mode = stm_32bit;
230                 if (!f_read_mem(hProcess, (void*)curr_switch, &ch, sizeof(ch), NULL))
231                     curr_switch = 0xFFFFFFFF;
232             }
233             else
234             {
235                 tmp.Mode    = AddrMode1616;
236                 tmp.Segment = SELECTOROF(next_switch);
237                 tmp.Offset  = OFFSETOF(next_switch);
238                 p = f_xlat_adr(hProcess, hThread, &tmp);
239
240                 if (!f_read_mem(hProcess, (void*)p, &frame16, sizeof(frame16), NULL))
241                 {
242                     WARN("Bad stack frame 0x%08lx\n", p);
243                     goto done_err;
244                 }
245
246                 TRACE("Got a 16 bit stack switch:"
247                       "\n\tframe32: %08lx"
248                       "\n\tedx:%08lx ecx:%08lx ebp:%08lx"
249                       "\n\tds:%04x es:%04x fs:%04x gs:%04x"
250                       "\n\tcall_from_ip:%08lx module_cs:%04lx relay=%08lx"
251                       "\n\tentry_ip:%04x entry_point:%08lx"
252                       "\n\tbp:%04x ip:%04x cs:%04x\n",
253                       (unsigned long)frame16.frame32,
254                       frame16.edx, frame16.ecx, frame16.ebp,
255                       frame16.ds, frame16.es, frame16.fs, frame16.gs,
256                       frame16.callfrom_ip, frame16.module_cs, frame16.relay,
257                       frame16.entry_ip, frame16.entry_point,
258                       frame16.bp, frame16.ip, frame16.cs);
259
260                       
261                 frame->AddrPC.Mode       = AddrMode1616;
262                 frame->AddrPC.Segment    = frame16.cs;
263                 frame->AddrPC.Offset     = frame16.ip;
264
265                 frame->AddrFrame.Mode    = AddrMode1616;
266                 frame->AddrFrame.Segment = SELECTOROF(next_switch);
267                 frame->AddrFrame.Offset  = frame16.bp;
268
269                 frame->AddrStack.Mode    = AddrMode1616;
270                 frame->AddrStack.Segment = SELECTOROF(next_switch);
271
272                 frame->AddrReturn.Mode    = AddrMode1616;
273                 frame->AddrReturn.Segment = frame16.cs;
274
275                 next_switch = curr_switch;
276                 if (!f_read_mem(hProcess, (void*)next_switch, &frame32, sizeof(frame32),
277                                 NULL))
278                 {
279                     WARN("Bad stack frame 0x%08lx\n", next_switch);
280                     goto done_err;
281                 }
282                 curr_switch = (DWORD)frame32.frame16;
283                 tmp.Mode    = AddrMode1616;
284                 tmp.Segment = SELECTOROF(curr_switch);
285                 tmp.Offset  = OFFSETOF(curr_switch);
286
287                 if (!f_read_mem(hProcess, (void*)f_xlat_adr(hProcess, hThread, &tmp),
288                                 &ch, sizeof(ch), NULL))
289                     curr_switch = 0xFFFFFFFF;
290                 curr_mode = stm_16bit;
291             }
292         }
293         else
294         {
295             frame->AddrPC = frame->AddrReturn;
296             if (curr_mode == stm_16bit)
297             {
298                 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(WORD);
299                 /* "pop up" previous BP value */
300                 if (!f_read_mem(hProcess, 
301                                 (void*)f_xlat_adr(hProcess, hThread, &frame->AddrFrame),
302                                 &val, sizeof(WORD), NULL))
303                     goto done_err;
304                 frame->AddrFrame.Offset = val;
305             }
306             else
307             {
308                 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(DWORD);
309                 /* "pop up" previous EBP value */
310                 if (!f_read_mem(hProcess, (void*)frame->AddrFrame.Offset, 
311                                 &frame->AddrFrame.Offset, sizeof(DWORD), NULL))
312                     goto done_err;
313             }
314         }
315     }
316
317     if (curr_mode == stm_16bit)
318     {
319         int     i;
320
321         p = f_xlat_adr(hProcess, hThread, &frame->AddrFrame);
322         if (!f_read_mem(hProcess, (void*)(p + sizeof(WORD)), &val, sizeof(WORD), NULL))
323             goto done_err;
324         frame->AddrReturn.Offset = val;
325         /* get potential cs if a far call was used */
326         if (!f_read_mem(hProcess, (void*)(p + 2 * sizeof(WORD)), 
327                         &val, sizeof(WORD), NULL))
328             goto done_err;
329         if (frame->AddrFrame.Offset & 1)
330             frame->AddrReturn.Segment = val; /* far call assumed */
331         else
332         {
333             /* not explicitly marked as far call, 
334              * but check whether it could be anyway
335              */
336             if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
337             {
338                 LDT_ENTRY       le;
339
340                 if (GetThreadSelectorEntry(hThread, val, &le) &&
341                     (le.HighWord.Bits.Type & 0x08)) /* code segment */
342                 {
343                     /* it is very uncommon to push a code segment cs as
344                      * a parameter, so this should work in most cases 
345                      */
346                     frame->AddrReturn.Segment = val;
347                 }
348             }
349         }
350         frame->AddrFrame.Offset &= ~1;
351         /* we "pop" parameters as 16 bit entities... of course, this won't
352          * work if the parameter is in fact bigger than 16bit, but
353          * there's no way to know that here
354          */
355         for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
356         {
357             f_read_mem(hProcess, (void*)(p + (2 + i) * sizeof(WORD)), 
358                        &val, sizeof(val), NULL);
359             frame->Params[i] = val;
360         }
361     }
362     else
363     {
364         if (!f_read_mem(hProcess, 
365                         (void*)(frame->AddrFrame.Offset + sizeof(DWORD)),
366                         &frame->AddrReturn.Offset, sizeof(DWORD), NULL))
367             goto done_err;
368         f_read_mem(hProcess, 
369                    (void*)(frame->AddrFrame.Offset + 2 * sizeof(DWORD)), 
370                    frame->Params, sizeof(frame->Params), NULL);
371     }
372
373     frame->Far = FALSE;
374     frame->Virtual = FALSE;
375
376     TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
377           wine_dbgstr_addr(&frame->AddrPC), 
378           wine_dbgstr_addr(&frame->AddrFrame),
379           wine_dbgstr_addr(&frame->AddrReturn),
380           wine_dbgstr_addr(&frame->AddrStack), 
381           curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
382           curr_switch, next_switch);
383
384     return TRUE;
385 done_err:
386     curr_mode = stm_done;
387     return FALSE;
388 }