dbghelp: Fixed missing parameters info in StackWalk64.
[wine/multimedia.git] / dlls / dbghelp / stack.c
blob69e9c1fcfd680aeb9c2bbe0207f4966ac9d7fc89
1 /*
2 * Stack walking
4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Eric Youngdale
6 * Copyright 1999 Ove Kåven
7 * Copyright 2004 Eric Pouech
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.
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.
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
24 #include "config.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <assert.h>
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"
38 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
40 enum st_mode {stm_start, stm_32bit, stm_16bit, stm_done};
42 static const char* wine_dbgstr_addr(const ADDRESS* addr)
44 if (!addr) return "(null)";
45 switch (addr->Mode)
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";
60 static BOOL CALLBACK read_mem(HANDLE hProcess, DWORD addr, void* buffer,
61 DWORD size, LPDWORD nread)
63 return ReadProcessMemory(hProcess, (void*)addr, buffer, size, nread);
66 static BOOL CALLBACK read_mem64(HANDLE hProcess, DWORD64 addr, void* buffer,
67 DWORD size, LPDWORD nread)
69 return ReadProcessMemory(hProcess, (void*)(DWORD_PTR)addr, buffer, size, nread);
72 /* indexes in Reserved array */
73 #define __CurrentMode 0
74 #define __CurrentSwitch 1
75 #define __NextSwitch 2
77 #define curr_mode (frame->Reserved[__CurrentMode])
78 #define curr_switch (frame->Reserved[__CurrentSwitch])
79 #define next_switch (frame->Reserved[__NextSwitch])
81 struct stack_walk_callback
83 HANDLE hProcess;
84 HANDLE hThread;
85 BOOL is32;
86 union
88 struct
90 PREAD_PROCESS_MEMORY_ROUTINE f_read_mem;
91 PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr;
92 } s32;
93 struct
95 PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem;
96 PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr;
97 } s64;
98 } u;
101 static inline void addr_32to64(const ADDRESS* addr32, ADDRESS64* addr64)
103 addr64->Offset = (ULONG64)addr32->Offset;
104 addr64->Segment = addr32->Segment;
105 addr64->Mode = addr32->Mode;
108 static inline void addr_64to32(const ADDRESS64* addr64, ADDRESS* addr32)
110 addr32->Offset = (ULONG)addr64->Offset;
111 addr32->Segment = addr64->Segment;
112 addr32->Mode = addr64->Mode;
115 static inline BOOL sw_read_mem(struct stack_walk_callback* cb, DWORD addr, void* ptr, DWORD sz)
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);
123 static inline DWORD sw_xlat_addr(struct stack_walk_callback* cb, ADDRESS* addr)
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)
129 ADDRESS64 addr64;
131 addr_32to64(addr, &addr64);
132 return cb->u.s64.f_xlat_adr(cb->hProcess, cb->hThread, &addr64);
134 else
135 return addr_to_linear(cb->hProcess, cb->hThread, addr);
138 static BOOL stack_walk(struct stack_walk_callback* cb, LPSTACKFRAME frame)
140 STACK32FRAME frame32;
141 STACK16FRAME frame16;
142 char ch;
143 ADDRESS tmp;
144 DWORD p;
145 WORD val;
146 BOOL do_switch;
148 /* sanity check */
149 if (curr_mode >= stm_done) return FALSE;
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);
159 if (curr_mode == stm_start)
161 THREAD_BASIC_INFORMATION info;
163 if ((frame->AddrPC.Mode == AddrModeFlat) &&
164 (frame->AddrFrame.Mode != AddrModeFlat))
166 WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
167 goto done_err;
170 /* Init done */
171 curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ?
172 stm_32bit : stm_16bit;
174 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
175 * address space
177 if (NtQueryInformationThread(cb->hThread, ThreadBasicInformation, &info,
178 sizeof(info), NULL) == STATUS_SUCCESS)
180 curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, WOW32Reserved);
181 if (!sw_read_mem(cb, curr_switch, &next_switch, sizeof(next_switch)))
183 WARN("Can't read TEB:WOW32Reserved\n");
184 goto done_err;
186 if (curr_mode == stm_16bit)
188 if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
190 WARN("Bad stack frame 0x%08lx\n", next_switch);
191 goto done_err;
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;
200 else
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)))
208 WARN("Bad stack frame 0x%08lx\n", p);
209 goto done_err;
211 curr_switch = (DWORD)frame16.frame32;
213 if (!sw_read_mem(cb, curr_switch, &ch, sizeof(ch)))
214 curr_switch = 0xFFFFFFFF;
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.
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
227 else
229 if (frame->AddrFrame.Offset == 0) goto done_err;
230 if (frame->AddrFrame.Mode == AddrModeFlat)
232 assert(curr_mode == stm_32bit);
233 do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
235 else
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);
243 if (do_switch)
245 if (curr_mode == stm_16bit)
247 if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
249 WARN("Bad stack frame 0x%08lx\n", next_switch);
250 goto done_err;
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;
260 frame->AddrStack.Mode = AddrModeFlat;
261 frame->AddrStack.Segment = 0;
262 frame->AddrReturn.Mode = AddrModeFlat;
263 frame->AddrReturn.Segment = 0;
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);
271 if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
273 WARN("Bad stack frame 0x%08lx\n", p);
274 goto done_err;
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;
281 else
283 tmp.Mode = AddrMode1616;
284 tmp.Segment = SELECTOROF(next_switch);
285 tmp.Offset = OFFSETOF(next_switch);
286 p = sw_xlat_addr(cb, &tmp);
288 if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
290 WARN("Bad stack frame 0x%08lx\n", p);
291 goto done_err;
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);
309 frame->AddrPC.Mode = AddrMode1616;
310 frame->AddrPC.Segment = frame16.cs;
311 frame->AddrPC.Offset = frame16.ip;
313 frame->AddrFrame.Mode = AddrMode1616;
314 frame->AddrFrame.Segment = SELECTOROF(next_switch);
315 frame->AddrFrame.Offset = frame16.bp;
317 frame->AddrStack.Mode = AddrMode1616;
318 frame->AddrStack.Segment = SELECTOROF(next_switch);
320 frame->AddrReturn.Mode = AddrMode1616;
321 frame->AddrReturn.Segment = frame16.cs;
323 next_switch = curr_switch;
324 if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
326 WARN("Bad stack frame 0x%08lx\n", next_switch);
327 goto done_err;
329 curr_switch = (DWORD)frame32.frame16;
330 tmp.Mode = AddrMode1616;
331 tmp.Segment = SELECTOROF(curr_switch);
332 tmp.Offset = OFFSETOF(curr_switch);
334 if (!sw_read_mem(cb, sw_xlat_addr(cb, &tmp), &ch, sizeof(ch)))
335 curr_switch = 0;
336 curr_mode = stm_16bit;
339 else
341 frame->AddrPC = frame->AddrReturn;
342 if (curr_mode == stm_16bit)
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;
351 else
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;
362 if (curr_mode == stm_16bit)
364 int i;
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
377 /* not explicitly marked as far call,
378 * but check whether it could be anyway
380 if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
382 LDT_ENTRY le;
384 if (GetThreadSelectorEntry(cb->hThread, val, &le) &&
385 (le.HighWord.Bits.Type & 0x08)) /* code segment */
387 /* it is very uncommon to push a code segment cs as
388 * a parameter, so this should work in most cases
390 frame->AddrReturn.Segment = val;
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
399 for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
401 sw_read_mem(cb, p + (2 + i) * sizeof(WORD), &val, sizeof(val));
402 frame->Params[i] = val;
405 else
407 if (!sw_read_mem(cb, frame->AddrFrame.Offset + sizeof(DWORD),
408 &frame->AddrReturn.Offset, sizeof(DWORD)))
410 WARN("Cannot read new frame offset %08lx\n", frame->AddrFrame.Offset + sizeof(DWORD));
411 goto done_err;
413 sw_read_mem(cb, frame->AddrFrame.Offset + 2 * sizeof(DWORD),
414 frame->Params, sizeof(frame->Params));
417 frame->Far = FALSE;
418 frame->Virtual = FALSE;
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);
428 return TRUE;
429 done_err:
430 curr_mode = stm_done;
431 return FALSE;
434 /***********************************************************************
435 * StackWalk (DBGHELP.@)
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)
444 struct stack_walk_callback swcb;
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);
451 if (MachineType != IMAGE_FILE_MACHINE_I386)
453 SetLastError(ERROR_INVALID_PARAMETER);
454 return FALSE;
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;
464 return stack_walk(&swcb, frame);
468 /***********************************************************************
469 * StackWalk64 (DBGHELP.@)
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)
478 struct stack_walk_callback swcb;
479 STACKFRAME frame32;
480 BOOL ret;
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);
487 if (MachineType != IMAGE_FILE_MACHINE_I386)
489 SetLastError(ERROR_INVALID_PARAMETER);
490 return FALSE;
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 */
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;
513 ret = stack_walk(&swcb, &frame32);
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 */
533 return ret;
536 /******************************************************************
537 * SymRegisterFunctionEntryCallback (DBGHELP.@)
541 BOOL WINAPI SymRegisterFunctionEntryCallback(HANDLE hProc,
542 PSYMBOL_FUNCENTRY_CALLBACK cb, PVOID user)
544 FIXME("(%p %p %p): stub!\n", hProc, cb, user);
545 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
546 return FALSE;
549 /******************************************************************
550 * SymRegisterFunctionEntryCallback64 (DBGHELP.@)
554 BOOL WINAPI SymRegisterFunctionEntryCallback64(HANDLE hProc,
555 PSYMBOL_FUNCENTRY_CALLBACK64 cb,
556 ULONG64 user)
558 FIXME("(%p %p %s): stub!\n", hProc, cb, wine_dbgstr_longlong(user));
559 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
560 return FALSE;