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/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 /* 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
)
87 TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p)\n",
88 MachineType
, hProcess
, hThread
, frame
, ctx
,
89 f_read_mem
, FunctionTableAccessRoutine
,
90 GetModuleBaseRoutine
, f_xlat_adr
);
92 if (MachineType
!= IMAGE_FILE_MACHINE_I386
)
94 SetLastError(ERROR_INVALID_PARAMETER
);
99 if (curr_mode
>= stm_done
) return FALSE
;
101 if (!f_read_mem
) f_read_mem
= ReadProcessMemory
;
102 if (!f_xlat_adr
) f_xlat_adr
= addr_to_linear
;
104 TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
105 wine_dbgstr_addr(&frame
->AddrPC
),
106 wine_dbgstr_addr(&frame
->AddrFrame
),
107 wine_dbgstr_addr(&frame
->AddrReturn
),
108 wine_dbgstr_addr(&frame
->AddrStack
),
109 curr_mode
== stm_start
? "start" : (curr_mode
== stm_16bit
? "16bit" : "32bit"),
110 curr_switch
, next_switch
);
112 if (curr_mode
== stm_start
)
114 THREAD_BASIC_INFORMATION info
;
116 if ((frame
->AddrPC
.Mode
== AddrModeFlat
) &&
117 (frame
->AddrFrame
.Mode
!= AddrModeFlat
))
119 WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
124 curr_mode
= (frame
->AddrPC
.Mode
== AddrModeFlat
) ?
125 stm_32bit
: stm_16bit
;
127 /* cur_switch holds address of curr_stack's field in TEB in debuggee
130 if (NtQueryInformationThread(hThread
, ThreadBasicInformation
, &info
,
131 sizeof(info
), NULL
) != STATUS_SUCCESS
)
133 curr_switch
= (unsigned long)info
.TebBaseAddress
+ FIELD_OFFSET(TEB
, WOW32Reserved
);
134 if (!f_read_mem(hProcess
, (void*)curr_switch
, &next_switch
,
135 sizeof(next_switch
), NULL
))
137 WARN("Can't read TEB:cur_stack\n");
140 if (curr_mode
== stm_16bit
)
142 if (!f_read_mem(hProcess
, (void*)next_switch
, &frame32
,
143 sizeof(frame32
), NULL
))
145 WARN("Bad stack frame 0x%08lx\n", next_switch
);
148 curr_switch
= (DWORD
)frame32
.frame16
;
149 tmp
.Mode
= AddrMode1616
;
150 tmp
.Segment
= SELECTOROF(curr_switch
);
151 tmp
.Offset
= OFFSETOF(curr_switch
);
152 if (!f_read_mem(hProcess
, (void*)f_xlat_adr(hProcess
, hThread
, &tmp
),
153 &ch
, sizeof(ch
), NULL
))
154 curr_switch
= 0xFFFFFFFF;
155 frame
->AddrReturn
.Mode
= frame
->AddrStack
.Mode
= AddrMode1616
;
159 tmp
.Mode
= AddrMode1616
;
160 tmp
.Segment
= SELECTOROF(next_switch
);
161 tmp
.Offset
= OFFSETOF(next_switch
);
162 p
= f_xlat_adr(hProcess
, hThread
, &tmp
);
163 if (!f_read_mem(hProcess
, (void*)p
, &frame16
, sizeof(frame16
), NULL
))
165 WARN("Bad stack frame 0x%08lx\n", p
);
168 curr_switch
= (DWORD
)frame16
.frame32
;
170 if (!f_read_mem(hProcess
, (void*)curr_switch
, &ch
, sizeof(ch
), NULL
))
171 curr_switch
= 0xFFFFFFFF;
172 frame
->AddrReturn
.Mode
= frame
->AddrStack
.Mode
= AddrModeFlat
;
174 /* don't set up AddrStack on first call. Either the caller has set it up, or
175 * we will get it in the next frame
180 if (frame
->AddrFrame
.Offset
== 0) goto done_err
;
181 if (frame
->AddrFrame
.Mode
== AddrModeFlat
)
183 assert(curr_mode
== stm_32bit
);
184 do_switch
= curr_switch
&& frame
->AddrFrame
.Offset
>= curr_switch
;
188 assert(curr_mode
== stm_16bit
);
189 do_switch
= OFFSETOF(curr_switch
) &&
190 frame
->AddrFrame
.Segment
== SELECTOROF(curr_switch
) &&
191 frame
->AddrFrame
.Offset
>= OFFSETOF(curr_switch
);
196 if (curr_mode
== stm_16bit
)
198 if (!f_read_mem(hProcess
, (void*)next_switch
, &frame32
,
199 sizeof(frame32
), NULL
))
201 WARN("Bad stack frame 0x%08lx\n", next_switch
);
205 frame
->AddrPC
.Mode
= AddrModeFlat
;
206 frame
->AddrPC
.Segment
= 0;
207 frame
->AddrPC
.Offset
= frame32
.retaddr
;
208 frame
->AddrFrame
.Mode
= AddrModeFlat
;
209 frame
->AddrFrame
.Segment
= 0;
210 frame
->AddrFrame
.Offset
= frame32
.ebp
;
212 frame
->AddrStack
.Mode
= AddrModeFlat
;
213 frame
->AddrStack
.Segment
= 0;
214 frame
->AddrReturn
.Mode
= AddrModeFlat
;
215 frame
->AddrReturn
.Segment
= 0;
217 next_switch
= curr_switch
;
218 tmp
.Mode
= AddrMode1616
;
219 tmp
.Segment
= SELECTOROF(next_switch
);
220 tmp
.Offset
= OFFSETOF(next_switch
);
221 p
= f_xlat_adr(hProcess
, hThread
, &tmp
);
223 if (!f_read_mem(hProcess
, (void*)p
, &frame16
, sizeof(frame16
), NULL
))
225 WARN("Bad stack frame 0x%08lx\n", p
);
228 curr_switch
= (DWORD
)frame16
.frame32
;
229 curr_mode
= stm_32bit
;
230 if (!f_read_mem(hProcess
, (void*)curr_switch
, &ch
, sizeof(ch
), NULL
))
231 curr_switch
= 0xFFFFFFFF;
235 tmp
.Mode
= AddrMode1616
;
236 tmp
.Segment
= SELECTOROF(next_switch
);
237 tmp
.Offset
= OFFSETOF(next_switch
);
238 p
= f_xlat_adr(hProcess
, hThread
, &tmp
);
240 if (!f_read_mem(hProcess
, (void*)p
, &frame16
, sizeof(frame16
), NULL
))
242 WARN("Bad stack frame 0x%08lx\n", p
);
246 TRACE("Got a 16 bit stack switch:"
248 "\n\tedx:%08lx ecx:%08lx ebp:%08lx"
249 "\n\tds:%04x es:%04x fs:%04x gs:%04x"
250 "\n\tcall_from_ip:%08lx module_cs:%04lx relay=%08lx"
251 "\n\tentry_ip:%04x entry_point:%08lx"
252 "\n\tbp:%04x ip:%04x cs:%04x\n",
253 (unsigned long)frame16
.frame32
,
254 frame16
.edx
, frame16
.ecx
, frame16
.ebp
,
255 frame16
.ds
, frame16
.es
, frame16
.fs
, frame16
.gs
,
256 frame16
.callfrom_ip
, frame16
.module_cs
, frame16
.relay
,
257 frame16
.entry_ip
, frame16
.entry_point
,
258 frame16
.bp
, frame16
.ip
, frame16
.cs
);
261 frame
->AddrPC
.Mode
= AddrMode1616
;
262 frame
->AddrPC
.Segment
= frame16
.cs
;
263 frame
->AddrPC
.Offset
= frame16
.ip
;
265 frame
->AddrFrame
.Mode
= AddrMode1616
;
266 frame
->AddrFrame
.Segment
= SELECTOROF(next_switch
);
267 frame
->AddrFrame
.Offset
= frame16
.bp
;
269 frame
->AddrStack
.Mode
= AddrMode1616
;
270 frame
->AddrStack
.Segment
= SELECTOROF(next_switch
);
272 frame
->AddrReturn
.Mode
= AddrMode1616
;
273 frame
->AddrReturn
.Segment
= frame16
.cs
;
275 next_switch
= curr_switch
;
276 if (!f_read_mem(hProcess
, (void*)next_switch
, &frame32
, sizeof(frame32
),
279 WARN("Bad stack frame 0x%08lx\n", next_switch
);
282 curr_switch
= (DWORD
)frame32
.frame16
;
283 tmp
.Mode
= AddrMode1616
;
284 tmp
.Segment
= SELECTOROF(curr_switch
);
285 tmp
.Offset
= OFFSETOF(curr_switch
);
287 if (!f_read_mem(hProcess
, (void*)f_xlat_adr(hProcess
, hThread
, &tmp
),
288 &ch
, sizeof(ch
), NULL
))
289 curr_switch
= 0xFFFFFFFF;
290 curr_mode
= stm_16bit
;
295 frame
->AddrPC
= frame
->AddrReturn
;
296 if (curr_mode
== stm_16bit
)
298 frame
->AddrStack
.Offset
= frame
->AddrFrame
.Offset
+ 2 * sizeof(WORD
);
299 /* "pop up" previous BP value */
300 if (!f_read_mem(hProcess
,
301 (void*)f_xlat_adr(hProcess
, hThread
, &frame
->AddrFrame
),
302 &val
, sizeof(WORD
), NULL
))
304 frame
->AddrFrame
.Offset
= val
;
308 frame
->AddrStack
.Offset
= frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
);
309 /* "pop up" previous EBP value */
310 if (!f_read_mem(hProcess
, (void*)frame
->AddrFrame
.Offset
,
311 &frame
->AddrFrame
.Offset
, sizeof(DWORD
), NULL
))
317 if (curr_mode
== stm_16bit
)
321 p
= f_xlat_adr(hProcess
, hThread
, &frame
->AddrFrame
);
322 if (!f_read_mem(hProcess
, (void*)(p
+ sizeof(WORD
)), &val
, sizeof(WORD
), NULL
))
324 frame
->AddrReturn
.Offset
= val
;
325 /* get potential cs if a far call was used */
326 if (!f_read_mem(hProcess
, (void*)(p
+ 2 * sizeof(WORD
)),
327 &val
, sizeof(WORD
), NULL
))
329 if (frame
->AddrFrame
.Offset
& 1)
330 frame
->AddrReturn
.Segment
= val
; /* far call assumed */
333 /* not explicitly marked as far call,
334 * but check whether it could be anyway
336 if ((val
& 7) == 7 && val
!= frame
->AddrReturn
.Segment
)
340 if (GetThreadSelectorEntry(hThread
, val
, &le
) &&
341 (le
.HighWord
.Bits
.Type
& 0x08)) /* code segment */
343 /* it is very uncommon to push a code segment cs as
344 * a parameter, so this should work in most cases
346 frame
->AddrReturn
.Segment
= val
;
350 frame
->AddrFrame
.Offset
&= ~1;
351 /* we "pop" parameters as 16 bit entities... of course, this won't
352 * work if the parameter is in fact bigger than 16bit, but
353 * there's no way to know that here
355 for (i
= 0; i
< sizeof(frame
->Params
) / sizeof(frame
->Params
[0]); i
++)
357 f_read_mem(hProcess
, (void*)(p
+ (2 + i
) * sizeof(WORD
)),
358 &val
, sizeof(val
), NULL
);
359 frame
->Params
[i
] = val
;
364 if (!f_read_mem(hProcess
,
365 (void*)(frame
->AddrFrame
.Offset
+ sizeof(DWORD
)),
366 &frame
->AddrReturn
.Offset
, sizeof(DWORD
), NULL
))
369 (void*)(frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
)),
370 frame
->Params
, sizeof(frame
->Params
), NULL
);
374 frame
->Virtual
= FALSE
;
376 TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
377 wine_dbgstr_addr(&frame
->AddrPC
),
378 wine_dbgstr_addr(&frame
->AddrFrame
),
379 wine_dbgstr_addr(&frame
->AddrReturn
),
380 wine_dbgstr_addr(&frame
->AddrStack
),
381 curr_mode
== stm_start
? "start" : (curr_mode
== stm_16bit
? "16bit" : "32bit"),
382 curr_switch
, next_switch
);
386 curr_mode
= stm_done
;