Différence entre les objects partagés (.so), les bibliothèques statiques (.a) et les DLL (.so)?

J’ai participé à un débat sur les bibliothèques sous Linux et je voudrais confirmer certaines choses.

C’est à ma compréhension (corrigez-moi si je me trompe et je vais éditer mon post plus tard), qu’il y a deux façons d’utiliser les bibliothèques lors de la création d’une application:

  1. Bibliothèques statiques (fichiers .a): au moment du lien, une copie de la bibliothèque entière est placée dans l’application finale afin que les fonctions de la bibliothèque soient toujours disponibles pour l’application appelante
  2. Objets partagés (fichiers .so): au moment du lien, l’object est juste vérifié par rapport à son API via le fichier d’en-tête (.h) correspondant. La bibliothèque n’est pas réellement utilisée jusqu’à l’exécution, où elle est nécessaire.

L’avantage évident des bibliothèques statiques est qu’elles permettent à l’application entière d’être autonome, alors que les bibliothèques dynamics ont l’avantage de pouvoir remplacer le fichier “.so” (c.-à-d. S’il doit être mis à jour à cause d’une sécurité). bug) sans exiger que l’application de base soit recompilée.

J’ai entendu certaines personnes faire une distinction entre les objects partagés et les bibliothèques liées dynamics (DLL), même s’il s’agit de fichiers “.so”. Existe-t-il une distinction entre les objects partagés et les DLL en ce qui concerne le développement C / C ++ sous Linux ou tout autre système d’exploitation compatible POSIX (à savoir: MINIX, UNIX, QNX, etc.)? On m’a dit que l’une des principales différences (jusqu’à présent) est que les objects partagés ne sont utilisés qu’au moment de l’exécution, tandis que les DLL doivent être ouverts en premier en utilisant l’appel dlopen () dans l’application.

Enfin, certains développeurs ont également mentionné des “archives partagées”, qui, à ma connaissance, sont également des bibliothèques statiques, mais qui ne sont jamais utilisées directement par une application. Au lieu de cela, d’autres bibliothèques statiques seront liées aux “archives partagées” pour extraire certaines fonctions / ressources de l’archive partagée dans la bibliothèque statique en cours de construction.

Merci à tous pour votre aide.

Mettre à jour


Dans le contexte dans lequel ces termes m’ont été fournis, j’ai découvert les légères différences entre ces termes, qui peuvent même être des expressions familières dans mon secteur:

  1. Objet partagé: bibliothèque automatiquement liée à un programme au démarrage du programme et existant en tant que fichier autonome. La bibliothèque est incluse dans la liste de liens au moment de la compilation (ex: LDOPTS+=-lmylib pour un fichier de bibliothèque nommé mylib.so ). La bibliothèque doit être présente à la compilation et au démarrage de l’application.
  2. Bibliothèque statique: Bibliothèque fusionnée dans le programme lui-même au moment de la construction pour une seule application (plus grande) contenant le code de l’application et le code de la bibliothèque qui est automatiquement lié à un programme lors de la construction du programme et le binary final contenant à la fois le programme principal et la bibliothèque elle-même existent en tant que fichier binary autonome unique. La bibliothèque est incluse dans la liste de liens au moment de la compilation (ex: LDOPTS+=-lmylib pour un fichier de bibliothèque nommé mylib.a). La bibliothèque doit être présente au moment de la compilation.
  3. DLL: Essentiellement identique à un object partagé, mais plutôt que d’être inclus dans la liste de liens au moment de la compilation, la bibliothèque est chargée via les commandes dlopen() / dlsym() afin que la bibliothèque n’ait pas besoin d’être présente lors de la compilation. le programme à comstackr. De plus, la bibliothèque n’a pas besoin d’être (nécessairement) présente au démarrage ou à la compilation de l’application , car elle n’est nécessaire qu’au moment où les dlopen / dlsym sont effectués.
  4. Archive partagée: essentiellement identique à une bibliothèque statique, mais compilée avec les indicateurs “export-shared” et “-fPIC”. La bibliothèque est incluse dans la liste de liens au moment de la compilation (ex: LDOPTS + = – lmylib S pour un fichier de bibliothèque nommé mylib S .a). La distinction entre les deux est que cet indicateur supplémentaire est requirejs si un object partagé ou une DLL souhaite lier statiquement l’archive partagée à son propre code ET être capable de mettre les fonctions de l’object partagé à la disposition d’autres programmes, plutôt que de les utiliser interne à la DLL. Ceci est utile dans le cas où quelqu’un vous fournit une bibliothèque statique et que vous souhaitez le reconditionner en tant que SO. La bibliothèque doit être présente au moment de la compilation.

Mise à jour supplémentaire

La distinction entre ” DLL ” et ” shared library ” était juste une expression familière (paresseuse et inexacte) dans la société dans laquelle je travaillais à l’époque (les développeurs Windows étant obligés de passer au développement Linux et le terme bloqué), respectant les descriptions Noté ci-dessus.

De plus, le littéral ” S ” suivant le nom de la bibliothèque, dans le cas des “archives partagées”, n’était qu’une convention utilisée dans cette société et non dans l’indussortinge en général.

J’ai toujours pensé que les DLL et les objects partagés ne sont que des termes différents pour la même chose – Windows les appelle DLL, tandis que sur les systèmes UNIX ils sont des objects partagés, avec le terme général – bibliothèque liée dynamicment – couvrant les deux open a .so sous UNIX s’appelle dlopen() après ‘dynamic library’).

Ils sont en effet uniquement liés au démarrage de l’application, mais votre notion de vérification par rapport au fichier d’en-tête est incorrecte. Le fichier d’en-tête définit les prototypes requirejs pour comstackr le code qui utilise la bibliothèque, mais au moment du lien, l’éditeur de liens recherche l’intérieur de la bibliothèque pour s’assurer que les fonctions nécessaires sont réellement présentes. L’éditeur de liens doit trouver les corps de fonctions quelque part au moment de la liaison ou il générera une erreur. Il le fait aussi à l’exécution, car comme vous le soulignez à juste titre, la bibliothèque elle-même a peut-être changé depuis la compilation du programme. C’est pourquoi la stabilité d’ABI est si importante dans les bibliothèques de plates-formes, car la modification de l’ABI est ce qui rompt les programmes existants compilés avec les anciennes versions.

Les bibliothèques statiques ne sont que des ensembles de fichiers d’object directement issus du compilateur, tout comme ceux que vous construisez vous-même dans le cadre de la compilation de votre projet. a chuté exactement de la même manière.

Une bibliothèque statique (.a) est une bibliothèque qui peut être directement liée au fichier exécutable final produit par l’éditeur de liens, elle y est contenue et il n’est pas nécessaire d’avoir la bibliothèque dans le système sur lequel le fichier exécutable sera déployé.

Une bibliothèque partagée (.so) est une bibliothèque liée mais non incorporée dans le fichier exécutable final. Elle sera donc chargée lors du lancement de l’exécutable et devra être présente dans le système sur lequel le fichier exécutable est déployé.

Une bibliothèque de liens dynamics sur Windows (.dll) ressemble à une bibliothèque partagée (.so) sur Linux mais il existe certaines différences entre les deux implémentations liées au système d’exploitation (Windows vs Linux):

Une DLL peut définir deux types de fonctions: exscope et interne. Les fonctions exscopes sont destinées à être appelées par d’autres modules, ainsi qu’à partir de la DLL où elles sont définies. Les fonctions internes sont généralement destinées à être appelées uniquement depuis la DLL où elles sont définies.

Une bibliothèque SO sous Linux n’a pas besoin d’instructions d’exportation spéciales pour indiquer les symboles exportables, car tous les symboles sont disponibles pour un processus d’interrogation.

Je peux élaborer sur les détails des DLL dans Windows pour aider à clarifier ces mystères à mes amis ici dans * NIX-land …

Une DLL est comme un fichier d’object partagé. Les deux sont des images, prêtes à être chargées en mémoire par le chargeur de programme du système d’exploitation correspondant. Les images sont accompagnées de différents éléments de métadonnées pour aider les éditeurs de liens et les chargeurs à créer les associations nécessaires et à utiliser la bibliothèque de code.

Les DLL Windows ont une table d’exportation. Les exportations peuvent être par nom ou par position de la table (numérique). Cette dernière méthode est considérée comme “old school” et est beaucoup plus fragile – la reconstruction de la DLL et la modification de la position d’une fonction dans la table se termineront par un désastre, alors qu’il n’ya pas de problème. Donc, oubliez cela comme un problème, mais soyez conscient que c’est là si vous travaillez avec du code “dinosaure” tel que des librairies tierces.

Les DLL Windows sont construites en compilant et en liant, comme vous le feriez pour un EXE (application exécutable), mais la DLL est conçue pour ne pas être autonome, tout comme un SO est destiné à être utilisé par une application, par liaison au moment de la liaison (la référence au SO est intégrée dans les métadonnées du binary de l’application, et le chargeur du programme du SE charge automatiquement les SO référencés). Les DLL peuvent référencer d’autres DLL, tout comme les SO peuvent référencer d’autres SO.

Sous Windows, les DLL ne fourniront que des points d’entrée spécifiques. Celles-ci sont appelées “exportations”. Le développeur peut utiliser un mot-clé de compilation spécial pour rendre un symbole visible de l’extérieur (vers d’autres éditeurs de liens et le chargeur dynamic), ou les exportations peuvent être répertoriées dans un fichier de définition de module utilisé au moment du lien. Étant créé. La pratique moderne consiste à décorer la définition de la fonction avec le mot-clé pour exporter le nom du symbole. Il est également possible de créer des fichiers d’en-tête avec des mots-clés qui déclareront ce symbole comme étant importé depuis une DLL en dehors de l’unité de compilation actuelle. Recherchez les mots-clés __declspec (dllexport) et __declspec (dllimport) pour plus d’informations.

Une des caractéristiques intéressantes des DLL est qu’elles peuvent déclarer une fonction standard de gestionnaire «on load / unload». Chaque fois que la DLL est chargée ou déchargée, la DLL peut effectuer une initialisation ou un nettoyage, selon le cas. Cela correspond bien à une DLL en tant que gestionnaire de ressources orienté object, tel qu’un pilote de périphérique ou une interface d’object partagé.

Lorsqu’un développeur souhaite utiliser une DLL déjà construite, il doit soit faire référence à une “bibliothèque d’exportation” (* .LIB) créée par le développeur DLL lors de la création de la DLL, soit charger la DLL au moment de l’exécution adresse du point d’entrée par nom via les mécanismes LoadLibrary () et GetProcAddress (). La plupart du temps, la mise en relation avec un fichier LIB (qui contient simplement les métadonnées de l’éditeur de liens pour les points d’entrée exportés de la DLL) est la manière dont les DLL sont utilisées. Le chargement dynamic est généralement réservé à la mise en œuvre du «polymorphism» ou de la «configurabilité à l’exécution» dans les comportements du programme (access aux modules complémentaires ou aux fonctionnalités définies ultérieurement, à savoir «plug-ins»).

La manière de faire de Windows peut parfois créer une certaine confusion. Le système utilise l’extension .LIB pour faire référence aux bibliothèques statiques normales (archives, telles que les fichiers POSIX * .a) et aux bibliothèques “stub d’exportation” nécessaires pour lier une application à une DLL au moment du lien. Donc, il faut toujours regarder si un fichier * .LIB a un fichier * .DLL du même nom. Si ce n’est pas le cas, il y a de bonnes chances que le fichier * .LIB soit une archive de bibliothèque statique et ne pas exporter les métadonnées de liaison pour une DLL.

Vous avez raison de dire que les fichiers statiques sont copiés dans l’application au moment de la liaison et que les fichiers partagés sont simplement vérifiés au moment de la liaison et chargés au moment de l’exécution.

L’appel dlopen ne concerne pas uniquement les objects partagés, si l’application souhaite le faire lors de l’exécution en son nom, sinon les objects partagés sont chargés automatiquement au démarrage de l’application. DLLS et .so sont la même chose. le dlopen existe pour append des capacités de chargement dynamic encore plus fines pour les processus. Vous n’avez pas besoin d’utiliser dlopen pour ouvrir / utiliser les DLL, cela se produit également au démarrage de l’application.