(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.
+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.
* 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".
* 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,
+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.
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.
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
@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
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
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
@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
@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}
@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.
@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.
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.
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
+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.
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
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.
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);
/* 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
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;
}
#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)
}
/* 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] != '+');
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);
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;
}
#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);
if (myattach (pid) != 0)
return -1;
+ attached = 1;
+
fprintf (stderr, "Attached; pid = %d\n", pid);
fflush (stderr);
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)
/* 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));
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");
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;
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));
&& 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);
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;
CORE_ADDR ofs;
unsigned char *spu_buf;
+ require_running (own_buf);
strcpy (own_buf, "E00");
spu_buf = malloc (packet_len - 15);
if (!spu_buf)
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')
const char *document;
char *annex;
+ require_running (own_buf);
+
/* Check for support. */
document = get_features_xml ("target.xml");
if (document == NULL)
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')
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+");
CORE_ADDR parts[3], address = 0;
int i, err;
+ require_running (own_buf);
+
for (i = 0; i < 3; i++)
{
char *p2;
}
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");
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,
{
if (strncmp (own_buf, "vCont;", 6) == 0)
{
+ require_running (own_buf);
handle_v_cont (own_buf, status, signal);
return;
}
&& 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;
disable_async_io ();
}
-static int attached;
-
static void
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[])
{
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))
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);
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,
}
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++];
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);
{
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')
}
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);
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);
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)
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);
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);
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;
{
int res;
+ require_running (own_buf);
res = (*the_target->insert_watchpoint) (type, addr, len);
if (res == 0)
write_ok (own_buf);
{
int res;
+ require_running (own_buf);
res = (*the_target->remove_watchpoint) (type, addr, len);
if (res == 0)
write_ok (own_buf);
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
{
{
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)
}
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
{
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
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
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,
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
+ PACKET_vAttach,
+ PACKET_vRun,
PACKET_MAX
};
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. */
/* 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.
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;
}
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 ();
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. */
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 ();
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
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);
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. */
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
{
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)
{
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;
{
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;
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);
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. */
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)
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)
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
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
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
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
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
_("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);
}
+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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+# 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" ""
--- /dev/null
+# 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" ""
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
}
}
+ 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]
# 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:"
}
# 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
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.
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]
+}