This commit adds a new remote protocol packet qExecAndArgs, and
updates GDB to use it.
When gdbserver is started a user can provide an executable and
arguments, these are used (by the remote target) to start an initial
inferior, this is the inferior to which GDB first connects.
When GDB is connected in extended-remote mode, if the user does a
'run' without specifying a new 'remote exec-file' then the executable
given on the gdbserver command line is reused to start the new
inferior.
Interestingly, the arguments given on the gdbserver command line are
only used when starting the first inferior, subsequent inferiors will
be passed an empty argument string by GDB. This might catch out a
user, causing the rerun to behave differently than the first run.
In this commit I will add a new qExecAndArgs packet, which I think
will improve the experience in this area.
The new qExecAndArgs packet is sent from GDB, and gdbserver replies
with a packet that includes the executable filename and the arguments
string that were used for starting the initial inferior.
On the GDB side this information can be used to update GDB's state,
the 'show remote exec-file' will reflect how gdbserver was started,
and 'show args' will reflect the arguments used for starting the
inferior.
As a result of updating the args, if the user restarts the inferior,
then this same argument string will be passed back to the remote
target, and used for the new inferior. Thus, rerunning the inferior
will behave just like the initial inferior, which I think is a good
improvement.
Finally, GDB will warn if the user has 'set remote exec-file' and
then connects to a gdbserver that was started with some alternative
filename, like this:
(gdb) set remote exec-file /tmp/foo
(gdb) target remote | gdbserver --once - /tmp/bar
... snip ...
warning: updating 'remote exec-file' to '/tmp/bar' to match remote target
... snip ...
I made the choice to have GDB update the remote exec-file setting to
match the remote, as, after the 'target remote', we are connected to
an inferior that is running /tmp/bar (in this case), so trying to hang
onto the non-matching user supplied setting doesn't seem helpful.
There is one case where I can see this choice being a problem, if a
user does:
(gdb) set remote exec-file /tmp/foo
(gdb) target extended-remote | gdbserver --multi --once - /tmp/bar
... snip ...
warning: updating 'remote exec-file' to '/tmp/bar' to match remote target
... snip ...
(gdb) run
In this case, prior to this patch, they would 'run' /tmp/foo, while
after this patch, they will run /tmp/bar. I think it is unfortunate
that I'm breaking this use case, but, I'm not _that_ sorry -- just
start gdbserver with the correct executable, or even no executable,
and the problem goes away.
This last point is important, in extended-remote mode, it is possible
to start gdbserver without specifying an executable, like this:
$ gdbserver --multi --once :54321
In this case gdbserver doesn't start an initial inferior. When GDB
connects the qExecAndArgs reply from gdbserver indicates that no
information (executable or arguments) were set, and any existing
information is retained, as in this session:
(gdb) set sysroot
(gdb) set remote exec-file /tmp/foo
(gdb) set args a b c
(gdb) target extended-remote | ./gdbserver/gdbserver --multi --once -
Remote debugging using | ./gdbserver/gdbserver --multi --once -
Remote debugging using stdio
(gdb) show remote exec-file
The remote exec-file is "/tmp/foo".
(gdb) show args
Argument list to give program being debugged when it is started is "a b c".
(gdb)
This is the second time proposing this new packet. The first attempt
can be found here:
https://inbox.sourceware.org/gdb-patches/
80d8b37d757033976b1a8ddd370c294c7aae8f8c.
1692200989.git.aburgess@redhat.com
The review feedback on this patch was that the inferior arguments
should be passed back as a vector of individual strings. This makes
sense, at the time that feedback was given, GDB would pass arguments
to gdbserver as a vector of individual arguments, so it would seem
sensible that gdbserver should adopt the same approach for passing
arguments back to GDB.
However, since then I have been working on how GDB passes the inferior
arguments to gdbserver, fixing a lot of broken corner cases, which
culminated in this patch:
commit
8e28eef6cdcbd86ad61325ce1e6bd563b0fad1e1
Date: Thu Nov 23 18:46:54 2023 +0000
gdb/gdbserver: pass inferior arguments as a single string
Though we do retain the vector of individual arguments behaviour for
backward compatibility with old remote targets, the preferred approach
now is for GDB to pass arguments to gdbserver as a single string.
This removes the need for GDB/gdbserver to try and figure out what is
the correct escaping to apply to the arguments, and fixes some
argument passing corner cases.
And so, now, I think it makes sense that gdbserver should also pass
the arguments back to GDB as a single string. I've updated the
documentation a little to (I hope) explain how gdbserver should escape
things before passing them back to GDB (TLDR: no additional escaping
should be added just for sending to GDB. The argument string should
be sent to GDB as if it were being sent to the 'set args' GDB
command).
The main test for this new functionality is
gdb.server/fetch-exec-and-args.exp, but I've also added a test
gdb.replay/fetch-exec-and-args.exp, which allows me to test a corner
case that isn't currently exercised by gdbserver, this is the case for
sending pack inferior arguments, but no executable.
The qExecAndArgs reply format is 'S;exec;args;' where 'exec' and
'args' are hex encoded strings. If 'args' is empty then this is
perfectly valid, this just means there were no command line
arguments. But what if 'exec' is empty? I needed to decide what to
do in this case. The easiest choice is to treat empty 'exec' as the
executable is not set. But currently, due to how gdbserver works, it
is not possible to hit this case, so I used the gdbreplay testing
framework to exercise this instead. There were a few supporting
changes needed to write this test though.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Tom Tromey <tom@tromey.com>
share them would usually lead to GDB crashing. GDB now prevents
this invalid sharing.
+* When connecting to a remote server, if the server supports the new
+ qExecAndArgs packet, then GDB will copy the argument string from the
+ server and update the 'args' setting, as if 'set args ...' had been
+ used. This means that the arguments are visible from GDB using
+ 'show args', and that, if using the extended-remote protocol,
+ subsequent runs of the inferior will use the same arguments as the
+ first run.
+
* New targets
GNU/Linux/MicroBlaze (gdbserver) microblazeel-*linux*
reply with the single-inf-arg feature to indicate that it is able to
accept arguments as a single string.
+* New remote packets
+
+qExecAndArgs
+ This packet returns the executable filename and argument string with
+ which the server was started. If no such information was given to
+ the server then this is reflected in the reply.
+
*** Changes in GDB 17
* Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer
target system. If it is not set, the target will use a default
filename (e.g.@: the last program run).
+When connecting to a remote system, with @kbd{target extended-remote}
+or @kbd{target remote}, if the remote server supports the
+@code{qExecAndArgs} packet (@pxref{qExecAndArgs}), then the
+@kbd{remote exec-file} will be updated to reflect the executable
+currently running on the remote system.
+
@item set remote interrupt-sequence
@cindex interrupt remote programs
@cindex select Ctrl-C, BREAK or BREAK-g
The remote server created a new process.
@end table
+@cindex query executable, remote request
+@cindex query program arguments, remote request
+@cindex @samp{qExecAndArgs} packet
+@item qExecAndArgs
+@anchor{qExecAndArgs}
+Return the program filename and arguments string with which the remote
+server was started, if the remote server was started with such things.
+If the remote server was started without the filename of a program to
+execute, or without any arguments, then the reply indicates this.
+
+Reply:
+@table @samp
+@item U
+The program filename and arguments are unset. If @value{GDBN} wants
+to start a new inferior, for example with @samp{vRun}, then it will
+need to provide the filename of a program to use.
+
+@item S;@var{prog};@var{args};
+@itemx S;;@var{args};
+The program filename provided to the remote server when it started was
+@var{prog}, which is a hex encoded string. The complete argument
+string passed to the inferior when it started as @var{args}, this is
+also a hex encoded string.
+
+If no arguments were passed when the inferior started then @var{args}
+be an empty string.
+
+It is valid for @var{prog} to be the empty string, this indicates that
+the server has no program set, @value{GDBN} will need to supply a
+program name in order to start a new inferior. It is valid to reply
+with an empty @var{prog} and non-empty @var{args}, @value{GDBN} will
+set the inferior arguments, but the user will need to supply a remote
+exec-file before an inferior can be started.
+
+The argument string @var{args} is passed directly to @value{GDBN} as
+if to the @kbd{set args} command. And escaping required should be
+present in @var{args}, no changes are made by @value{GDBN}.
+@end table
+
@item Qbtrace:bts
Enable branch tracing for the current thread using Branch Trace Store.
inferior arguments as a single string. */
PACKET_vRun_single_argument,
+ /* Support the qExecAndArgs packet. */
+ PACKET_qExecAndArgs,
+
PACKET_MAX
};
packet_config m_protocol_packets[PACKET_MAX];
};
+/* Data structure used to hold the results of the qExecAndArgs packet. */
+
+struct remote_exec_and_args_info
+{
+ /* The result state reflects whether the packet is supported by the
+ remote target, and then which bits of state the remote target actually
+ returned to GDB . */
+ enum class state
+ {
+ /* The remote does not support the qExecAndArgs packet. GDB
+ should not make assumptions about the remote target's executable
+ and arguments. */
+ UNKNOWN,
+
+ /* The remote does understand the qExecAndArgs, no executable
+ (and/or arguments) were set at the remote end. If GDB wants the
+ remote to start an inferior it will need to provide this information. */
+ UNSET,
+
+ /* The remote does understand the qExecAndArgs, an executable
+ and/or arguments were set at the remote end and this information is
+ held within this object. */
+ SET
+ };
+
+ /* Create an empty instance, STATE should be state::UNKNOWN or
+ state::UNSET only. */
+ explicit remote_exec_and_args_info (state state = state::UNKNOWN)
+ : m_state (state)
+ {
+ gdb_assert (m_state != state::SET);
+ }
+
+ /* Create an instance in state::SET, move EXEC and ARGS into this
+ instance. */
+ explicit remote_exec_and_args_info (std::string &&exec, std::string &&args)
+ : m_state (state::SET),
+ m_exec (std::move (exec)),
+ m_args (std::move (args))
+ { /* Nothing. */ }
+
+ /* Is this object in state::SET? */
+ bool is_set () const
+ {
+ return m_state == state::SET;
+ }
+
+ /* Return the argument string. Only call when is_set returns true. */
+ const std::string &args () const
+ {
+ gdb_assert (m_state == state::SET);
+ return m_args;
+ }
+
+ /* Return the executable string. Only call when is_set returns true. */
+ const std::string &exec () const
+ {
+ gdb_assert (m_state == state::SET);
+ return m_exec;
+ }
+
+private:
+ /* The state of this instance. */
+ state m_state = state::UNKNOWN;
+
+ /* The executable path returned from the remote target. */
+ std::string m_exec;
+
+ /* The argument string returned from the remote target. */
+ std::string m_args;
+};
+
class remote_target : public process_stratum_target
{
public:
private:
+ /* Fetch the executable filename and argument string from the remote. */
+ remote_exec_and_args_info fetch_remote_executable_and_arguments ();
+
bool start_remote_1 (int from_tty, int extended_p);
/* The remote state. Don't reference this directly. Use the
return stop_reply_up (stop_reply);
}
+/* Read, decode, and return a hex-encoded string from *PTR. Update *PTR to
+ point at the first character past the end of the string that was read
+ in. */
+
+static std::string
+get_semicolon_delimited_hex_as_string (const char **ptr)
+{
+ const char *end = *ptr;
+
+ for (; *end != ';' && *end != '\0'; ++end)
+ ;
+
+ std::string str = hex2str (*ptr, (end - *ptr) / 2);
+ *ptr = end;
+ return str;
+}
+
+/* See declaration in class above. */
+
+remote_exec_and_args_info
+remote_target::fetch_remote_executable_and_arguments ()
+{
+ /* Setup some constants. This helps keep the return lines shorter in the
+ rest of this function. */
+ constexpr remote_exec_and_args_info::state UNKNOWN
+ = remote_exec_and_args_info::state::UNKNOWN;
+ constexpr remote_exec_and_args_info::state UNSET
+ = remote_exec_and_args_info::state::UNSET;
+
+ if (m_features.packet_support (PACKET_qExecAndArgs) == PACKET_DISABLE)
+ return remote_exec_and_args_info (UNKNOWN);
+
+ struct remote_state *rs = get_remote_state ();
+
+ putpkt ("qExecAndArgs");
+ getpkt (&rs->buf, 0);
+
+ packet_result result
+ = m_features.packet_ok (rs->buf, PACKET_qExecAndArgs);
+ switch (result.status ())
+ {
+ case PACKET_ERROR:
+ warning (_("Remote error: %s"), result.err_msg ());
+ [[fallthrough]];
+ case PACKET_UNKNOWN:
+ return remote_exec_and_args_info (UNKNOWN);
+ case PACKET_OK:
+ break;
+ }
+
+ /* First character should be 'U', to indicate no information is set in
+ the server, or 'S' followed by the filename and arguments. We treat
+ anything that is not a 'S' as if it were 'U'. */
+ if (rs->buf[0] != 'S')
+ return remote_exec_and_args_info (UNSET);
+
+ if (rs->buf[1] != ';')
+ {
+ warning (_("missing first ';' in qExecAndArgs reply"));
+ return remote_exec_and_args_info (UNSET);
+ }
+
+ std::string filename, args;
+
+ try
+ {
+ const char *ptr = &rs->buf[2];
+ filename = get_semicolon_delimited_hex_as_string (&ptr);
+
+ /* PTR now points either at a semicolon, or at the end of the incoming
+ buffer data. If we are looking at a semicolon, then skip it. */
+ if (*ptr == ';')
+ ++ptr;
+
+ /* We'll collect the inferior arguments in ARGS. */
+ args = get_semicolon_delimited_hex_as_string (&ptr);
+ }
+ catch (const gdb_exception_error &ex)
+ {
+ warning (_("unable to decode qExecAndArgs reply: %s"),
+ ex.what ());
+ return remote_exec_and_args_info (UNKNOWN);
+ }
+
+ return remote_exec_and_args_info (std::move (filename), std::move (args));
+}
+
/* Helper for remote_target::start_remote, start the remote connection and
sync state. Return true if everything goes OK, otherwise, return false.
This function exists so that the scoped_restore created within it will
rs->noack_mode = 1;
}
+ remote_exec_and_args_info exec_and_args
+ = fetch_remote_executable_and_arguments ();
+
+ /* Update the inferior with the executable and argument string from the
+ target, these will be used when restarting the inferior, and also
+ allow the user to view this state. */
+ if (exec_and_args.is_set ())
+ {
+ current_inferior ()->set_args (exec_and_args.args ());
+
+ if (!exec_and_args.exec ().empty ())
+ {
+ const std::string &remote_exec = get_remote_exec_file ();
+ if (!remote_exec.empty () && remote_exec != exec_and_args.exec ())
+ warning (_("updating 'remote exec-file' to '%ps' to match "
+ "remote target"),
+ styled_string (file_name_style.style (),
+ exec_and_args.exec ().c_str ()));
+ set_pspace_remote_exec_file (current_program_space,
+ exec_and_args.exec ());
+ }
+ }
+
if (extended_p)
{
/* Tell the remote that we are using the extended protocol. */
"single-inferior-argument-feature",
"single-inferior-argument-feature", 0);
+ add_packet_config_cmd (PACKET_qExecAndArgs, "qExecAndArgs",
+ "fetch-exec-and-args", 0);
+
/* Assert that we've registered "set remote foo-packet" commands
for all packet configs. */
{
# Modify the log file by changing the *response* to
# the vMustReplayEmty packet to an error.
- update_log $remotelog $output_file "vMustReplyEmpty" $newline
+ update_log $remotelog $output_file "vMustReplyEmpty" $newline true
clean_restart $::testfile
# Make sure we're disconnected, in case we're testing with an
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2023-2025 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/>. */
+
+/* Simple test, do some work with the arguments so GDB has a chance to
+ break and check that the arguments are correct. */
+
+volatile int global_counter;
+
+int
+main (int argc, char *argv[])
+{
+ int i;
+
+ global_counter = 0; /* Break here. */
+
+ for (i = 0; i < argc; ++i)
+ argv[i] = (char *) 0;
+
+ return 0;
+}
--- /dev/null
+# Copyright 2025 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/>. */
+#
+# Starts a communication with gdbserver setting the remotelog file.
+# Modifies the remotelog with update_log proc, injects an error message
+# instead of the expected replay to the vMustReplyEmpty packet in order
+# to test GDB reacts to the error response properly. After the remotelog
+# modification, this test restarts GDB and starts communication with gdbreply
+# instead of the gdbserver using the remotelog.
+
+load_lib gdbserver-support.exp
+load_lib gdbreplay-support.exp
+
+require allow_gdbserver_tests
+require has_gdbreplay
+
+standard_testfile
+
+if { [build_executable "failed to prepare" $testfile $srcfile] } {
+ return
+}
+
+# Connect to gdbserver and run to a breakpoint in main. Record the
+# remotelogfile into the REMOTELOG.
+proc_with_prefix record_initial_logfile { remotelog } {
+ clean_restart
+
+ # Make sure we're disconnected, in case we're testing with an
+ # extended-remote board, therefore already connected.
+ gdb_test "disconnect" ".*"
+
+ gdb_test_no_output "set sysroot" \
+ "setting sysroot before starting gdbserver"
+
+ # Start gdbserver.
+ set res [gdbserver_start "" "$::binfile a b c"]
+ set gdbserver_protocol [lindex $res 0]
+ set gdbserver_gdbport [lindex $res 1]
+
+ gdb_test_no_output "set remotelogfile $remotelog" \
+ "setup remotelogfile"
+
+ # Connect to gdbserver.
+ if {[gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] != 0} {
+ unsupported "$testfile (couldn't connect to gdbserver)"
+ return
+ }
+
+ # If we're connecting as 'remote' then we can't use 'runto'.
+ gdb_breakpoint [gdb_get_line_number "Break here"]
+ gdb_continue_to_breakpoint "continuing to breakpoint"
+
+ gdb_test "show remote exec-file" \
+ "The remote exec-file is \"[string_to_regexp $::binfile]\"\\."
+ gdb_test "show args" \
+ "Argument list to give program being debugged when it is started is \"a b c\"\\."
+}
+
+proc update_log_file { src dst } {
+ # Get the reply to the qExecAndArgs packet.
+ set reply [get_reply_line $src "qExecAndArgs"]
+
+ # Remove the program name from the reply.
+ regsub "S;\[^;\]+;" $reply "S;;" reply
+
+ # Remove the leading "r $" as this is added back by update_log.
+ regsub "^r \\\$" $reply "" reply
+
+ # Write the new reply line into the modified log file.
+ update_log $src $dst "qExecAndArgs" $reply false
+}
+
+# Rerun REMOTELOG using gdbreplay. The log file has been modified so
+# that the qExecAndArgs packet reply no longer includes the program
+# name, this will look like 'S;;args;'. As a result, GDB should no
+# longer overwrite and existing remote exec-file setting.
+#
+# When REMOTE_EXEC is true set a remote exec-file value and check this
+# is retained after connecting to gdbreplay. When REMOTE_EXEC is
+# false, don't set a remote exec-file value, check that GDB shows the
+# remote exec-file as unset after connecting.
+proc_with_prefix replay_with_update_logfile { remotelog remote_exec } {
+ clean_restart
+
+ # Make sure we're disconnected, in case we're testing with an
+ # extended-remote board, therefore already connected.
+ gdb_test "disconnect" ".*"
+
+ gdb_test_no_output "set sysroot"
+
+ # If requested, set a remote exec-file.
+ if { $remote_exec } {
+ set fake_remote_exec "/xxx/yyy/zzz"
+ gdb_test_no_output "set remote exec-file $fake_remote_exec"
+ set exec_file_re \
+ "The remote exec-file is \"[string_to_regexp $fake_remote_exec]\"\\."
+ } else {
+ set exec_file_re \
+ "The remote exec-file is unset, the default remote executable will be used\\."
+ }
+
+ # Start gdbreplay.
+ set res [gdbreplay_start $remotelog]
+ set gdbserver_protocol [lindex $res 0]
+ set gdbserver_gdbport [lindex $res 1]
+
+ # Connect to gdbreplay.
+ gdb_assert {[gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] == 0} \
+ "connect to gdbreplay"
+
+ # Same breakpoint and continue as when we recorded the log.
+ gdb_breakpoint [gdb_get_line_number "Break here"]
+ gdb_continue_to_breakpoint "continuing to breakpoint"
+
+ # Inspect GDB's state. With the modified logfile the executable
+ # was passed back as an empty string, indicating that the
+ # executable should not be changed.
+ gdb_test "show remote exec-file" \
+ $exec_file_re
+ gdb_test "show args" \
+ "Argument list to give program being debugged when it is started is \"a b c\"\\."
+}
+
+# The replay log is placed in 'replay.log'.
+set remotelog [standard_output_file replay.log]
+set remotelog_modified [standard_output_file replay-modified.log]
+
+record_initial_logfile $remotelog
+
+update_log_file $remotelog $remotelog_modified
+
+foreach_with_prefix set_remote_exec { true false } {
+ replay_with_update_logfile $remotelog_modified $set_remote_exec
+}
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2023-2025 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/>. */
+
+/* Simple test, do some work with the arguments so GDB has a chance to
+ break and check that the arguments are correct. */
+
+volatile int global_counter;
+
+int
+main (int argc, char *argv[])
+{
+ int i;
+
+ global_counter = 0; /* Break here. */
+
+ for (i = 0; i < argc; ++i)
+ argv[i] = (char *) 0;
+
+ return 0;
+}
--- /dev/null
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2023 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 the qExecAndArgs packet, specifically, the argument fetching
+# component of the packet.
+
+# Skip test if target does not support argument passing.
+require {!target_info exists noargs}
+
+load_lib gdbserver-support.exp
+
+standard_testfile
+
+require allow_gdbserver_tests
+
+if {[build_executable "failed to build" $binfile $srcfile]} {
+ return
+}
+
+# Used as an override for extended_gdbserver_load_last_file in
+# configs/native-extended-gdbserver.exp, prevents the remote exec-file
+# from being set.
+proc do_nothing {} { return 0 }
+
+# Check the 'show args' output. If PACKET is 'on' then we expect to
+# see the arguments 'a b c', otherwise we don't expect to see any
+# arguments.
+proc check_show_args { packet } {
+ if { $packet } {
+ set args_re "a b c"
+ } else {
+ set args_re ""
+ }
+
+ gdb_test "show args" \
+ "Argument list to give program being debugged when it is started is \"$args_re\"\\."
+}
+
+# Check the 'show remote exec-file' output. PACKET is either 'on' or
+# 'off' and reflects whether the qExecAndArgs packet is turned on or
+# off. FILENAME is what we expect to see included in the output, and
+# is converted to a regexp by this function.
+proc check_remote_exec_file { packet filename } {
+ if { $filename eq "" } {
+ set remote_exec_re \
+ "The remote exec-file is unset, the default remote executable will be used\\."
+ } else {
+ set remote_exec_re \
+ "The remote exec-file is \"[string_to_regexp $filename]\"\\."
+ }
+
+ gdb_test "show remote exec-file" $remote_exec_re
+}
+
+# Check the inferior has 4 arguments. Arg 0 will be the program name,
+# while 1, 2, and 3 should be a, b, and c respectively.
+proc check_full_args {} {
+ set exec_filename ""
+ gdb_test "print argc" " = 4"
+ gdb_test_multiple "print argv\[0\]" "" {
+ -re -wrap " = $::hex \"(.*/${::testfile})\"" {
+ set exec_filename $expect_out(1,string)
+ pass $gdb_test_name
+ }
+ }
+ gdb_test "print argv\[1\]" " = $::hex \"a\""
+ gdb_test "print argv\[2\]" " = $::hex \"b\""
+ gdb_test "print argv\[3\]" " = $::hex \"c\""
+
+ return $exec_filename
+}
+
+# Close and cleanup gdbserver process.
+proc cleanup_gdbserver {} {
+ catch {
+ close -i $server_spawn_id
+ wait -nowait -i $server_spawn_id
+ }
+}
+
+# Check that GDB can fetch the arguments from the remote using the
+# qExecAndArgs packet. When PACKET is 'on' we allow GDB to use the
+# packet, but when PACKET is 'off' we disable use of the qExecAndArgs
+# packet and ensure GDB falls back to the expected behaviour.
+proc_with_prefix test_exec_and_arg_fetch { packet } {
+ clean_restart $::testfile
+
+ # Make sure we're disconnected, in case we're testing with an
+ # extended-remote board, therefore already connected.
+ gdb_test "disconnect" ".*"
+
+ gdb_test "set remote fetch-exec-and-args ${packet}" \
+ "Support for the 'qExecAndArgs' packet on future remote targets is set to \"${packet}\"\\."
+
+ gdbserver_run "a b c"
+
+ gdb_breakpoint [gdb_get_line_number "Break here"]
+ gdb_continue_to_breakpoint "run to breakpoint"
+
+ # Look in the inferior to check the arguments were passed
+ # correctly. We get back the name of the executable the inferior
+ # is running. If PACKET is 'on' then we expect GDB to have
+ # automatically fetched this executable name from the remote.
+ set exec_filename [check_full_args]
+ if { !$packet } {
+ set exec_filename ""
+ }
+
+ # Check 'show args' to ensure GDB sees the correct arguments.
+ check_show_args $packet
+
+ # Check 'show remote exec-file' to ensure GDB sees the correct
+ # filename.
+ check_remote_exec_file $packet $exec_filename
+
+ # Below this point we rely on restarting the inferior, which
+ # relies on the extended-remote protocol.
+ if {[target_info gdb_protocol] ne "extended-remote"} {
+ cleanup_gdbserver
+ return
+ }
+
+ with_test_prefix "rerun" {
+ # Don't restart GDB, but re-run the inferior.
+ gdb_run_cmd
+ gdb_test "" \
+ "Breakpoint $::decimal, main \\(\[^)\]+\\).*" \
+ "rerun until breakpoint in main"
+
+ # If the packet is enabled then we expect the arguments to
+ # still be correct, otherwise, we should have defaulted back
+ # to no additional arguments.
+ if { $packet } {
+ check_full_args
+ } else {
+ gdb_test "print argc" " = 1"
+ }
+
+ # Check 'show args' to ensure GDB sees the correct arguments.
+ check_show_args ${packet}
+
+ # Check 'show remote exec-file' to ensure GDB sees the correct
+ # filename.
+ check_remote_exec_file $packet $exec_filename
+ }
+
+ cleanup_gdbserver
+}
+
+# With the extended-remote target it is possible to start gdbserver
+# without specifying an inferior to run. In this case, gdbserver
+# should reply to the qExecAndArgs packet with a 'U' (for unset)
+# response. GDB should not override any currently set remote
+# executable or inferior arguments. The benefit of this is that a
+# user can set these values before connecting to gdbserver in this
+# case.
+proc_with_prefix test_exec_and_args_unset { packet } {
+ clean_restart
+
+ # Make sure we're disconnected, in case we're testing with an
+ # extended-remote board, therefore already connected.
+ gdb_test "disconnect" ".*"
+
+ # Enable or disable the qExecAndArgs packet.
+ gdb_test "set remote fetch-exec-and-args ${packet}" \
+ "Support for the 'qExecAndArgs' packet on future remote targets is set to \"${packet}\"\\."
+
+ # Setup a remote exec-file value, and some inferior arguments.
+ set fake_exec_name "/xxx/yyy/zzz"
+ set fake_args "1 2 3"
+ gdb_test_no_output "set remote exec-file $fake_exec_name"
+ gdb_test_no_output "set args $fake_args"
+
+ # Start gdbserver in extended-remote mode, don't specify an
+ # executable to start, or any inferior arguments.
+ set res [gdbserver_start "--multi" ""]
+ set gdbserver_protocol "extended-remote"
+ set gdbserver_gdbport [lindex $res 1]
+ gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport
+
+ # Check within GDB that connecting to the extended-remote target
+ # didn't clobber the remote exec-file or inferior arguments.
+ gdb_test "show remote exec-file" \
+ "The remote exec-file is \"[string_to_regexp $fake_exec_name]\"\\."
+ gdb_test "show args" \
+ "Argument list to give program being debugged when it is started is \"${fake_args}\"\\."
+
+ cleanup_gdbserver
+}
+
+# Start GDB and set 'remote exec-file' to some random file. Then
+# start gdbserver with the name of the actual executable. Connect to
+# gdbserver from GDB and check that GDB gives a warning about the
+# remove exec-file value having changed.
+proc_with_prefix test_remote_exec_warning {} {
+ clean_restart
+
+ gdb_test "disconnect" ".*"
+
+ # Set the file GDB is going to debug. For extended-remote boards
+ # this also sets the remote exec-file.
+ gdb_file_cmd $::binfile
+
+ set invalid_remote_exec "/xxx/yyy/zzz"
+ gdb_test_no_output "set remote exec-file $invalid_remote_exec"
+ check_remote_exec_file on $invalid_remote_exec
+
+ # Start gdbserver.
+ set test "start gdbserver"
+ set target_exec [gdbserver_download_current_prog]
+ set target_exec_and_args "$target_exec a b c"
+ set catchres [catch {set res [gdbserver_start "" "$target_exec_and_args"]} errmsg]
+ if { $catchres != 0 } {
+ fail "$test: $errmsg"
+ } else {
+ pass "$test"
+ }
+
+ # And connect to gdbserver. Check for the warning GDB emits when
+ # the remote exec-file is updated.
+ set gdbserver_protocol [lindex $res 0]
+ set gdbserver_gdbport [lindex $res 1]
+ set test "connect to gdbserver"
+ set extra_re "warning: updating 'remote exec-file' to '[string_to_regexp $target_exec]' to match remote target"
+ set res [gdb_target_cmd_ext $gdbserver_protocol $gdbserver_gdbport $extra_re]
+ if { $res == 0 } {
+ pass $test
+ } elseif { $res == 1 } {
+ fail $test
+ } else {
+ unsupported $test
+ }
+
+ cleanup_gdbserver
+}
+
+# This override prevents the remote exec-file from being set when
+# using the extended-remote protocol. This is harmless when using
+# other boards.
+with_override extended_gdbserver_load_last_file do_nothing {
+ foreach_with_prefix packet { on off } {
+ test_exec_and_args_unset $packet
+ test_exec_and_arg_fetch $packet
+ }
+
+ test_remote_exec_warning
+}
#
# update_log $logname "${logname}.updated" "vMustReplyEmpty" "E.failed"
-proc update_log { filename_in filename_out match_regexp newline } {
+proc update_log { filename_in filename_out match_regexp newline truncate } {
set fh_in [open $filename_in r]
set fh_out [open $filename_out w]
while { [gets $fh_in line] >= 0 } {
# Print the line to the file.
puts $fh_out $line
- if { [regexp $match_regexp $line] } {
- # print out NEWLINE.
+
+ # If this line matches, then inject NEWLINE.
+ if { $match_regexp ne "" && [regexp $match_regexp $line] } {
+ # Print out NEWLINE.
puts $fh_out "r +\$${newline}"
- # Don't truncate the file, otherwise gdbreplay will
- # close the connection early and this might impact
- # what GDB does. We want GDB to get a chance to
- # process the error.
- puts $fh_out "c q"
- puts $fh_out "w \$qTStatus#49"
- puts $fh_out "End of log"
+ # Discard the line this just replaced.
+ gets $fh_in line
- break
+ if { $truncate } {
+ # Don't truncate the file, otherwise gdbreplay will
+ # close the connection early and this might impact
+ # what GDB does. We want GDB to get a chance to
+ # process the error.
+ puts $fh_out "c q"
+ puts $fh_out "w \$qTStatus#49"
+ puts $fh_out "End of log"
+
+ break
+ }
+
+ # Clear MATCH_REGEXP so no further lines will match.
+ set match_regexp ""
}
}
close $fh_out
close $fh_in
}
+
+# Return the line immediately after the first line in FILENAME that
+# matches MATCH_REGEXP. If MATCH_REGEXP matches a packet sent from
+# GDB to gdbserver, then the line returned should be the reply packet.
+#
+# If anything goes wrong, e.g. MATCH_REGEXP doesn't match anything,
+# then an empty string is returned.
+
+proc get_reply_line { filename match_regexp } {
+ set fh_in [open $filename r]
+ set reply ""
+
+ while { [gets $fh_in line] >= 0 } {
+ if { [regexp $match_regexp $line] } {
+ gets $fh_in reply
+ break
+ }
+ }
+
+ close $fh_in
+
+ return $reply
+}
return;
}
+ if (strcmp ("qExecAndArgs", own_buf) == 0)
+ {
+ if (program_path.get () == nullptr)
+ sprintf (own_buf, "U");
+ else
+ {
+ std::string packet ("S;");
+
+ packet += bin2hex ((const gdb_byte *) program_path.get (),
+ strlen (program_path.get ()));
+ packet += ";";
+
+ packet += bin2hex ((const gdb_byte *) program_args.c_str (),
+ program_args.length ());
+ packet += ";";
+
+ if (packet.size () > PBUFSIZ)
+ {
+ sprintf (own_buf, "E.Program name and arguments too long.");
+ return;
+ }
+
+ strcpy (own_buf, packet.c_str ());
+ *new_packet_len_p = packet.size ();
+ }
+ return;
+ }
+
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
own_buf[0] = 0;