This is the mail archive of the gdb-cvs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Linux native: Use TRAP_BRKPT/TRAP_HWBPT


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=faf09f0119da40d9b408021ad5665a906e00ee59

commit faf09f0119da40d9b408021ad5665a906e00ee59
Author: Pedro Alves <palves@redhat.com>
Date:   Wed Mar 4 20:41:16 2015 +0000

    Linux native: Use TRAP_BRKPT/TRAP_HWBPT
    
    This patch adjusts the native Linux target backend to tell the core
    whether a trap was caused by a breakpoint.
    
    It teaches the target to get that information out of the si_code of
    the SIGTRAP siginfo.
    
    Tested on x86-64 Fedora 20, s390 RHEL 7, and PPC64 Fedora 18.  An
    earlier version was tested on ARM Fedora 21.
    
    gdb/ChangeLog:
    2015-03-04  Pedro Alves  <palves@redhat.com>
    
    	* linux-nat.c (save_sigtrap): Check for breakpoints before
    	checking watchpoints.
    	(status_callback) [USE_SIGTRAP_SIGINFO]: Don't check whether a
    	breakpoint is inserted if relying on SIGTRAP's siginfo.si_code.
    	(check_stopped_by_breakpoint) [USE_SIGTRAP_SIGINFO]: Decide whether
    	a breakpoint triggered based on the SIGTRAP's siginfo.si_code.
    	(linux_nat_stopped_by_sw_breakpoint)
    	(linux_nat_supports_stopped_by_sw_breakpoint)
    	(linux_nat_stopped_by_hw_breakpoint)
    	(linux_nat_supports_stopped_by_hw_breakpoint): New functions.
    	(linux_nat_wait_1): Don't re-increment the PC if relying on
    	SIGTRAP's siginfo->si_code.
    	(linux_nat_add_target): Install new target methods.
    	* linux-thread-db.c (check_event): Don't account for breakpoint PC
    	offset if the target already adjusted the PC.
    	* nat/linux-ptrace.h (USE_SIGTRAP_SIGINFO): New.
    	(GDB_ARCH_TRAP_BRKPT): New.
    	(TRAP_HWBKPT): Define if not already defined.

Diff:
---
 gdb/ChangeLog          |  21 ++++++++++
 gdb/linux-nat.c        | 109 ++++++++++++++++++++++++++++++++++++++++++++++---
 gdb/linux-thread-db.c  |   6 ++-
 gdb/nat/linux-ptrace.h |  51 +++++++++++++++++++++++
 4 files changed, 180 insertions(+), 7 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 22bda6a..1efb944 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,26 @@
 2015-03-04  Pedro Alves  <palves@redhat.com>
 
+	* linux-nat.c (save_sigtrap): Check for breakpoints before
+	checking watchpoints.
+	(status_callback) [USE_SIGTRAP_SIGINFO]: Don't check whether a
+	breakpoint is inserted if relying on SIGTRAP's siginfo.si_code.
+	(check_stopped_by_breakpoint) [USE_SIGTRAP_SIGINFO]: Decide whether
+	a breakpoint triggered based on the SIGTRAP's siginfo.si_code.
+	(linux_nat_stopped_by_sw_breakpoint)
+	(linux_nat_supports_stopped_by_sw_breakpoint)
+	(linux_nat_stopped_by_hw_breakpoint)
+	(linux_nat_supports_stopped_by_hw_breakpoint): New functions.
+	(linux_nat_wait_1): Don't re-increment the PC if relying on
+	SIGTRAP's siginfo->si_code.
+	(linux_nat_add_target): Install new target methods.
+	* linux-thread-db.c (check_event): Don't account for breakpoint PC
+	offset if the target already adjusted the PC.
+	* nat/linux-ptrace.h (USE_SIGTRAP_SIGINFO): New.
+	(GDB_ARCH_TRAP_BRKPT): New.
+	(TRAP_HWBKPT): Define if not already defined.
+
+2015-03-04  Pedro Alves  <palves@redhat.com>
+
 	* NEWS: Mention the new "swbreak" and "hwbreak" stop reasons.
 	* remote.c (struct remote_state) <remote_stopped_by_watchpoint_p>:
 	Delete field.
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 09edaf9..af77df2 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2399,11 +2399,19 @@ save_sigtrap (struct lwp_info *lp)
   gdb_assert (lp->stop_reason == TARGET_STOPPED_BY_NO_REASON);
   gdb_assert (lp->status != 0);
 
-  if (check_stopped_by_watchpoint (lp))
-    return;
-
+  /* Check first if this was a SW/HW breakpoint before checking
+     watchpoints, because at least s390 can't tell the data address of
+     hardware watchpoint hits, and the kernel returns
+     stopped-by-watchpoint as long as there's a watchpoint set.  */
   if (linux_nat_status_is_event (lp->status))
     check_stopped_by_breakpoint (lp);
+
+  /* Note that TRAP_HWBKPT can indicate either a hardware breakpoint
+     or hardware watchpoint.  Check which is which if we got
+     TARGET_STOPPED_BY_HW_BREAKPOINT.  */
+  if (lp->stop_reason == TARGET_STOPPED_BY_NO_REASON
+      || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT)
+    check_stopped_by_watchpoint (lp);
 }
 
 /* Returns true if the LWP had stopped for a watchpoint.  */
@@ -2557,6 +2565,8 @@ status_callback (struct lwp_info *lp, void *data)
 				paddress (target_gdbarch (), pc));
 	  discard = 1;
 	}
+
+#if !USE_SIGTRAP_SIGINFO
       else if (!breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
 	{
 	  if (debug_linux_nat)
@@ -2567,6 +2577,7 @@ status_callback (struct lwp_info *lp, void *data)
 
 	  discard = 1;
 	}
+#endif
 
       if (discard)
 	{
@@ -2669,10 +2680,49 @@ check_stopped_by_breakpoint (struct lwp_info *lp)
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
   CORE_ADDR pc;
   CORE_ADDR sw_bp_pc;
+#if USE_SIGTRAP_SIGINFO
+  siginfo_t siginfo;
+#endif
 
   pc = regcache_read_pc (regcache);
   sw_bp_pc = pc - target_decr_pc_after_break (gdbarch);
 
+#if USE_SIGTRAP_SIGINFO
+  if (linux_nat_get_siginfo (lp->ptid, &siginfo))
+    {
+      if (siginfo.si_signo == SIGTRAP)
+	{
+	  if (siginfo.si_code == GDB_ARCH_TRAP_BRKPT)
+	    {
+	      if (debug_linux_nat)
+		fprintf_unfiltered (gdb_stdlog,
+				    "CSBB: Push back software "
+				    "breakpoint for %s\n",
+				    target_pid_to_str (lp->ptid));
+
+	      /* Back up the PC if necessary.  */
+	      if (pc != sw_bp_pc)
+		regcache_write_pc (regcache, sw_bp_pc);
+
+	      lp->stop_pc = sw_bp_pc;
+	      lp->stop_reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+	      return 1;
+	    }
+	  else if (siginfo.si_code == TRAP_HWBKPT)
+	    {
+	      if (debug_linux_nat)
+		fprintf_unfiltered (gdb_stdlog,
+				    "CSBB: Push back hardware "
+				    "breakpoint/watchpoint for %s\n",
+				    target_pid_to_str (lp->ptid));
+
+	      lp->stop_pc = pc;
+	      lp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+	      return 1;
+	    }
+	}
+    }
+#else
   if ((!lp->step || lp->stop_pc == sw_bp_pc)
       && software_breakpoint_inserted_here_p (get_regcache_aspace (regcache),
 					      sw_bp_pc))
@@ -2704,10 +2754,53 @@ check_stopped_by_breakpoint (struct lwp_info *lp)
       lp->stop_reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
       return 1;
     }
+#endif
 
   return 0;
 }
 
+
+/* Returns true if the LWP had stopped for a software breakpoint.  */
+
+static int
+linux_nat_stopped_by_sw_breakpoint (struct target_ops *ops)
+{
+  struct lwp_info *lp = find_lwp_pid (inferior_ptid);
+
+  gdb_assert (lp != NULL);
+
+  return lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT;
+}
+
+/* Implement the supports_stopped_by_sw_breakpoint method.  */
+
+static int
+linux_nat_supports_stopped_by_sw_breakpoint (struct target_ops *ops)
+{
+  return USE_SIGTRAP_SIGINFO;
+}
+
+/* Returns true if the LWP had stopped for a hardware
+   breakpoint/watchpoint.  */
+
+static int
+linux_nat_stopped_by_hw_breakpoint (struct target_ops *ops)
+{
+  struct lwp_info *lp = find_lwp_pid (inferior_ptid);
+
+  gdb_assert (lp != NULL);
+
+  return lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT;
+}
+
+/* Implement the supports_stopped_by_hw_breakpoint method.  */
+
+static int
+linux_nat_supports_stopped_by_hw_breakpoint (struct target_ops *ops)
+{
+  return USE_SIGTRAP_SIGINFO;
+}
+
 /* Select one LWP out of those that have events pending.  */
 
 static void
@@ -3360,8 +3453,10 @@ linux_nat_wait_1 (struct target_ops *ops,
   gdb_assert (lp != NULL);
 
   /* Now that we've selected our final event LWP, un-adjust its PC if
-     it was a software breakpoint.  */
-  if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
+     it was a software breakpoint, and we can't reliably support the
+     "stopped by software breakpoint" stop reason.  */
+  if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
+      && !USE_SIGTRAP_SIGINFO)
     {
       struct regcache *regcache = get_thread_regcache (lp->ptid);
       struct gdbarch *gdbarch = get_regcache_arch (regcache);
@@ -4651,6 +4746,10 @@ linux_nat_add_target (struct target_ops *t)
   t->to_thread_address_space = linux_nat_thread_address_space;
   t->to_stopped_by_watchpoint = linux_nat_stopped_by_watchpoint;
   t->to_stopped_data_address = linux_nat_stopped_data_address;
+  t->to_stopped_by_sw_breakpoint = linux_nat_stopped_by_sw_breakpoint;
+  t->to_supports_stopped_by_sw_breakpoint = linux_nat_supports_stopped_by_sw_breakpoint;
+  t->to_stopped_by_hw_breakpoint = linux_nat_stopped_by_hw_breakpoint;
+  t->to_supports_stopped_by_hw_breakpoint = linux_nat_supports_stopped_by_hw_breakpoint;
 
   t->to_can_async_p = linux_nat_can_async_p;
   t->to_is_async_p = linux_nat_is_async_p;
diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c
index c3d479b..29b52b3 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -1437,8 +1437,10 @@ check_event (ptid_t ptid)
   info = get_thread_db_info (ptid_get_pid (ptid));
 
   /* Bail out early if we're not at a thread event breakpoint.  */
-  stop_pc = regcache_read_pc (regcache)
-	    - target_decr_pc_after_break (gdbarch);
+  stop_pc = regcache_read_pc (regcache);
+  if (!target_supports_stopped_by_sw_breakpoint ())
+    stop_pc -= target_decr_pc_after_break (gdbarch);
+
   if (stop_pc != info->td_create_bp_addr
       && stop_pc != info->td_death_bp_addr)
     return;
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index c5b0f14..8354a4d 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -88,6 +88,57 @@ struct buffer;
 #define __WALL          0x40000000 /* Wait for any child.  */
 #endif
 
+/* True if whether a breakpoint/watchpoint triggered can be determined
+   from the si_code of SIGTRAP's siginfo_t (TRAP_BRKPT/TRAP_HWBKPT).
+   That is, if the kernel can tell us whether the thread executed a
+   software breakpoint, we trust it.  The kernel will be determining
+   that from the hardware (e.g., from which exception was raised in
+   the CPU).  Relying on whether a breakpoint is planted in memory at
+   the time the SIGTRAP is processed to determine whether the thread
+   stopped for a software breakpoint can be too late.  E.g., the
+   breakpoint could have been removed since.  Or the thread could have
+   stepped an instruction the size of a breakpoint instruction, and
+   before the stop is processed a breakpoint is inserted at its
+   address.  Getting these wrong is disastrous on decr_pc_after_break
+   architectures.  The moribund location mechanism helps with that
+   somewhat but it is an heuristic, and can well fail.  Getting that
+   information out of the kernel and ultimately out of the CPU is the
+   way to go.  That said, some architecture may get the si_code wrong,
+   and as such we're leaving fallback code in place.  We'll remove
+   this after a while if no problem is reported.  */
+#define USE_SIGTRAP_SIGINFO 1
+
+/* The x86 kernel gets some of the si_code values backwards, like
+   this:
+
+   | what                                     | si_code    |
+   |------------------------------------------+------------|
+   | software breakpoints (int3)              | SI_KERNEL  |
+   | single-steps                             | TRAP_TRACE |
+   | single-stepping a syscall                | TRAP_BRKPT |
+   | user sent SIGTRAP                        | 0          |
+   | exec SIGTRAP (when no PTRACE_EVENT_EXEC) | 0          |
+   | hardware breakpoints/watchpoints         | TRAP_HWBPT |
+
+   That is, it reports SI_KERNEL for software breakpoints (and only
+   for those), and TRAP_BRKPT for single-stepping a syscall...  If the
+   kernel is ever fixed, we'll just have to detect it like we detect
+   optional ptrace features: by forking and debugging ourselves,
+   running to a breakpoint and checking what comes out of
+   siginfo->si_code.
+
+   The generic Linux target code should use GDB_ARCH_TRAP_BRKPT
+   instead of TRAP_BRKPT to abstract out this x86 peculiarity.  */
+#if defined __i386__ || defined __x86_64__
+# define GDB_ARCH_TRAP_BRKPT SI_KERNEL
+#else
+# define GDB_ARCH_TRAP_BRKPT TRAP_BRKPT
+#endif
+
+#ifndef TRAP_HWBKPT
+# define TRAP_HWBKPT 4
+#endif
+
 extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
 
 /* Find all possible reasons we could have failed to attach to PTID


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]