web-dev-qa-db-fra.com

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

Je sais que cela peut sembler assez basique pour les geeks. Mais je veux que ce soit clair.

Lorsque je veux utiliser une DLL Win32, j'appelle généralement les API comme LoadLibrary () et GetProcAdderss (). Mais récemment, je développe avec DirectX9, et je dois ajouter des fichiers d3d9.lib, d3dx9.lib, etc.

J'en ai assez entendu que LIB est pour la liaison statique et DLL est pour la liaison dynamique.

Donc, ma compréhension actuelle est que LIB contient l'implémentation des méthodes et est lié statiquement au moment du lien dans le cadre du fichier EXE final. While DLL est chargé dynamiquement au moment de l'exécution et ne fait pas partie du fichier EXE final.

Mais parfois, certains fichiers LIB viennent avec les fichiers DLL, donc:

  • À quoi servent ces fichiers LIB?
  • Comment parviennent-ils à ce à quoi ils sont destinés?
  • Y a-t-il des outils qui peuvent me permettre d'inspecter les internes de ces fichiers LIB?

Mise à jour 1

Après avoir vérifié wikipedia, je me souviens que ces fichiers LIB s'appellent import library . Mais je me demande comment cela fonctionne avec mon application principale et les DLL à charger dynamiquement.

Mise à jour 2

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

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

Quelles informations devraient donc figurer dans ces LIB? Je pourrais penser à ce qui suit:

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

Ai-je raison là-dessus? Y a-t-il quelque chose de plus?

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

73
smwikipedia

Lien vers un DLL peut se produire implicitement à compiler temps de liaison, ou explicitement au moment de l'exécution. Dans les deux cas, le DLL finit par être chargé dans l'espace mémoire du processus et tous ses points d'entrée exportés sont disponibles pour l'application.

En cas d'utilisation explicite au moment de l'exécution, vous utilisez LoadLibrary() et GetProcAddress() pour charger manuellement le 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 DLL exportation utilisée par le programme sont liés au programme à partir d'une bibliothèque d'importation, et ces stubs sont mis à jour en tant qu'EXE et = DLL sont chargés lorsque le processus démarre. (Oui, j'ai simplifié plus d'un peu ici ...)

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

De façon confuse, une version statique de la même bibliothèque serait également livrée sous forme de fichier .LIB. Il n'y a aucun moyen trivial de les différencier, sauf que les LIB qui sont des bibliothèques d'importation pour les DLL seront généralement plus petites (souvent beaucoup plus petites) que la LIB statique correspondante.

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

Mise à jour

Si vous ne pouvez tout simplement pas résister à savoir où se trouvent réellement tous les écrous et 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 examen approfondi du format de fichier exécutable portable Win32 est un aperçu très complet du format du Fichier EXE et comment il est chargé et exécuté. Il a même été mis à jour pour couvrir .NET et plus encore depuis son apparition dans MSDN Magazine ca. 2002.

En outre, il peut être utile de savoir exactement quelles DLL sont utilisées par un programme. L'outil pour cela est Dependency Walker, alias depend.exe. Une version de celui-ci est incluse avec Visual Studio, mais la dernière version est disponible auprès de son auteur à http://www.dependencywalker.com/ . Il peut identifier toutes les DLL qui ont été spécifiées au moment de la liaison (à la fois au chargement anticipé et au chargement différé) et il peut également exécuter le programme et surveiller 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 pour le clarifier lors de la relecture et pour utiliser les termes de l'art implicite et lien explicite pour la cohérence avec MSDN .

Nous avons donc trois façons de rendre les fonctions de bibliothèque disponibles pour être utilisées par un programme. La question de suivi évidente est alors: "Comment choisir quelle voie?"

La liaison statique est la façon dont la majeure partie du programme lui-même est liée. Tous vos 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 des tâches mineures telles que la fixation 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 statiquement. Les fichiers objets qui composent la bibliothèque sont rassemblés par un bibliothécaire dans un fichier .LIB que l'éditeur de liens recherche pour les modules contenant les symboles nécessaires. Un effet de la liaison statique est que seuls les modules de la bibliothèque qui sont utilisés par le programme y sont liés; les autres modules sont ignorés. Par exemple, la bibliothèque mathématique C traditionnelle comprend de nombreuses fonctions de trigonométrie. Mais si vous le liez et utilisez cos(), vous ne vous retrouvez pas avec une copie du code pour sin() ou tan() sauf si vous avez également appelé ces fonctions . Pour les grandes bibliothèques avec un riche ensemble de fonctionnalités, cette inclusion sélective des modules est importante. Sur de nombreuses plates-formes telles que les systèmes embarqués, la taille totale du code disponible pour une utilisation dans la bibliothèque peut être importante par rapport à l'espace disponible pour stocker un exécutable dans le périphérique. Sans inclusion sélective, il serait plus difficile de gérer les détails des programmes de construction pour ces plates-formes.

Cependant, avoir une copie de la bibliothèque même dans chaque programme en cours d'exécution crée une charge pour un système qui exécute normalement beaucoup de processus. Avec le bon type de système de mémoire virtuelle, les pages de mémoire ayant un contenu identique n'existent qu'une seule fois dans le système, mais peuvent être utilisées par de nombreux processus. Cela crée un avantage pour augmenter les chances que les pages contenant du code soient probablement identiques à certaines pages dans autant d'autres processus en cours d'exécution que possible. Mais, si les programmes se lient statiquement à la bibliothèque d'exécution, alors chacun a un mélange différent de fonctions, chacune étant présentée dans ce processus, la carte de la mémoire à différents endroits, et il n'y a pas beaucoup de pages de code partageables à moins que ce soit un programme qui seul soit exécuter plus de processus. Ainsi, l'idée d'un DLL a gagné un autre avantage majeur.

A 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 codes. Tout le monde y gagne. (Eh bien, jusqu'à vous mettez à jour un DLL avec la nouvelle version, mais cela ne fait pas partie de cette histoire. Google DLL Enfer pour ce côté du conte.)

Ainsi, le premier grand choix à faire lors de la planification d'un nouveau projet est entre la liaison dynamique et statique. Avec la liaison statique, vous avez moins de fichiers à installer et vous êtes à l'abri des tiers qui mettent à jour un DLL que vous utilisez. Cependant, votre programme est plus grand et il n'est pas aussi bon citoyen de l'écosystème Windows. Avec la liaison dynamique, vous avez plus de fichiers à installer, vous pourriez avoir des problèmes avec une tierce partie mettant à jour une DLL que vous utilisez, mais vous êtes généralement plus convivial avec les autres processus du système .

Un gros avantage d'un DLL est qu'il peut être chargé et utilisé sans recompiler ni même relier le programme principal. Cela peut permettre à un fournisseur de bibliothèque tiers (pensez à Microsoft et au runtime C, par exemple ) pour corriger un bogue dans sa bibliothèque et le distribuer. Une fois qu'un utilisateur final installe la DLL mise à jour, il obtient immédiatement l'avantage de ce correctif de bogue dans tous les programmes qui utilisent cette DLL. (Sauf si cela casse des choses. Voir DLL Enfer.)

L'autre avantage vient de la distinction entre chargement implicite et explicite. Si vous faites l'effort supplémentaire de chargement explicite, alors le DLL peut-être même pas existé lorsque le programme a été écrit et publié. Cela permet des mécanismes d'extension qui peuvent découvrir et charger des plugins, par exemple .

87
RBerteig

Ces fichiers de bibliothèque d'importation .LIB sont utilisés dans la propriété de projet suivante, Linker->Input->Additional Dependencies, lors de la création d'un groupe de DLL qui nécessitent des informations supplémentaires au moment de la liaison, fournies par les fichiers .LIB de la bibliothèque d'importation. Dans l'exemple ci-dessous pour ne pas obtenir d'erreurs de l'éditeur de liens, je dois faire référence aux A, B, C et D de la DLL à travers leurs fichiers lib. (notez que l'éditeur de liens pour trouver ces fichiers peut être nécessaire d'inclure leur chemin de déploiement dans Linker->General->Additional Library Directories sinon, vous obtiendrez une erreur de génération si vous ne trouvez aucun des fichiers lib fournis.)

Linker->Input->Additional Dependencies

Si votre solution crée toutes les bibliothèques dynamiques, vous avez peut-être pu éviter cette spécification de dépendance explicite en vous appuyant plutôt sur les indicateurs de référence exposés sous Common Properties->Framework and References boîte de dialogue. Ces indicateurs semblent effectuer automatiquement la liaison en votre nom à l'aide des fichiers * .lib. Framework and References

Cependant, c'est comme il est dit Propriétés communes , qui ne sont pas spécifiques à la configuration ou à la plate-forme. Si vous devez prendre en charge un scénario de génération mixte, comme dans notre application, nous avions une configuration de génération pour rendre une génération statique et une configuration spéciale qui générait une génération contrainte d'un sous-ensemble d'assemblys qui étaient déployés en tant que bibliothèques dynamiques. J'avais utilisé le Use Library Dependency Inputs et Link Library Dependencies drapeaux mis à true dans divers cas pour obtenir des choses à construire et réaliser plus tard pour simplifier les choses, mais lors de l'introduction de mon code dans les builds statiques, j'ai introduit une tonne d'avertissements de l'éditeur de liens et la build était incroyablement lente pour les builds statiques. J'ai fini par introduire un tas de ce genre d'avertissements ...

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

Et je me suis retrouvé en utilisant la spécification manuelle de Additional Dependencies pour satisfaire l'éditeur de liens pour les builds dynamiques tout en gardant les builders statiques heureux en n'utilisant pas une propriété commune qui les ralentissait. Lorsque je déploie la génération de sous-ensemble dynamique, je déploie uniquement les fichiers dll car ces fichiers lib ne sont utilisés qu'au moment de la liaison, pas au moment de l'exécution.

3
jxramos

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

Les bibliothèques statiques sont liées au code lors de la phase de liaison, elles se trouvent donc dans l'exécutable, contrairement à la bibliothèque partagée, qui n'a que des talons (symboles) à rechercher dans le fichier de bibliothèque partagée, qui est chargé au moment de l'exécution avant la la fonction principale est 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 besoin s'en fait sentir par le code que vous avez écrit.

2
zacsek
0
smwikipedia