如何从共享库导出符号并获取派生类实例?Boost.DLL场景
动态加载共享库获取派生类实例的链接错误解决方法
问题场景
需要实现插件机制,通过Boost.DLL加载共享库并获取库中派生类实例,现有代码及链接报错如下:
共享库头文件(test_lib.h)
#pragma once #include <iostream> #include <memory> #include <boost/dll/config.hpp> class BOOST_SYMBOL_EXPORT BaseA { public: virtual BOOST_SYMBOL_EXPORT ~BaseA() = default; }; class BOOST_SYMBOL_EXPORT DerivedA : public BaseA { public: BOOST_SYMBOL_EXPORT virtual ~DerivedA(); BOOST_SYMBOL_EXPORT void method2(); }; // 获取派生类实例的函数 extern "C" BOOST_SYMBOL_EXPORT BaseA* get_derived_instance();
共享库实现文件(lib.cpp)
#include <test_lib.h> #include <iostream> #include <boost/dll/alias.hpp> void DerivedA::method2() { std::cout << "M2" << std::endl; }; DerivedA::~DerivedA() { std::cout << "DDestr" << std::endl; } BaseA* get_derived_instance() { return new DerivedA(); }
测试主程序(main.cpp)
#include <memory> #include <boost/dll/import.hpp> #include <test_lib.h> int main() { typedef BaseA* (*get_derived_instance_t)(); void* thandle = dlopen("build/deblib/libisolated_proj.so", RTLD_LAZY); if (!thandle) { std::cerr << "load" << std::endl; exit(1); } auto creator = reinterpret_cast<get_derived_instance_t>(dlsym(thandle, "get_derived_instance")); if (!creator) { std::cerr << "fc" << std::endl; exit(1); } auto new_obj = creator(); DerivedA *da = dynamic_cast<DerivedA*>(new_obj); da->method2(); }
链接错误信息
[ 50%] Building CXX object deblib/CMakeFiles/isolated_proj.dir/lib.cpp.o [ 50%] Building CXX object CMakeFiles/testss.dir/main.o [ 75%] Linking CXX shared library libisolated_proj.so [100%] Linking CXX executable testss [100%] Built target isolated_proj /usr/bin/ld: CMakeFiles/testss.dir/main.o: warning: relocation against `_ZTI8DerivedA' in read-only section `.text' /usr/bin/ld: CMakeFiles/testss.dir/main.o: in function `main': main.cpp:(.text+0xcd): undefined reference to `typeinfo for DerivedA' /usr/bin/ld: main.cpp:(.text+0xf3): undefined reference to `DerivedA::method2()' /usr/bin/ld: warning: creating DT_TEXTREL in a PIE collect2: error: ld returned 1 exit status make[2]: *** [CMakeFiles/testss.dir/build.make:102: testss] Error 1 make[1]: *** [CMakeFiles/Makefile2:109: CMakeFiles/testss.dir/all] Error 2 make: *** [Makefile:91: all] Error 2
错误原因
链接器报错的核心原因是:主程序编译时直接引用了DerivedA的类型信息和成员函数符号,但这些符号仅存在于共享库中,主程序链接阶段无法找到。即便使用了动态加载,代码中dynamic_cast<DerivedA*>和da->method2()的写法会让编译器认为这些符号需要在链接时解析,而非运行时动态获取。
解决方法
方法1:面向基类编程(推荐)
插件机制的核心原则是依赖抽象而非具体实现,通过基类提供统一接口,避免主程序直接引用派生类。
- 修改基类,添加纯虚函数替代派生类的
method2:
// test_lib.h class BOOST_SYMBOL_EXPORT BaseA { public: virtual BOOST_SYMBOL_EXPORT ~BaseA() = default; virtual BOOST_SYMBOL_EXPORT void method2() = 0; // 添加纯虚函数接口 }; class BOOST_SYMBOL_EXPORT DerivedA : public BaseA { public: BOOST_SYMBOL_EXPORT virtual ~DerivedA(); BOOST_SYMBOL_EXPORT void method2() override; // 重写基类接口 };
- 主程序直接通过基类指针调用方法,无需
dynamic_cast:
// main.cpp int main() { typedef BaseA* (*get_derived_instance_t)(); void* thandle = dlopen("build/deblib/libisolated_proj.so", RTLD_LAZY); if (!thandle) { std::cerr << "load" << std::endl; exit(1); } auto creator = reinterpret_cast<get_derived_instance_t>(dlsym(thandle, "get_derived_instance")); if (!creator) { std::cerr << "fc" << std::endl; exit(1); } std::unique_ptr<BaseA> new_obj(creator()); new_obj->method2(); // 直接通过基类接口调用 }
方法2:动态解析派生类符号(按需使用)
如果必须在主程序中使用派生类的特定功能,需将派生类的相关符号也通过动态加载获取,避免编译时直接引用:
- 在共享库中导出派生类成员函数的获取接口:
// test_lib.h extern "C" BOOST_SYMBOL_EXPORT void (*get_derived_method2())();
// lib.cpp void (*get_derived_method2())() { return &DerivedA::method2; }
- 主程序中动态获取符号并调用:
// main.cpp int main() { typedef BaseA* (*get_derived_instance_t)(); typedef void (*method2_t)(DerivedA*); void* thandle = dlopen("build/deblib/libisolated_proj.so", RTLD_LAZY); if (!thandle) { std::cerr << "load" << std::endl; exit(1); } auto creator = reinterpret_cast<get_derived_instance_t>(dlsym(thandle, "get_derived_instance")); if (!creator) { std::cerr << "fc" << std::endl; exit(1); } auto get_method2 = reinterpret_cast<void (*)()>(dlsym(thandle, "get_derived_method2")); if (!get_method2) { std::cerr << "get method2 failed" << std::endl; exit(1); } method2_t method2 = reinterpret_cast<method2_t>(get_method2()); auto new_obj = creator(); // 此处用static_cast,因dynamic_cast需类型信息(需额外动态加载typeinfo符号,不推荐) DerivedA* da = static_cast<DerivedA*>(new_obj); method2(da); }
方法3:链接时显式依赖共享库(不推荐)
若主程序与共享库属于同一项目,可在链接主程序时直接指定共享库依赖,让链接器找到符号。但此方式会失去动态加载的灵活性,程序启动时就会加载共享库,不符合插件按需加载的设计初衷。
在CMake中添加链接依赖:
target_link_libraries(testss isolated_proj)
内容的提问来源于stack exchange,提问作者A.N.
相关产品推荐
相关产品推荐

