(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

前回はシリアル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対応したのが以下のソースコード.スタブはいちおう最新である第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

を実行することで,シリアル経由でターゲットボードに接続する.

画像はこちら

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

今回利用したスタブは,第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日であっさり動いてしまった.まあいろいろ詰めておきたいところは残っているのだけどとりあえずよかったかな.ていうかちょっと感動.

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