GDB/GDBserverによるクロスターゲットのリモートデバッグ
ターゲットプラットフォームでフル装備のGDBを動かさなくても GDBserver を使う手がある。別のマシンでGDBを実行できるようにするプログラムだ。GDBserverを使う利点は、GDBの消費するターゲットリソースのほんの一部しか消費しないことにある。デバッガの低レベルの機能(ブレークポイントの設定、ターゲットプロセッサのレジスタ操作、アプリケーションメモリの読み書き)しか実装されていないからだ。GDBserverはデバッグ対象アプリケーションの制御を奪い、開発ワークステーション上のリモートGDBからの指示を待つ。
いつものことだが、開発ワークステーションとターゲットプラットフォームでは搭載プロセッサの種類が異なる(前者がi686クラスのプロセッサなら、後者はARMやPowerPCといったところだろう)。これはワークステーションにインストールされているGDBバイナリがそのままでは使えないことを意味する。クロスターゲットデバッガが必要なのだ。つまり、GDBをソースコードから別途ビルドする必要がある。
GDBのビルド
以下の例ではターゲットプロセッサとして7450 PowerPCを使用する。
作業に入る前にGDBを動かすPCとターゲットプラットフォームの間の通信インターフェースを準備する必要がある。シリアルリンクでもよいが、できればEthernetネットワークで接続するのが望ましい。クロスターゲット・ツールチェーンとしてGNU Cコンパイラも必要だ(加えて、Cランタイムライブラリと、binutils、すなわちバイナリユーティリティ群も)。これを開発ワークステーション上で動かしてターゲットプロセッサ用の実行可能プログラムを生成するわけだ。GDBのソースコードから次の二組のバイナリをビルドすることになる。
- クロスターゲット ─ ホストは開発ワークステーション、ターゲットはターゲットプロセッサ
- ネイティブ ─ ホストとターゲットはどちらもターゲットプロセッサ
まず、GDBのソースコード圧縮アーカイブをダウンロードし、展開する:
mkdir -p ~/work/cross/gdb/downloads cd ~/work/cross/gdb/downloads wget http://ftp.gnu.org/gnu/gdb/gdb-6.7.1.tar.bz2 cd .. tar xvjf downloads/gdb-6.7.1.tar.bz2
GDBソースパッケージはGNUビルドシステムを使用しており、バイナリの生成は通常なら数個のコマンド(./configure ; make ; make install
)で完了する。しかし、今回のケースは少し注意が必要だ。2種類のホストプロセッサについてのバイナリをビルドし、各バイナリを別の場所にインストールしたいからである。そのため、各バイナリを、ソースパッケージを展開したディレクトリ内ではなく、それぞれ独自のディレクトリ内でビルドすることにする。さらに、configureスクリプトのコマンドラインオプションでターゲットおよびホストのプロセッサと、インストールディレクトリプレフィックスも指定する。
クロスターゲットのバイナリをビルドするには、--target
オプションでターゲットアーキテクチャを指定する。アーキテクチャ識別子(powerpc-7450-linux-gnu
)が、すべてのクロスツールチェーン・バイナリのプレフィックスとなる(クロスコンパイラのバイナリは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
ターゲットネイティブ・バイナリのビルドはもっとややこしい。一つにはホストアーキテクチャ(ターゲットアーキテクチャpowerpc-7450-linux-gnu
と同じ)を指定する必要があるからだ。もう一つの問題は欠けているライブラリだ。一部のライブラリがクロスツールチェーンで使えない可能性があり、ターゲットネイティブGDBのビルドに取りかかる前にビルドしておく必要がある。次の例は、ターゲットネイティブtermcap
ライブラリが欠けているとわかったときの対処法を示している(クロスビルドには多少の違いがあるので、不確かなときは「./configure --help
」で調べること)。
cd ~/work/cross/gdb/downloads wget ftp://ftp.gnu.org/gnu/termcap/termcap-1.3.1.tar.gz cd .. 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
検討すべき問題がもう一つある。バイナリを静的にリンクする必要があるかどうかだ。静的リンクが必要になるのは、GDBとGDBserverの運用に必要な共有ライブラリの一部が欠けている場合だが、それで生成される実行可能プログラムは動的リンクの場合よりもずっと大きくなる。静的リンクを指定するには、オプション-static
をLDFLAGS
環境変数に追加し、その後にconfigure
を実行する。追加ライブラリは、次のようにLDFLAGS
とCPPFLAGS
の両方で指定する必要がある。
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
ビルドプロセスの途中でGNUリンカから警告が出るはずだ。静的にリンクするアプリケーションで一部の関数(たとえば、dlopen、gethostbyname、その他いくつか)が使われているときは、実行時にリンク用としてGNU Cランタイムライブラリ版の共有ライブラリが必要になるからだ。それらのライブラリをターゲットプラットフォームにインストールしなければならないだろう。
GDBserverの実行可能プログラムを生成したら、ターゲットプラットフォームにコピーする。powerpc-7450-linux-gnu-strip
ユーティリティでデバッグ情報を取り除いておけば、ストレージを節約できる。
このビルド手続きを別の種類のターゲットプロセッサ用に書き換えるときは、アーキテクチャ識別子を変更するだけでよい。
デバッグの実際
デバッグをするためには、コンパイラ/リンカに-g
コマンドラインオプションを指定して、デバッグ情報付きでアプリケーションをコンパイルする必要がある。その結果、生成される実行可能ファイルが大きくなりすぎてターゲットプラットフォームのストレージスペースに入り切らないことがある。その場合は、ファイルを移動する前にpowerpc-7450-linux-gnu-strip
でデバッグ情報を取り除き、デバッグ情報除去後のファイルをターゲットプラットフォームに置けばよい。除去後のファイルがターゲットプラットフォームのGDBserverで実行され、除去前のファイルが開発ワークステーションのGDBに読み込まれる。
リモートデバッグについては、特に複雑なところはない。ターゲットプラットフォームで、GDBserverを用いてアプリケーションを起動し、その際、TCP接続からの情報をリスンするホストとポートを指定する。
gdbserver HOST:PORT PROG [ARGS ...]
続いて開発ワークステーションで、クロスターゲットGDBを起動する。
powerpc-7450-linux-gnu-gdb PROG
デバッグ情報を取り除く前の実行可能ファイルを指定すること。GDBコンソールで次のように入力する。
target remote HOST:PORT break main continue
これらのコマンドは、GDBをターゲットプラットフォームのGDBserverに接続し、プログラムの先頭にブレークポイントを設定し、最初のブレークポイントに達するまでプログラムを走らせる。
次のようにすることで、GDBserverを既に動作しているプロセスにアタッチすることもできる。
gdbserver HOST:PORT --attach PID
その後、プロセスは停止するので、リモートのGDBを用いてデバッグすることができる。
GDBのコマンドはリモートアプリケーションをデバッグするときも期待したように動くが、いくつか例外がある。顕著な違いは、run
コマンドを使わない点だ。デバッグセッションの開始時にプログラムが既に動いているからだ。また、プログラムを最後まで実行できるようにした場合、プログラムと共にリモートGDBserverも終了し、さらにリモートセッションが終了することも奇異に感ずるだろう。
実用的なリモートデバッグ環境をセットアップするのは、「結局、printf
が一番頼りになる」と宣う御仁には荷が重すぎるかもしれない。しかし、GDBを使えばコードやデータフローの追跡だけでなく修正も可能なので、コード自体に何も手を加えずにコードの挙動を詳しく調べることができる。バグを解決する魔法ではないが、頼りになることは間違いない。
Avi Rozenは、機械視覚を用いた製品を開発する企業の研究開発担当シニアエンジニア。