web-dev-qa-db-fra.com

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

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 parlent-ils exactement et de quoi sont-ils liés?

210
UnkwnTech

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

Le premier est la compilation qui transforme le code source en modules objets.

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

La distinction est faite, entre autres, en permettant aux bibliothèques tierces d’être incluses dans votre exécutable sans que vous ne voyiez leur code source (telles que les bibliothèques pour l’accès à la base de données, les communications réseau et les interfaces utilisateur graphiques), ou pour la compilation de code dans différentes langues ( C et code d'assemblage, par exemple) puis en les liant tous ensemble.

Lorsque vous liez statiquement 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 inséré physiquement dans l'exécutable que vous allez exécuter.

Lorsque vous liez dynamiquement, un pointeur sur le fichier en cours de liaison (le nom de fichier 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. C'est seulement lorsque vous exécutez plus tard l'exécutable dans 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.

C'est fondamentalement une méthode de liaison différée. Il existe une méthode même plus différée (appelée liaison tardive sur certains systèmes) qui n'apporte pas le fichier lié dynamiquement tant que vous n'avez pas réellement essayé d'appeler une fonction à l'intérieur de celui-ci.

Les fichiers liés statiquement sont "verrouillés" sur l'exécutable au moment de la liaison, ils ne changent donc 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 de mettre à jour les 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, des programmes peuvent cesser de fonctionner si les mises à jour sont incompatibles - c’est parfois à l’origine du redoutable "enfer de la DLL" que certaines personnes Il est à noter que les applications peuvent être interrompues si vous remplacez une bibliothèque liée dynamiquement par une autre non compatible (les développeurs qui le font devraient s'attendre à être traqués et punis sévèrement, en passant).


En tant que exemple, examinons le cas d'un utilisateur compilant son main.c fichier pour les liens statiques et dynamiques.

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 au moment de la liaison (par les développeurs). Etant donné que l'utilisateur ne peut généralement pas relier 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 C runtime (quelque chose qui déclare ce qu'il y a dans la bibliothèque dynamique mais ne le fait pas définir le). Cela permet à l'éditeur de liens de se lier même si le code réel est manquant.

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

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

411
paxdiablo

Je pense qu'une bonne réponse à cette question devrait expliquer ce qui lie 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, amène le processeur à ajouter, soustraire, comparer, "goto", lire la mémoire, écrire de la mémoire, ce genre de chose. Ce truc est stocké dans des fichiers objet (.o).

Il y a longtemps, les informaticiens ont inventé ce "procédé". Exécutez-ce-morceau-de-code-et-retour-ici. Ils ont vite compris que les sous-programmes les plus utiles pouvaient être stockés dans un emplacement spécial et utilisés par tout programme qui en avait besoin.

Au début, les programmeurs devaient entrer l'adresse mémoire dans laquelle ces sous-routines étaient situées. Quelque chose comme CALL 0x5A62. Cela était fastidieux et problématique si ces adresses de mémoire devaient être changées.

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

Liaison statique signifie que le programme de l'éditeur de liens (GNU est appelé ld ) ajoute le code machine de printf directement dans votre fichier exécutable et modifie le code 0x0000. à l'adresse de printf. Cela se produit lors de la création de votre exécutable.

Le couplage dynamique signifie que l'étape ci-dessus ne se produit pas. Le fichier exécutable encore contient une note disant "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 des fonctions qui seront liées statiquement (les fonctions de bibliothèque standard telles que printf sont généralement liées statiquement) et d’autres fonctions liées dynamiquement. Les statiques "font partie" de l'exécutable et les dynamiques "se joignent" à l'exécution de l'exécutable.

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

214
Artelius

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 coud le bit de bibliothèque dans votre exécutable. La liaison dynamique cuit uniquement dans une référence à la bibliothèque; les bits de la bibliothèque dynamique existent ailleurs et pourraient être échangés ultérieurement.

29
John D. Cook

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

Un simple programme en C

#include <stdio.h>

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

Relier dynamiquement le programme C

gcc simpleprog.c -o simpleprog

Et lancez file sur le binaire:

file simpleprog 

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

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

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

gcc simpleprog.c -static -o simpleprog

Le fichier en cours d'exécution sur ce binaire statiquement lié montrera:

file simpleprog 

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

Et vous pouvez voir que c'est heureusement lié statiquement. Malheureusement, toutefois, toutes les bibliothèques ne sont pas simples à lier statiquement de cette manière et peuvent nécessiter un effort supplémentaire en utilisant libtool ou en liant le code de l’objet et les bibliothèques C à la main.

Heureusement, beaucoup de bibliothèques C intégrées telles que musl offrent des options de liaison statique pour presque toutes les sinon toutes de leurs bibliothèques.

Maintenant strace le binaire que vous avez créé et vous pouvez voir qu’aucune bibliothèque n’a été accédée avant le début du programme:

strace ./simpleprog

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

13
user4832408

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

La liaison dynamique implique de savoir comment trouver une fonctionnalité requise pour laquelle vous ne disposez que d'une référence de votre programme. Votre langue d’exécution ou votre système d’exploitation recherche un morceau de code dans le cache de système de fichiers, de réseau ou de 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 le déplacement. Ils sont tous terminés au moment de l'exécution. Cela peut être fait manuellement ou par le compilateur. Il est possible de mettre à jour avec un risque de gâchis (à savoir, DLL enfer).

La liaison statique est faite au moment de la compilation, vous indiquez au compilateur où se trouvent toutes les pièces fonctionnelles et vous lui demandez de les intégrer. Il n'y a pas de recherche, pas d'ambiguïté, pas de possibilité de mettre à jour sans recompiler. Toutes vos dépendances ne font qu'un avec votre image de programme.

2
artificialidiot