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

On linking of shared libraries, are they really final, and if so, why?

我正在尝试了解更多关于 linking 和共享库的信息。

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

的方法

更一般地说,如果有一些源文件依赖于共享库,是否可以创建不再依赖于共享库的单个输出文件(库或可执行文件)? (也就是说,库中的所有相关 methods/variable 都将 exported/linked/inlined 到新的可执行文件,因此使依赖关系无效)。如果那不可能,技术上是什么阻止了它?

这里有人问过类似的问题:Merge multiple .so shared libraries。 其中一个回复包含以下文本:“如果您可以访问这两个库的源文件或目标文件,则可以直接从它们 compile/link 组合 SO。:无需解释技术细节。这是一个错误吗或者它是否成立?如果成立,该怎么做?

拥有共享库后 libfoo.so 使用它的唯一方法 在link年龄的任何其他东西是:-

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

$ gcc -o prog bar.o ... -lfoo

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

$ gcc -shared -o libbar.so bar.o ... -lfoo

在任何一种情况下,link年龄、proglibbar.so 的乘积 获得对 libfoo.so 的动态依赖。这意味着 prog|libfoo.so linker 在其中记录了指示 OS加载器,在运行时,找到libfoo.so,将其加载到 当前进程的地址 space 并将程序对 libfoo 的导出符号的引用绑定到 它们定义的地址。

所以libfoo.so必须和prog|libbar.so一样继续存在。 link libfoo.soprog|libbar.so 是不可能的 这样 libfoo.so 在物理上合并到 prog|libbar.so 并且不再是运行时依赖项。

有没有源码都无所谓 其他 linkage 输入文件 - bar.o ... - 取决于 libfoo.so。这 您可以使用共享库做的唯一一种 linkage 是动态的 linkage.

这与static library

的link年龄形成鲜明对比

您想知道 this answer 中的声明:

If you have access to either source or object files for both libraries, it is straightforward to compile/link a combined SO from them.

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

foo_a.c foo_b.c... bar_a.c bar_b.c

我编译成相应的目标文件:

foo_a.o foo_b.o... bar_a.o bar_b.o...

如果我只有那些目标文件。然后 - 或者代替 - link将它们放入两个共享库中:

$ gcc -shared -o libfoo.so foo_a.o foo_b.o...
$ gcc -shared -o libbar.so bar_a.o bar_b.o...

我可以 link 它们合二为一:

$ gcc -shared -o libfoobar.so foo_a.o foo_b.o... bar_a.o bar_b.o...

不会依赖于 libfoo.solibbar.so,即使它们存在。

虽然 可能 很简单,但也可能是错误的。如果有 在 foo_a.o foo_b.o... 中全局定义的任何符号 name 和 也在 bar_a.o bar_b.o... 中的任何一个中全局定义,那么没关系 到 linklibfoo.solibbar.so 的年龄(并且不需要动态 由他们中的任何一个导出)。但是 libfoobar.so 的 linkage 将失败 name.

的多重定义

如果我们建立一个共享库 libbar.so 依赖于 libfoo.so 并且有 本身已经 linkedlibfoo.so:

$ gcc -shared -o libbar.so bar.o ... -lfoo

然后我们想要 link 一个带有 libbar.so 的程序,我们可以通过这样的方式来实现 我们不需要提及它的依赖性libfoo.so:

$ gcc -o prog main.o ... -lbar -Wl,-rpath=<path/to/libfoo.so>

请参阅 进行跟进。但 这不会改变 libbar.solibfoo.so.

具有运行时依赖性的事实

If that's not possible, what is technically preventing it?

技术上是什么阻止 link使用某些程序共享库 或共享库 targ 以物理方式将其合并到 targ 中 共享库(如程序)不是 linker 知道的那种东西 如何物理合并到它的输出文件中。

linker 可以物理合并到 targ 中的输入文件需要 具有指导 linker 进行合并的结构属性。那就是目标文件的结构。 它们由标有各种属性的目标代码或数据的命名 输入部分 组成。 粗略地说,linker 将目标文件分割成它们的部分并将它们分发到 output sections 输出文件的属性,并使 对合并结果进行二进制修改以解析静态符号引用 或者启用 OS 加载器在运行时解析动态加载器。

这不是一个可逆的过程。 linker 无法使用程序或 共享库并重建创建它的目标文件 将它们再次合并到其他东西中。

但这真的不是重点。当输入文件在物理上 合并成targ,即所谓staticlinkage。 当输入文件只是 外部引用 in targ 到 让 OS 加载程序将它们映射到它为 targ 启动的进程中, 这叫做 动态 link年龄。技术发展给了我们 满足这些需求的文件格式解决方案:静态 linkage 的目标文件、共享库 对于动态 linkage。两者都不能用于另一个目的。