SIGSEGV error using SWIG to create shared java library

So I am trying to port the C library (libnfc) to Java using SWIG.

I need to have a shared library compiled and the main call to the "nfc_version ()" method will work. However, calling nfc_init () to set an event raises a SIGSEGV error. Calling the nfc library directly is fine.

The commands I used to create the shared library:

swig -java -I../libnfc/include nfclib.i 
gcc -c -I/usr/lib/jvm/java-7-openjdk-i386/include/ -I/usr/lib/jvm/java-7-openjdk-i386/include/linux nfclib_wrap.c
gcc -shared nfclib_wrap.o ../build/libnfc/libnfc.so libnfc_wrap.so

      

Libnfc.i file:

%module nfc
%{
#include <nfc/nfc.h>
#include <nfc/nfc-types.h>
#include <nfc/nfc-emulation.h>
%}

%include <nfc/nfc.h>
%include <nfc/nfc-types.h>
%include <nfc/nfc-emulation.h>

      

those. it should include all the methods that libnfc provides.

Here is the error log I receive: http://openetherpad.org/AyVDsO4XTg

Obviously, it could be that a specific solution may not be available from the information I have provided. But any suggestions on what to try would be really appreciated (I'm kind of at the end of my knowledge here).

+3


source to share


2 answers


To always pass the same function pointer automatically, it's pretty simple in SWIG. For example, given the header file test.h, which fixes the main part of your problem:

struct context; // only used for pointers

void init_context(struct context **ctx) { *ctx=malloc(1); printf("Init: %p\n", *ctx); }
void release_context(struct context *ctx) { printf("Delete: %p\n", ctx); free(ctx); }

void foo(struct context *ctx) { printf("foo: %p\n", ctx); }

      

We can wrap it up and automatically call the global context to be passed in whatever is expected by doing something like:

%module test

%{
#include "test.h"

// this code gets put in the generated C output from SWIG, but not wrapped:
static struct context *get_global_ctx() {
  static struct context *ctx = NULL;
  if (!ctx) 
    init_context(&ctx);
  return ctx;
}
%}

%typemap(in,numinputs=0) struct context *ctx "$1=get_global_ctx();"

%ignore init_context; // redundant since we call it automatically

%include "test.h"

      

This sets the typemap for struct context *ctx

, which, instead of typing input from Java, automatically calls get_global_ctx()

wherever it matches.

This is probably enough to create a sane-ish interface for a Java developer, but it is less ideal: it forces the context to be global and means no Java application can ever run multiple contexts at the same time.

A nicer solution, given that Java is an OO language, is to create the context as a first class object. We can also get SWIG to create such an interface for us, although it's a little confusing. Our SWIG module file becomes:

%module test

%{
#include "test.h"
%}

// These get called automatically, no need to expose:
%ignore init_context;
%ignore delete_context;

// Fake struct to convince SWIG it should be an object:
struct context {
  %extend {
    context() {
      // Constructor that gets called when this object is created from Java:
      struct context *ret = NULL;
      init_context(&ret); 
      return ret;
    }
    ~context() {
      release_context($self);
    }
  }
};

%include "test.h"

      

and we can successfully implement this code:

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");
    context ctx = new context();
    // You can't count on the finalizer if it exits:
    ctx.delete();
    ctx = null;
    // System.gc() might also do the trick and in a longer
    // running app it would happen at some point probably.
  }
}

      

gives:

Init: 0xb66dab40
Delete: 0xb66dab40

      

In a dynamically typed language, which would be complex, we could use some form of meta-programming to insert member functions as needed. So we could say something kind of new context().foo();

completely as expected. Java is statically typed, so we need something more. We can do this in SWIG in several ways:



  • Agree that we can call it test.foo(new context());

    pretty happily now - it still looks a lot like C in Java, although I would guess it might be a code smell if you end up writing a lot of Java that looks like C.

  • Use %extend

    to (manually) add methods to the context class, %extend

    in test.i becomes:

    %extend {
        context() {
          // Constructor that gets called when this object is created from Java:
          struct context *ret = NULL;
          init_context(&ret); 
          return ret;
        }
        ~context() {
          release_context($self);
        }
        void foo() {
          foo($self);
        }
      }
    
          

  • As with %extend

    , but write glue on the Java side using the following card:

    %typemap(javacode) struct context %{
      public void foo() {
        $module.foo(this);
      }
    %}
    
          

    (Note: this must be early enough in the interface file for it to work)

Note that nowhere have I shown SWIG the real definition of my context structure - it always prefers my "library" for anything where a real definition is required, so the opaque pointer remains fully opaque.


A simpler solution for wrapping with a init_context

double pointer would be to use %inline

to provide an additional function that is only used in the wrapper:

%module test

%{
#include "test.h"
%}

%inline %{
  struct context* make_context() {
    struct context *ctx;
    init_context(&ctx);
    return ctx;
  }
%}

%ignore init_context;

%include "test.h"

      

It is enough that we can write the following Java:

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");
    // This object behaves exactly like an opaque pointer in C:
    SWIGTYPE_p_context ctx = test.make_context();
    test.foo(ctx);
    // Important otherwise it will leak, exactly like C
    test.release_context(ctx);
  }
}

      

Alternative but similar approaches include using the cpointer.i library :

%module test

%{
#include "test.h"
%}

%include <cpointer.i>

%pointer_functions(struct context *,context_ptr);

%include "test.h"

      

which can then be used like:

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");
    SWIGTYPE_p_p_context ctx_ptr = test.new_context_ptr();
    test.init_context(ctx_ptr);
    SWIGTYPE_p_context ctx = test.context_ptr_value(ctx_ptr);
    // Don't leak the pointer to pointer, the thing it points at is untouched
    test.delete_context_ptr(ctx_ptr);
    test.foo(ctx);
    // Important otherwise it will leak, exactly like C
    test.release_context(ctx);
  }
}

      

There is also a macro pointer_class

that is a little more OO than that and might be worth using instead. The point is that you provide tools for working with opaque pointer objects that SWIG uses to represent pointers that it knows nothing about, but avoiding calls getCPtr()

that inherently undermine the type system.

+5


source


So Flexo's answer is the correct way to solve this problem, but SWIG also offers a "cpointer.i" module (described here: http://www.swig.org/Doc1.3/SWIGDocumentation.html#Library_nn3 ) which allowed me hack a quick solution so that I can verify that the main library was working for me. I thought I would put this answer just for completeness and suggest an alternative to anyone stumbling over this question.

The need for this is what I would call asymmetry in what the SWIG generates. An object of the base type has a property swigCPtr, which is the memory address of this object ("pointer to itself"). Then, to make a pointer, you just get swigCptr from the base type and pass it in the constructor for the pointer type (SWIGTYPE_p_X) that swig generates. BUT the pointer type ONLY exists in Java and just contains the swigCptr value. There is no memory block c holding this pointer. Hence, there is no pointer type equivalent to swigCPtr. Those. there is no built-in pointer in the pointer type, just as there is a native pointer stored in the base type.

Therefore, you cannot make a pointer to a pointer (SWIGTYPE_p_p_X), since you don't have the address of the pointer to pass when you create it.

My new .i 'file looks like this:



%module nfc
%{
#include <nfc/nfc.h>
#include <nfc/nfc-types.h>
#include <nfc/nfc-emulation.h>
%}

%include <nfc/nfc.h>
%include <nfc/nfc-types.h>
%include <nfc/nfc-emulation.h>

%include "cpointer.i"
%pointer_functions(nfc_context*, SWIGTYPE_p_p_nfc_context)

      

What this last macro does is it provides 4/5 functions for creating pointers. All functions take and return the types that swig should have generated. New usage in Java to get the nfc_init and nfc_open commands to work:

SWIGTYPE_p_p_nfc_context context_p_p = nfc.new_SWIGTYPE_p_p_nfc_context();
nfc.nfc_init(context_p_p);
//get the context pointer after init has set it up (the java doesn't represent what happening in the c) 
SWIGTYPE_p_nfc_context context_p = nfc.SWIGTYPE_p_p_nfc_context_value(context_p_p);
SWIGTYPE_p_nfc_device pnd = nfc.nfc_open(context_p, null);

      

please note that I need to get the pointer from the double pointer after init command completes as the information stored in the java pointer object is separate from the C 'world'. Thus, extracting the "value" for context_p_p will give context_p with the correct value for its pointer.

0


source







All Articles