Sync with trunk r63174.
[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 "dbghelp_private.h"
22
23 #ifndef DBGHELP_STATIC_LIB
24 #include <wine/winbase16.h>
25 #endif
26
27 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
28
29 #define STEP_FLAG 0x00000100 /* single step flag */
30 #define V86_FLAG 0x00020000
31
32 #define IS_VM86_MODE(ctx) (ctx->EFlags & V86_FLAG)
33
34 #if defined(__i386__) && !defined(DBGHELP_STATIC_LIB)
35 static ADDRESS_MODE get_selector_type(HANDLE hThread, const CONTEXT* ctx, WORD sel)
36 {
37 LDT_ENTRY le;
38
39 if (IS_VM86_MODE(ctx)) return AddrModeReal;
40 /* null or system selector */
41 if (!(sel & 4) || ((sel >> 3) < 17)) return AddrModeFlat;
42 if (hThread && GetThreadSelectorEntry(hThread, sel, &le))
43 return le.HighWord.Bits.Default_Big ? AddrMode1632 : AddrMode1616;
44 /* selector doesn't exist */
45 return -1;
46 }
47
48 static BOOL i386_build_addr(HANDLE hThread, const CONTEXT* ctx, ADDRESS64* addr,
49 unsigned seg, unsigned long offset)
50 {
51 addr->Mode = AddrModeFlat;
52 addr->Segment = seg;
53 addr->Offset = offset;
54 if (seg)
55 {
56 switch (addr->Mode = get_selector_type(hThread, ctx, seg))
57 {
58 case AddrModeReal:
59 case AddrMode1616:
60 addr->Offset &= 0xffff;
61 break;
62 case AddrModeFlat:
63 case AddrMode1632:
64 break;
65 default:
66 return FALSE;
67 }
68 }
69 return TRUE;
70 }
71 #endif
72
73 #ifndef DBGHELP_STATIC_LIB
74 static BOOL i386_get_addr(HANDLE hThread, const CONTEXT* ctx,
75 enum cpu_addr ca, ADDRESS64* addr)
76 {
77 #ifdef __i386__
78 switch (ca)
79 {
80 case cpu_addr_pc: return i386_build_addr(hThread, ctx, addr, ctx->SegCs, ctx->Eip);
81 case cpu_addr_stack: return i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Esp);
82 case cpu_addr_frame: return i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Ebp);
83 }
84 #endif
85 return FALSE;
86 }
87 #endif /* DBGHELP_STATIC_LIB */
88
89 #if defined(__i386__) && !defined(DBGHELP_STATIC_LIB)
90 /* fetch_next_frame32()
91 *
92 * modify (at least) context.{eip, esp, ebp} using unwind information
93 * either out of debug info (dwarf, pdb), or simple stack unwind
94 */
95 static BOOL fetch_next_frame32(struct cpu_stack_walk* csw,
96 CONTEXT* context, DWORD_PTR curr_pc)
97 {
98 DWORD_PTR xframe;
99 struct pdb_cmd_pair cpair[4];
100 DWORD val32;
101
102 if (dwarf2_virtual_unwind(csw, curr_pc, context, &xframe))
103 {
104 context->Esp = xframe;
105 return TRUE;
106 }
107 cpair[0].name = "$ebp"; cpair[0].pvalue = &context->Ebp;
108 cpair[1].name = "$esp"; cpair[1].pvalue = &context->Esp;
109 cpair[2].name = "$eip"; cpair[2].pvalue = &context->Eip;
110 cpair[3].name = NULL; cpair[3].pvalue = NULL;
111
112 #ifndef DBGHELP_STATIC_LIB
113 if (!pdb_virtual_unwind(csw, curr_pc, context, cpair))
114 #endif
115 {
116 /* do a simple unwind using ebp
117 * we assume a "regular" prologue in the function has been used
118 */
119 if (!context->Ebp) return FALSE;
120 context->Esp = context->Ebp + 2 * sizeof(DWORD);
121 if (!sw_read_mem(csw, context->Ebp + sizeof(DWORD), &val32, sizeof(DWORD)))
122 {
123 WARN("Cannot read new frame offset %p\n",
124 (void*)(DWORD_PTR)(context->Ebp + (int)sizeof(DWORD)));
125 return FALSE;
126 }
127 context->Eip = val32;
128 /* "pop up" previous EBP value */
129 if (!sw_read_mem(csw, context->Ebp, &val32, sizeof(DWORD)))
130 return FALSE;
131 context->Ebp = val32;
132 }
133 return TRUE;
134 }
135 #endif
136
137 enum st_mode {stm_start, stm_32bit, stm_16bit, stm_done};
138
139 /* indexes in Reserved array */
140 #define __CurrentModeCount 0
141 #define __CurrentSwitch 1
142 #define __NextSwitch 2
143
144 #define curr_mode (frame->Reserved[__CurrentModeCount] & 0x0F)
145 #define curr_count (frame->Reserved[__CurrentModeCount] >> 4)
146 #define curr_switch (frame->Reserved[__CurrentSwitch])
147 #define next_switch (frame->Reserved[__NextSwitch])
148
149 #define set_curr_mode(m) {frame->Reserved[__CurrentModeCount] &= ~0x0F; frame->Reserved[__CurrentModeCount] |= (m & 0x0F);}
150 #define inc_curr_count() (frame->Reserved[__CurrentModeCount] += 0x10)
151
152 #ifndef DBGHELP_STATIC_LIB
153 static BOOL i386_stack_walk(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame, CONTEXT* context)
154 {
155 STACK32FRAME frame32;
156 STACK16FRAME frame16;
157 char ch;
158 ADDRESS64 tmp;
159 DWORD p;
160 WORD val16;
161 DWORD val32;
162 BOOL do_switch;
163 #ifdef __i386__
164 unsigned deltapc;
165 CONTEXT _context;
166 #endif
167
168 /* sanity check */
169 if (curr_mode >= stm_done) return FALSE;
170
171 TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s Count=%s cSwitch=%p nSwitch=%p\n",
172 wine_dbgstr_addr(&frame->AddrPC),
173 wine_dbgstr_addr(&frame->AddrFrame),
174 wine_dbgstr_addr(&frame->AddrReturn),
175 wine_dbgstr_addr(&frame->AddrStack),
176 curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
177 wine_dbgstr_longlong(curr_count),
178 (void*)(DWORD_PTR)curr_switch, (void*)(DWORD_PTR)next_switch);
179
180 #ifdef __i386__
181 /* if we're at first call (which doesn't actually unwind, it just computes ReturnPC,
182 * or if we're doing the first real unwind (count == 1), then we can directly use
183 * eip. otherwise, eip is *after* the insn that actually made the call to
184 * previous frame, so decrease eip by delta pc (1!) so that we're inside previous
185 * insn.
186 * Doing so, we ensure that the pc used for unwinding is always inside the function
187 * we want to use for next frame
188 */
189 deltapc = curr_count <= 1 ? 0 : 1;
190
191 if (!context)
192 {
193 /* setup a pseudo context for the rest of the code (esp. unwinding) */
194 context = &_context;
195 memset(context, 0, sizeof(*context));
196 context->ContextFlags = CONTEXT_CONTROL | CONTEXT_SEGMENTS;
197 if (frame->AddrPC.Mode != AddrModeFlat) context->SegCs = frame->AddrPC.Segment;
198 context->Eip = frame->AddrPC.Offset;
199 if (frame->AddrFrame.Mode != AddrModeFlat) context->SegSs = frame->AddrFrame.Segment;
200 context->Ebp = frame->AddrFrame.Offset;
201 if (frame->AddrStack.Mode != AddrModeFlat) context->SegSs = frame->AddrStack.Segment;
202 context->Esp = frame->AddrStack.Offset;
203 }
204 #endif
205 if (curr_mode == stm_start)
206 {
207 THREAD_BASIC_INFORMATION info;
208
209 if ((frame->AddrPC.Mode == AddrModeFlat) &&
210 (frame->AddrFrame.Mode != AddrModeFlat))
211 {
212 WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
213 goto done_err;
214 }
215
216 /* Init done */
217 set_curr_mode((frame->AddrPC.Mode == AddrModeFlat) ? stm_32bit : stm_16bit);
218
219 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
220 * address space
221 */
222 if (NtQueryInformationThread(csw->hThread, ThreadBasicInformation, &info,
223 sizeof(info), NULL) == STATUS_SUCCESS)
224 {
225 curr_switch = (DWORD_PTR)info.TebBaseAddress + FIELD_OFFSET(TEB, WOW32Reserved);
226 if (!sw_read_mem(csw, curr_switch, &p, sizeof(p)))
227 {
228 WARN("Can't read TEB:WOW32Reserved\n");
229 goto done_err;
230 }
231 next_switch = p;
232 if (!next_switch) /* no 16-bit stack */
233 {
234 curr_switch = 0;
235 }
236 else if (curr_mode == stm_16bit)
237 {
238 if (!sw_read_mem(csw, next_switch, &frame32, sizeof(frame32)))
239 {
240 WARN("Bad stack frame %p\n", (void*)(DWORD_PTR)next_switch);
241 goto done_err;
242 }
243 curr_switch = (DWORD)frame32.frame16;
244 tmp.Mode = AddrMode1616;
245 tmp.Segment = SELECTOROF(curr_switch);
246 tmp.Offset = OFFSETOF(curr_switch);
247 if (!sw_read_mem(csw, sw_xlat_addr(csw, &tmp), &ch, sizeof(ch)))
248 curr_switch = 0xFFFFFFFF;
249 }
250 else
251 {
252 tmp.Mode = AddrMode1616;
253 tmp.Segment = SELECTOROF(next_switch);
254 tmp.Offset = OFFSETOF(next_switch);
255 p = sw_xlat_addr(csw, &tmp);
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 curr_switch = (DWORD_PTR)frame16.frame32;
262 if (!sw_read_mem(csw, curr_switch, &ch, sizeof(ch)))
263 curr_switch = 0xFFFFFFFF;
264 }
265 }
266 else
267 /* FIXME: this will allow to work when we're not attached to a live target,
268 * but the 16 <=> 32 switch facility won't be available.
269 */
270 curr_switch = 0;
271 frame->AddrReturn.Mode = frame->AddrStack.Mode = (curr_mode == stm_16bit) ? AddrMode1616 : AddrModeFlat;
272 /* don't set up AddrStack on first call. Either the caller has set it up, or
273 * we will get it in the next frame
274 */
275 memset(&frame->AddrBStore, 0, sizeof(frame->AddrBStore));
276 }
277 else
278 {
279 if (frame->AddrFrame.Mode == AddrModeFlat)
280 {
281 assert(curr_mode == stm_32bit);
282 do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
283 }
284 else
285 {
286 assert(curr_mode == stm_16bit);
287 do_switch = curr_switch &&
288 frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
289 frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
290 }
291
292 if (do_switch)
293 {
294 if (curr_mode == stm_16bit)
295 {
296 if (!sw_read_mem(csw, next_switch, &frame32, sizeof(frame32)))
297 {
298 WARN("Bad stack frame %p\n", (void*)(DWORD_PTR)next_switch);
299 goto done_err;
300 }
301
302 frame->AddrPC.Mode = AddrModeFlat;
303 frame->AddrPC.Segment = 0;
304 frame->AddrPC.Offset = frame32.retaddr;
305 frame->AddrFrame.Mode = AddrModeFlat;
306 frame->AddrFrame.Segment = 0;
307 frame->AddrFrame.Offset = frame32.ebp;
308
309 frame->AddrStack.Mode = AddrModeFlat;
310 frame->AddrStack.Segment = 0;
311 frame->AddrReturn.Mode = AddrModeFlat;
312 frame->AddrReturn.Segment = 0;
313
314 next_switch = curr_switch;
315 tmp.Mode = AddrMode1616;
316 tmp.Segment = SELECTOROF(next_switch);
317 tmp.Offset = OFFSETOF(next_switch);
318 p = sw_xlat_addr(csw, &tmp);
319
320 if (!sw_read_mem(csw, p, &frame16, sizeof(frame16)))
321 {
322 WARN("Bad stack frame 0x%08x\n", p);
323 goto done_err;
324 }
325 curr_switch = (DWORD_PTR)frame16.frame32;
326 set_curr_mode(stm_32bit);
327 if (!sw_read_mem(csw, curr_switch, &ch, sizeof(ch)))
328 curr_switch = 0;
329 }
330 else
331 {
332 tmp.Mode = AddrMode1616;
333 tmp.Segment = SELECTOROF(next_switch);
334 tmp.Offset = OFFSETOF(next_switch);
335 p = sw_xlat_addr(csw, &tmp);
336
337 if (!sw_read_mem(csw, p, &frame16, sizeof(frame16)))
338 {
339 WARN("Bad stack frame 0x%08x\n", p);
340 goto done_err;
341 }
342
343 TRACE("Got a 16 bit stack switch:"
344 "\n\tframe32: %p"
345 "\n\tedx:%08x ecx:%08x ebp:%08x"
346 "\n\tds:%04x es:%04x fs:%04x gs:%04x"
347 "\n\tcall_from_ip:%08x module_cs:%04x relay=%08x"
348 "\n\tentry_ip:%04x entry_point:%08x"
349 "\n\tbp:%04x ip:%04x cs:%04x\n",
350 frame16.frame32,
351 frame16.edx, frame16.ecx, frame16.ebp,
352 frame16.ds, frame16.es, frame16.fs, frame16.gs,
353 frame16.callfrom_ip, frame16.module_cs, frame16.relay,
354 frame16.entry_ip, frame16.entry_point,
355 frame16.bp, frame16.ip, frame16.cs);
356
357 frame->AddrPC.Mode = AddrMode1616;
358 frame->AddrPC.Segment = frame16.cs;
359 frame->AddrPC.Offset = frame16.ip;
360
361 frame->AddrFrame.Mode = AddrMode1616;
362 frame->AddrFrame.Segment = SELECTOROF(next_switch);
363 frame->AddrFrame.Offset = frame16.bp;
364
365 frame->AddrStack.Mode = AddrMode1616;
366 frame->AddrStack.Segment = SELECTOROF(next_switch);
367
368 frame->AddrReturn.Mode = AddrMode1616;
369 frame->AddrReturn.Segment = frame16.cs;
370
371 next_switch = curr_switch;
372 if (!sw_read_mem(csw, next_switch, &frame32, sizeof(frame32)))
373 {
374 WARN("Bad stack frame %p\n", (void*)(DWORD_PTR)next_switch);
375 goto done_err;
376 }
377 curr_switch = (DWORD)frame32.frame16;
378 tmp.Mode = AddrMode1616;
379 tmp.Segment = SELECTOROF(curr_switch);
380 tmp.Offset = OFFSETOF(curr_switch);
381
382 if (!sw_read_mem(csw, sw_xlat_addr(csw, &tmp), &ch, sizeof(ch)))
383 curr_switch = 0;
384 set_curr_mode(stm_16bit);
385 }
386 }
387 else
388 {
389 if (curr_mode == stm_16bit)
390 {
391 frame->AddrPC = frame->AddrReturn;
392 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(WORD);
393 /* "pop up" previous BP value */
394 if (!frame->AddrFrame.Offset ||
395 !sw_read_mem(csw, sw_xlat_addr(csw, &frame->AddrFrame),
396 &val16, sizeof(WORD)))
397 goto done_err;
398 frame->AddrFrame.Offset = val16;
399 }
400 else
401 {
402 #ifdef __i386__
403 if (!fetch_next_frame32(csw, context, sw_xlat_addr(csw, &frame->AddrPC) - deltapc))
404 goto done_err;
405
406 frame->AddrStack.Mode = frame->AddrFrame.Mode = frame->AddrPC.Mode = AddrModeFlat;
407 frame->AddrStack.Offset = context->Esp;
408 frame->AddrFrame.Offset = context->Ebp;
409 if (frame->AddrReturn.Offset != context->Eip)
410 FIXME("new PC=%s different from Eip=%x\n",
411 wine_dbgstr_longlong(frame->AddrReturn.Offset), context->Eip);
412 frame->AddrPC.Offset = context->Eip;
413 #endif
414 }
415 }
416 }
417
418 if (curr_mode == stm_16bit)
419 {
420 unsigned int i;
421
422 p = sw_xlat_addr(csw, &frame->AddrFrame);
423 if (!sw_read_mem(csw, p + sizeof(WORD), &val16, sizeof(WORD)))
424 goto done_err;
425 frame->AddrReturn.Offset = val16;
426 /* get potential cs if a far call was used */
427 if (!sw_read_mem(csw, p + 2 * sizeof(WORD), &val16, sizeof(WORD)))
428 goto done_err;
429 if (frame->AddrFrame.Offset & 1)
430 frame->AddrReturn.Segment = val16; /* far call assumed */
431 else
432 {
433 /* not explicitly marked as far call,
434 * but check whether it could be anyway
435 */
436 if ((val16 & 7) == 7 && val16 != frame->AddrReturn.Segment)
437 {
438 LDT_ENTRY le;
439
440 if (GetThreadSelectorEntry(csw->hThread, val16, &le) &&
441 (le.HighWord.Bits.Type & 0x08)) /* code segment */
442 {
443 /* it is very uncommon to push a code segment cs as
444 * a parameter, so this should work in most cases
445 */
446 frame->AddrReturn.Segment = val16;
447 }
448 }
449 }
450 frame->AddrFrame.Offset &= ~1;
451 /* we "pop" parameters as 16 bit entities... of course, this won't
452 * work if the parameter is in fact bigger than 16bit, but
453 * there's no way to know that here
454 */
455 for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
456 {
457 sw_read_mem(csw, p + (2 + i) * sizeof(WORD), &val16, sizeof(val16));
458 frame->Params[i] = val16;
459 }
460 #ifdef __i386__
461 if (context)
462 {
463 #define SET(field, seg, reg) \
464 switch (frame->field.Mode) \
465 { \
466 case AddrModeFlat: context->reg = frame->field.Offset; break; \
467 case AddrMode1616: context->seg = frame->field.Segment; context->reg = frame->field.Offset; break; \
468 default: assert(0); \
469 }
470 SET(AddrStack, SegSs, Esp);
471 SET(AddrFrame, SegSs, Ebp);
472 SET(AddrReturn, SegCs, Eip);
473 #undef SET
474 }
475 #endif
476 }
477 else
478 {
479 unsigned int i;
480 #ifdef __i386__
481 CONTEXT newctx = *context;
482
483 if (!fetch_next_frame32(csw, &newctx, frame->AddrPC.Offset - deltapc))
484 goto done_err;
485 frame->AddrReturn.Mode = AddrModeFlat;
486 frame->AddrReturn.Offset = newctx.Eip;
487 #endif
488 for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
489 {
490 sw_read_mem(csw, frame->AddrFrame.Offset + (2 + i) * sizeof(DWORD), &val32, sizeof(val32));
491 frame->Params[i] = val32;
492 }
493 }
494
495 frame->Far = TRUE;
496 frame->Virtual = TRUE;
497 p = sw_xlat_addr(csw, &frame->AddrPC);
498 if (p && sw_module_base(csw, p))
499 frame->FuncTableEntry = sw_table_access(csw, p);
500 else
501 frame->FuncTableEntry = NULL;
502
503 inc_curr_count();
504 TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s Count=%s cSwitch=%p nSwitch=%p FuncTable=%p\n",
505 wine_dbgstr_addr(&frame->AddrPC),
506 wine_dbgstr_addr(&frame->AddrFrame),
507 wine_dbgstr_addr(&frame->AddrReturn),
508 wine_dbgstr_addr(&frame->AddrStack),
509 curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
510 wine_dbgstr_longlong(curr_count),
511 (void*)(DWORD_PTR)curr_switch, (void*)(DWORD_PTR)next_switch, frame->FuncTableEntry);
512
513 return TRUE;
514 done_err:
515 set_curr_mode(stm_done);
516 return FALSE;
517 }
518 #endif /* DBGHELP_STATIC_LIB */
519
520 static unsigned i386_map_dwarf_register(unsigned regno)
521 {
522 unsigned reg;
523
524 switch (regno)
525 {
526 case 0: reg = CV_REG_EAX; break;
527 case 1: reg = CV_REG_ECX; break;
528 case 2: reg = CV_REG_EDX; break;
529 case 3: reg = CV_REG_EBX; break;
530 case 4: reg = CV_REG_ESP; break;
531 case 5: reg = CV_REG_EBP; break;
532 case 6: reg = CV_REG_ESI; break;
533 case 7: reg = CV_REG_EDI; break;
534 case 8: reg = CV_REG_EIP; break;
535 case 9: reg = CV_REG_EFLAGS; break;
536 case 10: reg = CV_REG_CS; break;
537 case 11: reg = CV_REG_SS; break;
538 case 12: reg = CV_REG_DS; break;
539 case 13: reg = CV_REG_ES; break;
540 case 14: reg = CV_REG_FS; break;
541 case 15: reg = CV_REG_GS; break;
542 case 16: case 17: case 18: case 19:
543 case 20: case 21: case 22: case 23:
544 reg = CV_REG_ST0 + regno - 16; break;
545 case 24: reg = CV_REG_CTRL; break;
546 case 25: reg = CV_REG_STAT; break;
547 case 26: reg = CV_REG_TAG; break;
548 case 27: reg = CV_REG_FPCS; break;
549 case 28: reg = CV_REG_FPIP; break;
550 case 29: reg = CV_REG_FPDS; break;
551 case 30: reg = CV_REG_FPDO; break;
552 /*
553 reg: fop 31
554 */
555 case 32: case 33: case 34: case 35:
556 case 36: case 37: case 38: case 39:
557 reg = CV_REG_XMM0 + regno - 32; break;
558 case 40: reg = CV_REG_MXCSR; break;
559 default:
560 FIXME("Don't know how to map register %d\n", regno);
561 return 0;
562 }
563 return reg;
564 }
565
566 static void* i386_fetch_context_reg(CONTEXT* ctx, unsigned regno, unsigned* size)
567 {
568 #ifdef __i386__
569 switch (regno)
570 {
571 case CV_REG_EAX: *size = sizeof(ctx->Eax); return &ctx->Eax;
572 case CV_REG_EDX: *size = sizeof(ctx->Edx); return &ctx->Edx;
573 case CV_REG_ECX: *size = sizeof(ctx->Ecx); return &ctx->Ecx;
574 case CV_REG_EBX: *size = sizeof(ctx->Ebx); return &ctx->Ebx;
575 case CV_REG_ESI: *size = sizeof(ctx->Esi); return &ctx->Esi;
576 case CV_REG_EDI: *size = sizeof(ctx->Edi); return &ctx->Edi;
577 case CV_REG_EBP: *size = sizeof(ctx->Ebp); return &ctx->Ebp;
578 case CV_REG_ESP: *size = sizeof(ctx->Esp); return &ctx->Esp;
579 case CV_REG_EIP: *size = sizeof(ctx->Eip); return &ctx->Eip;
580
581 /* These are x87 floating point registers... They do not match a C type in
582 * the Linux ABI, so hardcode their 80-bitness. */
583 case CV_REG_ST0 + 0: *size = 10; return &ctx->FloatSave.RegisterArea[0*10];
584 case CV_REG_ST0 + 1: *size = 10; return &ctx->FloatSave.RegisterArea[1*10];
585 case CV_REG_ST0 + 2: *size = 10; return &ctx->FloatSave.RegisterArea[2*10];
586 case CV_REG_ST0 + 3: *size = 10; return &ctx->FloatSave.RegisterArea[3*10];
587 case CV_REG_ST0 + 4: *size = 10; return &ctx->FloatSave.RegisterArea[4*10];
588 case CV_REG_ST0 + 5: *size = 10; return &ctx->FloatSave.RegisterArea[5*10];
589 case CV_REG_ST0 + 6: *size = 10; return &ctx->FloatSave.RegisterArea[6*10];
590 case CV_REG_ST0 + 7: *size = 10; return &ctx->FloatSave.RegisterArea[7*10];
591
592 case CV_REG_CTRL: *size = sizeof(DWORD); return &ctx->FloatSave.ControlWord;
593 case CV_REG_STAT: *size = sizeof(DWORD); return &ctx->FloatSave.StatusWord;
594 case CV_REG_TAG: *size = sizeof(DWORD); return &ctx->FloatSave.TagWord;
595 case CV_REG_FPCS: *size = sizeof(DWORD); return &ctx->FloatSave.ErrorSelector;
596 case CV_REG_FPIP: *size = sizeof(DWORD); return &ctx->FloatSave.ErrorOffset;
597 case CV_REG_FPDS: *size = sizeof(DWORD); return &ctx->FloatSave.DataSelector;
598 case CV_REG_FPDO: *size = sizeof(DWORD); return &ctx->FloatSave.DataOffset;
599
600 case CV_REG_EFLAGS: *size = sizeof(ctx->EFlags); return &ctx->EFlags;
601 case CV_REG_ES: *size = sizeof(ctx->SegEs); return &ctx->SegEs;
602 case CV_REG_CS: *size = sizeof(ctx->SegCs); return &ctx->SegCs;
603 case CV_REG_SS: *size = sizeof(ctx->SegSs); return &ctx->SegSs;
604 case CV_REG_DS: *size = sizeof(ctx->SegDs); return &ctx->SegDs;
605 case CV_REG_FS: *size = sizeof(ctx->SegFs); return &ctx->SegFs;
606 case CV_REG_GS: *size = sizeof(ctx->SegGs); return &ctx->SegGs;
607
608 }
609 #endif
610 FIXME("Unknown register %x\n", regno);
611 return NULL;
612 }
613
614 static const char* i386_fetch_regname(unsigned regno)
615 {
616 switch (regno)
617 {
618 case CV_REG_EAX: return "eax";
619 case CV_REG_EDX: return "edx";
620 case CV_REG_ECX: return "ecx";
621 case CV_REG_EBX: return "ebx";
622 case CV_REG_ESI: return "esi";
623 case CV_REG_EDI: return "edi";
624 case CV_REG_EBP: return "ebp";
625 case CV_REG_ESP: return "esp";
626 case CV_REG_EIP: return "eip";
627
628 case CV_REG_ST0 + 0: return "st0";
629 case CV_REG_ST0 + 1: return "st1";
630 case CV_REG_ST0 + 2: return "st2";
631 case CV_REG_ST0 + 3: return "st3";
632 case CV_REG_ST0 + 4: return "st4";
633 case CV_REG_ST0 + 5: return "st5";
634 case CV_REG_ST0 + 6: return "st6";
635 case CV_REG_ST0 + 7: return "st7";
636
637 case CV_REG_EFLAGS: return "eflags";
638 case CV_REG_ES: return "es";
639 case CV_REG_CS: return "cs";
640 case CV_REG_SS: return "ss";
641 case CV_REG_DS: return "ds";
642 case CV_REG_FS: return "fs";
643 case CV_REG_GS: return "gs";
644
645 case CV_REG_CTRL: return "fpControl";
646 case CV_REG_STAT: return "fpStatus";
647 case CV_REG_TAG: return "fpTag";
648 case CV_REG_FPCS: return "fpCS";
649 case CV_REG_FPIP: return "fpIP";
650 case CV_REG_FPDS: return "fpDS";
651 case CV_REG_FPDO: return "fpData";
652
653 case CV_REG_XMM0 + 0: return "xmm0";
654 case CV_REG_XMM0 + 1: return "xmm1";
655 case CV_REG_XMM0 + 2: return "xmm2";
656 case CV_REG_XMM0 + 3: return "xmm3";
657 case CV_REG_XMM0 + 4: return "xmm4";
658 case CV_REG_XMM0 + 5: return "xmm5";
659 case CV_REG_XMM0 + 6: return "xmm6";
660 case CV_REG_XMM0 + 7: return "xmm7";
661
662 case CV_REG_MXCSR: return "MxCSR";
663 }
664 FIXME("Unknown register %x\n", regno);
665 return NULL;
666 }
667
668 #ifndef DBGHELP_STATIC_LIB
669 static BOOL i386_fetch_minidump_thread(struct dump_context* dc, unsigned index, unsigned flags, const CONTEXT* ctx)
670 {
671 if (ctx->ContextFlags && (flags & ThreadWriteInstructionWindow))
672 {
673 /* FIXME: crop values across module boundaries, */
674 #ifdef __i386__
675 ULONG base = ctx->Eip <= 0x80 ? 0 : ctx->Eip - 0x80;
676 minidump_add_memory_block(dc, base, ctx->Eip + 0x80 - base, 0);
677 #endif
678 }
679
680 return TRUE;
681 }
682 #endif
683
684 static BOOL i386_fetch_minidump_module(struct dump_context* dc, unsigned index, unsigned flags)
685 {
686 /* FIXME: actually, we should probably take care of FPO data, unless it's stored in
687 * function table minidump stream
688 */
689 return FALSE;
690 }
691
692 DECLSPEC_HIDDEN struct cpu cpu_i386 = {
693 IMAGE_FILE_MACHINE_I386,
694 4,
695 CV_REG_EBP,
696 #ifndef DBGHELP_STATIC_LIB
697 i386_get_addr,
698 i386_stack_walk,
699 #else
700 NULL,
701 NULL,
702 #endif
703 NULL,
704 i386_map_dwarf_register,
705 i386_fetch_context_reg,
706 i386_fetch_regname,
707 #ifndef DBGHELP_STATIC_LIB
708 i386_fetch_minidump_thread,
709 i386_fetch_minidump_module,
710 #else
711 NULL,
712 NULL,
713 #endif
714 };