c# c++ linker static-linking dynamic-linking

c# - ¿Qué significa "vinculado estáticamente" y "vinculado dinámicamente"?



c++ linker (5)

A menudo escucho los términos ''estáticamente enlazado'' y ''dinámicamente enlazado'', a menudo en referencia al código escrito en C , C++ o C# , pero tampoco sé mucho sobre nada. ¿Qué son, de qué están hablando exactamente y qué están vinculando?


(No sé C #, pero es interesante tener un concepto de enlace estático para un lenguaje de máquina virtual)

La vinculación dinámica implica saber cómo encontrar una funcionalidad requerida que solo tiene una referencia de su programa. Su tiempo de ejecución de idioma o sistema operativo busca un fragmento de código en el sistema de archivos, la red o el caché de código compilado, que coincida con la referencia, y luego toma varias medidas para integrarlo en la imagen de su programa en la memoria, como la reubicación. Todos se hacen en tiempo de ejecución. Se puede hacer ya sea manualmente o por el compilador. Existe la posibilidad de actualizar con un riesgo de desorden (a saber, DLL hell).

La vinculación estática se realiza en el momento de la compilación, le dice al compilador dónde están todas las partes funcionales y le indica que las integre. No hay búsquedas, no hay ambigüedad, no se puede actualizar sin una recompilación. Todas sus dependencias son físicamente una con su imagen de programa.


Creo que una buena respuesta a esta pregunta debería explicar qué es la vinculación.

Cuando compila algún código C (por ejemplo), se traduce al lenguaje de máquina. Solo una secuencia de bytes que, cuando se ejecuta, hace que el procesador agregue, reste, compare, "goto", lea memoria, escriba memoria, ese tipo de cosas. Este material se almacena en archivos de objeto (.o).

Ahora, hace mucho tiempo, los científicos informáticos inventaron esta cosa de "subrutina". Ejecute este fragmento de código y devuelva aquí. No pasó mucho tiempo hasta que se dieron cuenta de que las subrutinas más útiles podían almacenarse en un lugar especial y ser utilizadas por cualquier programa que las necesitara.

Ahora, en los primeros días, los programadores tendrían que marcar la dirección de memoria en la que se encontraban estas subrutinas. Algo así como CALL 0x5A62 . Esto era tedioso y problemático si alguna vez se debían cambiar esas direcciones de memoria.

Entonces, el proceso fue automatizado. Usted escribe un programa que llama a printf() , y el compilador no conoce la dirección de memoria de printf . Así que el compilador simplemente escribe CALL 0x0000 , y agrega una nota al archivo de objeto que dice "debe reemplazar este 0x0000 con la ubicación de memoria de printf ".

La vinculación estática significa que el programa enlazador (el de GNU se llama ld ) agrega el código de máquina de printf directamente a su archivo ejecutable, y cambia el 0x0000 a la dirección de printf . Esto sucede cuando se crea su ejecutable.

Enlace dinámico significa que el paso anterior no sucede. El archivo ejecutable aún tiene una nota que dice "debe reemplazar 0x000 con la ubicación de memoria de printf". El cargador del sistema operativo necesita encontrar el código de printf, cargarlo en la memoria y corregir la dirección de LLAMADA, cada vez que se ejecuta el programa .

Es común que los programas llamen a algunas funciones que estarán vinculadas estáticamente (las funciones de biblioteca estándar como printf generalmente están vinculadas estáticamente) y otras funciones que están vinculadas dinámicamente. Los estáticos "se convierten en parte" del ejecutable y los dinámicos "se unen" cuando se ejecuta el ejecutable.

Hay ventajas y desventajas en ambos métodos, y hay diferencias entre los sistemas operativos. Pero como no preguntaste, terminaré esto aquí.


Debido a que ninguna de las publicaciones anteriores realmente muestra cómo vincular estáticamente algo y ver que lo hiciste correctamente, así que abordaré este problema:

Un simple programa en c

#include <stdio.h> int main(void) { printf("This is a string/n"); return 0; }

Vincular dinámicamente el programa C

gcc simpleprog.c -o simpleprog

Y ejecuta el file en el binario:

file simpleprog

Y eso lo demostrará está algo vinculado dinámicamente a lo largo de las líneas de:

"simpleprog: ejecutable LSB de ELF de 64 bits, x86-64, versión 1 (SYSV), enlazado dinámicamente (usa bibliotecas compartidas), para GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, not stripped" "

En su lugar, vamos a vincular estáticamente el programa esta vez:

gcc simpleprog.c -static -o simpleprog

El archivo en ejecución en este binario estáticamente vinculado mostrará:

file simpleprog

"simpleprog: ejecutable de LSB de 64 bits de ELF, x86-64, versión 1 (GNU / Linux), estáticamente vinculado, para GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, no eliminado"

Y se puede ver que está felizmente estáticamente vinculado. Lamentablemente, sin embargo, no todas las bibliotecas son sencillas de vincular de forma estática y pueden requerir un esfuerzo prolongado al utilizar libtool o vincular el código objeto y las bibliotecas C a mano.

Afortunadamente, muchas bibliotecas C integradas como musl ofrecen opciones de enlace estático para casi todas, si no todas, sus bibliotecas.

Ahora strace el binario que ha creado y podrá ver que no se accede a ninguna biblioteca antes de que comience el programa:

strace ./simpleprog

Ahora compare con la salida de strace en el programa enlazado dinámicamente y verá que el strace de la versión enlazada estáticamente es mucho más corto.


Hay (en la mayoría de los casos, descontar el código interpretado) dos etapas para obtener del código fuente (lo que escribe) al código ejecutable (lo que ejecuta).

La primera es la compilación que convierte el código fuente en módulos de objetos.

El segundo, vincular, es lo que combina los módulos de objetos para formar un ejecutable.

La distinción se hace para, entre otras cosas, permitir que se incluyan bibliotecas de terceros en su ejecutable sin que vea su código fuente (como bibliotecas para el acceso a bases de datos, comunicaciones de red e interfaces gráficas de usuario), o para compilar el código en diferentes idiomas ( C y código de ensamblaje, por ejemplo) y luego enlazarlos todos juntos.

Cuando vincula estáticamente un archivo a un ejecutable, el contenido de ese archivo se incluye en el momento del enlace. En otras palabras, el contenido del archivo se inserta físicamente en el ejecutable que ejecutará.

Cuando se vincula dinámicamente , se incluye un puntero al archivo que se está vinculando (el nombre del archivo, por ejemplo) en el archivo ejecutable y el contenido de dicho archivo no se incluye en el momento del enlace. Solo cuando más tarde ejecutas el ejecutable, estos archivos vinculados dinámicamente se compran y solo se compran en la copia en memoria del ejecutable, no en el disco.

Es básicamente un método de vinculación diferida. Hay un método aún más diferido (llamado enlace tardío en algunos sistemas) que no introducirá el archivo enlazado dinámicamente hasta que realmente intentes llamar a una función dentro de él.

Los archivos vinculados estáticamente están ''bloqueados'' al ejecutable en el momento del enlace para que nunca cambien. Un archivo vinculado dinámicamente al que hace referencia un ejecutable puede cambiar simplemente reemplazando el archivo en el disco.

Esto permite actualizaciones de la funcionalidad sin tener que volver a vincular el código; El cargador vuelve a enlazar cada vez que lo ejecutas.

Esto es bueno y malo; por un lado, permite actualizaciones y correcciones de errores más fáciles, por otro, puede hacer que los programas dejen de funcionar si las actualizaciones son incompatibles. Esto a veces es responsable del temido "infierno DLL" que algunas personas Mencione que las aplicaciones se pueden interrumpir si reemplaza una biblioteca enlazada dinámicamente con una que no es compatible (los desarrolladores que hacen esto deben esperar ser cazados y castigados severamente, por cierto).

Como ejemplo , veamos el caso de un usuario que compila su archivo main.c para un enlace estático y dinámico.

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 | +---------+ +---------+

Puede ver en el caso estático que el programa principal y la biblioteca de tiempo de ejecución de C están vinculados entre sí en el momento del enlace (por los desarrolladores). Como el usuario normalmente no puede volver a vincular el archivo ejecutable, está atascado con el comportamiento de la biblioteca.

En el caso dinámico, el programa principal está vinculado con la biblioteca de importación en tiempo de ejecución de C (algo que declara lo que está en la biblioteca dinámica pero en realidad no lo define ). Esto permite que el enlazador se vincule aunque falte el código real.

Luego, en el tiempo de ejecución, el cargador del sistema operativo realiza una vinculación tardía del programa principal con la DLL de tiempo de ejecución de C (biblioteca de enlace dinámico o biblioteca compartida u otra nomenclatura).

El propietario del tiempo de ejecución de C puede colocar una nueva DLL en cualquier momento para proporcionar actualizaciones o correcciones de errores. Como se dijo anteriormente, esto tiene ventajas y desventajas.


Las bibliotecas enlazadas estáticamente se enlazan en tiempo de compilación. Las bibliotecas enlazadas dinámicamente se cargan en tiempo de ejecución. La vinculación estática transfiere el bit de la biblioteca a su ejecutable. La vinculación dinámica solo permite hornear en una referencia a la biblioteca; los bits para la biblioteca dinámica existen en otros lugares y podrían intercambiarse más tarde.