Linux下3个so库之间的关系

文章目录

    • [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.solibcurl.so.4libcurl.so.4.8.0

(升级时仅需更新 soname 符号指向:libcurl.so.4libcurl.so.4.9.0

也有部分形式是符号链接直接指向真实文件名:

libcurl.solibcurl.so.4.8.0

libcurl.solibcurl.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

  1. 编译阶段
    gcc main.c -lcurl → 链接器通过 linkernamelibcurl.so)定位库 → 读取其 SONAMElibcurl.so.4)→ SONAME 写入可执行文件的 NEEDEDreadelf -d main | grep NEEDED 可验证)。


    这里实际看一下ffmpeg下编解码库libavcodec.soSONAME字段:

    shell 复制代码
    wangzhonglai@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子段。

  2. 运行阶段

    动态链接器(ld-linux.so)根据可执行文件中的 SONAMElibcurl.so.4)→ 在 /etc/ld.so.cacheLD_LIBRARY_PATH 中查找 → 加载对应 realnamelibcurl.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.3libcurl.so.4 可同时存在,各程序按需加载
开发与部署解耦 开发时需指定精确版本,部署复杂 linkername 简化编译:开发用 -lcurl(指向 linkername),部署时系统自动匹配兼容 soname
  • 安全更新libssl.so.1.1.1klibssl.so.1.1.1l(仅修订号变)
    → 更新 libssl.so.1.1 符号链接指向新文件,所有依赖 libssl.so.1.1 的程序无需重编译即可生效。
  • 重大升级 :OpenSSL 1.1 → 3.0(主版本变)
    → 生成新 soname libssl.so.3,旧程序仍用 libssl.so.1.1避免断裂式升级

5. 设计思想

  1. 职责分离
    • linkername 服务开发者(编译便捷)
    • soname 服务系统(运行时精准定位)
    • realname 服务维护者(版本追溯与回滚)
  2. 向后兼容基石:主版本号变更即 ABI 断裂点,强制程序显式适配新接口。
  3. 生态稳定性 :通过 ldconfig 自动管理符号链接与缓存(/etc/ld.so.cache),平衡灵活性与系统可靠性。

此设计是 Linux 软件包管理(如 .deb/.rpm)和跨发行版兼容的底层支柱,完美平衡了开发效率、运行安全、系统维护三大核心需求 。

相关推荐
释怀不想释怀6 小时前
Linux文件上传(rz)和下载(sz)压缩(tar.gz)和解压(zip)
linux·运维·服务器
IOsetting6 小时前
金山云主机添加开机路由
运维·服务器·开发语言·网络·php
酉鬼女又兒6 小时前
零基础入门Linux指南:每天一个Linux命令_sed
linux·运维·服务器
daad7776 小时前
tcpdump_BPF
linux·测试工具·tcpdump
予枫的编程笔记6 小时前
【Linux进阶篇】Linux网络配置+端口监听实战:ip/ss/iptables常用命令一次吃透
linux·iptables·网络配置·curl·端口监听·ping·ss命令
礼拜天没时间.7 小时前
深入Docker架构——C/S模式解析
linux·docker·容器·架构·centos
-dcr7 小时前
58.DevOps进阶
运维·devops
猫头虎7 小时前
如何使用Docker部署OpenClaw汉化中文版?
运维·人工智能·docker·容器·langchain·开源·aigc
XiaoMu_0017 小时前
自动化漏洞扫描与预警平台
运维·网络·自动化