非常奇怪的链接器行为

Posted

技术标签:

【中文标题】非常奇怪的链接器行为【英文标题】:Very strange linker behavior 【发布时间】:2012-04-13 15:23:28 【问题描述】:

这很奇怪,因为我能够通过删除对 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 部分,我不会收到错误消息。但是,我想知道是否有人知道为什么删除对所需库的引用会解决此问题。链接器如何知道要查看哪个库?另外 - 有没有办法查询构建的可执行文件并说“您解决了对“地板”的引用是哪个库?显然,发生了一些我不明白的事情,这让我很困扰......

【问题讨论】:

-Wl 选项后面通常跟一个逗号(将逗号后面的文本作为选项传递给链接器),你打算用它做什么? 【参考方案1】:

对发生的事情的解释很简单:

    您的libgplot.a 依赖于libm.so,但链接行上-lm-lgplot 的顺序是错误的。 链接行上库的顺序确实 matter。一般来说,系统库(-lpthread-lm-lrt-ldl)应该跟随链接行上的所有其他内容。

    当您从链接行中删除 -lm 时,libm.so.6 仍会被稍后出现在链接行上的某个其他库(libgdlibxml2libcurl)拉入链接因为那个库依赖于libm.so.6。但是现在libm.so.6 在链接线上的正确位置,所以一切正常。

如果我将 -lm 放在链接命令的末尾,将其列为最后一个库,则不会出现错误。

这证实了上面的解释。

【讨论】:

感谢您提供如此详细的解释 是的;明确添加-lm 是问题所在。谢谢!【参考方案2】:

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

【讨论】:

你能解释一下这是做什么的吗? -lm 用于链接标准 C 数学库 我的问题并不完全是最初的问题,但在我的 LDFLAGS 定义末尾的 makefile 中添加 -lm 是有效的。谢谢。【参考方案3】:

也许,您的库搜索路径(/usr/local/lib/ 或 /usr/lib/,...)不包含 64 位 libm,因此如果您使用 l 标志指定,gcc 无法找到它。如果您只指定目录,它看起来可以找到正确的目录。所以你可以试试:

LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu

并使用-lm

【讨论】:

好吧,我又玩了一些,如果我把 -lm 放在链接命令的末尾,将它列为最后一个库,我不会收到错误。非 64 位 libm 的理论可能仍然如此,因为它可能在命令末尾到达 -lm 之前“找到正确的”,所以 -lm 基本上被忽略了。仅供参考-我通过'ar -t'查询了libm,它列出了库的内容ok。所以这意味着它是 64 位/可搜索的。【参考方案4】:

很难说。因为命令行中有自定义库目录,所以可以想象-lm 链接了一个不兼容的替代版本。如果没有-lm,链接器可能会引入它的另一个版本,因为您链接的一个库需要它。

确保strace 两次调用并查看libm.so 在这两种情况下的来源。

顺便说一句,-Wl 开关似乎什么也没做,-L/usr/lib/x86_64-linux-gnu 被提及两次。

【讨论】:

open("/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3 读取(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 事实证明两者都是一样的.. 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 所以,我不知道为什么这是个问题,但幸运的是它消失了。我想教训是:确保库在可能引用它们的其他库之后。 strace 可能有一种方法可以进行更多调查,但我是该工具的新手。谢谢你的帮助!【参考方案5】:

只是为了添加到答案列表中,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,如果我过去没记错的话,顺序很重要...【参考方案6】:

一种解释可能是:

可能有一个在 libm 外部定义的弱链接函数 foo 被 libm 内部定义的 foo 的强链接版本替换,并且正是这个强链接版本调用了未定义的函数。

这将解释添加库如何导致未定义函数错误。

【讨论】:

请注意,该符号是版本化的。它仍然适用吗? @MaximYegorushkin:不确定抱歉。我认为未定义符号的版本与我可能的诊断正交。【参考方案7】:

我刚刚遇到了类似的问题;我记得过去对于 gcc,库的顺序并不重要(至少在我使用的情况下不重要)。在this question here 中,有人注意到行为似乎在 4.4 和 4.5 之间发生了变化。

就我而言,我通过在以下位置进行链接消除了错误消息:

 g++ -Wl,--copy-dt-needed-entries [options] [libraries] [object files] -o executable-file

【讨论】:

GCC 版本无关紧要,您说的是链接器更改而不是编译器更改。【参考方案8】:

我遇到了类似的问题,因为我手动更新了我的 centOS 机器上的开发工具链以解决 VScode Remote 依赖项,并将 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)

【讨论】:

【参考方案9】:

使用这个:

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

【讨论】:

以上是关于非常奇怪的链接器行为的主要内容,如果未能解决你的问题,请参考以下文章

-ObjC 链接器标志有啥作用?

C ++中反向迭代器的非常奇怪的行为

奇怪的 Xcode 链接器错误

使用 <experimental/filesystem> 并调用函数的链接器错误

项目存档上的 Apple Mach-O 链接器错误

构造一个指向 alloca 的函数指针会导致链接器错误?