Sync with trunk (r48414)
[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, CONTEXT* context)
102 {
103 STACK32FRAME frame32;
104 STACK16FRAME frame16;
105 char ch;
106 ADDRESS64 tmp;
107 DWORD p;
108 WORD val;
109 BOOL do_switch;
110 unsigned deltapc = 1;
111
112 /* sanity check */
113 if (curr_mode >= stm_done) return FALSE;
114
115 TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%p nSwitch=%p\n",
116 wine_dbgstr_addr(&frame->AddrPC),
117 wine_dbgstr_addr(&frame->AddrFrame),
118 wine_dbgstr_addr(&frame->AddrReturn),
119 wine_dbgstr_addr(&frame->AddrStack),
120 curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
121 (void*)(DWORD_PTR)curr_switch, (void*)(DWORD_PTR)next_switch);
122
123 if (curr_mode == stm_start)
124 {
125 THREAD_BASIC_INFORMATION info;
126
127 if ((frame->AddrPC.Mode == AddrModeFlat) &&
128 (frame->AddrFrame.Mode != AddrModeFlat))
129 {
130 WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
131 goto done_err;
132 }
133
134 /* Init done */
135 curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ? stm_32bit : stm_16bit;
136 deltapc = 0;
137
138 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
139 * address space
140 */
141 if (NtQueryInformationThread(csw->hThread, ThreadBasicInformation, &info,
142 sizeof(info), NULL) == STATUS_SUCCESS)
143 {
144 curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, WOW32Reserved);
145 if (!sw_read_mem(csw, curr_switch, &p, sizeof(p)))
146 {
147 WARN("Can't read TEB:WOW32Reserved\n");
148 goto done_err;
149 }
150 next_switch = p;
151 if (!next_switch) /* no 16-bit stack */
152 {
153 curr_switch = 0;
154 }
155 else if (curr_mode == stm_16bit)
156 {
157 if (!sw_read_mem(csw, next_switch, &frame32, sizeof(frame32)))
158 {
159 WARN("Bad stack frame %p\n", (void*)(DWORD_PTR)next_switch);
160 goto done_err;
161 }
162 curr_switch = (DWORD)frame32.frame16;
163 tmp.Mode = AddrMode1616;
164 tmp.Segment = SELECTOROF(curr_switch);
165 tmp.Offset = OFFSETOF(curr_switch);
166 if (!sw_read_mem(csw, sw_xlat_addr(csw, &tmp), &ch, sizeof(ch)))
167 curr_switch = 0xFFFFFFFF;
168 }
169 else
170 {
171 tmp.Mode = AddrMode1616;
172 tmp.Segment = SELECTOROF(next_switch);
173 tmp.Offset = OFFSETOF(next_switch);
174 p = sw_xlat_addr(csw, &tmp);
175 if (!sw_read_mem(csw, p, &frame16, sizeof(frame16)))
176 {
177 WARN("Bad stack frame 0x%08x\n", p);
178 goto done_err;
179 }
180 curr_switch = (DWORD_PTR)frame16.frame32;
181
182 if (!sw_read_mem(csw, curr_switch, &ch, sizeof(ch)))
183 curr_switch = 0xFFFFFFFF;
184 }
185 }
186 else
187 /* FIXME: this will allow to work when we're not attached to a live target,
188 * but the 16 <=> 32 switch facility won't be available.
189 */
190 curr_switch = 0;
191 frame->AddrReturn.Mode = frame->AddrStack.Mode = (curr_mode == stm_16bit) ? AddrMode1616 : AddrModeFlat;
192 /* don't set up AddrStack on first call. Either the caller has set it up, or
193 * we will get it in the next frame
194 */
195 memset(&frame->AddrBStore, 0, sizeof(frame->AddrBStore));
196 #ifdef __i386__
197 if (curr_mode == stm_32bit)
198 {
199 DWORD_PTR xframe;
200
201 if (dwarf2_virtual_unwind(csw, frame->AddrPC.Offset - deltapc, context, &xframe))
202 {
203 frame->AddrStack.Mode = frame->AddrFrame.Mode = frame->AddrReturn.Mode = AddrModeFlat;
204 frame->AddrStack.Offset = context->Esp = xframe;
205 frame->AddrFrame.Offset = context->Ebp;
206 frame->AddrReturn.Offset = context->Eip;
207 goto done_pep;
208 }
209 }
210 #endif
211 }
212 else
213 {
214 if (frame->AddrFrame.Offset == 0) goto done_err;
215 if (frame->AddrFrame.Mode == AddrModeFlat)
216 {
217 assert(curr_mode == stm_32bit);
218 do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
219 }
220 else
221 {
222 assert(curr_mode == stm_16bit);
223 do_switch = curr_switch &&
224 frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
225 frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
226 }
227
228 if (do_switch)
229 {
230 if (curr_mode == stm_16bit)
231 {
232 if (!sw_read_mem(csw, next_switch, &frame32, sizeof(frame32)))
233 {
234 WARN("Bad stack frame %p\n", (void*)(DWORD_PTR)next_switch);
235 goto done_err;
236 }
237
238 frame->AddrPC.Mode = AddrModeFlat;
239 frame->AddrPC.Segment = 0;
240 frame->AddrPC.Offset = frame32.retaddr;
241 frame->AddrFrame.Mode = AddrModeFlat;
242 frame->AddrFrame.Segment = 0;
243 frame->AddrFrame.Offset = frame32.ebp;
244
245 frame->AddrStack.Mode = AddrModeFlat;
246 frame->AddrStack.Segment = 0;
247 frame->AddrReturn.Mode = AddrModeFlat;
248 frame->AddrReturn.Segment = 0;
249
250 next_switch = curr_switch;
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 curr_switch = (DWORD_PTR)frame16.frame32;
262 curr_mode = stm_32bit;
263 if (!sw_read_mem(csw, curr_switch, &ch, sizeof(ch)))
264 curr_switch = 0;
265 }
266 else
267 {
268 tmp.Mode = AddrMode1616;
269 tmp.Segment = SELECTOROF(next_switch);
270 tmp.Offset = OFFSETOF(next_switch);
271 p = sw_xlat_addr(csw, &tmp);
272
273 if (!sw_read_mem(csw, p, &frame16, sizeof(frame16)))
274 {
275 WARN("Bad stack frame 0x%08x\n", p);
276 goto done_err;
277 }
278
279 TRACE("Got a 16 bit stack switch:"
280 "\n\tframe32: %08lx"
281 "\n\tedx:%08x ecx:%08x ebp:%08x"
282 "\n\tds:%04x es:%04x fs:%04x gs:%04x"
283 "\n\tcall_from_ip:%08x module_cs:%04x relay=%08x"
284 "\n\tentry_ip:%04x entry_point:%08x"
285 "\n\tbp:%04x ip:%04x cs:%04x\n",
286 (unsigned long)frame16.frame32,
287 frame16.edx, frame16.ecx, frame16.ebp,
288 frame16.ds, frame16.es, frame16.fs, frame16.gs,
289 frame16.callfrom_ip, frame16.module_cs, frame16.relay,
290 frame16.entry_ip, frame16.entry_point,
291 frame16.bp, frame16.ip, frame16.cs);
292
293 frame->AddrPC.Mode = AddrMode1616;
294 frame->AddrPC.Segment = frame16.cs;
295 frame->AddrPC.Offset = frame16.ip;
296
297 frame->AddrFrame.Mode = AddrMode1616;
298 frame->AddrFrame.Segment = SELECTOROF(next_switch);
299 frame->AddrFrame.Offset = frame16.bp;
300
301 frame->AddrStack.Mode = AddrMode1616;
302 frame->AddrStack.Segment = SELECTOROF(next_switch);
303
304 frame->AddrReturn.Mode = AddrMode1616;
305 frame->AddrReturn.Segment = frame16.cs;
306
307 next_switch = curr_switch;
308 if (!sw_read_mem(csw, next_switch, &frame32, sizeof(frame32)))
309 {
310 WARN("Bad stack frame %p\n", (void*)(DWORD_PTR)next_switch);
311 goto done_err;
312 }
313 curr_switch = (DWORD)frame32.frame16;
314 tmp.Mode = AddrMode1616;
315 tmp.Segment = SELECTOROF(curr_switch);
316 tmp.Offset = OFFSETOF(curr_switch);
317
318 if (!sw_read_mem(csw, sw_xlat_addr(csw, &tmp), &ch, sizeof(ch)))
319 curr_switch = 0;
320 curr_mode = stm_16bit;
321 }
322 }
323 else
324 {
325 frame->AddrPC = frame->AddrReturn;
326 if (curr_mode == stm_16bit)
327 {
328 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(WORD);
329 /* "pop up" previous BP value */
330 if (!sw_read_mem(csw, sw_xlat_addr(csw, &frame->AddrFrame),
331 &val, sizeof(WORD)))
332 goto done_err;
333 frame->AddrFrame.Offset = val;
334 }
335 else
336 {
337 #ifdef __i386__
338 DWORD_PTR xframe;
339
340 if (dwarf2_virtual_unwind(csw, frame->AddrPC.Offset - deltapc, context, &xframe))
341 {
342 frame->AddrStack.Mode = frame->AddrFrame.Mode = frame->AddrReturn.Mode = AddrModeFlat;
343 frame->AddrStack.Offset = context->Esp = xframe;
344 frame->AddrFrame.Offset = context->Ebp;
345 frame->AddrReturn.Offset = context->Eip;
346 goto done_pep;
347 }
348 #endif
349 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(DWORD);
350 /* "pop up" previous EBP value */
351 if (!sw_read_mem(csw, frame->AddrFrame.Offset,
352 &frame->AddrFrame.Offset, sizeof(DWORD)))
353 goto done_err;
354 }
355 }
356 }
357
358 if (curr_mode == stm_16bit)
359 {
360 unsigned int i;
361
362 p = sw_xlat_addr(csw, &frame->AddrFrame);
363 if (!sw_read_mem(csw, p + sizeof(WORD), &val, sizeof(WORD)))
364 goto done_err;
365 frame->AddrReturn.Offset = val;
366 /* get potential cs if a far call was used */
367 if (!sw_read_mem(csw, p + 2 * sizeof(WORD), &val, sizeof(WORD)))
368 goto done_err;
369 if (frame->AddrFrame.Offset & 1)
370 frame->AddrReturn.Segment = val; /* far call assumed */
371 else
372 {
373 /* not explicitly marked as far call,
374 * but check whether it could be anyway
375 */
376 if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
377 {
378 LDT_ENTRY le;
379
380 if (GetThreadSelectorEntry(csw->hThread, val, &le) &&
381 (le.HighWord.Bits.Type & 0x08)) /* code segment */
382 {
383 /* it is very uncommon to push a code segment cs as
384 * a parameter, so this should work in most cases
385 */
386 frame->AddrReturn.Segment = val;
387 }
388 }
389 }
390 frame->AddrFrame.Offset &= ~1;
391 /* we "pop" parameters as 16 bit entities... of course, this won't
392 * work if the parameter is in fact bigger than 16bit, but
393 * there's no way to know that here
394 */
395 for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
396 {
397 sw_read_mem(csw, p + (2 + i) * sizeof(WORD), &val, sizeof(val));
398 frame->Params[i] = val;
399 }
400 }
401 else
402 {
403 if (!sw_read_mem(csw, frame->AddrFrame.Offset + sizeof(DWORD),
404 &frame->AddrReturn.Offset, sizeof(DWORD)))
405 {
406 WARN("Cannot read new frame offset %p\n",
407 (void*)(DWORD_PTR)(frame->AddrFrame.Offset + (int)sizeof(DWORD)));
408 goto done_err;
409 }
410 sw_read_mem(csw, frame->AddrFrame.Offset + 2 * sizeof(DWORD),
411 frame->Params, sizeof(frame->Params));
412 }
413 #ifdef __i386__
414 if (context)
415 {
416 #define SET(field, seg, reg) \
417 switch (frame->field.Mode) \
418 { \
419 case AddrModeFlat: context->reg = frame->field.Offset; break; \
420 case AddrMode1616: context->seg = frame->field.Segment; context->reg = frame->field.Offset; break; \
421 default: assert(0); \
422 }
423 SET(AddrStack, SegSs, Esp);
424 SET(AddrFrame, SegSs, Ebp);
425 SET(AddrReturn, SegCs, Eip);
426 #undef SET
427 }
428 done_pep:
429 #endif
430
431 frame->Far = TRUE;
432 frame->Virtual = TRUE;
433 p = sw_xlat_addr(csw, &frame->AddrPC);
434 if (p && sw_module_base(csw, p))
435 frame->FuncTableEntry = sw_table_access(csw, p);
436 else
437 frame->FuncTableEntry = NULL;
438
439 TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%p nSwitch=%p FuncTable=%p\n",
440 wine_dbgstr_addr(&frame->AddrPC),
441 wine_dbgstr_addr(&frame->AddrFrame),
442 wine_dbgstr_addr(&frame->AddrReturn),
443 wine_dbgstr_addr(&frame->AddrStack),
444 curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
445 (void*)(DWORD_PTR)curr_switch, (void*)(DWORD_PTR)next_switch, frame->FuncTableEntry);
446
447 return TRUE;
448 done_err:
449 curr_mode = stm_done;
450 return FALSE;
451 }
452
453 static unsigned i386_map_dwarf_register(unsigned regno)
454 {
455 unsigned reg;
456
457 switch (regno)
458 {
459 case 0: reg = CV_REG_EAX; break;
460 case 1: reg = CV_REG_ECX; break;
461 case 2: reg = CV_REG_EDX; break;
462 case 3: reg = CV_REG_EBX; break;
463 case 4: reg = CV_REG_ESP; break;
464 case 5: reg = CV_REG_EBP; break;
465 case 6: reg = CV_REG_ESI; break;
466 case 7: reg = CV_REG_EDI; break;
467 case 8: reg = CV_REG_EIP; break;
468 case 9: reg = CV_REG_EFLAGS; break;
469 case 10: reg = CV_REG_CS; break;
470 case 11: reg = CV_REG_SS; break;
471 case 12: reg = CV_REG_DS; break;
472 case 13: reg = CV_REG_ES; break;
473 case 14: reg = CV_REG_FS; break;
474 case 15: reg = CV_REG_GS; break;
475 case 16: case 17: case 18: case 19:
476 case 20: case 21: case 22: case 23:
477 reg = CV_REG_ST0 + regno - 16; break;
478 case 24: reg = CV_REG_CTRL; break;
479 case 25: reg = CV_REG_STAT; break;
480 case 26: reg = CV_REG_TAG; break;
481 /*
482 reg: fiseg 27
483 reg: fioff 28
484 reg: foseg 29
485 reg: fooff 30
486 reg: fop 31
487 */
488 case 32: case 33: case 34: case 35:
489 case 36: case 37: case 38: case 39:
490 reg = CV_REG_XMM0 + regno - 32; break;
491 case 40: reg = CV_REG_MXCSR; break;
492 default:
493 FIXME("Don't know how to map register %d\n", regno);
494 return 0;
495 }
496 return reg;
497 }
498
499 static void* i386_fetch_context_reg(CONTEXT* ctx, unsigned regno, unsigned* size)
500 {
501 #ifdef __i386__
502 switch (regno)
503 {
504 case CV_REG_EAX: *size = sizeof(ctx->Eax); return &ctx->Eax;
505 case CV_REG_EDX: *size = sizeof(ctx->Edx); return &ctx->Edx;
506 case CV_REG_ECX: *size = sizeof(ctx->Ecx); return &ctx->Ecx;
507 case CV_REG_EBX: *size = sizeof(ctx->Ebx); return &ctx->Ebx;
508 case CV_REG_ESI: *size = sizeof(ctx->Esi); return &ctx->Esi;
509 case CV_REG_EDI: *size = sizeof(ctx->Edi); return &ctx->Edi;
510 case CV_REG_EBP: *size = sizeof(ctx->Ebp); return &ctx->Ebp;
511 case CV_REG_ESP: *size = sizeof(ctx->Esp); return &ctx->Esp;
512 case CV_REG_EIP: *size = sizeof(ctx->Eip); return &ctx->Eip;
513
514 case CV_REG_ST0 + 0: *size = sizeof(long double); return &ctx->FloatSave.RegisterArea[0*sizeof(long double)];
515 case CV_REG_ST0 + 1: *size = sizeof(long double); return &ctx->FloatSave.RegisterArea[1*sizeof(long double)];
516 case CV_REG_ST0 + 2: *size = sizeof(long double); return &ctx->FloatSave.RegisterArea[2*sizeof(long double)];
517 case CV_REG_ST0 + 3: *size = sizeof(long double); return &ctx->FloatSave.RegisterArea[3*sizeof(long double)];
518 case CV_REG_ST0 + 4: *size = sizeof(long double); return &ctx->FloatSave.RegisterArea[4*sizeof(long double)];
519 case CV_REG_ST0 + 5: *size = sizeof(long double); return &ctx->FloatSave.RegisterArea[5*sizeof(long double)];
520 case CV_REG_ST0 + 6: *size = sizeof(long double); return &ctx->FloatSave.RegisterArea[6*sizeof(long double)];
521 case CV_REG_ST0 + 7: *size = sizeof(long double); return &ctx->FloatSave.RegisterArea[7*sizeof(long double)];
522
523 case CV_REG_EFLAGS: *size = sizeof(ctx->EFlags); return &ctx->EFlags;
524 case CV_REG_ES: *size = sizeof(ctx->SegEs); return &ctx->SegEs;
525 case CV_REG_CS: *size = sizeof(ctx->SegCs); return &ctx->SegCs;
526 case CV_REG_SS: *size = sizeof(ctx->SegSs); return &ctx->SegSs;
527 case CV_REG_DS: *size = sizeof(ctx->SegDs); return &ctx->SegDs;
528 case CV_REG_FS: *size = sizeof(ctx->SegFs); return &ctx->SegFs;
529 case CV_REG_GS: *size = sizeof(ctx->SegGs); return &ctx->SegGs;
530
531 }
532 #endif
533 FIXME("Unknown register %x\n", regno);
534 return NULL;
535 }
536
537 static const char* i386_fetch_regname(unsigned regno)
538 {
539 switch (regno)
540 {
541 case CV_REG_EAX: return "eax";
542 case CV_REG_EDX: return "edx";
543 case CV_REG_ECX: return "ecx";
544 case CV_REG_EBX: return "ebx";
545 case CV_REG_ESI: return "esi";
546 case CV_REG_EDI: return "edi";
547 case CV_REG_EBP: return "ebp";
548 case CV_REG_ESP: return "esp";
549 case CV_REG_EIP: return "eip";
550
551 case CV_REG_ST0 + 0: return "st0";
552 case CV_REG_ST0 + 1: return "st1";
553 case CV_REG_ST0 + 2: return "st2";
554 case CV_REG_ST0 + 3: return "st3";
555 case CV_REG_ST0 + 4: return "st4";
556 case CV_REG_ST0 + 5: return "st5";
557 case CV_REG_ST0 + 6: return "st6";
558 case CV_REG_ST0 + 7: return "st7";
559
560 case CV_REG_EFLAGS: return "eflags";
561 case CV_REG_ES: return "es";
562 case CV_REG_CS: return "cs";
563 case CV_REG_SS: return "ss";
564 case CV_REG_DS: return "ds";
565 case CV_REG_FS: return "fs";
566 case CV_REG_GS: return "gs";
567 }
568 FIXME("Unknown register %x\n", regno);
569 return NULL;
570 }
571
572 struct cpu cpu_i386 = {
573 IMAGE_FILE_MACHINE_I386,
574 4,
575 i386_get_addr,
576 i386_stack_walk,
577 NULL,
578 i386_map_dwarf_register,
579 i386_fetch_context_reg,
580 i386_fetch_regname,
581 };