为什么动态库可以链接到其他库,而静态库却不能?

时间:2019-01-24 17:37:08

标签: c++ linker static-linking dynamic-linking

请考虑以下代码结构:

main.cpp -> depends on libone.a -> depends on libtwo.a

假设在main.cpp中仅使用libone.a中的函数。因此,实际上,编写main.cpp的程序员实际上只关心libone.a。在这一点上,他们甚至都不知道libone.alibtwo.a有依赖性。

他们试图按如下方式编译其代码并获得链接器错误:

g++ -o main main.cpp -lone

  

enter image description here-错误!未定义的符号!

这成为一个问题,因为自libone.a依赖于libtwo.a以来,使用libone.a的任何人都必须了解此依赖关系...正如您可以想象的那样,如果FAR有更多依赖关系,则会出现此问题。而不是单个图书馆,它很快就会成为联系的噩梦。


尝试1解决此问题:

解决此问题的第一个想法是“很简单,我在编译libone.a时将libtwo.alibone.a链接起来!

事实证明,它并不像我希望的那么简单...编译libone.a时,无法链接libtwo.a。静态库在编译时不会链接到任何东西,而是在将库编译成可执行文件时必须链接所有依赖项。

例如,要编译依赖于静态库而又依赖于另一个静态库的main.cpp,则必须链接两个库。总是。

g++ -o main main.cpp -lone -ltwo


尝试2解决此问题:

另一种想法是尝试将libone编译为链接到libtwo.a的动态库。

奇怪的是,它确实起作用了!编译并链接libone.so后,主程序只需要关心libone.so,就不再需要了解libtwo.a

g++ -o main main.cpp -lone

  

成功!


完成此练习后,仍然缺少一件。我只是似乎无法弄清楚为什么静态库无法链接到其他库,而动态库可以链接的任何原因。实际上,在链接libone.so之前,动态库libtwo.a根本不会编译。不过,这很好,因为作为libone.so的作者,我会知道它对libtwo.a的依赖性-main.cpp的作者却不知道。实际上,他们不必知道。

所以要问一个真正的问题...为什么动态库可以像这样链接到其他库,而静态库却不能?动态库比静态库似乎有明显的优势,但我从未见过任何地方提到它!

2 个答案:

答案 0 :(得分:5)

静态库只是对象文件的存档,没有依赖的概念,因为它从未链接过。

链接的共享库可以解决符号,并且它们可以具有依存关系。

答案 1 :(得分:1)

由于您的问题涉及gcc和.so / .a文件,因此我假设您使用的是使用ELF文件作为目标代码的Unix。

  

完成此练习后,仍然缺少一件。我只是   似乎无法找出导致静态库无法链接的任何原因   其他库,但动态库可以。

静态库未链接,如另一个答案中所述。它们只是已编译目标文件的存档。共享库实际上是链接的,这意味着链接器实际上解析任何导出符号可到达的所有符号。将导出的符号视为库的API。完全链接的共享库包含每个符号的定义,或者包含告诉操作系统(特别是动态加载程序)所需的 other 个共享库才能访问该符号所必需的依赖项信息。链接器将所有内容组合成一种特殊的文件格式,称为ELF shared object (动态库)。

  

事实上,动态库libone.so无法编译   直到我链接libtwo.a。没关系,因为   我是libone.so的作者,我会知道它对libtwo.a的依赖-   但是main.cpp的作者不知道。实际上,他们   不应该知道。

libone.so可能编译正常,但是由于未解析的符号,如果没有libtwo,则不会链接。由于链接器在链接共享库时必须解析所有可访问的符号,因此如果找不到链接器,它将失败。由于libone.so使用libtwo中的符号,因此链接程序需要了解libtwo.a才能找到它们。当您将静态库链接到共享库时,可通过将定义直接复制到输出共享库文件中来解析符号,因此,此时libone.so的用户对于{{ 1}},因为其符号位于libtwo中。

另一个选择是将共享库链接到其他共享库。如果要将libone.so链接到libtwo.so中(请注意.so后缀),则链接器通过在输出共享对象文件中添加一个特殊的节来解析libone.so所需的符号。在运行时需要libone。稍后,当操作系统加载libtwo.so时,它知道还需要加载libone.so。而且,如果您的应用程序仅直接使用libtwo.so,那么在构建时就需要告诉链接程序,因为它将在libone中进行链接,请注意它需要libone并递归下定决心,直到一切都好为止。

现在,操作系统必须在运行时进行的所有加载都会导致性能损失,而且如果您不小心的话,多个共享对象中都存在一些带有全局静态变量的陷阱。静态链接还有其他一些潜在的性能优势,我不会在这里讨论,但足以说明的是,使用动态库的平均性能并不高,但是对于大多数现实情况而言,这种差异也可以忽略不计。