【问题标题】:Order of libraries passed to linker传递给链接器的库顺序
【发布时间】:2016-04-01 23:52:43
【问题描述】:

在我生命中的某个时间点,传递给 gcc 的库的顺序很重要。您将 gcc 传递给从最依赖到最不依赖的库列表。例如考虑以下源代码:

testlib.c

include <math.h>

double proxy_sqrt(double x)
{
    return sqrt(x);
}

testlib.h

double proxy_sqrt(double);

使用-testlib.c

#include "testlib.h"

int main(int argc, char* argv[])
{
    proxy_sqrt(36);
    return 0;
}

然后编译链接:

gcc -c -o testlib.o testlib.c
ar rvs testlib.a testlib.o
gcc -o use-testlib use-testlib.c testlib.a -lm

请注意,最后一步使用依赖程度最高到最小的顺序。

但是这个(无效的?)命令在 SLES12 上有效

gcc -o use-testlib use-testlib.c -lm testlib.a 

但在 Ubuntu 14.04 上失败......

gcc -o use-testlib use-testlib.c -lm testlib.a
testlib.a(testlib.o): In function `proxy_sqrt':
testlib.c:(.text+0x1b): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status

有人知道为什么吗?

2 个编译器的详细输出显示在以下链接中:

sles12

http://pastebin.com/sKe8B7V9

ubuntu14.04

http://pastebin.com/vf8fTaE2

【问题讨论】:

  • 确定就是您创建testlib.o 对象文件的方式吗?你不应该在那里使用-c 标志吗?
  • 至于你的问题,你在不同平台上使用的是什么链接器,尤其是 version 是什么?
  • 使用-v 标志来查看你的链接器是如何真正被调用的。您可能会发现libm 是一个系统的默认值,而不是另一个系统的默认值....
  • @CarlNorum 也发生在其他库中。这是真实案例的简化示例。
  • @sashan,你还是应该弄清楚你的链接器到底在做什么。

标签: c gcc linker ubuntu-14.04 suse


【解决方案1】:

查看链接器 (collect2) 命令与 --as-needed--no-as-needed 选项的区别:

SLES 12

/usr/lib64/gcc/x86_64-suse-linux/4.8/collect2 --build-id --eh-frame-hdr \
    -m elf_x86_64 -dynamic-linker … /tmp/ccgoQd94.o -lm testlib.a -lgcc \
    --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s \
    --no-as-needed /usr/lib64/gcc/x86_64-suse-linux/4.8/crtend.o \
    /usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../lib64/crtn.o

Ubuntu 14

/usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 --sysroot=/ --build-id --eh-frame-hdr \
    -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker … \
    /tmp/cciheQTH.o -lm testlib.a -lgcc --as-needed -lgcc_s \
    --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed \
    /usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o \
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crtn.o

与 SLES 12 相比,在 Ubuntu 14 的选项开头使用 --as-needed 改变了事物的行为。据说,这是为了让事情变得更容易。我仍然相信——它似乎为代码在移动时破坏提供了新的方法。

【讨论】:

  • 因此,如果我正确解释了手册页,那么 --as-needed 会导致链接失败,因为它在使用 libm 符号的库 testlib.a 之前看到了库 libm,因此确实不发出 DT_NEEDED 标签,该标签的缺失使得不需要该库(libm)。然后它读取 testlib.a 并找到符号 'sqrt' 并且不在 libm 中查找(因为它没有标记为 DT_NEEDED)并确定没有什么可以解决它,因此链接失败。
  • 两个平台都对;他们只是不同。确实,如果您以正确的顺序列出库(以便较早的库在以后的库中调用函数,反之亦然,并避免库之间的引用循环),那么如果您先列出目标文件,然后再列出图书馆按顺序。目标文件和库的任何其他排序都容易在某些平台上失败。你对 DT_NEEDED 的分析听起来是正确的——它确实描述了你所看到的和我在它不起作用时所经历的。
【解决方案2】:

对库进行分组会导致重复搜索组中的每个库,直到没有更多引用被解析。

没有分组,每个库仅按从左到右的顺序搜索一次。

(可以在命令行中重复库名来帮助解决此类问题)

来自 gcc 链接器的手册页:http://linux.die.net/man/1/ld

--start-group 存档--end-group

档案应该是档案文件的列表。它们可以是显式文件名,也可以是 -l 选项。

重复搜索指定的档案,直到没有新的未定义引用被创建。通常,存档仅按照命令行中指定的顺序搜索一次。如果需要该存档中的符号来解析稍后出现在命令行上的存档中的对象所引用的未定义符号,则链接器将无法解析该引用。通过对档案进行分组,它们都被反复搜索,直到解决所有可能的参考。

使用此选项会产生巨大的性能成本。最好仅在两个或多个档案之间存在不可避免的循环引用时才使用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 2013-05-07
    • 2019-08-08
    • 1970-01-01
    相关资源
    最近更新 更多