Linux下C++加载带依赖共享库及调用导出函数的问题排查
加载带依赖的共享库并调用导出符号的问题与解决方案
运行环境
- OS:Linux Ubuntu 18.04,gcc 7.4.0
我需要实现一个可加载带有其他共享库依赖的SO文件、并调用所有SO导出函数的程序,目前找到的相关问题都没有明确覆盖该场景。
我设计的测试场景包含3个共享库和1个主程序:
libtest1.so 包含extern "C"修饰的打印函数 libtest2.so 包含基类Base,类内函数调用上述C函数 libtest3.so 包含派生类Test,类内函数调用上述C函数 DllTest 用于加载so的主程序
主程序预期执行逻辑如下:
pHandle = OpenSharedLib("./libtest1.so"); // 执行正常 OpenSymbol(pHandle, "GetName"); // 执行正常 CloseLib(pHandle); pHandle = OpenSharedLib("./libtest2.so"); // 报错 OpenSymbol(pHandle, "GetName"); OpenSymbol(pHandle, "printBase"); CloseLib(pHandle); pHandle = OpenSharedLib("./libtest3.so"); OpenSymbol(pHandle, "GetName"); OpenSymbol(pHandle, "printBase"); OpenSymbol(pHandle, "print"); CloseLib(pHandle);
实际运行时dlopen加载libtest2.so失败,错误为未定义符号:./libtest2.so: undefined symbol: GetName"。nm指令输出显示该符号缺失,我未找到规避该问题的方法。
我的核心需求是实现一个「前端SO」,聚合所有独立SO的能力,为主程序提供统一的库接口:即主程序只需加载libtest3.so,就可以调用所有子SO暴露的任意符号。
问题:上述需求是否可以实现,具体如何操作?我的代码存在什么错误?
完整代码与原始编译指令
libtest1.so
头文件代码:
extern "C" __attribute__((visibility("default"))) const char* GetName();
Cpp实现代码:
#include <stdio.h> #include <stdlib.h> #include "lib1.h" __attribute__((visibility("default"))) const char* GetName() { return "Hello from lib1"; }
原始编译指令:
g++ -c -fvisibility=hidden -fPIC -o lib1.o lib1.cpp g++ -shared -o libtest1.so lib1.o
libtest2.so
头文件代码:
#include <stdio.h> #include <stdlib.h> class __attribute__((visibility("default"))) Base { public: Base(); virtual ~Base(); const char* printBase(); int nTest; };
Cpp实现代码:
#include <stdio.h> #include <stdlib.h> #include "lib2.h" #include "lib1.h" // 用于调用GetName() Base::Base() { nTest=1; } Base::~Base() { } const char* Base::printBase() { return GetName(); }
原始编译指令:
g++ -c -fvisibility=hidden -fPIC -o lib2.o lib2.cpp g++ -shared -o libtest2.so -L. -ltest1 lib2.o
libtest3.so
实现代码:
#include <stdio.h> #include <stdlib.h> #include "lib1.h" #include "lib2.h" class __attribute__((visibility("default"))) Test : public Base { public: Test(); virtual ~Test(); const char* print(); }; Test::Test() { } Test::~Test() { } const char* Test::print() { char* pChar = (char*)GetName(); printf( "hello from lib3: %d", nTest); return "test3"; }
原始编译指令:
g++ -c -fvisibility=hidden -fPIC -o lib3.o lib3.cpp g++ -shared -o libtest3.so -L. -ltest1 -ltest2 lib3.o
主程序加载代码
#include <stdio.h> #include <stdlib.h> #include <iostream> #include <dlfcn.h> void OpenSymbol(void* pHandle, char* strName) { typedef char* (*pfnChar)(void); pfnChar pFunction = NULL; char* cError; printf(" Find symbol %s\n", strName); dlerror(); pFunction = (pfnChar)dlsym( pHandle, strName ); cError = dlerror(); if (cError != 0) { std::cout << cError << std::endl; exit(1); } printf(" Exec symbol: %p\n", pFunction ); std::cout << pFunction() << std::endl; } void* OpenSharedLib(char* strName) { void* pHandle; char* cError; printf(" open lib %s\n", strName); dlerror(); pHandle = dlopen( strName, RTLD_NOW ); cError = dlerror(); if (cError != 0) { std::cout << cError << std::endl; exit(1); } printf(" found DLL %p\n", pHandle ); return pHandle; } void* CloseLib(void* pHandle) { dlclose(pHandle); } main() { void* pHandle; pHandle = OpenSharedLib("./libtest1.so"); OpenSymbol(pHandle, "GetName"); CloseLib(pHandle); pHandle = OpenSharedLib("./libtest2.so"); OpenSymbol(pHandle, "GetName"); OpenSymbol(pHandle, "printBase"); CloseLib(pHandle); pHandle = OpenSharedLib("./libtest3.so"); OpenSymbol(pHandle, "GetName"); OpenSymbol(pHandle, "printBase"); OpenSymbol(pHandle, "print"); CloseLib(pHandle); std::cout << "done" << std::endl; }
诊断信息
运行nm -DC指令输出显示后两个共享库的部分符号未导出:
- libtest1.so符号:
... 000000000000057a T GetName
- libtest2.so符号:
... U GetName U operator delete(void*, unsigned long) 000000000000094c T Base::printBase() 00000000000008da T Base::Base() 00000000000008da T Base::Base() 0000000000000920 T Base::~Base() 0000000000000902 T Base::~Base() 0000000000000902 T Base::~Base() 0000000000200e08 V typeinfo for Base 0000000000000969 V typeinfo name for Base 0000000000200de8 V vtable for Base U vtable for __cxxabiv1::__class_type_info
- libtest3.so符号:
... U GetName U printf U operator delete(void*, unsigned long) U Base::Base() U Base::~Base() 0000000000000ab2 T Test::print() 0000000000000a2a T Test::Test() 0000000000000a2a T Test::Test() 0000000000000a86 T Test::~Test() 0000000000000a58 T Test::~Test() 0000000000000a58 T Test::~Test() U typeinfo for Base 0000000000200df0 V typeinfo for Test 0000000000000b0f V typeinfo name for Test 0000000000200dd0 V vtable for Test U vtable for __cxxabiv1::__si_class_type_info
- DllTest运行输出:
open lib ./libtest1.so found DLL 0x55965d711ea0 Find symbol GetName Exec symbol: 0x7f902c38157a Hello from lib1 open lib ./libtest2.so ./libtest2.so: undefined symbol: GetName
问题原因与修正方案
代码无法正常运行有两个核心问题:
- 动态加载器找不到依赖的共享库:加载libtest2.so时无法定位libtest1.so,加载libtest3.so时无法定位libtest2.so和libtest1.so,该问题可在链接阶段添加rpath路径解决;
- 访问非
extern "C"修饰的类等对象时,需要在共享库内完成对象实例化,因此需要重构lib2.h/cpp、lib3.h/cpp和DllTest.cpp。
修正后的完整编译序列如下:
g++ -c -fvisibility=hidden -fPIC -o lib1.o lib1.cpp g++ -shared -o libtest1.so lib1.o g++ -c -fvisibility=hidden -fPIC -o lib2.o lib2.cpp g++ -shared -o libtest2.so -Wl,-rpath,$PWD -L. lib2.o -ltest1 g++ -c -fvisibility=hidden -fPIC -o lib3.o lib3.cpp g++ -shared -o libtest3.so -Wl,-rpath,$PWD -L. lib3.o -ltest1 -ltest2 g++ -o DllTest DllTest.cpp -ldl
修正后无论加载三个共享库中的任意一个,都可以正常调用const char* GetName()函数。
内容的提问来源于stack exchange,提问作者CatMan
相关产品推荐
相关产品推荐

