(移植編第3回)GDB対応する

2009/04/07

あなたは 人目のお客様です.

前回はシリアル2本を使えるようにしたので, 今回は調子に乗ってGDB対応してみよう.いわゆるリモートデバッグというやつだ. ちなみにGDB対応については第10回で説明しているが, GDBスタブの実装が必要になる.

で,GDBスタブなのだけど,第10回ではgdbのソースに付属しているi386用のスタブを 改造して利用したのだけど,残念ながらPowerPC用のスタブは付属していないのだな. なのでPowerPC用のスタブ作成が必要になる.

ここでスタブの実装方法についてなのだけど,実装方法には2通りある.

  1. 不正メモリアクセスなどの例外処理を直接ラッチしてスタブに渡す.
  2. 不正メモリアクセスの発生時にはOSの例外処理に入り,その中からスタブの 処理を呼ぶ.
で,本来ならば1の実装をすべき.というのは2の実装だとOSを通してしまうので, OSのデバッグに利用できないからだ.

ただ1の実装だと,OSが設定している割り込みハンドラを書き換えて, スタブ側の割り込みハンドラが呼ばれるようにする必要がある. これは通常は set_debug_traps() という関数を用意して, set_debug_traps() が呼ばれたら割り込みハンドラをごっそり置き換える (そしてデバッグの有効化時には,set_debug_traps() を呼ぶ)という処理を 行うようにする.

あと当然だがスタブ側で割り込みハンドラを用意する必要が出てくる. これはアセンブラで書く必要がある.スタブのサンプルを見ると, インラインアセンブラでなにやら書いている部分が多くあるが, これが独自割り込みハンドラのコードだ.

で,本来はこういったものをPowerPC用に用意しなければならないのだけど, 今回は面倒なのでやらないで上記2の実装にすることにする.つまり割り込みの ハンドリングはOSに任せて,OSからスタブを呼んでもらうようにする. このようにすることで,割り込みハンドラの準備が不要になり,スタブの実装は かなり楽になる.

ついでにいうと,現状ではKOZOSのPowerPC版は,割り込み発生時にはOS用のスタックを 0x200000という決め打ちの値で取っている(これは startup.s の _intr 参照)ので, もしもスタブ側で割り込みハンドリングするとしたら,これとはぶつからないような アドレスに別途スタックを確保する必要がある.まあこれは別の話なので 今は考えなくていい.

で,上記2の実装を採用することで,アセンブラを書く必要が無くなるので, スタブの実装はすげー楽になる. 実際にはゼロから作るのではなくてきとうなCPU用のスタブを移植するのが楽だが, 移植時には具体的には以下の点だけ修正すればよい.

まあこのへんは実際にソースコードを紹介しながらあとで説明しよう.

で,実際にGDB対応したのが以下のソースコード.

(2009/04/10 ライセンスに関する文書として,KL-01とLICENSEを追加. 詳しくは第43回を参照)

スタブはいちおう最新である第42回のi386-stub.cを 流用して ppc-stub.c というのを作成した.差分については diff をとるなどして 確認してほしいのだが,とりあえず修正点の説明は次回あたりにまわそう. GDB対応については説明することがいろいろあるので,今回はひとまず 動かしてみることにする.(だって動いているのが見たいよね)

で,ソースコードの修正はこれでいいのだけれど,まずはPowerPC用のGDBを 用意しなければならない.で,GNUのサイトから てきとうにダウンロードする.とりあえず現在はgdb-6.8が最新のようなので, それを持ってきた.

ぼくの環境はFreeBSDなのだけど,まずは解凍して,

% tar xvzf gdb-6.8.tar.gz
% cd gdb-6.8
% ./configure --target=powerpc-linux --prefix=/usr/local --disable-nls
...(中略)...
% gmake
...(後略)...
で,うまくビルドできた.注意として ./configure 時に --disable-nls が無いと,
sysdep.h:173:21: libintl.h: No such file or directory
というようなエラーで途中で止まってしまう.あとビルドは gmake で行う必要が あるみたい.BSD make を利用すると,
config.status: creating po/POTFILES
config.status: creating po/Makefile
"Makefile", line 1146: Need an operator
make: fatal errors encountered -- cannot continue
*** Error code 1
とかなって,やっぱし止まってしまう.

ビルドが終了すると,gdb-6.8/gdb というディレクトリ以下に gdb という実行形式が 作成される.これが PowerPC 用のGDBの実行形式になる.

では実際に動かしてみよう.まずはcuで

# cu -l /dev/cuad0 -s 115200
のようにして,ターゲットボードにシリアルで接続する. で,ファームウエアをいつもどおりターゲットボードにダウンロードして起動.

ここでtrapコマンドを実行してみよう.

なんか「$T051:0030bf00;:000423ac;thread:0005a7a4;#8a」という, わけのわからんメッセージが出てきた.これは実はターゲットボードのGDBスタブが 動作して出しているメッセージだ.trapコマンドを実行することでPowerPCのトラップ 命令が実行され,スタブに処理が渡ったのだ. で,スタブはPC上のGDBに対して,トラップ命令でブレークしたことを通知しようとして メッセージを出力しているわけだ.

このターゲットボードはシリアルを2本持っていて,本来は1本目を通常用途, 2本目をGDB用に使おうかなーと思っていたのだけど,シリアルケーブルが手元に 1本しかないので,とりあえず今回は通常用途とGDB用で共有するようにしてある (PSC1のみ利用し,PSC2は利用していない).なのでtrapコマンドを実行した コンソールに,GDBメッセージがそのまま出てきてしまっている.

で,GDBと繋ぐためにはとりあえずコンソールは終了しなければならない. これはcuの場合には,「~」「.」を連続して押下することで抜けられる.

次に,emacsを起動する.まあ実はgdbはemacs無しで直接起動することもできるの だけど,emacsから利用したほうが絶っっっ対に便利なので,とりあえずemacsを 起動してしまう.

ここで M-x gdb で,gdbモードに入る.M-x ってなんだかわからないひとも いるかもしれないが,これは[ESC][x][g][d][b]と順番にキーを押せばよい.

起動するgdbがデフォルトのままでいいか聞かれている. これはこのままだと,FreeBSDに標準で付属しているi386用のgdbが起動してしまう. 今回はPowerPC用にビルドしたものを利用しなければならないので,BackSpaceで 削除して,ビルドしたgdbを指定する.あと実行形式には,ファームウエアの ビルド時に作成されたELFファイルである「sample」を指定する.

Enterを押すと,gdbがスタートする.

ビルドしたgdbが起動していることの確認として, gdbのバージョンが6.8になっていることを一応確認しよう.

ここでシリアルの速度を115200bpsにするために,

(gdb) set remotebaud 115200
を実行する(デフォルトでは9600bpsになっている).さらに,
(gdb) target remote /dev/cuad0
を実行することで,シリアル経由でターゲットボードに接続する.

おお!接続できて,ブレーク箇所が表示された!

今回利用したスタブは, 第18回第23回で スレッド対応済みのものを流用したものだ.なのですでにスレッド対応されている. ためしに info threads を実行して,スレッド情報を見てみよう.

おー,ちゃんと表示されているみたい.すげー.

ちなみにもしもうまく接続できなかったら,chmod 666 /dev/cuad0 する必要が あるかもしれない(デフォルトでは /dev/cuadX は660になっているので). あと,PC上のGDBとターゲットボードとの間の通信の内容とかは,

(gdb) set debug remote 1
(gdb) set debug serial 1
(gdb) set debug target 1
を設定しておくことで参照できる.これはスタブのデバッグ時に役に立つ.

うーん,1日であっさり動いてしまった.まあいろいろ詰めておきたいところは 残っているのだけどとりあえずよかったかな.ていうかちょっと感動.

あ,ソースコードの修正内容の説明をまったくしていなかったね.これは次回!


メールは kozos(アットマーク)kozos.jp まで