Binary Translation方式の命令セットエミュレータのRust実装、ある程度進んできたが、どんどん複雑なテストパタンを確認していかなければならない。その際に問題となるのはデバッグ機能だ。デバッグ機能については、いくつかやらなければならないことがある。
- ホスト命令をBasicBlock単位に変換するのではなく、1命令単位で変換して実行する。これにより命令毎にレジスタの値をダンプすることができるようになり、デバッグの幅が広がる。
- ホスト命令のディスアセンブル機能。どのようなホスト命令が変換ターゲットとなるのか確認できる。
- ゲスト命令のディスアセンブル機能。ホスト命令がどのようなゲスト命令に変換されたのかを確認できる。
この3つについて実装を進めていこうと思う。まずは1命令単位でエミュレーションをする機能について。
通常QEMUをはじめとするBinary Translation型のエミュレータは、BasicBlock単位で命令の変換を行う。つまり、分岐命令に到達するまですべての命令をホスト命令に変換し、分岐命令に到達した時点で変換処理をいったん中止する。
この場合、やっかうなのがデバッグだ。分岐命令に到達するまで一気に命令が進むので、レジスタの状態などを命令毎にダンプすることができず、途中の結果などの細かな情報がどうしても取得することができない。
そこで、BasicBlock単位で変換を行うのではなく、命令毎に変換して、命令毎にブロックを区切ることによりそのたびにレジスタダンプなどが行えるようにする。これによりデバッグ時に取得できる情報が増える。
実際のコードを見る。通常は、いかの処理をブロックが終了するまで連続で実行している。
#[allow(while_true)] while true { let guest_inst = unsafe { ((self .m_guest_text_mem .data() ... let mut tcg_inst = TranslateRiscv::translate(id, &inst_info); self.m_tcg_vec.append(&mut tcg_inst);
self.m_tcg_vec
に対してTCGを次々と追加し、それをX86命令に変換するのだが、デバッグ時は1つ分TCGを追加するだけで処理を中断する。
let mut tcg_inst = TranslateRiscv::translate(id, &inst_info); self.m_tcg_vec.append(&mut tcg_inst); // これを追加。デバッグモードの時はすぐにTBを中断しホストコードに戻るTCGを追加する。 if debug { let mut exit_tcg = vec![TCGOp::new_0op(TCGOpcode::EXIT_TB)]; self.m_tcg_vec.append(&mut exit_tcg); } ... if debug { break; // When Debug Mode, break for each instruction } }
これにより、1命令毎に変換して処理が完了する。こうすれば1命令毎に様々な情報を出力することができるようになる。やってみよう。
$ cargo run -- --debug --dump-gpr \ --elf-file /home/msyksphinz/work/riscv/riscv-tools/riscv-tests/build/isa/rv64ui-p-simple
========= BLOCK START ========= Guest PC Address = 00000000 04c0006f : j pc + 0x4c tb_address = 0x7f3476e00000 x00 = 0000000000000000 x01 = 0000000000000000 x02 = 0000000000000000 x03 = 0000000000000000 x04 = 0000000000000000 x05 = 0000000000000000 x06 = 0000000000000000 x07 = 0000000000000000 x08 = 0000000000000000 x09 = 0000000000000000 x10 = 0000000000000000 x11 = 0000000000000000 x12 = 0000000000000000 x13 = 0000000000000000 x14 = 0000000000000000 x15 = 0000000000000000 x16 = 0000000000000000 x17 = 0000000000000000 x18 = 0000000000000000 x19 = 0000000000000000 x20 = 0000000000000000 x21 = 0000000000000000 x22 = 0000000000000000 x23 = 0000000000000000 x24 = 0000000000000000 x25 = 0000000000000000 x26 = 0000000000000000 x27 = 0000000000000000 x28 = 0000000000000000 x29 = 0000000000000000 x30 = 0000000000000000 x31 = 0000000000000000 ========= BLOCK START ========= Guest PC Address = 0000004c f1402573 : csrr a0, mhartid tb_address = 0x7f3476dc0000 helper_csrrs(emu, 10, 0, 0xf14) is called! x00 = 0000000000000000 x01 = 0000000000000000 x02 = 0000000000000000 x03 = 0000000000000000 x04 = 0000000000000000 x05 = 0000000000000000 x06 = 0000000000000000 x07 = 0000000000000000 x08 = 0000000000000000 x09 = 0000000000000000 x10 = 0000000000000000 x11 = 0000000000000000 x12 = 0000000000000000 x13 = 0000000000000000 x14 = 0000000000000000 x15 = 0000000000000000 x16 = 0000000000000000 x17 = 0000000000000000 x18 = 0000000000000000 x19 = 0000000000000000 x20 = 0000000000000000 x21 = 0000000000000000 x22 = 0000000000000000 x23 = 0000000000000000 x24 = 0000000000000000 x25 = 0000000000000000 x26 = 0000000000000000 x27 = 0000000000000000 x28 = 0000000000000000 x29 = 0000000000000000 x30 = 0000000000000000 x31 = 0000000000000000 ========= BLOCK START ========= Guest PC Address = 00000050 00051063 : bnez a0, pc + 0 tb_address = 0x7f3476df0000 x00 = 0000000000000000 x01 = 0000000000000000 x02 = 0000000000000000 x03 = 0000000000000000 x04 = 0000000000000000 x05 = 0000000000000000 x06 = 0000000000000000 x07 = 0000000000000000 x08 = 0000000000000000 x09 = 0000000000000000 x10 = 0000000000000000 x11 = 0000000000000000 x12 = 0000000000000000 x13 = 0000000000000000 x14 = 0000000000000000 x15 = 0000000000000000 x16 = 0000000000000000 x17 = 0000000000000000 x18 = 0000000000000000 x19 = 0000000000000000 x20 = 0000000000000000 x21 = 0000000000000000 x22 = 0000000000000000 x23 = 0000000000000000 x24 = 0000000000000000 x25 = 0000000000000000 x26 = 0000000000000000 x27 = 0000000000000000 x28 = 0000000000000000 x29 = 0000000000000000 x30 = 0000000000000000 x31 = 0000000000000000
こんな感じで、1命令ずつレジスタをダンプできるようになる。上手く行ったようだ。