Comment lire une erreur d'édition des liens?

Les exemples de ce document utilisent l'écriture des messages d'erreur du compilateur C++ de Microsoft, mais l'idée est la même avec les autres compilateurs, g++ inclus.

Aussi, si les idées de pile (au sens de pile d'exécution d'un thread), de registres et de passage de paramètres vous échappe, il est possible que cet article vous semble un peu opaque. Vous pouvez examiner ces vieux documents pour vous aider à vous orienter, mais ils sont probablement tous destinés à être révisés (le temps me manque...) :

Pour en savoir plus sur la règle ODR, voir ce texte du Code Project par Mikhail Semenov en 2014 : http://www.codeproject.com/Articles/762413/The-One-Definition-Rule-in-Cplusplus-and-Cplusplus

Pour une belle explication sur Stack Overflow, voir http://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix

Il arrive souvent, dans une formation de programmation, quand les programmes commencent à devenir un peu plus complexes, que ces programmes compilent mais que des erreurs soient tout de même relevées. Ces erreurs peuvent d'ailleurs être cryptiques, du moins aux yeux moins aguerris.

À titre d'exemple, à la compilation d'un programme comme celui-ci :

int f(); // prototype, pas de définition
int main()
{
   f();
}

on aura par exemple le message d'erreur suivant – ici, avec Visual Studio 2008, mais l'idée est la même pour à peu près tous les compilateurs :

error LNK2019: symbole externe non résolu "int __cdecl f(void)" (?f@@YAHXZ) référencé dans la fonction _main

C'est un cas type d'erreur d'édition des liens (en anglais, de Linker). Il nous indique plusieurs choses sur le programme et sur le compilateur C++ que nous utilisons.

Voir cet article pour en savoir plus à propos des conventions d'appel.

Tout d'abord, notons le mot __cdecl. Il s'agit d'une convention d'appel. Le mot clé __cdecl en soi est propre au compilateur que nous avons utilisé ici mais le concept de convention d'appel est général, et transcende les langages et les plateformes.

Le préfixe error LNK2019: est une indication qu'il s'agit d'une erreur d'édition des liens. Cela devrait presque être encourageant, puisque la compilation doit avoir réussi pour qu'une édition des liens soit entreprise, donc à ce stade, votre programme compile, mais la génération ne peut être complétée. Les erreurs d'édition des liens sont essentiellement de deux types :

La phrase "symbole externe non résolu", qui indique l'erreur en soi. Il faut les lire, ces messages, vous savez!

Le symbole qui pose problème est identifié. Ici, il s'agit de "int __cdecl  f(void)", donc d'une fonction nommée f, respectant la convention d'appel du langage C, ne prenant pas de paramètres et retournant un int. On en déduit donc que la fonction de signature int f() a été déclarée (le programme compile) mais pas définie (erreur d'édition des liens). Manque-t-il un fichier source au projet? Manque-t-il une bibliothèque externe? C'est au programmeur de diagnostiquer le tout, bien entendu.

Un indice quant au lieu où le problème a été remarqué apparaît en fin de message, avec la mention "référencé dans la fonction _main". Notez le caractère de soulignement en début du nom de fonction : le nom dans le code généré n'a pas à être exactement le même que celui dans le code source.

Pour les fonctions C, il est assez typique de voir les symboles générés porter le même nom que ceux du code source à un préfixe comme _ près.

En effet, en C, il ne peut pas y avoir deux fonctions de même nom mais avec des paramètres différents, comme on en rencontre dans les langages OO comme C++, alors chaque nom est unique.

En C++, d'ailleurs, le fait que le nom ne soit pas suffisant pour distinguer deux symboles (pour les fonctions, il faut aussi tenir compte de l'arité – donc du nombre de paramètres – et du type de chaque paramètre, ce à quoi s'ajoutent – pour les méthodes d'instance – les qualifications const et volatile) a pour conséquence que les noms de symboles générés soient ce qu'on nomme des noms décorés, beaucoup plus difficiles à lire et variant d'un compilateur à l'autre.

Dans le message d'erreur donné en exemple, la mention (?f@@YAHXZ) nous indique que le nom de la fonction f() dans le code généré est ?f@@YAHXZ.


Valid XHTML 1.0 Transitional

CSS Valide !