Rappelez-vous une erreur indéfinie du symbole d'invite de lien gcc

1. Résumé préliminaire

Lors de l'utilisation d'une bibliothèque tierce la semaine dernière, un symbole d'invite de lien n'était pas défini. Cependant, lorsque vous utilisez readelf pour afficher les propriétés des symboles internes du tiers so, vous pouvez voir qu'ils sont réellement définis, mais il existe certaines différences entre les symboles internes de so et les symboles que j'ai signalés. Cela implique le rôle de "C" externe et certaines différences dans le traitement des symboles entre g++ et gcc. Il a été retardé pendant longtemps. Terminons la sortie de ce problème aujourd'hui.

2. Différence de signe

1. Phénomène

Ici, nous simulons et reproduisons simplement la scène à ce moment-là. Préparez simplement un code source " func.cpp ", incluant la définition de son fichier d'en-tête " func.h " :

// func.h
#include <stdio.h>

int func(int a, int b);

// func.cpp
#include "func.h"

int func(int a, int b) {
    
    
  return a + b;
}

Utilisez g++ -shared -fPIC -o libfunc.so func.cppla commande pour compiler et générer le fichier so correspondant.
insérez la description de l'image ici

Ensuite, nous écrivons simplement une fonction main, et appelons la fonction func implémentée par le so dans la fonction main. Le code source " main.c " est le suivant :

// main.c
#include "func.h"

int main () {
    
    
  int a = 1, b = 1;
  int c = 0;
  c = func(a, b);
  printf("%d + %d = %d\n", a, b, c);
  
  return 0;
}

Essayons de compiler main.c et le libfunc.so correspondant. avec la commande suivantegcc main.c -o exec -L./ -lfunc
insérez la description de l'image ici

À ce stade, je rencontrerai des problèmes lorsque je lierai des bibliothèques tierces au travail, suggérant qu'il existe un symbole indéfini mais référencé faisant référence à 'func' dans main.c.

2. Recherche préliminaire

J'étais assez confus à l'époque. Je l'ai clairement défini int func(int a, int b). Dans func.cpp, j'ai également lié à la bibliothèque so correspondante. Le " -lfunc " dans la commande de compilation en est une bonne preuve. Alors pourquoi indique-t-il que le symbole n'est pas défini ? J'ai donc utilisé readelfla commande pour vérifier la situation de func dans libfunc.so. readelf -a ./libfunc.so | grep "func", le résultat est le suivant :
insérez la description de l'image ici
vous pouvez voir qu'il existe une définition dans libfunc.so, mais ce symbole est différent du symbole qui a signalé le message d'erreur dans le lien ci-dessus. Ce devrait être ce problème qui a empêché la définition du symbole être trouvé lors de la liaison. Selon la forme du symbole, il est essentiellement déterminé que le symbole est modifié par le compilateur car il est compilé avec g++, mais gcc utilise toujours le symbole d'origine "func" lors de la compilation de main.c, ce qui entraîne des symboles indéfinis.

3. Différences dans le traitement des symboles entre g++ et gcc au moment de la compilation

Lorsqu'un tel problème survient, vérifiez rapidement une vague d'informations pour consolider le principe de traitement des symboles de g++.

Cela peut se résumer aux points suivants :
1) Sauf pour les variables globales qui n'ont pas besoin d'être modifiées, lorsque d'autres symboles doivent être modifiés, ils commencent tous par _Z ;
2) Si vous voulez indiquer qu'un certain symbole est dans un espace de noms ou une classe, doit commencer par "N" et se terminer par "E" ;
3) Tous les noms d'espace de noms, noms de classes, noms de fonctions ou noms de variables, lorsqu'ils sont adaptés, sont le nombre de caractères contenus dans le nom plus le nom réel ; 4
) Tous les noms sont adaptés dans l'ordre de la couche externe à la couche interne ;
5) S'il s'agit d'une fonction, tous les paramètres sont adaptés dans l'ordre d'apparition.
(Reportez-vous au billet de blog ici : https://blog.csdn.net/roland_sun/article/details/43233565)

Nous nous référons aux règles ci-dessus et modifions manuellement la fonction func :
1) func est un symbole de fonction global, il commence donc par "_Z" ;
2) Puisqu'il s'agit d'un symbole global, il n'a pas besoin de commencer par "N" et se termine par "E" ;
3 ) Le nom de la variable "func" a quatre caractères au total, donc c'est "4func"
4) Les paramètres sont "int a, int b", donc l'ordre est "ii"

Pour résumer, "func" devrait être "_Z4funcii" après avoir été compilé et modifié par g++, ce qui est readelfexactement la même chose que ce que nous avons vu.

Le processus de modification est complété par le compilateur "g++" indépendamment, et l'utilisateur n'a aucune perception. Et "gcc" ne décorera pas les symboles. Pourquoi est-ce? Pourquoi devrait-il être traité différemment?

C'est parce que C++ est différent de C en ajoutant de nouvelles fonctionnalités telles que les espaces de noms, les classes et la surcharge de fonctions, il y aura donc différents espaces de noms, ou différentes classes, ou la même classe, et il y aura des fonctions avec le même nom. Afin d'assurer l'unicité des symboles dans le fichier cible après la compilation, g++ modifie les symboles du fichier c++ d'origine, donc après avoir compris les règles de modification, nous pouvons trouver la position de symbole spécifique correspondante en fonction des symboles modifiés.

3. Résolution de problèmes

Maintenant que nous avons trouvé la cause première du problème, nous devons trouver une solution. Étant donné que la bibliothèque tierce est directement compilée par le tiers et mise à ma disposition, je ne peux pas la recompiler sans fichiers de code source, je ne peux donc trouver une solution que de mon côté.

1. Compilez main.c avec g++

Comme nous ne pouvons pas modifier les symboles à l'intérieur de la bibliothèque, nous ne pouvons modifier les symboles que lors de la compilation de main.c. J'essaie g++de compiler main.c en utilisant.

Nous utilisons la commande g++ -c -o main.o main.cpour compiler le fichier source et générer le fichier objet "main.o". Une fois la compilation terminée, nous utilisons readelfla commande pour vérifier la forme existante du symbole func dans le fichier cible, et le résultat est affiché dans la figure ci-dessous :
insérez la description de l'image ici
Vous pouvez voir que dans le fichier cible "main.o", le symbole " func " est devenu une forme modifiée" _Z4funcii ", alors le processus de liaison suivant ne devrait plus poser le problème que le symbole est introuvable. Essayons:
insérez la description de l'image ici
nous pouvons voir que nous avons pu nous connecter si normalement. Le fichier exécutable généré "exec" s'exécute sans aucun problème. Jusqu'à présent, ce problème a été fondamentalement résolu.

Mais nous voulons toujours suivre la spécification, g++ compile les fichiers C++, gcc compile les fichiers C, que devons-nous faire à ce stade. Pour ce type d'interface C, nous espérons toujours conserver le nom du symbole d'origine au lieu du nom du symbole modifié, que nous devons ensuite utiliser extern “C”pour modifier l'interface.

2、externe "C"

Nous pouvons souvent voir la structure suivante dans le fichier d'en-tête :

#ifdef __cplusplus
extern "C" {
    
    
#endif

/*
 ...符号声明...
 */

#ifdef __cplusplus
}
#endif

A quoi sert ce bout de code ? Essayons de l'utiliser dans le code de test précédent pour voir quel effet cela a. Modifions le fichier d'en-tête func.hpour qu'il ressemble à ceci :

// func.h
#ifndef _FUNC_H_
#define _FUNC_H_

#ifdef __cplusplus
extern "C" {
    
    
#endif

#include <stdio.h>

int func(int a, int b);

#ifdef __cplusplus
}
#endif
#endif

Essayez ensuite de recompiler "libfunc.so":
insérez la description de l'image ici

J'ai recompilé la bibliothèque so en fonction du fichier d'en-tête modifié, vérifions readelfà nouveau le symbole "func" dans le nouveau "libfunc.so":

insérez la description de l'image ici
Nous avons constaté qu'après avoir ajouté le code ci-dessus dans le fichier d'en-tête, le nom du symbole dans la bibliothèque so n'est plus modifié. Cela signifie-t-il que notre fichier main.c peut être compilé et lié directement avec gcc ? Essayez-le :
insérez la description de l'image ici
il est en effet possible de lier directement pour générer un fichier exécutable. Selon l'introduction ci-dessus à la différence entre g++ et gcc sur le traitement des symboles, nous pouvons conclure que extern “C”"dites" au compilateur que le code suivant doit être compilé selon la norme du langage C, alors le nom du symbole ne sera pas utilisé par le nettoyage du compilateur .

Ensuite, pour l'interface de type C, nous devrions utiliser extern “C”to declare, de sorte que lorsque nous nous référons au symbole dans le fichier source de type C, nous pouvons utiliser le compilateur "gcc" pour compiler le fichier source, et nous n'avons pas besoin d'utiliser "g++" pour garder le symbole cohérent Le fichier "C" est compilé.

3. Résumé

Jusqu'à présent, nous avons complètement résolu le problème des symboles indéfinis rencontrés cette fois-ci, et approfondi ses principes connexes et les solutions correspondantes. Le problème cette fois-ci, avec le recul, est en fait très simple et facile à résoudre, mais au moment de l'incident, j'étais désemparé. C'est peut-être aussi parce que j'ai fait des heures supplémentaires tardives à ce moment-là, ce qui m'a fait mal au cerveau. pas assez flexible, hahaha.

Bien que le problème soit simple, nous devons toujours approfondir la cause profonde, comprendre et apprendre les principes pertinents, puis essayer de résoudre le problème sous plusieurs angles et solutions, afin que nous puissions grandir !

Tous les grands dieux sont également les bienvenus sur mon site Web personnel (www.ccccxy.top/coding) pour laisser des commentaires et des conseils. Cet article sera également mis à jour simultanément sur mon site Web personnel, merci !

La vie est sans fin, les bugs sont sans fin, programmeurs, la route est longue et difficile ~

Je suppose que tu aimes

Origine blog.csdn.net/qq_38894585/article/details/109843764
conseillé
Classement