Réponses:
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.c
fichier 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.
.dll
ou .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.
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 0x0000
et 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 printf
le 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 printf
sont 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.
ld
documentation GNU .
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.
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 file
sur 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 libtool
ou en reliant le code objet et les bibliothèques C à la main.
Heureusement, de nombreuses bibliothèques C intégrées musl
offrent des options de liaison statique pour presque toutes, sinon toutes, leurs bibliothèques.
Maintenant, strace
le 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 strace
sur le programme lié dynamiquement et vous verrez que l'étendue de la version liée statiquement est beaucoup plus courte!
(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.