制作库的ranlib,ar和ld有啥区别

Posted

技术标签:

【中文标题】制作库的ranlib,ar和ld有啥区别【英文标题】:what is the difference between ranlib, ar, and ld for making libraries制作库的ranlib,ar和ld有什么区别 【发布时间】:2018-06-03 07:01:19 【问题描述】:

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

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

ld -r -o libgraphics.a *.o

这两种方法有什么区别,分别用于什么目的?

【问题讨论】:

ar 创建或更新库。 ranlib 重新生成它的索引(您可以进行多次更新,然后运行一次 ranlib)。 ld 创建了一种完全不同类型的库(我认为给它们 .a 后缀是错误的和误导性的;在你的情况下, ld 的输出是一个应该被赋予 .o 后缀的合并目标文件)。 【参考方案1】:

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 前端之一(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本身是可能的;它不能映射到一个进程中;它对操作系统加载器没有任何意义。

【讨论】:

我见过的最好的答案之一。感谢您分享您的知识。 (1) "...您的示例说明了一个欺骗。" - 在这种情况下,.a 扩展表示二进制文件是一个完整的即用型库而不是脱离上下文的目标文件。由于在静态库的 Unix 平台上,通常带有 .a 扩展名,也许以这种方式向拥有此类文件的用户表达它并不是一个坏主意。对于最终链接,它在技术上也不重要,所以在我看来,欺骗实际上是用相反的方式命名它(.o)。 (2) 这是一篇关于此类静态库的所谓动机的文章:Hiding Symbols in Static Libraries。但请放心,链接器无法从此类合并的目标文件中进行选择性提取会损害此类解决方案 IMO :) 一个很好的解释,我第一次使用“谢谢”图标。再次感谢。

以上是关于制作库的ranlib,ar和ld有啥区别的主要内容,如果未能解决你的问题,请参考以下文章

使用 gcc-ar 和 gcc-ranlib 的 autoconf 配方

打包静态库.a文件的方法(ar,ranlib,nm命令介绍)

arm-linux-ar 和 arm-linux-ranlib 的使用

无法读取符号:存档没有索引;运行ranlib添加一个

Binutils-2.27

华为AR1200和华为AR1220E-S有啥区别