You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

加载依赖主程序未引用符号的插件共享库时遇未定义符号问题求助

插件系统动态加载共享库时的符号未定义问题

问题背景

我基于共享库设计了一套插件系统,流程如下:

  • 提供带虚函数的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 15:45:44