如何查询C++共享库(.so)中程序退出时调用的静态析构函数列表
问题解决方案
实现思路
C++ 全局/静态类实例的析构函数确实会注册到共享库独立的 .fini_array 段中,该段存储了程序/库退出时需要顺序执行的函数指针数组,不会和其他库的同段内容混淆,基于此的定位逻辑完全成立:
- 静态读取思路:直接解析目标
.so文件的段结构,提取.fini_array段的所有指针值,对应到库内的符号偏移,解析出析构函数名 - 动态读取思路:进程运行时读取目标库的加载基址,加上
.fini_array段的文件偏移,拿到内存中该段的实际地址,遍历所有函数指针结合进程符号表解析函数名
可用工具与操作步骤
静态排查无需运行程序
使用系统自带的 readelf + nm 组合即可完成解析:
- 读取
.fini_array段的偏移与内容:
64位系统下输出的每8字节十六进制值就是析构函数在库内的偏移地址readelf -x .fini_array 目标库.so - 解析偏移对应的符号名,
-C用于还原C++符号的可读性:nm -C 目标库.so | grep 上一步拿到的偏移值(去掉前缀0x)
运行时动态验证
- gdb 直接导出段内容:
程序加载完全部共享库后,执行info sharedlibrary拿到目标库的加载基址,结合readelf拿到的.fini_array段在文件内的偏移,计算出内存中该段的起止地址,执行:
导出的二进制文件按8字节遍历即可拿到所有运行时要执行的析构函数地址dump memory fini_array.bin 段起始内存地址 段结束内存地址 - ltrace 直接跟踪退出阶段的析构调用:
执行以下命令运行程序,退出时会打印所有库的析构调用,直接过滤重复的类析构即可:
其中ltrace -e "~*" -f 你的程序路径~*匹配所有析构函数,-f跟踪所有库的调用
根治与快速排查补充方案
- 临时快速定位重复销毁:给可疑类的析构函数增加调试打印,输出实例地址与调用方所属库:
编译链接时加#include <dlfcn.h> #include <iostream> #include <typeinfo> // 析构函数内添加以下代码 Dl_info dlinfo; if (dladdr(__builtin_return_address(0), &dlinfo)) { std::cout << "析构实例:" << typeid(*this).name() << " 内存地址:" << this << " 调用来源库:" << dlinfo.dli_fname << std::endl; }-ldl即可,同一个地址出现两次析构打印就能直接定位两个调用方所属的库 - 从根源避免问题:所有库编译时加
-fvisibility=hidden参数,默认隐藏所有符号,仅需要对外暴露的接口显式标注__attribute__((visibility("default"))),彻底避免不同库意外导出同名类符号导致的实例共享问题。
内容的提问来源于stack exchange,提问作者Serge
相关产品推荐
相关产品推荐

