[RFA 4/4] Improved linker-debugger interface

Gary Benson gbenson@redhat.com
Fri Jul 13 09:42:00 GMT 2012


Gary Benson wrote:
> This patch builds on the probes-based runtime linker interface,
> adding incremental library list loading.

Again, a minor update to make the testcase use standard_output_file.

Thanks,
Gary

--
http://gbenson.net/
-------------- next part --------------
gdb/
2012-07-12  Gary Benson  <gbenson@redhat.com>

	* breakpoint.h (handle_solib_event): Moved function definition
	to solib.h, and added a new parameter.
	* breakpoint.c (handle_solib_event): Moved function to solib.c
	and added a new parameter.
	(bpstat_stop_status): Pass new argument to handle_solib_event.
	* solib.h (breakpoint.h): New include.
	(handle_solib_event): Moved function definition from breakpoint.h
	and added a new parameter.
	* solib.c (handle_solib_event): Moved function from breakpoint.c
	and added a new parameter.
	* infrun.c (handle_inferior_event): Pass new argument to
	handle_solib_event.
	* solist.h (breakpoint.h): New include.
	(target_so_ops): New field "handle_solib_event".
	* solib-svr4.c (svr4_free_library_list): New forward declaration.
	(probe_action): New enum.
	(probe_info): New field "action".
	(svr4_info): New field "solib_cache".
	(free_solib_cache): New function.
	(svr4_pspace_data_cleanup): Call the above.
	(svr4_copy_library_list): New function.
	(svr4_read_so_list): New parameter "prev_lm".
	Changed	return type from void to int.
	Return nonzero on success, zero on error.
	(svr4_current_sos): Return cached list if available.
	Add new argument to calls to svr4_read_so_list.
	(solib_event_probe_action): New function.
	(solib_cache_update_full): Likewise.
	(solib_cache_update_incremental): Likewise.
	(svr4_handle_solib_event): Likewise.
	(svr4_solib_create_inferior_hook): Free any cached solibs.
	(_initialize_svr4_solib): Initialise svr4_so_ops.handle_solib_event.

gdb/testsuite
2012-07-12  Gary Benson  <gbenson@redhat.com>

	* gdb.base/info-shared.exp: New file.
	* gdb.base/info-shared.c: Likewise.
	* gdb.base/info-shared-solib1.c: Likewise.
	* gdb.base/info-shared-solib2.c: Likewise.

diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 4e4f875..f678702 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1515,6 +1515,4 @@ extern int user_breakpoint_p (struct breakpoint *);
 /* Attempt to determine architecture of location identified by SAL.  */
 extern struct gdbarch *get_sal_arch (struct symtab_and_line sal);
 
-extern void handle_solib_event (void);
-
 #endif /* !defined (BREAKPOINT_H) */
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 6b9faf3..51c98a7 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5205,7 +5205,7 @@ bpstat_stop_status (struct address_space *aspace,
     {
       if (bs->breakpoint_at && bs->breakpoint_at->type == bp_shlib_event)
 	{
-	  handle_solib_event ();
+	  handle_solib_event (bs);
 	  break;
 	}
     }
@@ -5301,25 +5301,6 @@ handle_jit_event (void)
   target_terminal_inferior ();
 }
 
-/* Handle an solib event by calling solib_add.  */
-
-void
-handle_solib_event (void)
-{
-  clear_program_space_solib_cache (current_inferior ()->pspace);
-
-  /* Check for any newly added shared libraries if we're supposed to
-     be adding them automatically.  Switch terminal for any messages
-     produced by breakpoint_re_set.  */
-  target_terminal_ours_for_output ();
-#ifdef SOLIB_ADD
-  SOLIB_ADD (NULL, 0, &current_target, auto_solib_add);
-#else
-  solib_add (NULL, 0, &current_target, auto_solib_add);
-#endif
-  target_terminal_inferior ();
-}
-
 /* Prepare WHAT final decision for infrun.  */
 
 /* Decide what infrun needs to do with this bpstat.  */
diff --git a/gdb/solib.h b/gdb/solib.h
index 65e3857..6e92ddc 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -21,6 +21,9 @@
 #ifndef SOLIB_H
 #define SOLIB_H
 
+/* For bpstat.  */
+#include "breakpoint.h"
+
 /* Forward decl's for prototypes */
 struct so_list;
 struct target_ops;
@@ -91,6 +94,13 @@ extern CORE_ADDR gdb_bfd_lookup_symbol_from_symtab (bfd *abfd,
 								      void *),
 						    void *data);
 
+/* Handle an solib event by calling solib_add.  Targets which handle
+   solib events using breakpoints must pass a valid bpstat.  Targets
+   which handle solib events using some other mechanism should pass
+   NULL.  */
+
+extern void handle_solib_event (bpstat bs);
+
 /* Enable or disable optional solib event breakpoints as appropriate.  */
 
 extern void update_solib_breakpoints (void);
diff --git a/gdb/solib.c b/gdb/solib.c
index dda0130..072fe4d 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -1212,6 +1212,30 @@ no_shared_libraries (char *ignored, int from_tty)
 /* See solib.h.  */
 
 void
+handle_solib_event (bpstat bs)
+{
+  struct target_so_ops *ops = solib_ops (target_gdbarch);
+
+  if (ops->handle_solib_event != NULL)
+    ops->handle_solib_event (bs);
+
+  clear_program_space_solib_cache (current_inferior ()->pspace);
+
+  /* Check for any newly added shared libraries if we're supposed to
+     be adding them automatically.  Switch terminal for any messages
+     produced by breakpoint_re_set.  */
+  target_terminal_ours_for_output ();
+#ifdef SOLIB_ADD
+  SOLIB_ADD (NULL, 0, &current_target, auto_solib_add);
+#else
+  solib_add (NULL, 0, &current_target, auto_solib_add);
+#endif
+  target_terminal_inferior ();
+}
+
+/* See solib.h.  */
+
+void
 update_solib_breakpoints (void)
 {
   struct target_so_ops *ops = solib_ops (target_gdbarch);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 89fbcfc..7c621ca 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3318,7 +3318,7 @@ handle_inferior_event (struct execution_control_state *ecs)
 	    context_switch (ecs->ptid);
 	  regcache = get_thread_regcache (ecs->ptid);
 
-	  handle_solib_event ();
+	  handle_solib_event (NULL);
 
 	  ecs->event_thread->control.stop_bpstat
 	    = bpstat_stop_status (get_regcache_aspace (regcache),
diff --git a/gdb/solist.h b/gdb/solist.h
index 0d9046d..ea580a9 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -23,6 +23,8 @@
 #define SO_NAME_MAX_PATH_SIZE 512	/* FIXME: Should be dynamic */
 /* For domain_enum domain.  */
 #include "symtab.h"
+/* For bpstat.  */
+#include "breakpoint.h"
 
 /* Forward declaration for target specific link map information.  This
    struct is opaque to all but the target specific file.  */
@@ -150,6 +152,13 @@ struct target_so_ops
     int (*keep_data_in_core) (CORE_ADDR vaddr,
 			      unsigned long size);
 
+    /* Target-specific handling of solib events.  For targets which
+       handle solib events using breakpoints a valid bpstat must be
+       passed.  Targets which handle solib events using some other
+       mechanism should pass NULL.  This pointer can be NULL, in which
+       case no specific handling is necessary for this target.  */
+    void (*handle_solib_event) (bpstat bs);
+
     /* Enable or disable optional solib event breakpoints as
        appropriate.  This should be called whenever
        stop_on_solib_events is changed.  This pointer can be
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index c111f04..ebb8c3c 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -52,6 +52,7 @@
 static struct link_map_offsets *svr4_fetch_link_map_offsets (void);
 static int svr4_have_link_map_offsets (void);
 static void svr4_relocate_main_executable (void);
+static void svr4_free_library_list (void *p_list);
 
 /* Link map info to include in an allocated so_list entry.  */
 
@@ -94,6 +95,25 @@ static const char * const solib_break_names[] =
   NULL
 };
 
+/* What to do with the link_map cache.  */
+
+enum probe_action
+  {
+    /* No action is required.  The cache is still valid.  */
+    LM_CACHE_NO_ACTION,
+
+    /* Something went wrong.  The cache may be invalid and must be
+       cleared.  Do not attempt further caching at this stop.  */
+    LM_CACHE_INVALIDATE,
+
+    /* The cache should be reloaded now.  */
+    LM_CACHE_RELOAD,
+
+    /* Attempt to incrementally update the cache.  If the update
+       fails or is not possible, fall back to LM_CACHE_RELOAD.  */
+    LM_CACHE_UPDATE_OR_RELOAD
+  };
+
 /* A list of named probes which, if present in the dynamic linker,
    allow more fine-grained breakpoints to be placed on shared library
    events.  */
@@ -106,16 +126,20 @@ struct probe_info
   /* Nonzero if this probe must be stopped at even when
      stop-on-solib-events is off.  */
   int mandatory;
+
+  /* What to do with the link_map cache when a breakpoint at this
+     probe is hit.  */
+  enum probe_action action;
 };
 
 static const struct probe_info probe_info[] =
 {
-  { "init_start", 0 },
-  { "init_complete", 1 },
-  { "map_start", 0 },
-  { "reloc_complete", 1 },
-  { "unmap_start", 0 },
-  { "unmap_complete", 1 },
+  { "init_start", 0, LM_CACHE_NO_ACTION },
+  { "init_complete", 1, LM_CACHE_RELOAD },
+  { "map_start", 0, LM_CACHE_NO_ACTION },
+  { "reloc_complete", 1, LM_CACHE_UPDATE_OR_RELOAD },
+  { "unmap_start", 0, LM_CACHE_NO_ACTION },
+  { "unmap_complete", 1, LM_CACHE_RELOAD },
 };
 
 #define NUM_PROBES ARRAY_SIZE (probe_info)
@@ -347,6 +371,10 @@ struct svr4_info
 
   /* Named probes in the dynamic linker.  */
   VEC (probe_p) *probes[NUM_PROBES];
+
+  /* List of objects loaded from the inferior, used by the
+     probes-based interface to support incremental updates.  */
+  struct so_list *solib_cache;
 };
 
 /* Per-program-space data key.  */
@@ -365,6 +393,18 @@ free_probes (struct svr4_info *info)
   memset (info->probes, 0, sizeof (info->probes));
 }
 
+/* Free any cached solibs.  */
+
+static void
+free_solib_cache (struct svr4_info *info)
+{
+  if (info->solib_cache == NULL)
+    return;
+
+  svr4_free_library_list (&info->solib_cache);
+  info->solib_cache = NULL;
+}
+
 static void
 svr4_pspace_data_cleanup (struct program_space *pspace, void *arg)
 {
@@ -375,6 +415,7 @@ svr4_pspace_data_cleanup (struct program_space *pspace, void *arg)
     return;
 
   free_probes (info);
+  free_solib_cache (info);
 
   xfree (info);
 }
@@ -1036,6 +1077,36 @@ svr4_free_library_list (void *p_list)
     }
 }
 
+/* Copy library list.  */
+
+static struct so_list *
+svr4_copy_library_list (struct so_list *src)
+{
+  struct link_map_offsets *lmo = svr4_fetch_link_map_offsets ();
+  struct so_list *dst = NULL;
+  struct so_list **link = &dst;
+
+  while (src != NULL)
+    {
+      struct so_list *new;
+
+      new = XZALLOC (struct so_list);
+
+      memcpy (new, src, sizeof (struct so_list));
+
+      new->lm_info = xmalloc (lmo->link_map_size);
+      memcpy (new->lm_info, src->lm_info, lmo->link_map_size);
+
+      new->next = NULL;
+      *link = new;
+      link = &new->next;
+
+      src = src->next;
+    }
+
+  return dst;
+}
+
 #ifdef HAVE_LIBEXPAT
 
 #include "xml-support.h"
@@ -1215,15 +1286,17 @@ svr4_default_sos (void)
   return new;
 }
 
-/* Read the whole inferior libraries chain starting at address LM.  Add the
-   entries to the tail referenced by LINK_PTR_PTR.  Ignore the first entry if
-   IGNORE_FIRST and set global MAIN_LM_ADDR according to it.  */
+/* Read the whole inferior libraries chain starting at address LM.
+   Expect the first entry in the chain's previous entry to be PREV_LM.
+   Add the entries to the tail referenced by LINK_PTR_PTR.  Ignore the
+   first entry if IGNORE_FIRST and set global MAIN_LM_ADDR according
+   to it.  Returns nonzero upon success.  */
 
-static void
-svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr,
-		   int ignore_first)
+static int
+svr4_read_so_list (CORE_ADDR lm, CORE_ADDR prev_lm,
+		   struct so_list ***link_ptr_ptr, int ignore_first)
 {
-  CORE_ADDR prev_lm = 0, next_lm;
+  CORE_ADDR next_lm;
 
   for (; lm != 0; prev_lm = lm, lm = next_lm)
     {
@@ -1240,7 +1313,7 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr,
       if (new->lm_info == NULL)
 	{
 	  do_cleanups (old_chain);
-	  break;
+	  return 0;
 	}
 
       next_lm = new->lm_info->l_next;
@@ -1251,7 +1324,7 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr,
 		   paddress (target_gdbarch, prev_lm),
 		   paddress (target_gdbarch, new->lm_info->l_prev));
 	  do_cleanups (old_chain);
-	  break;
+	  return 0;
 	}
 
       /* For SVR4 versions, the first entry in the link map is for the
@@ -1297,6 +1370,8 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr,
       **link_ptr_ptr = new;
       *link_ptr_ptr = &new->next;
     }
+
+  return 1;
 }
 
 /* Implement the "current_sos" target_so_ops method.  */
@@ -1333,6 +1408,10 @@ svr4_current_sos (void)
 
   info = get_svr4_info ();
 
+  /* If we have a cached result then return a copy.  */
+  if (info->solib_cache != NULL)
+    return svr4_copy_library_list (info->solib_cache);
+
   /* Always locate the debug struct, in case it has moved.  */
   info->debug_base = 0;
   locate_base (info);
@@ -1355,7 +1434,7 @@ svr4_current_sos (void)
      `struct so_list' nodes.  */
   lm = solib_svr4_r_map (info);
   if (lm)
-    svr4_read_so_list (lm, &link_ptr, ignore_first);
+    svr4_read_so_list (lm, 0, &link_ptr, ignore_first);
 
   /* On Solaris, the dynamic linker is not in the normal list of
      shared objects, so make sure we pick it up too.  Having
@@ -1363,7 +1442,7 @@ svr4_current_sos (void)
      for skipping dynamic linker resolver code.  */
   lm = solib_svr4_r_ldsomap (info);
   if (lm)
-    svr4_read_so_list (lm, &link_ptr, 0);
+    svr4_read_so_list (lm, 0, &link_ptr, 0);
 
   discard_cleanups (back_to);
 
@@ -1494,6 +1573,146 @@ solib_event_probe_at (struct bp_location *loc, struct probe_and_info *result)
   return NULL;
 }
 
+/* Decide what action to take when the specified solib event probe is
+   hit.  */
+
+static enum probe_action
+solib_event_probe_action (struct probe_and_info *pi)
+{
+  enum probe_action action;
+  int update;
+  struct obj_section *os;
+  unsigned probe_argc;
+  struct svr4_info *info;
+  CORE_ADDR debug_base;
+
+  action = pi->info->action;
+  if (action == LM_CACHE_NO_ACTION || action == LM_CACHE_INVALIDATE)
+    return action;
+
+  gdb_assert (action == LM_CACHE_RELOAD
+	      || action == LM_CACHE_UPDATE_OR_RELOAD);
+
+  os = find_pc_section (pi->probe->address);
+  if (os == NULL)
+    return LM_CACHE_INVALIDATE;
+
+  /* Check that an appropriate number of arguments has been supplied.
+     We expect:
+       arg1: Lmid_t lmid (mandatory)
+       arg2: struct r_debug *r_debug (mandatory)
+       arg3: struct link_map *new (optional, for incremental updates)  */
+  probe_argc = get_probe_argument_count (os->objfile, pi->probe);
+  if (probe_argc == 2)
+    action = LM_CACHE_RELOAD;
+  else if (probe_argc < 2)
+    return LM_CACHE_INVALIDATE;
+
+  /* We only currently support the global namespace (PR gdb/11839).
+     If the probe's r_debug doesn't match the global r_debug then
+     this event refers to some other namespace and must be ignored.  */
+  info = get_svr4_info ();
+
+  /* Always locate the debug struct, in case it has moved.  */
+  info->debug_base = 0;
+  locate_base (info);
+
+  debug_base = value_as_address (evaluate_probe_argument (os->objfile,
+							  pi->probe, 1));
+
+  if (debug_base != info->debug_base)
+    return LM_CACHE_NO_ACTION;
+
+  return action;
+}
+
+/* Populate the solib cache with by reading the entire list of shared
+   objects from the inferior.  */
+
+static void
+solib_cache_update_full (void)
+{
+  struct svr4_info *info = get_svr4_info ();
+
+  gdb_assert (info->solib_cache == NULL);
+  info->solib_cache = svr4_current_sos ();
+}
+
+/* Update the solib cache starting from the link-map supplied by the
+   linker in the probe's third argument.  Returns nonzero if the list
+   was successfully updated, or zero to indicate failure.  */
+
+static int
+solib_cache_update_incremental (struct probe_and_info *pi)
+{
+  struct svr4_info *info = get_svr4_info ();
+  struct so_list *tail, **link;
+  struct obj_section *os;
+  CORE_ADDR lm;
+
+  if (info->solib_cache == NULL)
+    return 0;
+
+  tail = info->solib_cache;
+  while (tail->next)
+    tail = tail->next;
+  link = &tail->next;
+
+  os = find_pc_section (pi->probe->address);
+  if (os == NULL)
+    return 0;
+
+  lm = value_as_address (evaluate_probe_argument (os->objfile,
+						  pi->probe, 2));
+
+  if (lm == 0)
+    return 0;
+
+  return svr4_read_so_list (lm, tail->lm_info->lm_addr, &link, 0);
+}
+
+/* Update the solib cache as appropriate when using the probes-based
+   linker interface.  Do nothing if using the standard interface.  */
+
+static void
+svr4_handle_solib_event (bpstat bs)
+{
+  struct svr4_info *info = get_svr4_info ();
+  struct probe_and_info buf, *pi;
+  enum probe_action action;
+
+  /* It is possible that this function will be called incorrectly
+     by the handle_solib_event in handle_inferior_event if GDB goes
+     fully multi-target.  */
+  gdb_assert (bs != NULL);
+
+  if (!info->using_probes)
+    return;
+
+  pi = solib_event_probe_at (bs->bp_location_at, &buf);
+  if (pi == NULL)
+    action = LM_CACHE_INVALIDATE; /* Should never happen.  */
+  else
+    action = solib_event_probe_action (pi);
+
+  if (action == LM_CACHE_NO_ACTION)
+    return;
+
+  if (action == LM_CACHE_UPDATE_OR_RELOAD)
+    {
+      if (solib_cache_update_incremental (pi))
+	return;
+
+      action = LM_CACHE_RELOAD;
+    }
+
+  free_solib_cache (info);
+  if (action == LM_CACHE_INVALIDATE)
+    return;
+
+  solib_cache_update_full ();
+}
+
 /* Helper function for svr4_update_solib_event_breakpoints.  */
 
 static int
@@ -2446,6 +2665,9 @@ svr4_solib_create_inferior_hook (int from_tty)
 
   info = get_svr4_info ();
 
+  /* Free any solibs cached by the probes-based linker interface.  */
+  free_solib_cache (info);
+
   /* Relocate the main executable if necessary.  */
   svr4_relocate_main_executable ();
 
@@ -2712,5 +2934,6 @@ _initialize_svr4_solib (void)
   svr4_so_ops.lookup_lib_global_symbol = elf_lookup_lib_symbol;
   svr4_so_ops.same = svr4_same;
   svr4_so_ops.keep_data_in_core = svr4_keep_data_in_core;
+  svr4_so_ops.handle_solib_event = svr4_handle_solib_event;
   svr4_so_ops.update_breakpoints = svr4_update_solib_event_breakpoints;
 }
diff --git a/gdb/testsuite/gdb.base/info-shared-solib1.c b/gdb/testsuite/gdb.base/info-shared-solib1.c
new file mode 100644
index 0000000..9979ee7
--- /dev/null
+++ b/gdb/testsuite/gdb.base/info-shared-solib1.c
@@ -0,0 +1,24 @@
+/* Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+
+int
+foo (int n)
+{
+  printf ("foo %d\n", n);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/info-shared-solib2.c b/gdb/testsuite/gdb.base/info-shared-solib2.c
new file mode 100644
index 0000000..d4ed1e6
--- /dev/null
+++ b/gdb/testsuite/gdb.base/info-shared-solib2.c
@@ -0,0 +1,24 @@
+/* Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+
+int
+bar (int n)
+{
+  printf ("bar %d\n", n);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/info-shared.c b/gdb/testsuite/gdb.base/info-shared.c
new file mode 100644
index 0000000..d699a11
--- /dev/null
+++ b/gdb/testsuite/gdb.base/info-shared.c
@@ -0,0 +1,48 @@
+/* Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+
+void
+stop ()
+{
+}
+
+int
+main ()
+{
+  void *handle1, *handle2;
+  void (*func)(int);
+
+  handle1 = dlopen (SHLIB1_NAME, RTLD_LAZY);
+  stop ();
+
+  handle2 = dlopen (SHLIB2_NAME, RTLD_LAZY);
+  stop ();
+
+  func = (void (*)(int)) dlsym (handle1, "foo");
+  func (1);
+
+  func = (void (*)(int)) dlsym (handle2, "bar");
+  func (2);
+
+  dlclose (handle1);
+  stop ();
+
+  dlclose (handle2);
+  stop ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/info-shared.exp b/gdb/testsuite/gdb.base/info-shared.exp
new file mode 100644
index 0000000..f3a74c6
--- /dev/null
+++ b/gdb/testsuite/gdb.base/info-shared.exp
@@ -0,0 +1,139 @@
+# Copyright 2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if { [skip_shlib_tests] || [is_remote target] } {
+    return 0
+}
+
+standard_testfile
+
+set lib1name $testfile-solib1
+set srcfile_lib1 $srcdir/$subdir/$lib1name.c
+set binfile_lib1 [standard_output_file $lib1name.so]
+set define1 -DSHLIB1_NAME\=\"$binfile_lib1\"
+
+set lib2name $testfile-solib2
+set srcfile_lib2 $srcdir/$subdir/$lib2name.c
+set binfile_lib2 [standard_output_file $lib2name.so]
+set define2 -DSHLIB2_NAME\=\"$binfile_lib2\"
+
+if { [gdb_compile_shlib $srcfile_lib1 $binfile_lib1 \
+	  [list additional_flags=-fPIC]] != "" } {
+    untested "Could not compile $binfile_lib1."
+    return -1
+}
+
+if { [gdb_compile_shlib $srcfile_lib2 $binfile_lib2 \
+	  [list additional_flags=-fPIC]] != "" } {
+    untested "Could not compile $binfile_lib2."
+    return -1
+}
+
+set cflags "$define1 $define2"
+if { [prepare_for_testing $testfile.exp $testfile $srcfile \
+	  [list additional_flags=$cflags libs=-ldl]] } {
+    return -1
+}
+
+# Run "info sharedlibrary" and check for the presence or absence of
+# our libraries.
+proc check_info_shared { test expect1 expect2 } {
+    global lib1name
+    global lib2name
+    global gdb_prompt
+
+    set actual1 0
+    set actual2 0
+
+    gdb_test_multiple "info sharedlibrary" $test {
+	-re $lib1name {
+	    set actual1 1
+	    exp_continue
+	}
+	-re $lib2name {
+	    set actual2 1
+	    exp_continue
+	}
+	-re "\r\n$gdb_prompt $" {
+	    if { $actual1 == $expect1 && $actual2 == $expect2 } {
+		pass $test
+	    } else {
+		fail $test
+	    }
+	}
+    }
+}
+
+# Set up breakpoints.
+gdb_test "break stop" {Breakpoint [0-9]+ at .*}
+gdb_test_no_output "set breakpoint pending on"
+gdb_test "break foo" {Breakpoint [0-9]+ \(foo\) pending\.}
+gdb_test "break bar" {Breakpoint [0-9]+ \(bar\) pending\.}
+
+# Check neither of the libraries are loaded at the start.
+gdb_test "start" {Temporary breakpoint [0-9]+, .* in main \(\)}
+check_info_shared "info sharedlibrary #1" 0 0
+
+# Run to the first stop and check that only the first library is loaded.
+gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)}
+check_info_shared "info sharedlibrary #2" 1 0
+
+# Run to the second stop and check that both libraries are loaded.
+gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)}
+check_info_shared "info sharedlibrary #3" 1 1
+
+# Check that the next stop is in foo.
+gdb_test "c" {Breakpoint [0-9]+, .* in foo \(\) from .*}
+
+# Check that the next stop is in bar.
+gdb_test "c" {Breakpoint [0-9]+, .* in bar \(\) from .*}
+
+# Restart the inferior and make sure there are no breakpoint reset
+# errors.  These can happen with the probes-based runtime linker
+# interface if the cache is not cleared correctly.
+set test "restart"
+gdb_test_multiple "run" $test {
+    -re {Start it from the beginning\? \(y or n\) } {
+	send_gdb "y\n"
+	exp_continue
+    }
+    -re {Error in re-setting breakpoint} {
+	fail $test
+    }
+    -re "\r\n$gdb_prompt $" {
+	pass $test
+    }
+}
+
+# We're at the first stop.  Check that only the first library is loaded.
+check_info_shared "info sharedlibrary #4" 1 0
+
+# Run to the second stop and check that both libraries are loaded.
+gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)}
+check_info_shared "info sharedlibrary #5" 1 1
+
+# Check that the next stop is in foo.
+gdb_test "c" {Breakpoint [0-9]+, .* in foo \(\) from .*}
+
+# Check that the next stop is in bar.
+gdb_test "c" {Breakpoint [0-9]+, .* in bar \(\) from .*}
+
+# Run to the next stop and check that the first library has been unloaded.
+gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)}
+check_info_shared "info sharedlibrary #6" 0 1
+
+# Run to the last stop and check that both libraries are gone.
+gdb_test "c" {Breakpoint [0-9]+, .* in stop \(\)}
+check_info_shared "info sharedlibrary #7" 0 0


More information about the Gdb-patches mailing list