2

I'm trying to make an FFI call but I get a segmentation fault inside the function called by ffi_call (gtk_init in this case). I'm not sure where I screwed up.

/*
 * gtk_init.cc
 */

#include <ffi.h>
#include <gtk/gtk.h>

void test();

int main(int argc, char *argv[]) {
    test();
    return 0;
}


void test() {

    ffi_cif cif;
    ffi_type *arg_types[2];
    void *arg_values[2];
    ffi_status status;
    ffi_arg result;

    arg_types[0] = &ffi_type_uint;
    arg_types[1] = &ffi_type_pointer;

    status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_void, arg_types);

    if (status != FFI_OK) {
        printf("Failed to prep_ffi\n");
        return;
    }

    int argc = 4;
    char **argv = (char**)malloc(sizeof(char*) * argc);
    argv[0] = strcpy((char*)malloc(sizeof(char) * 6), "test");
    argv[1] = strcpy((char*)malloc(sizeof(char) * 13), "--gtk-debug");
    argv[2] = strcpy((char*)malloc(sizeof(char) * 6), "misc");
    argv[3] = strcpy((char*)malloc(sizeof(char) * 6), "last");

    arg_values[0] = &argc;
    arg_values[1] = &argv;

    ffi_call(&cif, FFI_FN(gtk_init), &result, arg_values);

    // gtk_init(&argc, &argv);

    for (int i = 0; i < argc; i++) {
        printf("%s\n", argv[i]);
        free(argv[i]);
    }

    free(argv);
}

The complete gist can be found here.

Stargateur
  • 24,473
  • 8
  • 65
  • 91
Rom Grk
  • 526
  • 5
  • 15
  • 1
    `argv` should probably be `NULL` terminated. I have no idea if `gtk_init` relies on that, but `main`'s `argc` and `argv` input parameters guarantee that [`argv[argc]` equals `NULL`](https://stackoverflow.com/questions/3772796/argvargc). – Cornstalks Jun 16 '18 at 02:36
  • OT: regarding: `printf("Failed to prep_ffi\n");` Error messages should be output to `stderr`.not `stdout`. When the error indication comes from a C library function, should also output the text reason the system thinks the error occurred to `stderr`. One easy way to do this is: `perror( "Failed to prep_ffi" );` – user3629249 Jun 17 '18 at 15:47
  • In C, when calling any of the heap allocation functions: `malloc` `calloc` `realloc`, 1) the returned type is `void*` which can be assigned to any pointer. Casting just clutters the code, making it more difficult to understand, debug, etc. 2) always check (!=NULL) the returned value to assure the operation was successful – user3629249 Jun 17 '18 at 15:52

1 Answers1

2

The problem is that ffi_call() take the address of the argument as gtk_init() take int * and char *** you need to take the address of these so that give int ** and char ****, fun ?

As said in comment gtk_init() also expect argv to be NULL terminated.

And the final problem is that ffi_type_uint is the wrong type you must use ffi_type_pointer in this case (and by the way int need ffi_type_sint).

So the final fixed code is:

#include <ffi.h>
#include <gtk/gtk.h>

void test(void);

int main(void) { test(); }

void test(void) {
  ffi_type *arg_types[] = {&ffi_type_pointer, &ffi_type_pointer};
  ffi_cif cif;
  ffi_status status =
      ffi_prep_cif(&cif, FFI_DEFAULT_ABI, sizeof arg_types / sizeof *arg_types,
                   &ffi_type_void, arg_types);
  if (status != FFI_OK) {
    printf("Failed to prep_ffi\n");
    return;
  }

  // bad we don't check malloc() !!! ;)
  int argc = 4;
  char **argv = malloc(sizeof *argv * (argc + 1));
#define X(x) strcpy(malloc(sizeof x), x);
  argv[0] = X("test");
  argv[1] = X("--gtk-debug");
  argv[2] = X("misc");
  argv[3] = X("last");
  argv[4] = NULL;
#undef X

  int *p_argc = &argc; // This is what expect gtk_init
  char ***p_argv = &argv;
  void *arg_values[] = {&p_argc, &p_argv}; // so ffi need their address

  ffi_arg result;
  ffi_call(&cif, FFI_FN(&gtk_init), &result, arg_values);

  for (int i = 0; i < argc; i++) {
    printf("%s\n", argv[i]);
    free(argv[i]);
  }

  free(argv);
}

Disclaimer: Not tested.

Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • It woooooooorks! :D Thanks! (you just missed some `(char*)` casts before `malloc`, but I'll let it pass because I don't check my `malloc`s ever, so we're even) – Rom Grk Jun 16 '18 at 05:02
  • 1
    @RomGrk A cast is needed for you because you compile your code in C++, your question was taged C and C++, Daniel A. White choice to remove C++ because you use `malloc()`, try to avoid to use both tag. C is not C++ is not C. Feel free to replace the C tag by a C++ tag if you want a C++ answer. I will probably give you a totally different code ;) – Stargateur Jun 16 '18 at 05:34