From 2d717e4f8a548fbfc4fabe506fd1d53f6b81994d Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Wed, 30 Jan 2008 00:51:50 +0000 Subject: [PATCH] * 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. --- gdb/ChangeLog | 39 ++ gdb/NEWS | 10 + gdb/doc/ChangeLog | 10 + gdb/doc/gdb.texinfo | 135 +++++- gdb/gdbserver/ChangeLog | 28 ++ gdb/gdbserver/README | 4 +- gdb/gdbserver/linux-low.c | 24 +- gdb/gdbserver/remote-utils.c | 12 +- gdb/gdbserver/server.c | 474 +++++++++++++++------ gdb/remote.c | 529 ++++++++++++++++++------ gdb/testsuite/ChangeLog | 10 + gdb/testsuite/gdb.server/ext-attach.c | 31 ++ gdb/testsuite/gdb.server/ext-attach.exp | 77 ++++ gdb/testsuite/gdb.server/ext-run.exp | 48 +++ gdb/testsuite/lib/gdbserver-support.exp | 69 +++- 15 files changed, 1223 insertions(+), 277 deletions(-) create mode 100644 gdb/testsuite/gdb.server/ext-attach.c create mode 100644 gdb/testsuite/gdb.server/ext-attach.exp create mode 100644 gdb/testsuite/gdb.server/ext-run.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 15d2cd0809..faa51f1b58 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,42 @@ +2008-01-29 Daniel Jacobowitz + + * 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 * Makefile.in (symfile.o): Update. diff --git a/gdb/NEWS b/gdb/NEWS index 44d08c666c..ea55423c57 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -50,6 +50,9 @@ targets even when the libthread_db library is not available. * The GDB remote stub, gdbserver, now supports the new file transfer commands (remote put, remote get, and remote delete). +* The GDB remote stub, gdbserver, now supports run and attach in +extended-remote mode. + * hppa*64*-*-hpux11* target broken The debugger is unable to start a program and fails with the following error: "Error trying to get information about dynamic linker". @@ -85,6 +88,13 @@ vFile:unlink: * GDB on GNU/Linux and HP/UX can now debug through "exec" of a new process. +vAttach + Attach to an existing process on the remote system, in extended-remote + mode. + +vRun + Run a new process on the remote system, in extended-remote mode. + *** Changes in GDB 6.7 * Resolved 101 resource leaks, null pointer dereferences, etc. in gdb, diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 5cafe2c6a0..b5d144c80e 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,13 @@ +2008-01-29 Daniel Jacobowitz + + * 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 * gdb.texinfo (Processes): Mention process command. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 7566294812..c613270942 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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 diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index ced28cd536..5a6e7b9e92 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,31 @@ +2008-01-29 Daniel Jacobowitz + + * 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 * linux-low.c (STACK_SIZE): Define. diff --git a/gdb/gdbserver/README b/gdb/gdbserver/README index eda00ba36d..9394198c0e 100644 --- a/gdb/gdbserver/README +++ b/gdb/gdbserver/README @@ -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. diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index e5cb5282da..f5bf5dce47 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -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; } diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index 68ed939a83..2e049ee22a 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -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); diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index a6cb1252b6..51dfbcce08 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -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 diff --git a/gdb/remote.c b/gdb/remote.c index 1239771671..9139ea82b3 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -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 ")); + + /* 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); } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 3de37e7728..32542a2f7a 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2008-01-29 Daniel Jacobowitz + + * 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 * gdb.base/foll-exec.exp: Update header. Skip on remote targets. diff --git a/gdb/testsuite/gdb.server/ext-attach.c b/gdb/testsuite/gdb.server/ext-attach.c new file mode 100644 index 0000000000..db7c0f8fd6 --- /dev/null +++ b/gdb/testsuite/gdb.server/ext-attach.c @@ -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 . */ + +/* 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 + +int main () +{ + int i; + + for (i = 0; i < 120; i++) + sleep (1); + + return 0; +} diff --git a/gdb/testsuite/gdb.server/ext-attach.exp b/gdb/testsuite/gdb.server/ext-attach.exp new file mode 100644 index 0000000000..4ab1bb64ca --- /dev/null +++ b/gdb/testsuite/gdb.server/ext-attach.exp @@ -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 . + +# Test attaching to already-running programs using extended-remote. + +load_lib gdbserver-support.exp + +set testfile "ext-attach" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if { [skip_gdbserver_tests] } { + return 0 +} + +# On SPU, this test currently fails because "sleep" is not supported. +if { [istarget "spu*-*-*"] } { + return 0 +} + +# We need to use TCL's exec to get the pid. +if [is_remote target] then { + return 0 +} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested ext-attach.exp + return -1 +} + +gdb_exit +gdb_start +gdb_load $binfile +gdb_reinitialize_dir $srcdir/$subdir + +set target_exec [gdbserver_download] +gdbserver_start_extended + +gdb_test "set remote exec-file $target_exec" "" "set remote exec-file" + +# Start the program running and then wait for a bit, to be sure +# that it can be attached to. +set testpid [eval exec $binfile &] +exec sleep 2 +if { [istarget "*-*-cygwin*"] } { + # testpid is the Cygwin PID, GDB uses the Windows PID, which might be + # different due to the way fork/exec works. + set testpid [ exec ps -e | gawk "{ if (\$1 == $testpid) print \$4; }" ] +} + +gdb_test "attach $testpid" "Attached to.*" \ + "attach to remote program 1" +gdb_test "backtrace" ".*main.*" "backtrace 1" + +gdb_test "detach" "Detached from remote process\\." +gdb_test "backtrace" "No stack\\." "backtrace with no program" + +gdb_test "attach $testpid" "Attached to.*" \ + "attach to remote program 2" +gdb_test "backtrace" ".*main.*" "backtrace 2" + +gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y" +gdb_test "monitor exit" "" diff --git a/gdb/testsuite/gdb.server/ext-run.exp b/gdb/testsuite/gdb.server/ext-run.exp new file mode 100644 index 0000000000..5af561159d --- /dev/null +++ b/gdb/testsuite/gdb.server/ext-run.exp @@ -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 . + +# 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" "" diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp index 50dafe0193..6fbaa22438 100644 --- a/gdb/testsuite/lib/gdbserver-support.exp +++ b/gdb/testsuite/lib/gdbserver-support.exp @@ -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] +}