gcc/g ++是如何引导的?

use*_*005 182 c c++ compiler-construction

这一直困扰着我.gcc/g ++如何编译自己?我猜测每个修订版都使用以前构建的修订版进行编译.这是真的?如果是的话,是否意味着最古老的g ++/gcc版本是用汇编语言编写的?

Fre*_*Foo 174

最古老的GCC版本是使用另一个C编译器编译的,因为编写它时还有其他版本.有史以来第一个C编译器(大约1973年,IIRC)是在PDP-11汇编中实现的,或者是在它之前的B编程语言中实现的,但无论如何B编译器都是用汇编语言编写的.同样,第一个C++编译器(CPre/Cfront,1979-1983)可能首先在C中实现,然后用C++重写.

当您编译GCC或任何其他自托管编译器时,构建的完整顺序是:

  1. 使用现有的C编译器构建新版本的GCC
  2. 使用您刚刚构建的GCC重新构建新版本的GCC
  3. (可选)重复步骤2以进行验证.

此过程称为bootstrapping.它测试编译器编译自身的能力,并确保生成的编译器是使用它自己实现的所有优化构建的.

编辑:Drew Dormann在评论中指出了Bjarne Stroustrup对最早实现C++的描述.它是用C++实现的,但是被Stroustrup称为从C++到C的"预处理器"的翻译; 他的定义并不是一个完整的编译器,但C++仍然是用C语言引导的.

  • bootstrap构建过程的3步版本确实用于验证:编译器本身用作自己的测试用例.用[other]编译的GCC应该产生相同的结果(相同的二进制文件,像___DATE__`和`__TIME__`这样的折扣,甚至在***编译器的调用之间变化)与用[其他]编译的GCC编译的GCC一样 - 如果没有,那就是一个bug,并且3阶段引导程序构建旨在捕获它. (19认同)
  • @pmjordan:"如果不是,这是一个错误",或者,不太可能,在引入过程中的一个狡猾的后门("信任信任的反思"). (19认同)
  • @sleske:那不是真的.步骤2的二进制输出必须与步骤3的二进制输出相同,否则会出现某个错误.原因是pmjordan说:NewCompiler1和NewCompiler2是具有相同源(NewCompiler)的程序.它们被赋予相同的输入(NewCompiler的源代码).因此,无论编译它们的编译器是什么,它们都会产生相同的输出(在这种情况下,NewCompiler1使用OldCompiler编译,NewCompiler2使用NewCompiler1编译).也就是说,NewCompiler2和NewCompiler3是二进制相同的. (12认同)
  • 我有没有想过:如果我们丢失所有C编译器二进制文件怎么办?并且必须从头开始引导?这就是我要做的事情:有Tiny C编译器(它实际上可以编译Linux内核,所以功能完备).所有它的C源文件只需要30k行代码,包括注释.虽然这是一个相当大的努力,但是理解C的人可以从源头学习,如何生成二进制输出并从手工"编译"TCC源(我实际上在想这里的穿孔卡).然后重新编译TCC并使用它来引导GCC或类似的. (12认同)
  • @datenwolf:是的,是的.如果我们可以假设我们已经丢失了所有C编译器二进制文件,但我们仍然有一个汇编程序,那么我们可能会编写一个汇编程序TinyTinyC.它将是一个功能较少的C编译器而不是TinyC:我们不需要它能够编译GCC或Linux内核,我们只需要它就能编译TinyC.然后在TinyC的源代码上运行它,这为我们提供了一个能够编译Linux的C编译器(希望是glibc和GCC),我们正在开展业务.如果我们甚至没有汇编程序,那么我们首先引导其中一个,它比C编译器更容易. (10认同)
  • @danielkza:不. (7认同)
  • Bootstrapping很酷.如何在没有平坦表面的情况下制作平坦表面?如何在没有机床的世界中获得机床?如何在没有工作芯片的世界中制造32nm芯片?制造没有车辆的车辆?所有这些都建立在前几代之上. (5认同)
  • 第 2 步不是通常重复几次吗?我相信 `clang` 可以做到。 (2认同)
  • @datenwolf:我可能不是唯一为它编写自己的VM和编译器的极客.这不是很难. (2认同)
  • @larsmans:正如你的维基百科链接(和Stroustrop)所描述的那样,**第一个C++编译器是用C++编写的,而不是C.当时,已经有一个工具可以将C++源代码转换为C源代码,这使得鸡/ egg场景可能. (2认同)

And*_*nas 10

如果要在现代环境(x86 Linux)中复制 GCC 的 bootstrap 过程,可以使用bootstrappable项目开发的工具:

  • 我们可以从hex0汇编程序开始(在 x86 上它是 357 字节的二进制文件),它大致完成以下两个命令的作用

    sed 's/[;#].*$//g' hex0_x86.hex0 | xxd -r -p > hex0
    chmod +x hex0
    
    Run Code Online (Sandbox Code Playgroud)

    即它将二进制程序的 ASCII 等价物转换为二进制代码,但它本身是用 hex0 编写的。

    基本上,hex0 具有与其二进制代码一一对应的等效源代码。

  • hex0可用于构建更强大的hex1汇编器,该汇编器支持更多功能(一个字符标签并计算偏移量)。hex1 是用 hex0 程序集编写的。

  • hex1可用于构建hex2(甚至支持多字符标签的更高级的汇编程序)。

  • hex2then 可用于构建宏汇编程序(其中程序使用宏而不是十六进制操作码)。

  • 然后您可以使用这个宏汇编器来构建cc_x86它是一个用汇编编写的“C 编译器”。cc_x86 仅支持 C 的一小部分,但这是一个令人印象深刻的开始。

  • 您可以使用cc_x86to build M2-Planet(Macro Platform Neutral Transpiler),这是一个用 C 编写的 C 编译器。M2-Planet 是自托管的,可以自行构建。

  • 然后您可以使用 M2-Planet 来构建GNU Mes,它是一个小型方案解释器。

  • mes 可用于运行 mescc,它是一个用 scheme 编写的 C 编译器,与 mes 位于同一存储库中。

  • mescc 可用于重建 mes 并构建 mes C 库。

  • 然后 mescc 可以用来构建一个稍微打了补丁的 Tiny C 编译器

  • 然后您可以使用它来构建更新版本的 TCC 0.9.27。

  • GCC 4.0.4 和 musl C 库可以用 TCC 0.9.27 构建。

  • 然后你可以使用旧的 GCC 构建新的 GCC。例如 GCC 4.0.4 -> GCC 4.7.4 -> 现代 GCC。

特尔;博士:

hex0 -> hex1 -> hex2 -> M0 -> M2-Planet -> Mes -> Mescc -> TCC -> GCC。