J'ai participé à certains débats concernant les bibliothèques sous Linux et voudrais confirmer certaines choses.
Si je me trompe, corrigez-moi si je me trompe et que je modifierai mon message ultérieurement), il existe deux manières d'utiliser les bibliothèques pour créer une application:
L’avantage évident des bibliothèques statiques est qu’elles permettent à l’ensemble de l’application d’être autonome. L’avantage des bibliothèques dynamiques est que le fichier ".so" peut être remplacé (c'est-à-dire: au cas où il faudrait le mettre à jour pour des raisons de sécurité). bug) sans nécessiter de recompiler l’application de base.
J'ai entendu certaines personnes faire une distinction entre les objets partagés et les bibliothèques liées dynamiques (DLL), même s'il s'agit de deux fichiers ".so". Existe-t-il une distinction entre les objets 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 (par exemple, MINIX, UNIX, QNX, etc.)? On me dit que la différence essentielle (jusqu’à présent) est que les objets partagés ne sont utilisés qu’au moment de l’exécution, tandis que les DLL doivent être ouvertes en premier à l’aide de l’appel dlopen () de l’application.
Enfin, j'ai aussi entendu certains développeurs mentionner les "archives partagées", qui, à mon sens, 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 (mais pas toutes) de l'archive partagée dans la bibliothèque statique en construction.
Merci à tous d'avance pour votre aide.
Dans le contexte dans lequel ces termes m'ont été fournis, il s'agissait effectivement de termes erronés utilisés par une équipe de développeurs Windows qui devaient apprendre Linux. J'ai essayé de les corriger, mais les normes de langage (incorrectes sont restées inchangées.
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.LDOPTS+=-lmylib
pour un fichier de bibliothèque nommé mylib.a). La bibliothèque doit être présente à la compilation.dlopen()
/dlsym()
, de sorte que la bibliothèque n'a pas besoin d'être présente. au moment de la compilation du programme. De plus, la bibliothèque n'a pas besoin d'être présente (nécessairement) au démarrage de l'application ou à la compilation, car elle n'est nécessaire qu'au moment où les appels dlopen
/dlsym
sont effectués. .S
pour un fichier de bibliothèque nommé mylibS
. A). La distinction entre les deux est que cet indicateur supplémentaire est requis si un objet partagé ou DLL veut lier de manière statique l'archive partagée dans son propre code ET pouvoir mettre les fonctions de l'objet partagé à la disposition d'autres programmes. , plutôt que de simplement les utiliser en interne à la DLL. Cela est utile lorsque quelqu'un vous fournit une bibliothèque statique et que vous souhaitez le reconditionner en tant que responsable de la sécurité. La bibliothèque doit être présente à la compilation.La distinction entre "DLL
" et "shared library
" n'était qu'un langage familier (paresseux, inexact) dans la société dans laquelle je travaillais à l'époque (les développeurs Windows étant obligés de passer au développement Linux, coincé), en adhérant aux descriptions notées ci-dessus.
De plus, le dernier caractère "S
" après le nom de la bibliothèque, dans le cas des "archives partagées", était simplement une convention utilisée dans cette société et non dans l'industrie en général.
J'ai toujours pensé que les DLL et les objets 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 objets partagés, avec le terme général - bibliothèque liée dynamiquement - couvrant ouvrir un fichier .so sous UNIX est appelé dlopen()
après "bibliothèque dynamique").
Ils ne sont en effet liés qu'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 nécessaires pour compiler le code qui utilise la bibliothèque, mais au moment du lien, l'éditeur de liens examine l'intérieur de la bibliothèque pour s'assurer que les fonctions dont il a besoin sont bien présentes. L'éditeur de liens doit trouver les corps des fonctions quelque part au moment du lien, sinon cela produira une erreur. Il le fait ÉGALEMENT à l'exécution, car, comme vous l'avez souligné à juste titre, la bibliothèque elle-même pourrait avoir changé depuis la compilation du programme. C'est la raison pour laquelle la stabilité ABI est si importante dans les bibliothèques de plates-formes, car le changement ABI est ce qui rompt les programmes existants compilés avec les versions antérieures.
Les bibliothèques statiques ne sont que des ensembles de fichiers objets provenant directement du compilateur, à l'instar de ceux que vous construisez vous-même dans le cadre de la compilation de votre projet. chuté de la même manière.
Une bibliothèque statique (.a) est une bibliothèque qui peut être liée directement dans l'exécutable final produit par l'éditeur de liens. Elle est contenue dans ce dernier et il n'est pas nécessaire de la placer dans le système où l'exécutable sera déployé.
Un bibliothèque partagée (.so) est une bibliothèque liée mais non intégrée dans l'exécutable final. Elle sera donc chargée lors du lancement de l'exécutable et doit être présente dans le système où l'exécutable est déployé. .
Une bibliothèque de liens dynamiques sous Windows (.dll) est semblable à une bibliothèque partagée (.so) sous Linux, mais il existe quelques différences entre les deux implémentations liées au système d'exploitation (Windows vs Linux):
Un DLL peut définir deux types de fonctions: exportées et internes. Les fonctions exportées sont destinées à être appelées par d'autres modules, ainsi que depuis la DLL où elles sont définies. Les fonctions internes sont généralement conçues pour être appelées uniquement à partir de la DLL où elles sont définies.
Une bibliothèque SO sous Linux n'a pas besoin d'instruction d'exportation spéciale 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 ...
Un DLL ressemble à un fichier d'objet partagé. Les deux sont des images, prêtes à être chargées en mémoire par le programme de chargement du système d'exploitation correspondant. Les images sont accompagnées de diverses bits de métadonnées pour aider les lieurs et les chargeurs à faire 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 table (numérique). Cette dernière méthode est considérée comme "vieille école" et est beaucoup plus fragile - reconstruire la DLL et changer la position d'une fonction dans le tableau aboutira à un désastre, alors qu'il n'y a pas de réel problème si la liaison de l'entrée points est par nom. Donc, oubliez cela comme un problème, mais sachez que c'est là si vous travaillez avec du code "dinosaure" tel que des bibliothèques de fournisseurs tiers.
Les DLL Windows sont créées en compilant et en liant, comme vous le feriez pour un EXE (application exécutable), mais le DLL ne doit pas être autonome, tout comme un SO être utilisé par une application, soit par un chargement dynamique, soit par une liaison au moment du lien (la référence au SO est incorporée dans les métadonnées du binaire de l'application, et le programme de chargement du système d'exploitation charge automatiquement le responsable de sécurité référencé) . Les DLL peuvent référencer d'autres DLL, tout comme les SO peuvent faire référence à d'autres SO.
Sous Windows, les DLL ne rendent disponibles que des points d’entrée spécifiques. Celles-ci sont appelées "exportations". Le développeur peut utiliser un mot clé spécial du compilateur pour rendre un symbole visible de l’extérieur (aux autres lieurs et au chargeur dynamique), ou bien les exportations peuvent être répertoriées dans un fichier de définition de module utilisé au moment du lien lorsque le DLL lui-même est en cours de création. La pratique moderne consiste à décorer la définition de fonction avec le mot-clé permettant d'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é d’un fichier DLL situé en dehors de l’unité de compilation en cours. Recherchez les mots clés __declspec (dllexport) et __declspec (dllimport) pour plus d'informations.
L'une des fonctionnalités intéressantes des DLL est qu'elles peuvent déclarer une fonction de gestionnaire standard "au chargement/déchargement". 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 parfaitement à avoir un DLL en tant que gestionnaire de ressources orienté objet, tel qu'un pilote de périphérique ou une interface d'objet partagé.
Lorsqu'un développeur souhaite utiliser une DLL déjà construite, il doit référencer une "bibliothèque d'exportation" (* .LIB) créée par le développeur DLL lors de la création de la DLL ou charger explicitement le fichier DLL au moment de l'exécution et demandez l'adresse du point d'entrée par son nom via les mécanismes LoadLibrary () et GetProcAddress (). La plupart du temps, la liaison à 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) correspond à la façon dont les DLL sont utilisées. Le chargement dynamique est généralement réservé à l'implémentation de "polymorphisme" ou de "configurabilité à l'exécution" dans les comportements de programme (accès à des modules complémentaires ou à des fonctionnalités définies ultérieurement, également appelés "plugins").
La façon de faire de Windows peut parfois être source de confusion. le système utilise l'extension .LIB pour faire référence aux bibliothèques statiques normales (archives, comme les fichiers POSIX * .a) et aux bibliothèques "stub d'exportation" nécessaires pour lier une application à un DLL au moment du lien. Il faut donc toujours vérifier si un fichier * .LIB a un fichier * .DLL du même nom; sinon, il y a de bonnes chances que le fichier * .LIB soit une archive de bibliothèque statique et non d'exporter des 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 du lien et que les fichiers partagés sont simplement vérifiés au moment du lien et chargés au moment de l'exécution.
L'appel dlopen ne concerne pas uniquement les objets partagés, si l'application souhaite le faire en son nom au moment de l'exécution, sinon les objets partagés sont chargés automatiquement au démarrage de l'application. DLLS et .so sont la même chose. dlopen existe pour ajouter des capacités de chargement dynamique encore plus fines pour les processus. Vous n'avez pas besoin d'utiliser dlopen vous-même pour ouvrir/utiliser les DLL, cela se produit également au démarrage de l'application.