cpointersdoublelibffi

Wrong value with a function that returns a double (libffi)


It seems that a function returning a double data type, from libffi, is not correctly casted when the function returns its value, here is the code I used:

#include <stdio.h>
#include <stdlib.h>
#include <ffi.h>
#include <math.h>  // The make call the function `cos` by the FFI.

int main()
{
  ffi_cif cif;
  ffi_type *args[1];
  void *values[1];
  ffi_arg rc;

  args[0] = &ffi_type_double;

  void *ptr = malloc(sizeof(double));
  *((double *)ptr) = 3.0;
  values[0] = ptr;

  if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_double, args) == FFI_OK)
      ffi_call(&cif, cos, &rc, values);

  printf("%f\n", (double)rc);

  return 0;
}

The result is as follows: 13830464316077631000.000000.

The ffi_arg type is an alias for unsigned long long. The documentation states that the argument of type ffi_arg passed to the ffi_call function, is used to store data if it is smaller than sizeof(ffi_arg), otherwise it is a pointer to the data in memory:

rvalue must point to storage that is sizeof(ffi_arg) or larger for non-floating point types. For smaller-sized return value types, the ffi_arg or ffi_sarg integral type must be used to hold the return value.

(https://manpages.debian.org/testing/libffi-dev/ffi_call.3.en.html)

So I tried it with a pointer dereference:

void* rc;
// The code to call the function and initialize the CIF...
double my_value = *(double *)((void *)rc);

Which crashed the program.

How am I supposed to access the value stored using the rc variable?

EDIT 1

The command line used to compile the program:

gcc source.c -lffi -o source

There are no errors or warnings at compile time.

EDIT 2

After adding the `-Wall' build option, I get:

warning: passing argument 2 of 'ffi_call' from incompatible pointer type [-Wincompatible-pointer-types]
ffi_call(&cif, cos, &rc, values);
               ^~~

This warning seems to be ignored in the libffi example. The example given works very well for me (with this warning).


Solution

  • The problem is simple. The ffi_arg is not the type you're supposed to put your return value to. Instead, the 3rd argument is defined as a pointer to void, that should point to an object that is suitably large to contain the return value, and of proper alignment, and of proper type, or interpreted as such, thus:

    double rv;
    ...
    ffi_call(&cif, cos, &rv, values);
    

    or

    void *rv = malloc(sizeof (double));
    ...
    ffi_call(&cif, cos, rv, values);
    double value = *(double *)rv;
    

    As for the warning, that happens because the libffi code is not strictly portable. A function pointer cannot be automatically converted to a void *. You can silence the warning with an explicit cast:

    ffi_call(&cif, (void *)cos, rv, values);
    

    At least it does not make it any more wrong than it already is.