对于未定义的符号,GCC以不同的方式对待对象和静态库

对于未定义的符号,GCC以不同的方式对待对象和静态库,gcc,linker,static-libraries,undefined-symbol,Gcc,Linker,Static Libraries,Undefined Symbol,最近我发现Linux链接器不会因为静态库中未定义的符号而失败,但是如果我直接与te对象文件链接,会因为相同的未定义符号而失败。下面是一个简单的例子: 源代码: $ cat main.c int main() { return 0; } $ cat src.c int outerUnusedFunc() { return innerUndefinedFunc(); } int innerUndefinedFunc(); 从中创建*.o和*.a,使用“nm”进行比较: (这里我们清楚地看

最近我发现Linux链接器不会因为静态库中未定义的符号而失败,但是如果我直接与te对象文件链接,会因为相同的未定义符号而失败。下面是一个简单的例子:

源代码:

$ cat main.c
int main() { return 0; } 
$ cat src.c
int outerUnusedFunc() {
    return innerUndefinedFunc();
}
int innerUndefinedFunc();
从中创建*.o和*.a,使用“nm”进行比较:

(这里我们清楚地看到,*.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将其区别对待的原因是什么?

在第二种情况下-使用静态库-命令行显示“从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的未定义引用)。

在第二种情况下-使用静态库-命令行显示“从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的未定义引用)。

如果您阅读 它将解释为什么没有来自
src.a
的对象文件链接到您的程序中,因此 为什么它们引用了什么未定义的符号并不重要

作为链接器输入,对象文件
foo.o
和静态库
libfoo.a
之间的区别是 一个对象文件总是无条件地链接到程序中,而同一个对象文件 在静态库中,
libfoo.a(foo.o)
libfoo.a
中提取并链接到 正如TagWiki所解释的,只有当链接器需要它来进行链接时,程序才会启动

当然,链接器只会为链接到程序中的对象文件中未定义的引用提供错误

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

给链接器
foo.o
告诉它:我希望程序中有这个。给链接器
libfoo.a
告诉它:下面是一些您可能需要或不需要的对象文件。

如果您阅读 它将解释为什么没有来自
src.a
的对象文件链接到您的程序中,因此 为什么它们引用了什么未定义的符号并不重要

作为链接器输入,对象文件
foo.o
和静态库
libfoo.a
之间的区别是 一个对象文件总是无条件地链接到程序中,而同一个对象文件 在静态库中,
libfoo.a(foo.o)
libfoo.a
中提取并链接到 正如TagWiki所解释的,只有当链接器需要它来进行链接时,程序才会启动

当然,链接器只会为链接到程序中的对象文件中未定义的引用提供错误

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

给链接器
foo.o
告诉它:我希望程序中有这个。给链接器
libfoo.a
告诉它:以下是一些您可能需要或不需要的对象文件。

这正是我的问题,为什么GCC对*.o有更严格的处理。你知道这种行为背后的逻辑是什么吗?这正是我的问题,为什么GCC对*.o有更严格的处理。你知道这种行为背后的逻辑吗?谢谢你的链接。它向我解释了我不明白的事情。我尝试使用“ld-r”从许多*.o创建一个原子的*.o,并使用这个原子的*.o而不是*.a。我不明白为什么exe的链接会以原子*.o失败(由于未使用符号,但报告为“未定义”),并以