A menudo escucho los términos 'enlazados estáticamente' y 'enlazados dinámicamente', a menudo en referencia al código escrito en C , C ++ o C # . ¿Qué son, de qué están hablando exactamente y qué están vinculando?
fuente
A menudo escucho los términos 'enlazados estáticamente' y 'enlazados dinámicamente', a menudo en referencia al código escrito en C , C ++ o C # . ¿Qué son, de qué están hablando exactamente y qué están vinculando?
Hay (en la mayoría de los casos, descontando el código interpretado) dos etapas para pasar del código fuente (lo que escribe) al código ejecutable (lo que ejecuta).
El primero es la compilación que convierte el código fuente en módulos de objeto.
El segundo, la vinculación, es lo que combina 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 acceso a bases de datos, comunicaciones de red e interfaces gráficas de usuario), o para compilar código en diferentes idiomas ( C y código de ensamblaje, por ejemplo) y luego vincularlos todos juntos.
Cuando vincula estáticamente un archivo a un archivo 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. Es solo cuando más tarde ejecuta el ejecutable que estos archivos vinculados dinámicamente se compran y solo se compran en la copia en memoria del ejecutable, no en el disco.
Básicamente es un método de enlace diferido. Hay un método aún más diferido (llamado enlace tardío en algunos sistemas) que no traerá el archivo vinculado dinámicamente hasta que realmente intente llamar a una función dentro de él.
Los archivos enlazados 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 se vuelve a vincular cada vez que lo ejecuta.
Esto es bueno y malo: por un lado, permite actualizaciones más fáciles y correcciones de errores, por otro lado puede hacer que los programas dejen de funcionar si las actualizaciones son incompatibles; esto a veces es responsable del temido "infierno de DLL" que algunas personas mencione que las aplicaciones pueden romperse si reemplaza una biblioteca vinculada dinámicamente por una que no es compatible (por cierto, los desarrolladores que hacen esto deben esperar ser perseguidos y castigados severamente).
Como ejemplo , veamos el caso de un usuario que compila su main.c
archivo para enlaces estáticos y dinámicos.
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 C están vinculados entre sí en el momento del enlace (por los desarrolladores). Como el usuario generalmente no puede volver a vincular el 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 de tiempo de ejecución C (algo que declara lo que hay en la biblioteca dinámica pero en realidad no lo define ). Esto permite que el vinculador se vincule aunque falte el código real.
Luego, en tiempo de ejecución, el cargador del sistema operativo realiza un enlace tardío del programa principal con la DLL de tiempo de ejecución 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 indicó anteriormente, esto tiene ventajas y desventajas.
.dll
o una.so
extensión); piensa en la respuesta como una explicación de los conceptos en lugar de ser una descripción exacta. Y, según el texto, este es un ejemplo que muestra enlaces estáticos y dinámicos solo para los archivos de tiempo de ejecución de C, así que sí, eso es lo que `crt indica en todos ellos.Creo que una buena respuesta a esta pregunta debería explicar qué es la vinculación .
Cuando compila un 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 sume, reste, compare, "pase a", lea la memoria, escriba la memoria, ese tipo de cosas. Estas cosas se almacenan en archivos de objetos (.o).
Ahora, hace mucho tiempo, los informáticos inventaron esta cosa de "subrutina". Ejecute este trozo de código y regrese aquí. No pasó mucho tiempo antes de que se dieran 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 introducir la dirección de memoria en la que se encontraban estas subrutinas. Algo así como
CALL 0x5A62
. Esto era tedioso y problemático en caso de que alguna vez fuera necesario cambiar esas direcciones de memoria.Entonces, el proceso fue automatizado. Escribes un programa que llama
printf()
y el compilador no conoce la dirección de memoria deprintf
. Entonces, el compilador simplemente escribeCALL 0x0000
y agrega una nota al archivo de objeto que dice "debe reemplazar este 0x0000 con la ubicación de memoria de printf ".Enlace estático significa que el programa enlazador (el GNU se llama ld ) agrega
printf
el código de máquina directamente a su archivo ejecutable y cambia el 0x0000 a la dirección deprintf
. Esto sucede cuando se crea su ejecutable.La vinculación dinámica significa que el paso anterior no sucede. El archivo ejecutable todavía 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 printf, cargarlo en la memoria y corregir la dirección CALL cada vez que se ejecuta el programa .
Es común que los programas invoquen algunas funciones que estarán vinculadas estáticamente (las funciones de biblioteca estándar, como
printf
las que normalmente 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í.
fuente
ld
documentación de GNU .Las bibliotecas enlazadas estáticamente están enlazadas en tiempo de compilación. Las bibliotecas vinculadas dinámicamente se cargan en tiempo de ejecución. La vinculación estática hornea el bit de la biblioteca en su ejecutable. La vinculación dinámica solo se hornea en una referencia a la biblioteca; los bits para la biblioteca dinámica existen en otro lugar y podrían cambiarse más tarde.
fuente
Debido a que ninguna de las publicaciones anteriores muestra realmente cómo vincular estáticamente algo y ver que lo hiciste correctamente, así que abordaré este problema:
Un simple programa en C
Enlace dinámicamente el programa C
Y correr
file
en el binario:Y eso mostrará que está vinculado dinámicamente a algo como:
"simpleprog: ELF ejecutable LSB de 64 bits, x86-64, versión 1 (SYSV), vinculado dinámicamente (usa libs compartidas), para GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, no eliminado"
En su lugar, vinculemos estáticamente el programa esta vez:
El archivo en ejecución en este binario enlazado estáticamente mostrará:
"simpleprog: ELF ejecutable LSB de 64 bits, x86-64, versión 1 (GNU / Linux), enlazado estáticamente, para GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, no despojado"
Y puedes ver que está felizmente vinculado estáticamente. Lamentablemente, sin embargo, no todas las bibliotecas son fáciles de vincular estáticamente de esta manera y pueden requerir un esfuerzo extendido usando
libtool
o vinculando el código objeto y las bibliotecas C a mano.Afortunadamente, muchas bibliotecas C incrustadas
musl
ofrecen opciones de enlace estático para casi todas, si no todas, sus bibliotecas.Ahora
strace
el binario que ha creado y puede ver que no hay bibliotecas accedidas antes de que comience el programa:¡Ahora compare con la salida del
strace
programa vinculado dinámicamente y verá que la versión de la versión estáticamente vinculada es mucho más corta!fuente
(No sé C # pero es interesante tener un concepto de enlace estático para un lenguaje VM)
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 lenguaje o sistema operativo busca un fragmento de código en el sistema de archivos, red o caché de código compilado, que coincida con la referencia, y luego toma varias medidas para integrarlo a la imagen de su programa en la memoria, como la reubicación. Todos se hacen en tiempo de ejecución. Se puede hacer manualmente o por el compilador. Existe la posibilidad de actualizar con el riesgo de equivocarse (es decir, 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úsqueda, no hay ambigüedad, no se puede actualizar sin una nueva compilación. Todas sus dependencias son físicamente una con la imagen de su programa.
fuente