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 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 *memset(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 tells 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. 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 emacs 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 targets.

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 command

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.