FPGA開発日記

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

Binary Translation型エミュレータを作る(AArch64に対応するために)

Binary Translation型のエミュレータを作っている。今のところRISC-Vのバイナリをx86に直接バイナリ変換して実行することができるようになった。riscv-testsの一部を実行してx86上でPassできるようになっている。

QEMUはもともとマルチプラットフォーム向けのDynamic Binary Translationエミュレータだ。エミュレーション元のターゲットはx86RISC-Vのみならず、AArch64やMIPSなどにも対応しており、さらにホストマシンの機械語としてもx86だけでなくAArch64、MIPSRISC-Vなどにも対応している。

ターゲットマシン(つまりゲストコード)は以下のアーキテクチャに対応している。

$ ls qemu/target
alpha  cris  i386  m68k        mips   nios2     ppc    rx     sh4    tilegx   unicore32
arm    hppa  lm32  microblaze  moxie  openrisc  riscv  s390x  sparc  tricore  xtensa

一方でホストマシン(つまりホストコード)は以下のアーキテクチャに対応している。

$ ls qemu/tcg
aarch64  arm  i386  mips  ppc  riscv  s390  sparc

自作エミュレータも同様に対応させたい。このためには、TCGの変換コードを拡張しなければならない。

RustのバイナリをAArch64向けにコンパイルする確認

Rustのビルド環境Cargoはマルチプラットフォームに対応しているようだった。AArch64向けにコンパイルするためには以下のようにビルドする。

$ rustup target add aarch64-unknown-linux-gnu

aarch64-unknown-linux-gnuで使用するコンパイラを指定する。私はUbuntu 18.04を使用しているので、以下のパッケージをインストールした。

$ sudo apt install gcc-aarch64-linux-gnu

Rustにaarch64-linux-gnu-gccを使ってもらうために、~/.cargo/configに以下を追加した。

[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"

これで自作エミュレータをビルドしてみる。x86の命令をDynamic Binary Translationで生成しているので実行できないのは当然だが、とりあえずビルドだけでも確認してみよう。

$ cargo build --target=aarch64-unknown-linux-gnu

生成されたバイナリを確認する。

$ file target/aarch64-unknown-linux-gnu/debug/uint_execute
target/aarch64-unknown-linux-gnu/debug/uint_execute: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=389de77b0ea4c87695c438c9c2d728e8a78006cc, with debug_info, not stripped

RustのバイナリをAArch64向けのQEMUで実行する確認

一応AArch64向けにバイナリを作成したので、これを動かしてみたい。Raspberry-Piとかを使えば良いのだが、いちいちバイナリをコピーしたりするのが面倒くさいので、AArch64向けに用意したQEMUを使って確認してみよう。

$ qemu-aarch64 target/aarch64-unknown-linux-gnu/debug/uint_execute riscv-tests/isa/rv64ui-p-slt
000000c0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000d0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000e0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000000f0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
reflect tb address = 0x5501b92000
qemu: uncaught target signal 4 (Illegal instruction) - core dumped

Illegal Instructionで落ちた。当たり前だ。一応ディスアセンブル結果も見て確認してみよう。

$ qemu-aarch64 -d in_asm target/aarch64-unknown-linux-gnu/debug/uint_execute riscv-tests/isa/rv64ui-p-slt
IN: _ZN12uint_execute7emu_env6EmuEnv3run17h5bf52c09850d1d5aE
0x550000d014:  f946efe1  ldr      x1, [sp, #0xdd8]
0x550000d018:  f94057e0  ldr      x0, [sp, #0xa8]
0x550000d01c:  f94023e8  ldr      x8, [sp, #0x40]
0x550000d020:  d63f0100  blr      x8

----------------
IN:
0x5501b91000:  48515455  ldxrh    w21, [x2]
0x5501b91004:  8148ef8b  .byte    0x8b, 0xef, 0x48, 0x81    // ここがx86のまま

これを修正していかなければならない。AArch64の命令セットについて調べていこう。