Search code examples
cffi

How do I make an FFI call with a char** pointer?


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.


Solution

  • 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.