Files
binutils-gdb/gdbserver
Andrew Burgess 8e28eef6cd gdb/gdbserver: pass inferior arguments as a single string
GDB holds the inferior arguments as a single string.  Currently when
GDB needs to pass the inferior arguments to a remote target as part of
a vRun packet, this is done by splitting the single argument string
into its component arguments by calling gdb::remote_args::split, which
uses the gdb_argv class to split the arguments for us.

The same gdb_argv class is used when the user has asked GDB/gdbserver
to start the inferior without first invoking a shell; the gdb_argv
class is used to split the argument string into it component
arguments, and each is passed as a separate argument to the execve
call which spawns the inferior.

There is however, a problem with using gdb_argv to split the arguments
before passing them to a remote target.  To understand this problem we
must first understand how gdb_argv is used when invoking an inferior
without a shell.

And to understand how gdb_argv is used to start an inferior without a
shell, I feel we need to first look at an example of starting an
inferior with a shell.

Consider these two cases:

  (a)  (gdb) set args \$VAR
  (b)  (gdb) set args $VAR

When starting with a shell, in case (a) the user expects the inferior
to receive a literal '$VAR' string as an argument, while in case (b)
the user expects to see the shell expanded value of the variable $VAR.

If the user does 'set startup-with-shell off', then in (a) GDB will
strip the '\' while splitting the arguments, and the inferior will be
passed a literal '$VAR'.  In (b) there is no '\' to strip, so also in
this case the inferior will receive a literal '$VAR', remember
startup-with-shell is off, so there is no shell that can ever expand
$VAR.

Notice, that when startup-with-shell is off, we end up with a many to
one mapping, both (a) and (b) result in the literal string $VAR being
passed to the inferior.  I think this is the correct behaviour in this
case.

However, as we use gdb_argv to split the remote arguments we have the
same many to one mapping within the vRun packet.  But the vRun packet
will be used when startup-with-shell is both on and off.  What this
means is that when gdbserver receives a vRun packet containing '$VAR'
it doesn't know if GDB actually had '$VAR', or if GDB had '\$VAR'.
And this is a huge problem.

We can address this by making the argument splitting for remote
targets smarter, and I do have patches that try to do this in this
series:

  https://inbox.sourceware.org/gdb-patches/cover.1730731085.git.aburgess@redhat.com

That series was pretty long, and wasn't getting reviewed, so I'm
pulling the individual patches out and posting them separately.

This patch doesn't try to improve remote argument splitting.  I think
that splitting and then joining the arguments is a mistake which can
only introduce problems.  The patch in the above series which tries to
make the splitting and joining "smarter" handles unquoted, single
quoted, and double quoted strings.  But that doesn't really address
parameter substitution, command substitution, or arithmetic expansion.
And even if we did try to address these cases, what rules exactly
would we implement?  Probably POSIX shell rules, but what if the
remote target doesn't have a POSIX shell?  The only reason we're
talking about which shell rules to follow is because the splitting and
joining logic needs to mirror those rules.  If we stop splitting and
joining then we no longer need to care about the target's shell.

Clearly, for backward compatibility we need to maintain some degree of
argument splitting and joining as we currently have; and that's why I
have a later patch (see the series above) that tries to improve that
splitting and joining a little.  But I think, what we should really
do, is add a new feature flag (as used by the qSupported packet) and,
if GDB and the remote target agree, we should pass the inferior
arguments as a single string.

This solves all our problems.  In the startup with shell case, we no
longer need to worry about splitting at all.  The arguments are passed
unmodified to the remote target, that can then pass the arguments to
the shell directly.

In the 'startup-with-shell off' case it is now up to the remote target
to split the arguments, though in gdbserver we already did this, so
nothing really changes in this case.  And if the remote target doesn't
have a POSIX shell, well GDB just doesn't need to worry about it!

Something similar to this was originally suggested in this series:

  https://inbox.sourceware.org/gdb-patches/20211022071933.3478427-1-m.weghorn@posteo.de/

though this series didn't try to maintain backward compatibility,
which I think is an issue that my patch solves.  Additionally, this
series only passed the arguments as a single string in some cases,
I've simplified this so that, when GDB and the remote agree, the
arguments are always passed as a single string.  I think this is a
little cleaner.

I've also added documentation and some tests with this commit,
including ensuring that we test both the new single string approach,
and the fallback split/join approach.

I've credited the author of the referenced series as co-author as they
did come to a similar conclusion, though I think my implementation is
different enough that I'm happy to list myself as primary author.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392

Co-Authored-By: Michael Weghorn <m.weghorn@posteo.de>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Tested-By: Guinevere Larsen <guinevere@redhat.com>
Approved-by: Kevin Buettner <kevinb@redhat.com>
2025-09-12 11:06:00 +01:00
..
2025-04-08 10:54:39 -06:00
2025-04-08 10:54:39 -06:00
2025-08-19 20:34:03 -04:00
2025-04-08 10:54:39 -06:00
2025-04-08 10:54:39 -06:00
2025-04-08 10:54:39 -06:00
2025-09-09 11:59:04 -06:00
2025-04-08 10:54:39 -06:00
2024-10-21 16:38:18 +02:00
2025-04-08 10:54:39 -06:00
2025-09-09 11:59:04 -06:00
2025-09-09 11:59:04 -06:00
2025-04-08 10:54:39 -06:00

		   README for GDBserver & GDBreplay
		    by Stu Grossman and Fred Fish

Introduction:

This is GDBserver, a remote server for Un*x-like systems.  It can be used to
control the execution of a program on a target system from a GDB on a different
host.  GDB and GDBserver communicate using the standard remote serial protocol.
They communicate via either a serial line or a TCP connection.

For more information about GDBserver, see the GDB manual:

    https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html

Usage (server (target) side):

First, you need to have a copy of the program you want to debug put onto
the target system.  The program can be stripped to save space if needed, as
GDBserver doesn't care about symbols.  All symbol handling is taken care of by
the GDB running on the host system.

To use the server, you log on to the target system, and run the `gdbserver'
program.  You must tell it (a) how to communicate with GDB, (b) the name of
your program, and (c) its arguments.  The general syntax is:

	target> gdbserver COMM PROGRAM [ARGS ...]

For example, using a serial port, you might say:

	target> gdbserver /dev/com1 emacs foo.txt

This tells GDBserver to debug emacs with an argument of foo.txt, and to
communicate with GDB via /dev/com1.  GDBserver now waits patiently for the
host GDB to communicate with it.

To use a TCP connection, you could say:

	target> gdbserver host:2345 emacs foo.txt

This says pretty much the same thing as the last example, except that we are
going to communicate with the host GDB via TCP.  The `host:2345' argument means
that we are expecting to see a TCP connection to local TCP port 2345.
(Currently, the `host' part is ignored.)  You can choose any number you want for
the port number as long as it does not conflict with any existing TCP ports on
the target system.  This same port number must be used in the host GDB's
`target remote' command, which will be described shortly. Note that if you chose
a port number that conflicts with another service, GDBserver will 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 --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.

Usage (host side):

You need an unstripped copy of the target program on your host system, since
GDB needs to examine it's symbol tables and such.  Start up GDB as you normally
would, with the target program as the first argument.  (You may need to use the
--baud option if the serial line is running at anything except 9600 baud.)
Ie: `gdb TARGET-PROG', or `gdb --baud BAUD TARGET-PROG'.  After that, the only
new command you need to know about is `target remote'.  It's argument is either
a device name (usually a serial device, like `/dev/ttyb'), or a HOST:PORT
descriptor.  For example:

	(gdb) target remote /dev/ttyb

communicates with the server via serial line /dev/ttyb, and:

	(gdb) target remote the-target:2345

communicates via a TCP connection to port 2345 on host `the-target', where
you previously started up GDBserver with the same port number.  Note that for
TCP connections, you must start up GDBserver prior to using the `target remote'
command, otherwise you may get an error that looks something like
`Connection refused'.

Building GDBserver:

See the `configure.srv` file for the list of host triplets you can build
GDBserver for.

Building GDBserver for your host is very straightforward.  If you build
GDB natively on a host which GDBserver supports, it will be built
automatically when you build GDB.  You can also build just GDBserver:

	% mkdir obj
	% cd obj
	% path-to-toplevel-sources/configure --disable-gdb
	% make all-gdbserver

(If you have a combined binutils+gdb tree, you may want to also
disable other directories when configuring, e.g., binutils, gas, gold,
gprof, and ld.)

If you prefer to cross-compile to your target, then you can also build
GDBserver that way.  For example:

	% export CC=your-cross-compiler
	% path-to-topevel-sources/configure --disable-gdb
	% make all-gdbserver

Using GDBreplay:

A special hacked down version of GDBserver can be used to replay remote
debug log files created by GDB.  Before using the GDB "target" command to
initiate a remote debug session, use "set remotelogfile <filename>" to tell
GDB that you want to make a recording of the serial or tcp session.  Note
that when replaying the session, GDB communicates with GDBreplay via tcp,
regardless of whether the original session was via a serial link or tcp.

Once you are done with the remote debug session, start GDBreplay and
tell it the name of the log file and the host and port number that GDB
should connect to (typically the same as the host running GDB):

	$ gdbreplay logfile host:port

Then start GDB (preferably in a different screen or window) and use the
"target" command to connect to GDBreplay:

	(gdb) target remote host:port

Repeat the same sequence of user commands to GDB that you gave in the
original debug session.  GDB should not be able to tell that it is talking
to GDBreplay rather than a real target, all other things being equal.

As GDBreplay communicates with GDB, it outputs only the commands
it expects from GDB. The --debug-logging option turns printing the
remotelogfile to stderr on. GDBreplay then echos the command lines
to stderr, as well as the contents of the packets it sends and receives.