web-dev-qa-db-fra.com

Vérification facile des symboles non résolus dans les bibliothèques partagées?

J'écris une bibliothèque d'objets partagés C++ assez grande et j'ai rencontré un petit problème qui rend le débogage difficile:

Si je définis une fonction/méthode dans un fichier d'en-tête, et oublie de créer un stub pour lui (pendant le développement), puisque je construis en tant que bibliothèque d'objets partagée plutôt qu'en tant qu'exécutable, aucune erreur n'apparaît au moment de la compilation me disant que j'ai oublié de mettre en œuvre cette fonction. La seule façon dont je découvre que quelque chose ne va pas est au moment de l'exécution, quand finalement une application se liant à cette bibliothèque tombe avec une erreur de "symbole non défini".

Je cherche un moyen facile de vérifier si j'ai tous les symboles dont j'ai besoin au moment de la compilation, peut-être quelque chose que je peux ajouter à mon Makefile.

Une solution que j'ai trouvée consiste à exécuter la bibliothèque compilée via nm -C -U pour obtenir une liste démêlée de toutes les références non définies. Le problème est que cela revient également à la liste de toutes les références qui se trouvent dans d'autres bibliothèques, telles que GLibC, qui seront bien sûr liées avec cette bibliothèque lorsque l'application finale sera mise en place. Il serait possible d'utiliser la sortie de nm vers grep dans tous mes fichiers d'en-tête et de voir si l'un des noms correspond .. mais cela semble insensé. Ce n'est sûrement pas un problème rare et il existe une meilleure façon de le résoudre?

75
David Claridge

Découvrez l'option de l'éditeur de liens -z defs/--no-undefined. Lors de la création d'un objet partagé, le lien échouera s'il existe des symboles non résolus.

Si vous utilisez gcc pour appeler l'éditeur de liens, vous utiliserez le compilateur -Wl option pour passer l'option à l'éditeur de liens:

gcc -shared ... -Wl,-z,defs

À titre d'exemple, considérons le fichier suivant:

#include <stdio.h>

void forgot_to_define(FILE *fp);

void doit(const char *filename)
{
    FILE *fp = fopen(filename, "r");
    if (fp != NULL)
    {
        forgot_to_define(fp);
        fclose(fp);
    }
}

Maintenant, si vous construisez cela dans un objet partagé, cela réussira:

> gcc -shared -fPIC -o libsilly.so silly.c && echo succeeded || echo failed
succeeded

Mais si vous ajoutez -z defs, le lien échouera et vous indiquera votre symbole manquant:

> gcc -shared -fPIC -o libsilly.so silly.c -Wl,-z,defs && echo succeeded || echo failed
/tmp/cccIwwbn.o: In function `doit':
silly.c:(.text+0x2c): undefined reference to `forgot_to_define'
collect2: ld returned 1 exit status
failed
88

Sous Linux (que vous semblez utiliser) ldd -r a.out devrait vous donner exactement la réponse que vous recherchez.

MISE À JOUR: une façon triviale de créer a.out par rapport auquel vérifier:

 echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so
 ldd -r ./a.out
14
Employed Russian

Et une suite de tests? Vous créez de faux exécutables qui pointent vers les symboles dont vous avez besoin. Si la liaison échoue, cela signifie que votre interface de bibliothèque est incomplète.

8
Stefano Borini

J'ai eu le même problème une fois. Je développais un modèle de composant en C++ et, bien sûr, les composants devraient se charger dynamiquement lors de l'exécution. Trois solutions me viennent à l'esprit, celles que j'ai appliquées:

  1. Prenez le temps de définir un système de génération capable de compiler statiquement. Vous perdrez un peu de temps à le concevoir, mais cela vous fera gagner beaucoup de temps en détectant ces erreurs d'exécution ennuyeuses.
  2. Regroupez vos fonctions dans des sections bien connues et bien comprises, afin de pouvoir regrouper des fonctions/stubs pour être sûr que chaque fonction correspondante a son stub. Si vous prenez le temps de bien le documenter, vous pouvez peut-être écrire un script qui vérifie les définitions (via, par exemple, ses commentaires doxygen) et vérifier le fichier .cpp correspondant.
  3. Effectuez plusieurs tests exécutables qui chargent le même ensemble de bibliothèques et spécifiez l'indicateur RTLD_NOW à dlopen (si vous êtes sous * NIX). Ils signaleront les symboles manquants.

J'espère que ça t'as aidé.

3
Diego Sevilla