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

时间:2018-05-15 21:22:31

标签: java java-native-interface or-tools

我正在使用Google OR-tools库(v6.4)进行项目(虽然我的问题不是特定于此库)。这包含一个jar,它有一些本机依赖项(一堆" .so" /" .dylib"目标文件,具体取决于操作系统)。我的项目的这个版本正在Ubuntu 14.04上进行

我遇到的问题:在尝试在运行时加载特定的目标文件时(使用System.load()),我收到一条UnsatisfiedLinkError消息为"未定义的符号&# 34; (我在下面添加了堆栈跟踪)。但是,我正在加载定义此符号的目标文件,因此我不确定为什么会抛出此错误。

我以下列方式加载依赖项:在构建期间将对象文件打包到Maven创建的jar中,并在运行时提取和加载(使用System.load())。方法如下:

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

此方法在所有依赖项的静态块内调用:

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

在为libdimacs.so运行System.load()时,会抛出UnsatisfiedLinkError。堆栈跟踪:

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)

但是,此符号&#34; _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5 _&#34;存在于libortools.so中,它在libdimacs之前加载。我通过运行以下命令验证了这一点:

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

这给了我以下输出:

0000000000ce12cc gw    F .text       00000091 _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_

所以看起来符号应该在System.load()调用时定义,除非在加载包含对象文件时存在一些问题。要检查目标文件是否已正确加载,我使用了this solution中详述的方法。除了该答案中详述的课程外,我在System.load()调用EnvironmentUtils.loadResourceFromJar()后添加了以下行,以打印最近加载的库名称:

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

输出(直到UnsatisfiedLinkError之前)如下:

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

所以libortools.so似乎正确加载,这意味着符号应该加载到内存中。完全相同的代码与相应的Mac(&#34; .dylib&#34;)依赖项完美配合(基于MacOS Sierra 10.12.5构建)。非常感谢有关解决此问题的任何建议。谢谢。

1 个答案:

答案 0 :(得分:0)

我很抱歉当前可能会破坏java工件......

你可以使用c ++ filt来解码符号;)

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

事实上,gflag最近将其命名空间从google::更改为gflags::和glog或protobobuf?试着找到正确的一个,我猜它失败了...
注意:仍然不能完全确定使用google::命名空间的坏人是谁,因为libortools合并了所有静态依赖关系,但我想现在你理解了这个错误......

note2:我在mizux / shared branch https://github.com/google/or-tools/commit/805bc0600f4b5645114da704a0eb04a0b1058e28#diff-e8590fe6fb5044985c8bf8c9e73c0d88R114中有一个补丁 警告:此分支目前已损坏且尚未准备就绪。我正在尝试,对于unix,从静态到动态依赖,所以我需要修复所有rpath,transitives deps等...在这个过程中我还必须修复这个问题(我在使用时没有复制)静态依赖)

如果完成时间太长(我们应该在2018年5月底之前创建一个版本6.7.2或6.8(即新工件)),这可能只包含此修复而不是我的分支......