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)
79 CONTEXT* ctx = (CONTEXT*)_ctx;
88 TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p)\n",
89 MachineType, hProcess, hThread, frame, _ctx,
90 f_read_mem, FunctionTableAccessRoutine,
91 GetModuleBaseRoutine, f_xlat_adr);
93 if (MachineType != IMAGE_FILE_MACHINE_I386)
95 SetLastError(ERROR_INVALID_PARAMETER);
100 if (curr_mode >= stm_done) return FALSE;
102 if (!f_read_mem) f_read_mem = ReadProcessMemory;
103 if (!f_xlat_adr) f_xlat_adr = addr_to_linear;
105 TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
106 wine_dbgstr_addr(&frame->AddrPC),
107 wine_dbgstr_addr(&frame->AddrFrame),
108 wine_dbgstr_addr(&frame->AddrReturn),
109 wine_dbgstr_addr(&frame->AddrStack),
110 curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
111 curr_switch, next_switch);
113 if (curr_mode == stm_start)
115 THREAD_BASIC_INFORMATION info;
118 curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ?
119 stm_32bit : stm_16bit;
121 /* Get the current ESP (don't know if this is valid) */
124 frame->AddrStack.Segment = 0;
125 frame->AddrStack.Offset = ctx->Esp;
126 frame->AddrStack.Mode = AddrModeFlat;
128 /* cur_switch holds address of curr_stack's field in TEB in debuggee
131 if (NtQueryInformationThread(hThread, ThreadBasicInformation, &info,
132 sizeof(info), NULL) != STATUS_SUCCESS)
134 curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, cur_stack);
135 if (!f_read_mem(hProcess, (void*)curr_switch, &next_switch,
136 sizeof(next_switch), NULL))
138 WARN("Can't read TEB:cur_stack\n");
141 if (curr_mode == stm_16bit)
143 if (!f_read_mem(hProcess, (void*)next_switch, &frame32,
144 sizeof(frame32), NULL))
146 WARN("Bad stack frame 0x%08lx\n", next_switch);
149 curr_switch = (DWORD)frame32.frame16;
150 tmp.Mode = AddrMode1616;
151 tmp.Segment = SELECTOROF(curr_switch);
152 tmp.Offset = OFFSETOF(curr_switch);
153 if (!f_read_mem(hProcess, (void*)f_xlat_adr(hProcess, hThread, &tmp),
154 &ch, sizeof(ch), NULL))
155 curr_switch = 0xFFFFFFFF;
156 frame->AddrReturn.Mode = frame->AddrStack.Mode = AddrMode1616;
157 /* "pop up" previous BP value */
158 if (!f_read_mem(hProcess, (void*)frame->AddrFrame.Offset,
159 &val, sizeof(WORD), NULL))
161 frame->AddrFrame.Offset = val;
165 tmp.Mode = AddrMode1616;
166 tmp.Segment = SELECTOROF(next_switch);
167 tmp.Offset = OFFSETOF(next_switch);
168 p = f_xlat_adr(hProcess, hThread, &tmp);
169 if (!f_read_mem(hProcess, (void*)p, &frame16, sizeof(frame16), NULL))
171 WARN("Bad stack frame 0x%08lx\n", p);
174 curr_switch = (DWORD)frame16.frame32;
176 if (!f_read_mem(hProcess, (void*)curr_switch, &ch, sizeof(ch), NULL))
177 curr_switch = 0xFFFFFFFF;
178 frame->AddrReturn.Mode = frame->AddrStack.Mode = AddrModeFlat;
179 /* "pop up" previous EBP value */
180 if (!f_read_mem(hProcess, (void*)frame->AddrFrame.Offset,
181 &frame->AddrFrame.Offset, sizeof(DWORD), NULL))
187 if (frame->AddrFrame.Offset == 0) goto done_err;
188 if (frame->AddrFrame.Mode == AddrModeFlat)
190 assert(curr_mode == stm_32bit);
191 do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
195 assert(curr_mode == stm_16bit);
196 do_switch = OFFSETOF(curr_switch) &&
197 frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
198 frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
203 if (curr_mode == stm_16bit)
205 if (!f_read_mem(hProcess, (void*)next_switch, &frame32,
206 sizeof(frame32), NULL))
208 WARN("Bad stack frame 0x%08lx\n", next_switch);
212 frame->AddrPC.Mode = AddrModeFlat;
213 frame->AddrPC.Segment = 0;
214 frame->AddrPC.Offset = frame32.retaddr;
215 frame->AddrFrame.Mode = AddrModeFlat;
216 frame->AddrFrame.Segment = 0;
217 frame->AddrFrame.Offset = frame32.ebp;
219 frame->AddrStack.Mode = AddrModeFlat;
220 frame->AddrStack.Segment = 0;
221 frame->AddrReturn.Mode = AddrModeFlat;
222 frame->AddrReturn.Segment = 0;
224 next_switch = curr_switch;
225 tmp.Mode = AddrMode1616;
226 tmp.Segment = SELECTOROF(next_switch);
227 tmp.Offset = OFFSETOF(next_switch);
228 p = f_xlat_adr(hProcess, hThread, &tmp);
230 if (!f_read_mem(hProcess, (void*)p, &frame16, sizeof(frame16), NULL))
232 WARN("Bad stack frame 0x%08lx\n", p);
235 curr_switch = (DWORD)frame16.frame32;
236 curr_mode = stm_32bit;
237 if (!f_read_mem(hProcess, (void*)curr_switch, &ch, sizeof(ch), NULL))
238 curr_switch = 0xFFFFFFFF;
242 tmp.Mode = AddrMode1616;
243 tmp.Segment = SELECTOROF(next_switch);
244 tmp.Offset = OFFSETOF(next_switch);
245 p = f_xlat_adr(hProcess, hThread, &tmp);
247 if (!f_read_mem(hProcess, (void*)p, &frame16, sizeof(frame16), NULL))
249 WARN("Bad stack frame 0x%08lx\n", p);
253 TRACE("Got a 16 bit stack switch:"
255 "\n\tedx:%08lx ecx:%08lx ebp:%08lx"
256 "\n\tds:%04x es:%04x fs:%04x gs:%04x"
257 "\n\tcall_from_ip:%08lx module_cs:%04lx relay=%08lx"
258 "\n\tentry_ip:%04x entry_point:%08lx"
259 "\n\tbp:%04x ip:%04x cs:%04x\n",
260 (unsigned long)frame16.frame32,
261 frame16.edx, frame16.ecx, frame16.ebp,
262 frame16.ds, frame16.es, frame16.fs, frame16.gs,
263 frame16.callfrom_ip, frame16.module_cs, frame16.relay,
264 frame16.entry_ip, frame16.entry_point,
265 frame16.bp, frame16.ip, frame16.cs);
268 frame->AddrPC.Mode = AddrMode1616;
269 frame->AddrPC.Segment = frame16.cs;
270 frame->AddrPC.Offset = frame16.ip;
272 frame->AddrFrame.Mode = AddrMode1616;
273 frame->AddrFrame.Segment = SELECTOROF(next_switch);
274 frame->AddrFrame.Offset = frame16.bp;
276 frame->AddrStack.Mode = AddrMode1616;
277 frame->AddrStack.Segment = SELECTOROF(next_switch);
279 frame->AddrReturn.Mode = AddrMode1616;
280 frame->AddrReturn.Segment = frame16.cs;
282 next_switch = curr_switch;
283 if (!f_read_mem(hProcess, (void*)next_switch, &frame32, sizeof(frame32),
286 WARN("Bad stack frame 0x%08lx\n", next_switch);
289 curr_switch = (DWORD)frame32.frame16;
290 tmp.Mode = AddrMode1616;
291 tmp.Segment = SELECTOROF(curr_switch);
292 tmp.Offset = OFFSETOF(curr_switch);
294 if (!f_read_mem(hProcess, (void*)f_xlat_adr(hProcess, hThread, &tmp),
295 &ch, sizeof(ch), NULL))
296 curr_switch = 0xFFFFFFFF;
297 curr_mode = stm_16bit;
302 frame->AddrPC = frame->AddrReturn;
303 if (curr_mode == stm_16bit)
305 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(WORD);
306 /* "pop up" previous BP value */
307 if (!f_read_mem(hProcess,
308 (void*)f_xlat_adr(hProcess, hThread, &frame->AddrFrame),
309 &val, sizeof(WORD), NULL))
311 frame->AddrFrame.Offset = val;
315 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(DWORD);
316 /* "pop up" previous EBP value */
317 if (!f_read_mem(hProcess, (void*)frame->AddrFrame.Offset,
318 &frame->AddrFrame.Offset, sizeof(DWORD), NULL))
324 if (curr_mode == stm_16bit)
328 p = f_xlat_adr(hProcess, hThread, &frame->AddrFrame);
329 if (!f_read_mem(hProcess, (void*)(p + sizeof(WORD)), &val, sizeof(WORD), NULL))
331 frame->AddrReturn.Offset = val;
332 /* get potential cs if a far call was used */
333 if (!f_read_mem(hProcess, (void*)(p + 2 * sizeof(WORD)),
334 &val, sizeof(WORD), NULL))
336 if (frame->AddrFrame.Offset & 1)
337 frame->AddrReturn.Segment = val; /* far call assumed */
340 /* not explicitly marked as far call,
341 * but check whether it could be anyway
343 if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
347 if (GetThreadSelectorEntry(hThread, val, &le) &&
348 (le.HighWord.Bits.Type & 0x08)) /* code segment */
350 /* it is very uncommon to push a code segment cs as
351 * a parameter, so this should work in most cases
353 frame->AddrReturn.Segment = val;
357 frame->AddrFrame.Offset &= ~1;
358 /* we "pop" paramaters as 16 bit entities... of course, this won't
359 * work if the parameter is in fact bigger than 16bit, but
360 * there's no way to know that here
362 for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
364 f_read_mem(hProcess, (void*)(p + (2 + i) * sizeof(WORD)),
365 &val, sizeof(val), NULL);
366 frame->Params[i] = val;
371 if (!f_read_mem(hProcess,
372 (void*)(frame->AddrFrame.Offset + sizeof(DWORD)),
373 &frame->AddrReturn.Offset, sizeof(DWORD), NULL))
376 (void*)(frame->AddrFrame.Offset + 2 * sizeof(DWORD)),
377 frame->Params, sizeof(frame->Params), NULL);
381 frame->Virtual = FALSE;
383 TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\n",
384 wine_dbgstr_addr(&frame->AddrPC),
385 wine_dbgstr_addr(&frame->AddrFrame),
386 wine_dbgstr_addr(&frame->AddrReturn),
387 wine_dbgstr_addr(&frame->AddrStack),
388 curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
389 curr_switch, next_switch);
393 curr_mode = stm_done;