aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog14
-rw-r--r--gdb/i386-linux-tdep.c69
-rw-r--r--gdb/i386-tdep.c7
-rw-r--r--gdb/testsuite/ChangeLog8
-rw-r--r--gdb/testsuite/gdb.base/disp-step-syscall.exp13
5 files changed, 104 insertions, 7 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 3c59ad9..387ce79 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,19 @@
2012-02-29 Jan Kratochvil <jan.kratochvil@redhat.com>
+ Fix disp-step-syscall.exp: fork: single step over fork.
+ * i386-linux-tdep.c (-i386_linux_get_syscall_number): Rename to ...
+ (i386_linux_get_syscall_number_from_regcache): ... here, new function
+ comment, change parameters gdbarch and ptid to regcache. Remove
+ parameter regcache, initialize gdbarch from regcache here.
+ (i386_linux_get_syscall_number, i386_linux_displaced_step_copy_insn):
+ New functions.
+ (i386_linux_init_abi): Install i386_linux_displaced_step_copy_insn
+ instead.
+ * i386-tdep.c (i386_syscall_p): Check also for 'sysenter' and
+ 'syscall'. Make the 'int' check more strict.
+
+2012-02-29 Jan Kratochvil <jan.kratochvil@redhat.com>
+
Fix reverse mode for syscall on AMD CPUs in 32-bit mode.
* i386-linux-tdep.c (i386_linux_intx80_sysenter_record): Rename to ...
(i386_linux_intx80_sysenter_syscall_record): ... here.
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 13857e5..bfb0e1c 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -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. */
+
+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)
{
@@ -891,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);
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 56e375e..d18aa99 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -521,7 +521,12 @@ i386_call_p (const gdb_byte *insn)
static int
i386_syscall_p (const gdb_byte *insn, int *lengthp)
{
- if (insn[0] == 0xcd)
+ /* Is it 'int $0x80'? */
+ if ((insn[0] == 0xcd && insn[1] == 0x80)
+ /* Or is it 'sysenter'? */
+ || (insn[0] == 0x0f && insn[1] == 0x34)
+ /* Or is it 'syscall'? */
+ || (insn[0] == 0x0f && insn[1] == 0x05))
{
*lengthp = 2;
return 1;
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index ba1ef51..f8bd318 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,13 @@
2012-02-29 Jan Kratochvil <jan.kratochvil@redhat.com>
+ Fix disp-step-syscall.exp: fork: single step over fork.
+ * gdb.base/disp-step-syscall.exp (syscall_insn): Anchor it by
+ whitespaces.
+ (single step over $syscall): Remove its check.
+ (single step over $syscall final pc): New check.
+
+2012-02-29 Jan Kratochvil <jan.kratochvil@redhat.com>
+
Support processors without SSSE3.
* gdb.reverse/i386-sse-reverse.c (sse_test): Move pabsb, pabsw and
pabsd into ...
diff --git a/gdb/testsuite/gdb.base/disp-step-syscall.exp b/gdb/testsuite/gdb.base/disp-step-syscall.exp
index c62938c..4cf5529 100644
--- a/gdb/testsuite/gdb.base/disp-step-syscall.exp
+++ b/gdb/testsuite/gdb.base/disp-step-syscall.exp
@@ -25,7 +25,7 @@ set syscall_insn ""
# Define the syscall instruction for each target.
if { [istarget "i\[34567\]86-*-linux*"] || [istarget "x86_64-*-linux*"] } {
- set syscall_insn "(int|syscall|sysenter)"
+ set syscall_insn "\[ \t\](int|syscall|sysenter)\[ \t\]"
} else {
return -1
}
@@ -118,7 +118,16 @@ proc disp_step_cross_syscall { syscall } { with_test_prefix "$syscall" {
gdb_test_no_output "set displaced-stepping on"
# Check the address of next instruction of syscall.
- gdb_test "stepi" ".*$syscall_insn_next_addr.*" "single step over $syscall"
+ gdb_test "stepi" ".*" "single step over $syscall"
+ set syscall_insn_next_addr_found [get_hexadecimal_valueof "\$pc" "0"]
+
+ set test "single step over $syscall final pc"
+ if {$syscall_insn_next_addr != 0
+ && $syscall_insn_next_addr == $syscall_insn_next_addr_found} {
+ pass $test
+ } else {
+ fail $test
+ }
# Delete breakpoint syscall insns to avoid interference to other syscalls.
gdb_test_no_output "delete $syscall_insn_bp" "delete break $syscall insn"