FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Binary Translation型エミュレータを作る(x86命令ディスアセンブルのサポート)

Binary Translation方式の命令セットエミュレータのRust実装、ある程度進んできたが、どんどん複雑なテストパタンを確認していかなければならない。デバッグ機能についてだが、ホスト命令(つまりx86命令)のディスアセンブル機能を実装したい。つまり、変換後の命令に対してディスアセンブラを適用し、どのような命令に変換されたのかを確認する機能だ。

これには、iced-x86というクレートを活用することにした。

github.com

https://crates.io/crates/iced-x86

使い方については、ほとんどREADMEに追従するような形で使用した。まあ変換後の命令を渡してそのままstdoutに出力してほしいだけなので簡単だ。

                TCGX86::tcg_gen(&self, pc_address, tcg, &mut mc_byte);
                for be in &mc_byte {
                    let be_data = *be;
                    self.m_tcg_tb_vec.push(be_data);
                }
                // disassemble_x86を呼び出す。
                disassemble_x86(mc_byte.as_slice(), self.m_tb_text_mem.data());
                pc_address += mc_byte.len() as u64;
            }
...
pub fn disassemble_x86(bytes: &[u8], host_code_addr: *const u8) {
    let mut decoder = Decoder::new(EXAMPLE_CODE_BITNESS, bytes, DecoderOptions::NONE);
    decoder.set_ip(unsafe { host_code_addr.offset(0) as u64 });

    // Formatters: Masm*, Nasm*, Gas* (AT&T) and Intel* (XED)
    let mut formatter = GasFormatter::new();

    // Change some options, there are many more
    formatter.options_mut().set_digit_separator("_");
    formatter.options_mut().set_first_operand_char_index(10);

    // String implements FormatterOutput
    let mut output = String::new();

    // Initialize this outside the loop because decode_out() writes to every field
    let mut instruction = Instruction::default();

    // The decoder also implements Iterator/IntoIterator so you could use a for loop:
    //      for instruction in &mut decoder { /* ... */ }
    // or collect():
    //      let instructions: Vec<_> = decoder.into_iter().collect();
    // but can_decode()/decode_out() is a little faster:
    while decoder.can_decode() {
        // There's also a decode() method that returns an instruction but that also
...

デバッグモード二次出力するようにオプションを整える。実行してみよう。

$ cargo run -- --debug --elf-file /home/msyksphinz/work/riscv/riscv-tools/riscv-tests/build/isa/rv64ui-p-simple
Guest PC Address = 0000004c
  f1402573 : 
  00051063 : 
00007F7BA53E0000 48BF201316C8FF7F0000 movabs    $0x7FFF_C816_1320,%rdi
00007F7BA53E000A 48BE0A00000000000000 movabs    $0xA,%rsi
00007F7BA53E0014 48BA0000000000000000 movabs    $0,%rdx
00007F7BA53E001E 48B9140F000000000000 movabs    $0xF14,%rcx
00007F7BA53E0028 FF9518030000         callq     *0x318(%rbp)
Added offset. code_ptr = 3e
00007F7BA53E0000 488B8558000000       mov       0x58(%rbp),%rax
00007F7BA53E0007 483B8508000000       cmp       8(%rbp),%rax
00007F7BA53E000E 0F850A000000         jne       0x0000_7F7B_A53E_001E[f:id:msyksphinz:20200930225446p:plain]
00007F7BA53E0000 48B85400000000000000 movabs    $0x54,%rax
00007F7BA53E000A 48898508020000       mov       %rax,0x208(%rbp)
00007F7BA53E0011 E9B7FF0200           jmp       0x0000_7F7B_A540_FFCD
Offset is set 58
00007F7BA53E0000 48B85000000000000000 movabs    $0x50,%rax
00007F7BA53E000A 48898508020000       mov       %rax,0x208(%rbp)
00007F7BA53E0011 E9A1FF0200           jmp       0x0000_7F7B_A540_FFB7

上手く行ったようだ。f140257300051063RISC-Vの2命令 (本当はcsrr a0, mhartidbnez a0, pc + 0命令)がどのようなx86命令に変換されているかを確認することができるようになった。

f:id:msyksphinz:20200930225446p:plain