1

我正在研究编译器引导,并研究了 Golang 如何从源代码实现引导,即通过构建用 C 实现的最后一个版本的 Golang 并使用生成的可执行文件来编译更新的 Go 版本。这让我很好奇如何用 C 完成同样的事情。你能在计算机上构建一个 C 编译器,而实际上它上面什么都没有吗?如果不是,那么我怎么能相信我使用的编译器的二进制文件不会自动填充它用间谍软件编译的二进制文件?

相关问题,既然第一个 C 编译器是用 B 编写的,而 B 是用 BCPL 编写的,那么 BCPL 是用什么编写的?

4

3 回答 3

3

你能在计算机上构建一个几乎没有任何内容的 C 编译器吗?

主要问题是(在 2021 年)你将如何为那台计算机编写程序!你将如何输入它?

在 1970 年代,计算机(如IBM 360大型机)有许多机械开关来输入一些初始程序。在 1960 年代,他们拥有更多,例如IBM1620

今天,您将如何输入该初始程序?你考虑过使用一些Arduino吗?即使是今天的示波器也包含带有程序的微处理器......

今天的一些爱好者已经设计(并花了很多钱) - 几年前 - 制造带有机械继电器的计算机。这些可能比您可以购买的最便宜的笔记本电脑(或计算机鼠标内的微控制器 - 您的鼠标也包含一些软件)慢数千倍。

您还可以购买许多分立晶体管(例如数千个 2N2222)并通过焊接它们来制造计算机。

即使是便宜的主板(例如MSI A320M A-PRO)今天也有一些称为UEFIBIOS的固件程序。它是随那个程序一起提供的……据传它主要是用 C 语言编写的(几十万条语句)。

在某些方面,计算机芯片是用VHDLSystemC等编码的“软件”……等等……

但是,原则上您仍然可以在 2021 年引导 C 编译器。

这是一个假设的故事......

想象一下,您今天有一台笔记本电脑,在某个孤岛上(如Robinson Crusoe)运行小型 Linux 发行版,没有任何 Internet 连接 - 但有书籍(包括Modern C和一些关于 x86-64 汇编和指令集架构的书籍以及许多其他书籍以纸张形式)、铅笔、纸张、食物和大量时间。想象一下,系统没有任何 C 编译器(例如,因为您刚刚gcc从某个Debian发行版中错误地删除了该软件包),而只有GNU binutils(即链接器ld和汇编器gas),一些二进制形式的编辑器(例如GNU emacsvim ), GNU 重击GNU make作为二进制包。我们假设您有足够的动力花费数月时间编写 C 编译器。我们还假设您可以访问某些纸质形式的手册页(尤其是elf(5)ld(1) ...)。我们必须假设您可以使用od(1)less(1)检查二进制形式的文件。

然后,您可以在纸上以EBNF 表示法设计 C 语言的子集 µC 。经过几个月的努力,您可以编写一个小型汇编程序,直接执行syscalls(2)(请参阅Linux Assembly HowTo)并解释该 µC 语言(因为编写解释器比编写编译器更容易;例如阅读Dragon book和Queinnec 的Lisp In Small Pieces和 Scott 的编程语言语用学书)。

一旦有了微型 µC 解释器,您就可以用 µC 编写一个简单的 µC 编译器(因为 Fabrice Bellard 已经能够编写他的tinyC编译器)。

一旦你调试了那个 µC 编译器,你就可以扩展它以接受 C 的所有语法和语义。

一旦你有一个完整的 C 编译器,你可以改进它以更好地优化,也许扩展它以接受 C++ 的一个小子集,你还可以编写一个受Frama-C启发的静态 C 代码分析器。

PS。Bootstrapping 可以概括很多——请参阅 Pitrat 关于引导人工智能的博客(Jacques Pitrat,1934 年出生,2019 年 10 月去世)和RefPerSys项目。

于 2021-01-16T15:55:27.787 回答
2

正如一些程序员花花公子在评论中所说,由于 C 是一种可移植的编程语言,因此您可以使用不同平台的编译器来生成交叉编译器,该平台上的交叉编译器将为目标平台生成可执行文件。

然后,您在该主机平台上为目标平台编译相同的 C 编译器,以便结果是目标平台的可执行文件。

然后你将该编译器二进制文件复制到目标机器上,然后它是自托管的。

自然地,在早期历史的某个时刻,有人真的不得不在某处用汇编程序或机器代码编写一些东西。今天,它不再是必需品,而是一种“生活选择”。


至于“我怎么能相信我使用的编译器的二进制文件不会自动填充它用间谍软件编译的二进制文件?” 问题已解决- 您可以使用两个独立的编译器从相同的源库和目标编译交叉编译器,并且这两个交叉编译器都应该为目标可执行文件生成按位相同的结果。然后你会知道结果要么没有间谍软件,要么你一开始使用的两个独立编译器会用完全相同的间谍软件感染生成的可执行文件——这是极不可能的。

于 2021-01-16T16:04:07.227 回答
1

您可以用汇编或机器代码编写一个非常微弱的 C 编译器,然后从那里引导。

在编程语言存在之前,您只是编写机器代码。这就是它的完成方式。

后来出现了assembler,它类似于“简单模式”的机器代码,并从那里演变出 Fortran 和 BCPL 等高级语言。通过使用适当的编译器进行翻译,这些与机器架构分离。

今天你可能会用 C 写一些东西,然后从那里开始,任何编译的东西都是合适的,尽管“编译”是一个松散的定义,因为 LLVM 存在,你可以直接敲出 LLVM IR 代码而不是实际的机器代码。例如,Rust 从 OCaml 开始,现在在 LLVM 之上“自托管”。

于 2021-01-16T15:50:39.690 回答