Oracle® Solaris 11.2 链接程序和库指南

退出打印视图

更新时间: 2014 年 7 月
 
 

提供 dlopen() 的替代项

延迟装入可以提供 dlopen(3C)dlsym(3C) 的替代方法。请参见运行时链接编程接口。例如,libfoo.so.1 中的以下代码将验证是否装入了目标文件,然后调用该目标文件提供的接口。

void foo()
{
        void *handle;

        if ((handle = dlopen("libbar.so.1", RTLD_LAZY)) != NULL) {
                int (*fptr)();

                if ((fptr = (int (*)())dlsym(handle, "bar1")) != NULL)
                        (*fptr)(arg1);
                if ((fptr = (int (*)())dlsym(handle, "bar2")) != NULL)
                        (*fptr)(arg2);
                ....
        }
}

尽管非常灵活,但这种使用 dlopen()dlsym() 的模型不是自然的编码样式,并存在一些缺点。

  • 必须要知道其中的符号将要退出的目标文件。

  • 使用函数指针的调用不会通过编译器或 lint(1) 提供任何验证方式。

如果提供所需接口的目标文件满足下列条件,则可以简化此代码。

  • 可在链接编辑时作为依赖项建立该目标文件。

  • 该目标文件始终可用。

通过使用函数引用来触发延迟装入,可以实现同样的 libbar.so.1 延迟装入。在此情况下,对函数 bar1() 的引用将导致延迟装入关联的依赖项。这种编码要自然得多,标准函数调用可用于编译器或 lint(1) 验证。

void foo()
{
        bar1(arg1);
        bar2(arg2);
        ....
}
$ cc -G -o libfoo.so.1 foo.c -L. -zdefs -zlazyload -lbar -R'$ORIGIN'

但是,如果提供所需接口的目标文件并非始终可用,则此模型会失败。在此情况下,最好能够在不必知道依赖项名称的情况下测试依赖项是否存在。需要一种测试满足函数引用要求的依赖项可用性的方法。

一个测试函数是否存在的强大模型,可以通过显式定义延迟依赖项,以及使用 dlsym(3C)RTLD_PROBE 句柄来实现。

显式定义的延迟依赖项是对延迟可装入依赖项的扩展。与延迟依赖项关联的符号引用则称为延迟符号。仅在首次引用符号时,才会处理根据该符号进行的重定位。这些重定位不会在 LD_BIND_NOW 处理过程中进行处理,也不会通过带有 RTLD_NOW 标志的 dlsym(3C) 进行处理。

延迟依赖项是在链接编辑时使用链接编辑器 –z deferred 选项建立的。

$ cc -G -o libfoo.so.1 foo.c -L. -zdefs -zdeferred -lbar -R'$ORIGIN'

使用已建立的 libbar.so.1 作为延迟依赖项时,对 bar1() 的引用可以验证依赖项是否可用。此测试可用于控制对依赖项以使用 dlsym(3C) 的方式提供的函数的引用。然后此代码可以对 bar1() bar2() 做出自然调用。这些调用更加清晰且更易于编写,并允许编译器捕捉调用序列中的错误。

void foo()
{
        if (dlsym(RTLD_PROBE, "bar1")) {
                bar1(arg1);
                bar2(arg2);
                ....
        }
}

延迟依赖项可提供其他级别的灵活性。如果尚未装入该依赖项,则可以在运行时对其进行更改。该机制提供的灵活性级别类似于 dlopen(3C),其中各个目标文件可以由调用者装入和绑定。

如果已知原始依赖项名称,则可以使用带有 RTLD_DI_DEFERRED 参数的 dlinfo(3C) 将原始依赖项更换为新的依赖项。或者,可以使用与依赖项关联的延迟符号来识别使用带有 RTLD_DI_DEFERRED_SYM 参数的 dlinfo(3C) 的延迟依赖项。