* linux-low.c (linux_attach_lwp): Do not _exit after errors.
authorDaniel Jacobowitz <drow@false.org>
Wed, 30 Jan 2008 00:51:50 +0000 (00:51 +0000)
committerDaniel Jacobowitz <drow@false.org>
Wed, 30 Jan 2008 00:51:50 +0000 (00:51 +0000)
(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.

* 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.

* 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.

* 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.

15 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/gdbserver/ChangeLog
gdb/gdbserver/README
gdb/gdbserver/linux-low.c
gdb/gdbserver/remote-utils.c
gdb/gdbserver/server.c
gdb/remote.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.server/ext-attach.c [new file with mode: 0644]
gdb/testsuite/gdb.server/ext-attach.exp [new file with mode: 0644]
gdb/testsuite/gdb.server/ext-run.exp [new file with mode: 0644]
gdb/testsuite/lib/gdbserver-support.exp

index 15d2cd080954304f79bfa768b6827a144c84b197..faa51f1b5866ff09a565be63d5b9fe908268a5bc 100644 (file)
@@ -1,3 +1,42 @@
+2008-01-29  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.
+
 2008-01-29  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * Makefile.in (symfile.o): Update.
index 44d08c666c256a0ff17bd09527e334fb55f7435e..ea55423c5737036fc8fd9337acd2befaadae6c61 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -50,6 +50,9 @@ targets even when the libthread_db library is not available.
 * 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".
@@ -85,6 +88,13 @@ vFile:unlink:
 * GDB on GNU/Linux and HP/UX can now debug through "exec" of a new
 process.
 
+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 5cafe2c6a0a51270abdc3cfda234cacc1c3329a9..b5d144c80e887e55d7efb6c4c087e826a83134dc 100644 (file)
@@ -1,3 +1,13 @@
+2008-01-29  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.
+
 2008-01-30  Nick Roberts  <nickrob@snap.net.nz>
 
        * gdb.texinfo (Processes): Mention process command.
index 7566294812d18c94074f2f43ffcad9f9076e5e7f..c61327094232967760bc2817db69de9f5defcea3 100644 (file)
@@ -12952,9 +12952,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.
@@ -12997,11 +13007,13 @@ conflicts with another service, @code{gdbserver} prints an error message
 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
@@ -13013,18 +13025,56 @@ You can debug processes by name instead of process ID if your target has the
 @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}
+@cindex gdbserver, multiple processes
+@cindex multiple processes with 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
@@ -13038,19 +13088,17 @@ Connect to your target (@pxref{Connecting,,Connecting to a Remote Target}).
 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
@@ -13065,6 +13113,13 @@ Disable or enable general debugging messages.
 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.
+Use @code{monitor exit} to terminate @code{gdbserver} at the end
+of a multi-process mode debug session.
+
 @end table
 
 @node Remote Configuration
@@ -13159,6 +13214,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
@@ -13205,10 +13269,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}
@@ -23298,6 +23370,7 @@ Here are the packet descriptions.
 
 @item !
 @cindex @samp{!} packet
+@anchor{extended mode}
 Enable extended mode.  In extended mode, the remote server is made
 persistent.  The @samp{R} packet is used to restart the program being
 debugged.
@@ -23563,7 +23636,7 @@ Don't use this packet; use the @samp{R} packet instead.
 @item R @var{XX}
 @cindex @samp{R} packet
 Restart the program being debugged.  @var{XX}, while needed, is ignored.
-This packet is only available in extended mode.
+This packet is only available in extended mode (@pxref{extended mode}).
 
 The @samp{R} packet has no reply.
 
@@ -23606,6 +23679,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 (@pxref{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.
@@ -23702,6 +23791,24 @@ The stub is permitted to delay or batch the effects of a group of
 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 (@pxref{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
index ced28cd5368660b752c29047d65cd5961f51a44d..5a6e7b9e920058135f0c7394ba86d2b98548f918 100644 (file)
@@ -1,3 +1,31 @@
+2008-01-29  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-20  Andreas Schwab  <schwab@suse.de>
 
        * linux-low.c (STACK_SIZE): Define.
index eda00ba36d050cac70f2c4072de5cd08430fdd4e..9394198c0e844a9dec929bccd3abdacc137ad108 100644 (file)
@@ -9,6 +9,8 @@ host.  GDB and GDBserver communicate using the standard remote serial protocol
 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 e5cb5282da74f9731f92763734c9dcdea26e7e36..f5bf5dce472f411076da572958a7a2adb9678b34 100644 (file)
@@ -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 68ed939a83cfe9a86a0fcd2b127ee186e242c023..2e049ee22a4cb783da42f52ffc0d046b2a40bc31 100644 (file)
@@ -187,15 +187,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)
@@ -575,7 +575,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] != '+');
@@ -617,7 +617,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 a6cb1252b65cce5810d1edc943c1e0e53a0fa787..51dfbcce08c8671b748af5cee11d5ee4d27abc37 100644 (file)
@@ -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;
@@ -68,9 +74,17 @@ 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 *statusptr, int *sigptr)
   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_len, int *new_packet_len_p)
   /* 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_len, int *new_packet_len_p)
 
   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_len, int *new_packet_len_p)
 
   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_len, int *new_packet_len_p)
 
   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_len, int *new_packet_len_p)
       && 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_len, int *new_packet_len_p)
       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_len, int *new_packet_len_p)
       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_len, int *new_packet_len_p)
       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_len, int *new_packet_len_p)
       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_len, int *new_packet_len_p)
       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_len, int *new_packet_len_p)
 
       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_len, int *new_packet_len_p)
       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_len, int *new_packet_len_p)
        }
       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 *status, int *signal,
 {
   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 *status, int *signal,
       && 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 *signalp, char *statusp)
   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);
     }
 
+  port = *next_arg;
+  next_arg++;
+  if (port == NULL || (!attach && !multi_mode && *next_arg == NULL))
+    {
+      gdbserver_usage ();
+      exit (1);
+    }
+
   bad_attach = 0;
   pid = 0;
-  attached = 0;
-  if (argc >= 3 && strcmp (argv[2], "--attach") == 0)
+
+  /* --attach used to come after PORT, so allow it there for
+       compatibility.  */
+  if (*next_arg != NULL && strcmp (*next_arg, "--attach") == 0)
     {
-      if (argc == 4
-         && argv[3][0] != '\0'
-         && (pid = strtoul (argv[3], &arg_end, 0)) != 0
-         && *arg_end == '\0')
-       {
-         ;
-       }
-      else
-       bad_attach = 1;
+      attach = 1;
+      next_arg++;
     }
 
-  if (argc < 3 || bad_attach)
+  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 12397716717a58f1b908f3e6cb2a5efe35a964bb..9139ea82b30f265fce2a2adc5d79d889a7d2c151 100644 (file)
@@ -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 = "";
+
 \f
 /* 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
 };
 
@@ -1993,11 +2008,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);
 }
 \f
 /* Clean up connection to a remote debugger.  */
@@ -2159,27 +2169,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.
@@ -2540,10 +2602,31 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
   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;
@@ -2584,6 +2667,9 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
     }
   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 ();
@@ -2605,15 +2691,6 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
      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.  */
@@ -2648,9 +2725,14 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
      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 ();
@@ -2670,8 +2752,12 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
       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
@@ -2680,13 +2766,16 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
    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);
@@ -2701,7 +2790,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.  */
@@ -2710,17 +2816,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
@@ -2845,7 +3012,7 @@ remote_vcont_resume (ptid_t ptid, int step, enum target_signal siggnal)
 {
   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)
@@ -3203,16 +3370,22 @@ remote_wait (ptid_t ptid, struct target_waitstatus *status)
     {
       char *buf, *p;
 
-      ofunc = signal (SIGINT, remote_interrupt);
-      /* If the user hit C-c before this packet, or between packets,
-        pretend that it was hit right here.  */
-      if (quit_flag)
+      if (rs->cached_wait_status)
+       /* Use the cached wait status, but only once.  */
+       rs->cached_wait_status = 0;
+      else
        {
-         quit_flag = 0;
-         remote_interrupt (SIGINT);
+         ofunc = signal (SIGINT, remote_interrupt);
+         /* If the user hit C-c before this packet, or between packets,
+            pretend that it was hit right here.  */
+         if (quit_flag)
+           {
+             quit_flag = 0;
+             remote_interrupt (SIGINT);
+           }
+         getpkt (&rs->buf, &rs->buf_size, 1);
+         signal (SIGINT, ofunc);
        }
-      getpkt (&rs->buf, &rs->buf_size, 1);
-      signal (SIGINT, ofunc);
 
       buf = rs->buf;
 
@@ -3419,24 +3592,30 @@ remote_async_wait (ptid_t ptid, struct target_waitstatus *status)
     {
       char *buf, *p;
 
-      if (!target_is_async_p ())
+      if (rs->cached_wait_status)
+       /* Use the cached wait status, but only once.  */
+       rs->cached_wait_status = 0;
+      else
        {
-         ofunc = signal (SIGINT, remote_interrupt);
-         /* If the user hit C-c before this packet, or between packets,
-            pretend that it was hit right here.  */
-         if (quit_flag)
+         if (!target_is_async_p ())
            {
-             quit_flag = 0;
-             remote_interrupt (SIGINT);
+             ofunc = signal (SIGINT, remote_interrupt);
+             /* If the user hit C-c before this packet, or between packets,
+                pretend that it was hit right here.  */
+             if (quit_flag)
+               {
+                 quit_flag = 0;
+                 remote_interrupt (SIGINT);
+               }
            }
+         /* 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);
        }
-      /* 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;
 
@@ -4705,6 +4884,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);
@@ -4713,6 +4893,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.  */
 
@@ -5014,11 +5198,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)
@@ -5150,19 +5339,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)
@@ -5171,71 +5347,167 @@ remote_mourn_1 (struct target_ops *target)
   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 ();
 
-   FIXME: One day add support for changing the exec file
-   we're debugging, arguments and an environment.  */
+  /* 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);
+    }
+}
 
 static void
-extended_remote_create_inferior (char *exec_file, char *args,
-                                char **env, int from_tty)
+extended_remote_mourn (void)
 {
-  /* Rip out the breakpoints; we'll reinsert them after restarting
-     the remote server.  */
-  remove_breakpoints ();
+  extended_remote_mourn_1 (&extended_remote_ops);
+}
 
-  /* Now restart the remote server.  */
-  extended_remote_restart ();
+static void
+extended_async_remote_mourn (void)
+{
+  extended_remote_mourn_1 (&extended_async_remote_ops);
+}
+
+static int
+extended_remote_run (char *args)
+{
+  struct remote_state *rs = get_remote_state ();
+  char *p;
+  int len;
 
-  /* 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.  */
+  /* 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;
 
-  /* 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[0] == '\0')
+       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>"));
 
-  /* 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.  */
+      /* Fall back to "R".  */
+      extended_remote_restart ();
+    }
 
-  /* 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 ();
+  /* 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);
+
+  /* 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);
 }
 \f
 
@@ -5793,6 +6065,12 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
       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
@@ -6994,6 +7272,8 @@ Specify the serial device it is connected to (e.g. /dev/ttya).",
     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
@@ -7126,7 +7406,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
@@ -7381,6 +7663,12 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   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
@@ -7413,6 +7701,13 @@ Transfer files to and from the remote target system."),
           _("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 3de37e7728ec0ee9d680ffeb31ef261936aecc3f..32542a2f7a18e258865cc7c7d5ca4e2d802ccab0 100644 (file)
@@ -1,3 +1,13 @@
+2008-01-29  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.
+
 2008-01-29  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * gdb.base/foll-exec.exp: Update header.  Skip on remote targets.
diff --git a/gdb/testsuite/gdb.server/ext-attach.c b/gdb/testsuite/gdb.server/ext-attach.c
new file mode 100644 (file)
index 0000000..db7c0f8
--- /dev/null
@@ -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;
+}
diff --git a/gdb/testsuite/gdb.server/ext-attach.exp b/gdb/testsuite/gdb.server/ext-attach.exp
new file mode 100644 (file)
index 0000000..4ab1bb6
--- /dev/null
@@ -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" ""
diff --git a/gdb/testsuite/gdb.server/ext-run.exp b/gdb/testsuite/gdb.server/ext-run.exp
new file mode 100644 (file)
index 0000000..5af5611
--- /dev/null
@@ -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 50dafe01937df937a8523da1305b57e66d3120e3..6fbaa224386cd7b891559a77c1f78dc47ffad739 100644 (file)
@@ -132,13 +132,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
@@ -172,6 +169,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]
@@ -182,7 +190,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:"
     }
@@ -199,23 +207,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
@@ -234,6 +242,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.
 
@@ -271,3 +297,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]
+}