プロジェクトに 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 でビルド) と完全に連携しています。これを解決するためのアドバイスをいただければ幸いです。ありがとうございました。