最可靠方法是直接查看/proc/pid/maps,它真实反映运行中进程内存映射的.so文件路径及完整版本后缀,覆盖dlopen动态加载等所有情况;其他工具如ldd仅静态分析,pldd可能因权限或匿名映射而漏检。

直接看 /proc/PID/maps,这是唯一能确认“此刻进程到底加载了哪个版本的 .so”的方法。其他工具如 ldd 或 pldd 都有盲区:前者只反映静态声明,后者可能漏掉无路径的库或因权限失败。
用 /proc/PID/maps 精确提取已加载的库路径与版本后缀
进程一旦完成 mmap,其实际加载的共享库路径(含完整版本号)就固化在 /proc/PID/maps 中。这比任何模拟或推测都可靠。
- 先获取目标 PID:
pidof myapp或ps aux | grep myapp - 执行:
cat /proc/1234/maps | grep '\.so' | grep -v 'vdso\|vvar' | awk '{print $6}' | sort -u(替换1234为真实 PID) - 输出类似
/usr/lib/x86_64-linux-gnu/libssl.so.1.1.1—— 末尾的.1.1.1就是当前生效版本 - 若看到同一库多个版本(如
libcurl.so.4和libcurl.so.4.7.0同时存在),说明有dlopen行为,极易引发符号冲突
为什么 pldd 有时不准,且容易失败
pldd 依赖读取 /proc/PID/mem,而该文件默认仅 root 可读;即使加 sudo,遇到 T 状态、被 ptrace 保护或 SELinux 限制的进程也会报 Operation not permitted。
-
pldd会跳过没有文件路径的库(如通过memfd_create加载的匿名库) - 它不显示库加载顺序,也不体现
LD_PRELOAD注入的库是否真被用了(/proc/PID/maps里若有路径就能确认) - 对 systemd 服务或容器内进程,
pldd常因命名空间隔离失败,而cat /proc/PID/maps在同主机 namespace 下始终有效
当怀疑加载行为异常时,用 strace 追踪 open/mmap 全过程
如果进程启动失败,或你发现 /proc/PID/maps 里没出现预期的库,说明它根本没被加载成功——这时需要回溯加载尝试过程。
- 启动时跟踪:
strace -e trace=openat,open,mmap -o trace.log ./myapp - 附加到运行中进程:
strace -p PID -e trace=openat,open,mmap 2>&1 | grep '\.so' - 关键看哪条
openat(..., "/path/to/libxxx.so.2", ...)返回了非负 fd,紧接着是否有对应mmap;若openat成功但无mmap,大概率是 ABI 不兼容被链接器拒绝 - 注意:
strace有性能开销,生产环境慎用;日志里路径不含版本后缀时(如libssl.so),需结合ls -l /path/to/libssl.so查真实指向
真正麻烦的不是找不到库,而是找到两个同名不同版本的库路径却无法判断哪个被符号解析器选中——这时候得看 /proc/PID/maps 里地址段的加载顺序和 readelf -d 输出的 DT_RUNPATH,而不是凭经验猜。











