为什么静态链接库和动态链接库不同?

11 浏览
0 Comments

为什么静态链接库和动态链接库不同?

如果它们都包含编译代码,为什么我们不能在运行时加载“静态”文件,为什么不能在编译时链接动态库?为什么需要单独的格式来包含“独立”的代码?在它们之中需要存储什么内容来证明它们的区别?

0
0 Comments

为什么静态库和动态库链接时会有所不同?

静态库是一个包含一组*.o文件的存档。每个文件都可以包含对未定义符号的引用,这些符号必须由链接器解析,例如,你的库可能对printf有一个引用。库对printf的位置并没有提供任何指示,期望链接器在要链接的其他库中找到它。

假设你的库包含以下代码:

read_png.o

write_png.o

read_jpg.o

write_jpg.o

resize_image.o

handle_error.o

如果一个应用程序只使用read_png和write_png,那么其他代码片段将不会加载到可执行文件中(除了handle_error,它被read_png和write_png调用)。

我们不能在运行时加载静态库,因为:

- 链接器不知道如何找到外部对象,例如printf。

- 这将会很慢。动态库经过了优化,以实现快速加载。

- 静态库没有命名空间的概念。我不能定义自己的handle_error,因为它会与库的定义冲突。

动态库在ELF系统上与可执行文件是相同类型的对象。它还导出更多的符号,一个可执行文件只需导出_start。动态库经过优化,整个库可以直接映射到内存中。

如果你的动态库中有一个对printf的调用,除了静态库的要求外,还有一些额外的要求:

- 你必须指定哪个库有printf。

- 你必须以一种特殊的方式调用函数,以让链接器插入printf的地址。在静态库中,链接器可以直接修改你的代码并插入地址,但对于共享库来说,这是不可能的。

我们不想使用动态库进行静态链接的原因是:

- 我们无法只链接动态库的一部分。即使我们的可执行文件从未调用read_jpg,它也会被包含进来,因为动态库是全包或不包的。

- 函数调用的额外开销是浪费的,即使它很小。

编译过程大致如下:

源代码 ==编译==> 目标文件 ==链接==> 可执行文件/共享库

静态库是一个尚未链接的对象的存档。还有很多工作要做。

动态库是一个链接好的最终产品,准备加载到内存中。

静态库是首先发明的。如果两者同时发明,它们可能会更加相似。

你真的是指这个吗:We can't load a static library at runtime because: The linker doesn't know where to find external objects, e.g., printf.... 这里有些拼写错误吗?你想说的是loader而不是linker吗?

不,链接器是正确的。动态链接器,在Linux上是ld.so,在OS X上是dyld,负责在运行时解析符号。(不,我不是想说“加载时间”)

0
0 Comments

静态链接库和动态链接库的区别是出现在链接过程中。静态链接库将外部函数的代码包含在库文件中,而动态链接库则在库文件中包含了对外部函数的引用。

这种区别的原因是因为在目标文件中,编译器会生成对外部函数的隐式引用,但这些引用在链接过程中并未解析。当目标文件被链接成动态链接库时,链接器会查找所有这些外部引用,并找到能够满足这些引用的静态或动态链接库。对于静态链接库中的函数,链接器会将函数体(或其他内容)包含在动态链接库中,而对于动态链接库,链接器会在动态链接库中包含被引用的函数的名称和库文件的名称。

从根本上说,并没有必要这样做。理论上,每次加载文件时,都可以编写加载器来完成这些操作。这实际上只是一种优化方式:链接器完成了相对较慢的部分任务。对于动态链接库的引用被保留下来,但它们已经解析到了一个程度,使得加载器能够相对较快地找到和加载目标文件(如果需要的话),并解析被引用的函数。当链接器正在执行其工作时,它会通过扫描长列表来查找您关心的定义,这会导致速度相对较慢。

0
0 Comments

为什么静态库和动态库在连接时有所不同?

静态库和动态库之间的区别在于它们的连接方式和可执行性。静态库包含一组目标文件,每个目标文件通常由一个源文件创建,包含机器码以及有关代码所需数据的信息。在连接步骤中,链接器将选择所需的目标文件,并将它们合并为一个可执行文件。

机器码的一个重要部分是跳转、调用和数据指针必须包含实际的内存地址。然而,如果一个目标文件需要调用另一个目标文件中的函数,它只能使用一个符号来引用该函数。当链接器将目标文件组合成可执行代码时,符号引用将被解析并转换为实际的内存地址。

动态库是可以加载到内存并立即执行的可执行代码。在某些操作系统上,可能还需要将代码重新定位,即将可执行代码移动到另一个位置,这需要将代码中的所有绝对地址按固定量进行偏移。这个操作仍然比链接器进行的目标文件合并和符号解析快得多。

总结一下:

- 静态库包含使用符号引用其他可执行代码的可执行代码片段

- 动态库(和可执行文件)包含现在位于固定位置的可执行代码,使得符号可以被真实的内存地址替换

如果你曾经尝试过链接一个相当大的项目,你会注意到它需要相当长的时间,可能比你愿意等待启动一个应用程序的时间还要长。这就解释了为什么不能执行静态库。而动态库已经经过优化和剥离,不包含除可执行代码以外的任何内容,这使它们不适合作为静态库使用。

关于不能执行静态库的问题,这涉及到编译器和链接器之间的重要区别。对于编译后的代码能够调用其他文件中的函数,它需要跳转(或者说调用)的内存地址。然而,当编译器执行时,它并不知道这些内存地址;它使用符号来代替。实际上,将符号替换为地址的工作是链接器的任务。更多信息可以参考这个问题。简而言之,你可以在运行链接器之后执行静态库,但动态库已经完成了这个过程。

0