g++链接错误-解决方法有效的原因

A g++ link error - why the workaround works

本文关键字:有效 方法 解决 链接 错误 g++      更新时间:2023-10-16


我正在做一个大项目。现在遇到链接错误
这个错误可以通过一个变通方法来避免,但我不明白它为什么有效。

以下是与我的问题相关的文件结构:

项目|-软件包a|--a.cpp|--。。。|-软件包b|--b.cpp|--c.cpp|--。。。|-其他软件包


package_a中的所有*.o将打包到package_b中的a.a和*.o将装入b.a

"g++ -o exec -Bstatic b.a a.a ..."用于生成二进制文件。

在package_b/b.cpp中,我添加了一个函数foo((
在package_a/a.cpp中,我使用了这个函数

但这里我会得到一个链接错误,说a.o中foo((的未定义引用
我可以(通过objdump(验证foo((已经在b.o.中

通过将link命令更改为"g++ -o exec -Bstatic a.a b.a ...",可以成功构建二进制文件。我现在明白了链接器确实关心链接列表中的顺序。但请理解这是一个大项目,我无权更改项目配置,因此必须保留原始链接顺序

然后我在package_b/c.cpp中添加了一个伪函数bar((,它什么都不做但只要调用foo((,原始的"g++ -o exec -Bstatic b.a a.a ..."就会运行通过而没有任何链接错误

有人能告诉我为什么在在这种情况下,同样的程序包会起作用吗?

我使用的是g++4.4.4和linux 2.6.18-194.el5

如有任何意见,我们将不胜感激。

这是正常的。链接时,链接器会遍历对象文件列表,找到未定义的引用,然后由之后的其他对象文件/库来满足这些引用。

你可以通过改变这种行为

  • 包括两次档案中的一次,如

    g++ -o exec a.a b.a a.a
    
  • 使用-(构建

    g++ -o exec -( a.a b.a -)
    

但请理解这是一个大项目,我无权更改项目配置,因此必须保留原始链接顺序。

运气不好。。。也许经理或其他人只是不希望您使用ab中的函数。

然后我在package_b/c.cpp中添加了一个伪函数bar((,它只调用foo((,然后原始的"g++-o exec-Bstatic b.a a.a…"将在没有任何链接错误的情况下运行。

可能是package_b/c.cpp的另一个函数已经被引用,链接器带走了bar()(因为它们在同一个文件中(,而这个函数引用了foo(),随后也包含在输出中。它成功了,因为foo也在b.a中。

您可能想了解链接器的工作原理。BTW,-Bstatic标志是不必要的,因为.a.对象文件档案仅静态链接(就好像在命令行上指定了.a中包含的对象文件列表而不是.a(。

或者,您可以始终使用--start-group/--end-group选项包装要链接的存档列表,使链接器多次扫描存档列表,这样就不需要对存档进行排序(就像MS VC++所做的那样(:

g++ -o exec -Wl,--start-group a.a b.a -Wl,--end-group

参见man ld:

   -( archives -)
   --start-group archives --end-group
       The archives should be a list of archive files.  They may be either
       explicit file names, or -l options.
       The specified archives are searched repeatedly until no new
       undefined references are created.  Normally, an archive is searched
       only once in the order that it is specified on the command line.
       If a symbol in that archive is needed to resolve an undefined
       symbol referred to by an object in an archive that appears later on
       the command line, the linker would not be able to resolve that
       reference.  By grouping the archives, they all be searched
       repeatedly until all possible references are resolved.
       Using this option has a significant performance cost.  It is best
       to use it only when there are unavoidable circular references
       between two or more archives.

GCC与VisualC++链接器不同,它要求按顺序提供静态库,以便在使用引用之前定义它们。不要问我为什么,但你必须始终检查你是否列出了要与GCC按正确顺序链接的文件。

这里有一个深入的解释。

使用静态库中的函数时,必须首先在命令行中放置使用该函数的文件,然后放置定义该函数的库。否则,如果首先放置定义,gcc(或者更具体地说,ld(将丢弃"未使用"的函数。抱歉,gcc就是这样工作的。