How to run aarch64 binary on host PC

Linux

この記事では、aarch64アーキテクチャであるJetson Nanoプラットフォームのバイナリをx86_64アーキテクチャのホストPC上で実行する仕組みについて解説します。

binfmt_misc

binfmt_miscは任意のファイルを直接実行できるようにするLinuxの仕組みです。「直接」とは以下のように./test.sh をshellから指定するとtest.shが実行されることを指しています。一方bash test.shなどとした場合はbashを経由してtest.shを実行しているため直接ではありません。

$ echo -e '#!/bin/sh\n/usr/bin/echo hoge' > test.sh
$ chmod +x test.sh
$ ./test.sh
hogeCode language: Bash (bash)

下の図は、shellによる上記test.shの実行シーケンスです。

  1. shell(PID=10)はforkもしくはcloneを呼び出し、子プロセスshell(PID=11)を作成する
  2. shell(PID=11)はexecve("./test.sh")を実行する
  3. kernelは./test.shに応じたハンドラーが見つかればそのハンドラを使用しバイナリをロードする
  4. test.sh(PID=11)はforkもしくはcloneを呼び出し、子プロセスtest.sh(PID=12)を作成する
  5. test.sh(PID=12)はexecve("/usr/bin/echo")を実行する
  6. kernelは./usr/bin/echoに応じたハンドラーが見つかればそのハンドラを使用しバイナリをロードする

kernelは通常内部に、スクリプトをロードするbinfmt_script、ELFバイナリ(実行ファイル、ライブラリファイル)をロードするbinfmt_elf等を持っていて、execveに指定された実行ファイルの先頭の2バイトが!#であればbinfmt_scriptが!#に続くインタープリタ(test.shの場合は/bin/sh)をロードします。先頭の数バイトがELFバイナリを示していると判断されれば、binfmt_elfがそのELFファイルをロードします。

ここからようやくbinfmt_miscの話になりますが、binfmt_miscもbinfmt_scriptと同様に先頭の特定のバイトが一致したら特定のインタープリタを実行するという動作をします。

早速、binfmt_miscをインストールします。

sudo apt update && sudo apt install -y binfmt-supportCode language: Bash (bash)

サンプルとして、JPEGを直接実行した場合にイメージビューワー/usr/bin/eogを起動するbinfmt_miscを作成してみます。

$ ls /proc/sys/fs/binfmt_misc
python3.8  register  status
$ sudo bash -c  "echo ':jpeg:M::\xff\xd8::/usr/bin/eog:' > /proc/sys/fs/binfmt_misc/register"
# make sure "jpeg" is created
$ ls /proc/sys/fs/binfmt_misc
jpeg  python3.8  register  status
$ cat /proc/sys/fs/binfmt_misc/jpeg
enabled
interpreter /usr/bin/eog
flags: 
offset 0
magic ffd8Code language: Bash (bash)

まず/proc/sys/fs/binfmt_miscは通常のファイルシステムではありません。kernelが作成した疑似ファイルシステムでファイルサイズは常にゼロです。

/proc/sys/fs/binfmt_misc/registerに下記フォーマットに従うデータを書き込むと、kernelにbinfmt_miscのエントリが登録され、登録されたエントリが/proc/sys/fs/binfmt_miscの下に作成されます。

:name:type:offset:magic:mask:interpreter:flagsCode language: plaintext (plaintext)

nameは登録されるエントリ名、typeはEならファイルの拡張子、Mならバイト列、interpreterは実行するプログラムです。詳しくは、 https://www.kernel.org/doc/Documentation/admin-guide/binfmt-misc.rst を参照してください。上記JPEGの例では、namejpegtypeMmagicffd8interpreter/usr/bin/eogを指定しています。registerに書き込んだ後、jpegというファイルが/proc/sys/fs/binfmt_miscの下に作成されているのが分かります。

早速JPEGを直接実行してみましょう。

wget http://www.ess.ic.kanagawa-it.ac.jp/std_img/colorimage/Lenna.jpg
chmod +x Lenna.jpg
./Lenna.jpgCode language: Bash (bash)

qemu-user-static

binfmt_miscの仕組みが分かったところで、qemu-user-staticをインストールします。

$ sudo apt install qemu-user-static
$ ls /proc/sys/fs/binfmt_misc/
jpeg          qemu-arm    qemu-m68k        qemu-mips64el   qemu-ppc         qemu-riscv32  qemu-sh4eb        qemu-xtensa
python3.8     qemu-armeb  qemu-microblaze  qemu-mipsel     qemu-ppc64       qemu-riscv64  qemu-sparc        qemu-xtensaeb
qemu-aarch64  qemu-cris   qemu-mips        qemu-mipsn32    qemu-ppc64abi32  qemu-s390x    qemu-sparc32plus  register
qemu-alpha    qemu-hppa   qemu-mips64      qemu-mipsn32el  qemu-ppc64le     qemu-sh4      qemu-sparc64      status
Code language: Bash (bash)

/proc/sys/fs/binfmt_miscの下にいくつかファイルが追加されていることが分かります。qemu-aarch64というファイルを見てみます。

$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64-static
flags: OCF
offset 0
magic 7f454c460201010000000000000000000200b700
mask ffffffffffffff00fffffffffffffffffeffffffCode language: Bash (bash)

これは、「ファイルの先頭の20バイト(とmaskのビット論理積をとった結果)がmagicと一致していたら、/usr/bin/qemu-aarch64-staticをインタープリタとしてそのファイルを実行する」、という意味です。magicの値はaarch64 ELFバイナリを表します。

/usr/bin/qemu-aarch64-staticはaarch64命令をx86_64命令に変換し実行するエミュレータです。詳しくはこちら https://www.qemu.org/docs/master/user/main.html を参照してください。aarch64 Linuxとx86_64 Linuxのsystem callの番号の変換や、エミューレーションできないatomic命令の扱いについても書かれています。

まずはJetson Nano上でプログラムを作成してみます。

$ cat hello.c
#include <stdio.h>

int main(int argc, char *argv[]) {
	printf("hello world\n");
	return 0;
}
$ gcc hello.c -o hello --staticCode language: C++ (cpp)

--staticをつけてビルドします。作成されたhelloバイナリをホストPCにコピーし以下を実行します。

$ file hello
hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.7.0, BuildID[sha1]=cc6b82b4c3ee848187cbdc1483af59440970480f, not strippedCode language: Bash (bash)

aarch64でビルドされていることが確認できます。実行してみます。

$ ./hello
hello worldCode language: Bash (bash)

binfmt_miscにより、/usr/bin/qemu-aarch64-staticがインタープリタとして起動されhelloが実行されました。

以上です。

参考