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] Multiple process support in gdbserver


This patch allows gdbserver (in "extended-remote" mode) to create new
processes, attach to running processes, and persist when no process
is running.  It works pretty much the same as a native GDB once
you connect.  Documentation, NEWS, and tests are all included.

Tested using gdbserver on x86_64.

All comments welcome!

-- 
Daniel Jacobowitz
CodeSourcery

2007-12-07  Daniel Jacobowitz  <dan@codesourcery.com>

	* linux-low.c (linux_attach_lwp): Do not _exit after errors.
	(linux_kill, linux_detach): Clean up the process list.
	* remote-utils.c (remote_open): Improve port number parsing.
	(putpkt_binary, input_interrupt): Only send interrupts if the target
	is running.
	* server.c (extended_protocol): Make static.
	(attached): Define earlier.
	(exit_requested, response_needed, program_argv): New variables.
	(target_running): New.
	(start_inferior): Clear attached here.
	(attach_inferior): Set attached here.
	(require_running): Define.
	(handle_query): Use require_running and target_running.  Implement
	"monitor exit".
	(handle_v_attach, handle_v_run): New.
	(handle_v_requests): Use require_running.  Handle vAttach and vRun.
	(gdbserver_usage): Update.
	(main): Redo argument parsing.  Handle --debug and --multi.  Handle
	--attach along with other options or after the port.  Save
	program_argv.  Support no initial program.  Resynchronize
	communication with GDB after an error.  Handle "monitor exit".
	Use require_running and target_running.  Always allow the extended
	protocol.  Do not error out for Hc0 or Hc-1.  Do not automatically
	restart in extended mode.
	* README: Refer to the GDB manual.  Update --attach usage.

2007-12-07  Daniel Jacobowitz  <dan@codesourcery.com>

	* remote.c (struct remote_state): Add cached_wait_status.
	(remote_exec_file): New variable.
	(PACKET_vAttach, PACKET_vRun): New constants.
	(extended_remote_restart): Do not query for status.
	(struct start_remote_args): New.
	(remote_start_remote): Take it as a second argument.  Check
	whether the target is running.  Issue an error for non-running
	non-extended targets.  Cache the wait status.  Set inferior_ptid
	here.
	(remote_open_1): Prompt to disconnect non-running targets.  Make
	sure the target is marked running.  Do not set inferior_ptid here.
	Update call to remote_start_remote.  Do not call remote_check_symbols
	if the target is not running.
	(remote_detach_1): Rename from remote_detach.  Take an EXTENDED
	argument.  Handle a non-running target.
	(remote_detach): Use it.
	(extended_remote_detach): New.
	(remote_disconnect): Fix typo.  Use remoute_mourn_1.
	(extended_remote_attach_1, extended_remote_attach)
	(extended_async_remote_attach): New.
	(remote_vcont_resume): Remove unused variable.
	(remote_wait, remote_async_wait): Use any cached wait status.
	(putpkt_binary, getpkt): Clear any cached wait status.
	(extended_remoute_mourn_1): New.
	(extended_remote_mourn): Use it.
	(extended_async_remote_mourn, extended_remote_run): New.
	(extended_remote_create_inferior_1): New.
	(extended_remote_create_inferior): Use it.
	(extended_remote_async_create_inferior): Likewise.
	(remote_xfer_partial): Skip for non-executing targets.
	(init_extended_remote_ops): Set to_detach and to_attach.
	(init_extended_async_remote_ops): Likewise.  Use
	extended_async_remote_mourn.
	(_initialize_remote): Register vAttach, vRun, and
	set remote exec-file.
	* NEWS: Mention vAttach, vRun, and gdbserver extended-remote support.

2007-12-07  Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.server/ext-attach.c, gdb.server/ext-attach.exp,
	gdb.server/ext-run.exp: New files.
	* lib/gdbserver-support.exp (gdbserver_download): New.
	(gdbserver_start): New.  Update gdbserver expected
	output.
	(gdbserver_spawn): Use them.
	(gdbserver_start_extended): New.

2007-12-07  Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.texinfo (Using the `gdbserver' Program): Add security
	warning.  Rearrange into subsections and subsubsections.  Document
	--multi and --debug.  Correct --with-sysroot typo.  Update --attach
	usage.  Make load reference clearer.  Document monitor exit.
	(Remote Configuration): Document set remote exec-file, attach-packet,
	and run-packet.
	(Packets): Document vAttach and vRun.  Correct syntax error.

---
 gdb/NEWS                                |   10 
 gdb/doc/gdb.texinfo                     |  130 +++++++-
 gdb/gdbserver/README                    |    4 
 gdb/gdbserver/linux-low.c               |   24 +
 gdb/gdbserver/remote-utils.c            |   12 
 gdb/gdbserver/server.c                  |  472 +++++++++++++++++++++++-------
 gdb/remote.c                            |  497 +++++++++++++++++++++++++-------
 gdb/testsuite/gdb.server/ext-attach.c   |   31 +
 gdb/testsuite/gdb.server/ext-attach.exp |   77 ++++
 gdb/testsuite/gdb.server/ext-run.exp    |   48 +++
 gdb/testsuite/lib/gdbserver-support.exp |   69 +++-
 11 files changed, 1114 insertions(+), 260 deletions(-)

Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo	2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/doc/gdb.texinfo	2007-12-07 15:22:05.000000000 -0500
@@ -12877,9 +12877,19 @@ choice for debugging.
 or a TCP connection, using the standard @value{GDBN} remote serial
 protocol.
 
-@table @emph
-@item On the target machine,
-you need to have a copy of the program you want to debug.
+@quotation
+@emph{Warning:} @code{gdbserver} does not have any built-in security.
+Do not run @code{gdbserver} connected to any public network; a
+@value{GDBN} connection to @code{gdbserver} provides access to the
+target system with the same privileges as the user running
+@code{gdbserver}.
+@end quotation
+
+@subsection Running @code{gdbserver}
+@cindex arguments, to @code{gdbserver}
+
+Run @code{gdbserver} on the target system.  You need a copy of the
+program you want to debug, including any libraries it requires.
 @code{gdbserver} does not need your program's symbol table, so you can
 strip the program if necessary to save space.  @value{GDBN} on the host
 system does all the symbol handling.
@@ -12922,11 +12932,13 @@ conflicts with another service, @code{gd
 and exits.}  You must use the same port number with the host @value{GDBN}
 @code{target remote} command.
 
+@subsubsection Attaching to a Running Program
+
 On some targets, @code{gdbserver} can also attach to running programs.
 This is accomplished via the @code{--attach} argument.  The syntax is:
 
 @smallexample
-target> gdbserver @var{comm} --attach @var{pid}
+target> gdbserver --attach @var{comm} @var{pid}
 @end smallexample
 
 @var{pid} is the process ID of a currently running process.  It isn't necessary
@@ -12938,18 +12950,54 @@ You can debug processes by name instead 
 @code{pidof} utility:
 
 @smallexample
-target> gdbserver @var{comm} --attach `pidof @var{program}`
+target> gdbserver --attach @var{comm} `pidof @var{program}`
 @end smallexample
 
 In case more than one copy of @var{program} is running, or @var{program}
 has multiple threads, most versions of @code{pidof} support the
 @code{-s} option to only return the first process ID.
 
-@item On the host machine,
-first make sure you have the necessary symbol files.  Load symbols for
+@subsubsection Multi-Process Mode for @code{gdbserver}
+
+When you connect to @code{gdbserver} using @code{target remote},
+@code{gdbserver} debugs the specified program only once.  When the
+program exits, or you detach from it, @value{GDBN} closes the connection
+and @code{gdbserver} exits.
+
+If you connect using @code{target extended-remote}, @code{gdbserver}
+enters multi-process mode.  When the debugged program exits, or you
+detach from it, @value{GDBN} stays connected to @code{gdbserver} even
+though no program is running.  The @code{run} and @code{attach}
+commands instruct @code{gdbserver} to run or attach to a new program.
+The @code{run} command uses @code{set remote exec-file} (@pxref{set
+remote exec-file}) to select the program to run.  Command line
+arguments are supported, except for wildcard expansion and I/O
+redirection (@pxref{Arguments}).
+
+To start @code{gdbserver} without supplying an initial command to run
+or process ID to attach, use the @option{--multi} command line option.
+Then you can connect using @code{target extended-remote} and start
+the program you want to debug.
+
+@code{gdbserver} does not automatically exit in multi-process mode.
+You can terminate it by using @code{monitor exit}
+(@pxref{Monitor Commands for gdbserver}).
+
+@subsubsection Other Command-Line Arguments for @code{gdbserver}
+
+You can include @option{--debug} on the @code{gdbserver} command line.
+@code{gdbserver} will display extra status information about the debugging
+process.  This option is intended for @code{gdbserver} development and
+for bug reports to the developers.
+
+@subsection Connecting to @code{gdbserver}
+
+Run @value{GDBN} on the host system.
+
+First make sure you have the necessary symbol files.  Load symbols for
 your application using the @code{file} command before you connect.  Use
 @code{set sysroot} to locate target libraries (unless your @value{GDBN}
-was compiled with the correct sysroot using @code{--with-system-root}).
+was compiled with the correct sysroot using @code{--with-sysroot}).
 
 The symbol file and target libraries must exactly match the executable
 and libraries on the target, with one exception: the files on the host
@@ -12963,19 +13011,17 @@ Connect to your target (@pxref{Connectin
 For TCP connections, you must start up @code{gdbserver} prior to using
 the @code{target remote} command.  Otherwise you may get an error whose
 text depends on the host system, but which usually looks something like
-@samp{Connection refused}.  You don't need to use the @code{load}
+@samp{Connection refused}.  Don't use the @code{load}
 command in @value{GDBN} when using @code{gdbserver}, since the program is
 already on the target.
 
-@end table
-
 @subsection Monitor Commands for @code{gdbserver}
 @cindex monitor commands, for @code{gdbserver}
+@anchor{Monitor Commands for gdbserver}
 
 During a @value{GDBN} session using @code{gdbserver}, you can use the
 @code{monitor} command to send special requests to @code{gdbserver}.
-Here are the available commands; they are only of interest when
-debugging @value{GDBN} or @code{gdbserver}.
+Here are the available commands.
 
 @table @code
 @item monitor help
@@ -12990,6 +13036,11 @@ Disable or enable general debugging mess
 Disable or enable specific debugging messages associated with the remote
 protocol (@pxref{Remote Protocol}).
 
+@item monitor exit
+Tell gdbserver to exit immediately.  This command should be followed by
+@code{disconnect} to close the debugging session.  @code{gdbserver} will
+detach from any attached processes and kill any processes it created.
+
 @end table
 
 @node Remote Configuration
@@ -13084,6 +13135,15 @@ responses.
 @itemx set remote hardware-breakpoint-limit @var{limit}
 Restrict @value{GDBN} to using @var{limit} remote hardware breakpoint or
 watchpoints.  A limit of -1, the default, is treated as unlimited.
+
+@item set remote exec-file @var{filename}
+@itemx show remote exec-file
+@anchor{set remote exec-file}
+@cindex executable file, for remote target
+Select the file used for @code{run} with @code{target
+extended-remote}.  This should be set to a filename valid on the
+target system.  If it is not set, the target will use a default
+filename (e.g.@: the last program run).
 @end table
 
 @cindex remote packets, enabling and disabling
@@ -13130,10 +13190,18 @@ are:
 @tab @code{qSymbol}
 @tab Detecting multiple threads
 
+@item @code{attach}
+@tab @code{vAttach}
+@tab @code{attach}
+
 @item @code{verbose-resume}
 @tab @code{vCont}
 @tab Stepping or resuming multiple threads
 
+@item @code{run}
+@tab @code{vRun}
+@tab @code{run}
+
 @item @code{software-breakpoint}
 @tab @code{Z0}
 @tab @code{break}
@@ -23520,6 +23588,22 @@ thread is dead
 Packets starting with @samp{v} are identified by a multi-letter name,
 up to the first @samp{;} or @samp{?} (or the end of the packet).
 
+@item vAttach;@var{pid}
+@cindex @samp{vAttach} packet
+Attach to a new process with the specified process ID.  @var{pid} is a
+hexadecimal integer identifying the process.  If the stub is currently
+controlling a process, it is killed.  The attached process is stopped.
+
+This packet is only available in extended mode.
+
+Reply:
+@table @samp
+@item E @var{nn}
+for an error
+@item @r{Any stop packet}
+for success (@pxref{Stop Reply Packets})
+@end table
+
 @item vCont@r{[};@var{action}@r{[}:@var{tid}@r{]]}@dots{}
 @cindex @samp{vCont} packet
 Resume the inferior, specifying different actions for each thread.
@@ -23616,6 +23700,24 @@ The stub is permitted to delay or batch 
 regions of flash memory are unpredictable until the @samp{vFlashDone}
 request is completed.
 
+@item vRun;@var{filename}@r{[};@var{argument}@r{]}@dots{}
+@cindex @samp{vRun} packet
+Run the program @var{filename}, passing it each @var{argument} on its
+command line.  The file and arguments are hex-encoded strings.  If
+@var{filename} is an empty string, the stub may use a default program
+(e.g.@: the last program run).  The program is created in the stopped
+state.  If the stub is currently controlling a process, it is killed.
+
+This packet is only available in extended mode.
+
+Reply:
+@table @samp
+@item E @var{nn}
+for an error
+@item @r{Any stop packet}
+for success (@pxref{Stop Reply Packets})
+@end table
+
 @item X @var{addr},@var{length}:@var{XX@dots{}}
 @anchor{X packet}
 @cindex @samp{X} packet
@@ -24775,7 +24877,7 @@ return -1 if an error occurs.  @var{path
 @var{flags} is an integer indicating a mask of open flags
 (@pxref{Open Flags}), and @var{mode} is an integer indicating a mask
 of mode bits to use if the file is created (@pxref{mode_t Values}).
-@xref{open} for details of the open flags and mode values.
+@ref{open} for details of the open flags and mode values.
 
 @item vFile:close: @var{fd}
 Close the open file corresponding to @var{fd} and return 0, or
Index: src/gdb/gdbserver/README
===================================================================
--- src.orig/gdb/gdbserver/README	2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/gdbserver/README	2007-12-07 10:38:30.000000000 -0500
@@ -9,6 +9,8 @@ host.  GDB and GDBserver communicate usi
 implemented in remote.c, and various *-stub.c files.  They communicate via
 either a serial line or a TCP connection.
 
+For more information about GDBserver, see the GDB manual.
+
 Usage (server (target) side):
 
 First, you need to have a copy of the program you want to debug put onto
@@ -47,7 +49,7 @@ print an error message and exit.
 On some targets, gdbserver can also attach to running programs.  This is
 accomplished via the --attach argument.  The syntax is:
 
-	target> gdbserver COMM --attach PID
+	target> gdbserver --attach COMM PID
 
 PID is the process ID of a currently running process.  It isn't necessary
 to point gdbserver at a binary for the running process.
Index: src/gdb/gdbserver/linux-low.c
===================================================================
--- src.orig/gdb/gdbserver/linux-low.c	2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/gdbserver/linux-low.c	2007-12-07 10:38:30.000000000 -0500
@@ -304,14 +304,18 @@ linux_attach_lwp (unsigned long pid)
 
   if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0)
     {
-      fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid,
+      if (all_threads.head != NULL)
+	{
+	  /* If we fail to attach to an LWP, just warn.  */
+	  fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid,
+		   strerror (errno), errno);
+	  fflush (stderr);
+	  return;
+	}
+      else
+	/* If we fail to attach to a process, report an error.  */
+	error ("Cannot attach to process %ld: %s (%d)\n", pid,
 	       strerror (errno), errno);
-      fflush (stderr);
-
-      /* If we fail to attach to an LWP, just return.  */
-      if (all_threads.head == NULL)
-	_exit (0177);
-      return;
     }
 
   ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE);
@@ -396,6 +400,10 @@ linux_kill (void)
       /* Make sure it died.  The loop is most likely unnecessary.  */
       wstat = linux_wait_for_event (thread);
     } while (WIFSTOPPED (wstat));
+
+  clear_inferiors ();
+  free (all_processes.head);
+  all_processes.head = all_processes.tail = NULL;
 }
 
 static void
@@ -434,6 +442,8 @@ linux_detach (void)
   delete_all_breakpoints ();
   for_each_inferior (&all_threads, linux_detach_one_process);
   clear_inferiors ();
+  free (all_processes.head);
+  all_processes.head = all_processes.tail = NULL;
   return 0;
 }
 
Index: src/gdb/gdbserver/remote-utils.c
===================================================================
--- src.orig/gdb/gdbserver/remote-utils.c	2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/gdbserver/remote-utils.c	2007-12-07 10:38:30.000000000 -0500
@@ -186,15 +186,15 @@ remote_open (char *name)
 #ifdef USE_WIN32API
       static int winsock_initialized;
 #endif
-      char *port_str;
       int port;
       struct sockaddr_in sockaddr;
       socklen_t tmp;
       int tmp_desc;
+      char *port_end;
 
-      port_str = strchr (name, ':');
-
-      port = atoi (port_str + 1);
+      port = strtoul (port_str + 1, &port_end, 10);
+      if (port_str[1] == '\0' || *port_end != '\0')
+	fatal ("Bad port argument: %s", name);
 
 #ifdef USE_WIN32API
       if (!winsock_initialized)
@@ -574,7 +574,7 @@ putpkt_binary (char *buf, int cnt)
 	}
 
       /* Check for an input interrupt while we're here.  */
-      if (buf3[0] == '\003')
+      if (buf3[0] == '\003' && current_inferior != NULL)
 	(*the_target->request_interrupt) ();
     }
   while (buf3[0] != '+');
@@ -616,7 +616,7 @@ input_interrupt (int unused)
 
       cc = read (remote_desc, &c, 1);
 
-      if (cc != 1 || c != '\003')
+      if (cc != 1 || c != '\003' || current_inferior == NULL)
 	{
 	  fprintf (stderr, "input_interrupt, count = %d c = %d ('%c')\n",
 		   cc, c, c);
Index: src/gdb/gdbserver/server.c
===================================================================
--- src.orig/gdb/gdbserver/server.c	2007-12-07 10:38:17.000000000 -0500
+++ src/gdb/gdbserver/server.c	2007-12-07 14:26:13.000000000 -0500
@@ -34,9 +34,15 @@ unsigned long general_thread;
 unsigned long step_thread;
 unsigned long thread_from_wait;
 unsigned long old_thread_from_wait;
-int extended_protocol;
 int server_waiting;
 
+static int extended_protocol;
+static int attached;
+static int response_needed;
+static int exit_requested;
+
+static char **program_argv;
+
 /* Enable miscellaneous debugging output.  The name is historical - it
    was originally used to debug LinuxThreads support.  */
 int debug_threads;
@@ -69,8 +75,16 @@ restore_old_foreground_pgrp (void)
 #endif
 
 static int
+target_running (void)
+{
+  return all_threads.head != NULL;
+}
+
+static int
 start_inferior (char *argv[], char *statusptr)
 {
+  attached = 0;
+
 #ifdef SIGTTOU
   signal (SIGTTOU, SIG_DFL);
   signal (SIGTTIN, SIG_DFL);
@@ -107,6 +121,8 @@ attach_inferior (int pid, char *statuspt
   if (myattach (pid) != 0)
     return -1;
 
+  attached = 1;
+
   fprintf (stderr, "Attached; pid = %d\n", pid);
   fflush (stderr);
 
@@ -254,6 +270,13 @@ monitor_show_help (void)
   monitor_output ("    Enable remote protocol debugging messages\n");
 }
 
+#define require_running(BUF)			\
+  if (!target_running ())			\
+    {						\
+      write_enn (BUF);				\
+      return;					\
+    }
+
 /* Handle all of the extended 'q' packets.  */
 void
 handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -263,6 +286,7 @@ handle_query (char *own_buf, int packet_
   /* Reply the current thread id.  */
   if (strcmp ("qC", own_buf) == 0)
     {
+      require_running (own_buf);
       thread_ptr = all_threads.head;
       sprintf (own_buf, "QC%x",
 	thread_to_gdb_id ((struct thread_info *)thread_ptr));
@@ -271,7 +295,7 @@ handle_query (char *own_buf, int packet_
 
   if (strcmp ("qSymbol::", own_buf) == 0)
     {
-      if (the_target->look_up_symbols != NULL)
+      if (target_running () && the_target->look_up_symbols != NULL)
 	(*the_target->look_up_symbols) ();
 
       strcpy (own_buf, "OK");
@@ -280,6 +304,7 @@ handle_query (char *own_buf, int packet_
 
   if (strcmp ("qfThreadInfo", own_buf) == 0)
     {
+      require_running (own_buf);
       thread_ptr = all_threads.head;
       sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
       thread_ptr = thread_ptr->next;
@@ -288,6 +313,7 @@ handle_query (char *own_buf, int packet_
 
   if (strcmp ("qsThreadInfo", own_buf) == 0)
     {
+      require_running (own_buf);
       if (thread_ptr != NULL)
 	{
 	  sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
@@ -305,7 +331,8 @@ handle_query (char *own_buf, int packet_
       && strcmp ("qOffsets", own_buf) == 0)
     {
       CORE_ADDR text, data;
-      
+
+      require_running (own_buf);
       if (the_target->read_offsets (&text, &data))
 	sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX",
 		 (long)text, (long)data, (long)data);
@@ -324,6 +351,7 @@ handle_query (char *own_buf, int packet_
       CORE_ADDR ofs;
       unsigned char *spu_buf;
 
+      require_running (own_buf);
       strcpy (own_buf, "E00");
       if (decode_xfer_read (own_buf + 15, &annex, &ofs, &len) < 0)
 	  return;
@@ -356,6 +384,7 @@ handle_query (char *own_buf, int packet_
       CORE_ADDR ofs;
       unsigned char *spu_buf;
 
+      require_running (own_buf);
       strcpy (own_buf, "E00");
       spu_buf = malloc (packet_len - 15);
       if (!spu_buf)
@@ -387,6 +416,8 @@ handle_query (char *own_buf, int packet_
       unsigned int len;
       char *annex;
 
+      require_running (own_buf);
+
       /* Reject any annex; grab the offset and length.  */
       if (decode_xfer_read (own_buf + 16, &annex, &ofs, &len) < 0
 	  || annex[0] != '\0')
@@ -420,6 +451,8 @@ handle_query (char *own_buf, int packet_
       const char *document;
       char *annex;
 
+      require_running (own_buf);
+
       /* Check for support.  */
       document = get_features_xml ("target.xml");
       if (document == NULL)
@@ -467,6 +500,8 @@ handle_query (char *own_buf, int packet_
       struct inferior_list_entry *dll_ptr;
       char *annex;
 
+      require_running (own_buf);
+
       /* Reject any annex; grab the offset and length.  */
       if (decode_xfer_read (own_buf + 21, &annex, &ofs, &len) < 0
 	  || annex[0] != '\0')
@@ -535,7 +570,7 @@ handle_query (char *own_buf, int packet_
 
       if (the_target->read_auxv != NULL)
 	strcat (own_buf, ";qXfer:auxv:read+");
-     
+
       if (the_target->qxfer_spu != NULL)
 	strcat (own_buf, ";qXfer:spu:read+;qXfer:spu:write+");
 
@@ -553,6 +588,8 @@ handle_query (char *own_buf, int packet_
       CORE_ADDR parts[3], address = 0;
       int i, err;
 
+      require_running (own_buf);
+
       for (i = 0; i < 3; i++)
 	{
 	  char *p2;
@@ -642,6 +679,8 @@ handle_query (char *own_buf, int packet_
 	}
       else if (strcmp (mon, "help") == 0)
 	monitor_show_help ();
+      else if (strcmp (mon, "exit") == 0)
+	exit_requested = 1;
       else
 	{
 	  monitor_output ("Unknown monitor command.\n\n");
@@ -772,6 +811,95 @@ err:
   return;
 }
 
+/* Attach to a new program.  Return 1 if successful, 0 if failure.  */
+int
+handle_v_attach (char *own_buf, char *status, int *signal)
+{
+  int pid;
+
+  pid = strtol (own_buf + 8, NULL, 16);
+  if (pid != 0 && attach_inferior (pid, status, signal) == 0)
+    {
+      prepare_resume_reply (own_buf, *status, *signal);
+      return 1;
+    }
+  else
+    {
+      write_enn (own_buf);
+      return 0;
+    }
+}
+
+/* Run a new program.  Return 1 if successful, 0 if failure.  */
+static int
+handle_v_run (char *own_buf, char *status, int *signal)
+{
+  char *p, **pp, *next_p, **new_argv;
+  int i, new_argc;
+
+  new_argc = 0;
+  for (p = own_buf + strlen ("vRun;"); p && *p; p = strchr (p, ';'))
+    {
+      p++;
+      new_argc++;
+    }
+
+  new_argv = malloc ((new_argc + 2) * sizeof (char *));
+  i = 0;
+  for (p = own_buf + strlen ("vRun;"); *p; p = next_p)
+    {
+      next_p = strchr (p, ';');
+      if (next_p == NULL)
+	next_p = p + strlen (p);
+
+      if (i == 0 && p == next_p)
+	new_argv[i] = NULL;
+      else
+	{
+	  new_argv[i] = malloc (1 + (next_p - p) / 2);
+	  unhexify (new_argv[i], p, (next_p - p) / 2);
+	  new_argv[i][(next_p - p) / 2] = '\0';
+	}
+
+      if (*next_p)
+	next_p++;
+      i++;
+    }
+  new_argv[i] = NULL;
+
+  if (new_argv[0] == NULL)
+    {
+      if (program_argv == NULL)
+	{
+	  write_enn (own_buf);
+	  return 0;
+	}
+
+      new_argv[0] = strdup (program_argv[0]);
+    }
+
+  /* Free the old argv.  */
+  if (program_argv)
+    {
+      for (pp = program_argv; *pp != NULL; pp++)
+	free (*pp);
+      free (program_argv);
+    }
+  program_argv = new_argv;
+
+  *signal = start_inferior (program_argv, status);
+  if (*status == 'T')
+    {
+      prepare_resume_reply (own_buf, *status, *signal);
+      return 1;
+    }
+  else
+    {
+      write_enn (own_buf);
+      return 0;
+    }
+}
+
 /* Handle all of the extended 'v' packets.  */
 void
 handle_v_requests (char *own_buf, char *status, int *signal,
@@ -779,6 +907,7 @@ handle_v_requests (char *own_buf, char *
 {
   if (strncmp (own_buf, "vCont;", 6) == 0)
     {
+      require_running (own_buf);
       handle_v_cont (own_buf, status, signal);
       return;
     }
@@ -793,6 +922,28 @@ handle_v_requests (char *own_buf, char *
       && handle_vFile (own_buf, packet_len, new_packet_len))
     return;
 
+  if (strncmp (own_buf, "vAttach;", 8) == 0)
+    {
+      if (target_running ())
+	{
+	  fprintf (stderr, "Killing inferior\n");
+	  kill_inferior ();
+	}
+      handle_v_attach (own_buf, status, signal);
+      return;
+    }
+
+  if (strncmp (own_buf, "vRun;", 5) == 0)
+    {
+      if (target_running ())
+	{
+	  fprintf (stderr, "Killing inferior\n");
+	  kill_inferior ();
+	}
+      handle_v_run (own_buf, status, signal);
+      return;
+    }
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
@@ -829,8 +980,6 @@ myresume (char *own_buf, int step, int *
   disable_async_io ();
 }
 
-static int attached;
-
 static void
 gdbserver_version (void)
 {
@@ -844,13 +993,25 @@ gdbserver_version (void)
 static void
 gdbserver_usage (void)
 {
-  printf ("Usage:\tgdbserver COMM PROG [ARGS ...]\n"
-	  "\tgdbserver COMM --attach PID\n"
+  printf ("Usage:\tgdbserver [OPTIONS] COMM PROG [ARGS ...]\n"
+	  "\tgdbserver [OPTIONS] --attach COMM PID\n"
+	  "\tgdbserver [OPTIONS] --multi COMM\n"
 	  "\n"
 	  "COMM may either be a tty device (for serial debugging), or \n"
-	  "HOST:PORT to listen for a TCP connection.\n");
+	  "HOST:PORT to listen for a TCP connection.\n"
+	  "\n"
+	  "Options:\n"
+	  "  --debug\t\tEnable debugging output.\n");
 }
 
+#undef require_running
+#define require_running(BUF)			\
+  if (!target_running ())			\
+    {						\
+      write_enn (BUF);				\
+      break;					\
+    }
+
 int
 main (int argc, char *argv[])
 {
@@ -862,18 +1023,38 @@ main (int argc, char *argv[])
   CORE_ADDR mem_addr;
   int bad_attach;
   int pid;
-  char *arg_end;
+  char *arg_end, *port;
+  char **next_arg = &argv[1];
+  int multi_mode = 0;
+  int attach = 0;
+  int was_running;
 
-  if (argc >= 2 && strcmp (argv[1], "--version") == 0)
+  while (*next_arg != NULL && **next_arg == '-')
     {
-      gdbserver_version ();
-      exit (0);
-    }
+      if (strcmp (*next_arg, "--version") == 0)
+	{
+	  gdbserver_version ();
+	  exit (0);
+	}
+      else if (strcmp (*next_arg, "--help") == 0)
+	{
+	  gdbserver_usage ();
+	  exit (0);
+	}
+      else if (strcmp (*next_arg, "--attach") == 0)
+	attach = 1;
+      else if (strcmp (*next_arg, "--multi") == 0)
+	multi_mode = 1;
+      else if (strcmp (*next_arg, "--debug") == 0)
+	debug_threads = 1;
+      else
+	{
+	  fprintf (stderr, "Unknown argument: %s\n", *next_arg);
+	  exit (1);
+	}
 
-  if (argc >= 2 && strcmp (argv[1], "--help") == 0)
-    {
-      gdbserver_usage ();
-      exit (0);
+      next_arg++;
+      continue;
     }
 
   if (setjmp (toplevel))
@@ -882,23 +1063,34 @@ main (int argc, char *argv[])
       exit (1);
     }
 
-  bad_attach = 0;
-  pid = 0;
-  attached = 0;
-  if (argc >= 3 && strcmp (argv[2], "--attach") == 0)
+  port = *next_arg;
+  next_arg++;
+  if (port == NULL || (!attach && !multi_mode && *next_arg == NULL))
     {
-      if (argc == 4
-	  && argv[3][0] != '\0'
-	  && (pid = strtoul (argv[3], &arg_end, 0)) != 0
-	  && *arg_end == '\0')
-	{
-	  ;
-	}
-      else
-	bad_attach = 1;
+      gdbserver_usage ();
+      exit (1);
     }
 
-  if (argc < 3 || bad_attach)
+  bad_attach = 0;
+  pid = 0;
+
+  /* --attach used to come after PORT, so allow it there for
+       compatibility.  */
+  if (*next_arg != NULL && strcmp (*next_arg, "--attach") == 0)
+    {
+      attach = 1;
+      next_arg++;
+    }
+
+  if (attach
+      && (*next_arg == NULL
+	  || (*next_arg)[0] == '\0'
+	  || (pid = strtoul (*next_arg, &arg_end, 0)) == 0
+	  || *arg_end != '\0'
+	  || next_arg[1] != NULL))
+    bad_attach = 1;
+
+  if (bad_attach)
     {
       gdbserver_usage ();
       exit (1);
@@ -910,26 +1102,34 @@ main (int argc, char *argv[])
   own_buf = malloc (PBUFSIZ + 1);
   mem_buf = malloc (PBUFSIZ);
 
-  if (pid == 0)
+  if (pid == 0 && *next_arg != NULL)
     {
+      int i, n;
+
+      n = argc - (next_arg - argv);
+      program_argv = malloc (sizeof (char *) * (n + 1));
+      for (i = 0; i < n; i++)
+	program_argv[i] = strdup (next_arg[i]);
+      program_argv[i] = NULL;
+
       /* Wait till we are at first instruction in program.  */
-      signal = start_inferior (&argv[2], &status);
+      signal = start_inferior (program_argv, &status);
 
       /* We are now (hopefully) stopped at the first instruction of
 	 the target process.  This assumes that the target process was
 	 successfully created.  */
     }
+  else if (pid != 0)
+    {
+      if (attach_inferior (pid, &status, &signal) == -1)
+	error ("Attaching not supported on this target");
+
+      /* Otherwise succeeded.  */
+    }
   else
     {
-      switch (attach_inferior (pid, &status, &signal))
-	{
-	case -1:
-	  error ("Attaching not supported on this target");
-	  break;
-	default:
-	  attached = 1;
-	  break;
-	}
+      status = 'W';
+      signal = 0;
     }
 
   /* Don't report shared library events on the initial connection,
@@ -945,27 +1145,43 @@ main (int argc, char *argv[])
     }
 
   if (status == 'W' || status == 'X')
+    was_running = 0;
+  else
+    was_running = 1;
+
+  if (!was_running && !multi_mode)
     {
-      fprintf (stderr, "No inferior, GDBserver exiting.\n");
+      fprintf (stderr, "No program to debug.  GDBserver exiting.\n");
       exit (1);
     }
 
   while (1)
     {
-      remote_open (argv[1]);
+      remote_open (port);
 
     restart:
-      setjmp (toplevel);
+      if (setjmp (toplevel) != 0)
+	{
+	  /* An error occurred.  */
+	  if (response_needed)
+	    {
+	      write_enn (own_buf);
+	      putpkt (own_buf);
+	    }
+	}
+
       disable_async_io ();
-      while (1)
+      while (!exit_requested)
 	{
 	  unsigned char sig;
 	  int packet_len;
 	  int new_packet_len = -1;
 
+	  response_needed = 0;
 	  packet_len = getpkt (own_buf);
 	  if (packet_len <= 0)
 	    break;
+	  response_needed = 1;
 
 	  i = 0;
 	  ch = own_buf[i++];
@@ -978,39 +1194,38 @@ main (int argc, char *argv[])
 	      handle_general_set (own_buf);
 	      break;
 	    case 'D':
+	      require_running (own_buf);
 	      fprintf (stderr, "Detaching from inferior\n");
 	      if (detach_inferior () != 0)
-		{
-		  write_enn (own_buf);
-		  putpkt (own_buf);
-		}
+		write_enn (own_buf);
 	      else
 		{
 		  write_ok (own_buf);
-		  putpkt (own_buf);
-		  remote_close ();
 
-		  /* If we are attached, then we can exit.  Otherwise, we
-		     need to hang around doing nothing, until the child
-		     is gone.  */
-		  if (!attached)
-		    join_inferior ();
+		  if (extended_protocol)
+		    {
+		      /* Treat this like a normal program exit.  */
+		      signal = 0;
+		      status = 'W';
+		    }
+		  else
+		    {
+		      putpkt (own_buf);
+		      remote_close ();
 
-		  exit (0);
+		      /* If we are attached, then we can exit.  Otherwise, we
+			 need to hang around doing nothing, until the child
+			 is gone.  */
+		      if (!attached)
+			join_inferior ();
+
+		      exit (0);
+		    }
 		}
+	      break;
 	    case '!':
-	      if (attached == 0)
-		{
-		  extended_protocol = 1;
-		  prepare_resume_reply (own_buf, status, signal);
-		}
-	      else
-		{
-		  /* We can not use the extended protocol if we are
-		     attached, because we can not restart the running
-		     program.  So return unrecognized.  */
-		  own_buf[0] = '\0';
-		}
+	      extended_protocol = 1;
+	      write_ok (own_buf);
 	      break;
 	    case '?':
 	      prepare_resume_reply (own_buf, status, signal);
@@ -1020,12 +1235,18 @@ main (int argc, char *argv[])
 		{
 		  unsigned long gdb_id, thread_id;
 
+		  require_running (own_buf);
 		  gdb_id = strtoul (&own_buf[2], NULL, 16);
-		  thread_id = gdb_id_to_thread_id (gdb_id);
-		  if (thread_id == 0)
+		  if (gdb_id == 0 || gdb_id == -1)
+		    thread_id = gdb_id;
+		  else
 		    {
-		      write_enn (own_buf);
-		      break;
+		      thread_id = gdb_id_to_thread_id (gdb_id);
+		      if (thread_id == 0)
+			{
+			  write_enn (own_buf);
+			  break;
+			}
 		    }
 
 		  if (own_buf[1] == 'g')
@@ -1048,15 +1269,18 @@ main (int argc, char *argv[])
 		}
 	      break;
 	    case 'g':
+	      require_running (own_buf);
 	      set_desired_inferior (1);
 	      registers_to_string (own_buf);
 	      break;
 	    case 'G':
+	      require_running (own_buf);
 	      set_desired_inferior (1);
 	      registers_from_string (&own_buf[1]);
 	      write_ok (own_buf);
 	      break;
 	    case 'm':
+	      require_running (own_buf);
 	      decode_m_packet (&own_buf[1], &mem_addr, &len);
 	      if (read_inferior_memory (mem_addr, mem_buf, len) == 0)
 		convert_int_to_ascii (mem_buf, own_buf, len);
@@ -1064,6 +1288,7 @@ main (int argc, char *argv[])
 		write_enn (own_buf);
 	      break;
 	    case 'M':
+	      require_running (own_buf);
 	      decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
 	      if (write_inferior_memory (mem_addr, mem_buf, len) == 0)
 		write_ok (own_buf);
@@ -1071,6 +1296,7 @@ main (int argc, char *argv[])
 		write_enn (own_buf);
 	      break;
 	    case 'X':
+	      require_running (own_buf);
 	      if (decode_X_packet (&own_buf[1], packet_len - 1,
 				   &mem_addr, &len, mem_buf) < 0
 		  || write_inferior_memory (mem_addr, mem_buf, len) != 0)
@@ -1079,6 +1305,7 @@ main (int argc, char *argv[])
 		write_ok (own_buf);
 	      break;
 	    case 'C':
+	      require_running (own_buf);
 	      convert_ascii_to_int (own_buf + 1, &sig, 1);
 	      if (target_signal_to_host_p (sig))
 		signal = target_signal_to_host (sig);
@@ -1087,6 +1314,7 @@ main (int argc, char *argv[])
 	      myresume (own_buf, 0, &signal, &status);
 	      break;
 	    case 'S':
+	      require_running (own_buf);
 	      convert_ascii_to_int (own_buf + 1, &sig, 1);
 	      if (target_signal_to_host_p (sig))
 		signal = target_signal_to_host (sig);
@@ -1095,10 +1323,12 @@ main (int argc, char *argv[])
 	      myresume (own_buf, 1, &signal, &status);
 	      break;
 	    case 'c':
+	      require_running (own_buf);
 	      signal = 0;
 	      myresume (own_buf, 0, &signal, &status);
 	      break;
 	    case 's':
+	      require_running (own_buf);
 	      signal = 0;
 	      myresume (own_buf, 1, &signal, &status);
 	      break;
@@ -1121,6 +1351,7 @@ main (int argc, char *argv[])
 		  {
 		    int res;
 
+		    require_running (own_buf);
 		    res = (*the_target->insert_watchpoint) (type, addr, len);
 		    if (res == 0)
 		      write_ok (own_buf);
@@ -1151,6 +1382,7 @@ main (int argc, char *argv[])
 		  {
 		    int res;
 
+		    require_running (own_buf);
 		    res = (*the_target->remove_watchpoint) (type, addr, len);
 		    if (res == 0)
 		      write_ok (own_buf);
@@ -1163,20 +1395,24 @@ main (int argc, char *argv[])
 		break;
 	      }
 	    case 'k':
+	      response_needed = 0;
+	      if (!target_running ())
+		/* The packet we received doesn't make sense - but we
+		   can't reply to it, either.  */
+		goto restart;
+
 	      fprintf (stderr, "Killing inferior\n");
 	      kill_inferior ();
-	      /* When using the extended protocol, we start up a new
-	         debugging session.   The traditional protocol will
-	         exit instead.  */
+
+	      /* When using the extended protocol, we wait with no
+		 program running.  The traditional protocol will exit
+		 instead.  */
 	      if (extended_protocol)
 		{
-		  write_ok (own_buf);
-		  fprintf (stderr, "GDBserver restarting\n");
-
-		  /* Wait till we are at 1st instruction in prog.  */
-		  signal = start_inferior (&argv[2], &status);
+		  status = 'X';
+		  signal = TARGET_SIGNAL_KILL;
+		  was_running = 0;
 		  goto restart;
-		  break;
 		}
 	      else
 		{
@@ -1187,6 +1423,7 @@ main (int argc, char *argv[])
 	      {
 		unsigned long gdb_id, thread_id;
 
+		require_running (own_buf);
 		gdb_id = strtoul (&own_buf[1], NULL, 16);
 		thread_id = gdb_id_to_thread_id (gdb_id);
 		if (thread_id == 0)
@@ -1202,18 +1439,25 @@ main (int argc, char *argv[])
 	      }
 	      break;
 	    case 'R':
+	      response_needed = 0;
+
 	      /* Restarting the inferior is only supported in the
 	         extended protocol.  */
 	      if (extended_protocol)
 		{
-		  kill_inferior ();
-		  write_ok (own_buf);
+		  if (target_running ())
+		    kill_inferior ();
 		  fprintf (stderr, "GDBserver restarting\n");
 
 		  /* Wait till we are at 1st instruction in prog.  */
-		  signal = start_inferior (&argv[2], &status);
+		  if (program_argv != NULL)
+		    signal = start_inferior (program_argv, &status);
+		  else
+		    {
+		      status = 'X';
+		      signal = TARGET_SIGNAL_KILL;
+		    }
 		  goto restart;
-		  break;
 		}
 	      else
 		{
@@ -1242,45 +1486,45 @@ main (int argc, char *argv[])
 	  else
 	    putpkt (own_buf);
 
-	  if (status == 'W')
-	    fprintf (stderr,
-		     "\nChild exited with status %d\n", signal);
-	  if (status == 'X')
-	    fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
-		     target_signal_to_host (signal),
-		     target_signal_to_name (signal));
-	  if (status == 'W' || status == 'X')
+	  response_needed = 0;
+
+	  if (was_running && (status == 'W' || status == 'X'))
 	    {
-	      if (extended_protocol)
-		{
-		  fprintf (stderr, "Killing inferior\n");
-		  kill_inferior ();
-		  write_ok (own_buf);
-		  fprintf (stderr, "GDBserver restarting\n");
+	      was_running = 0;
 
-		  /* Wait till we are at 1st instruction in prog.  */
-		  signal = start_inferior (&argv[2], &status);
-		  goto restart;
-		  break;
-		}
+	      if (status == 'W')
+		fprintf (stderr,
+			 "\nChild exited with status %d\n", signal);
+	      if (status == 'X')
+		fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
+			 target_signal_to_host (signal),
+			 target_signal_to_name (signal));
+
+	      if (extended_protocol)
+		goto restart;
 	      else
 		{
 		  fprintf (stderr, "GDBserver exiting\n");
 		  exit (0);
 		}
 	    }
-	}
 
-      /* We come here when getpkt fails.
+	  if (status != 'W' && status != 'X')
+	    was_running = 1;
+	}
 
-         For the extended remote protocol we exit (and this is the only
-         way we gracefully exit!).
+      /* If an exit was requested (using the "monitor exit" command),
+	 terminate now.  The only other way to get here is for
+	 getpkt to fail; close the connection and reopen it at the
+	 top of the loop.  */
 
-         For the traditional remote protocol close the connection,
-         and re-open it at the top of the loop.  */
-      if (extended_protocol)
+      if (exit_requested)
 	{
 	  remote_close ();
+	  if (attached && target_running ())
+	    detach_inferior ();
+	  else if (target_running ())
+	    kill_inferior ();
 	  exit (0);
 	}
       else
Index: src/gdb/remote.c
===================================================================
--- src.orig/gdb/remote.c	2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/remote.c	2007-12-07 15:05:13.000000000 -0500
@@ -237,6 +237,15 @@ struct remote_state
      a buffer in the stub), this will be set to that packet size.
      Otherwise zero, meaning to use the guessed size.  */
   long explicit_packet_size;
+
+  /* remote_wait is normally called when the target is running and
+     waits for a stop reply packet.  But sometimes we need to call it
+     when the target is already stopped.  We can send a "?" packet
+     and have remote_wait read the response.  Or, if we already have
+     the response, we can stash it in BUF and tell remote_wait to
+     skip calling getpkt.  This flag is set when BUF contains a
+     stop reply packet and the target is not waiting.  */
+  int cached_wait_status;
 };
 
 /* This data could be associated with a target, but we do not always
@@ -514,6 +523,10 @@ static int remote_address_size;
 
 static int remote_async_terminal_ours_p;
 
+/* The executable file to use for "run" on the remote side.  */
+
+static char *remote_exec_file = "";
+
 
 /* User configurable variables for the number of characters in a
    memory read/write packet.  MIN (rsa->remote_packet_size,
@@ -920,6 +933,8 @@ enum {
   PACKET_qGetTLSAddr,
   PACKET_qSupported,
   PACKET_QPassSignals,
+  PACKET_vAttach,
+  PACKET_vRun,
   PACKET_MAX
 };
 
@@ -1998,11 +2013,6 @@ extended_remote_restart (void)
   putpkt (rs->buf);
 
   remote_fileio_reset ();
-
-  /* Now query for status so this looks just like we restarted
-     gdbserver from scratch.  */
-  putpkt ("?");
-  getpkt (&rs->buf, &rs->buf_size, 0);
 }
 
 /* Clean up connection to a remote debugger.  */
@@ -2164,27 +2174,79 @@ get_offsets (void)
 
 /* Stub for catch_exception.  */
 
+struct start_remote_args
+{
+  int from_tty;
+
+  /* The current target.  */
+  struct target_ops *target;
+
+  /* Non-zero if this is an extended-remote target.  */
+  int extended_p;
+};
+
 static void
-remote_start_remote (struct ui_out *uiout, void *from_tty_p)
+remote_start_remote (struct ui_out *uiout, void *opaque)
 {
-  int from_tty = * (int *) from_tty_p;
+  struct remote_state *rs = get_remote_state ();
+  struct start_remote_args *args = opaque;
+  char *wait_status = NULL;
 
   immediate_quit++;		/* Allow user to interrupt it.  */
 
   /* Ack any packet which the remote side has already sent.  */
   serial_write (remote_desc, "+", 1);
 
+  /* Check whether the target is running now.  */
+  putpkt ("?");
+  getpkt (&rs->buf, &rs->buf_size, 0);
+
+  if (rs->buf[0] == 'W' || rs->buf[0] == 'X')
+    {
+      if (args->extended_p)
+	{
+	  /* We're connected, but not running.  Drop out before we
+	     call start_remote.  */
+	  target_mark_exited (args->target);
+	  return;
+	}
+      else
+	error (_("The target is not running (try extended-remote?)"));
+    }
+  else
+    {
+      if (args->extended_p)
+	target_mark_running (args->target);
+
+      /* Save the reply for later.  */
+      wait_status = alloca (strlen (rs->buf) + 1);
+      strcpy (wait_status, rs->buf);
+    }
+
   /* Let the stub know that we want it to return the thread.  */
   set_thread (-1, 0);
 
+  /* Without this, some commands which require an active target
+     (such as kill) won't work.  This variable serves (at least)
+     double duty as both the pid of the target process (if it has
+     such), and as a flag indicating that a target is active.
+     These functions should be split out into seperate variables,
+     especially since GDB will someday have a notion of debugging
+     several processes.  */
+  inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
+
+  /* Now, if we have thread information, update inferior_ptid.  */
   inferior_ptid = remote_current_thread (inferior_ptid);
 
   get_offsets ();		/* Get text, data & bss offsets.  */
 
-  putpkt ("?");			/* Initiate a query from remote machine.  */
-  immediate_quit--;
+  /* Use the previously fetched status.  */
+  gdb_assert (wait_status != NULL);
+  strcpy (rs->buf, wait_status);
+  rs->cached_wait_status = 1;
 
-  start_remote (from_tty);	/* Initialize gdb process mechanisms.  */
+  immediate_quit--;
+  start_remote (args->from_tty); /* Initialize gdb process mechanisms.  */
 }
 
 /* Open a connection to a remote debugger.
@@ -2545,10 +2607,31 @@ remote_open_1 (char *name, int from_tty,
   if (!async_p)
     wait_forever_enabled_p = 1;
 
+  /* If we're connected to a running target, target_preopen will kill it.
+     But if we're connected to a target system with no running process,
+     then we will still be connected when it returns.  Ask this question
+     first, before target_preopen has a chance to kill anything.  */
+  if (remote_desc != NULL && !target_has_execution)
+    {
+      if (!from_tty
+	  || query (_("Already connected to a remote target.  Disconnect? ")))
+	pop_target ();
+      else
+	error (_("Still connected."));
+    }
+
   target_preopen (from_tty);
 
   unpush_target (target);
 
+  /* This time without a query.  If we were connected to an
+     extended-remote target and target_preopen killed the running
+     process, we may still be connected.  If we are starting "target
+     remote" now, the extended-remote target will not have been
+     removed by unpush_target.  */
+  if (remote_desc != NULL && !target_has_execution)
+    pop_target ();
+
   /* Make sure we send the passed signals list the next time we resume.  */
   xfree (last_pass_packet);
   last_pass_packet = NULL;
@@ -2589,6 +2672,9 @@ remote_open_1 (char *name, int from_tty,
     }
   push_target (target);		/* Switch to using remote target now.  */
 
+  /* Assume that the target is running, unless we learn otherwise.  */
+  target_mark_running (target);
+
   /* Reset the target state; these things will be queried either by
      remote_query_supported or as they are needed.  */
   init_all_packet_configs ();
@@ -2610,15 +2696,6 @@ remote_open_1 (char *name, int from_tty,
      this before anything involving memory or registers.  */
   target_find_description ();
 
-  /* Without this, some commands which require an active target (such
-     as kill) won't work.  This variable serves (at least) double duty
-     as both the pid of the target process (if it has such), and as a
-     flag indicating that a target is active.  These functions should
-     be split out into seperate variables, especially since GDB will
-     someday have a notion of debugging several processes.  */
-
-  inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
-
   if (async_p)
     {
       /* With this target we start out by owning the terminal.  */
@@ -2653,9 +2730,14 @@ remote_open_1 (char *name, int from_tty,
      all the ``target ....'' commands to share a common callback
      function.  See cli-dump.c.  */
   {
-    struct gdb_exception ex
-      = catch_exception (uiout, remote_start_remote, &from_tty,
-			 RETURN_MASK_ALL);
+    struct gdb_exception ex;
+    struct start_remote_args args;
+
+    args.from_tty = from_tty;
+    args.target = target;
+    args.extended_p = extended_p;
+
+    ex = catch_exception (uiout, remote_start_remote, &args, RETURN_MASK_ALL);
     if (ex.reason < 0)
       {
 	pop_target ();
@@ -2675,8 +2757,12 @@ remote_open_1 (char *name, int from_tty,
       getpkt (&rs->buf, &rs->buf_size, 0);
     }
 
-  if (exec_bfd) 	/* No use without an exec file.  */
-    remote_check_symbols (symfile_objfile);
+  /* If we connected to a live target, do some additional setup.  */
+  if (target_has_execution)
+    {
+      if (exec_bfd) 	/* No use without an exec file.  */
+	remote_check_symbols (symfile_objfile);
+    }
 }
 
 /* This takes a program previously attached to and detaches it.  After
@@ -2685,13 +2771,16 @@ remote_open_1 (char *name, int from_tty,
    die when it hits one.  */
 
 static void
-remote_detach (char *args, int from_tty)
+remote_detach_1 (char *args, int from_tty, int extended)
 {
   struct remote_state *rs = get_remote_state ();
 
   if (args)
     error (_("Argument given to \"detach\" when remotely debugging."));
 
+  if (!target_has_execution)
+    error (_("No process to detach from."));
+
   /* Tell the remote target to detach.  */
   strcpy (rs->buf, "D");
   putpkt (rs->buf);
@@ -2706,7 +2795,24 @@ remote_detach (char *args, int from_tty)
 
   target_mourn_inferior ();
   if (from_tty)
-    puts_filtered ("Ending remote debugging.\n");
+    {
+      if (extended)
+	puts_filtered ("Detached from remote process.\n");
+      else
+	puts_filtered ("Ending remote debugging.\n");
+    }
+}
+
+static void
+remote_detach (char *args, int from_tty)
+{
+  remote_detach_1 (args, from_tty, 0);
+}
+
+static void
+extended_remote_detach (char *args, int from_tty)
+{
+  remote_detach_1 (args, from_tty, 1);
 }
 
 /* Same as remote_detach, but don't send the "D" packet; just disconnect.  */
@@ -2715,17 +2821,78 @@ static void
 remote_disconnect (struct target_ops *target, char *args, int from_tty)
 {
   if (args)
-    error (_("Argument given to \"detach\" when remotely debugging."));
+    error (_("Argument given to \"disconnect\" when remotely debugging."));
 
   /* Unregister the file descriptor from the event loop.  */
   if (target_is_async_p ())
     serial_async (remote_desc, NULL, 0);
 
-  target_mourn_inferior ();
+  /* Make sure we unpush even the extended remote targets; mourn
+     won't do it.  So call remote_mourn_1 directly instead of
+     target_mourn_inferior.  */
+  remote_mourn_1 (target);
+
   if (from_tty)
     puts_filtered ("Ending remote debugging.\n");
 }
 
+/* Attach to the process specified by ARGS.  If FROM_TTY is non-zero,
+   be chatty about it.  */
+
+static void
+extended_remote_attach_1 (struct target_ops *target, char *args, int from_tty)
+{
+  struct remote_state *rs = get_remote_state ();
+  pid_t pid;
+  char *dummy;
+
+  if (!args)
+    error_no_arg (_("process-id to attach"));
+
+  dummy = args;
+  pid = strtol (args, &dummy, 0);
+  /* Some targets don't set errno on errors, grrr!  */
+  if (pid == 0 && args == dummy)
+    error (_("Illegal process-id: %s."), args);
+
+  if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE)
+    error (_("This target does not support attaching to a process"));
+
+  sprintf (rs->buf, "vAttach;%x", pid);
+  putpkt (rs->buf);
+  getpkt (&rs->buf, &rs->buf_size, 0);
+
+  if (packet_ok (rs->buf, &remote_protocol_packets[PACKET_vAttach]) == PACKET_OK)
+    {
+      if (from_tty)
+	printf_unfiltered (_("Attached to %s\n"),
+			   target_pid_to_str (pid_to_ptid (pid)));
+
+      /* We have a wait response; reuse it.  */
+      rs->cached_wait_status = 1;
+    }
+  else if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE)
+    error (_("This target does not support attaching to a process"));
+  else
+    error (_("Attaching to %s failed"),
+	   target_pid_to_str (pid_to_ptid (pid)));
+
+  target_mark_running (target);
+  inferior_ptid = pid_to_ptid (pid);
+}
+
+static void
+extended_remote_attach (char *args, int from_tty)
+{
+  extended_remote_attach_1 (&extended_remote_ops, args, from_tty);
+}
+
+static void
+extended_async_remote_attach (char *args, int from_tty)
+{
+  extended_remote_attach_1 (&extended_async_remote_ops, args, from_tty);
+}
+
 /* Convert hex digit A to a number.  */
 
 static int
@@ -2850,7 +3017,7 @@ remote_vcont_resume (ptid_t ptid, int st
 {
   struct remote_state *rs = get_remote_state ();
   int pid = PIDGET (ptid);
-  char *buf = NULL, *outbuf;
+  char *outbuf;
   struct cleanup *old_cleanup;
 
   if (remote_protocol_packets[PACKET_vCont].support == PACKET_SUPPORT_UNKNOWN)
@@ -3208,9 +3375,15 @@ remote_wait (ptid_t ptid, struct target_
     {
       char *buf, *p;
 
-      ofunc = signal (SIGINT, remote_interrupt);
-      getpkt (&rs->buf, &rs->buf_size, 1);
-      signal (SIGINT, ofunc);
+      if (rs->cached_wait_status)
+	/* Use the cached wait status, but only once.  */
+	rs->cached_wait_status = 0;
+      else
+	{
+	  ofunc = signal (SIGINT, remote_interrupt);
+	  getpkt (&rs->buf, &rs->buf_size, 1);
+	  signal (SIGINT, ofunc);
+	}
 
       buf = rs->buf;
 
@@ -3417,15 +3590,21 @@ remote_async_wait (ptid_t ptid, struct t
     {
       char *buf, *p;
 
-      if (!target_is_async_p ())
-	ofunc = signal (SIGINT, remote_interrupt);
-      /* FIXME: cagney/1999-09-27: If we're in async mode we should
-         _never_ wait for ever -> test on target_is_async_p().
-         However, before we do that we need to ensure that the caller
-         knows how to take the target into/out of async mode.  */
-      getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p);
-      if (!target_is_async_p ())
-	signal (SIGINT, ofunc);
+      if (rs->cached_wait_status)
+	/* Use the cached wait status, but only once.  */
+	rs->cached_wait_status = 0;
+      else
+	{
+	  if (!target_is_async_p ())
+	    ofunc = signal (SIGINT, remote_interrupt);
+	  /* FIXME: cagney/1999-09-27: If we're in async mode we should
+	     _never_ wait for ever -> test on target_is_async_p().
+	     However, before we do that we need to ensure that the caller
+	     knows how to take the target into/out of async mode.  */
+	  getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p);
+	  if (!target_is_async_p ())
+	    signal (SIGINT, ofunc);
+	}
 
       buf = rs->buf;
 
@@ -4694,6 +4873,7 @@ putpkt (char *buf)
 static int
 putpkt_binary (char *buf, int cnt)
 {
+  struct remote_state *rs = get_remote_state ();
   int i;
   unsigned char csum = 0;
   char *buf2 = alloca (cnt + 6);
@@ -4702,6 +4882,10 @@ putpkt_binary (char *buf, int cnt)
   int tcount = 0;
   char *p;
 
+  /* We're sending out a new packet.  Make sure we don't look at a
+     stale cached response.  */
+  rs->cached_wait_status = 0;
+
   /* Copy the packet into buffer BUF2, encapsulating it
      and giving it a checksum.  */
 
@@ -5003,11 +5187,16 @@ getpkt (char **buf,
 static int
 getpkt_sane (char **buf, long *sizeof_buf, int forever)
 {
+  struct remote_state *rs = get_remote_state ();
   int c;
   int tries;
   int timeout;
   int val;
 
+  /* We're reading a new response.  Make sure we don't look at a
+     previously cached response.  */
+  rs->cached_wait_status = 0;
+
   strcpy (*buf, "timeout");
 
   if (forever)
@@ -5139,19 +5328,6 @@ remote_async_mourn (void)
   remote_mourn_1 (&remote_async_ops);
 }
 
-static void
-extended_remote_mourn (void)
-{
-  /* We do _not_ want to mourn the target like this; this will
-     remove the extended remote target  from the target stack,
-     and the next time the user says "run" it'll fail.
-
-     FIXME: What is the right thing to do here?  */
-#if 0
-  remote_mourn_1 (&extended_remote_ops);
-#endif
-}
-
 /* Worker function for remote_mourn.  */
 static void
 remote_mourn_1 (struct target_ops *target)
@@ -5160,71 +5336,167 @@ remote_mourn_1 (struct target_ops *targe
   generic_mourn_inferior ();
 }
 
-/* In the extended protocol we want to be able to do things like
-   "run" and have them basically work as expected.  So we need
-   a special create_inferior function.
+static void
+extended_remote_mourn_1 (struct target_ops *target)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  /* Unlike "target remote", we do not want to unpush the target; then
+     the next time the user says "run", we won't be connected.  */
+
+  /* Call common code to mark the inferior as not running.  */
+  generic_mourn_inferior ();
+
+  /* Check whether the target is running now - some remote stubs
+     automatically restart after kill.  */
+  putpkt ("?");
+  getpkt (&rs->buf, &rs->buf_size, 0);
+
+  if (rs->buf[0] == 'S' || rs->buf[0] == 'T')
+    {
+      /* Assume that the target has been restarted.  Set inferior_ptid
+	 so that bits of core GDB realizes there's something here, e.g.,
+	 so that the user can say "kill" again.  */
+      inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
+    }
+  else
+    {
+      /* Mark this (still pushed) target as not executable until we
+	 restart it.  */
+      target_mark_exited (target);
+    }
+}
 
-   FIXME: One day add support for changing the exec file
-   we're debugging, arguments and an environment.  */
+static void
+extended_remote_mourn (void)
+{
+  extended_remote_mourn_1 (&extended_remote_ops);
+}
 
 static void
-extended_remote_create_inferior (char *exec_file, char *args,
-				 char **env, int from_tty)
+extended_async_remote_mourn (void)
+{
+  extended_remote_mourn_1 (&extended_async_remote_ops);
+}
+
+static int
+extended_remote_run (char *args)
 {
-  /* Rip out the breakpoints; we'll reinsert them after restarting
-     the remote server.  */
-  remove_breakpoints ();
+  struct remote_state *rs = get_remote_state ();
+  char *p;
+  int len;
 
-  /* Now restart the remote server.  */
-  extended_remote_restart ();
+  /* If the user has disabled vRun support, or we have detected that
+     support is not available, do not try it.  */
+  if (remote_protocol_packets[PACKET_vRun].support == PACKET_DISABLE)
+    return -1;
 
-  /* NOTE: We don't need to recheck for a target description here; but
-     if we gain the ability to switch the remote executable we may
-     need to, if for instance we are running a process which requested
-     different emulated hardware from the operating system.  A
-     concrete example of this is ARM GNU/Linux, where some binaries
-     will have a legacy FPA coprocessor emulated and others may have
-     access to a hardware VFP unit.  */
-
-  /* Now put the breakpoints back in.  This way we're safe if the
-     restart function works via a unix fork on the remote side.  */
-  insert_breakpoints ();
+  strcpy (rs->buf, "vRun;");
+  len = strlen (rs->buf);
 
-  /* Clean up from the last time we were running.  */
-  clear_proceed_status ();
+  if (strlen (remote_exec_file) * 2 + len >= get_remote_packet_size ())
+    error (_("Remote file name too long for run packet"));
+  len += 2 * bin2hex ((gdb_byte *) remote_exec_file, rs->buf + len, 0);
+
+  if (*args)
+    {
+      struct cleanup *back_to;
+      int i;
+      char **argv;
+
+      argv = buildargv (args);
+      back_to = make_cleanup ((void (*) (void *)) freeargv, argv);
+      for (i = 0; argv[i] != NULL; i++)
+	{
+	  if (strlen (argv[i]) * 2 + 1 + len >= get_remote_packet_size ())
+	    error (_("Argument list too long for run packet"));
+	  rs->buf[len++] = ';';
+	  len += 2 * bin2hex ((gdb_byte *) argv[i], rs->buf + len, 0);
+	}
+      do_cleanups (back_to);
+    }
+
+  rs->buf[len++] = '\0';
+
+  putpkt (rs->buf);
+  getpkt (&rs->buf, &rs->buf_size, 0);
+
+  if (packet_ok (rs->buf, &remote_protocol_packets[PACKET_vRun]) == PACKET_OK)
+    {
+      /* We have a wait response; we don't need it, though.  All is well.  */
+      return 0;
+    }
+  else if (remote_protocol_packets[PACKET_vRun].support == PACKET_DISABLE)
+    /* It wasn't disabled before, but it is now.  */
+    return -1;
+  else
+    {
+      if (remote_exec_file == NULL)
+	error (_("Running the default executable on the remote target failed; "
+		 "try \"set remote exec-file\"?"));
+      else
+	error (_("Running \"%s\" on the remote target failed"),
+	       remote_exec_file);
+    }
 }
 
-/* Async version of extended_remote_create_inferior.  */
+/* In the extended protocol we want to be able to do things like
+   "run" and have them basically work as expected.  So we need
+   a special create_inferior function.  We support changing the
+   executable file and the command line arguments, but not the
+   environment.  */
+
 static void
-extended_remote_async_create_inferior (char *exec_file, char *args,
-				       char **env, int from_tty)
+extended_remote_create_inferior_1 (char *exec_file, char *args,
+				   char **env, int from_tty,
+				   int async_p)
 {
-  /* Rip out the breakpoints; we'll reinsert them after restarting
-     the remote server.  */
-  remove_breakpoints ();
-
   /* If running asynchronously, register the target file descriptor
      with the event loop.  */
-  if (target_can_async_p ())
+  if (async_p && target_can_async_p ())
     target_async (inferior_event_handler, 0);
 
   /* Now restart the remote server.  */
-  extended_remote_restart ();
+  if (extended_remote_run (args) == -1)
+    {
+      /* vRun was not supported.  Fail if we need it to do what the
+	 user requested.  */
+      if (remote_exec_file[0])
+	error (_("Remote target does not support \"set remote exec-file\""));
+      if (args[0])
+	error (_("Remote target does not support \"set args\" or run <ARGS>"));
+
+      /* Fall back to "R".  */
+      extended_remote_restart ();
+    }
+
+  /* Now mark the inferior as running before we do anything else.  */
+  inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
+  if (async_p)
+    target_mark_running (&extended_async_remote_ops);
+  else
+    target_mark_running (&extended_remote_ops);
 
-  /* NOTE: We don't need to recheck for a target description here; but
-     if we gain the ability to switch the remote executable we may
-     need to, if for instance we are running a process which requested
-     different emulated hardware from the operating system.  A
-     concrete example of this is ARM GNU/Linux, where some binaries
-     will have a legacy FPA coprocessor emulated and others may have
-     access to a hardware VFP unit.  */
-
-  /* Now put the breakpoints back in.  This way we're safe if the
-     restart function works via a unix fork on the remote side.  */
-  insert_breakpoints ();
+  /* Get updated offsets, if the stub uses qOffsets.  */
+  get_offsets ();
 
   /* Clean up from the last time we were running.  */
-  clear_proceed_status ();
+  init_thread_list ();
+  init_wait_for_inferior ();
+}
+
+static void
+extended_remote_create_inferior (char *exec_file, char *args,
+				 char **env, int from_tty)
+{
+  extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 0);
+}
+
+static void
+extended_remote_async_create_inferior (char *exec_file, char *args,
+				       char **env, int from_tty)
+{
+  extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 1);
 }
 
 
@@ -5782,6 +6054,12 @@ remote_xfer_partial (struct target_ops *
       int xfered;
       errno = 0;
 
+      /* If the remote target is connected but not running, we should
+	 pass this request down to a lower stratum (e.g. the executable
+	 file).  */
+      if (!target_has_execution)
+	return 0;
+
       if (writebuf != NULL)
 	xfered = remote_write_bytes (offset, writebuf, len);
       else
@@ -6982,6 +7260,8 @@ Specify the serial device it is connecte
     extended_remote_ops.to_open = extended_remote_open;
   extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
   extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
+  extended_remote_ops.to_detach = extended_remote_detach;
+  extended_remote_ops.to_attach = extended_remote_attach;
 }
 
 static int
@@ -7114,7 +7394,9 @@ init_extended_async_remote_ops (void)
 Specify the serial device it is connected to (e.g. /dev/ttya).",
     extended_async_remote_ops.to_open = extended_remote_async_open;
   extended_async_remote_ops.to_create_inferior = extended_remote_async_create_inferior;
-  extended_async_remote_ops.to_mourn_inferior = extended_remote_mourn;
+  extended_async_remote_ops.to_mourn_inferior = extended_async_remote_mourn;
+  extended_async_remote_ops.to_detach = extended_remote_detach;
+  extended_async_remote_ops.to_attach = extended_async_remote_attach;
 }
 
 static void
@@ -7369,6 +7651,12 @@ Show the maximum size of the address (in
   add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_unlink],
 			 "vFile:unlink", "hostio-unlink", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_vAttach],
+			 "vAttach", "attach", 0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_vRun],
+			 "vRun", "run", 0);
+
   /* Keep the old ``set remote Z-packet ...'' working.  Each individual
      Z sub-packet has its own set and show commands, but users may
      have sets to this variable in their .gdbinit files (or in their
@@ -7401,6 +7689,13 @@ Transfer files to and from the remote ta
 	   _("Delete a remote file."),
 	   &remote_cmdlist);
 
+  remote_exec_file = xstrdup ("");
+  add_setshow_string_noescape_cmd ("exec-file", class_files,
+				   &remote_exec_file, _("\
+Set the remote pathname for \"run\""), _("\
+Show the remote pathname for \"run\""), NULL, NULL, NULL,
+				   &remote_set_cmdlist, &remote_show_cmdlist);
+
   /* Eventually initialize fileio.  See fileio.c */
   initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist);
 }
Index: src/gdb/testsuite/gdb.server/ext-attach.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.server/ext-attach.c	2007-12-07 10:38:30.000000000 -0500
@@ -0,0 +1,31 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2007 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/>.  */
+
+/* This program is intended to be started outside of gdb, and then
+   attached to by gdb.  It loops for a while, but not forever.  */
+
+#include <unistd.h>
+
+int main ()
+{
+  int i;
+
+  for (i = 0; i < 120; i++)
+    sleep (1);
+
+  return 0;
+}
Index: src/gdb/testsuite/gdb.server/ext-attach.exp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.server/ext-attach.exp	2007-12-07 10:38:30.000000000 -0500
@@ -0,0 +1,77 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2007 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 attaching to already-running programs using extended-remote.
+
+load_lib gdbserver-support.exp
+
+set testfile "ext-attach"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [skip_gdbserver_tests] } {
+    return 0
+}
+
+# On SPU, this test currently fails because "sleep" is not supported.
+if { [istarget "spu*-*-*"] } {
+	return 0
+}
+
+# We need to use TCL's exec to get the pid.
+if [is_remote target] then {
+    return 0
+}
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    untested ext-attach.exp
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_load $binfile
+gdb_reinitialize_dir $srcdir/$subdir
+
+set target_exec [gdbserver_download]
+gdbserver_start_extended
+
+gdb_test "set remote exec-file $target_exec" "" "set remote exec-file"
+
+# Start the program running and then wait for a bit, to be sure
+# that it can be attached to.
+set testpid [eval exec $binfile &]
+exec sleep 2
+if { [istarget "*-*-cygwin*"] } {
+    # testpid is the Cygwin PID, GDB uses the Windows PID, which might be
+    # different due to the way fork/exec works.
+    set testpid [ exec ps -e | gawk "{ if (\$1 == $testpid) print \$4; }" ]
+}
+
+gdb_test "attach $testpid" "Attached to.*" \
+    "attach to remote program 1"
+gdb_test "backtrace" ".*main.*" "backtrace 1"
+
+gdb_test "detach" "Detached from remote process\\."
+gdb_test "backtrace" "No stack\\." "backtrace with no program"
+
+gdb_test "attach $testpid" "Attached to.*" \
+    "attach to remote program 2"
+gdb_test "backtrace" ".*main.*" "backtrace 2"
+
+gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y"
+gdb_test "monitor exit" ""
Index: src/gdb/testsuite/gdb.server/ext-run.exp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.server/ext-run.exp	2007-12-07 10:38:30.000000000 -0500
@@ -0,0 +1,48 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2007 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 running programs using extended-remote.
+
+load_lib gdbserver-support.exp
+
+set testfile "server"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [skip_gdbserver_tests] } {
+    return 0
+}
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_load $binfile
+gdb_reinitialize_dir $srcdir/$subdir
+
+set target_exec [gdbserver_download]
+gdbserver_start_extended
+
+gdb_test "set remote exec-file $target_exec" "" "set remote exec-file"
+
+gdb_breakpoint main
+gdb_test "run" "Breakpoint.* main .*" "continue to main"
+
+gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y"
+gdb_test "monitor exit" ""
Index: src/gdb/testsuite/lib/gdbserver-support.exp
===================================================================
--- src.orig/gdb/testsuite/lib/gdbserver-support.exp	2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/testsuite/lib/gdbserver-support.exp	2007-12-07 10:38:30.000000000 -0500
@@ -129,13 +129,10 @@ proc skip_gdbserver_tests { } {
   return 0
 }
 
-# Start a gdbserver process running SERVER_EXEC, and connect GDB
-# to it.  CHILD_ARGS are passed to the inferior.
-#
-# Returns the target protocol and socket to connect to.
+# Download the currently loaded program to the target if necessary.
+# Return the target system filename.
 
-proc gdbserver_spawn { child_args } {
-    global portnum
+proc gdbserver_download { } {
     global gdbserver_host_exec
     global gdbserver_host_mtime
     global gdbserver_server_exec
@@ -169,6 +166,17 @@ proc gdbserver_spawn { child_args } {
 	}
     }
 
+    return $gdbserver_server_exec
+}
+
+# Start a gdbserver process with initial OPTIONS and trailing ARGUMENTS.
+# The port will be filled in between them automatically.
+#
+# Returns the target protocol and socket to connect to.
+
+proc gdbserver_start { options arguments } {
+    global portnum
+
     # Port id -- either specified in baseboard file, or managed here.
     if [target_info exists gdb,socketport] {
 	set portnum [target_info gdb,socketport]
@@ -179,7 +187,7 @@ proc gdbserver_spawn { child_args } {
 
     # Extract the local and remote host ids from the target board struct.
     if [target_info exists sockethost] {
-	set debughost  [target_info sockethost]
+	set debughost [target_info sockethost]
     } else {
 	set debughost "localhost:"
     }
@@ -196,23 +204,23 @@ proc gdbserver_spawn { child_args } {
     # Export the host:port pair.
     set gdbport $debughost$portnum
 
-    # Fire off the debug agent.  This flavour of gdbserver takes as
-    # arguments the port information, the name of the executable file to
-    # be debugged, and any arguments.
-    set gdbserver_command "$gdbserver :$portnum $gdbserver_server_exec"
-    if { $child_args != "" } {
-	append gdbserver_command " $child_args"
+    # Fire off the debug agent.
+    set gdbserver_command "$gdbserver"
+    if { $options != "" } {
+	append gdbserver_command " $options"
+    }
+    append gdbserver_command " :$portnum"
+    if { $arguments != "" } {
+	append gdbserver_command " $arguments"
     }
 
     set server_spawn_id [remote_spawn target $gdbserver_command]
 
-    # Wait for the server to produce at least one line and an additional
-    # character of output.  This will wait until any TCP socket has been
-    # created, so that GDB can connect.
+    # Wait for the server to open its TCP socket, so that GDB can connect.
     expect {
 	-i $server_spawn_id
 	-notransfer
-	-re ".*\n." { }
+	-re "Listening on" { }
     }
 
     # We can't just call close, because if gdbserver is local then that means
@@ -231,6 +239,24 @@ proc gdbserver_spawn { child_args } {
     return [list $protocol $gdbport]
 }
 
+# Start a gdbserver process running SERVER_EXEC, and connect GDB
+# to it.  CHILD_ARGS are passed to the inferior.
+#
+# Returns the target protocol and socket to connect to.
+
+proc gdbserver_spawn { child_args } {
+    set target_exec [gdbserver_download]
+
+    # Fire off the debug agent.  This flavour of gdbserver takes as
+    # arguments the port information, the name of the executable file to
+    # be debugged, and any arguments.
+    set arguments "$target_exec"
+    if { $child_args != "" } {
+	append arguments " $child_args"
+    }
+    return [gdbserver_start "" $arguments]
+}
+
 # Start a gdbserver process running HOST_EXEC and pass CHILD_ARGS
 # to it.  Return 0 on success, or non-zero on failure.
 
@@ -268,3 +294,12 @@ proc gdbserver_reconnect { } {
 
     return [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport]
 }
+
+# Start and connect to a gdbserver in extended mode.
+proc gdbserver_start_extended { } {
+    set res [gdbserver_start "--multi" ""]
+    set gdbserver_protocol "extended-[lindex $res 0]"
+    set gdbserver_gdbport [lindex $res 1]
+
+    return [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport]
+}
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS	2007-12-07 16:09:57.000000000 -0500
+++ src/gdb/NEWS	2007-12-07 16:13:30.000000000 -0500
@@ -35,6 +35,9 @@ targets even when the libthread_db libra
 * The GDB remote stub, gdbserver, now supports the new file transfer
 commands (remote put, remote get, and remote delete).
 
+* The GDB remote stub, gdbserver, now supports run and attach in
+extended-remote mode.
+
 * hppa*64*-*-hpux11* target broken
   The debugger is unable to start a program and fails with the following
   error: "Error trying to get information about dynamic linker".
@@ -67,6 +70,13 @@ vFile:pwrite:
 vFile:unlink:
   Open, close, read, write, and delete files on the remote system.
 
+vAttach
+  Attach to an existing process on the remote system, in extended-remote
+  mode.
+
+vRun
+  Run a new process on the remote system, in extended-remote mode.
+
 *** Changes in GDB 6.7
 
 * Resolved 101 resource leaks, null pointer dereferences, etc. in gdb, 


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