Release 9.12.
[wine.git] / programs / winedbg / be_i386.c
blob1bac5f382b284a78111c704c339459cc67193574
1 /*
2 * Debugger i386 specific functions
4 * Copyright 2004 Eric Pouech
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.
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.
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
21 #include "debugger.h"
22 #include "wine/debug.h"
24 #if defined(__i386__) || defined(__x86_64__)
26 WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
28 #define STEP_FLAG 0x00000100 /* single step flag */
29 #define V86_FLAG 0x00020000
31 #define IS_VM86_MODE(ctx) (ctx->EFlags & V86_FLAG)
33 static const unsigned first_ldt_entry = 32;
35 static ADDRESS_MODE get_selector_type(HANDLE hThread, const WOW64_CONTEXT *ctx, WORD sel)
37 LDT_ENTRY le;
39 if (IS_VM86_MODE(ctx)) return AddrModeReal;
40 /* null or system selector */
41 if (!(sel & 4) || ((sel >> 3) < first_ldt_entry)) return AddrModeFlat;
42 if (dbg_curr_process->process_io->get_selector(hThread, sel, &le))
44 ULONG base = (le.HighWord.Bits.BaseHi << 24) + (le.HighWord.Bits.BaseMid << 16) + le.BaseLow;
45 if (le.HighWord.Bits.Default_Big)
46 return base == 0 ? AddrModeFlat : AddrMode1632;
47 return AddrMode1616;
49 /* selector doesn't exist */
50 return -1;
53 static void* be_i386_linearize(HANDLE hThread, const ADDRESS64* addr)
55 LDT_ENTRY le;
57 switch (addr->Mode)
59 case AddrModeReal:
60 return (void*)((DWORD_PTR)(LOWORD(addr->Segment) << 4) + (DWORD_PTR)addr->Offset);
61 case AddrMode1632:
62 if (!(addr->Segment & 4) || ((addr->Segment >> 3) < first_ldt_entry))
63 return (void*)(DWORD_PTR)addr->Offset;
64 /* fall through */
65 case AddrMode1616:
66 if (!dbg_curr_process->process_io->get_selector(hThread, addr->Segment, &le)) return NULL;
67 return (void*)((le.HighWord.Bits.BaseHi << 24) +
68 (le.HighWord.Bits.BaseMid << 16) + le.BaseLow +
69 (DWORD_PTR)addr->Offset);
70 case AddrModeFlat:
71 return (void*)(DWORD_PTR)addr->Offset;
73 return NULL;
76 static BOOL be_i386_build_addr(HANDLE hThread, const dbg_ctx_t *ctx, ADDRESS64* addr,
77 unsigned seg, DWORD64 offset)
79 addr->Mode = AddrModeFlat;
80 addr->Segment = seg;
81 addr->Offset = offset;
82 if (seg)
84 addr->Mode = get_selector_type(hThread, &ctx->x86, seg);
85 switch (addr->Mode)
87 case AddrModeReal:
88 case AddrMode1616:
89 addr->Offset &= 0xffff;
90 break;
91 case AddrModeFlat:
92 case AddrMode1632:
93 break;
94 default:
95 addr->Mode = -1;
96 return FALSE;
99 return TRUE;
102 static BOOL be_i386_get_addr(HANDLE hThread, const dbg_ctx_t *ctx,
103 enum be_cpu_addr bca, ADDRESS64* addr)
105 switch (bca)
107 case be_cpu_addr_pc:
108 return be_i386_build_addr(hThread, ctx, addr, ctx->x86.SegCs, ctx->x86.Eip);
109 case be_cpu_addr_stack:
110 return be_i386_build_addr(hThread, ctx, addr, ctx->x86.SegSs, ctx->x86.Esp);
111 case be_cpu_addr_frame:
112 return be_i386_build_addr(hThread, ctx, addr, ctx->x86.SegSs, ctx->x86.Ebp);
114 return FALSE;
117 static BOOL be_i386_get_register_info(int regno, enum be_cpu_addr* kind)
119 switch (regno)
121 case CV_REG_EIP: *kind = be_cpu_addr_pc; return TRUE;
122 case CV_REG_EBP: *kind = be_cpu_addr_frame; return TRUE;
123 case CV_REG_ESP: *kind = be_cpu_addr_stack; return TRUE;
125 return FALSE;
128 static void be_i386_single_step(dbg_ctx_t *ctx, BOOL enable)
130 if (enable) ctx->x86.EFlags |= STEP_FLAG;
131 else ctx->x86.EFlags &= ~STEP_FLAG;
134 static void be_i386_all_print_context(HANDLE hThread, const dbg_ctx_t *pctx)
136 static const char mxcsr_flags[16][4] = { "IE", "DE", "ZE", "OE", "UE", "PE", "DAZ", "IM",
137 "DM", "ZM", "OM", "UM", "PM", "R-", "R+", "FZ" };
138 const WOW64_CONTEXT *ctx = &pctx->x86;
139 XSAVE_FORMAT *xmm_area;
140 int cnt;
142 /* Break out the FPU state and the floating point registers */
143 dbg_printf("Floating Point Unit status:\n");
144 dbg_printf(" FLCW:%04x ", LOWORD(ctx->FloatSave.ControlWord));
145 dbg_printf(" FLTW:%04x ", LOWORD(ctx->FloatSave.TagWord));
146 dbg_printf(" FLEO:%08x ", (unsigned int) ctx->FloatSave.ErrorOffset);
147 dbg_printf(" FLSW:%04x", LOWORD(ctx->FloatSave.StatusWord));
149 /* Isolate the condition code bits - note they are not contiguous */
150 dbg_printf("(CC:%ld%ld%ld%ld", (ctx->FloatSave.StatusWord & 0x00004000) >> 14,
151 (ctx->FloatSave.StatusWord & 0x00000400) >> 10,
152 (ctx->FloatSave.StatusWord & 0x00000200) >> 9,
153 (ctx->FloatSave.StatusWord & 0x00000100) >> 8);
155 /* Now pull out the 3 bit of the TOP stack pointer */
156 dbg_printf(" TOP:%01x", (unsigned int) (ctx->FloatSave.StatusWord & 0x00003800) >> 11);
158 /* Lets analyse the error bits and indicate the status
159 * the Invalid Op flag has sub status which is tested as follows */
160 if (ctx->FloatSave.StatusWord & 0x00000001) { /* Invalid Fl OP */
161 if (ctx->FloatSave.StatusWord & 0x00000040) { /* Stack Fault */
162 if (ctx->FloatSave.StatusWord & 0x00000200) /* C1 says Overflow */
163 dbg_printf(" #IE(Stack Overflow)");
164 else
165 dbg_printf(" #IE(Stack Underflow)"); /* Underflow */
167 else dbg_printf(" #IE(Arthimetic error)"); /* Invalid Fl OP */
170 if (ctx->FloatSave.StatusWord & 0x00000002) dbg_printf(" #DE"); /* Denormalised OP */
171 if (ctx->FloatSave.StatusWord & 0x00000004) dbg_printf(" #ZE"); /* Zero Divide */
172 if (ctx->FloatSave.StatusWord & 0x00000008) dbg_printf(" #OE"); /* Overflow */
173 if (ctx->FloatSave.StatusWord & 0x00000010) dbg_printf(" #UE"); /* Underflow */
174 if (ctx->FloatSave.StatusWord & 0x00000020) dbg_printf(" #PE"); /* Precision error */
175 if (ctx->FloatSave.StatusWord & 0x00000040)
176 if (!(ctx->FloatSave.StatusWord & 0x00000001))
177 dbg_printf(" #SE"); /* Stack Fault (don't think this can occur) */
178 if (ctx->FloatSave.StatusWord & 0x00000080) dbg_printf(" #ES"); /* Error Summary */
179 if (ctx->FloatSave.StatusWord & 0x00008000) dbg_printf(" #FB"); /* FPU Busy */
180 dbg_printf(")\n");
182 /* Here are the rest of the registers */
183 dbg_printf(" FLES:%08lx FLDO:%08lx FLDS:%08lx FLCNS:%08lx\n",
184 ctx->FloatSave.ErrorSelector,
185 ctx->FloatSave.DataOffset,
186 ctx->FloatSave.DataSelector,
187 ctx->FloatSave.Cr0NpxState);
189 /* Now for the floating point registers */
190 dbg_printf("Floating Point Registers:\n");
191 for (cnt = 0; cnt < 8; cnt++)
193 const BYTE *p = &ctx->FloatSave.RegisterArea[cnt * 10];
194 if (cnt == 4) dbg_printf("\n");
195 dbg_printf(" ST%d:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x ", cnt,
196 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9] );
199 xmm_area = (XSAVE_FORMAT *) &ctx->ExtendedRegisters;
201 dbg_printf(" mxcsr: %04lx (", xmm_area->MxCsr );
202 for (cnt = 0; cnt < 16; cnt++)
203 if (xmm_area->MxCsr & (1 << cnt)) dbg_printf( " %s", mxcsr_flags[cnt] );
204 dbg_printf(" )\n");
206 for (cnt = 0; cnt < 8; cnt++)
208 dbg_printf( " xmm%u: uint=%08x%08x%08x%08x", cnt,
209 *((unsigned int *)&xmm_area->XmmRegisters[cnt] + 3),
210 *((unsigned int *)&xmm_area->XmmRegisters[cnt] + 2),
211 *((unsigned int *)&xmm_area->XmmRegisters[cnt] + 1),
212 *((unsigned int *)&xmm_area->XmmRegisters[cnt] + 0));
213 dbg_printf( " double={%g; %g}", *(double *)&xmm_area->XmmRegisters[cnt].Low,
214 *(double *)&xmm_area->XmmRegisters[cnt].High );
215 dbg_printf( " float={%g; %g; %g; %g}\n",
216 (double)*((float *)&xmm_area->XmmRegisters[cnt] + 0),
217 (double)*((float *)&xmm_area->XmmRegisters[cnt] + 1),
218 (double)*((float *)&xmm_area->XmmRegisters[cnt] + 2),
219 (double)*((float *)&xmm_area->XmmRegisters[cnt] + 3) );
221 dbg_printf("\n");
224 static void be_i386_print_context(HANDLE hThread, const dbg_ctx_t *pctx, int all_regs)
226 static const char flags[] = "aVR-N--ODITSZ-A-P-C";
227 const WOW64_CONTEXT *ctx = &pctx->x86;
228 int i;
229 char buf[33];
231 dbg_printf("Register dump:\n");
233 /* First get the segment registers out of the way */
234 dbg_printf(" CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x",
235 (WORD)ctx->SegCs, (WORD)ctx->SegSs,
236 (WORD)ctx->SegDs, (WORD)ctx->SegEs,
237 (WORD)ctx->SegFs, (WORD)ctx->SegGs);
239 strcpy(buf, flags);
240 for (i = 0; buf[i]; i++)
241 if (buf[i] != '-' && !(ctx->EFlags & (1 << (sizeof(flags) - 2 - i))))
242 buf[i] = ' ';
244 switch (get_selector_type(hThread, ctx, ctx->SegCs))
246 case AddrMode1616:
247 case AddrModeReal:
248 dbg_printf("\n IP:%04x SP:%04x BP:%04x FLAGS:%04x(%s)\n",
249 LOWORD(ctx->Eip), LOWORD(ctx->Esp),
250 LOWORD(ctx->Ebp), LOWORD(ctx->EFlags), buf);
251 dbg_printf(" AX:%04x BX:%04x CX:%04x DX:%04x SI:%04x DI:%04x\n",
252 LOWORD(ctx->Eax), LOWORD(ctx->Ebx),
253 LOWORD(ctx->Ecx), LOWORD(ctx->Edx),
254 LOWORD(ctx->Esi), LOWORD(ctx->Edi));
255 break;
256 case AddrModeFlat:
257 case AddrMode1632:
258 dbg_printf("\n EIP:%08lx ESP:%08lx EBP:%08lx EFLAGS:%08lx(%s)\n",
259 ctx->Eip, ctx->Esp, ctx->Ebp, ctx->EFlags, buf);
260 dbg_printf(" EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n",
261 ctx->Eax, ctx->Ebx, ctx->Ecx, ctx->Edx);
262 dbg_printf(" ESI:%08lx EDI:%08lx\n",
263 ctx->Esi, ctx->Edi);
264 break;
267 if (all_regs) be_i386_all_print_context(hThread, pctx);
271 static void be_i386_print_segment_info(HANDLE hThread, const dbg_ctx_t *ctx)
273 if (get_selector_type(hThread, &ctx->x86, ctx->x86.SegCs) == AddrMode1616)
275 info_win32_segments(ctx->x86.SegDs >> 3, 1);
276 if (ctx->x86.SegEs != ctx->x86.SegDs)
277 info_win32_segments(ctx->x86.SegEs >> 3, 1);
279 info_win32_segments(ctx->x86.SegFs >> 3, 1);
282 static struct dbg_internal_var be_i386_ctx[] =
284 {CV_REG_AL, "AL", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eax), dbg_itype_unsigned_int8},
285 {CV_REG_CL, "CL", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ecx), dbg_itype_unsigned_int8},
286 {CV_REG_DL, "DL", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edx), dbg_itype_unsigned_int8},
287 {CV_REG_BL, "BL", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebx), dbg_itype_unsigned_int8},
288 {CV_REG_AH, "AH", (void*)(FIELD_OFFSET(WOW64_CONTEXT, Eax)+1), dbg_itype_unsigned_int8},
289 {CV_REG_CH, "CH", (void*)(FIELD_OFFSET(WOW64_CONTEXT, Ecx)+1), dbg_itype_unsigned_int8},
290 {CV_REG_DH, "DH", (void*)(FIELD_OFFSET(WOW64_CONTEXT, Edx)+1), dbg_itype_unsigned_int8},
291 {CV_REG_BH, "BH", (void*)(FIELD_OFFSET(WOW64_CONTEXT, Ebx)+1), dbg_itype_unsigned_int8},
292 {CV_REG_AX, "AX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eax), dbg_itype_unsigned_int16},
293 {CV_REG_CX, "CX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ecx), dbg_itype_unsigned_int16},
294 {CV_REG_DX, "DX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edx), dbg_itype_unsigned_int16},
295 {CV_REG_BX, "BX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebx), dbg_itype_unsigned_int16},
296 {CV_REG_SP, "SP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Esp), dbg_itype_unsigned_int16},
297 {CV_REG_BP, "BP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebp), dbg_itype_unsigned_int16},
298 {CV_REG_SI, "SI", (void*)FIELD_OFFSET(WOW64_CONTEXT, Esi), dbg_itype_unsigned_int16},
299 {CV_REG_DI, "DI", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edi), dbg_itype_unsigned_int16},
300 {CV_REG_EAX, "EAX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eax), dbg_itype_unsigned_int32},
301 {CV_REG_ECX, "ECX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ecx), dbg_itype_unsigned_int32},
302 {CV_REG_EDX, "EDX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edx), dbg_itype_unsigned_int32},
303 {CV_REG_EBX, "EBX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebx), dbg_itype_unsigned_int32},
304 {CV_REG_ESP, "ESP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Esp), dbg_itype_unsigned_int32},
305 {CV_REG_EBP, "EBP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebp), dbg_itype_unsigned_int32},
306 {CV_REG_ESI, "ESI", (void*)FIELD_OFFSET(WOW64_CONTEXT, Esi), dbg_itype_unsigned_int32},
307 {CV_REG_EDI, "EDI", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edi), dbg_itype_unsigned_int32},
308 {CV_REG_ES, "ES", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegEs), dbg_itype_unsigned_int16},
309 {CV_REG_CS, "CS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegCs), dbg_itype_unsigned_int16},
310 {CV_REG_SS, "SS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegSs), dbg_itype_unsigned_int16},
311 {CV_REG_DS, "DS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegDs), dbg_itype_unsigned_int16},
312 {CV_REG_FS, "FS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegFs), dbg_itype_unsigned_int16},
313 {CV_REG_GS, "GS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegGs), dbg_itype_unsigned_int16},
314 {CV_REG_IP, "IP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eip), dbg_itype_unsigned_int16},
315 {CV_REG_FLAGS, "FLAGS", (void*)FIELD_OFFSET(WOW64_CONTEXT, EFlags), dbg_itype_unsigned_int16},
316 {CV_REG_EIP, "EIP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eip), dbg_itype_unsigned_int32},
317 {CV_REG_EFLAGS, "EFLAGS", (void*)FIELD_OFFSET(WOW64_CONTEXT, EFlags), dbg_itype_unsigned_int32},
318 {CV_REG_ST0, "ST0", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[ 0]), dbg_itype_long_real},
319 {CV_REG_ST0+1, "ST1", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[10]), dbg_itype_long_real},
320 {CV_REG_ST0+2, "ST2", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[20]), dbg_itype_long_real},
321 {CV_REG_ST0+3, "ST3", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[30]), dbg_itype_long_real},
322 {CV_REG_ST0+4, "ST4", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[40]), dbg_itype_long_real},
323 {CV_REG_ST0+5, "ST5", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[50]), dbg_itype_long_real},
324 {CV_REG_ST0+6, "ST6", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[60]), dbg_itype_long_real},
325 {CV_REG_ST0+7, "ST7", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[70]), dbg_itype_long_real},
326 {CV_AMD64_XMM0, "XMM0", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[0])), dbg_itype_m128a},
327 {CV_AMD64_XMM0+1, "XMM1", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[1])), dbg_itype_m128a},
328 {CV_AMD64_XMM0+2, "XMM2", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[2])), dbg_itype_m128a},
329 {CV_AMD64_XMM0+3, "XMM3", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[3])), dbg_itype_m128a},
330 {CV_AMD64_XMM0+4, "XMM4", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[4])), dbg_itype_m128a},
331 {CV_AMD64_XMM0+5, "XMM5", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[5])), dbg_itype_m128a},
332 {CV_AMD64_XMM0+6, "XMM6", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[6])), dbg_itype_m128a},
333 {CV_AMD64_XMM0+7, "XMM7", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[7])), dbg_itype_m128a},
334 {0, NULL, 0, dbg_itype_none}
337 static BOOL be_i386_is_step_over_insn(const void* insn)
339 BYTE ch;
341 for (;;)
343 if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
345 switch (ch)
347 /* Skip all prefixes */
348 case 0x2e: /* cs: */
349 case 0x36: /* ss: */
350 case 0x3e: /* ds: */
351 case 0x26: /* es: */
352 case 0x64: /* fs: */
353 case 0x65: /* gs: */
354 case 0x66: /* opcode size prefix */
355 case 0x67: /* addr size prefix */
356 case 0xf0: /* lock */
357 case 0xf2: /* repne */
358 case 0xf3: /* repe */
359 insn = (const char*)insn + 1;
360 continue;
362 /* Handle call instructions */
363 case 0xcd: /* int <intno> */
364 case 0xe8: /* call <offset> */
365 case 0x9a: /* lcall <seg>:<off> */
366 return TRUE;
368 case 0xff: /* call <regmodrm> */
369 if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch)))
370 return FALSE;
371 return (((ch & 0x38) == 0x10) || ((ch & 0x38) == 0x18));
373 /* Handle string instructions */
374 case 0x6c: /* insb */
375 case 0x6d: /* insw */
376 case 0x6e: /* outsb */
377 case 0x6f: /* outsw */
378 case 0xa4: /* movsb */
379 case 0xa5: /* movsw */
380 case 0xa6: /* cmpsb */
381 case 0xa7: /* cmpsw */
382 case 0xaa: /* stosb */
383 case 0xab: /* stosw */
384 case 0xac: /* lodsb */
385 case 0xad: /* lodsw */
386 case 0xae: /* scasb */
387 case 0xaf: /* scasw */
388 return TRUE;
390 default:
391 return FALSE;
396 static BOOL be_i386_is_function_return(const void* insn)
398 BYTE ch;
400 if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
401 if (ch == 0xF3) /* REP */
403 insn = (const char*)insn + 1;
404 if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
406 return (ch == 0xC2) || (ch == 0xC3);
409 static BOOL be_i386_is_break_insn(const void* insn)
411 BYTE c;
413 if (!dbg_read_memory(insn, &c, sizeof(c))) return FALSE;
414 return c == 0xCC;
417 static unsigned get_size(ADDRESS_MODE am)
419 if (am == AddrModeReal || am == AddrMode1616) return 16;
420 return 32;
423 static BOOL fetch_value(const char* addr, unsigned sz, int* value)
425 char value8;
426 short value16;
428 switch (sz)
430 case 8:
431 if (!dbg_read_memory(addr, &value8, sizeof(value8)))
432 return FALSE;
433 *value = value8;
434 break;
435 case 16:
436 if (!dbg_read_memory(addr, &value16, sizeof(value16)))
437 return FALSE;
438 *value = value16;
439 break;
440 case 32:
441 if (!dbg_read_memory(addr, value, sizeof(*value)))
442 return FALSE;
443 break;
444 default: return FALSE;
446 return TRUE;
449 static BOOL be_i386_is_func_call(const void* insn, ADDRESS64* callee)
451 BYTE ch;
452 int delta;
453 short segment;
454 unsigned dst = 0;
455 unsigned operand_size;
456 ADDRESS_MODE cs_addr_mode;
458 cs_addr_mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86,
459 dbg_context.x86.SegCs);
460 operand_size = get_size(cs_addr_mode);
462 /* get operand_size (also getting rid of the various prefixes */
465 if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
466 if (ch == 0x66)
468 operand_size = 48 - operand_size; /* 16 => 32, 32 => 16 */
469 insn = (const char*)insn + 1;
471 } while (ch == 0x66 || ch == 0x67);
473 switch (ch)
475 case 0xe8: /* relative near call */
476 callee->Mode = cs_addr_mode;
477 if (!fetch_value((const char*)insn + 1, operand_size, &delta))
478 return FALSE;
479 callee->Segment = dbg_context.x86.SegCs;
480 callee->Offset = (DWORD_PTR)insn + 1 + (operand_size / 8) + delta;
481 return TRUE;
483 case 0x9a: /* absolute far call */
484 if (!dbg_read_memory((const char*)insn + 1 + operand_size / 8,
485 &segment, sizeof(segment)))
486 return FALSE;
487 callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86,
488 segment);
489 if (!fetch_value((const char*)insn + 1, operand_size, &delta))
490 return FALSE;
491 callee->Segment = segment;
492 callee->Offset = delta;
493 return TRUE;
495 case 0xff:
496 if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch)))
497 return FALSE;
498 /* keep only the CALL and LCALL insn:s */
499 switch ((ch >> 3) & 0x07)
501 case 0x02:
502 segment = dbg_context.x86.SegCs;
503 break;
504 case 0x03:
505 if (!dbg_read_memory((const char*)insn + 1 + operand_size / 8,
506 &segment, sizeof(segment)))
507 return FALSE;
508 break;
509 default: return FALSE;
511 /* FIXME: we only support the 32 bit far calls for now */
512 if (operand_size != 32)
514 WINE_FIXME("Unsupported yet call insn (0xFF 0x%02x) with 16 bit operand-size at %p\n", ch, insn);
515 return FALSE;
517 switch (ch & 0xC7) /* keep Mod R/M only (skip reg) */
519 case 0x04:
520 case 0x44:
521 case 0x84:
522 WINE_FIXME("Unsupported yet call insn (0xFF 0x%02x) (SIB bytes) at %p\n", ch, insn);
523 return FALSE;
524 case 0x05: /* addr32 */
525 if ((ch & 0x38) == 0x10 || /* call */
526 (ch & 0x38) == 0x18) /* lcall */
528 void *addr;
529 if (!dbg_read_memory((const char *)insn + 2, &addr, sizeof(addr)))
530 return FALSE;
531 if ((ch & 0x38) == 0x18) /* lcall */
533 if (!dbg_read_memory((const char*)addr + operand_size, &segment, sizeof(segment)))
534 return FALSE;
536 else segment = dbg_context.x86.SegCs;
537 if (!dbg_read_memory((const char*)addr, &dst, sizeof(dst)))
538 return FALSE;
539 callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86, segment);
540 callee->Segment = segment;
541 callee->Offset = dst;
542 return TRUE;
544 return FALSE;
545 default:
546 switch (ch & 0x07)
548 case 0x00: dst = dbg_context.x86.Eax; break;
549 case 0x01: dst = dbg_context.x86.Ecx; break;
550 case 0x02: dst = dbg_context.x86.Edx; break;
551 case 0x03: dst = dbg_context.x86.Ebx; break;
552 case 0x04: dst = dbg_context.x86.Esp; break;
553 case 0x05: dst = dbg_context.x86.Ebp; break;
554 case 0x06: dst = dbg_context.x86.Esi; break;
555 case 0x07: dst = dbg_context.x86.Edi; break;
557 if ((ch >> 6) != 0x03) /* indirect address */
559 if (ch >> 6) /* we got a displacement */
561 if (!fetch_value((const char*)insn + 2, (ch >> 6) == 0x01 ? 8 : 32, &delta))
562 return FALSE;
563 dst += delta;
565 if (((ch >> 3) & 0x07) == 0x03) /* LCALL */
567 if (!dbg_read_memory((const char*)(UINT_PTR)dst + operand_size, &segment, sizeof(segment)))
568 return FALSE;
570 else segment = dbg_context.x86.SegCs;
571 if (!dbg_read_memory((const char*)(UINT_PTR)dst, &delta, sizeof(delta)))
572 return FALSE;
573 callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86,
574 segment);
575 callee->Segment = segment;
576 callee->Offset = delta;
578 else
580 callee->Mode = cs_addr_mode;
581 callee->Segment = dbg_context.x86.SegCs;
582 callee->Offset = dst;
585 return TRUE;
587 default:
588 return FALSE;
592 static BOOL be_i386_is_jump(const void* insn, ADDRESS64* jumpee)
594 BYTE ch;
595 int delta;
596 unsigned operand_size;
597 ADDRESS_MODE cs_addr_mode;
599 cs_addr_mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86,
600 dbg_context.x86.SegCs);
601 operand_size = get_size(cs_addr_mode);
603 /* get operand_size (also getting rid of the various prefixes */
606 if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
607 if (ch == 0x66)
609 operand_size = 48 - operand_size; /* 16 => 32, 32 => 16 */
610 insn = (const char*)insn + 1;
612 } while (ch == 0x66 || ch == 0x67);
614 switch (ch)
616 case 0xe9: /* jmp near */
617 jumpee->Mode = cs_addr_mode;
618 if (!fetch_value((const char*)insn + 1, operand_size, &delta))
619 return FALSE;
620 jumpee->Segment = dbg_context.x86.SegCs;
621 jumpee->Offset = (DWORD_PTR)insn + 1 + (operand_size / 8) + delta;
622 return TRUE;
623 default: WINE_FIXME("unknown %x\n", ch); return FALSE;
625 return FALSE;
628 #define DR7_CONTROL_SHIFT 16
629 #define DR7_CONTROL_SIZE 4
631 #define DR7_RW_EXECUTE (0x0)
632 #define DR7_RW_WRITE (0x1)
633 #define DR7_RW_READ (0x3)
635 #define DR7_LEN_1 (0x0)
636 #define DR7_LEN_2 (0x4)
637 #define DR7_LEN_4 (0xC)
639 #define DR7_LOCAL_ENABLE_SHIFT 0
640 #define DR7_GLOBAL_ENABLE_SHIFT 1
641 #define DR7_ENABLE_SIZE 2
643 #define DR7_LOCAL_ENABLE_MASK (0x55)
644 #define DR7_GLOBAL_ENABLE_MASK (0xAA)
646 #define DR7_CONTROL_RESERVED (0xFC00)
647 #define DR7_LOCAL_SLOWDOWN (0x100)
648 #define DR7_GLOBAL_SLOWDOWN (0x200)
650 #define DR7_ENABLE_MASK(dr) (1<<(DR7_LOCAL_ENABLE_SHIFT+DR7_ENABLE_SIZE*(dr)))
651 #define IS_DR7_SET(ctrl,dr) ((ctrl)&DR7_ENABLE_MASK(dr))
653 static inline int be_i386_get_unused_DR(dbg_ctx_t *pctx, DWORD** r)
655 WOW64_CONTEXT *ctx = &pctx->x86;
657 if (!IS_DR7_SET(ctx->Dr7, 0))
659 *r = &ctx->Dr0;
660 return 0;
662 if (!IS_DR7_SET(ctx->Dr7, 1))
664 *r = &ctx->Dr1;
665 return 1;
667 if (!IS_DR7_SET(ctx->Dr7, 2))
669 *r = &ctx->Dr2;
670 return 2;
672 if (!IS_DR7_SET(ctx->Dr7, 3))
674 *r = &ctx->Dr3;
675 return 3;
677 dbg_printf("All hardware registers have been used\n");
679 return -1;
682 static BOOL be_i386_insert_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
683 dbg_ctx_t *ctx, enum be_xpoint_type type,
684 void* addr, unsigned *val, unsigned size)
686 unsigned char ch;
687 SIZE_T sz;
688 DWORD *pr;
689 int reg;
690 unsigned int bits;
692 switch (type)
694 case be_xpoint_break:
695 if (size != 0) return FALSE;
696 if (!pio->read(hProcess, addr, &ch, 1, &sz) || sz != 1) return FALSE;
697 *val = ch;
698 ch = 0xcc;
699 if (!pio->write(hProcess, addr, &ch, 1, &sz) || sz != 1) return FALSE;
700 break;
701 case be_xpoint_watch_exec:
702 bits = DR7_RW_EXECUTE;
703 goto hw_bp;
704 case be_xpoint_watch_read:
705 bits = DR7_RW_READ;
706 goto hw_bp;
707 case be_xpoint_watch_write:
708 bits = DR7_RW_WRITE;
709 hw_bp:
710 if ((reg = be_i386_get_unused_DR(ctx, &pr)) == -1) return FALSE;
711 *pr = (DWORD_PTR)addr;
712 if (type != be_xpoint_watch_exec) switch (size)
714 case 4: bits |= DR7_LEN_4; break;
715 case 2: bits |= DR7_LEN_2; break;
716 case 1: bits |= DR7_LEN_1; break;
717 default: return FALSE;
719 *val = reg;
720 /* clear old values */
721 ctx->x86.Dr7 &= ~(0x0F << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg));
722 /* set the correct ones */
723 ctx->x86.Dr7 |= bits << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg);
724 ctx->x86.Dr7 |= DR7_ENABLE_MASK(reg) | DR7_LOCAL_SLOWDOWN;
725 break;
726 default:
727 dbg_printf("Unknown bp type %c\n", type);
728 return FALSE;
730 return TRUE;
733 static BOOL be_i386_remove_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
734 dbg_ctx_t *ctx, enum be_xpoint_type type,
735 void* addr, unsigned val, unsigned size)
737 SIZE_T sz;
738 unsigned char ch;
740 switch (type)
742 case be_xpoint_break:
743 if (size != 0) return FALSE;
744 if (!pio->read(hProcess, addr, &ch, 1, &sz) || sz != 1) return FALSE;
745 if (ch != (unsigned char)0xCC)
746 WINE_FIXME("Cannot get back %02x instead of 0xCC at %p\n", ch, addr);
747 ch = (unsigned char)val;
748 if (!pio->write(hProcess, addr, &ch, 1, &sz) || sz != 1) return FALSE;
749 break;
750 case be_xpoint_watch_exec:
751 case be_xpoint_watch_read:
752 case be_xpoint_watch_write:
753 /* simply disable the entry */
754 ctx->x86.Dr7 &= ~DR7_ENABLE_MASK(val);
755 break;
756 default:
757 dbg_printf("Unknown bp type %c\n", type);
758 return FALSE;
760 return TRUE;
763 static BOOL be_i386_is_watchpoint_set(const dbg_ctx_t *ctx, unsigned idx)
765 return ctx->x86.Dr6 & (1 << idx);
768 static void be_i386_clear_watchpoint(dbg_ctx_t *ctx, unsigned idx)
770 ctx->x86.Dr6 &= ~(1 << idx);
773 static int be_i386_adjust_pc_for_break(dbg_ctx_t *ctx, BOOL way)
775 if (way)
777 ctx->x86.Eip--;
778 return -1;
780 ctx->x86.Eip++;
781 return 1;
784 static BOOL be_i386_get_context(HANDLE thread, dbg_ctx_t *ctx)
786 ctx->x86.ContextFlags = WOW64_CONTEXT_ALL;
787 return Wow64GetThreadContext(thread, &ctx->x86);
790 static BOOL be_i386_set_context(HANDLE thread, const dbg_ctx_t *ctx)
792 return Wow64SetThreadContext(thread, &ctx->x86);
795 #define REG(f,n,t,r) {f, n, t, FIELD_OFFSET(WOW64_CONTEXT, r), sizeof(((WOW64_CONTEXT*)NULL)->r)}
797 static struct gdb_register be_i386_gdb_register_map[] = {
798 REG("core", "eax", NULL, Eax),
799 REG(NULL, "ecx", NULL, Ecx),
800 REG(NULL, "edx", NULL, Edx),
801 REG(NULL, "ebx", NULL, Ebx),
802 REG(NULL, "esp", "data_ptr", Esp),
803 REG(NULL, "ebp", "data_ptr", Ebp),
804 REG(NULL, "esi", NULL, Esi),
805 REG(NULL, "edi", NULL, Edi),
806 REG(NULL, "eip", "code_ptr", Eip),
807 REG(NULL, "eflags", "i386_eflags", EFlags),
808 REG(NULL, "cs", NULL, SegCs),
809 REG(NULL, "ss", NULL, SegSs),
810 REG(NULL, "ds", NULL, SegDs),
811 REG(NULL, "es", NULL, SegEs),
812 REG(NULL, "fs", NULL, SegFs),
813 REG(NULL, "gs", NULL, SegGs),
814 { NULL, "st0", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[ 0]), 10},
815 { NULL, "st1", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[10]), 10},
816 { NULL, "st2", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[20]), 10},
817 { NULL, "st3", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[30]), 10},
818 { NULL, "st4", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[40]), 10},
819 { NULL, "st5", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[50]), 10},
820 { NULL, "st6", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[60]), 10},
821 { NULL, "st7", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[70]), 10},
822 { NULL, "fctrl", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.ControlWord), 2},
823 { NULL, "fstat", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.StatusWord), 2},
824 { NULL, "ftag", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.TagWord), 2},
825 { NULL, "fiseg", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.ErrorSelector), 2},
826 REG(NULL, "fioff", NULL, FloatSave.ErrorOffset),
827 { NULL, "foseg", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.DataSelector), 2},
828 REG(NULL, "fooff", NULL, FloatSave.DataOffset),
829 { NULL, "fop", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.ErrorSelector)+2, 2},
831 { "sse", "xmm0", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[0]), 16},
832 { NULL, "xmm1", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[1]), 16},
833 { NULL, "xmm2", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[2]), 16},
834 { NULL, "xmm3", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[3]), 16},
835 { NULL, "xmm4", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[4]), 16},
836 { NULL, "xmm5", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[5]), 16},
837 { NULL, "xmm6", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[6]), 16},
838 { NULL, "xmm7", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[7]), 16},
839 { NULL, "mxcsr", "i386_mxcsr", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, MxCsr), 4},
842 struct backend_cpu be_i386 =
844 IMAGE_FILE_MACHINE_I386,
846 be_i386_linearize,
847 be_i386_build_addr,
848 be_i386_get_addr,
849 be_i386_get_register_info,
850 be_i386_single_step,
851 be_i386_print_context,
852 be_i386_print_segment_info,
853 be_i386_ctx,
854 be_i386_is_step_over_insn,
855 be_i386_is_function_return,
856 be_i386_is_break_insn,
857 be_i386_is_func_call,
858 be_i386_is_jump,
859 memory_disasm_one_x86_insn,
860 be_i386_insert_Xpoint,
861 be_i386_remove_Xpoint,
862 be_i386_is_watchpoint_set,
863 be_i386_clear_watchpoint,
864 be_i386_adjust_pc_for_break,
865 be_i386_get_context,
866 be_i386_set_context,
867 be_i386_gdb_register_map,
868 ARRAY_SIZE(be_i386_gdb_register_map),
870 #endif