Il semble assez clair qu'il est censé mettre les choses en place.
- Quand cela fonctionne-t-il exactement?
- Pourquoi y a-t-il deux parenthèses?
- Est
__attribute__
une fonction? Une macro? Syntaxe? - Est-ce que cela fonctionne en C? C ++?
- La fonction avec laquelle elle fonctionne doit-elle être statique?
- Quand
__attribute__((destructor))
fonctionne-t-il?
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
la source
#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..init/.fini
. (Vous pouvez valablement avoir plusieurs constructeurs et destructeurs dans une seule unité de traduction, plusieurs nevermind dans une seule bibliothèque - comment cela fonctionnerait-il?) Au lieu de cela, sur les plateformes utilisant le format binaire ELF (Linux, etc.), les constructeurs et destructeurs sont référencés dans les sections.ctors
et.dtors
de l'en-tête. Certes, dans l'ancien temps, les fonctions nomméesinit
etfini
seraient exécutées lors du chargement et du déchargement dynamiques de la bibliothèque si elles existaient, mais c'est obsolète maintenant, remplacé par ce meilleur mécanisme.__attribute__
est si vous n'utilisez pas gcc, car c'est aussi une extension gcc..init
/.fini
n'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
/.fini
est 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.init
sera 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.dtors
mé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.fini
place, 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
.init
entrant est exécuté avant.ctors
et le code entré.fini
aprè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
_init
et une_fini
fonction par module chargeable et que vous devrez probablement fragmenter le code plus.so
que 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_fini
par défaut (fournies parcrti.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 iciNotez dans le lien ci-dessus qu'une cascade vers l'original
_init()
n'est pas nécessaire car elle est toujours en place. Lecall
dans 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..init
Les mécanismes /.fini
et.ctors
/.detors
sont similaires, mais pas tout à fait. Le code dans.init
/.fini
s'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.so
fichiers..ctors
/.dtors
sont organisés différemment de.init
/.fini
..ctors
Les.dtors
sections / 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
.ctors
fonction au tableau de fonctions, principalement de la même manière que__attribute__((constructor))
(la méthode peut coexister avec__attribute__((constructor)))
.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.dtors
boucle 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 .
la source
__attribute__((constructor))/((destructor))
le destructeur ne fonctionne pas. J'ai essayé peu de choses comme l'ajout d'une entrée à .dtor comme indiqué ci-dessus. Mais pas de succès. Le problème est facile à dupliquer en exécutant le code avec numactl. Par exemple, supposons que test_code contient le destructeur (ajoutez un printf aux fonctions constructeur et desctructeur pour déboguer le problème). Ensuite, courezLD_PRELOAD=./test_code numactl -N 0 sleep 1
. Vous verrez que le constructeur est appelé deux fois mais le destructeur une seule fois.Cette page fournit une grande compréhension de la
constructor
et ladestructor
mise en œuvre d'attributs et les sections à l' intérieur au sein de ELF qui leur permettent de travailler. Après avoir digéré les informations fournies ici, j'ai compilé un peu d'informations supplémentaires et (en empruntant l'exemple de section à Michael Ambrus ci-dessus) j'ai créé un exemple pour illustrer les concepts et aider mon apprentissage. Ces résultats sont fournis ci-dessous avec l'exemple de source.Comme expliqué dans ce fil, les attributs
constructor
etdestructor
créent des entrées dans la section.ctors
et.dtors
du fichier objet. Vous pouvez placer des références à des fonctions dans l'une ou l'autre section de trois manières. (1) en utilisant soit l'section
attribut; (2)constructor
etdestructor
attributs ou (3) avec un appel d'assemblage en ligne (comme référencé le lien dans la réponse d'Ambrus).L'utilisation des attributs
constructor
etdestructor
vous permet en outre d'attribuer une priorité au constructeur / destructeur pour contrôler son ordre d'exécution avantmain()
son appel ou après son retour. Plus la valeur de priorité donnée est faible, plus la priorité d'exécution est élevée (les priorités inférieures s'exécutent avant les priorités supérieures avant main () - et après les priorités supérieures après main ()). Les valeurs de priorité que vous donnez doivent être supérieures à100
car le compilateur réserve des valeurs de priorité comprises entre 0 et 100 pour l'implémentation. Unconstructor
oudestructor
spécifié avec priorité s'exécute avant unconstructor
oudestructor
spécifié sans priorité.Avec l'attribut 'section' ou avec l'assemblage en ligne, vous pouvez également placer des références de fonction dans la section de code ELF
.init
et.fini
qui s'exécuteront respectivement avant tout constructeur et après tout destructeur. Toutes les fonctions appelées par la référence de fonction placée dans la.init
section s'exécuteront avant la référence de fonction elle-même (comme d'habitude).J'ai essayé d'illustrer chacun de ceux de l'exemple ci-dessous:
production:
L'exemple a aidé à cimenter le comportement constructeur / destructeur, j'espère qu'il sera également utile à d'autres.
la source
MAX_RESERVED_INIT_PRIORITY
), et qu'elles étaient les mêmes que C ++ (init_priority
) 7.7 C ++ - Variable Specific, Function, and Type Attributes . Ensuite , je l'ai essayé avec99
:warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));
.Voici un exemple "concret" (et peut - être utile ) de comment, pourquoi et quand utiliser ces constructions pratiques, mais disgracieuses ...
Xcode utilise un «utilisateur par défaut» «global» pour décider quelle
XCTestObserver
classe crache le cœur à la console assiégée .Dans cet exemple ... quand je charge implicitement cette bibliothèque de pseudo, appelons-la ...
libdemure.a
, via un indicateur dans ma cible de test á la ..Je veux..
Lors du chargement (c'est-à-dire lors du
XCTest
chargement de mon lot de tests), remplacez laXCTest
classe "par défaut" "observateur" ... (via laconstructor
fonction) PS: Autant que je sache .. tout ce qui est fait ici pourrait être fait avec un effet équivalent dans mon+ (void) load { ... }
méthode de classe .exécuter mes tests .... dans ce cas, avec moins de verbosité stupide dans les logs (implémentation sur demande)
Remettez la
XCTestObserver
classe "globale" dans son état d'origine ... afin de ne pas gâcher d'autresXCTest
pistes qui n'ont pas pris le train en marche (aka. Liées àlibdemure.a
). Je suppose que cela a toujours été fait endealloc
.. mais je ne vais pas commencer à jouer avec cette vieille sorcière.Donc...
Sans le drapeau de l'éditeur de liens ... (La police de la mode fourmille de Cupertino exigeant des représailles , mais le défaut d'Apple prévaut, comme on le souhaite, ici )
AVEC le
-ldemure.a
drapeau de l' éditeur de liens ... (Résultats compréhensibles, halètement ... "merciconstructor
/destructor
" ... Acclamations de la foule )la source
Voici un autre exemple concret: il s'agit d'une bibliothèque partagée. La fonction principale de la bibliothèque partagée est de communiquer avec un lecteur de carte à puce. Mais il peut également recevoir des «informations de configuration» lors de l'exécution via udp. L'udp est géré par un thread qui DOIT être démarré au moment de l'initialisation.
La bibliothèque a été écrite en c.
la source