GaucheでSchemeスクリプトをコンパイルする

    • >

Gaucheをゲームに組み込んで使いたい。でもゲームを制御するschemeファイルがユーザーに丸見えなのは避けたい。
スクリプトを暗号化するか、バイナリに変換してschemeファイルを隠蔽する方法はないか。調べてみたらコンパイルしてバイナリに変換できそうなことがわかったのでメモしておく。

以下の作業はUbuntu8.04(hardy)、Gauche-0.8.14(ソースからmakeしたもの)で行った。

バイナリに変換といってもネイティブバイナリにするのではなく、共有ライブラリを作る。
早速テスト用モジュールを書く。こんな感じ。
test-comp.scm

(define-module test-comp
 (export test-func str-reverse)
)
(select-module test-comp)

(define (test-func num)
 (* num num))

(define (str-reverse str)
 (list->string (reverse (string->list str))))

(provide "test-comp")

これをコンパイルして、共有ライブラリを作る。まずSchemeファイルからCファイルを生成する。
Cファイルの生成にはgencompというツールを使う。このツールはGaucheをソースからコンパイルする時にも使われているようだ(Makefileを参照)。

gosh gencomp test-comp.scm

これでtest-comp.cが生成される。次にCコンパイラでもって共有ライブラリを作る。

gcc -fPIC `gauche-config -I` `gauche-config --so-cflags` \
test-comp.c `gauche-config -l` `gauche-config --so-ldflags test-comp.so`

`gauche-config --so-ldflags`に-oが含まれているのでtest-comp.soにつける必要はない。
これで共有ライブラリが生成された。次にGaucheを組み込んだアプリから、いま生成したライブラリを読み込むコードを書く。

test-use-c.c

#include <gauche.h>

extern ScmObj Scm_DynLoad(ScmString* str, ScmObj, int);

int main(int argc, char* argv[])
{
	GC_INIT();
	Scm_Init(GAUCHE_SIGNATURE);

	Scm_AddLoadPath(".", FALSE);
	Scm_Load("gauche-init.scm", 0);
	Scm_DynLoad(SCM_STRING(SCM_MAKE_STR_COPYING("test-comp")), SCM_FALSE, 0);
	Scm_Load("test-use.scm", 0);

	Scm_Exit(0);

	return 0;
}

Scm_DynLoadで共有ライブラリをロードする。わざわざSchemeの文字列を作っているのは、この間数がScheme側から呼び出されることを想定しているからだろう。ソースを見ると、

Gauche-0.8.14/src/extlib.stub (1131行目から1134行目)

(define-cproc dynamic-load (file::<string>
                            &keyword (init-function #f)
                                     (export-symbols #f))
  (expr (Scm_DynLoad file init_function (not (SCM_FALSEP export_symbols)))))

とある。
簡単に説明すると、stubファイルはCとSchemeの間を埋めるためのスクリプトで、swigでいうところのiファイルみたいなものか。define-cprocはCで定義した関数をSchemeからどう呼び出すかを定義する。
ううむ、使い方が間違っているかも?

最後にロードして、実行するスクリプトはこんな感じ。これは実行時に読み込まれる。
test-use.scm

(display (test-func 6))(newline)
(display (str-reverse "日本語"))(newline)

そしてコンパイル

gcc `gauche-config -I` test-use-c.c `gauche-config -l`

実行。

$ ./a.out
36
語本日

test-use.scmは実行時に読み込まれ評価されるので、変更がtest-use.scmだけならコンパイルは必要ない。変更->実行のループがかなり早くできるようになると思う。
ただ、スクリプトの隠蔽は完全ではない。リリースする時にはtest-use.scmもコンパイルして使うとか、何か工夫する必要があるかも。