dbghelp: Fixed missing parameters info in StackWalk64.
[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 static BOOL CALLBACK read_mem64(HANDLE hProcess, DWORD64 addr, void* buffer,
67                                 DWORD size, LPDWORD nread)
68 {
69     return ReadProcessMemory(hProcess, (void*)(DWORD_PTR)addr, buffer, size, nread);
70 }
71
72 /* indexes in Reserved array */
73 #define __CurrentMode     0
74 #define __CurrentSwitch   1
75 #define __NextSwitch      2
76
77 #define curr_mode   (frame->Reserved[__CurrentMode])
78 #define curr_switch (frame->Reserved[__CurrentSwitch])
79 #define next_switch (frame->Reserved[__NextSwitch])
80
81 struct stack_walk_callback
82 {
83     HANDLE      hProcess;
84     HANDLE      hThread;
85     BOOL        is32;
86     union
87     {
88         struct
89         {
90             PREAD_PROCESS_MEMORY_ROUTINE        f_read_mem;
91             PTRANSLATE_ADDRESS_ROUTINE          f_xlat_adr;
92         } s32;
93         struct
94         {
95             PREAD_PROCESS_MEMORY_ROUTINE64      f_read_mem;
96             PTRANSLATE_ADDRESS_ROUTINE64        f_xlat_adr;
97         } s64;
98     } u;
99 };
100
101 static inline void addr_32to64(const ADDRESS* addr32, ADDRESS64* addr64)
102 {
103     addr64->Offset = (ULONG64)addr32->Offset;
104     addr64->Segment = addr32->Segment;
105     addr64->Mode = addr32->Mode;
106 }
107
108 static inline void addr_64to32(const ADDRESS64* addr64, ADDRESS* addr32)
109 {
110     addr32->Offset = (ULONG)addr64->Offset;
111     addr32->Segment = addr64->Segment;
112     addr32->Mode = addr64->Mode;
113 }
114
115 static inline BOOL sw_read_mem(struct stack_walk_callback* cb, DWORD addr, void* ptr, DWORD sz)
116 {
117     if (cb->is32)
118         return cb->u.s32.f_read_mem(cb->hProcess, addr, ptr, sz, NULL);
119     else
120         return cb->u.s64.f_read_mem(cb->hProcess, addr, ptr, sz, NULL);
121 }
122
123 static inline DWORD sw_xlat_addr(struct stack_walk_callback* cb, ADDRESS* addr)
124 {
125     if (cb->is32)
126         return cb->u.s32.f_xlat_adr(cb->hProcess, cb->hThread, addr);
127     else if (cb->u.s64.f_xlat_adr)
128     {
129         ADDRESS64       addr64;
130
131         addr_32to64(addr, &addr64);
132         return cb->u.s64.f_xlat_adr(cb->hProcess, cb->hThread, &addr64);
133     }
134     else
135         return addr_to_linear(cb->hProcess, cb->hThread, addr);
136 }
137
138 static BOOL stack_walk(struct stack_walk_callback* cb, LPSTACKFRAME frame)
139 {
140     STACK32FRAME        frame32;
141     STACK16FRAME        frame16;
142     char                ch;
143     ADDRESS             tmp;
144     DWORD               p;
145     WORD                val;
146     BOOL                do_switch;
147
148     /* sanity check */
149     if (curr_mode >= stm_done) return FALSE;
150
151     TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
152           wine_dbgstr_addr(&frame->AddrPC), 
153           wine_dbgstr_addr(&frame->AddrFrame),
154           wine_dbgstr_addr(&frame->AddrReturn),
155           wine_dbgstr_addr(&frame->AddrStack), 
156           curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
157           curr_switch, next_switch);
158
159     if (curr_mode == stm_start)
160     {
161         THREAD_BASIC_INFORMATION info;
162
163         if ((frame->AddrPC.Mode == AddrModeFlat) &&
164             (frame->AddrFrame.Mode != AddrModeFlat))
165         {
166             WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
167             goto done_err;
168         }
169
170         /* Init done */
171         curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ? 
172             stm_32bit : stm_16bit;
173
174         /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
175          * address space
176          */
177         if (NtQueryInformationThread(cb->hThread, ThreadBasicInformation, &info,
178                                      sizeof(info), NULL) == STATUS_SUCCESS)
179         {
180             curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, WOW32Reserved);
181             if (!sw_read_mem(cb, curr_switch, &next_switch, sizeof(next_switch)))
182             {
183                 WARN("Can't read TEB:WOW32Reserved\n");
184                 goto done_err;
185             }
186             if (curr_mode == stm_16bit)
187             {
188                 if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
189                 {
190                     WARN("Bad stack frame 0x%08lx\n", next_switch);
191                     goto done_err;
192                 }
193                 curr_switch = (DWORD)frame32.frame16;
194                 tmp.Mode    = AddrMode1616;
195                 tmp.Segment = SELECTOROF(curr_switch);
196                 tmp.Offset  = OFFSETOF(curr_switch);
197                 if (!sw_read_mem(cb, sw_xlat_addr(cb, &tmp), &ch, sizeof(ch)))
198                     curr_switch = 0xFFFFFFFF;
199             }
200             else
201             {
202                 tmp.Mode    = AddrMode1616;
203                 tmp.Segment = SELECTOROF(next_switch);
204                 tmp.Offset  = OFFSETOF(next_switch);
205                 p = sw_xlat_addr(cb, &tmp);
206                 if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
207                 {
208                     WARN("Bad stack frame 0x%08lx\n", p);
209                     goto done_err;
210                 }
211                 curr_switch = (DWORD)frame16.frame32;
212
213                 if (!sw_read_mem(cb, curr_switch, &ch, sizeof(ch)))
214                     curr_switch = 0xFFFFFFFF;
215             }
216         }
217         else
218             /* FIXME: this will allow to work when we're not attached to a live target, 
219              * but the 16 <=> 32 switch facility won't be available.
220              */
221             curr_switch = 0;
222         frame->AddrReturn.Mode = frame->AddrStack.Mode = (curr_mode == stm_16bit) ? AddrMode1616 : AddrModeFlat;
223         /* don't set up AddrStack on first call. Either the caller has set it up, or
224          * we will get it in the next frame
225          */
226     }
227     else
228     {
229         if (frame->AddrFrame.Offset == 0) goto done_err;
230         if (frame->AddrFrame.Mode == AddrModeFlat)
231         {
232             assert(curr_mode == stm_32bit);
233             do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
234         }
235         else
236         {
237             assert(curr_mode == stm_16bit);
238             do_switch = curr_switch && 
239                 frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
240                 frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
241         }
242            
243         if (do_switch)
244         {
245             if (curr_mode == stm_16bit)
246             {
247                 if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
248                 {
249                     WARN("Bad stack frame 0x%08lx\n", next_switch);
250                     goto done_err;
251                 }
252
253                 frame->AddrPC.Mode        = AddrModeFlat;
254                 frame->AddrPC.Segment     = 0;
255                 frame->AddrPC.Offset      = frame32.retaddr;
256                 frame->AddrFrame.Mode     = AddrModeFlat;
257                 frame->AddrFrame.Segment  = 0;
258                 frame->AddrFrame.Offset   = frame32.ebp;
259
260                 frame->AddrStack.Mode     = AddrModeFlat;
261                 frame->AddrStack.Segment  = 0;
262                 frame->AddrReturn.Mode    = AddrModeFlat;
263                 frame->AddrReturn.Segment = 0;
264
265                 next_switch = curr_switch;
266                 tmp.Mode    = AddrMode1616;
267                 tmp.Segment = SELECTOROF(next_switch);
268                 tmp.Offset  = OFFSETOF(next_switch);
269                 p = sw_xlat_addr(cb, &tmp);
270
271                 if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
272                 {
273                     WARN("Bad stack frame 0x%08lx\n", p);
274                     goto done_err;
275                 }
276                 curr_switch = (DWORD)frame16.frame32;
277                 curr_mode = stm_32bit;
278                 if (!sw_read_mem(cb, curr_switch, &ch, sizeof(ch)))
279                     curr_switch = 0;
280             }
281             else
282             {
283                 tmp.Mode    = AddrMode1616;
284                 tmp.Segment = SELECTOROF(next_switch);
285                 tmp.Offset  = OFFSETOF(next_switch);
286                 p = sw_xlat_addr(cb, &tmp);
287
288                 if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
289                 {
290                     WARN("Bad stack frame 0x%08lx\n", p);
291                     goto done_err;
292                 }
293
294                 TRACE("Got a 16 bit stack switch:"
295                       "\n\tframe32: %08lx"
296                       "\n\tedx:%08lx ecx:%08lx ebp:%08lx"
297                       "\n\tds:%04x es:%04x fs:%04x gs:%04x"
298                       "\n\tcall_from_ip:%08lx module_cs:%04lx relay=%08lx"
299                       "\n\tentry_ip:%04x entry_point:%08lx"
300                       "\n\tbp:%04x ip:%04x cs:%04x\n",
301                       (unsigned long)frame16.frame32,
302                       frame16.edx, frame16.ecx, frame16.ebp,
303                       frame16.ds, frame16.es, frame16.fs, frame16.gs,
304                       frame16.callfrom_ip, frame16.module_cs, frame16.relay,
305                       frame16.entry_ip, frame16.entry_point,
306                       frame16.bp, frame16.ip, frame16.cs);
307
308                       
309                 frame->AddrPC.Mode       = AddrMode1616;
310                 frame->AddrPC.Segment    = frame16.cs;
311                 frame->AddrPC.Offset     = frame16.ip;
312
313                 frame->AddrFrame.Mode    = AddrMode1616;
314                 frame->AddrFrame.Segment = SELECTOROF(next_switch);
315                 frame->AddrFrame.Offset  = frame16.bp;
316
317                 frame->AddrStack.Mode    = AddrMode1616;
318                 frame->AddrStack.Segment = SELECTOROF(next_switch);
319
320                 frame->AddrReturn.Mode    = AddrMode1616;
321                 frame->AddrReturn.Segment = frame16.cs;
322
323                 next_switch = curr_switch;
324                 if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
325                 {
326                     WARN("Bad stack frame 0x%08lx\n", next_switch);
327                     goto done_err;
328                 }
329                 curr_switch = (DWORD)frame32.frame16;
330                 tmp.Mode    = AddrMode1616;
331                 tmp.Segment = SELECTOROF(curr_switch);
332                 tmp.Offset  = OFFSETOF(curr_switch);
333
334                 if (!sw_read_mem(cb, sw_xlat_addr(cb, &tmp), &ch, sizeof(ch)))
335                     curr_switch = 0;
336                 curr_mode = stm_16bit;
337             }
338         }
339         else
340         {
341             frame->AddrPC = frame->AddrReturn;
342             if (curr_mode == stm_16bit)
343             {
344                 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(WORD);
345                 /* "pop up" previous BP value */
346                 if (!sw_read_mem(cb, sw_xlat_addr(cb, &frame->AddrFrame),
347                                  &val, sizeof(WORD)))
348                     goto done_err;
349                 frame->AddrFrame.Offset = val;
350             }
351             else
352             {
353                 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(DWORD);
354                 /* "pop up" previous EBP value */
355                 if (!sw_read_mem(cb, frame->AddrFrame.Offset, 
356                                  &frame->AddrFrame.Offset, sizeof(DWORD)))
357                     goto done_err;
358             }
359         }
360     }
361
362     if (curr_mode == stm_16bit)
363     {
364         int     i;
365
366         p = sw_xlat_addr(cb, &frame->AddrFrame);
367         if (!sw_read_mem(cb, p + sizeof(WORD), &val, sizeof(WORD)))
368             goto done_err;
369         frame->AddrReturn.Offset = val;
370         /* get potential cs if a far call was used */
371         if (!sw_read_mem(cb, p + 2 * sizeof(WORD), &val, sizeof(WORD)))
372             goto done_err;
373         if (frame->AddrFrame.Offset & 1)
374             frame->AddrReturn.Segment = val; /* far call assumed */
375         else
376         {
377             /* not explicitly marked as far call, 
378              * but check whether it could be anyway
379              */
380             if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
381             {
382                 LDT_ENTRY       le;
383
384                 if (GetThreadSelectorEntry(cb->hThread, val, &le) &&
385                     (le.HighWord.Bits.Type & 0x08)) /* code segment */
386                 {
387                     /* it is very uncommon to push a code segment cs as
388                      * a parameter, so this should work in most cases 
389                      */
390                     frame->AddrReturn.Segment = val;
391                 }
392             }
393         }
394         frame->AddrFrame.Offset &= ~1;
395         /* we "pop" parameters as 16 bit entities... of course, this won't
396          * work if the parameter is in fact bigger than 16bit, but
397          * there's no way to know that here
398          */
399         for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
400         {
401             sw_read_mem(cb, p + (2 + i) * sizeof(WORD), &val, sizeof(val));
402             frame->Params[i] = val;
403         }
404     }
405     else
406     {
407         if (!sw_read_mem(cb, frame->AddrFrame.Offset + sizeof(DWORD),
408                          &frame->AddrReturn.Offset, sizeof(DWORD)))
409         {
410             WARN("Cannot read new frame offset %08lx\n", frame->AddrFrame.Offset + sizeof(DWORD));
411             goto done_err;
412         }
413         sw_read_mem(cb, frame->AddrFrame.Offset + 2 * sizeof(DWORD), 
414                     frame->Params, sizeof(frame->Params));
415     }
416
417     frame->Far = FALSE;
418     frame->Virtual = FALSE;
419
420     TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
421           wine_dbgstr_addr(&frame->AddrPC), 
422           wine_dbgstr_addr(&frame->AddrFrame),
423           wine_dbgstr_addr(&frame->AddrReturn),
424           wine_dbgstr_addr(&frame->AddrStack), 
425           curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
426           curr_switch, next_switch);
427
428     return TRUE;
429 done_err:
430     curr_mode = stm_done;
431     return FALSE;
432 }
433
434 /***********************************************************************
435  *              StackWalk (DBGHELP.@)
436  */
437 BOOL WINAPI StackWalk(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
438                       LPSTACKFRAME frame, LPVOID ctx,
439                       PREAD_PROCESS_MEMORY_ROUTINE f_read_mem,
440                       PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
441                       PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
442                       PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr)
443 {
444     struct stack_walk_callback  swcb;
445
446     TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p)\n",
447           MachineType, hProcess, hThread, frame, ctx,
448           f_read_mem, FunctionTableAccessRoutine,
449           GetModuleBaseRoutine, f_xlat_adr);
450
451     if (MachineType != IMAGE_FILE_MACHINE_I386)
452     {
453         SetLastError(ERROR_INVALID_PARAMETER);
454         return FALSE;
455     }
456
457     swcb.hProcess = hProcess;
458     swcb.hThread = hThread;
459     swcb.is32 = TRUE;
460     /* sigh... MS isn't even consistent in the func prototypes */
461     swcb.u.s32.f_read_mem = (f_read_mem) ? f_read_mem : read_mem;
462     swcb.u.s32.f_xlat_adr = (f_xlat_adr) ? f_xlat_adr : addr_to_linear;
463
464     return stack_walk(&swcb, frame);
465 }
466
467
468 /***********************************************************************
469  *              StackWalk64 (DBGHELP.@)
470  */
471 BOOL WINAPI StackWalk64(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
472                         LPSTACKFRAME64 frame64, LPVOID ctx,
473                         PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem,
474                         PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
475                         PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
476                         PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr)
477 {
478     struct stack_walk_callback  swcb;
479     STACKFRAME                  frame32;
480     BOOL                        ret;
481
482     TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p) - stub!\n",
483           MachineType, hProcess, hThread, frame64, ctx,
484           f_read_mem, FunctionTableAccessRoutine,
485           GetModuleBaseRoutine, f_xlat_adr);
486
487     if (MachineType != IMAGE_FILE_MACHINE_I386)
488     {
489         SetLastError(ERROR_INVALID_PARAMETER);
490         return FALSE;
491     }
492
493     addr_64to32(&frame64->AddrPC,     &frame32.AddrPC);
494     addr_64to32(&frame64->AddrReturn, &frame32.AddrReturn);
495     addr_64to32(&frame64->AddrFrame,  &frame32.AddrFrame);
496     addr_64to32(&frame64->AddrStack,  &frame32.AddrStack);
497     addr_64to32(&frame64->AddrBStore, &frame32.AddrBStore);
498     frame32.FuncTableEntry = frame64->FuncTableEntry; /* FIXME */
499     frame32.Far = frame64->Far;
500     frame32.Virtual = frame64->Virtual;
501     frame32.Reserved[0] = (ULONG)frame64->Reserved[0];
502     frame32.Reserved[1] = (ULONG)frame64->Reserved[1];
503     frame32.Reserved[2] = (ULONG)frame64->Reserved[2];
504     /* we don't handle KdHelp */
505
506     swcb.hProcess = hProcess;
507     swcb.hThread = hThread;
508     swcb.is32 = FALSE;
509     /* sigh... MS isn't even consistent in the func prototypes */
510     swcb.u.s64.f_read_mem = (f_read_mem) ? f_read_mem : read_mem64;
511     swcb.u.s64.f_xlat_adr = f_xlat_adr;
512
513     ret = stack_walk(&swcb, &frame32);
514
515     addr_32to64(&frame32.AddrPC,     &frame64->AddrPC);
516     addr_32to64(&frame32.AddrReturn, &frame64->AddrReturn);
517     addr_32to64(&frame32.AddrFrame,  &frame64->AddrFrame);
518     addr_32to64(&frame32.AddrStack,  &frame64->AddrStack);
519     addr_32to64(&frame32.AddrBStore, &frame64->AddrBStore);
520     frame64->FuncTableEntry = frame32.FuncTableEntry; /* FIXME */
521     frame64->Params[0] = (ULONG)frame32.Params[0];
522     frame64->Params[1] = (ULONG)frame32.Params[1];
523     frame64->Params[2] = (ULONG)frame32.Params[2];
524     frame64->Params[3] = (ULONG)frame32.Params[3];
525     frame64->Params[4] = (ULONG)frame32.Params[4];
526     frame64->Far = frame32.Far;
527     frame64->Virtual = frame32.Virtual;
528     frame64->Reserved[0] = (ULONG)frame32.Reserved[0];
529     frame64->Reserved[1] = (ULONG)frame32.Reserved[1];
530     frame64->Reserved[2] = (ULONG)frame32.Reserved[2];
531     /* we don't handle KdHelp */
532
533     return ret;
534 }
535
536 /******************************************************************
537  *              SymRegisterFunctionEntryCallback (DBGHELP.@)
538  *
539  *
540  */
541 BOOL WINAPI SymRegisterFunctionEntryCallback(HANDLE hProc,
542                                              PSYMBOL_FUNCENTRY_CALLBACK cb, PVOID user)
543 {
544     FIXME("(%p %p %p): stub!\n", hProc, cb, user);
545     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
546     return FALSE;
547 }
548
549 /******************************************************************
550  *              SymRegisterFunctionEntryCallback64 (DBGHELP.@)
551  *
552  *
553  */
554 BOOL WINAPI SymRegisterFunctionEntryCallback64(HANDLE hProc,
555                                                PSYMBOL_FUNCENTRY_CALLBACK64 cb,
556                                                ULONG64 user)
557 {
558     FIXME("(%p %p %s): stub!\n", hProc, cb, wine_dbgstr_longlong(user));
559     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
560     return FALSE;
561 }