获取dlopen动态库方法
2026/6/7 16:51:06 网站建设 项目流程

要获取一个二进制文件通过dlopen()动态加载的库,你不能依赖ldd这类静态分析工具。因为dlopen()是在程序运行时才决定加载哪些库,这些库的路径甚至可以是动态拼接的,所以它们不会写在文件的依赖信息里。

你可以根据具体场景,从下面几种方法中选择一种。

方法一:运行时拦截与追踪 (最推荐)

这是最可靠的方法。通过拦截程序对dlopen的调用,你可以实时、准确地记录下所有动态加载的库。

1. 使用LD_PRELOAD技术 (通用)

LTTng 项目提供了一个名为liblttng-ust-dl.so的库,它的工作原理就是利用LD_PRELOAD环境变量,在目标程序启动时抢先注入自己,从而钩取 (hook) 所有的dlopen调用并打印出加载的库路径。

操作步骤

# 安装 lttng-ust-tools (不同发行版包名可能略有不同) # 对于 Debian/Ubuntu: sudo apt install lttng-ust-tools # 对于 RHEL/CentOS: sudo yum install lttng-ust # 使用 LD_PRELOAD 运行你的程序 LD_PRELOAD=liblttng-ust-dl.so ./your_program

当程序运行时,所有dlopen加载的库路径都会被追踪并输出。这是专业性能分析工具(如 OpenResty XRay)使用的同款方法。

仅仅设置LD_PRELOAD=liblttng-ust-dl.so是不够的,你还需要通过lttng命令启动一个追踪会话,才能看到记录下来的.so库信息。

liblttng-ust-dl.so这个库的作用是记录事件(比如dlopen的调用),但它本身不会直接把结果打印在屏幕上。你需要像一个“录音师”一样,用 LTTng 的工具去控制录制和读取最终的“磁带”

完整的操作需要以下三个步骤:

🛠️ 操作步骤

你可以直接复制下面的完整命令块一次性执行:

# 1. 创建一个追踪会话 lttng create my-dlopen-session --output=/tmp/my-trace # 2. 启用必要的事件(这是最关键的一步!) # “这是一个来自用户空间( -u )的事件,我们想追踪它(lttng_ust_dl:dlopen)” lttng enable-event --userspace lttng_ust_dl:dlopen # 3. 启动追踪 lttng start # 4. 运行你的程序(此时,预加载的库会开始记录 dlopen 的细节) LD_PRELOAD=liblttng-ust-dl.so ./你的程序 # 5. 程序运行完毕后,停止追踪 lttng stop # 6. 销毁会话(这会触发将内存中的数据刷新到磁盘) lttng destroy
👀 如何查看结果?

追踪完成后,原始数据是二进制格式,需要使用babeltrace2这个工具来将它转换成人类可读的文本。

babeltrace2 /tmp/my-trace

如果一切顺利,你会看到类似下面的输出,其中path = "..."这一行就是你想要找的动态库路径:

[16:45:21.123456789] (+0.000000123) 主机名 lttng_ust_dl:dlopen: { cpu_id = 0 }, { baddr = 0x7f1234560000, memsz = 123456, flags = 1, path = "/home/user/test/libexample.so", # <-- 这就是你需要的库路径 has_build_id = 0, has_debug_link = 0 }
💡 补充说明
  1. 关于lttng_ust_dl:dlopen事件
    从搜索结果来看,lttng_ust_dl:dlopen事件中path字段记录的就是dlopen()调用时传递的路径。官方手册也指出,虽然liblttng-ust-dl.so也能产生其他类型的事件,但对于追踪库加载路径这个需求,直接使用lttng_ust_dl:dlopen是最直观和可靠的。

  2. 为何需要lttng enable-event
    这是新手最容易遗漏的一步。liblttng-ust-dl.so就像一个“摄像机”,而lttng enable-event命令则是告诉 LTTng 系统:“请开始录制这台摄像机拍到的画面”。没有这个命令,即使摄像机在运行,也没有人在保存录像。

2. 使用strace(系统调用追踪)

strace可以追踪进程发起的系统调用。dlopen底层会调用openopenat等系统调用来读取动态库文件。

操作步骤

# -e 指定只追踪 open, openat 系统调用,-f 跟踪子进程 strace -e open,openat -f ./your_program 2>&1 | grep "\.so"

这个方法的优点是几乎所有 Linux 系统都自带,但输出会比较杂乱,需要进行过滤。

方法二:静态分析与启发式方法 (作为备选)

当无法动态运行程序时(例如在离线分析或逆向工程中),可以尝试以下方法,但它们只能作为线索,无法保证100%准确

1. 使用strings命令

这是最简单但最不可靠的方法。strings命令可以从二进制文件中提取所有可打印字符序列。如果程序将动态库的路径以字符串常量形式写在代码中,它就能被提取出来。

操作步骤

# -n 6 指定字符串最小长度为6,然后过滤出包含 .so 的行 strings -n 8 ./your_program | grep "\.so"

局限性:如果库路径是运行时拼接的(例如lib+ version +.so),或者被加密/混淆了,这个方法就会失效。

方法三:检查/proc文件系统 (针对运行中进程)

如果你已经运行了一个进程(比如 PID 是 1234),可以直接查看它的内存映射表。

操作步骤

cat /proc/1234/maps | grep "\.so"

这个文件会列出进程当前加载的所有内存区域,包括所有直接链接和通过dlopen打开的共享库。这种方法简单直接且权威,因为它是系统内核给出的实际状态。

总结与对比

方法核心命令/操作优点缺点
运行时拦截 (最推荐)LD_PRELOAD=liblttng-ust-dl.so ./程序100%准确,专为此设计,信息清晰需要额外安装lttng-ust工具
系统调用追踪strace -e open ./程序 2>&1 | grep .so无需安装额外工具,通用性强输出冗余,需要从大量日志中筛选
静态字符串提取strings ./程序 | grep .so无需运行程序,快速简单只能发现硬编码路径,漏报率高
进程内存检查cat /proc/PID/maps | grep .so针对已在运行的进程,准确且直接需要进程已经在运行

对于绝大多数的分析需求,优先推荐LD_PRELOAD方法,这是专门为解决此类问题而设计的。

参考:lttng-tools和lttng-ust包详解-CSDN博客

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询