Compilerbau Teil 1: Einführung Compiler und der ganze Rest
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Compilerbau Teil 1: Einführung Compiler und der ganze Rest Comppilerbau – SS 2018 – Teil 1/Einführung 05.04.18 1
Literatur und Web [1-1] Wirth, Niklaus: Compilerbau. Teubner, 3. Auflage, 1984 [1-2] Maurer, Dieter: Übersetzerbau: Theorie, Konstruktion, Generierung. Springer, 1992 [1-3] Doberkat, Ernst-Erich: Praktischer Übersetzerbau. Springer 1990 [1-4] https://de.wikipedia.org/wiki/Backus-Naur-Form https://de.wikipedia.org/wiki/Erweiterte_Backus-Naur-Form https://de.wikipedia.org/wiki/Angereicherte_Backus-Naur-Form https://www.iso.org/standard/26153.html https://en.wikipedia.org/wiki/Wirth_syntax_notation [1-5] https://de.wikipedia.org/wiki/Syntax https://de.wikipedia.org/wiki/Syntaxdiagramm [1-6] https://de.wikipedia.org/wiki/Compiler Comppilerbau – SS 2018 – Teil 1/Einführung 2
Übersetzungsprozess von C I – Überblick Syntax Analyse (Parser) Compiler Ableitungsbaum Optimierung Compiler Ableitungsbaum Codegenerierung Compiler Sequenz Zwischensprache Sequenz Assembler Assemblieren Assembler Module als Sequenz Tabellen Object-Files Zusammensetzen Linker/Linkage Editor Object-File Sequenz Tabellen Laden Lader/Loader Prozess- Daten im RAM Image Ausführung Comppilerbau – SS 2018 – Teil 1/Einführung 3
Übersetzungsprozess von C II C-Programm Beispiel.c Preprozessor Include-Dateien C-Programm mit expandierten Makros Beispiel.i (Eigentlicher) Übersetzer Übersetztes Programm ohne Bibliotheksroutinen Beispiel.o Binder Hauptprogramm Bibliotheken Fertiges ausführbares Beispiel Programm Lader in Linux Programm in Ausführung Comppilerbau – SS 2018 – Teil 1/Einführung 4
Durchlauf (Pass) • Das Übersetzen erfolgt in mehreren Durchläufen (Pass), in denen das gesamte Programm vollständig gelesen und interpretiert wird. • Nach jedem Durchlauf wird das Programm in überarbeiteter Form neu in einer speziellen Datei angelegt; diese wird bei dem nächsten Durchlauf benutzt. • Die Steuerung der Durchläufe übernimmt gcc(I). • Bei Compilern sind 4 bis 5 Durchläufe üblich, es können auch erheblich mehr sein, z. B. PL/1 hatte bis zu 60 Durchläufe. Comppilerbau – SS 2018 – Teil 1/Einführung 5
Übersetzungsprozess von C III – Syntaxanalyse 1. Durchlauf: Makroexpansion Es werden die Makrodefinitionen (C hat die Möglichkeit von Makros, in Programmiersprachen ohne Makros wird dieser Schritt ausgelassen) vermerkt und alle Makro-Aufrufe mit den Makrokörpern samt Parametern ersetzt. 2. Durchlauf: Syntaktische Prüfung Entspricht der entstandene Text den Regeln der Sprache? Z.B. Hat jede öffnende Klammer (rund oder geschweift) eine korrespondierende schließende? Wird jedes Statement durch ein Semikolon abgeschlossen? 3. Durchlauf: Semantische Prüfung Sind alle Variablen und Funktionen deklariert? Werden sie übereinstimmend damit benutzt? Comppilerbau – SS 2018 – Teil 1/Einführung 6
Übersetzungsprozess von C IV 4. Durchlauf: Optimierung (optional) Können Deklarationen weggelassen werden, da die Variablen nicht benutzt werden? Lassen sich Schleifen verkürzen? 5. Durchlauf: Erzeugung von Assembler-Code Für jedes Statement wird der entsprechende Assemblercode generiert, so dass das generierte Programm das tut, was es laut Sprachdefinition tun sollte. 6. Durchlauf: Assemblieren Der Compiler ist jetzt fertig; es wird ein Assembler gestartet, der das generierte Assembler-Programm in eine Objektdatei übersetzt. Comppilerbau – SS 2018 – Teil 1/Einführung 7
Übersetzungsprozess von C V • Die generierte Objektdatei ist aus folgenden Gründen nicht ausführbar: – Es fehlen aufgerufene und nicht programmierte Routinen, z. B. System.out.println() in Java oder printf() in C. – Globale Variablen haben noch keine feste Position (Adresse), sie könnten an verschiedenen Stellen angelegt werden. • Der Binder (Linker, Linkage Editor) setzt das endgültige Programm unter Verwendung von Bibliotheken zusammen und positioniert die globalen Variablen. Erst dessen Ergebnis kann ausgeführt werden. Comppilerbau – SS 2018 – Teil 1/Einführung 8
Übersetzungsprozess von C V 7. Durchlauf: Binden Der Binder durchsucht Objektbibliotheken, um ein unvollständiges Programm mit den nicht selbst programmierten, aber aufgerufenen Subroutinen zu ergänzen. Am Ende ist eine direkt ausführbare Datei entstanden. Jetzt erst kann die Datei mit Maschinencode vom Betriebssystem in den RAM geladen und ausgeführt werden. Java wird etwas anders realisiert: es wird nicht bis zum Maschinencode übersetzt, sondern in eine Zwischensprache: Java-Byte-Code. Dieser wird in einem Interpreter (Virtuelle Maschine) ausgeführt. Bei Linux ist es meistens auch etwas anders. Comppilerbau – SS 2018 – Teil 1/Einführung 9
Assembler I • Assembler = Übersetzer für Programme in einer symbolischen Maschinensprache • Die Sprache Assembler ist für jeden CPU-Typ anders und spiegelt die Eigenarten der CPU-Architektur wieder. • Zur Assembler-Sprache gehören u.a.: – Befehle (Instruktionen) der CPU. – Makros als Zusammenfassungen mehrerer Befehle. – Anweisungen zur Reservierung von Speicherplatz. – Anweisungen zur Belegung von Speicherplatz. • Der Assembler übersetzt das Assembler-Programm in ein maschinen-codiertes Format, dem Objektformat. • Diese Dateien heißen daher Objektdateien. Comppilerbau – SS 2018 – Teil 1/Einführung 10
Assembler II – Fiktives Beispiel Assembler Objekt-Datei Comppilerbau – SS 2018 – Teil 1/Einführung 11
Assembler III • Assemblersprachen sind in der Regel spalten-orientiert, d.h. die Zeilen haben ein festes Format, das einzuhalten ist. • Ein wichtiges Charakteristikum eines Assemblers ist, dass die Assembler-Befehle fast immer 1:1 zu Maschinenbefehlen umgesetzt werden (Ausnahme: Verwendung von Makros). • Sprungmarken = Label = Namen für Speicherstellen (symbolische Adressen) von bestimmten Instruktionen, z. B. Beginn einer Subroutine • Das Programmieren in Assembler ist sehr mühselig, da: – es viel Zeit kostet, – viele Fehler gemacht werden können. Aber: In Assembler sind die effizientesten Programme realisierbar Comppilerbau – SS 2018 – Teil 1/Einführung 12
Compiler vs. Assembler • Höhere Programmiersprachen, wie z. B. C oder Java, werden durch Compiler in Maschinensprache übersetzt. • Compiler = Übersetzer für Programme in einer höheren Programmiersprache, die sich dadurch auszeichnet, dass ein Statement ("Befehl") dieser Sprache in mehrere Befehle in der Maschinensprache übersetzt werden muss. Comppilerbau – SS 2018 – Teil 1/Einführung 13
Aufbau von Befehlen Comppilerbau – SS 2018 – Teil 1/Einführung 14
Beispiel: Objekt-Fileformat (a.out) I Comppilerbau – SS 2018 – Teil 1/Einführung 15
Beispiel: Objekt-Fileformat (a.out) II • Header = Deskriptor für den Aufbau der Datei • Text = Tabelle mit dem übersetzten Code • Data = Tabelle mit den vorbelegten globalen Daten (static) • Relocation Information = Verschiebungsinformation = Tabelle mit der Beschreibung der Stellen im Code, die durch den Linker korrigiert werden müssen • Symbol-Tabelle = Tabelle mit den Deskriptoren von Symbolen, z.B. Namen von Variablen oder Routinen • String-Tabelle = Feld mit de Zeichenketten (Strings), die die Symbole ausmachen Da dieselben Symbole mehrfach vorkommen können, werden die Strings in der String-Tabelle und die Verweise darauf in der Symbol-Tabelle abgelegt. Comppilerbau – SS 2018 – Teil 1/Einführung 16
Verschiebungsinformation (Relocation Info) • Wenn ein Modul auf eine static-Variable zugreift, dann muss der Ort, also dessen Adresse zum Zeitpunkt des Bindens berechnet werden, denn der Ort hängt von der Position des betreffenden Moduls innerhalb der Binärdatei ab. • In der Tabelle Relocation Information stehen Deskriptoren, die festlegen, an welchen Stellen im Code die zu korrigierende Adressen stehen. • Wenn die CPU mit einer relativen Adressierungsart darauf zugreift, ist die Korrektur zwingend erforderlich. • Wenn die CPU relativ zu einem Register, z.B. PC, darauf zugreift, so steht im Code lediglich die Differenz der Adressen zwischen Register und dem Ort. Dann ist eine Verschiebung nicht nötig. • Dasselbe gilt analog für Zugriffe auf absolute Adressen. Comppilerbau – SS 2018 – Teil 1/Einführung 17
Bemerkungen • Die beiden vorgestellten Formate sind nur Beispiele. • Für Objekt-Dateien gibt es unter Linux folgende Formate: – Klassisches a.out-Format (siehe oben, veraltet) – COFF (Common Object File Format), veraltet Siehe z.B. http://de.wikipedia.org/wiki/COFF – ELF (Executable and Linking Format) Siehe z.B. http://de.wikipedia.org/wiki/ Executable_and_Linking_Format http://www.linux-kernel.de/appendix/ap05.pdf Heutige Systeme benutzen nur noch das ELF-Format. Comppilerbau – SS 2018 – Teil 1/Einführung 18
Binden I Comppilerbau – SS 2018 – Teil 1/Einführung 19
Binden II - Binärcode (Objectcode) Comppilerbau – SS 2018 – Teil 1/Einführung 20
Binden III - Bibliotheken Comppilerbau – SS 2018 – Teil 1/Einführung 21
Begriffe • Bibliothek = Archiv = Library = Datei mit mehreren benannten Informationsblöcken einschließlich eines Verzeichnisses; jeder Block kann eine eigenständige Datei aufnehmen, z. B. Zip-Archive • Beispielstruktur: Number Datei Datei ... Datei Magic Header Index 1 2 N • Objektbibliothek = Bibliothek für Objektdateien • Binärprogramm = Aus vielen Objektdateien bzw. Bibliotheken zusammengesetztes ausführbares Programm Comppilerbau – SS 2018 – Teil 1/Einführung 22
Beispiel: Bibliothek (ar-Format) Siehe http://sourceware.org/binutils/docs/binutils/ranlib.html Comppilerbau – SS 2018 – Teil 1/Einführung 23
Binden IV Comppilerbau – SS 2018 – Teil 1/Einführung 24
Binden V • Jede übersetzte, ungebundene Objekt-Datei wird Modul genannt. • Der Linker arbeitet pro Modul in folgenden Schritten: 1. Kopieren des aktuellen Moduls ans Ende der bisherigen 2. Feststellen, was dieses neue Modul an Symbole definiert 3. Diese aus der Tabelle der Unbekannten entfernen und in die Tabelle der Bekannten eintragen 4. Alle Verweise (Aufrufe etc.) im bisher geladenen Teil mit dem neuen Modul verbinden 5. Alle Verweise auf noch nicht geladene Routinen bzw. in Tabelle der Unbekannten bringen. 6. Ist die Tabelle der Unbekannten leer, so terminiert das Laden, ansonsten werden die Bibliotheken nach der Definition der Unbekannten durchsucht; wird ein Modul dazu gefunden, geht es mit Schritt 1 weiter, ansonsten wird eine Fehlermeldung produziert. Comppilerbau – SS 2018 – Teil 1/Einführung 25
Zeitpunkte des Bindens • Direkt während der Übersetzung des Programm Dadurch entsteht eine ausführbare Datei mit allen Teilen. • Erst zum Zeitpunkt des Ladens in den RAM Es entstehen kleine nicht-ausführbare Programme, die während des Startens mit aktuellen Versionen der fehlenden Teile verbunden werden. – Der Linker ist dann Teil des Laders. – Dies wird meist bei Linux gemacht. • Während der Laufzeit des Programms Der Binder läuft parallel zum Programm und verbindet nur die Routinen, die aufgerufen werden. Comppilerbau – SS 2018 – Teil 1/Einführung 26
Hauptprogramm • C kennt keine Hauptprogramme, alle Routinen sind gleichwertige Funktionen, die Werte liefern (und deshalb einen Aufrufer benötigen). • Es wird ein in Assembler geschriebenes Hauptprogramm dazu gebunden, das eine C-Routine Namens "main" aufruft, so dass main() wie ein Hauptprogramm erscheint. Pseudocode des Hauptprogramms Initialize Register Initialize Stack Push(Parameter) call _main(argc,arv,env) /* Haupt-Programm */ call _exit(0) /* für return in main() */ Comppilerbau – SS 2018 – Teil 1/Einführung 27
Nach dieser Anstrengung etwas Entspannung... Da liegt noch ein steiniger Weg vor uns... Comppilerbau – SS 2018 – Teil 1/Einführung 28
Sie können auch lesen