关于共享库的链接,它们真的是最终的吗?如果是,为什么?

TuT*_*Tor 1 compiler-construction linker shared-libraries dynamic-library

我想了解更多有关链接和共享库的信息。

最后,我想知道是否可以向共享库添加方法。例如,假设有一个源文件 ac 和一个库 lib.so (没有源文件)。为了简单起见,我们进一步假设 ac 声明了一个方法,其名称不存在于 lib.so 中。我想也许可以在链接时将 ao 链接到 lib.so,同时指示创建 newLib.so,并强制链接器将 lib.so 中的所有方法/变量导出到 newLib.so 现在基本上是lib.so 以及 a.so 中添加的方法。

更一般地说,如果有一些依赖于共享库的源文件,是否可以创建一个不再依赖于共享库的单个输出文件(库或可执行文件)?(也就是说,库中的所有相关方法/变量都将被导出/链接/内联到新的可执行文件,从而使依赖项无效)。如果这是不可能的,那么技术上是什么阻止了它呢?

这里提出了一个类似的问题:合并多个 .so 共享库。其中一个回复包含以下文本:“如果您可以访问这两个库的源文件或目标文件,则可以直接编译/链接它们的组合 SO。:无需解释技术细节。这是一个错误还是确实是否成立?如果成立,该怎么办?

Mik*_*han 5

一旦您拥有共享库,libfoo.so您可以在链接其他任何内容时使用它的唯一方法是:-

链接一个动态依赖它的程序,例如

$ gcc -o prog bar.o ... -lfoo
Run Code Online (Sandbox Code Playgroud)

或者,链接另一个动态依赖于它的共享库,例如

$ gcc -shared -o libbar.so bar.o ... -lfoo
Run Code Online (Sandbox Code Playgroud)

在任何一种情况下,链接的产物,proglibbar.so 获得对 的动态依赖libfoo.so。这意味着prog|libfoo.so 链接器在其中记录了信息,指示操作系统加载器在运行时查找libfoo.so,将其加载到当前进程的地址空间中,并将程序对 的libfoo导出符号的引用绑定到其定义的地址。

所以libfoo.so必须继续存在以及prog|libbar.so。不可能以物理合并到 并且不再是运行时依赖项的方式进行libfoo.so链接。prog|libbar.solibfoo.soprog|libbar.so

无论您是否拥有bar.o ...依赖于libfoo.so. 可以对共享库进行的唯一链接是动态链接。

这与静态库的链接完全相反

您想知道这个答案中的陈述:

如果您可以访问这两个库的源文件或目标文件,则可以直接从它们编译/链接组合的 SO。

作者只是观察如果我有源文件

foo_a.c foo_b.c... bar_a.c bar_b.c
Run Code Online (Sandbox Code Playgroud)

我将其编译为相应的目标文件:

foo_a.o foo_b.o... bar_a.o bar_b.o...
Run Code Online (Sandbox Code Playgroud)

或者如果我只是有这些目标文件。然后以及 - 或者代替 - 将它们链接到两个共享库:

$ gcc -shared -o libfoo.so foo_a.o foo_b.o...
$ gcc -shared -o libbar.so bar_a.o bar_b.o...
Run Code Online (Sandbox Code Playgroud)

我可以将它们链接成一个:

$ gcc -shared -o libfoobar.so foo_a.o foo_b.o... bar_a.o bar_b.o...
Run Code Online (Sandbox Code Playgroud)

libfoo.so即使它们存在,也不会依赖它们libbar.so

尽管这可能很简单,但也可能是错误的。如果有任何符号name在任何一个中全局定义foo_a.o foo_b.o...,并且在任何一个中也全局定义,那么它对于或bar_a.o bar_b.o...的链接都没有关系(并且不需要由它们中的任何一个动态导出)。但对于 的多个定义,的链接将会失败。libfoo.solibbar.solibfoobar.soname

如果我们构建一个libbar.so依赖于libfoo.so并且本身已链接到的共享库libfoo.so

$ gcc -shared -o libbar.so bar.o ... -lfoo
Run Code Online (Sandbox Code Playgroud)

然后我们想要将一个程序链接到libbar.so,我们可以通过不需要提及它的依赖关系的方式来做到这一点libfoo.so

$ gcc -o prog main.o ... -lbar -Wl,-rpath=<path/to/libfoo.so>
Run Code Online (Sandbox Code Playgroud)

请参阅此答案以跟进。但这并没有改变libbar.so运行时依赖于libfoo.so.

如果这是不可能的,那么技术上是什么阻止了它呢?

从技术上讲,阻止共享库与某些程序或共享库targ以物理合并的方式链接的原因targ是共享库(如程序)不是链接器知道如何物理合并到其输出文件中的那种东西。

链接器可以物理合并的输入文件targ需要具有指导链接器进行合并的结构属性。这就是目标文件的结构。它们由标有各种属性的目标代码或数据的命名输入部分组成。粗略地说,链接器将目标文件分割成各个部分,并 根据其属性将它们分配到输出文件的输出部分,并对合并结果进行二进制修改以解析静态符号引用或使操作系统加载器能够解析动态符号引用在运行时。

这不是一个可逆的过程。链接器无法使用程序或共享库并重建目标文件,从而将它们再次合并到其他内容中。

但这真的不是重点。当输入文件物理合并到 时targ,称为静态链接。当输入文件只是从外部引用targ使操作系统加载程序将它们映射到它已启动的进程中时targ,这称为动态链接。技术发展为我们提供了满足每种需求的文件格式解决方案:用于静态链接的目标文件、用于动态链接的共享库。两者都不能用于对方的目的。