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