Linux下查看进程用到了哪些动态库的函数
Linux下查看进程用到了哪些动态库的函数
Linux下查看进程用到了哪些动态库的接口
在Linux下,查看进程使用的动态库及其接口,可以通过以下方法实现:
1. 查看进程加载的动态库文件
方法 1:通过 /proc/<PID>/maps
文件
1 | cat /proc/<PID>/maps | grep '\.so' |
- 说明:
列出进程内存映射中所有加载的.so
动态库文件路径。
替换<PID>
为实际进程ID。
方法 2:使用 pmap
命令
1 | pmap -x <PID> | grep '\.so' |
- 说明:
显示进程的内存映射,过滤出动态库路径。
方法 3:使用 lsof
命令
1 | lsof -p <PID> | grep '\.so' |
- 说明:
列出进程打开的文件,过滤出共享库文件(.so
)。
2. 查看动态库导出的接口(符号)
方法 1:使用 nm
命令
1 | nm -D /path/to/library.so |
- 说明:
-D
选项显示动态符号表(导出的接口)。
示例输出:1
20000000000003a60 T malloc
0000000000009400 T pthread_createT
表示全局函数(即接口)。
方法 2:使用 objdump
命令
1 | objdump -T /path/to/library.so |
- 说明:
显示动态库的动态符号表(导出的函数接口)。
方法 3:使用 readelf
命令
1 | readelf -s --dyn-syms /path/to/library.so |
- 说明:
--dyn-syms
选项列出动态符号(导出的接口)。
3. 实时监控进程调用的动态库接口
使用 ltrace
工具
1 | ltrace -p <PID> -l /path/to/library.so |
- 说明:
-p <PID>
:附加到目标进程。-l lib.so
:只监控特定库的调用(可省略)。
示例输出:显示进程调用的库函数及其参数。1
2libc.so.6->malloc(1024) = 0x55a6d5eeb260
libpthread.so.0->pthread_create(0x7ffd4a260d50, NULL, 0x55a6d5c8d1a0, NULL) = 0
4. 查看进程依赖的所有动态库
使用 ldd
命令(针对可执行文件)
1 | ldd /path/to/program |
- 注意:
ldd
只能查看可执行文件的依赖库(非运行时进程)。
查看运行中进程的库依赖需结合/proc/<PID>/maps
。
完整流程示例
假设进程ID为 1234
,查看其使用的 libc
接口:
- 找到
libc
库路径:1
2cat /proc/1234/maps | grep libc.so
# 输出示例:/usr/lib/x86_64-linux-gnu/libc.so.6 - 列出
libc
的导出接口:1
2
3
4nm -D /usr/lib/x86_64-linux-gnu/libc.so.6 | grep ' T ' | head -n 5
# 输出示例:
# 00000000000a7c50 T malloc
# 00000000000a7ce0 T free - 实时监控进程调用:
1
ltrace -p 1234 -l libc.so.6
总结
目标 | 命令/方法 |
---|---|
查看加载的动态库 | cat /proc/<PID>/maps | grep '\.so' |
列出动态库的导出接口 | nm -D /path/to/lib.so |
监控进程调用的库函数 | ltrace -p <PID> -l lib.so |
查看可执行文件的库依赖 | ldd /path/to/program |
注意:部分操作需
sudo
权限(如监控非当前用户的进程)。
dlopen函数用法及示例
dlopen
函数详解
dlopen
是 Linux 动态链接库的核心函数,用于在运行时动态加载共享库(.so
文件)。它允许程序按需加载库,无需在编译时静态链接,常用于插件系统、模块化设计等场景。
函数原型
1 |
|
参数说明:
filename
- 共享库路径(如
"libexample.so"
) - 若为
NULL
,返回主程序自身的句柄(用于访问全局符号)。
- 共享库路径(如
**
flags
**(常用标志组合)标志 作用 RTLD_LAZY
延迟绑定(推荐):在首次调用函数时解析符号(提高启动速度)。 RTLD_NOW
立即解析所有符号:加载时检查所有符号有效性,失败则返回错误。 RTLD_GLOBAL
使库的符号全局可见(可供后续加载的库使用)。 RTLD_LOCAL
符号仅对本库可见(默认)。
返回值:
- 成功:返回共享库的句柄(
void*
类型) - 失败:返回
NULL
,错误信息通过dlerror()
获取。
关联函数
函数 | 作用 |
---|---|
void* dlsym(void* handle, const char* symbol) |
从库中获取符号(函数/变量)地址 |
int dlclose(void* handle) |
关闭共享库(引用计数减为0时卸载) |
char* dlerror(void) |
获取最后一次错误信息(无错误返回 NULL ) |
使用步骤
- 用
dlopen
打开共享库 - 用
dlsym
获取符号地址 - 调用符号(函数或访问变量)
- 用
dlclose
关闭库 - 用
dlerror
检查错误
示例 1:动态加载数学库(libm.so
)
1 |
|
编译命令(需链接 dl
库):
1 | gcc -o math_demo math_demo.c -ldl |
示例 2:加载自定义共享库
步骤 1:创建共享库 libhello.so
1 | // hello.c |
编译为共享库:
1 | gcc -shared -fPIC -o libhello.so hello.c |
步骤 2:主程序动态加载 libhello.so
1 | // main.c |
编译并运行:
1 | gcc -o hello_demo main.c -ldl |
关键注意事项
路径问题
- 若库不在标准路径(如
/usr/lib
),需使用绝对路径(如"/home/user/libhello.so"
)或相对路径(如"./libhello.so"
)。
- 若库不在标准路径(如
符号可见性
- 共享库中的函数需声明为
extern
(默认)或使用编译选项-fvisibility=default
。
- 共享库中的函数需声明为
错误处理
- 每次调用后检查
dlerror()
(因连续调用会清除错误信息)。
- 每次调用后检查
资源释放
dlclose()
减少引用计数,计数为 0 时卸载库(避免内存泄漏)。
C++ 兼容性
- C++ 函数需用
extern "C"
避免名称修饰(Name Mangling):1
2
3extern "C" {
void say_hello(const char*);
}
- C++ 函数需用
常见错误场景
错误现象 | 可能原因 | 解决方案 |
---|---|---|
dlopen: file not found |
库路径错误 | 指定绝对路径或设置 LD_LIBRARY_PATH |
dlsym: undefined symbol |
函数名错误或未导出 | 用 nm -D lib.so 检查符号表 |
段错误(Segmentation Fault) | 函数指针类型不匹配 | 确保函数指针声明正确 |
进阶技巧
加载主程序符号
1
2void *self_handle = dlopen(NULL, RTLD_LAZY);
void *func = dlsym(self_handle, "local_function");控制符号可见性(GCC)
在库源码中声明导出符号:1
__attribute__((visibility("default"))) void public_func();
调试工具
- 使用
LD_DEBUG=libs
环境变量跟踪加载过程:1
LD_DEBUG=libs ./your_program
- 使用
通过 dlopen
实现动态加载,可以显著提升程序的灵活性和可扩展性。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Helloeuler!