要从 *.o 文件在 c++/unix 中创建库,我注意到我的项目(遗留代码)中有两种不同的方式:
ar qc libgraphics.a *.o
ranlib libgraphics.a
和
ld -r -o libgraphics.a *.o
这两种方法之间有什么区别,哪种方法将用于什么目的?
ar
在Linux中,ar
是GNU通用归档器。 (在其他类Unix操作系统中也有非GNU的ar
变体)。带有选项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 - 链接器在袋子中查找是否有 是其中为未解析的符号引用提供定义的任何对象文件 在联系中较早累积。如果找到任何内容,则从中提取这些对象文件 包并将它们链接到输出文件中,就像它们单独命名一样 在链接器命令行和存档中根本没有提及。所以整个 存档在链接中的作用是作为对象文件包,链接器可以从中获取 选择进行联动所需的那些。
默认情况下,GNUar
使其输出存档准备好用作链接器输入。它添加了一个虚假的"文件" 到存档中,带有一个神奇的虚假文件名,并在此虚假文件中写入内容 链接器能够从定义的全局符号中读取为查找表 通过存档中的任何对象文件到这些对象的名称和位置 存档中的文件。此查找表使链接器能够查找 存档并标识定义任何未解析符号引用的任何对象文件 它已经掌握在手中。
您可以使用以下q
( =快)选项 - 实际上您在自己的ar
示例中使用了该选项 - 以及 (大写)S
(=无符号表)选项。如果您调用ar
来创建或更新 由于任何原因没有(最新)符号表的存档,那么您 可以用s
选项给它一个。
兰利布
ranlib
没有 创建库。在 Linux 中,ranlib
是一个遗留程序,它添加了一个(最新的) 符号表到ar
存档(如果没有)。它的效果正是 与ar s
相同,与 GNUar
相同。从历史上看,在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
( =可重定位) 选项指示链接器通过以下方式生成对象文件目标(而不是程序或 DSO) 尽可能链接输入,并且在未定义的符号引用时不要使linkaqe失败 保留在输出文件中。这种用法称为部分链接。
ld -r ...
的输出文件是目标文件,而不是ar
存档,并且 指定看起来像ar
存档的输出文件名不会使其成为一个。 所以你的例子说明了一个欺骗。这:
ld -r -o graphics.o *.o
会说实话。我不清楚这种欺骗的目的是什么, 因为即使 ELF 对象文件被称为libgraphics.a
,并且以该名称输入到链接中, 或者通过-lgraphics
,链接器将正确地将其标识为 ELF 对象文件,而不是ar
存档,并且将使用 它以命令行中使用任何对象文件的方式:它无条件地链接它 到输出文件中,而输入真实存档的目的是链接 仅当成员被引用时,才对其进行存档。也许你只是有 此处为不知情链接的示例。
结束...
我们实际上只看到了一种生产东西的方法 通常称为库,这就是所谓的静态库的产生, 通过归档一些对象文件并将符号表放入归档。
我们根本没有看到如何生产另一种最重要的东西,传统上称为 一个库,即动态共享对象/共享库/动态库。
与程序一样,DSO由链接器生成。一个程序和一个DSO程序 是操作系统加载程序理解并可用于组装的 ELF 二进制文件的变体 正在运行的进程。通常我们通过一个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
本身是可能的;它不能映射到进程中;它对操作系统加载程序没有任何意义。