什么是 .so 文件?
.so 文件是 Linux 系统中的共享对象文件(Shared Object),相当于 Windows 系统中的 DLL(动态链接库)文件。它们包含了可以在多个程序之间共享的代码和数据。
共享库的主要优势在于:
- 内存效率:多个程序可以共享同一份库代码
- 磁盘空间节省:库文件只需存储一份
- 易于更新:更新库文件无需重新编译使用它的程序
- 模块化设计:支持插件式架构和动态加载
常见 .so 文件类型
Linux 系统中常见的 .so 文件包括:
libc.so.6- C 标准库libpthread.so.0- POSIX 线程库libm.so.6- 数学函数库libdl.so.2- 动态加载库libstdc++.so.6- C++ 标准库
创建共享库
以下是一个简单的共享库创建示例:
1. 编写源代码 (math_utils.c)
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
2. 编译为共享库
// 首先编译为目标文件 gcc -fPIC -c math_utils.c // 创建共享库 gcc -shared -o libmath_utils.so math_utils.o // 或者一步完成 gcc -fPIC -shared -o libmath_utils.so math_utils.c
使用共享库
有两种主要方式使用共享库:编译时链接和运行时加载。
编译时链接
// 编译主程序 gcc main.c -L. -lmath_utils -o program // 运行时需要指定库路径 export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./program
运行时动态加载
使用 dlopen()、dlsym() 和 dlclose() 函数:
#include <stdio.h>
#include <dlfcn.h>
int main() {
void *handle;
int (*add_func)(int, int);
char *error;
// 打开共享库
handle = dlopen("./libmath_utils.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
return 1;
}
// 获取函数地址
add_func = dlsym(handle, "add");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
dlclose(handle);
return 1;
}
// 使用函数
printf("Result: %d\n", add_func(5, 3));
// 关闭库
dlclose(handle);
return 0;
}
提示:编译动态加载程序需要链接 dl 库:
gcc program.c -ldl -o program
查看和调试 .so 文件
常用的工具和命令:
ldd libname.so- 查看库的依赖关系nm libname.so- 列出符号表objdump -t libname.so- 显示目标文件信息readelf -Ws libname.so- 显示ELF文件信息strings libname.so- 提取可打印字符串
版本控制与命名规范
Linux 共享库通常采用版本化命名:
- so-name:如
libc.so.6- 主版本号,二进制兼容 - soname:包含版本信息的符号链接
- 文件名:如
libc-2.31.so- 完整版本
使用 ln -s 创建符号链接来管理版本:
libmath_utils.so -> libmath_utils.so.1 libmath_utils.so.1 -> libmath_utils.so.1.0.0
最佳实践与注意事项
- 使用
-fPIC编译位置无关代码 - 合理设计库的接口稳定性
- 注意符号可见性(默认全局可见)
- 处理好库的依赖关系
- 考虑线程安全性
- 提供适当的错误处理机制
安全提示:只从可信来源加载 .so 文件,避免动态加载不可信的库文件,防止代码注入攻击。