链接程序和库指南

命名约定

链接编辑器和运行时链接程序都不根据文件名解释文件。将检查所有文件以确定其 ELF 类型(请参见ELF 头)。链接编辑器使用此信息来推导文件的处理要求。但是,共享库通常遵循两种命名约定之一,具体取决于这些目标文件是用作编译环境的一部分还是用作运行时环境的一部分。

当共享库用作编译环境的一部分时,链接编辑器将读取和处理这些共享库。虽然可以在传递到链接编辑器的命令中根据显式文件名指定这些共享库,但是通常使用 -l 选项来利用链接编辑器的库搜索功能。 请参见共享库处理

应该使用前缀 lib 和后缀 .so 来指定适用于此链接编辑器处理的共享库。例如,/lib/libc.so 便是可用于编译环境的标准 C 库的共享库。根据约定,64 位共享库位于 lib 目录名为 64 的子目录中。例如,/lib/libc.so.1 的对应 64 位名称是 /lib/64/libc.so.1

当共享库用作运行时环境的一部分时,运行时链接程序将读取和处理这些共享库。要允许在一系列软件发行版中对共享库的导出接口进行更改,请将共享库作为版本化文件名提供。

版本化文件名通常采用 .so 后缀后跟版本号的形式。例如,/lib/libc.so.1 便是可用于运行时环境的第一版标准 C 库的共享库。

如果从不打算在编译环境中使用共享库,则可能会从共享库名称中删除常规的 lib 前缀。仅用于 dlopen(3C) 的共享库便是此类共享库。仍然建议使用后缀 .so 来表明实际文件类型。此外,强烈建议使用版本号以便在一系列软件发行版中提供正确的共享库绑定。第 5 章,应用程序二进制接口与版本控制更详细地介绍了版本控制。


注 –

dlopen(3C) 中使用的共享库名称通常表示为不包含 "/" 的简单文件名。然后,运行时链接程序可以使用一组规则来查找实际文件。 有关更多详细信息,请参见装入其他目标文件


记录共享库名称

缺省情况下,动态可执行文件或共享库中的依赖项记录将是链接编辑器所引用的关联共享库的文件名。例如,根据同一共享库 libfoo.so 生成的以下动态可执行文件会导致对同一依赖项具有不同的解释。


$ cc -o ../tmp/libfoo.so -G foo.o

$ cc -o prog main.o -L../tmp -lfoo

$ dump -Lv prog | grep NEEDED

[1]     NEEDED   libfoo.so



$ cc -o prog main.o ../tmp/libfoo.so

$ dump -Lv prog | grep NEEDED

[1]     NEEDED   ../tmp/libfoo.so



$ cc -o prog main.o /usr/tmp/libfoo.so

$ dump -Lv prog | grep NEEDED

[1]     NEEDED   /usr/tmp/libfoo.so

如这些示例所示,这种记录依赖项机制会因编译技术的不同而出现不一致性。此外,在链接编辑过程中引用的共享库的位置也可能会与已安装系统上的共享库的最终位置有所不同。为了提供更一致的指定依赖项的方法,共享库可以在自身中记录运行时引用共享库应依据的文件名。

在链接编辑共享库过程中,可以使用 -h 选项在共享库自身中记录其运行时名称。在以下示例中,将在文件自身中记录共享库的运行时名称 libfoo.so.1。此标识称为 soname


$ cc -o ../tmp/libfoo.so -G -K pic -h libfoo.so.1 foo.c

以下示例说明如何使用 dump(1) 并引用具有 SONAME 标记的项来显示 soname 记录。


$ dump -Lvp ../tmp/libfoo.so



../tmp/libfoo.so:

[INDEX] Tag      Value

[1]     SONAME   libfoo.so.1

.........

当链接编辑器处理包含 soname 的共享库时,此名称便是作为要生成的输出文件中的依赖项记录的名称。

如果在上一示例的创建动态可执行文件 prog 的过程中使用此新版本的 libfoo.so,则所有三种创建可执行文件的方法都会记录同一依赖项。


$ cc -o prog main.o -L../tmp -lfoo

$ dump -Lv prog | grep NEEDED

[1]     NEEDED   libfoo.so.1



$ cc -o prog main.o ../tmp/libfoo.so

$ dump -Lv prog | grep NEEDED

[1]     NEEDED   libfoo.so.1



$ cc -o prog main.o /usr/tmp/libfoo.so

$ dump -Lv prog | grep NEEDED

[1]     NEEDED   libfoo.so.1

在上述示例中,使用 -h 选项来指定不包含 "/" 的简单文件名。借助此约定,运行时链接程序可以使用一组规则来查找实际文件。 有关更多详细信息,请参见查找共享库依赖项

在归档文件中包含共享库

如果共享库始终通过归档库进行处理,则在共享库中记录 soname 是基本机制。

归档文件可以根据一个或多个共享库生成,并可用于生成动态可执行文件或共享库。可以从归档文件中提取共享库来满足链接编辑的要求。与处理可重定位的目标文件(串联成要创建的输出文件)不同,从归档文件中提取的任何共享库都将记录为依赖项。有关归档文件提取条件的更多详细信息,请参见归档处理

归档成员的名称由链接编辑器构造,并且由归档文件名称和归档文件中的目标文件串联而成。 例如:


$ cc -o libfoo.so.1 -G -K pic foo.c

$ ar -r libfoo.a libfoo.so.1

$ cc -o main main.o libfoo.a

$ dump -Lv main | grep NEEDED

[1]     NEEDED   libfoo.a(libfoo.so.1)

由于具有此串联名称的文件无法在运行时存在,因此,在共享库中提供 soname 是生成依赖项有意义运行时文件名的唯一方法。


注 –

运行时链接程序不会从归档文件中提取目标文件。因此,在上一示例中,必须从归档文件中提取所需的共享库依赖项并使其可用于运行时环境。


已记录名称冲突

使用共享库创建动态可执行文件或其他共享库时,链接编辑器会执行多项一致性检查。这些检查可确保输出文件中记录的任何依赖项名称都是唯一的。

如果用作链接编辑的输入文件的两个共享库包含相同的 soname,则会出现依赖项名称冲突。 例如:


$ cc -o libfoo.so -G -K pic -h libsame.so.1 foo.c

$ cc -o libbar.so -G -K pic -h libsame.so.1 bar.c

$ cc -o prog main.o -L. -lfoo -lbar

ld: fatal: recording name conflict: file `./libfoo.so' and \

    file `./libbar.so' provide identical dependency names: libsame.so.1

ld: fatal: File processing errors. No output written to prog

如果某个没有已记录 soname 的共享库的文件名与同一链接编辑过程使用的其他共享库的 soname 匹配,则也会出现类似的错误情况。

如果要生成的共享库的运行时名称与它的某个依赖项匹配,则链接编辑器也会报告名称冲突。


$ cc -o libbar.so -G -K pic -h libsame.so.1 bar.c -L. -lfoo

ld: fatal: recording name conflict: file `./libfoo.so' and \

    -h option provide identical dependency names: libsame.so.1

ld: fatal: File processing errors. No output written to libbar.so