dlopen加载共享库致多单例:如何让D共享A的静态库B?
解决方案
问题本质是静态链接导致B库存在两份独立副本:A静态链接了B,D原本也静态链接B,所以各自拥有B的代码和单例实例,导致注册和查找不在同一个实例中。要让D复用A中的B副本,核心是让D的B相关符号从A中动态解析,而非自行静态链接一份。以下分平台给出具体构建调整方式:
Linux(Fedora 31)平台
- 调整应用A的构建:
- 编译链接A时加上
-rdynamic参数,这个参数会把主程序的符号导出到动态符号表,让后续dlopen的库能访问到这些符号。A继续保持静态链接B和C的方式。
- 编译链接A时加上
- 调整动态库D的构建:
- 从D的链接命令里删掉B和C的静态库依赖,不再静态链接它们。
- 链接D时添加
-Wl,--allow-shlib-undefined参数(如果之前用了-z defs就去掉),允许D存在未定义符号——这些符号会在加载时从A中找到。D继续动态链接E。
- 修改dlopen调用:
- A里调用
dlopen()时,用RTLD_NOW | RTLD_GLOBAL标志。RTLD_GLOBAL会让D能访问主程序A的全局符号,确保B的符号被正确解析。
- A里调用
这样调整后,D加载时会直接用A里的B副本,单例Instance()返回同一个实例,注册的对象能被A正常找到。
Windows(Win10/11)平台
Windows的符号导出机制和Linux不同,静态链接的库符号默认不会导出到主程序,需要额外处理:
- 调整库B的导出配置:
- 把B中需要被D调用的符号(比如
Instance()方法、Foo3::Bar::MyFunc(double))用__declspec(dllexport)标记,或者写一个.def文件列出这些符号,确保它们能被导出到A的符号表。
- 把B中需要被D调用的符号(比如
- 调整应用A的构建:
- A继续静态链接B和C,但链接时要加
/EXPORT选项(如果没通过代码标记导出),强制把B的符号导出到A的可执行文件导出表。比如针对目标mangled符号,链接命令加/EXPORT:?MyFunc@Bar@Foo3@@QAEXN@Z(具体符号名可通过dumpbin /symbols查看)。
- A继续静态链接B和C,但链接时要加
- 调整动态库D的构建:
- 删掉D链接命令里的B静态库依赖,改成链接B的导入库(.lib文件),这样D会生成对这些符号的导入引用。D继续动态链接E。
- LoadLibrary调用:
- A用
LoadLibrary()加载D时,Windows默认会从主程序的符号表中解析D的未定义符号,只要A导出了B的符号,D就能正确复用A中的B副本。
- A用
验证方法
- Linux:用
nm -D <A的可执行文件>查看是否包含B的关键符号;用ldd <D的动态库>确认D没静态链接B。 - Windows:用
dumpbin /exports <A的可执行文件>查看是否导出了B的目标符号;用dumpbin /dependents <D的动态库>确认D依赖B的导入库而非静态库。
内容的提问来源于stack exchange,提问作者Swiss Frank
相关产品推荐
相关产品推荐

