C / C++ Comment Fonctionne La Liaison Dynamique Sur Différentes Plates-Formes?
Comment fonctionne la liaison dynamique en général?
Sous Windows (LoadLibrary), vous avez besoin d'un .dll à appeler au moment de l'exécution, mais au moment du lien, vous devez fournir un correspondant .fichier lib ou le programme ne sera pas lié... Ce qui ne l' .le fichier lib contient? Une description de la .méthodes dll? N'est-ce pas ce que les en-têtes contiennent?
Par ailleurs, sur *nix, vous n'avez pas besoin d'un fichier lib... Comment comment le compilateur sait - il que les méthodes décrites dans l'en-tête seront disponibles à l'exécution?
Comme un débutant, quand vous pensez à l'un des deux régimes, alors l'autre, aucun d'entre eux n'a de sens...
9 réponses
Pour répondre À vos questions une par une:
La liaison Dynamique reporte une partie du processus de liaison à l'exécution. Il peut être utilisé de deux façons: implicitement et explicitement. Implicitement, l'éditeur de liens statique insérera des informations dans le exécutable qui entraînera le chargement et la résolution de la bibliothèque symboles nécessaires. Explicitement, vous devez appeler
LoadLibrary
oudlopen
manuellement, puisGetProcAddress
/dlsym
pour chaque symbole que vous devez utiliser. Le chargement implicite est utilisé pour les choses comme le système bibliothèque, où la mise en œuvre dépendra de la version du système, mais l'interface est garanti. Le chargement explicite est utilisé pour des choses comme des plugins, où l' bibliothèque à charger sera déterminé au moment de l'exécution.Le fichier
.lib
n'est nécessaire que pour le chargement implicite. Il contient les informations que la bibliothèque fournit réellement ceci symbole, de sorte que l'éditeur de liens ne se plaindra pas que le symbole est indéfini, et il indique à l'éditeur de liens dans quelle bibliothèque de symboles être situé, de sorte qu'il peut insérer les informations nécessaires pour provoquer cette bibliothèque à charger automatiquement. Tous les fichiers d'en-tête dire au compilateur est que les symboles existeront, quelque part; le linker a besoin du.lib
pour savoir où.Sous Unix, toutes les informations sont extraites de la
.so
. Pourquoi Windows nécessite deux fichiers distincts, plutôt que mettre toutes les informations dans un seul fichier, je ne sais pas; c'est en fait dupliquer la plupart des informations, puisque le les informations nécessaires dans le.lib
sont également nécessaires dans le.dll
. (Peut-être des questions de licences. Vous pouvez distribuer votre programme avec le.dll
, mais personne ne peut lier contre les bibliothèques à moins que ils ont un.lib
.)
La principale chose à retenir est que si vous voulez un chargement implicite,
vous devez fournir à l'éditeur de liens les informations appropriées,
soit avec un fichier .lib
ou un fichier .so
, afin qu'il puisse insérer
informations dans l'exécutable. Et ça si tu veux explicite
chargement, vous ne pouvez pas vous référer à l'un des symboles de la bibliothèque
directement; vous devez appeler GetProcAddress
/dlsym
pour obtenir leur
s'adresse à vous-même (et faites un casting drôle pour les utiliser).
Le fichier .lib
sur Windows n'est pas nécessaire pour charger une bibliothèque dynamique, il offre simplement un moyen pratique de le faire.
En principe, vous pouvez utiliser LoadLibrary
pour charger la dll, puis utiliser GetProcAddress
pour accéder aux fonctions fournies par cette dll. La compilation du programme englobant n'a pas besoin d'accéder à la dll dans ce cas, elle n'est nécessaire qu'au moment de l'exécution (ie. quand LoadLibrary
s'exécute réellement). MSDN a un exemple de code .
L'inconvénient ici est que vous devez écrire manuellement du code pour charger les fonctions de la dll. Dans le cas où vous avez compilé la dll vous-même en premier lieu, ce code duplique simplement la connaissance que le compilateur aurait pu extraire du code source dll automatiquement (comme les noms et les signatures des fonctions exportées).
C'est ce que fait le fichier .lib
: Il contient les appels GetProcAddress
pour les fonctions exportées par les DLL, générées par le compilateur afin que vous n'ayez pas à vous en soucier. En termes de Windows, c'est appelé liaison dynamique en temps de chargement, puisque la Dll est chargée automatiquement par le code de la .fichier lib lorsque votre programme englobant est chargé (par opposition à l'approche manuelle, appelée run-time dynamic linking).
Comment fonctionne la liaison dynamique en général?
Le fichier dynamic link library (aka shared object) contient des instructions et des données de code machine, ainsi qu'une table de métadonnées indiquant quels décalages dans ce code / données se rapportent à quels "symboles" , le type de symbole (par exemple fonction vs données), le nombre d'octets ou de mots dans les données, et quelques autres choses. Différents systèmes d'exploitation auront tendance à avoir différents formats de fichiers objets partagés, et en effet le même système d'exploitation peut en supporter plusieurs, mais c'est l'essentiel.
Alors, imaginez la bibliothèque partagée est un gros morceau d'octets avec un indice comme ceci:
SYMBOL ADDRESS TYPE SIZE
my_function 1000 function 2893
my_number 4800 variable 4
En général, le type exact des symboles n'a pas besoin d'être capturé dans la table de métadonnées - il est prévu que les déclarations dans les fichiers d'en-tête de la bibliothèque contiennent toutes les informations manquantes. C++ est un peu spécial-par rapport à dire C-parce que la surcharge peut signifier qu'il y a plusieurs fonctions avec le même nom, et les espaces de noms permettent d'autres symboles qui sinon, être nommé de manière ambiguë-pour cette raison, name mangling est généralement utilisé pour concaténer une représentation de l'espace de noms et des arguments de fonction au nom de la fonction, formant quelque chose qui peut être unique dans le fichier objet de la bibliothèque.
UN programme voulant utiliser l'objet partagé peut généralement faire l'une des deux choses suivantes:
Demandez au système d'exploitation de charger lui-même et l'objet partagé à peu près au même moment (avant d'exécuter
main()
), avec le chargeur du système d'exploitation responsable pour trouver les symboles et examiner les métadonnées dans l'image du fichier de programme sur l'utilisation de ces symboles, puis patcher les adresses de symboles dans la mémoire que le programme utilise, de sorte que le programme peut alors fonctionner et fonctionner fonctionnellement comme s'il avait connu les adresses de symboles quand il a été compilé (mais peut-être un peu]}Ou, explicitement dans son propre appel de code source
dlopen
quelque temps aprèsmain
s'exécute, puis utilisezdlsym
ou similaire pour obtenir les adresses de symboles, enregistrez - les dans (fonction/données) des pointeurs basés sur la connaissance du programmeur des types de données attendus, puis appelez-les explicitement en utilisant les pointeurs.
Sous Windows (LoadLibrary), vous avez besoin d'un .dll à appeler au moment de l'exécution, mais au moment du lien, vous devez fournir un correspondant .fichier lib ou le programme ne sera pas lié...
Ça ne sonne pas bien. Devrait être l'un ou l'autre je pense.
WTF fait le .le fichier lib contient? Une description de la .méthodes dll? N'est-ce pas ce que les en-têtes contiennent?
Un fichier lib est - à ce niveau de description - à peu près le même qu'un fichier objet partagé... la principale différence est que le compilateur trouve les adresses de symboles avant que le programme ne soit expédié et exécuté.
Les systèmes modernes * nix dérivent le processus de liaison dynamique à partir du système D'exploitation Solaris. Linux, en particulier, n'a pas besoin de séparer .fichier lib car toutes les dépendances externes sont contenues au format ELF. .interp
la section du fichier ELF indique qu'il y a des symboles externes à l'intérieur de cet exécutable qui devaient être résolus dynamiquement. Cela vient de liaison dynamique.
, Il existe un moyen de gérer la liaison dynamique dans l'espace utilisateur. Cette méthode est appelée chargement dynamique. C'est quand vous utilisez des appels système pour obtenir des pointeurs de fonction vers des méthodes externes *. so.
Plus d'informations peuvent être trouvées à partir de cet article http://www.ibm.com/developerworks/library/l-dynamic-libraries/.
Relatedly, sur OS X (et je suppose *nix... dlopen), vous n'avez pas besoin d'un fichier lib... Comment comment le compilateur sait - il que les méthodes décrites dans l'en-tête seront disponibles à l'exécution?
Les compilateurs ou les lieurs n'ont pas besoin de telles informations. Vous, le programmeur, devez gérer la situation où les bibliothèques partagées que vous essayez d'ouvrir par dlopen()
peuvent ne pas exister.
Vous pouvez utiliser un fichier DLL dans Windows de deux façons: soit vous liez avec lui, et vous avez terminé, rien de plus à faire. ou vous le chargez dynamiquement pendant l'exécution.
Si vous le liez, le fichier de bibliothèque DLL est utilisé. La bibliothèque de liens contient des informations que l'éditeur de liens utilise pour savoir quelle DLL charger et où se trouvent les fonctions DLL, afin qu'il puisse les appeler. Lorsque votre programme est chargé, le système d'exploitation charge également la DLL pour vous, essentiellement ce qu'il appelle LoadLibrary
pour vous.
Dans d'autres systèmes d'exploitation (comme OS X et Linux), il fonctionne de manière similaire. La différence est que sur ces systèmes, l'éditeur de liens peut regarder directement la Bibliothèque dynamique (la .so
/.dynlib
fichier) et comprendre ce qui est nécessaire sans une bibliothèque statique séparée comme sur Windows.
Pour charger une bibliothèque dynamiquement, vous n'avez pas besoin de lier avec quoi que ce soit lié à la bibliothèque que vous souhaitez charger.
Comme d'autres l'ont déjà dit: ce qui est inclus dans un fichier .lib
sous Windows est inclus directement dans le .so
/.dynlib
sous Linux / OS X . Mais la question principale est... Pourquoi?
La solution * nix n'est-elle pas meilleure ?
Je pense que c'est le cas, mais le .lib
a un avantage. Le développeur lié à la DLL n'a pas réellement besoin d'avoir accès au fichier DLL lui-même.
Un tel scénario arrive-t-il souvent dans le monde réel? Vaut - il la peine de maintenir deux fichiers par fichier DLL? Je n'ai pas savoir.
Edit: Ok, les gars, rendons les choses encore plus confuses! Vous pouvez lier directement à une DLL sous Windows, en utilisant MinGW. Donc, tout le problème de la bibliothèque d'importation n'est pas directement lié à Windows lui-même. Extrait de sampleDLL article de mingw wiki:
La bibliothèque d'importation créée par l'option de l'éditeur de liens" --out-implib " est obligatoire iff (==si et seulement si) la DLL doit être interfacée à partir de certains Compilateur C / c++ autre que la chaîne D'outils MinGW. Le MinGW la chaîne est parfaitement heureux de lier directement contre la DLL créée. Plus de détails peut être trouvé dans le ld.fichiers d'informations exe qui font partie des binutils package (qui fait partie de la chaîne d'outils).
Linux nécessite également de lier, mais à la place contre un .Bibliothèque Lib il doit être lié à l'éditeur de liens dynamique /lib/ld-linux.so.2
, mais cela se produit généralement dans les coulisses lors de L'utilisation de GCC (cependant, si vous utilisez un assembleur, vous devez le spécifier manuellement).
Les deux approches, soit les fenêtres .L'approche LIB ou L'approche Linux dynamic linker linking, sont considérées en réalité comme des liaisons statiques. Il y a, cependant, une différence que dans Windows une partie du travail est fait au moment du lien bien qu'il reste a du travail au moment du chargement (Je ne suis pas sûr, mais je pense que le .Le fichier LIB est simplement pour que l'éditeur de liens connaisse le nom de la bibliothèque physique, les symboles ne sont cependant résolus qu'au moment du chargement), tandis que sous Linux, tout ce qui est autre que le lien vers l'éditeur de liens dynamique se produit au moment du chargement.
La liaison dynamique se réfère généralement à ouvrir manuellement le fichier DLL au moment de l'exécution (par exemple en utilisant LoadLinrary()), auquel cas la charge est entièrement sur le programmeur.
Dans la bibliothèque partagée, par exemple .dll
.dylib
et .so
, Il y a quelques informations sur le nom et l'adresse du symbole, comme ceci:
------------------------------------
| symbol's name | symbol's address |
|----------------------------------|
| Foo | 0x12341234 |
| Bar | 0xabcdabcd |
------------------------------------
Et la fonction load, telle que LoadLibrary
et dlopen
, charge la bibliothèque partagée et la rend disponible à utiliser.
GetProcAddress
et dlsym
vous trouvez l'adresse du symbole. Par exemple:
HMODULE shared_lib = LoadLibrary("asdf.dll");
void *symbol = GetProcAddress("Foo");
// symbol is 0x12341234
Dans windows, il existe .lib
fichier à utiliser .dll
. Lorsque vous créez un lien vers ce fichier .lib
, vous n'avez pas besoin d'appeler LoadLibrary
et GetProcAddress
, et utilisez simplement les bibliothèques partagées fonctionne comme si ce sont des fonctions "normales". Comment cela peut-il fonctionner?
En fait, le .lib
contient une information d'importation . C'est comme ça:
void *Foo; // please put the address of Foo there
void *Bar; // please put the address of Bar there
Lorsque le système d'exploitation charge votre programme (à proprement parler, votre module ), le système d'exploitation exécute automatiquement LoadLibrary
et GetProcAddress
.
Et si vous écrivez du code tel que Foo();
, le compilateur le convertit automatiquement en (*Foo)();
. Vous pouvez donc les utiliser comme s'il s'agissait de fonctions" normales".