Ranlib、AR 和 LD 在制作库方面有什么区别?



要从 *.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前端(gccg++gfortran等)调用链接器:

链接程序:

gcc -o prog file.o ... -Ldir ... -lfoo ...

链接 DSO:

gcc -shared -o libbar.so file.o ... -Ldir ... -lfoo ...

共享库和静态库都可以提供给链接器 通过统一-lfoo协议,当您链接其他程序或 DSO 时。 该选项指示链接器扫描其指定或默认搜索方向以查找libfoo.solibfoo.a.默认情况下,一旦找到其中任何一个,它就会将该文件输入到链接中,并且 如果它在同一搜索目录中找到两者,它将首选libfoo.so。 如果选择了libfoo.so,则链接器会将该 DSO 添加到运行时依赖项列表中 您正在制作的任何程序或DSO。如果选择了libfoo.a然后,链接器使用存档作为链接的对象文件的选定选择 如果需要,可以立即放入输出文件中。不依赖于运行时libfoo.a本身是可能的;它不能映射到进程中;它对操作系统加载程序没有任何意义。

相关内容

  • 没有找到相关文章