非常奇怪的链接器行为

46

这很奇怪,因为我通过删除对libm的引用来消除了下面的错误。

gcc -o example example.o -Wl -L/home/kensey/cdev/lib -L/usr/lib/x86_64-linux-gnu   -lmysqlclient -lpthread -lz -L/usr/lib/x86_64-linux-gnu -lm -lrt -ldl -lcdev -L/home/kensey/www.tools/gplot-lib -lgplot -L/home/kensey/www.tools/gd1_3ret -lgd -lxml2 -lcurl
/usr/bin/ld: /home/kensey/www.tools/gplot-lib/libgplot.a(set.o): undefined reference to symbol 'floor@@GLIBC_2.2.5'
/usr/bin/ld: note: 'floor@@GLIBC_2.2.5' is defined in DSO /usr/lib/x86_64-linux-gnu/libm.so so try adding it to the linker command line
/usr/lib/x86_64-linux-gnu/libm.so: could not read symbols: Invalid operation
collect2: ld returned 1 exit status

如果我从命令中删除-lm,就不会出现错误。然而,我想知道为什么删除所需的库的引用就能解决这个问题。链接器如何知道要查找哪个库?另外,有没有一种方法可以查询已构建的可执行文件并说“你是如何解析对'floor'的引用的”?显然,有些事情我不理解,这让我感到困扰...


1
“-Wl”选项通常后跟逗号(将逗号后的文本作为链接器选项传递),您打算如何处理它? - Andre Holzner
9个回答

84

发生的情况很简单:

  1. 您的libgplot.a依赖于libm.so,但连接行中-lm-lgplot的顺序是错误的。库在连接行中的顺序很重要。通常,系统库(-lpthread-lm-lrt-ldl)应该在连接行中放在其他所有内容的后面。

  2. 当您从连接行中删除-lm时,libm.so.6仍然会被一些出现在连接行后面的其他库(如libgdlibxml2libcurl)拉入连接中,因为这些库依赖于libm.so.6。但现在libm.so.6在连接行中的位置正确了,所以一切都正常工作。

如果我在链接命令的末尾加上 -lm,并将其列为最后一个库,那么就不会出现错误。
这证实了上面的解释。

是的,明确添加“-lm”就是问题所在。谢谢! - Qix - MONICA WAS MISTREATED

9

我已经用 export LDFLAGS="$LDFLAGS -lm" 解决了同样的问题。


你能解释一下这个做什么吗? - lesolorzanov
2
用于链接标准C数学库的选项“-lm” - dmnc
我的问题并不完全是原始问题,但在我的LDFLAGS定义末尾添加-lm到makefile中起作用了。谢谢。 - Colin Keenan

6
也许你的库搜索路径 (/usr/local/lib/ 或 /usr/lib/, ...) 没有包含 64 位的 libm,因此如果你使用 l 标记指定它,gcc 就无法找到它。如果你只指定目录,那么看起来它就能找到正确的库。所以你可以尝试: LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu 并使用 -lm

好的,我又做了一些尝试,如果我在链接命令的末尾加上-lm,将其列为最后一个库,就不会出现错误。非64位libm的理论仍然可能成立,因为也许它可以在到达命令末尾的-lm之前‘找到正确的一个’,因此-lm基本上是被忽略了。FYI - 我通过'ar -t'查询了libm,并成功列出了库的内容。这表明它是64位/可搜索的。 - Don Wool

3
很难说。因为命令行中有自定义库目录,所以可以想象-lm链接了一个不兼容的替代版本。没有-lm,链接器可能会引入另一个版本,因为它是你链接的某个库所需的。
为了确保strace调用并查看在两种情况下libm.so来自哪里。
顺便说一下,-Wl开关似乎没有任何作用,并且-L/usr/lib/x86_64-linux-gnu被提到了两次。

打开("/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0pU\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0644, st_size=1022320, ...}) = 0 - Don Wool
结果对于两者来说都是相同的。 <br> kensey@kensey:〜/cdev $ strace ./example 2>&1 | grep libm open(“/usr/lib/x86_64-linux-gnu/libmysqlclient.so.18”,O_RDONLY | O_CLOEXEC)= 3 open(“/lib/x86_64-linux-gnu/libm.so.6”,O_RDONLY | O_CLOEXEC)= 3 <br>所以,我不确定为什么会出现这个问题,但幸运的是它消失了。 我想教训是:确保库在可能引用它们的其他库之后。 可能有一种方法可以使用strace进行更多调查,但我对该工具还不熟悉。 感谢您的帮助! - Don Wool

2

为了补充答案列表,http://fedoraproject.org/wiki/UnderstandingDSOLinkChange 是一个信息丰富的网站。虽然不相关于上述问题,但是它的解释与错误信息 /usr/bin/ld: note: 'some_reference' is defined in DSO some.so so try adding it to the linker command line 有关。


事实上,对于GNU工具链的早期版本,这似乎是一种行为变化。如果我没记错的话,过去在gcc中给出库的顺序是无关紧要的,因为它足够聪明以找到符号。在AIX上的xlC中,如果我没记错的话,过去顺序也很重要... - Andre Holzner

1
一个可能的解释是:
可能存在一个弱链接的函数`foo`在libm之外定义,被libm内定义的一个强链接版本的`foo`所取代,并且正是这个强链接版本调用了未定义的函数。
这可以解释为什么添加库会导致未定义函数错误。

请注意,该符号已经进行了版本控制。它是否仍然适用? - Maxim Egorushkin
@MaximYegorushkin:不确定,抱歉。我认为未定义符号的版本与我的可能诊断是正交的。 - Andrew Tomazos

1
我刚遇到了类似的问题;我记得在过去的gcc版本中,库的顺序并不重要(至少在我处理的情况下是这样)。在这里的问题中,有人注意到行为似乎在4.4和4.5之间发生了变化。
在我的情况下,我通过在以下位置进行链接来摆脱错误消息:
 g++ -Wl,--copy-dt-needed-entries [options] [libraries] [object files] -o executable-file

GCC版本无关紧要,你所说的是链接器的变化而不是编译器的变化。 - Jonathan Wakely

0
我曾经遇到过类似的问题,因为我手动更新了我的CentOS机器上的开发工具链,以解决VScode远程依赖性,并将C++库与C代码链接。
在我的情况下,我通过在Makefile中添加以下内容来解决这个问题: LDFLAG=-Wl,--copy-dt-needed-entries 我还将我的gcc指向我想要的版本(在更新工具链后,gcc指向工具链:/opt/rh/devtoolset-2/root/usr/bin/gcc) CC=\usr\bin\gcc 这是(gcc版本4.4.7)

-1

使用这个:

administrator@administrator-Veriton-M200-H81:~/ishan$ gcc polyscanline1.cpp -lglut -lGLU -lGL -lm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接