我正在使用C ++ dlopen()在我的主程序(目录B)中链接一个名为lib * .so的共享库(在目录A中)。
我试验了一些简单的函数加载。每件事都很有效。但是,当我尝试加载返回指向类对象的指针的类和工厂函数时,它让我头疼。 (我正在使用以下教程中的术语)
我使用的方法基于本教程https://www.tldp.org/HOWTO/C++-dlopen/thesolution.html#externC的第3.3章中的示例。
这里有一些多态... lib * .so包含一个从主程序目录(目录B)继承父抽象类的子类。当dlopen()尝试在主程序中加载lib * .so时,由于“未定义的符号”而失败。
我使用nm命令检查lib * .so和主程序二进制文件中的符号表。这些二进制文件中的符号是:
lib * .so:U _ZTI7ParentBox 主程序二进制:V _ZTI7ParentBox
ParentBox是lib * .so中ChildBox继承的父类的名称。请注意,父类头文件位于目录B中的另一个项目中。
虽然有名称错误,但符号名称完全相同。我只是想知道为什么动态链接器无法链接它们?并给我dlopen()的不完美的符号错误?
我在这里错过了对一些关键概念的理解吗?
附:更奇怪的是,它能够解析lib * .so(T类型符号)中的子类(U类型符号)和父类之间的成员函数的符号。为什么它能够执行此操作但无法解析父类名称的未定义符号?
(我一直在寻找很长一段时间,并试过-rdynamic,-ldl的东西虽然我不完全明白它们是什么,但没有任何效果)
2019年4月更新:这是我用来制作主程序二进制文件的g ++命令行。
g++ -fvisibility=hidden -pthread -static-libgcc -static-libstdc++ \
-m64 -fpic -ggdb3 -fno-var-tracking-assignments -std=c++14 \
-rdynamic \
-o ./build/main-prog \
/some_absolute_path/ParentBox.o \
/some_other_pathen/Triangle.o \
/some_other_pathen/Circle.o \
/some_other_pathen/<lots_of_depending_obj> \
/some_absolute_path/librandom.a \
-lz -ldl -lrt -lbz2
我在https://gcc.gnu.org/onlinedocs/gcc/Option-Index.html搜索了这个命令行的每个参数(对于所有使用复杂g ++行的大型项目的程序员来说,这似乎是一个很好的参考站点:))
感谢@Employed Russian。根据他的指示,问题缩小到导出主程序二进制中的符号。
但是,主程序二进制文件有很多依赖项,您可以从上面的命令,Circle,Triangle和许多其他目标文件中看到。我们还需要在Circle,Triangle和其他依赖对象文件的编译中添加“-rdynamic”。否则它不起作用。
就我而言,我将“-rdynamic”添加到项目中的所有文件以导出所有符号。不确定“-fvisibility = hidden”做了什么好事。无论如何我在Makefile中删除了所有这些...我知道这不是最好的方法,但是当一切都在功能上正确时我会担心速度。 :)
更多更新:正确的解决方案是在@Employed Russian的答案中更新。我以前的解决方案碰巧工作,因为我也删除了“-fvisibility = hidden”。将-rdynamic添加到最终链接中使用的所有对象不是必需的(也可能是错误的)。请参阅@Employed Russian解释核心问题的解释。
最后更新:对于对C / C ++程序如何执行以及如何链接库感兴趣的同行程序员来说,这是Xeno Kovah的一个很好的参考网络课程(Life of Binary):http://opensecuritytraining.info/LifeOfBinaries.html
您还可以在youtube上找到播放列表。只需搜索“二进制生活”
虽然有名称错误,但符号名称完全相同。我只是想知道为什么动态链接器无法链接它们?
最可能的解释是:符号不是从主二进制文件导出的。
用nm -D
重复你的命令:
nm -AD lib*.so main-prog | grep ' _ZTI7ParentBox$'
机会是,你会看到lib*.so: U _ZTI7ParentBox
和main-prog
什么也没有。
发生这种情况是因为通常链接器不会从main-prog
导出任何符号,参与链接的某个共享库没有引用(并且你的lib*.so
没有与main-prog
链接,否则你不需要dlopen
它)。
要更改该行为,可以在链接-Wl,--export-dynamic
时添加main-prog
链接器标志。这指示链接器导出链接到main-prog
的所有内容。
试过-rdynamic
这相当于-Wl,--export-dynamic
,应该有效(假设你将它添加到main-prog
链接行,而不是其他地方)。
更新:
现在一切正常!由于main-prog还依赖于其他一些对象,看起来简单地将-dynamic添加到最终的main-prog链接并不能解决问题。我们需要在这些依赖对象的编译中添加“-rdynamic”。
这是错误的解决方案。你的问题是-fvisibility=hidden
告诉编译器将所有进入main-prog
的符号标记为未导出,并且-rdynamic
不会导出任何隐藏的符号。
正确的解决方案是从定义要导出的符号的任何对象中删除-fvisibility=hidden
,并将-rdynamic
添加到最终链接。