使用SWIG制作Java共享库时发生SIGSEGV错误 [英] SIGSEGV error using SWIG to make a java shared library

查看:79
本文介绍了使用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 <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>

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

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

很显然,根据我提供的信息,可能无法使用特定的解决方案.但是,对任何尝试的建议都将不胜感激(在这里,我的知识还算够用完了.)

解决方案

要始终将相同的指针自动传递给函数,在SWIG中非常简单.例如,给定头"文件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_global_ctx().

这足以使Java开发人员能够使用一个明智的接口,但是它并不理想:它强制上下文成为全局上下文,并且意味着没有Java应用程序可以一次使用多个上下文. /p>

鉴于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
 

在动态类型化的语言中,这是很难完成的部分-我们可以根据需要使用一种形式或另一种形式的元编程来插入成员函数.因此,我们可以完全按预期说出new context().foo();之类的内容.尽管Java是静态类型的,所以我们还需要更多.我们可以通过以下几种方式在SWIG中进行此操作:

  1. 我们现在可以很高兴地调用test.foo(new context());了-尽管它看起来仍然很像Java中的C,所以如果您最终编写了很多看起来像Java的Java,我建议这可能是一种代码味道C.

  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一样,但是使用类型映射在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 <cpointer.i>

%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宏,它比该宏多一些,也许值得使用.但是,重点是您要提供与SWIG用于表示其不了解的指针的不透明指针对象一起使用的工具,但要避免getCPtr()调用固有地颠覆类型系统.

So, I'm trying to port a C library (libnfc) to Java using SWIG.

I've got to the point of having a compiled shared library, and a basic "nfc_version()" method call will work. However, calling "nfc_init()" to set things up causes a SIGSEGV error. Calling the nfc library directly is fine.

The commands I used to generate 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

The 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>

I.e. it should include all the methods that libnfc provides.

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

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

解决方案

To always pass the same pointer in to a functions automatically it's fairly straightforward in SWIG. For example given the "header" file test.h, which captures the core 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 and automatically cause a global context to be passed in everywhere one 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 a typemap for struct context *ctx that instead of taking an input from Java automatically calls get_global_ctx() everywhere it matches.

That's probably sufficient to make a sane-ish interface for a Java developer to use, however it's less than ideal: it forces the context to be a global and means that no Java application can ever work with multiple contexts at once.

A nicer solution, given that Java is an OO language, is to make the context become a first class Object. We can also make SWIG generate such an interface for us although it's a little more convoluted. 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 exercise this code successfully:

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 that would be the hard part done - we could use meta programming of one form or another to insert a the member functions as needed. Thus we would be able to say something like new context().foo(); entirely as expected. Java is statically typed though so we need something more. We can do this in SWIG in a number of ways:

  1. Accept that we can now call test.foo(new context()); quite happily - it looks a lot like C in Java still though so I'd suggest it might be a code smell if you end up writing lots of Java that looks like C.

  2. Use %extend to (manually) add the methods into the context class, the %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);
        }
      }
    

  3. As with %extend, but write the glue on the Java side, using a typemap:

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

    (Note: this needs to be early enough in the interface file to work)

Notice that nowhere here have I shown SWIG the real definition of my context struct - it always defers to my "library" for anything where the real definition is required, thus the opaque pointer remains complete opaque.


A simpler solution to wrap the init_context with a double pointer would be to use %inline to provide an extra 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"

Is sufficient to allow us to 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 would include using the cpointer.i library:

%module test

%{
#include "test.h"
%}

%include <cpointer.i>

%pointer_functions(struct context *,context_ptr);

%include "test.h"

Which you can then use as:

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's also a pointer_class macro that is a little more OO than that and might be worth using instead. The point though is that you're providing the tools to work with the opaque pointer objects that SWIG uses to represent pointers it knows nothing about, but avoiding the getCPtr() calls which are inherently subverting the type system.

这篇关于使用SWIG制作Java共享库时发生SIGSEGV错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆