(シミュレータ編第3回)シリアル対応の続きの説明

2010/09/12

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

前回はシリアル対応の説明をしたが, 使いかたについて続きの説明をしよう.

まず前回説明した修正をGDBに入れてgdbをビルドしなおす. さらにKOZOSのソースコード(ブートローダーとOS)にも修正を入れ,ビルドしておく.

で,ブートローダーのELF形式を引数にしてGDBを起動する. ぼくは普段はemacsからgdbを起動して利用しているのだけど, 今回は説明のためにベタで直接gdbを起動してみよう.

hiroaki@teapot:~/h8>% ./gcc/gdb-7.2/gdb/gdb ./sim/h8_sim_02/bootload/kzload.elf  
GNU gdb (GDB) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i386-unknown-freebsd6.2 --target=h8300-elf".
For bug reporting instructions, please see:
...
Reading symbols from /home/hiroaki/h8/sim/h8_sim_02/bootload/kzload.elf...done.
(gdb) 
起動したら target sim を実行する.
(gdb) target sim
Connected to the simulator.
(gdb) 
次に,load でメモリ上にロードする.
(gdb) load
connect to /dev/ttyp4
Loading section .vectors, size 0x100 vma 0x0
Loading section .text, size 0x1142 vma 0x100
Loading section .rodata, size 0xcd vma 0x1242
Loading section .data, size 0x10 vma 0xfffc20
Start address 0x100
Transfer rate: 39160 bits in <1 sec.
(gdb) 
まず .data セクションが 0xfffc20 というアドレスにロードされていることに 注意してほしい.前回も説明したが,GDBのH8シミュレータでは,VMAにロードされる ようだ.なので前回は,ブートローダー側でフラッシュROMからRAMにデータ領域を コピーする処理を無効化したわけだ.

もう1点,「connect to /dev/ttyp4」というメッセージが出ていることに注意して ほしい.これは前回説明したPTYを検索する以下の関数で出力されているメッセージだ.

static int search_freepty(host_callback *sim_callback)
{
  char ttydev[] = "/dev/ptyXX";
  int fd, i0 = 0, i1 = 0;
  char c0, c1;

  while ((c0 = "pqrsPQRS"[i0++]))
    {
      while ((c1 = "0123456789abcdefghijklmnopqrstuv"[i1++]))
        {
          ttydev[8] = c0;
          ttydev[9] = c1;
#ifdef USE_CALLBACK
          fd = sim_callback->open (sim_callback, ttydev, O_RDWR);
#else
          fd = open (ttydev, O_RDWR);
#endif
          if (fd < 0)
            continue;
          ttydev[5] = 't';
          (*sim_callback->printf_filtered) 
            (sim_callback, "connect to %s\n", ttydev);
          return fd;
        }
    }
  (*sim_callback->printf_filtered) (sim_callback,
                                    "Free pty not found.\n");
  return -1;
}
つまりここで出力されたデバイスファイルに対して接続すれば,シミュレータ上で 動作しているプログラムに対して接続できることになる.ということで 「connect to /dev/ttyp4」と出力されているので,ktermをもう一枚開いて, cuでttyp4に対して接続する.これはminicomとかkermitでもいい.
hiroaki@teapot:~>% su
Password:
teapot# cu -l /dev/ttyp4
Connected

ちなみにPTYはLinuxでも使えると思うが,上記関数はFreeBSD用なので, GNU/Linuxで動作させる場合はopenpty()のソースとかを参考にしてLinux用に 書き換える必要があるだろう.Windowsはよく知らないけど,PTYは使えないのでは ないかなあ.

で,runでH8シミュレータ上のプログラムが起動される.ようするにブートローダーが 起動する.

(gdb) run
Starting program: /home/hiroaki/h8/sim/h8_sim_02/bootload/kzload.elf 

ここでさっきのcuでの接続側に,コマンドプロンプトが出力される.
hiroaki@teapot:~>% su
Password:
teapot# cu -l /dev/ttyp4
Connected
kzload (kozos boot loader) started.
kzload> 
問題なく接続できているみたい.

次にOSのダウンロードだ.ブートローダーでloadコマンドを実行し, cuからはlsxを利用してXMODEMでOSの実行形式を転送してみる. まあ,やりかたは今までと同様だ.

なお前回説明したが,実機とシミュレータだと動作速度が異なるため, XMODEMでの最初のNAK出力と通信終了後のウエイトのループ回数を前回は調整している. これは各々のPCのスペックによって異なってくるので,各自でてきとうに調整して ほしい(調整の方法については,前回説明している).

kzload> load
~CLocal command? lsx sim/h8_sim_02/os/kozos
Sending sim/h8_sim_02/os/kozos, 48 blocks: Give your local XMODEM receive command now.
Bytes Sent:   6272   BPS:2105                            

Transfer complete

XMODEM receive succeeded.
kzload> 
無事にダウンロードできたようだ.

OSを起動してみよう.

kzload> run
starting from entry point: ffc020
kozos boot succeed!

command> echo test
 test
command> 
おー,起動できた.コマンド応答もできている.シリアル受信割り込みと 送信割り込みがちゃんと動いているということだ.

さて,実はここでちょっと問題がある.シミュレータで起動をかけているのは ブートローダーなので,GDBにはブートローダーのシンボル情報が読み込まれている. なのでOS起動後に関数名でブレークポイントを張ろうとしたり変数参照しようと しても,うまくいかない.

GDBは symbol-file というコマンドでシンボル情報を再ロードできるので, これで回避することはできる.やってみよう.

(gdb) symbol-file ./sim/h8_sim_02/os/kozos.elf 
Load new symbol table from "/home/hiroaki/h8/sim/h8_sim_02/os/kozos.elf"? (y or n) y
Reading symbols from /home/hiroaki/h8/sim/h8_sim_02/os/kozos.elf...done.
(gdb) break send_write
Breakpoint 1 at 0xffd4c8: file command.c, line 22.
(gdb) c
Continuing.

OSの send_write() にブレークポイントを張ってみた. この状態で,コマンド入力してみる.
command> echo sample

Enterを押すと,コマンド応答スレッドが動作してシリアル応答するために send_write()が呼ばれ,ブレークする.
(gdb) c
Continuing.

Breakpoint 1, send_write (str=0xffd974 " sample") at command.c:22
22        len = strlen(str);
(gdb) list
17      /* コンソールへの文字列出力をコンソール・ドライバに依頼する */
18      static void send_write(char *str)
19      {
20        char *p;
21        int len;
22        len = strlen(str);
23        p = kz_kmalloc(len + 2);
24        p[0] = '0';
25        p[1] = CONSDRV_CMD_WRITE;
26        memcpy(&p[2], str, len);
(gdb) 
無事にブレークしているようだ.

このようにシンボル情報を読み込み直せばOSもシンボルデバッグができる. できるのだが,問題はOSの起動部分(たとえばOSのmain()関数)にブレークを張りたい 場合だ.上の例だとOSの起動後にブレークを張っているため,起動直後にブレーク したい場合には,もっと先のタイミングでブレークポイントを張らなければならない.

このためには例えばブートローダーでloadコマンドの実行後,runコマンドでOSを 起動する直前にシンボル情報を再ロードしてブレークポイントを張る,ということが 考えられる.しかし困ったことに,これではダメなのだ.というのは OSのELF形式のメモリへの展開は,runコマンドの実行時にelf_load()によって 行われる.ところがどうもこれが行われると,ブレークポイントの設定が 無効になってしまうようなのだ.(たぶんブレーク位置にトラップを書き込むなど しているのだと思われる)

で,面倒だけどとりあえずは以下の手順で,OSの起動処理にブレークを張ることは できる.

  1. ブートローダーを起動し,OSをロードする.
  2. OSのロード後(runコマンドの実行前)に,ブートローダーのelf_load()に ブレークポイントを張る.
  3. ブートローダーでrunコマンドを実行する.
  4. elf_load()でブレークする.
  5. finishで,elf_load()の処理を完了する.
  6. GDBのsymbol-fileでOSのシンボル情報をロードする.
  7. main()にブレークポイントを張る.
  8. continueで処理を続行する.
  9. OSが起動し,main()でブレークする.
やってみよう.まず,ブートローダーを起動.
hiroaki@teapot:~/h8>% ./gcc/gdb-7.2/gdb/gdb ./sim/h8_sim_02/bootload/kzload.elf
...(中略)...
(gdb) target sim
Connected to the simulator.
(gdb) load
connect to /dev/ttyp4
Loading section .vectors, size 0x100 vma 0x0
Loading section .text, size 0x1142 vma 0x100
Loading section .rodata, size 0xcd vma 0x1242
Loading section .data, size 0x10 vma 0xfffc20
Start address 0x100
Transfer rate: 39160 bits in <1 sec.
(gdb) run
Starting program: /home/hiroaki/h8/sim/h8_sim_02/bootload/kzload.elf 

cuで接続.
teapot# cu -l /dev/ttyp4
Connected
kzload (kozos boot loader) started.
kzload> 
ブートローダーでloadコマンドを実行し,OSをダウンロード.
kzload> load
~CLocal command? lsx sim/h8_sim_02/os/kozos
Sending sim/h8_sim_02/os/kozos, 48 blocks: Give your local XMODEM receive command now.
Bytes Sent:   6272   BPS:1496                            

Transfer complete

XMODEM receive succeeded.
kzload> 
GDB側でCtrl-Cを入力することで強制ブレークし,GDBコマンドの入力待ちに入る.
(gdb) run
Starting program: /home/hiroaki/h8/sim/h8_sim_02/bootload/kzload.elf 
^C
Program received signal SIGINT, Interrupt.
0x00000cb2 in serial_is_recv_enable (index=1) at serial.c:94
94      {
(gdb) 
elf_loadにブレークポイントを張り,continueで処理続行.
(gdb) break elf_load
Breakpoint 1 at 0x11de: file elf.c, line 85.
(gdb) continue
Continuing.

ブートローダーでrunを実行する.
kzload> run

GDBがelf_load()でブレークする.
(gdb) continue
Continuing.

Breakpoint 1, elf_load (buf=0xffdf20 "\177ELF\001\002\001") at elf.c:85
85        struct elf_header *header = (struct elf_header *)buf;
(gdb) 
finishでelf_load()の処理を完了させる.
(gdb) finish
Run till exit from #0  elf_load (buf=0xffdf20 "\177ELF\001\002\001")
    at elf.c:85
0x00000446 in main () at main.c:99
99            entry_point = elf_load(loadbuf); /* メモリ上に展開(ロード) */
Value returned is $1 = 0xffc020 "z\a"
(gdb) 
symbol-fileコマンドでOSのシンボル情報をロードし,main()にブレークポイントを 張る.
(gdb) symbol-file sim/h8_sim_02/os/kozos.elf 
Load new symbol table from "/home/hiroaki/h8/sim/h8_sim_02/os/kozos.elf"? (y or n) y
Reading symbols from /home/hiroaki/h8/sim/h8_sim_02/os/kozos.elf...done.
Error in re-setting breakpoint 1: Function "elf_load" not defined.
(gdb) break main
Breakpoint 2 at 0xffc0ba: file main.c, line 25.
(gdb) 
continueで処理続行すると,main()でブレークする.
(gdb) continue
Continuing.

Breakpoint 2, main () at main.c:25
25        INTR_DISABLE; /* 割込み無効にする */
(gdb) list
20        return 0;
21      }
22
23      int main(void)
24      {
25        INTR_DISABLE; /* 割込み無効にする */
26
27        puts("kozos boot succeed!\n");
28
29        /* OSの動作開始 */
(gdb) 
おー,無事にOSのmain()でブレークしているようだ.

しかしこれは,なんぼなんでもめんどくさい.まあ簡単な解決策としては ブートローダーのrunコマンドでELFのロードと起動の両方をやっているのを止めて 別々のコマンドに独立させれば,OSの起動の前にシンボル情報を再ロードして ブレークポイントを張るタイミングができるので,だいぶ楽になる.

しかし,そもそも今後デバッグしたいのはブートローダーでなく,OSのほうだ. なのでOSのシンボルを再ロードするのもなんだかなあ,という気もする. この対策としては,そもそもシミュレータではブートローダーを介さずに,最初から OSを起動してしまうという方法がある.まあ現状でとりあえずはXMODEMのデバッグ とかできるようにブートローダーからOSを起動できるように作ってあるが, そのへんのデバッグをしないならば,ブートローダーからの起動は不要だし面倒だ. これについては次回かまあそのうちに説明しよう.


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