Implement NtAccessCheck.
[wine/gsoc-2012-control.git] / dlls / dbghelp / stack.c
blob34c6349ee878771b9df3d2c8a66b2e9a033577dd
1 /*
2 * Stack walking
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
24 #include "config.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <assert.h>
30 #include "dbghelp_private.h"
31 #include "winreg.h"
32 #include "ntstatus.h"
33 #include "winternl.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)";
44 switch (addr->Mode)
46 case AddrModeFlat:
47 return wine_dbg_sprintf("flat<%08lx>", addr->Offset);
48 case AddrMode1616:
49 return wine_dbg_sprintf("1616<%04x:%04lx>", addr->Segment, addr->Offset);
50 case AddrMode1632:
51 return wine_dbg_sprintf("1632<%04x:%08lx>", addr->Segment, addr->Offset);
52 case AddrModeReal:
53 return wine_dbg_sprintf("real<%04x:%04lx>", addr->Segment, addr->Offset);
54 default:
55 return "unknown";
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)
84 STACK32FRAME frame32;
85 STACK16FRAME frame16;
86 char ch;
87 ADDRESS tmp;
88 DWORD p;
89 WORD val;
90 BOOL do_switch;
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);
100 return FALSE;
103 /* sanity check */
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");
126 goto done_err;
129 /* Init done */
130 curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ?
131 stm_32bit : stm_16bit;
133 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
134 * address space
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");
144 goto done_err;
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);
152 goto done_err;
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;
162 else
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);
171 goto done_err;
173 curr_switch = (DWORD)frame16.frame32;
175 if (!f_read_mem(hProcess, curr_switch, &ch, sizeof(ch), NULL))
176 curr_switch = 0xFFFFFFFF;
179 else
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.
183 curr_switch = 0;
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
189 else
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;
197 else
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);
205 if (do_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);
213 goto done_err;
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);
237 goto done_err;
239 curr_switch = (DWORD)frame16.frame32;
240 curr_mode = stm_32bit;
241 if (!f_read_mem(hProcess, curr_switch, &ch, sizeof(ch), NULL))
242 curr_switch = 0;
244 else
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);
254 goto done_err;
257 TRACE("Got a 16 bit stack switch:"
258 "\n\tframe32: %08lx"
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);
290 goto done_err;
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))
299 curr_switch = 0;
300 curr_mode = stm_16bit;
303 else
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))
313 goto done_err;
314 frame->AddrFrame.Offset = val;
316 else
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))
322 goto done_err;
327 if (curr_mode == stm_16bit)
329 int i;
331 p = f_xlat_adr(hProcess, hThread, &frame->AddrFrame);
332 if (!f_read_mem(hProcess, p + sizeof(WORD), &val, sizeof(WORD), NULL))
333 goto done_err;
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))
338 goto done_err;
339 if (frame->AddrFrame.Offset & 1)
340 frame->AddrReturn.Segment = val; /* far call assumed */
341 else
343 /* not explicitly marked as far call,
344 * but check whether it could be anyway
346 if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
348 LDT_ENTRY le;
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;
372 else
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));
379 goto done_err;
381 f_read_mem(hProcess,
382 frame->AddrFrame.Offset + 2 * sizeof(DWORD),
383 frame->Params, sizeof(frame->Params), NULL);
386 frame->Far = FALSE;
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);
397 return TRUE;
398 done_err:
399 curr_mode = stm_done;
400 return FALSE;