rubyffimsys2fiddleruby-ffi

Ruby Fiddle - Cannot find function in WIndows MSYS2


I am trying to use Ruby Fiddle to access a C function I have developed. The C code is:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

double *linalg_positive_matrix(double *mat_a, int rows_a, int cols_a);

int main(void) {
}

double *linalg_positive_matrix(double *mat_a, int rows_a, int cols_a) {
    double *pos_mat = (double *) malloc(rows_a * cols_a * sizeof (double));
    int i, j;

    for (i = 0; i < rows_a; i++) {
        for (j = 0; j < cols_a; j++) {
            if (mat_a[i * cols_a + j] < 0.0) {
                pos_mat[i * cols_a + j] = 0.0;
            } else {
                pos_mat[i * cols_a + j] = mat_a[i * cols_a + j];
            }

        }
    }

    return pos_mat;
}

I am using MSYS2 so I compile it with:

gcc lib.c -o lib.dll

In Ruby (2.7.1) irb console I then use:

extend Fiddle::Importer
dlload "lib.dll"

to load the dll and I do not get any error, but when I try to load the function with

extern "double* linalg_positive_matrix(double*, int, int)"

I receive the error:

Traceback (most recent call last):
        6: from C:/Apps/rubies/Ruby27-x64/bin/irb.cmd:31:in `<main>'
        5: from C:/Apps/rubies/Ruby27-x64/bin/irb.cmd:31:in `load'
        4: from C:/Apps/rubies/Ruby27-x64/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `<top (required)>'
        3: from (irb):5
        2: from C:/Apps/rubies/Ruby27-x64/lib/ruby/2.7.0/fiddle/import.rb:172:in `extern'
        1: from C:/Apps/rubies/Ruby27-x64/lib/ruby/2.7.0/fiddle/import.rb:299:in `import_function'
Fiddle::DLError (cannot find the function: linalg_positive_matrix())

I use the same approach on macos and the function is imported correctly so I don't think the problem is in the code or function signature.

Just for completeness, in case it is important, I have installed MSYS2 and ruby separately, MSYS2 from the main website and Ruby from rubyinstallers. I am compiling from the MSYS2 MinGW 64 console and running irb from the Ruby terminal created by the installer. It is supposed to use MSYS2 as well.

EDIT (add more info after the correct answer) The key part for me was the -shared option.

I also uninstalled the MSYS2 from MSYS2 and used directly the MSYS from rubyinstaller as follows:

ridk enable
ridk use /27-/

At this point, if you haven't already, you can install the tool chains with

ridk install

One weird thing I still do not understand is the output of which:

which ruby
# /c/Apps/rubies/Ruby27-x64/bin/ruby
which gcc
# /mingw64/bin/gcc

They look different, but it might be because of the way MSYS2 works when bundled with rubyinstaller.


Solution

  • First of all, make sure you are using a MinGW gcc and a MinGW ruby so there aren't any toolchain compatibility issues. Run which gcc and which ruby in your shell and make sure both commands return a filename in /mingw64 or both return a filename in /mingw32.

    Secondly, make sure you compile your DLL correctly. You sould remove the main function because DLLs shouldn't have a main function. I think the correct command is this:

    gcc lib.c -shared -o lib.dll
    

    In your shell, run file prog.dll and make sure it says something like "PE32+ executable (DLL)".

    That might work for you. If not, you'll have to make sure your function is actually listed as one of the functions that is exported from the DLL. You can either do this by adding a dllexport attribute in the source code or by using the right compile options.