107 votes

Comment fonctionne la bibliothèque d'importation ? Détails ?

Je sais que cela peut sembler assez basique pour les geeks. Mais je tiens à ce que ce soit clair comme de l'eau de roche.

Lorsque je souhaite utiliser une DLL Win32, j'appelle généralement les API telles que LoadLibrary() et GetProcAdderss(). Mais récemment, j'ai développé avec DirectX9, et j'ai besoin d'ajouter d3d9.lib , d3dx9.lib etc.

J'ai suffisamment entendu dire que LIB est pour l'établissement de liens statiques et DLL pour l'établissement de liens dynamiques.

Je pense donc que la LIB contient l'implémentation des méthodes et qu'elle est liée statiquement au moment de la liaison, dans le cadre du fichier EXE final. La DLL est chargée dynamiquement au moment de l'exécution et ne fait pas partie du fichier EXE final.

Mais parfois, il y a des fichiers LIB venir avec les fichiers DLL, donc :

  • À quoi servent ces fichiers LIB ?
  • Comment atteignent-ils leur objectif ?
  • Existe-t-il des outils qui me permettent d'inspecter l'intérieur de ces fichiers LIB ?

Mise à jour 1

Après avoir consulté wikipedia, je me souviens que ces fichiers LIB sont appelés bibliothèque d'importation . Mais je me demande comment cela fonctionne avec mon application principale et les DLL à charger dynamiquement.

Mise à jour 2

Comme l'a dit RBerteig, il y a du code stub dans les fichiers LIB nés avec les DLL. La séquence d'appel devrait donc être la suivante :

Mon application principale --> stub dans la LIB --> DLL cible réelle

Quelles sont donc les informations qui doivent figurer dans ces lettres d'intention ? J'ai pensé aux éléments suivants :

  • Le fichier LIB doit contenir le chemin complet de la DLL correspondante ; ainsi la DLL peut être chargée par le runtime.
  • L'adresse relative (ou le décalage de fichier ?) du point d'entrée de chaque méthode d'exportation DLL devrait être encodée dans le stub ; ainsi, des sauts/appels de méthode corrects pourraient être effectués.

Ai-je raison ? Y a-t-il autre chose ?

BTW : Existe-t-il un outil permettant d'inspecter une bibliothèque d'importation ? Si je peux la voir, il n'y aura plus de doutes.

122voto

RBerteig Points 23331

L'établissement d'un lien avec un fichier DLL peut se produire implicitement à compiler temps de liaison, ou explicitement au moment de l'exécution. Dans tous les cas, la DLL est chargée dans l'espace mémoire du processus et tous ses points d'entrée exportés sont disponibles pour l'application.

S'il est utilisé explicitement au moment de l'exécution, vous utilisez LoadLibrary() y GetProcAddress() pour charger manuellement la DLL et obtenir des pointeurs vers les fonctions que vous devez appeler.

S'ils sont liés implicitement lors de la création du programme, les stubs de chaque exportation de DLL utilisée par le programme sont liés au programme à partir d'une bibliothèque d'importation, et ces stubs sont mis à jour lorsque l'EXE et la DLL sont chargés lors du lancement du processus. (Oui, j'ai simplifié plus qu'un peu ici...)

Ces stubs doivent provenir de quelque part, et dans la chaîne d'outils de Microsoft, ils proviennent d'une forme spéciale de fichier .LIB appelée bibliothèque d'importation . Le fichier .LIB requis est généralement construit en même temps que la DLL et contient un stub pour chaque fonction exportée par la DLL.

Il est difficile de comprendre qu'une version statique de la même bibliothèque est également livrée sous la forme d'un fichier .LIB. Il n'existe pas de moyen trivial de les distinguer, si ce n'est que les bibliothèques d'importation de DLL sont généralement plus petites (souvent beaucoup plus petites) que la bibliothèque statique correspondante.

Si vous utilisez la chaîne d'outils GCC, vous n'avez d'ailleurs pas besoin de bibliothèques d'importation pour correspondre à vos DLL. La version de l'éditeur de liens Gnu portée sur Windows comprend directement les DLL et peut synthétiser à la volée la plupart des stubs nécessaires.

Mise à jour

Si vous ne pouvez pas résister à l'envie de savoir où se trouvent tous les écrous et les boulons et ce qui se passe réellement, il y a toujours quelque chose sur MSDN pour vous aider. L'article de Matt Pietrek Un regard approfondi sur le format de fichier exécutable portable Win32 est un aperçu très complet du format du fichier EXE et de la manière dont il est chargé et exécuté. Il a même été mis à jour pour couvrir .NET et d'autres aspects depuis sa parution initiale dans MSDN Magazine ca. 2002.

Il peut également être utile de savoir comment connaître exactement les DLL utilisées par un programme. L'outil pour cela est Dependency Walker, alias depends.exe. Une version de cet outil est incluse dans Visual Studio, mais la dernière version est disponible auprès de son auteur à l'adresse suivante http://www.dependencywalker.com/ . Il peut identifier toutes les DLL qui ont été spécifiées au moment de la liaison (chargement anticipé et chargement différé) et il peut également exécuter le programme et surveiller toutes les DLL supplémentaires qu'il charge au moment de l'exécution.

Mise à jour 2

J'ai reformulé une partie du texte précédent afin de le clarifier à la relecture et d'utiliser les termes de l'art implicite y lien explicite pour des raisons de cohérence avec MSDN.

Nous avons donc trois façons de rendre les fonctions de la bibliothèque disponibles pour être utilisées par un programme. La question suivante qui s'impose est donc la suivante "Comment choisir le mode d'utilisation ?"

L'enchaînement statique est la façon dont l'essentiel du programme lui-même est lié. Tous les fichiers objets sont répertoriés et rassemblés dans le fichier EXE par l'éditeur de liens. En cours de route, l'éditeur de liens s'occupe de tâches mineures telles que la correction des références aux symboles globaux afin que vos modules puissent appeler les fonctions les uns des autres. Les bibliothèques peuvent également être liées de manière statique. Les fichiers objets qui composent la bibliothèque sont rassemblés par un bibliothécaire dans un fichier .LIB dans lequel l'éditeur de liens recherche les modules contenant les symboles nécessaires. L'un des effets de la liaison statique est que seuls les modules de la bibliothèque qui sont utilisés par le programme sont liés à celui-ci ; les autres modules sont ignorés. Par exemple, la bibliothèque mathématique traditionnelle en C comprend de nombreuses fonctions de trigonométrie. Mais si vous établissez un lien avec elle et que vous utilisez cos() vous ne vous retrouvez pas avec une copie du code de l'application sin() o tan() à moins que vous n'ayez également appelé ces fonctions. Pour les grandes bibliothèques dotées d'un riche ensemble de fonctionnalités, cette inclusion sélective de modules est importante. Sur de nombreuses plates-formes telles que les systèmes embarqués, la taille totale du code disponible dans la bibliothèque peut être importante par rapport à l'espace disponible pour stocker un exécutable dans l'appareil. Sans l'inclusion sélective, il serait plus difficile de gérer les détails de la construction de programmes pour ces plateformes.

Toutefois, le fait de disposer d'une copie de la même dans chaque programme en cours d'exécution crée une charge sur un système qui exécute normalement de nombreux processus. Avec un système de mémoire virtuelle adapté, les pages de mémoire dont le contenu est identique ne doivent exister qu'une seule fois dans le système, mais peuvent être utilisées par de nombreux processus. Cela présente l'avantage d'augmenter les chances que les pages contenant du code soient identiques à une page dans le plus grand nombre possible d'autres processus en cours d'exécution. Mais si les programmes sont liés statiquement à la bibliothèque d'exécution, chacun d'entre eux dispose d'un mélange différent de fonctions, chacune étant disposée dans la carte mémoire du processus à des endroits différents, et il n'y a pas beaucoup de pages de code partageables, à moins qu'il ne s'agisse d'un programme qui, à lui seul, est exécuté dans plus d'un processus. L'idée d'une DLL a donc gagné un autre avantage majeur.

Une DLL pour une bibliothèque contient toutes ses fonctions, prêtes à être utilisées par n'importe quel programme client. Si de nombreux programmes chargent cette DLL, ils peuvent tous partager ses pages de code. Tout le monde y gagne. (Enfin, jusqu'à ce que vous mettiez à jour une DLL avec une nouvelle version, mais cela ne fait pas partie de cette histoire. Pour en savoir plus, consultez Google DLL Hell).

Le premier grand choix à faire lors de la planification d'un nouveau projet est donc celui d'un lien dynamique ou statique. Avec la liaison statique, vous avez moins de fichiers à installer et vous êtes à l'abri des mises à jour par des tiers d'une DLL que vous utilisez. Cependant, votre programme est plus volumineux et n'est pas un aussi bon citoyen de l'écosystème Windows. Avec la liaison dynamique, vous avez plus de fichiers à installer, vous pouvez avoir des problèmes avec une tierce partie qui met à jour une DLL que vous utilisez, mais vous êtes généralement plus respectueux des autres processus du système.

Un grand avantage d'une DLL est qu'elle peut être chargée et utilisée sans avoir à recompiler ou même à relier le programme principal. Cela peut permettre à un fournisseur de bibliothèque tiers (comme Microsoft et le runtime C, par exemple) de corriger un bogue dans sa bibliothèque et de le distribuer. Dès qu'un utilisateur final installe la DLL mise à jour, il bénéficie immédiatement de cette correction de bogue dans tous les programmes qui utilisent cette DLL. (À moins qu'il n'y ait des problèmes, voir l'enfer des DLL).

L'autre avantage provient de la distinction entre chargement implicite et chargement explicite. Si vous faites l'effort supplémentaire d'un chargement explicite, il se peut que la DLL n'ait même pas existé lorsque le programme a été écrit et publié. Cela permet de mettre en place des mécanismes d'extension capables de découvrir et de charger des plugins, par exemple.

7voto

jxramos Points 79

Ces fichiers de bibliothèque d'importation .LIB sont utilisés dans la propriété de projet suivante, Linker->Input->Additional Dependencies Dans l'exemple ci-dessous, pour ne pas obtenir d'erreurs de l'éditeur de liens, je dois faire référence aux dll A, B, C et D par le biais des fichiers .LIB de la bibliothèque d'importation. Dans l'exemple ci-dessous, pour éviter les erreurs de l'éditeur de liens, je dois faire référence aux dll A, B, C et D par l'intermédiaire de leurs fichiers lib. (Remarque : pour que l'éditeur de liens puisse trouver ces fichiers, il peut être nécessaire d'inclure leur chemin de déploiement dans le champ Linker->General->Additional Library Directories sinon vous obtiendrez un message d'erreur concernant l'impossibilité de trouver l'un des fichiers de librairie fournis).

Linker->Input->Additional Dependencies

Si votre solution consiste à construire toutes les bibliothèques dynamiques, vous pouvez éviter cette spécification explicite des dépendances en vous appuyant sur les drapeaux de référence exposés dans la section Common Properties->Framework and References dialogue. Ces drapeaux semblent effectuer automatiquement la liaison en votre nom en utilisant les fichiers *.lib. Framework and References

Il s'agit toutefois, comme il est dit, d'un Communs qui n'est pas spécifique à une configuration ou à une plate-forme. Si vous devez prendre en charge un scénario de construction mixte comme dans notre application, nous avions une configuration de construction pour rendre une construction statique et une configuration spéciale qui construisait une construction contrainte d'un sous-ensemble d'assemblages qui étaient déployés en tant que bibliothèques dynamiques. J'ai utilisé l'option Use Library Dependency Inputs y Link Library Dependencies est fixé à true dans différents cas pour que les choses se construisent et se réalisent plus tard pour simplifier les choses, mais lorsque j'ai introduit mon code dans les constructions statiques, j'ai introduit une tonne d'avertissements du linker et la construction était incroyablement lente pour les constructions statiques. J'ai fini par introduire un tas d'avertissements de ce type...

warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored  D.lib(JSource.obj)

J'ai fini par utiliser la spécification manuelle de Additional Dependencies pour satisfaire l'éditeur de liens pour les constructions dynamiques tout en satisfaisant les constructeurs statiques en n'utilisant pas une propriété commune qui les ralentissait. Lorsque je déploie le sous-ensemble dynamique, je ne déploie que les fichiers dll, car ces fichiers lib ne sont utilisés qu'au moment de l'édition de liens, et non au moment de l'exécution.

3voto

smwikipedia Points 5491

2voto

zacsek Points 457

Il existe trois types de bibliothèques : les bibliothèques statiques, les bibliothèques partagées et les bibliothèques chargées dynamiquement.

Les bibliothèques statiques sont liées au code lors de la phase de liaison, de sorte qu'elles se trouvent réellement dans l'exécutable, contrairement à la bibliothèque partagée, qui n'a que des stubs (symboles) à rechercher dans le fichier de la bibliothèque partagée, qui est chargé au moment de l'exécution avant que la fonction principale ne soit appelée.

Les bibliothèques chargées dynamiquement ressemblent beaucoup aux bibliothèques partagées, sauf qu'elles sont chargées quand et si le code que vous avez écrit en a besoin.

0voto

邱怡霖 Points 3

A mon avis, il y a deux méthodes pour lier une dll à un exe.

  1. Utiliser implicitement la dll et la bibliothèque d'importation (fichier .lib)

  2. Utiliser explicitement des fonctions comme loadlibrary()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X