AUTEUR : Alex Kloss TRADUCTEUR : Denis Mugnier DATE: 2003-11-10 LICENCE : GNU Free Documentation License Version 1.2 SYNOPSIS : Que faire en cas d'erreurs DESCRIPTION : 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. PRÉREQUIS : Bon sens, LFS, patience, compétences en programmation (facultatif). 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 ! 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 ou locate (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 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 "" *.* 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|''|''|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 : --snip FUNC(3) Linux Programmer's Manual FUNC(3) NAME func, ffunc, vfunc - Example function without any use SYNOPSIS #include int func(char *format, ...); int ffunc(FILE *stream, const char *format, ...); #include int vfunc(const char *format, va list ap); DESCRIPTION ... --snap 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 ". 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 "" *.* | 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 " 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++. Puisse les sources être 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 ! :-)