简体   繁体   English

何时在程序和共享库之间执行动态链接?

[英]When is dynamic linking between a program and a shared library performed?

In C, when is dynamic linking between a program and a shared library performed: 在C语言中,何时在程序和共享库之间执行动态链接:

  • Once loading of the program into the memory, but before executing the main() of the program, or 一旦将程序加载到内存中,但是在执行程序的main()之前,或者

  • After executing the main() of the program, when the first call to a routine from the library is executed? 执行完程序的main()之后,何时执行对库中例程的第一次调用? Will dynamic linking happen again when a second or third or... call to a routine from the library is executed? 当执行第二次或第三次或从库中调用例程时,动态链接会再次发生吗?

I was thinking the first, until I read the following quote, and now I am not sure. 在开始阅读以下引文之前,我一直在想第一个,现在不确定。

Not sure if OS matters, I am using Linux. 不确定操作系统是否重要,我使用的是Linux。

From Operating System Concepts: 从操作系统概念:

With dynamic linking, a stub is included in the image for each library- routine reference. 通过动态链接,每个库例程参考的映像中都包含一个存根。 The stub is a small piece of code that indicates how to locate the appropriate memory-resident library routine or how to load the library if the routine is not already present. 存根是一小段代码,指示如何找到合适的驻留内存的库例程,或者如果该例程尚不存在,则如何加载该库。

When the stub is executed , it checks to see whether the needed routine is already in memory. 执行存根时 ,它将检查所需的例程是否已在内存中。 If it is not, the program loads the routine into memory. 如果不是,程序将例程加载到内存中。 Either way, the stub replaces itself with the address of the routine and executes the routine. 无论哪种方式,存根都会用例程的地址替换自身并执行该例程。 Thus, the next time that particular code segment is reached , the library routine is executed directly, incurring no cost for dynamic linking. 因此, 下次到达特定代码段时 ,将直接执行库例程,而不会产生动态链接的开销。 Under this scheme, all processes that use a language library execute only one copy of the library code. 在这种方案下,所有使用语言库的进程仅执行库代码的一个副本。

I was thinking the first, until I read the following quote, and now I am not sure. 在开始阅读以下引文之前,我一直在想第一个,现在不确定。

It's complicated (and depends on exactly what you call "dynamic linking"). 它很复杂(取决于您所谓的“动态链接”)。

The Linux kernel loads a.out into memory. Linux内核将a.out加载到内存中。 It then examines PT_INTERP segment (if any). 然后,它检查PT_INTERP段(如果有)。

If that segment is not present, the binary is statically linked and the kernel transfers control to the Elf{32,64}Ehdr.e_entry (usually the _start routine). 如果该段不存在,则二进制文件是静态链接的,内核将控制权转移到Elf{32,64}Ehdr.e_entry (通常是_start例程)。

If the PT_INTERP segment is present, the kernel loads it into memory, and transfers control to it's .e_entry . 如果PT_INTERP 出现,内核加载到内存中,并将控制权转移到它的 .e_entry It is here that the dynamic linking begins . 动态链接就是在这里开始的

The dynamic loader relocates itself, then looks in a.out s PT_DYNAMIC segment for instructions on what else is necessary. 动态加载程序重新定位自身,然后查找在a.out小号PT_DYNAMIC段上还有什么是必要的说明。

For example, it will usually find one or more DT_NEEDED entries -- shared libraries that a.out was directly linked against. 例如,它通常会发现一个或多个DT_NEEDED项-共享库a.out直接对链接。 The loader loads any such libraries, initializes them , and resolves any data references between them. 加载器加载任何这样的库, 它们初始化,并解决它们之间的任何数据的引用。

IF a.out s PT_DYNAMIC has a DT_FLAGS entry, and IF that entry contains DF_BIND_NOW flag, then function references from a.out will also be resolved. 如果a.outPT_DYNAMIC具有DT_FLAGS条目,并且如果该条目包含DF_BIND_NOW标志,则来自a.out 函数引用也将被解析。 Otherwise (and assuming that LD_BIND_NOW is not set in the environment), lazy PLT resolution will be performed (resolving functions as part of first call to any given function). 否则(并假设环境中未设置LD_BIND_NOW ),将执行惰性PLT解析(将函数解析为对任何给定函数的首次调用的一部分)。 Details here . 详细信息在这里

When the stub is executed, it checks to see whether the needed routine is already in memory. 执行存根时,它将检查所需的例程是否已在内存中。 If it is not, the program loads the routine into memory. 如果不是,程序将例程加载到内存中。

I don't know which book you are quoting from, but no current UNIX OS works that way. 我不知道您在引用哪本书,但是当前的UNIX OS都无法使用这种方式。

The OS (and compiler, etc. ) certainly matters: the language itself has nothing to say about dynamic libraries (and very little about linking in general). 操作系统(和编译器 )当然很重要:语言本身对动态库无话可说(一般而言,链接很少 )。 Even if we know that dynamic linking is occurring, a strictly-conforming program cannot observe any effect from timing among its translation units (since non-local initialization cannot have side effects ). 即使我们知道动态链接正在发生,严格遵循标准的程序也无法从其翻译单元之间的时序观察到任何影响(因为非本地初始化不会产生副作用 )。

That said, the common toolchains on Linux do support automatic initialization upon loading a dynamic library (for implementing C++, among other things). 也就是说,Linux上的常见工具链确实支持在加载动态库时自动初始化 (用于实现C ++等)。 Executables and the dynamic libraries on which they depend (usually specified with -l ) are loaded and initialized recursively to allow initialization in each module to (successfully) use functions from its dependencies. 可执行文件和它们所依赖的动态库(通常用-l指定)被递归加载和初始化,以允许每个模块中的初始化(成功)使用其依赖关系中的函数。 (There is an unfortunate choice of order in some cases.) Of course, dlopen(3) can be used to load and initialize more libraries later. (在某些情况下, 不幸的是选择了顺序。)当然, dlopen(3)可用于稍后加载和初始化更多库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM