ar
在 Linux 中,ar 是 GNU 通用归档程序。
(在其他类 Unix 操作系统中存在 ar 的非 GNU 变体)。使用选项c
ar c... archive-name file...
它会创建一个包含file... 副本的存档。 archive-name 传统上
但不一定有扩展名.a(用于存档)。每个file... 可能是
任何类型的文件,不一定是目标文件。
当归档文件都是目标文件时,通常打算使用
用于将目标文件的选择传递到程序链接中的存档
或 DSO(动态共享对象)。在这种情况下,archive-name 通常也会被赋予前缀lib,例如
libfoo.a,以便可以通过链接器选项 -lfoo 将其作为候选链接器输入文件发现。
用作链接器输入文件,libfoo.a 通常称为静态库。这
用法对于不熟练的程序员来说是一个永久的困惑源,因为它会导致他们
认为存档 libfoo.a 与 DSO libfoo.so 非常相似,
通常称为动态/共享库,并以此建立错误的期望
基础。实际上“静态库”和“动态库”根本不是相似的东西
并以完全不同的方式用于链接。
一个显着的区别是静态库不是由链接器生成的,
但是ar。所以不会发生链接,也不会发生符号解析。存档的
目标文件没有改变:它们只是放在一个袋子里。
当档案被输入到由 由
链接器 - 例如程序或 DSO - 链接器查看包中是否存在
是否有任何对象文件为未解析的符号引用提供定义
在链接中较早产生的。如果找到,它会从
袋子并将 它们 链接到输出文件中,就像它们被单独命名一样
在链接器命令行和存档中根本没有提到。所以整个
档案在链接中的作用是作为链接器可以从中获取的目标文件包
选择需要进行联动的。
默认情况下,GNU ar 使其输出存档准备好用作链接器输入。它添加了一个虚假的“文件”
到存档,带有一个神奇的假文件名,在这个假文件中它写入的内容
链接器能够从定义的全局符号中读取查找表
通过存档中的任何对象文件到这些对象的名称和位置
档案中的文件。该查找表使链接器能够查找
归档并识别定义任何未解析符号引用的任何目标文件
已经到手了。
您可以使用 q ( =
quick) 选项 - 实际上你已经在自己的 ar 示例中使用过 - 而且
使用(大写)S(= 无符号表)选项。如果您调用 ar 来创建或更新
由于任何原因没有(最新)符号表的存档,那么你
可以用s 选项给它一个。
ranlib
ranlib 没有
完全创建库。在 Linux 中,ranlib 是一个遗留程序,它添加了一个(更新的)
符号表到 ar 存档(如果它没有)。它的效果正是
与 ar s 相同,使用 GNU ar。从历史上看,在ar 配备生成
符号表本身,ranlib 是注入魔术假文件的工具
到存档中以使链接器能够从中挑选目标文件。在非 GNU
为此,可能仍需要类 Unix 操作系统 ranlib。你的例子:
ar qc libgraphics.a *.o
ranlib libgraphics.a
说:
- 通过将当前所有
*.o 文件附加到存档来创建libgraphics.a
目录,没有符号表。
- 然后将符号表添加到
libgraphics.a
在 linux 中,这与以下效果相同:
ar cr libgraphics.a *.o
ar qc libgraphics.a *.o 自己会创建一个存档,链接器
不能用,因为没有符号表。
ld
你的例子:
ld -r -o libgraphics.a *.o
实际上是非常非正统的。这说明了链接器的相当少见的使用,
ld,通过将多个输入文件链接到
尽可能在其中完成符号解析的单个输出目标文件,
给定输入文件。 -r ( = relocatable) 选项
指示链接器通过以下方式生成目标文件目标(而不是程序或 DSO)
如果未定义的符号引用,尽可能链接输入并且不使链接失败
保留在输出文件中。这种用法称为部分链接。
ld -r ... 的输出文件是一个目标文件,而不是一个ar存档,并且
指定一个看起来类似于ar 存档的输出文件名并不能使其成为一个。
所以你的例子说明了一个欺骗。这个:
ld -r -o graphics.o *.o
会是真实的。我不清楚这种欺骗的目的是什么,
因为即使一个 ELF 目标文件被称为libgraphics.a,并且通过该名称输入到链接,
或-lgraphics,链接器将正确地将其识别为 ELF 目标文件,而不是 ar 存档,并将消耗
它就像它在命令行中使用任何目标文件的方式一样:它无条件地链接它
到输出文件中,而输入真正存档的目的是链接
归档成员仅在他们被引用的条件下。也许你只是有
这里是一个不明智的链接示例。
结束...
我们实际上只看到了 一种 方法来生产某些东西
传统上称为 library,这就是所谓的 static library 的产生,
通过归档一些目标文件并将符号表放入归档中。
我们还没有看到如何生产另一种最重要的东西,通常称为
库,即动态共享对象/共享库/动态库。
与程序一样,DSO 由链接器生成。一个程序和一个 DSO
是 ELF 二进制文件的变体,OS 加载程序可以理解并可用于组装
一个正在运行的进程。通常我们通过 GCC 前端之一(gcc、g++、gfortran 等)调用链接器:
链接程序:
gcc -o prog file.o ... -Ldir ... -lfoo ...
链接 DSO:
gcc -shared -o libbar.so file.o ... -Ldir ... -lfoo ...
共享库和静态库都可以提供给链接器
通过统一的-lfoo 协议,当您链接一些其他程序或 DSO 时。
该选项指示链接器扫描其指定或默认搜索目录以查找
libfoo.so 或 libfoo.a。默认情况下,一旦找到其中任何一个,它就会将该文件输入到链接中,并且
如果在同一个搜索目录中找到两者,它将首选libfoo.so。
如果选择了libfoo.so,则链接器将该 DSO 添加到运行时依赖项列表
您正在制作的任何程序或 DSO。如果选择了libfoo.a
然后链接器使用存档作为链接的目标文件的选择
到输出文件中,如果需要,就在那里。没有运行时依赖
libfoo.a本身是可能的;它不能映射到一个进程中;它对操作系统加载器没有任何意义。