Linux ldd 怎样找出缺失的共享库依赖
来源:17golang原创
时间:2026-11-12 10:23:35 157浏览 收藏
我第一次遇到“程序明明存在,却提示找不到某个 .so 文件”时,先看了应用自己的日志,后来才发现真正有用的线索在依赖解析结果里。Linux 的 ldd 会把目标程序需要的共享对象以及解析到的路径列出来;其中出现 not found,通常就是当前环境没有为该依赖找到可用文件。
官方手册地址:https://man7.org/linux/man-pages/man1/ldd.1.html
这篇只解决一个问题:怎样用 ldd 找出缺失依赖,并判断是库文件不在搜索路径、重定位失败,还是目标文件本身不适合直接交给 ldd。涉及不可信二进制时,会切换到静态的 objdump -p 读取。
先读懂 ldd 输出中的三种状态
我现在排查这类问题,会先把输出分成三类,而不是一看到某一行就开始安装软件包:
- 已解析路径:类似
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6,说明动态链接器为这个名字找到了文件。 - not found:依赖名字被目标文件声明了,但按当前搜索规则没有找到对应对象。
- 特殊对象:
linux-vdso.so.1、动态链接器本身等并不等同于普通磁盘库,不能简单按普通.so文件处理。

官方 ldd(1) 手册说明,ldd 展示的是程序或共享对象所需的共享库;常见实现会让动态链接器在跟踪模式下检查这些依赖。因此它呈现的是“当前环境如何解析”,不只是目标文件里写着哪些名字。
用 not found 锁定真正缺失的库
先保存目标路径,再只筛选缺失行。下面的命令没有替读者决定安装哪个包,作用只是把证据从长输出中提出来:
target="/opt/example/bin/app" # 只筛选动态链接器明确标记为未解析的依赖。 ldd "$target" | grep -F 'not found'
如果没有任何输出,并不等于程序一定能启动。它只说明 ldd 没有在这次检查中报告未找到的共享对象。还要留意:
- 目标可能不是当前架构可运行的 ELF 文件;
- 依赖可能存在,但符号版本或重定位阶段仍不匹配;
- 程序在启动后通过
dlopen动态加载的库,不一定出现在最初的依赖清单里。
如果输出是 libexample.so => not found,先记下库的名字和目标文件,不要直接把一个同名文件复制到系统目录。下一步应解释动态链接器会去哪里找。
结合搜索路径解释为什么会找不到
动态链接器的规则决定了同一个程序在不同机器上可能得到不同路径。对不含斜杠的依赖名,官方 ld.so(8) 文档列出的关键来源包括 DT_RPATH、LD_LIBRARY_PATH、DT_RUNPATH、/etc/ld.so.cache 以及默认目录;具体优先级和安全执行模式也会影响结果。
target="/opt/example/bin/app"
# 查看目标的动态段,确认是否写入了 RPATH、RUNPATH 或 NEEDED。
# 这里看的是文件声明,不把它当成完整的运行时依赖树。
readelf -d "$target" | grep -E 'NEEDED|RPATH|RUNPATH'
# 临时显示当前 shell 是否带入了库搜索路径,避免把环境差异误判成缺库。
printf 'LD_LIBRARY_PATH=%s\n' "${LD_LIBRARY_PATH-}"
我会把证据分成两层:NEEDED 说明目标声明了什么,ldd 说明当前环境把这些声明解析成了什么。若只有某个部署用户报错,优先比较该用户的环境变量、服务启动方式和安全执行条件。
特别要注意空的 LD_LIBRARY_PATH 项可能代表当前工作目录,路径顺序也会让同名库被不同目录中的文件满足。生产修复应回到构建时的 RUNPATH、系统库缓存或明确的部署目录,而不是把临时环境变量永久写进服务。
区分缺库与重定位错误
有时依赖文件能被找到,但加载其中的数据或函数仍然失败。这时可以用 ldd 的重定位检查选项把问题再缩小一层:
target="/opt/example/bin/app" # 检查数据重定位,适合确认缺失对象或数据符号。 ldd -d "$target" # 同时检查函数重定位;输出可能比普通 ldd 更长。 ldd -r "$target"
-d 会执行数据重定位检查,-r 会连函数重定位一起检查。我的判断习惯是:
| 现象 | 优先怀疑 | 下一步 |
|---|---|---|
=> not found | 库文件或搜索路径缺失 | 核对 RUNPATH、缓存和部署目录 |
| 库路径已出现,但重定位报错 | 符号、ABI 或版本不匹配 | 比较符号版本与构建环境 |
| 普通 ldd 正常,启动后仍报错 | 运行期动态加载或环境差异 | 再看程序的加载逻辑与服务环境 |
不可信文件不要直接交给 ldd
这里是我后来最在意的一条边界:官方手册明确提醒,不要对不可信的可执行文件直接运行 ldd。在某些情况下,旧版本或特殊 ELF 解释器可能触发直接执行行为。目标文件来源不明时,先用静态方式读取它声明的直接依赖:

target="/path/to/untrusted-file" # 对来源不明的 ELF,优先静态读取直接依赖声明。 # NEEDED 只代表目标文件直接声明的共享对象,不等于完整依赖树。 objdump -p "$target" | grep -F 'NEEDED'
这个替代方案的取舍很明确:objdump -p 更适合先做静态筛查,但它只显示直接依赖;ldd 能展开更完整的依赖树,却要求你信任目标文件和当前工具链的行为。两者不能当成完全等价的命令。
我的可复用排障清单
- 确认目标是预期架构的 ELF 文件,并记录绝对路径。
- 对可信目标运行
ldd target,先找not found。 - 把
NEEDED、RPATH/RUNPATH 和当前库路径分开记录。 - 若路径已解析但仍失败,再用
ldd -d或ldd -r看重定位。 - 目标不可信时停止使用
ldd,改用objdump -p | grep NEEDED。 - 修复后重新检查同一个目标和同一个服务启动环境,确认不是只修好了交互式 shell。
对我来说,ldd 最有价值的地方不是“列出几个库”,而是把启动错误拆成了可观察的依赖解析证据。只要把 not found、搜索路径、重定位和文件信任边界分开,排查通常就不会退化成盲目复制 .so 文件。
相关问题
ldd 没有 not found,为什么程序仍然启动失败?
可能是符号版本、重定位、运行后 dlopen,或服务启动环境和当前 shell 不同。继续看 ldd -r、动态段和实际服务环境。
objdump -p 能完全替代 ldd 吗?
不能。它更适合不可信文件的静态筛查,只展示直接 NEEDED;ldd 侧重当前环境下的完整解析树。
看到 not found 可以直接设置 LD_LIBRARY_PATH 吗?
可以作为短时诊断手段,但生产修复应明确库的部署位置和构建时 RUNPATH,并评估路径顺序和安全执行模式,避免环境变量掩盖真正的打包问题。
-
426 收藏
-
387 收藏
-
242 收藏
-
238 收藏
-
402 收藏
-
441 收藏
-
348 收藏
-
230 收藏
-
177 收藏
-
405 收藏
-
106 收藏
-
159 收藏
-
254 收藏
-
377 收藏
-
275 收藏
-
245 收藏
-
500 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习