Как GCC и G ++ загружаются?

186

Это беспокоило меня некоторое время. Как GCC и G ++ компилируются сами?

Я предполагаю, что каждая ревизия компилируется с ранее созданной ревизией. Это правда? И если это так, значит ли это, что самые старые версии g ++ и GCC были написаны на ассемблере?

user1010005
источник
13
Каждая ревизия может быть окончательно скомпилирована. :)
Мартин Хеннингс
4
Это интересно прочитать, если вы хотите увидеть, как появились первые компиляторы.
parkovski
1
@parkovski Ссылка мертва?
Nubcake
Ссылка в последний раз была замечена 04 июня 2016 года: web.archive.org/web/20160604035203/homepage.ntlworld.com/…
akraf

Ответы:

175

Самая старая версия GCC была скомпилирована с использованием другого компилятора Си, поскольку были и другие, когда она была написана. Самый первый C-компилятор (приблизительно 1973, IIRC) был реализован либо в сборке PDP-11 , либо в языке программирования B, который предшествовал ему, но в любом случае B-компилятор был написан на ассемблере.Точно так же первый в мире компилятор C ++ (CPre / Cfront , 1979-1983), вероятно, был сначала реализован на C, а затем переписан на C ++.

Когда вы компилируете GCC или любой другой автономный компилятор, полный порядок сборки:

  1. Сборка новой версии GCC с существующим компилятором C
  2. пересоберите новую версию GCC с той, которую вы только что создали
  3. (необязательно) повторите шаг 2 в целях проверки.

Этот процесс называется начальной загрузкой . Он проверяет способность компилятора компилировать себя и проверяет, что полученный компилятор построен со всеми оптимизациями, которые он сам реализует.

РЕДАКТИРОВАТЬ : Дрю Дорманн, в комментариях, указывает на рассказ Бьярн Страуструп о самой ранней реализации C ++ . Он был реализован на C ++, но переведен тем, что Страуструп называет «препроцессором» с C ++ на C; не полный компилятор по его определению, но C ++ был загружен в C.

Фред Фу
источник
19
Трехэтапная версия процесса сборки начальной загрузки действительно для проверки: сам компилятор используется в качестве собственного тестового примера. GCC, скомпилированный с [other], должен давать те же результаты (идентичные двоичные файлы, похожие на макросы, которые отличаются друг от друга __DATE__и __TIME__различаются даже между вызовами одного и того же компилятора), как GCC, скомпилированный с [GCC, скомпилированный с [other]] - если нет, это ошибка Трехэтапная сборка начальной загрузки предназначена для этого.
pmdj
19
@pmjordan: «если нет, то это ошибка» или, что менее вероятно, коварный бэкдор в процессе внедрения («Размышления о доверии»).
Стив Джессоп
12
@Sleske: это не правда. Двоичный вывод шага 2 должен быть идентичен двоичному выводу шага 3, иначе где-то есть ошибка. Причина в том, что, как говорит pmjordan: NewCompiler1 и NewCompiler2 - это программы с одинаковым исходным кодом (из NewCompiler). Им дают одинаковый ввод (источник для NewCompiler). Следовательно, они будут выдавать идентичный вывод независимо от того, с каким компилятором они были скомпилированы (в этом случае NewCompiler1 был скомпилирован с OldCompiler, а NewCompiler2 скомпилирован с NewCompiler1). То есть NewCompiler2 и NewCompiler3 являются двоичными идентичными.
Стив Джессоп
12
Я когда-нибудь задавался вопросом: что, если мы потеряем все двоичные файлы компилятора C? И пришлось с самого начала загрузиться? Вот как я могу это сделать: есть компилятор Tiny C (который на самом деле может скомпилировать ядро ​​Linux, так что он вполне готов). Все исходные файлы на C составляют всего 30 тыс. Строк кода, включая комментарии. Хотя даже это было довольно трудоемким делом, кто-то, кто понимает C, мог бы узнать из источников, как генерировать двоичный вывод и «компилировать» источники TCC из рук (я на самом деле думаю о перфокартах здесь). Затем перекомпилируйте TCC с этим и используйте его для начальной загрузки GCC или подобного.
datenwolf
11
@datenwolf: что-то в этом роде, да. Если мы можем предположить, что мы потеряли все двоичные файлы компилятора C, но у нас все еще есть ассемблер, то мы могли бы написать программу на ассемблере TinyTinyC. Это будет менее полнофункциональный компилятор C, чем TinyC: он нам не нужен, чтобы иметь возможность компилировать GCC или ядро ​​Linux, нам нужен только он, чтобы иметь возможность компилировать TinyC. Затем запустите его на исходном коде TinyC, который дает нам компилятор C, способный компилировать Linux (и, надеюсь, glibc и GCC), и мы в деле. Если у нас даже нет ассемблера, тогда мы сначала загрузим один из них, это проще, чем компилятор Си.
Стив Джессоп