c++ - funcion - Relación entre archivo de objeto y archivo de objeto compartido
funcion estatica en fisica (2)
A .so es análogo a un .dll en Windows. A .o es exactamente lo mismo que un .obj en Visual Studio.
¿ .so
es la relación entre el archivo de objeto compartido ( .so
) y el archivo de objeto ( .o
)?
¿Puedes explicar por ejemplo?
Supongamos que tiene el siguiente archivo fuente C, name.c
#include <stdio.h>
#include <stdlib.h>
void print_name(const char * name)
{
printf("My name is %s/n", name);
}
Cuando lo compila, con cc name.c
genera name.o
. El .o contiene el código compilado y los datos de todas las funciones y variables definidas en name.c, así como el índice asociado a sus nombres con el código real. Si miras ese índice, digamos con la herramienta nm
(disponible en Linux y muchos otros Unix) notarás dos entradas:
00000000 T print_name
U printf
Lo que esto significa: hay dos símbolos (nombres de funciones o variables, pero no nombres de clases, estructuras o cualquier tipo) almacenados en el .o. El primero, marcado con T
realidad contiene su definición en name.o
El otro, marcado con U
es simplemente una referencia . El código para print_name
se puede encontrar aquí, pero el código para printf
no puede. Cuando se ejecute su programa real, necesitará encontrar todos los símbolos que sean referencias y buscar sus definiciones en otros archivos de objeto para poder vincularlos en un programa completo o una biblioteca completa. Un archivo objeto es, por lo tanto, las definiciones encontradas en el archivo fuente, convertido a formato binario, y disponible para colocar en un programa completo.
Puede vincular archivos .o uno por uno, pero no: en general, hay muchos y son un detalle de implementación. En realidad, preferiría tenerlos todos recogidos en paquetes de objetos relacionados, con nombres bien reconocidos. Estos paquetes se llaman bibliotecas y vienen en dos formas: estáticos y dinámicos.
Una biblioteca estática (en Unix) casi siempre tiene el sufijo de .a
(los ejemplos incluyen libc.a
que es la biblioteca de núcleo C, libm.a
, que es la biblioteca matemática C), y así sucesivamente. Continuando con el ejemplo, construirías tu biblioteca estática con ar rc libname.a name.o
. Si ejecuta nm
en libname.a
, verá esto:
name.o:
00000000 T print_name
U printf
Como puede ver, es principalmente una gran tabla de archivos de objetos con un índice que encuentra todos los nombres en él. Al igual que los archivos de objeto, contiene los símbolos definidos en cada .o
y los símbolos a los que hacen referencia. Si date.o
que enlazar en otro .o (por ejemplo, date.o
to print_date
), vería otra entrada como la de arriba.
Si vincula una biblioteca estática a un ejecutable, incrusta toda la biblioteca en el ejecutable. Esto es como vincular en todos los archivos .o
individuales. Como se puede imaginar, esto puede hacer que su programa sea muy grande, especialmente si está utilizando (como la mayoría de las aplicaciones modernas) muchas bibliotecas.
Una biblioteca dinámica o compartida tiene el sufijo .so
. Es, como su analogía estática, una gran tabla de archivos de objetos, que hace referencia a todo el código compilado. Lo construirías con cc -shared libname.so name.o
Sin embargo, mirar con nm
es bastante diferente de la biblioteca estática. En mi sistema, contiene alrededor de dos docenas de símbolos, de los cuales solo dos son print_name
y printf
:
00001498 a _DYNAMIC
00001574 a _GLOBAL_OFFSET_TABLE_
w _Jv_RegisterClasses
00001488 d __CTOR_END__
00001484 d __CTOR_LIST__
00001490 d __DTOR_END__
0000148c d __DTOR_LIST__
00000480 r __FRAME_END__
00001494 d __JCR_END__
00001494 d __JCR_LIST__
00001590 A __bss_start
w __cxa_finalize@@GLIBC_2.1.3
00000420 t __do_global_ctors_aux
00000360 t __do_global_dtors_aux
00001588 d __dso_handle
w __gmon_start__
000003f7 t __i686.get_pc_thunk.bx
00001590 A _edata
00001594 A _end
00000454 T _fini
000002f8 T _init
00001590 b completed.5843
000003c0 t frame_dummy
0000158c d p.5841
000003fc T print_name
U printf@@GLIBC_2.0
Una biblioteca compartida difiere de una biblioteca estática de una manera muy importante: no se incrusta en el ejecutable final. En cambio, el archivo ejecutable contiene una referencia a esa biblioteca compartida que se resuelve, no en el momento del enlace, sino en el tiempo de ejecución. Esto tiene una serie de ventajas:
- Su ejecutable es mucho más pequeño. Solo contiene el código que explícitamente vinculó a través de los archivos objeto. Las bibliotecas externas son referencias y su código no entra en el binario.
- Puede compartir (de ahí el nombre) los bits de una biblioteca entre múltiples ejecutables.
- Si tiene cuidado con la compatibilidad binaria, puede actualizar el código en la biblioteca entre ejecuciones del programa, y el programa seleccionará la nueva biblioteca sin necesidad de cambiarla.
Hay algunas desventajas:
- Lleva tiempo enlazar un programa. Con las bibliotecas compartidas, parte de este tiempo se difiere cada vez que se ejecuta el ejecutable.
- El proceso es más complejo. Todos los símbolos adicionales en la biblioteca compartida son parte de la infraestructura necesaria para hacer que la biblioteca se enlace en tiempo de ejecución.
- Usted corre el riesgo de incompatibilidades sutiles entre las diferentes versiones de la biblioteca. En Windows esto se llama "infierno DLL".
(Si lo piensas, muchos de estos son los motivos por los que los programas usan o no usan referencias y punteros en lugar de insertar directamente objetos de una clase en otros objetos. La analogía es bastante directa).
Ok, eso es un montón de detalles, y me he saltado un montón, como por ejemplo cómo funciona el proceso de vinculación. Espero que puedas seguirlo. Si no, pide una aclaración.