dladdr的dli_fname常为空或不完整,根本原因是它仅返回地址所属的已加载ELF映像路径,不解析符号重定向、DT_NEEDED链或RTLD_LOCAL隐藏符号;例如libc的IFUNC优化、静态库链接进主程序、或隐式依赖均导致路径缺失或指向错误模块。

dladdr 返回的 dli_fname 为什么经常是空或不完整?
根本原因在于 dladdr 只能查到符号所在 ELF 段所属的**已加载模块**(即 dlopen 打开的 so,或主程序、依赖库),它不解析符号重定向、不追溯 DT_NEEDED 链、也不处理 RTLD_LOCAL 下的隐藏符号。常见现象包括:
- 对
malloc、printf等 libc 符号调用dladdr,dli_fname可能为空(glibc 使用IFUNC或内联优化绕过 PLT) - 符号定义在静态库(.a)中并被链接进主程序,则
dli_fname指向可执行文件路径,而非某个 so - 使用
LD_PRELOAD注入的库中函数,dladdr能返回其路径,但若该库又被dlopen(RTLD_LOCAL)加载过,可能查不到
所以别指望 dladdr “自动找到源 so 文件”,它只告诉你“此刻这个地址映射自内存里哪块已加载的 ELF 映像”。
如何确保 dladdr 能稳定返回动态库的绝对路径?
关键不是调用方式,而是**让目标函数真正来自一个可定位的、独立加载的共享对象**。实操要点:
- 函数必须定义在 .so 中(非 inline,非 static,且未被 LTO 优化掉),编译时加
-fPIC -shared - so 必须通过
dlopen显式加载(哪怕用RTLD_LAZY | RTLD_GLOBAL),不能仅靠DT_NEEDED隐式依赖(否则路径可能指向依赖它的上层 so) - 调用
dladdr时传入的是函数指针(如(void*)&my_func),不是函数名字符串 - 检查返回值:必须判断
dladdr返回非零,且dli_fname不为nullptr,再用realpath(dli_fname, nullptr)转成绝对路径
示例:
立即学习“C++免费学习笔记(深入)”;
void* handle = dlopen("./libmymath.so", RTLD_LAZY | RTLD_GLOBAL);
if (!handle) { /* error */ }
typedef int (*add_t)(int, int);
add_t add = (add_t)dlsym(handle, "add");
Dl_info info;
if (dladdr((void*)add, &info) && info.dli_fname) {
char* abs_path = realpath(info.dli_fname, nullptr);
printf("lib path: %s\n", abs_path); // 如 /home/user/libmymath.so
free(abs_path);
}dladdr 查不到符号时的替代方案有哪些?
当 dladdr 失效(比如符号在主程序里、或被 strip 过),可组合使用以下手段逼近目标路径:
- 读
/proc/self/maps:按函数地址查找内存映射段,提取起始地址和路径字段(注意权限标记为r-xp的才是代码段) - 用
dl_iterate_phdr遍历所有加载模块,对每个dl_phdr_info调用dladdr测试其dlpi_addr+ 各段偏移,比单纯猜更可靠 - 编译时注入构建信息:在 so 中定义全局变量如
const char __lib_path[] = "/build/libmymath.so",运行时直接取地址(需确保未被优化掉,加__attribute__((used)))
注意:/proc/self/maps 是 Linux 特有,dl_iterate_phdr 在 glibc 和 musl 上行为略有差异,musl 不保证回调顺序。
为什么 realpath 后的路径仍可能不是“源构建路径”?
因为 dladdr 返回的是**运行时实际加载的文件路径**,它可能是符号链接、硬链接、bind mount 下的路径,甚至容器内挂载的路径。例如:
- so 文件是
/usr/lib/libfoo.so → /usr/lib/libfoo.so.2.1符号链接,realpath返回后者,但构建时路径可能是/build/libfoo.so - 程序在 Docker 中运行,宿主机的
/src/libfoo.so绑定挂载到容器内/app/libfoo.so,dladdr返回的就是/app/libfoo.so
如果真需要原始构建路径,得在编译阶段写死(如 CMake 的 add_definitions(-DBUILD_PATH="${CMAKE_CURRENT_SOURCE_DIR}")),运行时无法从 ELF 中可靠还原。


















