Remarques sur la construction de logiciels

Celles et ceux qui ont construit un système LFS connaissent sans doute les principes généraux du téléchargement et de l'extraction de logiciel. Certaines de ces informations sont répétées ici pour les nouveaux qui construisent leurs propres logiciels.

Chaque ensemble d'instructions d'installation contient une URL sur laquelle vous pouvez télécharger le paquet. Cependant, les correctifs sont enregistrés sur les serveurs LFS et disponibles via HTTP. Ces derniers sont référencés, si besoin, dans les instructions d'installation.

Même si vous pouvez placer les fichiers sources où vous voulez, nous supposons que vous avez extrait le paquet et êtes allé dans le répertoire créé par le processus de décompression (le répertoire des sources). Nous supposons aussi que vous avez décompressé les correctifs requis et qu'ils sont dans le répertoire directement au-dessus du répertoire des sources.

Nous ne saurions que trop vous recommander de démarrer à partir d'une arborescence de sources propre à chaque fois. Cela veut dire que si vous avez eu une erreur lors de la configuration ou de la compilation, il est généralement préférable de supprimer l'arborescence des sources et de l'extraire de nouveau avant de réessayer. Cela ne s'applique évidemment pas si vous êtes un utilisateur avancé habitué à modifier les Makefiles et le code C. Si vous avez un doute cependant, commencez à partir d'une arborescence propre.

Construction de logiciels en tant qu'utilisateur non privilégié (non root)

La règle d'or de l'administration d'un système Unix est de n'utiliser vos super-pouvoirs que si nécessaire. D'où la recommandation de BLFS de construire les logiciels en tant qu'utilisateur non privilégié et de ne devenir l'utilisateur root que lors de l'installation du logiciel. On suit cette philosophie dans tous les paquets de ce livre. Sauf spécifications contraires, toutes les instructions devraient être exécutées en tant qu'utilisateur non privilégié. Le livre vous conseillera sur les instructions qui ont besoin des privilèges root.

Décompresser le logiciel

S'il y a un fichier compressé au format .tar, on le décompresse en utilisant une des commandes suivantes :

tar -xvf filename.tar.gz
tar -xvf filename.tgz
tar -xvf filename.tar.Z
tar -xvf filename.tar.bz2
[Note]

Note

Vous pouvez ne pas utiliser le paramètre v dans les commandes décrites ci-dessus et ci-dessous si vous souhaitez supprimer le listage verbeux de tous les fichiers de l'archive au fur et à mesure qu'ils sont extraits. Cela peut aider à accélérer l'extraction mais aussi rendre plus évidentes les erreurs produites durant le processus.

Vous pouvez aussi utiliser une méthode légèrement différente :

bzcat filename.tar.bz2 | tar -xv

Enfin, nous avons parfois un fichier de correctif compressé au format .patch.gz ou .patch.bz2. La meilleure manière d'applique le correctif est de passer la sortie du décompresseur à l'utilitaire patch via un pipe. Par exemple :

gzip -cd ../patchname.patch.gz | patch -p1

Ou pour un correctif compressé avec bzip2 :

bzcat ../patchname.patch.bz2 | patch -p1

Vérifier l'intégrité des fichiers

En général, pour vérifier que le fichier téléchargé est complet, de nombreux mainteneurs de paquets distribuent aussi les sommes md5 des fichiers. Pour vérifier la somme md5 des fichiers téléchargés, téléchargez à la fois le fichier et le fichier md5sum correspondant dans le même répertoire (de préférence à partir d'emplacements différents en ligne) et (en supposant que file.md5sum est le fichier md5sum téléchargé), lancez la commande suivante :

md5sum -c file.md5sum

S'il y a une erreur, elle sera signalée. Remarquez que le livre BLFS contient aussi les sommes md5 de tous les fichiers sources. Pour utiliser les sommes md5 fournies par BLFS, vous pouvez créer un file.md5sum (mettez les données md5sum et le nom exact du fichier téléchargé sur la même ligne d'un fichier, séparés par un espace blanc), et lancez la commande montrée ci-dessus. Sinon, lancez simplement la commande décrite ci-dessus et comparez la sortie avec les données de somme md5 inscrites dans le livre BLFS.

md5sum <name_of_downloaded_file>

Le MD5 n'est pas cryptographiquement sûr, donc les sommes md5 ne sont fournies que pour détecter des changements non intentionnels au contenu du fichier. Par exemple, une erreur ou troncation introduite pendant le transfert réseau, ou une mise à jour « furtive » du paquet en amont (mise à jour du contenu d'une archive publiée au lieu de créer directement une nouvelle version).

Il n'y a pas de manière sure à « 100 % » pour s'assurer de l'authenticité des fichiers sources. En supposant que les développeurs en amont gèrent leur site correctement (la clé privée n'est pas communiquée et le domaine n'est pas usurpé), et que les ancres de confiance ont été correctement configurées avec make-ca-1.14 sur le système BLFS, on peut raisonnablement faire confiance aux URL des site web officiels avec le protocole https. Remarquez que le livre BLFS lui-même est publié sur un site avec https, donc vous devez déjà avoir un peu confiance en le protocole https ou vous ne pourriez pas faire confiance au contenu de ce livre.

Si le paquet est téléchargé à partir d'un emplacement non officiel (par exemple un miroir local), vous pouvez utiliser des sommes de contrôles générées par un algorithme cryptographiquement sûr (par exemple SHA256) pour vérifier l'authenticité du paquet. Téléchargez le fichier de somme de contrôle depuis le site officiel des développeurs en amont (ou depuis un endroit auquel vous faites confiance) et comparez la somme de contrôle du paquet de l'emplacement non officiel. Par exemple, vous pouvez vérifier une somme de contrôle SHA256 avec la commande :

[Note]

Note

Si la somme de contrôle et le paquet sont téléchargés à partir de la même source non fiable, vous ne gagnerez rien à vérifier le paquet avec la somme de contrôle. L'attaquant peut présenter une fausse somme de contrôle en plus de compromettre le paquet en lui-même.

sha256sum -c file.sha256sum

Si GnuPG-2.4.5 est installé, vous pouvez aussi vérifier l'authenticité du paquet avec une signature GPG. Importez la clé publique GPG amont avec :

gpg --recv-key keyID

keyID devrait être remplacée par l'identifiant de la clé fournie par un site fiable (par exemple, copiez-la depuis le site officiel en utilisant https). Maintenant vous pouvez vérifier la signature avec :

gpg --recv-key file.sig file

L'avantage de la signature GnuPG est que, une fois une clé publique de confiance importée, vous pouvez télécharger à la fois le paquet et sa signature à partir du même emplacement non officiel et les vérifier avec la clé publique. Donc vous n'avez pas besoin de vous connecter au site officiel en amont pour récupérer une somme de contrôle pour chaque nouvelle version. Vous n'aurez besoin de mettre à jour la clé publique que si elle est révoquée ou expirée.

Créer des fichiers journaux pendant l'installation

Pour les gros paquets, il est commode de créer des fichiers journaux plutôt que de fixer indéfiniment l'écran en espérant trouver une erreur ou un avertissement particulier. Les fichiers journaux sont aussi utiles pour déboguer et garder des enregistrements. La commande suivante vous permet de créer un journal d'installation. Remplacez <commande> par la commande que vous cherchez à exécuter.

( <command> 2>&1 | tee compile.log && exit $PIPESTATUS )

2>&1 redirige les messages d'erreur vers le même endroit que la sortie standard. La commande tee vous permet de voir la sortie en journalisant les résultats dans un fichier. Les parenthèses autour de la commande exécutent toute la commande dans un sous-shell. Enfin, la commande exit $PIPESTATUS s'assure que c'est bien le résultat de <commande> qui est retourné et pas le résultat de la commande tee.

Utilisation de processeurs multiples

Pour la plupart des systèmes modernes avec des processeurs multiples (ou cœurs) le temps de compilation pour un paquet peut être réduit en effectuant une « construction parallèle » soit en initialisant une variable d'environnement, soit en disant au programme make d'exécuter plusieurs tâches en même temps.

Par exemple, un CPU Intel Core i9-13900K contient 8 cœurs performants (P) et 16 cœurs efficaces (E). Les cœurs P prennent en charge SMT (Simultaneous MultiThreading, aussi appelé « Hyper-Threading ») donc chaque cœur P peut exécuter deux tâches en même temps et le noyau Linux traitera chaque cœur P comme deux cœurs logiques. En conséquence, il y a 32 cœurs logiques en tout. Pour utiliser tous ces cœurs logiques en exécutant make, il est possible de configurer une variable d'environnement pour dire à make d'exécuter 32 tâches en parallèle :

export MAKEFLAGS='-j32'

ou en compilant simplement avec :

make -j32

Si vous avez appliqué le sed facultatif pendant la construction de ninja dans LFS, vous pouvez utiliser :

export NINJAJOBS=32

quand un paquet utilise ninja, ou simplement :

ninja -j32

Si vous n'êtes pas sûr de connaître le nombre de cœurs logiques, exécutez la commande nproc.

Pour make, le nombre de tâches par défaut est 1. Mais pour ninja, le nombre de travaux par défaut est N + 2, si le nombre de cœurs logiques N est plus grand que 2. Sinon, N + 1 si N vaut 1 ou 2. La raison pour laquelle cet outil utilise un nombre de tâches plus grand que le nombre de cœurs logiques est de faire travailler tous les processeurs logiques même si certaines tâches effectuent des opérations mémoires.

Remarquez que les options -j ne limitent que les tâche parallèles démarrées par make ou ninja, mais chaque tâche peut encore générer ses propres processus ou threads. Par exemple, ld.gold utilisera plusieurs threads pour l'édition des liens, et certains tests des paquets peuvent créer plusieurs threads pour tester les propriétés de sûreté en parallèle. Il n'y a pas de manière générique pour un système de construction de connaître le nombre de processus ou de threads générés par une tâche. En général, il ne faut pas considérer la valeur passée avec -j comme une limite en dur sur le nombre de cœurs logiques à utiliser. Consultez la section intitulée « Utiliser les groups de contrôle Linux pour limiter l'utilisation des ressources » si vous voulez mettre en place ce genre de limite.

Généralement le nombre de processus ne doit pas trop dépasser le nombre de cœurs supportés par le CPU. Pour lister les processeurs de votre système, tapez : grep processor /proc/cpuinfo.

Dans certains cas, l'utilisation de processeurs multiples peut amener dans une situation de compétition où le succès de la construction dépend de l'ordre des commandes lancées par le programme make. Par exemple, si un exécutable demande un fichier A et un fichier B, essayer de lier le programme avant qu'un des composants dépendants ne soit disponible aboutira à un échec. Cela arrive en général quand les développeurs n'ont pas correctement désigné tous les prérequis utiles pour accomplir une étape du Makefile.

Si cela arrive, la meilleure chose à faire est de recommencer la construction avec un seul processeur. En ajoutant -j1 à une commande make, cela écrasera l'initialisation similaire dans une variable d'environnement MAKEFLAGS.

[Important]

Important

Un autre problème peut survenir avec les CPU modernes avec beaucoup de cœurs. Chaque tâche démarrée consomme de la mémoire, et si la somme de la mémoire requise par chaque tâche dépasse la quantité de mémoire disponible, vous aurez soit une interruption noyau OOM (plus de mémoire) ou une utilisation de l'espace d'échange qui ralentira excessivement la construction.

Comme certaines compilation avec g++ peuvent consommer jusqu'à 2,5 Go de mémoire, pour être sûr, vous devriez restreindre le nombre de tâches à (mémoire totale en Go)/2,5, au moins pour les gros paquets comme LLVM, WebKitGtk, QtWebEngine ou libreoffice.

Utiliser les groups de contrôle Linux pour limiter l'utilisation des ressources

Parfois nous voulons limiter l'utilisation des ressources lors de la construction d'un paquet. Par exemple, lorsqu'on a 8 cœurs logiques, nous pourrions vouloir utiliser seulement 6 cœurs pour construire le paquet et réserver les deux autres cœurs à la lecture d'un film. Le noyau Linux fournit une fonctionnalité appelée les groupes de contrôle (cgroup) pour ce besoin.

Activez les groupes de contrôle dans la configuration du noyau, puis reconstruisez le noyau et redémarrez si nécessaire :

General setup --->
  [*] Control Group support --->                                       [CGROUPS]
    [*] Memory controller                                                [MEMCG]
    [*] Cpuset controller                                              [CPUSETS]

Assurez-vous que Systemd-256.4 et Shadow-4.16.0 ont été reconstruits avec la prise en charge de Linux-PAM-1.6.1 (si vous interagissez à travers une session SSH ou graphique, assurez-vous également que le serveur OpenSSH-9.8p1 ou le gestionnaire de bureau a été construit avec Linux-PAM-1.6.1). En tant qu'utilisateur root, créez un fichier de configuration pour permettre le contrôle des ressources sans privilège root, et dites à systemd de recharger la configuration :

mkdir -pv /etc/systemd/system/user@.service.d &&
cat > /etc/systemd/system/user@.service.d/delegate.conf << EOF &&
[Service]
Delegate=memory cpuset
EOF
systemctl daemon-reload

Ensuite, déconnectez-vous et reconnectez-vous. Maintenant pour exécuter make -j5 avec les 4 premiers cœurs logiques et 8 Go de mémoire système, exécutez :

systemctl   --user start dbus                &&
systemd-run --user --pty --pipe --wait -G -d \
            -p MemoryHigh=8G                 \
            -p AllowedCPUs=0-3               \
            make -j5

Avec MemoryHigh=8G, une limite souple pour l'utilisation de la mémoire est mise en place. Si les processus du cgroup (make et tous ses descendants) utilisent plus de 8 Go de mémoire système en tout, le noyau va les ralentir et essayer de récupérer la mémoire système qui leur a alloué. Mais ils peuvent toujours utiliser plus de 8 Go de mémoire système. Si vous voulez plutôt donner une limite nette, remplacez MemoryHigh par MemoryMax. Mais cela fera tuer les processus si 8 Go n'est pas suffisant pour eux.

AllowedCPUs=0-3 fait en sorte que le noyau ne lance les processus dans le cgroup que sur les cœurs logiques avec les numéros 0, 1, 2 ou 3. Vous aurez peut-être besoin d'ajuster ce paramètre en fonction de la correspondance entre les cœurs logiques et physiques. Par exemple, avec un CPU Intel Core i9-13900K, les cœurs logiques 0, 2, 4, …, 14 correspondent au premier thread des huit cœurs physiques P, les cœurs logiques 1, 3, 5, …, 15 correspondent au second thread des cœurs physiques P, et les cœurs logiques 16, 17, …, 31 correspondent aux cœurs physiques E. Donc si vous voulez utiliser quatre threads de différents cœurs P, nous devons spécifier 0,2,4,6 au lieu de 0-3. Remarquez que les autres modèles de CPU peuvent avoir une autre correspondance. Si vous ne connaissez pas la correspondance entre cœurs logiques et physiques, exécutez la commande lscpu --extended qui affichera les ID de cœurs logique dans la colonne CPU et les ID de cœurs physiques dans la colonne CORE.

Lorsque la commande nproc ou ninja est exécutée dans un cgroup, elle utilisera le nombre de cœurs logiques assignés au cgroup comme « nombre de cœurs logiques du système ». Par exemple, dans un cgroup avec les cœurs logiques 0-3 assignés, nproc affichera 4 et ninja exécutera 6 (4 + 2) tâche simultanées sans option -j explicite.

Consultez les pages de manuel systemd-run(1) et systemd.resource-control(5) pour des explications détaillées des paramètre de la commande.

Procédures de construction automatique

Automatiser la construction d'un paquet peut parfois s'avérer utile. On a tous des raisons différentes pour automatiser la construction, et on le fait par nos propres moyens. Soit en créant des Makefiles, des scripts Bash, des scripts Perl ou simplement une liste de commandes utilisées à copier-coller. Ce sont toutes des méthodes que vous pouvez utiliser pour automatiser la construction de paquets BLFS. Détailler et donner des exemples sur les nombreuses manières d'automatiser la construction de paquets va au-delà des objectifs de cette section. Cette section vous présentera l'utilisation de la redirection de fichiers et de la commande yes pour vous donner des idées sur la façon d'automatiser vos constructions.

Redirection de fichier pour automatiser l'entrée

Il y aura des moments, tout au long de votre aventure BLFS, où vous tomberez sur un paquet ayant une invite de commande vous demandant des informations. Ces informations peuvent être des détails de configuration, un chemin de répertoire ou une réponse à un accord de licence. Cela peut être un challenge pour automatiser la construction de ce paquet. On vous demandera occasionnellement différentes informations via une série de questions. Une méthode pour automatiser ce type de scénario est de mettre les réponses désirées dans un fichier et d'utiliser la redirection pour que le programme utilise les données du fichier comme réponses aux questions.

Cela fait logiquement utilise les réponses du fichier comme entrée pour les questions de la suite de tests. Vous aurez parfois à faire des séries d'essais et erreurs pour déterminer le format exact de votre fichier d'entrée pour certaines choses, mais une fois expérimenté et documenté, vous pouvez utiliser cela pour automatiser la construction du paquet.

Utiliser yes pour automatiser l'entrée

Vous n'aurez parfois besoin de ne fournir qu'une réponse ou alors la même réponse à de nombreuses invites. Dans ces cas-là, la commande yes fonctionne vraiment bien. On peut utiliser la commande yes pour fournir une réponse (la même) à une ou plusieurs questions. On peut l'utiliser pour simuler un simple appui sur la touche Entrée, l'entrée de la touche Y ou l'entrée d'une chaîne de texte. La manière la plus facile de montrer son utilisation est peut-être de prendre un exemple.

Créez tout d'abord un petit script Bash en entrant les commandes suivantes :

cat > blfs-yes-test1 << "EOF"
#!/bin/bash

echo -n -e "\n\nPlease type something (or nothing) and press Enter ---> "

read A_STRING

if test "$A_STRING" = ""; then A_STRING="Just the Enter key was pressed"
else A_STRING="You entered '$A_STRING'"
fi

echo -e "\n\n$A_STRING\n\n"
EOF
chmod 755 blfs-yes-test1

Maintenant, lancez le script en exécutant ./blfs-yes-test1 depuis la ligne de commande. Il attendra une réponse, qui peut être n'importe quoi (ou rien) suivi de la touche Entrée. Après avoir entré quelque chose, le résultat sera affiché à l'écran. Utilisez maintenant la commande yes pour automatiser l'entrée d'une réponse :

yes | ./blfs-yes-test1

Remarquez que la redirection (le piping) de yes en lui-même vers le script aboutit à ce que y est passé au script. Essayez-la maintenant avec une chaîne de texte :

yes 'This is some text' | ./blfs-yes-test1

La chaîne exacte était utilisée comme réponse au script. Enfin, essayez-la en utilisant une chaîne vide (null) :

yes '' | ./blfs-yes-test1

Remarquez que cela aboutit à ne passer au script que l'appui sur la touche Entrée. C'est utile parfois quand la réponse par défaut à l'invite est suffisante. Cette syntaxe est utilisée dans les instructions de Net-tools pour accepter tous les réglages par défaut à toutes les invites lors de l'étape de configuration. Vous pouvez maintenant supprimer le script de test si vous le désirez.

Redirection de fichiers pour automatiser la sortie

Pour automatiser la construction de certains paquets, surtout ceux qui vous demandent de lire un accord de licence page après page, il faut utiliser une méthode qui évite de devoir appuyer sur une touche pour afficher chaque page. On peut utiliser la redirection de sortie vers un fichier dans ce cas-là pour vous aider à automatiser. La section précédente de cette page a visé à créer des fichiers journaux de la sortie de la construction. La méthode de redirection qui y est décrite utilisait la commande tee pour rediriger la sortie tout en affichant aussi la sortie à l'écran. Ici on ne verra la sortie que dans un fichier.

De nouveau, la manière la plus facile de montrer la technique est de présenter un exemple. Lancez d'abord la commande :

ls -l /usr/bin | less

Bien entendu, vous devrez voir la sortie page par page car on a utilisé le filtre less. Essayez maintenant la même commande, mais en redirigeant cette fois la sortie vers un fichier. Le fichier spécial /dev/null peut être utilisé à la place du fichier indiqué, mais vous n'aurez pas de fichier journal à examiner :

ls -l /usr/bin | less > redirect_test.log 2>&1

Vous remarquerez que cette fois, la commande est immédiatement revenue à l'invite du shell sans devoir parcourir la sortie page par page. Vous pouvez maintenant supprimer le fichier journal.

Le dernier exemple utilisera la commande yes associée à la redirection de sortie pour éviter de naviguer page par page dans la sortie, puis de fournir un y à l'invite. Cette technique peut être utilisée dans les cas où vous devriez, sans elle, naviguer page par page dans la sortie d'un fichier (tel qu'un accord de licence), puis répondre à la question Acceptez-vous ce qui précède ?. Pour cet exemple, on a besoin d'un autre petit script Bash :

cat > blfs-yes-test2 << "EOF"
#!/bin/bash

ls -l /usr/bin | less

echo -n -e "\n\nDid you enjoy reading this? (y,n) "

read A_STRING

if test "$A_STRING" = "y"; then A_STRING="You entered the 'y' key"
else A_STRING="You did NOT enter the 'y' key"
fi

echo -e "\n\n$A_STRING\n\n"
EOF
chmod 755 blfs-yes-test2

On peut utiliser ce script pour simuler un programme qui demande que vous lisiez un accord de licence et que vous acceptiez le contrat avant que le programme n'installe quoique ce soit. Lancez d'abord le script sans techniques d'automatisation en exécutant ./blfs-yes-test2.

Maintenant lancez la commande suivante qui utilise deux techniques d'automatisation, rendant l'utilisation convenable dans un script de construction automatisé :

yes | ./blfs-yes-test2 > blfs-yes-test2.log 2>&1

Si vous le désirez, lancez tail blfs-yes-test2.log pour voir la fin de la sortie paginée et la confirmation que y a été passé au script. Une fois que cela marche comme cela devrait, vous pouvez supprimer le script et le fichier journal.

Enfin, gardez à l'esprit qu'il y a de nombreux moyens d'automatiser ou de scripter les commandes de construction. Il n'y a pas « une seule » manière de procéder. Votre imagination est la seule limite.

Dépendances

Pour chaque paquet décrit, BLFS liste les dépendances connues. Elles sont listées sous plusieurs en-têtes, dont la signification est la suivante :

  • Requis signifie que le paquet cible ne peut pas se construire correctement sans avoir d'abord installé la dépendance, sauf si la dépendance est dite « à l'exécution », ce qui signifie que le paquet cible peut être construit mais ne peut pas fonctionner sans elle.

    Remarquez qu'un paquet cible peut commencer à « fonctionner » de plusieurs manières subtiles : un fichier de configuration installé peut le faire exécuter au système d'initialisation, au démon cron ou au démon de bus automatiquement. Ou bien un autre paquet qui utilise le paquet cible en dépendance peut exécuter le programme du paquet cible dans son système de construction. Ou encore, les sections de configuration du livre BLFS peuvent également exécuter un problème du paquet tout juste installé. Si vous installez le paquet cible sans un dépendance Requise (à l'exécution), vous devriez installer la dépendance aussi vite que possible après l'installation du paquet cible.

  • Recommandées signifie que BLFS suggère fortement d'installer préalablement ce paquet (sauf si elle est dite « à l'exécution », voir plus bas) pour une construction propre et sans problème, ni pendant le processus de construction ni au moment de l'exécution. Les instructions dans le livre considèrent que ces paquets sont installés. Des modifications ou contournements peuvent être requis si ces paquets ne sont pas installés. Si une dépendance recommandée est dite « à l'exécution », cela signifie que BLFS suggère fortement d'installer cette dépendance avant d'utiliser le paquet, pour avoir toutes les fonctionnalités.

  • Facultatives signifie que ce paquet peut être installé pour ajouter une fonctionnalité. Souvent BLFS décrira la dépendance pour expliquer la fonctionnalité ajoutée. Certaines dépendances facultatives peuvent être automatiquement trouvées par le paquet cible si la dépendance est installée, alors que d'autres dépendances facultatives nécessitent des options de configuration supplémentaires pour être activées à la construction du paquet cible. Ces options seront documentées dans le livre BLFS. Si un dépendance facultative est dite « à l'exécution », cela signifie que vous pouvez installer la dépendance après l'installation du paquet cible pour prendre en charge des fonctionnalités facultatives du paquet cible si vous en avez besoin.

    Une dépendance facultative peut se trouver en dehors de BLFS. Si vous avez besoin d'une dépendance facultative externe pour des fonctionnalités, consultez Aller au-delà de BLFS pour trouver l'aide générique sur l'installation des paquets en dehors de BLFS.

Utilisation de paquets sources les plus récents

Il peut arriver occasionnellement qu'un paquet ne se construise pas ou ne fonctionne pas correctement dans le livre. Bien que les éditeurs tentent de faire en sorte que chaque paquet dans le livre se construise et fonctionne correctement, il arrive parfois qu'un paquet ait été oublié ou n'ait pas été testé avec cette version particulière de BLFS.

Si vous découvrez un paquet qui ne se construit pas ou ne fonctionne pas correctement, vous pouvez regarder s'il s'agit de la version la plus récente du paquet. En général, cela signifie que vous devez vous rendre sur le site web du responsable, télécharger l'archive la plus récente et tenter de construire le paquet. Si vous ne pouvez pas déterminer le site web du responsable en regardant l'URL de chargement, utilisez Google et cherchez le nom du paquet. Par exemple, dans la barre de recherche de Google tapez : « nom_du_paquet download » (sans les guillemets) ou quelque chose de similaire. Parfois en tapant : « nom_du_paquet home page » vous trouverez le site web du responsable.

Nettoyage une fois de plus

Dans LFS, le nettoyage des symboles de débogage et des entrées de la table des symboles inutiles a été abordé plusieurs fois. Pour la construction des paquets BLFS, il n'y a généralement pas d'instructions qui abordent de nouveau le nettoyage. Le nettoyage peut avoir lieu après l'installation d'un paquet, ou plus tard.

Nettoyage à l'installation d'un paquet

Il y a plusieurs manières de nettoyer les exécutables installés par un paquet. Elles dépendent du système de construction utilisé (voir plus bas la section sur les systèmes de construction), c'est pourquoi nous ne listons que quelques généralités ici :

[Note]

Note

Les méthodes suivantes qui utilisent la fonctionnalité d'un système de construction (autotools, meson ou cmake) ne nettoieront pas les bibliothèques statiques si elles sont installées. Heureusement il n'y a pas tant de bibliothèques statiques dans BLFS et une bibliothèque statique peut toujours être nettoyée sans problème en exécutant strip --strip-unneeded dessus manuellement.

  • Les paquets qui utilisent les autotools ont habituellement une cible install-strip dans leur fichier Makefile généré. Donc installer des exécutables nettoyer est aussi simple qu'utiliser make install-strip au lieu de make install.

  • Les paquets qui utilisent le système de construction meson acceptent -D strip=true au lancement de meson. Si vous avez oublié d'ajouter cette option en lançant meson, vous pouvez aussi lancer meson install --strip au lieu de ninja install.

  • cmake génère des cibles install/strip aussi bien pour le générateur de Makefiles Unix que pour le générateur Ninja (par défaut c'est le générateur de Makefiles Unix qui sera utilisé sur linux). Lancez simplement make install/strip ou ninja install/strip au lieu de l'équivalent avec install.

  • Supprimer (ou éviter de générer) les symboles de débogage peut aussi se faire en supprimant les options -g<quelque chose> dans les appels au compilateur C/C++. La manière de procéder est spécifique à chaque paquet. En plus, cela ne supprime pas les entrées inutiles de la table des symboles. Nous ne l'expliquons donc pas ici. Voir plus bas les paragraphes sur l'optimisation.

Nettoyer les exécutables installés

L'utilitaire strip modifie les fichiers en place, ce qui peut casser tout ce qui l'utilise s'il est chargé en mémoire. Remarquez que si un fichier est utilisé mais simplement supprimé du disque (c.-à-d. pas écrasé ou modifié), ce n'est pas un problème car le noyau peut utiliser des fichiers « supprimés ». Regardez dans /proc/*/maps, et il est probable que vous verrez des entrées (deleted). La commande mv supprime seulement le fichier de destination du répertoire mais ne change pas son contenu, donc elle satisfait aux conditions pour que le noyau utilise l'ancien fichier supprimé. Mais cette approche peut détacher des liens en dur et créer des copies dupliquées, causant une plus forte utilisation du disque, ce qui n'est évidemment pas souhaitable quand nous nettoyons pour réduire la taille du système. Si deux fichiers du même système de fichiers partage le même numéro d'inœud, ils sont liés en dur et nous devrions reconstruire le lien. Le script ci-dessous est juste un exemple. Il devrait être lancé en root :

cat > /usr/sbin/strip-all.sh << "EOF"
#!/usr/bin/bash

if [ $EUID -ne 0 ]; then
  echo "Need to be root"
  exit 1
fi

last_fs_inode=
last_file=

{ find /usr/lib -type f -name '*.so*' ! -name '*dbg'
  find /usr/lib -type f -name '*.a'
  find /usr/{bin,sbin,libexec} -type f
} | xargs stat -c '%m %i %n' | sort | while read fs inode file; do
       if ! readelf -h $file >/dev/null 2>&1; then continue; fi
       if file $file | grep --quiet --invert-match 'not stripped'; then continue; fi

       if [ "$fs $inode" = "$last_fs_inode" ]; then
         ln -f $last_file $file;
         continue;
       fi

       cp --preserve $file    ${file}.tmp
       strip --strip-unneeded ${file}.tmp
       mv ${file}.tmp $file

       last_fs_inode="$fs $inode"
       last_file=$file
done
EOF
chmod 744 /usr/sbin/strip-all.sh

Si vous installez des programmes dans d'autres répertoires tels que /opt ou /usr/local, vous pouvez vouloir nettoyer les fichiers ici aussi. Ajoutez simplement d'autres répertoires à scanner dans la liste des commandes find entre accolades.

Pour plus d'information sur le nettoyage, regardez https://www.technovelty.org/linux/stripping-shared-libraries.html.

Travailler avec différents systèmes de construction

Il y a maintenant trois systèmes de construction différents utilisés régulièrement pour convertir du code source C ou C++ en un programme compilé ou en une bibliothèque et leur fonctionnement (en particulier, comment trouver les options disponibles et leurs valeurs par défaut) est différent. Il peut être plus facile de comprendre les problèmes causés par certains choix (typiquement une exécution lente ou l'utilisation inattendue, ou l'omission, des optimisations en commençant par les variables d'environnement CFLAGS, CXXFLAGS et LDFLAGS. Certains programmes utilisent aussi Rust.

La plupart des lecteurs de LFS et de BLFS connaissent probablement déjà les bases de l'utilisation de CFLAGS et CXXFLAGS pour altérer la compilation d'un programme. En général, certaines formes d'optimisations sont utilisées par les développeurs en amont (-O2 ou -O3), parfois avec la création de symboles de débogage (-g) par défaut.

S'il y a des drapeaux contradictoires (p. ex. plusieurs valeurs différentes de -O), la dernière valeur sera utilisée. Parfois les drapeaux spécifiés dans les variables d'environnement sont insérés avant les valeurs en dur dans le Makefile, ce qui signifie qu'ils sont ignorés. Par exemple, si un utilisateur spécifie -O2et qu'il est suivi de -O3, la construction utilisera -O3.

On peut passer divers autres drapeaux dans CFLAGS et CXXFLAGS, comme permettre d'utiliser les extensions des jeux d'instruction disponibles pour une microarchitecture spécifique (p. ex. -march=amdfam10 ou -march=native), optimiser le code généré pour une microarchitecture donnée (p. ex. -mtune=tigerlake ou -mtune=native, si -mtune= n'est pas utilisé, la microarchitecture du paramètre -march= sera utilisé) ou pour spécifier un standard C ou C++ particulier (-std=c++17 par exemple). Mais ce qui commence à apparaître, c'est l'inclusion par les développeurs d'assertions de débogage dans leur code qui sont généralement désactivés dans les versions publiées avec -D NDEBUG. Plus spécifiquement, si Mesa-24.1.5 est construit avec ces assertions, certaines activités comme le chargement de niveaux dans les jeux peuvent prendre très longtemps, même sur des cartes vidéo haut de gamme.

Autotools avec Make

Cette combinaison est souvent décrite comme « CMMI » (configure, make, make install) et est utilisée ici pour couvrir aussi certains paquets dont le script configure n'a pas été généré par les autotools.

Parfois lancer ./configure --help affichera les options utiles des paramètres qui peuvent être utilisés. D'autres fois, après avoir regardé la sortie de configure vous pourriez avoir besoin de regarder les détails du script pour comprendre ce qu'il cherchait vraiment.

De nombreux scripts configure récupéreront les CFLAGS et CXXFLAGS de l'environnement, mais les paquets CMMI varient dans la manière dont ils seront insérés avec les drapeaux qui sinon seraient utilisés (au choix : ignorés, utilisés à la place de la suggestion des programmeurs, utilisés avant la suggestion des programmeurs ou utilisés après la suggestion des programmeurs).

Dans la plupart des paquets CMMI, make listera les commandes lancées, séparées par des avertissements éventuels. Mais certains paquets essayent d'être « silencieux » et ne montrent que les fichiers qu'ils compilent ou dont ils éditent les liens au lieu de montrer la ligne de commande. Si vous devez inspecter la commande, soit à cause d'une erreur, ou juste pour voir les options et les drapeaux utilisés, ajouterV=1 à l'invocation make peut aider.

CMake

CMake fonctionne de manière différente et il a deux moteurs qui peuvent être utilisés sur BLFS : make et ninja. Le moteur par défaut est make, mais ninja est plus rapide sur les paquets plus gros avec plusieurs processeurs. Pour utiliser ninja, spécifiez -G Ninja dans la commande cmake. Cependant, certains paquets peuvent avoir des erreurs fatales dans leurs fichiers ninja tout en pouvant être construits sans problème avec les Makefile Unix par défaut.

La partie la plus dure de l'utilisation de CMake est de connaître les options que vous voudrez spécifier. La seule manière de récupérer une liste de ce que le paquet connaît est de lancer cmake -LAH et de regarder la sortie pour cette configuration par défaut.

Peut-être que la chose la plus importante à propos de CMake est qu'il a plusieurs valeurs de CMAKE_BUILD_TYPE et qu'elles affectent les drapeaux. La valeur par défaut est vide et aucun drapeau n'est généré. Les CFLAGS et CXXFLAGS dans l'environnement seront utilisés. Si le programmeur a codé des assertions de débogage, elles seront activées à moins que vous n'utilisiez -D NDEBUG. Les valeurs CMAKE_BUILD_TYPE suivantes généreront les drapeaux associés et ils seront ajoutés après les drapeaux dans l'environnement et prennent donc le pas dessus.

Value Flags
Debug -g
Release -O3 -D NDEBUG
RelWithDebInfo -O2 -g -D NDEBUG
MinSizeRel -Os -D NDEBUG

CMake essaye de produire des constructions silencieuses. Pour voir les détails des commandes lancées, utilisez make VERBOSE=1 ou ninja -v.

Par défaut, CMake traite l'installation des fichiers différemment des autres systèmes de construction : si un fichier existe déjà et n'est pas plus récent qu'un fichier qui le remplacerait, alors le fichier n'est pas installé. cela peut être un problème si vous voulez enregistrer quels fichiers appartiennent à quel paquet, soit avec LD_PRELOAD, soit en listant les fichiers plus récents qu'un certain horodatage. Ce comportement par défaut peut être modifié en paramétrant la variable CMAKE_INSTALL_ALWAYS à 1 dans l'environnement, par exemple en l'exportant.

Meson

Meson ressemble un peu à CMake, mais avec beaucoup de différences. Pour trouver les détails des définitions que vous pourriez avoir besoin de modifier, vous pouvez regarder dans meson_options.txt qui est généralement dans le répertoire de plus haut niveau.

Si vous avez déjà configuré le paquet en lançant meson et que vous souhaitez maintenant changer un ou plusieurs paramètres, vous pouvez soit supprimer le répertoire de construction, le recréer et utiliser les options modifiées, soit lancer meson configure dans le répertoire de construction, p. ex. pour ajouter une option :

meson configure -D <some_option>=true

Si vous faites cela, le fichier meson-private/cmd_line.txt contiendra les dernières commandes qui ont été utilisées.

Meson fournit les valeurs buildtype suivantes, et les drapeaux qu'elles activent viennent après les drapeaux fournis dans l'environnement et prennent donc le pas.

  • plain : pas de drapeau supplémentaire. Cela est utilisé pour spécifier ses propres CFLAGS, CXXFLAGS et LDFLAGS. Il n'y a pas de raison évidente pour utiliser cela dans BLFS.

  • debug : -g — c'est la valeur par défaut si rien n'est spécifié dans meson.build ni sur la ligne de commande. Cependant, cela crée des binaires gros et lents, donc vous devez le remplacer dans BLFS.

  • debugoptimized : -O2 -g : c'est la valeur par défaut spécifiée dans meson.build pour certains paquets.

  • release : -O3 (parfois un paquet forcera -O2) — c'est le type de construction utilisé pour la plupart des paquets avec le système de construction Meson dans BLFS.

Le drapeau -D NDEBUG est sous-entendu par le type de construction release pour certains paquets (par exemple Mesa-24.1.5). Il peut également être fournit en passant -D b_ndebug=true.

Pour voir les détails des commandes qui sont lancées dans un paquet qui utilise meson, utilisez ninja -v.

Rustc et Cargo

La plupart des programmes rustc publiés sont fournis dans des archives (ou « crates ») qui demanderont à un serveur de vérifier les versions actuelles des dépendances et les téléchargera au besoin. Ces paquets sont construits avec cargo --release. En théorie, on peut manipuler les RUSTFLAGS pour changer le niveau d'optimisation (la valeur par défaut pour --release est 3, c.-à-d. -Copt-level=3, comme -O3) ou pour forcer la construction pour la machine sur laquelle il est compilé, avec -Ctarget-cpu=native mais en pratique cela ne semble pas faire de réelle différence.

Si vous compilez un programme Rust autonome (en tant que fichier .rs sans paquet) en exécutant directement rustc, vous devriez spécifier -O (abbreviation de -Copt-level=2) ou -Copt-level=3 sinon il effectuera une compilation non optimisée et s'exécutera bien plus lentement. Si vous compilez un programme pour le déboguer, remplacez les options -O ou -Copt-level= par -g pour produire un programme non optimisé avec des informations de débogage.

Comme pour ninja, par défaut cargo utilise tous les cœurs logiques. Cela peut souvent être changé, soit en exportant CARGO_BUILD_JOBS=<N> ou en passant --jobs <N> à cargo. Pour compiler rustc lui-même, spécifiez --jobs <N> lors de l'invocation de x.py (avec la variable d'environnement CARGO_BUILD_JOBS, ce qui ressemble à une approche « ceinture et bretelle » mais a l'air nécessaire). L'exception est le lancement des tests à la construction de rustc, où certains d'entre eux utiliseront tout de même tous les CPU en ligne, au moins à partir de rustc-1.42.0.

Optimisation de la construction

De nombreuses personnes préfèrent optimiser la compilation à leur goût, en fournissant CFLAGS ou CXXFLAGS. Vous trouverez une introduction aux options disponibles avec gcc et g++ sur https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Optimize-Options.html. Ce contenu est également disponible dans info gcc.

Certains paquets utilisent par défaut-O2 -g, d'autres -O3 -g et si les CFLAGS ou CXXFLAGS sont fournis ils peuvent être ajoutés aux valeurs par défaut du paquet, remplacer les valeurs par défaut ou même être ignorés. Il y a des détails sur les dernières versions (en avril 2019) de certains paquets graphiques sur https://www.linuxfromscratch.org/~ken/tuning/ — en particulier, README.txt, tuning-1-packages-and-notes.txt et tuning-notes-2B.txt. Ce dont il faut se souvenir, c'est que si vous voulez essayer certains des drapeaux les plus intéressants vous devez forcer les constructions à être plus verbeuses pour confirmer qu'elles sont utilisées.

Clairement, si vous optimisez votre propre programme vous pouvez passer du temps à effectuer des mesures et peut-être recoder certaines parties plutôt lentes. Mais pour construire un système complet cette approche est impossible. En général, -O3 produit des programmes plus rapides que -O2. Spécifier -march=native est aussi bénéfique, mais cela signifie que vous ne pouvez pas utiliser les binaires sur une machine incompatible — cela s'applique aussi à des machines plus récentes, pas seulement à des machines plus vieilles. Par exemple les programmes compilés pour amdfam10 peuvent tourner sur les vieux Phenoms, Kaveris et Ryzens ; mais les programmes compilés pour un Kaveri ne tourneront pas sur un Ryzen parce que certains opcodes ne sont pas présents. De même, si vous construisez pour un Haswell tout ne tournera pas sur un SandyBridge.

[Note]

Note

Soyez conscient que le nom d'un paramètre -march ne correspond pas toujours à la microarchitecture de base du même nom. Par exemple, les processeurs Celeron d'Intel basés sur Skylake ne prennent pas en charge AVX du tout, mais -march=skylake suppose AVX et même AVX2.

Lorsqu'une bibliothèque partagée est construite par GCC, une fonctionnalité nommée « interposition sémantique » est activée par défaut. Lorsque la bibliothèque partagée se réfère à un nom de symbole avec un lien externe et la visibilité par défaut, si le symbole existe à la fois dans la bibliothèque partagée et l'exécutable principal, l'interposition sémantique garanti que le symbole de l'exécutable principal sera toujours utilisé. Cette fonctionnalité a été inventée pour essayer de rendre la liaison d'une bibliothèque partagée et d'une bibliothèque statique aussi proches que possible. Aujourd'hui seul un petit nombre de paquets dépendent toujours de l'interposition sémantique, mais la fonctionnalité est toujours proposée par défaut par GCC, ce qui désactive plusieurs optimisations pour les bibliothèques partagées car elles entrent en conflit avec l'interposition sémantique. L'option -fno-semantic-interposition peut être passée à gcc ou g++ pour désactiver l'interposition sémantique et activer plus d'optimisations pour les bibliothèques partagées. Cette option est utilisée par défaut dans certains paquets (par exemple Python-3.12.5) et elle est activée par défaut dans Clang.

Il y a aussi diverses autres options que certains pensent bénéfiques. Au pire, vous devez recompiler et tester, pour découvrir que pour votre utilisation ces options ne font rien de spécial.

Si vous construisez des modules Perl ou Python, en général les CFLAGS et CXXFLAGS utilisés sont ceux utilisés pour construire ces paquets « parents ».

Pour LDFLAGS, il y a trois options pour l'optimisation. Elles sont relativement sures à utiliser et le système de construction de certains paquets utilisent certaines de ces options par défaut.

Avec -Wl,-O1, l'éditeur des liens optimisera la table de hash pour accélérer la liaison dynamique. Remarquez que -Zl,-O1 est complètement décorrélée du drapeau d'optimisation du compilateur -O1.

Avec -Wl,--as-needed, l'éditeur des liens ignorera les options -ltoto inutiles de la ligne de commande, c.-à-d. que la bibliothèque partagée libtoto ne sera liée que si un symbole de libtoto est vraiment demandé par l'exécutable ou la bibliothèque dynamique en train d'être liée. Cela peut parfois éviter des problèmes de « dépendances excessives à des bibliothèques partagées » causés par libtool.

Avec -Wl,-z,-pack-relative-relocs, l'éditeur des liens générera une forme plus compacte des entrées de relocations relatives pour les exécutables repositionnables et les bibliothèques partagées. Cela réduit la taille des exécutables ou des bibliothèques partagées et accélère leur chargement.

Le préfixe -Wl, est nécessaire car malgré le nom de la variable LDFLAGS, son contenu est en fait passé à gcc (ou g++, clang, etc) pendant l'édition des liens, pas directement à ld.

Options pour durcir la construction

Même sur un système de bureau, il y a des vulnérabilités exploitables. Pour beaucoup, l'attaque vient de javascript dans un navigateur. Souvent, une série de vulnérabilités sont utilisées pour récupérer un accès aux données (ou parfois pour powner, c.-à-d. cracker la machine et installer des rootkits). La plupart des distributions commerciales appliqueront diverses mesures de durcissement.

Par le passé, il existait un Hardened LFS (LFS durci) où gcc (une version beaucoup plus vieille) était forcé à utiliser le durcissement (avec des options pour les désactiver au cas par cas). Les livres LFS et BLFS actuels portent encore une partie de cet esprit en activant PIE (-fPIE -pie) et SSP (-fstack-protector-strong) dans les options par défaut de GCC et de clang. De plus, les éditeurs des liens (ld.bfd et ld.gold) ont aussi activé -Wl,-z,relro ce qui rend une partie de la table globale de décalage (GOT) immuable par défaut depuis Binutils 2.27. Ce dont on parle ici est différent — vous devrez déjà vous assurer que le paquet utilise bien vos options supplémentaires et qu'il ne les écrase pas.

Pour les options de durcissement qui sont raisonnablement peu coûteuses, on en parle un peu dans le lien sur les expériences de peaufinage précédent (parfois une ou plus de ces options peuvent être inappropriées pour un paquet). Ces options sont -D _FORTIFY_SOURCE=2 (ou -D _FORTIFY_SOURCE=3 qui est plus sécurisé mais avec un coût additionnel plus élevé) et (pour C++) -D _GLIBCXX_ASSERTIONS. Sur les machines modernes elles ne devraient avoir qu'un très faible impact sur la vitesse des programmes et souvent ne seront même pas perceptibles.

Les distributions majeures en utilisent bien plus, comme :

  • -Wl,-z,now : désactive les liaisons paresseuses pour améliorer -Wl,-z,relro, ce qui rend toute la GOT immuable.

  • -fstack-clash-protection : évite qu'un attaquant utilise un décalage suffisamment grand et mal vérifié pour sauter par-dessus la page de garde de la pile placée par le noyau et le canari de pile placé par -fstack-protector=strong, et ainsi modifier la pile à partir d'une adresse de tas, et inversement.

  • -ftrivial-auto-var-init=zero : initialise certaines variables en les remplissant à zéro si elles ne sont pas initialisées autrement.

  • -fcf-protection=full : utilise la technologie CET d'Intel et AMD pour limiter les adresses cibles des instructions de transfert de flux de contrôle. Pour les activer pour un paquet, tous les paquets qui fournissent une bibliothèque partagée utilisée par le paquet doivent être construites avec cette option, ainsi que le paquet lui-même, Glibc doit être configuré avec l'option --enable-cet et le système doit s'exécuter sur Intel Tiger Lake ou plus récent, ou AMD Zen 3 ou plus récent. Si les critères ne sont pas tous remplis le programme compilé avec ces options tournera toujours, mais il ne sera pas vraiment protégé par CET.

Avec GCC 14, l'option -fhardened est un raccourci pour activer toutes les options de durcissement mentionnées ci-dessus. Elle indique -D _FORTIFY_SOURCE=3 au lieu de -D _FORTIFY_SOURCE=2.

Vous pouvez aussi rencontrer les « retpolines en espace utilisateur » (-mindirect-branch=thunk etc) qui sont équivalents aux atténuations de spectre appliquées au noyau Linux fin 2018. Les atténuations du noyau ont causé de nombreuses plaintes à propos d'une perte de performance, donc si vous avez un serveur en production vous devriez peut-être tester ça ainsi que d'autres options disponibles, pour voir si les performances sont toujours satisfaisantes.

Tandis que gcc a de nombreuses options de durcissement, la force de clang/LLVM se trouve ailleurs. Certaines options fournies par gcc sont dites moins efficaces dans clang/LLVM.