Index: gdb/i386-linux-tdep.c |
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c |
index eb114ef18c20d7d065c7cfdb35304b0164a7dea9..8475d7a8e1062e36064e84a1023edc38f7c9d74a 100644 |
--- a/gdb/i386-linux-tdep.c |
+++ b/gdb/i386-linux-tdep.c |
@@ -254,7 +254,7 @@ static int |
i386_linux_sigtramp_p (struct frame_info *this_frame) |
{ |
CORE_ADDR pc = get_frame_pc (this_frame); |
- char *name; |
+ const char *name; |
find_pc_partial_function (pc, &name, NULL, NULL); |
@@ -279,7 +279,7 @@ i386_linux_dwarf_signal_frame_p (struct gdbarch *gdbarch, |
struct frame_info *this_frame) |
{ |
CORE_ADDR pc = get_frame_pc (this_frame); |
- char *name; |
+ const char *name; |
find_pc_partial_function (pc, &name, NULL, NULL); |
@@ -418,7 +418,7 @@ i386_canonicalize_syscall (int syscall) |
static struct linux_record_tdep i386_linux_record_tdep; |
static int |
-i386_linux_intx80_sysenter_record (struct regcache *regcache) |
+i386_linux_intx80_sysenter_syscall_record (struct regcache *regcache) |
{ |
int ret; |
LONGEST syscall_native; |
@@ -459,10 +459,10 @@ i386_linux_intx80_sysenter_record (struct regcache *regcache) |
#define I386_LINUX_xstate 270 |
#define I386_LINUX_frame_size 732 |
-int |
+static int |
i386_linux_record_signal (struct gdbarch *gdbarch, |
struct regcache *regcache, |
- enum target_signal signal) |
+ enum gdb_signal signal) |
{ |
ULONGEST esp; |
@@ -491,11 +491,17 @@ i386_linux_record_signal (struct gdbarch *gdbarch, |
} |
+/* Core of the implementation for gdbarch get_syscall_number. Get pending |
+ syscall number from REGCACHE. If there is no pending syscall -1 will be |
+ returned. Pending syscall means ptrace has stepped into the syscall but |
+ another ptrace call will step out. PC is right after the int $0x80 |
+ / syscall / sysenter instruction in both cases, PC does not change during |
+ the second ptrace step. */ |
+ |
static LONGEST |
-i386_linux_get_syscall_number (struct gdbarch *gdbarch, |
- ptid_t ptid) |
+i386_linux_get_syscall_number_from_regcache (struct regcache *regcache) |
{ |
- struct regcache *regcache = get_thread_regcache (ptid); |
+ struct gdbarch *gdbarch = get_regcache_arch (regcache); |
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
/* The content of a register. */ |
gdb_byte buf[4]; |
@@ -512,6 +518,18 @@ i386_linux_get_syscall_number (struct gdbarch *gdbarch, |
return ret; |
} |
+/* Wrapper for i386_linux_get_syscall_number_from_regcache to make it |
+ compatible with gdbarch get_syscall_number method prototype. */ |
+ |
+static LONGEST |
+i386_linux_get_syscall_number (struct gdbarch *gdbarch, |
+ ptid_t ptid) |
+{ |
+ struct regcache *regcache = get_thread_regcache (ptid); |
+ |
+ return i386_linux_get_syscall_number_from_regcache (regcache); |
+} |
+ |
/* The register sets used in GNU/Linux ELF core-dumps are identical to |
the register sets in `struct user' that are used for a.out |
core-dumps. These are also used by ptrace(2). The corresponding |
@@ -643,6 +661,49 @@ i386_linux_core_read_description (struct gdbarch *gdbarch, |
return tdesc_i386_mmx_linux; |
} |
+/* Linux kernel shows PC value after the 'int $0x80' instruction even if |
+ inferior is still inside the syscall. On next PTRACE_SINGLESTEP it will |
+ finish the syscall but PC will not change. |
+ |
+ Some vDSOs contain 'int $0x80; ret' and during stepping out of the syscall |
+ i386_displaced_step_fixup would keep PC at the displaced pad location. |
+ As PC is pointing to the 'ret' instruction before the step |
+ i386_displaced_step_fixup would expect inferior has just executed that 'ret' |
+ and PC should not be adjusted. In reality it finished syscall instead and |
+ PC should get relocated back to its vDSO address. Hide the 'ret' |
+ instruction by 'nop' so that i386_displaced_step_fixup is not confused. |
+ |
+ It is not fully correct as the bytes in struct displaced_step_closure will |
+ not match the inferior code. But we would need some new flag in |
+ displaced_step_closure otherwise to keep the state that syscall is finishing |
+ for the later i386_displaced_step_fixup execution as the syscall execution |
+ is already no longer detectable there. The new flag field would mean |
+ i386-linux-tdep.c needs to wrap all the displacement methods of i386-tdep.c |
+ which does not seem worth it. The same effect is achieved by patching that |
+ 'nop' instruction there instead. */ |
+ |
+static struct displaced_step_closure * |
+i386_linux_displaced_step_copy_insn (struct gdbarch *gdbarch, |
+ CORE_ADDR from, CORE_ADDR to, |
+ struct regcache *regs) |
+{ |
+ struct displaced_step_closure *closure; |
+ |
+ closure = i386_displaced_step_copy_insn (gdbarch, from, to, regs); |
+ |
+ if (i386_linux_get_syscall_number_from_regcache (regs) != -1) |
+ { |
+ /* Since we use simple_displaced_step_copy_insn, our closure is a |
+ copy of the instruction. */ |
+ gdb_byte *insn = (gdb_byte *) closure; |
+ |
+ /* Fake nop. */ |
+ insn[0] = 0x90; |
+ } |
+ |
+ return closure; |
+} |
+ |
static void |
i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) |
{ |
@@ -856,8 +917,9 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) |
i386_linux_record_tdep.arg5 = I386_EDI_REGNUM; |
i386_linux_record_tdep.arg6 = I386_EBP_REGNUM; |
- tdep->i386_intx80_record = i386_linux_intx80_sysenter_record; |
- tdep->i386_sysenter_record = i386_linux_intx80_sysenter_record; |
+ tdep->i386_intx80_record = i386_linux_intx80_sysenter_syscall_record; |
+ tdep->i386_sysenter_record = i386_linux_intx80_sysenter_syscall_record; |
+ tdep->i386_syscall_record = i386_linux_intx80_sysenter_syscall_record; |
/* N_FUN symbols in shared libaries have 0 for their values and need |
to be relocated. */ |
@@ -890,7 +952,7 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) |
/* Displaced stepping. */ |
set_gdbarch_displaced_step_copy_insn (gdbarch, |
- i386_displaced_step_copy_insn); |
+ i386_linux_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); |