This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] [1/2] First.2.2 cut at multi-executable support.


Here's part 2 of http://sourceware.org/ml/gdb-patches/2009-07/msg00044.html

Index: src/gdb/inferior.h
===================================================================
--- src.orig/gdb/inferior.h	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/inferior.h	2009-07-01 19:17:15.000000000 +0100
@@ -41,6 +41,8 @@ struct terminal_info;
 /* For struct frame_id.  */
 #include "frame.h"
 
+#include "symspace.h"
+
 /* Two structures are used to record inferior state.
 
    inferior_thread_state contains state about the program itself like its
@@ -149,6 +151,11 @@ extern int step_stop_if_no_debug;
    are kept running freely.  */
 extern int non_stop;
 
+/* If set (default), when following a fork, GDB will detach from one
+   the fork branches, child or parent.  Exactly which branch is
+   detached depends on 'set follow-fork-mode' setting.  */
+extern int detach_fork;
+
 extern void generic_mourn_inferior (void);
 
 extern void terminal_save_ours (void);
@@ -408,6 +415,12 @@ struct inferior
      the ptid_t.pid member of threads of this inferior.  */
   int pid;
 
+  /* The address space bound to this inferior.  */
+  struct address_space *aspace;
+
+  /* The symbol space bound to this inferior.  */
+  struct symbol_space *sspace;
+
   /* See the definition of stop_kind above.  */
   enum stop_kind stop_soon;
 
@@ -415,6 +428,25 @@ struct inferior
      forked.  */
   int attach_flag;
 
+  /* If this inferior is a vfork child, then this is the pointer to
+     its vfork parent.  Since a vfork parent is left stopped by the
+     kernel until the child execs or exits, a process can only have
+     one vfork parent.  */
+  struct inferior *vfork_parent;
+
+  /* A vfork parent is left stopped by the kernel until the child
+     execs or exits, so a process can only have one vfork child.  */
+  struct inferior *vfork_child;
+
+  /* True if this inferior should be detached when it's vfork sibling
+     exits or execs.  */
+  int pending_detach;
+
+  /* True if this inferior is a vfork parent waiting for a vfork child
+     not under our control to be done with the shared memory region,
+     either by exiting or execing.  */
+  int waiting_for_vfork_done;
+
   /* What is left to do for an execution command after any thread of
      this inferior stops.  For continuations associated with a
      specific thread, see `struct thread_info'.  */
@@ -468,9 +500,16 @@ extern int in_inferior_list (int pid);
    not the system's).  */
 extern int valid_gdb_inferior_id (int num);
 
-/* Search function to lookup a inferior by target 'pid'.  */
+/* Search function to lookup an inferior by target 'pid'.  */
 extern struct inferior *find_inferior_pid (int pid);
 
+/* Search function to lookup an inferior by GDB 'num'.  */
+extern struct inferior *find_inferior_id (int num);
+
+/* Find an inferior bound to SSPACE.  */
+extern struct inferior *
+  find_inferior_for_symbol_space (struct symbol_space *sspace);
+
 /* Inferior iterator function.
 
    Calls a callback function once for each inferior, so long as the
@@ -502,4 +541,6 @@ extern int have_live_inferiors (void);
    this if there is no current inferior.  */
 extern struct inferior *current_inferior (void);
 
+extern struct inferior *inferior_list;
+
 #endif /* !defined (INFERIOR_H) */
Index: src/gdb/inferior.c
===================================================================
--- src.orig/gdb/inferior.c	2009-07-01 19:17:08.000000000 +0100
+++ src/gdb/inferior.c	2009-07-01 19:17:15.000000000 +0100
@@ -18,6 +18,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "exec.h"
 #include "inferior.h"
 #include "target.h"
 #include "command.h"
@@ -29,7 +30,7 @@
 
 void _initialize_inferiors (void);
 
-static struct inferior *inferior_list = NULL;
+struct inferior *inferior_list = NULL;
 static int highest_inferior_num;
 
 /* Print notices on inferior events (attach, detach, etc.), set with
@@ -125,11 +126,12 @@ delete_thread_of_inferior (struct thread
 
 /* If SILENT then be quiet -- don't announce a inferior death, or the
    exit of its threads.  */
+
 static void
 delete_inferior_1 (int pid, int silent)
 {
   struct inferior *inf, *infprev;
-  struct delete_thread_of_inferior_arg arg = { pid, silent };
+  struct delete_thread_of_inferior_arg arg;
 
   infprev = NULL;
 
@@ -146,7 +148,7 @@ delete_inferior_1 (int pid, int silent)
   iterate_over_threads (delete_thread_of_inferior, &arg);
 
   /* Notify the observers before removing the inferior from the list,
-     so that the observers have a change to look it up.  */
+     so that the observers have a chance to look it up.  */
   observer_notify_inferior_exit (pid);
 
   if (infprev)
@@ -193,7 +195,7 @@ discard_all_inferiors (void)
     }
 }
 
-static struct inferior *
+struct inferior *
 find_inferior_id (int num)
 {
   struct inferior *inf;
@@ -217,6 +219,22 @@ find_inferior_pid (int pid)
   return NULL;
 }
 
+/* Find an inferior bound to SSPACE.  */
+
+struct inferior *
+find_inferior_for_symbol_space (struct symbol_space *sspace)
+{
+  struct inferior *inf;
+
+  for (inf = inferior_list; inf != NULL; inf = inf->next)
+    {
+      if (inf->sspace == sspace)
+	return inf;
+    }
+
+  return NULL;
+}
+
 struct inferior *
 iterate_over_inferiors (int (*callback) (struct inferior *, void *),
 			void *data)
@@ -322,11 +340,13 @@ print_inferior (struct ui_out *uiout, in
       return;
     }
 
-  old_chain = make_cleanup_ui_out_table_begin_end (uiout, 3, inf_count,
+  old_chain = make_cleanup_ui_out_table_begin_end (uiout, 5, inf_count,
 						   "inferiors");
-  ui_out_table_header (uiout, 3, ui_right, "current", "Cur");
-  ui_out_table_header (uiout, 4, ui_right, "id", "Id");
-  ui_out_table_header (uiout, 7, ui_right, "target-id", "PID");
+  ui_out_table_header (uiout, 1, ui_left, "current", "");
+  ui_out_table_header (uiout, 4, ui_left, "id", "Id");
+  ui_out_table_header (uiout, 17, ui_left, "target-id", "Target ID");
+  ui_out_table_header (uiout, 6, ui_left, "sspace-id", "SSpace");
+  ui_out_table_header (uiout, 17, ui_left, "exec", "Main Program");
   ui_out_table_body (uiout);
 
   for (inf = inferior_list; inf; inf = inf->next)
@@ -344,12 +364,40 @@ print_inferior (struct ui_out *uiout, in
 	ui_out_field_skip (uiout, "current");
 
       ui_out_field_int (uiout, "id", inf->num);
-      ui_out_field_int (uiout, "target-id", inf->pid);
+      ui_out_field_string (uiout, "target-id",
+			   target_pid_to_str (pid_to_ptid (inf->pid)));
+
+      ui_out_field_int (uiout, "sspace-id", inf->sspace->num);
+
+      if (inf->sspace->ebfd)
+	ui_out_field_string (uiout, "exec",
+			     bfd_get_filename (inf->sspace->ebfd));
+      else
+	ui_out_field_skip (uiout, "exec");
+
+      /* Print extra info that isn't really fit to always present in
+	 tabular form.  Currently we print the vfork parent/child
+	 relationships, if any.  */
+      if (inf->vfork_parent)
+	{
+	  ui_out_text (uiout, _("\n\tis vfork child of inferior "));
+	  ui_out_field_int (uiout, "vfork-parent", inf->vfork_parent->num);
+	}
+      if (inf->vfork_child)
+	{
+	  ui_out_text (uiout, _("\n\tis vfork parent of inferior "));
+	  ui_out_field_int (uiout, "vfork-child", inf->vfork_child->num);
+	}
 
       ui_out_text (uiout, "\n");
       do_cleanups (chain2);
     }
 
+  if (inferior_list
+      && ptid_equal (inferior_ptid, null_ptid))
+    ui_out_message (uiout, 0, "\n\
+No selected inferior/thread.  See `help thread' or `help inferior'.\n");
+
   do_cleanups (old_chain);
 }
 
Index: src/gdb/objfiles.h
===================================================================
--- src.orig/gdb/objfiles.h	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/objfiles.h	2009-07-01 19:17:15.000000000 +0100
@@ -23,11 +23,13 @@
 
 #include "gdb_obstack.h"	/* For obstack internals.  */
 #include "symfile.h"		/* For struct psymbol_allocation_list */
+#include "symspace.h"
 
 struct bcache;
 struct htab;
 struct symtab;
 struct objfile_data;
+struct symbol_space;
 
 /* This structure maintains information on a per-objfile basis about the
    "entry point" of the objfile, and the scope within which the entry point
@@ -200,6 +202,10 @@ struct objfile
 
     unsigned short flags;
 
+    /* The symbol space associated with this objfile.  */
+
+    struct symbol_space *sspace;
+
     /* Each objfile points to a linked list of symtabs derived from this file,
        one symtab structure for each compilation unit (source file).  Each link
        in the symtab list contains a backpointer to this objfile. */
@@ -419,12 +425,6 @@ struct objfile
 
 #define OBJF_KEEPBFD	(1 << 4)	/* Do not delete bfd */
 
-
-/* The object file that the main symbol table was loaded from (e.g. the
-   argument to the "symbol-file" or "file" command).  */
-
-extern struct objfile *symfile_objfile;
-
 /* The object file that contains the runtime common minimal symbols
    for SunOS4. Note that this objfile has no associated BFD.  */
 
@@ -445,11 +445,6 @@ extern struct objfile *rt_common_objfile
 
 extern struct objfile *current_objfile;
 
-/* All known objfiles are kept in a linked list.  This points to the
-   root of this list. */
-
-extern struct objfile *object_files;
-
 /* Declarations for functions defined in objfiles.c */
 
 extern struct objfile *allocate_objfile (bfd *, int);
@@ -510,14 +505,27 @@ extern void *objfile_data (struct objfil
 			   const struct objfile_data *data);
 
 
-/* Traverse all object files.  ALL_OBJFILES_SAFE works even if you delete
-   the objfile during the traversal.  */
+/* Traverse all object files in the current symbol space.
+   ALL_OBJFILES_SAFE works even if you delete the objfile during the
+   traversal.  */
+
+/* Traverse all object files in symbol space SS.  */
+
+#define ALL_SSPACE_OBJFILES(ss, obj)					\
+  for ((obj) = ss->objfiles; (obj) != NULL; (obj) = (obj)->next)	\
 
-#define	ALL_OBJFILES(obj) \
-  for ((obj) = object_files; (obj) != NULL; (obj) = (obj)->next)
+#define ALL_SSPACE_OBJFILES_SAFE(ss, obj, nxt)		\
+  for ((obj) = ss->objfiles;			\
+       (obj) != NULL? ((nxt)=(obj)->next,1) :0;	\
+       (obj) = (nxt))
+
+#define ALL_OBJFILES(obj)			    \
+  for ((obj) = current_symbol_space->objfiles; \
+       (obj) != NULL;				    \
+       (obj) = (obj)->next)
 
-#define	ALL_OBJFILES_SAFE(obj,nxt) \
-  for ((obj) = object_files; 	   \
+#define ALL_OBJFILES_SAFE(obj,nxt)			\
+  for ((obj) = current_symbol_space->objfiles;	\
        (obj) != NULL? ((nxt)=(obj)->next,1) :0;	\
        (obj) = (nxt))
 
@@ -536,27 +544,44 @@ extern void *objfile_data (struct objfil
 #define	ALL_OBJFILE_MSYMBOLS(objfile, m) \
     for ((m) = (objfile) -> msymbols; SYMBOL_LINKAGE_NAME(m) != NULL; (m)++)
 
-/* Traverse all symtabs in all objfiles.  */
+/* Traverse all symtabs in all objfiles in the current symbol
+   space.  */
 
 #define	ALL_SYMTABS(objfile, s) \
   ALL_OBJFILES (objfile)	 \
     ALL_OBJFILE_SYMTABS (objfile, s)
 
-/* Traverse all symtabs in all objfiles, skipping included files
-   (which share a blockvector with their primary symtab).  */
+#define ALL_SSPACE_SYMTABS(ss, objfile, s)		\
+  ALL_SSPACE_OBJFILES (ss, objfile)			\
+    ALL_OBJFILE_SYMTABS (objfile, s)
+
+/* Traverse all symtabs in all objfiles in the current symbol space,
+   skipping included files (which share a blockvector with their
+   primary symtab).  */
 
 #define ALL_PRIMARY_SYMTABS(objfile, s) \
   ALL_OBJFILES (objfile)		\
     ALL_OBJFILE_SYMTABS (objfile, s)	\
       if ((s)->primary)
 
-/* Traverse all psymtabs in all objfiles.  */
+#define ALL_SSPACE_PRIMARY_SYMTABS(sspace, objfile, s)	\
+  ALL_SSPACE_OBJFILES (ss, objfile)			\
+    ALL_OBJFILE_SYMTABS (objfile, s)			\
+      if ((s)->primary)
+
+/* Traverse all psymtabs in all objfiles in the current symbol
+   space.  */
 
 #define	ALL_PSYMTABS(objfile, p) \
   ALL_OBJFILES (objfile)	 \
     ALL_OBJFILE_PSYMTABS (objfile, p)
 
-/* Traverse all minimal symbols in all objfiles.  */
+#define ALL_SSPACE_PSYMTABS(ss, objfile, p)		\
+  ALL_SSPACE_OBJFILES (ss, objfile)			\
+    ALL_OBJFILE_PSYMTABS (objfile, p)
+
+/* Traverse all minimal symbols in all objfiles in the current symbol
+   space.  */
 
 #define	ALL_MSYMBOLS(objfile, m) \
   ALL_OBJFILES (objfile)	 \
Index: src/gdb/objfiles.c
===================================================================
--- src.orig/gdb/objfiles.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/objfiles.c	2009-07-01 19:17:15.000000000 +0100
@@ -59,9 +59,7 @@ static void objfile_free_data (struct ob
 /* Externally visible variables that are owned by this module.
    See declarations in objfile.h for more info. */
 
-struct objfile *object_files;	/* Linked list of all objfiles */
 struct objfile *current_objfile;	/* For symbol file being read in */
-struct objfile *symfile_objfile;	/* Main symbol table loaded from */
 struct objfile *rt_common_objfile;	/* For runtime common symbols */
 
 /* Locate all mappable sections of a BFD file. 
@@ -201,6 +199,8 @@ allocate_objfile (bfd *abfd, int flags)
       objfile->name = xstrdup ("<<anonymous objfile>>");
     }
 
+  objfile->sspace = current_symbol_space;
+
   /* Initialize the section indexes for this objfile, so that we can
      later detect if they are used w/o being properly assigned to. */
 
Index: src/gdb/linespec.c
===================================================================
--- src.orig/gdb/linespec.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/linespec.c	2009-07-01 19:17:15.000000000 +0100
@@ -1574,6 +1574,8 @@ decode_all_digits (char **argptr, struct
 
   init_sal (&val);
 
+  val.sspace = current_symbol_space;
+
   /* This is where we need to make sure that we have good defaults.
      We must guarantee that this section of code is never executed
      when we are called with just a function name, since
@@ -1625,6 +1627,7 @@ decode_all_digits (char **argptr, struct
   if (val.symtab == 0)
     val.symtab = file_symtab;
 
+  val.sspace = SYMTAB_SSPACE (val.symtab);
   val.pc = 0;
   values.sals = (struct symtab_and_line *)
     xmalloc (sizeof (struct symtab_and_line));
@@ -1696,6 +1699,7 @@ decode_dollar (char *copy, int funfirstl
   val.symtab = file_symtab ? file_symtab : default_symtab;
   val.line = valx;
   val.pc = 0;
+  val.sspace = current_symbol_space;
 
   values.sals = (struct symtab_and_line *) xmalloc (sizeof val);
   values.sals[0] = val;
Index: src/gdb/source.c
===================================================================
--- src.orig/gdb/source.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/source.c	2009-07-01 19:17:15.000000000 +0100
@@ -91,6 +91,8 @@ static struct symtab *current_source_sym
 
 static int current_source_line;
 
+static struct symbol_space *current_source_sspace;
+
 /* Default number of lines to print with commands like "list".
    This is based on guessing how many long (i.e. more than chars_per_line
    characters) lines there will be.  To be completely correct, "list"
@@ -151,6 +153,7 @@ get_current_source_symtab_and_line (void
 {
   struct symtab_and_line cursal = { 0 };
 
+  cursal.sspace = current_source_sspace;
   cursal.symtab = current_source_symtab;
   cursal.line = current_source_line;
   cursal.pc = 0;
@@ -189,15 +192,17 @@ struct symtab_and_line
 set_current_source_symtab_and_line (const struct symtab_and_line *sal)
 {
   struct symtab_and_line cursal = { 0 };
-  
+
+  cursal.sspace = current_source_sspace;
   cursal.symtab = current_source_symtab;
   cursal.line = current_source_line;
+  cursal.pc = 0;
+  cursal.end = 0;
 
+  current_source_sspace = sal->sspace;
   current_source_symtab = sal->symtab;
   current_source_line = sal->line;
-  cursal.pc = 0;
-  cursal.end = 0;
-  
+
   return cursal;
 }
 
@@ -231,6 +236,7 @@ select_source_symtab (struct symtab *s)
     {
       current_source_symtab = s;
       current_source_line = 1;
+      current_source_sspace = SYMTAB_SSPACE (s);
       return;
     }
 
@@ -244,6 +250,7 @@ select_source_symtab (struct symtab *s)
       sals = decode_line_spec (main_name (), 1);
       sal = sals.sals[0];
       xfree (sals.sals);
+      current_source_sspace = sal.sspace;
       current_source_symtab = sal.symtab;
       current_source_line = max (sal.line - (lines_to_list - 1), 1);
       if (current_source_symtab)
@@ -255,7 +262,7 @@ select_source_symtab (struct symtab *s)
 
   current_source_line = 1;
 
-  for (ofp = object_files; ofp != NULL; ofp = ofp->next)
+  ALL_OBJFILES (ofp)
     {
       for (s = ofp->symtabs; s; s = s->next)
 	{
@@ -263,15 +270,19 @@ select_source_symtab (struct symtab *s)
 	  int len = strlen (name);
 	  if (!(len > 2 && (strcmp (&name[len - 2], ".h") == 0
 	      || strcmp (name, "<<C++-namespaces>>") == 0)))
-	    current_source_symtab = s;
+	    {
+	      current_source_sspace = current_symbol_space;
+	      current_source_symtab = s;
+	    }
 	}
     }
+
   if (current_source_symtab)
     return;
 
   /* How about the partial symbol tables?  */
 
-  for (ofp = object_files; ofp != NULL; ofp = ofp->next)
+  ALL_OBJFILES (ofp)
     {
       for (ps = ofp->psymtabs; ps != NULL; ps = ps->next)
 	{
@@ -292,6 +303,7 @@ select_source_symtab (struct symtab *s)
 	}
       else
 	{
+	  current_source_sspace = current_symbol_space;
 	  current_source_symtab = PSYMTAB_TO_SYMTAB (cs_pst);
 	}
     }
@@ -316,11 +328,13 @@ show_directories (char *ignore, int from
 void
 forget_cached_source_info (void)
 {
+  struct symbol_space *sspace;
   struct symtab *s;
   struct objfile *objfile;
   struct partial_symtab *pst;
 
-  for (objfile = object_files; objfile != NULL; objfile = objfile->next)
+  ALL_SSPACES (sspace)
+    ALL_SSPACE_OBJFILES (sspace, objfile)
     {
       for (s = objfile->symtabs; s != NULL; s = s->next)
 	{
Index: src/gdb/symfile.c
===================================================================
--- src.orig/gdb/symfile.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/symfile.c	2009-07-01 19:17:15.000000000 +0100
@@ -2802,7 +2802,7 @@ clear_symtab_users (void)
 
   clear_displays ();
   breakpoint_re_set ();
-  set_default_breakpoint (0, 0, 0, 0);
+  set_default_breakpoint (0, NULL, 0, 0, 0);
   clear_pc_function_cache ();
   observer_notify_new_objfile (NULL);
 
Index: src/gdb/symmisc.c
===================================================================
--- src.orig/gdb/symmisc.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/symmisc.c	2009-07-01 19:17:15.000000000 +0100
@@ -128,10 +128,12 @@ free_symtab (struct symtab *s)
 void
 print_symbol_bcache_statistics (void)
 {
+  struct symbol_space *sspace;
   struct objfile *objfile;
 
   immediate_quit++;
-  ALL_OBJFILES (objfile)
+  ALL_SSPACES (sspace)
+    ALL_SSPACE_OBJFILES (sspace, objfile)
   {
     printf_filtered (_("Byte cache statistics for '%s':\n"), objfile->name);
     print_bcache_statistics (objfile->psymbol_cache, "partial symbol cache");
@@ -143,13 +145,15 @@ print_symbol_bcache_statistics (void)
 void
 print_objfile_statistics (void)
 {
+  struct symbol_space *sspace;
   struct objfile *objfile;
   struct symtab *s;
   struct partial_symtab *ps;
   int i, linetables, blockvectors;
 
   immediate_quit++;
-  ALL_OBJFILES (objfile)
+  ALL_SSPACES (sspace)
+    ALL_SSPACE_OBJFILES (sspace, objfile)
   {
     printf_filtered (_("Statistics for '%s':\n"), objfile->name);
     if (OBJSTAT (objfile, n_stabs) > 0)
@@ -869,6 +873,7 @@ maintenance_print_msymbols (char *args, 
   struct cleanup *cleanups;
   char *filename = DEV_TTY;
   char *symname = NULL;
+  struct symbol_space *sspace;
   struct objfile *objfile;
 
   struct stat sym_st, obj_st;
@@ -904,10 +909,11 @@ maintenance_print_msymbols (char *args, 
   make_cleanup_ui_file_delete (outfile);
 
   immediate_quit++;
-  ALL_OBJFILES (objfile)
-    if (symname == NULL
-	|| (!stat (objfile->name, &obj_st) && sym_st.st_ino == obj_st.st_ino))
-      dump_msymbols (objfile, outfile);
+  ALL_SSPACES (sspace)
+    ALL_SSPACE_OBJFILES (sspace, objfile)
+      if (symname == NULL
+	  || (!stat (objfile->name, &obj_st) && sym_st.st_ino == obj_st.st_ino))
+	dump_msymbols (objfile, outfile);
   immediate_quit--;
   fprintf_filtered (outfile, "\n\n");
   do_cleanups (cleanups);
@@ -916,13 +922,15 @@ maintenance_print_msymbols (char *args, 
 void
 maintenance_print_objfiles (char *ignore, int from_tty)
 {
+  struct symbol_space *sspace;
   struct objfile *objfile;
 
   dont_repeat ();
 
   immediate_quit++;
-  ALL_OBJFILES (objfile)
-    dump_objfile (objfile);
+  ALL_SSPACES (sspace)
+    ALL_SSPACE_OBJFILES (sspace, objfile)
+      dump_objfile (objfile);
   immediate_quit--;
 }
 
@@ -931,12 +939,14 @@ maintenance_print_objfiles (char *ignore
 void
 maintenance_info_symtabs (char *regexp, int from_tty)
 {
+  struct symbol_space *sspace;
   struct objfile *objfile;
 
   if (regexp)
     re_comp (regexp);
 
-  ALL_OBJFILES (objfile)
+  ALL_SSPACES (sspace)
+    ALL_SSPACE_OBJFILES (sspace, objfile)
     {
       struct symtab *symtab;
       
@@ -988,12 +998,14 @@ maintenance_info_symtabs (char *regexp, 
 void
 maintenance_info_psymtabs (char *regexp, int from_tty)
 {
+  struct symbol_space *sspace;
   struct objfile *objfile;
 
   if (regexp)
     re_comp (regexp);
 
-  ALL_OBJFILES (objfile)
+  ALL_SSPACES (sspace)
+    ALL_SSPACE_OBJFILES (sspace, objfile)
     {
       struct partial_symtab *psymtab;
 
Index: src/gdb/symtab.h
===================================================================
--- src.orig/gdb/symtab.h	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/symtab.h	2009-07-01 19:17:15.000000000 +0100
@@ -32,6 +32,7 @@ struct block;
 struct blockvector;
 struct axs_value;
 struct agent_expr;
+struct symbol_space;
 
 /* Some of the structures in this file are space critical.
    The space-critical structures are:
@@ -823,6 +824,7 @@ struct symtab
 
 #define BLOCKVECTOR(symtab)	(symtab)->blockvector
 #define LINETABLE(symtab)	(symtab)->linetable
+#define SYMTAB_SSPACE(symtab)	(symtab)->objfile->sspace
 
 
 /* Each source file that has not been fully read in is represented by
@@ -1170,6 +1172,9 @@ extern void msymbols_sort (struct objfil
 
 struct symtab_and_line
 {
+  /* The symbol space of this sal.  */
+  struct symbol_space *sspace;
+
   struct symtab *symtab;
   struct obj_section *section;
   /* Line number.  Line numbers start at 1 and proceed through symtab->nlines.
Index: src/gdb/symtab.c
===================================================================
--- src.orig/gdb/symtab.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/symtab.c	2009-07-01 19:17:15.000000000 +0100
@@ -690,6 +690,7 @@ symbol_search_name (const struct general
 void
 init_sal (struct symtab_and_line *sal)
 {
+  sal->sspace = NULL;
   sal->symtab = 0;
   sal->section = 0;
   sal->line = 0;
@@ -1994,9 +1995,12 @@ find_pc_sect_symtab (CORE_ADDR pc, struc
   struct symtab *best_s = NULL;
   struct partial_symtab *ps;
   struct objfile *objfile;
+  struct symbol_space *sspace;
   CORE_ADDR distance = 0;
   struct minimal_symbol *msymbol;
 
+  sspace = current_symbol_space;
+
   /* If we know that this is not a text address, return failure.  This is
      necessary because we loop based on the block's high and low code
      addresses, which do not include the data ranges, and because
@@ -2152,6 +2156,8 @@ find_pc_sect_line (CORE_ADDR pc, struct 
 
   init_sal (&val);		/* initialize to zeroes */
 
+  val.sspace = current_symbol_space;
+
   /* It's tempting to assume that, if we can't find debugging info for
      any function enclosing PC, that we shouldn't search for line
      number info, either.  However, GAS can emit line number info for
@@ -2656,6 +2662,11 @@ find_function_start_sal (struct symbol *
   struct symtab_and_line sal;
   struct block *b, *function_block;
 
+  struct cleanup *old_chain;
+
+  old_chain = save_current_space_and_thread ();
+  switch_to_symbol_space_and_thread (objfile->sspace);
+
   pc = BLOCK_START (block);
   fixup_symbol_section (sym, objfile);
   if (funfirstline)
@@ -2707,6 +2718,7 @@ find_function_start_sal (struct symbol *
     }
 
   sal.pc = pc;
+  sal.sspace = objfile->sspace;
 
   /* Check if we are now inside an inlined function.  If we can,
      use the call site of the function instead.  */
@@ -2727,6 +2739,7 @@ find_function_start_sal (struct symbol *
       sal.symtab = SYMBOL_SYMTAB (BLOCK_FUNCTION (function_block));
     }
 
+  do_cleanups (old_chain);
   return sal;
 }
 
@@ -4555,6 +4568,7 @@ symtab_observer_executable_changed (void
    initializing it from SYMTAB, LINENO and PC.  */
 static void
 append_expanded_sal (struct symtabs_and_lines *sal,
+		     struct symbol_space *sspace,
 		     struct symtab *symtab,
 		     int lineno, CORE_ADDR pc)
 {
@@ -4562,6 +4576,7 @@ append_expanded_sal (struct symtabs_and_
 			sizeof (sal->sals[0])
 			* (sal->nelts + 1));
   init_sal (sal->sals + sal->nelts);
+  sal->sals[sal->nelts].sspace = sspace;
   sal->sals[sal->nelts].symtab = symtab;
   sal->sals[sal->nelts].section = NULL;
   sal->sals[sal->nelts].end = 0;
@@ -4581,14 +4596,16 @@ append_exact_match_to_sals (char *filena
 			    struct linetable_entry **best_item,
 			    struct symtab **best_symtab)
 {
+  struct symbol_space *sspace;
   struct objfile *objfile;
   struct symtab *symtab;
   int exact = 0;
   int j;
   *best_item = 0;
   *best_symtab = 0;
-  
-  ALL_SYMTABS (objfile, symtab)
+
+  ALL_SSPACES (sspace)
+    ALL_SSPACE_SYMTABS (sspace, objfile, symtab)
     {
       if (strcmp (filename, symtab->filename) == 0)
 	{
@@ -4606,7 +4623,8 @@ append_exact_match_to_sals (char *filena
 	      if (item->line == lineno)
 		{
 		  exact = 1;
-		  append_expanded_sal (ret, symtab, lineno, item->pc);
+		  append_expanded_sal (ret, objfile->sspace,
+				       symtab, lineno, item->pc);
 		}
 	      else if (!exact && item->line > lineno
 		       && (*best_item == NULL
@@ -4621,11 +4639,10 @@ append_exact_match_to_sals (char *filena
   return exact;
 }
 
-/* Compute a set of all sals in
-   the entire program that correspond to same file
-   and line as SAL and return those.  If there
-   are several sals that belong to the same block,
-   only one sal for the block is included in results.  */
+/* Compute a set of all sals in all symbol spaces that correspond to
+   same file and line as SAL and return those.  If there are several
+   sals that belong to the same block, only one sal for the block is
+   included in results.  */
 
 struct symtabs_and_lines
 expand_line_sal (struct symtab_and_line sal)
@@ -4639,10 +4656,12 @@ expand_line_sal (struct symtab_and_line 
   int deleted = 0;
   struct block **blocks = NULL;
   int *filter;
+  struct cleanup *old_chain;
 
   ret.nelts = 0;
   ret.sals = NULL;
 
+  /* Only expand sals that represent file.c:line.  */
   if (sal.symtab == NULL || sal.line == 0 || sal.pc != 0)
     {
       ret.sals = xmalloc (sizeof (struct symtab_and_line));
@@ -4652,11 +4671,14 @@ expand_line_sal (struct symtab_and_line 
     }
   else
     {
+      struct symbol_space *sspace;
       struct linetable_entry *best_item = 0;
       struct symtab *best_symtab = 0;
       int exact = 0;
+      char *match_filename;
 
       lineno = sal.line;
+      match_filename = sal.symtab->filename;
 
       /* We need to find all symtabs for a file which name
 	 is described by sal.  We cannot just directly
@@ -4669,17 +4691,23 @@ expand_line_sal (struct symtab_and_line 
 	 the right name.  Then, we iterate over symtabs, knowing
 	 that all symtabs we're interested in are loaded.  */
 
-      ALL_PSYMTABS (objfile, psymtab)
+      old_chain = save_current_symbol_space ();
+      ALL_SSPACES (sspace)
+	ALL_SSPACE_PSYMTABS (sspace, objfile, psymtab)
 	{
-	  if (strcmp (sal.symtab->filename,
-		      psymtab->filename) == 0)
-	    PSYMTAB_TO_SYMTAB (psymtab);
+	  if (strcmp (match_filename, psymtab->filename) == 0)
+	    {
+	      set_current_symbol_space (sspace);
+
+	      PSYMTAB_TO_SYMTAB (psymtab);
+	    }
 	}
+      do_cleanups (old_chain);
 
       /* Now search the symtab for exact matches and append them.  If
 	 none is found, append the best_item and all its exact
 	 matches.  */
-      exact = append_exact_match_to_sals (sal.symtab->filename, lineno,
+      exact = append_exact_match_to_sals (match_filename, lineno,
 					  &ret, &best_item, &best_symtab);
       if (!exact && best_item)
 	append_exact_match_to_sals (best_symtab->filename, best_item->line,
@@ -4695,13 +4723,21 @@ expand_line_sal (struct symtab_and_line 
      blocks -- for each PC found above we see if there are other PCs
      that are in the same block.  If yes, the other PCs are filtered out.  */
 
+  old_chain = save_current_symbol_space ();
   filter = alloca (ret.nelts * sizeof (int));
   blocks = alloca (ret.nelts * sizeof (struct block *));
   for (i = 0; i < ret.nelts; ++i)
     {
+      struct blockvector *bl;
+      struct block *b;
+
+      set_current_symbol_space (ret.sals[i].sspace);
+
       filter[i] = 1;
-      blocks[i] = block_for_pc (ret.sals[i].pc);
+      blocks[i] = block_for_pc_sect (ret.sals[i].pc, ret.sals[i].section);
+
     }
+  do_cleanups (old_chain);
 
   for (i = 0; i < ret.nelts; ++i)
     if (blocks[i] != NULL)
Index: src/gdb/target.h
===================================================================
--- src.orig/gdb/target.h	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/target.h	2009-07-01 19:17:15.000000000 +0100
@@ -110,6 +110,15 @@ enum target_waitkind
 
     TARGET_WAITKIND_EXECD,
 
+    /* The program had previously vforked, and now the child is done
+       with the shared memory region, because it exec'ed or exited.
+       Note that the event if reported to the vfork parent.  This is
+       only used if GDB did not stay attached to the vfork child,
+       otherwise, a TARGET_WAITKIND_EXECD or
+       TARGET_WAITKIND_EXIT|SIGNALLED event associated with the child
+       has the same effect.  */
+    TARGET_WAITKIND_VFORK_DONE,
+
     /* The program has entered or returned from a system call.  On
        HP-UX, this is used in the hardware watchpoint implementation.
        The syscall's unique integer ID number is in value.syscall_id */
@@ -651,6 +660,10 @@ extern void target_store_registers (stru
 #define	target_prepare_to_store(regcache)	\
      (*current_target.to_prepare_to_store) (regcache)
 
+/* Determine current address space of thread PTID.  */
+
+struct address_space *target_thread_address_space (ptid_t);
+
 /* Returns true if this target can debug multiple processes
    simultaneously.  */
 
Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/target.c	2009-07-01 19:17:15.000000000 +0100
@@ -1934,7 +1934,7 @@ target_detach (char *args, int from_tty)
   else
     /* If we're in breakpoints-always-inserted mode, have to remove
        them before detaching.  */
-    remove_breakpoints ();
+    remove_breakpoints_pid (PIDGET (inferior_ptid));
 
   for (t = current_target.beneath; t != NULL; t = t->beneath)
     {
@@ -2434,6 +2434,27 @@ target_get_osdata (const char *type)
   return target_read_stralloc (t, TARGET_OBJECT_OSDATA, type);
 }
 
+/* Determine the current address space of thread PTID.  */
+
+struct address_space *
+target_thread_address_space (ptid_t ptid)
+{
+  struct inferior *inf;
+
+  /* For now, assume frame chains and inferiors only see one address
+     space.  */
+
+  /* Fall-back to the "main" address space of the inferior.  */
+  inf = find_inferior_pid (ptid_get_pid (ptid));
+
+  if (inf == NULL || inf->aspace == NULL)
+    internal_error (__FILE__, __LINE__, "\
+Can't determine the current address space of thread %s\n",
+		    target_pid_to_str (ptid));
+
+  return inf->aspace;
+}
+
 static int
 default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
 {
Index: src/gdb/top.c
===================================================================
--- src.orig/gdb/top.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/top.c	2009-07-01 19:17:15.000000000 +0100
@@ -1195,20 +1195,6 @@ struct qt_args
   int from_tty;
 };
 
-/* Callback for iterate_over_threads.  Finds any thread of inferior
-   given by ARG (really an int*).  */
-
-static int
-any_thread_of (struct thread_info *thread, void *arg)
-{
-  int pid = * (int *)arg;
-
-  if (PIDGET (thread->ptid) == pid)
-    return 1;
-
-  return 0;
-}
-
 /* Callback for iterate_over_inferiors.  Kills or detaches the given
    inferior, depending on how we originally gained control of it.  */
 
@@ -1218,7 +1204,7 @@ kill_or_detach (struct inferior *inf, vo
   struct qt_args *qt = args;
   struct thread_info *thread;
 
-  thread = iterate_over_threads (any_thread_of, &inf->pid);
+  thread = any_thread_of_process (inf->pid);
   if (thread)
     {
       switch_to_thread (thread->ptid);
Index: src/gdb/solist.h
===================================================================
--- src.orig/gdb/solist.h	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/solist.h	2009-07-01 19:17:15.000000000 +0100
@@ -52,6 +52,9 @@ struct so_list
     /* shared object file name, expanded to something GDB can open */
     char so_name[SO_NAME_MAX_PATH_SIZE];
 
+    /* Symbol space this shared library belongs to.  */
+    struct symbol_space *sspace;
+
     /* The following fields of the structure are built from
        information gathered from the shared object file itself, and
        are set when we actually add it to our symbol tables.
Index: src/gdb/solib.h
===================================================================
--- src.orig/gdb/solib.h	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/solib.h	2009-07-01 19:17:15.000000000 +0100
@@ -25,6 +25,7 @@
 struct so_list;
 struct target_ops;
 struct target_so_ops;
+struct symbol_space;
 
 /* Called when we free all symtabs, to free the shared library information
    as well. */
@@ -45,7 +46,7 @@ extern void solib_create_inferior_hook (
 
 /* If ADDR lies in a shared library, return its name.  */
 
-extern char *solib_name_from_address (CORE_ADDR);
+extern char *solib_name_from_address (struct symbol_space *, CORE_ADDR);
 
 /* Return 1 if ADDR lies within SOLIB.  */
 
Index: src/gdb/solib.c
===================================================================
--- src.orig/gdb/solib.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/solib.c	2009-07-01 19:17:15.000000000 +0100
@@ -85,9 +85,8 @@ set_solib_ops (struct gdbarch *gdbarch, 
    configuration needs to call set_solib_ops.  */
 struct target_so_ops *current_target_so_ops;
 
-/* local data declarations */
-
-static struct so_list *so_list_head;	/* List of known shared objects */
+/* List of known shared objects */
+#define so_list_head current_symbol_space->so_list
 
 /* Local function prototypes */
 
@@ -659,6 +658,7 @@ update_solib_list (int from_tty, struct 
       for (i = inferior; i; i = i->next)
 	{
 	  i->from_tty = from_tty;
+	  i->sspace = current_symbol_space;
 
 	  /* Fill in the rest of the `struct so_list' node.  */
 	  catch_errors (solib_map_sections, i,
@@ -880,11 +880,11 @@ solib_contains_address_p (const struct s
  */
 
 char *
-solib_name_from_address (CORE_ADDR address)
+solib_name_from_address (struct symbol_space *sspace, CORE_ADDR address)
 {
-  struct so_list *so = 0;	/* link map state variable */
+  struct so_list *so = NULL;
 
-  for (so = so_list_head; so; so = so->next)
+  for (so = sspace->so_list; so; so = so->next)
     if (solib_contains_address_p (so, address))
       return (so->so_name);
 
Index: src/gdb/solib-svr4.c
===================================================================
--- src.orig/gdb/solib-svr4.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/solib-svr4.c	2009-07-01 19:17:15.000000000 +0100
@@ -273,12 +273,10 @@ IGNORE_FIRST_LINK_MAP_ENTRY (struct so_l
 				ptr_type) == 0;
 }
 
-/* Per-inferior SVR4 specific data.  */
+/* Per sspace SVR4 specific data.  */
 
 struct svr4_info
 {
-  int pid;
-
   CORE_ADDR debug_base;	/* Base of dynamic linker structures */
 
   /* Validity flag for debug_loader_offset.  */
@@ -292,69 +290,45 @@ struct svr4_info
 
   /* Load map address for the main executable.  */
   CORE_ADDR main_lm_addr;
+
+  CORE_ADDR interp_text_sect_low;
+  CORE_ADDR interp_text_sect_high;
+  CORE_ADDR interp_plt_sect_low;
+  CORE_ADDR interp_plt_sect_high;
 };
 
 /* List of known processes using solib-svr4 shared libraries, storing
    the required bookkeeping for each.  */
 
 typedef struct svr4_info *svr4_info_p;
-DEF_VEC_P(svr4_info_p);
-VEC(svr4_info_p) *svr4_info = NULL;
-
-/* Get svr4 data for inferior PID (target id).  If none is found yet,
-   add it now.  This function always returns a valid object.  */
-
-struct svr4_info *
-get_svr4_info (int pid)
-{
-  int ix;
-  struct svr4_info *it;
-
-  gdb_assert (pid != 0);
-
-  for (ix = 0; VEC_iterate (svr4_info_p, svr4_info, ix, it); ++ix)
-    {
-      if (it->pid == pid)
-	return it;
-    }
 
-  it = XZALLOC (struct svr4_info);
-  it->pid = pid;
-
-  VEC_safe_push (svr4_info_p, svr4_info, it);
-
-  return it;
-}
-
-/* Get rid of any svr4 related bookkeeping for inferior PID (target
-   id).  */
+/* Per-symbol-space data key.  */
+static const struct symbol_space_data *solib_svr4_sspace_data;
 
 static void
-remove_svr4_info (int pid)
+svr4_sspace_data_cleanup (struct symbol_space *sspace, void *arg)
 {
-  int ix;
-  struct svr4_info *it;
+  struct svr4_info *info;
 
-  for (ix = 0; VEC_iterate (svr4_info_p, svr4_info, ix, it); ++ix)
-    {
-      if (it->pid == pid)
-	{
-	  VEC_unordered_remove (svr4_info_p, svr4_info, ix);
-	  return;
-	}
-    }
+  info = symbol_space_data (current_symbol_space, solib_svr4_sspace_data);
+  xfree (info);
 }
 
-/* This is an "inferior_exit" observer.  Inferior PID (target id) is
-   being removed from the inferior list, because it exited, was
-   killed, detached, or we just dropped the connection to the debug
-   interface --- discard any solib-svr4 related bookkeeping for this
-   inferior.  */
+/* Get the current svr4 data.  If none is found yet, add it now.  This
+   function always returns a valid object.  */
 
-static void
-solib_svr4_inferior_exit (int pid)
+static struct svr4_info *
+get_svr4_info (void)
 {
-  remove_svr4_info (pid);
+  struct svr4_info *info;
+
+  info = symbol_space_data (current_symbol_space, solib_svr4_sspace_data);
+  if (info != NULL)
+    return info;
+
+  info = XZALLOC (struct svr4_info);
+  set_symbol_space_data (current_symbol_space, solib_svr4_sspace_data, info);
+  return info;
 }
 
 /* Local function prototypes */
@@ -918,7 +892,7 @@ open_symbol_file_object (void *from_ttyp
   int l_name_size = TYPE_LENGTH (ptr_type);
   gdb_byte *l_name_buf = xmalloc (l_name_size);
   struct cleanup *cleanups = make_cleanup (xfree, l_name_buf);
-  struct svr4_info *info = get_svr4_info (PIDGET (inferior_ptid));
+  struct svr4_info *info = get_svr4_info ();
 
   if (symfile_objfile)
     if (!query (_("Attempt to reload symbols from process? ")))
@@ -969,8 +943,7 @@ open_symbol_file_object (void *from_ttyp
 static struct so_list *
 svr4_default_sos (void)
 {
-  struct inferior *inf = current_inferior ();
-  struct svr4_info *info = get_svr4_info (inf->pid);
+  struct svr4_info *info = get_svr4_info ();
 
   struct so_list *head = NULL;
   struct so_list **link_ptr = &head;
@@ -1025,14 +998,9 @@ svr4_current_sos (void)
   struct so_list *head = 0;
   struct so_list **link_ptr = &head;
   CORE_ADDR ldsomap = 0;
-  struct inferior *inf;
   struct svr4_info *info;
 
-  if (ptid_equal (inferior_ptid, null_ptid))
-    return NULL;
-
-  inf = current_inferior ();
-  info = get_svr4_info (inf->pid);
+  info = get_svr4_info ();
 
   /* Always locate the debug struct, in case it has moved.  */
   info->debug_base = 0;
@@ -1129,7 +1097,7 @@ CORE_ADDR
 svr4_fetch_objfile_link_map (struct objfile *objfile)
 {
   struct so_list *so;
-  struct svr4_info *info = get_svr4_info (PIDGET (inferior_ptid));
+  struct svr4_info *info = get_svr4_info ();
 
   /* Cause svr4_current_sos() to be run if it hasn't been already.  */
   if (info->main_lm_addr == 0)
@@ -1169,16 +1137,16 @@ match_main (char *soname)
 
 /* Return 1 if PC lies in the dynamic symbol resolution code of the
    SVR4 run time loader.  */
-static CORE_ADDR interp_text_sect_low;
-static CORE_ADDR interp_text_sect_high;
-static CORE_ADDR interp_plt_sect_low;
-static CORE_ADDR interp_plt_sect_high;
 
 int
 svr4_in_dynsym_resolve_code (CORE_ADDR pc)
 {
-  return ((pc >= interp_text_sect_low && pc < interp_text_sect_high)
-	  || (pc >= interp_plt_sect_low && pc < interp_plt_sect_high)
+  struct svr4_info *info = get_svr4_info ();
+
+  return ((pc >= info->interp_text_sect_low
+	   && pc < info->interp_text_sect_high)
+	  || (pc >= info->interp_plt_sect_low
+	      && pc < info->interp_plt_sect_high)
 	  || in_plt_section (pc, NULL));
 }
 
@@ -1252,14 +1220,13 @@ enable_break (struct svr4_info *info)
   asection *interp_sect;
   gdb_byte *interp_name;
   CORE_ADDR sym_addr;
-  struct inferior *inf = current_inferior ();
 
   /* First, remove all the solib event breakpoints.  Their addresses
      may have changed since the last time we ran the program.  */
   remove_solib_event_breakpoints ();
 
-  interp_text_sect_low = interp_text_sect_high = 0;
-  interp_plt_sect_low = interp_plt_sect_high = 0;
+  info->interp_text_sect_low = info->interp_text_sect_high = 0;
+  info->interp_plt_sect_low = info->interp_plt_sect_high = 0;
 
   /* If we already have a shared library list in the target, and
      r_debug contains r_brk, set the breakpoint there - this should
@@ -1295,18 +1262,20 @@ enable_break (struct svr4_info *info)
 	  interp_sect = bfd_get_section_by_name (tmp_bfd, ".text");
 	  if (interp_sect)
 	    {
-	      interp_text_sect_low =
+	      info->interp_text_sect_low =
 		bfd_section_vma (tmp_bfd, interp_sect) + load_addr;
-	      interp_text_sect_high =
-		interp_text_sect_low + bfd_section_size (tmp_bfd, interp_sect);
+	      info->interp_text_sect_high =
+		info->interp_text_sect_low
+		+ bfd_section_size (tmp_bfd, interp_sect);
 	    }
 	  interp_sect = bfd_get_section_by_name (tmp_bfd, ".plt");
 	  if (interp_sect)
 	    {
-	      interp_plt_sect_low =
+	      info->interp_plt_sect_low =
 		bfd_section_vma (tmp_bfd, interp_sect) + load_addr;
-	      interp_plt_sect_high =
-		interp_plt_sect_low + bfd_section_size (tmp_bfd, interp_sect);
+	      info->interp_plt_sect_high =
+		info->interp_plt_sect_low
+		+ bfd_section_size (tmp_bfd, interp_sect);
 	    }
 
 	  create_solib_event_breakpoint (sym_addr);
@@ -1398,18 +1367,20 @@ enable_break (struct svr4_info *info)
       interp_sect = bfd_get_section_by_name (tmp_bfd, ".text");
       if (interp_sect)
 	{
-	  interp_text_sect_low =
+	  info->interp_text_sect_low =
 	    bfd_section_vma (tmp_bfd, interp_sect) + load_addr;
-	  interp_text_sect_high =
-	    interp_text_sect_low + bfd_section_size (tmp_bfd, interp_sect);
+	  info->interp_text_sect_high =
+	    info->interp_text_sect_low
+	    + bfd_section_size (tmp_bfd, interp_sect);
 	}
       interp_sect = bfd_get_section_by_name (tmp_bfd, ".plt");
       if (interp_sect)
 	{
-	  interp_plt_sect_low =
+	  info->interp_plt_sect_low =
 	    bfd_section_vma (tmp_bfd, interp_sect) + load_addr;
-	  interp_plt_sect_high =
-	    interp_plt_sect_low + bfd_section_size (tmp_bfd, interp_sect);
+	  info->interp_plt_sect_high =
+	    info->interp_plt_sect_low
+	    + bfd_section_size (tmp_bfd, interp_sect);
 	}
 
       /* Now try to set a breakpoint in the dynamic linker.  */
@@ -1669,7 +1640,7 @@ svr4_solib_create_inferior_hook (void)
   struct thread_info *tp;
   struct svr4_info *info;
 
-  info = get_svr4_info (PIDGET (inferior_ptid));
+  info = get_svr4_info ();
 
   /* Relocate the main executable if necessary.  */
   svr4_relocate_main_executable ();
@@ -1709,7 +1680,6 @@ svr4_solib_create_inferior_hook (void)
 static void
 svr4_clear_solib (void)
 {
-  remove_svr4_info (PIDGET (inferior_ptid));
 }
 
 static void
@@ -1908,6 +1878,8 @@ void
 _initialize_svr4_solib (void)
 {
   solib_svr4_data = gdbarch_data_register_pre_init (solib_svr4_init);
+  solib_svr4_sspace_data
+    = register_symbol_space_data_with_cleanup (svr4_sspace_data_cleanup);
 
   svr4_so_ops.relocate_section_addresses = svr4_relocate_section_addresses;
   svr4_so_ops.free_so = svr4_free_so;
@@ -1919,6 +1891,4 @@ _initialize_svr4_solib (void)
   svr4_so_ops.in_dynsym_resolve_code = svr4_in_dynsym_resolve_code;
   svr4_so_ops.lookup_lib_global_symbol = elf_lookup_lib_symbol;
   svr4_so_ops.same = svr4_same;
-
-  observer_attach_inferior_exit (solib_svr4_inferior_exit);
 }
Index: src/gdb/printcmd.c
===================================================================
--- src.orig/gdb/printcmd.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/printcmd.c	2009-07-01 19:17:15.000000000 +0100
@@ -134,16 +134,25 @@ struct display
   {
     /* Chain link to next auto-display item.  */
     struct display *next;
+
     /* The expression as the user typed it.  */
     char *exp_string;
+
     /* Expression to be evaluated and displayed.  */
     struct expression *exp;
+
     /* Item number of this auto-display item.  */
     int number;
+
     /* Display format specified.  */
     struct format_data format;
+
+    /* Symbol space associated with `block'.  */
+    struct symbol_space *sspace;
+
     /* Innermost block required by this expression when evaluated */
     struct block *block;
+
     /* Status of this display (enabled or disabled) */
     int enabled_p;
   };
@@ -1440,6 +1449,7 @@ display_command (char *exp, int from_tty
       new->exp_string = xstrdup (exp);
       new->exp = expr;
       new->block = innermost_block;
+      new->sspace = current_symbol_space;
       new->next = display_chain;
       new->number = ++display_number;
       new->format = fmt;
@@ -1576,7 +1586,12 @@ do_one_display (struct display *d)
     }
 
   if (d->block)
-    within_current_scope = contained_in (get_selected_block (0), d->block);
+    {
+      if (d->sspace == current_symbol_space)
+	within_current_scope = contained_in (get_selected_block (0), d->block);
+      else
+	within_current_scope = 0;
+    }
   else
     within_current_scope = 1;
   if (!within_current_scope)
@@ -1801,6 +1816,7 @@ display_uses_solib_p (const struct displ
   const union exp_element *const elts = exp->elts;
 
   if (d->block != NULL
+      && d->sspace == solib->sspace
       && solib_contains_address_p (solib, d->block->startaddr))
     return 1;
 
@@ -1819,9 +1835,15 @@ display_uses_solib_p (const struct displ
 	  const struct symbol *const symbol = elts[i + 2].symbol;
 	  const struct obj_section *const section =
 	    SYMBOL_OBJ_SECTION (symbol);
+#if 0
+	  /* FIXME: this info isn't there yet.  */
+	  const struct symbol_space *sspace = elts[i + 3].symbol;
+#endif
 
 	  if (block != NULL
-	      && solib_contains_address_p (solib, block->startaddr))
+	      /* FIXME: && sspace == solib->sspace */
+	      && solib_contains_address_p (solib,
+					   block->startaddr))
 	    return 1;
 
 	  if (section && section->objfile == solib->objfile)
Index: src/gdb/stack.c
===================================================================
--- src.orig/gdb/stack.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/stack.c	2009-07-01 19:17:15.000000000 +0100
@@ -630,7 +630,8 @@ print_frame_info (struct frame_info *fra
     }
 
   if (print_what != LOCATION)
-    set_default_breakpoint (1, get_frame_pc (frame), sal.symtab, sal.line);
+    set_default_breakpoint (1, sal.sspace,
+			    get_frame_pc (frame), sal.symtab, sal.line);
 
   annotate_frame_end ();
 
@@ -806,7 +807,8 @@ print_frame (struct frame_info *frame, i
 #ifdef PC_SOLIB
       char *lib = PC_SOLIB (get_frame_pc (frame));
 #else
-      char *lib = solib_name_from_address (get_frame_pc (frame));
+      char *lib = solib_name_from_address (get_frame_symbol_space (frame),
+					   get_frame_pc (frame));
 #endif
       if (lib)
 	{
Index: src/gdb/gdbthread.h
===================================================================
--- src.orig/gdb/gdbthread.h	2009-07-01 19:17:08.000000000 +0100
+++ src/gdb/gdbthread.h	2009-07-01 19:17:15.000000000 +0100
@@ -246,6 +246,10 @@ struct thread_info *first_thread_of_proc
 /* Returns any thread of process PID.  */
 extern struct thread_info *any_thread_of_process (int pid);
 
+/* Returns any non-exited thread of process PID, giving preference for
+   already stopped threads.  */
+extern struct thread_info *any_live_thread_of_process (int pid);
+
 /* Change the ptid of thread OLD_PTID to NEW_PTID.  */
 void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
 
Index: src/gdb/thread.c
===================================================================
--- src.orig/gdb/thread.c	2009-07-01 19:17:08.000000000 +0100
+++ src/gdb/thread.c	2009-07-01 19:17:15.000000000 +0100
@@ -440,6 +440,24 @@ any_thread_of_process (int pid)
   return NULL;
 }
 
+struct thread_info *
+any_live_thread_of_process (int pid)
+{
+  struct thread_info *tp;
+  struct thread_info *tp_running = NULL;
+
+  for (tp = thread_list; tp; tp = tp->next)
+    if (ptid_get_pid (tp->ptid) == pid)
+      {
+	if (tp->state_ == THREAD_STOPPED)
+	  return tp;
+	else if (tp->state_ == THREAD_RUNNING)
+	  tp_running = tp;
+      }
+
+  return tp_running;
+}
+
 /* Print a list of thread ids currently known, and the total number of
    threads. To be used from within catch_errors. */
 static int
@@ -849,6 +867,13 @@ switch_to_thread (ptid_t ptid)
     return;
 
   inferior_ptid = ptid;
+
+  /* Switch the symbol space as well, if we can infer it from the now
+     current thread.  Otherwise, it's up to the caller to select the
+     space it wants.  */
+  if (!ptid_equal (inferior_ptid, null_ptid))
+    set_current_symbol_space (current_inferior ()->sspace);
+
   reinit_frame_cache ();
   registers_changed ();
 
@@ -913,7 +938,7 @@ restore_selected_frame (struct frame_id 
   select_frame (get_current_frame ());
 
   /* Warn the user.  */
-  if (!ui_out_is_mi_like_p (uiout))
+  if (frame_level > 0 && !ui_out_is_mi_like_p (uiout))
     {
       warning (_("\
 Couldn't restore frame #%d in current thread, at reparsed frame #0\n"),
Index: src/gdb/linux-fork.c
===================================================================
--- src.orig/gdb/linux-fork.c	2009-07-01 19:17:08.000000000 +0100
+++ src/gdb/linux-fork.c	2009-07-01 19:17:15.000000000 +0100
@@ -27,6 +27,7 @@
 #include "gdb_string.h"
 #include "linux-fork.h"
 #include "linux-nat.h"
+#include "gdbthread.h"
 
 #include <sys/ptrace.h>
 #include "gdb_wait.h"
@@ -40,9 +41,6 @@ static int highest_fork_num;
 /* Prevent warning from -Wmissing-prototypes.  */
 extern void _initialize_linux_fork (void);
 
-int detach_fork = 1;		/* Default behavior is to detach
-				   newly forked processes (legacy).  */
-
 /* Fork list data structure:  */
 struct fork_info
 {
@@ -646,14 +644,6 @@ _initialize_linux_fork (void)
 {
   init_fork_list ();
 
-  /* Set/show detach-on-fork: user-settable mode.  */
-
-  add_setshow_boolean_cmd ("detach-on-fork", class_obscure, &detach_fork, _("\
-Set whether gdb will detach the child of a fork."), _("\
-Show whether gdb will detach the child of a fork."), _("\
-Tells gdb whether to detach the child of a fork."), 
-			   NULL, NULL, &setlist, &showlist);
-
   /* Set/show restart-auto-finish: user-settable count.  Causes the
      first "restart" of a fork to do some number of "finish" commands
      before returning to user.
Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/infrun.c	2009-07-01 19:17:15.000000000 +0100
@@ -108,6 +108,9 @@ int sync_execution = 0;
 
 static ptid_t previous_inferior_ptid;
 
+/* Default behavior is to detach newly forked processes (legacy).  */
+int detach_fork = 1;
+
 int debug_displaced = 0;
 static void
 show_debug_displaced (struct ui_file *file, int from_tty,
@@ -460,6 +463,200 @@ follow_inferior_reset_breakpoints (void)
   insert_breakpoints ();
 }
 
+/* The child has exited or execed: resume threads of the parent the
+   user wanted to be executing.  */
+
+static int
+proceed_after_vfork_done (struct thread_info *thread,
+			  void *arg)
+{
+  int pid = * (int *) arg;
+
+  if (ptid_get_pid (thread->ptid) == pid
+      && is_running (thread->ptid)
+      && !is_executing (thread->ptid)
+      && !thread->stop_requested
+      && thread->stop_signal == TARGET_SIGNAL_0)
+    {
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog,
+			    "infrun: resuming vfork parent thread %s\n",
+			    target_pid_to_str (thread->ptid));
+
+      switch_to_thread (thread->ptid);
+      clear_proceed_status ();
+      proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+    }
+
+  return 0;
+}
+
+/* Called whenever we notice an exec or exit event, to handle
+   detaching or resuming a vfork parent.  */
+
+static void
+handle_vfork_child_exec_or_exit (int exec)
+{
+  struct inferior *inf = current_inferior ();
+
+  if (inf->vfork_parent)
+    {
+      int resume_parent = -1;
+
+      /* This exec or exit marks the end of the shared memory region
+	 between the parent and the child.  If the user wanted to
+	 detach from the parent, now is the time.  */
+
+      if (inf->vfork_parent->pending_detach)
+	{
+	  struct thread_info *tp;
+	  struct cleanup *old_chain;
+	  struct symbol_space *sspace;
+	  struct address_space *aspace;
+
+	  /* follow-fork child, detach-on-fork on */
+
+	  old_chain = make_cleanup_restore_current_thread ();
+
+	  /* We're letting loose of the parent.  */
+	  tp = any_live_thread_of_process (inf->vfork_parent->pid);
+	  switch_to_thread (tp->ptid);
+
+	  /* We're about to detach from the parent, which implicitly
+	     removes breakpoints from its address space.  There's a
+	     catch here: we want to reuse the spaces for the child,
+	     but, parent/child are still sharing the sspace at this
+	     point, although the exec in reality makes the kernel give
+	     the child a fresh set of new pages.  The problem here is
+	     that the breakpoints module being unaware of this, would
+	     likely chose the child process to write to the parent
+	     address space.  Swapping the child temporarily away from
+	     the spaces has the desired effect.  Yes, this is "sort
+	     of" a hack.  */
+
+	  sspace = inf->sspace;
+	  aspace = inf->aspace;
+	  inf->aspace = NULL;
+	  inf->sspace = NULL;
+
+	  if (debug_infrun || info_verbose)
+	    {
+	      target_terminal_ours ();
+
+	      if (exec)
+		fprintf_filtered (gdb_stdlog,
+				  "Detaching vfork parent process %d after child exec.\n",
+				  inf->vfork_parent->pid);
+	      else
+		fprintf_filtered (gdb_stdlog,
+				  "Detaching vfork parent process %d after child exit.\n",
+				  inf->vfork_parent->pid);
+	    }
+
+	  target_detach (NULL, 0);
+
+	  /* Put it back.  */
+	  inf->sspace = sspace;
+	  inf->aspace = aspace;
+
+	  do_cleanups (old_chain);
+	}
+      else if (exec)
+	{
+	  /* We're staying attached to the parent, so, really give the
+	     child a new address space.  */
+	  inf->sspace = add_symbol_space (maybe_new_address_space ());
+	  inf->aspace = inf->sspace->aspace;
+	  inf->sspace->removable = 1;
+	  set_current_symbol_space (inf->sspace);
+
+	  resume_parent = inf->vfork_parent->pid;
+
+	  /* Break the bonds.  */
+	  inf->vfork_parent->vfork_child = NULL;
+	}
+      else
+	{
+	  struct cleanup *old_chain;
+	  struct symbol_space *sspace;
+
+	  /* If this is a vfork child exiting, then the sspace and
+	     aspaces where shared with the parent.  Since we're
+	     reporting the process exit, we'll be mourning all that is
+	     found in the address space, and switching to null_ptid,
+	     preparing to start a new inferior.  But, since we don't
+	     want to clobber the parent's address/symbol spaces, we go
+	     ahead and create a new one for this exiting inferior.
+	     The alternative, is to make normal_stop switch to the
+	     parent process automatically, without messing up with the
+	     parent's spaces.  */
+
+	  /* Switch to null_ptid, so that clone_symbol_space doesn't want
+	     to read the selected frame of a dead process.  */
+	  old_chain = save_inferior_ptid ();
+	  inferior_ptid = null_ptid;
+
+	  /* This inferior is dead, so avoid giving the breakpoints
+	     module the option to write through to it (cloning a
+	     symbol space resets breakpoints).  */
+	  inf->aspace = NULL;
+	  inf->sspace = NULL;
+	  sspace = add_symbol_space (maybe_new_address_space ());
+	  set_current_symbol_space (sspace);
+	  sspace->removable = 1;
+	  clone_symbol_space (sspace, inf->vfork_parent->sspace);
+	  inf->sspace = sspace;
+	  inf->aspace = sspace->aspace;
+
+	  /* Put back inferior_ptid.  We'll continue mourning this
+	     inferior. */
+	  do_cleanups (old_chain);
+
+	  resume_parent = inf->vfork_parent->pid;
+	  /* Break the bonds.  */
+	  inf->vfork_parent->vfork_child = NULL;
+	}
+
+      inf->vfork_parent = NULL;
+
+      gdb_assert (current_symbol_space == inf->sspace);
+
+      if (non_stop && resume_parent != -1)
+	{
+	  /* If the user wanted the parent to be running, let it go
+	     free now.  */
+	  struct cleanup *old_chain = make_cleanup_restore_current_thread ();
+
+	  if (debug_infrun)
+	    fprintf_unfiltered (gdb_stdlog, "infrun: resuming vfork parent process %d\n",
+				resume_parent);
+
+	  iterate_over_threads (proceed_after_vfork_done, &resume_parent);
+
+	  do_cleanups (old_chain);
+	}
+    }
+}
+
+/* Enum strings for "set|show displaced-stepping".  */
+
+static const char follow_exec_mode_replace[] = "replace";
+static const char follow_exec_mode_keep[] = "keep";
+static const char *follow_exec_mode_names[] =
+{
+  follow_exec_mode_replace,
+  follow_exec_mode_keep,
+  NULL,
+};
+
+static const char *follow_exec_mode_string = follow_exec_mode_replace;
+static void
+show_follow_exec_mode_string (struct ui_file *file, int from_tty,
+			      struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Follow exec mode is \"%s\".\n"),  value);
+}
+
 /* EXECD_PATHNAME is assumed to be non-NULL. */
 
 static void
@@ -467,6 +664,7 @@ follow_exec (ptid_t pid, char *execd_pat
 {
   struct target_ops *tgt;
   struct thread_info *th = inferior_thread ();
+  struct inferior *inf = current_inferior ();
 
   /* This is an exec event that we actually wish to pay attention to.
      Refresh our symbol table to the newly exec'd program, remove any
@@ -488,6 +686,9 @@ follow_exec (ptid_t pid, char *execd_pat
      that may write the bp's "shadow contents" (the instruction
      value that was overwritten witha TRAP instruction).  Since
      we now have a new a.out, those shadow contents aren't valid. */
+
+  mark_breakpoints_out ();
+
   update_breakpoints_after_exec ();
 
   /* If there was one, it's gone now.  We cannot truly step-to-next
@@ -505,7 +706,9 @@ follow_exec (ptid_t pid, char *execd_pat
   th->stop_requested = 0;
 
   /* What is this a.out's name? */
-  printf_unfiltered (_("Executing new program: %s\n"), execd_pathname);
+  printf_unfiltered (_("%s is executing new program: %s\n"),
+		     target_pid_to_str (inferior_ptid),
+		     execd_pathname);
 
   /* We've followed the inferior through an exec.  Therefore, the
      inferior has essentially been killed & reborn. */
@@ -524,9 +727,6 @@ follow_exec (ptid_t pid, char *execd_pat
       execd_pathname = name;
     }
 
-  /* That a.out is now the one to use. */
-  exec_file_attach (execd_pathname, 0);
-
   /* Reset the shared library package.  This ensures that we get a
      shlib event when the child reaches "_start", at which point the
      dld will have had a chance to initialize the child.  */
@@ -535,6 +735,25 @@ follow_exec (ptid_t pid, char *execd_pat
      previous incarnation of this process.  */
   no_shared_libraries (NULL, 0);
 
+  if (follow_exec_mode_string == follow_exec_mode_keep)
+    {
+      struct symbol_space *sspace;
+
+      /* The user wants to keep the old symbol space around.  Create a
+	 new fresh one, and switch to it.  */
+
+      sspace = add_symbol_space (maybe_new_address_space ());
+      sspace->removable = 1;
+      set_current_symbol_space (sspace);
+      inf->sspace = sspace;
+      inf->aspace = sspace->aspace;
+    }
+
+  gdb_assert (current_symbol_space == inf->sspace);
+
+  /* That a.out is now the one to use. */
+  exec_file_attach (execd_pathname, 0);
+
   /* Load the main file's symbols.  */
   symbol_file_add_main (execd_pathname, 0);
 
@@ -961,8 +1180,10 @@ displaced_step_fixup (ptid_t event_ptid,
   while (displaced_step_request_queue)
     {
       struct displaced_step_request *head;
+      struct frame_info *frame;
       ptid_t ptid;
       CORE_ADDR actual_pc;
+      struct regcache *regcache;
 
       head = displaced_step_request_queue;
       ptid = head->ptid;
@@ -971,9 +1192,10 @@ displaced_step_fixup (ptid_t event_ptid,
 
       context_switch (ptid);
 
-      actual_pc = regcache_read_pc (get_thread_regcache (ptid));
+      regcache = get_thread_regcache (ptid);
+      actual_pc = regcache_read_pc (regcache);
 
-      if (breakpoint_here_p (actual_pc))
+      if (breakpoint_here_p (get_regcache_aspace (regcache), actual_pc))
 	{
 	  if (debug_displaced)
 	    fprintf_unfiltered (gdb_stdlog,
@@ -1131,6 +1353,7 @@ resume (int step, enum target_signal sig
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
   struct thread_info *tp = inferior_thread ();
   CORE_ADDR pc = regcache_read_pc (regcache);
+  struct address_space *aspace = get_regcache_aspace (regcache);
 
   QUIT;
 
@@ -1156,7 +1379,7 @@ resume (int step, enum target_signal sig
      removed or inserted, as appropriate.  The exception is if we're sitting
      at a permanent breakpoint; we need to step over it, but permanent
      breakpoints can't be removed.  So we have to test for it here.  */
-  if (breakpoint_here_p (pc) == permanent_breakpoint_here)
+  if (breakpoint_here_p (aspace, pc) == permanent_breakpoint_here)
     {
       if (gdbarch_skip_permanent_breakpoint_p (gdbarch))
 	gdbarch_skip_permanent_breakpoint (gdbarch, regcache);
@@ -1269,7 +1492,7 @@ a command like `return' or `jump' to con
 	  /* Most targets can step a breakpoint instruction, thus
 	     executing it normally.  But if this one cannot, just
 	     continue and we will hit it anyway.  */
-	  if (step && breakpoint_inserted_here_p (pc))
+	  if (step && breakpoint_inserted_here_p (aspace, pc))
 	    step = 0;
 	}
 
@@ -1342,23 +1565,26 @@ clear_proceed_status_callback (struct th
 void
 clear_proceed_status (void)
 {
+  if (!non_stop)
+    {
+      /* In all-stop mode, delete the per-thread status of all
+	 threads, even if inferior_ptid is null_ptid, there may be
+	 threads on the list.  E.g., we may be launching a new
+	 process, while selecting the executable.  */
+      iterate_over_threads (clear_proceed_status_callback, NULL);
+    }
+
   if (!ptid_equal (inferior_ptid, null_ptid))
     {
       struct inferior *inferior;
 
       if (non_stop)
 	{
-	  /* If in non-stop mode, only delete the per-thread status
-	     of the current thread.  */
+	  /* If in non-stop mode, only delete the per-thread status of
+	     the current thread.  */
 	  clear_proceed_status_thread (inferior_thread ());
 	}
-      else
-	{
-	  /* In all-stop mode, delete the per-thread status of
-	     *all* threads.  */
-	  iterate_over_threads (clear_proceed_status_callback, NULL);
-	}
-  
+
       inferior = current_inferior ();
       inferior->stop_soon = NO_STOP_QUIETLY;
     }
@@ -1420,7 +1646,8 @@ prepare_to_proceed (int step)
     {
       struct regcache *regcache = get_thread_regcache (wait_ptid);
 
-      if (breakpoint_here_p (regcache_read_pc (regcache)))
+      if (breakpoint_here_p (get_regcache_aspace (regcache),
+			     regcache_read_pc (regcache)))
 	{
 	  /* If stepping, remember current thread to switch back to.  */
 	  if (step)
@@ -1458,6 +1685,7 @@ proceed (CORE_ADDR addr, enum target_sig
   struct gdbarch *gdbarch;
   struct thread_info *tp;
   CORE_ADDR pc;
+  struct address_space *aspace;
   int oneproc = 0;
 
   /* If we're stopped at a fork/vfork, follow the branch set by the
@@ -1472,6 +1700,7 @@ proceed (CORE_ADDR addr, enum target_sig
 
   regcache = get_current_regcache ();
   gdbarch = get_regcache_arch (regcache);
+  aspace = get_regcache_aspace (regcache);
   pc = regcache_read_pc (regcache);
 
   if (step > 0)
@@ -1481,7 +1710,7 @@ proceed (CORE_ADDR addr, enum target_sig
 
   if (addr == (CORE_ADDR) -1)
     {
-      if (pc == stop_pc && breakpoint_here_p (pc) 
+      if (pc == stop_pc && breakpoint_here_p (aspace, pc)
 	  && execution_direction != EXEC_REVERSE)
 	/* There is a breakpoint at the address we will resume at,
 	   step one instruction before inserting breakpoints so that
@@ -2214,6 +2443,7 @@ adjust_pc_after_break (struct execution_
 {
   struct regcache *regcache;
   struct gdbarch *gdbarch;
+  struct address_space *aspace;
   CORE_ADDR breakpoint_pc;
 
   /* If we've hit a breakpoint, we'll normally be stopped with SIGTRAP.  If
@@ -2279,6 +2509,8 @@ adjust_pc_after_break (struct execution_
   if (gdbarch_decr_pc_after_break (gdbarch) == 0)
     return;
 
+  aspace = get_regcache_aspace (regcache);
+
   /* Find the location where (if we've hit a breakpoint) the
      breakpoint would be.  */
   breakpoint_pc = regcache_read_pc (regcache)
@@ -2292,8 +2524,8 @@ adjust_pc_after_break (struct execution_
      already queued and arrive later.  To suppress those spurious
      SIGTRAPs, we keep a list of such breakpoint locations for a bit,
      and retire them after a number of stop events are reported.  */
-  if (software_breakpoint_inserted_here_p (breakpoint_pc)
-      || (non_stop && moribund_breakpoint_here_p (breakpoint_pc)))
+  if (software_breakpoint_inserted_here_p (aspace, breakpoint_pc)
+      || (non_stop && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
     {
       struct cleanup *old_cleanups = NULL;
       if (RECORD_IS_USED)
@@ -2546,6 +2778,8 @@ handle_inferior_event (struct execution_
       if (debug_infrun)
         fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_EXITED\n");
       inferior_ptid = ecs->ptid;
+      set_current_symbol_space (current_inferior ()->sspace);
+      handle_vfork_child_exec_or_exit (0);
       target_terminal_ours ();	/* Must do this before mourn anyway */
       print_stop_reason (EXITED, ecs->ws.value.integer);
 
@@ -2564,6 +2798,8 @@ handle_inferior_event (struct execution_
       if (debug_infrun)
         fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SIGNALLED\n");
       inferior_ptid = ecs->ptid;
+      set_current_symbol_space (current_inferior ()->sspace);
+      handle_vfork_child_exec_or_exit (0);
       stop_print_frame = 0;
       target_terminal_ours ();	/* Must do this before mourn anyway */
 
@@ -2620,19 +2856,45 @@ handle_inferior_event (struct execution_
 
       stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
 
-      ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
+      ecs->event_thread->stop_bpstat
+	= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
+			      stop_pc, ecs->ptid);
 
       ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
 
       /* If no catchpoint triggered for this, then keep going.  */
       if (ecs->random_signal)
 	{
+	  ptid_t parent;
+	  ptid_t child;
 	  int should_resume;
+	  int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
 
 	  ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
 
 	  should_resume = follow_fork ();
 
+	  parent = ecs->ptid;
+	  child = ecs->ws.value.related_pid;
+
+	  /* In non-stop mode, also resume the other branch.  */
+	  if (non_stop && !detach_fork)
+	    {
+	      if (follow_child)
+		switch_to_thread (parent);
+	      else
+		switch_to_thread (child);
+
+	      ecs->event_thread = inferior_thread ();
+	      ecs->ptid = inferior_ptid;
+	      keep_going (ecs);
+	    }
+
+	  if (follow_child)
+	    switch_to_thread (child);
+	  else
+	    switch_to_thread (parent);
+
 	  ecs->event_thread = inferior_thread ();
 	  ecs->ptid = inferior_ptid;
 
@@ -2645,6 +2907,22 @@ handle_inferior_event (struct execution_
       ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
       goto process_event_stop_test;
 
+    case TARGET_WAITKIND_VFORK_DONE:
+      /* Done with the shared memory region.  Re-insert breakpoints in
+	 the parent, and keep going.  */
+
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_VFORK_DONE\n");
+
+      if (!ptid_equal (ecs->ptid, inferior_ptid))
+	context_switch (ecs->ptid);
+
+      current_inferior ()->waiting_for_vfork_done = 0;
+      /* This also takes care of reinserting breakpoints in the
+	 previously locked inferior.  */
+      keep_going (ecs);
+      return;
+
     case TARGET_WAITKIND_EXECD:
       if (debug_infrun)
         fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_EXECD\n");
@@ -2657,12 +2935,17 @@ handle_inferior_event (struct execution_
 
       stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
 
+      /* Do whatever is necessary to the parent branch of the vfork.  */
+      handle_vfork_child_exec_or_exit (1);
+
       /* This causes the eventpoints and symbol table to be reset.
          Must do this now, before trying to determine whether to
          stop.  */
       follow_exec (inferior_ptid, ecs->ws.value.execd_pathname);
 
-      ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
+      ecs->event_thread->stop_bpstat
+	= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
+			      stop_pc, ecs->ptid);
       ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
 
       /* Note that this may be referenced from inside
@@ -2863,14 +3146,15 @@ targets should add new threads to the th
   if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP)
     {
       int thread_hop_needed = 0;
+      struct address_space *aspace = get_regcache_aspace (get_current_regcache ());
 
       /* Check if a regular breakpoint has been hit before checking
          for a potential single step breakpoint. Otherwise, GDB will
          not see this breakpoint hit when stepping onto breakpoints.  */
-      if (regular_breakpoint_inserted_here_p (stop_pc))
+      if (regular_breakpoint_inserted_here_p (aspace, stop_pc))
 	{
 	  ecs->random_signal = 0;
-	  if (!breakpoint_thread_match (stop_pc, ecs->ptid))
+	  if (!breakpoint_thread_match (aspace, stop_pc, ecs->ptid))
 	    thread_hop_needed = 1;
 	}
       else if (singlestep_breakpoints_inserted_p)
@@ -3159,7 +3443,8 @@ targets should add new threads to the th
      non-standard signals can't be explained by the breakpoint.  */
   if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP
       || (! ecs->event_thread->trap_expected
-          && breakpoint_inserted_here_p (stop_pc)
+          && breakpoint_inserted_here_p (get_regcache_aspace (get_current_regcache ()),
+					 stop_pc)
 	  && (ecs->event_thread->stop_signal == TARGET_SIGNAL_ILL
 	      || ecs->event_thread->stop_signal == TARGET_SIGNAL_SEGV
 	      || ecs->event_thread->stop_signal == TARGET_SIGNAL_EMT))
@@ -3216,8 +3501,10 @@ targets should add new threads to the th
 	}
 
       /* See if there is a breakpoint at the current PC.  */
-      ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
-      
+      ecs->event_thread->stop_bpstat
+	= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
+			      stop_pc, ecs->ptid);
+
       /* Following in case break condition called a
 	 function.  */
       stop_print_frame = 1;
@@ -3731,6 +4018,7 @@ infrun: not switching back to stepped th
 	  struct symtab_and_line sr_sal;
 	  init_sal (&sr_sal);
 	  sr_sal.pc = pc_after_resolver;
+	  sr_sal.sspace = get_frame_symbol_space (get_current_frame ());
 
 	  insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
 	}
@@ -3828,6 +4116,7 @@ infrun: not switching back to stepped th
 	      /* Normal function call return (static or dynamic).  */
 	      init_sal (&sr_sal);
 	      sr_sal.pc = ecs->stop_func_start;
+	      sr_sal.sspace = get_frame_symbol_space (get_current_frame ());
 	      insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
 	    }
 	  else
@@ -3853,7 +4142,7 @@ infrun: not switching back to stepped th
 	  struct symtab_and_line sr_sal;
 	  init_sal (&sr_sal);
 	  sr_sal.pc = ecs->stop_func_start;
-
+	  sr_sal.sspace = get_frame_symbol_space (get_current_frame ());
 	  insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
 	  keep_going (ecs);
 	  return;
@@ -3898,6 +4187,7 @@ infrun: not switching back to stepped th
 	  struct symtab_and_line sr_sal;
 	  init_sal (&sr_sal);
 	  sr_sal.pc = ecs->stop_func_start;
+	  sr_sal.sspace = get_frame_symbol_space (get_current_frame ());
 	  insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
 	}
       else
@@ -3930,6 +4220,7 @@ infrun: not switching back to stepped th
 	  init_sal (&sr_sal);	/* initialize to zeroes */
 	  sr_sal.pc = real_stop_pc;
 	  sr_sal.section = find_pc_overlay (sr_sal.pc);
+	  sr_sal.sspace = get_frame_symbol_space (get_current_frame ());
 
 	  /* Do not specify what the fp should be when we stop since
 	     on some machines the prologue is where the new fp value
@@ -4208,6 +4499,7 @@ handle_step_into_function (struct gdbarc
       init_sal (&sr_sal);	/* initialize to zeroes */
       sr_sal.pc = ecs->stop_func_start;
       sr_sal.section = find_pc_overlay (ecs->stop_func_start);
+      sr_sal.sspace = get_frame_symbol_space (get_current_frame ());
 
       /* Do not specify what the fp should be when we stop since on
          some machines the prologue is where the new fp value is
@@ -4297,6 +4589,7 @@ insert_step_resume_breakpoint_at_frame (
 
   sr_sal.pc = gdbarch_addr_bits_remove (gdbarch, get_frame_pc (return_frame));
   sr_sal.section = find_pc_overlay (sr_sal.pc);
+  sr_sal.sspace = get_frame_symbol_space (return_frame);
 
   insert_step_resume_breakpoint_at_sal (sr_sal, get_stack_frame_id (return_frame));
 }
@@ -4331,6 +4624,7 @@ insert_step_resume_breakpoint_at_caller 
   sr_sal.pc = gdbarch_addr_bits_remove (gdbarch,
 					frame_unwind_caller_pc (next_frame));
   sr_sal.section = find_pc_overlay (sr_sal.pc);
+  sr_sal.sspace = frame_unwind_symbol_space (next_frame);
 
   insert_step_resume_breakpoint_at_sal (sr_sal,
 					frame_unwind_caller_id (next_frame));
@@ -4846,6 +5140,11 @@ done:
 	   Delete any breakpoint that is to be deleted at the next stop.  */
 	breakpoint_auto_delete (inferior_thread ()->stop_bpstat);
     }
+
+  /* Try to get rid of automatically added symbol spaces that are no
+     longer needed.  Keeping those around slows down things linearly.
+     Note that this never removes the current symbol space.  */
+  prune_symbol_spaces ();
 }
 
 static int
@@ -5906,6 +6205,25 @@ By default, the debugger will follow the
 			show_follow_fork_mode_string,
 			&setlist, &showlist);
 
+  add_setshow_enum_cmd ("follow-exec-mode", class_run,
+			follow_exec_mode_names,
+			&follow_exec_mode_string, _("\
+Set debugger response to a program call of exec."), _("\
+Show debugger response to a program call of exec."), _("\
+An exec call replaces the process'es program image.  follow-exec-mode can be:\n\
+\n\
+  keep - The previous symbol space is kept so that it can\n\
+be restarted afterwards.  The process is assigned a new symbol space.\n\
+\n\
+  replace - the process'es previous symbol space is reused.  The new\n\
+executable replaces the previous executable in the symbol space.\n\
+\n\
+By default, the debugger will reuse the symbol space, replacing its main\n\
+executable."),
+			NULL,
+			show_follow_exec_mode_string,
+			&setlist, &showlist);
+
   add_setshow_enum_cmd ("scheduler-locking", class_run, 
 			scheduler_enums, &scheduler_mode, _("\
 Set mode for locking scheduler during execution."), _("\
@@ -5964,6 +6282,14 @@ Options are 'forward' or 'reverse'."),
 			set_exec_direction_func, show_exec_direction_func,
 			&setlist, &showlist);
 
+  /* Set/show detach-on-fork: user-settable mode.  */
+
+  add_setshow_boolean_cmd ("detach-on-fork", class_run, &detach_fork, _("\
+Set whether gdb will detach the child of a fork."), _("\
+Show whether gdb will detach the child of a fork."), _("\
+Tells gdb whether to detach the child of a fork."),
+			   NULL, NULL, &setlist, &showlist);
+
   /* ptid initializations */
   null_ptid = ptid_build (0, 0, 0);
   minus_one_ptid = ptid_build (-1, 0, 0);
Index: src/gdb/regcache.c
===================================================================
--- src.orig/gdb/regcache.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/regcache.c	2009-07-01 19:17:15.000000000 +0100
@@ -185,6 +185,11 @@ register_size (struct gdbarch *gdbarch, 
 struct regcache
 {
   struct regcache_descr *descr;
+
+  /* The address space of this register cache (for registers where it
+     makes sense, like PC or SP).  */
+  struct address_space *aspace;
+
   /* The register buffers.  A read-only register cache can hold the
      full [0 .. gdbarch_num_regs + gdbarch_num_pseudo_regs) while a read/write
      register cache can only hold [0 .. gdbarch_num_regs).  */
@@ -219,6 +224,7 @@ regcache_xmalloc (struct gdbarch *gdbarc
     = XCALLOC (descr->sizeof_raw_registers, gdb_byte);
   regcache->register_valid_p
     = XCALLOC (descr->sizeof_raw_register_valid_p, gdb_byte);
+  regcache->aspace = NULL;
   regcache->readonly_p = 1;
   regcache->ptid = minus_one_ptid;
   return regcache;
@@ -254,6 +260,12 @@ get_regcache_arch (const struct regcache
   return regcache->descr->gdbarch;
 }
 
+struct address_space *
+get_regcache_aspace (const struct regcache *regcache)
+{
+  return regcache->aspace;
+}
+
 /* Return  a pointer to register REGNUM's buffer cache.  */
 
 static gdb_byte *
@@ -340,10 +352,14 @@ regcache_cpy (struct regcache *dst, stru
 {
   int i;
   gdb_byte *buf;
+
   gdb_assert (src != NULL && dst != NULL);
   gdb_assert (src->descr->gdbarch == dst->descr->gdbarch);
   gdb_assert (src != dst);
   gdb_assert (src->readonly_p || dst->readonly_p);
+
+  dst->aspace = src->aspace;
+
   if (!src->readonly_p)
     regcache_save (dst, do_cooked_read, src);
   else if (!dst->readonly_p)
@@ -362,6 +378,8 @@ regcache_cpy_no_passthrough (struct regc
      move of data into the current regcache.  Doing this would be
      silly - it would mean that valid_p would be completely invalid.  */
   gdb_assert (dst->readonly_p);
+
+  dst->aspace = src->aspace;
   memcpy (dst->registers, src->registers, dst->descr->sizeof_raw_registers);
   memcpy (dst->register_valid_p, src->register_valid_p,
 	  dst->descr->sizeof_raw_register_valid_p);
@@ -435,6 +453,8 @@ struct regcache *get_thread_regcache (pt
   current_regcache = regcache_xmalloc (thread_gdbarch);
   current_regcache->readonly_p = 0;
   current_regcache->ptid = ptid;
+  current_regcache->aspace = target_thread_address_space (ptid);
+  gdb_assert (current_regcache->aspace != NULL);
 
   return current_regcache;
 }
Index: src/gdb/regcache.h
===================================================================
--- src.orig/gdb/regcache.h	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/regcache.h	2009-07-01 19:17:15.000000000 +0100
@@ -23,6 +23,7 @@
 
 struct regcache;
 struct gdbarch;
+struct address_space;
 
 extern struct regcache *get_current_regcache (void);
 extern struct regcache *get_thread_regcache (ptid_t ptid);
@@ -35,6 +36,10 @@ struct regcache *regcache_xmalloc (struc
 
 extern struct gdbarch *get_regcache_arch (const struct regcache *regcache);
 
+/* Return REGCACHE's address space.  */
+
+extern struct address_space *get_regcache_aspace (const struct regcache *regcache);
+
 /* Transfer a raw register [0..NUM_REGS) between core-gdb and the
    regcache. */
 
Index: src/gdb/tui/tui-disasm.c
===================================================================
--- src.orig/gdb/tui/tui-disasm.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/tui/tui-disasm.c	2009-07-01 19:17:15.000000000 +0100
@@ -36,6 +36,7 @@
 #include "tui/tui-stack.h"
 #include "tui/tui-file.h"
 #include "tui/tui-disasm.h"
+#include "symspace.h"
 
 #include "gdb_curses.h"
 
@@ -257,7 +258,8 @@ tui_set_disassem_content (CORE_ADDR pc)
 
       /* See whether there is a breakpoint installed.  */
       src->has_break = (!src->is_exec_point
-                       && breakpoint_here_p (pc) != no_breakpoint_here);
+			&& breakpoint_here_p (current_symbol_space->aspace, pc)
+			!= no_breakpoint_here);
 
       xfree (asm_lines[i].addr_string);
       xfree (asm_lines[i].insn);
Index: src/gdb/infcall.c
===================================================================
--- src.orig/gdb/infcall.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/infcall.c	2009-07-01 19:17:15.000000000 +0100
@@ -734,6 +734,7 @@ call_function_by_hand (struct value *fun
     struct breakpoint *bpt;
     struct symtab_and_line sal;
     init_sal (&sal);		/* initialize to zeroes */
+    sal.sspace = current_symbol_space;
     sal.pc = bp_addr;
     sal.section = find_pc_overlay (sal.pc);
     /* Sanity.  The exact same SP value is returned by
Index: src/gdb/arch-utils.c
===================================================================
--- src.orig/gdb/arch-utils.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/arch-utils.c	2009-07-01 19:17:15.000000000 +0100
@@ -720,6 +720,14 @@ gdbarch_info_fill (struct gdbarch_info *
   gdb_assert (info->bfd_arch_info != NULL);
 }
 
+int
+default_has_shared_address_space (struct gdbarch *gdbarch)
+{
+  /* Simply say no.  In most unix-like targets each inferior/process
+     has its own address space.  */
+  return 0;
+}
+
 /* */
 
 extern initialize_file_ftype _initialize_gdbarch_utils; /* -Wmissing-prototypes */
Index: src/gdb/arch-utils.h
===================================================================
--- src.orig/gdb/arch-utils.h	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/arch-utils.h	2009-07-01 19:17:15.000000000 +0100
@@ -139,4 +139,6 @@ extern void gdbarch_info_fill (struct gd
 
 extern struct gdbarch *gdbarch_from_bfd (bfd *abfd);
 
+extern int default_has_shared_address_space (struct gdbarch *);
+
 #endif
Index: src/gdb/gdbarch.sh
===================================================================
--- src.orig/gdb/gdbarch.sh	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/gdbarch.sh	2009-07-01 19:17:15.000000000 +0100
@@ -736,6 +736,9 @@ v:int:has_global_solist:::0:0::0
 # visible to all address spaces automatically.  For such cases,
 # this property should be set to true.
 v:int:has_global_breakpoints:::0:0::0
+
+# True if inferiors share an address space (e.g., uClinux).
+m:int:has_shared_address_space:void:::default_has_shared_address_space::0
 EOF
 }
 
Index: src/gdb/gdbarch.h
===================================================================
--- src.orig/gdb/gdbarch.h	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/gdbarch.h	2009-07-01 19:17:15.000000000 +0100
@@ -872,6 +872,12 @@ extern void set_gdbarch_has_global_solis
 extern int gdbarch_has_global_breakpoints (struct gdbarch *gdbarch);
 extern void set_gdbarch_has_global_breakpoints (struct gdbarch *gdbarch, int has_global_breakpoints);
 
+/* True if inferiors share an address space (e.g., uClinux). */
+
+typedef int (gdbarch_has_shared_address_space_ftype) (struct gdbarch *gdbarch);
+extern int gdbarch_has_shared_address_space (struct gdbarch *gdbarch);
+extern void set_gdbarch_has_shared_address_space (struct gdbarch *gdbarch, gdbarch_has_shared_address_space_ftype *has_shared_address_space);
+
 extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch);
 
 
Index: src/gdb/gdbarch.c
===================================================================
--- src.orig/gdb/gdbarch.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/gdbarch.c	2009-07-01 19:17:15.000000000 +0100
@@ -246,6 +246,7 @@ struct gdbarch
   gdbarch_record_special_symbol_ftype *record_special_symbol;
   int has_global_solist;
   int has_global_breakpoints;
+  gdbarch_has_shared_address_space_ftype *has_shared_address_space;
 };
 
 
@@ -383,6 +384,7 @@ struct gdbarch startup_gdbarch =
   0,  /* record_special_symbol */
   0,  /* has_global_solist */
   0,  /* has_global_breakpoints */
+  default_has_shared_address_space,  /* has_shared_address_space */
   /* startup_gdbarch() */
 };
 
@@ -466,6 +468,7 @@ gdbarch_alloc (const struct gdbarch_info
   gdbarch->displaced_step_location = NULL;
   gdbarch->target_signal_from_host = default_target_signal_from_host;
   gdbarch->target_signal_to_host = default_target_signal_to_host;
+  gdbarch->has_shared_address_space = default_has_shared_address_space;
   /* gdbarch_alloc() */
 
   return gdbarch;
@@ -640,6 +643,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of record_special_symbol, has predicate */
   /* Skip verify of has_global_solist, invalid_p == 0 */
   /* Skip verify of has_global_breakpoints, invalid_p == 0 */
+  /* Skip verify of has_shared_address_space, invalid_p == 0 */
   buf = ui_file_xstrdup (log, &dummy);
   make_cleanup (xfree, buf);
   if (strlen (buf) > 0)
@@ -873,6 +877,9 @@ gdbarch_dump (struct gdbarch *gdbarch, s
                       "gdbarch_dump: has_global_solist = %s\n",
                       plongest (gdbarch->has_global_solist));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: has_shared_address_space = <%s>\n",
+                      host_address_to_string (gdbarch->has_shared_address_space));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: have_nonsteppable_watchpoint = %s\n",
                       plongest (gdbarch->have_nonsteppable_watchpoint));
   fprintf_unfiltered (file,
@@ -3415,6 +3422,23 @@ set_gdbarch_has_global_breakpoints (stru
   gdbarch->has_global_breakpoints = has_global_breakpoints;
 }
 
+int
+gdbarch_has_shared_address_space (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->has_shared_address_space != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_has_shared_address_space called\n");
+  return gdbarch->has_shared_address_space (gdbarch);
+}
+
+void
+set_gdbarch_has_shared_address_space (struct gdbarch *gdbarch,
+                                      gdbarch_has_shared_address_space_ftype has_shared_address_space)
+{
+  gdbarch->has_shared_address_space = has_shared_address_space;
+}
+
 
 /* Keep a registry of per-architecture data-pointers required by GDB
    modules. */
Index: src/gdb/linux-tdep.c
===================================================================
--- src.orig/gdb/linux-tdep.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/linux-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -20,6 +20,9 @@
 #include "defs.h"
 #include "gdbtypes.h"
 #include "linux-tdep.h"
+#include "auxv.h"
+#include "target.h"
+#include "elf/common.h"
 
 /* This function is suitable for architectures that don't
    extend/override the standard siginfo structure.  */
@@ -137,3 +140,18 @@ linux_get_siginfo_type (struct gdbarch *
 
   return siginfo_type;
 }
+
+int
+linux_has_shared_address_space (void)
+{
+  /* Determine whether we are running on uClinux or normal Linux
+     kernel.  */
+  CORE_ADDR dummy;
+  int target_is_uclinux;
+
+  target_is_uclinux
+    = (target_auxv_search (&current_target, AT_NULL, &dummy) > 0
+       && target_auxv_search (&current_target, AT_PAGESZ, &dummy) == 0);
+
+  return target_is_uclinux;
+}
Index: src/gdb/arm-tdep.c
===================================================================
--- src.orig/gdb/arm-tdep.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/arm-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -2171,7 +2171,8 @@ arm_software_single_step (struct frame_i
      CPSR heuristics are used.  */
 
   CORE_ADDR next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
-  insert_single_step_breakpoint (next_pc);
+  insert_single_step_breakpoint (get_frame_address_space (frame),
+				 next_pc);
 
   return 1;
 }
Index: src/gdb/arm-linux-tdep.c
===================================================================
--- src.orig/gdb/arm-linux-tdep.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/arm-linux-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -585,7 +585,7 @@ arm_linux_software_single_step (struct f
   if (next_pc > 0xffff0000)
     next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
 
-  insert_single_step_breakpoint (next_pc);
+  insert_single_step_breakpoint (get_frame_address_space (frame), next_pc);
 
   return 1;
 }
Index: src/gdb/cris-tdep.c
===================================================================
--- src.orig/gdb/cris-tdep.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/cris-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -470,7 +470,7 @@ crisv32_single_step_through_delay (struc
     {
       /* In delay slot - check if there's a breakpoint at the preceding
 	 instruction.  */
-      if (breakpoint_here_p (erp & ~0x1))
+      if (breakpoint_here_p (get_frame_address_space (this_frame), erp & ~0x1))
 	ret = 1;
     }
   return ret;
@@ -2137,13 +2137,14 @@ cris_software_single_step (struct frame_
          and possibly another one for a branch, jump, etc.  */
       CORE_ADDR next_pc =
 	(CORE_ADDR) inst_env.reg[gdbarch_pc_regnum (get_frame_arch (frame))];
-      insert_single_step_breakpoint (next_pc);
+      insert_single_step_breakpoint (get_frame_address_space (frame), next_pc);
       if (inst_env.branch_found 
 	  && (CORE_ADDR) inst_env.branch_break_address != next_pc)
 	{
 	  CORE_ADDR branch_target_address
 		= (CORE_ADDR) inst_env.branch_break_address;
-	  insert_single_step_breakpoint (branch_target_address);
+	  insert_single_step_breakpoint (get_frame_address_space (frame),
+					 branch_target_address);
 	}
     }
 
Index: src/gdb/mips-tdep.c
===================================================================
--- src.orig/gdb/mips-tdep.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/mips-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -2380,7 +2380,7 @@ mips_addr_bits_remove (struct gdbarch *g
    the sequence.  */
 
 static int
-deal_with_atomic_sequence (CORE_ADDR pc)
+deal_with_atomic_sequence (struct frame_info *frame, CORE_ADDR pc)
 {
   CORE_ADDR breaks[2] = {-1, -1};
   CORE_ADDR loc = pc;
@@ -2468,7 +2468,8 @@ deal_with_atomic_sequence (CORE_ADDR pc)
 
   /* Effectively inserts the breakpoints.  */
   for (index = 0; index <= last_breakpoint; index++)
-      insert_single_step_breakpoint (breaks[index]);
+    insert_single_step_breakpoint (get_frame_address_space (frame),
+				   breaks[index]);
 
   return 1;
 }
@@ -2484,12 +2485,13 @@ mips_software_single_step (struct frame_
   CORE_ADDR pc, next_pc;
 
   pc = get_frame_pc (frame);
-  if (deal_with_atomic_sequence (pc))
+  if (deal_with_atomic_sequence (frame, pc))
     return 1;
 
   next_pc = mips_next_pc (frame, pc);
 
-  insert_single_step_breakpoint (next_pc);
+  insert_single_step_breakpoint (get_frame_address_space (frame),
+				 next_pc);
   return 1;
 }
 
@@ -4665,7 +4667,7 @@ mips_single_step_through_delay (struct g
   if (mips_pc_is_mips16 (pc))
     return 0;
 
-  if (!breakpoint_here_p (pc + 4))
+  if (!breakpoint_here_p (get_frame_address_space (frame), pc + 4))
     return 0;
 
   if (!safe_frame_unwind_memory (frame, pc, buf, sizeof buf))
Index: src/gdb/rs6000-aix-tdep.c
===================================================================
--- src.orig/gdb/rs6000-aix-tdep.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/rs6000-aix-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -688,7 +688,8 @@ rs6000_software_single_step (struct fram
       /* ignore invalid breakpoint. */
       if (breaks[ii] == -1)
 	continue;
-      insert_single_step_breakpoint (breaks[ii]);
+      insert_single_step_breakpoint (get_frame_address_space (frame),
+				     breaks[ii]);
     }
 
   errno = 0;			/* FIXME, don't ignore errors! */
Index: src/gdb/rs6000-tdep.c
===================================================================
--- src.orig/gdb/rs6000-tdep.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/rs6000-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -1143,7 +1143,8 @@ ppc_deal_with_atomic_sequence (struct fr
 
   /* Effectively inserts the breakpoints.  */
   for (index = 0; index <= last_breakpoint; index++)
-    insert_single_step_breakpoint (breaks[index]);
+    insert_single_step_breakpoint (get_frame_address_space (frame),
+				   breaks[index]);
 
   return 1;
 }
Index: src/gdb/solib-irix.c
===================================================================
--- src.orig/gdb/solib-irix.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/solib-irix.c	2009-07-01 19:17:15.000000000 +0100
@@ -357,8 +357,11 @@ enable_break (void)
 {
   if (symfile_objfile != NULL)
     {
+      struct frame_info *frame = get_current_frame ();
+
       base_breakpoint
-	= deprecated_insert_raw_breakpoint (entry_point_address ());
+	= deprecated_insert_raw_breakpoint (get_frame_address_space (frame),
+					    entry_point_address ());
 
       if (base_breakpoint != NULL)
 	return 1;
Index: src/gdb/sparc-tdep.c
===================================================================
--- src.orig/gdb/sparc-tdep.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/sparc-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -1297,10 +1297,12 @@ sparc_software_single_step (struct frame
   /* Analyze the instruction at PC.  */
   nnpc = sparc_analyze_control_transfer (frame, pc, &npc);
   if (npc != 0)
-    insert_single_step_breakpoint (npc);
+    insert_single_step_breakpoint (get_frame_address_space (frame),
+				   npc);
 
   if (nnpc != 0)
-    insert_single_step_breakpoint (nnpc);
+    insert_single_step_breakpoint (get_frame_address_space (frame),
+				   nnpc);
 
   /* Assert that we have set at least one breakpoint, and that
      they're not set at the same spot - unless we're going
Index: src/gdb/spu-tdep.c
===================================================================
--- src.orig/gdb/spu-tdep.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/spu-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -1307,7 +1307,8 @@ spu_software_single_step (struct frame_i
   else
     next_pc = (pc + 4) & (SPU_LS_SIZE - 1);
 
-  insert_single_step_breakpoint (next_pc);
+  insert_single_step_breakpoint (get_frame_address_space (frame),
+				 next_pc);
 
   if (is_branch (insn, &offset, &reg))
     {
@@ -1323,7 +1324,8 @@ spu_software_single_step (struct frame_i
 
       target = target & (SPU_LS_SIZE - 1);
       if (target != next_pc)
-	insert_single_step_breakpoint (target);
+	insert_single_step_breakpoint (get_frame_address_space (frame),
+				       target);
     }
 
   return 1;
Index: src/gdb/alpha-tdep.c
===================================================================
--- src.orig/gdb/alpha-tdep.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/alpha-tdep.c	2009-07-01 19:17:15.000000000 +0100
@@ -1482,7 +1482,8 @@ alpha_software_single_step (struct frame
   pc = get_frame_pc (frame);
   next_pc = alpha_next_pc (frame, pc);
 
-  insert_single_step_breakpoint (next_pc);
+  insert_single_step_breakpoint (get_frame_address_space (frame),
+				 next_pc);
   return 1;
 }
 
Index: src/gdb/record.c
===================================================================
--- src.orig/gdb/record.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/record.c	2009-07-01 19:17:15.000000000 +0100
@@ -608,15 +608,18 @@ record_wait (struct target_ops *ops,
 	      if (status->kind == TARGET_WAITKIND_STOPPED
 		  && status->value.sig == TARGET_SIGNAL_TRAP)
 		{
+		  struct regcache *regcache;
+
 		  /* Check if there is a breakpoint.  */
 		  registers_changed ();
-		  tmp_pc = regcache_read_pc (get_current_regcache ());
-		  if (breakpoint_inserted_here_p (tmp_pc))
+		  regcache = get_current_regcache ();
+		  tmp_pc = regcache_read_pc (regcache);
+		  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
+						  tmp_pc))
 		    {
 		      /* There is a breakpoint.  */
-		      CORE_ADDR decr_pc_after_break =
-			gdbarch_decr_pc_after_break
-			(get_regcache_arch (get_current_regcache ()));
+		      CORE_ADDR decr_pc_after_break
+			= gdbarch_decr_pc_after_break (get_regcache_arch (regcache));
 		      if (decr_pc_after_break)
 			{
 			  regcache_write_pc (get_thread_regcache (ret),
@@ -658,7 +661,8 @@ record_wait (struct target_ops *ops,
       if (execution_direction == EXEC_FORWARD)
 	{
 	  tmp_pc = regcache_read_pc (regcache);
-	  if (breakpoint_inserted_here_p (tmp_pc))
+	  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
+					  tmp_pc))
 	    {
 	      if (record_debug)
 		fprintf_unfiltered (gdb_stdlog,
@@ -783,7 +787,8 @@ record_wait (struct target_ops *ops,
 
 		  /* check breakpoint */
 		  tmp_pc = regcache_read_pc (regcache);
-		  if (breakpoint_inserted_here_p (tmp_pc))
+		  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
+						  tmp_pc))
 		    {
 		      if (record_debug)
 			fprintf_unfiltered (gdb_stdlog,
Index: src/gdb/fork-child.c
===================================================================
--- src.orig/gdb/fork-child.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/fork-child.c	2009-07-01 19:17:15.000000000 +0100
@@ -138,6 +138,7 @@ fork_inferior (char *exec_file_arg, char
   int shell = 0;
   static char **argv;
   const char *inferior_io_terminal = get_inferior_io_terminal ();
+  struct inferior *inf;
 
   /* If no exec file handed to us, get it from the exec-file command
      -- with a good, common error message if none is specified.  */
@@ -395,7 +396,10 @@ fork_inferior (char *exec_file_arg, char
   if (!have_inferiors ())
     init_thread_list ();
 
-  add_inferior (pid);
+  inf = add_inferior (pid);
+
+  inf->aspace = current_symbol_space->aspace;
+  inf->sspace = current_symbol_space;
 
   /* Needed for wait_for_inferior stuff below.  */
   inferior_ptid = pid_to_ptid (pid);
Index: src/gdb/inf-ptrace.c
===================================================================
--- src.orig/gdb/inf-ptrace.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/inf-ptrace.c	2009-07-01 19:17:15.000000000 +0100
@@ -67,6 +67,8 @@ inf_ptrace_follow_fork (struct target_op
       child_inf = add_inferior (fpid);
       child_inf->attach_flag = parent_inf->attach_flag;
       copy_terminal_info (child_inf, parent_inf);
+      inf->sspace = parent_inf->sspace;
+      inf->aspace = parent_inf->aspace;
 
       /* Before detaching from the parent, remove all breakpoints from
 	 it.  */
@@ -226,6 +228,8 @@ inf_ptrace_attach (struct target_ops *op
   inferior_ptid = pid_to_ptid (pid);
 
   inf = add_inferior (pid);
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
   inf->attach_flag = 1;
 
   /* Always add a main thread.  If some target extends the ptrace
Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c	2009-07-01 19:17:08.000000000 +0100
+++ src/gdb/linux-nat.c	2009-07-01 19:17:15.000000000 +0100
@@ -54,6 +54,7 @@
 #include "gdb_dirent.h"
 #include "xml-support.h"
 #include "terminal.h"
+#include "solib.h"
 
 #ifdef HAVE_PERSONALITY
 # include <sys/personality.h>
@@ -596,24 +597,44 @@ linux_child_follow_fork (struct target_o
   if (!detach_fork)
     linux_enable_event_reporting (pid_to_ptid (child_pid));
 
+  if (has_vforked
+      && (!target_is_async_p () || sync_execution)
+      && !(follow_child || detach_fork || sched_multi))
+    {
+      /* The parent stays blocked inside the vfork syscall until the
+	 child execs or exits.  If we don't let the child run, then
+	 the parent stays blocked.  If we're telling the parent to run
+	 in the foreground, the user will not be able to ctrl-c to get
+	 back the terminal, effectively hanging the debug session.  */
+      fprintf_filtered (gdb_stderr, _("\
+Can not resume the parent process over vfork in the foreground while \n\
+holding the child stopped.  Try \"set detach-on-fork\" or \
+\"set schedule-multiple\".\n"));
+      return 1;
+    }
+
   if (! follow_child)
     {
-      /* We're already attached to the parent, by default. */
+      struct lwp_info *child_lp = NULL;
 
-      /* Before detaching from the child, remove all breakpoints from
-         it.  If we forked, then this has already been taken care of
-         by infrun.c.  If we vforked however, any breakpoint inserted
-         in the parent is visible in the child, even those added while
-         stopped in a vfork catchpoint.  This won't actually modify
-         the breakpoint list, but will physically remove the
-         breakpoints from the child.  This will remove the breakpoints
-         from the parent also, but they'll be reinserted below.  */
-      if (has_vforked)
-	detach_breakpoints (child_pid);
+      /* We're already attached to the parent, by default. */
 
       /* Detach new forked process?  */
       if (detach_fork)
 	{
+	  /* Before detaching from the child, remove all breakpoints
+	     from it.  If we forked, then this has already been taken
+	     care of by infrun.c.  If we vforked however, any
+	     breakpoint inserted in the parent is visible in the
+	     child, even those added while stopped in a vfork
+	     catchpoint.  This will remove the breakpoints from the
+	     parent also, but they'll be reinserted below.  */
+	  if (has_vforked)
+	    {
+	      /* keep breakpoints list in sync.  */
+	      remove_breakpoints_pid (GET_PID (inferior_ptid));
+	    }
+
 	  if (info_verbose || debug_linux_nat)
 	    {
 	      target_terminal_ours ();
@@ -627,7 +648,6 @@ linux_child_follow_fork (struct target_o
       else
 	{
 	  struct inferior *parent_inf, *child_inf;
-	  struct lwp_info *lp;
 	  struct cleanup *old_chain;
 
 	  /* Add process to GDB's tables.  */
@@ -638,12 +658,47 @@ linux_child_follow_fork (struct target_o
 	  copy_terminal_info (child_inf, parent_inf);
 
 	  old_chain = save_inferior_ptid ();
+	  save_current_symbol_space ();
 
 	  inferior_ptid = ptid_build (child_pid, child_pid, 0);
 	  add_thread (inferior_ptid);
-	  lp = add_lwp (inferior_ptid);
-	  lp->stopped = 1;
+	  child_lp = add_lwp (inferior_ptid);
+	  child_lp->stopped = 1;
+	  child_lp->resumed = 1;
+
+	  /* If this is a vfork child, then the address-space is
+	     shared with the parent.  */
+	  if (has_vforked)
+	    {
+	      child_inf->sspace = parent_inf->sspace;
+	      child_inf->aspace = parent_inf->aspace;
 
+	      /* The parent will be frozen until the child is done
+		 with the shared region.  Keep track of the
+		 parent.  */
+	      child_inf->vfork_parent = parent_inf;
+	      child_inf->pending_detach = 0;
+	      parent_inf->vfork_child = child_inf;
+	      parent_inf->pending_detach = 0;
+	    }
+	  else
+	    {
+	      child_inf->aspace = new_address_space ();
+	      child_inf->sspace = add_symbol_space (child_inf->aspace);
+	      child_inf->sspace->removable = 1;
+	      set_current_symbol_space (child_inf->sspace);
+	      clone_symbol_space (child_inf->sspace, parent_inf->sspace);
+
+	      /* Let the shared library layer (solib-svr4) learn about
+		 this new process, relocate the cloned exec, pull in
+		 shared libraries, and install the solib event
+		 breakpoint.  If a "cloned-VM" event was propagated
+		 better throughout the core, this wouldn't be
+		 required.  */
+	      solib_create_inferior_hook ();
+	    }
+
+	  /* Let the thread_db layer learn about this new process.  */
 	  check_for_thread_db ();
 
 	  do_cleanups (old_chain);
@@ -651,16 +706,34 @@ linux_child_follow_fork (struct target_o
 
       if (has_vforked)
 	{
+	  struct lwp_info *lp;
+	  struct inferior *parent_inf;
+
+	  parent_inf = current_inferior ();
+
+	  /* If we detached from the child, then we have to be careful
+	     to not insert breakpoints in the parent until the child
+	     is done with the shared memory region.  However, if we're
+	     staying attached to the child, then we can and should
+	     insert breakpoints, so that we can debug it.  A
+	     subsequent child exec or exit is enough to know when does
+	     the child stops using the parent's address space.  */
+	  parent_inf->waiting_for_vfork_done = detach_fork;
+
+	  lp = find_lwp_pid (pid_to_ptid (parent_pid));
 	  gdb_assert (linux_supports_tracefork_flag >= 0);
 	  if (linux_supports_tracevforkdone (0))
 	    {
-	      int status;
+  	      if (debug_linux_nat)
+  		fprintf_unfiltered (gdb_stdlog,
+  				    "LCFF: waiting for VFORK_DONE on %d\n",
+  				    parent_pid);
 
-	      ptrace (PTRACE_CONT, parent_pid, 0, 0);
-	      my_waitpid (parent_pid, &status, __WALL);
-	      if ((status >> 16) != PTRACE_EVENT_VFORK_DONE)
-		warning (_("Unexpected waitpid result %06x when waiting for "
-			 "vfork-done"), status);
+	      lp->stopped = 1;
+	      lp->resumed = 1;
+
+	      /* We'll handle the VFORK_DONE event like any other
+		 event, in target_wait.  */
 	    }
 	  else
 	    {
@@ -695,12 +768,26 @@ linux_child_follow_fork (struct target_o
 		 is only the single-step breakpoint at vfork's return
 		 point.  */
 
+  	      if (debug_linux_nat)
+  		fprintf_unfiltered (gdb_stdlog,
+  				    "LCFF: no VFORK_DONE support, sleeping a bit\n");
+
 	      usleep (10000);
-	    }
 
-	  /* Since we vforked, breakpoints were removed in the parent
-	     too.  Put them back.  */
-	  reattach_breakpoints (parent_pid);
+	      /* Pretend we've seen a PTRACE_EVENT_VFORK_DONE event,
+		 and leave it pending.  The next linux_nat_resume call
+		 will notice a pending event, and bypasses actually
+		 resuming the inferior.  */
+	      lp->status = 0;
+	      lp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+	      lp->stopped = 0;
+	      lp->resumed = 1;
+
+	      /* If we're in async mode, need to tell the event loop
+		 there's something here to process.  */
+	      if (target_can_async_p ())
+		async_file_mark ();
+	    }
 	}
     }
   else
@@ -708,16 +795,19 @@ linux_child_follow_fork (struct target_o
       struct thread_info *tp;
       struct inferior *parent_inf, *child_inf;
       struct lwp_info *lp;
-
-      /* Before detaching from the parent, remove all breakpoints from it. */
-      remove_breakpoints ();
+      struct symbol_space *parent_sspace;
 
       if (info_verbose || debug_linux_nat)
 	{
 	  target_terminal_ours ();
-	  fprintf_filtered (gdb_stdlog,
-			    "Attaching after fork to child process %d.\n",
-			    child_pid);
+	  if (has_vforked)
+	    fprintf_filtered (gdb_stdlog, _("\
+Attaching after process %d vfork to child process %d.\n"),
+			      parent_pid, child_pid);
+	  else
+	    fprintf_filtered (gdb_stdlog, _("\
+Attaching after process %d fork to child process %d.\n"),
+			      parent_pid, child_pid);
 	}
 
       /* Add the new inferior first, so that the target_detach below
@@ -729,53 +819,68 @@ linux_child_follow_fork (struct target_o
       child_inf->attach_flag = parent_inf->attach_flag;
       copy_terminal_info (child_inf, parent_inf);
 
-      /* If we're vforking, we may want to hold on to the parent until
-	 the child exits or execs.  At exec time we can remove the old
-	 breakpoints from the parent and detach it; at exit time we
-	 could do the same (or even, sneakily, resume debugging it - the
-	 child's exec has failed, or something similar).
-
-	 This doesn't clean up "properly", because we can't call
-	 target_detach, but that's OK; if the current target is "child",
-	 then it doesn't need any further cleanups, and lin_lwp will
-	 generally not encounter vfork (vfork is defined to fork
-	 in libpthread.so).
-
-	 The holding part is very easy if we have VFORKDONE events;
-	 but keeping track of both processes is beyond GDB at the
-	 moment.  So we don't expose the parent to the rest of GDB.
-	 Instead we quietly hold onto it until such time as we can
-	 safely resume it.  */
+      parent_sspace = parent_inf->sspace;
+
+      /* If we're vforking, we want to hold on to the parent until the
+	 child exits or execs.  At child exec or exit time we can
+	 remove the old breakpoints from the parent and detach or
+	 resume it.  Otherwise, detach the parent now; we'll want to
+	 reuse it's symbol/address spaces, but we can't set them to
+	 the child before removing breakpoints from the parent,
+	 otherwise, the breakpoints module could decide to remove
+	 breakpoints from the wrong process (since they'd be assigned
+	 to the same address space).  */
 
       if (has_vforked)
 	{
-	  struct lwp_info *parent_lwp;
-
-	  linux_parent_pid = parent_pid;
-
-	  /* Get rid of the inferior on the core side as well.  */
-	  inferior_ptid = null_ptid;
-	  detach_inferior (parent_pid);
-
-	  /* Also get rid of all its lwps.  We will detach from this
-	     inferior soon-ish, but, we will still get an exit event
-	     reported through waitpid when it exits.  If we didn't get
-	     rid of the lwps from our list, we would end up reporting
-	     the inferior exit to the core, which would then try to
-	     mourn a non-existing (from the core's perspective)
-	     inferior.  */
-	  parent_lwp = find_lwp_pid (pid_to_ptid (parent_pid));
-	  purge_lwp_list (GET_PID (parent_lwp->ptid));
-	  linux_parent_pid = parent_pid;
+	  gdb_assert (child_inf->vfork_parent == NULL);
+	  gdb_assert (parent_inf->vfork_child == NULL);
+	  child_inf->vfork_parent = parent_inf;
+	  child_inf->pending_detach = 0;
+	  parent_inf->vfork_child = child_inf;
+	  parent_inf->pending_detach = detach_fork;
+	  parent_inf->waiting_for_vfork_done = 0;
 	}
       else if (detach_fork)
 	target_detach (NULL, 0);
 
+      /* Note that the detach above makes PARENT_INF dangling.  */
+
+      /* Add the child thread to the appropriate lists, and switch to
+	 this new thread, before cloning the symbol space, and
+	 informing the solib layer about this new process.  */
+
       inferior_ptid = ptid_build (child_pid, child_pid, 0);
       add_thread (inferior_ptid);
       lp = add_lwp (inferior_ptid);
       lp->stopped = 1;
+      lp->resumed = 1;
+
+      /* If this is a vfork child, then the address-space is shared
+	 with the parent.  If we detached from the parent, then we can
+	 reuse the parent's symbol/address spaces.  */
+      if (has_vforked || detach_fork)
+	{
+	  child_inf->sspace = parent_sspace;
+	  child_inf->aspace = child_inf->sspace->aspace;
+	}
+      else
+	{
+	  child_inf->aspace = new_address_space ();
+	  child_inf->sspace = add_symbol_space (child_inf->aspace);
+	  child_inf->sspace->removable = 1;
+	  set_current_symbol_space (child_inf->sspace);
+	  clone_symbol_space (child_inf->sspace, parent_sspace);
+
+	  /* Let the shared library layer (solib-svr4) learn about
+	     this new process, relocate the cloned exec, pull in
+	     shared libraries, and install the solib event breakpoint.
+	     If a "cloned-VM" event was propagated better throughout
+	     the core, this wouldn't be required.  */
+	  solib_create_inferior_hook ();
+	}
 
+      /* Let the thread_db layer learn about this new process.  */
       check_for_thread_db ();
     }
 
@@ -1607,7 +1712,16 @@ linux_nat_detach (struct target_ops *ops
 static int
 resume_callback (struct lwp_info *lp, void *data)
 {
-  if (lp->stopped && lp->status == 0)
+  struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
+
+  if (lp->stopped && inf->vfork_child != NULL)
+    {
+      if (debug_linux_nat)
+	fprintf_unfiltered (gdb_stdlog,
+			    "RC: Not resuming %s (vfork parent)\n",
+			    target_pid_to_str (lp->ptid));
+    }
+  else if (lp->stopped && lp->status == 0)
     {
       if (debug_linux_nat)
 	fprintf_unfiltered (gdb_stdlog,
@@ -1728,7 +1842,7 @@ linux_nat_resume (struct target_ops *ops
 	}
     }
 
-  if (lp->status)
+  if (lp->status || lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
     {
       /* FIXME: What should we do if we are supposed to continue
 	 this thread with a signal?  */
@@ -1956,25 +2070,28 @@ linux_handle_extended_wait (struct lwp_i
       ourstatus->value.execd_pathname
 	= xstrdup (linux_child_pid_to_exec_file (pid));
 
-      if (linux_parent_pid)
+      return 0;
+    }
+
+  if (event == PTRACE_EVENT_VFORK_DONE)
+    {
+      if (current_inferior ()->waiting_for_vfork_done)
 	{
-	  detach_breakpoints (linux_parent_pid);
-	  ptrace (PTRACE_DETACH, linux_parent_pid, 0, 0);
+	  if (debug_linux_nat)
+	    fprintf_unfiltered (gdb_stdlog, "\
+LHEW: Got expected PTRACE_EVENT_VFORK_DONE from LWP %ld: stopping\n",
+				GET_LWP (lp->ptid));
 
-	  linux_parent_pid = 0;
+	  ourstatus->kind = TARGET_WAITKIND_VFORK_DONE;
+	  return 0;
 	}
 
-      /* At this point, all inserted breakpoints are gone.  Doing this
-	 as soon as we detect an exec prevents the badness of deleting
-	 a breakpoint writing the current "shadow contents" to lift
-	 the bp.  That shadow is NOT valid after an exec.
-
-	 Note that we have to do this after the detach_breakpoints
-	 call above, otherwise breakpoints wouldn't be lifted from the
-	 parent on a vfork, because detach_breakpoints would think
-	 that breakpoints are not inserted.  */
-      mark_breakpoints_out ();
-      return 0;
+      if (debug_linux_nat)
+	fprintf_unfiltered (gdb_stdlog, "\
+LHEW: Got PTRACE_EVENT_VFORK_DONE from LWP %ld: resuming\n",
+			    GET_LWP (lp->ptid));
+      ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
+      return 1;
     }
 
   internal_error (__FILE__, __LINE__,
@@ -2164,6 +2281,13 @@ maybe_clear_ignore_sigint (struct lwp_in
 static int
 stop_wait_callback (struct lwp_info *lp, void *data)
 {
+  struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
+
+  /* If this is a vfork parent, bail out, it is not going to report
+     any SIGSTOP until the vfork is done with.  */
+  if (inf->vfork_child != NULL)
+    return 0;
+
   if (!lp->stopped)
     {
       int status;
@@ -2387,7 +2511,7 @@ cancel_breakpoint (struct lwp_info *lp)
   CORE_ADDR pc;
 
   pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
-  if (breakpoint_inserted_here_p (pc))
+  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
     {
       if (debug_linux_nat)
 	fprintf_unfiltered (gdb_stdlog,
Index: src/gdb/linux-thread-db.c
===================================================================
--- src.orig/gdb/linux-thread-db.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/linux-thread-db.c	2009-07-01 19:17:15.000000000 +0100
@@ -1201,18 +1201,15 @@ thread_db_wait (struct target_ops *ops,
 
   if (ourstatus->kind == TARGET_WAITKIND_EXECD)
     {
-      /* Breakpoints have already been marked non-inserted by the
-	 layer below.  We're safe in knowing that removing them will
-	 not write the shadows of the old image into the new
-	 image.  */
-      remove_thread_event_breakpoints ();
-
       /* New image, it may or may not end up using thread_db.  Assume
 	 not unless we find otherwise.  */
       delete_thread_db_info (GET_PID (ptid));
       if (!thread_db_list)
  	unpush_target (&thread_db_ops);
 
+      /* Thread event breakpoints are deleted by
+	 update_breakpoints_after_exec.  */
+
       return ptid;
     }
 
@@ -1249,13 +1246,12 @@ thread_db_mourn_inferior (struct target_
 
   delete_thread_db_info (GET_PID (inferior_ptid));
 
-  /* Delete the old thread event breakpoints.  Mark breakpoints out,
-     so that we don't try to un-insert them.  */
-  mark_breakpoints_out ();
-  remove_thread_event_breakpoints ();
-
   target_beneath->to_mourn_inferior (target_beneath);
 
+  /* Delete the old thread event breakpoints.  Do this after mourning
+     the inferior, so that we don't try to uninsert them.  */
+  remove_thread_event_breakpoints ();
+
   /* Detach thread_db target ops.  */
   if (!thread_db_list)
     unpush_target (ops);
Index: src/gdb/corelow.c
===================================================================
--- src.orig/gdb/corelow.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/corelow.c	2009-07-01 19:17:15.000000000 +0100
@@ -45,6 +45,7 @@
 #include "exceptions.h"
 #include "solib.h"
 #include "filenames.h"
+#include "symspace.h"
 
 
 #ifndef O_LARGEFILE
@@ -286,6 +287,7 @@ core_open (char *filename, int from_tty)
   int scratch_chan;
   int flags;
   int corelow_pid = CORELOW_PID;
+  struct inferior *inf;
 
   target_preopen (from_tty);
   if (!filename)
@@ -371,7 +373,9 @@ core_open (char *filename, int from_tty)
   push_target (&core_ops);
   discard_cleanups (old_chain);
 
-  add_inferior_silent (corelow_pid);
+  inf = add_inferior_silent (corelow_pid);
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
 
   /* Do this before acknowledging the inferior, so if
      post_create_inferior throws (can happen easilly if you're loading
Index: src/gdb/remote.c
===================================================================
--- src.orig/gdb/remote.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/remote.c	2009-07-01 19:17:15.000000000 +0100
@@ -1179,6 +1179,23 @@ remote_add_inferior (int pid, int attach
 
   inf->attach_flag = attached;
 
+  inf->sspace = current_symbol_space;
+
+  if (gdbarch_has_global_solist (target_gdbarch))
+    {
+      /* If the target shares code across all inferiors, then every
+	 inferior will use the same symbol space.  However, each
+	 inferior may still have its own address space.  */
+      inf->aspace = maybe_new_address_space ();
+    }
+  else
+    {
+      /* In the traditional debugging scenario, there's a 1-1 match
+	 between symbol/address spaces.  We simply bind the inferior
+	 to the symbol space's address space.  */
+      inf->aspace = inf->sspace->aspace;
+    }
+
   return inf;
 }
 
@@ -2638,6 +2655,10 @@ remote_start_remote (struct ui_out *uiou
      this before anything involving memory or registers.  */
   target_find_description ();
 
+  /* Next, now that we know something about the target, update the
+     address spaces in the symbol spaces.  */
+  update_address_spaces ();
+
   /* On OSs where the list of libraries is global to all
      processes, we fetch them early.  */
   if (gdbarch_has_global_solist (target_gdbarch))
@@ -6759,11 +6780,14 @@ extended_remote_create_inferior_1 (char 
       extended_remote_restart ();
     }
 
-  /* Clean up from the last time we ran, before we mark the target
-     running again.  This will mark breakpoints uninserted, and
-     get_offsets may insert breakpoints.  */
-  init_thread_list ();
-  init_wait_for_inferior ();
+  if (!have_inferiors ())
+    {
+      /* Clean up from the last time we ran, before we mark the target
+	 running again.  This will mark breakpoints uninserted, and
+	 get_offsets may insert breakpoints.  */
+      init_thread_list ();
+      init_wait_for_inferior ();
+    }
 
   /* Now mark the inferior as running before we do anything else.  */
   inferior_ptid = magic_null_ptid;
Index: src/gdb/darwin-nat.c
===================================================================
--- src.orig/gdb/darwin-nat.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/darwin-nat.c	2009-07-01 19:17:15.000000000 +0100
@@ -1517,6 +1517,8 @@ darwin_attach (struct target_ops *ops, c
   inferior_ptid = pid_to_ptid (pid);
   inf = add_inferior (pid);
   inf->attach_flag = 1;
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
   /* Always add a main thread.  */
   add_thread_silent (inferior_ptid);
 
Index: src/gdb/gnu-nat.c
===================================================================
--- src.orig/gdb/gnu-nat.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/gnu-nat.c	2009-07-01 19:17:15.000000000 +0100
@@ -2179,6 +2179,8 @@ gnu_attach (struct target_ops *ops, char
 
   inferior = add_inferior (pid);
   inferior->attach_flag = 1;
+  inferior->sspace = current_symbol_space;
+  inferior->aspace = inferior->sspace->aspace;
 
   inf_update_procs (inf);
 
Index: src/gdb/go32-nat.c
===================================================================
--- src.orig/gdb/go32-nat.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/go32-nat.c	2009-07-01 19:17:15.000000000 +0100
@@ -649,6 +649,7 @@ go32_create_inferior (struct target_ops 
   char *cmdline;
   char **env_save = environ;
   size_t cmdlen;
+  struct inferior *inf;
 
   /* If no exec file handed to us, get it from the exec-file command -- with
      a good, common error message if none is specified.  */
@@ -714,7 +715,9 @@ go32_create_inferior (struct target_ops 
 #endif
 
   inferior_ptid = pid_to_ptid (SOME_PID);
-  add_inferior_silent (SOME_PID);
+  inf = add_inferior_silent (SOME_PID);
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
 
   push_target (&go32_ops);
 
Index: src/gdb/inf-ttrace.c
===================================================================
--- src.orig/gdb/inf-ttrace.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/inf-ttrace.c	2009-07-01 19:17:15.000000000 +0100
@@ -453,6 +453,8 @@ inf_ttrace_follow_fork (struct target_op
       inferior_ptid = ptid_build (fpid, flwpid, 0);
       inf = add_inferior (fpid);
       inf->attach_flag = parent_inf->attach_flag;
+      inf->sspace = parent_inf->sspace;
+      inf->aspace = parent_inf->aspace;
       copy_terminal_info (inf, parent_inf);
       detach_breakpoints (pid);
 
@@ -727,6 +729,8 @@ inf_ttrace_attach (struct target_ops *op
 
   inf = add_inferior (pid);
   inf->attach_flag = 1;
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
 
   /* Set the initial event mask.  */
   memset (&tte, 0, sizeof (tte));
Index: src/gdb/monitor.c
===================================================================
--- src.orig/gdb/monitor.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/monitor.c	2009-07-01 19:17:15.000000000 +0100
@@ -817,7 +817,9 @@ monitor_open (char *args, struct monitor
 
   /* Make run command think we are busy...  */
   inferior_ptid = monitor_ptid;
-  add_inferior_silent (ptid_get_pid (inferior_ptid));
+  inf = add_inferior_silent (ptid_get_pid (inferior_ptid));
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
   add_thread_silent (inferior_ptid);
 
   /* Give monitor_wait something to read */
Index: src/gdb/nto-procfs.c
===================================================================
--- src.orig/gdb/nto-procfs.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/nto-procfs.c	2009-07-01 19:17:15.000000000 +0100
@@ -544,6 +544,8 @@ procfs_attach (struct target_ops *ops, c
   inferior_ptid = do_attach (pid_to_ptid (pid));
   inf = add_inferior (pid);
   inf->attach_flag = 1;
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
 
   push_target (ops);
 
@@ -1096,6 +1098,8 @@ procfs_create_inferior (struct target_op
 
   inf = add_inferior (pid);
   inf->attach_flag = 0;
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
 
   flags = _DEBUG_FLAG_KLC;	/* Kill-on-Last-Close flag.  */
   errn = devctl (ctl_fd, DCMD_PROC_SET_FLAG, &flags, sizeof (flags), 0);
Index: src/gdb/procfs.c
===================================================================
--- src.orig/gdb/procfs.c	2009-07-01 19:15:25.000000000 +0100
+++ src/gdb/procfs.c	2009-07-01 19:17:15.000000000 +0100
@@ -3707,6 +3707,8 @@ do_attach (ptid_t ptid)
   inf = add_inferior (pi->pid);
   /* Let GDB know that the inferior was attached.  */
   inf->attach_flag = 1;
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
 
   /* Create a procinfo for the current lwp.  */
   lwpid = proc_get_current_thread (pi);
Index: src/gdb/windows-nat.c
===================================================================
--- src.orig/gdb/windows-nat.c	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/windows-nat.c	2009-07-01 19:17:15.000000000 +0100
@@ -1587,6 +1587,8 @@ do_initial_windows_stuff (struct target_
 
   inf = add_inferior (pid);
   inf->attach_flag = attaching;
+  inf->sspace = current_symbol_space;
+  inf->aspace = inf->sspace->aspace;
 
   /* Make the new process the current inferior, so terminal handling
      can rely on it.  When attaching, we don't know about any thread
Index: src/gdb/testsuite/gdb.base/foll-vfork.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/foll-vfork.exp	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/testsuite/gdb.base/foll-vfork.exp	2009-07-01 19:17:15.000000000 +0100
@@ -93,10 +93,10 @@ proc check_vfork_catchpoints {} {
 proc vfork_parent_follow_through_step {} {
    global gdb_prompt
 
-   send_gdb "set follow parent\n"
+   send_gdb "set follow-fork parent\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow parent, vfork through step"}
-      timeout         {fail "set follow parent, vfork through step"}
+      -re "$gdb_prompt $" {pass "set follow-fork parent, vfork through step"}
+      timeout         {fail "set follow-fork parent, vfork through step"}
    }
    send_gdb "next\n"
    gdb_expect {
@@ -116,10 +116,10 @@ proc vfork_parent_follow_to_bp {} {
    global gdb_prompt
    global srcfile
 
-   send_gdb "set follow parent\n"
+   send_gdb "set follow-fork parent\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow parent, vfork to bp"}
-      timeout         {fail "set follow parent, vfork to bp"}
+      -re "$gdb_prompt $" {pass "set follow-fork parent, vfork to bp"}
+      timeout         {fail "set follow-fork parent, vfork to bp"}
    }
    send_gdb "break ${srcfile}:18\n"
    gdb_expect {
@@ -144,14 +144,14 @@ proc vfork_and_exec_child_follow_to_main
    global gdb_prompt
    global binfile
 
-   send_gdb "set follow child\n"
+   send_gdb "set follow-fork child\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow child, vfork and exec to main bp"}
-      timeout         {fail "set follow child, vfork and exec to main bp"}
+      -re "$gdb_prompt $" {pass "set follow-fork child, vfork and exec to main bp"}
+      timeout         {fail "set follow-fork child, vfork and exec to main bp"}
    }
    send_gdb "continue\n"
    gdb_expect {
-      -re "Attaching after fork to.*Executing new program.*Breakpoint.*vforked-prog.c:9.*$gdb_prompt "\
+      -re "Attaching after.* vfork to.*xecuting new program.*Breakpoint.*vforked-prog.c:9.*$gdb_prompt "\
                       {pass "vfork and exec child follow, to main bp"}
       -re "$gdb_prompt $" {fail "vfork and exec child follow, to main bp"}
       timeout         {fail "(timeout) vfork and exec child follow, to main bp" }
@@ -193,7 +193,7 @@ proc vfork_and_exec_child_follow_through
 # This test cannot be performed prior to HP-UX 10.30, because ptrace-based
 # debugging of a vforking program basically doesn't allow the child to do
 # things like hit a breakpoint between a vfork and exec.  This means that
-# saying "set follow child; next" at a vfork() call won't work, because
+# saying "set follow-fork child; next" at a vfork() call won't work, because
 # the implementation of "next" sets a "step resume" breakpoint at the
 # return from the vfork(), which the child will hit on its way to exec'ing.
 #
@@ -202,10 +202,10 @@ proc vfork_and_exec_child_follow_through
       return 0
    }
 
-   send_gdb "set follow child\n"
+   send_gdb "set follow-fork child\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow child, vfork and exec through step"}
-      timeout         {fail "set follow child, vfork and exec through step"}
+      -re "$gdb_prompt $" {pass "set follow-fork child, vfork and exec through step"}
+      timeout         {fail "set follow-fork child, vfork and exec through step"}
    }
    send_gdb "next\n"
    gdb_expect {
@@ -248,10 +248,10 @@ proc tcatch_vfork_then_parent_follow {} 
    global gdb_prompt
    global srcfile
 
-   send_gdb "set follow parent\n"
+   send_gdb "set follow-fork parent\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow parent, tcatch vfork"}
-      timeout         {fail "set follow parent, tcatch vfork"}
+      -re "$gdb_prompt $" {pass "set follow-fork parent, tcatch vfork"}
+      timeout         {fail "set follow-fork parent, tcatch vfork"}
    }
    send_gdb "tcatch vfork\n"
    gdb_expect {
@@ -294,10 +294,10 @@ proc tcatch_vfork_then_child_follow {} {
    global srcfile
    global srcfile2
 
-   send_gdb "set follow child\n"
+   send_gdb "set follow-fork child\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow child, tcatch vfork"}
-      timeout         {fail "set follow child, tcatch vfork"}
+      -re "$gdb_prompt $" {pass "set follow-fork child, tcatch vfork"}
+      timeout         {fail "set follow-fork child, tcatch vfork"}
    }
    send_gdb "tcatch vfork\n"
    gdb_expect {
Index: src/gdb/testsuite/gdb.base/foll-exec.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/foll-exec.exp	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/testsuite/gdb.base/foll-exec.exp	2009-07-01 19:17:15.000000000 +0100
@@ -152,7 +152,7 @@ proc do_exec_tests {} {
    #
    send_gdb "next\n"
    gdb_expect {
-     -re "Executing new program: .*${testfile2}.*${srcfile2}:23.*int  local_j = argc;.*$gdb_prompt $"\
+     -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:23.*int  local_j = argc;.*$gdb_prompt $"\
                      {pass "step through execlp call"}
      -re "$gdb_prompt $" {fail "step through execlp call"}
      timeout         {fail "(timeout) step through execlp call"}
@@ -230,7 +230,7 @@ proc do_exec_tests {} {
    setup_xfail hppa2.0w-hp-hpux* CLLbs16760
    send_gdb "continue\n"
    gdb_expect {
-     -re ".*Executing new program:.*${testfile2}.*Catchpoint .*(exec\'d .*${testfile2}).*in .*$gdb_prompt $"\
+     -re ".*xecuting new program:.*${testfile2}.*Catchpoint .*(exec\'d .*${testfile2}).*in .*$gdb_prompt $"\
                      {pass "hit catch exec"}
      -re "$gdb_prompt $" {fail "hit catch exec"}
      timeout         {fail "(timeout) hit catch exec"}
@@ -299,7 +299,7 @@ proc do_exec_tests {} {
    #
    send_gdb "next 2\n"
    gdb_expect {
-     -re "Executing new program: .*${testfile2}.*${srcfile2}:23.*int  local_j = argc;.*$gdb_prompt $"\
+     -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:23.*int  local_j = argc;.*$gdb_prompt $"\
                      {pass "step through execl call"}
      -re "$gdb_prompt $" {fail "step through execl call"}
      timeout         {fail "(timeout) step through execl call"}
@@ -353,7 +353,7 @@ proc do_exec_tests {} {
    }
    send_gdb "next\n"
    gdb_expect {
-     -re "Executing new program: .*${testfile2}.*${srcfile2}:23.*int  local_j = argc;.*$gdb_prompt $"\
+     -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:23.*int  local_j = argc;.*$gdb_prompt $"\
                      {pass "step through execv call"}
      -re "$gdb_prompt $" {fail "step through execv call"}
      timeout         {fail "(timeout) step through execv call"}
@@ -394,7 +394,7 @@ proc do_exec_tests {} {
    #
    send_gdb "continue\n"
    gdb_expect {
-     -re "Executing new program: .*${testfile2}.*${srcfile2}:23.*int  local_j = argc;.*$gdb_prompt $"\
+     -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:23.*int  local_j = argc;.*$gdb_prompt $"\
                      {pass "continue through exec"}
      -re "$gdb_prompt $" {fail "continue through exec"}
      timeout         {fail "(timeout) continue through exec"}
Index: src/gdb/testsuite/gdb.base/foll-fork.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/foll-fork.exp	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/testsuite/gdb.base/foll-fork.exp	2009-07-01 19:17:15.000000000 +0100
@@ -64,7 +64,7 @@ proc check_fork_catchpoints {} {
 proc default_fork_parent_follow {} {
    global gdb_prompt
 
-   send_gdb "show follow\n"
+   send_gdb "show follow-fork\n"
    gdb_expect {
       -re "Debugger response to a program call of fork or vfork is \"parent\"..*$gdb_prompt $"\
                       {pass "default show parent follow, no catchpoints"}
@@ -88,12 +88,12 @@ proc default_fork_parent_follow {} {
 proc explicit_fork_parent_follow {} {
    global gdb_prompt
 
-   send_gdb "set follow parent\n"
+   send_gdb "set follow-fork parent\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow parent"}
-      timeout         {fail "(timeout) set follow parent"}
+      -re "$gdb_prompt $" {pass "set follow-fork parent"}
+      timeout         {fail "(timeout) set follow-fork parent"}
    }
-   send_gdb "show follow\n"
+   send_gdb "show follow-fork\n"
    gdb_expect {
       -re "Debugger response to a program call of fork or vfork is \"parent\"..*$gdb_prompt $"\
                       {pass "explicit show parent follow, no catchpoints"}
@@ -117,12 +117,12 @@ proc explicit_fork_parent_follow {} {
 proc explicit_fork_child_follow {} {
    global gdb_prompt
 
-   send_gdb "set follow child\n"
+   send_gdb "set follow-fork child\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow child"}
-      timeout         {fail "(timeout) set follow child"}
+      -re "$gdb_prompt $" {pass "set follow-fork child"}
+      timeout         {fail "(timeout) set follow-fork child"}
    }
-   send_gdb "show follow\n"
+   send_gdb "show follow-fork\n"
    gdb_expect {
       -re "Debugger response to a program call of fork or vfork is \"child\"..*$gdb_prompt $"\
                       {pass "explicit show child follow, no catchpoints"}
@@ -131,7 +131,7 @@ proc explicit_fork_child_follow {} {
    }
    send_gdb "next 2\n"
    gdb_expect {
-      -re "Attaching after fork to.*$gdb_prompt $"\
+      -re "Attaching after.* fork to.*$gdb_prompt $"\
                       {pass "explicit child follow, no catchpoints"}
       -re "$gdb_prompt $" {fail "explicit child follow, no catchpoints"}
       timeout         {fail "(timeout) explicit child follow, no catchpoints"}
@@ -185,24 +185,24 @@ proc catch_fork_child_follow {} {
      }
    }
 
-   send_gdb "set follow child\n"
+   send_gdb "set follow-fork child\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow child"}
-      timeout         {fail "(timeout) set follow child"}
+      -re "$gdb_prompt $" {pass "set follow-fork child"}
+      timeout         {fail "(timeout) set follow-fork child"}
    }
    send_gdb "tbreak ${srcfile}:$bp_after_fork\n"
    gdb_expect {
       -re "Temporary breakpoint.*, line $bp_after_fork.*$gdb_prompt $"\
-                      {pass "set follow child, tbreak"}
-      -re "$gdb_prompt $" {fail "set follow child, tbreak"}
-      timeout         {fail "(timeout) set follow child, tbreak"}
+                      {pass "set follow-fork child, tbreak"}
+      -re "$gdb_prompt $" {fail "set follow-fork child, tbreak"}
+      timeout         {fail "(timeout) set follow-fork child, tbreak"}
    }
    send_gdb "continue\n"
    gdb_expect {
-      -re "Attaching after fork to.* at .*$bp_after_fork.*$gdb_prompt $"\
-                      {pass "set follow child, hit tbreak"}
-      -re "$gdb_prompt $" {fail "set follow child, hit tbreak"}
-      timeout         {fail "(timeout) set follow child, hit tbreak"}
+      -re "Attaching after.* fork to.* at .*$bp_after_fork.*$gdb_prompt $"\
+                      {pass "set follow-fork child, hit tbreak"}
+      -re "$gdb_prompt $" {fail "set follow-fork child, hit tbreak"}
+      timeout         {fail "(timeout) set follow-fork child, hit tbreak"}
    }
    # The parent has been detached; allow time for any output it might
    # generate to arrive, so that output doesn't get confused with
@@ -215,12 +215,12 @@ proc catch_fork_child_follow {} {
          send_gdb "y\n"
          gdb_expect {
             -re "$gdb_prompt $"\
-                    {pass "set follow child, cleanup"}
-            timeout {fail "(timeout) set follow child, cleanup"}
+                    {pass "set follow-fork child, cleanup"}
+            timeout {fail "(timeout) set follow-fork child, cleanup"}
          }
       }
-      -re "$gdb_prompt $" {fail "set follow child, cleanup"}
-      timeout         {fail "(timeout) set follow child, cleanup"}
+      -re "$gdb_prompt $" {fail "set follow-fork child, cleanup"}
+      timeout         {fail "(timeout) set follow-fork child, cleanup"}
    }
 }
 
@@ -244,7 +244,7 @@ proc catch_fork_unpatch_child {} {
        "Breakpoint .*file .*$srcfile, line .*" \
        "unpatch child, breakpoint at exit call"
 
-   gdb_test "set follow child" "" "unpatch child, set follow child"
+   gdb_test "set follow-fork child" "" "unpatch child, set follow-fork child"
 
    set test "unpatch child, unpatched parent breakpoints from child"
    gdb_test_multiple "continue" $test {
@@ -297,24 +297,24 @@ proc tcatch_fork_parent_follow {} {
       -re "$gdb_prompt $" {fail "explicit parent follow, tcatch fork"}
       timeout         {fail "(timeout) explicit parent follow, tcatch fork"}
    }
-   send_gdb "set follow parent\n"
+   send_gdb "set follow-fork parent\n"
    gdb_expect {
-      -re "$gdb_prompt $" {pass "set follow parent"}
-      timeout         {fail "(timeout) set follow parent"}
+      -re "$gdb_prompt $" {pass "set follow-fork parent"}
+      timeout         {fail "(timeout) set follow-fork parent"}
    }
    send_gdb "tbreak ${srcfile}:$bp_after_fork\n"
    gdb_expect {
       -re "Temporary breakpoint.*, line $bp_after_fork.*$gdb_prompt $"\
-                      {pass "set follow parent, tbreak"}
-      -re "$gdb_prompt $" {fail "set follow parent, tbreak"}
-      timeout         {fail "(timeout) set follow child, tbreak"}
+                      {pass "set follow-fork parent, tbreak"}
+      -re "$gdb_prompt $" {fail "set follow-fork parent, tbreak"}
+      timeout         {fail "(timeout) set follow-fork child, tbreak"}
    }
    send_gdb "continue\n"
    gdb_expect {
       -re ".*Detaching after fork from.* at .*$bp_after_fork.*$gdb_prompt $"\
-                      {pass "set follow parent, hit tbreak"}
-      -re "$gdb_prompt $" {fail "set follow parent, hit tbreak"}
-      timeout         {fail "(timeout) set follow parent, hit tbreak"}
+                      {pass "set follow-fork parent, hit tbreak"}
+      -re "$gdb_prompt $" {fail "set follow-fork parent, hit tbreak"}
+      timeout         {fail "(timeout) set follow-fork parent, hit tbreak"}
    }
    # The child has been detached; allow time for any output it might
    # generate to arrive, so that output doesn't get confused with
@@ -327,12 +327,12 @@ proc tcatch_fork_parent_follow {} {
          send_gdb "y\n"
          gdb_expect {
             -re "$gdb_prompt $"\
-                    {pass "set follow parent, cleanup"}
-            timeout {fail "(timeout) set follow parent, cleanup"}
+                    {pass "set follow-fork parent, cleanup"}
+            timeout {fail "(timeout) set follow-fork parent, cleanup"}
          }
       }
-      -re "$gdb_prompt $" {fail "set follow parent, cleanup"}
-      timeout         {fail "(timeout) set follow parent, cleanup"}
+      -re "$gdb_prompt $" {fail "set follow-fork parent, cleanup"}
+      timeout         {fail "(timeout) set follow-fork parent, cleanup"}
    }
 }
 
@@ -349,35 +349,35 @@ A fork or vfork creates a new process.  
 .*child   - the new process is debugged after a fork.*
 The unfollowed process will continue to run..*
 By default, the debugger will follow the parent process..*$gdb_prompt $"\
-                      { pass "help set follow" }
+                      { pass "help set follow-fork" }
       -re "$gdb_prompt $" { fail "help set follow" }
-      timeout         { fail "(timeout) help set follow" }
+      timeout         { fail "(timeout) help set follow-fork" }
    }
 
    # Verify that we can set follow-fork-mode, using an abbreviation
    # for both the flag and its value.
    #
-   send_gdb "set follow ch\n"
-   send_gdb "show fol\n"
+   send_gdb "set follow-fork ch\n"
+   send_gdb "show follow-fork\n"
    gdb_expect {
      -re "Debugger response to a program call of fork or vfork is \"child\".*$gdb_prompt $"\
-             {pass "set follow, using abbreviations"}
-     timeout {fail "(timeout) set follow, using abbreviations"}
+             {pass "set follow-fork, using abbreviations"}
+     timeout {fail "(timeout) set follow-fork, using abbreviations"}
    }
 
    # Verify that we cannot set follow-fork-mode to nonsense.
    #
-   send_gdb "set follow chork\n"
+   send_gdb "set follow-fork chork\n"
    gdb_expect {
      -re "Undefined item: \"chork\".*$gdb_prompt $"\
-                     {pass "set follow to nonsense is prohibited"}
-     -re "$gdb_prompt $" {fail "set follow to nonsense is prohibited"}
-     timeout         {fail "(timeout) set follow to nonsense is prohibited"}
+                     {pass "set follow-fork to nonsense is prohibited"}
+     -re "$gdb_prompt $" {fail "set follow-fork to nonsense is prohibited"}
+     timeout         {fail "(timeout) set follow-fork to nonsense is prohibited"}
    }
-   send_gdb "set follow parent\n"
+   send_gdb "set follow-fork parent\n"
    gdb_expect {
-     -re "$gdb_prompt $" {pass "set follow to nonsense is prohibited (reset parent)"}
-     timeout         {fail "set follow to nonsense is prohibited (reset parent)"}
+     -re "$gdb_prompt $" {pass "set follow-fork to nonsense is prohibited (reset parent)"}
+     timeout         {fail "set follow-fork to nonsense is prohibited (reset parent)"}
    }
 
    # Check that fork catchpoints are supported, as an indicator for whether
Index: src/gdb/testsuite/gdb.base/multi-forks.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/multi-forks.exp	2009-07-01 19:17:08.000000000 +0100
+++ src/gdb/testsuite/gdb.base/multi-forks.exp	2009-07-01 19:17:15.000000000 +0100
@@ -121,7 +121,7 @@ proc continue_to_exit_bp_loc {} {
 # The result should be that each of the 4 forks returns zero.
 
 runto_main
-gdb_test "set follow child"
+gdb_test "set follow-fork child"
 continue_to_exit_bp_loc
 
 gdb_test "print pids" "\\$.* = \\{0, 0, 0, 0\\}.*" "follow child, print pids"
@@ -130,7 +130,7 @@ gdb_test "print pids" "\\$.* = \\{0, 0, 
 # Result should be that none of the 4 forks returns zero.
 
 runto_main
-gdb_test "set follow parent" "" ""
+gdb_test "set follow-fork parent" "" ""
 continue_to_exit_bp_loc
 
 gdb_test "print pids\[0\]==0 || pids\[1\]==0 || pids\[2\]==0 || pids\[3\]==0" \
Index: src/gdb/testsuite/gdb.base/maint.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/maint.exp	2009-07-01 19:15:24.000000000 +0100
+++ src/gdb/testsuite/gdb.base/maint.exp	2009-07-01 19:17:15.000000000 +0100
@@ -480,9 +480,9 @@ set bp_location6 [gdb_get_line_number "s
 
 send_gdb "maint info breakpoints\n"
 gdb_expect {
-    -re "Num\[ \t\]+Type\[ \t\]+Disp\[ \t\]+Enb\[ \t\]+Address\[ \t\]+What\r\n1\[ \t\]+breakpoint\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex\[ \t\]+in main at.*break.c:$bp_location6\r\n\[ \t\]+breakpoint already hit 1 time\r\n.*$gdb_prompt $"\
+    -re "Num\[ \t\]+Type\[ \t\]+Disp\[ \t\]+Enb\[ \t\]+Address\[ \t\]+What\r\n1\[ \t\]+breakpoint\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex\[ \t\]+in main at.*break.c:$bp_location6 sspace 1\r\n\[ \t\]+breakpoint already hit 1 time\r\n.*$gdb_prompt $"\
 		{ pass "maint info breakpoints" }
-	-re "Num\[ \t\]+Type\[ \t\]+Disp\[ \t\]+Enb\[ \t\]+Address\[ \t\]+What\r\n1\[ \t\]+breakpoint\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex in main at.*break.c:$bp_location6\r\n\[ \t\]+breakpoint already hit 1 time\r\n-1\[ \t\]+shlib events\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex.*breakpoint already hit.*$gdb_prompt $"\
+	-re "Num\[ \t\]+Type\[ \t\]+Disp\[ \t\]+Enb\[ \t\]+Address\[ \t\]+What\r\n1\[ \t\]+breakpoint\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex in main at.*break.c:$bp_location6 sspace 1\r\n\[ \t\]+breakpoint already hit 1 time\r\n-1\[ \t\]+shlib events\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex.*breakpoint already hit.*$gdb_prompt $"\
 		{ pass "maint info breakpoints (with shlib events)" }
 	-re ".*$gdb_prompt $"       { fail "maint info breakpoints" }
 	timeout         { fail "(timeout) maint info breakpoints" }
Index: src/gdb/testsuite/Makefile.in
===================================================================
--- src.orig/gdb/testsuite/Makefile.in	2009-07-01 19:16:35.000000000 +0100
+++ src/gdb/testsuite/Makefile.in	2009-07-01 19:17:15.000000000 +0100
@@ -35,7 +35,7 @@ SUBDIRS = @subdirs@
 RPATH_ENVVAR = @RPATH_ENVVAR@
 ALL_SUBDIRS = gdb.ada gdb.arch gdb.asm gdb.base gdb.cp gdb.disasm \
 	gdb.dwarf2 \
-	gdb.fortran gdb.server gdb.java gdb.mi \
+	gdb.fortran gdb.server gdb.java gdb.mi gdb.multi \
 	gdb.objc gdb.opt gdb.pascal gdb.python gdb.threads gdb.trace \
 	gdb.xml \
 	$(SUBDIRS)
Index: src/gdb/testsuite/gdb.multi/Makefile.in
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.multi/Makefile.in	2009-07-01 19:17:15.000000000 +0100
@@ -0,0 +1,14 @@
+VPATH = @srcdir@
+srcdir = @srcdir@
+
+EXECUTABLES = hello hangout goodbye bkpt-multi-exec crashme
+
+all info install-info dvi install uninstall installcheck check:
+	@echo "Nothing to be done for $@..."
+
+clean mostlyclean:
+	-rm -f *~ *.o *.ci
+	-rm -f core $(EXECUTABLES)
+
+distclean maintainer-clean realclean: clean
+	-rm -f Makefile config.status config.log
Index: src/gdb/testsuite/gdb.multi/base.exp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.multi/base.exp	2009-07-01 19:17:15.000000000 +0100
@@ -0,0 +1,102 @@
+# Copyright 2009 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/>.
+
+# Test multi-exec / multi-process features that work for all configurations,
+# even ones that cannot run multiple processes simultaneously.
+
+set testfile "base"
+
+set exec1 "hello"
+set srcfile1 ${exec1}.c
+set binfile1 ${objdir}/${subdir}/${exec1}
+
+set exec2 "hangout"
+set srcfile2 ${exec2}.c
+set binfile2 ${objdir}/${subdir}/${exec2}
+
+set exec3 "goodbye"
+set srcfile3 ${exec3}.c
+set binfile3 ${objdir}/${subdir}/${exec3}
+
+if { [prepare_for_testing ${testfile}.exp ${exec1} "${srcfile1}" {debug nowarnings}] } {
+    return -1
+}
+
+if { [prepare_for_testing ${testfile}.exp ${exec2} "${srcfile2}" {debug nowarnings}] } {
+    return -1
+}
+
+if { [prepare_for_testing ${testfile}.exp ${exec3} "${srcfile3}" {debug nowarnings}] } {
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile1}
+
+# Add an empty symbol space, switch to it, and load a main executable
+# into it.
+gdb_test "add-symbol-space" "1 symbol spaces added.*"
+gdb_test "sspace 2" "Switching to sspace 2.*"
+gdb_test "file ${binfile2}" ""
+
+# Add a new symbol space and load a main executable into it in one
+# command.
+gdb_test "add-symbol-space -exec ${binfile3}"
+
+# Check that we have multiple spaces.
+
+gdb_test "info sspaces" \
+    "Main Program.*${exec3}.*${exec2}.*${exec1}.*"
+
+# Test that we have multiple symbol tables.
+
+gdb_test "sspace 1"
+gdb_test "info functions commonfun" "${srcfile1}.*"
+
+gdb_test "sspace 3"
+gdb_test "info functions commonfun" "${srcfile3}.*"
+
+
+gdb_test "sspace 1"
+
+gdb_test "set listsize 1" ""
+
+gdb_test "list commonfun" "from hello.*"
+
+gdb_test "print hglob" "1"
+
+gdb_test "print glob" "92" "print glob (${exec1})"
+
+
+gdb_test "sspace 3"
+
+gdb_test "print gglob" "2"
+
+gdb_test "print glob" "45" "print glob (${exec3})"
+
+gdb_test "list commonfun" "from goodbye.*"
+
+
+# Let's run the hello program.
+gdb_test "sspace 1"
+
+if { ![runto_main] } then {
+    return -1
+}
+
+gdb_test "break hello" ""
+gdb_test "continue" "Breakpoint \[0-9\].*, hello.*"
Index: src/gdb/testsuite/gdb.multi/goodbye.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.multi/goodbye.c	2009-07-01 19:17:15.000000000 +0100
@@ -0,0 +1,62 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009 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/>.  */
+
+int gglob = 2;
+
+int glob = 45;
+
+int verylongfun()
+{
+  glob += 2;
+  glob *= 2;
+  glob += 3;
+  glob *= 3;
+  glob += 4;
+  glob *= 4;
+  glob += 5;
+  glob *= 5;
+  glob += 6;
+  glob *= 6;
+  glob += 7;
+  glob *= 7;
+  glob += 8;
+  glob *= 8;
+  glob += 9;
+  glob *= 9;
+}
+
+main() {
+  mailand();
+  foo(glob);
+  verylongfun();
+  goodbye();
+}
+
+foo(int x) {
+  return x + 92;
+}
+
+mailand()
+{
+  glob = 46;
+}
+
+void commonfun() { mailand(); } /* from goodbye */
+
+goodbye() {
+  ++glob;
+}
Index: src/gdb/testsuite/gdb.multi/hangout.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.multi/hangout.c	2009-07-01 19:17:15.000000000 +0100
@@ -0,0 +1,26 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009 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/>.  */
+
+main(int argc, char *argv[])
+{
+  int i;
+
+  for (i = 0; i < argc; ++i)
+    {
+      printf("Arg %d is %s\n", i, argv[i]);
+    }
+}
Index: src/gdb/testsuite/gdb.multi/hello.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.multi/hello.c	2009-07-01 19:17:15.000000000 +0100
@@ -0,0 +1,46 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009 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/>.  */
+
+short hglob = 1;
+
+short glob = 92;
+
+int commonfun() { bar(); } /* from hello */
+
+bar()
+{
+  if (glob == 0)
+    exit(1);
+}
+
+hello(int x)
+{
+  x *= 2;
+  return x + 45;
+}
+
+main()
+{
+  int tmpx;
+
+  bar();
+  tmpx = hello(glob);
+  commonfun();
+  glob = tmpx;
+  commonfun();
+}
+
Index: src/gdb/testsuite/gdb.multi/bkpt-multi-exec.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.multi/bkpt-multi-exec.c	2009-07-01 19:17:15.000000000 +0100
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+int main (void)
+{
+  printf ("foll-exec is about to execl(crashme)...\n");
+
+  execl ("gdb.multi/crashme",
+         "gdb.multi/crashme",
+         (char *)0);
+}
Index: src/gdb/testsuite/gdb.multi/bkpt-multi-exec.exp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.multi/bkpt-multi-exec.exp	2009-07-01 19:17:15.000000000 +0100
@@ -0,0 +1,84 @@
+# Copyright 2009 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 { [is_remote target] || ![isnative] } then {
+    continue
+}
+
+set testfile "bkpt-multi-exec"
+
+set exec1 "bkpt-multi-exec"
+set srcfile1 ${exec1}.c
+set binfile1 ${objdir}/${subdir}/${exec1}
+
+set exec2 "crashme"
+set srcfile2 ${exec2}.c
+set binfile2 ${objdir}/${subdir}/${exec2}
+
+if { [prepare_for_testing ${testfile}.exp ${exec1} "${srcfile1}" {debug nowarnings}] } {
+    return -1
+}
+
+if { [prepare_for_testing ${testfile}.exp ${exec2} "${srcfile2}" {debug nowarnings}] } {
+    return -1
+}
+# Until "catch exec" is implemented on other targets...
+#
+if {![istarget "hppa*-hp-hpux*"] && ![istarget "*-linux*"]} then {
+    continue
+}
+
+# Start with a fresh gdb
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile1}
+
+# Start the program running, and stop at main.
+#
+if ![runto_main] then {
+    perror "Couldn't run ${binfile1}"
+    return
+}
+
+delete_breakpoints
+
+# continuing should exec and trigger the bug
+gdb_test "continue" "SIGSEGV.*"
+
+# Start over, but this time, set a breakpoint before the app crashes.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile1}
+
+gdb_test "add-symbol-space -exec ${binfile2}"
+
+set bp_location [gdb_get_line_number "set breakpoint here" ${srcfile2}]
+
+gdb_test "sspace 2"
+gdb_test "break ${srcfile2}:${bp_location}" ""
+
+# Start the program running, and stop at main.
+#
+gdb_test "sspace 1"
+
+# Now run to the breakpoint.  This should cross the exec, and stop at
+# the breakpoint before the crash.
+gdb_test "run" "${srcfile2}:${bp_location}.*set breakpoint here.*"
+
+return 0
Index: src/gdb/testsuite/gdb.multi/crashme.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.multi/crashme.c	2009-07-01 19:17:15.000000000 +0100
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+  int *foo = NULL;
+
+  printf ("Oh no, a bug!\n"); /* set breakpoint here */
+
+  return *foo;
+}
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo	2009-07-01 19:17:08.000000000 +0100
+++ src/gdb/doc/gdb.texinfo	2009-07-01 19:17:15.000000000 +0100
@@ -1791,8 +1791,9 @@ kill a child process.
 * Kill Process::                Killing the child process
 
 * Inferiors::                   Debugging multiple inferiors
+* Multiple Programs::           Debugging multiple programs
 * Threads::                     Debugging programs with multiple threads
-* Processes::                   Debugging programs with multiple processes
+* Forks::                       Debugging forks
 * Checkpoint/Restart::          Setting a @emph{bookmark} to return to later
 @end menu
 
@@ -2423,6 +2424,92 @@ Show whether messages will be printed wh
 inferiors have started, exited or have been detached.
 @end table
 
+@node Multiple Programs
+@section Debugging Multiple Programs
+
+@value{GDBN} lets you run and debug multiple programs in a single
+session.  In addition, @value{GDBN} on some systems may let you run
+several programs simultaneously (otherwise you have to exit from one
+before starting another).  In the most general case, you can have
+multiple threads of execution in each of multiple processes, launched
+from multiple executables.
+
+@cindex inferior
+To keep everything straight, @value{GDBN} represents the state of each
+program execution with an object called an @dfn{inferior}.  An
+inferior typically corresponds to a process, but is more general and
+applies also to targets that don't have processes.  Each run of an
+executable creates a new inferior, as does each attachment to an
+existing process.  Inferiors have unique identifiers that are
+different from process ids.  Usually each inferior will also have its
+own distinct address space, although in some cases, you'll have more
+than one inferior running in the same address space, like when
+debugging programs that call @code{vfork}.  Other examples include
+embedded targets that may have several inferiors running in different
+parts of a single address space.  Each inferior may in turn have
+multiple threads running in it.
+
+@cindex symbol space
+To keep everything straight, @value{GDBN} represents a symbolic view
+of an address space with an object called a @dfn{symbol space}.  In
+the tradicional debugging scenario, there's a one-to-one
+correspondance between a symbol space, an inferior and an address
+space.  On embedded targets that may have several inferiors running in
+different parts of a single address space, each inferior is still
+bound to its own symbol space --- meaning that @value{GDBN} manages
+the single address space behind the scenes.  On some other targets,
+even though each inferior runs in its own address space, they all run
+the same code and see all symbols at the same addresses.  In such
+cases, all inferiors are bound to the same symbol space.
+
+You can get multiple executables into a debugging session via the
+@code{add-symbol-space} command.  On some systems @value{GDBN} can add
+programs to the debug session automatically by following calls to
+@code{exec}.
+
+@table @code
+@kindex add-symbol-space
+@item add-symbol-space [ -copies @var{n} ] [ -exec @var{executable} ]
+Adds @var{n} symbol spaces to be run using @var{exec} as the
+executable.  @var{n} defaults to 1.  If no executable is specified,
+the symbol space begins empty.  You can still load a program to the
+symbol space by using e.g., the @code{file} command with the
+executable name as its argument.
+
+@kindex remove-symbol-space
+@item remove-symbol-space @var{ID}
+Removes the symbol space @var{ID}.
+
+@kindex clone-symbol-space
+@item clone-symbol-space [ -copies @var{n} ] [ @var{ID} ]
+Adds @var{n} symbol spaces to be run using the same executable as
+symbol space @var{ID}.  @var{n} defaults to 1.
+
+@kindex info symbol-spaces
+@kindex info sspaces
+@item info symbol-spaces
+Print a list of all symbol spaces currently being managed by
+@value{GDBN}.  E.g,
+
+@smallexample
+(@value{GDBP}) info sspaces
+  Id   Main Program
+  2    goodbye
+* 1    hello
+@end smallexample
+
+@kindex symbol-space
+@item symbol-space @var{ID}
+@itemx sspace @var{ID}
+Set symbol space @var{ID} as the current symbol space.  If there's any
+inferior bound the the symbol space, @value{GDBN} switches to it.
+
+@end table
+
+Many commands will work the same with multiple programs as with a
+single program: @code{print myglobal} will simply display the value of
+@code{myglobal} in the current symbol space.
+
 @node Threads
 @section Debugging Programs with Multiple Threads
 
@@ -2702,8 +2789,8 @@ only on some platforms.
 Display current libthread_db search path.
 @end table
 
-@node Processes
-@section Debugging Programs with Multiple Processes
+@node Forks
+@section Debugging Forks
 
 @cindex fork, debugging programs which call
 @cindex multiple processes
@@ -13102,6 +13189,16 @@ via @code{gdbserver} (@pxref{Server, fil
 Program}).  In these situations the @value{GDBN} commands to specify
 new files are useful.
 
+@value{GDBN} is capable of working with multiple programs at once, so
+you have the option of adding executables instead of replacing them,
+using commands such as @code{add-symbol-space} and @code{file}.  While
+this can be useful, for instance to debug both client and server
+programs from the same @value{GDBN}, it can also be confusing to have
+several functions named @code{main}, several globals with the same
+name but different values, and so forth.  You may wish to try using a
+separate @value{GDBN} instance for each program before deciding which
+is the better approach.
+
 @table @code
 @cindex executable file
 @kindex file


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]