.init/ .finin'est pas déconseillé. Cela fait toujours partie de la norme ELF et j'oserais dire que ce sera pour toujours. Le code dans .init/ .finiest exécuté par le chargeur / runtime-linker lorsque le code est chargé / déchargé. C'est-à-dire sur chaque chargement ELF (par exemple une bibliothèque partagée) le code .initsera exécuté. Il est toujours possible d'utiliser ce mécanisme pour obtenir à peu près la même chose qu'avec __attribute__((constructor))/((destructor)). C'est de la vieille école, mais cela présente certains avantages.
.ctors/ Le .dtorsmécanisme nécessite par exemple le support de system-rtl / loader / linker-script. Ceci est loin d'être certain d'être disponible sur tous les systèmes, par exemple les systèmes profondément embarqués où le code s'exécute sur du métal nu. C'est-à-dire que même s'il __attribute__((constructor))/((destructor))est pris en charge par GCC, il n'est pas certain qu'il fonctionnera car c'est à l'éditeur de liens de l'organiser et au chargeur (ou, dans certains cas, au code de démarrage) de l'exécuter. Pour utiliser .init/ à la .finiplace, la manière la plus simple consiste à utiliser des indicateurs de l'éditeur de liens: -init & -fini (c'est-à-dire à partir de la ligne de commande GCC, la syntaxe serait -Wl -init my_init -fini my_fini).
Sur un système prenant en charge les deux méthodes, un avantage possible est que le code .initentrant est exécuté avant .ctorset le code entré .finiaprès .dtors. Si l'ordre est pertinent, c'est au moins un moyen simple mais simple de distinguer les fonctions init / exit.
Un inconvénient majeur est que vous ne pouvez pas facilement avoir plus d'une _initet une _finifonction par module chargeable et que vous devrez probablement fragmenter le code plus .soque motivé. Un autre est que lorsque vous utilisez la méthode de l'éditeur de liens décrite ci-dessus, on remplace les fonctions d'origine _init et _finipar défaut (fournies par crti.o). C'est là que se produisent généralement toutes sortes d'initialisations (sous Linux, c'est là que l'affectation des variables globales est initialisée). Un moyen de contourner cela est décrit ici
Notez dans le lien ci-dessus qu'une cascade vers l'original _init()n'est pas nécessaire car elle est toujours en place. Le calldans l'assemblage en ligne est cependant x86 mnémoniques et appeler une fonction de montage serait complètement différent pour beaucoup d' autres architectures (comme ARM par exemple). C'est-à-dire que le code n'est pas transparent.
.initLes mécanismes / .finiet .ctors/ .detorssont similaires, mais pas tout à fait. Le code dans .init/ .finis'exécute "tel quel". C'est-à-dire que vous pouvez avoir plusieurs fonctions dans .init/ .fini, mais il est AFAIK syntaxiquement difficile de les mettre de manière totalement transparente en C pur sans casser le code dans de nombreux petits .sofichiers.
.ctors/ .dtorssont organisés différemment de .init/ .fini. .ctorsLes .dtorssections / ne sont que des tables avec des pointeurs vers des fonctions, et "l'appelant" est une boucle fournie par le système qui appelle chaque fonction indirectement. C'est-à-dire que l'appelant en boucle peut être spécifique à l'architecture, mais comme il fait partie du système (s'il existe), cela n'a pas d'importance.
L'extrait suivant ajoute de nouveaux pointeurs de .ctorsfonction au tableau de fonctions, principalement de la même manière que __attribute__((constructor))(la méthode peut coexister avec __attribute__((constructor))).
#define SECTION( S ) __attribute__ ((section ( S )))
void test(void) {
printf("Hello\n");
}
void (*funcptr)(void) SECTION(".ctors") =test;
void (*funcptr2)(void) SECTION(".ctors") =test;
void (*funcptr3)(void) SECTION(".dtors") =test;
On peut également ajouter les pointeurs de fonction à une section auto-inventée complètement différente. Un script de l'éditeur de liens modifié et une fonction supplémentaire imitant le chargeur .ctors/ la .dtorsboucle sont nécessaires dans ce cas. Mais avec cela, on peut obtenir un meilleur contrôle sur l'ordre d'exécution, ajouter des arguments et renvoyer le code de gestion eta (dans un projet C ++ par exemple, il serait utile s'il a besoin de quelque chose en cours d'exécution avant ou après les constructeurs globaux).
Je préfère __attribute__((constructor))/((destructor))autant que possible, c'est une solution simple et élégante même si on a l'impression de tricher. Pour les codeurs nus comme moi, ce n'est tout simplement pas toujours une option.
Quelques bonnes références dans le livre Linkers & loaders .
#define __attribute__(x)). Si vous avez plusieurs attributs, par exemple,__attribute__((noreturn, weak))il serait difficile de "macro-out" s'il n'y avait qu'un seul ensemble de crochets.