ranlib、ar 和 ld 在制作库方面有什么区别

what is the difference between ranlib, ar, and ld for making libraries

为了从 *.o 文件在 c++/unix 中创建库,我注意到我的项目中有两种不同的方法(遗留代码):

ar qc libgraphics.a *.o
ranlib libgraphics.a

ld -r -o libgraphics.a *.o

这两种方法有什么区别,用途是什么?

ar

在Linux中,ar是GNU通用归档器。 (在其他类 Unix OSes 中有 ar 的非 GNU 变体)。使用选项 c

ar c... archive-name file...

它创建一个包含 file... 副本的存档。 archive-name 约定俗成 但不一定具有扩展名 .a(对于 archive)。每个 file... 可能是 任何类型的文件,不一定是目标文件。

当归档文件都是目标文件时,通常打算使用 用于将 selection 目标文件传送到 linkage of programs 的存档 或 DSO(动态共享对象)。在这种情况下,archive-name 也将按照惯例被赋予前缀 lib,例如 libfoo.a,以便可以通过 linker 选项 -lfoo.

作为候选 linker 输入文件被发现

用作linker 输入文件,libfoo.a 通常称为静态库。这个 用法对于不熟练的程序员来说是一个永久的困惑源,因为它导致他们 认为存档 libfoo.a 与 DSO 非常相似,libfoo.so, 通常称为 dynamic/shared 库 ,并在此基础上建立错误的期望 基础。事实上 "static library" 和 "dynamic library" 根本不是相似的东西 并以完全不同的方式在 linkage 中使用。

一个明显的区别是静态库不是由linker生成的, 但是 ar。所以没有 linkage 发生,没有符号解析发生。存档的 目标文件没有改变:它们只是放在一个包里。

当存档在 link 由 linker - 例如程序或 DSO - linker 在包中查看是否有 其中是否有任何目标文件为未解析的符号引用提供定义 在 link 年龄的早期积累的。如果找到任何文件,它会从中提取这些目标文件 bag 和 links them 到输出文件中,就好像它们是单独命名的一样 在 linker 命令行和存档中根本没有提到。所以整个 linkage 中存档的作用是作为目标文件包,linker 可以从中 select 它需要继续 link 年龄。

默认情况下,GNU ar 将其输出存档准备好用作 linker 输入。它添加了一个假 "file" 到存档,带有一个神奇的假文件名,并在这个假文件中写入内容 linker 能够从定义的全局符号中读取 table 通过存档中的任何对象文件到这些对象的名称和位置 存档中的文件。此查找 table 使 linker 能够查看 存档并识别定义任何未解析符号引用的任何目标文件 它已经到手了。

您可以使用 q ( = quick) 选项 - 实际上你已经在自己的 ar 示例中使用过 - 而且 使用 (capital) S ( = no symbol table) 选项。如果您调用 ar 来创建或更新 出于任何原因没有(最新的)符号 table 的存档,那么你 可以用 s 选项给它一个。

ranlib

ranlib 没有 完全创建库。在 Linux 中,ranlib 是一个遗留程序,它添加了一个(最新的) 符号 table 到 ar 档案,如果它没有的话。它的效果正是 与 ar s 相同,GNU ar。从历史上看,在 ar 装备生成之前 一个符号 table 本身,ranlib 是注入魔法虚假文件的杂耍 放入存档中,使 linker 能够从中挑选目标文件。在非 GNU 中 为此,可能仍需要类 Unix OSes,ranlib。你的例子:

ar qc libgraphics.a *.o
ranlib libgraphics.a

说:

  • 通过将当前目录中的所有 *.o 文件附加到存档来创建 libgraphics.a 目录,无符号 table.
  • 然后在libgraphics.a
  • 中加上一个符号table

在 linux 中,这与以下内容具有相同的净效果:

ar cr libgraphics.a *.o

ar qc libgraphics.a *.o 本身创建了一个存档,linker 不能使用,因为它没有符号 table.

ld

你的例子:

ld -r -o libgraphics.a *.o

其实很不正统。这说明 linker 的使用相当罕见, ld,通过 link 将多个输入文件合并到 合并 目标文件 一个单一的输出目标文件,其中符号解析已经尽可能, 给定输入文件。 -r ( = relocatable) 选项 指示 linker 通过以下方式生成目标文件目标(而不是程序或 DSO) link尽可能输入,如果未定义的符号引用,不要让 linkaqe 失败 保留在输出文件中。这种用法叫做 partial linking.

ld -r ...的输出文件是目标文件,不是ararchive,并且 指定一个 看起来 类似于 ar 存档的输出文件名并不能使它成为一个。 所以你的例子说明了一种欺骗。这个:

ld -r -o graphics.o *.o

会说实话。我不清楚这种欺骗的目的是什么, 因为即使一个 ELF 目标文件被称为 libgraphics.a,并以该名称输入到 linkage, 或者通过 -lgraphics,linker 将正确地将其识别为 ELF 对象文件,而不是 ar 存档,并将使用 它在命令行中使用任何目标文件的方式:它 link 无条件地使用它 到输出文件中,而输入真正存档的目的是 link 存档成员仅在引用它们的条件下。也许你只是有 消息不灵通的例子 linking here.

总结...

我们实际上只见过 一种 生产方式 通常称为,这就是所谓的静态库的产生, 通过归档一些目标文件并在归档中放置一个符号 table。

而且我们还没有看到如何生产另一种最重要的东西,通常被称为 一个,即动态共享Object/sharedlibrary/dynamic库。

像程序一样,DSO 是由 linker 生成的。程序和 DSO 是 OS 加载程序理解并可用于 assemble 的 ELF 二进制变体 一个 运行 过程。通常我们通过 GCC 前端之一调用 linker(gccg++gfortran 等):

链接程序:

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

链接 DSO:

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

共享库和静态库都可以提供给 linker 通过统一的 -lfoo 协议,当你正在 linking 一些其他程序或 DSO 时。 该选项指示 linker 扫描其指定的或默认的搜索目录以查找 libfoo.solibfoo.a。默认情况下,一旦找到其中任何一个,它就会将该文件输入到 linkage,并且 如果它在同一个搜索目录中找到两者,它会更喜欢 libfoo.so。 如果 libfoo.so 被 selected 则 linker 将该 DSO 添加到运行时依赖列表 您正在制作的任何程序或 DSO。如果 libfoo.a 被 selected 然后 linker 使用存档作为 selection of object files for linkage 进入输出文件,如果需要,就在那里。没有运行时依赖 libfoo.a本身是可以的;它不能映射到一个过程中;它对 OS 加载程序没有任何意义。