2


我在一个大项目中工作。现在遇到链接错误。
可以通过解决方法避免此错误,但我无法弄清楚它为什么会起作用。

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

项目
  |-package_a
    |--a.cpp
    |--...
  |-package_b
    |--b.cpp
    |--c.cpp
    |--...
  |-package_others


package_a 中的所有 *.o 将被打包到 aa 中,而 package_b 中的 *.o 将被打包到 ba

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

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

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

通过将链接命令更改为"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

任何评论将不胜感激

4

4 回答 4

4

这个是正常的。链接时,链接器遍历目标文件列表,找到未定义的引用,然后由其后的其他目标文件/库满足

您可以通过以下任一方式更改此行为

  • 包括其中一个档案两次,如

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

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

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

运气不好...也许经理或任何不希望您使用 from 中的功能的ba

然后我在 package_b/c.cpp 中添加了一个虚拟函数 bar(),它只调用 foo(),然后原始的“g++ -o exec -Bstatic ba aa ...”将运行,没有任何链接错误。

可能package_b/c.cpp是已经引用了另一个函数,并且链接器使用了bar()它(因为它们在同一个文件中)和 this referenced foo(),随后也包含在输出中。它成功了,因为它foo也在b.a里面。

于 2011-11-07T12:15:43.203 回答
3

您可能想了解链接器的工作原理。顺便说一句,-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.
于 2011-11-07T12:13:59.243 回答
2

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

这里有深入的解释。

于 2011-11-07T12:12:19.877 回答
0

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

于 2011-11-07T12:17:12.420 回答