Starting to debug

Now that you have gdbserver installed on the target and a cross GDB on the host you can start a debug session.

The connection between GDB and gdbserver can be through a network or a serial interface. In the case of a network connection, you launch gdbserver with the TCP port number to listen on and, optionally, an IP address to accept connections from. In most cases you don't care which IP address is going to connect, so you can just give the port number. In this example gdbserver waits for a connection on port 10000 from any host:

Next, start the copy of GDB from your toolchain, giving the same program as an argument so that GDB can load the symbol table:

In GDB, you use the command target remote to make the connection, giving the IP address or host name of the target and the port it is waiting on:

When gdbserver sees the connection from the host it prints the following:

The procedure is similar for a serial connection. On the target, you tell gdbserver which serial port to use:

You may need to configure the port baud rate beforehand using stty or a similar program. A simple example would be as follows:

There are many other options to stty, please read the man page for more details. It is worthwhile noting that the port must not be used for anything else, for example, you can't use a port that is being used as the system console. On the host, you make the connection to gdbserver using target remote plus the serial device at the host end of the cable. In most cases you will want to set the baud rate of the host serial port using the GDB command set remotebaud:

GDB needs to know where to find debug symbols and source code for shared libraries. When debugging natively the paths are well known and built in to GDB, but when using a cross toolchain, GDB has no way to guess where the root of the target filesystem is. You do so by setting the sysroot. The Yocto Project and Buildroot have different ways of handling library symbols so the location of the sysroot is quite different.

The Yocto Project includes debug information in the target filesystem image, so you need to unpack the target image tar file that is generated in build/tmp/deploy/images, for which you would need to do something like this:

Buildroot compiles libraries with minimal or full debug symbols, depending on BR2_ENABLE_DEBUG, puts them into the staging directory, then strips them as they are copied into target image. So, for Buildroot, the sysroot is always the staging area regardless of where the root filesystem is extracted.

There are some things that you need to do each time you run GDB, for example, setting the sysroot. It is convenient to put such commands into a command file and run them each time GDB is started. GDB reads commands from $HOME/.gdbinit, then from .gdbinit in the current directory and then from files specified on the command line with the -x parameter. However, recent versions of GDB will refuse to load .gdbinit from the current directory for security reasons. You can override that behavior for a single directory by adding a line like this to your $HOME/.gdbinit:

You can also disable the check globally by adding:

My personal preference is use the -x parameter to point to the command file, which exposes the location of the file so I don't forget about it.

To help you set up GDB, Buildroot creates a GDB command file containing the correct sysroot command in output/staging/usr/share/buildroot/gdbinit. It will contain a command similar to this one:

GDB has a great many commands, which are described in the online manual and in the resources mentioned in the Further Reading section. To help you get going as quickly as possible, here is a list of the most commonly used commands. In most cases there is a short-hand for the command, which is listed underneath the full command.

The following table shows the commands for breakpoints:

Commands

Use

break <location>

b <location>

Set a breakpoint on a function name, line number or line. Examples are: "main", "5", and "sortbug.c:42"

info break

i b

List breakpoints

delete break <N>

d b <N>

Delete breakpoint N

The following table shows the commands for running and stepping:

Commands

Use

run

r

Load a fresh copy of the program into memory and start it running. This does not work for remote debug using gdbserver

continue

c

Continue execution from a breakpoint

Ctrl-C

Stop the program being debugged

step

s

Step one line of code, stepping into any function that is called

next

n

Step one line of code, stepping over a function call

finish

Run until the current function returns

The following table shows the commands for getting information:

Commands

Use

backtrace

bt

List the call stack

info threads

Continue execution from a breakpoint

Info libs

Stop the program

print <variable>

p <variable>

Print the value of a variable, e.g. print foo

list

List lines of code around the current program counter

Gdbserver loads the program into memory and sets a breakpoint at the first instruction, then waits for a connection from GDB. When the connection is made you enter into a debug session. However, you will find that if you try to single step immediately you will get this message:

This is because the program is halted in code written in assembly that creates the run time environment for C and C++ programs. The first line of C or C++ code is the main() function. Supposing that you want to stop at main(), you would set a breakpoint there and then use the continue command (abbreviation c) to tell gdbserver to continue from the breakpoint at the start of the program and stop at main:

If at this point you see the following:

That means that you have forgotten the set sysroot!

This is all very different to starting a program natively, where you just type run. In fact, if you try typing run in a remote debug session, you will either see a message saying that the remote target does not support run, or in older versions of GDB it will just hang without any explanation.