news 2026/9/13 10:31:43

Linux | 程序 / 进程调用库依赖关系查看

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux | 程序 / 进程调用库依赖关系查看

:本文为 “Linux | 程序 / 进程调用库依赖查看” 方法相关合辑。
略作重排,如有内容异常,请看原文。


Linux 库依赖检查与共享库分析

在 Linux 系统中,程序通常依赖外部库才能正确运行。这些库在运行时被动态加载,使得程序可以复用通用代码并减小体积。当程序缺少所需的库或库版本不兼容时,就会出现“无法打开共享对象文件”之类的错误,导致程序无法启动。

例如,执行sudo dpkg命令时,如果系统缺少libpthread.so.0库,就会报告如下错误:

$sudodpkg sudo: errorwhileloading shared libraries: libpthread.so.0: cannotopenshared object file: No suchfileor directory

为了解决此类问题,Linux 提供了多种工具来检查程序的库依赖关系。这些工具可以帮助你定位缺失的库,以便进行安装或更新。

工具分类说明

Linux 环境下获取 ELF 可执行文件的共享库依赖,分为两类方案:

  1. 运行时解析:ldd,调用动态链接器,可同时读取直接依赖传递依赖;存在执行风险,可捕获部分动态加载行为,但无法捕获dlopen在程序运行过程中才加载的库。
  2. 文件静态解析:objdumpreadelf,仅读取 ELF 文件内预写入的动态段,不执行目标程序,安全性更高;仅读取编译阶段写入的直接依赖,不能自动递归展开传递依赖,同样无法捕获运行期dlopen加载库。

限制:所有静态解析工具均无法获取程序运行时通过dlopen()动态打开的共享库,该类库不会记录在 ELF 文件动态段中。

检查可执行文件的依赖

以下方法用于分析静态的二进制文件,查看其编译时记录的依赖信息。

ldd命令

ldd命令通过调用动态链接器来显示一个程序所需的所有共享库。这是最常用的方法。

$ ldd /usr/bin/bash linux-vdso.so.1(0x00007ffdd2749000)libtinfo.so.6=>/lib/x86_64-linux-gnu/libtinfo.so.6(0x00007fcecb9b6100)libdl.so.2=>/lib/x86_64-linux-gnu/libdl.so.2(0x00007fcecb9b0000)libc.so.6=>/lib/x86_64-linux-gnu/libc.so.6(0x00007fcecb7c5000)/lib64/ld-linux-x86-64.so.2(0x00007fcecbb21000)

输出中的箭头=>指向了系统中库文件的实际路径。如果某个依赖缺失,则会显示not found

安全警告:不建议对任何不可信的第三方程序运行ldd。因为某些版本的ldd可能会直接执行该程序来确定其依赖关系,这可能带来安全风险。

ldd命令详细模式ldd --verbose

通过--verbose选项,可以查看更详细的信息,包括符号版本要求。这对于排查“在A机器上能运行,在B机器上不能运行”的问题非常有帮助。

$ ldd--verbose/usr/bin/bash linux-vdso.so.1(0x00007ffce299c000)libtinfo.so.6=>/lib/x86_64-linux-gnu/libtinfo.so.6(0x00007f6fb24dd000)# ... 其他库信息 ...Version information: /usr/bin/bash: libdl.so.2(GLIBC_2.2.5)=>/lib/x86_64-linux-gnu/libdl.so.2 libc.so.6(GLIBC_2.11)=>/lib/x86_64-linux-gnu/libc.so.6# ... 其他版本信息 ...

objdumpreadelf命令

对于来自不可信来源的二进制文件,使用objdumpreadelf是更安全的选择。它们直接读取 ELF 文件的元数据,而不会执行任何代码。

  • objdump: 通过-p选项查看程序头,并筛选出NEEDED的库。

    $ objdump-p/usr/bin/git|grepNEEDED NEEDED libpcre.so.3 NEEDED libz.so.1 NEEDED libresolv.so.2 NEEDED libpthread.so.0 NEEDED libc.so.6
  • readelf: 通过--dynamic选项查看动态段信息,同样筛选NEEDED条目。

    $ readelf--dynamic/usr/bin/bash|grepNEEDED 0x0000000000000001(NEEDED)Shared library:[libtinfo.so.6]0x0000000000000001(NEEDED)Shared library:[libdl.so.2]0x0000000000000001(NEEDED)Shared library:[libc.so.6]

ld.so(动态链接器直接调用)

动态链接器本身可以模拟加载,效果等价于ldd底层逻辑。

/lib64/ld-linux-x86-64.so.2 ./target_bin# x86_64架构,直接调用动态链接器打印依赖

检查运行中进程的依赖

以下方法用于分析一个已经启动的进程,查看其实际加载到内存中的库。

pldd命令

pldd命令可以显示一个正在运行的进程所加载的所有共享对象。执行此命令需要root权限。

$sudopldd11491149: /usr/sbin/sshd linux-vdso.so.1 /lib/x86_64-linux-gnu/libwrap.so.0 /lib/x86_64-linux-gnu/libpam.so.0 /lib/x86_64-linux-gnu/libselinux.so.1# ... 其他库路径 ...

pmap命令

pmap命令用于报告一个进程的内存映射。通过筛选输出,可以列出该进程使用的所有共享库。

$sudopmap14601460: /usr/sbin/sshd-D00007f1b69e63000 44K r-x-- libnss_files-2.19.so 00007f1b69e6e000 2044K ----- libnss_files-2.19.so# ... 其他内存映射 ...00007f1b6a485000 92K r-x-- libresolv-2.19.so

lsof命令

列出进程打开的文件,通过筛选内存映射(mem)类型的文件来查看共享库。

$lsof-p$(pgrepbash|head-n1)|grepmembash4470user mem REG8,151672404577/usr/lib/x86_64-linux-gnu/libnss_files-2.29.sobash4470user mem REG8,12000480403822/usr/lib/x86_64-linux-gnu/libc-2.29.so# ... 其他库信息 ...

适用场景:程序已经启动,需要查看当前实际加载的库,可捕获运行期dlopen加载的库;无法用于未启动的可执行文件。

/proc文件系统

可以直接读取进程的内存映射文件来获取库信息。

$awk'/\.so/{print $6}'/proc/$(pgrepbash|head-n1)/maps|sort-u/usr/lib/x86_64-linux-gnu/ld-2.29.so /usr/lib/x86_64-linux-gnu/libc-2.29.so /usr/lib/x86_64-linux-gnu/libdl-2.29.so /usr/lib/x86_64-linux-gnu/libnss_files-2.29.so /usr/lib/x86_64-linux-gnu/libtinfo.so.6.1

扫描目录下全部可执行文件并统计共享库引用频次

基础脚本:扫描指定目录,统计共享库引用频次

以扫描/bin目录下全部可执行文件为例:

find/bin-typef-perm/a+x-execldd{}\;2>/dev/null\|grep'so'\|sed-e's/^\t//'\|sed-e's/.*=> //'\|sed-e's/ (0x.*//'\|sort\|uniq-c\|sort-nr
  • find /bin -type f -perm /a+x:在/bin目录检索普通文件(-type f),且对所有用户开放可执行权限(-perm /a+x)。如需扫描整个系统,可将/bin替换为/,该操作需要root权限,执行耗时更长。
  • -exec ldd {} \;:对检索到的每一个可执行文件执行lddldd打印该程序依赖的共享库列表。
  • 2>/dev/null:将错误信息(静态链接文件等产生的报错)重定向至空设备,清理输出内容。
  • grep 'so':过滤输出行,仅保留包含so(共享对象 Shared Object)的行,剔除静态链接与无关信息。
  • sed数据清洗:
    • s/^\t//:移除行首制表符
    • s/.*=> //:删除库路径前的文本,仅保留库文件路径,例如/lib64/libc.so.6
    • s/ (0x.*//:剔除内存地址片段,例如(0x00007f...)
  • sort | uniq -c | sort -nr
    1. sort:对库路径排序,为去重统计做准备
    2. uniq -c:统计重复行数量,对应单个库被程序引用的次数
    3. sort -nr:按数字(-n)逆序(-r)排列,高引用次数的库置于输出上方

输出样例(仅/bin目录)

1 /lib64/libexpat.so.0 1 /lib64/libgcc_s.so.1 1 /lib64/libnsl.so.1 1 /lib64/libpcre.so.0 1 /lib64/libproc-3.2.7.so 1 /usr/lib64/libbeecrypt.so.6 1 /usr/lib64/libbz2.so.1 1 /usr/lib64/libelf.so.1 1 /usr/lib64/libpopt.so.0 1 /usr/lib64/librpm-4.4.so 1 /usr/lib64/librpmdb-4.4.so 1 /usr/lib64/librpmio-4.4.so 1 /usr/lib64/libsqlite3.so.0 1 /usr/lib64/libstdc++.so.6 1 /usr/lib64/libz.so.1 2 /lib64/libasound.so.2 2 /lib64/libblkid.so.1 2 /lib64/libdevmapper.so.1.02 2 /lib64/libpam_misc.so.0 2 /lib64/libpam.so.0 2 /lib64/libuuid.so.1 3 /lib64/libaudit.so.0 3 /lib64/libcrypt.so.1 3 /lib64/libdbus-1.so.3 4 /lib64/libresolv.so.2 4 /lib64/libtermcap.so.2 5 /lib64/libacl.so.1 5 /lib64/libattr.so.1 5 /lib64/libcap.so.1 6 /lib64/librt.so.1 7 /lib64/libm.so.6 9 /lib64/libpthread.so.0 13 /lib64/libselinux.so.1 13 /lib64/libsepol.so.1 22 /lib64/libdl.so.2 83 /lib64/ld-linux-x86-64.so.2 83 /lib64/libc.so.6

反向检索脚本:查找指定目录下依赖某库的全部可执行文件

以检索依赖libc.so.6的程序为例:

\# 遍历 /bin 目录下所有用户可执行的普通文件forfilein$(find/bin-typef-perm/a+x);do\# 检查文件是否依赖 libc.so.6(屏蔽静态链接文件的 stderr 报错)ifldd"$file"2>/dev/null|grep-q"libc.so.6";thenecho"$file"fidone

安全注意事项

ldd内部依靠 Linux 动态链接器,在处理特制恶意 ELF 文件时存在执行代码风险。不可信二进制文件禁止使用ldd,优先使用readelfobjdump静态解析。

ldd通过设置特殊环境变量运行目标可执行文件,Linux 动态链接器识别该标记后仅打印库信息,而不运行程序本体。ldd在多数系统上是 bash 脚本。若可执行文件为静态链接、使用自定义系统调用并指定其他加载器,则该文件可执行任意操作。不要对不可信二进制文件使用ldd

针对不受信文件,使用以下命令读取依赖更为安全:

objdump-p<file>|grepNEEDED# 或readelf-d<file>|grepNEEDED

各工具对比简表

工具是否执行程序能否获取传递依赖能否捕获 dlopen
ldd仅程序启动阶段
readelf
objdump
lsof否(附着到已运行进程)是(进程当前已加载)是(进程运行后dlopen加载)

补充:dlopen动态加载共享库的统计与排查方案

dlopen动态加载的共享库无法被ldd捕获,这类库不会计入统计结果。

dlopen的功能定位为运行时动态加载库并返回句柄,并非统计工具。若仅为了获取已加载库的列表而反复调用dlopen,会引入性能开销与资源泄漏风险。针对不同的排查需求,应采用系统提供的标准机制。

一、 不建议使用dlopen进行统计的原因

  1. 性能开销dlopen涉及文件读取、内存映射、符号解析与重定位。若动态库数量较多(如 300~400 个),会显著拖慢程序启动或运行速度。
  2. 引用计数机制:每次dlopen会增加库的引用计数,需配合dlclose释放。若未正确释放会导致资源泄漏;若在释放后仍有指针在使用,则会导致程序崩溃(SIGSEGV)。
  3. 重复加载问题:动态链接器会对已加载的库做缓存。多次dlopen同一库通常只增加引用计数,并不会重新加载,这使得“通过加载次数统计”的逻辑变得复杂且不准确。

二、 正确的统计与排查方案

根据需求场景,推荐以下标准方案:

  1. 运行时统计(查看当前进程已加载的库)
    若需查看程序运行中实际加载了哪些库(含dlopen加载的库),无需调用dlopen,可直接使用系统工具:

    • pldd <PID>:专门列出进程加载的动态共享对象,包含dlopen加载的库。
    • cat /proc/<PID>/maps | grep '\.so':查看进程内存映射,过滤出共享库路径。
    • lsof -p <PID> | grep '\.so':列出进程打开的.so文件。
    • pmap <PID> | grep '\.so':查看进程内存映射及共享库依赖。
  2. 启动时跟踪(查看加载过程与耗时)
    若需跟踪程序启动过程中哪些库被加载、耗时多久:

    • LD_DEBUG=libs,statistics ./your_app:启用动态链接器调试,输出库加载路径、符号解析及重定位统计信息。
    • strace -e open,mmap ./your_app:跟踪系统调用,观察.so文件的打开与内存映射过程。
  3. 代码内统计(程序内部获取已加载库列表)
    若需在代码中获取当前进程已加载的库列表,不应循环调用dlopen,而应使用:

    • dl_iterate_phdr()glibc提供的 API,可遍历当前进程已加载的所有共享对象,并获取其路径、地址、大小等信息。
    • 读取/proc/self/maps:在 Linux 下直接读取该文件并解析,即可获取当前进程的内存映射信息。
  4. 性能测试场景(统计加载耗时)
    若确实需要测量dlopen加载特定库的耗时(如性能优化场景),可编写专门的测试代码,但需注意:

    • 使用clock()gettimeofday()计时。
    • 每次加载后调用dlclose释放,确保下次是全新加载。
    • 仅用于测试,不可用于生产逻辑。

示例代码:

#include<stdio.h>#include<dlfcn.h>#include<time.h>intmain(){constchar*lib_path="/path/to/libtest.so";clock_tstart=clock();void*handle=dlopen(lib_path,RTLD_LAZY);clock_tend=clock();if(handle){printf("加载耗时: %.6f 秒\n",(double)(end-start)/CLOCKS_PER_SEC);dlclose(handle);// 务必释放}else{fprintf(stderr,"加载失败: %s\n",dlerror());}return0;}

编译时需链接-ldl

三、 方案总结

需求场景推荐方案是否调用dlopen
查看运行中进程加载了哪些库pldd/proc/PID/mapslsof
跟踪库加载过程与耗时LD_DEBUGstrace
代码内获取已加载库列表dl_iterate_phdr()、读取/proc/self/maps
性能测试:测量加载耗时专用测试代码(加载后立即关闭)仅测试用
生产环境业务逻辑正常dlopen/dlclose管理资源

dlopen是加载工具,不是查询工具。统计和查询应使用系统提供的自省机制(如pldddl_iterate_phdr/proc文件系统),避免在生产代码中滥用dlopen做统计。


Reference

  • How to check what libraries are used by a program or process on Linux
    https://www.xmodulo.com/check-library-dependency-program-process-linux.html
  • How to show shared library dependencies in Linux
    https://www.simplified.guide/linux/show-shared-library-dependency
  • How to show all shared libraries used by executables in Linux? - Stack Overflow
    https://stackoverflow.com/questions/50159/how-to-show-all-shared-libraries-used-by-executables-in-linux
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 10:29:59

智能体系统架构实践:隔离、集成与治理全解析

开头这段时间我一直在梳理智能体系统架构这件事。单看单个智能体本身&#xff0c;框架选型、Prompt编排、工具调用都不算难——难的是当多个智能体、多个数据源、多个业务系统真正跑在一起的时候&#xff0c;隔离、集成、治理这三件事怎么落地。我调研了一圈相关方案&#xff0…

作者头像 李华
网站建设 2026/9/13 10:27:27

二叉堆实现动态中位数计算的高效算法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 10:27:01

环形Halbach磁体阵列原理与工程实现指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 10:26:55

高项论文备考:优秀导师的四维教学法解析

1. 项目背景与核心命题解析"高项论文"通常指高级项目管理师认证考试中的论文写作部分&#xff0c;这是国内项目管理领域含金量极高的专业资质认证。而"老金"在这个语境中&#xff0c;指的是项目管理培训领域的资深专家金老师&#xff08;化名&#xff09;。…

作者头像 李华