加载依赖主程序未引用符号的插件共享库时遇未定义符号问题求助
插件系统动态加载共享库时的符号未定义问题
问题背景
我基于共享库设计了一套插件系统,流程如下:
- 提供带虚函数的
Plugin基类模板,其头文件包含插件所需的所有依赖 - 插件开发者仅需基于该系统依赖重写虚函数实现功能
- 主可执行文件通过包含
Plugin基类头文件继承依赖,以循环运行,通过dlopen动态加载共享库,由单例PluginManager每帧调用插件函数
示例代码
plugin.h
// plugin.h #include "third_party/test.pb.h" // 主程序未使用,但某插件会用到 #include "basic/math/math_util.h" // 主程序会使用 class Plugin { public: Plugin() = default; virtual ~Plugin() {} virtual void Execute() {} };
do_something.h
// do_something.h #include "plugin.h" class DoSomething : public Plugin { public: DoSomething() : Plugin() {} ~DoSomething() {} virtual void Execute() override; // 会使用test.pb.h和math_util.h中的结构与函数 private: test::TestBody test_; // 定义于test.pb.h };
main.cc
// main.cc #include "plugin.h" int main(int argc, char** argv) { int should_continue = 1; while (should_continue) { // 执行dlopen相关操作,加载后调用插件的Execute() } }
报错信息
编译正常,但加载DoSomething插件时,dlerror()返回如下错误:
Can't open handle: /opt/ourprogram/plugins/libdo_something.so: undefined symbol: _ZTIN4test8TestBodyE
我理解这是预期行为——主程序没有导出该符号的理由。移除插件中TestBody的使用,或在主程序中实例化test::TestBody,插件即可正常加载。但作为插件系统,我们不希望用户修改核心代码(尤其是入口文件),仅允许引用头文件,因此需要寻找无需修改主程序核心代码的解决方案。
解决方案
1. 插件静态链接依赖库
将插件依赖的test.pb这类库静态链接到插件共享库中,让插件自身包含test::TestBody的符号,无需从主程序获取。编译插件时调整链接参数,例如使用-lprotobuf-static(具体名称根据你的静态库调整),确保静态库被完整链接进插件。
2. 主程序编译时导出所有符号
修改主程序的编译链接选项,添加-rdynamic(GCC/Clang环境),该参数会将主程序的所有全局符号加入动态符号表,供动态加载的插件解析使用。这种方式无需修改主程序代码,仅需调整编译脚本。
3. 拆分公共依赖为独立共享库
将test.pb对应的实现打包为独立的共享库(如libtest_proto.so),主程序和插件都动态链接这个库。此时test::TestBody的符号由该独立库提供,插件加载时会自动从该库中解析符号,无需主程序导出。
4. 启用延迟加载
调用dlopen时添加RTLD_LAZY标志,它会延迟符号解析直到第一次使用该符号,而非加载时立即解析所有符号。注意:这种方式仅作为辅助手段,若符号最终不存在于任何已加载的库中,运行时仍会报错。
内容的提问来源于stack exchange,提问作者Feej
相关产品推荐
相关产品推荐

