Fix breakpoint size when stepping over a permanent breakpoint in GDBServer.
authorAntoine Tremblay <antoine.tremblay@ericsson.com>
Mon, 30 Nov 2015 20:08:04 +0000 (15:08 -0500)
committerAntoine Tremblay <antoine.tremblay@ericsson.com>
Mon, 30 Nov 2015 20:08:04 +0000 (15:08 -0500)
When manually stepping over a permanent breakpoint on ARM we need to fetch the
right breakpoint size based on the current instruction set used.

Since this is not encoded in the stop_pc, the instruction mode needs to be
fetched from the CPSR register.

This is done by introducing a new target operation called :
breakpoint_kind_from_current_state.

For other targets that do not need this, breakpoint_kind_from_pc is used.

No regressions, tested on ubuntu 14.04 ARMv7 and x86.
With gdbserver-{native,extended} / { -marm -mthumb }

gdb/gdbserver/ChangeLog:

* linux-arm-low.c (arm_is_thumb_mode): New function.
(arm_breakpoint_at): Use arm_is_thumb_mode.
(arm_breakpoint_kind_from_current_state): New function.
(struct linux_target_ops) <breakpoint_kind_from_current_state>:
Initialize.
* linux-low.c (linux_wait_1): Call breakpoint_kind_from_current_state.
(linux_breakpoint_kind_from_current_state): New function.
(struct target_ops <breakpoint_kind_from_current_state>: Initialize.
* linux-low.h (struct linux_target_ops)
<breakpoint_kind_from_current_state>: New field.
* target.h (struct target_ops): Likewise.
(target_breakpoint_kind_from_current_state): New macro.

gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-arm-low.c
gdb/gdbserver/linux-low.c
gdb/gdbserver/linux-low.h
gdb/gdbserver/target.h

index 6330c03236bd65ed88d782cec0b1cf5c1751a287..87ce9e7eefa5a8a18eeda0c551c295e8d677d9fb 100644 (file)
@@ -1,3 +1,18 @@
+2015-11-30  Antoine Tremblay  <antoine.tremblay@ericsson.com>
+
+       * linux-arm-low.c (arm_is_thumb_mode): New function.
+       (arm_breakpoint_at): Use arm_is_thumb_mode.
+       (arm_breakpoint_kind_from_current_state): New function.
+       (struct linux_target_ops) <breakpoint_kind_from_current_state>:
+       Initialize.
+       * linux-low.c (linux_wait_1): Call breakpoint_kind_from_current_state.
+       (linux_breakpoint_kind_from_current_state): New function.
+       (struct target_ops <breakpoint_kind_from_current_state>: Initialize.
+       * linux-low.h (struct linux_target_ops)
+       <breakpoint_kind_from_current_state>: New field.
+       * target.h (struct target_ops): Likewise.
+       (target_breakpoint_kind_from_current_state): New macro.
+
 2015-11-30  Pedro Alves  <palves@redhat.com>
 
        * linux-low.c (linux_resume): Wake up the event loop before
index dda37cb36a2702a8f3e62e041d1c46e2a3eab454..6f37f581096a02c6014df6da2655698cb0573f55 100644 (file)
@@ -264,8 +264,10 @@ static const unsigned short thumb_breakpoint = 0xde01;
 static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
 #define thumb2_breakpoint_len 4
 
+/* Returns 1 if the current instruction set is thumb, 0 otherwise.  */
+
 static int
-arm_breakpoint_at (CORE_ADDR where)
+arm_is_thumb_mode (void)
 {
   struct regcache *regcache = get_thread_regcache (current_thread, 1);
   unsigned long cpsr;
@@ -273,6 +275,17 @@ arm_breakpoint_at (CORE_ADDR where)
   collect_register_by_name (regcache, "cpsr", &cpsr);
 
   if (cpsr & 0x20)
+    return 1;
+  else
+    return 0;
+}
+
+/* Returns 1 if there is a software breakpoint at location.  */
+
+static int
+arm_breakpoint_at (CORE_ADDR where)
+{
+  if (arm_is_thumb_mode ())
     {
       /* Thumb mode.  */
       unsigned short insn;
@@ -996,6 +1009,23 @@ arm_sw_breakpoint_from_kind (int kind , int *size)
   return NULL;
 }
 
+/* Implementation of the linux_target_ops method
+   "breakpoint_kind_from_current_state".  */
+
+static int
+arm_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
+{
+  if (arm_is_thumb_mode ())
+    {
+      *pcptr = MAKE_THUMB_ADDR (*pcptr);
+      return arm_breakpoint_kind_from_pc (pcptr);
+    }
+  else
+    {
+      return arm_breakpoint_kind_from_pc (pcptr);
+    }
+}
+
 struct linux_target_ops the_low_target = {
   arm_arch_setup,
   arm_regs_info,
@@ -1021,6 +1051,14 @@ struct linux_target_ops the_low_target = {
   arm_new_thread,
   arm_new_fork,
   arm_prepare_to_resume,
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  arm_breakpoint_kind_from_current_state
 };
 
 void
index 086b202d9448a1c05142ebb399bb786ee8f98eb3..207a5ba90b94433f2c93e204601611b81970b917 100644 (file)
@@ -3084,7 +3084,8 @@ linux_wait_1 (ptid_t ptid,
       int breakpoint_kind = 0;
       CORE_ADDR stop_pc = event_child->stop_pc;
 
-      breakpoint_kind = the_target->breakpoint_kind_from_pc (&stop_pc);
+      breakpoint_kind =
+       the_target->breakpoint_kind_from_current_state (&stop_pc);
       the_target->sw_breakpoint_from_kind (breakpoint_kind, &increment_pc);
 
       if (debug_threads)
@@ -7036,6 +7037,18 @@ linux_sw_breakpoint_from_kind (int kind, int *size)
   return (*the_low_target.sw_breakpoint_from_kind) (kind, size);
 }
 
+/* Implementation of the target_ops method
+   "breakpoint_kind_from_current_state".  */
+
+static int
+linux_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
+{
+  if (the_low_target.breakpoint_kind_from_current_state != NULL)
+    return (*the_low_target.breakpoint_kind_from_current_state) (pcptr);
+  else
+    return linux_breakpoint_kind_from_pc (pcptr);
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_arch_setup,
@@ -7133,6 +7146,7 @@ static struct target_ops linux_target_ops = {
   linux_breakpoint_kind_from_pc,
   linux_sw_breakpoint_from_kind,
   linux_proc_tid_get_name,
+  linux_breakpoint_kind_from_current_state
 };
 
 static void
index f1d4f0f5a462666ce296a5ceef4adfc66f955c6b..e2bfeb70c18504d9c1691b67183e0fdff64ffbf7 100644 (file)
@@ -233,6 +233,9 @@ struct linux_target_ops
 
   /* Returns true if the low target supports range stepping.  */
   int (*supports_range_stepping) (void);
+
+  /* See target.h.  */
+  int (*breakpoint_kind_from_current_state) (CORE_ADDR *pcptr);
 };
 
 extern struct linux_target_ops the_low_target;
index a09ba2fd54f8d41a8be6b56233e58097b7e97803..d3fcb36033330a89af55d18f5474b6b5ee7e3c9f 100644 (file)
@@ -457,6 +457,12 @@ struct target_ops
   /* Return the thread's name, or NULL if the target is unable to determine it.
      The returned value must not be freed by the caller.  */
   const char *(*thread_name) (ptid_t thread);
+
+  /* Return the breakpoint kind for this target based on the current
+     processor state (e.g. the current instruction mode on ARM) and the
+     PC.  The PCPTR is adjusted to the real memory location in case a flag
+     (e.g., the Thumb bit on ARM) is present in the PC.  */
+  int (*breakpoint_kind_from_current_state) (CORE_ADDR *pcptr);
 };
 
 extern struct target_ops *the_target;
@@ -644,6 +650,11 @@ int kill_inferior (int);
    ? (*the_target->breakpoint_kind_from_pc) (pcptr) \
    : default_breakpoint_kind_from_pc (pcptr))
 
+#define target_breakpoint_kind_from_current_state(pcptr) \
+  (the_target->breakpoint_kind_from_current_state \
+   ? (*the_target->breakpoint_kind_from_current_state) (pcptr) \
+   : target_breakpoint_kind_from_pc (pcptr))
+
 /* Start non-stop mode, returns 0 on success, -1 on failure.   */
 
 int start_non_stop (int nonstop);