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

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

问题原因与修正方案

代码无法正常运行有两个核心问题:

  1. 动态加载器找不到依赖的共享库:加载libtest2.so时无法定位libtest1.so,加载libtest3.so时无法定位libtest2.so和libtest1.so,该问题可在链接阶段添加rpath路径解决;
  2. 访问非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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 18:36:04