aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Evans <dje@google.com>2010-06-22 00:09:10 +0000
committerDoug Evans <dje@google.com>2010-06-22 00:09:10 +0000
commitb55078be0adf688f7da6bc6feb6f7687dc319cc4 (patch)
tree574a110e9685138888d0908d2b16e05f19647d9a
parentd5bc977ffb7653fa0f5c377dcfb8d5f82e994ae7 (diff)
downloadfsf-binutils-gdb-b55078be0adf688f7da6bc6feb6f7687dc319cc4.zip
fsf-binutils-gdb-b55078be0adf688f7da6bc6feb6f7687dc319cc4.tar.gz
fsf-binutils-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/ChangeLog11
-rw-r--r--gdb/i386-linux-tdep.c2
-rw-r--r--gdb/i386-tdep.c48
-rw-r--r--gdb/i386-tdep.h3
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,