303 votes

Que signifient les termes "lié statiquement" et "lié dynamiquement" ?

J'entends souvent les termes "lié de manière statique" et "lié de manière dynamique", souvent en référence à du code écrit en format C , C++ o C# . Qu'est-ce qu'ils sont, de quoi parlent-ils exactement, et qu'est-ce qu'ils relient ?

555voto

paxdiablo Points 341644

Il y a (dans la plupart des cas, à l'exception 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.

La seconde, la liaison, est ce qui combine les modules d'objets ensemble pour former un exécutable.

La distinction est faite, entre autres, pour permettre l'inclusion de bibliothèques tierces dans votre exécutable sans que vous ne 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érents langages (C et code assembleur par exemple) et les lier ensuite tous ensemble.

Quand vous statiquement lier un fichier à un exécutable, le contenu de ce fichier est inclus au moment de la liaison. En d'autres termes, le contenu du fichier est physiquement inséré dans l'exécutable que vous allez exécuter.

Lorsque vous reliez dynamiquement un pointeur vers le fichier à lier (le nom du fichier, par exemple) est inclus dans l'exécutable et le contenu de ce fichier n'est pas inclus au moment de la liaison. Ce n'est qu'au moment où l'on exécuter l'exécutable que ces fichiers liés dynamiquement sont achetés et 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 y a même plus une méthode différée (appelée liaison tardive sur certains systèmes) qui ne fait pas intervenir le fichier lié dynamiquement tant que vous n'essayez pas d'appeler une fonction dans ce fichier.

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

Cela permet de mettre à jour les fonctionnalités sans avoir à refaire le lien avec le code ; le chargeur refait le lien à chaque fois que vous l'exécutez.

C'est à la fois une bonne et une mauvaise chose - d'un côté, cela permet des mises à jour et des corrections de bogues plus faciles, de l'autre cela peut conduire à ce que les programmes cessent de fonctionner si les mises à jour sont incompatibles - cela est parfois responsable du redoutable "enfer des DLL" que certaines personnes mentionnent, dans la mesure où les applications peuvent être cassées si vous remplacez une bibliothèque liée dynamiquement par une autre qui n'est pas compatible (les développeurs qui font cela doivent s'attendre à être traqués et sévèrement punis, d'ailleurs).


En tant que exemple Dans ce cas, considérons le cas d'un utilisateur qui compile ses main.c pour la 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). Comme l'utilisateur ne peut généralement pas relier l'exécutable, il doit se contenter du comportement de la bibliothèque.

Dans le cas d'un programme dynamique, le programme principal est lié à la bibliothèque d'importation du runtime C (quelque chose qui déclare ce qui se trouve dans la bibliothèque dynamique, mais qui n'est pas réellement une bibliothèque dynamique). définir il). Cela permet à l'éditeur de liens de lier 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 (bibliothèque de liaison dynamique ou bibliothèque partagée ou autre nomenclature) du runtime C.

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

14 votes

Corrigez-moi si je me trompe, mais sous Windows, les logiciels ont tendance à inclure leurs propres bibliothèques avec l'installation, même si elles sont liées dynamiquement. Sur de nombreux systèmes Linux dotés d'un gestionnaire de paquets, de nombreuses bibliothèques liées dynamiquement ("objets partagés") sont en fait partagées entre les logiciels.

6 votes

@PaulF : des choses comme les contrôles communs Windows, DirectX, .NET et ainsi de suite sont livrés avec les applications alors que sur Linux, vous avez tendance à utiliser apt ou yum ou quelque chose comme ça pour gérer les dépendances - donc vous avez raison dans ce sens. Les applications Windows qui livrent leurs propre car les DLL ont tendance à ne pas les partager.

0 votes

Il est vrai qu'il faut mettre à jour uniquement la bibliothèque dynamique si la fonctionnalité JUST est mise à jour. Qu'en est-il des nouvelles fonctions exposées ou des signatures modifiées présentes dans la DLL ? Je pense que vous devez refaire le lien.

266voto

Artelius Points 25772

Je pense qu'une bonne réponse à cette question devrait expliquer ce qui relie est .

Lorsque vous compilez un code C (par exemple), celui-ci est traduit en langage machine. Il s'agit simplement d'une séquence d'octets qui, lorsqu'elle est exécutée, amène le processeur à additionner, soustraire, comparer, "goto", lire la mémoire, écrire la mémoire, etc. Ces éléments sont stockés dans des fichiers objets (.o).

Il y a longtemps, les informaticiens ont inventé ce truc de "sous-routine". Exécuter ce morceau de code et retourner ici. Il n'a pas fallu longtemps pour qu'ils réalisent que les sous-routines les plus utiles pouvaient être stockées dans un endroit spécial et utilisées par tout programme qui en avait besoin.

Au début, les programmeurs devaient saisir l'adresse mémoire où se trouvaient ces sous-programmes. Quelque chose comme CALL 0x5A62 . C'était fastidieux et problématique si ces adresses mémoire devaient un jour être modifiées.

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

La liaison statique signifie que le programme de liaison (celui de GNU s'appelle ld ) ajoute printf directement dans votre fichier exécutable, et change le 0x0000 en 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 toujours a une note qui dit "doit remplacer 0x000 avec 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 fois que le programme est exécuté .

Il est courant pour les programmes d'appeler certaines fonctions qui seront liées statiquement (fonctions de la bibliothèque standard telles que printf sont généralement liées statiquement) et d'autres fonctions qui sont liées dynamiquement. Les fonctions statiques "font partie" de l'exécutable et les fonctions dynamiques "s'y joignent" lorsque l'exécutable est exécuté.

Les deux méthodes présentent des avantages et des inconvénients, et il existe des différences entre les systèmes d'exploitation. Mais puisque vous n'avez pas demandé, je vais m'arrêter là.

4 votes

Moi aussi, mais je ne peux choisir qu'une seule réponse.

3 votes

Artelius, je cherche des explications plus approfondies sur la façon dont ces choses folles de bas niveau fonctionnent. s'il vous plaît répondez avec quels livres nous devons lire pour obtenir des connaissances approfondies sur les choses ci-dessus. merci.

3 votes

Désolé, je ne peux pas vous suggérer de livres. Vous devriez d'abord apprendre le langage assembleur. Ensuite, Wikipedia peut donner un aperçu décent de ces sujets. Vous pouvez également consulter le manuel GNU ld documentation.

43voto

John D. Cook Points 19036

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 intègre le bit de la bibliothèque dans votre exécutable. La liaison dynamique n'intègre qu'une référence à la bibliothèque ; les bits de la bibliothèque dynamique existent ailleurs et peuvent être remplacés ultérieurement.

27voto

Parce qu'aucun des postes ci-dessus montrent en fait comment pour lier statiquement quelque chose et voir que vous l'avez fait correctement, je vais donc aborder cette question :

Un simple programme C

#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 courir file sur le binaire :

file simpleprog 

Et cela montrera qu'il est lié dynamiquement, quelque chose du genre :

"simpleprog : Exécutable ELF 64 bits LSB, x86-64, version 1 (SYSV), lié dynamiquement (utilise des librairies partagées), pour GNU/Linux 2.6.26, BuildID[sha1]=0xf715572611a8b04f686809d90d1c0d75c6028f0f, non dépouillé".

Au lieu de cela, lions statiquement le programme cette fois-ci :

gcc simpleprog.c -static -o simpleprog

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

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 dépouillé".

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 un effort supplémentaire en utilisant la commande libtool ou en liant le code objet et les bibliothèques C à la main.

Heureusement, de nombreuses bibliothèques C embarquées comme musl offrent des options de liaison statique pour presque toutes les si pas tous de leurs bibliothèques.

Maintenant strace le binaire que vous avez créé et vous pouvez voir qu'il n'y a pas d'accès aux bibliothèques avant le début du programme :

strace ./simpleprog

Comparez maintenant avec la sortie de strace sur le programme lié dynamiquement et vous verrez que le strace de la version liée statiquement est beaucoup plus court !

2voto

artificialidiot Points 3584

(Je ne connais pas le 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 dans votre programme. Le runtime de votre langage ou le système d'exploitation recherche un morceau de code sur le système de fichiers, le réseau ou le cache du code compilé, correspondant à la référence, puis prend plusieurs mesures pour l'intégrer à l'image de votre programme dans la mémoire, comme la relocalisation. Toutes ces mesures sont prises au moment de l'exécution. Cela peut être fait manuellement ou par le compilateur. Il est possible d'effectuer des mises à jour avec le risque de se planter (à savoir, l'enfer des 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 aucune recherche, aucune ambiguïté, aucune possibilité de mise à jour sans recompilation. Toutes vos dépendances ne font physiquement qu'un avec votre image de programme.

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