OSvへmrubyを移植してみたらとてもあっさりと動いた話

注:この記事はカーネル/VM+K*BUG勉強会@関西 ごかいめのダイジェスト版です。
詳しくは、以下の動画とスライドをご覧下さい。

OSvは、OpenJDKを仮想環境で高速に動かす事を目指していて、ユーザは基本的にJavaアプリケーションしか実行しないという前提になっています。
メモリプロテクションやリングプロテクションなどは使われず、JavaVMがユーザアプリケーションからの不正アクセスを保護します。

イメージ図:

RubyプログラムやPythonプログラムはJRubyJythonによって実行する事が可能ですが、CRubyやCPythonは含まれていません。

しかし、ユーザアプリケーションを実行する言語環境は別にJavaVMじゃなくてもいいのではないか?という考えも成り立つと思われます。
例えばCRubyを動かしてみるとか。

…という訳で、やってみようと思ったんですが、いきなりCRubyやるのは大変そうだなぁという事でmrubyを移植してみました。

diff --git a/.gitmodules b/.gitmodules
index c7b42e5..62a2a0d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,3 +22,6 @@
 [submodule "mgmt"]
 	path = mgmt
 	url = ../../cloudius-systems/mgmt.git
+[submodule "external/mruby"]
+	path = external/mruby
+	url = https://github.com/mruby/mruby
diff --git a/bootfs.manifest b/bootfs.manifest
index a9e4fe5..498879a 100644
--- a/bootfs.manifest
+++ b/bootfs.manifest
@@ -74,4 +74,7 @@
 /libzfs.so: libzfs.so
 /zfs.so: zfs.so
 /tools/mkfs.so: tools/mkfs/mkfs.so
+/mirb.so: mirb.so
+/mruby.so: mruby.so
 
diff --git a/build.mk b/build.mk
index 11304f2..96c80de 100644
--- a/build.mk
+++ b/build.mk
@@ -526,6 +526,7 @@ objects += core/run.o
 
 include $(src)/fs/build.mk
 include $(src)/libc/build.mk
+include $(src)/mruby/build.mk
 
 objects += $(addprefix fs/, $(fs))
 objects += $(addprefix libc/, $(libc))
@@ -586,7 +587,7 @@ $(jni): INCLUDES += -I /usr/lib/jvm/java/include -I /usr/lib/jvm/java/include/li
 
 bootfs.bin: scripts/mkbootfs.py bootfs.manifest $(tests) $(tools) \
 		tests/testrunner.so java/java.so java/runjava.jar \
-		zpool.so zfs.so
+		zpool.so zfs.so mruby.so mirb.so
 	$(call quiet, $(src)/scripts/mkbootfs.py -o $@ -d $@.d -m $(src)/bootfs.manifest \
 		-D jdkbase=$(jdkbase) -D gccbase=$(gccbase) -D \
 		glibcbase=$(glibcbase) -D miscbase=$(miscbase), MKBOOTFS $@)
diff --git a/external/mruby b/external/mruby
new file mode 160000
index 0000000..9b2f4c4
--- /dev/null
+++ b/external/mruby
@@ -0,0 +1 @@
+Subproject commit 9b2f4c4423ed11f12d6393ae1f0dd4fe3e51ffa0-dirty
diff --git a/mruby/build.mk b/mruby/build.mk
new file mode 100644
index 0000000..3a0661f
--- /dev/null
+++ b/mruby/build.mk
@@ -0,0 +1,40 @@
+mruby-objects = mruby/mruby.o mruby/libmruby.a
+mirb-objects = mruby/mirb.o mruby/libmruby.a
+
+define mruby-includes
+  external/mruby/src
+  external/mruby/include
+endef
+
+cflags-mruby-include = $(foreach path, $(strip $(mruby-includes)), -isystem $(src)/$(path))
+
+$(mruby-objects): local-includes += $(cflags-mruby-include)
+$(mruby-objects): post-includes-bsd =
+$(mruby-objects): kernel-defines =
+$(mruby-objects): CFLAGS += -Wno-unknown-pragmas
+
+$(mirb-objects): local-includes += $(cflags-mruby-include)
+$(mirb-objects): post-includes-bsd =
+$(mirb-objects): kernel-defines =
+$(mirb-objects): CFLAGS += -Wno-unknown-pragmas
+
+mruby/libmruby.a:
+	cp ../../mruby/build_config.rb ../../external/mruby
+	make -C ../../external/mruby clean
+	make -C ../../external/mruby
+	mkdir -p mruby/
+	cp ../../external/mruby/build/host/lib/libmruby.a mruby/
+
+mruby/mruby.o:
+	$(CC) $(CFLAGS) -c -o $@ ../../external/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
+
+mruby/mirb.o:
+	$(CC) $(CFLAGS) -c -o $@ ../../external/mruby/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
+
+mruby.so: $(mruby-objects)
+	$(makedir)
+	$(q-build-so)
+
+mirb.so: $(mirb-objects)
+	$(makedir)
+	$(q-build-so)
diff --git a/mruby/build_config.rb b/mruby/build_config.rb
new file mode 100644
index 0000000..01032e1
--- /dev/null
+++ b/mruby/build_config.rb
@@ -0,0 +1,22 @@
+MRuby::Build.new do |conf|
+  # load specific toolchain settings
+  toolchain :gcc
+
+  # include the default GEMs
+  conf.gembox 'default'
+
+  # C compiler settings
+  conf.cc do |cc|
+      cc.flags = "-fPIC -std=gnu99 -MD -MP -g -Wall -Wno-pointer-arith -Wformat=0 -D __BSD_VISIBLE=1 -U _FORTIFY_SOURCE -fno-stack-protector  -I. -I../../arch/x64 -I../.. -I../../external/libunwind/include -I../../include -isystem ../../include/glibc-compat -isystem ../../external/gcc.bin/usr/include/c++/4.8.1 -isystem ../../external/gcc.bin/usr/include/c++/4.8.1/x86_64-redhat-linux -isystem ../../external/acpica/source/include -isystem ../../external/misc.bin/usr/include -isystem ../../include/api -isystem ../../include/api/x86_64 -isystem ../../external/gcc.bin/usr/lib/gcc/x86_64-redhat-linux/4.8.1/include -isystem ../../build/release/gen/include -isystem ../../bsd/sys -isystem ../../bsd/ -isystem ../../bsd/sys -isystem ../../bsd/  -D_KERNEL -fno-omit-frame-pointer -DHAVE_ATTR_COLD_LABEL -include ../../compiler/include/intrinsics.hh  -isystem ../../external/openjdk.bin/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.25-2.3.12.3.fc19.x86_64/include  -isystem ../../external/openjdk.bin/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.25-2.3.12.3.fc19.x86_64/include/linux -msse4.1 -O2 -DACPI_MACHINE_WIDTH=64 -DACPI_USE_LOCAL_CACHE   -DCONF_preempt=1  -DCONF_debug_memory=0  -DCONF_logger_debug=0 -nostdinc -D__OSV__ -D__XEN_INTERFACE_VERSION__='0x00030207' -DXEN -DXENHVM -DSMP -D'__FBSDID(__str__)=extern int __bogus__' -D__x86_64__ -I ../../libc/internal -I  ../../libc/arch/x64 -Wno-missing-braces -Wno-parentheses -Wno-unused-but-set-variable"
+    if ENV['BUILD_BIT'] == "64"
+      cc.flags << ' -DMRB_INT64'
+    end
+  end
+
+  # Linker settings
+  conf.linker do |linker|
+    if ENV['BUILD_BIT'] == "64"
+      linker.flags = '-DMRB_INT64'
+    end
+  end
+end

…なんとコードを一行も弄らずに一発でビルドが通ってまともに走ってしまった。
とは言っても、このままだと組み込まれているgemsが少ないのでAPIが足らず、ネットワークに繋ぐなど役に立ちそうな作業は何一つ出来ないのだが。
それにしてもmrubyは移植性が高いなぁ、という話でした(OSvの話じゃなかった)。

試したい方はこちらからどうぞ