Que signifient «liés statiquement» et «liés dynamiquement»?

230

J'entends souvent les termes «lié statiquement» et «lié dynamiquement», souvent en référence à du code écrit en C , C ++ ou C # . De quoi s'agit-il, de quoi parlent-ils exactement et de quoi sont-ils liés?

UnkwnTech
la source

Réponses:

446

Il y a (dans la plupart des cas, actualisation du code interprété) deux étapes pour passer du code source (ce que vous écrivez) au code exécutable (ce que vous exécutez).

La première est la compilation qui transforme le code source en modules objets.

Le second, la liaison, est ce qui combine les modules objets ensemble pour former un exécutable.

La distinction est faite, entre autres, pour permettre aux bibliothèques tierces d'être incluses dans votre exécutable sans que vous voyiez leur code source (comme les bibliothèques pour l'accès aux bases de données, les communications réseau et les interfaces utilisateur graphiques), ou pour compiler du code dans différentes langues ( C et le code d'assemblage par exemple), puis les lier tous ensemble.

Lorsque vous liez statiquement un fichier à un exécutable, le contenu de ce fichier est inclus au moment du lien. En d'autres termes, le contenu du fichier est physiquement inséré dans l'exécutable que vous exécuterez.

Lorsque vous liez dynamiquement , un pointeur vers le fichier lié (le nom du fichier, par exemple) est inclus dans l'exécutable et le contenu dudit fichier n'est pas inclus au moment du lien. Ce n'est que lorsque vous exécutez ultérieurement l'exécutable que ces fichiers liés dynamiquement sont achetés et qu'ils ne sont achetés que dans la copie en mémoire de l'exécutable, pas celle sur le disque.

Il s'agit essentiellement d'une méthode de liaison différée. Il existe une méthode encore plus différée (appelée liaison tardive sur certains systèmes) qui n'introduira pas le fichier lié dynamiquement jusqu'à ce que vous essayiez réellement d'appeler une fonction à l'intérieur.

Les fichiers liés statiquement sont «verrouillés» sur l'exécutable au moment de la liaison afin qu'ils ne changent jamais. Un fichier lié dynamiquement référencé par un exécutable peut changer simplement en remplaçant le fichier sur le disque.

Cela permet des mises à jour des fonctionnalités sans avoir à relier le code; le chargeur se reconnecte à chaque fois que vous l'exécutez.

C'est à la fois bon et mauvais - d'une part, cela permet des mises à jour et des corrections de bogues plus faciles, d'autre part, cela peut conduire à des programmes cessant de fonctionner si les mises à jour sont incompatibles - cela est parfois responsable du redoutable «enfer des DLL» que certaines personnes mentionner que les applications peuvent être cassées si vous remplacez une bibliothèque liée dynamiquement par une bibliothèque non compatible (les développeurs qui le font devraient s'attendre à être traqués et punis sévèrement, soit dit en passant).


À titre d' exemple , examinons le cas d'un utilisateur qui compile son main.cfichier pour une liaison statique et dynamique.

Phase     Static                    Dynamic
--------  ----------------------    ------------------------
          +---------+               +---------+
          | main.c  |               | main.c  |
          +---------+               +---------+
Compile........|.........................|...................
          +---------+ +---------+   +---------+ +--------+
          | main.o  | | crtlib  |   | main.o  | | crtimp |
          +---------+ +---------+   +---------+ +--------+
Link...........|..........|..............|...........|.......
               |          |              +-----------+
               |          |              |
          +---------+     |         +---------+ +--------+
          |  main   |-----+         |  main   | | crtdll |
          +---------+               +---------+ +--------+
Load/Run.......|.........................|..........|........
          +---------+               +---------+     |
          | main in |               | main in |-----+
          | memory  |               | memory  |
          +---------+               +---------+

Vous pouvez voir dans le cas statique que le programme principal et la bibliothèque d'exécution C sont liés ensemble au moment de la liaison (par les développeurs). Étant donné que l'utilisateur ne peut généralement pas lier de nouveau l'exécutable, il est bloqué par le comportement de la bibliothèque.

Dans le cas dynamique, le programme principal est lié à la bibliothèque d'importation d'exécution C (quelque chose qui déclare ce qui se trouve dans la bibliothèque dynamique mais ne le définit pas réellement ). Cela permet à l'éditeur de liens d'établir un lien même si le code réel est manquant.

Ensuite, au moment de l'exécution, le chargeur du système d'exploitation effectue une liaison tardive du programme principal avec la DLL d'exécution C (bibliothèque de liens dynamiques ou bibliothèque partagée ou autre nomenclature).

Le propriétaire du runtime C peut déposer une nouvelle DLL à tout moment pour fournir des mises à jour ou des corrections de bogues. Comme indiqué précédemment, cela présente à la fois des avantages et des inconvénients.

paxdiablo
la source
11
Veuillez me corriger si je me trompe, mais sous Windows, le logiciel a tendance à inclure ses propres bibliothèques avec l'installation, même si elles sont liées dynamiquement. Sur de nombreux systèmes Linux avec un gestionnaire de packages, de nombreuses bibliothèques liées dynamiquement («objets partagés») sont en fait partagées entre les logiciels.
Paul Fisher
6
@PaulF: des choses comme les contrôles communs de Windows, DirectX, .NET et ainsi de suite sont souvent livrées avec les applications alors que sous Linux, vous avez tendance à utiliser apt ou yum ou quelque chose comme ça pour gérer les dépendances - vous avez donc raison dans ce sens . Les applications Windows qui livrent leur propre code en tant que DLL ont tendance à ne pas les partager.
paxdiablo
32
Il y a une place spéciale réservée dans le neuvième cercle de l'enfer pour ceux qui mettent à jour leurs DLL et brisent la compatibilité descendante. Oui, si les interfaces disparaissent ou sont modifiées, la liaison dynamique tombera en tas. Voilà pourquoi cela ne devrait pas être fait. Ajoutez par tous les moyens une fonction2 () à votre DLL mais ne changez pas function () si les gens l'utilisent. La meilleure façon de gérer cela est de recoder la fonction () de telle manière qu'elle appelle function2 (), mais ne changez pas la signature de la fonction ().
paxdiablo
1
@ Paul Fisher, je sais que c'est tard mais ... la bibliothèque livrée avec une DLL Windows n'est pas la bibliothèque complète, c'est juste un tas de talons qui indiquent à l'éditeur de liens ce que contient la DLL. L'éditeur de liens peut alors automatiquement mettre les informations dans le .exe pour charger la DLL, et les symboles ne s'affichent pas comme indéfinis.
Mark Ransom
1
@Santropedro, vous avez raison sur tous les points concernant la signification des noms de lib, import et DLL. Le suffixe est une convention uniquement, alors ne lisez pas trop (par exemple, la DLL peut avoir une extension .dllou .so) - pensez à la réponse comme expliquant les concepts plutôt que comme une description exacte. Et, selon le texte, il s'agit d'un exemple montrant la liaison statique et dynamique pour les fichiers d'exécution C donc, oui, c'est ce que `crt indique dans chacun d'eux.
paxdiablo
221

Je pense qu'une bonne réponse à cette question devrait expliquer ce que la liaison est .

Lorsque vous compilez du code C (par exemple), il est traduit en langage machine. Juste une séquence d'octets qui, lorsqu'elle est exécutée, oblige le processeur à ajouter, soustraire, comparer, "goto", lire la mémoire, écrire la mémoire, ce genre de chose. Ces informations sont stockées dans des fichiers objets (.o).

Il y a longtemps, les informaticiens ont inventé ce "sous-programme". Exécutez-ce-morceau-de-code-et-retournez ici. Il ne fallut pas trop longtemps avant de se rendre compte que les sous-programmes les plus utiles pouvaient être stockés dans un endroit spécial et utilisés par n'importe quel programme qui en avait besoin.

Maintenant, au début, les programmeurs devaient saisir l'adresse mémoire dans laquelle se trouvaient ces sous-programmes. Quelque chose comme CALL 0x5A62. Cela était fastidieux et problématique si ces adresses mémoire devaient être modifiées.

Ainsi, le processus a été automatisé. Vous écrivez un programme qui appelle printf()et le compilateur ne connaît pas l'adresse mémoire de printf. Ainsi, le compilateur écrit simplement CALL 0x0000et ajoute une note au fichier objet disant "doit remplacer ce 0x0000 par l'emplacement mémoire de printf ".

La liaison statique signifie que le programme de l'éditeur de liens (celui de GNU est appelé ld ) ajoute printfle code machine de directement à votre fichier exécutable et change le 0x0000 en l'adresse de printf. Cela se produit lorsque votre exécutable est créé.

La liaison dynamique signifie que l'étape ci-dessus ne se produit pas. Le fichier exécutable a toujours une note qui dit "doit remplacer 0x000 par l'emplacement mémoire de printf". Le chargeur du système d'exploitation doit trouver le code printf, le charger en mémoire et corriger l'adresse CALL à chaque exécution du programme .

Il est courant que les programmes appellent certaines fonctions qui seront liées statiquement (les fonctions de bibliothèque standard comme printfsont généralement liées statiquement) et d'autres fonctions qui sont liées dynamiquement. Les statiques "font partie" de l'exécutable et les dynamiques "se joignent" lorsque l'exécutable est exécuté.

Les deux méthodes présentent des avantages et des inconvénients, ainsi que des différences entre les systèmes d'exploitation. Mais comme vous ne l'avez pas demandé, je termine ici.

Artelius
la source
4
Moi aussi, mais je ne peux choisir qu'une seule réponse.
UnkwnTech
1
Artelius, je regarde en détail votre explication sur le fonctionnement de ces choses folles de bas niveau. veuillez répondre avec quels livres nous devons lire pour avoir une connaissance approfondie des choses ci-dessus. Merci.
mahesh
1
Désolé, je ne peux suggérer aucun livre. Vous devez d'abord apprendre le langage d'assemblage. Wikipédia peut alors donner un aperçu décent de ces sujets. Vous voudrez peut-être consulter la lddocumentation GNU .
Artelius
31

Les bibliothèques liées statiquement sont liées au moment de la compilation. Les bibliothèques liées dynamiquement sont chargées au moment de l'exécution. La liaison statique cuit le bit de bibliothèque dans votre exécutable. La liaison dynamique ne fait que cuire dans une référence à la bibliothèque; les bits de la bibliothèque dynamique existent ailleurs et pourraient être échangés ultérieurement.

John D. Cook
la source
16

Parce qu'aucun des articles ci-dessus ne montre comment lier statiquement quelque chose et voir que vous l'avez fait correctement, je vais donc résoudre ce problème:

Un programme C simple

#include <stdio.h>

int main(void)
{
    printf("This is a string\n");
    return 0;
}

Lier dynamiquement le programme C

gcc simpleprog.c -o simpleprog

Et exécutez filesur le binaire:

file simpleprog 

Et cela montrera qu'il est lié dynamiquement à quelque chose comme:

"simpleprog: exécutable ELF 64 bits LSB, x86-64, version 1 (SYSV), lié dynamiquement (utilise des bibliothèques partagées), pour GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, non supprimé"

Au lieu de cela, relions statiquement le programme cette fois:

gcc simpleprog.c -static -o simpleprog

L'exécution du fichier sur ce binaire lié statiquement affichera:

file simpleprog 

"simpleprog: exécutable ELF 64 bits LSB, x86-64, version 1 (GNU / Linux), lié statiquement, pour GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, non supprimé"

Et vous pouvez voir qu'il est heureusement lié statiquement. Malheureusement, toutes les bibliothèques ne sont pas simples à lier statiquement de cette façon et peuvent nécessiter des efforts étendus en utilisant libtoolou en reliant le code objet et les bibliothèques C à la main.

Heureusement, de nombreuses bibliothèques C intégrées musloffrent des options de liaison statique pour presque toutes, sinon toutes, leurs bibliothèques.

Maintenant, stracele binaire que vous avez créé et vous pouvez voir qu'aucune bibliothèque n'est accessible avant le début du programme:

strace ./simpleprog

Comparez maintenant avec la sortie de stracesur le programme lié dynamiquement et vous verrez que l'étendue de la version liée statiquement est beaucoup plus courte!


la source
2

(Je ne connais pas C # mais il est intéressant d'avoir un concept de liaison statique pour un langage VM)

La liaison dynamique implique de savoir comment trouver une fonctionnalité requise dont vous ne disposez que d'une référence de votre programme. Votre langage d'exécution ou votre système d'exploitation recherche un morceau de code sur le système de fichiers, le réseau ou le cache de code compilé, correspondant à la référence, puis prend plusieurs mesures pour l'intégrer à votre image de programme dans la mémoire, comme la relocalisation. Ils sont tous exécutés au moment de l'exécution. Cela peut être fait manuellement ou par le compilateur. Il y a la possibilité de mettre à jour avec un risque de gâcher (à savoir, l'enfer DLL).

La liaison statique est effectuée au moment de la compilation, vous indiquez au compilateur où se trouvent toutes les parties fonctionnelles et lui demandez de les intégrer. Il n'y a pas de recherche, pas d'ambiguïté, pas de possibilité de mise à jour sans recompilation. Toutes vos dépendances sont physiquement un avec votre image de programme.

ificialidiot
la source