先日サイボウズラボの開発合宿に参加させていただきました. 関係者のかた,どうもありがとうございました.
雰囲気としてはもくもく会によく似た感じで各自が自分のテーマに沿ってもくもくと開発を 行うというもので,いろいろとお話もできたしとても楽しめた. で,ぼくはKOZOSのGDB対応というテーマで進めたのだが,期間中になんとかそれっぽく 動くところまではいったのでさっそく公開しておこう.
GDB対応だが,第10回の「いよいよGDB対応だ!」以降で いろいろ説明してあるが,スタブという簡単な応答プログラムを実装することになる. PCで起動したGDBからGDBプロトコルによってシリアルなどを経由して 「このレジスタの値を教えて」とか「このメモリの値を教えて」とか聞いてくるので, それに答えることになる.
スタブの実装例はGDBに以下のものが付属している.
i386-stub.c m32r-stub.c m68k-stub.c sh-stub.c sparc-stub.cこれらを適当にながめてH8用に移植すればいいわけだ.ということでsparc-stub.cあたりが シンプルな実装なのでそのへんを見てH8向けに stub.c を実装してみた.
修正点を説明しよう.
まずMakefileだが,stub.cをコンパイル対象にするのとデバッグオプション-gを 追加する修正を行っている.
diff -ruN h8_15/os/Makefile h8_16/os/Makefile --- h8_15/os/Makefile 2011-12-29 16:18:49.000000000 +0900 +++ h8_16/os/Makefile 2011-12-29 17:54:46.000000000 +0900 @@ -24,12 +24,15 @@ # for simulator OBJS += vector.o intr.o +# for debugger +OBJS += stub.o + TARGET = kozos CFLAGS = -Wall -mh -nostdinc -nostdlib -fno-builtin #CFLAGS += -mint32 # intを32ビットにすると掛算/割算ができなくなる CFLAGS += -I. -#CFLAGS += -g +CFLAGS += -g CFLAGS += -Os #CFLAGS += -O0 CFLAGS += -DKOZOS
次にcommand.cを修正して,debugコマンドを実行するとデバッグモードに入って 強制ブレークするようにしてある.あとブレークポイントのテスト用にcallという コマンドを実行するとfunc()というダミー関数を呼ぶような処理を追加.
diff -ruN h8_15/os/command.c h8_16/os/command.c --- h8_15/os/command.c 2011-12-29 16:18:49.000000000 +0900 +++ h8_16/os/command.c 2011-12-29 17:48:54.000000000 +0900 @@ -4,6 +4,7 @@ #include "timerdrv.h" #include "netdrv.h" #include "icmp.h" +#include "stub.h" #include "lib.h" /* コンソール・ドライバの使用開始をコンソール・ドライバに依頼する */ @@ -51,6 +52,13 @@ kz_send(MSGBOX_ID_ICMPPROC, 0, (char *)buf); } +char *func(char *str) +{ + static char buf[32]; + strcpy(buf, str); + return buf; +} + int command_main(int argc, char *argv[]) { char *p; @@ -78,6 +86,11 @@ } else if (!strncmp(p, "ping", 4)) { /* pingコマンド */ send_write("ping start.\n"); send_icmp(); + } else if (!strncmp(p, "debug", 5)) { /* デバッガ起動 */ + set_debug_traps(); + force_break(); + } else if (!strncmp(p, "call", 4)) { /* ダミー関数の呼び出し */ + send_write(func(p + 4)); } else { send_write("unknown.\n"); }次に割り込みまわりの修正で,trapa #3 をブレークポイントとして拾えるように 割り込みベクタを追加する修正.これはブートローダー側に加える修正だが, 現在はシミュレータ対応のためにOS側も同じものを持っているので,OS側にも 同様に手を入れる.
diff -ruN h8_15/os/intr.S h8_16/os/intr.S --- h8_15/os/intr.S 2011-12-29 16:18:49.000000000 +0900 +++ h8_16/os/intr.S 2011-12-29 16:25:07.000000000 +0900 @@ -131,3 +131,29 @@ mov.l @er7+,er5 mov.l @er7+,er6 rte + + .global _intr_bkpoint +# .type _intr_bkpoint,@function +_intr_bkpoint: + mov.l er6,@-er7 + mov.l er5,@-er7 + mov.l er4,@-er7 + mov.l er3,@-er7 + mov.l er2,@-er7 + mov.l er1,@-er7 + mov.l er0,@-er7 + mov.l er7,er1 + mov.l #_intrstack,sp + mov.l er1,@-er7 + mov.w #SOFTVEC_TYPE_BKPOINT,r0 + jsr @_interrupt + mov.l @er7+,er1 + mov.l er1,er7 + mov.l @er7+,er0 + mov.l @er7+,er1 + mov.l @er7+,er2 + mov.l @er7+,er3 + mov.l @er7+,er4 + mov.l @er7+,er5 + mov.l @er7+,er6 + rte diff -ruN h8_15/os/intr.h h8_16/os/intr.h --- h8_15/os/intr.h 2011-12-29 16:18:49.000000000 +0900 +++ h8_16/os/intr.h 2011-12-29 16:25:23.000000000 +0900 @@ -3,12 +3,13 @@ /* ソフトウエア・割込みベクタの定義 */ -#define SOFTVEC_TYPE_NUM 5 +#define SOFTVEC_TYPE_NUM 6 #define SOFTVEC_TYPE_SOFTERR 0 #define SOFTVEC_TYPE_SYSCALL 1 #define SOFTVEC_TYPE_SERINTR 2 #define SOFTVEC_TYPE_TIMINTR 3 #define SOFTVEC_TYPE_ETHINTR 4 +#define SOFTVEC_TYPE_BKPOINT 5 #endif diff -ruN h8_15/os/vector.c h8_16/os/vector.c --- h8_15/os/vector.c 2011-12-29 16:18:49.000000000 +0900 +++ h8_16/os/vector.c 2011-12-29 16:24:26.000000000 +0900 @@ -6,6 +6,7 @@ extern void intr_serintr(void); /* シリアル割込み */ extern void intr_timintr(void); /* タイマ割込み */ extern void intr_ethintr(void); /* イーサネット・コントローラ割込み */ +extern void intr_bkpoint(void); /* デバッガ用ブレークポイント */ /* * 割込みベクタの設定. @@ -13,7 +14,7 @@ */ void (*vectors[])(void) = { start, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - intr_syscall, intr_softerr, intr_softerr, intr_softerr, + intr_syscall, intr_softerr, intr_softerr, intr_bkpoint, NULL, NULL, NULL, NULL, NULL, intr_ethintr /* IRQ5 */, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,最後にstub.cについてだが,以下の点が注意.それ以外はsparcの実装とかを見て テキトーに実装しただけ.
謎なのでGDBのソースコードを追ってみる.grep breakpoint とかやって追っかけると 以下のようにどうも呼ばれているようだ.
/*static unsigned char breakpoint[] = { 0x7A, 0xFF }; *//* ??? */ static unsigned char breakpoint[] = { 0x01, 0x80 }; /* Sleep */ちなみに上の「0x7aff」というのは逆アセンブルすると「bpt」という命令になる. まんま「ブレークポイント」のことのように思える.でもH8のマニュアルとか見ても, bptという命令は出てこなくて謎.
これについてなのだが,どうもシミュレータで動作させるときのもののように思える. というのはGDBのH8シミュレータのソース(sim/h8300/compile.c)を見ると, スリープ命令やbpt命令のときにSIGTRAPを発行してブレークするような実装に思える (compile.cのO_BPTやO_SLEEPの部分).つまりbpt命令というのは実H8には存在しないが ブレークのためにGDB側で独自に定義している疑似命令なのではないかと思うのだ. またシミュレータではsleep命令があるとそこでブレークする,という動作のようだ. なのでブレークポイントにsleep命令を埋め込んでいるのだろう.
まあ理由はさだかではないがいずれにしてもGDBは標準ではブレークポイントに上記0x0180を 埋め込んでしまうので,スリープ命令が埋め込まれてしまい,そのままではブレークポイント がうまく動作しない.ということで上記 h8300_breakpoint_from_pc() を以下のように 書きかえることで trapa #3 を埋め込むようになる.この修正をしてgdbをリビルドすると ブレークポイントが使えるようになる.
--- h8300-tdep.c~ 2011-03-19 03:52:30.000000000 +0900 +++ h8300-tdep.c 2011-12-27 16:43:01.000000000 +0900 @@ -1192,21 +1192,22 @@ if (regno == E_EXR_REGNUM) return E_PSEUDO_EXR_REGNUM (gdbarch); return regno; } const static unsigned char * h8300_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr) { /*static unsigned char breakpoint[] = { 0x7A, 0xFF }; *//* ??? */ - static unsigned char breakpoint[] = { 0x01, 0x80 }; /* Sleep */ + /*static unsigned char breakpoint[] = { 0x01, 0x80 }; *//* Sleep */ + static unsigned char breakpoint[] = { 0x57, 0x30 }; /* trapa #0x3 */ *lenptr = sizeof (breakpoint); return breakpoint; } static void h8300_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, struct frame_info *frame, const char *args) { fprintf_filtered (file, "\もうひとつの策として,スタブ側で対策するという方法もある. たとえばGDBから0x0180を書き込むような指令がきたら,スタブはそれを書き込まずに 「0x0x5730」を書き込んでしまうというものだ.これだとGDB側を修正しなくて済むが, ほんとに「0x0180」という値を書き込みたいときに誤動作することになる.
まあとりあえずはスタブがそれっぽく動いた.でもまだステップ実行に対応していないので, ブレークして変数の値を見るくらいしかできないのだが,これだけでもデバッグ用には けっこう使えるかもしんない.