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.
Nous savons que GCC passe 2 cassera la chaîne d'outils croisée. Nous ne considérons pas cela comme un bogue car GCC passe 2 est le dernier paquet à être compilé de manière croisée dans le livre, et nous ne le « corrigerons » pas tant que nous n'aurons pas vraiment besoin de compiler certains paquets de manière croisée après GCC passe 2.
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.
Il y a deux points clés pour une compilation croisée :
Lorsqu'on produit et traite du code machine censé être exécuté sur « l'hôte », la chaîne d'outils croisée doit être utilisée. Remarquez que la chaîne d'outils native de « la construction » peut quand même être invoquée pour générer du code machine censée être exécuté sur « la construction ». Par exemple, le système de construction peut compiler un générateur avec la chaîne d'outils native, puis générer un fichier source C avec le générateur, puis enfin compiler le fichier source C avec la chaîne d'outils croisée pour que le code généré puisse être exécuté sur « l'hôte ».
Avec un système de construction basé sur autoconf, ce
prérequis est assuré en utilisant le paramètre --host
pour spécifier le
triplet « hôte ». Avec ce paramètre, le
système de construction utilisera les composants de la chaîne
d'outils préfixés de
pour générer et traiter le
code machine pour « l'hôte » ; p. ex. le
compilateur sera <the host
triplet>
<the host
triplet>
-gcc et l'outil
readelf sera
<the host
triplet>
-readelf.
Le système de construction ne devrait pas essayer d'exécuter
du code machine généré censé être exécuté sur « l'hôte ». Par exemple, lorsque
vous construisez un utilitaire nativement, sa page de manuel
sera générée en exécutant l'utilitaire avec le paramètre
--help
et en traitant
la sortie, mais en général ce n'est pas possible pour une
compilation croisée puisque l'utilitaire ne se lancera pas
sur « la
construction » : il est évidemment
impossible d'exécuter du code machine ARM64 sur un CPU x86
(sans un émulateur).
Avec un système de construction basé sur autoconf, ce
prérequis est satisfait dans « le mode de compilation
croisée » où les fonctionnalités
facultatives qui nécessitent d'exécuter du code machine pour
« l'hôte » pendant la construction
sont désactivées. Lorsque le triplet « hôte »
est spécifié explicitement, « le mode de compilation
croisée » est activée si et seulement si
le script configure échoue à exécuter
un simple programme compilé vers le code machine de
« l'hôte », ou si le triplet de
« la
construction » est explicitement spécifié
par le paramètre --build
et qu'il est différent
du triple de « l'hôte ».
Pour compiler un paquet de manière croisée pour le système LFS
temporaire, le nom du triplet système est légèrement ajusté en
modifiant le champ "fabriquant" dans la variable LFS_TGT
pour qu'il indique "lfs" et LFS_TGT
est ensuite spécifié comme triplet
« hôte » via --host
, pour que la chaîne d'outils
croisée puisse être utilisée pour générer et traiter le code
machine qui s'exécute dans le système LFS temporaire. De plus, nous
devons activer le « mode de compilation croisée » :
bien que le code machine de « l'hôte », c.-à-d. le code machine pour
le système LFS temporaire, puisse s'exécuter sur le CPU actuel, il
peut référencer des bibliothèques qui ne sont pas disponibles sur
« la
construction » (la distribution hôte), ou bien
du code ou de la donnée pourrait ne pas exister ou être défini
différemment dans ces bibliothèques même si elles existent. Lors de
la compilation croisée d'un paquet pour le système LFS temporaire,
nous ne pouvons pas nous reposer sur le script configure pour détecter ce
problème avec le programme simple : il utilise seulement
quelques composants de la libc
que
la libc
de la distribution hôte
fournit probablement (à moins que la distribution hôte n'utilise
une implémentation de la libc
différente, comme Musl). Ce programme n'échouera donc pas,
contrairement aux programmes vraiment utiles. Ainsi, nous devons
spécifier explicitement le triplet de « la
construction » pour activer le « mode de compilation
croisée ». La valeur que nous utilisons est
simplement la valeur par défaut, c.-à-d. le triplet système
original de la sortie de config.guess, mais le
« mode de compilation
croisée » dépend d'une définition explicite
comme nous l'avons indiqué.
Nous utilisons l'option --with-sysroot
lors de la
construction de l'éditeur des liens croisé et du compilateur
croisé, pour leur dire où trouver les fichiers requis pour
« l'hôte ». Cela s'assure presque qu'aucun
autre programme construit dans Chapitre 6
puisse se lier aux bibliothèques de « la
construction ». Nous utilisons le mot
« presque » car libtool, une enveloppe de
« compatibilité » du compilateur et de
l'éditeur des liens pour les systèmes de construction basés sur
autoconf, peut essayer d'être trop intelligent et passer par erreur
des options qui permettent à l'éditeur des liens de trouver les
bibliothèques de « la
construction ». Pour éviter ce désastre, nous
devons supprimer les fichiers d'archives libtool (.la
) et corriger une ancienne copie de libtool
fournie dans le code de Binutils.
É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 éventuellement 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. Comme indiqué, nous ne pouvons pas exécuter cc-lfs sur pc (la distribution hôte) car il peut nécessiter certaines bibliothèques, du code ou des données qui ne sont pas disponibles sur « la construction » (la distribution hôte). Ainsi, lorsque nous construisons la deuxième étape de gcc, nous remplaçons le chemin de recherche des bibliothèques pour se lier à libstdc++ de la libgcc nouvellement reconstruite au lieu de l'ancienne construction dégradée. Cela rend la libstdc++ reconstruite complètement fonctionnelle.
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 certaines fonctionnalités facultatives sont désactivées à cause de dépendances manquantes ou du « mode de compilation croisée » . 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.
Ensuite vient glibc. C'est le premier paquet que nous compilons de
manière croisée. Nous utilisons l'option --host=$LFS_TGT
pour faire utiliser
ces outils préfixés par $LFS_TGT
au
système de construction, et l'option --build=$(../scripts/config.guess)
pour activer le « mode
de compilation croisée » comme indiqué. La
variable DESTDIR
est utilisée pour
forcer l'installation dans le système de fichiers LFS.
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. Les étapes pour ces paquets sont similaires aux étapes pour glibc.
À 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.
À 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.