简体   繁体   English

即使成功加载所需的目标文件后,在加载JNI依赖项时,也会在Java中获取UnsatisfiedLinkError(未定义符号)

[英]Getting an UnsatisfiedLinkError (undefined symbol) in Java while loading JNI dependencies even after successfully loading the required object file

I'm using Google OR-tools library (v6.4) for a project (though my question is not specific to this library). 我正在将Google OR工具库(v6.4)用于项目(尽管我的问题并不特定于该库)。 This consists of one jar, which has a few native dependencies (a bunch of ".so"/".dylib" object files, depending on the OS). 它由一个jar组成,该jar具有一些本地依赖项(一堆“ .so” /“。dylib”对象文件,具体取决于操作系统)。 This build for my project is being made on Ubuntu 14.04 我的项目的构建是在Ubuntu 14.04上进行的

The problem I'm facing: On trying to load a specific object file at runtime (using System.load() ), I'm getting an UnsatisfiedLinkError with the message as "undefined symbol" (I've added the stacktrace below). 我面临的问题:在尝试在运行时加载特定的目标文件时(使用System.load() ),我收到了UnsatisfiedLinkError消息,消息为“未定义符号”(我在下面添加了堆栈跟踪)。 However, I am loading the object file defining this symbol just before this, so I'm not sure why this error is being thrown. 但是,我正要在此之前加载定义此符号的目标文件,所以我不确定为什么会引发此错误。

I'm loading the dependencies in the following way: The object files are being packed into the jar created by Maven during build, and are being extracted and loaded (using System.load() ) at runtime. 我通过以下方式加载依赖项:在构建过程中,将目标文件打包到Maven创建的jar中,并在运行时将其提取并加载(使用System.load() )。 The method for that is as follows: 该方法如下:

public class EnvironmentUtils {

    public static void loadResourceFromJar(String prefix, String suffix) {
        String tempFilesDirectory = System.getProperty("java.io.tmpdir");
        File tempFile = null;
        try {
            tempFile = new File(tempFilesDirectory + "/" + prefix + suffix);
            tempFile.deleteOnExit();
            try (final InputStream inputStream = EnvironmentUtils.class.getClassLoader().
                    getResourceAsStream(prefix+suffix)) {
                if (inputStream == null) {
                    throw new RuntimeException(prefix + suffix + " was not found inside JAR.");
                } else {
                    Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                }
            }
            System.load(tempFile.getAbsolutePath());
        } catch (Exception e) {
            //Log top 10 lines of stack trace
        }
    }
}

This method is being called inside a static block for all dependencies: 在所有依赖项的静态块内调用此方法:

public class DummyClass {
    static {
        String sharedLibraryExtension = EnvironmentUtils.getSharedLibraryExtension(); //.so for linux, .dylib for Mac
        String jniLibraryExtension = EnvironmentUtils.getJniLibraryExtension(); //.so for linux, .jnilib for Mac
        EnvironmentUtils.loadResourceFromJar("libfap", sharedLibraryExtension);
        EnvironmentUtils.loadResourceFromJar("libcvrptw_lib", sharedLibraryExtension);
        EnvironmentUtils.loadResourceFromJar("libortools", sharedLibraryExtension);
        EnvironmentUtils.loadResourceFromJar("libdimacs", sharedLibraryExtension);
        EnvironmentUtils.loadResourceFromJar("libjniortools", jniLibraryExtension);
    }
}

On running System.load() for libdimacs.so, an UnsatisfiedLinkError is thrown. 在为libdimacs.so运行System.load() ,引发UnsatisfiedLinkError。 Stacktrace: 堆栈跟踪:

java.lang.UnsatisfiedLinkError: /tmp/libdimacs.so: /tmp/libdimacs.so: undefined symbol: _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_
    at java.lang.ClassLoader$NativeLibrary.load(Native Method)
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
    at java.lang.Runtime.load0(Runtime.java:809)
    at java.lang.System.load(System.java:1086)
    at com.(PROJECT_NAME).utils.EnvironmentUtils.loadResourceFromJar(EnvironmentUtils.java:78)
    at com.(PROJECT_NAME).DummyClass.<clinit>(DummyClass.java:28)

However, this symbol "_ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_" is present in libortools.so, which is being loaded before libdimacs. 但是,libortools.so中存在此符号“ _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_”,该符号已在libdimacs之前加载。 I verified this by running the following command: 我通过运行以下命令验证了这一点:

objdump -t (LIBRARY_PATH)/libortools.so | grep _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_

This gave me the following output: 这给了我以下输出:

0000000000ce12cc gw    F .text       00000091 _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_

So it would seem that the symbol should have been defined at the time of the System.load() call, unless there was some issue in loading the containing object file. 因此,似乎该符号应该在System.load()调用时已定义,除非加载包含的目标文件时出现了一些问题。 To check if the object file had been loaded correctly, I used the approach detailed in this solution . 为了检查目标文件是否已正确加载,我使用了此解决方案中详细介绍的方法 Apart from the class detailed in that answer, I added the following lines after System.load() call in EnvironmentUtils.loadResourceFromJar() to print the most recently loaded library name: 除了该答案中详细介绍的类之外,我在EnvironmentUtils.loadResourceFromJar() System.load()调用之后添加了以下几行,以打印最近加载的库名:

public class EnvironmentUtils {

    public static void loadResourceFromJar(String prefix, String suffix) {
        ...
        System.load(tempFile.getAbsolutePath());
        final String[] libraries = ClassScope.getLoadedLibraries(ClassLoader.getSystemClassLoader());
        System.out.println(libraries[libraries.length - 1]);
    }
}

The output (till just before the UnsatisfiedLinkError) is as follows: 输出(直到UnsatisfiedLinkError之前)如下:

/tmp/libfap.so
/tmp/libcvrptw_lib.so
/tmp/libortools.so

So libortools.so seems to be loading correctly, which means the symbol should be loaded in memory. 因此libortools.so似乎正确加载,这意味着该符号应加载到内存中。 The exact same code is working perfectly with the corresponding Mac (".dylib") dependencies (Built on MacOS Sierra 10.12.5). 完全相同的代码可以与相应的Mac(.dylib)依赖项完美配合(在MacOS Sierra 10.12.5上内置)。 Would appreciate any advice on resolving this. 希望能就解决此问题提供任何建议。 Thank you. 谢谢。

I'm apologize that the java artifact may be broken currently... 我很抱歉Java工件目前可能已损坏...

you can use c++filt to demangle the symbol ;) 您可以使用c ++ filt对符号进行分解;)

c++filt _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_
google::FlagRegisterer::FlagRegisterer<bool>(char const*, char const*, char const*, bool*, bool*)

In fact gflag has recently change its namespace from google:: to gflags:: and glog or protobobuf? 实际上,gflag最近已将其名称空间从google::更改为gflags::以及glog或protobobuf? try to find the correct one and I guess it failed... 尝试找到正确的一个,我想它失败了...
note: Still not completely sure whose is the bad guy who use the google:: namespace since libortools merge all its static dependencies but I guess now you understand the bug... 注意:由于libortools合并了其所有静态依赖项,因此仍不能完全确定使用google::名称空间的是谁是坏人,但是我想您现在已经了解了该错误...

note2: I have a patch in mizux/shared branch https://github.com/google/or-tools/commit/805bc0600f4b5645114da704a0eb04a0b1058e28#diff-e8590fe6fb5044985c8bf8c9e73c0d88R114 note2:我在mizux / shared分支中有一个补丁https://github.com/google/or-tools/commit/805bc0600f4b5645114da704a0eb04a0b1058e28#diff-e8590fe6fb5044985c8bf8c9e73c0d88R114
warning : this branch is currently broken and not ready yet. 警告 :此分支当前已损坏,尚未准备就绪。 I'm trying ,for unix, to move from static to dynamic dependencies, so I need to fix all rpath, transitives deps etc... and in the process I also had to fix this issue (that I didn't reproduced while using static dependencies) 我正在尝试将unix从静态依赖关系转换为动态依赖关系,因此我需要修复所有rpath,可传递对象deps等。在此过程中,我还必须解决此问题(在使用时未复制)静态依赖项)

If too long to finish (we should create a release 6.7.2 or 6.8 (ie new artifact) by the end of May 2018) which maybe only contains this fix and not my branch... 如果完成时间太长(我们应该在2018年5月底之前创建6.7.2或6.8版本(即新工件)),则可能仅包含此修复程序而不包含我的分支...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM