boottrpping如何为gcc工作?

时间:2011-06-20 22:13:51

标签: gcc architecture compiler-construction boot bootstrapping

我正在查找pypy项目(Python中的Python),并开始思考运行python外层的问题?当然,我猜想,它不能像古老的说法那样“乌龟一路走下去”!毕竟,python是无效的x86程序集!

很快我就想起了bootstrapping的概念,并查找了编译器引导。 “好的”,我想,“所以它可以用不同的语言编写,也可以用汇编编写”。为了性能,我确信C编译器只是从汇编中构建的。

这一切都很好,但问题仍然存在,计算机如何获得该汇编文件?!

说我买了一个没有任何东西的新cpu。在第一次操作期间,我希望安装一个运行C的操作系统。什么运行C编译器? BIOS中是否有微型C编译器?

有人可以向我解释一下吗?

3 个答案:

答案 0 :(得分:12)

  

说我买了一个没有任何东西的新cpu。在第一次操作期间,我希望安装一个运行C的操作系统。什么运行C编译器? BIOS中是否有微型C编译器?

我明白你在问什么...如果我们没有C编译器并且不得不从头开始会怎么样?

答案是你必须从装配或硬件开始。也就是说,您可以在软件或硬件中构建编译器。如果全世界都没有编译器,那么现在你可以在组装中更快地完成它;然而,在当天我认为编译器实际上是专用硬件。 wikipedia article有点短暂,并没有支持我,但没关系。

我想下一个问题是今天发生了什么?那些编译器编写者多年来一直忙于编写可移植C,因此编译器应该能够自己编译。值得讨论的是汇编是什么。基本上,您接受一组语句并从中生成程序集。而已。好吧,它实际上比那更复杂 - 你可以用词法分析器和解析器做各种各样的事情,我只理解它的一小部分,但实际上,你正在寻找将C映射到汇编。

在正常操作下,编译器会生成与您的平台匹配的汇编代码,但不一定如此。它可以为您喜欢的任何平台生成汇编代码,前提是它知道如何操作。因此,在您的平台上使C工作的第一步是在现有编译器中创建目标,开始添加指令并使基本代码正常工作。

一旦完成,理论上,您现在可以交叉编译从一个平台到另一个平台。接下来的阶段是:为该平台构建内核,引导加载程序和一些基本的用户态实用程序。

然后,您可以开始编译该平台的编译器(一旦您拥有了工作用户空间以及运行构建过程所需的一切)。如果成功,您将拥有基本实用程序,工作内核,用户空间和编译器系统。你现在就好了。

请注意,在移植编译器的过程中,您可能还需要为该平台编写汇编程序和链接程序。为了简化描述,我省略了它们。

如果感兴趣,Linux from Scratch是一个有趣的读物。它没有告诉你如何从头开始创建一个新目标(这是非常重要的) - 它假设你要为现有的已知目标构建,但它确实向你展示了如何交叉编译基本要素并开始构建系统。

Python实际上并没有组装到程序集。首先,正在运行的python程序会跟踪对象的引用计数,这是cpu不会为你做的事情。但是,基于指令的代码的概念也是Python的核心。玩一玩:

>>> def hello(x, y, z, q):
...     print "Hello, world"
...     q()
...     return x+y+z
... 
>>> import dis
dis.dis(hello)


  2           0 LOAD_CONST               1 ('Hello, world')
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  3           5 LOAD_FAST                3 (q)
              8 CALL_FUNCTION            0
             11 POP_TOP             

  4          12 LOAD_FAST                0 (x)
             15 LOAD_FAST                1 (y)
             18 BINARY_ADD          
             19 LOAD_FAST                2 (z)
             22 BINARY_ADD          
             23 RETURN_VALUE

在那里你可以看到Python如何看待你输入的代码。这是python字节码,即python的汇编语言。如果你喜欢实现这种语言,它实际上有自己的“指令集”。这是虚拟机的概念。

Java有着完全相同的想法。我拿了一个类函数并运行javap -c class来得到这个:

invalid.site.ningefingers.main:();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iconst_0
   3:   istore_1
   4:   iload_1
   5:   aload_0
   6:   arraylength
   7:   if_icmpge   57
   10:  getstatic   #2; 
   13:  new #3; 
   16:  dup
   17:  invokespecial   #4; 
   20:  ldc #5; 
   22:  invokevirtual   #6; 
   25:  iload_1
   26:  invokevirtual   #7; 
   //.......
}

我认为你明白了。这些是python和java世界的汇编语言,即python解释器和java编译器分别如何思考。

值得一读的其他内容是JonesForth。这既是一个有效的解释器和一个教程,我不能推荐它足以考虑“如何执行”以及如何编写一个简单,轻量级的语言。

答案 1 :(得分:6)

  

为了提高性能,我确信C编译器只是从汇编中构建的。

现在,C编译器(几乎?)完全用C语言编写(或者更高级语言 - 例如Clang是C ++)。编译器从包含手写汇编代码中获得的收益甚微。花费大部分时间的事情和它们一样慢,因为它们解决了非常困难的问题,其中“硬”意味着“大计算复杂性” - 在汇编中重写最多会带来不断的加速,但那些在那里并不重要水平。

此外,大多数编译器都希望具有高可移植性,因此前端和中端的体系结构特定技巧是不可能的(在后端,它们也不可取,因为它们可能会破坏交叉编译)。

  

说我买了一个没有任何东西的新cpu。在第一次操作期间,我希望安装一个运行C的操作系统。什么运行C编译器? BIOS中是否有微型C编译器?

当您安装操作系统时,(通常)没有C编译器运行。安装CD中充满了针对该架构的易于编译的二进制文件。如果包含一个C编译器(就像许多Linux发行版的情况一样),那也是一个已经编译好的可编译器。那些让你构建自己的内核等的发行版也至少包含一个可执行文件 - 编译器。当然,除非你必须使用C编译器在任何现有的安装上编译自己的内核。

如果“新CPU”是指一种新的架构,它不能向后兼容任何尚未支持的架构,那么自托管编译器可以遵循通常的移植过程:首先为该新目标编写后端,然后编译自己对于它,突然间你在新平台上有一个成熟的编译器和一个经过实战编译的(编译完整的编译器)本机后端。

答案 2 :(得分:3)

如果您购买带有预安装操作系统的新机器,它甚至不需要在任何地方都包含编译器,因为所有可执行代码都是在其他机器上编译的,无论谁提供操作系统 - 您的机器都没有不需要自己编译任何东西。

如果你有一个全新的CPU架构,你怎么做到这一点?在这种情况下,您可能首先为新的CPU体系结构编写新的代码生成后端(“目标”),以便在其他平台(“主机”)上运行的现有C编译器 - {{3 }}

一旦您的交叉编译器(在主机上运行)运行良好,可以生成将在目标上运行的正确编译器(以及必要的库等),那么您可以在目标平台上自行编译编译器,最终得到一个目标本机编译器,它在目标上运行并生成在目标上运行的代码。

这与新语言的原理相同:你必须用现有语言编写代码,你有一个工具链,它会将你的新语言编译成你可以使用的东西(我们称之为“引导编译器”) “)。一旦你能够很好地工作,就可以用新语言编写一个编译器(“真正的编译器”),然后使用bootstrap编译器编译真正的编译器。此时,您正在用新语言编写新语言的编译器,并且您的语言被称为“自托管”。