この記事では、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
の実行シーケンスです。
- shell(PID=10)はforkもしくはcloneを呼び出し、子プロセスshell(PID=11)を作成する
- shell(PID=11)は
execve("./test.sh")
を実行する - kernelは
./test.sh
に応じたハンドラーが見つかればそのハンドラを使用しバイナリをロードする - test.sh(PID=11)はforkもしくはcloneを呼び出し、子プロセスtest.sh(PID=12)を作成する
- test.sh(PID=12)は
execve("/usr/bin/echo")
を実行する - 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の例では、name
はjpeg
、type
はM
、magic
はffd8
、interpreter
は/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が実行されました。
以上です。