W jaki sposób ładowane są GCC i g ++?

185

Martwi mnie to od dłuższego czasu. Jak się kompilują GCC i g ++?

Zgaduję, że każda wersja jest kompilowana z poprzednio zbudowaną wersją. Czy to prawda? A jeśli tak, to czy oznacza to, że najstarsze wersje g ++ i GCC zostały napisane w asemblerze?

użytkownik1010005
źródło
13
Każda wersja może być ostatecznie skompilowana samodzielnie. :)
Martin Hennings
4
Jest to interesujące do przeczytania, jeśli chcesz zobaczyć, jak powstały pierwsze kompilatory.
parkovski
1
@parkovski Czy link nie działa?
Nubcake
Link ostatnio widziany 04 czerwca 2016 r .: web.archive.org/web/20160604035203/homepage.ntlworld.com/…
akraf

Odpowiedzi:

175

Najstarsza wersja GCC została skompilowana przy użyciu innego kompilatora C, ponieważ były inne, kiedy był napisany. Pierwszy kompilator C w historii (ok. 1973 r., IIRC) został zaimplementowany albo w zestawie PDP-11 , albo w języku programowania B, który go poprzedzał, ale w każdym razie kompilator B został napisany w zestawie.Podobnie, pierwszy w historii kompilator C ++ (CPre / Cfront , 1979-1983) został prawdopodobnie najpierw zaimplementowany w C, a następnie przepisany w C ++.

Podczas kompilacji GCC lub innego kompilatora samonośnego pełna kolejność budowania jest następująca:

  1. Zbuduj nową wersję GCC z istniejącym kompilatorem C.
  2. przebuduj nową wersję GCC z właśnie utworzoną
  3. (opcjonalnie) powtórz krok 2 w celu weryfikacji.

Ten proces nazywa się ładowaniem początkowym . Testuje zdolność kompilatora do samodzielnej kompilacji i upewnia się, że wynikowy kompilator jest zbudowany ze wszystkimi optymalizacjami, które sam implementuje.

EDYCJA : Drew Dormann w komentarzach wskazuje na konto Bjarne Stroustrup dotyczące najwcześniejszej implementacji C ++ . Został zaimplementowany w C ++, ale przetłumaczony przez to, co Stroustrup nazywa „preprocesorem” z C ++ do C; według definicji nie jest to pełny kompilator, ale C ++ został załadowany w C.

Fred Foo
źródło
19
3-etapowa wersja procesu kompilacji bootstrap jest rzeczywiście do weryfikacji: sam kompilator jest używany jako własny przypadek testowy. GCC skompilowany z [other] powinien dawać takie same wyniki (identyczne pliki binarne, pomijanie makr jak __DATE__i __TIME__które różnią się nawet między wywołaniami tego samego kompilatora) jak GCC skompilowany z [GCC skompilowanym z [other]] - jeśli nie, to jest błąd i 3-etapowa kompilacja bootstrap została zaprojektowana tak, aby to złapać.
pmdj
19
@pmjordan: „jeśli nie, to błąd” lub, co mniej prawdopodobne, przebiegły backdoor w trakcie wprowadzania („Refleksje na temat zaufania Trust”).
Steve Jessop
12
@sleske: to nieprawda. Wyjście binarne z kroku 2 musi być identyczne z wyjściem binarnym z kroku 3, w przeciwnym razie gdzieś wystąpi błąd. Powód jest taki, jak mówi pmjordan: NewCompiler1 i NewCompiler2 to programy z identycznym źródłem (tym z NewCompiler). Otrzymują identyczne dane wejściowe (źródło dla NewCompiler). Dlatego będą generować identyczne dane wyjściowe bez względu na kompilator, z którym sami zostały skompilowane (w tym przypadku NewCompiler1 został skompilowany z OldCompiler, a NewCompiler2 został skompilowany z NewCompiler1). Oznacza to, że NewCompiler2 i NewCompiler3 są binarnie identyczne.
Steve Jessop
12
Zastanawiałem się kiedyś: co jeśli stracimy wszystkie pliki binarne kompilatora C? I musiałem bootstrap od zera? Oto jak bym to zrobił: istnieje Kompilator Tiny C (który faktycznie może skompilować jądro Linuksa, więc jest kompletnie funkcjonalny). Wszystkie pliki źródłowe C tworzą zaledwie 30 000 wierszy kodu, w tym komentarze. Chociaż nawet był to dość trudny wysiłek, ktoś, kto rozumie C, mógł nauczyć się ze źródeł, jak generować wyjście binarne i „kompilować” źródła TCC z ręki (tak naprawdę myślę o kartach dziurkowanych). Następnie ponownie skompiluj TCC i użyj go do uruchomienia GCC lub podobnego.
datenwolf
11
@datenwolf: coś takiego, tak. Jeśli możemy założyć, że utraciliśmy wszystkie pliki binarne kompilatora C, ale nadal mamy asembler, możemy napisać program asemblerowy TinyTinyC. Byłby to mniej kompletny kompilator C niż TinyC: nie potrzebujemy go do kompilacji GCC ani jądra linuksa, potrzebujemy go tylko do kompilacji TinyC. Następnie uruchom go na źródle TinyC, który daje nam kompilator C zdolny do kompilacji Linuksa (i mam nadzieję, że glibc i GCC) i jesteśmy w biznesie. Jeśli nawet nie mamy asemblera, najpierw chcielibyśmy uruchomić jeden z nich, jest to łatwiejsze niż kompilator C.
Steve Jessop