在Linux系统中如何调试Java的system.loadlibrary错误?

4
我有一个Java程序,通过JNI调用C代码,并尝试在Linux上运行。外部代码由两个`.so`文件组成:一个用于JNI绑定(使用swig构建),另一个包含实际功能的库。我将这两个库放在同一个目录下,并正确设置了`LD_LIBRARY_PATH`。在命令行中运行时,`ldd`没有报告任何问题,但是当我在Eclipse编辑器的“运行配置”对话框中设置相同值的`LD_LIBRARY_PATH`并尝试执行程序时,就会得到以下错误:

java.lang.UnsatisfiedLinkError:[库路径] / [JNI绑定库].so:[实际代码库].so:无法打开共享对象文件:没有那个文件或目录

这让我相信JNI包装库已成功加载,但当该库尝试加载包含实际代码的库时会失败。是否有任何方法可以进一步调试此问题?

我还要指出,这个问题发生在Eclipse编辑器本身中,而我还没有尝试将代码打包成一个jar并在单独的JVM实例中运行它。

7个回答

2
我认为问题出在对System.loadLibrary(String)的调用和使用LD_LIBRARY_PATH上。使用loadLibrary("foo")会在你的java.library.path中寻找名为libfoo.so的内容。如果没有找到名为libfoo.so的内容,就会出现这个错误。
现在,如果你只是设置了LD_LIBRARY_PATH,你想要的本地符号将会自动被链接器选取,因此你不需要设置-Djava.library.path。
根据我在gdal项目中使用swig的经验,这个错误实际上是无害的,并且由于LD_LIBRARY_PATH已经设置好了,所以这将工作得很好。
我建议使用-Djava.library.path并显式调用loadLibrary,原因是如果你决定使用webstart部署你的应用程序,你将需要显式调用loadLibrary来获取你的本地库。
当我使用eclipse时,我遵循Daff给出的说明,在Build Path的Libraries选项卡下编辑jar包中的本地库。再次提醒一下,这只是在幕后设置了java.library.path。

1

可能只是需要在运行配置对话框中找到正确的位置来放置 -Djava.library.path=... 选项。 我想你希望在参数选项卡上将 -D 定义放在“vm arguments”中,而如果您想定义 LD_LIBRARY_PATH,则将其放在环境选项卡上。无论如何,我以前就是这样使用库的,如果有机会我会查找我做了什么并在此编辑我的答案。

另一种尝试的方法是玩一下 LD_DEBUG。 您可以将环境变量 LD_DEBUG 设为各种值(尝试 ALL),然后 Linux 加载器将披露有关应用程序正在尝试加载的内容、位置等所有有用信息。当然,这预设您从命令行启动 Eclipse,因此您可以设置环境变量并查看加载器诊断;但是对于系统而言,在 Eclipse 内运行应用程序时,您的应用程序只是 Eclipse 执行的某些操作,因此可以通过这种方式查看任何库加载行为。


0

你的两个库是否依赖于其他库?如果是,你需要确保它们对JVM也是可访问的。

请注意,手动设置“-Djava.library.path”似乎会擦除默认的库路径。

因此,使用以下代码:

public class LibTest {
    public static void main(String[] args) {
        String property = System.getProperty("java.library.path");
        StringTokenizer parser = new StringTokenizer(property, ":");
        while (parser.hasMoreTokens()) {
            System.err.println(parser.nextToken());
        }
    }
}

从Java 1.6.0_14在Eclipse中启动输出:

/opt/java/jre/lib/i386/client
/opt/java/jre/lib/i386
/opt/java/jre/../lib/i386
/opt/java/jre/lib/i386/client
/opt/java/jre/lib/i386
/usr/lib/xulrunner-devel-1.9.0.11
/usr/lib/xulrunner-devel-1.9.0.11
/usr/java/packages/lib/i386
/lib
/usr/lib

但是当我设置JVM参数“-Djava.library.path=/tmp/”时,我只得到:
/tmp/

如果您手动设置了java.library.path,这可能解释了为什么ldd从命令行工作,但您的.so文件在eclipse/java中无法工作。

您可以尝试不设置java.library.path,并使用绝对路径调用System.load来加载库,而不是调用System.loadLibrary。这样可以让JVM找到您的.so文件,并在搜索其依赖项时仍使用默认路径。

当然,如果这没有用,您还可以尝试在命令行上使用“-verbose:jni”打开jni调试输出。这可能会给您一些解决问题的线索。


0

是的,LD_LIBRARY_PATH 对我有效


0

你可以尝试在命令行参数中加入-Djava.library.path=actual.so

在Windows上,我曾经遇到过类似的问题,当时使用了一个JNI包装DLL来调用第三方库的DLL。我的项目将DLL文件放在了lib目录下,所以我将lib添加到了PATH环境变量中(例如:PATH=%PATH%;./lib),然后一切都开始正常工作了。


0
据我所知,Eclipse不使用LD_LIBRARY_PATH。设置正确的本地库路径最简单的方法是进入项目属性->Java Build Path->Libraries,然后展开JRE System Library条目或(如果可用)使用本地库的Jar文件,选择“Native Library Location”,然后单击“编辑...”,选择您的库所在的文件夹。实际上,它确实设置了-Djava.library.path变量,因此如果您从Eclipse外部启动程序,则必须在命令行中包含此变量。

0

添加这个答案可能会有用。在AIX机器上,我们需要设置LIBPATH环境变量而不是LD_LIBRARY_PATH。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接