先日サイボウズラボの開発合宿に参加させていただきました. 関係者のかた,どうもありがとうございました.
雰囲気としてはもくもく会によく似た感じで各自が自分のテーマに沿ってもくもくと開発を 行うというもので,いろいろとお話もできたしとても楽しめた. で,ぼくは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」という値を書き込みたいときに誤動作することになる.
まあとりあえずはスタブがそれっぽく動いた.でもまだステップ実行に対応していないので, ブレークして変数の値を見るくらいしかできないのだが,これだけでもデバッグ用には けっこう使えるかもしんない.