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
31 #define WIN32_NO_STATUS
32 #include "dbghelp_private.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)";
48 return wine_dbg_sprintf("flat<%08lx>", addr
->Offset
);
50 return wine_dbg_sprintf("1616<%04x:%04lx>", addr
->Segment
, addr
->Offset
);
52 return wine_dbg_sprintf("1632<%04x:%08lx>", addr
->Segment
, addr
->Offset
);
54 return wine_dbg_sprintf("real<%04x:%04lx>", addr
->Segment
, addr
->Offset
);
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
90 PREAD_PROCESS_MEMORY_ROUTINE f_read_mem
;
91 PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr
;
95 PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem
;
96 PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr
;
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
)
118 return cb
->u
.s32
.f_read_mem(cb
->hProcess
, addr
, ptr
, sz
, NULL
);
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
)
126 return cb
->u
.s32
.f_xlat_adr(cb
->hProcess
, cb
->hThread
, addr
);
127 else if (cb
->u
.s64
.f_xlat_adr
)
131 addr_32to64(addr
, &addr64
);
132 return cb
->u
.s64
.f_xlat_adr(cb
->hProcess
, cb
->hThread
, &addr64
);
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
;
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");
171 curr_mode
= (frame
->AddrPC
.Mode
== AddrModeFlat
) ?
172 stm_32bit
: stm_16bit
;
174 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
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");
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
);
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;
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
);
211 curr_switch
= (DWORD
)frame16
.frame32
;
213 if (!sw_read_mem(cb
, curr_switch
, &ch
, sizeof(ch
)))
214 curr_switch
= 0xFFFFFFFF;
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.
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
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
;
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
);
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
);
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
);
276 curr_switch
= (DWORD
)frame16
.frame32
;
277 curr_mode
= stm_32bit
;
278 if (!sw_read_mem(cb
, curr_switch
, &ch
, sizeof(ch
)))
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
);
294 TRACE("Got a 16 bit stack switch:"
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
);
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
)))
336 curr_mode
= stm_16bit
;
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
),
349 frame
->AddrFrame
.Offset
= val
;
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
)))
362 if (curr_mode
== stm_16bit
)
366 p
= sw_xlat_addr(cb
, &frame
->AddrFrame
);
367 if (!sw_read_mem(cb
, p
+ sizeof(WORD
), &val
, sizeof(WORD
)))
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
)))
373 if (frame
->AddrFrame
.Offset
& 1)
374 frame
->AddrReturn
.Segment
= val
; /* far call assumed */
377 /* not explicitly marked as far call,
378 * but check whether it could be anyway
380 if ((val
& 7) == 7 && val
!= frame
->AddrReturn
.Segment
)
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
;
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
));
413 sw_read_mem(cb
, frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
),
414 frame
->Params
, sizeof(frame
->Params
));
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
);
430 curr_mode
= stm_done
;
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
);
457 swcb
.hProcess
= hProcess
;
458 swcb
.hThread
= hThread
;
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
;
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
);
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
;
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 */
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
);
549 /******************************************************************
550 * SymRegisterFunctionEntryCallback64 (DBGHELP.@)
554 BOOL WINAPI
SymRegisterFunctionEntryCallback64(HANDLE hProc
,
555 PSYMBOL_FUNCENTRY_CALLBACK64 cb
,
558 FIXME("(%p %p %s): stub!\n", hProc
, cb
, wine_dbgstr_longlong(user
));
559 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);