[RFA 2/4] Improved linker-debugger interface

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


Gary Benson wrote:
> This patch implements the basics of the improved runtime linker
> interface in GDB--everything except the incremental library
> list loading, basically.

This is 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>

	* infrun.c (set_stop_on_solib_events): New function.
	(_initialize_infrun): Use the above for "set stop-on-solib-events".
	* solib-svr4.c (probe_info): New struct.
	(probe_info): New static variable.
	(NUM_PROBES): New definition.
	(svr4_info): New fields  "using_probes" and "probes".
	(free_probes): New function.
	(svr4_pspace_data_cleanup): Free probes.
	(probe_and_info): New struct.
	(solib_event_probe_at): New function.
	(svr4_update_solib_event_breakpoint): Likewise.
	(svr4_update_solib_event_breakpoints): Likewise.
	(svr4_create_solib_event_breakpoints): Likewise.
	(enable_break): Free probes before creating breakpoints.
	Use svr4_create_solib_event_breakpoints to create breakpoints.
	(_initialize_svr4_solib): Initialise svr4_so_ops.update_breakpoints.
	* solib.h (update_solib_breakpoints): New function definition.
	* solib.c (update_solib_breakpoints): New function.
	* solist.h (target_so_ops): New field "update_breakpoints".

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

	* gdb.base/break-interp.exp (solib_bp): New constant.
	(reach_1): Use the above instead of "_dl_debug_state".
	(test_attach): Likewise.
	(test_ld): Likewise.
	* gdb.base/break-probes.exp: New file.
	* gdb.base/break-probes.c: Likewise.
	* gdb.base/break-probes-solib.c: Likewise.

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 11f981f..89fbcfc 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -361,6 +361,16 @@ static struct symbol *step_start_function;
 /* Nonzero if we want to give control to the user when we're notified
    of shared library events by the dynamic linker.  */
 int stop_on_solib_events;
+
+/* Enable or disable optional shared library event breakpoints
+   as appropriate when the above flag is changed.  */
+
+static void
+set_stop_on_solib_events (char *args, int from_tty, struct cmd_list_element *c)
+{
+  update_solib_breakpoints ();
+}
+
 static void
 show_stop_on_solib_events (struct ui_file *file, int from_tty,
 			   struct cmd_list_element *c, const char *value)
@@ -7204,7 +7214,7 @@ Show stopping for shared library events."), _("\
 If nonzero, gdb will give control to the user when the dynamic linker\n\
 notifies gdb of shared library events.  The most common event of interest\n\
 to the user would be loading/unloading of a new library."),
-			    NULL,
+			    set_stop_on_solib_events,
 			    show_stop_on_solib_events,
 			    &setlist, &showlist);
 
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
index 307e483..c111f04 100644
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -47,6 +47,8 @@
 #include "auxv.h"
 #include "exceptions.h"
 
+#include "probe.h"
+
 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);
@@ -92,6 +94,32 @@ static const char * const solib_break_names[] =
   NULL
 };
 
+/* A list of named probes which, if present in the dynamic linker,
+   allow more fine-grained breakpoints to be placed on shared library
+   events.  */
+
+struct probe_info
+{
+  /* The name of the probe.  */
+  const char *name;
+
+  /* Nonzero if this probe must be stopped at even when
+     stop-on-solib-events is off.  */
+  int mandatory;
+};
+
+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 },
+};
+
+#define NUM_PROBES ARRAY_SIZE (probe_info)
+
 static const char * const bkpt_names[] =
 {
   "_start",
@@ -313,17 +341,41 @@ struct svr4_info
   CORE_ADDR interp_text_sect_high;
   CORE_ADDR interp_plt_sect_low;
   CORE_ADDR interp_plt_sect_high;
+
+  /* Nonzero if we are using the probes-based interface.  */
+  int using_probes;
+
+  /* Named probes in the dynamic linker.  */
+  VEC (probe_p) *probes[NUM_PROBES];
 };
 
 /* Per-program-space data key.  */
 static const struct program_space_data *solib_svr4_pspace_data;
 
+/* Free any allocated probe vectors.  */
+
+static void
+free_probes (struct svr4_info *info)
+{
+  int i;
+
+  for (i = 0; i < NUM_PROBES; i++)
+    VEC_free (probe_p, info->probes[i]);
+
+  memset (info->probes, 0, sizeof (info->probes));
+}
+
 static void
 svr4_pspace_data_cleanup (struct program_space *pspace, void *arg)
 {
   struct svr4_info *info;
 
   info = program_space_data (pspace, solib_svr4_pspace_data);
+  if (info == NULL)
+    return;
+
+  free_probes (info);
+
   xfree (info);
 }
 
@@ -1400,6 +1452,168 @@ exec_entry_point (struct bfd *abfd, struct target_ops *targ)
 					     targ);
 }
 
+/* A probe and its associated information structure.  */
+
+struct probe_and_info
+{
+  /* The probe.  */
+  struct probe *probe;
+
+  /* The probe_info from which the probe was created.  */
+  const struct probe_info *info;
+};
+
+/* Get the solib event probe at the specified location, and the
+   probe_info the probe was created with.  Returns NULL if no solib
+   event probe exists at that location.  */
+
+static struct probe_and_info *
+solib_event_probe_at (struct bp_location *loc, struct probe_and_info *result)
+{
+  struct svr4_info *info = get_svr4_info ();
+  int i;
+
+  for (i = 0; i < NUM_PROBES; i++)
+    {
+      struct probe *probe;
+      int ix;
+
+      for (ix = 0; VEC_iterate (probe_p, info->probes[i], ix, probe); ++ix)
+	{
+	  if (loc->pspace == current_program_space
+	      && loc->address == probe->address)
+	    {
+	      result->info = &probe_info[i];
+	      result->probe = probe;
+
+	      return result;
+	    }
+	}
+    }
+
+  return NULL;
+}
+
+/* Helper function for svr4_update_solib_event_breakpoints.  */
+
+static int
+svr4_update_solib_event_breakpoint (struct breakpoint *b, void *arg)
+{
+  struct svr4_info *info = get_svr4_info ();
+  struct bp_location *loc;
+
+  if (b->type != bp_shlib_event)
+    return 0;
+
+  for (loc = b->loc; loc; loc = loc->next)
+    {
+      struct probe_and_info buf, *pi;
+
+      pi = solib_event_probe_at (loc, &buf);
+      if (pi != NULL)
+	{
+	  if (!pi->info->mandatory)
+	    b->enable_state = (stop_on_solib_events
+			       ? bp_enabled : bp_disabled);
+
+	  return 0;
+	}
+    }
+
+  return 0;
+}
+
+/* Enable or disable optional solib event breakpoints as appropriate.
+   Called whenever stop_on_solib_events is changed.  */
+
+static void
+svr4_update_solib_event_breakpoints (void)
+{
+  struct svr4_info *info = get_svr4_info ();
+
+  if (info->using_probes)
+    iterate_over_breakpoints (svr4_update_solib_event_breakpoint, NULL);
+}
+
+/* Both the SunOS and the SVR4 dynamic linkers call a marker function
+   before and after mapping and unmapping shared libraries.  The sole
+   purpose of this method is to allow debuggers to set a breakpoint so
+   they can track these changes.
+
+   Some versions of the glibc dynamic linker contain named probes
+   to allow more fine grained stopping.  Given the address of the
+   original marker function, this function attempts to find these
+   probes, and if found, sets breakpoints on those instead.  If the
+   probes aren't found, a single breakpoint is set on the original
+   marker function.  */
+
+static void
+svr4_create_solib_event_breakpoints (struct gdbarch *gdbarch, CORE_ADDR address)
+{
+  struct svr4_info *info = get_svr4_info ();
+  struct obj_section *os;
+
+  os = find_pc_section (address);
+  if (os != NULL)
+    {
+      int with_prefix;
+
+      for (with_prefix = 0; with_prefix <= 1; with_prefix++)
+	{
+	  int all_probes_found = 1;
+	  int i;
+
+	  for (i = 0; i < NUM_PROBES; i++)
+	    {
+	      char name[32] = { '\0' };
+
+	      /* Fedora 17, RHEL 6.2, and RHEL 6.3 shipped with an
+		 early version of the probes code in which the probes'
+		 names were prefixed with "rtld_".  The locations and
+		 arguments of the probes are otherwise the same, so we
+		 check for the prefixed version if the unprefixed
+		 probes are not found.  */
+
+	      if (with_prefix)
+		strncat (name, "rtld_", sizeof (name));
+
+	      strncat (name, probe_info[i].name, sizeof (name));
+
+	      info->probes[i] = find_probes_in_objfile (os->objfile, "rtld",
+							name);
+
+	      if (!VEC_length (probe_p, info->probes[i]))
+		{
+		  free_probes (info);
+		  all_probes_found = 0;
+		  break;
+		}
+	    }
+
+	  if (all_probes_found)
+	    {
+	      info->using_probes = 1;
+
+	      for (i = 0; i < NUM_PROBES; i++)
+		{
+		  struct probe *probe;
+		  int ix;
+
+		  for (ix = 0;
+		       VEC_iterate (probe_p, info->probes[i], ix, probe);
+		       ++ix)
+		    create_solib_event_breakpoint (gdbarch, probe->address);
+		}
+
+	      svr4_update_solib_event_breakpoints ();
+	      return;
+	    }
+	}
+    }
+
+  create_solib_event_breakpoint (gdbarch, address);
+}
+
 /* Helper function for gdb_bfd_lookup_symbol.  */
 
 static int
@@ -1452,6 +1666,9 @@ enable_break (struct svr4_info *info, int from_tty)
   info->interp_text_sect_low = info->interp_text_sect_high = 0;
   info->interp_plt_sect_low = info->interp_plt_sect_high = 0;
 
+  free_probes (info);
+  info->using_probes = 0;
+
   /* If we already have a shared library list in the target, and
      r_debug contains r_brk, set the breakpoint there - this should
      mean r_brk has already been relocated.  Assume the dynamic linker
@@ -1483,7 +1700,7 @@ enable_break (struct svr4_info *info, int from_tty)
 	 That knowledge is encoded in the address, if it's Thumb the low bit
 	 is 1.  However, we've stripped that info above and it's not clear
 	 what all the consequences are of passing a non-addr_bits_remove'd
-	 address to create_solib_event_breakpoint.  The call to
+	 address to svr4_create_solib_event_breakpoints.  The call to
 	 find_pc_section verifies we know about the address and have some
 	 hope of computing the right kind of breakpoint to use (via
 	 symbol info).  It does mean that GDB needs to be pointed at a
@@ -1521,7 +1738,7 @@ enable_break (struct svr4_info *info, int from_tty)
 		+ bfd_section_size (tmp_bfd, interp_sect);
 	    }
 
-	  create_solib_event_breakpoint (target_gdbarch, sym_addr);
+	  svr4_create_solib_event_breakpoints (target_gdbarch, sym_addr);
 	  return 1;
 	}
     }
@@ -1676,7 +1893,8 @@ enable_break (struct svr4_info *info, int from_tty)
 
       if (sym_addr != 0)
 	{
-	  create_solib_event_breakpoint (target_gdbarch, load_addr + sym_addr);
+	  svr4_create_solib_event_breakpoints (target_gdbarch,
+					       load_addr + sym_addr);
 	  xfree (interp_name);
 	  return 1;
 	}
@@ -1702,7 +1920,7 @@ enable_break (struct svr4_info *info, int from_tty)
 	  sym_addr = gdbarch_convert_from_func_ptr_addr (target_gdbarch,
 							 sym_addr,
 							 &current_target);
-	  create_solib_event_breakpoint (target_gdbarch, sym_addr);
+	  svr4_create_solib_event_breakpoints (target_gdbarch, sym_addr);
 	  return 1;
 	}
     }
@@ -1718,7 +1936,7 @@ enable_break (struct svr4_info *info, int from_tty)
 	      sym_addr = gdbarch_convert_from_func_ptr_addr (target_gdbarch,
 							     sym_addr,
 							     &current_target);
-	      create_solib_event_breakpoint (target_gdbarch, sym_addr);
+	      svr4_create_solib_event_breakpoints (target_gdbarch, sym_addr);
 	      return 1;
 	    }
 	}
@@ -2494,4 +2712,5 @@ _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.update_breakpoints = svr4_update_solib_event_breakpoints;
 }
diff --git a/gdb/solib.h b/gdb/solib.h
index 7a2ff84..65e3857 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -91,4 +91,8 @@ extern CORE_ADDR gdb_bfd_lookup_symbol_from_symtab (bfd *abfd,
 								      void *),
 						    void *data);
 
+/* Enable or disable optional solib event breakpoints as appropriate.  */
+
+extern void update_solib_breakpoints (void);
+
 #endif /* SOLIB_H */
diff --git a/gdb/solib.c b/gdb/solib.c
index 90439ba..dda0130 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -1209,6 +1209,18 @@ no_shared_libraries (char *ignored, int from_tty)
   objfile_purge_solibs ();
 }
 
+/* See solib.h.  */
+
+void
+update_solib_breakpoints (void)
+{
+  struct target_so_ops *ops = solib_ops (target_gdbarch);
+
+  if (ops->update_breakpoints != NULL)
+    ops->update_breakpoints ();
+}
+
+
 /* Reload shared libraries, but avoid reloading the same symbol file
    we already have loaded.  */
 
diff --git a/gdb/solist.h b/gdb/solist.h
index 7413e3b..0d9046d 100644
--- a/gdb/solist.h
+++ b/gdb/solist.h
@@ -149,6 +149,13 @@ struct target_so_ops
        core file (in particular, for readonly sections).  */
     int (*keep_data_in_core) (CORE_ADDR vaddr,
 			      unsigned long size);
+
+    /* Enable or disable optional solib event breakpoints as
+       appropriate.  This should be called whenever
+       stop_on_solib_events is changed.  This pointer can be
+       NULL, in which case no enabling or disabling is necessary
+       for this target.  */
+    void (*update_breakpoints) (void);
   };
 
 /* Free the memory associated with a (so_list *).  */
diff --git a/gdb/testsuite/gdb.base/break-interp.exp b/gdb/testsuite/gdb.base/break-interp.exp
index 4012e66..7aa6537 100644
--- a/gdb/testsuite/gdb.base/break-interp.exp
+++ b/gdb/testsuite/gdb.base/break-interp.exp
@@ -109,12 +109,19 @@ proc strip_debug {dest} {
     }
 }
 
+# The marker function for the standard runtime linker interface is
+# _dl_debug_state.  The probes-based interface has no specific marker
+# function; the probe we will stop on (init_start) is in dl_main so we
+# check for that.
+
+set solib_bp {(_dl_debug_state|dl_main)}
+
 # Implementation of reach.
 
 proc reach_1 {func command displacement} {
-    global gdb_prompt expect_out
+    global gdb_prompt expect_out solib_bp
 
-    if {$func == "_dl_debug_state"} {
+    if {$func == $solib_bp} {
 	# Breakpoint on _dl_debug_state can have problems due to its overlap
 	# with the existing internal breakpoint from GDB.
 	gdb_test_no_output "set stop-on-solib-events 1"
@@ -142,21 +149,21 @@ proc reach_1 {func command displacement} {
 	    exp_continue
 	}
 	-re "Breakpoint \[0-9\]+, \\.?(__GI_)?$func \\(.*\\) at .*:\[0-9\]+\r\n.*$gdb_prompt $" {
-	    if {$func == "_dl_debug_state"} {
+	    if {$func == $solib_bp} {
 		fail $test
 	    } else {
 		pass $test
 	    }
 	}
 	-re "Breakpoint \[0-9\]+, \[0-9xa-f\]+ in \\.?(__GI_)?$func \\(\\).*\r\n$gdb_prompt $" {
-	    if {$func == "_dl_debug_state"} {
+	    if {$func == $solib_bp} {
 		fail $test
 	    } else {
 		pass $test
 	    }
 	}
 	-re "Stopped due to (spurious )?shared library event.*\r\n$gdb_prompt $" {
-	    if {$func == "_dl_debug_state"} {
+	    if {$func == $solib_bp} {
 		if {$debug_state_count == 0} {
 		    # First stop does not yet relocate the _start function
 		    # descriptor on ppc64.
@@ -175,7 +182,7 @@ proc reach_1 {func command displacement} {
 	fail $test_displacement
     }
 
-    if {$func == "_dl_debug_state"} {
+    if {$func == $solib_bp} {
 	gdb_test_no_output "set stop-on-solib-events 0"
     }
 }
@@ -357,7 +364,7 @@ proc test_attach {file displacement {relink_args ""}} {
 }
 
 proc test_ld {file ifmain trynosym displacement} {
-    global srcdir subdir gdb_prompt expect_out inferior_exited_re
+    global srcdir subdir gdb_prompt expect_out inferior_exited_re solib_bp
 
     # First test normal `file'-command loaded $FILE with symbols.
 
@@ -385,9 +392,9 @@ proc test_ld {file ifmain trynosym displacement} {
 	gdb_test_no_output "set args ${objdir}/${subdir}/$binfile_test" "set args OBJDIR/${subdir}/$binfile_test"
     }
 
-    reach "_dl_debug_state" "run" $displacement
+    reach $solib_bp "run" $displacement
 
-    gdb_test "bt" "#0 +\[^\r\n\]*\\m(__GI_)?_dl_debug_state\\M.*" "dl bt"
+    gdb_test "bt" "#0 +\[^\r\n\]*\\m(__GI_)?$solib_bp\\M.*" "dl bt"
 
     if $ifmain {
 	reach "main" continue "NONE"
@@ -399,7 +406,7 @@ proc test_ld {file ifmain trynosym displacement} {
 
     # Try re-run if the new PIE displacement takes effect.
     gdb_test "kill" "" "kill" {Kill the program being debugged\? \(y or n\) } "y"
-    reach "_dl_debug_state" "run" $displacement
+    reach $solib_bp "run" $displacement
 
     if $ifmain {
 	test_core $file $displacement
@@ -431,7 +438,7 @@ proc test_ld {file ifmain trynosym displacement} {
 	gdb_test "exec-file $file" "exec-file $escapedfile" "load"
 
 	if $ifmain {
-	    reach "_dl_debug_state" run $displacement
+	    reach $solib_bp run $displacement
 
 	    # Use two separate gdb_test_multiple statements to avoid timeouts due
 	    # to slow processing of wildcard capturing long output
diff --git a/gdb/testsuite/gdb.base/break-probes-solib.c b/gdb/testsuite/gdb.base/break-probes-solib.c
new file mode 100644
index 0000000..9979ee7
--- /dev/null
+++ b/gdb/testsuite/gdb.base/break-probes-solib.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/break-probes.c b/gdb/testsuite/gdb.base/break-probes.c
new file mode 100644
index 0000000..a778099
--- /dev/null
+++ b/gdb/testsuite/gdb.base/break-probes.c
@@ -0,0 +1,26 @@
+/* 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>
+
+int
+main ()
+{
+  void *handle = dlopen (SHLIB_NAME, RTLD_LAZY);
+
+  dlclose (handle);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/break-probes.exp b/gdb/testsuite/gdb.base/break-probes.exp
new file mode 100644
index 0000000..94b125a
--- /dev/null
+++ b/gdb/testsuite/gdb.base/break-probes.exp
@@ -0,0 +1,76 @@
+# 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 libname $testfile-solib
+set srcfile_lib $srcdir/$subdir/$libname.c
+set binfile_lib [standard_output_file $libname.so]
+
+set normal_bp "_dl_debug_state"
+set probes_bp "dl_main"
+
+if { [gdb_compile_shlib $srcfile_lib $binfile_lib \
+	  [list additional_flags=-fPIC]] != "" } {
+    untested "Could not compile $binfile_lib."
+    return -1
+}
+
+if { [prepare_for_testing $testfile.exp $testfile $srcfile \
+	  [list additional_flags=-DSHLIB_NAME\=\"$binfile_lib\" libs=-ldl]] } {
+    return -1
+}
+
+# Enable stop-on-solib-events
+gdb_test_no_output "set stop-on-solib-events 1"
+
+# Run to the first stop
+gdb_test "run" ".*Stopped due to shared library event.*"
+
+# XFAIL if we are not using probes
+set test "ensure using probes"
+set using_probes 0
+gdb_test_multiple "bt" $test {
+    -re "#0 +\[^\r\n\]*\\m(__GI_)?$normal_bp\\M.*$gdb_prompt $" {
+	xfail $test
+    }
+    -re "#0 +\[^\r\n\]*\\m(__GI_)?$probes_bp\\M.*$gdb_prompt $" {
+	pass $test
+	set using_probes 1
+    }
+}
+
+if { $using_probes } {
+    # Run til it loads our library
+    set test "run til our library loads"
+    set loaded_library 0
+    while { !$loaded_library } {
+	gdb_test_multiple "c" $test {
+	    -re "Inferior loaded $binfile_lib\\M.*$gdb_prompt $" {
+		pass $test
+		set loaded_library 1
+	    }
+	    -re "Stopped due to shared library event\\M.*$gdb_prompt $" {
+	    }
+	}
+    }
+
+    # Call something to ensure that relocation occurred
+    gdb_test "call foo(23)" "foo 23.*\\\$.* = .*"
+}


More information about the Gdb-patches mailing list