【问题标题】:What does linking in the compilation process actually do?编译过程中的链接实际上是做什么的?
【发布时间】:2016-06-13 11:00:42
【问题描述】:

据我了解,GCC 编译器在我编译 C 程序时执行四个步骤。

  1. 预处理 - 带宏的 C 代码 (*.c) 到不带宏的 C 代码 (*.c)
  2. 编译 - C 代码 (*.c) 到汇编语言 (*.s)
  3. 汇编 - 汇编语言 (*.s) 到目标代码 (*.o)
  4. 链接 - 对象代码 (*.o) 到可执行文件 (*)

前三个步骤对我来说非常有意义,但我仍然对链接的实际作用感到困惑。

在第三步之后为什么我不能运行 *.o 文件?那时我的 C 代码现在是对象/机器/字节代码,可以由 CPU 直接解释。然而,当我使我的 *.o 文件可执行并尝试运行它时,我得到了这个错误:

bash: ./helloworld.o: cannot execute binary file: Exec format error

为什么会出现此错误?如果我有一个只有一个 C 文件的小型 C 程序(例如一个 hello world 程序),在我看来,链接没有任何意义,因为没有什么可链接的。那么编译过程中的链接到底是做什么的呢?

提前感谢您的任何回复。

【问题讨论】:

  • 你在helloworld程序中使用的printf()怎么样?这是哪里来的?
  • @SouravGhosh 非常正确,我没有想到这一点。因此,链接不仅将我的目标文件链接在一起,还链接其他目标文件(例如标准库)。谢谢。

标签: c gcc compilation linker


【解决方案1】:

如果我有一个很小的 ​​C 程序(例如一个 hello world 程序)

甚至你的 helloworld 程序也使用#inlude<stdio.h>,不是吗?这意味着您正在使用库,并且链接步骤用于组合必要的目标代码(此处为库代码)为您创建二进制文件。


有关链接步骤的详细说明(并与编译进行比较) - 请参阅此question

【讨论】:

  • 这不能回答问题。 OP 可以创建完全空的main 程序,但他仍然无法运行目标文件。
  • 即便如此,我们还需要链接许多其他内容才能从加载程序入口点(通常是 _start)到您的主函数。
  • @doron 谢谢,你能给我一些其他的例子吗?
【解决方案2】:

粗略的解释是:

  • 从每个目标文件中找到所有匹配的段,并将它们连接在一起。这样我们就可以得到一个大的 .code、一个 .data、一个 .bss 等等。
  • 解析所有使用的符号。许多符号是本地的,因此可以立即解析。将在请求链接的库中搜索未解析的符号。完成后,结果将是一个符号表/链接映射。
  • 制作一个实际可执行的文件。在 Linux 上,通常只是碰巧可执行文件、库和目标文件都是 ELF 格式。并非所有平台都如此。

【讨论】:

  • 感谢您的回答,尽管我已将@artm 回答标记为已接受,您的回答也很有帮助。
【解决方案3】:

简单的答案是 .o 可执行文件有不同的用途和不同的格式。

如果您想要完整的答案,您需要阅读适用于您平台二进制格式的必要文档。

在 Linux 上,这将是 here。本文档将描述中间格式和最终可执行格式之间的区别。

顺便说一句,Linux 内核模块加载器确实直接使用 .o(或更确切地说是 .ko)文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-04
    • 2011-03-11
    • 2010-11-26
    • 2015-06-23
    • 2017-12-26
    • 2016-10-19
    • 1970-01-01
    相关资源
    最近更新 更多