c-为什么对与位置无关的代码函数调用使用延迟绑定



我正在读一本教科书,上面写着:

延迟绑定的动机是,一个典型的应用程序只会调用共享库(如libc.so(导出的数百或数千个函数中的一小部分。通过将函数地址的解析推迟到实际调用,动态链接器可以避免加载时数百或数千次不必要的重新定位

我有点明白了,但还是很困惑。假设一个程序只调用一个共享库的10个函数,该库内部有100个函数。如果没有延迟绑定,链接器只需要对程序使用的函数进行10次重新定位,那么使用延迟绑定,动态链接器如何避免数百或数千次(在这种情况下为100个函数(不必要的重新定位?这就像你试图解决一个实际上并不存在的问题?

从我所看到的关于懒惰绑定的情况来看,在加载时不需要重新定位,所以你确实节省了10次重新定位的时间,这是我所能看到的唯一好处,我的理解正确吗?但作者似乎指出,如果没有延迟绑定,链接器需要进行100次重新定位?

文本在编写时令人困惑,但它所说的是被引用但从未被调用的函数,而不是从未被引用的函数。无论延迟绑定是否在使用中,对于未被引用的函数都不需要查找。

如果您的程序引用了库中的10个函数,但调用了其中的0个,则延迟绑定与立即绑定的区别是0次查找与10次查找。如果您的程序引用了库中的100个函数,但只调用其中的10个函数,那么查找次数与查找次数的区别是10次与100次。延迟绑定旨在节省时间的情况是,由于一个共享库依赖于另一个库,因此引用了大量函数。例如,假设您的程序使用依赖于库B的库A,而库A引用了库B中的所有10000个函数,但您的程序只使用了库A中仅调用库B中一个函数的一小部分功能。现在,只完成了一个查找,而不是10000个查找。这听起来是一大好处。

然而,另一方面,惰性绑定非常容易出错(由于惰性解析器的调用机制,它如何与关于寄存器使用的调用ABI契约交互,以及第一次调用可能发生在非常尴尬的上下文中,比如来自信号处理程序(,并且在调用函数时一次查找一个,而不是一次查找所有函数,会对程序流、缓存利用率等造成更大的破坏,如果实际调用库中的所有函数,则会花费更多的总时间。当然,这是一次性的(每个流程实例(成本。

这本教科书有点误导人。符号绑定过去很慢,而延迟绑定的意义在于,有了它,程序可以更快地达到一个点,即它们可以做一些的事情。基本上,启动性能会降低。即使在今天,如果一个小程序依赖于一个依赖于另一个大型库的大型库,这一点也会很明显。如果没有延迟绑定,第一个库需要在流程开始时完全重新定位,而使用延迟绑定,则只会重新定位实际使用的部分。但正如您所指出的,在没有这两个库的情况下,延迟绑定并没有多大区别。

懒惰绑定也存在语义差异,这可能是当今更重要的方面。使用它,您可以dlopen另一个共享对象,该对象提供当前共享对象的符号依赖关系。使用非延迟绑定时,符号在初始重新定位期间不存在,从而导致加载失败。这种方法可能会使从懒惰绑定迁移出去变得非常困难,比如说为了增强安全性。

最新更新