2009-07-31 21 views

Respuesta

2

A .so es análogo a un .dll en windows. A .o es exactamente lo mismo que un .obj en Visual Studio.

65

Digamos que usted tiene el siguiente fichero fuente C, lo llaman name.c

#include <stdio.h> 
#include <stdlib.h> 

void print_name(const char * name) 
{ 
    printf("My name is %s\n", name); 
} 

Cuando se compila, con cc name.c se 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 nos fijamos en ese índice, por ejemplo con la herramienta nm (disponible en Linux y muchos otros sistemas Unix) se dará cuenta de 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 en 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 objetos para enlazarlos en un programa completo o 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 es así: generalmente hay muchos de ellos, 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áticas y dinámicas.

A biblioteca estática (en Unix) es casi siempre con el sufijo .a (los ejemplos incluyen libc.a que es la biblioteca C núcleo, 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á este:

name.o: 
00000000 T print_name 
     U printf 

Como se puede ver que es sobre todo una gran mesa de ficheros objeto con un índice de búsqueda de 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 tuviera que vincular en otro .o (por ejemplo, date.o a print_date), vería otra entrada como la de arriba.

Si vincula una biblioteca estática a un archivo ejecutable, inserta toda la biblioteca en el archivo ejecutable. Esto es como vincular en todos los archivos individuales .o. 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.

Un dinámico o biblioteca 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 que contiene alrededor de dos docenas de símbolos sólo dos de las cuales 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 [email protected]@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 [email protected]@GLIBC_2.0 

una biblioteca compartida se diferencia de una biblioteca estática de una manera muy importante: no incrustarse en su ejecutable final. En cambio, el archivo ejecutable contiene una referencia a esa biblioteca compartida que se resuelve, no en tiempo de enlace, sino en 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.
  • Puede, si tiene cuidado con la compatibilidad binaria, actualizar el código en la biblioteca entre las ejecuciones del programa, y ​​el programa seleccionará la nueva biblioteca sin necesidad de cambiarla.

hay algunas desventajas:

  • Se necesita tiempo para vincular un programa juntos. 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.
  • Corre el riesgo de incompatibilidades sutiles entre las diferentes versiones de la biblioteca. En Windows esto se llama "infierno DLL".

(Si se piensa en ello muchos de estos son los programas razones usan o no usan referencias y punteros en lugar de incrustar objetos directamente 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.

+8

Vale la pena observar que las bibliotecas compartidas son una forma común de implementar complementos. El complemento es una forma de extender la funcionalidad de la aplicación sin cambiarla. La aplicación puede cargar una biblioteca compartida llamando a dlopen() con ruta a la biblioteca un argumento, seguido de dlsym() para encontrar un símbolo particular dentro de la biblioteca (por ejemplo, función). Que la aplicación llama a la función para realizar funciones desde la biblioteca. – dimba

+1

Una buena introducción al mundo de la carga y la vinculación :) +1 – xtofl

+0

Bien explicado :) +1 –

Cuestiones relacionadas