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

OSvへmrubyを移植してみたらとてもあっさりと動いた話 - かーねる・う゛いえむにっきの続きです。

さて、OSvでmrubyが動くようになったのですがmrbgemsを追加していないのでこのままではAPIがむっちゃ少なくてなにも書けません。
でもmrbgemsの移植ってそれぞれ依存ライブラリあったりして動かすの面倒よなー、とか思ってたら、こんなのが目にとまりました。

GitHub - mobiruby/mruby-cfunc: Interface to C functions on mruby

       |
   \  __  /
   _ (m) _ピコーン
      |ミ|
    /  `´  \
     ('A`)
     ノヽノヽ
       くく

OSvってプロセスがカーネル権限で走っていて何でも出来るしカーネルのC関数直接叩けるので、これ入れると何でもありになるんじゃね…?
危険だ、ヤバい。

じゃあ移植してみよう(いいのか)。
てな訳で、3分mrubyクッキング〜。

手始めに、build_config.rbにmruby-cfuncをくわえます。

   conf.gem :github => 'mobiruby/mruby-cfunc', :branch => 'master'

これでビルドを試してみると、当然ffi.hがねーよって怒られます。
本来ならばlibffiもmrubyのようにOSvへ移植すべきでしょうが、幸いなことにOSvはある程度Linuxとの互換性があります。
…ので、今回は、libffiのクロスコンパイルを避けてLinuxバイナリとのリンクを目指します。
ヘッダはLinuxからコピーしちゃいます。Ubuntuだとこの辺ですね:

$ dpkg -L libffi-dev|grep ffi.h
/usr/include/x86_64-linux-gnu/ffi.h

これをosv/mruby/includeを作ってそこに入れました。
そして、コンパイル時に見に行くようにcc.flagsへ”-I../../mruby/include”を追加します。

そうするとビルドは通りますが、libffiをリンクしていないためこのままでは動きません。
ダイナミックリンクライブラリのロード面倒だな、スタティックに行けないかなって思ってたら、こんなのを見つけました。

$ dpkg -L libffi-dev|grep libffi_pic.a
/usr/lib/x86_64-linux-gnu/libffi_pic.a

これだ!
というわけで、これをosv/mruby/へコピー。

mruby/build.mkにlibffi_pic.aを足します。

mruby-objects = mruby/mruby.o mruby/libmruby.a mruby/libffi_pic.a
mirb-objects = mruby/mirb.o mruby/libmruby.a mruby/libffi_pic.a

で、sudo make clean external allしてみるとビルド成功!
早速OSvでmirbを起動して試してみましょう。

syuu@hiratake:~/osv$ sudo ./scripts/run.py -n -v -e "/mirb.so"
SCRIPT, tap0
 
 
 
 
 
 
 
 
acpi 0 apic 0
acpi 1 apic 1
acpi 2 apic 2
acpi 3 apic 3
APIC base fee00000
OSv v0.02-33-gfb74f15 Copyright 2013 Cloudius Systems
locale works
 
 
 
 
 
 
 
 
All ACPI Tables successfully acquired
VFS: mounting ramfs at /
VFS: mounting devfs at /dev
ACPI: RSDP 0xfd8d0 00014 (v00 BOCHS )ACPI: RSDT 0x3fffe380 00034 (v01 BOCHS  BXP
CRSDT 00000001 BXPC 00000001)ACPI: FACP 0x3fffff80 00074 (v01 BOCHS  BXPCFACP 00
000001 BXPC 00000001)ACPI: DSDT 0x3fffe3c0 011A9 (v01   BXPC   BXDSDT 00000001 I
NTL 20100528)ACPI: FACS 0x3fffff40 00040ACPI: SSDT 0x3ffff6e0 00858 (v01 BOCHS  
BXPCSSDT 00000001 BXPC 00000001)ACPI: APIC 0x3ffff5b0 00090 (v01 BOCHS  BXPCAPIC
 00000001 BXPC 00000001)ACPI: HPET 0x3ffff570 00038 (v01 BOCHS  BXPCHPET 0000000
1 BXPC 00000001)ACPI: RSDP 0xfd8d0 00014 (v00 BOCHS )ACPI: RSDT 0x3fffe380 00034
 (v01 BOCHS  BXPCRSDT 00000001 BXPC 00000001)ACPI: FACP 0x3fffff80 00074 (v01 BO
CHS  BXPCFACP 00000001 BXPC 00000001)ACPI: DSDT 0x3fffe3c0 011A9 (v01   BXPC   B
XDSDT 00000001 INTL 20100528)ACPI: FACS 0x3fffff40 00040ACPI: SSDT 0x3ffff6e0 00
858 (v01 BOCHS  BXPCSSDT 00000001 BXPC 00000001)ACPI: APIC 0x3ffff5b0 00090 (v01
 BOCHS  BXPCAPIC 00000001 BXPC 00000001)ACPI: HPET 0x3ffff570 00038 (v01 BOCHS  
BXPCHPET 00000001 BXPC 00000001)ACPI: RAM disk at 0x0xffffc0003e26b000 (4096K by
tes)
Initializing network stack...
Done!
eth0: ethernet address: 52:54:0:12:34:56
VFS: mounting zfs at /usr
zfs: mounting osv/usr from device /dev/vblk0.1
[I/38 dhcp]: Waiting for IP...
[I/329 dhcp]: Configuring eth0: ip 192.168.122.89 subnet mask 255.255.255.0 gate
way 192.168.122.1
[I/329 dhcp]: Server acknowledged IP for interface eth0
mirb - Embeddable Interactive Ruby Shell
 
This is a very early version, please test and report errors.
Thanks :)
 
ptr = CFunc::Pointer.malloc(12)     
>  => <CFunc::Void pointer=0xffffc0003bea8e50>
result = CFunc::call(CFunc::Pointer, "strcpy", ptr, "Hello")
stub dlclose()
>  => <CFunc::Void pointer=0xffffc0003bea8e50>
p result.to_s
> "Hello"
 => "Hello"
>  => #<CFunc::UInt64:0xffffc0003bbf25a0>
CFunc::call(CFunc::UInt64, "time", nil) 
stub dlclose()
>  => #<CFunc::UInt64:0xffffc0003bbf22d0>
CFunc::call(CFunc::UInt64, "time", nil) 
stub dlclose()
>  => #<CFunc::UInt64:0xffffc0003bbdcfa0>
CFunc::call(CFunc::UInt64, "time", nil) 
stub dlclose()
>  => #<CFunc::UInt64:0xffffc0003bbdce50>
CFunc::call(CFunc::UInt64, "time", nil) 
stub dlclose()
>  => #<CFunc::UInt64:0xffffc0003bbdcd00>
CFunc::call(CFunc::UInt64, "time", nil) 
stub dlclose()
>  => #<CFunc::UInt64:0xffffc0003bbdcbb0>
CFunc::call(CFunc::UInt64, "time", nil) 
stub dlclose()
>  => #<CFunc::UInt64:0xffffc0003bbdca60>

構造体とかを使っていると指定が面倒くさいので、取り敢えずstrcpyとtimeを試してみましたが、どうやら動いてそうですね!
てなわけで、OSvはLinuxと互換性があってライブラリの移植が簡単だなぁ、というお話でした(今回はOSv関係あった!)