¿Diferencia entre objetos compartidos (.so), bibliotecas estáticas (.a) y DLL (.so)?

273

He estado involucrado en un debate con respecto a las bibliotecas en Linux, y me gustaría confirmar algunas cosas.

Según tengo entendido (corríjame si me equivoco y editaré mi publicación más tarde), hay dos formas de usar las bibliotecas al crear una aplicación:

  1. Bibliotecas estáticas (archivos .a): en el momento del enlace, una copia de toda la biblioteca se coloca en la aplicación final para que las funciones dentro de la biblioteca estén siempre disponibles para la aplicación que realiza la llamada.
  2. Objetos compartidos (archivos .so): en el momento del enlace, el objeto solo se verifica contra su API a través del archivo de encabezado (.h) correspondiente. La biblioteca no se usa realmente hasta el tiempo de ejecución, donde se necesita.

La ventaja obvia de las bibliotecas estáticas es que permiten que toda la aplicación sea autónoma, mientras que el beneficio de las bibliotecas dinámicas es que el archivo ".so" puede reemplazarse (es decir, en caso de que deba actualizarse debido a una seguridad error) sin requerir que se vuelva a compilar la aplicación base.

He oído que algunas personas hacen una distinción entre objetos compartidos y bibliotecas vinculadas dinámicas (DLL), a pesar de que ambos son archivos ".so". ¿Hay alguna distinción entre los objetos compartidos y las DLL cuando se trata del desarrollo de C / C ++ en Linux o en cualquier otro sistema operativo compatible con POSIX (es decir: MINIX, UNIX, QNX, etc.)? Me dijeron que una diferencia clave (hasta ahora) es que los objetos compartidos solo se usan en tiempo de ejecución, mientras que los DLL deben abrirse primero usando la llamada dlopen () dentro de la aplicación.

Finalmente, también escuché que algunos desarrolladores mencionan "archivos compartidos", que, según tengo entendido, también son bibliotecas estáticas, pero una aplicación nunca los usa directamente. En cambio, otras bibliotecas estáticas se vincularán con los "archivos compartidos" para extraer algunas (pero no todas) funciones / recursos del archivo compartido a la biblioteca estática que se está creando.

Gracias a todos de antemano por su ayuda.

Actualizar


En el contexto en el que se me proporcionaron estos términos, fueron un término efectivamente erróneo utilizado por un equipo de desarrolladores de Windows que tuvieron que aprender Linux. Traté de corregirlos, pero las normas de lenguaje (incorrectas) se quedaron.

  1. Objeto compartido: una biblioteca que se vincula automáticamente a un programa cuando se inicia el programa, y ​​existe como un archivo independiente. La biblioteca se incluye en la lista de enlaces en tiempo de compilación (es decir, LDOPTS+=-lmylibpara un archivo de biblioteca llamado mylib.so). La biblioteca debe estar presente en el momento de la compilación y cuando se inicia la aplicación.
  2. Biblioteca estática: una biblioteca que se fusiona con el programa real en el momento de la compilación para una sola aplicación (más grande) que contiene el código de la aplicación y el código de la biblioteca que se vincula automáticamente a un programa cuando se construye el programa, y ​​el binario final que contiene ambos El programa principal y la biblioteca en sí existe como un único archivo binario independiente. La biblioteca se incluye en la lista de enlaces en tiempo de compilación (es decir, LDOPTS+=-lmylibpara un archivo de biblioteca llamado mylib.a). La biblioteca debe estar presente en el momento de la compilación.
  3. DLL: esencialmente lo mismo que un objeto compartido, pero en lugar de incluirse en la lista de enlaces en tiempo de compilación, la biblioteca se carga a través de dlopen()/ dlsym()comandos para que la biblioteca no necesite estar presente en el momento de la compilación para que el programa compile. Además, la biblioteca no necesita estar presente (necesariamente) en el inicio de la aplicación o en el momento de la compilación , ya que solo se necesita en el momento en que se realizan las llamadas dlopen/ dlsym.
  4. Shared Archive: esencialmente lo mismo que una biblioteca estática, pero se compila con los indicadores "export-shared" y "-fPIC". La biblioteca se incluye en la lista de enlaces en tiempo de compilación (es decir, LDOPTS+=-lmylibSpara un archivo de biblioteca llamado mylibS.a). La distinción entre los dos es que este indicador adicional es necesario si un objeto compartido o DLL desea vincular estáticamente el archivo compartido en su propio código Y poder hacer que las funciones en el objeto compartido estén disponibles para otros programas, en lugar de solo usarlos interno a la DLL. Esto es útil en el caso de que alguien le proporcione una biblioteca estática y desee volver a empaquetarla como SO. La biblioteca debe estar presente en el momento de la compilación.

Actualización adicional

La distinción entre " DLL" y " shared library" era solo un coloquialismo (vago, inexacto) en la empresa en la que trabajaba en ese momento (los desarrolladores de Windows se vieron obligados a cambiar al desarrollo de Linux, y el término se atascó), cumpliendo con las descripciones mencionadas anteriormente.

Además, el " S" literal final después del nombre de la biblioteca, en el caso de los "archivos compartidos" era solo una convención utilizada en esa compañía, y no en la industria en general.

Nube
fuente
14
Para los .aarchivos, la "a" en realidad significa "archove", y es simplemente un archivo de archivos de objetos. Los enlazadores modernos deberían ser lo suficientemente buenos como para no necesitar incluir la biblioteca while, solo los archivos de objetos en el archivo que se necesitan, e incluso podrían simplemente usar las secciones de código / datos en los archivos de objetos a los que se hace referencia.
Algún programador amigo
44
DLL es solo la terminología de Windows. No se usa en unidades.
R .. GitHub DEJA DE AYUDAR AL HIELO
2
Lea tldp.org/HOWTO/Program-Library-HOWTO
Basile Starynkevitch el
2
@DevNull "arch i ve", por supuesto. :)
Algún programador amigo

Respuestas:

94

Siempre he pensado que las DLL y los objetos compartidos son términos diferentes para lo mismo: Windows los llama DLL, mientras que en los sistemas UNIX son objetos compartidos, con el término general, biblioteca vinculada dinámicamente, que cubre ambos (incluso la función para abrir un .so en UNIX se llama dlopen()después de 'biblioteca dinámica').

De hecho, solo están vinculados al inicio de la aplicación, sin embargo, su noción de verificación contra el archivo de encabezado es incorrecta. El archivo de encabezado define los prototipos necesarios para compilar el código que utiliza la biblioteca, pero en el momento del enlace, el enlazador mira dentro de la biblioteca para asegurarse de que las funciones que necesita están realmente allí. El enlazador tiene que encontrar los cuerpos de las funciones en algún lugar en el momento del enlace o generará un error. TAMBIÉN lo hace en tiempo de ejecución, porque como señala correctamente, la biblioteca en sí misma podría haber cambiado desde que se compiló el programa. Es por eso que la estabilidad de ABI es tan importante en las bibliotecas de plataforma, ya que el cambio de ABI es lo que rompe los programas existentes compilados con versiones anteriores.

Las bibliotecas estáticas son solo paquetes de archivos de objetos directamente del compilador, al igual que los que está construyendo usted mismo como parte de la compilación de su proyecto, por lo que se introducen y se envían al enlazador exactamente de la misma manera, y los bits no utilizados son cayó exactamente de la misma manera.

Matthew Walton
fuente
1
¿Por qué algunos proyectos que veo en Linux tienen que usar la llamada dlopen () para acceder a las funciones dentro de un archivo ".so", y algunos no tienen que hacerlo en absoluto? ¡Gracias por cierto!
Nube
9
Aquellos que no lo hacen obtienen las funciones que les da el cargador de procesos, es decir, el cargador de elfos de Linux. dlopen existe si la aplicación quiere abrir y usar un .so o .dll que no estaba allí para compilar eso o simplemente agregar funcionalidad adicional, como complementos.
rapadura
Pero, ¿no se compilará la aplicación si el .so no está presente en el momento de la compilación? ¿Es posible forzar al enlazador a construir el programa final sin el .so presente? Gracias.
Nube
1
Creo que depende de cómo use las funciones de .so, pero aquí mi conocimiento de esto se detiene: / Buenas preguntas.
rapadura
1
Con respecto a dlopen () y su familia de funciones, tengo entendido que esto se usa para abrir / cerrar mediante programación un dll para que no tenga que cargarse en la memoria durante toda la ejecución de la aplicación. De lo contrario, debe decirle al vinculador en sus argumentos de línea de comandos (también conocido como su archivo MAKE) que desea cargar la biblioteca. Se cargará en tiempo de ejecución y permanecerá cargado en la memoria hasta que la aplicación salga. Hay más cosas que pueden suceder a nivel del sistema operativo, pero esto es más o menos lo que sucede en lo que respecta a su aplicación.
Taylor Price
198

Una biblioteca estática (.a) es una biblioteca que se puede vincular directamente al ejecutable final producido por el vinculador, está contenida en ella y no es necesario tener la biblioteca en el sistema donde se implementará el ejecutable.

Una biblioteca compartida (.so) es una biblioteca que está vinculada pero no incrustada en el ejecutable final, por lo que se cargará cuando se ejecute el ejecutable y deba estar presente en el sistema donde se implementa el ejecutable.

Una biblioteca de enlaces dinámicos en Windows (.dll) es como una biblioteca compartida (.so) en Linux, pero hay algunas diferencias entre las dos implementaciones que están relacionadas con el sistema operativo (Windows vs Linux):

Una DLL puede definir dos tipos de funciones: exportadas e internas. Las funciones exportadas están destinadas a ser llamadas por otros módulos, así como desde dentro de la DLL donde están definidas. Por lo general, las funciones internas están destinadas a ser llamadas desde dentro de la DLL donde están definidas.

Una biblioteca SO en Linux no necesita una declaración de exportación especial para indicar símbolos exportables, ya que todos los símbolos están disponibles para un proceso de interrogación.

aleroot
fuente
1
+1 buena explicación simple. Si una función se declara "Interna" en una DLL, ¿eso significa que no se puede llamar desde fuera de la biblioteca?
Mike
23
No es necesariamente cierto que todos los símbolos estén disponibles en una biblioteca SO. Los símbolos ocultos son posibles y recomendados porque no hay una buena razón para que los usuarios de la biblioteca vean todos sus símbolos.
Zan Lynx
3
FYI: g ++ tiene __attribute__sintaxis para símbolos de 'exportación' selectivamente:#define DLLEXPORT __attribute__ ((visibility("default"))) #define DLLLOCAL __attribute__ ((visibility("hidden")))
Brian Haak
33

Puedo elaborar los detalles de las DLL en Windows para ayudar a aclarar esos misterios a mis amigos aquí en * NIX-land ...

Una DLL es como un archivo de objeto compartido. Ambas son imágenes, listas para cargar en la memoria por el cargador de programas del sistema operativo respectivo. Las imágenes están acompañadas por varios bits de metadatos para ayudar a los vinculadores y cargadores a hacer las asociaciones necesarias y usar la biblioteca de código.

Las DLL de Windows tienen una tabla de exportación. Las exportaciones pueden ser por nombre o por posición de tabla (numérico). El último método se considera "vieja escuela" y es mucho más frágil: reconstruir la DLL y cambiar la posición de una función en la tabla terminará en un desastre, mientras que no existe un problema real si la vinculación de los puntos de entrada es por nombre. Entonces, olvídalo como un problema, pero ten en cuenta que está ahí si trabajas con código de "dinosaurio", como las librerías de proveedores de terceros.

Las DLL de Windows se compilan compilando y vinculando, tal como lo haría para un EXE (aplicación ejecutable), pero la DLL no debe estar sola, al igual que una aplicación debe utilizar un SO, ya sea mediante carga dinámica o por enlace de tiempo de enlace (la referencia al SO está incrustada en los metadatos del binario de la aplicación, y el cargador de programas del SO cargará automáticamente los SO referenciados). Las DLL pueden hacer referencia a otras DLL, así como las SO pueden hacer referencia a otras SO.

En Windows, las DLL solo pondrán a disposición puntos de entrada específicos. Estos se llaman "exportaciones". El desarrollador puede usar una palabra clave especial del compilador para hacer que un símbolo sea visible externamente (para otros vinculadores y el cargador dinámico), o las exportaciones se pueden enumerar en un archivo de definición de módulo que se usa en el momento del enlace cuando la propia DLL está siendo creado. La práctica moderna es decorar la definición de la función con la palabra clave para exportar el nombre del símbolo. También es posible crear archivos de encabezado con palabras clave que declararán ese símbolo como uno para importar desde una DLL fuera de la unidad de compilación actual. Busque las palabras clave __declspec (dllexport) y __declspec (dllimport) para obtener más información.

Una de las características interesantes de las DLL es que pueden declarar una función estándar de controlador "al cargar / descargar". Cada vez que la DLL se carga o descarga, la DLL puede realizar una inicialización o limpieza, según sea el caso. Esto se adapta perfectamente a tener una DLL como un administrador de recursos orientado a objetos, como un controlador de dispositivo o una interfaz de objeto compartido.

Cuando un desarrollador quiere usar una DLL ya construida, debe hacer referencia a una "biblioteca de exportación" (* .LIB) creada por el desarrollador de DLL cuando creó la DLL, o debe cargar explícitamente la DLL en tiempo de ejecución y solicitar el dirección del punto de entrada por nombre a través de los mecanismos LoadLibrary () y GetProcAddress (). La mayoría de las veces, la vinculación contra un archivo LIB (que simplemente contiene los metadatos del vinculador para los puntos de entrada exportados de la DLL) es la forma en que se utilizan las DLL. La carga dinámica se reserva normalmente para implementar el "polimorfismo" o la "configurabilidad de tiempo de ejecución" en los comportamientos del programa (acceder a complementos o funcionalidades definidas posteriormente, también conocidas como "complementos").

La forma en que Windows hace las cosas puede causar cierta confusión a veces; el sistema utiliza la extensión .LIB para referirse tanto a las bibliotecas estáticas normales (archivos, como archivos POSIX * .a) como a las bibliotecas "exportar código auxiliar" necesarias para vincular una aplicación a un archivo DLL en el momento del enlace. Por lo tanto, uno siempre debe mirar para ver si un archivo * .LIB tiene un archivo * .DLL con el mismo nombre; de lo contrario, es muy probable que el archivo * .LIB sea un archivo de biblioteca estático y no exporte metadatos vinculantes para un archivo DLL.

JoGusto
fuente
4

Tiene razón en que los archivos estáticos se copian en la aplicación en el momento del enlace, y que los archivos compartidos se verifican en el momento del enlace y se cargan en el tiempo de ejecución.

La llamada dlopen no es solo para objetos compartidos, si la aplicación desea hacerlo en tiempo de ejecución en su nombre, de lo contrario, los objetos compartidos se cargan automáticamente cuando se inicia la aplicación. DLLS y .so son lo mismo. el dlopen existe para agregar aún más capacidades de carga dinámica de grano fino para los procesos. No tiene que usar dlopen usted mismo para abrir / usar las DLL, eso también ocurre al iniciar la aplicación.

rapadura
fuente
¿Cuál sería un ejemplo de uso de dlopen () para más control de carga? Si el SO / DLL se carga automáticamente al inicio, ¿se cierra y vuelve a abrir dlopen () con diferentes permisos o restricciones, por ejemplo? Gracias.
Nube
1
Creo que el dlopen es para complementos o funcionalidades similares. Los permisos / restricciones deben ser los mismos que para la carga automática, y de todos modos un dlopen cargará recursivamente las bibliotecas dependientes.
rapadura
DLL y no.so son exactamente lo mismo. Ver esta respuesta
Basile Starynkevitch