LoginSignup
7
4

More than 5 years have passed since last update.

libffiをVisual C++でビルドする

Last updated at Posted at 2015-07-15

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スタイルの変数宣言が使えないため、その修正をしてあります。)

foo.c
#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でビルドします。

MSYS2
$ ./configure --build=i686-pc-mingw32
$ cd i686-pc-mingw32
$ make

出来たDLLからシンボルを抽出し、.defファイルを作成します。VC2010の場合は、

Cygwin
$ 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以降の場合は、

Cygwin
$ 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でビルドします。

Cygwin
$ 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
7
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4