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