* 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>
* 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
commands (remote put, remote get, and remote delete).
* The GDB remote stub, gdbserver, now supports run and attach in
extended-remote mode.
* hppa*64*-*-hpux11* target broken
The debugger is unable to start a program and fails with the following
error: "Error trying to get information about dynamic linker".
@ -85,6 +88,13 @@ vFile:unlink:
* GDB on GNU/Linux and HP/UX can now debug through "exec" of a new
process.
vAttach
Attach to an existing process on the remote system, in extended-remote
mode.
vRun
Run a new process on the remote system, in extended-remote mode.
*** Changes in GDB 6.7
* Resolved 101 resource leaks, null pointer dereferences, etc. in gdb,

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>
* 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
protocol.
@table @emph
@item On the target machine,
you need to have a copy of the program you want to debug.
@quotation
@emph{Warning:} @code{gdbserver} does not have any built-in security.
Do not run @code{gdbserver} connected to any public network; a
@value{GDBN} connection to @code{gdbserver} provides access to the
target system with the same privileges as the user running
@code{gdbserver}.
@end quotation
@subsection Running @code{gdbserver}
@cindex arguments, to @code{gdbserver}
Run @code{gdbserver} on the target system. You need a copy of the
program you want to debug, including any libraries it requires.
@code{gdbserver} does not need your program's symbol table, so you can
strip the program if necessary to save space. @value{GDBN} on the host
system does all the symbol handling.
@ -12997,11 +13007,13 @@ conflicts with another service, @code{gdbserver} prints an error message
and exits.} You must use the same port number with the host @value{GDBN}
@code{target remote} command.
@subsubsection Attaching to a Running Program
On some targets, @code{gdbserver} can also attach to running programs.
This is accomplished via the @code{--attach} argument. The syntax is:
@smallexample
target> gdbserver @var{comm} --attach @var{pid}
target> gdbserver --attach @var{comm} @var{pid}
@end smallexample
@var{pid} is the process ID of a currently running process. It isn't necessary
@ -13013,18 +13025,56 @@ You can debug processes by name instead of process ID if your target has the
@code{pidof} utility:
@smallexample
target> gdbserver @var{comm} --attach `pidof @var{program}`
target> gdbserver --attach @var{comm} `pidof @var{program}`
@end smallexample
In case more than one copy of @var{program} is running, or @var{program}
has multiple threads, most versions of @code{pidof} support the
@code{-s} option to only return the first process ID.
@item On the host machine,
first make sure you have the necessary symbol files. Load symbols for
@subsubsection Multi-Process Mode for @code{gdbserver}
@cindex gdbserver, multiple processes
@cindex multiple processes with gdbserver
When you connect to @code{gdbserver} using @code{target remote},
@code{gdbserver} debugs the specified program only once. When the
program exits, or you detach from it, @value{GDBN} closes the connection
and @code{gdbserver} exits.
If you connect using @code{target extended-remote}, @code{gdbserver}
enters multi-process mode. When the debugged program exits, or you
detach from it, @value{GDBN} stays connected to @code{gdbserver} even
though no program is running. The @code{run} and @code{attach}
commands instruct @code{gdbserver} to run or attach to a new program.
The @code{run} command uses @code{set remote exec-file} (@pxref{set
remote exec-file}) to select the program to run. Command line
arguments are supported, except for wildcard expansion and I/O
redirection (@pxref{Arguments}).
To start @code{gdbserver} without supplying an initial command to run
or process ID to attach, use the @option{--multi} command line option.
Then you can connect using @code{target extended-remote} and start
the program you want to debug.
@code{gdbserver} does not automatically exit in multi-process mode.
You can terminate it by using @code{monitor exit}
(@pxref{Monitor Commands for gdbserver}).
@subsubsection Other Command-Line Arguments for @code{gdbserver}
You can include @option{--debug} on the @code{gdbserver} command line.
@code{gdbserver} will display extra status information about the debugging
process. This option is intended for @code{gdbserver} development and
for bug reports to the developers.
@subsection Connecting to @code{gdbserver}
Run @value{GDBN} on the host system.
First make sure you have the necessary symbol files. Load symbols for
your application using the @code{file} command before you connect. Use
@code{set sysroot} to locate target libraries (unless your @value{GDBN}
was compiled with the correct sysroot using @code{--with-system-root}).
was compiled with the correct sysroot using @code{--with-sysroot}).
The symbol file and target libraries must exactly match the executable
and libraries on the target, with one exception: the files on the host
@ -13038,19 +13088,17 @@ Connect to your target (@pxref{Connecting,,Connecting to a Remote Target}).
For TCP connections, you must start up @code{gdbserver} prior to using
the @code{target remote} command. Otherwise you may get an error whose
text depends on the host system, but which usually looks something like
@samp{Connection refused}. You don't need to use the @code{load}
@samp{Connection refused}. Don't use the @code{load}
command in @value{GDBN} when using @code{gdbserver}, since the program is
already on the target.
@end table
@subsection Monitor Commands for @code{gdbserver}
@cindex monitor commands, for @code{gdbserver}
@anchor{Monitor Commands for gdbserver}
During a @value{GDBN} session using @code{gdbserver}, you can use the
@code{monitor} command to send special requests to @code{gdbserver}.
Here are the available commands; they are only of interest when
debugging @value{GDBN} or @code{gdbserver}.
Here are the available commands.
@table @code
@item monitor help
@ -13065,6 +13113,13 @@ Disable or enable general debugging messages.
Disable or enable specific debugging messages associated with the remote
protocol (@pxref{Remote Protocol}).
@item monitor exit
Tell gdbserver to exit immediately. This command should be followed by
@code{disconnect} to close the debugging session. @code{gdbserver} will
detach from any attached processes and kill any processes it created.
Use @code{monitor exit} to terminate @code{gdbserver} at the end
of a multi-process mode debug session.
@end table
@node Remote Configuration
@ -13159,6 +13214,15 @@ responses.
@itemx set remote hardware-breakpoint-limit @var{limit}
Restrict @value{GDBN} to using @var{limit} remote hardware breakpoint or
watchpoints. A limit of -1, the default, is treated as unlimited.
@item set remote exec-file @var{filename}
@itemx show remote exec-file
@anchor{set remote exec-file}
@cindex executable file, for remote target
Select the file used for @code{run} with @code{target
extended-remote}. This should be set to a filename valid on the
target system. If it is not set, the target will use a default
filename (e.g.@: the last program run).
@end table
@cindex remote packets, enabling and disabling
@ -13205,10 +13269,18 @@ are:
@tab @code{qSymbol}
@tab Detecting multiple threads
@item @code{attach}
@tab @code{vAttach}
@tab @code{attach}
@item @code{verbose-resume}
@tab @code{vCont}
@tab Stepping or resuming multiple threads
@item @code{run}
@tab @code{vRun}
@tab @code{run}
@item @code{software-breakpoint}
@tab @code{Z0}
@tab @code{break}
@ -23298,6 +23370,7 @@ Here are the packet descriptions.
@item !
@cindex @samp{!} packet
@anchor{extended mode}
Enable extended mode. In extended mode, the remote server is made
persistent. The @samp{R} packet is used to restart the program being
debugged.
@ -23563,7 +23636,7 @@ Don't use this packet; use the @samp{R} packet instead.
@item R @var{XX}
@cindex @samp{R} packet
Restart the program being debugged. @var{XX}, while needed, is ignored.
This packet is only available in extended mode.
This packet is only available in extended mode (@pxref{extended mode}).
The @samp{R} packet has no reply.
@ -23606,6 +23679,22 @@ thread is dead
Packets starting with @samp{v} are identified by a multi-letter name,
up to the first @samp{;} or @samp{?} (or the end of the packet).
@item vAttach;@var{pid}
@cindex @samp{vAttach} packet
Attach to a new process with the specified process ID. @var{pid} is a
hexadecimal integer identifying the process. If the stub is currently
controlling a process, it is killed. The attached process is stopped.
This packet is only available in extended mode (@pxref{extended mode}).
Reply:
@table @samp
@item E @var{nn}
for an error
@item @r{Any stop packet}
for success (@pxref{Stop Reply Packets})
@end table
@item vCont@r{[};@var{action}@r{[}:@var{tid}@r{]]}@dots{}
@cindex @samp{vCont} packet
Resume the inferior, specifying different actions for each thread.
@ -23702,6 +23791,24 @@ The stub is permitted to delay or batch the effects of a group of
regions of flash memory are unpredictable until the @samp{vFlashDone}
request is completed.
@item vRun;@var{filename}@r{[};@var{argument}@r{]}@dots{}
@cindex @samp{vRun} packet
Run the program @var{filename}, passing it each @var{argument} on its
command line. The file and arguments are hex-encoded strings. If
@var{filename} is an empty string, the stub may use a default program
(e.g.@: the last program run). The program is created in the stopped
state. If the stub is currently controlling a process, it is killed.
This packet is only available in extended mode (@pxref{extended mode}).
Reply:
@table @samp
@item E @var{nn}
for an error
@item @r{Any stop packet}
for success (@pxref{Stop Reply Packets})
@end table
@item X @var{addr},@var{length}:@var{XX@dots{}}
@anchor{X packet}
@cindex @samp{X} packet

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>
* 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
either a serial line or a TCP connection.
For more information about GDBserver, see the GDB manual.
Usage (server (target) side):
First, you need to have a copy of the program you want to debug put onto
@ -47,7 +49,7 @@ print an error message and exit.
On some targets, gdbserver can also attach to running programs. This is
accomplished via the --attach argument. The syntax is:
target> gdbserver COMM --attach PID
target> gdbserver --attach COMM PID
PID is the process ID of a currently running process. It isn't necessary
to point gdbserver at a binary for the running process.

View File

@ -304,14 +304,18 @@ linux_attach_lwp (unsigned long pid)
if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0)
{
fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid,
if (all_threads.head != NULL)
{
/* If we fail to attach to an LWP, just warn. */
fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid,
strerror (errno), errno);
fflush (stderr);
return;
}
else
/* If we fail to attach to a process, report an error. */
error ("Cannot attach to process %ld: %s (%d)\n", pid,
strerror (errno), errno);
fflush (stderr);
/* If we fail to attach to an LWP, just return. */
if (all_threads.head == NULL)
_exit (0177);
return;
}
ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE);
@ -396,6 +400,10 @@ linux_kill (void)
/* Make sure it died. The loop is most likely unnecessary. */
wstat = linux_wait_for_event (thread);
} while (WIFSTOPPED (wstat));
clear_inferiors ();
free (all_processes.head);
all_processes.head = all_processes.tail = NULL;
}
static void
@ -434,6 +442,8 @@ linux_detach (void)
delete_all_breakpoints ();
for_each_inferior (&all_threads, linux_detach_one_process);
clear_inferiors ();
free (all_processes.head);
all_processes.head = all_processes.tail = NULL;
return 0;
}

View File

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

View File

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

View File

@ -237,6 +237,15 @@ struct remote_state
a buffer in the stub), this will be set to that packet size.
Otherwise zero, meaning to use the guessed size. */
long explicit_packet_size;
/* remote_wait is normally called when the target is running and
waits for a stop reply packet. But sometimes we need to call it
when the target is already stopped. We can send a "?" packet
and have remote_wait read the response. Or, if we already have
the response, we can stash it in BUF and tell remote_wait to
skip calling getpkt. This flag is set when BUF contains a
stop reply packet and the target is not waiting. */
int cached_wait_status;
};
/* This data could be associated with a target, but we do not always
@ -514,6 +523,10 @@ static int remote_address_size;
static int remote_async_terminal_ours_p;
/* The executable file to use for "run" on the remote side. */
static char *remote_exec_file = "";
/* User configurable variables for the number of characters in a
memory read/write packet. MIN (rsa->remote_packet_size,
@ -920,6 +933,8 @@ enum {
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
PACKET_vAttach,
PACKET_vRun,
PACKET_MAX
};
@ -1993,11 +2008,6 @@ extended_remote_restart (void)
putpkt (rs->buf);
remote_fileio_reset ();
/* Now query for status so this looks just like we restarted
gdbserver from scratch. */
putpkt ("?");
getpkt (&rs->buf, &rs->buf_size, 0);
}
/* Clean up connection to a remote debugger. */
@ -2159,27 +2169,79 @@ get_offsets (void)
/* Stub for catch_exception. */
static void
remote_start_remote (struct ui_out *uiout, void *from_tty_p)
struct start_remote_args
{
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. */
/* Ack any packet which the remote side has already sent. */
serial_write (remote_desc, "+", 1);
/* Check whether the target is running now. */
putpkt ("?");
getpkt (&rs->buf, &rs->buf_size, 0);
if (rs->buf[0] == 'W' || rs->buf[0] == 'X')
{
if (args->extended_p)
{
/* We're connected, but not running. Drop out before we
call start_remote. */
target_mark_exited (args->target);
return;
}
else
error (_("The target is not running (try extended-remote?)"));
}
else
{
if (args->extended_p)
target_mark_running (args->target);
/* Save the reply for later. */
wait_status = alloca (strlen (rs->buf) + 1);
strcpy (wait_status, rs->buf);
}
/* Let the stub know that we want it to return the thread. */
set_thread (-1, 0);
/* Without this, some commands which require an active target
(such as kill) won't work. This variable serves (at least)
double duty as both the pid of the target process (if it has
such), and as a flag indicating that a target is active.
These functions should be split out into seperate variables,
especially since GDB will someday have a notion of debugging
several processes. */
inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
/* Now, if we have thread information, update inferior_ptid. */
inferior_ptid = remote_current_thread (inferior_ptid);
get_offsets (); /* Get text, data & bss offsets. */
putpkt ("?"); /* Initiate a query from remote machine. */
immediate_quit--;
/* Use the previously fetched status. */
gdb_assert (wait_status != NULL);
strcpy (rs->buf, wait_status);
rs->cached_wait_status = 1;
start_remote (from_tty); /* Initialize gdb process mechanisms. */
immediate_quit--;
start_remote (args->from_tty); /* Initialize gdb process mechanisms. */
}
/* Open a connection to a remote debugger.
@ -2540,10 +2602,31 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
if (!async_p)
wait_forever_enabled_p = 1;
/* If we're connected to a running target, target_preopen will kill it.
But if we're connected to a target system with no running process,
then we will still be connected when it returns. Ask this question
first, before target_preopen has a chance to kill anything. */
if (remote_desc != NULL && !target_has_execution)
{
if (!from_tty
|| query (_("Already connected to a remote target. Disconnect? ")))
pop_target ();
else
error (_("Still connected."));
}
target_preopen (from_tty);
unpush_target (target);
/* This time without a query. If we were connected to an
extended-remote target and target_preopen killed the running
process, we may still be connected. If we are starting "target
remote" now, the extended-remote target will not have been
removed by unpush_target. */
if (remote_desc != NULL && !target_has_execution)
pop_target ();
/* Make sure we send the passed signals list the next time we resume. */
xfree (last_pass_packet);
last_pass_packet = NULL;
@ -2584,6 +2667,9 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
}
push_target (target); /* Switch to using remote target now. */
/* Assume that the target is running, unless we learn otherwise. */
target_mark_running (target);
/* Reset the target state; these things will be queried either by
remote_query_supported or as they are needed. */
init_all_packet_configs ();
@ -2605,15 +2691,6 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
this before anything involving memory or registers. */
target_find_description ();
/* Without this, some commands which require an active target (such
as kill) won't work. This variable serves (at least) double duty
as both the pid of the target process (if it has such), and as a
flag indicating that a target is active. These functions should
be split out into seperate variables, especially since GDB will
someday have a notion of debugging several processes. */
inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
if (async_p)
{
/* With this target we start out by owning the terminal. */
@ -2648,9 +2725,14 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
all the ``target ....'' commands to share a common callback
function. See cli-dump.c. */
{
struct gdb_exception ex
= catch_exception (uiout, remote_start_remote, &from_tty,
RETURN_MASK_ALL);
struct gdb_exception ex;
struct start_remote_args args;
args.from_tty = from_tty;
args.target = target;
args.extended_p = extended_p;
ex = catch_exception (uiout, remote_start_remote, &args, RETURN_MASK_ALL);
if (ex.reason < 0)
{
pop_target ();
@ -2670,8 +2752,12 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
getpkt (&rs->buf, &rs->buf_size, 0);
}
if (exec_bfd) /* No use without an exec file. */
remote_check_symbols (symfile_objfile);
/* If we connected to a live target, do some additional setup. */
if (target_has_execution)
{
if (exec_bfd) /* No use without an exec file. */
remote_check_symbols (symfile_objfile);
}
}
/* This takes a program previously attached to and detaches it. After
@ -2680,13 +2766,16 @@ remote_open_1 (char *name, int from_tty, struct target_ops *target,
die when it hits one. */
static void
remote_detach (char *args, int from_tty)
remote_detach_1 (char *args, int from_tty, int extended)
{
struct remote_state *rs = get_remote_state ();
if (args)
error (_("Argument given to \"detach\" when remotely debugging."));
if (!target_has_execution)
error (_("No process to detach from."));
/* Tell the remote target to detach. */
strcpy (rs->buf, "D");
putpkt (rs->buf);
@ -2701,7 +2790,24 @@ remote_detach (char *args, int from_tty)
target_mourn_inferior ();
if (from_tty)
puts_filtered ("Ending remote debugging.\n");
{
if (extended)
puts_filtered ("Detached from remote process.\n");
else
puts_filtered ("Ending remote debugging.\n");
}
}
static void
remote_detach (char *args, int from_tty)
{
remote_detach_1 (args, from_tty, 0);
}
static void
extended_remote_detach (char *args, int from_tty)
{
remote_detach_1 (args, from_tty, 1);
}
/* Same as remote_detach, but don't send the "D" packet; just disconnect. */
@ -2710,17 +2816,78 @@ static void
remote_disconnect (struct target_ops *target, char *args, int from_tty)
{
if (args)
error (_("Argument given to \"detach\" when remotely debugging."));
error (_("Argument given to \"disconnect\" when remotely debugging."));
/* Unregister the file descriptor from the event loop. */
if (target_is_async_p ())
serial_async (remote_desc, NULL, 0);
target_mourn_inferior ();
/* Make sure we unpush even the extended remote targets; mourn
won't do it. So call remote_mourn_1 directly instead of
target_mourn_inferior. */
remote_mourn_1 (target);
if (from_tty)
puts_filtered ("Ending remote debugging.\n");
}
/* Attach to the process specified by ARGS. If FROM_TTY is non-zero,
be chatty about it. */
static void
extended_remote_attach_1 (struct target_ops *target, char *args, int from_tty)
{
struct remote_state *rs = get_remote_state ();
pid_t pid;
char *dummy;
if (!args)
error_no_arg (_("process-id to attach"));
dummy = args;
pid = strtol (args, &dummy, 0);
/* Some targets don't set errno on errors, grrr! */
if (pid == 0 && args == dummy)
error (_("Illegal process-id: %s."), args);
if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE)
error (_("This target does not support attaching to a process"));
sprintf (rs->buf, "vAttach;%x", pid);
putpkt (rs->buf);
getpkt (&rs->buf, &rs->buf_size, 0);
if (packet_ok (rs->buf, &remote_protocol_packets[PACKET_vAttach]) == PACKET_OK)
{
if (from_tty)
printf_unfiltered (_("Attached to %s\n"),
target_pid_to_str (pid_to_ptid (pid)));
/* We have a wait response; reuse it. */
rs->cached_wait_status = 1;
}
else if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE)
error (_("This target does not support attaching to a process"));
else
error (_("Attaching to %s failed"),
target_pid_to_str (pid_to_ptid (pid)));
target_mark_running (target);
inferior_ptid = pid_to_ptid (pid);
}
static void
extended_remote_attach (char *args, int from_tty)
{
extended_remote_attach_1 (&extended_remote_ops, args, from_tty);
}
static void
extended_async_remote_attach (char *args, int from_tty)
{
extended_remote_attach_1 (&extended_async_remote_ops, args, from_tty);
}
/* Convert hex digit A to a number. */
static int
@ -2845,7 +3012,7 @@ remote_vcont_resume (ptid_t ptid, int step, enum target_signal siggnal)
{
struct remote_state *rs = get_remote_state ();
int pid = PIDGET (ptid);
char *buf = NULL, *outbuf;
char *outbuf;
struct cleanup *old_cleanup;
if (remote_protocol_packets[PACKET_vCont].support == PACKET_SUPPORT_UNKNOWN)
@ -3203,16 +3370,22 @@ remote_wait (ptid_t ptid, struct target_waitstatus *status)
{
char *buf, *p;
ofunc = signal (SIGINT, remote_interrupt);
/* If the user hit C-c before this packet, or between packets,
pretend that it was hit right here. */
if (quit_flag)
if (rs->cached_wait_status)
/* Use the cached wait status, but only once. */
rs->cached_wait_status = 0;
else
{
quit_flag = 0;
remote_interrupt (SIGINT);
ofunc = signal (SIGINT, remote_interrupt);
/* If the user hit C-c before this packet, or between packets,
pretend that it was hit right here. */
if (quit_flag)
{
quit_flag = 0;
remote_interrupt (SIGINT);
}
getpkt (&rs->buf, &rs->buf_size, 1);
signal (SIGINT, ofunc);
}
getpkt (&rs->buf, &rs->buf_size, 1);
signal (SIGINT, ofunc);
buf = rs->buf;
@ -3419,24 +3592,30 @@ remote_async_wait (ptid_t ptid, struct target_waitstatus *status)
{
char *buf, *p;
if (!target_is_async_p ())
if (rs->cached_wait_status)
/* Use the cached wait status, but only once. */
rs->cached_wait_status = 0;
else
{
ofunc = signal (SIGINT, remote_interrupt);
/* If the user hit C-c before this packet, or between packets,
pretend that it was hit right here. */
if (quit_flag)
if (!target_is_async_p ())
{
quit_flag = 0;
remote_interrupt (SIGINT);
ofunc = signal (SIGINT, remote_interrupt);
/* If the user hit C-c before this packet, or between packets,
pretend that it was hit right here. */
if (quit_flag)
{
quit_flag = 0;
remote_interrupt (SIGINT);
}
}
/* FIXME: cagney/1999-09-27: If we're in async mode we should
_never_ wait for ever -> test on target_is_async_p().
However, before we do that we need to ensure that the caller
knows how to take the target into/out of async mode. */
getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p);
if (!target_is_async_p ())
signal (SIGINT, ofunc);
}
/* FIXME: cagney/1999-09-27: If we're in async mode we should
_never_ wait for ever -> test on target_is_async_p().
However, before we do that we need to ensure that the caller
knows how to take the target into/out of async mode. */
getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p);
if (!target_is_async_p ())
signal (SIGINT, ofunc);
buf = rs->buf;
@ -4705,6 +4884,7 @@ putpkt (char *buf)
static int
putpkt_binary (char *buf, int cnt)
{
struct remote_state *rs = get_remote_state ();
int i;
unsigned char csum = 0;
char *buf2 = alloca (cnt + 6);
@ -4713,6 +4893,10 @@ putpkt_binary (char *buf, int cnt)
int tcount = 0;
char *p;
/* We're sending out a new packet. Make sure we don't look at a
stale cached response. */
rs->cached_wait_status = 0;
/* Copy the packet into buffer BUF2, encapsulating it
and giving it a checksum. */
@ -5014,11 +5198,16 @@ getpkt (char **buf,
static int
getpkt_sane (char **buf, long *sizeof_buf, int forever)
{
struct remote_state *rs = get_remote_state ();
int c;
int tries;
int timeout;
int val;
/* We're reading a new response. Make sure we don't look at a
previously cached response. */
rs->cached_wait_status = 0;
strcpy (*buf, "timeout");
if (forever)
@ -5150,19 +5339,6 @@ remote_async_mourn (void)
remote_mourn_1 (&remote_async_ops);
}
static void
extended_remote_mourn (void)
{
/* We do _not_ want to mourn the target like this; this will
remove the extended remote target from the target stack,
and the next time the user says "run" it'll fail.
FIXME: What is the right thing to do here? */
#if 0
remote_mourn_1 (&extended_remote_ops);
#endif
}
/* Worker function for remote_mourn. */
static void
remote_mourn_1 (struct target_ops *target)
@ -5171,71 +5347,167 @@ remote_mourn_1 (struct target_ops *target)
generic_mourn_inferior ();
}
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
"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
we're debugging, arguments and an environment. */
static void
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
extended_remote_create_inferior (char *exec_file, char *args,
char **env, int from_tty)
{
/* Rip out the breakpoints; we'll reinsert them after restarting
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 ();
extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 0);
}
/* Async version of extended_remote_create_inferior. */
static void
extended_remote_async_create_inferior (char *exec_file, char *args,
char **env, int from_tty)
{
/* 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 ())
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 ();
extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 1);
}
@ -5793,6 +6065,12 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
int xfered;
errno = 0;
/* If the remote target is connected but not running, we should
pass this request down to a lower stratum (e.g. the executable
file). */
if (!target_has_execution)
return 0;
if (writebuf != NULL)
xfered = remote_write_bytes (offset, writebuf, len);
else
@ -6994,6 +7272,8 @@ Specify the serial device it is connected to (e.g. /dev/ttya).",
extended_remote_ops.to_open = extended_remote_open;
extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_remote_ops.to_detach = extended_remote_detach;
extended_remote_ops.to_attach = extended_remote_attach;
}
static int
@ -7126,7 +7406,9 @@ init_extended_async_remote_ops (void)
Specify the serial device it is connected to (e.g. /dev/ttya).",
extended_async_remote_ops.to_open = extended_remote_async_open;
extended_async_remote_ops.to_create_inferior = extended_remote_async_create_inferior;
extended_async_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_async_remote_ops.to_mourn_inferior = extended_async_remote_mourn;
extended_async_remote_ops.to_detach = extended_remote_detach;
extended_async_remote_ops.to_attach = extended_async_remote_attach;
}
static void
@ -7381,6 +7663,12 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_unlink],
"vFile:unlink", "hostio-unlink", 0);
add_packet_config_cmd (&remote_protocol_packets[PACKET_vAttach],
"vAttach", "attach", 0);
add_packet_config_cmd (&remote_protocol_packets[PACKET_vRun],
"vRun", "run", 0);
/* Keep the old ``set remote Z-packet ...'' working. Each individual
Z sub-packet has its own set and show commands, but users may
have sets to this variable in their .gdbinit files (or in their
@ -7413,6 +7701,13 @@ Transfer files to and from the remote target system."),
_("Delete a remote file."),
&remote_cmdlist);
remote_exec_file = xstrdup ("");
add_setshow_string_noescape_cmd ("exec-file", class_files,
&remote_exec_file, _("\
Set the remote pathname for \"run\""), _("\
Show the remote pathname for \"run\""), NULL, NULL, NULL,
&remote_set_cmdlist, &remote_show_cmdlist);
/* Eventually initialize fileio. See fileio.c */
initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist);
}

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