【问题标题】:GCC differently treats an object and a static library regarding undefined symbolsGCC 对未定义符号的对象和静态库进行不同的处理
【发布时间】:2018-11-21 14:29:17
【问题描述】:

最近我发现 Linux 链接器不会由于静态库中的未定义符号而失败,但是如果我直接与目标文件链接,则会由于相同的未定义符号而失败。这是一个简单的例子:

源代码:

$ cat main.c
int main() { return 0; } 
$ cat src.c
int outerUnusedFunc() {
    return innerUndefinedFunc();
}
int innerUndefinedFunc();

从中创建 *.o 和 *.a,使用“nm”进行比较:

$ gcc -c -o main.o main.c
$ gcc -c -o src.o src.c
$ ar r src.a src.o
ar: creating src.a
$ nm src.o
                 U innerUndefinedFunc
0000000000000000 T outerUnusedFunc
$ nm src.a

src.o:
                 U innerUndefinedFunc
0000000000000000 T outerUnusedFunc

(这里我们清楚地看到 *.o 和 *.a 都包含等号列表)

现在……

$ ld -o exe main.o src.o
src.o: In function `outerUnusedFunc':
src.c:(.text+0xa): undefined reference to `innerUndefinedFunc'
$ echo $?
1
$ ld -o exe main.o src.a
$ echo $?
0

GCC 区别对待的原因是什么?

【问题讨论】:

    标签: gcc linker static-libraries undefined-symbol


    【解决方案1】:

    如果您阅读static-libraries tag wiki 它将解释为什么没有来自src.a 的目标文件链接到您的程序中,因此 为什么它们中引用了哪些未定义的符号并不重要。

    作为链接器输入的目标文件foo.o 和静态库libfoo.a 之间的区别是 一个目标文件总是无条件地链接到你的程序中,而同一个目标文件 在静态库库中,libfoo.a(foo.o)libfoo.a 中提取并链接到 程序只有在链接器需要它来进行链接时,如标签 wiki 所解释的那样。

    当然,链接器只会对链接到程序中的目标文件中的未定义引用给出错误

    您观察到的行为是 链接器 的行为,无论您是否通过 GCC 调用它 前端。

    给链接器foo.o 告诉它:我想要这个在程序中。提供链接器 libfoo.a 告诉它:这里有一些你可能需要也可能不需要的目标文件

    【讨论】:

    • 感谢您的链接。它向我解释了我不明白的地方。我尝试使用“ld -r”从许多 *.o 创建一个原子 *.o,并使用这个原子 *.o 而不是 *.a。我不明白为什么用原子 *.o 链接 exe 失败(由于未使用的符号,但报告为“未定义”)并通过 *.a 传递。根据那篇文章,链接器不会分析 *.o 并从中获取所有符号,无论它们是否调用,而 *.a 它的处理方式不同,即提取、检查 *.a 中的每个 *.o 并包含如果调用了 *.o 的至少一个符号,则从 *.o 到 exe 的所有符号。
    【解决方案2】:

    在第二种情况下——使用静态库——命令行显示“从 main.o 构建 exe 并从 src.a 添加所有必需内容”。 ld 只是忽略该库,因为 main.o 不需要外部符号(outerUnusedFunc 未从 main.o 引用)。

    但在第一种情况下,命令行显示“从 main.o 和 src.o 构建 exe”。 ld 应该将 src.o 内容放入输出文件中。 因此,它有义务分析src.o 模块,将outerUnusedFunc 添加到输出文件中并解析outerUnusedFunc 的所有符号,尽管它未使用。

    您可以为代码段启用垃圾回收

    gcc --function-sections -Wl,--gc-sections -o exe main.c src.c
    

    在这种情况下outerUnusedFunc(以及所有其他函数)将被放置 在单独的部分。 ld 将看到该部分未使用(未引用符号)。它将从输出文件中删除所有部分,以便不会引用 innerUndefinedFunc 并且不应解析符号 - 与库案例的结果相同。

    另一方面,您可以手动将outerUnusedFunc 引用为“未定义”,以便ld 应该在库中找到它并添加到输出文件中。

    ld -o exe main.o -u outerUnusedFunc src.a
    

    在这种情况下,将产生相同的错误(对 innerUndefinedFunc 的未定义引用)。

    【讨论】:

    • 这正是我的问题,为什么 GCC 对 *.o 有更严格的处理。你知道这种行为背后的逻辑是什么吗?
    猜你喜欢
    • 2016-02-24
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    • 2014-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多