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