libffiをビルドするためには、公式ドキュメントにも記載されているとおり、./configure
が動く環境が必要です。今回は、VC2010(32bit)とCygwin(32bit)を組み合わせて使うことにしました。
ライブラリのビルド
まずは、VC2010のコマンドプロンプトを立ち上げます。そこから C:\cygwin\bin\mintty -
を実行することで、cl.exe
にパスが通った状態でCygwinを立ち上げます。
現時点のlibffiの最新版はv3.2.1ですが、そのままではVC2010ではコンパイルが通らないため、Ruby 2.2のソースに含まれるパッチを元にしたパッチを当てます。(v3.1であればパッチを当てずともビルドできます。)
$ git clone git://github.com/atgreen/libffi.git
$ cd libffi
$ git checkout -b v3.2.1-msvc v3.2.1
$ wget https://gist.githubusercontent.com/k-takata/c7a78f0c5fbbcb8365d0/raw/master/0001-Support-MSVC.patch
$ git am 0001-Support-MSVC.patch
$ ./autogen.sh
$ ./configure CC=$PWD/msvcc.sh CXX=$PWD/msvcc.sh CPP="cl -nologo -EP"
※64bitのCygwinを使う場合は、./configure
に--build=i686-pc-cygwin
オプションを指定する必要があります。
$ ./configure CC=$PWD/msvcc.sh CXX=$PWD/msvcc.sh CPP="cl -nologo -EP" --build=i686-pc-cygwin
ffitarget.h
がCygwin形式のシンボリックリンクとして作成されますが、当然cl.exe
からは読めないので、修正してからmakeします。
$ rm i686-pc-cygwin/include/ffitarget.h
$ echo '#include "../../src/x86/ffitarget.h"' > i686-pc-cygwin/include/ffitarget.h
$ make
これで i686-pc-cygwin/.libs/libffi-6.dll
が出来ますが、シンボルがエクスポートされていないため使用できません。
スタティックリンク用のライブラリは、i686-pc-cygwin/.libs/libffi_convenience.lib
が利用できます。
スタティックリンク版の動作確認
man/ffi_call.3
にあるサンプルコードを少し修正し、以下のコードを用意します。
(VC2010ではC99スタイルの変数宣言が使えないため、その修正をしてあります。)
# include <ffi.h>
# include <stdio.h>
unsigned char
foo(unsigned int, float);
int
main(int argc, const char **argv)
{
ffi_cif cif;
ffi_type *arg_types[2];
void *arg_values[2];
ffi_status status;
unsigned int arg1;
float arg2;
// Because the return value from foo() is smaller than sizeof(long), it
// must be passed as ffi_arg or ffi_sarg.
ffi_arg result;
// Specify the data type of each argument. Available types are defined
// in <ffi/ffi.h>.
arg_types[0] = &ffi_type_uint;
arg_types[1] = &ffi_type_float;
// Prepare the ffi_cif structure.
if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI,
2, &ffi_type_uint8, arg_types)) != FFI_OK)
{
// Handle the ffi_status error.
}
// Specify the values of each argument.
arg1 = 42;
arg2 = 5.1;
arg_values[0] = &arg1;
arg_values[1] = &arg2;
// Invoke the function.
ffi_call(&cif, FFI_FN(foo), &result, arg_values);
// The ffi_arg 'result' now contains the unsigned char returned from foo(),
// which can be accessed by a typecast.
printf("result is %hhu", (unsigned char)result);
return 0;
}
// The target function.
unsigned char
foo(unsigned int x, float y)
{
unsigned char result = x - y;
return result;
}
以下のコマンドで、ビルド&実行します。スタティックリンクする場合は、-DFFI_BUILDING
が必要です。
$ cd i686-pc-cygwin
$ cl foo.c -Iinclude -MD -DFFI_BUILDING .libs/libffi_convenience.lib
$ ./foo.exe
result is 36
result is 36
と表示されたので、どうやら動いているようです。
ダイナミックリンク版
MSYS2 (MinGW-w64)でDLLをビルドし、出来たDLLから.defファイルを作成し、それを読み込ませることでシンボルをエクスポートします。.defファイルを読み込ませるためには、Makefileを修正する必要があります。
--- a/i686-pc-cygwin/Makefile
+++ b/i686-pc-cygwin/Makefile
@@ -1149,7 +1149,7 @@
src/vax/$(DEPDIR)/$(am__dirstamp)
libffi.la: $(libffi_la_OBJECTS) $(libffi_la_DEPENDENCIES) $(EXTRA_libffi_la_DEPENDENCIES)
- $(AM_V_CCLD)$(libffi_la_LINK) -rpath $(toolexeclibdir) $(libffi_la_OBJECTS) $(libffi_la_LIBADD) $(LIBS)
+ $(AM_V_CCLD)$(libffi_la_LINK) -rpath $(toolexeclibdir) $(libffi_la_OBJECTS) $(libffi_la_LIBADD) $(LIBS) '-Wl,-link -def:libffi.def'
libffi_convenience.la: $(libffi_convenience_la_OBJECTS) $(libffi_convenience_la_DEPENDENCIES) $(EXTRA_libffi_convenience_la_DEPENDENCIES)
$(AM_V_CCLD)$(LINK) $(libffi_convenience_la_OBJECTS) $(libffi_convenience_la_LIBADD) $(LIBS)
MSYS2でビルドします。
$ ./configure --build=i686-pc-mingw32
$ cd i686-pc-mingw32
$ make
出来たDLLからシンボルを抽出し、.defファイルを作成します。VC2010の場合は、
$ dumpbin -exports i686-pc-mingw32/.libs/libffi-6.dll | awk 'BEGIN {print "EXPORTS"} $3 ~ /^[0-9A-F]+$/{if($4 ~ /longdouble|complex/){$4=";" $4} print "\t", $4}' > i686-pc-cygwin/libffi.def
VC2013以降の場合は、
$ dumpbin -exports i686-pc-mingw32/.libs/libffi-6.dll | awk 'BEGIN {print "EXPORTS"} $3 ~ /^[0-9A-F]+$/{if($4 ~ /longdouble/){$4=";" $4} print "\t", $4}' > i686-pc-cygwin/libffi.def
VCでビルドします。
$ cd i686-pc-cygwin
$ rm libffi.la # リンクを再実行させるため
$ make
64bitの場合
まずは、VC2010(64bit)のコマンドプロンプトを立ち上げます。そこから C:\cygwin64\bin\mintty -
を実行し、cl.exe
にパスが通った状態でCygwinを立ち上げます。
$ git clone git://github.com/atgreen/libffi.git
$ cd libffi
$ git checkout -b v3.2.1-msvc v3.2.1
$ wget https://gist.githubusercontent.com/k-takata/c7a78f0c5fbbcb8365d0/raw/master/0001-Support-MSVC.patch
$ git am 0001-Support-MSVC.patch
$ ./autogen.sh
$ ./configure CC="$PWD/msvcc.sh -m64" CXX="$PWD/msvcc.sh -m64" CPP="cl -nologo -EP"
$ rm x86_64-unknown-cygwin/include/ffitarget.h
$ echo '#include "../../src/x86/ffitarget.h"' > x86_64-unknown-cygwin/include/ffitarget.h
$ make