linker - 关于共享库的链接,它们真的是最终的吗?如果是,为什么?
问题描述
我正在尝试更多地了解链接和共享库。
最终,我想知道是否可以将方法添加到共享库中。例如,假设有一个源文件 ac 和一个库 lib.so(没有源文件)。为简单起见,让我们进一步假设 ac 声明了一个方法,该方法的名称不在 lib.so 中。我想也许有可能在链接时将 ao 链接到 lib.so,同时指示创建 newLib.so,并强制链接器将 lib.so 中的所有方法/变量导出到 newLib.so 现在基本上是lib.so 使用来自 a.so 的添加方法。
更一般地说,如果一个人有一些依赖于共享库的源文件,那么可以创建一个不再依赖于共享库的单个输出文件(库或可执行文件)吗?(也就是说,库中的所有相关方法/变量都将被导出/链接/内联到新的可执行文件,从而使依赖项无效)。如果这不可能,是什么在技术上阻止它?
这里提出了一个类似的问题:Merge multiple .so shared libraries。其中一个回复包括以下文本:“如果您可以访问两个库的源文件或目标文件,则可以直接从它们编译/链接组合 SO。:无需解释技术细节。是错误还是错误它持有吗?如果是这样,怎么办?
解决方案
一旦你有一个共享库libfoo.so
,你可以在其他任何东西的链接中使用它的唯一方法是: -
链接一个动态依赖它的程序,例如
$ gcc -o prog bar.o ... -lfoo
或者,链接另一个动态依赖它的共享库,例如
$ gcc -shared -o libbar.so bar.o ... -lfoo
在任何一种情况下,链接的产品,prog
或libbar.so
获得对 的动态依赖libfoo.so
。这意味着prog|libfoo.so
链接器在其中记录了信息,该链接器指示 OS 加载器在运行时查找libfoo.so
,将其加载到当前进程的地址空间中,并将程序对libfoo
的导出符号的引用绑定到其定义的地址。
所以libfoo.so
必须继续存在以及prog|libbar.so
。不可能以物理合并到
运行时依赖项且不再是运行时依赖项libfoo.so
的prog|libbar.so
方式进行链接。libfoo.so
prog|libbar.so
无论您是否拥有其他链接输入文件的源代码bar.o ...
- 依赖于libfoo.so
. 您可以对共享库进行的唯一一种链接是动态链接。
您想知道这个答案中的陈述,它说:
如果您可以访问这两个库的源文件或目标文件,则可以直接从它们编译/链接组合的 SO。
作者只是观察如果我有源文件
foo_a.c foo_b.c... bar_a.c bar_b.c
我将其编译为相应的目标文件:
foo_a.o foo_b.o... bar_a.o bar_b.o...
或者如果我只是拥有那些目标文件。然后以及 - 或代替 - 将它们链接到两个共享库:
$ gcc -shared -o libfoo.so foo_a.o foo_b.o...
$ gcc -shared -o libbar.so bar_a.o bar_b.o...
我可以将它们链接成一个:
$ gcc -shared -o libfoobar.so foo_a.o foo_b.o... bar_a.o bar_b.o...
这将不依赖于它们,libfoo.so
或者libbar.so
即使它们存在。
尽管这可能很简单,但也可能是错误的。如果有任何符号name
在 any 中全局定义,并且在 any 中foo_a.o foo_b.o...
也全局定义,bar_a.o bar_b.o...
那么它与libfoo.so
or的链接libbar.so
无关(并且不需要由它们中的任何一个动态导出)。但是libfoobar.so
对于 的多个定义, 的链接将失败name
。
如果我们构建一个共享库libbar.so
,它依赖于libfoo.so
并且本身已经链接到libfoo.so
:
$ gcc -shared -o libbar.so bar.o ... -lfoo
然后我们想将一个程序与 链接起来libbar.so
,我们可以这样做,我们不需要提及它的依赖关系libfoo.so
:
$ gcc -o prog main.o ... -lbar -Wl,-rpath=<path/to/libfoo.so>
请参阅此答案以跟进。但这并没有改变libbar.so
运行时依赖于libfoo.so
.
如果这不可能,是什么在技术上阻止它?
targ
技术上阻止以物理合并方式将共享库与某些程序或共享库链接的targ
原因是共享库(如程序)不是链接器知道如何物理合并到其输出文件中的那种东西。
链接器可以物理合并到的输入文件targ
需要具有指导链接器进行合并的结构属性。那就是目标文件的结构。它们由标有各种属性的目标代码或数据的命名输入部分组成。粗略地说,链接器将目标文件分成各自的部分,并根据它们的属性将它们分发到
输出文件的输出部分,并对合并的结果进行二进制修改以解析静态符号引用或使 OS 加载器能够解析动态符号引用在运行时。
这不是一个可逆的过程。链接器不能使用程序或共享库并重建从中创建它的目标文件以将它们再次合并到其他东西中。
但这真的是无关紧要的。当输入文件物理合并到targ
中时,称为静态链接。当输入文件只是被外部引用以targ
使操作系统加载程序将它们映射到它已经启动的进程中时targ
,这称为动态链接。技术发展为我们提供了满足这些需求的文件格式解决方案:用于静态链接的目标文件、用于动态链接的共享库。两者都不能用于另一个目的。
推荐阅读
- python - 实验室:计算折扣
- javascript - 如何在猫鼬的路由器中等待函数完成其所有内部表达式?
- anylogic - 道路交通图书馆的奇怪行为
- kubernetes - Filebeat:再次删除字段 kubernetes
- python - 使用 unittest 框架回滚 SQLAlchemy
- mysql - 如何创建一个忽略对第三个表具有特定引用的行的选择查询?
- r - 如何更改 $ 运算符在环境中的行为?
- azure - Azure ARM 策略模板拒绝特定资源
- node.js - 有没有办法安排邮件?
- c# - “safebrowsing”标志不再适用于 selenium 中最新的 chromedriver 版本 93.0.4577.1500