Cette section explique certains détails logiques et techniques de la méthode de construction. Il n'est pas essentiel de tout comprendre immédiatement. La plupart de ces informations seront plus claires une fois votre première construction complète terminée. Cette section peut servir de référence à tout moment lors du processus de construction.
Le but global du Chapitre 5 et du Chapitre 6 est de générer un espace temporaire comportant un ensemble d'outils connus et approuvés, qui sont isolés du système hôte. En utilisant la commande chroot, les compilations réalisées dans le reste des chapitres se cantonneront à cet environnement, en assurant une construction du système LFS cible propre et sans faille. Le processus de construction a été conçu pour minimiser les risques auprès des nouveaux lecteurs et pour fournir une valeur éducative maximale.
Le processus de construction se base sur la compilation croisée. La compilation croisée est normalement utilisée pour construire un compilateur ainsi que sa chaîne de compilation sur une machine différente de celle utilisée pour la construction. Ce n’est pas nécessaire pour LFS, puisque la machine sur laquelle le nouveau système est construit est la même que celle utilisée pour la construction. Mais le grand avantage de la compilation croisée, c’est que tout ce qui est compilé est indépendant de l'environnement hôte.
Le livre LFS n'est pas (et ne contient pas) un tutoriel générique sur la construction d'une chaîne de compilation croisée (ou native). N'utilisez pas les commandes de ce livre pour construire une chaîne de compilation croisée autre que celle utilisée pour LFS, à moins de vraiment comprendre ce que vous faites.
La compilation croisée utilise certains concepts qui mériteraient une section à part. Même si vous pouvez 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.
est la machine où nous construisons les programmes. Cette machine est appelée « hôte » dans les autres sections.
est la machine ou le système où les programmes seront lancés. Le terme « hôte » n’a pas la même définition dans les autres sections.
est seulement utilisée pour les compilateurs. C'est la machine pour laquelle le compilateur produit du code. Elle peut être différente de l’hôte et de la construction.
Par exemple, imaginons le scénario suivant (parfois appelé « Canadian Cross ») : nous avons un compilateur sur une machine lente, que nous appellerons A, et le compilateur ccA. Nous avons également une machine rapide (B), mais aucun compilateur pour (B). Nous voulons produire du code pour une troisième machine, lente cette fois (C). Il y a trois étapes à suivre pour construire un compilateur destiné à la machine C.
É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 programmes requis par la machine C peuvent être compilés avec cc2 sur la machine rapide B. À moins que B ne puisse lancer les programmes produits pour C, il n'existe aucun moyen de tester les nouveaux programmes construits avant de les lancer sur la machine C. Par exemple, pour tester ccC, nous pouvons 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 ci-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 compilateurs natifs.
Tous les paquets compilés de manière croisée dans ce livre utilisent un système de construction basé sur autoconf. Le système de construction basé sur autoconf accepte des types de systèmes de la forme cpu-fabriquant-noyau-os, nommés triplets systèmes. Comme le champ fabriquant est souvent inutile, autoconf vous autorise à ne pas le renseigner.
Le lecteur attentif se demandera pourquoi un « triplet »
désigne un nom à quatre composantes. Le champ du noyau et de l'os
ont d'abord commencé comme un seul champ « système ».
Cette forme à trois champs est toujours valide de nos jours pour
certains systèmes, par exemple x86_64-unknown-freebsd
. Mais deux systèmes
peuvent partager le même noyau et être trop différents pour
utiliser le même triplet pour les désigner. Par exemple Android
qui tourne sur les téléphones est complètement différent d'Ubuntu
sur un serveur ARM64, même s'ils utilisent tous les deux le même
type de CPU (ARM64) et utilisent le même noyau (Linux).
Sans une couche d'émulation, vous ne pouvez pas lancer un
exécutable pour un serveur sur un téléphone portable et
vice-versa. Donc le champ « système » a été divisé en les champs
noyau et os, pour désigner trois systèmes sans ambiguïté. Dans
notre exemple, le système Android est appelé aarch64-unknown-linux-android
et le système
Ubuntu est appelé aarch64-unknown-linux-gnu
.
Le mot « triplet » reste ancré dans le
vocabulaire informatique. Pour déterminer simplement votre
triplet système, vous pouvez lancer le script config.guess fournit avec les
sources de nombreux paquets. Désarchivez les sources de binutils,
lancez le script ./config.guess
, et notez la
sortie. Par exemple pour un processeur Intel 32 bits la
sortie sera i686-pc-linux-gnu. Sur un système
64 bits elle sera x86_64-pc-linux-gnu. Sur la plupart
des systèmes Linux la commande encore plus simple gcc -dumpmachine vous donner
cette information.
Faites également attention au nom de l'éditeur de liens
dynamiques de la plateforme, souvent appelé chargeur dynamique (à
ne pas confondre avec l'éditeur de liens standard ld qui fait partie de
binutils). Le chargeur dynamique fourni par glibc trouve et
charge les bibliothèques partagées nécessaires à l’exécution d’un
programme, prépare le programme, puis l'exécute.Le nom du
chargeur dynamique pour une machine Intel 32 bits sera
ld-linux.so.2
(ld-linux-x86-64.so.2
pour les systèmes
64 bits). Pour déterminer le nom du chargeur dynamique,
inspectez un binaire au hasard sur le système hôte en
exécutant : readelf -l
<nom du binaire> | grep interpreter
et
récupérez le résultat. La référence officielle qui couvre toutes
les plateformes se trouve sur une page wiki de
Glibc.
Pour simuler une compilation croisée dans LFS, le nom du triplet
hôte est légèrement modifié en changeant le champ
« fabriquant » dans la variable d’environnement
LFS_TGT
pour qu’il indique
« lfs . Nous utilisons également l'option --with-sysroot
lors de la
construction de l'éditeur de liens et du compilateur croisés pour
leur indiquer l’emplacement des fichiers hôte requis. Cette option
permet de s’assurer qu'aucun autre programme construit dans le
Chapitre 6
ne peut s’associer aux bibliothèques sur la machine de
construction. Seules deux étapes sont obligatoires, en plus d’une
étape supplémentaire destinée aux 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 précédent, « sur pc » signifie que les commandes sont exécutées sur une machine qui utilise la distribution déjà installée. « Sur lfs » signifie que les commandes sont exécutées dans un environnement chroot.
Ce n'est pas la fin de l'histoire. Le langage C n'est pas seulement un compilateur, mais il définit aussi une bibliothèque standard. Dans ce livre, nous utilisons la bibliothèque C de GNU, appelée glibc (son alternative étant « musl »). Cette bibliothèque doit être compilée pour la machine LFS, c'est-à-dire à l’aide du compilateur croisé cc1. Mais le compilateur lui-même utilise une bibliothèque interne qui exécute des instructions complexes indisponibles dans le jeu d'instructions de l'assembleur. Cette bibliothèque interne, libgcc, doit être liée à la bibliothèque glibc pour fonctionner correctement ! De plus, la bibliothèque standard C++ (libstdc++) a aussi besoin d'être associée à glibc. La solution à ce problème consiste d'abord à construire une libgcc inférieure basée sur cc1, qui ne dispose pas de fonctionnalités avancées comme les threads et le traitement des exceptions, puis de construire glibc avec ce compilateur inférieur (glibc elle-même n'étant pas inférieure), puis de construire libstdc++. Cette bibliothèque ne dispose pas des fonctionnalités avancées de libgcc.
La conséquence du paragraphe précédent est que cc1 est incapable de construire une libstdc++ complètement fonctionnelle avec la libgcc dégradée, mais cc1 est le seul compilateur disponible pour construire les bibliothèques C/C++ à la deuxième étape. Il y a deux raisons pour lesquelles nous n'utilisons pas immédiatement le compilateur construit à l'étape 2, cc-lfs, pour construire ces bibliothèques.
En général, cc-lfs ne peut pas se lancer sur pc (le système hôte). Même si les triplets pour pc et lfs sont compatibles l'un avec l'autre, un exécutable pour lfs doit dépendre de glibc-2.40. La distribution hôte peut utiliser une implémentation différent de la libc (par exemple, musl) ou une version précédente de glibc (par exemple glibc-2.13).
Même si cc-lfs peut s'exécuter sur pc, l'utiliser sur pc induirait le risque de se lier aux bibliothèques de pc, comme cc-lfs est un compilateur natif.
Ainsi, lorsque nous construisons l’étape 2 de gcc, nous demandons au système de construction de reconstruire libgcc et libstdc++ avec cc1, mais nous lions libstdc++ à la nouvelle libgcc reconstruite plutôt qu’à l'ancienne construction dégradée, pour faire en sorte que la libstdc++ reconstruite soit entièrement opérationnelle.
Dans le Chapitre 8, (ou « l’étape 3 »), tous les paquets nécessaires au système LFS sont construits. Même si vous avez déjà installé un paquet sur le système LFS dans un chapitre précédent, vous devrez reconstruire le paquet, à moins d’être certain qu’il n’est pas nécessaire. La stabilisation de ces paquets est la raison principale de leur reconstruction : si vous réinstallez un paquet LFS sur un système complet LFS, le contenu du paquet installé devrait être identique au contenu de ce paquet installé dans le Chapitre 8. Les paquets temporaires installés dans le Chapitre 6 ou le Chapitre 7 ne sont pas concernés, car certains d’entre eux sont construits sans dépendance optionnelle, et autoconf ne peut pas exécuter certaines vérifications dans le Chapitre 6 à cause de la compilation croisée. Les paquets temporaires ne disposent donc pas de certaines fonctionnalités optionnelles ou utilisent des routines sous-optimales. De plus, en reconstruisant les paquets, vous permettez l’exécution de la suite de tests.
Le compilateur croisé sera installé dans un répertoire $LFS/tools
séparé, puisqu’il ne fera pas partie
du système final.
Binutils est installé en premier parce que la commande configure de gcc et glibc effectuent des tests de fonctionnalités sur l'assembleur et l'éditeur de liens pour déterminer quelles fonctionnalités logicielles activer ou désactiver. Cette installation est plus importante que ce que vous pouvez penser. Un gcc ou une glibc mal configurés peuvent casser la chaîne de compilation, et l'impact d’une telle configuration ne se verrait qu’à la fin de la construction de la distribution complète. La suite de tests indiquera généralement cette erreur avant que vous n’ayez trop avancé.
Binutils installe son assembleur et son éditeur de liens à deux
emplacements, $LFS/tools/bin
et
$LFS/tools/$LFS_TGT/bin
. Les outils
situés à un emplacement sont liés à l’autre par un lien en dur.
L’ordre de recherche des bibliothèques est un aspect important de
l’éditeur de liens. Vous pouvez obtenir des informations détaillées
à partir de la commande ld en lui passant l’option
--verbose
. Par exemple, la
commande ld --verbose | grep
SEARCH affichera les chemins de recherche actuels
et leur ordre. Cet exemple peut être exécuté en mode lecture par
l’utilisateur lfs
. Si vous revenez
plus tard sur cette page, remplacez la commande $LFS_TGT-ld par ld.
Le prochain paquet installé est gcc. Voici un exemple de ce qui peut s’afficher pendant l'exécution de la commande configure :
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
Il s’agit d’un paquet important pour les raisons mentionnées plus haut. C’est également la preuve que le script configure de gcc ne parcourt pas les répertoires du PATH pour trouver quels outils utiliser. Cependant, lors de l’exécution normale de la commande gcc, les mêmes chemins de recherche ne sont pas forcément utilisés. Pour trouver quel éditeur de liens standard gcc utilise, exécutez la commande $LFS_TGT-gcc -print-prog-name=ld. À nouveau, retirez la commande $LFS_TGT- si vous revenez ici plus tard.
Vous pouvez obtenir des informations détaillées grâce à la commande
gcc en lui passant
l’option -v
lors de la
compilation d'un programme. Par exemple, la commande $LFS_TGT-gcc -v example.c
(sans
$LFS_TGT- si vous
revenez plus tard) affichera des informations détaillées sur les
phases de préprocesseur, de compilation et d’assemblage, ainsi que
les chemins de recherche de gcc pour les en-têtes inclus et
leur ordre.
L’installation suivante concerne les en-têtes nettoyés de l’API de Linux. Ils permettent à la bibliothèque standard C (glibc) d’interagir avec les fonctionnalités fournies par le noyau Linux.
Glibc est le paquet suivant à installer. Le compilateur, les outils
binaires et les en-têtes du noyau sont les éléments les plus
importants à prendre en considération pour construire glibc. Le
compilateur et les outils binaires ne posent généralement pas de
problème car glibc utilise toujours ceux liés à l’option --host
passée à son script configure.
Par exemple, dans notre cas, le compilateur sera $LFS_TGT-gcc et l'outil
readelf sera
$LFS_TGT-readelf. Les
en-têtes du noyau s’avèrent un peu plus compliqués. Ne prenez donc
pas de risque et utilisez l'option de configure disponible pour
exécuter la bonne sélection. Après l'exécution de configure, vérifiez le contenu et
les détails importants du fichier config.make
dans le répertoire build
. Ces éléments mettent en avant un aspect
important du paquet glibc : il est auto-suffisant en termes de
construction et ne repose généralement pas sur la chaîne de
compilation par défaut.
Comme indiqué précédemment, la bibliothèque standard C++ est
ensuite compilée, suivie dans le Chapitre 6
par les autres programmes qui nécessitent une compilation croisée
en raison des dépendances circulaires qu’ils cassent lors de la
construction. L’installation forcée de tous ces paquets sur le
système de fichiers LFS nécessite la variable DESTDIR
.
À la fin du Chapitre 6,
le compilateur LFS natif est installé. binutils-pass2 est construit
en premier, avec le même répertoire d’installation DESTDIR
que les autres programmes, puis la deuxième
passe de gcc est construite sans les bibliothèques inutiles. En
raison d’un comportement illogique dans le script configure de gcc,
CC_FOR_TARGET
devient cc lorsque l'hôte est identique à
la cible, mais différent du système de construction. C'est pourquoi
le paramètre CC_FOR_TARGET=$LFS_TGT-gcc
est
déclaré de façon explicite dans les options de configure.
À l’entrée de l'environnement chroot dans le Chapitre 7, les programmes nécessaires au bon fonctionnement de la chaîne de compilation sont installés de manière temporaire. À partir de là, la chaîne de construction de base est auto-suffisante et auto-hébergée. Dans le Chapitre 8, vous construirez, testerez et installerez les versions finales de tous les paquets nécessaires au bon fonctionnement du système complet.