* linux-low.c (linux_wait_1): Avoid setting need_step_over is
[deliverable/binutils-gdb.git] / gdb / gdbserver / mem-break.c
index 33de80ccc2b302630d5520c9424c2db1348ebf6b..5256cb7ff58d346901a753733cbb9a59a2e36ccf 100644 (file)
@@ -26,9 +26,53 @@ int breakpoint_len;
 
 #define MAX_BREAKPOINT_LEN 8
 
+/* GDB will never try to install multiple breakpoints at the same
+   address.  But, we need to keep track of internal breakpoints too,
+   and so we do need to be able to install multiple breakpoints at the
+   same address transparently.  We keep track of two different, and
+   closely related structures.  A raw breakpoint, which manages the
+   low level, close to the metal aspect of a breakpoint.  It holds the
+   breakpoint address, and a buffer holding a copy of the instructions
+   that would be in memory had not been a breakpoint there (we call
+   that the shadow memory of the breakpoint).  We occasionally need to
+   temporarilly uninsert a breakpoint without the client knowing about
+   it (e.g., to step over an internal breakpoint), so we keep an
+   `inserted' state associated with this low level breakpoint
+   structure.  There can only be one such object for a given address.
+   Then, we have (a bit higher level) breakpoints.  This structure
+   holds a callback to be called whenever a breakpoint is hit, a
+   high-level type, and a link to a low level raw breakpoint.  There
+   can be many high-level breakpoints at the same address, and all of
+   them will point to the same raw breakpoint, which is reference
+   counted.  */
+
+/* The low level, physical, raw breakpoint.  */
+struct raw_breakpoint
+{
+  struct raw_breakpoint *next;
+
+  /* A reference count.  Each high level breakpoint referencing this
+     raw breakpoint accounts for one reference.  */
+  int refcount;
+
+  /* The breakpoint's insertion address.  There can only be one raw
+     breakpoint for a given PC.  */
+  CORE_ADDR pc;
+
+  /* The breakpoint's shadow memory.  */
+  unsigned char old_data[MAX_BREAKPOINT_LEN];
+
+  /* Non-zero if this breakpoint is currently inserted in the
+     inferior.  */
+  int inserted;
+};
+
 /* The type of a breakpoint.  */
 enum bkpt_type
   {
+    /* A GDB breakpoint, requested with a Z0 packet.  */
+    gdb_breakpoint,
+
     /* A basic-software-single-step breakpoint.  */
     reinsert_breakpoint,
 
@@ -37,38 +81,57 @@ enum bkpt_type
     other_breakpoint,
   };
 
+/* A high level (in gdbserver's perspective) breakpoint.  */
 struct breakpoint
 {
   struct breakpoint *next;
-  CORE_ADDR pc;
-  unsigned char old_data[MAX_BREAKPOINT_LEN];
-
-  /* Non-zero if this breakpoint is currently inserted in the
-     inferior.  */
-  int inserted;
 
   /* The breakpoint's type.  */
   enum bkpt_type type;
 
+  /* Link to this breakpoint's raw breakpoint.  This is always
+     non-NULL.  */
+  struct raw_breakpoint *raw;
+
   /* Function to call when we hit this breakpoint.  If it returns 1,
-     the breakpoint shall be deleted; 0, it will be left inserted.  */
+     the breakpoint shall be deleted; 0 or if this callback is NULL,
+     it will be left inserted.  */
   int (*handler) (CORE_ADDR);
 };
 
-static void uninsert_breakpoint (struct breakpoint *bp);
+static struct raw_breakpoint *
+find_raw_breakpoint_at (CORE_ADDR where)
+{
+  struct process_info *proc = current_process ();
+  struct raw_breakpoint *bp;
 
-static struct breakpoint *
+  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+    if (bp->pc == where)
+      return bp;
+
+  return NULL;
+}
+
+static struct raw_breakpoint *
 set_raw_breakpoint_at (CORE_ADDR where)
 {
   struct process_info *proc = current_process ();
-  struct breakpoint *bp;
+  struct raw_breakpoint *bp;
   int err;
 
   if (breakpoint_data == NULL)
     error ("Target does not support breakpoints.");
 
+  bp = find_raw_breakpoint_at (where);
+  if (bp != NULL)
+    {
+      bp->refcount++;
+      return bp;
+    }
+
   bp = xcalloc (1, sizeof (*bp));
   bp->pc = where;
+  bp->refcount = 1;
 
   err = (*the_target->read_memory) (where, bp->old_data,
                                    breakpoint_len);
@@ -97,8 +160,8 @@ set_raw_breakpoint_at (CORE_ADDR where)
 
   /* Link the breakpoint in.  */
   bp->inserted = 1;
-  bp->next = proc->breakpoints;
-  proc->breakpoints = bp;
+  bp->next = proc->raw_breakpoints;
+  proc->raw_breakpoints = bp;
   return bp;
 }
 
@@ -107,10 +170,11 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
 {
   struct process_info *proc = current_process ();
   struct breakpoint *bp;
+  struct raw_breakpoint *raw;
 
-  bp = set_raw_breakpoint_at (where);
+  raw = set_raw_breakpoint_at (where);
 
-  if (bp == NULL)
+  if (raw == NULL)
     {
       /* warn? */
       return NULL;
@@ -118,6 +182,8 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
 
   bp = xcalloc (1, sizeof (struct breakpoint));
   bp->type = other_breakpoint;
+
+  bp->raw = raw;
   bp->handler = handler;
 
   bp->next = proc->breakpoints;
@@ -126,11 +192,84 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
   return bp;
 }
 
-static void
-delete_breakpoint (struct breakpoint *todel)
+static int
+delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel)
+{
+  struct raw_breakpoint *bp, **bp_link;
+  int ret;
+
+  bp = proc->raw_breakpoints;
+  bp_link = &proc->raw_breakpoints;
+
+  while (bp)
+    {
+      if (bp == todel)
+       {
+         if (bp->inserted)
+           {
+             struct raw_breakpoint *prev_bp_link = *bp_link;
+
+             *bp_link = bp->next;
+
+             ret = (*the_target->write_memory) (bp->pc, bp->old_data,
+                                                breakpoint_len);
+             if (ret != 0)
+               {
+                 /* Something went wrong, relink the breakpoint.  */
+                 *bp_link = prev_bp_link;
+
+                 if (debug_threads)
+                   fprintf (stderr,
+                            "Failed to uninsert raw breakpoint "
+                            "at 0x%s (%s) while deleting it.\n",
+                            paddress (bp->pc), strerror (ret));
+                 return ret;
+               }
+
+           }
+         else
+           *bp_link = bp->next;
+
+         free (bp);
+         return 0;
+       }
+      else
+       {
+         bp_link = &bp->next;
+         bp = *bp_link;
+       }
+    }
+
+  warning ("Could not find raw breakpoint in list.");
+  return ENOENT;
+}
+
+static int
+release_breakpoint (struct process_info *proc, struct breakpoint *bp)
+{
+  int newrefcount;
+  int ret;
+
+  newrefcount = bp->raw->refcount - 1;
+  if (newrefcount == 0)
+    {
+      ret = delete_raw_breakpoint (proc, bp->raw);
+      if (ret != 0)
+       return ret;
+    }
+  else
+    bp->raw->refcount = newrefcount;
+
+  free (bp);
+
+  return 0;
+}
+
+static int
+delete_breakpoint_1 (struct process_info *proc, struct breakpoint *todel)
 {
-  struct process_info *proc = current_process ();
   struct breakpoint *bp, **bp_link;
+  int err;
 
   bp = proc->breakpoints;
   bp_link = &proc->breakpoints;
@@ -141,9 +280,12 @@ delete_breakpoint (struct breakpoint *todel)
        {
          *bp_link = bp->next;
 
-         uninsert_breakpoint (bp);
-         free (bp);
-         return;
+         err = release_breakpoint (proc, bp);
+         if (err != 0)
+           return err;
+
+         bp = *bp_link;
+         return 0;
        }
       else
        {
@@ -153,30 +295,71 @@ delete_breakpoint (struct breakpoint *todel)
     }
 
   warning ("Could not find breakpoint in list.");
+  return ENOENT;
+}
+
+static int
+delete_breakpoint (struct breakpoint *todel)
+{
+  struct process_info *proc = current_process ();
+  return delete_breakpoint_1 (proc, todel);
 }
 
 static struct breakpoint *
-find_breakpoint_at (CORE_ADDR where)
+find_gdb_breakpoint_at (CORE_ADDR where)
 {
   struct process_info *proc = current_process ();
-  struct breakpoint *bp = proc->breakpoints;
+  struct breakpoint *bp;
 
-  while (bp != NULL)
-    {
-      if (bp->pc == where)
-       return bp;
-      bp = bp->next;
-    }
+  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+    if (bp->type == gdb_breakpoint && bp->raw->pc == where)
+      return bp;
 
   return NULL;
 }
 
-void
-delete_breakpoint_at (CORE_ADDR addr)
+int
+set_gdb_breakpoint_at (CORE_ADDR where)
 {
-  struct breakpoint *bp = find_breakpoint_at (addr);
-  if (bp != NULL)
-    delete_breakpoint (bp);
+  struct breakpoint *bp;
+
+  if (breakpoint_data == NULL)
+    return 1;
+
+  bp = set_breakpoint_at (where, NULL);
+  if (bp == NULL)
+    return -1;
+
+  bp->type = gdb_breakpoint;
+  return 0;
+}
+
+int
+delete_gdb_breakpoint_at (CORE_ADDR addr)
+{
+  struct breakpoint *bp;
+  int err;
+
+  if (breakpoint_data == NULL)
+    return 1;
+
+  bp = find_gdb_breakpoint_at (addr);
+  if (bp == NULL)
+    return -1;
+
+  err = delete_breakpoint (bp);
+  if (err)
+    return -1;
+
+  return 0;
+}
+
+int
+gdb_breakpoint_here (CORE_ADDR where)
+{
+  struct breakpoint *bp = find_gdb_breakpoint_at (where);
+
+  return (bp != NULL);
 }
 
 void
@@ -185,7 +368,6 @@ set_reinsert_breakpoint (CORE_ADDR stop_at)
   struct breakpoint *bp;
 
   bp = set_breakpoint_at (stop_at, NULL);
-
   bp->type = reinsert_breakpoint;
 }
 
@@ -203,13 +385,7 @@ delete_reinsert_breakpoints (void)
       if (bp->type == reinsert_breakpoint)
        {
          *bp_link = bp->next;
-
-         /* If something goes wrong, maybe this is a shared library
-            breakpoint, and the shared library has been unmapped.
-            Assume the breakpoint is gone anyway.  */
-         uninsert_breakpoint (bp);
-         free (bp);
-
+         release_breakpoint (proc, bp);
          bp = *bp_link;
        }
       else
@@ -221,7 +397,7 @@ delete_reinsert_breakpoints (void)
 }
 
 static void
-uninsert_breakpoint (struct breakpoint *bp)
+uninsert_raw_breakpoint (struct raw_breakpoint *bp)
 {
   if (bp->inserted)
     {
@@ -245,9 +421,9 @@ uninsert_breakpoint (struct breakpoint *bp)
 void
 uninsert_breakpoints_at (CORE_ADDR pc)
 {
-  struct breakpoint *bp;
+  struct raw_breakpoint *bp;
 
-  bp = find_breakpoint_at (pc);
+  bp = find_raw_breakpoint_at (pc);
   if (bp == NULL)
     {
       /* This can happen when we remove all breakpoints while handling
@@ -261,11 +437,11 @@ uninsert_breakpoints_at (CORE_ADDR pc)
     }
 
   if (bp->inserted)
-    uninsert_breakpoint (bp);
+    uninsert_raw_breakpoint (bp);
 }
 
 static void
-reinsert_raw_breakpoint (struct breakpoint *bp)
+reinsert_raw_breakpoint (struct raw_breakpoint *bp)
 {
   int err;
 
@@ -285,16 +461,16 @@ reinsert_raw_breakpoint (struct breakpoint *bp)
 void
 reinsert_breakpoints_at (CORE_ADDR pc)
 {
-  struct breakpoint *bp;
+  struct raw_breakpoint *bp;
 
-  bp = find_breakpoint_at (pc);
+  bp = find_raw_breakpoint_at (pc);
   if (bp == NULL)
     {
       /* This can happen when we remove all breakpoints while handling
         a step-over.  */
       if (debug_threads)
        fprintf (stderr,
-                "Could not find breakpoint at 0x%s "
+                "Could not find raw breakpoint at 0x%s "
                 "in list (reinserting).\n",
                 paddress (pc));
       return;
@@ -314,9 +490,9 @@ check_breakpoints (CORE_ADDR stop_pc)
 
   while (bp)
     {
-      if (bp->pc == stop_pc)
+      if (bp->raw->pc == stop_pc)
        {
-         if (!bp->inserted)
+         if (!bp->raw->inserted)
            {
              warning ("Hit a removed breakpoint?");
              return;
@@ -326,7 +502,7 @@ check_breakpoints (CORE_ADDR stop_pc)
            {
              *bp_link = bp->next;
 
-             delete_breakpoint (bp);
+             release_breakpoint (proc, bp);
 
              bp = *bp_link;
              continue;
@@ -348,34 +524,24 @@ set_breakpoint_data (const unsigned char *bp_data, int bp_len)
 int
 breakpoint_here (CORE_ADDR addr)
 {
-  struct process_info *proc = current_process ();
-  struct breakpoint *bp;
-
-  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
-    if (bp->pc == addr)
-      return 1;
-
-  return 0;
+  return (find_raw_breakpoint_at (addr) != NULL);
 }
 
 int
 breakpoint_inserted_here (CORE_ADDR addr)
 {
-  struct process_info *proc = current_process ();
-  struct breakpoint *bp;
+  struct raw_breakpoint *bp;
 
-  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
-    if (bp->pc == addr && bp->inserted)
-      return 1;
+  bp = find_raw_breakpoint_at (addr);
 
-  return 0;
+  return (bp != NULL && bp->inserted);
 }
 
 void
 check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
 {
   struct process_info *proc = current_process ();
-  struct breakpoint *bp = proc->breakpoints;
+  struct raw_breakpoint *bp = proc->raw_breakpoints;
   CORE_ADDR mem_end = mem_addr + mem_len;
 
   for (; bp != NULL; bp = bp->next)
@@ -401,7 +567,8 @@ check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
       copy_offset = start - bp->pc;
       buf_offset = start - mem_addr;
 
-      memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len);
+      if (bp->inserted)
+       memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len);
     }
 }
 
@@ -409,7 +576,7 @@ void
 check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
 {
   struct process_info *proc = current_process ();
-  struct breakpoint *bp = proc->breakpoints;
+  struct raw_breakpoint *bp = proc->raw_breakpoints;
   CORE_ADDR mem_end = mem_addr + mem_len;
 
   for (; bp != NULL; bp = bp->next)
@@ -449,7 +616,7 @@ delete_all_breakpoints (void)
   struct process_info *proc = current_process ();
 
   while (proc->breakpoints)
-    delete_breakpoint (proc->breakpoints);
+    delete_breakpoint_1 (proc, proc->breakpoints);
 }
 
 /* Release all breakpoints, but do not try to un-insert them from the
@@ -458,12 +625,15 @@ delete_all_breakpoints (void)
 void
 free_all_breakpoints (struct process_info *proc)
 {
-  struct breakpoint *bp;
+  struct raw_breakpoint *raw_bp;
 
+  for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next)
+    raw_bp->inserted = 0;
+
+  /* Note: use PROC explicitly instead of deferring to
+     delete_all_breakpoints --- CURRENT_INFERIOR may already have been
+     released when we get here.  There should be no call to
+     current_process from here on.  */
   while (proc->breakpoints)
-    {
-      bp = proc->breakpoints;
-      proc->breakpoints = bp->next;
-      free (bp);
-    }
+    delete_breakpoint_1 (proc, proc->breakpoints);
 }
This page took 0.030614 seconds and 4 git commands to generate.