Commit | Line | Data |
---|---|---|
deca2502 EP |
1 | /* |
2 | * Debugger i386 specific functions | |
3 | * | |
4 | * Copyright 2004 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 | |
360a3f91 | 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
deca2502 EP |
19 | */ |
20 | ||
21 | #include "debugger.h" | |
22 | #include "wine/debug.h" | |
23 | ||
24 | WINE_DEFAULT_DEBUG_CHANNEL(winedbg); | |
25 | ||
26 | #ifdef __i386__ | |
27 | ||
71fe9c95 | 28 | /* db_disasm.c */ |
1d04f826 | 29 | extern void be_i386_disasm_one_insn(ADDRESS64* addr, int display); |
deca2502 EP |
30 | |
31 | #define STEP_FLAG 0x00000100 /* single step flag */ | |
32 | #define V86_FLAG 0x00020000 | |
33 | ||
34 | #define IS_VM86_MODE(ctx) (ctx->EFlags & V86_FLAG) | |
35 | ||
36 | static ADDRESS_MODE get_selector_type(HANDLE hThread, const CONTEXT* ctx, WORD sel) | |
37 | { | |
38 | LDT_ENTRY le; | |
39 | ||
40 | if (IS_VM86_MODE(ctx)) return AddrModeReal; | |
41 | /* null or system selector */ | |
42 | if (!(sel & 4) || ((sel >> 3) < 17)) return AddrModeFlat; | |
bce64150 | 43 | if (dbg_curr_process->process_io->get_selector(hThread, sel, &le)) |
deca2502 EP |
44 | return le.HighWord.Bits.Default_Big ? AddrMode1632 : AddrMode1616; |
45 | /* selector doesn't exist */ | |
46 | return -1; | |
47 | } | |
48 | ||
1d04f826 | 49 | static void* be_i386_linearize(HANDLE hThread, const ADDRESS64* addr) |
deca2502 EP |
50 | { |
51 | LDT_ENTRY le; | |
52 | ||
53 | switch (addr->Mode) | |
54 | { | |
55 | case AddrModeReal: | |
1d04f826 | 56 | return (void*)((DWORD)(LOWORD(addr->Segment) << 4) + (DWORD)addr->Offset); |
deca2502 EP |
57 | case AddrMode1632: |
58 | if (!(addr->Segment & 4) || ((addr->Segment >> 3) < 17)) | |
1d04f826 | 59 | return (void*)(DWORD)addr->Offset; |
deca2502 EP |
60 | /* fall through */ |
61 | case AddrMode1616: | |
bce64150 | 62 | if (!dbg_curr_process->process_io->get_selector(hThread, addr->Segment, &le)) return NULL; |
deca2502 | 63 | return (void*)((le.HighWord.Bits.BaseHi << 24) + |
1d04f826 GG |
64 | (le.HighWord.Bits.BaseMid << 16) + le.BaseLow + |
65 | (DWORD)addr->Offset); | |
deca2502 | 66 | case AddrModeFlat: |
1d04f826 | 67 | return (void*)(DWORD)addr->Offset; |
deca2502 EP |
68 | } |
69 | return NULL; | |
70 | } | |
71 | ||
1d04f826 | 72 | static unsigned be_i386_build_addr(HANDLE hThread, const CONTEXT* ctx, ADDRESS64* addr, |
deca2502 EP |
73 | unsigned seg, unsigned long offset) |
74 | { | |
75 | addr->Mode = AddrModeFlat; | |
76 | addr->Segment = seg; | |
77 | addr->Offset = offset; | |
78 | if (seg) | |
79 | { | |
80 | addr->Mode = get_selector_type(hThread, ctx, seg); | |
81 | switch (addr->Mode) | |
82 | { | |
83 | case AddrModeReal: | |
84 | case AddrMode1616: | |
85 | addr->Offset &= 0xffff; | |
86 | break; | |
87 | case AddrModeFlat: | |
88 | case AddrMode1632: | |
89 | break; | |
90 | default: | |
91 | addr->Mode = -1; | |
92 | return FALSE; | |
93 | } | |
94 | } | |
95 | return TRUE; | |
96 | } | |
97 | ||
98 | static unsigned be_i386_get_addr(HANDLE hThread, const CONTEXT* ctx, | |
1d04f826 | 99 | enum be_cpu_addr bca, ADDRESS64* addr) |
deca2502 EP |
100 | { |
101 | switch (bca) | |
102 | { | |
103 | case be_cpu_addr_pc: | |
104 | return be_i386_build_addr(hThread, ctx, addr, ctx->SegCs, ctx->Eip); | |
105 | case be_cpu_addr_stack: | |
106 | return be_i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Esp); | |
107 | case be_cpu_addr_frame: | |
108 | return be_i386_build_addr(hThread, ctx, addr, ctx->SegSs, ctx->Ebp); | |
109 | } | |
110 | return FALSE; | |
111 | } | |
112 | ||
d6d8682d EP |
113 | static unsigned be_i386_get_register_info(int regno, enum be_cpu_addr* kind) |
114 | { | |
115 | switch (regno) | |
116 | { | |
117 | case CV_REG_EIP: *kind = be_cpu_addr_pc; return TRUE; | |
118 | case CV_REG_EBP: *kind = be_cpu_addr_frame; return TRUE; | |
119 | case CV_REG_ESP: *kind = be_cpu_addr_stack; return TRUE; | |
120 | } | |
121 | return FALSE; | |
122 | } | |
123 | ||
deca2502 EP |
124 | static void be_i386_single_step(CONTEXT* ctx, unsigned enable) |
125 | { | |
126 | if (enable) ctx->EFlags |= STEP_FLAG; | |
127 | else ctx->EFlags &= ~STEP_FLAG; | |
128 | } | |
129 | ||
09941267 JL |
130 | static void be_i386_all_print_context(HANDLE hThread, const CONTEXT* ctx) |
131 | { | |
132 | long double ST[8]; /* These are for floating regs */ | |
133 | int cnt; | |
134 | ||
135 | /* Break out the FPU state and the floating point registers */ | |
136 | dbg_printf("Floating Point Unit status:\n"); | |
137 | dbg_printf(" FLCW:%04x ", LOWORD(ctx->FloatSave.ControlWord)); | |
138 | dbg_printf(" FLTW:%04x ", LOWORD(ctx->FloatSave.TagWord)); | |
139 | dbg_printf(" FLEO:%08x ", (unsigned int) ctx->FloatSave.ErrorOffset); | |
140 | dbg_printf(" FLSW:%04x", LOWORD(ctx->FloatSave.StatusWord)); | |
141 | ||
142 | /* Isolate the condition code bits - note they are not contiguous */ | |
67449294 AJ |
143 | dbg_printf("(CC:%d%d%d%d", (ctx->FloatSave.StatusWord & 0x00004000) >> 14, |
144 | (ctx->FloatSave.StatusWord & 0x00000400) >> 10, | |
145 | (ctx->FloatSave.StatusWord & 0x00000200) >> 9, | |
146 | (ctx->FloatSave.StatusWord & 0x00000100) >> 8); | |
09941267 | 147 | |
423a447f | 148 | /* Now pull out the 3 bit of the TOP stack pointer */ |
09941267 JL |
149 | dbg_printf(" TOP:%01x", (unsigned int) (ctx->FloatSave.StatusWord & 0x00003800) >> 11); |
150 | ||
151 | /* Lets analyse the error bits and indicate the status | |
152 | * the Invalid Op flag has sub status which is tested as follows */ | |
153 | if (ctx->FloatSave.StatusWord & 0x00000001) { /* Invalid Fl OP */ | |
154 | if (ctx->FloatSave.StatusWord & 0x00000040) { /* Stack Fault */ | |
155 | if (ctx->FloatSave.StatusWord & 0x00000200) /* C1 says Overflow */ | |
156 | dbg_printf(" #IE(Stack Overflow)"); | |
157 | else | |
158 | dbg_printf(" #IE(Stack Underflow)"); /* Underflow */ | |
159 | } | |
160 | else dbg_printf(" #IE(Arthimetic error)"); /* Invalid Fl OP */ | |
161 | } | |
162 | ||
163 | if (ctx->FloatSave.StatusWord & 0x00000002) dbg_printf(" #DE"); /* Denormalised OP */ | |
164 | if (ctx->FloatSave.StatusWord & 0x00000004) dbg_printf(" #ZE"); /* Zero Divide */ | |
165 | if (ctx->FloatSave.StatusWord & 0x00000008) dbg_printf(" #OE"); /* Overflow */ | |
166 | if (ctx->FloatSave.StatusWord & 0x00000010) dbg_printf(" #UE"); /* Underflow */ | |
167 | if (ctx->FloatSave.StatusWord & 0x00000020) dbg_printf(" #PE"); /* Precision error */ | |
168 | if (ctx->FloatSave.StatusWord & 0x00000040) | |
169 | if (!(ctx->FloatSave.StatusWord & 0x00000001)) | |
170 | dbg_printf(" #SE"); /* Stack Fault (don't think this can occur) */ | |
171 | if (ctx->FloatSave.StatusWord & 0x00000080) dbg_printf(" #ES"); /* Error Summary */ | |
172 | if (ctx->FloatSave.StatusWord & 0x00008000) dbg_printf(" #FB"); /* FPU Busy */ | |
173 | dbg_printf(")\n"); | |
174 | ||
175 | /* Here are the rest of the registers */ | |
d1110dd2 FG |
176 | dbg_printf(" FLES:%08x FLDO:%08x FLDS:%08x FLCNS:%08x\n", |
177 | ctx->FloatSave.ErrorSelector, | |
178 | ctx->FloatSave.DataOffset, | |
179 | ctx->FloatSave.DataSelector, | |
180 | ctx->FloatSave.Cr0NpxState); | |
09941267 JL |
181 | |
182 | /* Now for the floating point registers */ | |
183 | dbg_printf("Floating Point Registers:\n"); | |
184 | for (cnt = 0; cnt < 4; cnt++) | |
185 | { | |
186 | memcpy(&ST[cnt], &ctx->FloatSave.RegisterArea[cnt * 10], 10); | |
187 | dbg_printf(" ST%d:%Lf ", cnt, ST[cnt]); | |
188 | } | |
189 | dbg_printf("\n"); | |
190 | for (cnt = 4; cnt < 8; cnt++) | |
191 | { | |
192 | memcpy(&ST[cnt], &ctx->FloatSave.RegisterArea[cnt * 10], 10); | |
193 | dbg_printf(" ST%d:%Lf ", cnt, ST[cnt]); | |
194 | } | |
195 | dbg_printf("\n"); | |
196 | } | |
197 | ||
198 | static void be_i386_print_context(HANDLE hThread, const CONTEXT* ctx, int all_regs) | |
deca2502 | 199 | { |
b26ae69f AJ |
200 | static const char flags[] = "aVR-N--ODITSZ-A-P-C"; |
201 | int i; | |
deca2502 | 202 | char buf[33]; |
deca2502 EP |
203 | |
204 | dbg_printf("Register dump:\n"); | |
205 | ||
206 | /* First get the segment registers out of the way */ | |
207 | dbg_printf(" CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x", | |
208 | (WORD)ctx->SegCs, (WORD)ctx->SegSs, | |
209 | (WORD)ctx->SegDs, (WORD)ctx->SegEs, | |
210 | (WORD)ctx->SegFs, (WORD)ctx->SegGs); | |
211 | ||
b26ae69f AJ |
212 | strcpy(buf, flags); |
213 | for (i = 0; buf[i]; i++) | |
214 | if (buf[i] != '-' && !(ctx->EFlags & (1 << (sizeof(flags) - 2 - i)))) | |
215 | buf[i] = ' '; | |
216 | ||
deca2502 EP |
217 | switch (get_selector_type(hThread, ctx, ctx->SegCs)) |
218 | { | |
219 | case AddrMode1616: | |
220 | case AddrModeReal: | |
221 | dbg_printf("\n IP:%04x SP:%04x BP:%04x FLAGS:%04x(%s)\n", | |
222 | LOWORD(ctx->Eip), LOWORD(ctx->Esp), | |
223 | LOWORD(ctx->Ebp), LOWORD(ctx->EFlags), buf); | |
224 | dbg_printf(" AX:%04x BX:%04x CX:%04x DX:%04x SI:%04x DI:%04x\n", | |
225 | LOWORD(ctx->Eax), LOWORD(ctx->Ebx), | |
226 | LOWORD(ctx->Ecx), LOWORD(ctx->Edx), | |
227 | LOWORD(ctx->Esi), LOWORD(ctx->Edi)); | |
228 | break; | |
229 | case AddrModeFlat: | |
230 | case AddrMode1632: | |
67449294 | 231 | dbg_printf("\n EIP:%08x ESP:%08x EBP:%08x EFLAGS:%08x(%s)\n", |
deca2502 | 232 | ctx->Eip, ctx->Esp, ctx->Ebp, ctx->EFlags, buf); |
67449294 | 233 | dbg_printf(" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n", |
deca2502 | 234 | ctx->Eax, ctx->Ebx, ctx->Ecx, ctx->Edx); |
67449294 | 235 | dbg_printf(" ESI:%08x EDI:%08x\n", |
deca2502 EP |
236 | ctx->Esi, ctx->Edi); |
237 | break; | |
238 | } | |
09941267 JL |
239 | |
240 | if (all_regs) be_i386_all_print_context(hThread, ctx); /* print floating regs */ | |
241 | ||
deca2502 EP |
242 | } |
243 | ||
244 | static void be_i386_print_segment_info(HANDLE hThread, const CONTEXT* ctx) | |
245 | { | |
246 | if (get_selector_type(hThread, ctx, ctx->SegCs) == AddrMode1616) | |
247 | { | |
248 | info_win32_segments(ctx->SegDs >> 3, 1); | |
249 | if (ctx->SegEs != ctx->SegDs) info_win32_segments(ctx->SegEs >> 3, 1); | |
250 | } | |
251 | info_win32_segments(ctx->SegFs >> 3, 1); | |
252 | } | |
253 | ||
254 | static struct dbg_internal_var be_i386_ctx[] = | |
255 | { | |
ec73f0b7 AJ |
256 | {CV_REG_AL, "AL", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Eax), dbg_itype_unsigned_char_int}, |
257 | {CV_REG_CL, "CL", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ecx), dbg_itype_unsigned_char_int}, | |
258 | {CV_REG_DL, "DL", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Edx), dbg_itype_unsigned_char_int}, | |
259 | {CV_REG_BL, "BL", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ebx), dbg_itype_unsigned_char_int}, | |
260 | {CV_REG_AH, "AH", (DWORD_PTR*)(FIELD_OFFSET(CONTEXT, Eax)+1), dbg_itype_unsigned_char_int}, | |
261 | {CV_REG_CH, "CH", (DWORD_PTR*)(FIELD_OFFSET(CONTEXT, Ecx)+1), dbg_itype_unsigned_char_int}, | |
262 | {CV_REG_DH, "DH", (DWORD_PTR*)(FIELD_OFFSET(CONTEXT, Edx)+1), dbg_itype_unsigned_char_int}, | |
263 | {CV_REG_BH, "BH", (DWORD_PTR*)(FIELD_OFFSET(CONTEXT, Ebx)+1), dbg_itype_unsigned_char_int}, | |
264 | {CV_REG_AX, "AX", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Eax), dbg_itype_unsigned_short_int}, | |
265 | {CV_REG_CX, "CX", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ecx), dbg_itype_unsigned_short_int}, | |
266 | {CV_REG_DX, "DX", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Edx), dbg_itype_unsigned_short_int}, | |
267 | {CV_REG_BX, "BX", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ebx), dbg_itype_unsigned_short_int}, | |
268 | {CV_REG_SP, "SP", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Esp), dbg_itype_unsigned_short_int}, | |
269 | {CV_REG_BP, "BP", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ebp), dbg_itype_unsigned_short_int}, | |
270 | {CV_REG_SI, "SI", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Esi), dbg_itype_unsigned_short_int}, | |
271 | {CV_REG_DI, "DI", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Edi), dbg_itype_unsigned_short_int}, | |
272 | {CV_REG_EAX, "EAX", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Eax), dbg_itype_unsigned_int}, | |
273 | {CV_REG_ECX, "ECX", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ecx), dbg_itype_unsigned_int}, | |
274 | {CV_REG_EDX, "EDX", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Edx), dbg_itype_unsigned_int}, | |
275 | {CV_REG_EBX, "EBX", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ebx), dbg_itype_unsigned_int}, | |
276 | {CV_REG_ESP, "ESP", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Esp), dbg_itype_unsigned_int}, | |
277 | {CV_REG_EBP, "EBP", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Ebp), dbg_itype_unsigned_int}, | |
278 | {CV_REG_ESI, "ESI", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Esi), dbg_itype_unsigned_int}, | |
279 | {CV_REG_EDI, "EDI", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Edi), dbg_itype_unsigned_int}, | |
280 | {CV_REG_ES, "ES", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, SegEs), dbg_itype_unsigned_short_int}, | |
281 | {CV_REG_CS, "CS", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, SegCs), dbg_itype_unsigned_short_int}, | |
282 | {CV_REG_SS, "SS", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, SegSs), dbg_itype_unsigned_short_int}, | |
283 | {CV_REG_DS, "DS", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, SegDs), dbg_itype_unsigned_short_int}, | |
284 | {CV_REG_FS, "FS", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, SegFs), dbg_itype_unsigned_short_int}, | |
285 | {CV_REG_GS, "GS", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, SegGs), dbg_itype_unsigned_short_int}, | |
286 | {CV_REG_IP, "IP", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Eip), dbg_itype_unsigned_short_int}, | |
287 | {CV_REG_FLAGS, "FLAGS", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, EFlags), dbg_itype_unsigned_short_int}, | |
288 | {CV_REG_EIP, "EIP", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, Eip), dbg_itype_unsigned_int}, | |
289 | {CV_REG_EFLAGS, "EFLAGS", (DWORD_PTR*)FIELD_OFFSET(CONTEXT, EFlags), dbg_itype_unsigned_int}, | |
deca2502 EP |
290 | {0, NULL, 0, dbg_itype_none} |
291 | }; | |
292 | ||
deca2502 EP |
293 | static unsigned be_i386_is_step_over_insn(const void* insn) |
294 | { | |
295 | BYTE ch; | |
296 | ||
297 | for (;;) | |
298 | { | |
299 | if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE; | |
300 | ||
301 | switch (ch) | |
302 | { | |
303 | /* Skip all prefixes */ | |
304 | case 0x2e: /* cs: */ | |
305 | case 0x36: /* ss: */ | |
306 | case 0x3e: /* ds: */ | |
307 | case 0x26: /* es: */ | |
308 | case 0x64: /* fs: */ | |
309 | case 0x65: /* gs: */ | |
310 | case 0x66: /* opcode size prefix */ | |
311 | case 0x67: /* addr size prefix */ | |
312 | case 0xf0: /* lock */ | |
313 | case 0xf2: /* repne */ | |
314 | case 0xf3: /* repe */ | |
315 | insn = (const char*)insn + 1; | |
316 | continue; | |
317 | ||
318 | /* Handle call instructions */ | |
319 | case 0xcd: /* int <intno> */ | |
320 | case 0xe8: /* call <offset> */ | |
321 | case 0x9a: /* lcall <seg>:<off> */ | |
322 | return TRUE; | |
323 | ||
324 | case 0xff: /* call <regmodrm> */ | |
325 | if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch))) | |
326 | return FALSE; | |
327 | return (((ch & 0x38) == 0x10) || ((ch & 0x38) == 0x18)); | |
328 | ||
329 | /* Handle string instructions */ | |
330 | case 0x6c: /* insb */ | |
331 | case 0x6d: /* insw */ | |
332 | case 0x6e: /* outsb */ | |
333 | case 0x6f: /* outsw */ | |
334 | case 0xa4: /* movsb */ | |
335 | case 0xa5: /* movsw */ | |
336 | case 0xa6: /* cmpsb */ | |
337 | case 0xa7: /* cmpsw */ | |
338 | case 0xaa: /* stosb */ | |
339 | case 0xab: /* stosw */ | |
340 | case 0xac: /* lodsb */ | |
341 | case 0xad: /* lodsw */ | |
342 | case 0xae: /* scasb */ | |
343 | case 0xaf: /* scasw */ | |
344 | return TRUE; | |
345 | ||
346 | default: | |
347 | return FALSE; | |
348 | } | |
349 | } | |
350 | } | |
351 | ||
352 | static unsigned be_i386_is_function_return(const void* insn) | |
353 | { | |
354 | BYTE ch; | |
355 | ||
356 | if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE; | |
357 | return (ch == 0xC2) || (ch == 0xC3); | |
358 | } | |
359 | ||
360 | static unsigned be_i386_is_break_insn(const void* insn) | |
361 | { | |
362 | BYTE c; | |
363 | ||
d6be549a | 364 | if (!dbg_read_memory(insn, &c, sizeof(c))) return FALSE; |
deca2502 EP |
365 | return c == 0xCC; |
366 | } | |
367 | ||
ec087dac EP |
368 | static unsigned get_size(ADDRESS_MODE am) |
369 | { | |
370 | if (am == AddrModeReal || am == AddrMode1616) return 16; | |
371 | return 32; | |
372 | } | |
373 | ||
374 | static BOOL fetch_value(const char* addr, unsigned sz, int* value) | |
375 | { | |
376 | char value8; | |
377 | short value16; | |
378 | ||
379 | switch (sz) | |
380 | { | |
381 | case 8: | |
382 | if (!dbg_read_memory(addr, &value8, sizeof(value8))) | |
383 | return FALSE; | |
384 | *value = value8; | |
385 | break; | |
386 | case 16: | |
387 | if (!dbg_read_memory(addr, &value16, sizeof(value16))) | |
388 | return FALSE; | |
389 | *value = value16; | |
d15a04bd | 390 | break; |
ec087dac EP |
391 | case 32: |
392 | if (!dbg_read_memory(addr, value, sizeof(*value))) | |
393 | return FALSE; | |
394 | break; | |
395 | default: return FALSE; | |
396 | } | |
397 | return TRUE; | |
398 | } | |
399 | ||
1d04f826 | 400 | static unsigned be_i386_is_func_call(const void* insn, ADDRESS64* callee) |
deca2502 | 401 | { |
ec087dac EP |
402 | BYTE ch; |
403 | int delta; | |
404 | short segment; | |
405 | unsigned dst = 0; | |
406 | unsigned operand_size; | |
407 | ADDRESS_MODE cs_addr_mode; | |
408 | ||
409 | cs_addr_mode = get_selector_type(dbg_curr_thread->handle, &dbg_context, | |
410 | dbg_context.SegCs); | |
411 | operand_size = get_size(cs_addr_mode); | |
412 | ||
413 | /* get operand_size (also getting rid of the various prefixes */ | |
414 | do | |
415 | { | |
416 | if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE; | |
417 | if (ch == 0x66) | |
418 | { | |
419 | operand_size = 48 - operand_size; /* 16 => 32, 32 => 16 */ | |
420 | insn = (const char*)insn + 1; | |
421 | } | |
422 | } while (ch == 0x66 || ch == 0x67); | |
deca2502 | 423 | |
d6be549a | 424 | switch (ch) |
deca2502 | 425 | { |
ec087dac EP |
426 | case 0xe8: /* relative near call */ |
427 | callee->Mode = cs_addr_mode; | |
428 | if (!fetch_value((const char*)insn + 1, operand_size, &delta)) | |
429 | return FALSE; | |
430 | callee->Segment = dbg_context.SegCs; | |
431 | callee->Offset = (DWORD)insn + 1 + (operand_size / 8) + delta; | |
432 | return TRUE; | |
deca2502 | 433 | |
ec087dac EP |
434 | case 0x9a: /* absolute far call */ |
435 | if (!dbg_read_memory((const char*)insn + 1 + operand_size / 8, | |
436 | &segment, sizeof(segment))) | |
437 | return FALSE; | |
438 | callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context, | |
439 | segment); | |
440 | if (!fetch_value((const char*)insn + 1, operand_size, &delta)) | |
441 | return FALSE; | |
442 | callee->Segment = segment; | |
443 | callee->Offset = delta; | |
deca2502 | 444 | return TRUE; |
ec087dac | 445 | |
d6be549a EP |
446 | case 0xff: |
447 | if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch))) | |
448 | return FALSE; | |
ec087dac EP |
449 | /* keep only the CALL and LCALL insn:s */ |
450 | switch ((ch >> 3) & 0x07) | |
451 | { | |
452 | case 0x02: | |
453 | segment = dbg_context.SegCs; | |
454 | break; | |
455 | case 0x03: | |
456 | if (!dbg_read_memory((const char*)insn + 1 + operand_size / 8, | |
457 | &segment, sizeof(segment))) | |
458 | return FALSE; | |
459 | break; | |
460 | default: return FALSE; | |
461 | } | |
462 | /* FIXME: we only support the 32 bit far calls for now */ | |
463 | if (operand_size != 32) | |
464 | { | |
465 | WINE_FIXME("Unsupported yet call insn (0xFF 0x%02x) with 16 bit operand-size at %p\n", ch, insn); | |
466 | return FALSE; | |
467 | } | |
468 | switch (ch & 0xC7) /* keep Mod R/M only (skip reg) */ | |
469 | { | |
470 | case 0x04: | |
471 | case 0x44: | |
472 | case 0x84: | |
473 | WINE_FIXME("Unsupported yet call insn (0xFF 0x%02x) (SIB bytes) at %p\n", ch, insn); | |
474 | return FALSE; | |
475 | case 0x05: /* addr32 */ | |
87fe0145 AJ |
476 | if ((ch & 0x38) == 0x10 || /* call */ |
477 | (ch & 0x38) == 0x18) /* lcall */ | |
478 | { | |
479 | void *addr; | |
480 | if (!dbg_read_memory((const char *)insn + 2, &addr, sizeof(addr))) | |
481 | return FALSE; | |
482 | if ((ch & 0x38) == 0x18) /* lcall */ | |
483 | { | |
484 | if (!dbg_read_memory((const char*)addr + operand_size, &segment, sizeof(segment))) | |
485 | return FALSE; | |
486 | } | |
487 | else segment = dbg_context.SegCs; | |
488 | if (!dbg_read_memory((const char*)addr, &dst, sizeof(dst))) | |
489 | return FALSE; | |
490 | callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context, segment); | |
491 | callee->Segment = segment; | |
492 | callee->Offset = dst; | |
493 | return TRUE; | |
494 | } | |
ec087dac EP |
495 | return FALSE; |
496 | default: | |
497 | switch (ch & 0x07) | |
498 | { | |
499 | case 0x00: dst = dbg_context.Eax; break; | |
500 | case 0x01: dst = dbg_context.Ecx; break; | |
501 | case 0x02: dst = dbg_context.Edx; break; | |
502 | case 0x03: dst = dbg_context.Ebx; break; | |
503 | case 0x04: dst = dbg_context.Esp; break; | |
504 | case 0x05: dst = dbg_context.Ebp; break; | |
505 | case 0x06: dst = dbg_context.Esi; break; | |
506 | case 0x07: dst = dbg_context.Edi; break; | |
507 | } | |
508 | if ((ch >> 6) != 0x03) /* indirect address */ | |
509 | { | |
510 | if (ch >> 6) /* we got a displacement */ | |
511 | { | |
512 | if (!fetch_value((const char*)insn + 2, (ch >> 6) == 0x01 ? 8 : 32, &delta)) | |
513 | return FALSE; | |
514 | dst += delta; | |
515 | } | |
516 | if (((ch >> 3) & 0x07) == 0x03) /* LCALL */ | |
517 | { | |
518 | if (!dbg_read_memory((const char*)dst + operand_size, &segment, sizeof(segment))) | |
519 | return FALSE; | |
520 | } | |
521 | else segment = dbg_context.SegCs; | |
522 | if (!dbg_read_memory((const char*)dst, &delta, sizeof(delta))) | |
523 | return FALSE; | |
524 | callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context, | |
525 | segment); | |
526 | callee->Segment = segment; | |
527 | callee->Offset = delta; | |
528 | } | |
529 | else | |
530 | { | |
531 | callee->Mode = cs_addr_mode; | |
532 | callee->Segment = dbg_context.SegCs; | |
533 | callee->Offset = dst; | |
534 | } | |
535 | } | |
536 | return TRUE; | |
537 | ||
d6be549a EP |
538 | default: |
539 | return FALSE; | |
deca2502 | 540 | } |
deca2502 EP |
541 | } |
542 | ||
ae9f8604 EP |
543 | static unsigned be_i386_is_jump(const void* insn, ADDRESS64* jumpee) |
544 | { | |
545 | BYTE ch; | |
546 | int delta; | |
547 | unsigned operand_size; | |
548 | ADDRESS_MODE cs_addr_mode; | |
549 | ||
550 | cs_addr_mode = get_selector_type(dbg_curr_thread->handle, &dbg_context, | |
551 | dbg_context.SegCs); | |
552 | operand_size = get_size(cs_addr_mode); | |
553 | ||
554 | /* get operand_size (also getting rid of the various prefixes */ | |
555 | do | |
556 | { | |
557 | if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE; | |
558 | if (ch == 0x66) | |
559 | { | |
560 | operand_size = 48 - operand_size; /* 16 => 32, 32 => 16 */ | |
561 | insn = (const char*)insn + 1; | |
562 | } | |
563 | } while (ch == 0x66 || ch == 0x67); | |
564 | ||
565 | switch (ch) | |
566 | { | |
567 | case 0xe9: /* jmp near */ | |
568 | jumpee->Mode = cs_addr_mode; | |
569 | if (!fetch_value((const char*)insn + 1, operand_size, &delta)) | |
570 | return FALSE; | |
571 | jumpee->Segment = dbg_context.SegCs; | |
572 | jumpee->Offset = (DWORD)insn + 1 + (operand_size / 8) + delta; | |
573 | return TRUE; | |
574 | default: WINE_FIXME("unknown %x\n", ch); return FALSE; | |
575 | } | |
576 | return FALSE; | |
577 | } | |
578 | ||
deca2502 EP |
579 | #define DR7_CONTROL_SHIFT 16 |
580 | #define DR7_CONTROL_SIZE 4 | |
581 | ||
582 | #define DR7_RW_EXECUTE (0x0) | |
583 | #define DR7_RW_WRITE (0x1) | |
584 | #define DR7_RW_READ (0x3) | |
585 | ||
586 | #define DR7_LEN_1 (0x0) | |
587 | #define DR7_LEN_2 (0x4) | |
588 | #define DR7_LEN_4 (0xC) | |
589 | ||
590 | #define DR7_LOCAL_ENABLE_SHIFT 0 | |
591 | #define DR7_GLOBAL_ENABLE_SHIFT 1 | |
592 | #define DR7_ENABLE_SIZE 2 | |
593 | ||
594 | #define DR7_LOCAL_ENABLE_MASK (0x55) | |
595 | #define DR7_GLOBAL_ENABLE_MASK (0xAA) | |
596 | ||
597 | #define DR7_CONTROL_RESERVED (0xFC00) | |
598 | #define DR7_LOCAL_SLOWDOWN (0x100) | |
599 | #define DR7_GLOBAL_SLOWDOWN (0x200) | |
600 | ||
601 | #define DR7_ENABLE_MASK(dr) (1<<(DR7_LOCAL_ENABLE_SHIFT+DR7_ENABLE_SIZE*(dr))) | |
602 | #define IS_DR7_SET(ctrl,dr) ((ctrl)&DR7_ENABLE_MASK(dr)) | |
603 | ||
2bc33389 | 604 | static inline int be_i386_get_unused_DR(CONTEXT* ctx, DWORD** r) |
deca2502 EP |
605 | { |
606 | if (!IS_DR7_SET(ctx->Dr7, 0)) | |
607 | { | |
608 | *r = &ctx->Dr0; | |
609 | return 0; | |
610 | } | |
611 | if (!IS_DR7_SET(ctx->Dr7, 1)) | |
612 | { | |
613 | *r = &ctx->Dr1; | |
614 | return 1; | |
615 | } | |
616 | if (!IS_DR7_SET(ctx->Dr7, 2)) | |
617 | { | |
618 | *r = &ctx->Dr2; | |
619 | return 2; | |
620 | } | |
621 | if (!IS_DR7_SET(ctx->Dr7, 3)) | |
622 | { | |
623 | *r = &ctx->Dr3; | |
624 | return 3; | |
625 | } | |
626 | dbg_printf("All hardware registers have been used\n"); | |
627 | ||
628 | return -1; | |
629 | } | |
630 | ||
f16f847c | 631 | static unsigned be_i386_insert_Xpoint(HANDLE hProcess, const struct be_process_io* pio, |
d6be549a EP |
632 | CONTEXT* ctx, enum be_xpoint_type type, |
633 | void* addr, unsigned long* val, unsigned size) | |
deca2502 EP |
634 | { |
635 | unsigned char ch; | |
f0279726 | 636 | SIZE_T sz; |
2bc33389 | 637 | DWORD *pr; |
deca2502 EP |
638 | int reg; |
639 | unsigned long bits; | |
640 | ||
641 | switch (type) | |
642 | { | |
643 | case be_xpoint_break: | |
644 | if (size != 0) return 0; | |
d6be549a | 645 | if (!pio->read(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0; |
deca2502 EP |
646 | *val = ch; |
647 | ch = 0xcc; | |
d6be549a | 648 | if (!pio->write(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0; |
deca2502 EP |
649 | break; |
650 | case be_xpoint_watch_exec: | |
651 | bits = DR7_RW_EXECUTE; | |
652 | goto hw_bp; | |
653 | case be_xpoint_watch_read: | |
654 | bits = DR7_RW_READ; | |
655 | goto hw_bp; | |
656 | case be_xpoint_watch_write: | |
657 | bits = DR7_RW_WRITE; | |
658 | hw_bp: | |
659 | if ((reg = be_i386_get_unused_DR(ctx, &pr)) == -1) return 0; | |
2bc33389 | 660 | *pr = (DWORD)addr; |
deca2502 EP |
661 | if (type != be_xpoint_watch_exec) switch (size) |
662 | { | |
663 | case 4: bits |= DR7_LEN_4; break; | |
664 | case 2: bits |= DR7_LEN_2; break; | |
665 | case 1: bits |= DR7_LEN_1; break; | |
666 | default: return 0; | |
667 | } | |
668 | *val = reg; | |
669 | /* clear old values */ | |
670 | ctx->Dr7 &= ~(0x0F << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg)); | |
671 | /* set the correct ones */ | |
672 | ctx->Dr7 |= bits << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg); | |
673 | ctx->Dr7 |= DR7_ENABLE_MASK(reg) | DR7_LOCAL_SLOWDOWN; | |
674 | break; | |
675 | default: | |
676 | dbg_printf("Unknown bp type %c\n", type); | |
677 | return 0; | |
678 | } | |
679 | return 1; | |
680 | } | |
681 | ||
f16f847c | 682 | static unsigned be_i386_remove_Xpoint(HANDLE hProcess, const struct be_process_io* pio, |
d6be549a EP |
683 | CONTEXT* ctx, enum be_xpoint_type type, |
684 | void* addr, unsigned long val, unsigned size) | |
deca2502 | 685 | { |
f0279726 | 686 | SIZE_T sz; |
deca2502 EP |
687 | unsigned char ch; |
688 | ||
689 | switch (type) | |
690 | { | |
691 | case be_xpoint_break: | |
692 | if (size != 0) return 0; | |
d6be549a | 693 | if (!pio->read(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0; |
deca2502 EP |
694 | if (ch != (unsigned char)0xCC) |
695 | WINE_FIXME("Cannot get back %02x instead of 0xCC at %08lx\n", | |
696 | ch, (unsigned long)addr); | |
697 | ch = (unsigned char)val; | |
d6be549a | 698 | if (!pio->write(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0; |
deca2502 EP |
699 | break; |
700 | case be_xpoint_watch_exec: | |
701 | case be_xpoint_watch_read: | |
702 | case be_xpoint_watch_write: | |
703 | /* simply disable the entry */ | |
704 | ctx->Dr7 &= ~DR7_ENABLE_MASK(val); | |
705 | break; | |
706 | default: | |
707 | dbg_printf("Unknown bp type %c\n", type); | |
708 | return 0; | |
709 | } | |
710 | return 1; | |
711 | } | |
712 | ||
713 | static unsigned be_i386_is_watchpoint_set(const CONTEXT* ctx, unsigned idx) | |
714 | { | |
715 | return ctx->Dr6 & (1 << idx); | |
716 | } | |
717 | ||
718 | static void be_i386_clear_watchpoint(CONTEXT* ctx, unsigned idx) | |
719 | { | |
720 | ctx->Dr6 &= ~(1 << idx); | |
721 | } | |
722 | ||
723 | static int be_i386_adjust_pc_for_break(CONTEXT* ctx, BOOL way) | |
724 | { | |
725 | if (way) | |
726 | { | |
727 | ctx->Eip--; | |
728 | return -1; | |
729 | } | |
730 | ctx->Eip++; | |
731 | return 1; | |
732 | } | |
733 | ||
734 | static int be_i386_fetch_integer(const struct dbg_lvalue* lvalue, unsigned size, | |
9daaab54 | 735 | unsigned ext_sign, LONGLONG* ret) |
deca2502 EP |
736 | { |
737 | if (size != 1 && size != 2 && size != 4 && size != 8) return FALSE; | |
738 | ||
739 | memset(ret, 0, sizeof(*ret)); /* clear unread bytes */ | |
740 | /* FIXME: this assumes that debuggee and debugger use the same | |
741 | * integral representation | |
742 | */ | |
743 | if (!memory_read_value(lvalue, size, ret)) return FALSE; | |
744 | ||
745 | /* propagate sign information */ | |
746 | if (ext_sign && size < 8 && (*ret >> (size * 8 - 1)) != 0) | |
747 | { | |
9daaab54 | 748 | ULONGLONG neg = -1; |
deca2502 EP |
749 | *ret |= neg << (size * 8); |
750 | } | |
751 | return TRUE; | |
752 | } | |
753 | ||
754 | static int be_i386_fetch_float(const struct dbg_lvalue* lvalue, unsigned size, | |
755 | long double* ret) | |
756 | { | |
7d3a9c6f | 757 | char tmp[sizeof(long double)]; |
deca2502 EP |
758 | |
759 | /* FIXME: this assumes that debuggee and debugger use the same | |
760 | * representation for reals | |
761 | */ | |
762 | if (!memory_read_value(lvalue, size, tmp)) return FALSE; | |
763 | ||
764 | /* float & double types have to be promoted to a long double */ | |
765 | switch (size) | |
766 | { | |
767 | case sizeof(float): *ret = *(float*)tmp; break; | |
768 | case sizeof(double): *ret = *(double*)tmp; break; | |
769 | case sizeof(long double): *ret = *(long double*)tmp; break; | |
770 | default: return FALSE; | |
771 | } | |
772 | return TRUE; | |
773 | } | |
774 | ||
775 | struct backend_cpu be_i386 = | |
776 | { | |
a775d41c | 777 | IMAGE_FILE_MACHINE_I386, |
0d7a6f13 | 778 | 4, |
deca2502 EP |
779 | be_i386_linearize, |
780 | be_i386_build_addr, | |
781 | be_i386_get_addr, | |
d6d8682d | 782 | be_i386_get_register_info, |
deca2502 EP |
783 | be_i386_single_step, |
784 | be_i386_print_context, | |
785 | be_i386_print_segment_info, | |
ea21a327 | 786 | be_i386_ctx, |
deca2502 EP |
787 | be_i386_is_step_over_insn, |
788 | be_i386_is_function_return, | |
789 | be_i386_is_break_insn, | |
790 | be_i386_is_func_call, | |
ae9f8604 | 791 | be_i386_is_jump, |
deca2502 EP |
792 | be_i386_disasm_one_insn, |
793 | be_i386_insert_Xpoint, | |
794 | be_i386_remove_Xpoint, | |
795 | be_i386_is_watchpoint_set, | |
796 | be_i386_clear_watchpoint, | |
797 | be_i386_adjust_pc_for_break, | |
798 | be_i386_fetch_integer, | |
799 | be_i386_fetch_float, | |
800 | }; | |
801 | #endif |