对于上下文:我有一个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:
foo.hpp
class foo { }; extern "C" const void* libfoo_foo_typeinfo();
foo.cpp
#include "foo.hpp" #include <typeinfo> extern "C" const void* libfoo_foo_typeinfo() { return &typeid(foo); }
bar.cpp
#include "foo.hpp" #include <typeinfo> extern "C" const void* libbar_foo_typeinfo() { return &typeid(foo); }
main.cpp
#include <iostream> #include <typeinfo> #include <dlfcn.h> int main() { void* libfoo = dlopen("./libfoo.so", RTLD_NOW | RTLD_LOCAL); void* libbar = dlopen("./libbar.so", RTLD_NOW | RTLD_LOCAL); auto libfoo_fn = reinterpret_cast<const void* (*)()>( dlsym(libfoo, "libfoo_foo_typeinfo")); auto libbar_fn = reinterpret_cast<const void* (*)()>( dlsym(libbar, "libbar_foo_typeinfo")); auto libfoo_ti = static_cast<const std::type_info*>(libfoo_fn()); auto libbar_ti = static_cast<const std::type_info*>(libbar_fn()); std::cout << std::boolalpha << (libfoo_ti == libbar_ti) << "\n" << (*libfoo_ti == *libbar_ti) << "\n"; return 0; }
Makefile
all: libfoo.so libbar.so main libfoo.so: foo.cpp $(CXX) -fpic -shared -Wl,-soname=$@ $^ -o $@ libbar.so: bar.cpp $(CXX) -fpic -shared -Wl,-soname=$@ $^ -L. -lfoo -o $@ main: main.cpp $(CXX) $^ -ldl -o $@
在我的系统上,我得到
$ 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的。
答案 0 :(得分:2)
否,那是不可能的。 RTLD_LOCAL
试图防止这种情况的发生,不幸的是,System.loadLibrary
必须使用System.loadLibrary
,因为否则,如果您foo
两个都定义了不同的group by
类的库,则会发生不好的事情。>