デバッグに便利なGDBですが、Raspberry Pi用にクロスコンパイルしてリモートデバッグを試してみました。
GNU関係のソフトは、必ずしもバージョンが新しければよいというわけではない(むしろ新しいバージョンだと思いもよらないバグがあったりする)のですが、今回はなんとなく新しめということで8.0で試してみました。
※よく見ると gdb-8.0.1.tar.gz というバージョンが出ているのでそっちの方がいいかも。
目次
構成と手順
ホスト環境 : Linux Mint
ターゲット環境: RaspberryPi (RASPBIAN STRETCH WITH DESKTOP 2017-09-07)
の環境で試しました。
大まかな作業の流れですが、
- (ホスト環境で)ターゲット(RaspberryPi)上で動かすGDBをクロスコンパイルする
- ホスト環境上で動作するGDBクライアントをビルドする
- クロスコンパイルしたモジュールをRaspberryPiにコピーする
- デバッグ用サンプルプログラムをビルドし、gdbserverを起動する
- ホスト環境上のGDBクライアントから(RaspberryPi上)のGdbserverに接続する
のような流れになります。結構やることが多いですね。
wgetからのクロスコンパイルまで
ターゲット用GDBのクロスコンパイル
私の手元の環境はUbuntu系のLinux Mintですが、arm用のクロスコンパイラ(arm-linux-gnueabihf)はapt-getでインストールできます。
$ sudo apt-get install gcc-arm-linux-gnueabihf
クロスコンパイラの用意ができれば、まずはターゲット上で動くGDBをクロスコンパイルします。
$ wget http://ftp.gnu.org/gnu/gdb/gdb-8.0.tar.gz $ tar xzfv gdb-8.0.tar.gz $ cd gdb-8.0 $ ./configure --target=arm-linux-gnueabihf --host=arm-linux-gnueabihf $ make
ビルドが終わると gdb フォルダ以下にビルド結果が出来上がっています。実行モジュールとしては、
- gdb
- gdbserver/gdbserver
- gdbserver/gdbreplay
が出来上がっています。
$file gdb/gdb gdb/gdb: ELF 32-bit LSB executable, ARM, EABI5 version 1 (GNU/Linux), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=01ea845e822ebef8d1db986a768ab9725828b02b, not stripped
問題なくクロスコンパイルできてそうです。ラズパイ側へコピーして動かしてみます。
gdbreplay は何に使うのか分かりませんが、いつか何かをreplayする時に役に立ちそうなので一緒にコピーしておきます。
ホスト環境用GDBのビルド
ホスト環境にプリインストールされているGDBはホストOSのアーキテクチャ向けのGDBなので、これをそのままターゲット(RaspberryPi)のリモートデバッグには使えません。
GDBのリモートデバッグをする場合、ターゲットのアーキテクチャ用のGDBクライアントが必要になります。
といっても話は簡単で、先ほどクロスコンパイルの手順にあったconfigure時に、
$ ./configure --target=arm-linux-gnueabihf $ make
とすればホストマシン上で動作する指定したtarget(今回はarm)用のgdbクライアントをビルドできます。
コンパイルしてできるモジュールは gdb というファイルになります。
これでGDBのビルドに関する作業は完了です。
RaspberryPi上でGDBを動かす
ここからはRspberryPi上での作業になります。コピーしておいたgdbをRaspberryPi上で動かしてみます。
と、その前にGDBは高機能なので、依存関係とかが気になるところです。
ラズパイ上で念のためlddしてみました。
$ldd gdbserver linux-vdso.so.1 (0x7ef96000) /usr/lib/arm-linux-gnueabihf/libarmmem.so (0x76ef8000) libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0x76ecc000) libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0x76d8b000) /lib/ld-linux-armhf.so.3 (0x54b65000)
うむ。ライブラリ全部解決できてる。素晴らしい!
※注意点として、raspbian の場合、普通にgdb を実行すると標準でインストールされているされているgdbが動きます。
$ gdb -v GNU gdb (Raspbian 7.7.1+dfsg-5) 7.7.1
手元のラズパイ環境では、7.7.1 が入っていました。
コピーしてきたGDBの動作を確認します。
以下、クロスコンパイルしてコピーしたgdb,gdbserver,gdbreplayが直下にあるものとします。
$ ./gdb -v GNU gdb (GDB) 8.0
gdbを起動して show configuration とたたくとビルド時の configuration を確認できるようです。
(gdb) show configuration This GDB was configured as follows: configure --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf --with-auto-load-dir=$debugdir:$datadir/auto-load --with-auto-load-safe-path=$debugdir:$datadir/auto-load --with-expat --with-gdb-datadir=/usr/share/gdb (relocatable) --with-jit-reader-dir=/usr/lib/gdb (relocatable) --without-libunwind-ia64 --with-lzma --with-python=/usr (relocatable) --with-separate-debug-dir=/usr/lib/debug (relocatable) --with-system-gdbinit=/etc/gdb/gdbinit --with-zlib --without-babeltrace
とりあえず何かデバッグしてみる。
クロスコンパイルしたGDB動いてそうということで、何かデバッグしてみます。
デバッグするプログラム(main.c)
#include <stdio.h> int main(int argc, char **argv) { int i = 0; int total = 0; for (i = 0; i < 10; i++) { total += i; } printf("%d\n", total); return 0; }
例によって0から9の合計を求める通称ずいぶんとダサいコードで動作を確かめてみます。
$ gcc main.c -g $ ./gdb a.out (gdb)b main (gdb)r Starting program: /home/pi/a.out Breakpoint 1, main (argc=1, argv=0x7efff744) at main.c:5 5 int i = 0; (gdb) info registers r0 0x1 1 r1 0x7efff744 2130704196 r2 0x7efff74c 2130704204 r3 0x1042c 66604 r4 0x0 0 r5 0x0 0 r6 0x102f8 66296 r7 0x0 0 r8 0x0 0 r9 0x0 0 r10 0x76fff000 1996484608 r11 0x7efff5ec 2130703852 r12 0x76f9d000 1996083200 sp 0x7efff5d8 0x7efff5d8 lr 0x76e78294 1994883732 pc 0x10440 0x10440 <main+20> cpsr 0x60000010 1610612752 (gdb)
間違いない。こいつはARMだ。
ということでGDBは問題なく動いてそうです。
いよいよリモートデバッグに挑戦!
リモートデバッグの手順は大きく、
の2フェーズになります。
ターゲット上でGDBサーバを起動
gdbserverをコピーし動かします。デバッグするプログラムは先ほどのサンプルプログラムです。
$./gdbserver localhost:10000 a.out
これで、ポート10000でGDBクライアントからの接続を待機している状態になります。
ホスト環境からの接続してデバッグ
さて、ターゲット側での準備が整いました。
後はホスト環境のGDBクライアントから接続をするのですが、
の2つが必要になります。
今回の例の場合、
デバッグしたいモジュール → a.out
デバッグしたいモジュールのソースコード → main.c(ずいぶんとダサいコード)
になります。これら2つはターゲット環境からホスト環境にコピーします。
コピーが終わったら、ホスト環境上でGDBクライアントを起動しリモート接続します。
リモート接続は、
$gdb デバッグするプログラム名
でgdbを起動した後、
target remote ターゲットのIPアドレス(ホスト名):ポート番号
を実行するとリモートのGDBサーバーに接続します。
私の手元の環境では、RaspberryPiのIPアドレスは192.168.11.17 になっています。gdbserver側のポート番号は10000を指定して起動しました。
$ ./gdb a.out (gdb) target remote 192.168.11.17:10000 Remote debugging using 192.168.11.17:10000 Reading /lib/ld-linux-armhf.so.3 from remote target... warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead. Reading /lib/ld-linux-armhf.so.3 from remote target... Reading symbols from target:/lib/ld-linux-armhf.so.3...Reading /lib/f72fb00897d4f06093d6f0451c9ca7d1f6e14c.debug from remote target... Reading /lib/.debug/f72fb00897d4f06093d6f0451c9ca7d1f6e14c.debug from remote target... (no debugging symbols found)...done. Python Exception <type 'exceptions.NameError'> Installation error: gdb.execute_unwinders function is missing: 0x76fce9e0 in ?? () from target:/lib/ld-linux-armhf.so.3 (gdb)
何やらメッセージがパラパラとでましたが、なんとなくつながってそうですね。
ちなみにこの時点でターゲット側のターミナルには、
Remote debugging from host 192.168.11.16 Remote side has terminated connection. GDBserver will reopen the connection. Listening on port 10000 Remote debugging from host 192.168.11.16
というログが出力されていました。
デバッグ操作
無事リモート接続できたら後の操作は普通のGDBのデバッグ時と同じ感覚で操作できるようです。
ただし、 run コマンドは実行できなくてブレークポイントを張って continue するところからデバッグが始められるようです。
私の環境では、
Python Exception <type 'exceptions.NameError'> Installation error: gdb.execute_unwinders function is missing:
というエラーメッセージが出ていましたがとりあえずステップ実行(next)や変数の値確認(print)などは問題なくできていました。
感想
こうして書いてみると長くなりましたが、記事が長くなったように実際の手順としても、リモートデバッグは準備するのが少し面倒だなと感じました。
小さなモジュールであればprintfデバッグの方が圧倒的に早そうですが、規模の大きなプログラムであればリモートデバッグの環境を整えてGDBでデバッグすることのメリットが活かせるように思います。
今回はターミナル上でのリモートデバッグを試してみましたが、Eclipse CDTからもリモートデバッグができるようなので試してみたと思います。
ちなみに、GDBに関する書籍は何冊か出ているのですが、printfデバッグ派なのであまり読んだことがありません。
組み込み開発(というよりは組み込みLinuxかも)でGDBを使う場合にはやはり「GDBを使った実践的デバッグ手法」が参考になります。

実践 デバッグ技法 ―GDB、DDD、Eclipseによるデバッギング
- 作者: Norman Matloff,Peter Salzman,相川愛三
- 出版社/メーカー: オライリージャパン
- 発売日: 2009/06/08
- メディア: 大型本
- 購入: 10人 クリック: 199回
- この商品を含むブログ (31件) を見る

GDBを使った実践的デバッグ手法―Emacs,Eclipse,Cygwin,Insi (Interface増刊)
- 作者: インターフェース編集部
- 出版社/メーカー: CQ出版
- 発売日: 2008/07/01
- メディア: 単行本
- 購入: 5人 クリック: 45回
- この商品を含むブログ (11件) を見る
GDBのプロトコルとかDWARFとか興味はあるので、勉強したいなぁとかよく思います。
そういえば、以前DWARFのドキュメントを読みかけたことがあるのですが、結構奥が深くて、挫折したままになっているのでまた気合をいれて勉強できればと思います。