0

I am struggling with libffi (Function Foreign Interface library), probably, due to the lack of C experience. I have the following program that calls a function my_func dynamically using libffi:

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

unsigned my_func(double a, double b, double *res) {
    res[0] = a + b;
    res[1] = a - b;

    return 0;
}


int main(int argc, char *argv[])
{
    ffi_cif cif;
    ffi_type *arg_types[3] = {
        &ffi_type_double,
        &ffi_type_double,
        &ffi_type_pointer
    };
    ffi_type *rettype = &ffi_type_uint;

    if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, rettype, arg_types) != FFI_OK) {
        fprintf(stderr, "ffi_prep_cif is not successful\n");
        exit(EXIT_FAILURE);
    }

    double a = 3.0;
    double b = 2.0;
    double res[2] = {99.0, 15.0};
    void *arg_values[3] = {
        &a,
        &b,
        res
    };

    unsigned status;
    ffi_call(&cif, FFI_FN(my_func), &status, arg_values);

    printf("Function return status code %u\n", status);
    printf("Values in res array: \n");
    printf("[0] = %f\n", res[0]);
    printf("[1] = %f\n", res[1]);
    
    return 0;
}

When I debug the program, it seems that all the arg_values are set correctly:

(gdb) p ((double *) arg_values[2])[0]
$1 = 99
(gdb) p ((double *) arg_values[2])[1]
$2 = 15

You can see above that the arg_values third values is correctly set to the res array.

However, when I am inside the function my_func, the third argument (the res array) becomes NULL:

Breakpoint 2, my_func (a=3, b=2, res=0x0) at test_ffi.c:6
6           res[0] = a + b;

I do not really understand what is wrong here. Could somebody explain to me, why this happens and how to fix it? Thank you!

Update

Thanks to the comments by @selbie, the following modified program works (note that we obtain a second pointer to the array res and pass its address to FFI):

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

unsigned my_func(double a, double b, double *res) {
    res[0] = a + b;
    res[1] = a - b;

    return 0;
}

int main(int argc, char *argv[]) {
    ffi_cif cif;
    ffi_type *arg_types[3] = {
        &ffi_type_double,
        &ffi_type_double,
        &ffi_type_pointer
    };
    ffi_type *rettype = &ffi_type_uint;

    if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 3, rettype, arg_types) != FFI_OK) {
        fprintf(stderr, "ffi_prep_cif is not successful\n");
        exit(EXIT_FAILURE);
    }

    double a = 3.0;
    double b = 2.0;
    double res[2] = {99.0, 15.0};
    double *p_res = res;
    void *arg_values[3] = {
        &a,
        &b,
        // res
        &p_res
    };

    unsigned status;
    ffi_call(&cif, FFI_FN(my_func), &status, arg_values);

    printf("Function return status code %u\n", status);
    printf("Values in res array: \n");
    printf("[0] = %f\n", res[0]);
    printf("[1] = %f\n", res[1]);
    
    return 0;
}
Dmitry Kabanov
  • 710
  • 1
  • 7
  • 20

1 Answers1

1

Shouldn't this:

if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, rettype, arg_types) != FFI_OK)

Be this:

if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 3, rettype, arg_types) != FFI_OK)

Given that you are invoking a function that expects three parameters, but only initialized the ffi stuff to expect 2 parameters, it shouldn't be a surprised that one parameter goes missing.

selbie
  • 100,020
  • 15
  • 103
  • 173
  • Yes, thank you, you're right, silly mistake. However, now the arg_values look completely weird: `(gdb) p ((double *) arg_values)[0] $2 = 6.9533558067599175e-310 (gdb) p ((double *) arg_values)[1] $3 = 6.9533558067603127e-310 ` – Dmitry Kabanov Aug 15 '23 at 16:08
  • You might might need to Assign `double* ptr = res;` And then make `&ptr` the third argument to `arg_values` Also, does ffi_call_prep need to know that the third argument is an array, not a pointer. – selbie Aug 15 '23 at 16:14
  • I am not sure why would I pass `&ptr`? `ptr` is already a pointer to double, and this is the third argument to the function `my_func`. – Dmitry Kabanov Aug 16 '23 at 09:09
  • you're right, obtaining a second pointer (`ptr`) to the same array and passing `&ptr` works. I am not sure what is going on, as `res` and `ptr` are having the same value in the `main` function. – Dmitry Kabanov Aug 16 '23 at 09:51
  • @DmitryKabanov - what you are you observing is a lesser-known subtlety of C/C++. And that is that a pointer to a local array is the array itself. That is given `int array[N]`. Then **within the same scope**, `&array == array` as well as `&array == &array[0]`. Since FFI wants a pointer to a pointer, we just save off the array address to an actual pointer instance and pass the pointer to that variable. https://stackoverflow.com/questions/2528318/how-come-an-arrays-address-is-equal-to-its-value-in-c – selbie Aug 16 '23 at 15:58
  • thank you very much for all the explanations. It seems like the equivalence of arrays and pointers is not what they usually say. I was reading this: https://c-faq.com/aryptr/aryvsadr.html and it says there that `&array` and `array` have different type (smalll experiment with `gcc` confirms that): `&array` is a pointer to array, while `array` is a pointer to the first element, and they are not the same, although the values are the same – Dmitry Kabanov Aug 24 '23 at 15:26