Betriebssysteme Teil 2: Übersetzung von C-Programmen Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 09.10.15 1 Übersicht • • • • • Übersetzungsprozess gcc(I) als Steuerprogramm Assembler Binder Hauptprogramme in C In dieser Einheit werden nur die Grundprinzipien dargestellt, dies ist keine Beschreibung konkreter Formate etc. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 2 Übersetzungsprozess von C I – Überblick Syntax Analyse (Parser) Compiler Ableitungsbaum Optimierung Compiler Ableitungsbaum Codegenerierung Compiler Sequenz Zwischensprache Sequenz Assembler Assemblieren Assembler Module als Object-Files Sequenz Tabellen Zusammensetzen Linker/Linkage Editor Object-File Sequenz Tabellen Laden ProzessImage Lader/Loader Daten im RAM Ausführung Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 3 Übersetzungsprozess von C II C-Programm Beispiel.c Preprozessor C-Programm mit expandierten Makros Übersetztes Programm ohne Bibliotheksroutinen Beispiel.i (Eigentlicher) Übersetzer Beispiel.o Binder Fertiges ausführbares Programm Include-Dateien Hauptprogramm Bibliotheken Beispiel Lader in Linux Programm in Ausführung Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 5 Kommando gcc(I) I gcc [-c] [-g] [-o ofile] [-E] [-M] [-S] file1 file2 ... fileN Die angegeben Dateien file1 bis fileN werden als C-Programme angesehen und entsprechend den Flags übersetzt. Flag -c bewirkt, dass vor dem Linken der Vorgang beendet wird, d.h. dass lediglich *.o-Dateien erzeugt werden. Flag -g bewirkt, dass Debugging-Informationen den Binärdateien hinzugefügt werden. Flag -o dient der Benennung der Ausgabedatei als ofile. Flag -E bewirkt einen Stopp nach der Preprozessor (Generieren von *.iDateien). Flag -M bewirkt die Ausgabe einer Regel zur Benutzung in make(1); es wird nicht übersetzt. Flag -S bewirkt die Erzeugung des Assemblers. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 6 Kommando gcc(I) II • gcc (GNU C) ist ein Programm, das alle Schritte der Übersetzung durch Starten von weiteren Programmen veranlasst und steuert. • Per Parameter kann der globale Ablauf nach jedem Schritt abgebrochen werden, z.B. – Parameter -E: nur bis *.i-Datei – Parameter -c: nur bis *.o-Datei • Anhand der Dateinamen-Endung erkennt gcc(I) wie weit der Übersetzungsvorgang vorangeschritten ist und veranlasst entsprechend die nächsten Schritte, so dass am Ende das fertig gebundene und ausführbare Programm vorhanden ist. Beispiel: "gcc a.o b.c c.o" führt für b.c alle, für a.o und c.o nur den letzten Schritt (Binden) durch. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 7 Ü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? Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 8 Ü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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 9 Ü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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 10 Ü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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 11 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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 12 Assembler II – Fiktives Beispiel Assembler Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung Objekt-Datei 13 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 Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 14 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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 15 Reales Beispiel I W F STATUS …… TRISC EQU EQU EQU 0 1 0x003 ; Bit für Working Register ; Bit für File Register ; Adresse Statusregister EQU 0x087 ; Adresse Tristatekontrolle PORT C ORG goto ORG goto ORG start bsf movlw movwf clrf bcf ; unendliche loop movf movwf goto END 0 ; start ; 4 ; start ; 5 ; STATUS,RPO ; Oxff ; TRISB ; TRISC ; STATUS,RPO ; Arbeitsschleife PORTB,w ; PORTC ; loop ; Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung Beginn Sprung zum Programmanfang Interrupt-Einsprung nicht besetzt Anfang des Programms RPO <- 1: Registerbank 1 1111 1111 nach W-Register W-Register nach Steuerung Eingang 0000 0000 Port C ist Ausgang RPO <- 0: Registerbank 0 speichere Eingabe nach W-Register speichere W-Register nach Ausgabe springe immer (Schleife) Aus [8-3] S.44 16 Reales Beispiel II – Pseudo-Instruktionen Instruktion Erläuterung EQU Equivalence Definition von Konstanten ORG Origin Fiktive Speicherzellen-Adresse, an der die folgenden Instruktionen platziert werden END Ende des Assembler-Programms bzw. Moduls Erläuterungen der (Pseudo-)Befehle von einem Assembler für PIC-Microcontroller. • Pseudo-Befehl = Anweisung an den Assembler zu dessen Steuerung • Pseudo-Befehle entsprechend keinen Anweisungen des Prozessors – sie sind Anweisungen an den Assembler. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 17 Aufbau von Befehlen Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 18 Arten von Operanden (Adressierungsarten) Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 19 Aufbau von Befehlen (fiktives Beispiel) Adressierungsart = Art und Weise der Bestimmung bzw. Adresse des Operanden Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 20 Beispiel: Objekt-Fileformat (a.out) I Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 21 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. • • • • Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 22 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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 23 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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 24 Binden I Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 25 Binden II - Binärcode (Objectcode) Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 26 Binden III - Bibliotheken Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 27 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: Header Index Datei 1 Datei 2 ... Datei N • Objektbibliothek = Bibliothek für Objektdateien • Binärprogramm = Aus vielen Objektdateien bzw. Bibliotheken zusammengesetztes ausführbares Programm Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 28 Beispiel: Bibliothek (ar-Format) Siehe http://sourceware.org/binutils/docs/binutils/ranlib.html Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 29 Binden IV Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 30 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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 31 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. Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 32 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() */ Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 33 Nach dieser Anstrengung etwas Entspannung.... Betriebssysteme – WS 2015/16 - Teil 2/Übersetzung 34