2011-11-07 70 views
2


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

下面是有关我的问题的文件结构:

project 
    |-package_a 
    |--a.cpp 
    |--... 
    |-package_b 
    |--b.cpp 
    |--c.cpp 
    |--... 
    |-package_others


所有*在package_a的.o将被装在到AA和*的.o在package_b 被打成BA

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

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

但我在这里会得到一个链接错误,说undefined参考foo()在a.o
我可以验证(通过objdump)foo()已经在b.o.

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

然后我说在package_b/c.cpp虚拟函数bar(),它什么也不做 只是调用foo(),那么原来"g++ -o exec -Bstatic b.a a.a ..."将运行 通过没有任何链接错误。

任何人都可以告诉我为什么只是在 中添加一个虚拟函数,在这种情况下,同一个包可以工作吗?

我使用克++ 4.4.4和Linux 2.6.18-194.el5

任何评论将被理解

回答

4

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

您可以通过

  • 包括档案的一个两次使用改变这种行为,如

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

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

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

很幸运......也许经理或谁不想让你使用b中的函数a

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

难道说的package_b/c.cpp另一个功能已经引用,并链接了bar()它(因为它们是在同一个文件),这引用foo(),后来包括在输出了。它成功了,因为foo也在b.a之内。

+0

'可能是package_b/c.cpp的另一个函数已经被引用,并且链接器带有bar()(因为它们在同一个文件中),并且这个引用的foo()随后被包含在输出中,太。它成功了,因为foo也在b.a中。'
我仍然怀疑它,你的意思是package_b/c.cpp在链接器处理a.a之前已经被引用了吗?但是当它引用package_b/c.cpp中的那些符号时,怎么可能没有链接错误? –

+0

@ user1033573:当一个目标文件引用当前库/档案中的一个符号时,它就会起作用,你不需要做任何特殊的事情。或者我误解了你的问题? – jpalecek

+0

你说c.cpp中的另一个函数,比如bar2()被引用,并且链接器带上了bar()。我的理解是,这发生在连接器交付a.a之前,对吗?但为什么连接器不抱怨bar2()没有定义,因为b.a仍然在链接序列中。 –

2

GCC,不同于视觉-C++ - 连接体,需要将供给的静态库以便在使用之前定义引用。不要问我为什么,但是你总是必须检查你是否用GCC以正确的顺序列出了要链接的文件。

有一个深入的解释here

+0

谢谢Pollex,它解释了第一个解决方案,但不是第二个(在c.cpp中添加了虚拟函数) –

+0

对循环依赖的支持付出了代价:您需要使用两遍算法来解决所有符号(性能更差),或者你需要保留反向引用到未解码符号的地方(更糟糕的代码);恕我直言,这个好处对一些人来说很小,对大多数人来说是无效的。 IMHO2,循环依赖是一个值得怀疑的问题:如果你同时需要这两个,为什么不把它们作为一个包。 –

3

您可能想了解how linkers work。顺便说一句,-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. 
+1

这不是连接器通常工作的方式。 VC++可以以任何顺序处理依赖关系,我想不出为什么GCC不应该能够做同样的事情。 –

+1

VC++在这方面更为宽容。 UNIX连接器可以像VC++一样工作,但需要问一下,看看我的更新。 –

+0

@ maxim-yegorushkin最好有选择做它。 “以任何顺序”很方便,但速度较慢。循环依赖性总是代码或库组织设计糟糕的标志。 –

0

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