Le livre LFS a un court, mais beau chapitre, à propos des erreurs. Une plus longue dissertation sur la façon de localiser l'erreur, comment la décrire (sur IRC ou la liste de diffusion), et peut-être la contourner est le but de cette astuce.
Presque tous les adeptes de LFS ont vus des lignes comme :
- make[1]: Error
- Segmentation Fault
- ld returned signal 2: …
Le premier réflexe est d’écrire sur la liste de diffusion ou sur IRC quelque chose comme :
J’ai une erreur dans le programme <remplacer par la mention appropriée> !
Première chose, est ce vraiment une erreur ? Si vous trouver l’option -Werror
dans les lignes qui appellent gcc, l’“erreur” à laquelle vous êtes confronté
peut aussi bien être un avertissement (-Werror stipule à gcc de traiter tous les
avertissements comme des erreurs). Vous rencontrerez souvent des messages
d’avertissement et d’erreurs mélangés avant le classique make[1]: Error
. Un
avertissement est quelque chose dont gcc se plaint, mais il continue sans
erreur, alors qu’une erreur est quelque chose qui arrête la compilation du
paquet que vous êtes en train de construire. Pour désactiver les messages
d’avertissement distrayant, utilisez export CFLAGS="-w"
.
Généralement, il n’y a jamais assez d’informations sur les erreurs, ce qui est nuisible pour celui qui demande et pour celui qui tente de répondre, en raison du dialogue ennuyeux qui va suivre.
Je dois admettre que la liste de diffusion LFS et IRC n’échouent jamais à résoudre mes problèmes (et dans une bonne ambiance ), mais j’en suis arrivé à un point où je voulais résoudre autant de mes problèmes que possible. J’ai donc dû apprendre beaucoup de choses, ce qui était sans aucun doute plaisant.
Quelle sorte d’erreur ?
Vous devez savoir distinguer les différentes sortes d’erreurs. Plus vous pouvez en apprendre à propos de l’erreur, plus vous pourrez la résoudre facilement.
Cela devrait être une astuce normale, mais je pense qu’il est plus facile de dessiner un schéma :
Question :
- Quand est-ce arrivé ?
- Qu’est-il arrivé ?
- Où est-ce arrivé ?
, Compilation (gcc) ...
, ... introuvable ---<- Dépendances (depmod)
Erreur de compilation < -, ` Liaison (ld)
/ `. `- gcc-3.4.x*
/ \
Erreur < Erreur de segmentation
`. ,'
Erreur d'exécution ----< , complet
` plantage <
` programme seulement
- gcc-3.4 et ses successeurs, n’acceptent ni les étiquettes à la fin des instructions composées ni l’utilisation de fonctions protégées à l’intérieur d’autres fichiers.
Cela semble vraiment simple, non ? Mais c’est seulement le début. Nous allons examiner de plus près chacun de ces types d’erreur !
1 Erreurs de compilation
En premier, vérifiez si le paquet que vous êtes en train de compiler contient des fichiers comme README et/ou INSTALL. Vous pouvez contourner la plupart des erreurs en suivant strictement leurs instructions.
Durant la construction de votre paquet, vous rencontrez parfois une erreur qui vous signale que quelque chose est manquant, malformé ou simplement incompilable.
1.1 … introuvable
1.1.1 Compilation (gcc)
Il y a beaucoup de choses que gcc peut ne pas pouvoir trouver. S’il y a quelque chose à inclure, c’est peut être le fichier à inclure qui manque.
Les questions ici sont :
- Que manque-t-il ?
- Que faire contre cela ?
1.1.1.1 Fichier d’entête manquant
S’il manque seulement un fichier d’entête, vous aurez un message d’erreur ressemblant à :
foo.c:3:31: /usr/include/bar.h: No such file or directory
Si un fichier manque, vous pouvez le chercher sur votre système :
find / -name <nom_du_fichier> ou
locate <nom_du_fichier> (lancer updatedb, si locate le demande)
Si vous ne trouvez pas le fichier, la question suivante sera :
- D’où ce fichier devrait-il venir ?
- Est-ce un prérequis que vous avez oublié ?
- Tous les outils sont-ils disponibles dans la version requise ?
Si le fichier est ailleurs que dans le chemin d’inclusion standard
(/usr/include
, /usr/local/include
), vous devez ajouter
-I<chemin_non_standard_pour_include>
dans le CFLAGS, par exemple
export CFLAGS=-I/usr/X11R6/include
. Si l’instruction #include
contient un
sous-répertoire, alors que le fichier à inclure est dans le répertoire standard,
vous devez éditer l’instruction #include
.
Dans la plupart des cas le fichier sera dans un répertoire que le développeur n’attendais pas. La façon la plus simple de contourner cela sera un lien symbolique, mais ce n’est pas une solution propre. Alors nous cherchons dans les sources les occurrences du fichier “oublié” en premier :
grep -R "<chemin et nom du fichier oublié>" *.*
Maintenant, éditez chaque fichier qui utilise le mauvais chemin dans ces
instructions #include
.
L’utilisateur paresseux peut utiliser sed :
for i in *.*; do
mv $i $i.bak
sed s|'<fichier "oublié">'|'<fichier trouvé>'|g $i.bak > $i
done
Cela doit résoudre le problème ; vous pouvez continuer la construction du paquet.
1.1.1.2 Déclaration oubliée
Un message d’erreur agréable vient d’une déclaration oubliée :
foo:124:4: bla undefined
Si “bla” est une fonction des bibliothèques génériques (comme glibc), ce sera probablement documenté avec une page de manuel qui contiendra les informations donnant le ou les fichiers d’entête qui doivent être inclus :
man bla
Regardez /usr/share/man/man3
pour les appels de fonction documentés :
La page de manuel ressemblera à quelque chose comme :
FUNC(3) Linux Programmer's Manual FUNC(3)
NAME
func, ffunc, vfunc - Example function without any use
SYNOPSIS
#include <stdfunc.h>
int func(char *format, ...);
int ffunc(FILE *stream, const char *format, ...);
#include <vstdfunc.h>
int vfunc(const char *format, va list ap);
DESCRIPTION
...
Dans la plupart des cas le fichier d’entête n’est pas inclus là où il est utile,
alors vous devez juste écrire dans le fichier où c’est oublié :
#include <stdfunc.h>
.
Si la définition n’est dans aucune bibliothèque standard, vous devez chercher dans le code du programme que vous êtes en train de compiler la fonction oubliée :
grep "<nom de la fonction>" *.* | less
Maintenant cherchez quelque chose comme #define bla ( const char * ...
. Si
vous ne trouvez rien, la fonction doit probablement être incluse dans d’autres
sources, alors vous devriez revérifier les exigences du paquet que vous êtes en
train de compiler, dans le cas ou quelque chose vous a échappé.
Si le fichier ou la définition est incluse est un fichier d’entête (*.h), il suffit de l’inclure, autrement copiez et collez la définition dans le fichier dont gcc se plaint.
Parfois ce n’est pas une définition, mais un argument oublié pour une fonction.
Le dernier exemple vécu de ce gizmo était une erreur due à quelques changements
d’API dans le pilote alsa-1.0-pre ; Quand on compilait n’importe quel paquets
qui utilisait au moins une fois les fonctions snd_pcm_hw_param
(par exemple,
mplayer ou wine), l’erreur liée était affichée comme ci-dessous :
audio.c: In function `Alsa_TraceParameters':
audio.c:292: error: too few arguments to function `snd_pcm_hw_params_get_format'
(...)
Dans ce cas vous devez connaître les arguments attendus par la fonction.
Donc, vous cherchez le fichier d’entête qui défini la fonction (comme expliqué à
propos des fonctions oubliées). Pour notre exemple alsa, la ligne dans le
fichier d’entête était dans /usr/include/alsa/pcm.h
et ressemblait à :
int snd_pcm_hw_params_get_format(const snd_pcm_hw_params_t *params,
snd_pcm_format_t *val);
Alors que le code où cette fonction était appelée utilisait seulement :
(...) format = snd_pcm_hw_params_get_format(hw_params);
Il faut noter que seul le premier argument est donné, l’autre argument
snd_pcm_format_t
du type *val
est oublié. Maintenant vous devez savoir
ce qu’est le type *val, pour ensuite pouvoir l’insérer dans audio.c.
1.1.1.3 fonction bla… redéfinie
Une autre erreur presque identique apparaît si quelque chose est défini en
double. Le compilateur n’est pas capable de dire si les deux définitions sont
égales, alors il enverra souvent une erreur dans ce cas. Vous devez chercher les
définitions, vérifier laquelle est valide dans votre cas et encadrer la fonction
“invalide” avec #ifndef <Nom>
et #endif
. Certain voudront simplement effacer
la définition “invalide”, mais si un autre paquet en a besoin, elle sera
absente, alors la solution #ifndef/#endif
est clairement la meilleure.
1.1.2 Édition de lien (ld)
L’édition de lien échoue souvent à cause de bibliothèques manquantes. Soyez
certain que votre /etc/ld.so.conf énumére tout les répertoires où sont les
bibliothèques. Dans le cas où un autre répertoire est nécessaire, utilisez
LDFLAGS : export LDFLAGS=-L/usr/X11R6/lib
pour inclure les bibliothèques
Xfree86. /lib
et /usr/lib
sont toujours inclus par défaut et ne doivent pas
être ajoutés.
Une autre erreur (occasionnelle) peut arriver si les bibliothèques ne sont pas
liées correctement. J’ai vu arriver cela une fois seulement avec un programme
lié à libpng, mais pas à libz, qui est utilisée par libpng, mais qui doit être
liée également. Alors dans le Makefile où j’ai oublié LIBS=-lpng
, j’ai ajouté
aussi LIBS=-lpng -lz
. La plupart du temps la fonction manquante est indiquée ;
vous pouvez essayer de faire un grep dans la bibliothèque (motifs binaires).
1.1.3 Vérification de la dépendance d’un module (depmod)
Une autre erreur qui arrive seulement si le noyau exécuté est différent de celui
avec lesquelles les sources ont été compilée (ce qui peut être le cas quand la
compilation est faite en mode chroot) est l’erreur unresolved dependency in module
.
Pour contourner ce bogue, lancez depmod avec l’option
-F /usr/src/linux/System.map
. Et assurez-vous de compiler les modules avec le
même compilateur que celui utilisé pour le noyau.
1.2 gcc-3.4.x
La version 3.4.x introduit quelques nouvelles erreurs, qui compilait correctement avec une version antérieure du même compilateur. Au lieu de ressortir une ancienne version, il peut être plus facile de les corriger.
1.2.1 gcc-3.4.x : étiquette à la fin des instructions composées
Depuis gcc-3.4.x, les étiquettes à la fin des instructions composées sont traitées comme des erreurs, mais elles sont largement utilisées en dépit de leur impropriété. Sans aucun doute ce problème peut être facilement corrigé, il suffit de remplacer les occurrences de
goto [label];
par
return;
et effacer l’étiquette du code source ou la commenter. En règle général, bannissez les instructions goto de votre code C.
1.2.2 gcc-3.4.x : fonctions protégées
Le message
Error: `foo::bar function(pointer*)' is protected
montre que quelque part dans le code il y a une fonction préfixée par
protected:
Bien que ceci ait un sens, cela arrête la compilation de notre application, alors nous pouvons facilement le commenter :
// protected:
et continuer la compilation.
1.3 Erreur de segmentation
C’est la plus ennuyeuse. Cela signifie qu’une application tente d’obtenir quelque chose depuis un fichier/tunnel/périphérique/variable d’environnement qui n’est pas initialisé et n’a pas d’autre solution s’il est nul qu’un plantage et un arrêt immédiat. Si les informations suivantes ne sont pas suffisantes pour vous, vous pouvez regarder la FAQ de SIG11 disponible à l’adresse http://www.linux-france.org/article/sig11-fr/ mais regardez cette section en premier.
1.3.1 Erreur de segmentation pendant la compilation
Les erreurs de segmentation pendant la compilation sont rares. Vous pouvez seulement avoir un SIG11 si la mémoire est pleine pendant la construction d’un paquet - cela arrivera seulement sur les systèmes avec peu de mémoire. Vous pouvez ajouter un périphérique loop de swap pour augmenter votre mémoire ; cela ralentira la compilation, mais au moins cela fonctionnera sur les périphériques qui ont une mémoire insuffisante :
dd if=/dev/zero of=/tmp/swapspace bs=1M count=128
losetup /dev/loop0 /tmp/swapspace
mkswap /dev/loop0
swapon /dev/loop0
initialisera 128 Mo d’espace de swap (ou mémoire virtuelle). Si cela continue à échouer, augmentez la taille de l’espace disque utilisé (count=256; count=512; count=XXX). Si vous avez terminé la compilation ou voulez augmenter la taille, effacez l’espace de swap ajouté avec :
swapoff /dev/loop0
losetup -d /dev/loop0
rm /tmp/swapspace
1.3.2 Erreur de segmentation pendant l’exécution
Si un programme a une erreur de segmentation, il n’y a rien que vous puissiez
facilement faire pour éliminer l’erreur sans que vous ayez des connaissances en
programmation. Contacter le développeur et donnez lui une vue détaillée de votre
système ; peut-être que dans /var/log
il y a quelque chose à propos de
l’erreur ? Si vous voulez éliminer le bogue par vous même, lisez la FAQ de SIG11
et utilisez strace que vous pouvez trouver à l’adresse
http://www.liacs.nl/~wichert/strace et
qui est facile à installer dans le
programme ; cela pourra vous aider à trouver quel fichier/tunnel/chaîne
d’environnement/etc le programme attend. Ensuite essayez de faire un grep sur
les sources du programme qui est en erreur de segmentation après le
fichier/tunnel/etc qui échoue. Ajoutez une routine de secours. Un bel exemple
est gsview-4.4-patch
. Gsview 4.4 essaye d’obtenir la variable d’environnement
LANG, mais n’a pas de solution de secours dans le cas ou elle n’est pas
initialisée. La partie du code source en cause ressemble à
strncpy(lang, getenv("LANG"), sizeof(lang)-1);
… qui aurait copié une partie de la variable d’environnement LANG(age) sans le dernier caractère (Si LANG était vide, il aurait tenterait de copier -1 caractère, ce qui donne une erreur de segmentation). La solution facile serait d’initialiser LANG avec quelque chose, mais la meilleure solution est de fournir une solution de secours en modifiant le code comme suit :
strncpy(lang, (getenv("LANG") == 0) ? "C" : getenv("LANG"),sizeof(lang)-1);
Ce qui est un peu flou pour ceux qui ne connaissent par le C, mais qui signifie “si LANG est 0, alors utiliser ‘C’ à la place de la variable d’environnement LANG (qui est standard), sinon utiliser la variable d’environnement LANG moins un caractère”. Maintenant c’est votre tour, si vous voulez résoudre ce bogue par vous même !
1.4 Plantage
Les plantages sont les plus ennuyeuses erreurs qui y aient. Heureusement avec linux ils sont aussi rare qu’ennuyeux (sauf si vous utilisez les dernières versions des sources). Les plantages sont le plus souvent causés par des boucles sans fin, des problèmes de pilote qui conduisent à un verrouillage du bus, et des problèmes matériels (comme un condensateur défectueux dans l’alimentation du CPU : vérifiez ceux qui sont éclatés). Les boucles infinies sont facilement détectées par la plupart des compilateurs, le dernier est plus difficile à trouver. Essayez de rétrograder le pilote que vous pensez responsable du plantage et envoyez un rapport sur sa liste de diffusion.
1.4.1 Plantage complet
Vous reconnaissez un plantage complet en pressant la touche [CAPS LOCK]. Si la led s’allume, le clavier est toujours lié à la console, donc pas de plantage totale. Sinon, essayez de presser différentes touches. Si rien ne fonctionne, utilisez un redémarrage matériel (ce qui est toujours le dernier recours pour reprendre la main). Si le clavier semble actif, mais que l’écran reste blanc, essayez de redémarrer avec [ALT][CTRL][DEL]. Si même cela ne fonctionne pas, vous pouvez être chanceux et avoir la fonction touche sysrq de compilé dans votre noyau. Pour plus d’informations, lire [/usr/src/linux/Documentation/sysrq.txt].
1.4.2 Plantage d’un programme seulement
Si un programme plante en laissant le reste du système intact, vous pouvez utiliser la commande appropriée dans kill/killall/xkill pour le tuer. Le plantage isolé d’un programme se produit le plus souvent en raison d’une boucle infinie, par exemple en essayant de lire depuis un tunnel bloqué, dans la plupart des cas la charge augmentera visiblement.
1.5 Autres erreurs
Si vous rencontrez un message d’erreur absent de cette astuce, vérifiez les listes de diffusion appropriées, entrez le message d’erreur dans Google et regardez s’il y a une nouvelle version ou si la version cvs) si elle est disponible) a la même erreur. Si rien ne vous aide, demandez sur IRC ou par mail sur la liste de diffusion des développeurs, ou envoyez un rapport de bogue. Pensez à décrire précisément l’erreur et donnez toutes les informations sur le système sur lequel vous êtes en train d’essayer de construire le paquet (logs, versions, sortie de strace, sortie de dmesg, messages de débogage et ainsi de suite).
1.6 Quelques liens utiles
À propos de l’erreur SIG11 (erreur de segmentation) : http://www.linux-france.org/article/sig11-fr/. Cette page a quelques informations générales à propos de l’erreur SIG11.
Acquérir des connaissances en programmation : http://ibiblio.org/obp/thinkCS Cette page présente un livre appelé “How to think like a computer scientist”, qui peut être téléchargée librement dans les variantes Java, C++ et Python. La variante C++ sera la plus utile pour les adeptes de LFS, depuis que la plupart des projets GNU utilisent soit C soit C++.
Que les sources soient avec vous !
Modifications
- [2002-01-04]
- Démarrage de l’écriture de cette astuce.
- [2003-10-07]
- Version initiale, de petits ajouts.
- [2003-10-08]
- Oubli de mentionner Tushar dans les remerciements, de petits changements et ajouts.
- De petits changements et corrections suggérés par Bill Maltby
- [2003-10-26]
- Ajout d’un lien vers la FAQ SIG11, quelques trucs en plus sur les erreurs de segmentation et quelques mots à propos des problèmes de depmod avec des noyaux différents.
- [2003-11-10]
- Ajout d’un chapitre liens avec un lien vers un livre qui aide à acquérir les connaissances en C++.
- [2004-01-20]
- Ajout d’une petite partie sur les fonctions redéfinies.
- [2004-09-07]
- Des corrections mineures, mention de l’incapacité de gcc-3.4 à accepter les étiquettes à la fin des instructions composées et de sa solution.
- [2004-09-08]
- D’autres corrections mineures et des choses de gcc-3.4 à propos des fonctions protégées.
Remerciements
Merci à teemu pour m’avoir rappeler “-I” et “-l” autant qu’à Tushar pour l’avertissement sur les avertissements et pour avoir sonner la cloche pour l’option “-w”, sans oublier Bill pour ses corrections. Merci à Gérard pour la partie de LFS sur les erreurs qui m’a inspirée ! Merci à Allen B. Downey pour son livre brillant qui est distribué librement sous licence GPL ! :-)