遅ればせながらNetBSD Advent Calendar 2018 19日目の記事です。
今日はNetBSDでLinuxバイナリをエミュレーションして動かす手順を紹介しようと思います。
NetBSDでのLinuxバイナリエミュレーション
NetBSDでは、Linuxバイナリを直接実行するためのエミュレーションレイヤが用意されています。
設定手順はNetBSDドキュメントの"Chapter 10. Linux emulation]の章に記載されています。
Linuxバイナリを実行する環境を作る
基本的にはChapter 10. Linux emulationの内容をそのまま実施するだけでLinuxバイナリの実行環境が構築できます。
今回は実際にドキュメントで紹介されている、Linux版のAcrobat Readerを動かす例を試してみましょう。
必要なカーネルオプション
ドキュメントには、以下のカーネルオプションを有効にする必要がある旨が書かれていますが、NetBSD-amd64環境では、GENERICカーネル(デフォルトのカーネル)で既に有効化された状態になっています。
option COMPAT_LINUX
option EXEC_ELF32
また、どのLinuxカーネルバージョンに相当するかは、 sysctl
コマンドで確認できます。今回の例では Linux-3.11.6
のようです。
# sysctl -a | grep -i linux
emul.linux.kern.ostype = Linux
emul.linux.kern.osrelease = 3.11.6
emul.linux.kern.osversion = #1 SMP PREEMPT Thu Oct 24 16:23:02 UTC 2013
emul.linux32.kern.ostype = Linux
emul.linux32.kern.osrelease = 3.11.6
emul.linux32.kern.osversion = #1 SMP PREEMPT Thu Oct 24 16:23:02 UTC 2013
カーネルモジュールをロードする
Linuxバイナリエミュレーション機能を提供するカーネルモジュールをロードします。
こちらもNetBSD-amd64な環境では、デフォルトでロードされる設定になっているようです。
# modload compat_linux
# modload exec_elf32
#
# modstat | grep '^compat_linux'
compat_linux exec builtin - 1 - compat,compat_ossaudio,sysv_ipc,exec_elf64
compat_linux32 exec builtin - 0 - compat_linux,exec_elf32,compat_netbsd32,compat_netbsd32_sy\
svipc
Linuxバイナリが参照する共有ライブラリの設定・インストール
NetBSDやLinuxの実行バイナリフォーマットに用いられているELF(Executable and Linkable Format)では、libc等のライブラリを共有ライブラリの形で別ファイルに分けておくことができます。そのためNetBSDでLinuxバイナリを実行する際には、Linuxの共有ライブラリをあらかじめインストールしておく必要があります。
NetBSDでのLinuxバイナリエミュレーションでは、SUSEのインストールCDで提供されている suse100_base
というパッケージが必要なります。が、便利なことにこれらのパッケージは以下のNetBSDパッケージによりインストールと必要なディレクトリ構成の構築まで行ってくれます。
# pkg_add -v suse_base suse_compat suse_x11
Linuxバイナリの実行時には /etc
や /proc
以下のファイルが参照されますが、このままだとNetBSDのファイルを参照されてしまいます。そのため、 /emul
というディレクトリを別途用意し、その中にLinuxバイナリ向けの /etc
や /proc
を用意し、そちらを参照させる形になります。これらの設定は、前述のパッケージがインストールされる際に行われます。
とりあえず、これでLinuxバイナリエミュレーションのための準備が整いました。
Linux版 Acrobat Readerをインストールする
pkgsrcを用意する
NetBSDにはpackageとpkgsrcという機構があり、packageはバイナリのパッケージインストール(RedHat/CentOSでいうところのrpm/yum)とパッケージをソースからビルド・インストールするpkgsrcがあります。
今回はpkgsrcを使用してAcrobat Readerをインストールします。まずはpkgsrcの入手と展開です。
以下のコマンドで /usr/src/pkgsrc
ディレクトリ以下にpkgsrcのファイルが展開されます。
# curl -O http://ftp.jaist.ac.jp/pub/NetBSD/packages/current-src/pkgsrc.tgz
# tar zxvf pkgsrc.tgz -C /usr
後は単に make
コマンドを実行するだけです。
ただし、Acrobat Readerの場合は /etc/mk.conf
にライセンスを受け入れる旨の記述を追記しておく必要があります。
# echo 'ACCEPTABLE_LICENSES+=adobe-acrobat-license' >> /etc/mk.conf
# unset PKG_PATH
#
# cd /usr/pkgsrc/print/acroread7/
# make
# make install
これでLinux版 Acrobat Readerがインストールできました。
自分でコンパイルしたLinuxバイナリも動かしてみる
もう一つ試しに、自分でコンパイルしたLinuxバイナリも動かしてみましょう。
Linux環境(Ubuntu 14.04.2 LTS(32bit))でコンパイルしたHello,WorldプログラムをNetBSD上で動かしてみます。
# # NetBSDはamd64な環境
# uname -a
NetBSD nbsd8tmp 8.0 NetBSD 8.0 (GENERIC) #0: Tue Jul 17 14:59:51 UTC 2018 mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/amd64/compile/GENERIC amd64
#
# # 実行するLinuxバイナリは、32bitバイナリ
# file hello
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=8aa0745ddc1c3160fee8da580b84ecda40910eb9, with debug_info, not stripped
#
# # NetBSDのバイナリは以下のような表示になる
# file /bin/echo
/bin/echo: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /libexec/ld.elf_so, for NetBSD 8.0, not stripped
普通にLinuxバイナリが実行できます。
# ./hello
hello,world.
ちなみに、Linuxエミュレーション環境を設定していないNetBSDでは、以下のような結果になります。
$ file hello
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=8aa0745ddc1c3160fee8da580b84ecda40910eb9, with debug_info, not stripped
$ ./hello
-bash: ./hello: No such file or directory
さらに ktrace
( strace
のBSD版的なコマンド)で呼ばれているシステムコールをトレースしてみましょう。
# ktrace ./hello
hello,world.
# kdump ktrace.out
以下のような出力になっています。カーネル的には netbsd32_read()
等のシステムコールに対応するカーネル関数に置き換えられて実行されているようです。この動作を見ると、エミュレーションというよりかは、互換レイヤーというイメージの方が実態に近いですね。
3602 1 ktrace EMUL "netbsd"
3602 1 ktrace CALL execve(0x7f7fff87bf07,0x7f7fff87b960,0x7f7fff87b970)
3602 1 ktrace NAMI "/root/./hello"
3602 1 ktrace NAMI "/emul/linux32"
3602 1 ktrace NAMI "/emul/linux32/lib/ld-linux.so.2"
3602 3602 hello EMUL "linux32"
3602 3602 hello RET execve -1 errno -2 No such file or directory
3602 3602 hello CALL brk(0)
3602 3602 hello RET brk 134524928/0x804b000
3602 3602 hello CALL uname(0xbffff9a2)
3602 3602 hello RET uname 0
3602 3602 hello CALL netbsd32_access(0xfbef6c3f,4)
3602 3602 hello NAMI "/emul/linux32/etc/ld.so.preload"
3602 3602 hello NAMI "/etc/ld.so.preload"
3602 3602 hello RET netbsd32_access -1 errno -2 No such file or directory
3602 3602 hello CALL open(0xfbef7737,0x80000,0)
3602 3602 hello NAMI "/emul/linux32/etc/ld.so.cache"
3602 3602 hello RET open 3
3602 3602 hello CALL fstat64(3,0xbffff608)
3602 3602 hello RET fstat64 0
3602 3602 hello CALL mmap2(0,0x45ba,1,2,3,0)
3602 3602 hello RET mmap2 -68325376/0xfbed7000
3602 3602 hello CALL netbsd32_close(3)
3602 3602 hello RET netbsd32_close 0
3602 3602 hello CALL open(0xfbeda153,0x80000,0xfbed7aa8)
3602 3602 hello NAMI "/emul/linux32/lib/libc.so.6"
3602 3602 hello RET open 3
3602 3602 hello CALL netbsd32_read(3,0xbffff738,0x200)
3602 3602 hello GIO fd 3 read 512 bytes
"\^?ELF\^A\^A\^A\0\0\0\0\0\0\0\0\0\^C\0\^C\0\^A\0\0\0@\M^X\^A\0004\0\0\
\0hK\^W\0\0\0\0\0004\0 \0\n\0(\0D\0A\0\^F\0\0\0004\0\0\0004\0\0\0004\0\
\0\0@\^A\0\0@\^A\0\0\^E\0\0\0\^D\0\0\0\^C\0\0\0\240\M^V\^S\0\240\M^V\
\^S\0\240\M^V\^S\0\^S\0\0\0\^S\0\0\0\^D\0\0\0\^A\0\0\0\^A\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\M-x\b\^W\0\M-x\b\^W\0\^E\0\0\0\0\^P\0\0\^A\0\0\0\
\M-D\^Q\^W\0\M-D\^Q\^W\0\M-D\^Q\^W\0\M-X,\0\0\M^XX\0\0\^F\0\0\0\0\^P\0\
\0\^B\0\0\0\M^H-\^W\0\M^H-\^W\0\M^H-\^W\0\M-x\0\0\0\M-x\0\0\0\^F\0\0\0\
\^D\0\0\0\^D\0\0\0t\^A\0\0t\^A\0\0t\^A\0\0D\0\0\0D\0\0\0\^D\0\0\0\^D\0\
\0\0\a\0\0\0\M-D\^Q\^W\0\M-D\^Q\^W\0\M-D\^Q\^W\0\b\0\0\0L\0\0\0\^D\0\0\
\0\^D\0\0\0P\M-etd\M-4\M^V\^S\0\M-4\M^V\^S\0\M-4\M^V\^S\0\^\q\0\0\^\q\
\0\0\^D\0\0\0\^D\0\0\0Q\M-etd\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\^F\0\0\0\^D\0\0\0R\M-etd\M-D\^Q\^W\0\M-D\^Q\^W\0\M-D\^Q\^W\0<\^^\0\0<\
\^^\0\0\^D\0\0\0\^A\0\0\0\^D\0\0\0\^T\0\0\0\^C\0\0\0GNU\0K\M-s\M-'\M-P\
\M-S\M-]\M-y\M-0\^Bm\M-01\M-(\rj4\M-0\M-@\M-X[\^D\0\0\0\^P\0\0\0\^A\0\
\0\0GNU\0\0\0\0\0\^B\0\0\0\^F\0\0\0\^P\0\0\0\M-s\^C\0\0\n\0\0\0\0\^B\0\
\0\^N\0\0\0\2400\^PD\M^@ \^B\^A\M^L\^C\M-f\M^PAE\M^H\0\M^D\0\b\0E\M^@\
\0`\M-@\M^@\0\f\M^J\f\0\^A0\0\b@2\b\M-.\^D\M^HH6l\240\^V0\0&\M^D\M^@\
\M^N\^D\bB$"
3602 3602 hello RET netbsd32_read 512/0x200
3602 3602 hello CALL fstat64(3,0xbffff668)
3602 3602 hello RET fstat64 0
3602 3602 hello CALL mmap2(0,0x1000,3,0x22,0xffffffff,0)
3602 3602 hello RET mmap2 -68329472/0xfbed6000
3602 3602 hello CALL mmap2(0,0x176a5c,5,0x802,3,0)
3602 3602 hello RET mmap2 -69865472/0xfbd5f000
3602 3602 hello CALL mmap2(0xfbed0000,0x3000,3,0x812,3,0x171)
3602 3602 hello RET mmap2 -68354048/0xfbed0000
3602 3602 hello CALL mmap2(0xfbed3000,0x2a5c,3,0x32,0xffffffff,0)
3602 3602 hello RET mmap2 -68341760/0xfbed3000
3602 3602 hello CALL netbsd32_close(3)
3602 3602 hello RET netbsd32_close 0
3602 3602 hello CALL mmap2(0,0x1000,3,0x22,0xffffffff,0)
3602 3602 hello RET mmap2 -69869568/0xfbd5e000
3602 3602 hello CALL set_thread_area(0xbffffb28)
3602 3602 hello RET set_thread_area 0
3602 3602 hello CALL mprotect(0xfbed0000,0x2000,1)
3602 3602 hello RET mprotect 0
3602 3602 hello CALL mprotect(0x8049000,0x1000,1)
3602 3602 hello RET mprotect 0
3602 3602 hello CALL mprotect(0xfbefd000,0x1000,1)
3602 3602 hello RET mprotect 0
3602 3602 hello CALL netbsd32_munmap(0xfbed7000,0x45ba)
3602 3602 hello RET netbsd32_munmap 0
3602 3602 hello CALL fstat64(1,0xbffffb20)
3602 3602 hello RET fstat64 0
3602 3602 hello CALL ioctl(1,_IO('T',0x1,0),0xbffffa68)
3602 3602 hello RET ioctl 0
3602 3602 hello CALL mmap2(0,0x1000,3,0x22,0xffffffff,0)
3602 3602 hello RET mmap2 -68308992/0xfbedb000
3602 3602 hello CALL netbsd32_write(1,0xfbedb000,0xd)
3602 3602 hello GIO fd 1 wrote 13 bytes
"hello,world.\n"
3602 3602 hello RET netbsd32_write 13/0xd
3602 3602 hello CALL exit_group(0)
まとめ
NetBSDでLinuxバイナリエミュレーション機能を使用し、Linuxバイナリを動かすための手順を紹介しました。
エミュレーションといいつつも、実態はLinuxのシステムコールをNetBSDのものに置き換えて呼び出しており、互換レイヤーというイメージの方が近いようです。