[英]Why can dynamic libraries link to other libraries, but static ones can't?
考虑以下代码结构:
main.cpp -> depends on libone.a -> depends on libtwo.a
假设在main.cpp
只使用来自libone.a
函数。 所以实际上编写main.cpp
的程序员真的只关心libone.a
。 此时他们甚至不知道libone.a
依赖于libtwo.a
。
他们尝试如下编译他们的代码并得到链接器错误:
g++ -o main main.cpp -lone
- 错误! 未定义的符号!
这成为一个问题,因为因为libone.a
依赖于libtwo.a
,任何使用libone.a
人都必须知道这个依赖......正如你想象的那样,这个问题可能发生在比单个库更多的依赖中,并且可以很快成为一个连接噩梦。
解决这个问题的第一个想法是“这很简单,当我编译libone.a
时,我只需将libone.a
与libtwo.a
链接!
事实证明它并不像我希望的那么简单......在编译libone.a
,无法链接libtwo.a
。 静态库在编译时不会链接到任何内容,而是在将库编译为可执行文件时必须链接所有依赖项。
例如,要编译依赖于另一个静态库的静态库的main.cpp
,您必须链接这两个库。 总是。
g++ -o main main.cpp -lone -ltwo
另一个想法是尝试将libone
编译为链接到libtwo.a
的动态库。
奇怪的是,这刚刚奏效! 编译链接libone.so
,主程序只需要关心libone.so
,不再需要关心libtwo.a
。
g++ -o main main.cpp -lone
成功!
完成此练习后,仍然缺少一件。 我似乎无法弄清楚为什么静态库不能链接到其他库,而动态库可以。 事实上,在我链接libtwo.a
之前,动态库libone.so
根本不会编译。 不过这很好,因为作为libone.so
的作者,我会知道它对libtwo.a
依赖 - main.cpp
的作者,但是不知道。 实际上,他们不应该知道。
所以回到真正的问题......为什么动态库可以链接到其他这样的库而静态库不能? 这似乎是动态库相对于静态库的明显优势,但我从未在任何地方看到过它!
静态库只是目标文件的存档,没有依赖的概念,因为它从未链接过。
共享库是链接的、解决符号的,因此它们可以具有依赖关系。
由于您的问题涉及 gcc 和 .so/.a 文件,我假设您使用的是某种将 ELF 文件用于目标代码的 Unix。
完成此练习后,仍然缺少一件。 我似乎无法弄清楚为什么静态库不能链接到其他库,而动态库可以。
正如另一个答案中提到的那样,静态库没有链接。 它们只是已编译目标文件的存档。 共享库实际上是链接的,这意味着链接器实际上解析了任何导出符号可访问的所有符号。 将导出的符号视为库的 API。 一个完全链接的共享库要么包含每个符号的定义,要么包含告诉操作系统(特别是动态加载器)需要哪些其他共享库来访问符号所需的依赖信息。 链接器将所有这些组合成一种称为ELF 共享对象(动态库)的特殊文件格式。
事实上,在我链接 libtwo.a 之前,动态库 libone.so 根本不会编译。 不过这很好,因为作为 libone.so 的作者,我会知道它对 libtwo.a 的依赖——但是 main.cpp 的作者不知道。 实际上,他们不应该知道。
libone.so
可能编译得很好,但由于未解析的符号,没有libtwo
将无法链接。 因为链接器在链接共享库时必须解析所有可访问的符号,所以如果找不到任何符号,它将失败。 由于libone.so
在使用符号libtwo
,链接器需要了解libtwo.a
找到他们。 当您将静态库链接到共享库时,通过将定义直接复制到输出共享对象文件中来解析符号,因此此时, libone.so
用户可能对libone.so
的使用libtwo
因为它的符号只是在libone.so
。
另一种选择是将共享库链接到其他共享库。 如果您将libtwo.so
链接到libone.so
(注意 .so 后缀),则链接器通过向输出共享对象文件添加一个特殊部分来解析libone
所需的符号,该部分表明它在运行时需要libtwo.so
。 稍后,当操作系统加载libone.so
,它知道它还需要加载libtwo.so
。 而且,如果您的应用程序仅直接使用libone
,那么您只需要在构建时告诉链接器即可,因为它将在libone
链接,看到它需要libtwo
,并递归解析直到一切正常。
现在,操作系统必须在运行时加载的所有内容都会导致性能成本,并且如果您不小心,多个共享对象中存在全局静态变量的一些问题。 静态链接还有一些其他潜在的性能优势,我不会在这里介绍,但可以说使用动态库的平均性能并不那么好,但对于大多数现实世界情况,这种差异也可以忽略不计。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.