Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Jan Kratochvil <jan.kratochvil@redhat.com>
To: Yao Qi <yao@codesourcery.com>
Cc: gdb-patches@sourceware.org
Subject: Re: [patch] Fix disp-step-syscall.exp on some i386 targets
Date: Tue, 28 Feb 2012 13:55:00 -0000	[thread overview]
Message-ID: <20120228134241.GA24390@host2.jankratochvil.net> (raw)
In-Reply-To: <4F4CA669.7040209@codesourcery.com>

On Tue, 28 Feb 2012 11:03:21 +0100, Yao Qi wrote:
> It would be better if we can add some comments to explain this fix is a hack
> and why we have to do in this way.

I hope it is OK this way.


Thanks,
Jan



gdb/
2012-02-28  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.

gdb/testsuite/
2012-02-28  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.

--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -491,11 +491,17 @@ i386_linux_record_signal (struct gdbarch *gdbarch,
 }
 \f
 
+/* 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)
 {
@@ -890,7 +951,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);
--- 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;
--- 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"


  reply	other threads:[~2012-02-28 13:43 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-27 20:49 Jan Kratochvil
2012-02-28  8:17 ` Yao Qi
2012-02-28  9:24   ` Jan Kratochvil
2012-02-28 10:14     ` Yao Qi
2012-02-28 13:55       ` Jan Kratochvil [this message]
2012-02-28 14:57         ` Yao Qi
2012-02-29 16:14           ` [commit] " Jan Kratochvil
2012-02-28 15:40         ` Pedro Alves

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120228134241.GA24390@host2.jankratochvil.net \
    --to=jan.kratochvil@redhat.com \
    --cc=gdb-patches@sourceware.org \
    --cc=yao@codesourcery.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox