En Windows, cuando compilo código C / C ++ en un proyecto DLL en MSVC obtengo 2 archivos:
MyDll.dll
MyDll.lib
donde, por lo que entiendo, MyDll.lib
contiene algún tipo de tabla de punteros que indica las ubicaciones de las funciones en el dll Al usar este dll, por ejemplo, en un archivo exe, MyDll.lib
se incrusta en el archivo exe durante el enlace, por lo que en tiempo de ejecución "sabe" dónde se encuentran las funciones MyDll.dll
y puede usarlas.
Pero si puedo compilar el mismo código en Linux estoy consiguiendo solamente un solo archivo MySo.so
sin MySo.a
(el equivalente a lib
archivo en Linux) Entonces, ¿cómo un archivo ejecutable en Linux sabe donde las funciones se encuentran en MySo.so
si nada se incrusta en él durante el enlace?
El enlazador MSVC puede enlazar archivos de objetos (.obj) y bibliotecas de objetos (.lib) para producir un .EXE o un .DLL.
Para vincular con un archivo DLL, el proceso en MSVC es utilizar una llamada biblioteca de importación (.LIB) que actúa como un pegamento entre los nombres de las funciones C y la tabla de exportación del archivo DLL (en un archivo DLL, una función se puede exportar por nombre o por ordinal : este último se usaba a menudo para API indocumentadas).
Sin embargo, en la mayoría de los casos, la tabla de exportación de DLL tiene todos los nombres de funciones y, por lo tanto, la biblioteca de importación (.LIB) contiene información en gran parte redundante (" función de importación ABC -> función exportada ABC ", etc.).
Incluso es posible generar un .LIB a partir de un .DLL existente.
Los vinculadores en otras plataformas no tienen esta "característica" y pueden vincularse directamente con bibliotecas dinámicas.
fuente
La diferencia que está viendo es más un detalle de implementación: bajo el capó, tanto Linux como Windows funcionan de manera similar: codifica llamadas a una función stub que está estáticamente vinculada en su ejecutable y este stub luego carga DLL / shlib si es necesario (en caso de retraso cargando , de lo contrario la biblioteca se carga cuando se inicia el programa) y (en la primera llamada) resuelve el símbolo a través de
GetProcAddress
/dlsym
.La única diferencia es que en Linux estas funciones de apéndice (que se denominan apéndices PLT) se generan dinámicamente cuando vincula su aplicación con la biblioteca dinámica (la biblioteca contiene suficiente información para generarlas), mientras que en Linux se generan cuando el propio DLL está creado, en un
.lib
archivo separado .Los dos enfoques son tan similares que en realidad es posible imitar las bibliotecas de importación de Windows en Linux (vea el proyecto Implib.so ).
fuente
En Linux, pasa
MySo.so
al vinculador y puede extraer solo lo que se necesita para la fase de enlace, poniendo una referencia queMySo.so
se necesita en tiempo de ejecución.fuente
.dll
o.so
son bibliotecas compartidas (vinculado en tiempo de ejecución), mientras que.a
y.lib
es una biblioteca estática (vinculado en tiempo de compilación). Esto no es una diferencia entre Windows y Linux.La diferencia es cómo se manejan. Nota: la diferencia es solo en la aduana, cómo se usan. No sería demasiado difícil hacer compilaciones de Linux en Windows y viceversa, excepto que prácticamente nadie hace esto.
Si usamos un dll, o llamamos a una función incluso desde nuestro propio binario, hay una manera simple y clara. Por ejemplo, en C, vemos que:
Sin embargo, a nivel asm, podría haber muchas diferencias. Por ejemplo, en x86,
call
se ejecuta un código de operación y42
se proporciona en la pila. O en algunos registros. O en cualquier parte. Nadie sabe que antes de escribir el dll , cómo se usará. O cómo los proyectos querrán usarlo, posiblemente escrito con un compilador (¡o en un idioma!) Que ni siquiera existe ahora (o es desconocido para los desarrolladores del dll).Por ejemplo, de manera predeterminada, tanto C como Pascal colocan los argumentos (y obtienen los valores de retorno) de la pila, pero lo hacen en un orden diferente . También puede intercambiar argumentos entre sus funciones en los registros mediante alguna optimización dependiente del compilador.
Como puede ver correctamente, la costumbre de Windows es que al crear un dll, también creamos un mínimo
.a
/.lib
con él. Esta biblioteca estática mínima es solo un contenedor, los símbolos (funciones) de ese dll se alcanzan a través de ella. Esto hace las conversiones de llamadas de nivel asm requeridas.Su ventaja es la compatibilidad. Su desventaja es que si solo tiene un .dll, puede tener dificultades para descubrir cómo se llamará a sus funciones. Esto hace que el uso de dlls sea una tarea de hackeo, si el desarrollador del dll no le da el
.a
. Por lo tanto, sirve principalmente para fines de cierre, por ejemplo, es más fácil obtener efectivo adicional para los SDK.Su otra desventaja es que incluso si usa una biblioteca dinámica, necesita compilar este pequeño contenedor estáticamente.
En Linux, la interfaz binaria de los dlls es estándar y sigue la convención C. Por lo tanto, no
.a
se requiere y hay compatibilidad binaria entre las bibliotecas compartidas, a cambio no tenemos las ventajas de la costumbre de Microsoft.fuente