ARM64のsemihosting機能をQEMU上で試してみる

# 動機
ARMが提供するsemihosting機能を試してみたい。

# semihosting機能とは
- [ARM セミホスティングとは](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203ij/Bgbjjgij.html)

> セミホスティングとは、ARM ターゲット上のアプリケーションコードから発行される入出力要求を、デバッガが実行されているホストコンピュータに伝達するメカニズムです。 例えば、このメカニズムを使用すると、printf() や scanf() などの C ライブラリ関数で、ターゲットシステム上の画面とキーボードではなく、ホストの画面とキーボードを使用することができます。

技術的には、特殊な命令をARM CPUが実行したときに、デバッガもしくはシミュレーション環境であればシミュレータがその命令をハンドルすることで、ホストマシン側のターミナルと入出力をつなぐ。ここで「特殊な命令」というのは、例えばラズパイなどで使われているCoretex-A53(ARMv8)の場合、aarch64モードでは`HLT 0xf000`と[定められている](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/CIHHGDBH.html)。

上記に引用の通り、通常DS-5などの開発環境と一緒にsemihosting機能を実装したCライブラリ関数printf()やscanf()が提供される。
QEMUでは、[こちら](https://github.com/qemu/qemu/blob/master/target/arm/arm-semi.c)のようにsemihostingを利用してホストの入出力を使用している。
また、u-bootやlinux kernelでは、semihostingを使ったデバドラが実装されている。
他にも、linaroが公開しているnewlibのlibgrossではsemihosting機能で実装した[システムコールAPI](https://git.linaro.org/toolchain/newlib.git/tree/libgloss/aarch64/syscalls.c)が提供されている。

今回はもっともお手軽に、QEMU上でaarch64のユーザアプリケーションを動かすことで、semihosting機能を試してみる。

# 環境
- OS: Ubuntu16.04 x86_64
- CPU: Intel(R) Core(TM) i7-3517U CPU @ 1.90GHz x4

# 手順
## 1. 必要なパッケージのインストール

```
$ sudo apt install gcc-5-aarch64-linux-gnu qemu-user-static
```

## 2. テストコードのビルド&実行

```
$ git clone https://github.com/takeoverjp/semihosting.git
$ cd semihosting
$ make
aarch64-linux-gnu-gcc -g -O0 -o semihosting -static semihosting.c
$ ./semihosting
This string is output via semihosting.
```

非常に短いプログラムなので、全文を以下に記す。
stdio.hを使っていないし、システムコールも呼び出していないのに文字が出力できている点がポイント。
smh_writec関数で1文字セミホスティングで出力している。インラインアセンブリなので少しビビるかも知れないが、やってることは非常に単純。`x0`レジスタに3(1文字出力を表す)を、`x1`レジスタに出力したい文字コードを格納し、`hlt 0xf000`命令を発行しているだけ。

```c
static void
smh_writec (int c)
{
  asm volatile ("mov  x1, %0\n\t"
                "mov  x0, #3\n\t"
                "hlt  0xf000\n\t"
                : : "r" (&c) : "x0", "x1", "memory");
}

int
main (int argc, char *argv[])
{
  const char str[] = "This string is output via semihosting.\n";

  for (int i = 0; str[i] != '\0'; i++)
    {
      smh_writec (str[i]);
    }

  return 0;
}
```

もちろんセミホスティングには1文字出力以外にもたくさんの機能があるが、基本的には`smh_writec()`でやっているように、`x0`レジスタに機能種別を格納して、`x1`, `x2`, `x3`レジスタに引数を格納し、`hlt 0xf000`命令を発行するだけで利用できる。

前述のとおり、ドライバやライブラリに組み込まれるケースが多いと思われるので、あまり直接使う機会はないかもしれないが、仕組みがわかっていれば他には影響を与えず必要な箇所だけ部分的に使うことも簡単にできそうなので、覚えておきたい。


# Tips
## QEMUユーザモードエミュレーションにおけるgdbデバッグ
qemu-user-staticをインストールすると、armの実行ファイルが直接実行できるようになる。とても便利なのだが、なぜ直接実行できるかわかっていないと、gdbデバッグするときに少し混乱するかも知れない。
これは、binfmtという仕組みで実現している。binfmtとは、特定のバイナリパターンで始まるファイルを実行するときには、予め指定したインタプリタを使って実行するという仕組みだ。つまり、シェルスクリプトなどで指定するシェバンのもっと汎用的な仕組みだと思えばいい。
ただしシェバンとは違い、実行ファイル単体を見ても、どのインタプリタを使うかはわからない。下記のようにすれば、binfmtが選択するインタプリタを確認することができる。

```
$ update-binfmts --find semihosting
/usr/bin/qemu-aarch64-static
```

binfmtのルールを確認したいときは、/procから確認できる。
```
$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64 
enabled
interpreter /usr/bin/qemu-aarch64-static
flags: OC
offset 0
magic 7f454c460201010000000000000000000200b700
mask ffffffffffffff00fffffffffffffffffeffffff
```

話が少し逸れたが、つまりシミュレータである`/usr/bin/qemu-aarch64-static`が`semihosting`というaarch64用の実行ファイルを逐次実行していくことになる。
このqemu-aarch64-staticに`-g`オプションを指定すれば、aarch64-linux-gnu-gdbでリモートデバッグできる。

```
$ qemu-aarch64-static -g 4321 semihosting
$ aarch64-linux-gnu-gdb -i=mi semihosting
(gdb) target remote :4321
Remote debugging using :4321
0x00000000004004c8 in _start ()
```

また、`qemu-aarch64-static --help`を見ればわかるが、`QEMU_GDB`環境変数でも`-g`オプションと同等の指定ができる。

```
$ QEMU_GDB=4321 ./semihosting
$ aarch64-linux-gnu-gdb -i=mi semihosting
(gdb) target remote :4321
Remote debugging using :4321
0x00000000004004c8 in _start ()
```


# 参考
- [GCCのインラインアセンブラの書き方 for x86](http://d.hatena.ne.jp/wocota/20090628/1246188338)
- [binfmtという魔法 - Linux上でC#とmonoが出会うまで](http://d.hatena.ne.jp/papamitra/20071215/binfmt)