By Avi Rozen on November 28, 2007
In theory, GDB, the GNU debugger, can ease the chore of debugging applications running on a Linux-based embedded system. In practice, setting up GDB for this task is a bit of a challenge; it takes some work, and there are some technical hurdles to overcome. However, the benefits of having a way to methodically debug a program instead of guessing what's wrong with it far outweigh the effort involved. Here are some tips for easing the difficulties.
Rather than run a full-blown instance of GDB on the target platform, you can use GDBserver, a program that lets you run GDB on a different machine than the one on which your program is running. The advantage of using GDBserver is that it needs just a fraction of the target resources that GDB consumes, because it implements only the low-level functionality of the debugger -- namely setting breakpoints and accessing the target processor registers and read/write application memory. GDBserver takes control of the application being debugged, then waits for instructions from a remote instance of GDB running on a development workstation.
Typically, the development workstation has a different processor (say, an i686 class processor) than the target platform (which may be ARM, PowerPC, or something else). This means you can't simply use the GDB executable that's installed on the workstation -- you want a cross-target debugger. In other words, you have to custom-build GDB from source code.
The following example uses a 7450 PowerPC as the target processor.
Before you begin, you need a communication interface between the PC running GDB and the target platform: either a serial link or, preferably, an Ethernet network connection. You also need a cross-target toolchain: a GNU C compiler (together with a C run-time library and binary utilities, a.k.a. binutils) that runs on the development workstation and generates executables for the target processor. You will build two sets of binaries from the GDB source code:
First download the GDB source code compressed archive, and unpack it:
mkdir -p ~/work/cross/gdb/downloads cd ~/work/cross/gdb/downloads wget <a href="http://ftp.gnu.org/gnu/gdb/gdb-6.7.1.tar.bz2<br /> cd" title="http://ftp.gnu.org/gnu/gdb/gdb-6.7.1.tar.bz2<br /> cd">http://ftp.gnu.org/gnu/gdb/gdb-6.7.1.tar.bz2<br /> cd</a> .. tar xvjf downloads/gdb-6.7.1.tar.bz2
To build the cross-target binaries, specify the target architecture with the --target option. The architecture identifier (here powerpc-7450-linux-gnu) is the prefix of all the cross-toolchain binaries (here the cross compiler binary is powerpc-7450-linux-gnu-gcc).
mkdir -p ~/work/cross/gdb/build/host_x86 cd ~/work/cross/gdb/build/host_x86 ../../gdb-6.7.1/configure --prefix=/opt/gdb/powerpc-7450-linux-gnu/cross --target=powerpc-7450-linux-gnu make make install
cd ~/work/cross/gdb/downloads wget <a href="ftp://ftp.gnu.org/gnu/termcap/termcap-1.3.1.tar.gz<br /> cd" title="ftp://ftp.gnu.org/gnu/termcap/termcap-1.3.1.tar.gz<br /> cd">ftp://ftp.gnu.org/gnu/termcap/termcap-1.3.1.tar.gz<br /> cd</a> .. tar xvzf downloads/termcap-1.3.1.tar.gz mkdir -p ~/work/cross/gdb/build/termcap cd ~/work/cross/gdb/build/termcap export CC=powerpc-7450-linux-gnu-gcc export RANLIB=powerpc-7450-linux-gnu-ranlib ../../termcap-1.3.1/configure --host=powerpc-7450-linux-gnu --prefix=$HOME/work/cross/termcap make make install
export LDFLAGS="-static -L$HOME/work/cross/termcap/lib" export CPPFLAGS="-I$HOME/work/cross/termcap/include" ../../gdb-6.7.1/configure --prefix=/opt/gdb/powerpc-7450-linux-gnu/native --host=powerpc-7450-linux-gnu --target=powerpc-7450-linux-gnu make make install
Once you've generated the GDBserver executable, copy it to the target platform. You can save some storage space by stripping all the debug information from it first, using the powerpc-7450-linux-gnu-strip utility.
Adapting the build procedure for a different target processor should only be a matter of using a different architecture identifier.
To facilitate debugging, you must compile an application with debug information by providing the -g command line option to the compiler/linker. The resulting executable file may be too big to fit into the storage space available on the target platform, so before moving it there you can strip the debug information from a copy of it using powerpc-7450-linux-gnu-strip, and place the stripped copy on the target platform. The stripped version is to be run with GDBserver on the target platform, and the non-stripped copy is to be loaded into GDB on the development workstation.
Remote debugging is rather straightforward: on the target platform, launch the application with GDBserver, while specifying the host and port for listening to an incoming TCP connection:
gdbserver HOST:PORT PROG [ARGS ...]
powerpc-7450-linux-gnu-gdb PROG
target remote HOST:PORT break main continue
You can also attach GDBserver to a process that's already running:
gdbserver HOST:PORT --attach PID
GDB commands work as expected while debugging a remote application, with a few exceptions -- most notably, the run command isn't used, since the program is already running when you start the debug session. Another quirk is that if the program is allowed to continue until it exits, then the remote GDBserver will exit too, and the remote session will terminate.
Setting up a working remote debugging environment may seem like too much of hassle to some -- "after all, nothing beats a well placed printf." But with GDB you can both trace and modify code and data flow, and otherwise analyse the behaviour of your code, without explicitly changing any of it. It doesn't magically solve bugs, but it sure helps.
Avi Rozen is a senior R&D engineer at a company that develops machine-vision-based products.
Источник: http://www.linux.com/feature/121735