对于未定义的符号,GCC以不同的方式对待对象和静态库
最近我发现Linux链接器不会因为静态库中未定义的符号而失败,但是如果我直接与te对象文件链接,会因为相同的未定义符号而失败。下面是一个简单的例子: 源代码:对于未定义的符号,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”进行比较: (这里我们清楚地看
$ 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失败(由于未使用符号,但报告为“未定义”),并以