使用SWIG创建Java共享库时出现SIGSEGV错误

所以,我正在尝试使用SWIG将C库(libnfc)移植到Java。

我已经到了编译共享库的地步,并且基本的“nfc_version()”方法调用将起作用。 但是,调用“nfc_init()”进行设置会导致SIGSEGV错误。 直接调用nfc库很好。

我用来生成共享库的命令:

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文件:

 %module nfc %{ #include  #include  #include  %} %include  %include  %include  

即它应该包括libnfc提供的所有方法。

这是我得到的错误日志: http : //openetherpad.org/AyVDsO4XTg

显然,可能无法从我提供的信息中获得特定的解决方案。 但任何有关尝试的事情的建议都会非常感激(我的知识就在这里)。

为了始终将相同的指针自动传递给函数,它在SWIG中相当简单。 例如,给定“header”文件test.h,它捕获问题的核心部分:

 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); } 

我们可以将它包装起来并自动导致全局上下文通过执行以下操作在任何地方传递:

 %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" 

这为struct context *ctx设置了一个typemap,它不是从Java获取输入,而是在匹配的任何地方自动调用get_global_ctx()

这可能足以为Java开发人员提供一个理智的界面,但它不太理想:它强制上下文成为全局,并且意味着没有Java应用程序可以同时使用多个上下文。

考虑到Java是一种OO语言,一个更好的解决方案是使上下文成为第一类Object。 我们也可以让SWIG为我们生成这样一个界面,虽然它有点复杂。 我们的SWIG模块文件变为:

 %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" 

我们可以成功运用此代码:

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

得到:

 Init: 0xb66dab40 Delete: 0xb66dab40 

在动态类型语言中,这将是艰难的部分 – 我们可以使用一种或另一种forms的元编程来根据需要插入成员函数。 因此,我们可以说类似new context().foo(); 完全按照预期。 Java是静态类型的,所以我们需要更多东西。 我们可以通过多种方式在SWIG中执行此操作:

  1. 接受我们现在可以调用test.foo(new context()); 非常高兴 – 它看起来很像Java中的C仍然如此,所以我建议如果你最终编写了大量看起来像C的Java,那可能是代码味道。

  2. 使用%extend (手动)将方法添加到上下文类中,test.i中的%extend变为:

     %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); } } 
  3. %extend ,但是使用typemap在Java端编写粘合剂:

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

    (注意:这需要在接口文件中足够早才能工作)

请注意,我在这里没有向SWIG显示我的上下文结构的真实定义 – 它总是按照我的“库”来处理需要实际定义的任何内容,因此不透明指针保持完全不透明。


使用双指针包装init_context一个更简单的解决方案是使用%inline来提供仅在包装器中使用的额外函数:

 %module test %{ #include "test.h" %} %inline %{ struct context* make_context() { struct context *ctx; init_context(&ctx); return ctx; } %} %ignore init_context; %include "test.h" 

足以让我们编写以下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); } } 

替代方案,但类似的方法将包括使用cpointer.i库 :

 %module test %{ #include "test.h" %} %include  %pointer_functions(struct context *,context_ptr); %include "test.h" 

您可以将其用作:

 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); } } 

还有一个pointer_class宏比它更多OO,可能值得使用。 关键是你要提供工具来处理SWIG用来表示它一无所知的指针的不透明指针对象,但避免了本质上颠覆类型系统的getCPtr()调用。

所以Flexo的答案是解决这个问题的正确方法,但SWIG还提供了“cpointer.i”模块(在此描述: http ://www.swig.org/Doc1.3/SWIGDocumentation.html#Library_nn3),这使我能够破解一个快速的解决方案,以便我可以测试我有基本的库工作。 我想我会把这个答案只是为了完整性而提供一个替代任何偶然发现这个问题的人。

对此的需求是因为我将其描述为SWIG生成的不对称。 基类型对象有一个属性swigCPtr,它是该对象的内存地址(“自指针”)。 然后要创建一个指针,只需从基类型中获取swigCptr,并将其传递给swig生成的指针类型(SWIGTYPE_p_X)的构造函数。 但指针类型仅存在于Java中,只保存swigCptr值。 持有该指针的内存没有’c’块。 因此,指针类型中没有等效的swigCPtr。 即,指针类型中没有存储自指针,就像存储在基类型中的自指针一样。

所以你不能指向一个指针(SWIGTYPE_p_p_X),因为你没有指针的地址来构造它时传递它。

我的新’.i’文件如下:

 %module nfc %{ #include  #include  #include  %} %include  %include  %include  %include "cpointer.i" %pointer_functions(nfc_context*, SWIGTYPE_p_p_nfc_context) 

最后一个宏的作用是提供4/5函数来制作指针。 这些函数都接受并返回swig应该已经生成的类型。 新的Java用法使nfc_init和nfc_open命令起作用:

 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's 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); 

请注意,我必须在init命令完成从双指针获取指针,因为存储在java指针对象中的信息与C’world’分开。 因此,检索context_p_p的’value’将为context_p提供其指针的正确值。