Qu’advient-il des variables globales et statiques dans une bibliothèque partagée lorsqu’elle est liée dynamicment?

J’essaie de comprendre ce qui se passe lorsque des modules avec des variables globales et des variables statiques sont liés dynamicment à une application. Par modules, je veux dire chaque projet dans une solution (je travaille beaucoup avec Visual Studio!). Ces modules sont intégrés dans * .lib ou * .dll ou dans le fichier * .exe lui-même.

Je comprends que le binary d’une application contient des données globales et statiques de toutes les unités de traduction individuelles (fichiers objects) du segment de données (et du segment de données en lecture seule si const).

  • Que se passe-t-il lorsque cette application utilise un module A avec une liaison dynamic au moment du chargement? Je suppose que la DLL a une section pour ses globales et statiques. Le système d’exploitation les charge-t-il? Si oui, où sont-ils chargés?

  • Et que se passe-t-il lorsque l’application utilise un module B avec une liaison dynamic à l’exécution?

  • Si j’ai deux modules dans mon application qui utilisent tous les deux A et B, les copies des globales de A et B sont-elles créées comme indiqué ci-dessous (s’il s’agit de processus différents)?

  • Les DLL A et B ont-elles access aux applications globales?

(Merci d’indiquer vos raisons)

Citant de MSDN :

Les variables déclarées globales dans un fichier de code source DLL sont traitées comme des variables globales par le compilateur et l’éditeur de liens, mais chaque processus qui charge une DLL donnée obtient sa propre copie des variables globales de cette DLL. La scope des variables statiques est limitée au bloc dans lequel les variables statiques sont déclarées. Par conséquent, chaque processus a sa propre instance des variables globales et statiques de DLL par défaut.

et d’ ici :

Lors de la liaison dynamic de modules, il peut être difficile de savoir si différentes bibliothèques ont leurs propres instances de globals ou si les globales sont partagés.

Merci.

C’est une différence assez célèbre entre les systèmes Windows et ceux de type Unix.

Peu importe ce que:

  • Chaque processus a son propre espace d’adressage, ce qui signifie qu’il n’y a jamais de mémoire partagée entre les processus (sauf si vous utilisez une bibliothèque ou des extensions de communication inter-processus).
  • La règle de définition unique (ODR) s’applique toujours, ce qui signifie qu’une seule définition de la variable globale est visible au moment du lien (liaison statique ou dynamic).

La question clé est donc vraiment la visibilité .

Dans tous les cas, static variables globales static (ou fonctions) ne sont jamais visibles de l’extérieur d’un module (dll / so ou exécutable). Le standard C ++ exige qu’ils aient un lien interne, ce qui signifie qu’ils ne sont pas visibles en dehors de l’unité de traduction (qui devient un fichier object) dans lequel ils sont définis. Donc, cela règle le problème.

Là où cela devient compliqué, c’est quand vous avez des variables globales extern . Ici, les systèmes Windows et Unix-like sont complètement différents.

Dans le cas de Windows (.exe et .dll), les variables globales extern ne font pas partie des symboles exportés. En d’autres termes, les différents modules n’ont aucune connaissance des variables globales définies dans les autres modules. Cela signifie que vous obtiendrez des erreurs de l’éditeur de liens si vous essayez, par exemple, de créer un exécutable censé utiliser une variable extern définie dans une DLL, car cela n’est pas autorisé. Vous devez fournir un fichier object (ou une bibliothèque statique) avec une définition de cette variable externe et le lier statiquement à la fois à l’exécutable et à la DLL, ce qui donne deux variables globales distinctes (une appartenant à l’exécutable et l’autre à la DLL). ).

Pour exporter réellement une variable globale dans Windows, vous devez utiliser une syntaxe similaire à la syntaxe de la fonction export / import, à savoir:

 #ifdef COMPILING_THE_DLL #define MY_DLL_EXPORT extern "C" __declspec(dllexport) #else #define MY_DLL_EXPORT extern "C" __declspec(dllimport) #endif MY_DLL_EXPORT int my_global; 

Lorsque vous faites cela, la variable globale est ajoutée à la liste des symboles exportés et peut être liée comme toutes les autres fonctions.

Dans le cas des environnements de type Unix (comme Linux), les bibliothèques dynamics, appelées “objects partagés” avec extension .so exportent toutes les variables globales (ou fonctions) extern . Dans ce cas, si vous effectuez une liaison temps-charge depuis un endroit quelconque vers un fichier object partagé, les variables globales sont partagées, c’est-à-dire liées entre elles. Fondamentalement, les systèmes de type Unix sont conçus pour faire en sorte qu’il n’y ait pratiquement aucune différence entre la liaison avec une bibliothèque statique ou dynamic. Encore une fois, l’ODR s’applique à tous les niveaux: une variable globale extern sera partagée entre les modules, ce qui signifie qu’elle ne devrait avoir qu’une seule définition pour tous les modules chargés.

Enfin, dans les deux cas, pour Windows ou les systèmes de type Unix, vous pouvez GetProcAddress() liaison à l’ exécution de la bibliothèque dynamic, par exemple en utilisant LoadLibrary() / GetProcAddress() / FreeLibrary() ou dlopen() / dlsym() / dlclose() . Dans ce cas, vous devez manuellement obtenir un pointeur sur chacun des symboles que vous souhaitez utiliser, et cela inclut les variables globales que vous souhaitez utiliser. Pour les variables globales, vous pouvez utiliser GetProcAddress() ou dlsym() la même manière que pour les fonctions, à condition que les variables globales fassent partie de la liste des symboles exportés (par les règles des paragraphes précédents).

Et bien sûr, comme dernière remarque nécessaire: les variables globales doivent être évitées . Et je crois que le texte que vous avez cité (à propos de choses “peu claires”) fait référence exactement aux différences spécifiques à la plate-forme que je viens d’expliquer (les bibliothèques dynamics ne sont pas vraiment définies par le standard C ++, est beaucoup moins fiable / portable).