[CMAKE]
[reactos.git] / dll / win32 / dbghelp / cpu_i386.c
1 /*
2 * File cpu_i386.c
3 *
4 * Copyright (C) 2009-2009, Eric Pouech.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include <assert.h>
22
23 #include "ntstatus.h"
24 #define WIN32_NO_STATUS
25 #include "dbghelp_private.h"
26 #include "wine/winbase16.h"
27 #include "winternl.h"
28 #include "wine/debug.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
31
32 #define STEP_FLAG 0x00000100 /* single step flag */
33 #define V86_FLAG 0x00020000
34
35 #define IS_VM86_MODE(ctx) (ctx->EFlags & V86_FLAG)
36
37 #ifdef __i386__
38 static ADDRESS_MODE get_selector_type(HANDLE hThread, const CONTEXT* ctx, WORD sel)
39 {
40 LDT_ENTRY le;
41
42 if (IS_VM86_MODE(ctx)) return AddrModeReal;
43 /* null or system selector */
44 if (!(sel & 4) || ((sel >> 3) < 17)) return AddrModeFlat;
45 if (hThread && GetThreadSelectorEntry(hThread, sel, &le))
46 return le.HighWord.Bits.Default_Big ? AddrMode1632 : AddrMode1616;
47 /* selector doesn't exist */
48 return -1;
49 }
50
51 static unsigned i386_build_addr(HANDLE hThread, const CONTEXT* ctx, ADDRESS64* addr,
52 unsigned seg, unsigned long offset)
53 {
54 addr->Mode = AddrModeFlat;
55 addr->Segment = seg;
56 addr->Offset = offset;
57 if (seg)
58 {
59 switch (addr->Mode = get_selector_type(hThread, ctx, seg))
60 {
61 case AddrModeReal:
62 case AddrMode1616:
63 addr->Offset &= 0xffff;
64 break;
65 case AddrModeFlat:
66 case AddrMode1632:
67 break;
68 default:
69 return FALSE;
70 }
71 }
72 return TRUE;
73 }
74 #endif
75
76 static unsigned i386_get_addr(HANDLE hThread, const CONTEXT* ctx,
77 enum cpu_addr ca, ADDRESS64* addr)
78 {
79 #ifdef __i386__
80 switch (ca)
81 {
82 case cpu_addr_pc: return i386_build_addr(hThread, ctx, addr, ctx->SegCs, ctx->Eip);
83 case cpu_addr_stack: return i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Esp);
84 case cpu_addr_frame: return i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Ebp);
85 }
86 #endif
87 return FALSE;
88 }
89
90 enum st_mode {stm_start, stm_32bit, stm_16bit, stm_done};
91
92 /* indexes in Reserved array */
93 #define __CurrentMode 0
94 #define __CurrentSwitch 1
95 #define __NextSwitch 2
96
97 #define curr_mode (frame->Reserved[__CurrentMode])
98 #define curr_switch (frame->Reserved[__CurrentSwitch])
99 #define next_switch (frame->Reserved[__NextSwitch])
100
101 static BOOL i386_stack_walk(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame)
102 {
103 STACK32FRAME frame32;
104 STACK16FRAME frame16;
105 char ch;
106 ADDRESS64 tmp;
107 DWORD p;
108 WORD val;
109 BOOL do_switch;
110
111 /* sanity check */
112 if (curr_mode >= stm_done) return FALSE;
113
114 TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%p nSwitch=%p\n",
115 wine_dbgstr_addr(&frame->AddrPC),
116 wine_dbgstr_addr(&frame->AddrFrame),
117 wine_dbgstr_addr(&frame->AddrReturn),
118 wine_dbgstr_addr(&frame->AddrStack),
119 curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
120 (void*)(DWORD_PTR)curr_switch, (void*)(DWORD_PTR)next_switch);
121
122 if (curr_mode == stm_start)
123 {
124 THREAD_BASIC_INFORMATION info;
125
126 if ((frame->AddrPC.Mode == AddrModeFlat) &&
127 (frame->AddrFrame.Mode != AddrModeFlat))
128 {
129 WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
130 goto done_err;
131 }
132
133 /* Init done */
134 curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ? stm_32bit : stm_16bit;
135
136 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
137 * address space
138 */
139 if (NtQueryInformationThread(csw->hThread, ThreadBasicInformation, &info,
140 sizeof(info), NULL) == STATUS_SUCCESS)
141 {
142 curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, WOW32Reserved);
143 if (!sw_read_mem(csw, curr_switch, &p, sizeof(p)))
144 {
145 WARN("Can't read TEB:WOW32Reserved\n");
146 goto done_err;
147 }
148 next_switch = p;
149 if (!next_switch) /* no 16-bit stack */
150 {
151 curr_switch = 0;
152 }
153 else if (curr_mode == stm_16bit)
154 {
155 if (!sw_read_mem(csw, next_switch, &frame32, sizeof(frame32)))
156 {
157 WARN("Bad stack frame %p\n", (void*)(DWORD_PTR)next_switch);
158 goto done_err;
159 }
160 curr_switch = (DWORD)frame32.frame16;
161 tmp.Mode = AddrMode1616;
162 tmp.Segment = SELECTOROF(curr_switch);
163 tmp.Offset = OFFSETOF(curr_switch);
164 if (!sw_read_mem(csw, sw_xlat_addr(csw, &tmp), &ch, sizeof(ch)))
165 curr_switch = 0xFFFFFFFF;
166 }
167 else
168 {
169 tmp.Mode = AddrMode1616;
170 tmp.Segment = SELECTOROF(next_switch);
171 tmp.Offset = OFFSETOF(next_switch);
172 p = sw_xlat_addr(csw, &tmp);
173 if (!sw_read_mem(csw, p, &frame16, sizeof(frame16)))
174 {
175 WARN("Bad stack frame 0x%08x\n", p);
176 goto done_err;
177 }
178 curr_switch = (DWORD_PTR)frame16.frame32;
179
180 if (!sw_read_mem(csw, curr_switch, &ch, sizeof(ch)))
181 curr_switch = 0xFFFFFFFF;
182 }
183 }
184 else
185 /* FIXME: this will allow to work when we're not attached to a live target,
186 * but the 16 <=> 32 switch facility won't be available.
187 */
188 curr_switch = 0;
189 frame->AddrReturn.Mode = frame->AddrStack.Mode = (curr_mode == stm_16bit) ? AddrMode1616 : AddrModeFlat;
190 /* don't set up AddrStack on first call. Either the caller has set it up, or
191 * we will get it in the next frame
192 */
193 memset(&frame->AddrBStore, 0, sizeof(frame->AddrBStore));
194 }
195 else
196 {
197 if (frame->AddrFrame.Offset == 0) goto done_err;
198 if (frame->AddrFrame.Mode == AddrModeFlat)
199 {
200 assert(curr_mode == stm_32bit);
201 do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
202 }
203 else
204 {
205 assert(curr_mode == stm_16bit);
206 do_switch = curr_switch &&
207 frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
208 frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
209 }
210
211 if (do_switch)
212 {
213 if (curr_mode == stm_16bit)
214 {
215 if (!sw_read_mem(csw, next_switch, &frame32, sizeof(frame32)))
216 {
217 WARN("Bad stack frame %p\n", (void*)(DWORD_PTR)next_switch);
218 goto done_err;
219 }
220
221 frame->AddrPC.Mode = AddrModeFlat;
222 frame->AddrPC.Segment = 0;
223 frame->AddrPC.Offset = frame32.retaddr;
224 frame->AddrFrame.Mode = AddrModeFlat;
225 frame->AddrFrame.Segment = 0;
226 frame->AddrFrame.Offset = frame32.ebp;
227
228 frame->AddrStack.Mode = AddrModeFlat;
229 frame->AddrStack.Segment = 0;
230 frame->AddrReturn.Mode = AddrModeFlat;
231 frame->AddrReturn.Segment = 0;
232
233 next_switch = curr_switch;
234 tmp.Mode = AddrMode1616;
235 tmp.Segment = SELECTOROF(next_switch);
236 tmp.Offset = OFFSETOF(next_switch);
237 p = sw_xlat_addr(csw, &tmp);
238
239 if (!sw_read_mem(csw, p, &frame16, sizeof(frame16)))
240 {
241 WARN("Bad stack frame 0x%08x\n", p);
242 goto done_err;
243 }
244 curr_switch = (DWORD_PTR)frame16.frame32;
245 curr_mode = stm_32bit;
246 if (!sw_read_mem(csw, curr_switch, &ch, sizeof(ch)))
247 curr_switch = 0;
248 }
249 else
250 {
251 tmp.Mode = AddrMode1616;
252 tmp.Segment = SELECTOROF(next_switch);
253 tmp.Offset = OFFSETOF(next_switch);
254 p = sw_xlat_addr(csw, &tmp);
255
256 if (!sw_read_mem(csw, p, &frame16, sizeof(frame16)))
257 {
258 WARN("Bad stack frame 0x%08x\n", p);
259 goto done_err;
260 }
261
262 TRACE("Got a 16 bit stack switch:"
263 "\n\tframe32: %08lx"
264 "\n\tedx:%08x ecx:%08x ebp:%08x"
265 "\n\tds:%04x es:%04x fs:%04x gs:%04x"
266 "\n\tcall_from_ip:%08x module_cs:%04x relay=%08x"
267 "\n\tentry_ip:%04x entry_point:%08x"
268 "\n\tbp:%04x ip:%04x cs:%04x\n",
269 (unsigned long)frame16.frame32,
270 frame16.edx, frame16.ecx, frame16.ebp,
271 frame16.ds, frame16.es, frame16.fs, frame16.gs,
272 frame16.callfrom_ip, frame16.module_cs, frame16.relay,
273 frame16.entry_ip, frame16.entry_point,
274 frame16.bp, frame16.ip, frame16.cs);
275
276 frame->AddrPC.Mode = AddrMode1616;
277 frame->AddrPC.Segment = frame16.cs;
278 frame->AddrPC.Offset = frame16.ip;
279
280 frame->AddrFrame.Mode = AddrMode1616;
281 frame->AddrFrame.Segment = SELECTOROF(next_switch);
282 frame->AddrFrame.Offset = frame16.bp;
283
284 frame->AddrStack.Mode = AddrMode1616;
285 frame->AddrStack.Segment = SELECTOROF(next_switch);
286
287 frame->AddrReturn.Mode = AddrMode1616;
288 frame->AddrReturn.Segment = frame16.cs;
289
290 next_switch = curr_switch;
291 if (!sw_read_mem(csw, next_switch, &frame32, sizeof(frame32)))
292 {
293 WARN("Bad stack frame %p\n", (void*)(DWORD_PTR)next_switch);
294 goto done_err;
295 }
296 curr_switch = (DWORD)frame32.frame16;
297 tmp.Mode = AddrMode1616;
298 tmp.Segment = SELECTOROF(curr_switch);
299 tmp.Offset = OFFSETOF(curr_switch);
300
301 if (!sw_read_mem(csw, sw_xlat_addr(csw, &tmp), &ch, sizeof(ch)))
302 curr_switch = 0;
303 curr_mode = stm_16bit;
304 }
305 }
306 else
307 {
308 frame->AddrPC = frame->AddrReturn;
309 if (curr_mode == stm_16bit)
310 {
311 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(WORD);
312 /* "pop up" previous BP value */
313 if (!sw_read_mem(csw, sw_xlat_addr(csw, &frame->AddrFrame),
314 &val, sizeof(WORD)))
315 goto done_err;
316 frame->AddrFrame.Offset = val;
317 }
318 else
319 {
320 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(DWORD);
321 /* "pop up" previous EBP value */
322 if (!sw_read_mem(csw, frame->AddrFrame.Offset,
323 &frame->AddrFrame.Offset, sizeof(DWORD)))
324 goto done_err;
325 }
326 }
327 }
328
329 if (curr_mode == stm_16bit)
330 {
331 unsigned int i;
332
333 p = sw_xlat_addr(csw, &frame->AddrFrame);
334 if (!sw_read_mem(csw, p + sizeof(WORD), &val, sizeof(WORD)))
335 goto done_err;
336 frame->AddrReturn.Offset = val;
337 /* get potential cs if a far call was used */
338 if (!sw_read_mem(csw, p + 2 * sizeof(WORD), &val, sizeof(WORD)))
339 goto done_err;
340 if (frame->AddrFrame.Offset & 1)
341 frame->AddrReturn.Segment = val; /* far call assumed */
342 else
343 {
344 /* not explicitly marked as far call,
345 * but check whether it could be anyway
346 */
347 if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
348 {
349 LDT_ENTRY le;
350
351 if (GetThreadSelectorEntry(csw->hThread, val, &le) &&
352 (le.HighWord.Bits.Type & 0x08)) /* code segment */
353 {
354 /* it is very uncommon to push a code segment cs as
355 * a parameter, so this should work in most cases
356 */
357 frame->AddrReturn.Segment = val;
358 }
359 }
360 }
361 frame->AddrFrame.Offset &= ~1;
362 /* we "pop" parameters as 16 bit entities... of course, this won't
363 * work if the parameter is in fact bigger than 16bit, but
364 * there's no way to know that here
365 */
366 for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
367 {
368 sw_read_mem(csw, p + (2 + i) * sizeof(WORD), &val, sizeof(val));
369 frame->Params[i] = val;
370 }
371 }
372 else
373 {
374 if (!sw_read_mem(csw, frame->AddrFrame.Offset + sizeof(DWORD),
375 &frame->AddrReturn.Offset, sizeof(DWORD)))
376 {
377 WARN("Cannot read new frame offset %p\n",
378 (void*)(DWORD_PTR)(frame->AddrFrame.Offset + (int)sizeof(DWORD)));
379 goto done_err;
380 }
381 sw_read_mem(csw, frame->AddrFrame.Offset + 2 * sizeof(DWORD),
382 frame->Params, sizeof(frame->Params));
383 }
384
385 frame->Far = TRUE;
386 frame->Virtual = TRUE;
387 p = sw_xlat_addr(csw, &frame->AddrPC);
388 if (p && sw_module_base(csw, p))
389 frame->FuncTableEntry = sw_table_access(csw, p);
390 else
391 frame->FuncTableEntry = NULL;
392
393 TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%p nSwitch=%p FuncTable=%p\n",
394 wine_dbgstr_addr(&frame->AddrPC),
395 wine_dbgstr_addr(&frame->AddrFrame),
396 wine_dbgstr_addr(&frame->AddrReturn),
397 wine_dbgstr_addr(&frame->AddrStack),
398 curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
399 (void*)(DWORD_PTR)curr_switch, (void*)(DWORD_PTR)next_switch, frame->FuncTableEntry);
400
401 return TRUE;
402 done_err:
403 curr_mode = stm_done;
404 return FALSE;
405 }
406
407 struct cpu cpu_i386 = {
408 IMAGE_FILE_MACHINE_I386,
409 4,
410 i386_get_addr,
411 i386_stack_walk,
412 };