1

プロジェクトに Google OR-tools ライブラリ (v6.4) を使用しています (私の質問はこのライブラリに固有のものではありません)。これは、いくつかのネイティブな依存関係 (OS に応じて、".so"/".dylib" オブジェクト ファイルの束) を持つ 1 つの jar で構成されます。私のプロジェクトのこのビルドは、Ubuntu 14.04 で作成されています

私が直面している問題: 実行時に特定のオブジェクト ファイルをロードしようとすると (を使用してSystem.load())、「未定義のシンボル」というメッセージと共に UnsatisfiedLinkError が発生します (以下にスタック トレースを追加しました)。ただし、この直前にこのシンボルを定義するオブジェクト ファイルをロードしているため、このエラーがスローされる理由がわかりません。

次の方法で依存関係をロードしています。オブジェクト ファイルは、ビルド中に 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)

ただし、このシンボル「_ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_」は、libdimacs の前にロードされている libortools.so に存在します。次のコマンドを実行してこれを確認しました。

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

これにより、次の出力が得られました。

0000000000ce12cc gw    F .text       00000091 _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_

System.load()そのため、含まれているオブジェクト ファイルの読み込みに問題がなければ、呼び出し時にシンボルを定義する必要があるように思われます。オブジェクト ファイルが正しく読み込まれたかどうかを確認するために、このソリューションで詳しく説明されている方法を使用しました。その回答で詳述されているクラスとは別に、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 でビルド) と完全に連携しています。これを解決するためのアドバイスをいただければ幸いです。ありがとうございました。

4

1 に答える 1