Enlace dinámico - Linux vs. Ventanas

10

En Windows, cuando compilo código C / C ++ en un proyecto DLL en MSVC obtengo 2 archivos:

  1. MyDll.dll
  2. MyDll.lib

donde, por lo que entiendo, MyDll.libcontiene 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.libse incrusta en el archivo exe durante el enlace, por lo que en tiempo de ejecución "sabe" dónde se encuentran las funciones MyDll.dlly puede usarlas.

Pero si puedo compilar el mismo código en Linux estoy consiguiendo solamente un solo archivo MySo.sosin MySo.a(el equivalente a libarchivo en Linux) Entonces, ¿cómo un archivo ejecutable en Linux sabe donde las funciones se encuentran en MySo.sosi nada se incrusta en él durante el enlace?

Benny K
fuente

Respuestas:

1

En Linux, el enlazador (no el enlazador dinámico) busca a través de las bibliotecas compartidas especificadas en el momento del enlace y crea referencias a ellas dentro del ejecutable. Cuando el enlazador dinámico carga estos ejecutables, carga las bibliotecas compartidas que requieren en la memoria y resuelve los símbolos, lo que permite que se ejecuten los archivos binarios.

MySo.a, si se crea, en realidad incluiría los símbolos que se vincularán directamente al binario en lugar de las "tablas de búsqueda de símbolos" utilizadas en Windows.

La respuesta de rustyx explica el proceso en Windows más a fondo que yo; Ha pasado mucho tiempo desde que usé Windows.

SS Anne
fuente
1
"Windows adopta un enfoque diferente ... especifica al sistema operativo exactamente dónde están los símbolos en la DLL"; esto contradice la wiki , que dice que los nombres de las funciones aún se resuelven (al inicio o en la primera llamada a la función de biblioteca) incluso cuando use ordinales (a menos que se use un enlace directo de dirección que nadie haría porque obliga a los usuarios de la biblioteca a recompilar y volver a implementar su código cada vez que la biblioteca cambie).
1919
@yugr Quitó esa parte, estaba agarrando pajas de todos modos.
SS Anne
4

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.

rustyx
fuente
"Los enlazadores en otras plataformas no tienen esta característica"; sin embargo, es fácil de implementar (por ejemplo, Implib.so lo hace para Linux) para lograr una carga demorada y otras ventajas.
1919
@yugr: por eso "característica" está entre comillas: no es algo que generalmente desee hacer y es un trabajo adicional que debe hacer en Windows.
Chris Dodd
1

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 .libarchivo 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 ).

yugr
fuente
0

En Linux, pasa MySo.soal vinculador y puede extraer solo lo que se necesita para la fase de enlace, poniendo una referencia que MySo.sose necesita en tiempo de ejecución.

Un programador
fuente
-3

.dllo .soson bibliotecas compartidas (vinculado en tiempo de ejecución), mientras que .ay .libes 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:

int example(int x) {
  ...do_something...
}

int ret = example(42);

Sin embargo, a nivel asm, podría haber muchas diferencias. Por ejemplo, en x86, callse ejecuta un código de operación y 42se 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/ .libcon é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 .ase requiere y hay compatibilidad binaria entre las bibliotecas compartidas, a cambio no tenemos las ventajas de la costumbre de Microsoft.

peterh - Restablece a Monica
fuente
1
Proporcione un enlace de prueba de que las funciones de código auxiliar pueden cambiar el orden de los argumentos. Nunca he oído hablar de esto antes y es difícil de creer, dado lo grande que sería la sobrecarga de rendimiento.
yugr
@yugr Un simple registro / reordenamiento de pila no es una sobrecarga de rendimiento. Si usa dlls compilados con msvc de binarios compilados con msvc, entonces obviamente no sucederá demasiado, pero podría suceder.
peterh - Restablece a Monica el
1
Podríamos discutir sobre esto, pero en caso de que tenga razón, debería ser fácil proporcionar enlaces de prueba de que las funciones de código auxiliar son capaces de procesar no trivialmente los argumentos (y ser más que simples trampolines falsos).
1919
@yugr Los apéndices tienen acceso a las firmas de funciones de la dll, lo que hace que el procesamiento no trivial sea trivial.
peterh - Restablece a Monica el
1
Solo le sugiero que complete su respuesta con algunos enlaces de prueba con respecto a lo que hace la biblioteca de importación (porque algunas de las afirmaciones son cuestionables).
1919