对共享目标文件的运行时引用应始终引用版本化的文件名。版本化的文件名通常表示为文件名加版本号后缀。
如果共享目标文件的接口以不兼容的方式更改,会导致旧的应用程序中断。在这种情况下,应当使用新的版本化文件名分发新的共享目标文件。另外,还必须分发原始的版本化文件名,以提供旧的应用程序所需要的接口。
在跨一系列软件发行版生成应用程序时,应当在运行时环境中以单独的版本化文件名提供共享目标文件。这样可以保证生成应用程序所依据的接口在应用程序执行期间可用于绑定。
以下各节介绍了如何在编译和运行时环境之间协调接口的绑定。
链接编辑通常使用链接编辑器的 -l 选项引用共享目标文件依赖项。此选项使用链接编辑器的库搜索机制定位前缀为 lib、后缀为 .so 的共享目标文件。
但是,在运行时,任何共享目标文件依赖项都应当作为版本化文件名存在。我们不保留两个遵循不同命名约定的不同共享目标文件,而是在两个文件名之间创建文件系统链接。
例如,可以使用符号链接将共享目标文件 libfoo.so.1 设为可用于编译环境。编译文件名是指向运行时文件名的一个符号链接。
$ cc -o libfoo.so.1 -G -K pic foo.c $ ln -s libfoo.so.1 libfoo.so $ ls -l libfoo* lrwxrwxrwx 1 usr grp 11 1991 libfoo.so -> libfoo.so.1 -rwxrwxr-x 1 usr grp 3136 1991 libfoo.so.1
符号链接和硬链接均可使用。但是,作为一种文档和诊断的辅助手段,符号链接更为实用。
已经为运行时环境生成了共享目标文件 libfoo.so.1。符号链接 libfoo.so 还使该文件可以用在编译环境中。
$ cc -o prog main.o -L. -lfoo
链接编辑器使用共享目标文件 libfoo.so.1 描述的接口处理可重定位目标文件 main.o,按照符号链接 libfoo.so 可以找到该共享目标文件。
经过一系列软件发行版,新版本的 libfoo.so 可以使用已更改的接口进行分发。通过更改符号链接,可以将编译环境构造为使用适用的接口。
$ ls -l libfoo* lrwxrwxrwx 1 usr grp 11 1993 libfoo.so -> libfoo.so.3 -rwxrwxr-x 1 usr grp 3136 1991 libfoo.so.1 -rwxrwxr-x 1 usr grp 3237 1992 libfoo.so.2 -rwxrwxr-x 1 usr grp 3554 1993 libfoo.so.3
在此示例中,有三个主要的共享目标文件版本可用。libfoo.so.1 和 libfoo.so.2 这两个版本提供现有应用程序的依赖项。libfoo.so.3 提供用于创建和运行新应用程序的最新主要发行版。
仅仅使用这种符号链接机制,并不足以协调编译共享目标文件与运行时版本化文件名。如示例所体现的,链接编辑器将其处理的共享目标文件的文件名记录在动态可执行文件 prog 中。在这种情况下,链接编辑器看到的文件名是编译环境文件。
$ elfdump -d prog | grep libfoo [0] NEEDED 0x1b7 libfoo.so
当执行应用程序 prog 时,运行时链接器会搜索依赖项 libfoo.so。prog 会绑定到该符号链接所指向的文件。
为了确保将正确的运行时名称记录为依赖项,应当使用 soname 定义生成共享目标文件 libfoo.so.1 。此定义可以识别共享目标文件的运行时名称。此名称将作为依赖项名称,供任何依据该共享目标文件进行链接的目标文件使用。在创建共享目标文件期间,可以使用 -h 选型提供此定义。
$ cc -o libfoo.so.1 -G -K pic -h libfoo.so.1 foo.c $ ln -s libfoo.so.1 libfoo.so $ cc -o prog main.o -L. -lfoo $ elfdump -d prog | grep libfoog [0] NEEDED 0x1b7 libfoo.so.1
此符号链接和 soname 机制在编译环境和运行时环境的共享目标文件命名约定之间建立了一种强健的协调方式。链接编辑期间处理的接口将准确记录在所生成的输出文件中。这种记录功能可以确保在运行时提供所需要的接口。
创建新的外部版本化共享目标文件属于一项重大更改。请确保您了解任何使用其中一个外部版本化共享目标文件的进程的完整依赖项。
例如,某个应用程序可能具有一个对 libfoo.so.1 的依赖项和一个外部提供的目标文件 libISV.so.1。后者也可能具有对 libfoo.so.1 的依赖项。应用程序可以重新设计,以使用 libfoo.so.2 中的新接口。但是,应用程序可能无法更改对外部目标文件 libISV.so.1 的使用。根据运行时装入的 libfoo.so 实现的可见性作用域,文件的两个主要版本均可引入正在运行的进程。更改 libfoo.so 的版本的唯一原因是要标记一项不兼容的更改。因此,在一个进程内使用目标文件的两个版本会导致不正确的符号绑定,并因此而产生意外的交互。
应当避免不兼容的接口更改。仅当您可以完全控制接口定义和引用此定义的所有目标文件时,才可以考虑进行不兼容的更改。