ii. Notes techniques sur la chaîne d'outils

Cette section explique certains détails rationnels et techniques derrière la méthode de construction. Il n'est pas essentiel de comprendre immédiatement tout ce qui se trouve dans cette section. La plupart des informations seront plus claires après avoir réalisé réellement une construction complète. Cette section peut servir de référence à tout moment lors du processus de construction.

Le but global des chapitres Chapitre 5 et Chapitre 6 est de fournir une zone temporaire qui contient un ensemble d'outils connus qui peuvent être isolés du système hôte. En utilisant chroot, les commandes dans le reste des chapitres se cantonneront à cet environnement, en assurant une construction du système LFS cible propre, sans soucis. Le processus de construction a été conçu pour minimiser les risques pour les nouveaux lecteurs et pour fournir une valeur éducative maximale en même temps.

Le processus de construction se base sur de la compilation croisée. La compilation croisée s'utilise normalement pour construire un compilateur et sa chaîne de construction pour une machine différente de celle utilisée pour la construction. Cela n'est pas strictement requis pour LFS, comme la machine où le nouveau système est construit est la même que celle utilisée pour la construction. Mais la compilation croisée a le grand avantage que tout ce qui est compilé ne peut pas dépendre de l'environnement hôte.

À propos de la compilation croisée

La compilation croisée utilise certains concepts qui méritent une section à part. Bien que vous puissiez passer cette section lors de votre première lecture, nous vous recommandons fortement d'y revenir plus tard pour bien comprendre le processus de construction.

Définissons d'abord certains termes utilisés dans ce contexte :

build (construction)

est la machine où nous construisons les programmes. Remarquez que cette machine sera appelée « hôte » dans les autres sections.

host (hôte)

est la machine ou le système où les programmes seront lancés. Remarquez que nous n'utilisons pas le terme « hôte » de la même manière ici que dans les autres sections.

target (cible)

est seulement utilisé pour les compilateurs. C'est la machine pour laquelle le compilateur produit du code. Elle peut être différente de la machine hôte ou de construction.

Par exemple, imaginons le scénario suivant (parfois appelé « Canadian Cross ») : on peut avoir un compilateur sur une machine lente, appelons-la A, et le compilateur ccA. On peut aussi avoir une machine rapide (B) sans compilateur, et on veut produire du code pour une autre machine lente (C). Pour construire un compilateur pour une machine C, on effectuerait trois étapes :

Étape Construction Hôte Cible Action
1 A A B construire un compilateur croisé cc1 avec ccA sur la machine A
2 A B C construire un compilateur croisé cc2 avec cc1 sur la machine A
3 B C C construire le compilateur ccC avec cc2 sur la machine B

Ensuite, tous les autres programmes requis par la machine C peuvent être compilés avec cc2 sur la machine rapide B. Remarquez qu'à moins que B ne puisse lancer les programmes produits pour C, il n'y a aucun moyen de tester les programmes construits avant de les lancer sur la machine C. Par exemple, pour tester ccC, on peut ajouter une quatrième étape :

Étape Construction Hôte Cible Action
4 C C C reconstruire et tester ccC avec lui-même sur la machine C

Dans l'exemple au dessus, seuls cc1 et cc2 sont des compilateurs croisés, c'est à dire qu'ils produisent du code pour une machine différente de celle sur laquelle ils tournent. Les autres compilateurs ccA et ccC produisent du code pour la machine sur laquelle ils tournent. Ces compilateurs sont appelés des compilateurs natifs.

Implémentation de la compilation croisée dans LFS

[Note]

Note

Presque tous les systèmes de construction utilisent des noms de la forme cpu-fabriquant-noyau-os, souvent appelé le triplet machine. Le lecteur attentif se demandera pourquoi on appelle un « triplet » un nom à quatre composants. La raison est historique : initialement, on utilisait trois composants pour désigner une machine sans ambiguïté, mais avec les nouvelles machines et les nouveaux systèmes, cela s'avère insuffisant. Le mot « triplet » est resté. Une façon simple de déterminer le nom du triplet machine est de lancer le script config.guess venant avec les sources d'un grand nombre de paquets. Déballez les sources de binutils, lancez le script ./config.guess et notez la sortie. Par exemple, pour un processeur Intel 32 bits moderne, la sortie sera du type i686-pc-linux-gnu. Sur un système 64 bits cela sera x86_64-pc-linux-gnu.

De même, faites attention au nom de l'éditeur de liens de la plateforme, souvent appelé le chargeur dynamique (à ne pas confondre avec l'éditeur de liens standard ld faisant partie de Binutils). Le chargeur dynamique fourni par Glibc trouve et charge les bibliothèques partagées nécessaires à un programme pour s'exécuter, puis l'exécute. Le nom de l'éditeur dynamique pour une machine Intel 32 bits sera ld-linux.so.2 (ld-linux-x86-64.so.2 pour les systèmes 64 bits). Une façon sûre de déterminer le nom de l'éditeur de liens dynamiques est d'inspecter un binaire au hasard du système hôte en exécutant : readelf -l <nom du binaire> | grep interpreter et de noter le résultat. La référence faisant autorité couvrant toutes les plateformes est dans le fichier shlib-versions à la racine du répertoire des sources de Glibc.

Pour simuler une compilation croisée, le nom du triplet hôte est légèrement ajusté en changeant la partie « fabriquant » dans la variable LFS_TGT. Nous utilisons aussi l'option --with-sysroot lors de la construction de l'éditeur des liens et du compilateur croisés pour leur dire où trouver les fichiers hôtes requis. Cela s'assure qu'aucun autre programme construit dans Chapitre 6 ne peut se lier aux bibliothèques sur la machine de construction. Seuls deux étapes sont requises, et une autre pour les tests :

Étape Construction Hôte Cible Action
1 pc pc lfs construire un compilateur croisé cc1 avec cc-pc sur pc
2 pc lfs lfs construire un compilateur cc-lfs avec cc1 sur pc
3 lfs lfs lfs reconstruire et tester cc-lfs avec lui-même sur lfs

Dans le tableau plus haut, « sur pc » signifie que les commandes sont lancées sur une machine qui utilise la distribution déjà installée. « Sur lfs » signifie que les commandes sont lancées dans un environnement chroot.

Maintenant, il y a plus à savoir sur la compilation croisée : le langage C n'est pas seulement un compilateur, mais définie aussi une bibliothèque standard. Dans ce livre, on utilise la bibliothèque C de GNU, glibc. Cette bibliothèque doit être compilée pour la machine lfs, c'est-à-dire, avec le compilateur croisé cc1. Mais le compilateur lui-même utilise une bibliothèque interne implémentant des instructions complexes qui ne sont pas disponibles dans l'ensemble d'instructions de l'assembleur. Cette bibliothèque interne, libgcc, doit être liée à la bibliothèque glibc pour fonctionner correctement ! En plus, la bibliothèque standard du C++ (libstdc++) a aussi besoin d'être liée à la glibc. La solution pour ce problème de poule et d'œuf est de d'abord construire une libgcc dégradée basée sur cc1, qui n'a pas de fonctionnalité avancée comme les threads et le traitement des exceptions, puis de construire glibc avec ce compilateur dégradé (glibc elle-même n'est pas dégradée), puis de construire libstdc++. Mais cette dernière n'aura pas les fonctionnalités que libgcc n'a pas non plus.

Ce n'est pas la fin de l'histoire : la conclusion du paragraphe précédent est que cc1 est incapable de trouver une libstdc++ complètement fonctionnelle, mais c'est le seul compilateur disponible pour construire les bibliothèques C/C++ lors de la deuxième étape ! Évidemment, le compilateur construit à l'étape 2, cc-lfs, serait capable de construire ces bibliothèques, mais (1) le système de construction de GCC ne sait pas qu'il est utilisable sur pc, et (2) l'utiliser sur pc risquerait de le lier à des bibliothèques de pc, comme cc-lfs est un compilateur natif. Donc nous devons compiler libstdc++ plus tard, dans le chroot.

Autres détails sur la procédure

Le compilateur croisé sera installé dans un répertoire $LFS/tools séparé, comme il ne fera pas partie du système final.

Binutils est tout d'abord installé parce que les exécutions de Glibc et GCC par configure réalisent quelques tests de fonctionnalités sur l'assembleur et l'éditeur de liens pour déterminer quelle fonctionnalité logicielle activer ou désactiver. Ceci est plus important que ce que vous pouvez imaginer. Un GCC ou une Glibc mal configuré peut aboutir à une chaîne d'outils subtilement cassée, et l'impact d'une telle cassure ne se verrait pas avant la fin de la construction de la distribution complète. Un échec dans la suite de tests surlignera habituellement cette erreur avant que trop de travail supplémentaire n'ait été réalisé.

Binutils installe son assembleur et son éditeur de liens à deux endroits, $LFS/tools/bin et $LFS/tools/$LFS_TGT/bin. Les outils dans un emplacement sont liés en dur à l'autre. Un aspect important de l'éditeur de liens est son ordre de recherche des bibliothèques. Vous pouvez obtenir des informations détaillées à partir de ld en lui passant le paramètre --verbose. Par exemple, un ld --verbose | grep SEARCH illustrera les chemins de recherche réels et leur ordre. Il montre quels fichiers sont liés par ld en compilant un programme de test et en passant le paramètre --verbose à l'éditeur de liens. Par exemple, $LFS_TGT-gcc dummy.c -Wl,--verbose 2>&1 | grep succeeded affichera tous les fichiers ouverts avec succès lors de l'édition des liens.

Le prochain paquet installé est GCC. Un exemple de ce qui peut être vu pendant son exécution de configure est :

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

C'est important pour les raisons mentionnées plus haut. Cela démontre aussi que le script configure de GCC ne cherche pas les répertoires du PATH pour trouver les outils à utiliser. Néanmoins, lors d'une opération normale de gcc, les mêmes chemins de recherche ne sont pas forcément utilisés. Pour trouver quel éditeur de liens standard gcc utilisera, lancez : $LFS_TGT-gcc -print-prog-name=ld.

Vous pouvez obtenir des informations détaillées à partir de gcc en lui fournissant l'option en ligne de commande -v lors de la compilation d'un programme de tests. Par exemple, gcc -v dummy.c affichera des informations détaillées sur les étapes du préprocesseur, de la compilation et de l'assemblage, avec les chemins de recherche inclus par gcc et leur ordre.

Ce qui est ensuite installé est les en-têtes de l'API de Linux nettoyées. Elles permettent à la bibliothèque standard (Glibc) d'interagir avec les fonctionnalités que le noyau Linux fournira.

Le paquet installé ensuite est Glibc. Les choses les plus importantes à prendre en considération pour construire Glibc sont le compilateur, les outils binaires et les en-têtes du noyau. Le compilateur ne pose généralement pas de problème car Glibc utilise toujours le compilateur lié au paramètre --host passé à son script configure, par exemple, dans notre cas, le compilateur sera $LFS_TGT-gcc. Les outils binaires et les en-têtes du noyau peuvent être un peu plus compliqués. Ne prenez donc pas de risque et utilisez les options de configure disponibles pour assurer les bonnes sélections. Après l'exécution de configure, vérifiez le contenu du fichier config.make dans le répertoire glibc-build pour tous les détails importants. Notez l'utilisation de CC="$LFS_TGT-gcc" (où $LFS_TGT est étendue) pour contrôler les outils binaires utilisés, et l'utilisation des paramètres -nostdinc et -isystem pour contrôler le chemin de recherche des en-têtes du compilateur. Ces éléments soulignent un aspect important du paquet glibc — il est auto-suffisant en termes de machinerie de construction et ne repose généralement pas sur la chaîne d'outils par défaut.

Comme nous venons de le dire, la bibliothèque standard C++ est ensuite compilée, suivi dans Chapitre 6 par tous les programmes qui ont besoin d'eux-mêmes pour être construits. L'étape initiale de tous ces paquets utilise la variable DESTDIR pour que les programmes soient installés dans le système de fichiers LFS.

À la fin de Chapitre 6 le compilateur lfs natif est installé. binutils-pass2 est d'abord construit, avec la même installation DESTDIR que les autres programmes, puis la seconde passe de GCC est construite, sans libstdc++ et les autres bibliothèques non importantes. À cause d'une logique bizarre dans le script de construction de GCC, CC_FOR_TARGET devient cc quand l'hôte est le même que la cible, mais est différent du système de construction. C'est pourquoi nous mettons explicitement CC_FOR_TARGET=$LFS_TGT-gcc dans les options de configure.

En entrant dans l'environnement chroot dans Chapitre 7, la première tache consiste à installer libstdc++. Ensuite, ojn effectue des installations temporaires de programmes requis pour le bon fonctionnement de la chaîne d'outils. On construit aussi des programmes requis pour tester d'autres programmes. À partir de là, la chaîne de construction de base est auto-suffisante et auto-hébergée. Dans Chapitre 8, on construit, teste et installe les versions finales de tous les paquets requis pour un système complètement fonctionnel.