[4/5] Handle breakpoint kinds for software breakpoints in GDBServer.

Message ID 1442577749-6650-5-git-send-email-antoine.tremblay@ericsson.com
State New, archived
Headers

Commit Message

Antoine Tremblay Sept. 18, 2015, 12:02 p.m. UTC
  In order to enable software breakpoints kinds, GDBServer needs to make use
of the kind parameter sent by the Z0 packet.

Using this kind parameter, GDBServer is able to write the right breakpoint
by asking the new operation called breakpoint_from_length to return
the breakpoint data for a specific kind.

Also, GDBServer always needs to handle that the software breakpoint size
as it can be coded as a kind field and thus always decode this to the real
length of the breakpoint when doing memory operations on the breakpoint
using check_breakpoint_from_length.

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

gdb/gdbserver/ChangeLog:
	* linux-low.c (linux_breakpoint_from_length): New function.
	* linux-low.h: Add breakpoint_from_length operation.
	* mem-break.c (check_breakpoint_from_length): New function.
	(insert_memory_breakpoint): Call check_breakpoint_from_length.
	(remove_memory_breakpoint): Likewise.
	(validate_inserted_breakpoint): Call check_breakpoint_from_length.
	(check_mem_read): Likewise.
	(check_mem_write): Likewise.
	* target.h (struct target_ops): Add breakpoint_from_length operation.
---
 gdb/gdbserver/linux-low.c | 12 +++++++++
 gdb/gdbserver/linux-low.h |  4 +++
 gdb/gdbserver/mem-break.c | 68 ++++++++++++++++++++++++++++++++++++++++-------
 gdb/gdbserver/target.h    |  4 +++
 4 files changed, 78 insertions(+), 10 deletions(-)
  

Comments

Yao Qi Sept. 28, 2015, 10:33 a.m. UTC | #1
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:

> diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
> index 603819e..bf4c3c7 100644
> --- a/gdb/gdbserver/target.h
> +++ b/gdb/gdbserver/target.h
> @@ -446,6 +446,10 @@ struct target_ops
>       can be NULL, the default breakpoint for the target should be returned in
>       this case.  */
>    const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
> +
> +  /* Returns a breakpoint from a length, the length can have target specific
> +     meaning like the z0 kind parameter.  */
> +  const unsigned char *(*breakpoint_from_length) (int *len);
>  };

If you agree on my comments to patch 2/5, we can rename this hook to
breakpoint_from_z0_kind.
  
Antoine Tremblay Sept. 29, 2015, 11:55 a.m. UTC | #2
On 09/28/2015 06:33 AM, Yao Qi wrote:
> Antoine Tremblay <antoine.tremblay@ericsson.com> writes:
>
>> diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
>> index 603819e..bf4c3c7 100644
>> --- a/gdb/gdbserver/target.h
>> +++ b/gdb/gdbserver/target.h
>> @@ -446,6 +446,10 @@ struct target_ops
>>        can be NULL, the default breakpoint for the target should be returned in
>>        this case.  */
>>     const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
>> +
>> +  /* Returns a breakpoint from a length, the length can have target specific
>> +     meaning like the z0 kind parameter.  */
>> +  const unsigned char *(*breakpoint_from_length) (int *len);
>>   };
>
> If you agree on my comments to patch 2/5, we can rename this hook to
> breakpoint_from_z0_kind.
>

I've renamed this to breakpoint_from_kind since I want to be able to 
reuse the same operation for future tracepoint kinds and don't want the 
confusion with z0.

This follows the terminology introduced in patch 2/5 in general too.
  

Patch

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 06387a0..8e8b7c0 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5965,6 +5965,17 @@  linux_supports_range_stepping (void)
   return (*the_low_target.supports_range_stepping) ();
 }
 
+static const unsigned char*
+linux_breakpoint_from_length (int *len)
+{
+  if (*the_low_target.breakpoint_from_length != NULL)
+    {
+      return (*the_low_target.breakpoint_from_length) (len);
+    }
+  else
+    return NULL;
+
+}
 /* Enumerate spufs IDs for process PID.  */
 static int
 spu_enumerate_spu_ids (long pid, unsigned char *buf, CORE_ADDR offset, int len)
@@ -7042,6 +7053,7 @@  static struct target_ops linux_target_ops = {
   linux_mntns_unlink,
   linux_mntns_readlink,
   linux_breakpoint_from_pc,
+  linux_breakpoint_from_length
 };
 
 static void
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index c623150..bb56584 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -228,6 +228,10 @@  struct linux_target_ops
 
   /* Returns true if the low target supports range stepping.  */
   int (*supports_range_stepping) (void);
+
+  /* Returns the proper breakpoint from size, the length can have target
+     specific meaning like the z0 kind parameter */
+  const unsigned char *(*breakpoint_from_length) (int *length);
 };
 
 extern struct linux_target_ops the_low_target;
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 68e14c3..dca228c 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -295,6 +295,38 @@  find_raw_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type, int size)
   return NULL;
 }
 
+/* Check if if we can insert the breakpoint of size, try to resolve the real
+   breakpoint size from an architecture specific encoded size if needed.  */
+
+static int
+check_breakpoint_from_length (struct raw_breakpoint *bp,
+			      const unsigned char **breakpoint_data,
+			      int *breakpoint_len)
+{
+  /* If the architecture treats the size field of Z packets as a
+     'kind' field, then we'll need to be able to know which is the
+     breakpoint instruction too.  */
+  if (bp->size != *breakpoint_len)
+    {
+      /* Get the arch dependent breakpoint */
+      if (*the_target->breakpoint_from_length != NULL)
+	{
+	  /* Update magic coded size to the right size if needed.  */
+	  int size = bp->size;
+	  *breakpoint_data =
+	    (*the_target->breakpoint_from_length) (&size);
+	  *breakpoint_len = size;
+	}
+      else {
+	if (debug_threads)
+	  debug_printf ("Don't know breakpoints of size %d.\n",
+                     bp->size);
+	return -1;
+      }
+    }
+
+  return 1;
+}
 /* See mem-break.h.  */
 
 int
@@ -312,16 +344,9 @@  insert_memory_breakpoint (struct raw_breakpoint *bp)
   if (breakpoint_data == NULL)
     return 1;
 
-  /* If the architecture treats the size field of Z packets as a
-     'kind' field, then we'll need to be able to know which is the
-     breakpoint instruction too.  */
-  if (bp->size != breakpoint_len)
-    {
-      if (debug_threads)
-	debug_printf ("Don't know how to insert breakpoints of size %d.\n",
-		      bp->size);
-      return -1;
-    }
+  if ((err = check_breakpoint_from_length (bp, &breakpoint_data,
+					   &breakpoint_len)) < 0)
+    return err;
 
   /* Note that there can be fast tracepoint jumps installed in the
      same memory range, so to get at the original memory, we need to
@@ -356,6 +381,7 @@  int
 remove_memory_breakpoint (struct raw_breakpoint *bp)
 {
   unsigned char buf[MAX_BREAKPOINT_LEN];
+  const unsigned char* breakpoint_data;
   int err;
   int breakpoint_len;
   CORE_ADDR pc;
@@ -363,6 +389,10 @@  remove_memory_breakpoint (struct raw_breakpoint *bp)
   pc = bp->pcfull;
   the_target->breakpoint_from_pc (&pc, &breakpoint_len);
 
+  if ((err = check_breakpoint_from_length (bp, &breakpoint_data,
+					   &breakpoint_len)) < 0)
+    return err;
+
   /* Since there can be trap breakpoints inserted in the same address
      range, we use `write_inferior_memory', which takes care of
      layering breakpoints on top of fast tracepoints, and on top of
@@ -1693,6 +1723,14 @@  validate_inserted_breakpoint (struct raw_breakpoint *bp)
 
   breakpoint_data = the_target->breakpoint_from_pc (&raw_pc, &breakpoint_len);
 
+  if ((err = check_breakpoint_from_length (bp, &breakpoint_data,
+				    &breakpoint_len)) < 0)
+    {
+      /* Tag it as gone.  */
+      bp->inserted = -1;
+      return 0;
+    }
+
   gdb_assert (bp->inserted);
   gdb_assert (bp->raw_type == raw_bkpt_type_sw);
 
@@ -1791,11 +1829,16 @@  check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
     {
       int breakpoint_len;
       CORE_ADDR raw_pc;
+      const unsigned char* breakpoint_data;
       CORE_ADDR bp_end, start, end;
       int copy_offset, copy_len, buf_offset;
 
       raw_pc = bp->pcfull;
+
       the_target->breakpoint_from_pc (&raw_pc, &breakpoint_len);
+      check_breakpoint_from_length (bp, &breakpoint_data,
+				    &breakpoint_len);
+
       bp_end = bp->pc + breakpoint_len;
 
       if (bp->raw_type != raw_bkpt_type_sw)
@@ -1890,8 +1933,13 @@  check_mem_write (CORE_ADDR mem_addr, unsigned char *buf,
       int copy_offset, copy_len, buf_offset;
 
       raw_pc = bp->pcfull;
+
       breakpoint_data =
 	the_target->breakpoint_from_pc (&raw_pc, &breakpoint_len);
+
+      check_breakpoint_from_length (bp, &breakpoint_data,
+				    &breakpoint_len);
+
       bp_end = bp->pc + breakpoint_len;
 
       if (bp->raw_type != raw_bkpt_type_sw)
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 603819e..bf4c3c7 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -446,6 +446,10 @@  struct target_ops
      can be NULL, the default breakpoint for the target should be returned in
      this case.  */
   const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
+
+  /* Returns a breakpoint from a length, the length can have target specific
+     meaning like the z0 kind parameter.  */
+  const unsigned char *(*breakpoint_from_length) (int *len);
 };
 
 extern struct target_ops *the_target;