1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

NetBSDでLinuxバイナリエミュレーションしてみる

Posted at

遅ればせながら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がインストールできました。

sample6.gif

自分でコンパイルした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のものに置き換えて呼び出しており、互換レイヤーというイメージの方が近いようです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?