延迟装入可以提供 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) 的延迟依赖项。