私は、Javaコードにクライアントによって書かれた(かなり大きい)のネイティブライブラリをリンクしようとしています。私は試みがライブラリをロードし、自明ライブラリからネイティブメソッドを呼び出すようにすることを、この簡略化されたテストクラスを書きました。私はまた、いくつかのデバッグコードを追加しました。
public class JniVtaTest {
static {
try {
Process exec = Runtime.getRuntime().exec("ldd /usr/java/packages/lib/libvtajni.so");
byte[] bytes = exec.getErrorStream().readAllBytes();
System.out.println(new String(bytes));
bytes = exec.getInputStream().readAllBytes();
System.out.println(new String(bytes));
} catch (IOException e) {
e.printStackTrace();
}
String property = System.getProperty("java.library.path");
System.out.println(property);
// above code generates the debugging outpout shown below
System.loadLibrary("vtajni");
}
// running with options:
// -Djava.library.path="/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib:/usr/lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu" -verbose:jni -Xcheck:jni
public static void main(String[] args) {
new JniVtaTest().vtaAnalyze(args[0]);
}
private native String vtaAnalyze(String str);
}
私が指摘したオプションを持つ上で実行すると、私は、JVMのクラスの動的リンクについてJVM出力の多くを取得してから、この:
linux-vdso.so.1 (0x00007ffe2239d000)
libiodbc.so.2 => /usr/lib/x86_64-linux-gnu/libiodbc.so.2 (0x00007ff6093b1000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff609028000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff608c8a000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff608a72000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff608681000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff60847d000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff62713d000)
11.0.5
/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib:/usr/lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu
Exception in thread "main" java.lang.UnsatisfiedLinkError: 'java.lang.String com.customer.jni.JniVtaTest.vtaAnalyze(java.lang.String)'
at com.customer.jni.JniVtaTest.vtaAnalyze(Native Method)
at com.customer.jni.JniVtaTest.main(JniVtaTest.java:26)
ライブラリによってロードされるライブラリのすべてが存在するように見えます。
gus@ns-l1:/usr/java/packages$ ls -al /usr/lib/x86_64-linux-gnu/libiodbc.so.2
lrwxrwxrwx 1 root root 18 Dec 12 2017 /usr/lib/x86_64-linux-gnu/libiodbc.so.2 -> libiodbc.so.2.1.20
gus@ns-l1:/usr/java/packages$ ls -al /usr/lib/x86_64-linux-gnu/libstdc++.so.6
lrwxrwxrwx 1 root root 19 Dec 4 09:45 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 -> libstdc++.so.6.0.25
gus@ns-l1:/usr/java/packages$ ls -al /lib/x86_64-linux-gnu/libm.so.6
lrwxrwxrwx 1 root root 12 Apr 16 2018 /lib/x86_64-linux-gnu/libm.so.6 -> libm-2.27.so
gus@ns-l1:/usr/java/packages$ ls -al /lib/x86_64-linux-gnu/libgcc_s.so.1
-rw-r--r-- 1 root root 96616 Dec 4 09:45 /lib/x86_64-linux-gnu/libgcc_s.so.1
gus@ns-l1:/usr/java/packages$ ls -al /lib/x86_64-linux-gnu/libc.so.6
lrwxrwxrwx 1 root root 12 Apr 16 2018 /lib/x86_64-linux-gnu/libc.so.6 -> libc-2.27.so
gus@ns-l1:/usr/java/packages$ ls -al /lib/x86_64-linux-gnu/libdl.so.2
lrwxrwxrwx 1 root root 13 Apr 16 2018 /lib/x86_64-linux-gnu/libdl.so.2 -> libdl-2.27.so
gus@ns-l1:/usr/java/packages$ ls -al /lib64/ld-linux-x86-64.so.2
lrwxrwxrwx 1 root root 32 Apr 16 2018 /lib64/ld-linux-x86-64.so.2 -> /lib/x86_64-linux-gnu/ld-2.27.so
gus@ns-l1:/usr/java/packages$
彼らはまた、すべてを経由して検索可能ですldconfig -v -N
し、私はそれはldconfigがletersを持つ任意のライブラリが見つかりません確認vta
私は偶然の名前の重複から安全だと思うので、このようなことでは:
ldconfig -v -N 2>&1 | grep vta
(no output shown)
顧客のライブラリ自体は明らかに私はそれがロードライブラリに死なないデバッグするとき、デバッガはメソッドを呼び出す行に停止しますので、ロードされ、それが名前のライブラリーを発見したことを、このJVMコードのショーに降圧され/usr/java/packages/lib/libvtajni.so
、それが入っていることループのための最初のを探してJava_com_customer_jni_JniVtaTest_vtaAnalyze
探して再びとJava_com_customer_jni_JniVtaTest_vtaAnalyze__Ljava_lang_String_2
(それらのそれぞれのために二度チェックしているようです)
private static long findNative(ClassLoader loader, String entryName) {
Map<String, NativeLibrary> libs =
loader != null ? loader.nativeLibraries() : systemNativeLibraries();
if (libs.isEmpty())
return 0;
// the native libraries map may be updated in another thread
// when a native library is being loaded. No symbol will be
// searched from it yet.
for (NativeLibrary lib : libs.values()) {
long entry = lib.findEntry(entryName); <<<<< STOP DEBUGGER HERE
if (entry != 0) return entry;
}
return 0;
}
で生成されたヘッダファイルをjavah com.customer.jni.JniVtaTest
次のようになります。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_customer_jni_JniVtaTest */
#ifndef _Included_com_customer_jni_JniVtaTest
#define _Included_com_customer_jni_JniVtaTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_customer_jni_JniVtaTest
* Method: vtaAnalyze
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_customer_jni_JniVtaTest_vtaAnalyze
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
CPPファイル内のメソッドは次のようになります。
JNIEXPORT jstring JNICALL Java_com_customer_jni_JniVtaTest_vtaAnalyze
(JNIEnv * env, jobject obj, jstring jInputText) {
cout << "foo";
// customer code...
return env->NewStringUTF(obuf);
}
そして、私は見たことがないfoo
私は、それは方法を見つけ、その後、メソッド内で失敗しているとは考えていないので、プリントアウトします。
全JDK&システム(Ubuntuの18.04)情報:
openjdk 11.0.5 2019-10-15 LTS
OpenJDK Runtime Environment Zulu11.35+15-CA (build 11.0.5+10-LTS)
OpenJDK 64-Bit Server VM Zulu11.35+15-CA (build 11.0.5+10-LTS, mixed mode)
Linux ns-l1 4.15.0-88-generic #88-Ubuntu SMP Tue Feb 11 20:11:34 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
以下のようなページ検索や読書の時間https://developer.android.com/training/articles/perf-jni#faq:-why-do-i-get-unsatisfiedlinkerror-でとスペックをhttps://docs.oracle .COM / EN / javaの/のJava SE / 11 /ドキュメント/スペック/ JNI / design.html#解決するネイティブ・メソッドの名前は、私の頭を悩ま私を残しています。
私の質問:なぜ私はこのエラーを取得していますか?私は何を逃しました。
編集:コメント欄で質問ごとに、私はとのシンボルを見つけられませんnm -D
が、私はで何かを見つけるのですかnm -A
...今私はそれがある理由を把握する必要があります。
gus@ns-l1:~/clients/customer/code/vta_jni$ nm -A /usr/java/packages/lib/libvtajni.so | grep com_
/usr/java/packages/lib/libvtajni.so:00000000112a3d38 t _GLOBAL__sub_I__Z44Java_com_customer_jni_JniVtaTest_vtaAnalyzeP7JNIEnv_P8_jobjectP8_jstring
/usr/java/packages/lib/libvtajni.so:00000000112a388a T _Z44Java_com_customer_jni_JniVtaTest_vtaAnalyzeP7JNIEnv_P8_jobjectP8_jstring
編集2:ヘッダファイルを経由して含まれています...
#include "com_customer_jni_JniVtaTest.h"
編集3:使用にcmakeのファイルを変更した後add_library(vtajni SHARED ...
(ととクライアントコードを再コンパイル-fPIC
)私は今、これを取得します:
gus@ns-l1:~/clients/customer/code/vta_jni$ nm -D /usr/java/packages/lib/libvtajni.so | grep com_
00000000113587ba T _Z44Java_com_customer_jni_JniVtaTest_vtaAnalyzeP7JNIEnv_P8_jobjectP8_jstring
しかし名前はまだ以下のコメントで述べたようにexternと第二の問題を示唆し、マングルされているが、ヘッダは、上記のように含まれています。
解決済み:マングリングは、私は今、「成功した」原因となっているCプロジェクト(私はそれを持っていた、まだ上記の貼り付けられたことをJavaプロジェクトの元に生成されたファイル)のヘッダファイルに対するextern一部をコメントアウト忘れデバッグ編集によるものでしたそれは印刷します
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
仕事への機能のために、正確な名前がで資本Tに現れなければなりませんnm -D yourfile.so
。そうでない場合は、理由を見つける必要があります。
ここでは一般的な問題を示す三つの機能を持つファイルの例です:
extern "C" {
void correct();
extern void notInThisSo();
}
void correct() { }
void missingJniHeader() {}
static void* dummyUsage = (void*) ¬InThisSo;
ここにありますnm
(先行ゼロを剥離)出力は:
$ gcc foo.cc -shared -o foo.so && nm -D foo.so
000010f5 T correct # This works
w __cxa_finalize
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U notInThisSo # This name is believed to be in another .so
000010fc T _Z16missingJniHeaderv # This name is C++ mangled: missing extern "C" from header
中まで自分の名前が示すように何もない場合はnm -D
、確認してくださいnm -A yourfile.so
:
$ cat bar.cc
extern "C" {
__attribute((visibility("default"))) void visible() {}
void not_visible() { }
}
$ gcc -fvisibility=hidden bar.cc -shared -o bar.so && nm -A bar.so
[...]
bar.so:000010fc t not_visible
bar.so:000010f5 T visible
ここにあなたが見ることができるnot_visible
小文字を持つt
ビルドが使用されているため、-fvisibility=hidden
シンボルを隠すために、そして何も明示的にホワイトリストに登録しません。JNIは、隠された記号にアクセスすることはできません。
(場合nm -A
与えnm: bar.so: no symbols
、それはライブラリを剥離されることを意味します。あなたはまだ使用することができnm -D
剥奪ライブラリに)。