假设我有两个文件
// a.c
int a() {return 1;}
// b.c
int a();
int b() {return a();}
,然后将它们分别编译为a.o
和b.o
。
为了制作可执行文件或共享库,可以调用gcc a.o b.o -o libab.so -shared
。但我也注意到,也可以调用gcc b.o -L. -l:a.o -o libab.so -shared
来生成(显然)相同的输出。令我惊讶的是,即使运行gcc a.o -L. -l:b.o -shared
也会导致同时具有a()
和b()
的库。 (链接器是否应丢弃未使用的库b.o
,因为a.o
不依赖它?)
后两个可能通过a
好像a.o
是一个库一样。现在,如果我运行ar rcs liba.a a.o
,则gcc b.o -L. -l:liba.a -shared
和gcc b.o liba.a -shared
都可以正常运行,并给出相同的输出。
但是,我也看到了这种技巧不起作用并导致未定义引用的情况。因此,我的问题就如标题所说:将对象作为库和普通对象文件传递之间有什么区别?在C ++方面有什么区别?
问题出现在一个更大的项目中。很抱歉缺少mcve,因为我似乎无法隔离问题。
答案 0 :(得分:1)
[如何]将
-l<libname>
传递给lib<libname>.a
与直接传递给链接器?
传递-llibname.so
会使GNU链接程序在搜索符号时仅遍历库一次(如果不在--whole-archive
选项之后)。直接将.a
文件指定给链接器,使其可以在.a
文件内的所有目标文件中搜索每个符号的符号,而不仅是搜索一次。
来自GCC Linker options(重点是我):
-图书馆
...
在命令中写入此选项的位置有所不同;链接器按照指定的顺序搜索和处理库和目标文件。因此,“ foo.o -lz bar.o”在文件foo.o之后但在bar.o之前搜索库“ z”。如果bar.o引用了“ z”中的函数,则可能不会加载这些函数。
-l namespec
...
链接器将在命令行中指定的位置仅搜索一次存档。如果归档文件定义了在命令行上归档文件之前出现的某个对象中未定义的符号,则链接器将包含归档文件中的相应文件。但是,稍后出现在命令行中的对象中未定义的符号将不会导致链接程序再次搜索档案。
答案 1 :(得分:1)
将对象作为库与普通对象文件进行传递有什么区别?在C ++中有什么区别?
这取决于实现方式。从最一般的意义上讲,诸如Unix之类的链接器是在库搜索路径中询问通过-l
选项命名的搜索对象,而如果直接命名文件,则必须指定确切的文件。
此外,如果使用-l
选项指定要链接的文件,则在通常情况下,链接器通过在参数前添加“ lib”并附加“ .a”来构造自变量的文件名,或者在其他方式,例如也搜索“ .so”文件。 (当参数的第一个字符为:
时,您似乎正在使用的GNU链接器对此行为提供了例外。在这种情况下,它将其余的参数用作精确的文件名,并进行搜索。 )
许多链接器还接受在命令行上指定的显式库名称(例如,libfoo.a
而不是-lfoo
),因此,这些链接器需要能够确定每个文件的类型。通常,这是通过检查文件而不是依靠文件名来进行的。而且,GNU ld
至少将此文件类型检测扩展到通过-l
选项指定的文件。
在命令行上以任何特定形式指定对象和库的顺序对于典型的链接器实现都很重要。例如,the docs for GNU ld指定
引用文件的选项(例如“ -l”或“ -T”)导致文件 在该选项出现在命令行的位置处被读取, 相对于目标文件和其他文件选项
这很重要,因为
链接器仅在存档位置搜索一次存档 在命令行上指定。如果档案库定义了符号 在出现在存档之前的某些对象中未定义 在命令行上,链接器将包含适当的文件 从档案中。但是,出现在对象中的未定义符号 稍后在命令行上将不会导致链接器搜索 再次存档。
当然可以
您可以在命令行上多次列出同一档案。</ p>
文档对此尚不完全清楚,但是根据经验,以上术语“归档”的使用意义重大。实际上,只有 archive 文件(静态库)适用“仅搜索一次”的规定。初步估算,无论如何指定,GNU链接器命令行上不同普通对象文件和共享库的相对顺序都不会影响符号解析。
是的,是否对(GNU)链接器指定常规对象文件,静态归档文件或共享库都很重要,它们的顺序在一定程度上很重要,但在其中指定的 manner 他们没关系。
我还看到这种技巧不起作用并导致未定义引用的情况。
对于GNU链接器,这是因为确实缺少库或对象,或者是因为静态归档相对于其他对象文件或归档的顺序不合适。其他一些链接器更加敏感。
答案 2 :(得分:0)
简短答案:
-L
和-l
选项为查找库档案(和共享库)提供了快捷方式。但是,一旦您使用-l
来定位库(在标准位置或由-L
指定的位置)后,该库的读取就与您读取库的方式相同在命令行上的同一位置明确指定其文件名(例如/lib/libx.a
)。
指定单个对象(.o
)文件时,该文件的全部内容将无条件加载。指定库存档(.a
)文件时,仅加载其中必需的(满足未定义的未定义引用)的那些对象。