2
votes

L'extension de l'éditeur de liens gcc __attribute __ ((constructeur)) provoque un crash dans main ()

J'ai d'abord un fichier singleton.cpp pour construire un objet singleton, et déclarer l'instance en utilisant attribute[((constructor))

g++ singleton.cpp main.cpp -o main

./main
beforeFunction
Segmentation fault

Et un simple main .cpp

#include<iostream>
using namespace std;
int main(){
    return 0;
}

Je construis main.cpp singleton.cpp ensemble:

#include<iostream>
using namespace std;
class singleton{
public:
    singleton(){cout<<"singleton ctor\n";}
};
__attribute__((constructor)) static void beforeFunction()
{
    printf("beforeFunction\n");
    singleton obj;
}

Alors pourquoi mon programme plante, que s'est-il passé ? Comment le réparer? J'utilise gcc sur ubuntu. Merci beaucoup.


0 commentaires

3 Réponses :


1
votes

Alors pourquoi mon programme plante, que s'est-il passé?

Très probablement, la machine iostream n'est pas encore initialisée au moment où les fonctions __attribute __ ((constructeur)) sont exécutées.

Comment y remédier?

Soit utilisez C I / O comme printf , ce qui semble fonctionner dans votre cas; ou mieux, évitez d'utiliser complètement __attribute __ ((constructeur)) (ce n'est pas du C ni du C ++ standard, c'est-à-dire que votre programme n'est pas portable).

Notez qu'il n'est pas nécessaire d'utiliser __attribute __ ((constructor)) pour créer des singletons ou des objets globaux.


0 commentaires

2
votes

J'ai reproduit ceci avec g ++ (Debian 7.3.0-5) , et aussi g ++ (GCC) 9.0.0 20180902 (expérimental) .

C'est intéressant que cela échoue:

$ g++ main.cpp singleton2.cpp && ./a.out
singleton ctor

$ g++ singleton2.cpp main.cpp && ./a.out
singleton ctor

mais cela fonctionne comme prévu:

// singleton2.cpp
#include<iostream>
using namespace std;

class singleton{
  public:
    singleton(){cout<<"singleton ctor\n";}
};

static singleton obj;

Comme Acorn l'a correctement indiqué, le iostream Les machines / std :: cout n'ont pas été correctement initialisées au moment où vous avez appelé le constructeur singleton. Cela se produit car un code spécial est émis dans main.o (et uniquement main.o ) qui appelle std :: ios_base :: Init :: Init () . Et seulement parce que main.cpp a des #include superflus .

Comment y remédier?

La meilleure solution est de ne pas utiliser du tout __attribute __ ((constructor)) . Dans votre cas, il n'y a aucune raison de faire ce que vous faites. Faites ceci à la place:

$ g++ main.cpp singleton.cpp && ./a.out
beforeFunction
singleton ctor

Avec le code ci-dessus, l'un ou l'autre ordre de liaison fonctionne:

$ g++ singleton.cpp main.cpp && ./a.out
beforeFunction
Segmentation fault

Si vous insistez pour utiliser __ attribut __ ((constructeur)) , puis assurez-vous que main.o est sur votre ligne de lien avant tout autre objet qui pourrait utiliser iostream s.


1 commentaires

Je suis presque sûr que la commande main.cpp singleton.cpp ne fonctionne que parce qu'il y a un #include errant dans main.cpp .



1
votes

std :: cout est initialisé à l'aide d'un objet C ++ statique, voir :

  // For construction of filebuffers for cout, cin, cerr, clog et. al.
  static ios_base::Init __ioinit;

Depuis votre le code repose à la fois sur ce constructeur statique et possède un constructeur ELF, il s'exécute dans ce limitation GCC :

Cependant, à l'heure actuelle, l'ordre dans lequel les constructeurs pour les objets C ++ avec une durée de stockage statique et les fonctions décorées avec l'attribut constructeur sont invoqués n'est pas spécifié.

Si vous utilisez un objet C ++ à la place, l'ordre est bien défini. Le manuel GCC suggère également d'utiliser le attribut init_priority , mais comme vous ne pouvez pas l'appliquer à la définition de __ioinit (sauf via le piratage du préprocesseur), je ne pense pas que cela soit utile dans ce contexte. p>


0 commentaires