一、引言
在 Linux 开发过程中,有时会遇到这样的情况:程序已经编译成功,文件也确实存在,但执行时却提示找不到共享库。
例如:
error while loading shared libraries: libxxx.so:
cannot open shared object file: No such file or directory
遇到这类问题时,很多人首先想到重新编译程序,或者直接修改 LD_LIBRARY_PATH。但如果没有查清楚程序的实际依赖关系,盲目修改环境变量往往无法解决问题。
Linux 可执行文件通常采用 ELF(Executable and Linkable Format)格式。对于动态链接程序,ELF 文件会记录需要加载的共享库信息,系统动态链接器负责在运行时寻找并加载这些库。
本文介绍几种常用的动态库排查方法,包括 file、ldd、readelf 和 LD_LIBRARY_PATH,并给出一套适用于 Linux 和 ARM64 开发环境的排查流程。
二、使用 file 查看程序类型
在检查动态库之前,首先应该确认程序的文件类型和目标架构。
执行:
file ./myapp
可能看到类似输出:
myapp: ELF 64-bit LSB pie executable, ARM aarch64,
dynamically linked, interpreter /lib/ld-linux-aarch64.so.1
这说明该程序是一个 64 位 ARM AArch64 ELF 可执行文件,并且采用动态链接方式。
其中需要重点关注:
-
ELF 64-bit:ELF 文件的位数。
-
ARM aarch64:目标处理器架构。
-
dynamically linked:程序采用动态链接。
-
interpreter:程序请求使用的 ELF 解释器,也就是动态链接器的路径。
如果程序是 x86-64 架构,却被放到 ARM64 Linux 系统中运行,就可能出现 Exec format error。这种情况下,首先需要解决架构问题,而不是修改动态库搜索路径。
注意:以上输出为示例,实际内容取决于程序的编译方式和目标平台。
三、使用 ldd 查看动态库依赖
3.1 基本用法
ldd ./myapp
在动态链接环境中,输出可能类似:
linux-vdso.so.1 (0x0000ffff…)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (…)
libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (…)
这里展示了程序运行时解析到的共享库及其路径。
如果某个依赖无法解析,可能出现:
libexample.so => not found
这表示当前解析环境未能找到该共享库。常见原因包括:
库文件没有安装。
库文件所在目录不在动态链接器的搜索范围内。
配置的搜索路径不正确。
库的架构或 ABI 与程序不兼容。
3.2 ldd 的适用范围
ldd 适合快速查看可信程序的动态库依赖,但不能完全替代 ELF 静态分析工具。
对于来源不明的可执行文件,不建议直接运行 ldd。在某些历史实现或特殊情况下,检查过程可能涉及执行目标程序。更稳妥的做法是先使用 file 和 readelf 检查 ELF 信息。
此外,ldd 展示的是当前环境下的解析结果,换一台机器、改变环境变量或切换根文件系统后,结果都可能不同。
四、使用 readelf 分析 ELF 文件
readelf 可以直接解析 ELF 文件中的结构信息,不需要依赖 ldd 来解析动态库。
4.1 查看 ELF 头部
readelf -h ./myapp
重点查看:
Class: ELF64
Machine: AArch64
Class 表示 ELF 类别,Machine 表示目标架构。
对于 ARM64 程序,应重点确认 Machine 是否为 AArch64。
4.2 查看程序需要哪些动态库
readelf -d ./myapp
重点关注 NEEDED 条目,例如:
(NEEDED) Shared library: [libc.so.6]
(NEEDED) Shared library: [libexample.so]
这些条目记录了 ELF 文件声明的直接动态库依赖。
也可以过滤查看:
readelf -d ./myapp | grep -E 'NEEDED|RPATH|RUNPATH'
其中:
-
NEEDED:声明的直接共享库依赖。
-
RPATH:ELF 中记录的一种运行时库搜索路径。
-
RUNPATH:另一种运行时搜索路径,和 RPATH 的搜索规则存在区别。
需要注意,readelf -d 只能告诉我们程序声明依赖哪些库,以及是否记录了相关路径信息。它不会自动证明这些库在当前系统中都能被成功加载。
4.3 查看 ELF 解释器
readelf -l ./myapp | grep -i interpreter
可能输出:
[Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
这个路径非常重要。即使程序本身存在,如果目标系统缺少该动态链接器,程序仍然可能无法正常启动。
在交叉编译和嵌入式 Linux 环境中,应确认目标系统的动态链接器路径与程序要求一致。
五、动态链接器如何查找共享库?
当动态链接程序启动时,系统需要根据依赖信息找到对应的共享库。
常见影响因素包括:
-
ELF 中记录的 RPATH 或 RUNPATH。
-
环境变量 LD_LIBRARY_PATH。
-
系统动态链接器配置。
-
系统默认的共享库目录。
-
动态链接缓存(在支持该机制的系统上)。
具体搜索顺序会受到 ELF 标记、动态链接器实现、是否为安全执行模式等因素影响,因此不能简单认为所有系统都严格按照同一个固定顺序查找。
5.1 使用 LD_LIBRARY_PATH 临时指定目录
假设共享库位于:
/opt/myapp/lib/libexample.so
可以在启动程序时临时指定搜索目录:
LD_LIBRARY_PATH=/opt/myapp/lib ./myapp
也可以使用:
export LD_LIBRARY_PATH=/opt/myapp/lib:$LD_LIBRARY_PATH
./myapp
这种方式适合调试和临时验证。
但需要注意:
环境变量只对当前进程及其继承该环境的子进程生效。
它不会自动解决架构或 ABI 不兼容的问题。
不建议在没有分析依赖关系的情况下,长期将大量目录加入全局环境变量。
安全执行模式下,动态链接器可能忽略或限制部分环境变量。
六、如何排查 not found 问题?
假设执行 ldd ./myapp 后出现:
libexample.so => not found
可以按照以下步骤排查。
第一步:确认库文件是否存在
find /opt /usr/local/lib /usr/lib \\
-name 'libexample.so*' 2>/dev/null
这里仅搜索几个常见目录。如果库可能安装在其他路径,应相应调整搜索范围。
第二步:检查库文件的架构
file /opt/myapp/lib/libexample.so
如果程序是 ARM64,而共享库是 x86-64,那么即使找到文件,也不能将它当作可兼容的依赖直接使用。
第三步:检查程序声明的依赖
readelf -d ./myapp | grep NEEDED
确认缺失的库名称是否确实出现在程序的动态依赖条目中。
第四步:检查搜索路径
readelf -d ./myapp | grep -E 'RPATH|RUNPATH'
echo "$LD_LIBRARY_PATH"
根据结果判断是否需要调整临时环境变量、程序运行路径或系统库配置。
第五步:再次验证
LD_LIBRARY_PATH=/opt/myapp/lib ldd ./myapp
如果缺失库已经解析到正确路径,还需要继续检查该库自身的依赖,以及程序启动时是否出现其他错误。
七、ARM64 交叉编译环境中的注意事项
在 ARM64 开发中,动态库问题经常与交叉编译环境混在一起。
例如,开发主机是 x86-64 Linux,而目标设备是 AArch64 Linux。此时应注意:
第一,编译器目标架构必须正确。
可以使用:
aarch64-linux-gnu-gcc –version
检查交叉编译器是否存在。
第二,程序和共享库必须匹配目标架构。
file ./myapp
file ./libexample.so
不要把主机上的 x86-64 库直接复制到 ARM64 设备上使用。
第三,目标系统的 libc 和动态链接器也必须匹配。
仅仅让程序和库都显示为 AArch64,并不意味着它们一定兼容。不同系统的 C 库版本、ABI、符号版本以及运行环境也可能造成加载失败。
第四,交叉编译时的依赖解析环境不等于目标设备的运行环境。
在开发主机上运行 ldd,看到的是主机环境的解析结果,不能据此直接判断目标设备上所有依赖都正常。
如果程序最终运行在独立的目标板上,应结合目标根文件系统、交叉编译工具链和目标设备上的实际库文件进行验证。
八、常见错误总结
| libxxx.so => not found | 库缺失或搜索路径不正确 | ldd、find、检查搜索路径 |
| Exec format error | 可执行文件格式或架构不匹配 | file、readelf -h |
| No such file or directory,但程序文件存在 | 可能缺少 ELF 解释器,或脚本解释器不存在 | readelf -l、检查解释器路径 |
| undefined symbol | 符号缺失、库版本不兼容或 ABI 不匹配 | readelf -Ws、检查依赖版本 |
| 主机上运行正常,目标板上失败 | 目标系统缺少库,或运行环境不一致 | 检查目标设备的库和动态链接器 |
需要注意,错误信息通常只能提供线索,不能仅凭一条报错就断定唯一原因。
九、总结
Linux 动态库排查可以形成一套相对固定的流程:
使用 file 确认程序类型和架构。
使用 readelf -h 检查 ELF 头部。
使用 readelf -d 查看动态依赖和搜索路径标记。
对可信程序使用 ldd 检查当前环境中的库解析结果。
检查缺失库是否存在、架构是否匹配。
检查动态链接器路径以及目标系统运行环境。
修复后重新验证程序及其依赖。
对于 Linux 和嵌入式 ARM64 开发,掌握这些方法有助于区分架构错误、库缺失、动态链接器缺失和 ABI 不兼容等不同问题,减少盲目重编译和反复修改环境变量的情况。






实现100%通过率-171主机测评](https://www.171host.com/wp-content/uploads/2026/10/20261026191216-6adfa61015a7f-220x80.gif)