Technische Anmerkungen zur Toolchain

Dieser Abschnitt soll einige technische Details zum gesamten Kompilier- und Installationsprozess erläutern. Es ist nicht zwingend erforderlich, dass 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 Chapter 5 ist es, eine gut funktionierende temporäre Arbeitsumgebung zu erschaffen, in die wir uns später abkapseln und von wo aus wir in Chapter 6 ohne Schwierigkeiten ein sauberes endgültiges LFS-System erstellen 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, dass der gesamte Prozess so ausgelegt ist, jegliche Risiken für neue Leser zu minimieren und gleichzeitig den Lerneffekt zu maximieren. Kurz gesagt, man könnte auch fortgeschrittenere Techniken einsetzen, um das System zu erstellen.

[Wichtig]

Wichtig

Bevor Sie fortfahren, sollten Sie den Namen der Plattform kennen, auf der Sie arbeiten; diese wird auch oft als Ziel-Tripplet bezeichnet. Für die meisten wird das Ziel-Tripplet zum Beispiel i686-pc-linux-gnu sein. Ein einfacher Weg sein Ziel-Tripplet herauszufinden besteht darin, das Skript config.guess auszuführen, das 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, der Bestandteil 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 zur Ausführung 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 64-Bit-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 den Ordner /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 Basisordner des Glibc-Quellordners.

Hier ein paar technische Hinweise zum Kompilierprozess in Chapter 5:

Als erstes wird Binutils installiert, da sowohl GCC als auch Glibc beim Durchlaufen des ./configure-Skriptes einige Tests zum Assembler und Linker durchführen und auf dem Ergebnis basierend bestimmte Funktionen ein- bzw. ausschalten. Das ist wichtiger als man zunächst 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 Testsuites im Regelfall auf solche Probleme hin, bevor zuviel Zeit vergeudet wird.

Binutils installiert seinen Assembler an zwei Stellen, /tools/bin und /tools/$ZIEL_TRIPPLET/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 erhalten Sie 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, dass alle Dateien beim Linken erfolgreich geöffnet werden konnten.

Das nächste zu installierende Paket ist GCC, und während des Durchlaufs 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, dass 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 man wirklich verwendet, kann man mittels gcc -print-prog-name=ld herausfinden. Detaillierte 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 Präprozessor, Komilierungs- und Assemblierungsphasen inklusive gcc's Suchpfaden und der Reihenfolge aus.

Das nächste zu installierende Paket ist Glibc. Die wichtigsten Überlegungen zum Kompilieren von Glibc beschäftigen sich mit dem Compiler, den Binärwerkzeugen und den Kernel-Headern. Der Compiler ist normalerweise kein Problem, weil Glibc immer den gcc nimmt, der in den $PATH Ordnern gefunden wird. Die Binutils und die 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 Ordner nach den Details durchsuchen. Sie werden ein paar interessante Dinge finden, wie zum Beispiel CC="gcc -B/tools/bin/" zum Kontrollieren der verwendeten Binärwerkzeuge, oder die Parameter -nostdinc und -isystem zum Kontrollieren des Suchpfades des Compilers. Diese Besonderheiten heben einen wichtigen Aspekt des Paketes Glibc hervor: Es ist kompiliertechnisch gesehen 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, dass Suchen und Verlinken nur innerhalb unseres Prefix /tools stattfindet. Wir installieren einen angepassten ld, welcher einen fest angegebenen Suchpfad auf /tools/lib hat. Dann bearbeiten wir die spec-Datei von gcc so, dass sie auf den neuen Dynamischen Linker in /tools/lib verweist. Der letzte Schritt ist entscheidend für den gesamten Prozess. Wie oben bereits angemerkt, wird ein fest eingestellter Pfad zum Dynamischen Linker in jeder ausführbaren 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 der Specs-Datei von gccstellen wir sicher, dass jedes von hier an kompilierte Programm bis zum Ende des Kapitels unseren neuen Dynamischen Linker in /tools/lib benutzt.

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

Während des zweiten Durchlaufs der 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 Chapter 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 Chapter 6 installieren. Wenn die Glibc erstmal nach /usr installiert ist, werden wir schnell ein paar Voreinstellungen in der Toolchain ändern und dann schreiten wir mit dem Erstellen des endgültigen LFS-Systems fort.

Bemerkungen zum statischen Linken

Fast alle Programme führen neben ihrer eigentlichen Aufgabe noch einige sehr typische, manchmal völlig triviale Dinge aus. Das beinhaltet das Reservieren von Speicher, Durchsuchen von Ordnern, Lesen und Schreiben von Dateien, Verarbeitung 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 Kode der genutzten Funktionen in die ausführbare Datei eingebettet; das resultiert in einem relativ großen 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 besteht darin, 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 große Vorteile gegenüber dem statischen Linken. Erstens braucht man nur eine Kopie des ausführbaren Kodes, anstelle vieler Kopien des selben Kodes, die in allen möglichen ausführbaren Dateien eingebunden sind -- nebenbei spart das auch Speicherplatz auf der Festplatte. Zweitens: Benutzen mehrere Programme die gleichen Bibliotheksfunktionen gleichzeitig, so 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 diese eine Bibliothek neu kompilieren und nicht jedes Programm, das diese Funktion benutzt.

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 noch einen Schritt 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.