From 1903f0e64bb6f600388af42a7af7aff02e70bdef Mon Sep 17 00:00:00 2001 From: Doug Evans Date: Tue, 17 Feb 2009 06:14:17 +0000 Subject: [PATCH] * amd64-tdep.c (amd64_skip_prefixes): Renamed from skip_prefixes. All callers updated. (amd64_get_insn_details): Handle more 3-byte opcode insns. (amd64_breakpoint_p): Delete. (amd64_displaced_step_fixup): When fixing up after stepping an int3, don't back up pc to the start of the int3. * i386-tdep.c: #include opcode/i386.h. (i386_skip_prefixes): New function. (i386_absolute_jmp_p): Constify argument. (i386_absolute_call_p,i386_ret_p,i386_call_p,i386_syscall_p): Ditto. (i386_breakpoint_p): Delete. (i386_displaced_step_fixup): Handle unnecessary or redundant prefixes. When fixing up after stepping an int3, don't back up pc to the start of the int3. * gdb.arch/amd64-disp-step.S (test_int3): New test. * gdb.arch/amd64-disp-step.exp (test_int3): New test. * gdb.arch/i386-disp-step.S (test_prefixed_abs_jump): New test. (test_prefixed_syscall,test_int3): New tests. * gdb.arch/i386-disp-step.exp (test_prefixed_abs_jump): New test. (test_prefixed_syscall,test_int3): New tests. --- gdb/ChangeLog | 17 +++++ gdb/amd64-tdep.c | 43 ++++------- gdb/i386-tdep.c | 89 +++++++++++++++------- gdb/testsuite/ChangeLog | 9 +++ gdb/testsuite/gdb.arch/amd64-disp-step.S | 28 +++++++ gdb/testsuite/gdb.arch/amd64-disp-step.exp | 20 +++++ gdb/testsuite/gdb.arch/i386-disp-step.S | 65 +++++++++++++++- gdb/testsuite/gdb.arch/i386-disp-step.exp | 58 ++++++++++++++ 8 files changed, 271 insertions(+), 58 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 71c1f6a0e9..424ae5484f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,20 @@ +2009-02-16 Doug Evans + + * amd64-tdep.c (amd64_skip_prefixes): Renamed from skip_prefixes. + All callers updated. + (amd64_get_insn_details): Handle more 3-byte opcode insns. + (amd64_breakpoint_p): Delete. + (amd64_displaced_step_fixup): When fixing up after stepping an int3, + don't back up pc to the start of the int3. + * i386-tdep.c: #include opcode/i386.h. + (i386_skip_prefixes): New function. + (i386_absolute_jmp_p): Constify argument. + (i386_absolute_call_p,i386_ret_p,i386_call_p,i386_syscall_p): Ditto. + (i386_breakpoint_p): Delete. + (i386_displaced_step_fixup): Handle unnecessary or redundant prefixes. + When fixing up after stepping an int3, don't back up pc to the start + of the int3. + 2009-02-16 Pedro Alves * corelow.c (core_close): Don't hardcode the core's pid. diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index ad26493c27..4eef55d100 100644 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -805,7 +805,7 @@ rex_prefix_p (gdb_byte pfx) about falling off the end of the buffer. */ static gdb_byte * -skip_prefixes (gdb_byte *insn) +amd64_skip_prefixes (gdb_byte *insn) { while (1) { @@ -974,7 +974,7 @@ amd64_get_insn_details (gdb_byte *insn, struct amd64_insn *details) details->modrm_offset = -1; /* Skip legacy instruction prefixes. */ - insn = skip_prefixes (insn); + insn = amd64_skip_prefixes (insn); /* Skip REX instruction prefix. */ if (rex_prefix_p (*insn)) @@ -992,13 +992,21 @@ amd64_get_insn_details (gdb_byte *insn, struct amd64_insn *details) need_modrm = twobyte_has_modrm[*insn]; /* Check for three-byte opcode. */ - if (*insn == 0x38 || *insn == 0x3a) + switch (*insn) { + case 0x24: + case 0x25: + case 0x38: + case 0x3a: + case 0x7a: + case 0x7b: ++insn; details->opcode_len = 3; + break; + default: + details->opcode_len = 2; + break; } - else - details->opcode_len = 2; } else { @@ -1217,14 +1225,6 @@ amd64_call_p (const struct amd64_insn *details) return 0; } -static int -amd64_breakpoint_p (const struct amd64_insn *details) -{ - const gdb_byte *insn = &details->raw_insn[details->opcode_offset]; - - return insn[0] == 0xcc; /* int 3 */ -} - /* Return non-zero if INSN is a system call, and set *LENGTHP to its length in bytes. Otherwise, return zero. */ @@ -1323,20 +1323,9 @@ amd64_displaced_step_fixup (struct gdbarch *gdbarch, { ULONGEST rip = orig_rip - insn_offset; - /* If we have stepped over a breakpoint, set %rip to - point at the breakpoint instruction itself. - - (gdbarch_decr_pc_after_break was never something the core - of GDB should have been concerned with; arch-specific - code should be making PC values consistent before - presenting them to GDB.) */ - if (amd64_breakpoint_p (insn_details)) - { - if (debug_displaced) - fprintf_unfiltered (gdb_stdlog, - "displaced: stepped breakpoint\n"); - rip--; - } + /* If we just stepped over a breakpoint insn, we don't backup + the pc on purpose; this is to match behaviour without + stepping. */ regcache_cooked_write_unsigned (regs, AMD64_RIP_REGNUM, rip); diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index 496c4ff30a..b74270adc6 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -20,6 +20,7 @@ along with this program. If not, see . */ #include "defs.h" +#include "opcode/i386.h" #include "arch-utils.h" #include "command.h" #include "dummy-frame.h" @@ -278,9 +279,44 @@ i386_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pc, int *len) /* Displaced instruction handling. */ +/* Skip the legacy instruction prefixes in INSN. + Not all prefixes are valid for any particular insn + but we needn't care, the insn will fault if it's invalid. + The result is a pointer to the first opcode byte, + or NULL if we run off the end of the buffer. */ + +static gdb_byte * +i386_skip_prefixes (gdb_byte *insn, size_t max_len) +{ + gdb_byte *end = insn + max_len; + + while (insn < end) + { + switch (*insn) + { + case DATA_PREFIX_OPCODE: + case ADDR_PREFIX_OPCODE: + case CS_PREFIX_OPCODE: + case DS_PREFIX_OPCODE: + case ES_PREFIX_OPCODE: + case FS_PREFIX_OPCODE: + case GS_PREFIX_OPCODE: + case SS_PREFIX_OPCODE: + case LOCK_PREFIX_OPCODE: + case REPE_PREFIX_OPCODE: + case REPNE_PREFIX_OPCODE: + ++insn; + continue; + default: + return insn; + } + } + + return NULL; +} static int -i386_absolute_jmp_p (gdb_byte *insn) +i386_absolute_jmp_p (const gdb_byte *insn) { /* jmp far (absolute address in operand) */ if (insn[0] == 0xea) @@ -301,7 +337,7 @@ i386_absolute_jmp_p (gdb_byte *insn) } static int -i386_absolute_call_p (gdb_byte *insn) +i386_absolute_call_p (const gdb_byte *insn) { /* call far, absolute */ if (insn[0] == 0x9a) @@ -322,7 +358,7 @@ i386_absolute_call_p (gdb_byte *insn) } static int -i386_ret_p (gdb_byte *insn) +i386_ret_p (const gdb_byte *insn) { switch (insn[0]) { @@ -339,7 +375,7 @@ i386_ret_p (gdb_byte *insn) } static int -i386_call_p (gdb_byte *insn) +i386_call_p (const gdb_byte *insn) { if (i386_absolute_call_p (insn)) return 1; @@ -351,16 +387,11 @@ i386_call_p (gdb_byte *insn) return 0; } -static int -i386_breakpoint_p (gdb_byte *insn) -{ - return insn[0] == 0xcc; /* int 3 */ -} - /* Return non-zero if INSN is a system call, and set *LENGTHP to its length in bytes. Otherwise, return zero. */ + static int -i386_syscall_p (gdb_byte *insn, ULONGEST *lengthp) +i386_syscall_p (const gdb_byte *insn, ULONGEST *lengthp) { if (insn[0] == 0xcd) { @@ -373,6 +404,7 @@ i386_syscall_p (gdb_byte *insn, ULONGEST *lengthp) /* Fix up the state of registers and memory after having single-stepped a displaced instruction. */ + void i386_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_closure *closure, @@ -388,6 +420,8 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch, /* Since we use simple_displaced_step_copy_insn, our closure is a copy of the instruction. */ gdb_byte *insn = (gdb_byte *) closure; + /* The start of the insn, needed in case we see some prefixes. */ + gdb_byte *insn_start = insn; if (debug_displaced) fprintf_unfiltered (gdb_stdlog, @@ -401,6 +435,18 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch, /* Relocate the %eip, if necessary. */ + /* The instruction recognizers we use assume any leading prefixes + have been skipped. */ + { + /* This is the size of the buffer in closure. */ + size_t max_insn_len = gdbarch_max_insn_length (gdbarch); + gdb_byte *opcode = i386_skip_prefixes (insn, max_insn_len); + /* If there are too many prefixes, just ignore the insn. + It will fault when run. */ + if (opcode != NULL) + insn = opcode; + } + /* Except in the case of absolute or indirect jump or call instructions, or a return instruction, the new eip is relative to the displaced instruction; make it relative. Well, signal @@ -430,7 +476,7 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch, it unrelocated. Goodness help us if there are PC-relative system calls. */ if (i386_syscall_p (insn, &insn_len) - && orig_eip != to + insn_len) + && orig_eip != to + (insn - insn_start) + insn_len) { if (debug_displaced) fprintf_unfiltered (gdb_stdlog, @@ -441,20 +487,9 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch, { ULONGEST eip = (orig_eip - insn_offset) & 0xffffffffUL; - /* If we have stepped over a breakpoint, set the %eip to - point at the breakpoint instruction itself. - - (gdbarch_decr_pc_after_break was never something the core - of GDB should have been concerned with; arch-specific - code should be making PC values consistent before - presenting them to GDB.) */ - if (i386_breakpoint_p (insn)) - { - if (debug_displaced) - fprintf_unfiltered (gdb_stdlog, - "displaced: stepped breakpoint\n"); - eip--; - } + /* If we just stepped over a breakpoint insn, we don't backup + the pc on purpose; this is to match behaviour without + stepping. */ regcache_cooked_write_unsigned (regs, I386_EIP_REGNUM, eip); @@ -493,8 +528,6 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch, paddr_nz (retaddr)); } } - - #ifdef I386_REGNO_TO_SYMMETRY #error "The Sequent Symmetry is no longer supported." diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index e760e8a3ac..24dc96dad4 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2009-02-16 Doug Evans + + * gdb.arch/amd64-disp-step.S (test_int3): New test. + * gdb.arch/amd64-disp-step.exp (test_int3): New test. + * gdb.arch/i386-disp-step.S (test_prefixed_abs_jump): New test. + (test_prefixed_syscall,test_int3): New tests. + * gdb.arch/i386-disp-step.exp (test_prefixed_abs_jump): New test. + (test_prefixed_syscall,test_int3): New tests. + 2009-02-14 Vladimir Prus * lib/mi-support.exp (mi_expect_stop): Adjust the order of fields. diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step.S b/gdb/testsuite/gdb.arch/amd64-disp-step.S index 45eeb9b17d..1ce0184c8f 100644 --- a/gdb/testsuite/gdb.arch/amd64-disp-step.S +++ b/gdb/testsuite/gdb.arch/amd64-disp-step.S @@ -23,6 +23,8 @@ main: nop +/***********************************************/ + /* test call/ret */ .global test_call @@ -33,6 +35,8 @@ test_call: test_ret_end: nop +/***********************************************/ + /* test abs-jmp/rep-ret */ test_abs_jmp_setup: @@ -48,6 +52,8 @@ test_abs_jmp_return: test_rep_ret_end: nop +/***********************************************/ + /* test syscall */ .global test_syscall @@ -58,6 +64,24 @@ test_syscall: test_syscall_end: nop +/***********************************************/ + +/* Test stepping over int3. + The prefixes are pointless, but it's possible, so we exercise it. */ + + nop + .global test_int3 +test_int3: + repz + repz + int3 + nop + .global test_int3_end +test_int3_end: + nop + +/***********************************************/ + /* test rip-relative GDB picks a spare register to hold the rip-relative address. Exercise all the possibilities (rax-rdi, sans rsp). */ @@ -118,6 +142,8 @@ test_rip_rdi_end: answer: .8byte 42 +/***********************************************/ + /* all done */ done: @@ -139,6 +165,8 @@ test_call_end: test_ret: ret +/***********************************************/ + /* subroutine to help test abs-jmp/rep-ret */ test_abs_jmp_subr: diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step.exp b/gdb/testsuite/gdb.arch/amd64-disp-step.exp index 26ebe59c76..3b0f83b3fc 100644 --- a/gdb/testsuite/gdb.arch/amd64-disp-step.exp +++ b/gdb/testsuite/gdb.arch/amd64-disp-step.exp @@ -141,6 +141,26 @@ gdb_test "continue" \ ########################################## +# int3 (with prefixes) +# These don't occur in normal code, but gdb should still DTRT. + +gdb_test "break test_int3" \ + "Breakpoint.*at.* file .*$srcfile, line.*" \ + "break test_int3" +gdb_test "break test_int3_end" \ + "Breakpoint.*at.* file .*$srcfile, line.*" \ + "break test_int3_end" + +gdb_test "continue" \ + "Continuing.*Breakpoint.*, test_int3 ().*" \ + "continue to test_int3" + +gdb_test "continue" \ + "Continuing.*Breakpoint.*, test_int3_end ().*" \ + "continue to test_int3_end" + +########################################## + # Test rip-relative. # GDB picks a spare register to hold the rip-relative address. # Exercise all the possibilities (rax-rdi, sans rsp). diff --git a/gdb/testsuite/gdb.arch/i386-disp-step.S b/gdb/testsuite/gdb.arch/i386-disp-step.S index a56ff1cf0c..2d5c37289b 100644 --- a/gdb/testsuite/gdb.arch/i386-disp-step.S +++ b/gdb/testsuite/gdb.arch/i386-disp-step.S @@ -23,8 +23,11 @@ main: nop -/* test call/ret */ +/***********************************************/ +/* Test call/ret. */ + + nop .global test_call test_call: call test_call_subr @@ -33,16 +36,72 @@ test_call: test_ret_end: nop -/* test syscall */ +/***********************************************/ + +/* Absolute jump with leading prefixes. + These don't occur in normal code, but gdb should still DTRT. */ + + nop + .global test_prefixed_abs_jump +test_prefixed_abs_jump: + ds + jmp *test_prefixed_abs_jump_addr + .data +test_prefixed_abs_jump_addr: + .4byte test_prefixed_abs_jump_target + .text +test_prefixed_abs_jump_target: + nop + .global test_prefixed_abs_jump_end +test_prefixed_abs_jump_end: + nop + +/***********************************************/ + +/* Test syscall. */ - .global test_syscall mov $0x14,%eax /* getpid */ + .global test_syscall test_syscall: int $0x80 nop + .global test_syscall_end test_syscall_end: nop +/***********************************************/ + +/* Test syscall again, this time with a prefix. + These don't occur in normal code, but gdb should still DTRT. */ + + mov $0x14,%eax /* getpid */ + .global test_prefixed_syscall +test_prefixed_syscall: + repnz + int $0x80 + nop + .global test_prefixed_syscall_end +test_prefixed_syscall_end: + nop + +/***********************************************/ + +/* Test stepping over int3. + The prefixes are pointless, but it's possible, so we exercise it. */ + + nop + .global test_int3 +test_int3: + repz + repz + int3 + nop + .global test_int3_end +test_int3_end: + nop + +/***********************************************/ + /* all done */ pushl $0 diff --git a/gdb/testsuite/gdb.arch/i386-disp-step.exp b/gdb/testsuite/gdb.arch/i386-disp-step.exp index 06c5fb2cdb..5fc2af88be 100644 --- a/gdb/testsuite/gdb.arch/i386-disp-step.exp +++ b/gdb/testsuite/gdb.arch/i386-disp-step.exp @@ -89,6 +89,25 @@ gdb_test "continue" \ ########################################## +# Absolute jump with leading prefixes. +# These don't occur in normal code, but gdb should still DTRT. + +gdb_test "break test_prefixed_abs_jump" \ + "Breakpoint.*at.* file .*$srcfile, line.*" \ + "break test_prefixed_abs_jump" +gdb_test "break test_prefixed_abs_jump_end" \ + "Breakpoint.*at.* file .*$srcfile, line.*" \ + "break test_prefixed_abs_jump_end" + +gdb_test "continue" \ + "Continuing.*Breakpoint.*, test_prefixed_abs_jump ().*" \ + "continue to test_prefixed_abs_jump" +gdb_test "continue" \ + "Continuing.*Breakpoint.*, test_prefixed_abs_jump_end ().*" \ + "continue to test_prefixed_abs_jump_end" + +########################################## + # Test syscall. gdb_test "break test_syscall" \ @@ -107,6 +126,45 @@ gdb_test "continue" \ ########################################## +# Test prefixed syscall. +# These don't occur in normal code, but gdb should still DTRT. + +gdb_test "break test_prefixed_syscall" \ + "Breakpoint.*at.* file .*$srcfile, line.*" \ + "break test_prefixed_syscall" +gdb_test "break test_prefixed_syscall_end" \ + "Breakpoint.*at.* file .*$srcfile, line.*" \ + "break test_prefixed_syscall_end" + +gdb_test "continue" \ + "Continuing.*Breakpoint.*, test_prefixed_syscall ().*" \ + "continue to test_prefixed_syscall" +gdb_test "continue" \ + "Continuing.*Breakpoint.*, test_prefixed_syscall_end ().*" \ + "continue to test_prefixed_syscall_end" + +########################################## + +# int3 (with prefixes) +# These don't occur in normal code, but gdb should still DTRT. + +gdb_test "break test_int3" \ + "Breakpoint.*at.* file .*$srcfile, line.*" \ + "break test_int3" +gdb_test "break test_int3_end" \ + "Breakpoint.*at.* file .*$srcfile, line.*" \ + "break test_int3_end" + +gdb_test "continue" \ + "Continuing.*Breakpoint.*, test_int3 ().*" \ + "continue to test_int3" + +gdb_test "continue" \ + "Continuing.*Breakpoint.*, test_int3_end ().*" \ + "continue to test_int3_end" + +########################################## + # Done, run program to exit. gdb_continue_to_end "i386-disp-step"