R71*_*R71 17 linker ld unix-ar
要使用*.o文件在c ++/unix中创建库,我注意到我的项目中有两种不同的方式(遗留代码):
ar qc libgraphics.a *.o
ranlib libgraphics.a
Run Code Online (Sandbox Code Playgroud)
和
ld -r -o libgraphics.a *.o
Run Code Online (Sandbox Code Playgroud)
这两种方法有什么区别,哪种方法用于什么目的?
Mik*_*han 32
AR
在Linux中,ar是GNU通用归档器.(ar在其他类Unix操作系统中有非GNU变体).有了选项c
ar c... archive-name file...
Run Code Online (Sandbox Code Playgroud)
它创建一个包含副本的存档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是一个遗留程序,它将(uptodate)符号表添加到ar归档中(如果它没有).它的效果ar s与GNU 完全相同ar.从历史上看,之前ar有能力生成符号表本身,ranlib是将魔术文件注入存档的kludge,使链接器可以从中选择对象文件.在非GNU类Unix操作系统中,ranlib可能仍然需要用于此目的.你的例子:
ar qc libgraphics.a *.o
ranlib libgraphics.a
Run Code Online (Sandbox Code Playgroud)
说:
libgraphics.a通过将*.o当前目录中的所有文件附加到存档来创建,不包含符号表.libgraphics.a在linux中,这具有与以下相同的净效果:
ar cr libgraphics.a *.o
Run Code Online (Sandbox Code Playgroud)
它本身ar qc libgraphics.a *.o创建了一个链接器无法使用的存档,因为它没有符号表.
LD
你的例子:
ld -r -o libgraphics.a *.o
Run Code Online (Sandbox Code Playgroud)
其实很不正统.这说明相当罕见使用的连接器,
ld以产生合并通过连接多个输入文件到一个输出目标文件,其中符号解析已经完成目标文件,只要是可能给定的输入文件.所述-r(= 重定位)选项指示链接器以产生一个对象文件目标(而不是节目,或DSO)通过链接输入尽可能而不是如果未定义的符号引用在输出文件中失败的linkaqe.此用法称为部分链接.
输出文件ld -r ... 是一个目标文件,而不是一个 ar 存档,并指定一个看起来像ar存档的输出文件名不会使它成为一个.所以你的例子说明了一个欺骗.这个:
ld -r -o graphics.o *.o
Run Code Online (Sandbox Code Playgroud)
这是真实的.我不清楚这种欺骗的目的是什么,因为即使调用ELF目标文件libgraphics.a,并且通过该名称输入到链接,或者-lgraphics链接器将正确地将其标识为ELF目标文件而不是ar存档,并且会以它在命令行中使用任何目标文件的方式使用它:它将它无条件地链接到输出文件中,而输入真正存档的目的是仅在引用它们的条件下链接存档成员.也许你只是在这里有一个不明智的链接的例子.
包起来...
我们实际上只看到了一种生成通常称为库的东西的方法,那就是生成一个所谓的静态库,通过归档一些目标文件并在归档中放置一个符号表.
我们根本没有看到如何生成通常称为库的其他最重要的东西,即动态共享对象/共享库/动态库.
与程序一样,DSO由链接器生成.程序和DSO是ELF二进制的变体,OS加载器可以理解并可以用来组装正在运行的进程.通常我们通过GCC前端的一个一个(调用链接gcc,g++,gfortran,等):
链接程序:
gcc -o prog file.o ... -Ldir ... -lfoo ...
Run Code Online (Sandbox Code Playgroud)
链接DSO:
gcc -shared -o libbar.so file.o ... -Ldir ... -lfoo ...
Run Code Online (Sandbox Code Playgroud)
-lfoo当您链接某些其他程序或DSO时,可以通过统一协议向链接器提供共享库和静态库.该选项指示链接器扫描其指定或默认搜索目录以查找
libfoo.so或libfoo.a.默认情况下,一旦找到其中任何一个,它就会将该文件输入到链接中,如果它在同一个搜索目录中找到它,则会更喜欢libfoo.so.如果libfoo.so选择了,则链接器会将该DSO添加到您正在创建的任何程序或DSO的运行时依赖关系列表中.如果libfoo.a选择了,则链接器将存档用作选择的目标文件,以便在需要时链接到输出文件中,然后在那里.没有运行时依赖于
libfoo.a自身; 它无法映射到一个过程; 它对OS加载器没有任何意义.