Debian上にOSvビルド環境を構築してみた

Debian上にOSvビルド環境を構築してみた

OSvもくもく会#1 〜OSvで遊んでみよう〜 の予習を兼ねて、Debian上にOSvビルド環境を構築してみました。

OSv

OSvはマイナビニュースで説明されているように、「クラウドオペレーティングシステム」という位置づけになっています。https://github.com/cloudius-systems/osvの説明では、Debian,Fedoraでのビルド環境構築が可能なようであり、今回はそれを参照しながら作業してみました。

ビルド環境構築の流れ

以下の順番でビルド環境の構築を進めます。

Debian(amd64)のインストール

gcc-4.9.0のビルド・インストール

OSvのビルドにはgcc-4.8.0以上が必要ですが、Debianのパッケージはgcc-4.7が最新版であり、必要なgccバージョンを満たしていません。このため、gcc-4.9.0(2014/04/22にリリース)を自分でビルドします。

自分でビルドするものについては既存の環境と混ぜこぜにしたくないので、/opt以下にインストールする方針で作業を進めます。

Prerequisites for GCCにはgccのビルドに必要なツール、ライブラリが記載されています。gccのビルドの前に以下のライブラリをビルドします。バージョンの指定がある点に注意が必要です。

  • GNU Multiple Precision Library (GMP) version 4.3.2 (or later)
  • MPFR Library version 2.4.2 (or later)
  • MPC Library version 0.8.1 (or later)
  • ISL Library version 0.12.2
  • CLooG 0.18.1

gccビルドに関連するパッケージのインストール

まずはgccのビルド作業に必要となるツールをパッケージからインストールします。

$ sudo sudo apt-get install lzip     # .lz形式のgmp配布物の展開に必要
$ sudo sudo apt-get install autogen  # autogenが無いとgccのビルドでエラーになる

ソースファイルの準備(ダウンロード)

インストール前に決めておくこと

  • インストール先は/opt/gcc-4.9.0とする。

必要なライブラリのビルドとインストール

必要なソースアーカイブはあらかじめダウンロードしておきます。

$ ls Download/
cloog-0.18.1.tar.gz  gmp-6.0.0a.tar.lz   mpc-1.0.2.tar.gz
gcc-4.9.0.tar.gz     isl-0.12.2.tar.bz2  mpfr-3.1.2.tar.gz

ライブラリパスの設定

インストール先を/opt/gcc-4.9.0に想定しているので、作業前にライブラリパスを設定してしまいます。

後から設定しようとすると忘れてしまいがちで、make checkでテストがFAILしてからライブラリパスが未設定だったことに気づいたりします...。

$ export LD_LIBRARY_PATH=/opt/gcc-4.9.0/lib:$LD_LIBRARY_PATH
$ export LD_RUN_PATH=/opt/gcc-4.9.0/lib:$LD_RUN_PATH

gmpのビルド

gmpのみソースアーカイブがlzip形式のみでの提供になっています。

$ lzip -dc Download/gmp-6.0.0a.tar.lz | tar xvf -
$ cd gmp-6.0.0/
$ mkdir build ; cd build
$ ../configure --enable-cxx --prefix=/opt/gcc-4.9.0 2>&1 | tee -a _configure.log
$ (date ; make ; date) 2>&1 | tee -a _make.log
$ (date ; make check ; date) 2>&1 | tee -a _make_check.log
$ sudo make install 2>&1 | tee -a _make_install.log

mpfrのビルド

$ tar zxvf Download/mpfr-3.1.2.tar.gz
$ cd mpfr-3.1.2/
$ mkdir build ; cd build
$ ../configure --prefix=/opt/gcc-4.9.0 \
               --with-gmp-include=/opt/gcc-4.9.0/include \
               --with-gmp-lib=/opt/gcc-4.9.0/lib \
               2>&1 | tee -a _configure.log
$ (date ; make ; date) 2>&1 | tee -a _make.log
$ (date ; make check ; date) 2>&1 | tee -a _make_check.log
$ sudo make install 2>&1 | tee -a _make_install.log

mpcのビルド

$ tar zxvf Download/mpc-1.0.2.tar.gz
$ cd mpc-1.0.2/
$ mkdir build ; cd build
$ ../configure --prefix=/opt/gcc-4.9.0 \
               --with-gmp-include=/opt/gcc-4.9.0/include \
               --with-gmp-lib=/opt/gcc-4.9.0/lib \
               --with-mpfr-include=/opt/gcc-4.9.0/include \
               --with-mpfr-lib=/opt/gcc-4.9.0/lib \
               2>&1 | tee -a _configure.log
$ (date ; make ; date) 2>&1 | tee -a _make.log
$ (date ; make check ; date) 2>&1 | tee -a _make_check.log
$ sudo make install 2>&1 | tee -a _make_install.log

islのビルド

$ tar jxvf Download/isl-0.12.2.tar.bz2
$ cd isl-0.12.2
$ mkdir build ; cd build
$ ../configure --prefix=/opt/gcc-4.9.0 \
               --with-gmp-builddir=/opt/gcc-4.9.0/lib \
               CFLAGS="-I/opt/gcc-4.9.0/include" \
               LDLAGS="-I/opt/gcc-4.9.0/lib" \
               2>&1 | tee -a _configure.log
$ (date ; make ; date) 2>&1 | tee -a _make.log
$ (date ; make check ; date) 2>&1 | tee -a _make_check.log
$ sudo make install 2>&1 | tee -a _make_install.log

cloogのビルド

$ tar zxvf Download/cloog-0.18.1.tar.gz
$ cd cloog-0.18.1
$ mkdir build ; cd build
$ ../configure --prefix=/opt/gcc-4.9.0 \
               --with-isl=system \
               --with-isl-prefix=/opt/gcc-4.9.0 \
               --with-gmp=system \
               --with-gmp-prefix=/opt/gcc-4.9.0 \
               CFLAGS="-I/opt/gcc-4.9.0/include" \
               LDLAGS="-I/opt/gcc-4.9.0/lib" \
               2>&1 | tee -a _configure.log
$ (date ; make ; date) 2>&1 | tee -a _make.log
$ (date ; make check ; date) 2>&1 | tee -a _make_check.log
$ sudo make install 2>&1 | tee -a _make_install.log

gcc-4.9.0のビルド・インストール

今回欲しいのはC++コンパイラなので、--enable-languages=c,c++を指定して必要なコンパイラのみビルドします(C言語も不要かもしれません)。

$ tar zxvf Download/gcc-4.9.0.tar.gz
$ cd gcc-4.9.0
$ mkdir build ; cd build
$ ../configure --prefix=/opt/gcc-4.9.0 \
               --disable-multilib \
               --program-suffix=-4.9.0 \
               --enable-stage1-languages=c,c++ \
               --enable-languages=c,c++ \
               --disable-isl-version-check \
               --with-mpc-include=/opt/gcc-4.9.0/include \
               --with-mpc-lib=/opt/gcc-4.9.0/lib \
               --with-mpfr-include=/opt/gcc-4.9.0/include \
               --with-mpfr-lib=/opt/gcc-4.9.0/lib \
               --with-isl-include=/opt/gcc-4.9.0/include \
               --with-isl-lib=/opt/gcc-4.9.0/include \
               --with-gmp-include=/opt/gcc-4.9.0/include \
               --with-gmp-lib=/opt/gcc-4.9.0/lib \
               CFLAGS="-I$/opt/gcc-4.9.0/include" \
               LDLAGS="-L/opt/gcc-4.9.0/lib -L/usr/lib" \
               2>&1 | tee -a _configure.log
$ (date ; make ; date) 2>&1 | tee -a _make.log
$ (date ; make check ; date) 2>&1 | tee -a _make_check.log
$ sudo make install 2>&1 | tee -a _make_install.log

簡単な動作確認

$ export LD_LIBRARY_PATH=/opt/gcc-4.9.0/lib:$LD_LIBRARY_PATH
$ export LD_RUN_PATH=/opt/gcc-4.9.0/lib:$LD_RUN_PATH
$ export PATH=/opt/gcc-4.9.0/bin:$PATH
$ which gcc-4.9.0
/opt/gcc-4.9.0/bin/gcc-4.9.0
$ which g++-4.9.0
/opt/gcc-4.9.0/bin/g++-4.9.0
$ gcc-4.9.0 --version
gcc-4.9.0 (GCC) 4.9.0
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++-4.9.0 --version
g++-4.9.0 (GCC) 4.9.0
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

qemu-2.0.0のビルド・インストール

OSvのビルド時にqemuの機能を利用しますが、Debianqemuパッケージだとvirtio-rng-pciなデバイスが利用できないため、ビルドエラーとなります。

https://github.com/cloudius-systems/osv/issues/127を見ると、./configure --enable-virtfsしたqemuを使う方法が紹介されており、これに倣いqemuを自分でビルドします。最初Debianqemuパッケージと同じバージョンのqemu(1.1.2)をビルドしましたが、そのバージョンではvirtio-rng-pciが提供されていないため、最新版のqemuを使います。

ビルドに必要なパッケージのインストール

$ sudo apt-get install pkg-config
$ sudo apt-get install libglib2.0-dev  # Development files for the GLib library
$ sudo apt-get install libcap-dev
$ sudo apt-get install libattr1-dev

libcap-dev,libattr1-devパッケージが無いと、configure --enable-virtfs時に以下のエラーとなります。

VirtFS is supported only on Linux and requires libcap-devel and libattr-devel
$ tar jxvf ./Download/qemu-2.0.0.tar.bz2
$ cd qemu-2.0.0
$ ./configure --prefix=/opt/qemu-2.0.0 \
              --target-list=x86_64-softmmu,x86_64-linux-user \
              --enable-virtfs \
              2>&1 | tee -a _configure.log
$ (date ; make ; date) 2>&1 | tee -a _make.log
$ sudo make install 2>&1 | tee -a _make_install.log

インストール後、virtio-rng-pciが使用可能デバイスに含まれていることを確認します。

$ qemu-system-x86_64 -device ? 2>&1 | grep virtio-rng-pci
name "virtio-rng-pci", bus PCI

OSvのビルド

環境変数の設定

/opt以下にインストールしたgcc-4.9.0,qemu-2.0.0と必要なライブラリを参照するよう、.bashrc等に以下を追加します。

LD_LIBRARY_PATH=/opt/gcc-4.9.0/lib64:$LD_LIBRARY_PATH
LD_LIBRARY_PATH=/opt/gcc-4.9.0/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH

LD_RUN_PATH=/opt/gcc-4.9.0/lib64:$LD_RUN_PATH
LD_RUN_PATH=/opt/gcc-4.9.0/lib:$LD_RUN_PATH
export LD_RUN_PATH

PATH=/opt/gcc-4.9.0/bin$PATH
PATH=/opt/qemu-2.0.0/bin:$PATH
export PATH=/opt/gcc-4.9.0/bin:/opt/qemu-2.0.0/bin:$PATH

export CXX=/opt/gcc-4.9.0/bin/g++-4.9.0

GitHubからOSvソースコードをclone

OSvのGitHubの手順にしたがいソースコードをcloneします。clone後、"git submodule update"が必要です。

$ git clone https://github.com/cloudius-systems/osv.git
$ git submodule update --init --recursive

OSvのビルド

$ cd osv
$ (date ; make ; date) 2>&1 | tee -a _make.log

OSv上でHello,Worldを動かす

OSvを実行する前に、kvm.koをロードします(デフォルトではkvm.koがロードされていない)。

$ sudo /sbin/modprobe kvm
$ lsmod | grep kvm
kvm                   287749  0

Hello,world.を動かしてみます。

$ cat apps/java-example/Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
$ make image=java-example
$ scripts/run.py -e "java.so -cp /java-example Hello"
OSv v0.08-60-g8dd67e0
eth0: 192.168.122.15
Hello, World!

ハマりどころ

何もかもが上手く進んだ場合、前述までの手順でOSvのビルドまで行えます、が、OSvのビルドではハマり所がたくさんあります(ありました...)。

何点かハマった点について、エラー内容と解消方法を以下に記載します。

Makeファイルのjar-jarsターゲットの実行でエラーが出る

jar-jarsターゲットの実行で以下のようなエラーが出る場合があります。

$ make
...中略...
  MVN java-jars
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project cloudius: Fatal error compiling: invalid target release: 1.7 -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
make[1]: *** [java-jars] Error 1
make[1]: Leaving directory `/home/fpig/work/osv_buld/osv/build/release'
make: *** [all] Error 2
$ make
...中略...
  MVN java-jars
[ERROR] BUILD ERROR
make[1]: *** [java-jars] Error 1
make[1]: Leaving directory `/home/fpig/work/osv_buld/osv/build/release'
make: *** [all] Error 2

私の環境では、Maven,JDKのバージョンを複数インストールしていたことが原因でした。

$ /usr/share/maven/bin/mvn --version
Apache Maven 3.0.4
Maven home: /usr/share/maven
Java version: 1.6.0_31, vendor: Sun Microsystems Inc.
Java home: /usr/lib/jvm/java-6-openjdk-amd64/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "3.2.0-4-amd64", arch: "amd64", family: "unix"

$ /usr/share/maven2/bin/mvn --version
Apache Maven 2.2.1 (rdebian-8)
Java version: 1.6.0_31
Java home: /usr/lib/jvm/java-6-openjdk-amd64/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux" version: "3.2.0-4-amd64" arch: "amd64" Family: "unix"

Maven 3.0.4が使われると思っていましたが、実際はMaven 2.2.1が使われていました。

$ mvn -version
Apache Maven 2.2.1 (rdebian-8)
Java version: 1.7.0_55
Java home: /usr/lib/jvm/java-7-openjdk-amd64/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux" version: "3.2.0-4-amd64" arch: "amd64" Family: "unix"

さっそく切り替えます。

$ sudo update-alternatives --config mvn
There are 2 choices for the alternative mvn (providing /usr/bin/mvn).

  Selection    Path                       Priority   Status
------------------------------------------------------------
* 0            /usr/share/maven2/bin/mvn   200       auto mode
  1            /usr/share/maven/bin/mvn    150       manual mode
  2            /usr/share/maven2/bin/mvn   200       manual mode

Press enter to keep the current choice[*], or type selection number: 1
update-alternatives: using /usr/share/maven/bin/mvn to provide /usr/bin/mvn (mvn) in manual mode

これでOK。

$ mvn -version
Apache Maven 3.0.4
Maven home: /usr/share/maven
Java version: 1.7.0_55, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-7-openjdk-amd64/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "3.2.0-4-amd64", arch: "amd64", family: "unix"

ちなみに、mvn -eで詳細なエラー表示が行えます。Mavenまわりで上記以外のエラーが出た際はbuild.mk内のMVNに-eを追加すると原因切り分けが楽になるかもしれません。

$ man mvn
...中略...
       -e,--errors
              Produce execution error messages

JDKについても、私の環境ではこちらも予想と異なるjdkが使われていました。
(あれこれ試行錯誤する中でmaven2やopenjdk-6-jdkとかインストールしたのがマズかったようです...)

$ javac -version
javac 1.6.0_31
$ sudo update-alternatives --config javac
There are 2 choices for the alternative javac (providing /usr/bin/javac).

  Selection    Path                                         Priority   Status
------------------------------------------------------------
* 0            /usr/lib/jvm/java-6-openjdk-amd64/bin/javac   1061      auto mode
  1            /usr/lib/jvm/java-6-openjdk-amd64/bin/javac   1061      manual mode
  2            /usr/lib/jvm/java-7-openjdk-amd64/bin/javac   1051      manual mode

Press enter to keep the current choice[*], or type selection number: 2
update-alternatives: using /usr/lib/jvm/java-7-openjdk-amd64/bin/javac to provide /usr/bin/javac (javac) in manual mode

java,javac共にjava-7-openjdk-amd64を参照することを確認します。

$ java -version
java version "1.7.0_55"
OpenJDK Runtime Environment (IcedTea 2.4.7) (7u55-2.4.7-1~deb7u1)
OpenJDK 64-Bit Server VM (build 24.51-b03, mixed mode)
$ javac -version
javac 1.7.0_55

"undefined reference to `__cxa_throw_bad_array_new_length'"が発生する

これはエラーメッセージの通り、__cxa_throw_bad_array_new_length()が見つけられないことが原因です。

$ make
...中略...
  LD loader.elf
arch/x64/smp.o: In function `smp_init()':
/home/fpig/work/osv_buld/osv/arch/x64/smp.cc:78: undefined reference to `__cxa_throw_bad_array_new_length'
core/mempool.o: In function `memory::mark_smp_allocator_intialized::mark_smp_allocator_intialized()':
/home/fpig/work/osv_buld/osv/core/mempool.cc:386: undefined reference to `__cxa_throw_bad_array_new_length'
make[1]: *** [loader.elf] Error 1
...中略...

__cxa_throw_bad_array_new_length()は/opt/gcc-4.9.0/lib64/libstdc++.soにあります(前述のgccビルド手順を実施した場合)。

$ for i in `find /opt/gcc-4.9.0/ | grep \\.so$`;
  do
       nm $i | grep __cxa_throw_bad_array_new_length
       if [ $? -eq 0 ]; then
           echo $i
       fi
  done
000000000005d3a0 T __cxa_throw_bad_array_new_length
/opt/gcc-4.9.0/lib64/libstdc++.so

build.mkの中でlibstdc++.aの場所を指定しているので、/opt/gcc-4.9.0以下のライブラリを探索するようにbuild.mkを修正します。

$ diff -u build.mk.orig build.mk
--- build.mk.orig       2014-05-09 02:53:53.124712117 +0900
+++ build.mk    2014-05-09 08:37:33.570625829 +0900
@@ -784,6 +784,7 @@
 objects += $(addprefix fs/, $(fs))
 objects += $(addprefix libc/, $(libc))

+gccbase = /opt/gcc-4.9.0
 libstdc++.a = $(shell find $(gccbase)/ -name libstdc++.a)
 libsupc++.a = $(shell find $(gccbase)/ -name libsupc++.a)
 libgcc_s.a = $(shell find $(gccbase)/ -name libgcc.a |  grep -v /32/)

bare.imgの作成時に"qemu-system-x86_64: -device virtio-rng-pci: Parameter 'driver' expects device type"のエラーが出る

$ make
...中略...
Creating bare.img as qcow2


/home/fpig/work/osv_buld/osv/scripts/mkzfs.py -o bare.img -d bare.img.d -m /home/fpig/work/osv_buld/osv/build/release/bootfs.manifest
qemu-system-x86_64: -device virtio-rng-pci: Parameter 'driver' expects device type
QEMU 1.1.2 monitor - type 'help' for more information
(qemu) QEMU 1.1.2 monitor - type 'help' for more information
(qemu)
Traceback (most recent call last):
  File "/home/fpig/work/osv_buld/osv/scripts/mkzfs.py", line 44, in <module>
    upload_manifest.upload(osv, manifest, depends)
  File "/home/fpig/work/osv_buld/osv/scripts/upload_manifest.py", line 62, in upload
    s.connect(("127.0.0.1", 10000));
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 111] Connection refused
make[1]: *** [bare.img] Error 1
make[1]: *** Deleting file `bare.img'
make[1]: Leaving directory `/home/fpig/work/osv_buld/osv/build/release'
make: *** [all] Error 2

これはDebianqemuパッケージではvirtio-rng-pciが利用できないことが原因です。前述のqemuビルドの手順を実施します。

まとめ

Debian上にOSvのビルド環境を構築し、Hello,Worldまで動かしてみました。ビルド環境構築にはいくつかハマり所があり、以下の点に気をつける必要がありました。

  • Debianamd64版を使用する(i386だとダメ)
  • OSvで2GBのメモリを使用するので、ビルド環境のマシンはそれを見越したメモリ量にする
  • MavenJDKは複数バージョンの混在に注意する
    • 混在していた場合は、update-alternativesで適切なバージョンを利用するよう設定する
  • gccは4.8.0以上を使用する
  • gccビルドではconfigure --disable-multilib,--enable-languages=c,c++を忘れずに指定する

参照ドキュメント

gcc4.9のビルド手順を調べるにあたり、以下のWebサイト・blogの内容を参考にさせていただきました。

変更履歴

  • 2014/05/17:OSvソースコードのclone手順に抜けがあったため追記しました。