动态共享库的三个名称属性

动态共享库的三个名称属性

Content #

动态共享库通常是有版本的,并且按照一定规则安装到系统中。举个例子,一个名为 libfoo 的动态共享库,在安装的目录下文件集合通常是这样:

2022-03-10 12:28 libfoo.so -> libfoo.so.0.0.0*
2022-03-10 12:28 libfoo.so.0 -> libfoo.so.0.0.0*
2022-03-10 12:28 libfoo.so.0.0.0*

按惯例,每个动态共享库都有多个名字属性,包括 real name、soname 和 linker name。下面我们来分别看下。

  1. real name:实际包含共享库代码的那个文件的名字 (如上面例子中的 libfoo.so.0.0.0)。动态共享库的真实版本信息就在 real name 中,显然 real name 中的版本号符合语义版本规范,即 major.minor.patch。当两个版本的 major 号一致,说明是向后兼容的两个版本;

  2. soname:shared object name 的缩写,也是这三个名字中最重要的一个。无论是在编译阶段还是在运行阶段,系统链接器都是通过动态共享库的 soname (如上面例子中的 libfoo.so.0)来唯一识别共享库的。我们看到的 soname 实际上是仅包含 major 号的共享库名字;参看 SO-NAME

  3. linker name:编译阶段提供给编译器的名字(如上面例子中的 libfoo.so)。如果你构建的共享库的 real name 跟上面例子中 libfoo.so.0.0.0 类似,带有版本号,那么你在编译器命令中直接使用 -L path -lfoo 是无法让链接器找到对应的共享库文件的,除非你为 libfoo.so.0.0.0 提供了一个 linker name(如 libfoo.so,一个指向 libfoo.so.0.0.0 的符号链接)。 linker name 一般在共享库安装时手工创建。

动态共享库有了这三个名称属性,依赖管理就有了依据。但由于在链接的时候使用的是 linker name,而 linker name 并不带有版本号,真实版本与主机环境有关,因此要实现 C 应用的可重现构建还是比较难。在实践中,我们通常会使用专门的构建主机,项目组将该主机上的依赖管理起来,进而保证每次构建所使用的依赖版本是可控的。同时,应用部署的目标主机上的依赖版本也应该得到管理,避免运行时出现动态共享库版本不匹配的问题。

Viewpoints #

From #

大咖助阵|Tony Bai:Go 程序员拥抱 C 语言简明指南