Technische Anmerkungen zur toolchain

Dieser Abschnitt soll einige technische Details zum gesamten Bau- und Installationsprozess erläutern. Es ist nicht zwingend erforderlich das sie alles hier sofort verstehen. Das meiste ergibt sich von selbst wenn sie erstmal die ersten Pakete installiert haben. Scheuen sie sich nicht, zwischendurch noch einmal in diesem Abschnitt nachzulesen.

Das Ziel von Kapitel 5 ist es, eine gut funktionierende temporäre Arbeitsumgebung zu erschaffen in die wir uns später abkapseln und von wo aus wir in Kapitel 6 ohne Schwierigkeiten ein sauberes endgültiges LFS-System erzeugen können. Wir werden uns so weit wie möglich vom Host-System abschotten und so eine in sich geschlossene toolchain erzeugen. Bitte beachten sie das der gesamte Prozess ausgelegt ist um jegliches Risiken für neue Leser zu minimieren und gleichzeitig den Lerneffekt zu maximieren. Kurz gesagt könnte man auch fortgeschrittenere Techniken einsetzen um das System zu erstellen.

Wichtig: Bevor sie fortfahren sollten sie den Namen der Plattform kennen auf der sie arbeiten, diese wird auch oft als Ziel-Triplet bezeichnet. Für die meisten wird das Ziel-Triplet zum Beispiel i686-pc-linux-gnu sein. Ein einfacher Weg sein Ziel-Triplet herauszufinden ist, das config.guess Skript auszuführen welches mit den Quellen vieler Pakete mitgeliefert wird. Entpacken sie die Binutils Quellen, führen sie das Skript aus: ./config.guess und notieren sie sich die Ausgabe.

Sie sollten auch den Namen des dynamischen Linkers für ihre Plattform kennen (manchmal auch als dynamischer Lader bezeichnet), nicht zu verwechseln mit dem standard Linker ld welcher Teil der Binutils ist. Der dynamische Linker kommt mit Glibc und seine Aufgabe ist es, von einem Programm benötigte gemeinsame Bibliotheken zu finden und zu laden, das Programm zum ausführen vorzubereiten und schliesslich das Programm selbst auszuführen. Im Regelfall wird der Name des dynamischen Linkers ld-linux.so.2 sein. Für weniger gängige Systeme könnte der Name auch ld.so.1 sein und auf neueren 64bit Plattformen könnte er sogar völlig verschieden sein. Sie müssten den Namen ihres dynamischen Linkers herausfinden können wenn sie auf ihrem Host-System in das Verzeichnis /lib schauen. Um wirklich sicher zu gehen können sie eine beliebige Binärdatei auf ihrem Host-System überprüfen: 'readelf -l <name of binary> | grep interpreter'. Notieren sie die Ausgabe. Eine Referenz die alle Plattformen abdeckt finden sie in der Datei shlib-versions im Hauptverzeichnis des Glibc Quellverzeichnisses.

Ein paar technische Hinweise zum Kompilierprozess in Kapitel 5:

Binutils wird als erstes installiert weil sowohl GCC als auch Glibc beim durchlaufen des configure Skriptes einige Tests zum Assembler und Linker durchführen und auf dem Ergebnis basierend bestimmte Features ein- bzw. ausschalten. Das ist wichtiger als man anfangs denken mag. Ein falsch konfigurierter GCC oder Glibc kann zu Fehlern in der toolchain führen die erst am Ende der Installation des LFS Systems bemerkt werden. Zum Glück weisen Fehlschläge beim durchlaufen der Test-suites im Regelfall auf solche Probleme hin bevor zuviel Zeit vergeudet wird.

Binutils installiert seinen Assembler an zwei Stellen, /tools/bin und /tools/$TARGET_TRIPLET/bin. In wirklichkeit sind die Programme an der einen Stelle mit denen an der anderen durch einen harten Link verknüpft. Ein wichtiger Aspekt des Linkers ist seine Suchreihenfolge für Bibliotheken. Genaue Informationen erhält man mit ld und dem Parameter --verbose. Zum Beispiel: 'ld --verbose | grep SEARCH' gibt die aktuellen Suchpfade und ihre Reihenfolge aus. Sie können sehen, welche Dateien tatsächlich von ld verlinkt werden, indem sie ein dummy Programm kompilieren und den Parameter --verbose angeben. Zum Beispiel: 'gcc dummy.c -Wl,--verbose 2 >&1 | grep succeeded' zeigt das alle Dateien beim Linken erfolgreich geöffnet werden konnten.

Das nächste zu installierende Paket ist GCC, und während dem Durchlauf von ./configure sehen sie zum Beispiel:

checking what assembler to use... /tools/i686-pc-linux-gnu/bin/as
checking what linker to use... /tools/i686-pc-linux-gnu/bin/ld

Das ist aus den oben genannten Gründen wichtig.Hier wird auch deutlich, das GCC's configure Skript nicht die $PATH Verzeichnisse durchsucht um herauszufinden welche Werkzeuge verwendet werden sollen. Dennoch werden beim tatsächlichen ausführen von gcc nicht unbedingt die gleichen Suchpfade verwendet. Welchen Standard Linker gcc wirklich verwendet, kann man mittels 'gcc -print-prog-name=ld' herausfinden. Dateillierte Informationen erhält man von gcc indem man den Parameter -v beim kompilieren eines Dummy Programmes übergibt. 'gcc -v dummy.c' zum Beispiel gibt Informationen über den Proprozessor, Komilierungs- und Assemblierungsphasen inklusive gcc's Suchpfaden und der Reihenfolge aus.

Das nächste zu installierende Paket ist Gilbc. Die wichtigsten Überlegungen zum kompilieren von Glibc muss man zu Compiler, Binärtools und den Kernel Headern machen. Der Compiler ist normalerweise kein Proglem weil Glibc immer den gcc nimmt der in den $PATH Verzeichnissen gefunden wird. Die Binärtools und Kernel Header können da schon etwas schwieriger sein. Daher gehen wir kein Risiko ein und benutzen die verfügbaren configure Optionen um die korrekten Entscheidungen zu erzwingen. Nach dem Durchlauf von ./configure können sie den Inhalt von config.make im glibc-build Verzeichnis nach den Details durchsuchen. Sie werden ein paar interessante Dinge finden, wie zum Beispiel CC="gcc -B/tools/bin/" zum kontrollieren der verwendeten Binärtools, oder die Parameter -nostdinc und -isystem zum kontrollieren des Suchpfades des Compilers. Diese Besonderheiten heben einen wichtigen Aspekt des Glibc Paketes hervor: Es ist kompiliertechnisch sehr eigenständig und nicht nicht von toolchain-Vorgaben abhängig.

Nach der Installation von Glibc nehmen wir noch ein paar Anpassungen vor, damit stellen wir sicher das Suchen und Verlinken nur innerhalb unseres /tools Prefix stattfindet. Wir installieren einen angepassten ld, welcher einen fest angegebenen Suchpfad auf /tools/lib hat. Dann bearbeiten wir gcc's Spec Dateie so, das sie auf den neuen dynamischen Linker in /tools/lib zeigt. Der letzte Schritt ist entscheidend für den gesamten Prozess. Wie oben bereits angemerkt, wird ein fest eingestellter Pfad zum dynamischen Linker in jede ausführbare ELF Datei eingebettet. Sie können das überprüfen indem sie dieses Kommando ausführen: 'readelf -l <Name der ausführbaren Datei> | grep interpreter'. Durch das anpassen von gcc's Specs Datei stellen wir sicher, das jedes von hier an kompilierte Programm bis zum Ende von Kapitel 5 unseren neuen dynamischen Linker in /tools/lib benutzt.

Die Notwendigkeit den neuen Linker zu benutzen ist auch der Grund dafür, das wir den Specs Patch für den zweiten GCC Durchlauf anwenden. Ein Fehler dabei würde dazu führen, das die GCC Programme selbst den Linker Namen des /lib Verzeichnisses des Host-Systems eingebettet hätten, und das würde unserem Ziel, uns vom Host-System zu trennen, entgegenwirken.

Während dem zweiten Durchlauf von Binutils können wir den configure Parameter --with-lib-path benutzen um den Bibliotheksuchpfad von ld zu kontrollieren. Von diesem Punkt an ist die toolchain unabhängig. Die Verbleibenden Pakete aus Kapitel 5 kompilieren alle mit der neuen Glibc in /tools und alles ist in Ordnung.

Aufgrund ihrer bereits erwähnten eigenständigen Natur ist die Glibc das erste wichtige Paket das wir nach dem eintreten in die chroot Umgebung in Kapitel 6 installieren. Wenn die Glibc erstmal nach /usr installiert ist werden wir schnell ein paar Vorgaben in der toolchain ändern und dann schreiten wir in Kapitel 6 mit dem erstellen des endgültigen LFS Systems fort.

Bemerkungen zum statischen linken

Fast alle Programme führen neben ihrer eigentlichen Aufgabe noch einige weniger übliche, manchmal sehr triviale Dinge aus. Das beinhaltet das reservieren von Speicher, durchsuchen von Verzeichnissen, lesen und schreiben von Dateien, bearbeiten von Zeichenketten, Mustersuche, Arithmetik und viele andere Dinge. Anstatt Programme zu zwingen das "Rad neu zu erfinden", stellt das GNU System all diese Basisfunktionen in fertigen Bibliotheken zur Verfügung. Die wichtigste dieser Bibliotheken auf jedem Linux System ist die Glibc.

Es gibt zwei elementare Wege wie man Funktionen einer Bibliothek in ein Programm einbinden kann: statisch oder dynamisch. Beim statischen linken eines Programmes wird der Code der genutzten Funktionen in die ausführbare Datei eingebettet; das resultiert in einem relativ umfangreichen und klobigen ausführbaren Programm. Beim dynamischen linken eines Programmes wird in der ausführbaren Datei nur eine Referenz auf den dynamischen Linker, den Namen der Bibliothek und den Namen der Funktion eingebettet; daraus ergibt sich eine wesentlich kleinere ausführbare Datei. (Ein dritter möglicher Weg ist die Programmierschnittstelle des dynamischen Linkers zu benutzen. Schauen sie für weitere Informationen bitte in die Manpage von dlopen.)

Dynamisches linken ist unter Linux der Standard und hat drei grosse Vorteile gegenüber dem statischen linken. Erstens braucht man nur eine Kopie der ausführbaren Bibliothek anstatt viele Kopien in allen möglichen ausführbaren Dateien eingebettet zu haben -- nebenbei spart das auch Speicherplatz auf der Festplatte. Zweitens: Wenn viele Programme die gleichen Bibliotheksfunktionen gleichzeitig nutzen, wird dennoch nur eine Kopie der Funktion geladen -- das spart Arbeitsspeicher. Drittens: Wenn in einer Bibliotheksfunktion ein Fehler behoben wird oder sie auch einfach nur verbessert/erweitert wird, müssen sie nur die eine Bibliothek neu kompilieren anstatt alle Programme neu zu kompilieren die die Bibliothek benutzen.

Wenn der dynamische Linker so viele Vorteile hat, warum linken wir die ersten beiden Pakete in diesem Kapitel dann statisch? Das hat drei Gründe: historische, den Lerneffekt und technische Hintergründe. Historische Gründe deshalb, weil in früheren LFS Versionen alle Pakete in diesem Kapitel statisch verlinkt wurden. Lerntechnische Gründe, weil es Sinn macht, den Unterschied zu kennen. Technische, weil wir durch diesen Schritt einen weiteren Punkt unabhängiger vom Host-System werden. Das bedeutet, diese Programme können unabhängig vom Host-System eingesetzt werden. Natürlich könnten wir auch dann noch ein gut funktionierendes LFS System erstellen wenn diese Pakete dynamisch gelinkt werden.