对于上下文:我有一个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,则会发生不好的事情。

10-02 06:20