f78
The GDB remote serial protocol
Contents|Index|Previous|Next
The GDB remote serial protocol
To debug a program running on another machine (the debugging target machine), you must first arrange for all the usual prerequisites for the
program to run by itself. For example, for a C program, you need:
1. A startup routine to set up the C runtime environment; these usually have a
name like ‘crt0’. The startup routine may be supplied by your hardware supplier, or you may
have to write your own.
2. You probably need a C subroutine library to support your program’s subroutine
calls, notably managing input and output.
3. A way of getting your program to the other machine—for example, a download
program. These are often supplied by the hardware manufacturer, but you may have
to write your own from hardware documentation.
The next step is to arrange for your program to use a serial port to
communicate with the machine where GDB is running (the host machine). In general terms, the scheme looks like the following.
On the host
GDB already understands how to use this protocol; when everything else is set
up, you can simply use the ‘
target remote’ command (see Specifying a Debugging Target).
On the target
You must link with your program a few special-purpose subroutines that
implement the GDB remote serial protocol. The file containing these subroutines is
called a debugging stub. On certain remote targets, you can use an auxiliary
program
gdbserver instead of linking a stub into your program. See Using the gdbserver program for details.
The debugging stub is specific to the architecture of the remote machine; for
example, use ‘
sparc-stub.c’ to debug programs on SPARC boards. The following working remote stubs are distributed with GDB.
sparc-stub.c
For SPARC architectures.
m68k-stub.c
For Motorola 680x0 architectures.
i386-stub.c
For Intel 386 and compatible architectures.
The ‘
README’ file in the GDB distribution may list other recently added stubs.
What the stub can do for you
The debugging stub for your architecture supplies the following three
subroutines:
set_debug_traps
This routine arranges for handle_exception to run when your program stops. You must call this subroutine explicitly near
the beginning of your program.
handle_exception
This is the central workhorse, but your program never calls it explicitly—the
setup code arranges for handle_exception to run when a trap is triggered.
handle_exception takes control when your program stops during execution (for example, on a
breakpoint), and mediates communications with GDB on the host machine. This is
where the communications protocol is implemented; handle_ exception acts as the GDB representative on the target machine; it begins by sending
summary information on the state of your program, then continues to execute,
retrieving and transmitting any information GDB needs, until you execute a GDB
command that makes your program resume; at that point, handle_exception returns control to your own code on the target machine.
breakpoint
Use this auxiliary subroutine to make your program contain a breakpoint.
Depending on the particular situation, this may be the only way for GDB to get
control. For instance, if your target machine has some sort of interrupt button, you
won’t need to call this; pressing the interrupt button transfers control to handle_exception—in effect, to GDB. On some machines, simply receiving characters on the
serial port may also trigger a trap; again, in that situation, you don’t need to
call breakpoint from your own program—simply running ‘target remote’ from the host GDB session gets control.
Call
breakpoint if none of these is true, or if you simply want to make certain your program
stops at a predetermined point for the start of your debugging session.
What you must do for the stub
The debugging stubs that come with GDB are set up for a particular chip
architecture, but they have no information about the rest of your debugging target
machine.
First of all, you need to tell the stub how to communicate with the serial
port.
int getDebugChar()
Write this subroutine to read a single character from the serial port. It may
be identical to getchar for your target system; a different name is used to allow you to distinguish
the two if you wish.
void putDebugChar(int)
Write this subroutine to write a single character to the serial port. It may
be identical to <
ffb
FONT FACE="Courier New" SIZE="3">putchar for your target system; a different name is used to allow you to distinguish
the two if you wish.
If you want GDB to be able to stop your program while it is running, you need
to use an interrupt-driven serial driver, and arrange for it to stop when it
receives a
ˆC (‘\003’, the Control-C character). That is the character which GDB uses to tell the remote system to
stop.
Getting the debugging target to return the proper status to GDB probably
requires changes to the standard stub; one quick and dirty way is to just execute a
breakpoint instruction (the “dirty” part is that GDB reports a
SIGTRAP instead of a SIGINT).
Other routines you need to supply are the following.
void exceptionHandler (int exception_number, void *exception_address)
Write this function to install exception_address in the exception handling tables.
You need to do this because the stub does not have any way of knowing what the
exception handling tables on your target system are like (for example, the
processor’s table might be in ROM, containing entries which point to a table in RAM).
exception_number is the exception number which should be changed; its meaning is
architecture-dependent (for example, different numbers might represent divide by zero,
misaligned access, etc). When this exception occurs, control should be transferred
directly to exception_address, and the processor state (stack, registers, and so on) should be just as it
is when a processor exception occurs. So if you want to use a jump instruction
to reach exception_address, it should be a simple jump, not a jump to subroutine.
For the 386,
exception_address should be installed as an interrupt gate so that interrupts are masked while
the handler runs. The gate should be at privilege level 0 (the most privileged
level). The SPARC and 68k stubs are able to mask interrup themselves without help from exceptionHandler.
void flush_i_cache()
(sparc and sparclite only) Write this subroutine to flush the instruction
cache, if any, on your target machine. If there is no instruction cache, this
subroutine may be a no-op.
On target machines that have instruction caches, GDB requires this function to
make certain that the state of your program is stable.
You must also make sure the following library routine is available.
void *mem
ffb
set(void *, int, int)
This is the standard library function, memset, which sets an area of memory to a known value. If you have one of the free
versions of libc.a, memset can be found there; otherwise, you must either obtain it from your hardware
manufacturer, or write your own.
If you do not use the GNU C compiler, you may need other standard library subroutines as well; this
varies from one stub to another, but in general the stubs are likely to use any of
the common library subroutines which
gcc generates as inline code.
Putting it all together
In summary, when your program is ready to debug, you must use the following
steps.
1. Make sure you have the supporting low-level routines (see What you must do for the stub): getDebugChar, putDebugChar, flush_i_cache, memset, exceptionHandler.
2. Insert these lines near the top of your program:
set_debug_traps();
breakpoint();
3. For the 680x0 stub only, you need to provide a variable called exceptionHook. Normally you just use void (*exceptionHook)() = 0;, but if before calling set_debug_traps, you set it to point to a function in your program, that function is called
when GDB continues after stopping on a trap (for example, bus error). The
function indicated by exceptionHook is called with one parameter: an int which is the exception number.
4. Compile and link together: your program, the GDB debugging stub for your
target architecture, and the supporting subroutines.
5. Make sure you have a serial connection between your target machine and the GDB
host, and identify the serial port on the host.
6. Download your program to your target machine (or get it there by whatever
means the manufacturer provides), and start it.
7. To start remote debugging, run GDB on the host machine, and specify as an
executable file the program that is running in the remote machine. This tell
ffb
s GDB
how to find your program’s symbols and the contents of its pure text.
Then establish communication using the
target remote command. Its argument specifies how to communicate with the target
machine—either via a devicename attached to a direct serial line, or a TCP port (usually
to a terminal server which in turn has a serial line to the target). For
example, to use a serial line connected to the device named ‘/dev/ttyb’, use target remote /dev/ttyb.
To use a TCP connection, use an argument of the form
host:port. For example, to connect to port 2828 on a terminal server named manyfarms, use target remote manyfarms:2828.
Now you can use all the usual commands to examine and change data and to step
and continue the remote program.
To resume the remote program and stop debugging it, use the
detach command.
Whenever GDB is waiting for the remote program, if you type the interrupt
character (often,
C-C), GDB attempts to stop the program. This may or may not succeed, depending in
part on the hardware and the serial drivers the remote system uses. If you
type the interrupt character once again, GDB displays the following prompt:
Interrupted while waiting for the program.
Give up (and stop debugging it)? (y or n)
If you type y, GDB abandons the remote debugging session. (If you decide you want to try
again later, you can use target remote again to connect once more.) If you type n, GDB goes back to waiting.
Communication protocol
The stub files provided with GDB implement the target side of the
communication protocol, and the GDB side is implemented in the GDB source file ‘remote.c’. Normally, you can simply allow these subroutines to communicate, and ignore
the details. (If you’re implementing your own stub file, you can still ignore
the details: start with one of the existing stub files. ‘sparc-stub.c’ is the best organized, and therefore the easiest to read.)
However, there may be occasions when you need to know something about the
protocol—for example, if there is only one serial port to your target machine, you
might want your program to do something special if it recognizes a packet meant
for GDB.
All GDB commands and responses (other than acknowledgements, which are single
characters) are sent as a packet which includes a check-sum. A packet is
introduced with the character, ‘
$’, and ends with the character, ‘#’, followed by a two-digit checksum: $packet info#checksum.
checksum is computed as the modulo 256 sum of the packet info characters.
When either the host or the target machine receives a packet, the first
response expected is an acknowledgement: a single character, either ‘
+’ (to indicate the package was received correctly) or ‘-’ (to request retransmission). The host (GDB) sends commands, and the target
(the debugging stub incorporated in your program) sends data in response. The
target also sends data when your program stops.
Command packets are distinguished by their first character, which identifies
the kind of command.
The following are some of the commands currently supported (for a complete
list of commands, look in ‘
gdb/remote.c.’):
g
Requests the values of CPU registers.
G
Sets the values of CPU registers.
maddr, count
Read countbytes at location addr.
Maddr, count:...
Write count bytes at location, addr.
c c addr
Resume execution at the current address (or at addr, if supplied).
s s addr
Step the target program for one instruction, from either the current program
counter or from addr, if supplied.
k
Kill the target program.
?
Report the most recent signal. To allow you to take advantage of the GDB
signal handling commands, one of the functions of the debugging stub is to report
CPU traps as the corresponding POSIX signal values.
T
Allows the remote stub to send only the registers that GDB needs to make a
quick decision about single-stepping or conditional breakpoints. This eliminates
the need to fetch the entire register set for each instruction being stepped
through.
The GDB remote serial protocol now implements a write-through cache for
registers. GDB only re-reads the registers if the target has run.
If you have trouble with the serial connection, you can use the command,
set remotedebug
ffb
FONT>. This makes GDB report on all packets sent back and forth across the serial
line to the remote machine. The packet-debugging information is printed on the
GDB standard output stream. set remotedebug off turns it off, and show remotedebug shows you its current state.
Using the gdbserver program
gdbserver is a control program for Unix-like systems, which allows you to connect your
program with a remote GDB via target remote—but without linking in the usual debugging stub.
gdbserver is not a complete replacement for the debugging stubs, because it requires
essentially the same operating-system facilities that GDB itself does. In fact, a
system that can run gdbserver to connect to a remote GDB could also run GDB locally! gdbserver is sometimes useful nevertheless, because it is a much smaller program than
GDB itself. It is also easier to port than all of GDB, so you may be able to get
started more quickly on a new system by using gdbserver. Finally, if you develop code for real-time systems, you may find that the
tradeoffs involved in real-time operation make it more convenient to do as much
development work as possible on another system, for example by cross-compiling.
You can use gdbserver to make a similar choice for debugging.
GDB and
gdbserver communicate via either a serial line or a TCP connection, using the standard
GDB remote serial protocol.
On the target machine
You need to have a copy of the program you want to debug.
gdbserver does not need your program’s symbol table, so you can strip the program if
necessary to save space. GDB on the host system does all the symbol handling. To
use the server, you must tell it how to communicate with GDB; the name of your
program; and the arguments for your program. The syntax is: target> gdbserver comm program [args...].
comm is either a device name (to use a serial line) or a TCP hostname and
portnumber. For example, to debug Emacs with the argument, foo.txt, and communicate with GDB over the serial port, /dev/com1, use the following.
target> gdbserver /dev/com1 emacs foo.txt.
gdbserver
waits passively for the host GDB to communicate with it. To use a TCP
connection instead of a serial line, use the following.
target> gdbserver host:2345 e
ffb
macs foo.txt.
The only difference from the previous example is the first argument,
specifying that you are communicating with the host GDB via TCP. The ‘host:2345’ argument means that gdbserver is to expect a TCP connection from machine ‘host’ 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 TCP ports already in use on the target
system (for example, 23 is reserved for telnet). If you choose a port number that conflicts with another service, gdbserver prints an error message and exits.
You must use the same port number with the host GDB
target remote command.
On the GDB host machine
You need an unstripped copy of your program, since GDB needs symbols and
debugging information.
Start up GDB as usual, using the name of the local copy of your program as the
first argument. (You may also need the ‘
--baud’ option if the serial line is running at anything other than 9600 bps.)
After that, use
target remote to establish communications with gdbserver.
Its argument is either a device name (usually a serial device like
/dev/ttyb) or a TCP port descriptor in the form, host:PORT. For example, (gdb) target remote /dev/ttyb communicates with the server via serial line, /dev/ttyb.
(gdb) target remote the-target:2345 communicates via a TCP connection to port 2345 on host, the-target. For TCP connections, you must start up gdbserver prior to using the target remote command. Otherwise you may get an error whose text depends on the host
system, but which usually looks something like ‘Connection refused’.
Using the gdbserve.nlm program
gdbserve.nlm is a control program for NetWare systems, which allows you to connect your
program with a remote GDB via target remote.
GDB and
gdbserve.nlm communicate via a serial line, using the standard GDB remote serial protocol.
On the target machine
You need to have a copy of the program you want to debug.
gdbserve.nlm does not need your program’s symbol table, so you can strip the program if
necessary to save space. GDB on the host system does all the symbol handling. To
use the server, you must tell it: how to communicate with GDB, the name of your
program, and the arguments for your program. The syntax is the following.
load gdbserve [ BOARD=board ] [ PORT=port ]
[ BAUD=baud ] program [ args ... ]
board
and port specify the serial line; baud specifies the baud rate used by the connection. port and node default to 0, baud defaults to 9600 bps. For example, to debug Emacs with the argument ‘foo.txt’and communicate with GDB over serial port number 2 or board 1 using a 19200
bps connection, use the following.
load gdbserve BOARD=1 PORT=2 BAUD=19200 emacs foo.txt
On the GDB host machine, you need an unstripped copy of your program, since
GDB needs symbols and debugging information. Start up GDB as usual, using the
name of the local copy of your program as the first argument. (You may also need
the ‘--baud’ option if the serial line is running at anything other than 9600 bps.
After that, use
target remote to establish communications with gdbserve.nlm. Its argument is a device name (usually a serial device, like /dev/ttyb). For example, (gdb) target remote /dev/ttyb communicates with the server via serial line, /dev/ttyb.
GDB with a remote i960 (Nindy)
Nindy is a ROM Monitor program for Intel 960 target systems. When GDB is configured to
control a remote Intel 960 using Nindy, you can tell GDB how to connect to the 960
in the following ways:
Through command line options specifying serial port, version of the Nindy
protocol, and communications speed;
By responding to a prompt on startup;
By using the
target command at any point during your GDB session. See Commands for managing tar
ffb
gets.
Startup with Nindy
If you simply start gdb without using any command-line options, you are prompted for what serial port
to use, before you reach the ordinary GDB prompt:
Attach /dev/ttyNN -- specify NN, or "quit" to quit:
Respond to the prompt with whatever suffix (after /dev/tty) identifies the serial port you want to use. You can, if you choose, simply
start up with no Nindy connection by responding to the prompt with an empty
line. If you do this and later wish to attach to Nindy, use target (see Commands for managing targets).
Options for Nindy
The following are the startup options for beginning your GDB session with a
Nindy-960 board attached.
-r port
Specify the serial port name of a serial interface to be used to connect to
the target system. This option is only available when GDB is configured for the
Intel 960 target architecture. You may specify port as any of: a full pathname (e.g., -r /dev/ttya), a device name in ‘/dev’ (e.g., -r ttya), or simply the unique suffix for a specific tty (e.g., -r a).
-O
(An uppercase letter “O”, not a zero.) Specify that GDB should use the “old”
Nindy monitor protocol to connect to the target system. This option is only
available when GDB is configured for the Intel 960 target architecture.
Warning:
If you specify ‘
-O’, but are actually trying to connect to a target system that expects the
newer protocol, the connection fails, appearing to be a speed mismatch. GDB
repeatedly attempts to reconnect at several different line speeds. You can abort this
process with an interrupt.
-brk
Specify that GDB should first send a BREAK signal to the target system, in an attempt to reset it, before connecting to
a Nindy target.
Warning:
Many target systems do not have the hardware that this requires; it only works
with a few boards.
The standard ‘
-b’ option controls the line speed used on the serial port.
Nindy reset com
17b
mand
reset
For a Nindy target, this command sends a “break” to the remote target system;
this is only useful if the target has been equipped with a circuit to perform a
hard reset (or some other interesting action) when a break is detected.
0