使用SWIG创建java共享库时发生SIGSEGV错误

使用SWIG创建java共享库时发生SIGSEGV错误,java,swig,nfc,segmentation-fault,Java,Swig,Nfc,Segmentation Fault,所以,我尝试使用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-

所以,我尝试使用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 <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>
%模块nfc
%{
#包括
#包括
#包括
%}
%包括
%包括
%包括
也就是说,它应该包括libnfc提供的所有方法

这是我得到的错误日志:


显然,根据我提供的信息,可能无法提供特定的解决方案。但是,任何关于尝试的建议都将非常感谢(我在这里的知识已经不多了)。

要始终自动将同一指针传递给函数,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
设置一个类型映射,它不会从Java获取输入,而是在匹配的任何地方自动调用
get\u global\u ctx()

这可能足以让Java开发人员使用一个健全的ish接口,但它并不理想:它强制上下文是全局的,这意味着任何Java应用程序都不能同时处理多个上下文

考虑到Java是一种OO语言,一个更好的解决方案是使上下文成为第一类对象。我们还可以让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
删除:0xB6640
在一种动态类型的语言中,这将是很难完成的部分——我们可以使用一种或另一种形式的元编程来根据需要插入成员函数。因此,我们可以说类似于
newcontext().foo()完全符合预期。Java是静态类型的,所以我们需要更多的东西。我们可以通过多种方式在SWIG中实现这一点:

  • 接受我们现在可以调用
    test.foo(newcontext())非常令人高兴-尽管它看起来仍然很像Java中的C,所以我建议如果你最终编写了很多看起来像C的Java,那么这可能是一种代码味道

  • 使用
    %extend
    将方法(手动)添加到上下文类中,测试中的
    %extend
    。i变为:

    %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);
        }
      }
    
  • %extend
    一样,使用类型映射在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);
      }
    }
    
    可供选择但类似的方法包括使用:


    还有一个
    pointer\u类
    宏,它的面向对象性稍强,可能值得使用。但关键是,您提供了使用不透明指针对象的工具,SWIG使用这些对象来表示它不知道的指针,但避免了
    getCPtr()
    调用,因为这些调用本质上破坏了类型系统。

    所以Flexo的答案是解决这个问题的正确方法,但SWIG也提供了“cpointer.i”模块(此处描述:)允许我快速拼凑出一个解决方案,以便测试基本库是否正常工作。我想我会把这个答案放在这里只是为了完整,并为任何偶然发现这个问题的人提供一个替代方案

    之所以需要这样做,是因为我将其描述为SWIG产生的不对称性。基类型对象有一个属性swigCPtr,它是该对象的内存地址(“自指针”)。然后,要生成指针,只需从基类型获取swigCptr,并将其传递到swig生成的指针类型(SWIGTYPE_p_X)的构造函数中。但是指针类型只存在于Java中,并且只保存swigCptr值。没有保存该指针的“c”内存块。因此,指针类型中没有与swigCPtr等价的类型。即指针类型中没有存储自指针,与基类型中存储自指针的方式相同

    因此,您不能将指针指向指针(SWIGTYPE_p_p_X),因为在构造指针时,您没有要传递的指针地址

    我的新“.i”文件如下所示:

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

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

    您说您正在调用
    nfc_init
    -您从哪里获得它需要调用的
    SWIGTYPE_p_nfc_context
    ?如果您只是用Java编写
    新SWIGTYPE\u p\u nfc\u context()
    ,它将生成一个不指向任何内容的指针,这可能会解释您的错误Java中的接口可能是用于
    nfc_init
    创建上下文并返回该上下文。从该接口开始的逻辑步骤是,所有函数将
    nfc_context
    作为它们的第一个参数,作为上下文上的方法,该上下文是随最初传入的参数一起返回的。SWIG可以为您生成该代码,如果它是你在干什么
    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);
      }
    }
    
    %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)
    
    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);