文章目录
-
- [1. 背景](#1. 背景)
- [2. 符号定义与链接关系](#2. 符号定义与链接关系)
- [3. 编译与运行阶段的 `so`](#3. 编译与运行阶段的
so) - [4. 设计原因](#4. 设计原因)
- [5. 设计思想](#5. 设计思想)
Linux下3个so库之间的关系
1. 背景
最近在看SDK编译的动态库目录时,看到基本同名库都是3个出现,分别是 .so, .so.x, .so.x.y.z,实际看了一下文件信息,都是执行最后一个文件的软链接。于是了解了下三者的关系,在此处进行记录。
2. 符号定义与链接关系
| 角色 | 示例 | 本质 | 核心作用 |
|---|---|---|---|
| realname(真实文件名) | libcurl.so.4.8.0 |
实际二进制文件 | 包含完整主/次/修订版本号,存储具体代码 |
| soname(共享对象名) | libcurl.so.4 |
符号链接 → realname | 仅含主版本号,记录在 ELF 的 SONAME 字段中 |
| linkername(链接器名) | libcurl.so |
符号链接 → soname | 编译时 -l 参数指定的名称(如 -lcurl) |
典型链接链 :
libcurl.so → libcurl.so.4 → libcurl.so.4.8.0
(升级时仅需更新 soname 符号指向:libcurl.so.4 → libcurl.so.4.9.0)
也有部分形式是符号链接直接指向真实文件名:
libcurl.so →libcurl.so.4.8.0
libcurl.so → libcurl.so.4.8.0
看一下实际的ffmpeg编译出来的.so关系
shell
总用量 379M
drwxr-xr-x 5 root root 4.0K 2月 5 22:32 .
drwxr-xr-x 12 root root 4.0K 2月 17 2023 ..
-rw-r--r-- 1 root root 220M 2月 5 22:32 libavcodec.a
lrwxrwxrwx 1 root root 23 2月 5 22:32 libavcodec.so -> libavcodec.so.62.11.100
lrwxrwxrwx 1 root root 23 2月 5 22:32 libavcodec.so.62 -> libavcodec.so.62.11.100
-rwxr-xr-x 1 root root 15M 2月 5 22:32 libavcodec.so.62.11.100
-rw-r--r-- 1 root root 1.9M 2月 5 22:32 libavdevice.a
lrwxrwxrwx 1 root root 23 2月 5 22:32 libavdevice.so -> libavdevice.so.62.1.100
lrwxrwxrwx 1 root root 23 2月 5 22:32 libavdevice.so.62 -> libavdevice.so.62.1.100
-rwxr-xr-x 1 root root 104K 2月 5 22:32 libavdevice.so.62.1.100
-rw-r--r-- 1 root root 60M 2月 5 22:32 libavfilter.a
lrwxrwxrwx 1 root root 23 2月 5 22:32 libavfilter.so -> libavfilter.so.11.4.100
lrwxrwxrwx 1 root root 23 2月 5 22:32 libavfilter.so.11 -> libavfilter.so.11.4.100
-rwxr-xr-x 1 root root 4.5M 2月 5 22:32 libavfilter.so.11.4.100
-rw-r--r-- 1 root root 56M 2月 5 22:32 libavformat.a
lrwxrwxrwx 1 root root 23 2月 5 22:32 libavformat.so -> libavformat.so.62.3.100
lrwxrwxrwx 1 root root 23 2月 5 22:32 libavformat.so.62 -> libavformat.so.62.3.100
-rwxr-xr-x 1 root root 2.9M 2月 5 22:32 libavformat.so.62.3.100
-rw-r--r-- 1 root root 7.9M 2月 5 22:32 libavutil.a
lrwxrwxrwx 1 root root 21 2月 5 22:32 libavutil.so -> libavutil.so.60.8.100
lrwxrwxrwx 1 root root 21 2月 5 22:32 libavutil.so.60 -> libavutil.so.60.8.100
-rwxr-xr-x 1 root root 999K 2月 5 22:32 libavutil.so.60.8.100
-rw-r--r-- 1 root root 540K 5月 21 2023 libmujs.a
-rw-r--r-- 1 root root 856K 2月 5 22:32 libswresample.a
lrwxrwxrwx 1 root root 24 2月 5 22:32 libswresample.so -> libswresample.so.6.1.100
lrwxrwxrwx 1 root root 24 2月 5 22:32 libswresample.so.6 -> libswresample.so.6.1.100
-rwxr-xr-x 1 root root 119K 2月 5 22:32 libswresample.so.6.1.100
-rw-r--r-- 1 root root 9.3M 2月 5 22:32 libswscale.a
lrwxrwxrwx 1 root root 21 2月 5 22:32 libswscale.so -> libswscale.so.9.1.100
lrwxrwxrwx 1 root root 21 2月 5 22:32 libswscale.so.9 -> libswscale.so.9.1.100
-rwxr-xr-x 1 root root 731K 2月 5 22:32 libswscale.so.9.1.100
其实本质都是一样的,因为链接库 ld在链接阶段实际会把 SONAME写入到可运行文件中,只要SONAME指向的realname,就可以找到最终的库。
3. 编译与运行阶段的 so
-
编译阶段
gcc main.c -lcurl→ 链接器通过linkername(libcurl.so)定位库 → 读取其SONAME(libcurl.so.4)→ 将SONAME写入可执行文件的NEEDED段 (readelf -d main | grep NEEDED可验证)。
这里实际看一下
ffmpeg下编解码库libavcodec.so的SONAME字段:shellwangzhonglai@shell:/usr/local/lib$ readelf -d libavcodec.so* | grep SONAME 0x000000000000000e (SONAME) Library soname: [libavcodec.so.62] 0x000000000000000e (SONAME) Library soname: [libavcodec.so.62] 0x000000000000000e (SONAME) Library soname: [libavcodec.so.62]可以看到实际3个
SONAME字段相同,而编译链接阶段只会把SONAME写到可执行文件的NEEDED子段。 -
运行阶段
动态链接器(
ld-linux.so)根据可执行文件中的SONAME(libcurl.so.4)→ 在/etc/ld.so.cache或LD_LIBRARY_PATH中查找 → 加载对应realname(libcurl.so.4.8.0)。
✅ 验证命令:
ldd ./program→ 显示程序实际依赖的 soname (如libcurl.so.4 => /usr/lib/libcurl.so.4)
4. 设计原因
| 问题 | 传统方案缺陷 | 三层命名机制解决方案 |
|---|---|---|
| 库升级需重编译 | 若程序直接链接 realname(如 libcurl.so.4.8.0),版本更新后程序失效 |
soname 作为桥梁:升级仅需更新符号链接,程序无需重编译(因依赖的是 libcurl.so.4) |
| ABI 兼容性保障 | 无法区分兼容/不兼容更新 | 主版本号隔离:soname 仅含主版本号(x),主版本变化(x→x+1)表示 ABI 不兼容,确保程序加载正确版本 |
| 多版本共存冲突 | 多程序依赖不同版本库时易冲突 | 独立 soname 空间:libcurl.so.3 与 libcurl.so.4 可同时存在,各程序按需加载 |
| 开发与部署解耦 | 开发时需指定精确版本,部署复杂 | linkername 简化编译:开发用 -lcurl(指向 linkername),部署时系统自动匹配兼容 soname |
- 安全更新 :
libssl.so.1.1.1k→libssl.so.1.1.1l(仅修订号变)
→ 更新libssl.so.1.1符号链接指向新文件,所有依赖libssl.so.1.1的程序无需重编译即可生效。 - 重大升级 :OpenSSL 1.1 → 3.0(主版本变)
→ 生成新 sonamelibssl.so.3,旧程序仍用libssl.so.1.1,避免断裂式升级。
5. 设计思想
- 职责分离 :
linkername服务开发者(编译便捷)soname服务系统(运行时精准定位)realname服务维护者(版本追溯与回滚)
- 向后兼容基石:主版本号变更即 ABI 断裂点,强制程序显式适配新接口。
- 生态稳定性 :通过
ldconfig自动管理符号链接与缓存(/etc/ld.so.cache),平衡灵活性与系统可靠性。
此设计是 Linux 软件包管理(如
.deb/.rpm)和跨发行版兼容的底层支柱,完美平衡了开发效率、运行安全、系统维护三大核心需求 。