链接程序和库指南

绑定到版本定义

根据其他共享库生成动态可执行文件或共享库时,会在生成的目标文件中记录这些依赖项。有关更多详细信息,请参见共享库处理记录共享库名称。如果这些共享库依赖项还包含版本定义,则会在正在生成的目标文件中记录关联的版本依赖项。

以下示例采用上一节中的数据文件并生成适用于编译时环境的共享库。在随后的绑定示例中将使用此共享库 libfoo.so.1


$ cc -o libfoo.so.1 -h libfoo.so.1 -M mapfile -G foo.o bar.o \

data.o

$ ln -s libfoo.so.1 libfoo.so

$ pvs -dsv libfoo.so.1

        libfoo.so.1:

                _end;

                _GLOBAL_OFFSET_TABLE_;

                _DYNAMIC;

                _edata;

                _PROCEDURE_LINKAGE_TABLE_;

                _etext;

        SUNW_1.1:

                foo1;

                SUNW_1.1;

        SUNW_1.2:                {SUNW_1.1}:

                foo2;

                SUNW_1.2;

        SUNW_1.2.1 [WEAK]:       {SUNW_1.2}:

                SUNW_1.2.1;

        SUNW_1.3a:               {SUNW_1.2}:

                bar1;

                SUNW_1.3a;

        SUNW_1.3b:               {SUNW_1.2}:

                bar2;

                SUNW_1.3b

实际上,此共享库提供六个公共接口。其中 SUNW_1.1SUNW_1.2SUNW_1.3a 以及 SUNW_1.3b 这四个接口定义导出的符号名称。还有一个接口 SUNW_1.2.1 介绍共享库的内部实现更改,另一个接口 libfoo.so.1 定义一些保留标号。使用此共享库作为依赖项而创建的动态库将记录其绑定到的接口的版本名称。

以下示例将创建引用符号 foo1foo2 的应用程序。可以使用带有 -r 选项的 pvs(1) 来检查此应用程序中记录的版本控制依赖项信息。


$ cat prog.c

extern void foo1();

extern void foo2();



main()

{

        foo1();

        foo2();

}

$ cc -o prog prog.c -L. -R. -lfoo

$ pvs -r prog

        libfoo.so.1 (SUNW_1.2, SUNW_1.2.1);

在本示例中,应用程序 prog 绑定到两个接口 SUNW_1.1SUNW_1.2。这两个接口分别提供全局符号 foo1foo2

由于在 libfoo.so.1 内定义的版本定义 SUNW_1.1 由版本定义 SUNW_1.2 继承,因此还需要记录 SUNW_1.2 的版本依赖项。通过对版本定义依赖项进行这种标准化,可以减少目标文件内维护的版本信息量,并减少运行时需要进行的处理。

由于应用程序 prog 是根据包含弱版本定义 (weak version definition) SUNW_1.2.1 的共享库的实现而生成的,因此还会记录此依赖项。尽管此版本定义被定义为继承版本定义 SUNW_1.2,但此版本的弱性质不会包括通过 SUNW_1.1 进行的标准化,并且会生成单独的依赖项记录。

如果存在多个相互继承的弱版本定义 (weak version definition),则会按照标准化非弱版本定义 (non-weak version definition) 的方式来标准化这些定义。


注 –

可以使用链接编辑器的 -z noversion 选项来抑制记录版本依赖项。


记录了这些版本定义依赖项之后,运行时链接程序将验证执行应用程序时绑定到的目标文件中是否存在所需的版本定义。可以使用带有 -v 选项的 ldd(1) 来显示此验证。例如,通过对应用程序 prog 运行 ldd(1),便会显示在共享库 libfoo.so.1 中正确地找到了版本定义依赖项:


$ ldd -v prog



   find object=libfoo.so.1; required by prog

        libfoo.so.1 =>   ./libfoo.so.1

   find version=libfoo.so.1;

        libfoo.so.1 (SUNW_1.2) =>            ./libfoo.so.1

        libfoo.so.1 (SUNW_1.2.1) =>          ./libfoo.so.1

   ....

注 –

带有 -v 选项的 ldd(1) 意味着详细输出,将生成所有依赖项以及所有版本控制需求的递归列表。


如果找不到非弱版本定义 (non-weak version definition) 依赖项,则应用程序初始化过程中将发生致命错误。所有找不到的弱版本定义 (weak version definition) 依赖项将被忽略而无任何提示。例如,如果应用程序 proglibfoo.so.1 只包含版本定义 SUNW_1.1 的环境中运行,则会发生以下致命错误:


$ pvs -dv libfoo.so.1

        libfoo.so.1;

        SUNW_1.1;

$ prog

ld.so.1: prog: fatal: libfoo.so.1: version `SUNW_1.2' not \

found (required by file prog)

在应用程序 prog 未记录任何版本定义依赖项的情况下,如果必需的接口符号 foo2 不存在,则可能表明本身在执行应用程序期间的某个时刻出现致命的重定位错误。此重定位错误可能会在进程初始化时或进程执行过程中发生,也可能根本不会发生(如果应用程序的执行路径未调用函数 foo2)。 请参见重定位错误

记录版本定义依赖项也可以即时指出应用程序所需接口的可用性。

如果应用程序 proglibfoo.so.1 只包含版本定义 SUNW_1.1SUNW_1.2 的环境中运行,则会满足所有非弱版本定义 (non-weak version definition) 需求。缺少弱版本定义 (weak version definition) SUNW_1.2.1 被认为不具有致命性,因此不生成任何运行时错误状态。但是,可以使用 ldd(1) 来显示所有找不到的版本定义:


$ pvs -dv libfoo.so.1

        libfoo.so.1;

        SUNW_1.1;

        SUNW_1.2:                {SUNW_1.1};

$ prog

string used by foo1()

string used by foo2()

$ ldd prog

        libfoo.so.1 =>   ./libfoo.so.1

        libfoo.so.1 (SUNW_1.2.1) =>          (version not found)

        ...........

注 –

如果目标文件需要给定依赖项中的版本定义,并且在运行时找到此依赖项的实现,但此依赖项不包含版本定义信息,则会忽略此依赖项的版本验证而无任何提示。此策略提供从非版本化共享库转换到版本化共享库时的向下兼容性级别。但是,仍可使用 ldd(1) 来显示任何版本需求差异。可以使用环境变量 LD_NOVERSION 来抑制所有运行时版本验证。


检验附加目标文件中的版本

版本定义符号还提供了一种机制,可检验通过 dlopen(3C) 获取的目标文件的版本需求。使用此函数添加到进程地址空间的任何目标文件都不会使运行时链接程序执行自动版本依赖项验证。这样,此函数的调用程序将负责检验是否满足所有版本控制需求。

可以通过使用 dlsym(3C) 查找关联的版本定义符号来检验是否存在所需版本定义。在以下示例中,使用 dlopen(3C) 将共享库 libfoo.so.1 添加到进程中,并检验接口 SUNW_1.2 是否可用。


#include        <stdio.h>

#include        <dlfcn.h>

 

main()

{

        void *       handle;

        const char * file = "libfoo.so.1";

        const char * vers = "SUNW_1.2";

        ....

 

        if ((handle = dlopen(file, (RTLD_LAZY | RTLD_FIRST))) == NULL) {

                (void) printf("dlopen: %s\n", dlerror());

                exit (1);

        }

 

        if (dlsym(handle, vers) == NULL) {

                (void) printf("fatal: %s: version `%s' not found\n",

                    file, vers);

                exit (1);

        }

        ....