Search code examples
cygwinexpectautoconfmingw32libffi

How can I build and test libffi under cygwin with mingw32?


After checking (most recent) tag v3.2.1:

% sh autogen.sh
% ./configure CC=i686-pc-mingw32-gcc
% make check

All tests appear to fail.

Using CC=gcc, tests seem to work properly. Unfortunately I need the resulting build to have no cygwin dependencies, since I'm building a JNI DLL.


Solution

  • I tried building libffi using MSYS2 environment and mingw-w64 and I hit the same wall:

    a) all tests seem to fail when I run make check

    b) when I try to compile the libffi Hello World example with -lffi, the linker complains about unresolved references to all ffi-related symbols (the symbols are indeed included in libffi.a, but probably due to circular dependencies and the order of object files, the linker fails to collect all the symbols)

    Fortunately, if I drop -lffi and instead include all object files (*.o) created by libffi make operation, the created executable runs just fine.

    Here's a link to the libffi Hello World example I used: http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Closure-Example.html

    [EDIT]

    After some additional experiments, I managed to compile the program by replacing -lffi with -Wl,--whole-archive,-lffi,--no-whole-archive. This way, the linker would include all object files from libffi.a and everything would work just fine.

    Here's the Hello World example (hello.c) with detailed steps I used, in case someone finds this info useful:

    /*
     * Steps for building libffi on Windows and running this Hello World example:
     *
     * 1. Download and install the latest version of MSYS2
     *    a) download the latest (64-bit or 32-bit) installer from http://msys2.github.io
     *    b) run the installer accepting default settings
     *    c) execute the following commands to update the system core
     *        pacman -Sy pacman
     *        pacman -Syu
     *        pacman -Su
     *    d) restart MSYS2, if requested to do so
     *    e) execute the following command to install development tools
     *       for 64-bit gcc:
     *        pacman --needed -S base-devel dejagnu mingw-w64-x86_64-gcc
     *       for 32-bit gcc:
     *        pacman --needed -S base-devel dejagnu mingw-w64-i686-gcc
     *    f) restart MSYS2
     * 2. Download and compile the latest version of libffi
     *    a) download the latest source code bundle from https://github.com/libffi/libffi/releases
     *    b) unpack the source code bundle in MSYS2 tmp directory (e.g. C:\msys64\tmp)
     *    c) execute the following MSYS2 commands to compile libffi (adapt the version number):
     *        cd /tmp/libffi-3.2.1
     *        ./autogen.sh
     *        ./configure --prefix=/tmp/out --enable-static --disable-shared
     *        make
     *    d) optionally, execute the following command to run the tests:
     *        make check
     *    e) copy the distributable files to the configured /tmp/out directory
     *        make install
     *       the following files are needed for the next step:
     *        /tmp/out/lib/libffi.a
     *        /tmp/out/lib/libffi-3.2.1/include/ffi.h
     *        /tmp/out/lib/libffi-3.2.1/include/ffitarget.h
     * 3. Compile this example
     *    a) copy this file to MSYS2 tmp directory (e.g. C:\msys64\tmp\hello.c)
     *    b) execute the following MSYS2 command to compile the example:
     *        gcc -I /tmp/out/lib/libffi-3.2.1/include -L /tmp/out/lib -lffi -o /tmp/hello /tmp/hello.c
     *    c) run the example (/tmp/hello.exe), the output should be:
     *        Hello World!
     *
     * Troubleshooting
     *
     * If the tests seem to fail and the compilation in step 3b) above reports undefined references to 'ffi_*' symbols,
     * try compiling using the following command instead:
     *        gcc -I /tmp/out/lib/libffi-3.2.1/include -L /tmp/out/lib -Wl,--whole-archive,-lffi,--no-whole-archive -o /tmp/hello /tmp/hello.c
     * Another alternative is to try linking the original libffi object files (*.o) and drop -lffi as follows:
     * For 64-bit version:
     *        export SRC=/tmp/libffi-3.2.1/x86_64-w64-mingw32/src
     *        gcc -I /tmp/out/lib/libffi-3.2.1/include -o /tmp/hello /tmp/hello.c $SRC/prep_cif.o $SRC/types.o $SRC/raw_api.o $SRC/java_raw_api.o $SRC/closures.o $SRC/x86/ffi.o $SRC/x86/win64.o
     * For 32-bit version:
     *        export SRC=/tmp/libffi-3.2.1/i686-w64-mingw32/src
     *        gcc -I /tmp/out/lib/libffi-3.2.1/include -o /tmp/hello /tmp/hello.c $SRC/prep_cif.o $SRC/types.o $SRC/raw_api.o $SRC/java_raw_api.o $SRC/closures.o $SRC/x86/ffi.o $SRC/x86/win32.o
     */
    
    #include <stdio.h>
    #include <ffi.h>
    
    /* Acts like puts with the file given at time of enclosure */
    void puts_binding(ffi_cif* cif, void* ret, void* args[], void* stream) {
      *(ffi_arg*) ret = fputs(*(char**) args[0], (FILE*) stream);
    }
    
    typedef int (*puts_t)(char*);
    
    int main() {
      ffi_cif cif; /* The call interface */
      ffi_type* args[1]; /* The array of pointers to function argument types */
      ffi_closure* closure; /* The allocated closure writable address */
      void* bound_puts; /* The allocated closure executable address */
      int rc; /* The function invocation return code */
    
      /* Allocate closure (writable address) and bound_puts (executable address) */
      closure = ffi_closure_alloc(sizeof(ffi_closure), &bound_puts);
    
      if (closure) {
        /* Initialize the array of pointers to function argument types */
        args[0] = &ffi_type_pointer;
    
        /* Initialize the call interface describing the function prototype */
        if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_sint, args) == FFI_OK) {
          /* Initialize the closure, setting stream to stdout */
          if (ffi_prep_closure_loc(closure, &cif, puts_binding, stdout, bound_puts) == FFI_OK) {
            rc = ((puts_t) bound_puts)("Hello World!");
            /* rc now holds the result of the call to fputs */
          }
        }
      }
    
      /* Deallocate both closure, and bound_puts */
      ffi_closure_free(closure);
    
      return 0;
    }