[v2,1/7] Add breakpoint_from_pc target_ops for software breakpoints in GDBServer.

Message ID 1444063455-31558-2-git-send-email-antoine.tremblay@ericsson.com
State New, archived
Headers

Commit Message

Antoine Tremblay Oct. 5, 2015, 4:44 p.m. UTC
  In this v2 :
 - Comments added for linux_target_ops method implementation
 - Removed cris_get_pc (void) change
 - Removed unneeded {}
 - Fixed PC/PCPTR comments
 - Comments adjusted for breakpoint_from_pc
 - Improved ChangeLog
---

Add breakpoint_from_pc target_ops for software breakpoints in GDBServer.

This patch is in preparation for software breakpoints on ARM
linux.  It refactors breakpoint and breakpoint_len into
breakpoint_from_pc to prepare the case where we have multiple types of
breakpoints.

breakpoint_from_pc returns the breakpoint for this PC as a string of bytes,
the length of the breakpoint and ajusts the PC to the real memory location in
case a flag was present in the PC.

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

Also since the target_ops have been changed compilation was tested on
all affected archs namely : aarch64, arm, bfin, cris, crisv32, m32r,
m68k, mips, nios2, ppc, s390, sh, sparc, tic6x, tile, x86, steins.

gdb/gdbserver/ChangeLog:
	* linux-aarch64-low.c (aarch64_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-arm-low.c (arm_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-bfin-low.c (bfin_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-cris-low.c (cris_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-crisv32-low.c (cris_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-low.c (linux_wait_1): Add call to breakpoint_from_pc.
	(linux_breakpoint_from_pc): New function.
        (static struct target_ops) <breakpoint_from_pc>: Initialize field.
	(initialize_low): Add call to breakpoint_from_pc.
	* linux-low.h (struct linux_target_ops) <breakpoint_from_pc>: New field.
	* linux-m32r-low.c (m32r_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-m68k-low.c (m68k_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-mips-low.c (mips_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-nios2-low.c (nios2_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-ppc-low.c (ppc_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-s390-low.c (s390_breakpoint_from_pc): Likewise.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-sh-low.c (sh_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-sparc-low.c (sparc_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-tic6x-low.c (tic6x_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-tile-low.c (tile_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-x86-low.c (x86_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* linux-xtensa-low.c (xtensa_breakpoint_from_pc): New function.
	(struct linux_target_ops) <breakpoint>: Remove.
	(struct linux_target_ops) <breakpoint_len>: Remove.
	(struct linux_target_ops) <breakpoint_from_pc>: Initialize field.
	* target.h (struct target_ops) <breakpoint_from_pc>: New field.
---
 gdb/gdbserver/linux-aarch64-low.c | 12 ++++++++++--
 gdb/gdbserver/linux-arm-low.c     | 28 ++++++++++++++++------------
 gdb/gdbserver/linux-bfin-low.c    | 12 ++++++++++--
 gdb/gdbserver/linux-cris-low.c    | 12 ++++++++++--
 gdb/gdbserver/linux-crisv32-low.c | 12 ++++++++++--
 gdb/gdbserver/linux-low.c         | 26 +++++++++++++++++++++++---
 gdb/gdbserver/linux-low.h         |  8 ++++++--
 gdb/gdbserver/linux-m32r-low.c    | 12 ++++++++++--
 gdb/gdbserver/linux-m68k-low.c    | 12 ++++++++++--
 gdb/gdbserver/linux-mips-low.c    | 12 ++++++++++--
 gdb/gdbserver/linux-nios2-low.c   | 23 +++++++++++++++--------
 gdb/gdbserver/linux-ppc-low.c     | 12 ++++++++++--
 gdb/gdbserver/linux-s390-low.c    | 12 ++++++++++--
 gdb/gdbserver/linux-sh-low.c      | 12 ++++++++++--
 gdb/gdbserver/linux-sparc-low.c   | 11 +++++++++--
 gdb/gdbserver/linux-tic6x-low.c   | 15 +++++++++++----
 gdb/gdbserver/linux-tile-low.c    | 12 ++++++++++--
 gdb/gdbserver/linux-x86-low.c     | 12 ++++++++++--
 gdb/gdbserver/linux-xtensa-low.c  | 12 ++++++++++--
 gdb/gdbserver/target.h            |  5 +++++
 20 files changed, 215 insertions(+), 57 deletions(-)
  

Comments

Yao Qi Oct. 15, 2015, 8:26 a.m. UTC | #1
Antoine Tremblay <antoine.tremblay@ericsson.com> writes:

> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index 3a1a6ae..dc16fe0 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -3012,7 +3012,11 @@ linux_wait_1 (ptid_t ptid,
>    if (!ptid_equal (step_over_bkpt, null_ptid)
>        && event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
>      {
> -      unsigned int increment_pc = the_low_target.breakpoint_len;
> +      int increment_pc = 0;
> +      CORE_ADDR stop_pc = event_child->stop_pc;
> +
> +      (*the_low_target.breakpoint_from_pc)
> +	(&stop_pc, &increment_pc);
>  

They can be in the same line.

>        if (debug_threads)
>  	{
> @@ -6932,6 +6936,15 @@ current_lwp_ptid (void)
>    return ptid_of (current_thread);
>  }
>  
> +const unsigned char *
> +linux_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
> +{
> +  if (the_low_target.breakpoint_from_pc != NULL)
> +    return (*the_low_target.breakpoint_from_pc) (pcptr, lenptr);
> +  else
> +    return NULL;
> +}

If the breakpoint_from_pc is NULL, this GDBserver port should be
broken.  We can use gdb_assert to assert
the_low_target.breakpoint_from_pc isn't NULL.

Otherwise the patch looks good to me.
  
Pedro Alves Oct. 15, 2015, 3:33 p.m. UTC | #2
On 10/05/2015 05:44 PM, Antoine Tremblay wrote:
> +static const unsigned char *

gdb_byte?

> +aarch64_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
> +{
> +  *len = aarch64_breakpoint_len;
> +  return (const unsigned char *) &aarch64_breakpoint;

... and then this cast goes away.

> +}
> +

> +static const unsigned char *
> +arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
> +{

Missing '/* Implementation of ... */' comment.

> +}
>  struct linux_target_ops the_low_target = {

Missing empty line.


> +
> +  /* Return the raw breakpoint for this target based on PC.  The PCPTR is
> +     ajusted to the real memory location in case a flag was present in the

"adjusted".  Suggest an example, like "a flag (e.g., the Thumb bit on ARM) was"

> +     PC.  */
> +  const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);

Thanks,
Pedro Alves
  
Antoine Tremblay Oct. 15, 2015, 3:58 p.m. UTC | #3
On 10/15/2015 11:33 AM, Pedro Alves wrote:
> On 10/05/2015 05:44 PM, Antoine Tremblay wrote:
>> +static const unsigned char *
>
> gdb_byte?
>
>> +aarch64_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
>> +{
>> +  *len = aarch64_breakpoint_len;
>> +  return (const unsigned char *) &aarch64_breakpoint;
>
> ... and then this cast goes away.
>
>> +}
>> +
>
Ouch indeed that was very wrong , I also fixed bfin, m68k, s390 and 
sparc that had the same kind of issue.

>> +static const unsigned char *
>> +arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
>> +{
>
> Missing '/* Implementation of ... */' comment.
>
Done.

>> +}
>>   struct linux_target_ops the_low_target = {
>
> Missing empty line.
>
Done.
>
>> +
>> +  /* Return the raw breakpoint for this target based on PC.  The PCPTR is
>> +     ajusted to the real memory location in case a flag was present in the
>
> "adjusted".  Suggest an example, like "a flag (e.g., the Thumb bit on ARM) was"
>
Done.
>> +     PC.  */
>> +  const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
>
> Thanks,
> Pedro Alves
>
  
Antoine Tremblay Oct. 15, 2015, 5:05 p.m. UTC | #4
On 10/15/2015 11:58 AM, Antoine Tremblay wrote:
>
>
> On 10/15/2015 11:33 AM, Pedro Alves wrote:
>> On 10/05/2015 05:44 PM, Antoine Tremblay wrote:
>>> +static const unsigned char *
>>
>> gdb_byte?

Note I ended up changing the breakpoint_from_pc signature to return 
gdb_byte * and changed all targets to match this in their breakpoint 
definitions and return from breakpoint_from_pc.
  

Patch

diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 5592e61..1e7a0ff 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -205,6 +205,15 @@  aarch64_set_pc (struct regcache *regcache, CORE_ADDR pc)
    (aarch64_default_breakpoint).  */
 static const gdb_byte aarch64_breakpoint[] = {0x00, 0x00, 0x20, 0xd4};
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+aarch64_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = aarch64_breakpoint_len;
+  return (const unsigned char *) &aarch64_breakpoint;
+}
+
 /* Implementation of linux_target_ops method "breakpoint_at".  */
 
 static int
@@ -3238,8 +3247,7 @@  struct linux_target_ops the_low_target =
   NULL, /* fetch_register */
   aarch64_get_pc,
   aarch64_set_pc,
-  (const unsigned char *) &aarch64_breakpoint,
-  aarch64_breakpoint_len,
+  aarch64_breakpoint_from_pc,
   NULL, /* breakpoint_reinsert_addr */
   0,    /* decr_pc_after_break */
   aarch64_breakpoint_at,
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index a277bb6..367c704 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -913,6 +913,21 @@  arm_regs_info (void)
     return &regs_info_arm;
 }
 
+static const unsigned char *
+arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = arm_breakpoint_len;
+   /* Define an ARM-mode breakpoint; we only set breakpoints in the C
+     library, which is most likely to be ARM.  If the kernel supports
+     clone events, we will never insert a breakpoint, so even a Thumb
+     C library will work; so will mixing EABI/non-EABI gdbserver and
+     application.  */
+#ifndef __ARM_EABI__
+  return (const unsigned char *) &arm_breakpoint;
+#else
+  return (const unsigned char *) &arm_eabi_breakpoint;
+#endif
+}
 struct linux_target_ops the_low_target = {
   arm_arch_setup,
   arm_regs_info,
@@ -921,18 +936,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   arm_get_pc,
   arm_set_pc,
-
-  /* Define an ARM-mode breakpoint; we only set breakpoints in the C
-     library, which is most likely to be ARM.  If the kernel supports
-     clone events, we will never insert a breakpoint, so even a Thumb
-     C library will work; so will mixing EABI/non-EABI gdbserver and
-     application.  */
-#ifndef __ARM_EABI__
-  (const unsigned char *) &arm_breakpoint,
-#else
-  (const unsigned char *) &arm_eabi_breakpoint,
-#endif
-  arm_breakpoint_len,
+  arm_breakpoint_from_pc,
   arm_reinsert_addr,
   0,
   arm_breakpoint_at,
diff --git a/gdb/gdbserver/linux-bfin-low.c b/gdb/gdbserver/linux-bfin-low.c
index 4002f22..2dc57b4 100644
--- a/gdb/gdbserver/linux-bfin-low.c
+++ b/gdb/gdbserver/linux-bfin-low.c
@@ -75,6 +75,15 @@  bfin_set_pc (struct regcache *regcache, CORE_ADDR pc)
 #define bfin_breakpoint_len 2
 static const unsigned char bfin_breakpoint[bfin_breakpoint_len] = {0xa1, 0x00};
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+bfin_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = bfin_breakpoint_len;
+  return (const unsigned char *) &bfin_breakpoint;
+}
+
 static int
 bfin_breakpoint_at (CORE_ADDR where)
 {
@@ -122,8 +131,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   bfin_get_pc,
   bfin_set_pc,
-  bfin_breakpoint,
-  bfin_breakpoint_len,
+  bfin_breakpoint_from_pc,
   NULL, /* breakpoint_reinsert_addr */
   2,
   bfin_breakpoint_at,
diff --git a/gdb/gdbserver/linux-cris-low.c b/gdb/gdbserver/linux-cris-low.c
index e0bfa1a..64413f8 100644
--- a/gdb/gdbserver/linux-cris-low.c
+++ b/gdb/gdbserver/linux-cris-low.c
@@ -81,6 +81,15 @@  cris_set_pc (struct regcache *regcache, CORE_ADDR pc)
 static const unsigned short cris_breakpoint = 0xe938;
 #define cris_breakpoint_len 2
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+cris_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = cris_breakpoint_len;
+  return (const unsigned char *) &cris_breakpoint;
+}
+
 static int
 cris_breakpoint_at (CORE_ADDR where)
 {
@@ -140,8 +149,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   cris_get_pc,
   cris_set_pc,
-  (const unsigned char *) &cris_breakpoint,
-  cris_breakpoint_len,
+  cris_breakpoint_from_pc,
   cris_reinsert_addr,
   0,
   cris_breakpoint_at,
diff --git a/gdb/gdbserver/linux-crisv32-low.c b/gdb/gdbserver/linux-crisv32-low.c
index 5120863..7e499f2 100644
--- a/gdb/gdbserver/linux-crisv32-low.c
+++ b/gdb/gdbserver/linux-crisv32-low.c
@@ -77,6 +77,15 @@  cris_set_pc (struct regcache *regcache, CORE_ADDR pc)
 static const unsigned short cris_breakpoint = 0xe938;
 #define cris_breakpoint_len 2
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+cris_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = cris_breakpoint_len;
+  return (const unsigned char *) &cris_breakpoint;
+}
+
 static int
 cris_breakpoint_at (CORE_ADDR where)
 {
@@ -420,8 +429,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   cris_get_pc,
   cris_set_pc,
-  (const unsigned char *) &cris_breakpoint,
-  cris_breakpoint_len,
+  cris_breakpoint_from_pc,
   cris_reinsert_addr,
   0,
   cris_breakpoint_at,
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 3a1a6ae..dc16fe0 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -3012,7 +3012,11 @@  linux_wait_1 (ptid_t ptid,
   if (!ptid_equal (step_over_bkpt, null_ptid)
       && event_child->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
     {
-      unsigned int increment_pc = the_low_target.breakpoint_len;
+      int increment_pc = 0;
+      CORE_ADDR stop_pc = event_child->stop_pc;
+
+      (*the_low_target.breakpoint_from_pc)
+	(&stop_pc, &increment_pc);
 
       if (debug_threads)
 	{
@@ -6932,6 +6936,15 @@  current_lwp_ptid (void)
   return ptid_of (current_thread);
 }
 
+const unsigned char *
+linux_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
+{
+  if (the_low_target.breakpoint_from_pc != NULL)
+    return (*the_low_target.breakpoint_from_pc) (pcptr, lenptr);
+  else
+    return NULL;
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_arch_setup,
@@ -7026,6 +7039,7 @@  static struct target_ops linux_target_ops = {
   linux_mntns_open_cloexec,
   linux_mntns_unlink,
   linux_mntns_readlink,
+  linux_breakpoint_from_pc,
 };
 
 static void
@@ -7053,10 +7067,16 @@  void
 initialize_low (void)
 {
   struct sigaction sigchld_action;
+  int breakpoint_len = 0;
+  const unsigned char *breakpoint = NULL;
+
   memset (&sigchld_action, 0, sizeof (sigchld_action));
   set_target_ops (&linux_target_ops);
-  set_breakpoint_data (the_low_target.breakpoint,
-		       the_low_target.breakpoint_len);
+
+  breakpoint = the_target->breakpoint_from_pc (NULL, &breakpoint_len);
+
+  set_breakpoint_data (breakpoint,
+		       breakpoint_len);
   linux_init_signals ();
   linux_ptrace_init_warnings ();
 
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index f8f6e78..a9964ac 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -141,8 +141,12 @@  struct linux_target_ops
 
   CORE_ADDR (*get_pc) (struct regcache *regcache);
   void (*set_pc) (struct regcache *regcache, CORE_ADDR newpc);
-  const unsigned char *breakpoint;
-  int breakpoint_len;
+
+  /* Return the raw breakpoint for this target based on PC.  The PCPTR is
+     ajusted to the real memory location in case a flag was present in the
+     PC.  */
+  const unsigned char *(*breakpoint_from_pc) (CORE_ADDR *pcptr, int *lenptr);
+
   CORE_ADDR (*breakpoint_reinsert_addr) (void);
 
   int decr_pc_after_break;
diff --git a/gdb/gdbserver/linux-m32r-low.c b/gdb/gdbserver/linux-m32r-low.c
index 8ffeda2..41c8252 100644
--- a/gdb/gdbserver/linux-m32r-low.c
+++ b/gdb/gdbserver/linux-m32r-low.c
@@ -73,6 +73,15 @@  m32r_set_pc (struct regcache *regcache, CORE_ADDR pc)
 static const unsigned short m32r_breakpoint = 0x10f1;
 #define m32r_breakpoint_len 2
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+m32r_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = m32r_breakpoint_len;
+  return (const unsigned char *) &m32r_breakpoint;
+}
+
 static int
 m32r_breakpoint_at (CORE_ADDR where)
 {
@@ -120,8 +129,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   m32r_get_pc,
   m32r_set_pc,
-  (const unsigned char *) &m32r_breakpoint,
-  m32r_breakpoint_len,
+  m32r_breakpoint_from_pc,
   NULL,
   0,
   m32r_breakpoint_at,
diff --git a/gdb/gdbserver/linux-m68k-low.c b/gdb/gdbserver/linux-m68k-low.c
index 39c9cc5..2f0c6a0 100644
--- a/gdb/gdbserver/linux-m68k-low.c
+++ b/gdb/gdbserver/linux-m68k-low.c
@@ -125,6 +125,15 @@  static struct regset_info m68k_regsets[] = {
 static const unsigned char m68k_breakpoint[] = { 0x4E, 0x4F };
 #define m68k_breakpoint_len 2
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+m68k_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = m68k_breakpoint_len;
+  return (unsigned char*) &m68k_breakpoint;
+}
+
 static CORE_ADDR
 m68k_get_pc (struct regcache *regcache)
 {
@@ -215,8 +224,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   m68k_get_pc,
   m68k_set_pc,
-  m68k_breakpoint,
-  m68k_breakpoint_len,
+  m68k_breakpoint_from_pc,
   NULL,
   2,
   m68k_breakpoint_at,
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index d1181b6..96cfc1c 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -266,6 +266,15 @@  mips_set_pc (struct regcache *regcache, CORE_ADDR pc)
 static const unsigned int mips_breakpoint = 0x0005000d;
 #define mips_breakpoint_len 4
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+mips_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = mips_breakpoint_len;
+  return (const unsigned char *) &mips_breakpoint;
+}
+
 /* We only place breakpoints in empty marker functions, and thread locking
    is outside of the function.  So rather than importing software single-step,
    we can just run until exit.  */
@@ -881,8 +890,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   mips_get_pc,
   mips_set_pc,
-  (const unsigned char *) &mips_breakpoint,
-  mips_breakpoint_len,
+  mips_breakpoint_from_pc,
   mips_reinsert_addr,
   0,
   mips_breakpoint_at,
diff --git a/gdb/gdbserver/linux-nios2-low.c b/gdb/gdbserver/linux-nios2-low.c
index 71542b4..d04dbe0 100644
--- a/gdb/gdbserver/linux-nios2-low.c
+++ b/gdb/gdbserver/linux-nios2-low.c
@@ -127,9 +127,23 @@  nios2_set_pc (struct regcache *regcache, CORE_ADDR pc)
 #define NIOS2_BREAKPOINT 0x003b6ffa
 #endif
 
+/* We only register the 4-byte breakpoint, even on R2 targets which also
+   support 2-byte breakpoints.  Since there is no supports_z_point_type
+   function provided, gdbserver never inserts software breakpoints itself
+   and instead relies on GDB to insert the breakpoint of the correct length
+   via a memory write.  */
 static const unsigned int nios2_breakpoint = NIOS2_BREAKPOINT;
 #define nios2_breakpoint_len 4
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+nios2_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = nios2_breakpoint_len;
+  return (const unsigned char *) &nios2_breakpoint;
+}
+
 /* Implement the breakpoint_reinsert_addr linux_target_ops method.  */
 
 static CORE_ADDR
@@ -263,14 +277,7 @@  struct linux_target_ops the_low_target =
   NULL,
   nios2_get_pc,
   nios2_set_pc,
-
-  /* We only register the 4-byte breakpoint, even on R2 targets which also
-     support 2-byte breakpoints.  Since there is no supports_z_point_type
-     function provided, gdbserver never inserts software breakpoints itself
-     and instead relies on GDB to insert the breakpoint of the correct length
-     via a memory write.  */
-  (const unsigned char *) &nios2_breakpoint,
-  nios2_breakpoint_len,
+  nios2_breakpoint_from_pc,
   nios2_reinsert_addr,
   0,
   nios2_breakpoint_at,
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index 188fac0..92ebb03 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -486,6 +486,15 @@  ppc_arch_setup (void)
 static const unsigned int ppc_breakpoint = 0x7d821008;
 #define ppc_breakpoint_len 4
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+ppc_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = ppc_breakpoint_len;
+  return (const unsigned char *) &ppc_breakpoint;
+}
+
 static int
 ppc_breakpoint_at (CORE_ADDR where)
 {
@@ -685,8 +694,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   ppc_get_pc,
   ppc_set_pc,
-  (const unsigned char *) &ppc_breakpoint,
-  ppc_breakpoint_len,
+  ppc_breakpoint_from_pc,
   NULL,
   0,
   ppc_breakpoint_at,
diff --git a/gdb/gdbserver/linux-s390-low.c b/gdb/gdbserver/linux-s390-low.c
index 8a0a689..d973f96 100644
--- a/gdb/gdbserver/linux-s390-low.c
+++ b/gdb/gdbserver/linux-s390-low.c
@@ -397,6 +397,15 @@  static struct regset_info s390_regsets[] = {
 static const unsigned char s390_breakpoint[] = { 0, 1 };
 #define s390_breakpoint_len 2
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+s390_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = s390_breakpoint_len;
+  return (const unsigned char *) &s390_breakpoint;
+}
+
 static CORE_ADDR
 s390_get_pc (struct regcache *regcache)
 {
@@ -665,8 +674,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   s390_get_pc,
   s390_set_pc,
-  s390_breakpoint,
-  s390_breakpoint_len,
+  s390_breakpoint_from_pc,
   NULL,
   s390_breakpoint_len,
   s390_breakpoint_at,
diff --git a/gdb/gdbserver/linux-sh-low.c b/gdb/gdbserver/linux-sh-low.c
index 218d4d3..2b5cb15 100644
--- a/gdb/gdbserver/linux-sh-low.c
+++ b/gdb/gdbserver/linux-sh-low.c
@@ -77,6 +77,15 @@  sh_set_pc (struct regcache *regcache, CORE_ADDR pc)
 static const unsigned short sh_breakpoint = 0xc3c3;
 #define sh_breakpoint_len 2
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+sh_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = sh_breakpoint_len;
+  return (const unsigned char *) &sh_breakpoint;
+}
+
 static int
 sh_breakpoint_at (CORE_ADDR where)
 {
@@ -148,8 +157,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   sh_get_pc,
   sh_set_pc,
-  (const unsigned char *) &sh_breakpoint,
-  sh_breakpoint_len,
+  sh_breakpoint_from_pc,
   NULL,
   0,
   sh_breakpoint_at,
diff --git a/gdb/gdbserver/linux-sparc-low.c b/gdb/gdbserver/linux-sparc-low.c
index 796af8a..8e1febf 100644
--- a/gdb/gdbserver/linux-sparc-low.c
+++ b/gdb/gdbserver/linux-sparc-low.c
@@ -240,6 +240,14 @@  static const unsigned char sparc_breakpoint[INSN_SIZE] = {
 };
 #define sparc_breakpoint_len INSN_SIZE
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+sparc_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = sparc_breakpoint_len;
+  return (const unsigned char *) &sparc_breakpoint;
+}
 
 static int
 sparc_breakpoint_at (CORE_ADDR where)
@@ -323,8 +331,7 @@  struct linux_target_ops the_low_target = {
   sparc_get_pc,
   /* No sparc_set_pc is needed.  */
   NULL,
-  (const unsigned char *) sparc_breakpoint,
-  sparc_breakpoint_len,
+  sparc_breakpoint_from_pc,
   sparc_reinsert_addr,
   0,
   sparc_breakpoint_at,
diff --git a/gdb/gdbserver/linux-tic6x-low.c b/gdb/gdbserver/linux-tic6x-low.c
index a2ac3ee..a259e72 100644
--- a/gdb/gdbserver/linux-tic6x-low.c
+++ b/gdb/gdbserver/linux-tic6x-low.c
@@ -171,6 +171,16 @@  extern struct linux_target_ops the_low_target;
 
 static int *tic6x_regmap;
 static unsigned int tic6x_breakpoint;
+#define tic6x_breakpoint_len 4
+
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+tic6x_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = tic6x_breakpoint_len;
+  return (const unsigned char *) &tic6x_breakpoint;
+}
 
 /* Forward definition.  */
 static struct usrregs_info tic6x_usrregs_info;
@@ -247,8 +257,6 @@  tic6x_set_pc (struct regcache *regcache, CORE_ADDR pc)
   supply_register_by_name (regcache, "PC", newpc.buf);
 }
 
-#define tic6x_breakpoint_len 4
-
 static int
 tic6x_breakpoint_at (CORE_ADDR where)
 {
@@ -367,8 +375,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   tic6x_get_pc,
   tic6x_set_pc,
-  (const unsigned char *) &tic6x_breakpoint,
-  tic6x_breakpoint_len,
+  tic6x_breakpoint_from_pc,
   NULL,
   0,
   tic6x_breakpoint_at,
diff --git a/gdb/gdbserver/linux-tile-low.c b/gdb/gdbserver/linux-tile-low.c
index 6aaea6a..04c7cd0 100644
--- a/gdb/gdbserver/linux-tile-low.c
+++ b/gdb/gdbserver/linux-tile-low.c
@@ -88,6 +88,15 @@  tile_set_pc (struct regcache *regcache, CORE_ADDR pc)
 static uint64_t tile_breakpoint = 0x400b3cae70166000ULL;
 #define tile_breakpoint_len 8
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+tile_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = tile_breakpoint_len;
+  return (const unsigned char *) &tile_breakpoint;
+}
+
 static int
 tile_breakpoint_at (CORE_ADDR where)
 {
@@ -182,8 +191,7 @@  struct linux_target_ops the_low_target =
   NULL,
   tile_get_pc,
   tile_set_pc,
-  (const unsigned char *) &tile_breakpoint,
-  tile_breakpoint_len,
+  tile_breakpoint_from_pc,
   NULL,
   0,
   tile_breakpoint_at,
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 20d4257..41803c4 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -3243,6 +3243,15 @@  x86_emit_ops (void)
     return &i386_emit_ops;
 }
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+x86_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = x86_breakpoint_len;
+  return x86_breakpoint;
+}
+
 static int
 x86_supports_range_stepping (void)
 {
@@ -3261,8 +3270,7 @@  struct linux_target_ops the_low_target =
   NULL, /* fetch_register */
   x86_get_pc,
   x86_set_pc,
-  x86_breakpoint,
-  x86_breakpoint_len,
+  x86_breakpoint_from_pc,
   NULL,
   1,
   x86_breakpoint_at,
diff --git a/gdb/gdbserver/linux-xtensa-low.c b/gdb/gdbserver/linux-xtensa-low.c
index debe467..eb7c5bb 100644
--- a/gdb/gdbserver/linux-xtensa-low.c
+++ b/gdb/gdbserver/linux-xtensa-low.c
@@ -154,6 +154,15 @@  static struct regset_info xtensa_regsets[] = {
 static const unsigned char xtensa_breakpoint[] = XTENSA_BREAKPOINT;
 #define xtensa_breakpoint_len 2
 
+/* Implementation of linux_target_ops method "breakpoint_from_pc".  */
+
+static const unsigned char *
+xtensa_breakpoint_from_pc (CORE_ADDR *pcptr, int *len)
+{
+  *len = xtensa_breakpoint_len;
+  return xtensa_breakpoint;
+}
+
 static CORE_ADDR
 xtensa_get_pc (struct regcache *regcache)
 {
@@ -234,8 +243,7 @@  struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   xtensa_get_pc,
   xtensa_set_pc,
-  xtensa_breakpoint,
-  xtensa_breakpoint_len,
+  xtensa_breakpoint_from_pc,
   NULL,
   0,
   xtensa_breakpoint_at,
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index a2842b4..603819e 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -441,6 +441,11 @@  struct target_ops
      readlink(2).  */
   ssize_t (*multifs_readlink) (int pid, const char *filename,
 			       char *buf, size_t bufsiz);
+
+  /* Return the raw breakpoint for this target based on PC.  Note that the PC
+     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);
 };
 
 extern struct target_ops *the_target;