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"
34 #include "wine/winbase16.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
39 enum st_mode
{stm_start
, stm_32bit
, stm_16bit
, stm_done
};
41 static const char* wine_dbgstr_addr(const ADDRESS
* addr
)
43 if (!addr
) return "(null)";
47 return wine_dbg_sprintf("flat<%08lx>", addr
->Offset
);
49 return wine_dbg_sprintf("1616<%04x:%04lx>", addr
->Segment
, addr
->Offset
);
51 return wine_dbg_sprintf("1632<%04x:%08lx>", addr
->Segment
, addr
->Offset
);
53 return wine_dbg_sprintf("real<%04x:%04lx>", addr
->Segment
, addr
->Offset
);
59 static BOOL CALLBACK
read_mem(HANDLE hProcess
, DWORD addr
, void* buffer
,
60 DWORD size
, LPDWORD nread
)
62 return ReadProcessMemory(hProcess
, (void*)addr
, buffer
, size
, nread
);
65 /* indexes in Reserved array */
66 #define __CurrentMode 0
67 #define __CurrentSwitch 1
68 #define __NextSwitch 2
70 #define curr_mode (frame->Reserved[__CurrentMode])
71 #define curr_switch (frame->Reserved[__CurrentSwitch])
72 #define next_switch (frame->Reserved[__NextSwitch])
74 /***********************************************************************
75 * StackWalk (DBGHELP.@)
77 BOOL WINAPI
StackWalk(DWORD MachineType
, HANDLE hProcess
, HANDLE hThread
,
78 LPSTACKFRAME frame
, LPVOID ctx
,
79 PREAD_PROCESS_MEMORY_ROUTINE f_read_mem
,
80 PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine
,
81 PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine
,
82 PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr
)
92 TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p)\n",
93 MachineType
, hProcess
, hThread
, frame
, ctx
,
94 f_read_mem
, FunctionTableAccessRoutine
,
95 GetModuleBaseRoutine
, f_xlat_adr
);
97 if (MachineType
!= IMAGE_FILE_MACHINE_I386
)
99 SetLastError(ERROR_INVALID_PARAMETER
);
104 if (curr_mode
>= stm_done
) return FALSE
;
106 /* sigh... MS isn't even consistent in the func prototypes */
107 if (!f_read_mem
) f_read_mem
= read_mem
;
108 if (!f_xlat_adr
) f_xlat_adr
= addr_to_linear
;
110 TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
111 wine_dbgstr_addr(&frame
->AddrPC
),
112 wine_dbgstr_addr(&frame
->AddrFrame
),
113 wine_dbgstr_addr(&frame
->AddrReturn
),
114 wine_dbgstr_addr(&frame
->AddrStack
),
115 curr_mode
== stm_start
? "start" : (curr_mode
== stm_16bit
? "16bit" : "32bit"),
116 curr_switch
, next_switch
);
118 if (curr_mode
== stm_start
)
120 THREAD_BASIC_INFORMATION info
;
122 if ((frame
->AddrPC
.Mode
== AddrModeFlat
) &&
123 (frame
->AddrFrame
.Mode
!= AddrModeFlat
))
125 WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
130 curr_mode
= (frame
->AddrPC
.Mode
== AddrModeFlat
) ?
131 stm_32bit
: stm_16bit
;
133 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
136 if (NtQueryInformationThread(hThread
, ThreadBasicInformation
, &info
,
137 sizeof(info
), NULL
) == STATUS_SUCCESS
)
139 curr_switch
= (unsigned long)info
.TebBaseAddress
+ FIELD_OFFSET(TEB
, WOW32Reserved
);
140 if (!f_read_mem(hProcess
, curr_switch
, &next_switch
,
141 sizeof(next_switch
), NULL
))
143 WARN("Can't read TEB:WOW32Reserved\n");
146 if (curr_mode
== stm_16bit
)
148 if (!f_read_mem(hProcess
, next_switch
, &frame32
,
149 sizeof(frame32
), NULL
))
151 WARN("Bad stack frame 0x%08lx\n", next_switch
);
154 curr_switch
= (DWORD
)frame32
.frame16
;
155 tmp
.Mode
= AddrMode1616
;
156 tmp
.Segment
= SELECTOROF(curr_switch
);
157 tmp
.Offset
= OFFSETOF(curr_switch
);
158 if (!f_read_mem(hProcess
, f_xlat_adr(hProcess
, hThread
, &tmp
),
159 &ch
, sizeof(ch
), NULL
))
160 curr_switch
= 0xFFFFFFFF;
164 tmp
.Mode
= AddrMode1616
;
165 tmp
.Segment
= SELECTOROF(next_switch
);
166 tmp
.Offset
= OFFSETOF(next_switch
);
167 p
= f_xlat_adr(hProcess
, hThread
, &tmp
);
168 if (!f_read_mem(hProcess
, p
, &frame16
, sizeof(frame16
), NULL
))
170 WARN("Bad stack frame 0x%08lx\n", p
);
173 curr_switch
= (DWORD
)frame16
.frame32
;
175 if (!f_read_mem(hProcess
, curr_switch
, &ch
, sizeof(ch
), NULL
))
176 curr_switch
= 0xFFFFFFFF;
180 /* FIXME: this will allow to work when we're not attached to a live target,
181 * but the 16 <=> 32 switch facility won't be available.
184 frame
->AddrReturn
.Mode
= frame
->AddrStack
.Mode
= (curr_mode
== stm_16bit
) ? AddrMode1616
: AddrModeFlat
;
185 /* don't set up AddrStack on first call. Either the caller has set it up, or
186 * we will get it in the next frame
191 if (frame
->AddrFrame
.Offset
== 0) goto done_err
;
192 if (frame
->AddrFrame
.Mode
== AddrModeFlat
)
194 assert(curr_mode
== stm_32bit
);
195 do_switch
= curr_switch
&& frame
->AddrFrame
.Offset
>= curr_switch
;
199 assert(curr_mode
== stm_16bit
);
200 do_switch
= curr_switch
&&
201 frame
->AddrFrame
.Segment
== SELECTOROF(curr_switch
) &&
202 frame
->AddrFrame
.Offset
>= OFFSETOF(curr_switch
);
207 if (curr_mode
== stm_16bit
)
209 if (!f_read_mem(hProcess
, next_switch
, &frame32
,
210 sizeof(frame32
), NULL
))
212 WARN("Bad stack frame 0x%08lx\n", next_switch
);
216 frame
->AddrPC
.Mode
= AddrModeFlat
;
217 frame
->AddrPC
.Segment
= 0;
218 frame
->AddrPC
.Offset
= frame32
.retaddr
;
219 frame
->AddrFrame
.Mode
= AddrModeFlat
;
220 frame
->AddrFrame
.Segment
= 0;
221 frame
->AddrFrame
.Offset
= frame32
.ebp
;
223 frame
->AddrStack
.Mode
= AddrModeFlat
;
224 frame
->AddrStack
.Segment
= 0;
225 frame
->AddrReturn
.Mode
= AddrModeFlat
;
226 frame
->AddrReturn
.Segment
= 0;
228 next_switch
= curr_switch
;
229 tmp
.Mode
= AddrMode1616
;
230 tmp
.Segment
= SELECTOROF(next_switch
);
231 tmp
.Offset
= OFFSETOF(next_switch
);
232 p
= f_xlat_adr(hProcess
, hThread
, &tmp
);
234 if (!f_read_mem(hProcess
, p
, &frame16
, sizeof(frame16
), NULL
))
236 WARN("Bad stack frame 0x%08lx\n", p
);
239 curr_switch
= (DWORD
)frame16
.frame32
;
240 curr_mode
= stm_32bit
;
241 if (!f_read_mem(hProcess
, curr_switch
, &ch
, sizeof(ch
), NULL
))
246 tmp
.Mode
= AddrMode1616
;
247 tmp
.Segment
= SELECTOROF(next_switch
);
248 tmp
.Offset
= OFFSETOF(next_switch
);
249 p
= f_xlat_adr(hProcess
, hThread
, &tmp
);
251 if (!f_read_mem(hProcess
, p
, &frame16
, sizeof(frame16
), NULL
))
253 WARN("Bad stack frame 0x%08lx\n", p
);
257 TRACE("Got a 16 bit stack switch:"
259 "\n\tedx:%08lx ecx:%08lx ebp:%08lx"
260 "\n\tds:%04x es:%04x fs:%04x gs:%04x"
261 "\n\tcall_from_ip:%08lx module_cs:%04lx relay=%08lx"
262 "\n\tentry_ip:%04x entry_point:%08lx"
263 "\n\tbp:%04x ip:%04x cs:%04x\n",
264 (unsigned long)frame16
.frame32
,
265 frame16
.edx
, frame16
.ecx
, frame16
.ebp
,
266 frame16
.ds
, frame16
.es
, frame16
.fs
, frame16
.gs
,
267 frame16
.callfrom_ip
, frame16
.module_cs
, frame16
.relay
,
268 frame16
.entry_ip
, frame16
.entry_point
,
269 frame16
.bp
, frame16
.ip
, frame16
.cs
);
272 frame
->AddrPC
.Mode
= AddrMode1616
;
273 frame
->AddrPC
.Segment
= frame16
.cs
;
274 frame
->AddrPC
.Offset
= frame16
.ip
;
276 frame
->AddrFrame
.Mode
= AddrMode1616
;
277 frame
->AddrFrame
.Segment
= SELECTOROF(next_switch
);
278 frame
->AddrFrame
.Offset
= frame16
.bp
;
280 frame
->AddrStack
.Mode
= AddrMode1616
;
281 frame
->AddrStack
.Segment
= SELECTOROF(next_switch
);
283 frame
->AddrReturn
.Mode
= AddrMode1616
;
284 frame
->AddrReturn
.Segment
= frame16
.cs
;
286 next_switch
= curr_switch
;
287 if (!f_read_mem(hProcess
, next_switch
, &frame32
, sizeof(frame32
), NULL
))
289 WARN("Bad stack frame 0x%08lx\n", next_switch
);
292 curr_switch
= (DWORD
)frame32
.frame16
;
293 tmp
.Mode
= AddrMode1616
;
294 tmp
.Segment
= SELECTOROF(curr_switch
);
295 tmp
.Offset
= OFFSETOF(curr_switch
);
297 if (!f_read_mem(hProcess
, f_xlat_adr(hProcess
, hThread
, &tmp
),
298 &ch
, sizeof(ch
), NULL
))
300 curr_mode
= stm_16bit
;
305 frame
->AddrPC
= frame
->AddrReturn
;
306 if (curr_mode
== stm_16bit
)
308 frame
->AddrStack
.Offset
= frame
->AddrFrame
.Offset
+ 2 * sizeof(WORD
);
309 /* "pop up" previous BP value */
310 if (!f_read_mem(hProcess
,
311 f_xlat_adr(hProcess
, hThread
, &frame
->AddrFrame
),
312 &val
, sizeof(WORD
), NULL
))
314 frame
->AddrFrame
.Offset
= val
;
318 frame
->AddrStack
.Offset
= frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
);
319 /* "pop up" previous EBP value */
320 if (!f_read_mem(hProcess
, frame
->AddrFrame
.Offset
,
321 &frame
->AddrFrame
.Offset
, sizeof(DWORD
), NULL
))
327 if (curr_mode
== stm_16bit
)
331 p
= f_xlat_adr(hProcess
, hThread
, &frame
->AddrFrame
);
332 if (!f_read_mem(hProcess
, p
+ sizeof(WORD
), &val
, sizeof(WORD
), NULL
))
334 frame
->AddrReturn
.Offset
= val
;
335 /* get potential cs if a far call was used */
336 if (!f_read_mem(hProcess
, p
+ 2 * sizeof(WORD
),
337 &val
, sizeof(WORD
), NULL
))
339 if (frame
->AddrFrame
.Offset
& 1)
340 frame
->AddrReturn
.Segment
= val
; /* far call assumed */
343 /* not explicitly marked as far call,
344 * but check whether it could be anyway
346 if ((val
& 7) == 7 && val
!= frame
->AddrReturn
.Segment
)
350 if (GetThreadSelectorEntry(hThread
, val
, &le
) &&
351 (le
.HighWord
.Bits
.Type
& 0x08)) /* code segment */
353 /* it is very uncommon to push a code segment cs as
354 * a parameter, so this should work in most cases
356 frame
->AddrReturn
.Segment
= val
;
360 frame
->AddrFrame
.Offset
&= ~1;
361 /* we "pop" parameters as 16 bit entities... of course, this won't
362 * work if the parameter is in fact bigger than 16bit, but
363 * there's no way to know that here
365 for (i
= 0; i
< sizeof(frame
->Params
) / sizeof(frame
->Params
[0]); i
++)
367 f_read_mem(hProcess
, p
+ (2 + i
) * sizeof(WORD
),
368 &val
, sizeof(val
), NULL
);
369 frame
->Params
[i
] = val
;
374 if (!f_read_mem(hProcess
,
375 frame
->AddrFrame
.Offset
+ sizeof(DWORD
),
376 &frame
->AddrReturn
.Offset
, sizeof(DWORD
), NULL
))
378 WARN("Cannot read new frame offset %08lx\n", frame
->AddrFrame
.Offset
+ sizeof(DWORD
));
382 frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
),
383 frame
->Params
, sizeof(frame
->Params
), NULL
);
387 frame
->Virtual
= FALSE
;
389 TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
390 wine_dbgstr_addr(&frame
->AddrPC
),
391 wine_dbgstr_addr(&frame
->AddrFrame
),
392 wine_dbgstr_addr(&frame
->AddrReturn
),
393 wine_dbgstr_addr(&frame
->AddrStack
),
394 curr_mode
== stm_start
? "start" : (curr_mode
== stm_16bit
? "16bit" : "32bit"),
395 curr_switch
, next_switch
);
399 curr_mode
= stm_done
;