java - 即使在成功加载所需的目标文件后,在加载 JNI 依赖项时也会在 Java 中获取 UnsatisfiedLinkError( undefined symbol )

标签 java java-native-interface or-tools

我正在为一个项目使用 Google OR-tools 库 (v6.4)(尽管我的问题并非特定于该库)。它由一个 jar 组成,它具有一些 native 依赖项(一堆“.so”/“.dylib”对象文件,具体取决于操作系统)。我的项目的这个构建是在 Ubuntu 14.04 上进行的

我面临的问题:在尝试在运行时加载特定的对象文件时(使用 System.load() ),我收到 UnsatisfiedLinkError ,消息为“ undefined symbol ”(我在下面添加了堆栈跟踪)。但是,我正在加载在此之前定义此符号的目标文件,因此我不确定为什么会引发此错误。

我通过以下方式加载依赖项:目标文件在构建期间被打包到 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
        }
    }
}

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

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

运行时System.load()对于 libdimacs.so,会抛出 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)

但是,libortools.so 中存在此符号“_ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_”,该符号在 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 中详细介绍的方法。 。除了该答案中详细介绍的类(class)之外,我还在 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(“.dylib”)依赖项(在 MacOS Sierra 10.12.5 上构建)完美配合。对于解决此问题的任何建议,我们将不胜感激。谢谢。

最佳答案

很抱歉,java 工件目前可能已损坏......

您可以使用 c++filt 来分解符号;)

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

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

note2:我在 mizux/shared 分支 https://github.com/google/or-tools/commit/805bc0600f4b5645114da704a0eb04a0b1058e28#diff-e8590fe6fb5044985c8bf8c9e73c0d88R114 有一个补丁
警告:该分支目前已损坏且尚未准备好。我正在尝试,对于unix,从静态依赖项转移到动态依赖项,所以我需要修复所有rpath、传递依赖项等...并且在这个过程中我还必须解决这个问题(在使用静态依赖项时我没有重现)

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

关于java - 即使在成功加载所需的目标文件后,在加载 JNI 依赖项时也会在 Java 中获取 UnsatisfiedLinkError( undefined symbol ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50359341/

相关文章:

java - 来自 C++ 的 JNI Java;打印输出?

android - Android Studio OpenCV 3.2,无法使用 native OpenCV 打开视频文件或安卓相机

python - 如何使用Python中的or-tools解决累积旅行商问题?

java - 如何使Java ServiceActivator在JUnit Test中可见?

Java 无法在命令行上运行

java - JNI - 为 Windows 打包 64 位和 32 位 DLL

python - 如何在 Google OR-Tools 中设置每条路线的最小位置?

python-3.x - 如何定义TSP而不返回仓库?

Java Axis 故障字符串

Java Mail - 电子邮件不通过 Gmail 发送