欢迎光临
我们一直在努力

Linux 下如何查看程序依赖的动态库?从 ldd 到 readelf 实战

一、引言

在 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 不兼容等不同问题,减少盲目重编译和反复修改环境变量的情况。

    赞(0)
    未经允许不得转载:171主机测评 » Linux 下如何查看程序依赖的动态库?从 ldd 到 readelf 实战
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址