Eric Pouech | deca250 | 2004-06-04 00:59:16 +0000 | [diff] [blame] | 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 |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 19 | */ |
| 20 | |
| 21 | #include "debugger.h" |
| 22 | #include "wine/debug.h" |
| 23 | |
| 24 | WINE_DEFAULT_DEBUG_CHANNEL(winedbg); |
| 25 | |
| 26 | #ifdef __i386__ |
| 27 | |
| 28 | /* debugger/db_disasm.c */ |
| 29 | extern void be_i386_disasm_one_insn(ADDRESS* addr, int display); |
| 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; |
| 43 | if (GetThreadSelectorEntry(hThread, sel, &le)) |
| 44 | return le.HighWord.Bits.Default_Big ? AddrMode1632 : AddrMode1616; |
| 45 | /* selector doesn't exist */ |
| 46 | return -1; |
| 47 | } |
| 48 | |
| 49 | static void* be_i386_linearize(HANDLE hThread, const ADDRESS* addr) |
| 50 | { |
| 51 | LDT_ENTRY le; |
| 52 | |
| 53 | switch (addr->Mode) |
| 54 | { |
| 55 | case AddrModeReal: |
| 56 | return (void*)((DWORD)(LOWORD(addr->Segment) << 4) + addr->Offset); |
| 57 | case AddrMode1632: |
| 58 | if (!(addr->Segment & 4) || ((addr->Segment >> 3) < 17)) |
| 59 | return (void*)addr->Offset; |
| 60 | /* fall through */ |
| 61 | case AddrMode1616: |
| 62 | if (!GetThreadSelectorEntry(hThread, addr->Segment, &le)) return NULL; |
| 63 | return (void*)((le.HighWord.Bits.BaseHi << 24) + |
| 64 | (le.HighWord.Bits.BaseMid << 16) + le.BaseLow + addr->Offset); |
| 65 | break; |
| 66 | case AddrModeFlat: |
| 67 | return (void*)addr->Offset; |
| 68 | } |
| 69 | return NULL; |
| 70 | } |
| 71 | |
| 72 | static unsigned be_i386_build_addr(HANDLE hThread, const CONTEXT* ctx, ADDRESS* addr, |
| 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, |
| 99 | enum be_cpu_addr bca, ADDRESS* addr) |
| 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 | |
| 113 | static void be_i386_single_step(CONTEXT* ctx, unsigned enable) |
| 114 | { |
| 115 | if (enable) ctx->EFlags |= STEP_FLAG; |
| 116 | else ctx->EFlags &= ~STEP_FLAG; |
| 117 | } |
| 118 | |
| 119 | static void be_i386_print_context(HANDLE hThread, const CONTEXT* ctx) |
| 120 | { |
| 121 | char buf[33]; |
| 122 | char* pt; |
| 123 | |
| 124 | dbg_printf("Register dump:\n"); |
| 125 | |
| 126 | /* First get the segment registers out of the way */ |
| 127 | dbg_printf(" CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x", |
| 128 | (WORD)ctx->SegCs, (WORD)ctx->SegSs, |
| 129 | (WORD)ctx->SegDs, (WORD)ctx->SegEs, |
| 130 | (WORD)ctx->SegFs, (WORD)ctx->SegGs); |
| 131 | |
| 132 | strcpy(buf, " - 00 - - - "); |
| 133 | pt = buf + strlen(buf) - 1; |
| 134 | if (ctx->EFlags & 0x00000001) *pt-- = 'C'; /* Carry Flag */ |
| 135 | if (ctx->EFlags & 0x00000002) *pt-- = '1'; |
| 136 | if (ctx->EFlags & 0x00000004) *pt-- = 'P'; /* Parity Flag */ |
| 137 | if (ctx->EFlags & 0x00000008) *pt-- = '-'; |
| 138 | if (ctx->EFlags & 0x00000010) *pt-- = 'A'; /* Auxiliary Carry Flag */ |
| 139 | if (ctx->EFlags & 0x00000020) *pt-- = '-'; |
| 140 | if (ctx->EFlags & 0x00000040) *pt-- = 'Z'; /* Zero Flag */ |
| 141 | if (ctx->EFlags & 0x00000080) *pt-- = 'S'; /* Sign Flag */ |
| 142 | if (ctx->EFlags & 0x00000100) *pt-- = 'T'; /* Trap/Trace Flag */ |
| 143 | if (ctx->EFlags & 0x00000200) *pt-- = 'I'; /* Interupt Enable Flag */ |
| 144 | if (ctx->EFlags & 0x00000400) *pt-- = 'D'; /* Direction Indicator */ |
| 145 | if (ctx->EFlags & 0x00000800) *pt-- = 'O'; /* Overflow flags */ |
| 146 | if (ctx->EFlags & 0x00001000) *pt-- = '1'; /* I/O Privilege Level */ |
| 147 | if (ctx->EFlags & 0x00002000) *pt-- = '1'; /* I/O Privilege Level */ |
| 148 | if (ctx->EFlags & 0x00004000) *pt-- = 'N'; /* Nested Task Flag */ |
| 149 | if (ctx->EFlags & 0x00008000) *pt-- = '-'; |
| 150 | if (ctx->EFlags & 0x00010000) *pt-- = 'R'; /* Resume Flag */ |
| 151 | if (ctx->EFlags & 0x00020000) *pt-- = 'V'; /* Vritual Mode Flag */ |
| 152 | if (ctx->EFlags & 0x00040000) *pt-- = 'a'; /* Alignment Check Flag */ |
| 153 | |
| 154 | switch (get_selector_type(hThread, ctx, ctx->SegCs)) |
| 155 | { |
| 156 | case AddrMode1616: |
| 157 | case AddrModeReal: |
| 158 | dbg_printf("\n IP:%04x SP:%04x BP:%04x FLAGS:%04x(%s)\n", |
| 159 | LOWORD(ctx->Eip), LOWORD(ctx->Esp), |
| 160 | LOWORD(ctx->Ebp), LOWORD(ctx->EFlags), buf); |
| 161 | dbg_printf(" AX:%04x BX:%04x CX:%04x DX:%04x SI:%04x DI:%04x\n", |
| 162 | LOWORD(ctx->Eax), LOWORD(ctx->Ebx), |
| 163 | LOWORD(ctx->Ecx), LOWORD(ctx->Edx), |
| 164 | LOWORD(ctx->Esi), LOWORD(ctx->Edi)); |
| 165 | break; |
| 166 | case AddrModeFlat: |
| 167 | case AddrMode1632: |
| 168 | dbg_printf("\n EIP:%08lx ESP:%08lx EBP:%08lx EFLAGS:%08lx(%s)\n", |
| 169 | ctx->Eip, ctx->Esp, ctx->Ebp, ctx->EFlags, buf); |
| 170 | dbg_printf(" EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx\n", |
| 171 | ctx->Eax, ctx->Ebx, ctx->Ecx, ctx->Edx); |
| 172 | dbg_printf(" ESI:%08lx EDI:%08lx\n", |
| 173 | ctx->Esi, ctx->Edi); |
| 174 | break; |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | static void be_i386_print_segment_info(HANDLE hThread, const CONTEXT* ctx) |
| 179 | { |
| 180 | if (get_selector_type(hThread, ctx, ctx->SegCs) == AddrMode1616) |
| 181 | { |
| 182 | info_win32_segments(ctx->SegDs >> 3, 1); |
| 183 | if (ctx->SegEs != ctx->SegDs) info_win32_segments(ctx->SegEs >> 3, 1); |
| 184 | } |
| 185 | info_win32_segments(ctx->SegFs >> 3, 1); |
| 186 | } |
| 187 | |
| 188 | static struct dbg_internal_var be_i386_ctx[] = |
| 189 | { |
| 190 | {CV_REG_AL, "AL", (DWORD*)FIELD_OFFSET(CONTEXT, Eax), dbg_itype_unsigned_char_int}, |
| 191 | {CV_REG_CL, "CL", (DWORD*)FIELD_OFFSET(CONTEXT, Ecx), dbg_itype_unsigned_char_int}, |
| 192 | {CV_REG_DL, "DL", (DWORD*)FIELD_OFFSET(CONTEXT, Edx), dbg_itype_unsigned_char_int}, |
| 193 | {CV_REG_BL, "BL", (DWORD*)FIELD_OFFSET(CONTEXT, Ebx), dbg_itype_unsigned_char_int}, |
| 194 | {CV_REG_AH, "AH", (DWORD*)(FIELD_OFFSET(CONTEXT, Eax)+1), dbg_itype_unsigned_char_int}, |
| 195 | {CV_REG_CH, "CH", (DWORD*)(FIELD_OFFSET(CONTEXT, Ecx)+1), dbg_itype_unsigned_char_int}, |
| 196 | {CV_REG_DH, "DH", (DWORD*)(FIELD_OFFSET(CONTEXT, Edx)+1), dbg_itype_unsigned_char_int}, |
| 197 | {CV_REG_BH, "BH", (DWORD*)(FIELD_OFFSET(CONTEXT, Ebx)+1), dbg_itype_unsigned_char_int}, |
| 198 | {CV_REG_AX, "AX", (DWORD*)FIELD_OFFSET(CONTEXT, Eax), dbg_itype_unsigned_short_int}, |
| 199 | {CV_REG_CX, "CX", (DWORD*)FIELD_OFFSET(CONTEXT, Ecx), dbg_itype_unsigned_short_int}, |
| 200 | {CV_REG_DX, "DX", (DWORD*)FIELD_OFFSET(CONTEXT, Edx), dbg_itype_unsigned_short_int}, |
| 201 | {CV_REG_BX, "BX", (DWORD*)FIELD_OFFSET(CONTEXT, Ebx), dbg_itype_unsigned_short_int}, |
| 202 | {CV_REG_SP, "SP", (DWORD*)FIELD_OFFSET(CONTEXT, Esp), dbg_itype_unsigned_short_int}, |
| 203 | {CV_REG_BP, "BP", (DWORD*)FIELD_OFFSET(CONTEXT, Ebp), dbg_itype_unsigned_short_int}, |
| 204 | {CV_REG_SI, "SI", (DWORD*)FIELD_OFFSET(CONTEXT, Esi), dbg_itype_unsigned_short_int}, |
| 205 | {CV_REG_DI, "DI", (DWORD*)FIELD_OFFSET(CONTEXT, Edi), dbg_itype_unsigned_short_int}, |
| 206 | {CV_REG_EAX, "EAX", (DWORD*)FIELD_OFFSET(CONTEXT, Eax), dbg_itype_unsigned_int}, |
| 207 | {CV_REG_ECX, "ECX", (DWORD*)FIELD_OFFSET(CONTEXT, Ecx), dbg_itype_unsigned_int}, |
| 208 | {CV_REG_EDX, "EDX", (DWORD*)FIELD_OFFSET(CONTEXT, Edx), dbg_itype_unsigned_int}, |
| 209 | {CV_REG_EBX, "EBX", (DWORD*)FIELD_OFFSET(CONTEXT, Ebx), dbg_itype_unsigned_int}, |
| 210 | {CV_REG_ESP, "ESP", (DWORD*)FIELD_OFFSET(CONTEXT, Esp), dbg_itype_unsigned_int}, |
| 211 | {CV_REG_EBP, "EBP", (DWORD*)FIELD_OFFSET(CONTEXT, Ebp), dbg_itype_unsigned_int}, |
| 212 | {CV_REG_ESI, "ESI", (DWORD*)FIELD_OFFSET(CONTEXT, Esi), dbg_itype_unsigned_int}, |
| 213 | {CV_REG_EDI, "EDI", (DWORD*)FIELD_OFFSET(CONTEXT, Edi), dbg_itype_unsigned_int}, |
| 214 | {CV_REG_ES, "ES", (DWORD*)FIELD_OFFSET(CONTEXT, SegEs), dbg_itype_unsigned_short_int}, |
| 215 | {CV_REG_CS, "CS", (DWORD*)FIELD_OFFSET(CONTEXT, SegCs), dbg_itype_unsigned_short_int}, |
| 216 | {CV_REG_SS, "SS", (DWORD*)FIELD_OFFSET(CONTEXT, SegSs), dbg_itype_unsigned_short_int}, |
| 217 | {CV_REG_DS, "DS", (DWORD*)FIELD_OFFSET(CONTEXT, SegDs), dbg_itype_unsigned_short_int}, |
| 218 | {CV_REG_FS, "FS", (DWORD*)FIELD_OFFSET(CONTEXT, SegFs), dbg_itype_unsigned_short_int}, |
| 219 | {CV_REG_GS, "GS", (DWORD*)FIELD_OFFSET(CONTEXT, SegGs), dbg_itype_unsigned_short_int}, |
| 220 | {CV_REG_IP, "IP", (DWORD*)FIELD_OFFSET(CONTEXT, Eip), dbg_itype_unsigned_short_int}, |
| 221 | {CV_REG_FLAGS, "FLAGS", (DWORD*)FIELD_OFFSET(CONTEXT, EFlags), dbg_itype_unsigned_short_int}, |
| 222 | {CV_REG_EIP, "EIP", (DWORD*)FIELD_OFFSET(CONTEXT, Eip), dbg_itype_unsigned_int}, |
| 223 | {CV_REG_EFLAGS, "EFLAGS", (DWORD*)FIELD_OFFSET(CONTEXT, EFlags), dbg_itype_unsigned_int}, |
| 224 | {0, NULL, 0, dbg_itype_none} |
| 225 | }; |
| 226 | |
| 227 | static const struct dbg_internal_var* be_i386_init_registers(CONTEXT* ctx) |
| 228 | { |
| 229 | struct dbg_internal_var* div; |
| 230 | |
| 231 | for (div = be_i386_ctx; div->name; div++) |
| 232 | div->pval = (DWORD*)((char*)ctx + (DWORD)div->pval); |
| 233 | return be_i386_ctx; |
| 234 | } |
| 235 | |
| 236 | static unsigned be_i386_is_step_over_insn(const void* insn) |
| 237 | { |
| 238 | BYTE ch; |
| 239 | |
| 240 | for (;;) |
| 241 | { |
| 242 | if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE; |
| 243 | |
| 244 | switch (ch) |
| 245 | { |
| 246 | /* Skip all prefixes */ |
| 247 | case 0x2e: /* cs: */ |
| 248 | case 0x36: /* ss: */ |
| 249 | case 0x3e: /* ds: */ |
| 250 | case 0x26: /* es: */ |
| 251 | case 0x64: /* fs: */ |
| 252 | case 0x65: /* gs: */ |
| 253 | case 0x66: /* opcode size prefix */ |
| 254 | case 0x67: /* addr size prefix */ |
| 255 | case 0xf0: /* lock */ |
| 256 | case 0xf2: /* repne */ |
| 257 | case 0xf3: /* repe */ |
| 258 | insn = (const char*)insn + 1; |
| 259 | continue; |
| 260 | |
| 261 | /* Handle call instructions */ |
| 262 | case 0xcd: /* int <intno> */ |
| 263 | case 0xe8: /* call <offset> */ |
| 264 | case 0x9a: /* lcall <seg>:<off> */ |
| 265 | return TRUE; |
| 266 | |
| 267 | case 0xff: /* call <regmodrm> */ |
| 268 | if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch))) |
| 269 | return FALSE; |
| 270 | return (((ch & 0x38) == 0x10) || ((ch & 0x38) == 0x18)); |
| 271 | |
| 272 | /* Handle string instructions */ |
| 273 | case 0x6c: /* insb */ |
| 274 | case 0x6d: /* insw */ |
| 275 | case 0x6e: /* outsb */ |
| 276 | case 0x6f: /* outsw */ |
| 277 | case 0xa4: /* movsb */ |
| 278 | case 0xa5: /* movsw */ |
| 279 | case 0xa6: /* cmpsb */ |
| 280 | case 0xa7: /* cmpsw */ |
| 281 | case 0xaa: /* stosb */ |
| 282 | case 0xab: /* stosw */ |
| 283 | case 0xac: /* lodsb */ |
| 284 | case 0xad: /* lodsw */ |
| 285 | case 0xae: /* scasb */ |
| 286 | case 0xaf: /* scasw */ |
| 287 | return TRUE; |
| 288 | |
| 289 | default: |
| 290 | return FALSE; |
| 291 | } |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | static unsigned be_i386_is_function_return(const void* insn) |
| 296 | { |
| 297 | BYTE ch; |
| 298 | |
| 299 | if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE; |
| 300 | return (ch == 0xC2) || (ch == 0xC3); |
| 301 | } |
| 302 | |
| 303 | static unsigned be_i386_is_break_insn(const void* insn) |
| 304 | { |
| 305 | BYTE c; |
| 306 | |
| 307 | if (!dbg_read_memory(insn, &c, 1)) return FALSE; |
| 308 | return c == 0xCC; |
| 309 | } |
| 310 | |
| 311 | static unsigned be_i386_is_func_call(const void* insn, ADDRESS* callee) |
| 312 | { |
| 313 | BYTE ch; |
| 314 | int delta; |
| 315 | |
| 316 | dbg_read_memory(insn, &ch, sizeof(ch)); |
| 317 | if (ch == 0xe8) |
| 318 | { |
| 319 | dbg_read_memory((const char*)insn + 1, &delta, sizeof(delta)); |
| 320 | |
| 321 | callee->Mode = AddrModeFlat; |
| 322 | callee->Offset = (DWORD)insn; |
| 323 | be_i386_disasm_one_insn(callee, FALSE); |
| 324 | callee->Offset += delta; |
| 325 | |
| 326 | return TRUE; |
| 327 | } |
| 328 | return FALSE; |
| 329 | } |
| 330 | |
| 331 | #define DR7_CONTROL_SHIFT 16 |
| 332 | #define DR7_CONTROL_SIZE 4 |
| 333 | |
| 334 | #define DR7_RW_EXECUTE (0x0) |
| 335 | #define DR7_RW_WRITE (0x1) |
| 336 | #define DR7_RW_READ (0x3) |
| 337 | |
| 338 | #define DR7_LEN_1 (0x0) |
| 339 | #define DR7_LEN_2 (0x4) |
| 340 | #define DR7_LEN_4 (0xC) |
| 341 | |
| 342 | #define DR7_LOCAL_ENABLE_SHIFT 0 |
| 343 | #define DR7_GLOBAL_ENABLE_SHIFT 1 |
| 344 | #define DR7_ENABLE_SIZE 2 |
| 345 | |
| 346 | #define DR7_LOCAL_ENABLE_MASK (0x55) |
| 347 | #define DR7_GLOBAL_ENABLE_MASK (0xAA) |
| 348 | |
| 349 | #define DR7_CONTROL_RESERVED (0xFC00) |
| 350 | #define DR7_LOCAL_SLOWDOWN (0x100) |
| 351 | #define DR7_GLOBAL_SLOWDOWN (0x200) |
| 352 | |
| 353 | #define DR7_ENABLE_MASK(dr) (1<<(DR7_LOCAL_ENABLE_SHIFT+DR7_ENABLE_SIZE*(dr))) |
| 354 | #define IS_DR7_SET(ctrl,dr) ((ctrl)&DR7_ENABLE_MASK(dr)) |
| 355 | |
| 356 | static inline int be_i386_get_unused_DR(CONTEXT* ctx, unsigned long** r) |
| 357 | { |
| 358 | if (!IS_DR7_SET(ctx->Dr7, 0)) |
| 359 | { |
| 360 | *r = &ctx->Dr0; |
| 361 | return 0; |
| 362 | } |
| 363 | if (!IS_DR7_SET(ctx->Dr7, 1)) |
| 364 | { |
| 365 | *r = &ctx->Dr1; |
| 366 | return 1; |
| 367 | } |
| 368 | if (!IS_DR7_SET(ctx->Dr7, 2)) |
| 369 | { |
| 370 | *r = &ctx->Dr2; |
| 371 | return 2; |
| 372 | } |
| 373 | if (!IS_DR7_SET(ctx->Dr7, 3)) |
| 374 | { |
| 375 | *r = &ctx->Dr3; |
| 376 | return 3; |
| 377 | } |
| 378 | dbg_printf("All hardware registers have been used\n"); |
| 379 | |
| 380 | return -1; |
| 381 | } |
| 382 | |
| 383 | static unsigned be_i386_insert_Xpoint(HANDLE hProcess, CONTEXT* ctx, |
| 384 | enum be_xpoint_type type, void* addr, |
| 385 | unsigned long* val, unsigned size) |
| 386 | { |
| 387 | unsigned char ch; |
| 388 | unsigned long sz; |
| 389 | unsigned long* pr; |
| 390 | int reg; |
| 391 | unsigned long bits; |
| 392 | |
| 393 | switch (type) |
| 394 | { |
| 395 | case be_xpoint_break: |
| 396 | if (size != 0) return 0; |
| 397 | if (!ReadProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0; |
| 398 | *val = ch; |
| 399 | ch = 0xcc; |
| 400 | if (!WriteProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0; |
| 401 | break; |
| 402 | case be_xpoint_watch_exec: |
| 403 | bits = DR7_RW_EXECUTE; |
| 404 | goto hw_bp; |
| 405 | case be_xpoint_watch_read: |
| 406 | bits = DR7_RW_READ; |
| 407 | goto hw_bp; |
| 408 | case be_xpoint_watch_write: |
| 409 | bits = DR7_RW_WRITE; |
| 410 | hw_bp: |
| 411 | if ((reg = be_i386_get_unused_DR(ctx, &pr)) == -1) return 0; |
| 412 | *pr = (unsigned long)addr; |
| 413 | if (type != be_xpoint_watch_exec) switch (size) |
| 414 | { |
| 415 | case 4: bits |= DR7_LEN_4; break; |
| 416 | case 2: bits |= DR7_LEN_2; break; |
| 417 | case 1: bits |= DR7_LEN_1; break; |
| 418 | default: return 0; |
| 419 | } |
| 420 | *val = reg; |
| 421 | /* clear old values */ |
| 422 | ctx->Dr7 &= ~(0x0F << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg)); |
| 423 | /* set the correct ones */ |
| 424 | ctx->Dr7 |= bits << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg); |
| 425 | ctx->Dr7 |= DR7_ENABLE_MASK(reg) | DR7_LOCAL_SLOWDOWN; |
| 426 | break; |
| 427 | default: |
| 428 | dbg_printf("Unknown bp type %c\n", type); |
| 429 | return 0; |
| 430 | } |
| 431 | return 1; |
| 432 | } |
| 433 | |
| 434 | static unsigned be_i386_remove_Xpoint(HANDLE hProcess, CONTEXT* ctx, |
| 435 | enum be_xpoint_type type, void* addr, |
| 436 | unsigned long val, unsigned size) |
| 437 | { |
| 438 | unsigned long sz; |
| 439 | unsigned char ch; |
| 440 | |
| 441 | switch (type) |
| 442 | { |
| 443 | case be_xpoint_break: |
| 444 | if (size != 0) return 0; |
| 445 | if (!ReadProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0; |
| 446 | if (ch != (unsigned char)0xCC) |
| 447 | WINE_FIXME("Cannot get back %02x instead of 0xCC at %08lx\n", |
| 448 | ch, (unsigned long)addr); |
| 449 | ch = (unsigned char)val; |
| 450 | if (!WriteProcessMemory(hProcess, addr, &ch, 1, &sz) || sz != 1) return 0; |
| 451 | break; |
| 452 | case be_xpoint_watch_exec: |
| 453 | case be_xpoint_watch_read: |
| 454 | case be_xpoint_watch_write: |
| 455 | /* simply disable the entry */ |
| 456 | ctx->Dr7 &= ~DR7_ENABLE_MASK(val); |
| 457 | break; |
| 458 | default: |
| 459 | dbg_printf("Unknown bp type %c\n", type); |
| 460 | return 0; |
| 461 | } |
| 462 | return 1; |
| 463 | } |
| 464 | |
| 465 | static unsigned be_i386_is_watchpoint_set(const CONTEXT* ctx, unsigned idx) |
| 466 | { |
| 467 | return ctx->Dr6 & (1 << idx); |
| 468 | } |
| 469 | |
| 470 | static void be_i386_clear_watchpoint(CONTEXT* ctx, unsigned idx) |
| 471 | { |
| 472 | ctx->Dr6 &= ~(1 << idx); |
| 473 | } |
| 474 | |
| 475 | static int be_i386_adjust_pc_for_break(CONTEXT* ctx, BOOL way) |
| 476 | { |
| 477 | if (way) |
| 478 | { |
| 479 | ctx->Eip--; |
| 480 | return -1; |
| 481 | } |
| 482 | ctx->Eip++; |
| 483 | return 1; |
| 484 | } |
| 485 | |
| 486 | static int be_i386_fetch_integer(const struct dbg_lvalue* lvalue, unsigned size, |
| 487 | unsigned ext_sign, long long int* ret) |
| 488 | { |
| 489 | if (size != 1 && size != 2 && size != 4 && size != 8) return FALSE; |
| 490 | |
| 491 | memset(ret, 0, sizeof(*ret)); /* clear unread bytes */ |
| 492 | /* FIXME: this assumes that debuggee and debugger use the same |
| 493 | * integral representation |
| 494 | */ |
| 495 | if (!memory_read_value(lvalue, size, ret)) return FALSE; |
| 496 | |
| 497 | /* propagate sign information */ |
| 498 | if (ext_sign && size < 8 && (*ret >> (size * 8 - 1)) != 0) |
| 499 | { |
| 500 | long long unsigned int neg = -1; |
| 501 | *ret |= neg << (size * 8); |
| 502 | } |
| 503 | return TRUE; |
| 504 | } |
| 505 | |
| 506 | static int be_i386_fetch_float(const struct dbg_lvalue* lvalue, unsigned size, |
| 507 | long double* ret) |
| 508 | { |
| 509 | char tmp[12]; |
| 510 | |
| 511 | /* FIXME: this assumes that debuggee and debugger use the same |
| 512 | * representation for reals |
| 513 | */ |
| 514 | if (!memory_read_value(lvalue, size, tmp)) return FALSE; |
| 515 | |
| 516 | /* float & double types have to be promoted to a long double */ |
| 517 | switch (size) |
| 518 | { |
| 519 | case sizeof(float): *ret = *(float*)tmp; break; |
| 520 | case sizeof(double): *ret = *(double*)tmp; break; |
| 521 | case sizeof(long double): *ret = *(long double*)tmp; break; |
| 522 | default: return FALSE; |
| 523 | } |
| 524 | return TRUE; |
| 525 | } |
| 526 | |
| 527 | struct backend_cpu be_i386 = |
| 528 | { |
| 529 | be_i386_linearize, |
| 530 | be_i386_build_addr, |
| 531 | be_i386_get_addr, |
| 532 | be_i386_single_step, |
| 533 | be_i386_print_context, |
| 534 | be_i386_print_segment_info, |
| 535 | be_i386_init_registers, |
| 536 | be_i386_is_step_over_insn, |
| 537 | be_i386_is_function_return, |
| 538 | be_i386_is_break_insn, |
| 539 | be_i386_is_func_call, |
| 540 | be_i386_disasm_one_insn, |
| 541 | be_i386_insert_Xpoint, |
| 542 | be_i386_remove_Xpoint, |
| 543 | be_i386_is_watchpoint_set, |
| 544 | be_i386_clear_watchpoint, |
| 545 | be_i386_adjust_pc_for_break, |
| 546 | be_i386_fetch_integer, |
| 547 | be_i386_fetch_float, |
| 548 | }; |
| 549 | #endif |