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 hoge
Code 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-support
Code 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 ffd8
Code 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:flags
Code 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.jpg
Code 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 ffffffffffffff00fffffffffffffffffeffffff
Code 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 --static
Code 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 stripped
Code language: Bash (bash)

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

$ ./hello hello world
Code language: Bash (bash)

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

以上です。

参考