对于上下文:我有一个Java项目,该项目部分由两个JNI库实现。例如,libbar.so
取决于libfoo.so
。如果这些是系统库,
System.loadLibrary("bar");
会成功的但由于它们是自定义库,因此我将随JAR一起提供,因此我必须做类似
System.load("/path/to/libfoo.so");
System.load("/path/to/libbar.so");
libfoo需要先走,因为否则
libbar
找不到它,因为它不在系统库搜索路径中。这已经运行了一段时间了,但是我现在遇到了一个问题,尽管类型正确,但
std::any_cast
会抛出std::bad_any_cast
。我将其归结为以下事实:两个库对该类型的typeinfo都有不同的定义,并且它们在运行时没有合并。这似乎是因为System.load()
最终使用dlopen()
而不是RTLD_LOCAL
调用了RTLD_GLOBAL
。我编写此代码是为了演示该行为而无需JNI:
在我的系统上,我得到
$ make
...
$ ./main
false
true
这是因为即使typeinfo地址不同,GCC的libstdc++也会使用乱码来实现相等。例如,在LLVM的libc++上,相等性基于typeinfo地址本身,因此我得到:
$ make CXX="clang++ -stdlib=libc++"
$ ./main
false
false
如果我改为通过
RTLD_GLOBAL
,我会看到true
true
如果我编辑
main.cpp
以首先加载libbar.so
,它也可以工作,只要我告诉它可以在哪里找到libfoo.so
即可:$ LD_LIBRARY_PATH=. ./main
true
true
但是由于本文开头所述的原因,这些都不是可行的解决方法。
这与https://github.com/android-ndk/ndk/issues/533非常相似,但具有非动态类型,因此无法添加“键函数”以强制typeinfo成为强符号。我碰巧首先在Android上重现了该问题,但它不是特定于Android的。
最佳答案
不,那是不可能的。 RTLD_LOCAL
试图防止这种情况的发生,不幸的是,必须将System.loadLibrary
用于System.loadLibrary
,否则,如果您对两个分别定义了不同foo
类的库进行ojit_code,则会发生不好的事情。