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

	* 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.
This commit is contained in:
Daniel Jacobowitz
2008-01-30 00:51:50 +00:00
parent 9dc513fc0e
commit 2d717e4f8a
15 changed files with 1223 additions and 277 deletions

View 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> 2008-01-29 Daniel Jacobowitz <dan@codesourcery.com>
* Makefile.in (symfile.o): Update. * Makefile.in (symfile.o): Update.

View File

@ -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 * The GDB remote stub, gdbserver, now supports the new file transfer
commands (remote put, remote get, and remote delete). 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 * hppa*64*-*-hpux11* target broken
The debugger is unable to start a program and fails with the following The debugger is unable to start a program and fails with the following
error: "Error trying to get information about dynamic linker". 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 * GDB on GNU/Linux and HP/UX can now debug through "exec" of a new
process. 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 *** Changes in GDB 6.7
* Resolved 101 resource leaks, null pointer dereferences, etc. in gdb, * Resolved 101 resource leaks, null pointer dereferences, etc. in gdb,

View 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> 2008-01-30 Nick Roberts <nickrob@snap.net.nz>
* gdb.texinfo (Processes): Mention process command. * gdb.texinfo (Processes): Mention process command.

View File

@ -12952,9 +12952,19 @@ choice for debugging.
or a TCP connection, using the standard @value{GDBN} remote serial or a TCP connection, using the standard @value{GDBN} remote serial
protocol. protocol.
@table @emph @quotation
@item On the target machine, @emph{Warning:} @code{gdbserver} does not have any built-in security.
you need to have a copy of the program you want to debug. 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 @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 strip the program if necessary to save space. @value{GDBN} on the host
system does all the symbol handling. 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} and exits.} You must use the same port number with the host @value{GDBN}
@code{target remote} command. @code{target remote} command.
@subsubsection Attaching to a Running Program
On some targets, @code{gdbserver} can also attach to running programs. On some targets, @code{gdbserver} can also attach to running programs.
This is accomplished via the @code{--attach} argument. The syntax is: This is accomplished via the @code{--attach} argument. The syntax is:
@smallexample @smallexample
target> gdbserver @var{comm} --attach @var{pid} target> gdbserver --attach @var{comm} @var{pid}
@end smallexample @end smallexample
@var{pid} is the process ID of a currently running process. It isn't necessary @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: @code{pidof} utility:
@smallexample @smallexample
target> gdbserver @var{comm} --attach `pidof @var{program}` target> gdbserver --attach @var{comm} `pidof @var{program}`
@end smallexample @end smallexample
In case more than one copy of @var{program} is running, or @var{program} In case more than one copy of @var{program} is running, or @var{program}
has multiple threads, most versions of @code{pidof} support the has multiple threads, most versions of @code{pidof} support the
@code{-s} option to only return the first process ID. @code{-s} option to only return the first process ID.
@item On the host machine, @subsubsection Multi-Process Mode for @code{gdbserver}
first make sure you have the necessary symbol files. Load symbols for @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 your application using the @code{file} command before you connect. Use
@code{set sysroot} to locate target libraries (unless your @value{GDBN} @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 The symbol file and target libraries must exactly match the executable
and libraries on the target, with one exception: the files on the host 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 For TCP connections, you must start up @code{gdbserver} prior to using
the @code{target remote} command. Otherwise you may get an error whose the @code{target remote} command. Otherwise you may get an error whose
text depends on the host system, but which usually looks something like 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 command in @value{GDBN} when using @code{gdbserver}, since the program is
already on the target. already on the target.
@end table
@subsection Monitor Commands for @code{gdbserver} @subsection Monitor Commands for @code{gdbserver}
@cindex 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 During a @value{GDBN} session using @code{gdbserver}, you can use the
@code{monitor} command to send special requests to @code{gdbserver}. @code{monitor} command to send special requests to @code{gdbserver}.
Here are the available commands; they are only of interest when Here are the available commands.
debugging @value{GDBN} or @code{gdbserver}.
@table @code @table @code
@item monitor help @item monitor help
@ -13065,6 +13113,13 @@ Disable or enable general debugging messages.
Disable or enable specific debugging messages associated with the remote Disable or enable specific debugging messages associated with the remote
protocol (@pxref{Remote Protocol}). 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 @end table
@node Remote Configuration @node Remote Configuration
@ -13159,6 +13214,15 @@ responses.
@itemx set remote hardware-breakpoint-limit @var{limit} @itemx set remote hardware-breakpoint-limit @var{limit}
Restrict @value{GDBN} to using @var{limit} remote hardware breakpoint or Restrict @value{GDBN} to using @var{limit} remote hardware breakpoint or
watchpoints. A limit of -1, the default, is treated as unlimited. 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 @end table
@cindex remote packets, enabling and disabling @cindex remote packets, enabling and disabling
@ -13205,10 +13269,18 @@ are:
@tab @code{qSymbol} @tab @code{qSymbol}
@tab Detecting multiple threads @tab Detecting multiple threads
@item @code{attach}
@tab @code{vAttach}
@tab @code{attach}
@item @code{verbose-resume} @item @code{verbose-resume}
@tab @code{vCont} @tab @code{vCont}
@tab Stepping or resuming multiple threads @tab Stepping or resuming multiple threads
@item @code{run}
@tab @code{vRun}
@tab @code{run}
@item @code{software-breakpoint} @item @code{software-breakpoint}
@tab @code{Z0} @tab @code{Z0}
@tab @code{break} @tab @code{break}
@ -23298,6 +23370,7 @@ Here are the packet descriptions.
@item ! @item !
@cindex @samp{!} packet @cindex @samp{!} packet
@anchor{extended mode}
Enable extended mode. In extended mode, the remote server is made Enable extended mode. In extended mode, the remote server is made
persistent. The @samp{R} packet is used to restart the program being persistent. The @samp{R} packet is used to restart the program being
debugged. debugged.
@ -23563,7 +23636,7 @@ Don't use this packet; use the @samp{R} packet instead.
@item R @var{XX} @item R @var{XX}
@cindex @samp{R} packet @cindex @samp{R} packet
Restart the program being debugged. @var{XX}, while needed, is ignored. 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. 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, 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). 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{} @item vCont@r{[};@var{action}@r{[}:@var{tid}@r{]]}@dots{}
@cindex @samp{vCont} packet @cindex @samp{vCont} packet
Resume the inferior, specifying different actions for each thread. 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} regions of flash memory are unpredictable until the @samp{vFlashDone}
request is completed. 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{}} @item X @var{addr},@var{length}:@var{XX@dots{}}
@anchor{X packet} @anchor{X packet}
@cindex @samp{X} packet @cindex @samp{X} packet

View 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> 2007-12-20 Andreas Schwab <schwab@suse.de>
* linux-low.c (STACK_SIZE): Define. * linux-low.c (STACK_SIZE): Define.

View 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 implemented in remote.c, and various *-stub.c files. They communicate via
either a serial line or a TCP connection. either a serial line or a TCP connection.
For more information about GDBserver, see the GDB manual.
Usage (server (target) side): Usage (server (target) side):
First, you need to have a copy of the program you want to debug put onto 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 On some targets, gdbserver can also attach to running programs. This is
accomplished via the --attach argument. The syntax 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 PID is the process ID of a currently running process. It isn't necessary
to point gdbserver at a binary for the running process. to point gdbserver at a binary for the running process.

View File

@ -304,14 +304,18 @@ linux_attach_lwp (unsigned long pid)
if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0) 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); 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); 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. */ /* Make sure it died. The loop is most likely unnecessary. */
wstat = linux_wait_for_event (thread); wstat = linux_wait_for_event (thread);
} while (WIFSTOPPED (wstat)); } while (WIFSTOPPED (wstat));
clear_inferiors ();
free (all_processes.head);
all_processes.head = all_processes.tail = NULL;
} }
static void static void
@ -434,6 +442,8 @@ linux_detach (void)
delete_all_breakpoints (); delete_all_breakpoints ();
for_each_inferior (&all_threads, linux_detach_one_process); for_each_inferior (&all_threads, linux_detach_one_process);
clear_inferiors (); clear_inferiors ();
free (all_processes.head);
all_processes.head = all_processes.tail = NULL;
return 0; return 0;
} }

View File

@ -187,15 +187,15 @@ remote_open (char *name)
#ifdef USE_WIN32API #ifdef USE_WIN32API
static int winsock_initialized; static int winsock_initialized;
#endif #endif
char *port_str;
int port; int port;
struct sockaddr_in sockaddr; struct sockaddr_in sockaddr;
socklen_t tmp; socklen_t tmp;
int tmp_desc; int tmp_desc;
char *port_end;
port_str = strchr (name, ':'); port = strtoul (port_str + 1, &port_end, 10);
if (port_str[1] == '\0' || *port_end != '\0')
port = atoi (port_str + 1); fatal ("Bad port argument: %s", name);
#ifdef USE_WIN32API #ifdef USE_WIN32API
if (!winsock_initialized) if (!winsock_initialized)
@ -575,7 +575,7 @@ putpkt_binary (char *buf, int cnt)
} }
/* Check for an input interrupt while we're here. */ /* Check for an input interrupt while we're here. */
if (buf3[0] == '\003') if (buf3[0] == '\003' && current_inferior != NULL)
(*the_target->request_interrupt) (); (*the_target->request_interrupt) ();
} }
while (buf3[0] != '+'); while (buf3[0] != '+');
@ -617,7 +617,7 @@ input_interrupt (int unused)
cc = read (remote_desc, &c, 1); 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", fprintf (stderr, "input_interrupt, count = %d c = %d ('%c')\n",
cc, c, c); cc, c, c);

View File

@ -34,9 +34,15 @@ unsigned long general_thread;
unsigned long step_thread; unsigned long step_thread;
unsigned long thread_from_wait; unsigned long thread_from_wait;
unsigned long old_thread_from_wait; unsigned long old_thread_from_wait;
int extended_protocol;
int server_waiting; 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 /* Enable miscellaneous debugging output. The name is historical - it
was originally used to debug LinuxThreads support. */ was originally used to debug LinuxThreads support. */
int debug_threads; int debug_threads;
@ -68,9 +74,17 @@ restore_old_foreground_pgrp (void)
} }
#endif #endif
static int
target_running (void)
{
return all_threads.head != NULL;
}
static int static int
start_inferior (char *argv[], char *statusptr) start_inferior (char *argv[], char *statusptr)
{ {
attached = 0;
#ifdef SIGTTOU #ifdef SIGTTOU
signal (SIGTTOU, SIG_DFL); signal (SIGTTOU, SIG_DFL);
signal (SIGTTIN, SIG_DFL); signal (SIGTTIN, SIG_DFL);
@ -107,6 +121,8 @@ attach_inferior (int pid, char *statusptr, int *sigptr)
if (myattach (pid) != 0) if (myattach (pid) != 0)
return -1; return -1;
attached = 1;
fprintf (stderr, "Attached; pid = %d\n", pid); fprintf (stderr, "Attached; pid = %d\n", pid);
fflush (stderr); fflush (stderr);
@ -254,6 +270,13 @@ monitor_show_help (void)
monitor_output (" Enable remote protocol debugging messages\n"); 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. */ /* Handle all of the extended 'q' packets. */
void void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p) 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. */ /* Reply the current thread id. */
if (strcmp ("qC", own_buf) == 0) if (strcmp ("qC", own_buf) == 0)
{ {
require_running (own_buf);
thread_ptr = all_threads.head; thread_ptr = all_threads.head;
sprintf (own_buf, "QC%x", sprintf (own_buf, "QC%x",
thread_to_gdb_id ((struct thread_info *)thread_ptr)); 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 (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) (); (*the_target->look_up_symbols) ();
strcpy (own_buf, "OK"); 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) if (strcmp ("qfThreadInfo", own_buf) == 0)
{ {
require_running (own_buf);
thread_ptr = all_threads.head; thread_ptr = all_threads.head;
sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
thread_ptr = thread_ptr->next; 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) if (strcmp ("qsThreadInfo", own_buf) == 0)
{ {
require_running (own_buf);
if (thread_ptr != NULL) if (thread_ptr != NULL)
{ {
sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); 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) && strcmp ("qOffsets", own_buf) == 0)
{ {
CORE_ADDR text, data; CORE_ADDR text, data;
require_running (own_buf);
if (the_target->read_offsets (&text, &data)) if (the_target->read_offsets (&text, &data))
sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX", sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX",
(long)text, (long)data, (long)data); (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; CORE_ADDR ofs;
unsigned char *spu_buf; unsigned char *spu_buf;
require_running (own_buf);
strcpy (own_buf, "E00"); strcpy (own_buf, "E00");
if (decode_xfer_read (own_buf + 15, &annex, &ofs, &len) < 0) if (decode_xfer_read (own_buf + 15, &annex, &ofs, &len) < 0)
return; return;
@ -356,6 +384,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
CORE_ADDR ofs; CORE_ADDR ofs;
unsigned char *spu_buf; unsigned char *spu_buf;
require_running (own_buf);
strcpy (own_buf, "E00"); strcpy (own_buf, "E00");
spu_buf = malloc (packet_len - 15); spu_buf = malloc (packet_len - 15);
if (!spu_buf) if (!spu_buf)
@ -387,6 +416,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
unsigned int len; unsigned int len;
char *annex; char *annex;
require_running (own_buf);
/* Reject any annex; grab the offset and length. */ /* Reject any annex; grab the offset and length. */
if (decode_xfer_read (own_buf + 16, &annex, &ofs, &len) < 0 if (decode_xfer_read (own_buf + 16, &annex, &ofs, &len) < 0
|| annex[0] != '\0') || annex[0] != '\0')
@ -420,6 +451,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
const char *document; const char *document;
char *annex; char *annex;
require_running (own_buf);
/* Check for support. */ /* Check for support. */
document = get_features_xml ("target.xml"); document = get_features_xml ("target.xml");
if (document == NULL) 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; struct inferior_list_entry *dll_ptr;
char *annex; char *annex;
require_running (own_buf);
/* Reject any annex; grab the offset and length. */ /* Reject any annex; grab the offset and length. */
if (decode_xfer_read (own_buf + 21, &annex, &ofs, &len) < 0 if (decode_xfer_read (own_buf + 21, &annex, &ofs, &len) < 0
|| annex[0] != '\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) if (the_target->read_auxv != NULL)
strcat (own_buf, ";qXfer:auxv:read+"); strcat (own_buf, ";qXfer:auxv:read+");
if (the_target->qxfer_spu != NULL) if (the_target->qxfer_spu != NULL)
strcat (own_buf, ";qXfer:spu:read+;qXfer:spu:write+"); 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; CORE_ADDR parts[3], address = 0;
int i, err; int i, err;
require_running (own_buf);
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
{ {
char *p2; 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) else if (strcmp (mon, "help") == 0)
monitor_show_help (); monitor_show_help ();
else if (strcmp (mon, "exit") == 0)
exit_requested = 1;
else else
{ {
monitor_output ("Unknown monitor command.\n\n"); monitor_output ("Unknown monitor command.\n\n");
@ -772,6 +811,95 @@ err:
return; 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. */ /* Handle all of the extended 'v' packets. */
void void
handle_v_requests (char *own_buf, char *status, int *signal, 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) if (strncmp (own_buf, "vCont;", 6) == 0)
{ {
require_running (own_buf);
handle_v_cont (own_buf, status, signal); handle_v_cont (own_buf, status, signal);
return; return;
} }
@ -793,6 +922,28 @@ handle_v_requests (char *own_buf, char *status, int *signal,
&& handle_vFile (own_buf, packet_len, new_packet_len)) && handle_vFile (own_buf, packet_len, new_packet_len))
return; 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 /* Otherwise we didn't know what packet it was. Say we didn't
understand it. */ understand it. */
own_buf[0] = 0; own_buf[0] = 0;
@ -829,8 +980,6 @@ myresume (char *own_buf, int step, int *signalp, char *statusp)
disable_async_io (); disable_async_io ();
} }
static int attached;
static void static void
gdbserver_version (void) gdbserver_version (void)
{ {
@ -844,13 +993,25 @@ gdbserver_version (void)
static void static void
gdbserver_usage (void) gdbserver_usage (void)
{ {
printf ("Usage:\tgdbserver COMM PROG [ARGS ...]\n" printf ("Usage:\tgdbserver [OPTIONS] COMM PROG [ARGS ...]\n"
"\tgdbserver COMM --attach PID\n" "\tgdbserver [OPTIONS] --attach COMM PID\n"
"\tgdbserver [OPTIONS] --multi COMM\n"
"\n" "\n"
"COMM may either be a tty device (for serial debugging), or \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 int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
@ -862,18 +1023,38 @@ main (int argc, char *argv[])
CORE_ADDR mem_addr; CORE_ADDR mem_addr;
int bad_attach; int bad_attach;
int pid; 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 (); if (strcmp (*next_arg, "--version") == 0)
exit (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) next_arg++;
{ continue;
gdbserver_usage ();
exit (0);
} }
if (setjmp (toplevel)) if (setjmp (toplevel))
@ -882,23 +1063,34 @@ main (int argc, char *argv[])
exit (1); exit (1);
} }
bad_attach = 0; port = *next_arg;
pid = 0; next_arg++;
attached = 0; if (port == NULL || (!attach && !multi_mode && *next_arg == NULL))
if (argc >= 3 && strcmp (argv[2], "--attach") == 0)
{ {
if (argc == 4 gdbserver_usage ();
&& argv[3][0] != '\0' exit (1);
&& (pid = strtoul (argv[3], &arg_end, 0)) != 0
&& *arg_end == '\0')
{
;
}
else
bad_attach = 1;
} }
if (argc < 3 || bad_attach) bad_attach = 0;
pid = 0;
/* --attach used to come after PORT, so allow it there for
compatibility. */
if (*next_arg != NULL && strcmp (*next_arg, "--attach") == 0)
{
attach = 1;
next_arg++;
}
if (attach
&& (*next_arg == NULL
|| (*next_arg)[0] == '\0'
|| (pid = strtoul (*next_arg, &arg_end, 0)) == 0
|| *arg_end != '\0'
|| next_arg[1] != NULL))
bad_attach = 1;
if (bad_attach)
{ {
gdbserver_usage (); gdbserver_usage ();
exit (1); exit (1);
@ -910,26 +1102,34 @@ main (int argc, char *argv[])
own_buf = malloc (PBUFSIZ + 1); own_buf = malloc (PBUFSIZ + 1);
mem_buf = malloc (PBUFSIZ); 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. */ /* 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 /* We are now (hopefully) stopped at the first instruction of
the target process. This assumes that the target process was the target process. This assumes that the target process was
successfully created. */ successfully created. */
} }
else if (pid != 0)
{
if (attach_inferior (pid, &status, &signal) == -1)
error ("Attaching not supported on this target");
/* Otherwise succeeded. */
}
else else
{ {
switch (attach_inferior (pid, &status, &signal)) status = 'W';
{ signal = 0;
case -1:
error ("Attaching not supported on this target");
break;
default:
attached = 1;
break;
}
} }
/* Don't report shared library events on the initial connection, /* Don't report shared library events on the initial connection,
@ -945,27 +1145,43 @@ main (int argc, char *argv[])
} }
if (status == 'W' || status == 'X') 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); exit (1);
} }
while (1) while (1)
{ {
remote_open (argv[1]); remote_open (port);
restart: restart:
setjmp (toplevel); if (setjmp (toplevel) != 0)
{
/* An error occurred. */
if (response_needed)
{
write_enn (own_buf);
putpkt (own_buf);
}
}
disable_async_io (); disable_async_io ();
while (1) while (!exit_requested)
{ {
unsigned char sig; unsigned char sig;
int packet_len; int packet_len;
int new_packet_len = -1; int new_packet_len = -1;
response_needed = 0;
packet_len = getpkt (own_buf); packet_len = getpkt (own_buf);
if (packet_len <= 0) if (packet_len <= 0)
break; break;
response_needed = 1;
i = 0; i = 0;
ch = own_buf[i++]; ch = own_buf[i++];
@ -978,39 +1194,38 @@ main (int argc, char *argv[])
handle_general_set (own_buf); handle_general_set (own_buf);
break; break;
case 'D': case 'D':
require_running (own_buf);
fprintf (stderr, "Detaching from inferior\n"); fprintf (stderr, "Detaching from inferior\n");
if (detach_inferior () != 0) if (detach_inferior () != 0)
{ write_enn (own_buf);
write_enn (own_buf);
putpkt (own_buf);
}
else else
{ {
write_ok (own_buf); write_ok (own_buf);
putpkt (own_buf);
remote_close ();
/* If we are attached, then we can exit. Otherwise, we if (extended_protocol)
need to hang around doing nothing, until the child {
is gone. */ /* Treat this like a normal program exit. */
if (!attached) signal = 0;
join_inferior (); 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 '!': case '!':
if (attached == 0) extended_protocol = 1;
{ write_ok (own_buf);
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';
}
break; break;
case '?': case '?':
prepare_resume_reply (own_buf, status, signal); prepare_resume_reply (own_buf, status, signal);
@ -1020,12 +1235,18 @@ main (int argc, char *argv[])
{ {
unsigned long gdb_id, thread_id; unsigned long gdb_id, thread_id;
require_running (own_buf);
gdb_id = strtoul (&own_buf[2], NULL, 16); gdb_id = strtoul (&own_buf[2], NULL, 16);
thread_id = gdb_id_to_thread_id (gdb_id); if (gdb_id == 0 || gdb_id == -1)
if (thread_id == 0) thread_id = gdb_id;
else
{ {
write_enn (own_buf); thread_id = gdb_id_to_thread_id (gdb_id);
break; if (thread_id == 0)
{
write_enn (own_buf);
break;
}
} }
if (own_buf[1] == 'g') if (own_buf[1] == 'g')
@ -1048,15 +1269,18 @@ main (int argc, char *argv[])
} }
break; break;
case 'g': case 'g':
require_running (own_buf);
set_desired_inferior (1); set_desired_inferior (1);
registers_to_string (own_buf); registers_to_string (own_buf);
break; break;
case 'G': case 'G':
require_running (own_buf);
set_desired_inferior (1); set_desired_inferior (1);
registers_from_string (&own_buf[1]); registers_from_string (&own_buf[1]);
write_ok (own_buf); write_ok (own_buf);
break; break;
case 'm': case 'm':
require_running (own_buf);
decode_m_packet (&own_buf[1], &mem_addr, &len); decode_m_packet (&own_buf[1], &mem_addr, &len);
if (read_inferior_memory (mem_addr, mem_buf, len) == 0) if (read_inferior_memory (mem_addr, mem_buf, len) == 0)
convert_int_to_ascii (mem_buf, own_buf, len); convert_int_to_ascii (mem_buf, own_buf, len);
@ -1064,6 +1288,7 @@ main (int argc, char *argv[])
write_enn (own_buf); write_enn (own_buf);
break; break;
case 'M': case 'M':
require_running (own_buf);
decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
if (write_inferior_memory (mem_addr, mem_buf, len) == 0) if (write_inferior_memory (mem_addr, mem_buf, len) == 0)
write_ok (own_buf); write_ok (own_buf);
@ -1071,6 +1296,7 @@ main (int argc, char *argv[])
write_enn (own_buf); write_enn (own_buf);
break; break;
case 'X': case 'X':
require_running (own_buf);
if (decode_X_packet (&own_buf[1], packet_len - 1, if (decode_X_packet (&own_buf[1], packet_len - 1,
&mem_addr, &len, mem_buf) < 0 &mem_addr, &len, mem_buf) < 0
|| write_inferior_memory (mem_addr, mem_buf, len) != 0) || write_inferior_memory (mem_addr, mem_buf, len) != 0)
@ -1079,6 +1305,7 @@ main (int argc, char *argv[])
write_ok (own_buf); write_ok (own_buf);
break; break;
case 'C': case 'C':
require_running (own_buf);
convert_ascii_to_int (own_buf + 1, &sig, 1); convert_ascii_to_int (own_buf + 1, &sig, 1);
if (target_signal_to_host_p (sig)) if (target_signal_to_host_p (sig))
signal = target_signal_to_host (sig); signal = target_signal_to_host (sig);
@ -1087,6 +1314,7 @@ main (int argc, char *argv[])
myresume (own_buf, 0, &signal, &status); myresume (own_buf, 0, &signal, &status);
break; break;
case 'S': case 'S':
require_running (own_buf);
convert_ascii_to_int (own_buf + 1, &sig, 1); convert_ascii_to_int (own_buf + 1, &sig, 1);
if (target_signal_to_host_p (sig)) if (target_signal_to_host_p (sig))
signal = target_signal_to_host (sig); signal = target_signal_to_host (sig);
@ -1095,10 +1323,12 @@ main (int argc, char *argv[])
myresume (own_buf, 1, &signal, &status); myresume (own_buf, 1, &signal, &status);
break; break;
case 'c': case 'c':
require_running (own_buf);
signal = 0; signal = 0;
myresume (own_buf, 0, &signal, &status); myresume (own_buf, 0, &signal, &status);
break; break;
case 's': case 's':
require_running (own_buf);
signal = 0; signal = 0;
myresume (own_buf, 1, &signal, &status); myresume (own_buf, 1, &signal, &status);
break; break;
@ -1121,6 +1351,7 @@ main (int argc, char *argv[])
{ {
int res; int res;
require_running (own_buf);
res = (*the_target->insert_watchpoint) (type, addr, len); res = (*the_target->insert_watchpoint) (type, addr, len);
if (res == 0) if (res == 0)
write_ok (own_buf); write_ok (own_buf);
@ -1151,6 +1382,7 @@ main (int argc, char *argv[])
{ {
int res; int res;
require_running (own_buf);
res = (*the_target->remove_watchpoint) (type, addr, len); res = (*the_target->remove_watchpoint) (type, addr, len);
if (res == 0) if (res == 0)
write_ok (own_buf); write_ok (own_buf);
@ -1163,20 +1395,24 @@ main (int argc, char *argv[])
break; break;
} }
case 'k': 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"); fprintf (stderr, "Killing inferior\n");
kill_inferior (); kill_inferior ();
/* When using the extended protocol, we start up a new
debugging session. The traditional protocol will /* When using the extended protocol, we wait with no
exit instead. */ program running. The traditional protocol will exit
instead. */
if (extended_protocol) if (extended_protocol)
{ {
write_ok (own_buf); status = 'X';
fprintf (stderr, "GDBserver restarting\n"); signal = TARGET_SIGNAL_KILL;
was_running = 0;
/* Wait till we are at 1st instruction in prog. */
signal = start_inferior (&argv[2], &status);
goto restart; goto restart;
break;
} }
else else
{ {
@ -1187,6 +1423,7 @@ main (int argc, char *argv[])
{ {
unsigned long gdb_id, thread_id; unsigned long gdb_id, thread_id;
require_running (own_buf);
gdb_id = strtoul (&own_buf[1], NULL, 16); gdb_id = strtoul (&own_buf[1], NULL, 16);
thread_id = gdb_id_to_thread_id (gdb_id); thread_id = gdb_id_to_thread_id (gdb_id);
if (thread_id == 0) if (thread_id == 0)
@ -1202,18 +1439,25 @@ main (int argc, char *argv[])
} }
break; break;
case 'R': case 'R':
response_needed = 0;
/* Restarting the inferior is only supported in the /* Restarting the inferior is only supported in the
extended protocol. */ extended protocol. */
if (extended_protocol) if (extended_protocol)
{ {
kill_inferior (); if (target_running ())
write_ok (own_buf); kill_inferior ();
fprintf (stderr, "GDBserver restarting\n"); fprintf (stderr, "GDBserver restarting\n");
/* Wait till we are at 1st instruction in prog. */ /* 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; goto restart;
break;
} }
else else
{ {
@ -1242,45 +1486,45 @@ main (int argc, char *argv[])
else else
putpkt (own_buf); putpkt (own_buf);
if (status == 'W') response_needed = 0;
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')
{
if (extended_protocol)
{
fprintf (stderr, "Killing inferior\n");
kill_inferior ();
write_ok (own_buf);
fprintf (stderr, "GDBserver restarting\n");
/* Wait till we are at 1st instruction in prog. */ if (was_running && (status == 'W' || status == 'X'))
signal = start_inferior (&argv[2], &status); {
goto restart; was_running = 0;
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 else
{ {
fprintf (stderr, "GDBserver exiting\n"); fprintf (stderr, "GDBserver exiting\n");
exit (0); exit (0);
} }
} }
if (status != 'W' && status != 'X')
was_running = 1;
} }
/* We come here when getpkt fails. /* 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 extended remote protocol we exit (and this is the only if (exit_requested)
way we gracefully exit!).
For the traditional remote protocol close the connection,
and re-open it at the top of the loop. */
if (extended_protocol)
{ {
remote_close (); remote_close ();
if (attached && target_running ())
detach_inferior ();
else if (target_running ())
kill_inferior ();
exit (0); exit (0);
} }
else else

View File

@ -237,6 +237,15 @@ struct remote_state
a buffer in the stub), this will be set to that packet size. a buffer in the stub), this will be set to that packet size.
Otherwise zero, meaning to use the guessed size. */ Otherwise zero, meaning to use the guessed size. */
long explicit_packet_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 /* 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; static int remote_async_terminal_ours_p;
/* The executable file to use for "run" on the remote side. */
static char *remote_exec_file = "";
/* User configurable variables for the number of characters in a /* User configurable variables for the number of characters in a
memory read/write packet. MIN (rsa->remote_packet_size, memory read/write packet. MIN (rsa->remote_packet_size,
@ -920,6 +933,8 @@ enum {
PACKET_qGetTLSAddr, PACKET_qGetTLSAddr,
PACKET_qSupported, PACKET_qSupported,
PACKET_QPassSignals, PACKET_QPassSignals,
PACKET_vAttach,
PACKET_vRun,
PACKET_MAX PACKET_MAX
}; };
@ -1993,11 +2008,6 @@ extended_remote_restart (void)
putpkt (rs->buf); putpkt (rs->buf);
remote_fileio_reset (); remote_fileio_reset ();
/* Now query for status so this looks just like we restarted
gdbserver from scratch. */
putpkt ("?");
getpkt (&rs->buf, &rs->buf_size, 0);
} }
/* Clean up connection to a remote debugger. */ /* Clean up connection to a remote debugger. */
@ -2159,27 +2169,79 @@ get_offsets (void)
/* Stub for catch_exception. */ /* Stub for catch_exception. */
static void struct start_remote_args
remote_start_remote (struct ui_out *uiout, void *from_tty_p)
{ {
int from_tty = * (int *) from_tty_p; 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 *opaque)
{
struct remote_state *rs = get_remote_state ();
struct start_remote_args *args = opaque;
char *wait_status = NULL;
immediate_quit++; /* Allow user to interrupt it. */ immediate_quit++; /* Allow user to interrupt it. */
/* Ack any packet which the remote side has already sent. */ /* Ack any packet which the remote side has already sent. */
serial_write (remote_desc, "+", 1); 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. */ /* Let the stub know that we want it to return the thread. */
set_thread (-1, 0); 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); inferior_ptid = remote_current_thread (inferior_ptid);
get_offsets (); /* Get text, data & bss offsets. */ get_offsets (); /* Get text, data & bss offsets. */
putpkt ("?"); /* Initiate a query from remote machine. */ /* Use the previously fetched status. */
immediate_quit--; 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. /* 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) if (!async_p)
wait_forever_enabled_p = 1; 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); target_preopen (from_tty);
unpush_target (target); 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. */ /* Make sure we send the passed signals list the next time we resume. */
xfree (last_pass_packet); xfree (last_pass_packet);
last_pass_packet = NULL; 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. */ 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 /* Reset the target state; these things will be queried either by
remote_query_supported or as they are needed. */ remote_query_supported or as they are needed. */
init_all_packet_configs (); 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. */ this before anything involving memory or registers. */
target_find_description (); 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) if (async_p)
{ {
/* With this target we start out by owning the terminal. */ /* 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 all the ``target ....'' commands to share a common callback
function. See cli-dump.c. */ function. See cli-dump.c. */
{ {
struct gdb_exception ex struct gdb_exception ex;
= catch_exception (uiout, remote_start_remote, &from_tty, struct start_remote_args args;
RETURN_MASK_ALL);
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) if (ex.reason < 0)
{ {
pop_target (); 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); getpkt (&rs->buf, &rs->buf_size, 0);
} }
if (exec_bfd) /* No use without an exec file. */ /* If we connected to a live target, do some additional setup. */
remote_check_symbols (symfile_objfile); 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 /* 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. */ die when it hits one. */
static void 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 (); struct remote_state *rs = get_remote_state ();
if (args) if (args)
error (_("Argument given to \"detach\" when remotely debugging.")); error (_("Argument given to \"detach\" when remotely debugging."));
if (!target_has_execution)
error (_("No process to detach from."));
/* Tell the remote target to detach. */ /* Tell the remote target to detach. */
strcpy (rs->buf, "D"); strcpy (rs->buf, "D");
putpkt (rs->buf); putpkt (rs->buf);
@ -2701,7 +2790,24 @@ remote_detach (char *args, int from_tty)
target_mourn_inferior (); target_mourn_inferior ();
if (from_tty) 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. */ /* 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) remote_disconnect (struct target_ops *target, char *args, int from_tty)
{ {
if (args) 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. */ /* Unregister the file descriptor from the event loop. */
if (target_is_async_p ()) if (target_is_async_p ())
serial_async (remote_desc, NULL, 0); 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) if (from_tty)
puts_filtered ("Ending remote debugging.\n"); 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. */ /* Convert hex digit A to a number. */
static int 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 (); struct remote_state *rs = get_remote_state ();
int pid = PIDGET (ptid); int pid = PIDGET (ptid);
char *buf = NULL, *outbuf; char *outbuf;
struct cleanup *old_cleanup; struct cleanup *old_cleanup;
if (remote_protocol_packets[PACKET_vCont].support == PACKET_SUPPORT_UNKNOWN) 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; char *buf, *p;
ofunc = signal (SIGINT, remote_interrupt); if (rs->cached_wait_status)
/* If the user hit C-c before this packet, or between packets, /* Use the cached wait status, but only once. */
pretend that it was hit right here. */ rs->cached_wait_status = 0;
if (quit_flag) else
{ {
quit_flag = 0; ofunc = signal (SIGINT, remote_interrupt);
remote_interrupt (SIGINT); /* 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; buf = rs->buf;
@ -3419,24 +3592,30 @@ remote_async_wait (ptid_t ptid, struct target_waitstatus *status)
{ {
char *buf, *p; 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 (!target_is_async_p ())
/* 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; ofunc = signal (SIGINT, remote_interrupt);
remote_interrupt (SIGINT); /* 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; buf = rs->buf;
@ -4705,6 +4884,7 @@ putpkt (char *buf)
static int static int
putpkt_binary (char *buf, int cnt) putpkt_binary (char *buf, int cnt)
{ {
struct remote_state *rs = get_remote_state ();
int i; int i;
unsigned char csum = 0; unsigned char csum = 0;
char *buf2 = alloca (cnt + 6); char *buf2 = alloca (cnt + 6);
@ -4713,6 +4893,10 @@ putpkt_binary (char *buf, int cnt)
int tcount = 0; int tcount = 0;
char *p; 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 /* Copy the packet into buffer BUF2, encapsulating it
and giving it a checksum. */ and giving it a checksum. */
@ -5014,11 +5198,16 @@ getpkt (char **buf,
static int static int
getpkt_sane (char **buf, long *sizeof_buf, int forever) getpkt_sane (char **buf, long *sizeof_buf, int forever)
{ {
struct remote_state *rs = get_remote_state ();
int c; int c;
int tries; int tries;
int timeout; int timeout;
int val; 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"); strcpy (*buf, "timeout");
if (forever) if (forever)
@ -5150,19 +5339,6 @@ remote_async_mourn (void)
remote_mourn_1 (&remote_async_ops); 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. */ /* Worker function for remote_mourn. */
static void static void
remote_mourn_1 (struct target_ops *target) remote_mourn_1 (struct target_ops *target)
@ -5171,71 +5347,167 @@ remote_mourn_1 (struct target_ops *target)
generic_mourn_inferior (); generic_mourn_inferior ();
} }
static void
extended_remote_mourn_1 (struct target_ops *target)
{
struct remote_state *rs = get_remote_state ();
/* Unlike "target remote", we do not want to unpush the target; then
the next time the user says "run", we won't be connected. */
/* Call common code to mark the inferior as not running. */
generic_mourn_inferior ();
/* Check whether the target is running now - some remote stubs
automatically restart after kill. */
putpkt ("?");
getpkt (&rs->buf, &rs->buf_size, 0);
if (rs->buf[0] == 'S' || rs->buf[0] == 'T')
{
/* Assume that the target has been restarted. Set inferior_ptid
so that bits of core GDB realizes there's something here, e.g.,
so that the user can say "kill" again. */
inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
}
else
{
/* Mark this (still pushed) target as not executable until we
restart it. */
target_mark_exited (target);
}
}
static void
extended_remote_mourn (void)
{
extended_remote_mourn_1 (&extended_remote_ops);
}
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;
/* 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;
strcpy (rs->buf, "vRun;");
len = strlen (rs->buf);
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);
}
}
/* In the extended protocol we want to be able to do things like /* In the extended protocol we want to be able to do things like
"run" and have them basically work as expected. So we need "run" and have them basically work as expected. So we need
a special create_inferior function. a special create_inferior function. We support changing the
executable file and the command line arguments, but not the
environment. */
FIXME: One day add support for changing the exec file static void
we're debugging, arguments and an environment. */ extended_remote_create_inferior_1 (char *exec_file, char *args,
char **env, int from_tty,
int async_p)
{
/* If running asynchronously, register the target file descriptor
with the event loop. */
if (async_p && target_can_async_p ())
target_async (inferior_event_handler, 0);
/* Now restart the remote server. */
if (extended_remote_run (args) == -1)
{
/* vRun was not supported. Fail if we need it to do what the
user requested. */
if (remote_exec_file[0])
error (_("Remote target does not support \"set remote exec-file\""));
if (args[0])
error (_("Remote target does not support \"set args\" or run <ARGS>"));
/* Fall back to "R". */
extended_remote_restart ();
}
/* Now mark the inferior as running before we do anything else. */
inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
if (async_p)
target_mark_running (&extended_async_remote_ops);
else
target_mark_running (&extended_remote_ops);
/* Get updated offsets, if the stub uses qOffsets. */
get_offsets ();
/* Clean up from the last time we were running. */
init_thread_list ();
init_wait_for_inferior ();
}
static void static void
extended_remote_create_inferior (char *exec_file, char *args, extended_remote_create_inferior (char *exec_file, char *args,
char **env, int from_tty) char **env, int from_tty)
{ {
/* Rip out the breakpoints; we'll reinsert them after restarting extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 0);
the remote server. */
remove_breakpoints ();
/* Now restart the remote server. */
extended_remote_restart ();
/* NOTE: We don't need to recheck for a target description here; but
if we gain the ability to switch the remote executable we may
need to, if for instance we are running a process which requested
different emulated hardware from the operating system. A
concrete example of this is ARM GNU/Linux, where some binaries
will have a legacy FPA coprocessor emulated and others may have
access to a hardware VFP unit. */
/* Now put the breakpoints back in. This way we're safe if the
restart function works via a unix fork on the remote side. */
insert_breakpoints ();
/* Clean up from the last time we were running. */
clear_proceed_status ();
} }
/* Async version of extended_remote_create_inferior. */
static void static void
extended_remote_async_create_inferior (char *exec_file, char *args, extended_remote_async_create_inferior (char *exec_file, char *args,
char **env, int from_tty) char **env, int from_tty)
{ {
/* Rip out the breakpoints; we'll reinsert them after restarting extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 1);
the remote server. */
remove_breakpoints ();
/* If running asynchronously, register the target file descriptor
with the event loop. */
if (target_can_async_p ())
target_async (inferior_event_handler, 0);
/* Now restart the remote server. */
extended_remote_restart ();
/* NOTE: We don't need to recheck for a target description here; but
if we gain the ability to switch the remote executable we may
need to, if for instance we are running a process which requested
different emulated hardware from the operating system. A
concrete example of this is ARM GNU/Linux, where some binaries
will have a legacy FPA coprocessor emulated and others may have
access to a hardware VFP unit. */
/* Now put the breakpoints back in. This way we're safe if the
restart function works via a unix fork on the remote side. */
insert_breakpoints ();
/* Clean up from the last time we were running. */
clear_proceed_status ();
} }
@ -5793,6 +6065,12 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
int xfered; int xfered;
errno = 0; 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) if (writebuf != NULL)
xfered = remote_write_bytes (offset, writebuf, len); xfered = remote_write_bytes (offset, writebuf, len);
else 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_open = extended_remote_open;
extended_remote_ops.to_create_inferior = extended_remote_create_inferior; extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn; 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 static int
@ -7126,7 +7406,9 @@ init_extended_async_remote_ops (void)
Specify the serial device it is connected to (e.g. /dev/ttya).", 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_open = extended_remote_async_open;
extended_async_remote_ops.to_create_inferior = extended_remote_async_create_inferior; 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 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], add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_unlink],
"vFile:unlink", "hostio-unlink", 0); "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 /* Keep the old ``set remote Z-packet ...'' working. Each individual
Z sub-packet has its own set and show commands, but users may 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 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."), _("Delete a remote file."),
&remote_cmdlist); &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 */ /* Eventually initialize fileio. See fileio.c */
initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist); initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist);
} }

View 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> 2008-01-29 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.base/foll-exec.exp: Update header. Skip on remote targets. * gdb.base/foll-exec.exp: Update header. Skip on remote targets.

View File

@ -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;
}

View File

@ -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" ""

View File

@ -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" ""

View File

@ -132,13 +132,10 @@ proc skip_gdbserver_tests { } {
return 0 return 0
} }
# Start a gdbserver process running SERVER_EXEC, and connect GDB # Download the currently loaded program to the target if necessary.
# to it. CHILD_ARGS are passed to the inferior. # Return the target system filename.
#
# Returns the target protocol and socket to connect to.
proc gdbserver_spawn { child_args } { proc gdbserver_download { } {
global portnum
global gdbserver_host_exec global gdbserver_host_exec
global gdbserver_host_mtime global gdbserver_host_mtime
global gdbserver_server_exec 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. # Port id -- either specified in baseboard file, or managed here.
if [target_info exists gdb,socketport] { if [target_info exists gdb,socketport] {
set portnum [target_info 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. # Extract the local and remote host ids from the target board struct.
if [target_info exists sockethost] { if [target_info exists sockethost] {
set debughost [target_info sockethost] set debughost [target_info sockethost]
} else { } else {
set debughost "localhost:" set debughost "localhost:"
} }
@ -199,23 +207,23 @@ proc gdbserver_spawn { child_args } {
# Export the host:port pair. # Export the host:port pair.
set gdbport $debughost$portnum set gdbport $debughost$portnum
# Fire off the debug agent. This flavour of gdbserver takes as # Fire off the debug agent.
# arguments the port information, the name of the executable file to set gdbserver_command "$gdbserver"
# be debugged, and any arguments. if { $options != "" } {
set gdbserver_command "$gdbserver :$portnum $gdbserver_server_exec" append gdbserver_command " $options"
if { $child_args != "" } { }
append gdbserver_command " $child_args" append gdbserver_command " :$portnum"
if { $arguments != "" } {
append gdbserver_command " $arguments"
} }
set server_spawn_id [remote_spawn target $gdbserver_command] set server_spawn_id [remote_spawn target $gdbserver_command]
# Wait for the server to produce at least one line and an additional # Wait for the server to open its TCP socket, so that GDB can connect.
# character of output. This will wait until any TCP socket has been
# created, so that GDB can connect.
expect { expect {
-i $server_spawn_id -i $server_spawn_id
-notransfer -notransfer
-re ".*\n." { } -re "Listening on" { }
} }
# We can't just call close, because if gdbserver is local then that means # 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] 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 # Start a gdbserver process running HOST_EXEC and pass CHILD_ARGS
# to it. Return 0 on success, or non-zero on failure. # 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] 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]
}