diff options
author | Doug Evans <dje@google.com> | 2010-06-22 00:09:10 +0000 |
---|---|---|
committer | Doug Evans <dje@google.com> | 2010-06-22 00:09:10 +0000 |
commit | b55078be0adf688f7da6bc6feb6f7687dc319cc4 (patch) | |
tree | 574a110e9685138888d0908d2b16e05f19647d9a | |
parent | d5bc977ffb7653fa0f5c377dcfb8d5f82e994ae7 (diff) | |
download | gdb-b55078be0adf688f7da6bc6feb6f7687dc319cc4.zip gdb-b55078be0adf688f7da6bc6feb6f7687dc319cc4.tar.gz gdb-b55078be0adf688f7da6bc6feb6f7687dc319cc4.tar.bz2 |
* i386-tdep.h (i386_displaced_step_copy_insn): Declare.
* i386-tdep.c (i386_displaced_step_copy_insn): New function.
(i386_syscall_p): Change type of lengthp to int.
(i386_displaced_step_fixup): Handle kernels that run one past a
syscall insn.
* i386-linux-tdep.c (i386_linux_init_abi): Use
i386_displaced_step_copy_insn instead of
simple_displaced_step_copy_insn.
-rw-r--r-- | gdb/ChangeLog | 11 | ||||
-rw-r--r-- | gdb/i386-linux-tdep.c | 2 | ||||
-rw-r--r-- | gdb/i386-tdep.c | 48 | ||||
-rw-r--r-- | gdb/i386-tdep.h | 3 |
4 files changed, 60 insertions, 4 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index dce4a93..568f55c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,14 @@ +2010-06-21 Doug Evans <dje@google.com> + + * i386-tdep.h (i386_displaced_step_copy_insn): Declare. + * i386-tdep.c (i386_displaced_step_copy_insn): New function. + (i386_syscall_p): Change type of lengthp to int. + (i386_displaced_step_fixup): Handle kernels that run one past a + syscall insn. + * i386-linux-tdep.c (i386_linux_init_abi): Use + i386_displaced_step_copy_insn instead of + simple_displaced_step_copy_insn. + 2010-06-21 Tom Tromey <tromey@redhat.com> * dwarf2read.c (read_base_type): Handle DW_ATE_UTF. diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c index 3ae19a7..8ca7377 100644 --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -888,7 +888,7 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Displaced stepping. */ set_gdbarch_displaced_step_copy_insn (gdbarch, - simple_displaced_step_copy_insn); + i386_displaced_step_copy_insn); set_gdbarch_displaced_step_fixup (gdbarch, i386_displaced_step_fixup); set_gdbarch_displaced_step_free_closure (gdbarch, simple_displaced_step_free_closure); diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index 5e366ea..e0f519d 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -518,7 +518,7 @@ i386_call_p (const gdb_byte *insn) length in bytes. Otherwise, return zero. */ static int -i386_syscall_p (const gdb_byte *insn, ULONGEST *lengthp) +i386_syscall_p (const gdb_byte *insn, int *lengthp) { if (insn[0] == 0xcd) { @@ -529,6 +529,43 @@ i386_syscall_p (const gdb_byte *insn, ULONGEST *lengthp) return 0; } +/* Some kernels may run one past a syscall insn, so we have to cope. + Otherwise this is just simple_displaced_step_copy_insn. */ + +struct displaced_step_closure * +i386_displaced_step_copy_insn (struct gdbarch *gdbarch, + CORE_ADDR from, CORE_ADDR to, + struct regcache *regs) +{ + size_t len = gdbarch_max_insn_length (gdbarch); + gdb_byte *buf = xmalloc (len); + + read_memory (from, buf, len); + + /* GDB may get control back after the insn after the syscall. + Presumably this is a kernel bug. + If this is a syscall, make sure there's a nop afterwards. */ + { + int syscall_length; + gdb_byte *insn; + + insn = i386_skip_prefixes (buf, len); + if (insn != NULL && i386_syscall_p (insn, &syscall_length)) + insn[syscall_length] = NOP_OPCODE; + } + + write_memory (to, buf, len); + + if (debug_displaced) + { + fprintf_unfiltered (gdb_stdlog, "displaced: copy %s->%s: ", + paddress (gdbarch, from), paddress (gdbarch, to)); + displaced_step_dump_bytes (gdb_stdlog, buf, len); + } + + return (struct displaced_step_closure *) buf; +} + /* Fix up the state of registers and memory after having single-stepped a displaced instruction. */ @@ -587,7 +624,7 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch, && ! i386_ret_p (insn)) { ULONGEST orig_eip; - ULONGEST insn_len; + int insn_len; regcache_cooked_read_unsigned (regs, I386_EIP_REGNUM, &orig_eip); @@ -606,7 +643,12 @@ 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 - insn_start) + insn_len) + && orig_eip != to + (insn - insn_start) + insn_len + /* GDB can get control back after the insn after the syscall. + Presumably this is a kernel bug. + i386_displaced_step_copy_insn ensures its a nop, + we add one to the length for it. */ + && orig_eip != to + (insn - insn_start) + insn_len + 1) { if (debug_displaced) fprintf_unfiltered (gdb_stdlog, diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index 6520d67..49e0727 100644 --- a/gdb/i386-tdep.h +++ b/gdb/i386-tdep.h @@ -362,6 +362,9 @@ extern const struct regset * const char *sect_name, size_t sect_size); +extern struct displaced_step_closure *i386_displaced_step_copy_insn + (struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, + struct regcache *regs); extern void i386_displaced_step_fixup (struct gdbarch *gdbarch, struct displaced_step_closure *closure, CORE_ADDR from, CORE_ADDR to, |