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
30 #include "dbghelp_private.h"
33 #include "thread.h" /* FIXME: must be included before winternl.h */
35 #include "wine/debug.h"
36 #include "stackframe.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 /* indexes in Reserved array */
61 #define __CurrentMode 0
62 #define __CurrentSwitch 1
63 #define __NextSwitch 2
65 #define curr_mode (frame->Reserved[__CurrentMode])
66 #define curr_switch (frame->Reserved[__CurrentSwitch])
67 #define next_switch (frame->Reserved[__NextSwitch])
69 /***********************************************************************
70 * StackWalk (DBGHELP.@)
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
)
80 CONTEXT
* ctx
= (CONTEXT
*)_ctx
;
89 TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p)\n",
90 MachineType
, hProcess
, hThread
, frame
, _ctx
,
91 f_read_mem
, FunctionTableAccessRoutine
,
92 GetModuleBaseRoutine
, f_xlat_adr
);
94 if (MachineType
!= IMAGE_FILE_MACHINE_I386
)
96 SetLastError(ERROR_INVALID_PARAMETER
);
101 if (curr_mode
>= stm_done
) return FALSE
;
103 if (!f_read_mem
) f_read_mem
= ReadProcessMemory
;
104 if (!f_xlat_adr
) f_xlat_adr
= addr_to_linear
;
106 TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
107 wine_dbgstr_addr(&frame
->AddrPC
),
108 wine_dbgstr_addr(&frame
->AddrFrame
),
109 wine_dbgstr_addr(&frame
->AddrReturn
),
110 wine_dbgstr_addr(&frame
->AddrStack
),
111 curr_mode
== stm_start
? "start" : (curr_mode
== stm_16bit
? "16bit" : "32bit"),
112 curr_switch
, next_switch
);
114 if (curr_mode
== stm_start
)
116 THREAD_BASIC_INFORMATION info
;
119 curr_mode
= (frame
->AddrPC
.Mode
== AddrModeFlat
) ?
120 stm_32bit
: stm_16bit
;
122 /* Get the current ESP (don't know if this is valid) */
125 frame
->AddrStack
.Segment
= 0;
126 frame
->AddrStack
.Offset
= ctx
->Esp
;
127 frame
->AddrStack
.Mode
= AddrModeFlat
;
129 /* cur_switch holds address of curr_stack's field in TEB in debuggee
132 if (NtQueryInformationThread(hThread
, ThreadBasicInformation
, &info
,
133 sizeof(info
), NULL
) != STATUS_SUCCESS
)
135 curr_switch
= (unsigned long)info
.TebBaseAddress
+ FIELD_OFFSET(TEB
, cur_stack
);
136 if (!f_read_mem(hProcess
, (void*)curr_switch
, &next_switch
,
137 sizeof(next_switch
), NULL
))
139 WARN("Can't read TEB:cur_stack\n");
142 if (curr_mode
== stm_16bit
)
144 if (!f_read_mem(hProcess
, (void*)next_switch
, &frame32
,
145 sizeof(frame32
), NULL
))
147 WARN("Bad stack frame 0x%08lx\n", next_switch
);
150 curr_switch
= (DWORD
)frame32
.frame16
;
151 tmp
.Mode
= AddrMode1616
;
152 tmp
.Segment
= SELECTOROF(curr_switch
);
153 tmp
.Offset
= OFFSETOF(curr_switch
);
154 if (!f_read_mem(hProcess
, (void*)f_xlat_adr(hProcess
, hThread
, &tmp
),
155 &ch
, sizeof(ch
), NULL
))
156 curr_switch
= 0xFFFFFFFF;
157 frame
->AddrReturn
.Mode
= frame
->AddrStack
.Mode
= AddrMode1616
;
158 /* "pop up" previous BP value */
159 if (!f_read_mem(hProcess
, (void*)frame
->AddrFrame
.Offset
,
160 &val
, sizeof(WORD
), NULL
))
162 frame
->AddrFrame
.Offset
= val
;
166 tmp
.Mode
= AddrMode1616
;
167 tmp
.Segment
= SELECTOROF(next_switch
);
168 tmp
.Offset
= OFFSETOF(next_switch
);
169 p
= f_xlat_adr(hProcess
, hThread
, &tmp
);
170 if (!f_read_mem(hProcess
, (void*)p
, &frame16
, sizeof(frame16
), NULL
))
172 WARN("Bad stack frame 0x%08lx\n", p
);
175 curr_switch
= (DWORD
)frame16
.frame32
;
177 if (!f_read_mem(hProcess
, (void*)curr_switch
, &ch
, sizeof(ch
), NULL
))
178 curr_switch
= 0xFFFFFFFF;
179 frame
->AddrReturn
.Mode
= frame
->AddrStack
.Mode
= AddrModeFlat
;
180 /* "pop up" previous EBP value */
181 if (!f_read_mem(hProcess
, (void*)frame
->AddrFrame
.Offset
,
182 &frame
->AddrFrame
.Offset
, sizeof(DWORD
), NULL
))
188 if (frame
->AddrFrame
.Offset
== 0) goto done_err
;
189 if (frame
->AddrFrame
.Mode
== AddrModeFlat
)
191 assert(curr_mode
== stm_32bit
);
192 do_switch
= curr_switch
&& frame
->AddrFrame
.Offset
>= curr_switch
;
196 assert(curr_mode
== stm_16bit
);
197 do_switch
= OFFSETOF(curr_switch
) &&
198 frame
->AddrFrame
.Segment
== SELECTOROF(curr_switch
) &&
199 frame
->AddrFrame
.Offset
>= OFFSETOF(curr_switch
);
204 if (curr_mode
== stm_16bit
)
206 if (!f_read_mem(hProcess
, (void*)next_switch
, &frame32
,
207 sizeof(frame32
), NULL
))
209 WARN("Bad stack frame 0x%08lx\n", next_switch
);
213 frame
->AddrPC
.Mode
= AddrModeFlat
;
214 frame
->AddrPC
.Segment
= 0;
215 frame
->AddrPC
.Offset
= frame32
.retaddr
;
216 frame
->AddrFrame
.Mode
= AddrModeFlat
;
217 frame
->AddrFrame
.Segment
= 0;
218 frame
->AddrFrame
.Offset
= frame32
.ebp
;
220 frame
->AddrStack
.Mode
= AddrModeFlat
;
221 frame
->AddrStack
.Segment
= 0;
222 frame
->AddrReturn
.Mode
= AddrModeFlat
;
223 frame
->AddrReturn
.Segment
= 0;
225 next_switch
= curr_switch
;
226 tmp
.Mode
= AddrMode1616
;
227 tmp
.Segment
= SELECTOROF(next_switch
);
228 tmp
.Offset
= OFFSETOF(next_switch
);
229 p
= f_xlat_adr(hProcess
, hThread
, &tmp
);
231 if (!f_read_mem(hProcess
, (void*)p
, &frame16
, sizeof(frame16
), NULL
))
233 WARN("Bad stack frame 0x%08lx\n", p
);
236 curr_switch
= (DWORD
)frame16
.frame32
;
237 curr_mode
= stm_32bit
;
238 if (!f_read_mem(hProcess
, (void*)curr_switch
, &ch
, sizeof(ch
), NULL
))
239 curr_switch
= 0xFFFFFFFF;
243 tmp
.Mode
= AddrMode1616
;
244 tmp
.Segment
= SELECTOROF(next_switch
);
245 tmp
.Offset
= OFFSETOF(next_switch
);
246 p
= f_xlat_adr(hProcess
, hThread
, &tmp
);
248 if (!f_read_mem(hProcess
, (void*)p
, &frame16
, sizeof(frame16
), NULL
))
250 WARN("Bad stack frame 0x%08lx\n", p
);
254 TRACE("Got a 16 bit stack switch:"
256 "\n\tedx:%08lx ecx:%08lx ebp:%08lx"
257 "\n\tds:%04x es:%04x fs:%04x gs:%04x"
258 "\n\tcall_from_ip:%08lx module_cs:%04lx relay=%08lx"
259 "\n\tentry_ip:%04x entry_point:%08lx"
260 "\n\tbp:%04x ip:%04x cs:%04x\n",
261 (unsigned long)frame16
.frame32
,
262 frame16
.edx
, frame16
.ecx
, frame16
.ebp
,
263 frame16
.ds
, frame16
.es
, frame16
.fs
, frame16
.gs
,
264 frame16
.callfrom_ip
, frame16
.module_cs
, frame16
.relay
,
265 frame16
.entry_ip
, frame16
.entry_point
,
266 frame16
.bp
, frame16
.ip
, frame16
.cs
);
269 frame
->AddrPC
.Mode
= AddrMode1616
;
270 frame
->AddrPC
.Segment
= frame16
.cs
;
271 frame
->AddrPC
.Offset
= frame16
.ip
;
273 frame
->AddrFrame
.Mode
= AddrMode1616
;
274 frame
->AddrFrame
.Segment
= SELECTOROF(next_switch
);
275 frame
->AddrFrame
.Offset
= frame16
.bp
;
277 frame
->AddrStack
.Mode
= AddrMode1616
;
278 frame
->AddrStack
.Segment
= SELECTOROF(next_switch
);
280 frame
->AddrReturn
.Mode
= AddrMode1616
;
281 frame
->AddrReturn
.Segment
= frame16
.cs
;
283 next_switch
= curr_switch
;
284 if (!f_read_mem(hProcess
, (void*)next_switch
, &frame32
, sizeof(frame32
),
287 WARN("Bad stack frame 0x%08lx\n", next_switch
);
290 curr_switch
= (DWORD
)frame32
.frame16
;
291 tmp
.Mode
= AddrMode1616
;
292 tmp
.Segment
= SELECTOROF(curr_switch
);
293 tmp
.Offset
= OFFSETOF(curr_switch
);
295 if (!f_read_mem(hProcess
, (void*)f_xlat_adr(hProcess
, hThread
, &tmp
),
296 &ch
, sizeof(ch
), NULL
))
297 curr_switch
= 0xFFFFFFFF;
298 curr_mode
= stm_16bit
;
303 frame
->AddrPC
= frame
->AddrReturn
;
304 if (curr_mode
== stm_16bit
)
306 frame
->AddrStack
.Offset
= frame
->AddrFrame
.Offset
+ 2 * sizeof(WORD
);
307 /* "pop up" previous BP value */
308 if (!f_read_mem(hProcess
,
309 (void*)f_xlat_adr(hProcess
, hThread
, &frame
->AddrFrame
),
310 &val
, sizeof(WORD
), NULL
))
312 frame
->AddrFrame
.Offset
= val
;
316 frame
->AddrStack
.Offset
= frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
);
317 /* "pop up" previous EBP value */
318 if (!f_read_mem(hProcess
, (void*)frame
->AddrFrame
.Offset
,
319 &frame
->AddrFrame
.Offset
, sizeof(DWORD
), NULL
))
325 if (curr_mode
== stm_16bit
)
329 p
= f_xlat_adr(hProcess
, hThread
, &frame
->AddrFrame
);
330 if (!f_read_mem(hProcess
, (void*)(p
+ sizeof(WORD
)), &val
, sizeof(WORD
), NULL
))
332 frame
->AddrReturn
.Offset
= val
;
333 /* get potential cs if a far call was used */
334 if (!f_read_mem(hProcess
, (void*)(p
+ 2 * sizeof(WORD
)),
335 &val
, sizeof(WORD
), NULL
))
337 if (frame
->AddrFrame
.Offset
& 1)
338 frame
->AddrReturn
.Segment
= val
; /* far call assumed */
341 /* not explicitly marked as far call,
342 * but check whether it could be anyway
344 if ((val
& 7) == 7 && val
!= frame
->AddrReturn
.Segment
)
348 if (GetThreadSelectorEntry(hThread
, val
, &le
) &&
349 (le
.HighWord
.Bits
.Type
& 0x08)) /* code segment */
351 /* it is very uncommon to push a code segment cs as
352 * a parameter, so this should work in most cases
354 frame
->AddrReturn
.Segment
= val
;
358 frame
->AddrFrame
.Offset
&= ~1;
359 /* we "pop" parameters as 16 bit entities... of course, this won't
360 * work if the parameter is in fact bigger than 16bit, but
361 * there's no way to know that here
363 for (i
= 0; i
< sizeof(frame
->Params
) / sizeof(frame
->Params
[0]); i
++)
365 f_read_mem(hProcess
, (void*)(p
+ (2 + i
) * sizeof(WORD
)),
366 &val
, sizeof(val
), NULL
);
367 frame
->Params
[i
] = val
;
372 if (!f_read_mem(hProcess
,
373 (void*)(frame
->AddrFrame
.Offset
+ sizeof(DWORD
)),
374 &frame
->AddrReturn
.Offset
, sizeof(DWORD
), NULL
))
377 (void*)(frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
)),
378 frame
->Params
, sizeof(frame
->Params
), NULL
);
382 frame
->Virtual
= FALSE
;
384 TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
385 wine_dbgstr_addr(&frame
->AddrPC
),
386 wine_dbgstr_addr(&frame
->AddrFrame
),
387 wine_dbgstr_addr(&frame
->AddrReturn
),
388 wine_dbgstr_addr(&frame
->AddrStack
),
389 curr_mode
== stm_start
? "start" : (curr_mode
== stm_16bit
? "16bit" : "32bit"),
390 curr_switch
, next_switch
);
394 curr_mode
= stm_done
;
397 FIXME("(%ld, %p, %p, %p, %p, %p, %p, %p, %p): stub\n",
398 MachineType
, hProcess
, hThread
, frame
, _ctx
,
399 f_read_mem
, FunctionTableAccessRoutine
,
400 GetModuleBaseRoutine
, f_xlat_adr
);