web-dev-qa-db-fra.com

Fonctions exécutées automatiquement lors du chargement de bibliothèques partagées

Lors du chargement de bibliothèques partagées sous Windows, l'appel LoadLibrary() entraîne l'exécution de DllMain dans la bibliothèque pour chaque nouveau processus et la bibliothèque de threads attachés, et pour chaque cache de bibliothèque de processus et de threads.

Existe-t-il un mécanisme similaire pour Mac OS X, Linux et éventuellement d'autres systèmes d'exploitation compatibles POSIX?

36
toriningen

Vous pouvez définir une fonction de chargement pour une bibliothèque Linux à l'aide du mécanisme .init. Cela revient à spécifier le point d'entrée au moment du chargement pour un fichier binaire (par exemple, en utilisant autre chose que principal comme point d'entrée pour un programme).

Lorsque vous liez directement avec ld, vous utilisez les éléments suivants:

-init <function name>

ou si vous utilisez cc/gcc pour lier, vous utilisez:

-Wl,-init,<function name>

C'est au niveau le plus simple.

Edit Pour les destructeurs/finaliseurs, vous utilisez le mécanisme .fini. Cela fonctionne de la même manière que l'option init et vous utilisez:

-fini <function name>

en appelant ld. La disponibilité est limitée à l'option -init sur la plate-forme Mac OSX.

Vous devriez aussi pouvoir utiliser la syntaxe __attribute__((constructor)) pour gcc:

static void con() __attribute__((constructor));

void con() {
    printf("I'm a constructor\n");
}

Ce qui est probablement une manière plus portable plutôt que de visser avec les options de l'éditeur de liens. Tous les constructeurs doivent être appelés au moment du chargement, mais non dépendent de l'ordre dans lequel ils ont été initialisés, ce qui conduit à la folie et à des bogues non reproductibles qui nécessitent du temps et des efforts de débogage.

Edit 2 L'utilisation de la sémantique __attribute__((constructor))/__attribute__((destructor)) est le mécanisme préférable pour le langage de programmation C/C++.

Pour le langage de programmation D, vous devriez vraiment utiliser le constructeur/destructeur de module statique:

static this() {
    printf("static this for mymodule\n");
}
static ~this() {
    printf("static ~this for mymodule\n");
}

Ou le constructeur de classe statique:

class Foo {
    static this() {
        printf("static this for Foo\n");
    }
}

Ceci est fortement indiqué dans l'écriture de DLL32 win32 et dans la spécification de langage relative aux constructeurs/destructeurs statiques .

Edit 3 Vous devrez créer un lien dans un .o qui exporte les routines constructeur/destructeur, ce qui permettra l'utilisation des initialiseurs statiques. Comme tout ce qu'il devrait faire est d'appeler Runtime.initialize (), cela appelle en fait tous les constructeurs/destructeurs statiques du code D.

Code de remplacement pour l'initialiseur (dans un fichier appelé myshared.d):

import core.runtime;

extern (C) {
    void attach();
    void detach();
}

export void attach() {
    Runtime.initialize();
}

export void detach() {
    Runtime.terminate();
}

Créez le .o pour ce stub:

 dmd -m32 -c myshared.d

Vérifiez les noms des fonctions attacher/détacher:

nm myshared.o

Affiche (entre autres sorties):

0000001c S _D8myshared6attachFZv
00000034 S _D8myshared6detachFZv

pour obtenir un exemple de code .c (appelé export.c dans ce cas-ci), nous référons les noms des routines exportées à partir du fichier my shared.o:

extern void D8myshared6attachFZv(void);
extern void D8myshared6detachFZv(void);

void __attach(void) __attribute__((constructor));
void __detach(void) __attribute__((destructor));

void __attach(void)
{
    D8myshared6attachFZv();
}

void __detach(void)
{
    D8myshared6detachFZv();
}

Notez que les références extern void doivent utiliser le nom mutilé de la fonction exportée. Ceux-ci doivent correspondre ou le code ne sera pas lié.

compiler le code C en utilisant:

gcc -m32 -c export.c

liez les fichiers .c.o et .d.o ensemble en utilisant:

cc -o libmyshared.dylib -m32 -shared myshared.o export.o -lphobos2

En supposant que la bibliothèque phobos2 se trouve dans votre chemin de recherche standard de l'éditeur de liens. Le nombre d'options -m32 pour le compilateur et l'éditeur de liens est dû au fait que la version du compilateur D que j'ai construite localement ne prend en charge que la version 32 bits.

Cela produit un fichier .dylib qui peut être lié. Cela semble fonctionner sur la base des tests limités que j'ai effectués. Il semble que le support pour les objets partagés/bibliothèques dynamiques soit très limité, il y a donc de bonnes chances qu'un autre obstacle se pose.

49
Petesh

Pour qu'une fonction soit exécutée chaque fois que la bibliothèque partagée est chargée ou déchargée, vous pouvez marquer une fonction de constructeur et de destructeur à l'aide de la syntaxe d'attribut spécifique à GCC:

__attribute__((constructor)) void init(void) { ... }
__attribute__((destructor))  void fini(void) { ... }

Étant donné que diverses parties d’un environnement C dépendent d’initialisations dans le code .init standard ajouté par GCC en coulisse, l’utilisation directe de -Wl,-init,<function name> peut provoquer le blocage de votre programme.

Pour plus d'informations, consultez le HOWTO Libary sur Fonctions de constructeur et de destructeur de bibliothèque .

9

GCC, et aussi Claw AFAIK, supporte les attributs constructeur et destructeur GCC. Pour plus de détails, voir Comment fonctionne exactement __attribute __ ((constructeur))?

6
janneb

Pour C++, vous pouvez créer une classe et utiliser son constructeur et son destructeur pour initialiser la bibliothèque.

Après, il vous suffit de définir une variable pour cette classe.

Exemple d'initialisation openssl dans la bibliothèque:

class InitLibrary {
public:
  InitLibrary() {
    CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
    SSL_library_init(); // Initialize OpenSSL's SSL libraries
    SSL_load_error_strings(); // Load SSL error strings
    ERR_load_BIO_strings(); // Load BIO error strings
    OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
  }

  ~InitLibrary() {
    ERR_remove_state(0);
    CRYPTO_cleanup_all_ex_data();
    ENGINE_cleanup();
  }
};

et ajoutez seulement cette ligne dans le fichier cpp: InitLibrary InitLib;

0
oml