Sé que esto puede parecer bastante básico para los geeks. Pero quiero dejarlo muy claro.
Cuando quiero usar una DLL de Win32, generalmente solo llamo a las API como LoadLibrary () y GetProcAdderss (). Pero recientemente, estoy desarrollando con DirectX9, y necesito agregar archivos d3d9.lib , d3dx9.lib , etc.
He escuchado lo suficiente que LIB es para enlaces estáticos y DLL es para enlaces dinámicos.
Entonces, mi entendimiento actual es que LIB contiene la implementación de los métodos y está vinculado estáticamente en el momento del enlace como parte del archivo EXE final. Mientras que DLL se carga dinámicamente en tiempo de ejecución y no forma parte del archivo EXE final.
Pero a veces, hay algunos archivos LIB que vienen con los archivos DLL, así que:
- ¿Para qué sirven estos archivos LIB?
- ¿Cómo logran lo que están destinados?
- ¿Existe alguna herramienta que pueda permitirme inspeccionar el interior de estos archivos LIB?
Actualización 1
Después de consultar wikipedia, recuerdo que estos archivos LIB se llaman biblioteca de importación . Pero me pregunto cómo funciona con mi aplicación principal y las DLL que se cargan dinámicamente.
Actualización 2
Tal como dijo RBerteig, hay un código auxiliar en los archivos LIB que nacen con las DLL. Entonces, la secuencia de llamada debería ser así:
Mi aplicación principal -> stub en LIB -> DLL de destino real
Entonces, ¿qué información debería estar contenida en estos LIB? Podría pensar en lo siguiente:
- El archivo LIB debe contener la ruta completa de la DLL correspondiente; Por tanto, el tiempo de ejecución podría cargar la DLL.
- La dirección relativa (¿o el desplazamiento del archivo?) Del punto de entrada de cada método de exportación DLL debe estar codificado en el stub; Por lo tanto, se podrían realizar llamadas correctas de saltos / métodos.
¿Estoy en lo cierto en esto? Hay algo mas
Por cierto: ¿Existe alguna herramienta que pueda inspeccionar una biblioteca de importación? Si puedo verlo, no habrá más dudas.
fuente
lib /list xxx.lib
ylink /dump /linkermember xxx.lib
. Consulte esta pregunta de Stack Overflow .dumpbin -headers xxx.lib
proporciona información más detallada, en comparación con las utilidadeslib
ylink
.Respuestas:
La vinculación a un archivo DLL puede ocurrir implícitamente durante la
compilación delvínculo o explícitamente durante la ejecución. De cualquier manera, la DLL termina cargada en el espacio de memoria del proceso y todos sus puntos de entrada exportados están disponibles para la aplicación.Si se usa explícitamente en tiempo de ejecución, usa
LoadLibrary()
yGetProcAddress()
para cargar manualmente la DLL y obtener punteros a las funciones que necesita llamar.Si se vincula implícitamente cuando se crea el programa, los códigos auxiliares para cada exportación de DLL utilizada por el programa se vinculan al programa desde una biblioteca de importación, y esos códigos auxiliares se actualizan a medida que se cargan el EXE y el DLL cuando se inicia el proceso. (Sí, lo he simplificado más que un poco aquí ...)
Esos stubs deben provenir de algún lugar, y en la cadena de herramientas de Microsoft provienen de una forma especial de archivo .LIB llamada biblioteca de importación . El .LIB requerido generalmente se crea al mismo tiempo que la DLL y contiene un código auxiliar para cada función exportada desde la DLL.
Confusamente, una versión estática de la misma biblioteca también se enviaría como un archivo .LIB. No hay una forma trivial de distinguirlos, excepto que las LIB que son bibliotecas de importación para DLL normalmente serán más pequeñas (a menudo mucho más pequeñas) que la LIB estática correspondiente.
Si usa la cadena de herramientas de GCC, por cierto, no necesita bibliotecas de importación que coincidan con sus DLL. La versión del enlazador Gnu portado a Windows comprende las DLL directamente y puede sintetizar la mayoría de los stubs necesarios sobre la marcha.
Actualizar
Si simplemente no puede resistirse a saber dónde están realmente todas las tuercas y tornillos y qué está pasando realmente, siempre hay algo en MSDN para ayudarlo. El artículo de Matt Pietrek Una mirada en profundidad al formato de archivo ejecutable portátil Win32 es una descripción general muy completa del formato del archivo EXE y cómo se carga y ejecuta. Incluso se ha actualizado para cubrir .NET y más desde que apareció originalmente en MSDN Magazine ca. 2002.
Además, puede resultar útil saber cómo saber exactamente qué DLL utiliza un programa. La herramienta para eso es Dependency Walker, también conocido como depende.exe. Se incluye una versión con Visual Studio, pero la última versión está disponible de su autor en http://www.dependencywalker.com/ . Puede identificar todas las DLL que se especificaron en el momento del enlace (carga anticipada y carga retardada) y también puede ejecutar el programa y observar cualquier DLL adicional que cargue en tiempo de ejecución.
Actualización 2
He vuelto a redactar parte del texto anterior para aclararlo al volver a leerlo y para usar los términos del arte de vinculación implícita y explícita para mantener la coherencia con MSDN.
Entonces, tenemos tres formas en que las funciones de la biblioteca pueden estar disponibles para ser utilizadas por un programa. La pregunta de seguimiento obvia es entonces: "¿Cómo elijo cuál camino?"
La vinculación estática es la forma en que se vincula la mayor parte del programa en sí. Todos sus archivos de objeto se enumeran y el vinculador los recopila en el archivo EXE. En el camino, el enlazador se encarga de tareas menores como arreglar referencias a símbolos globales para que sus módulos puedan llamar a las funciones de los demás. Las bibliotecas también se pueden vincular estáticamente. Un bibliotecario recopila los archivos de objeto que componen la biblioteca en un archivo .LIB en el que el vinculador busca módulos que contengan los símbolos necesarios. Un efecto de la vinculación estática es que sólo los módulos de la biblioteca que utiliza el programa están vinculados a ella; otros módulos se ignoran. Por ejemplo, la biblioteca matemática tradicional de C incluye muchas funciones de trigonometría. Pero si lo vincula y usa
cos()
, no termina con una copia del código parasin()
oatan()
menos que también llame a esas funciones. Para bibliotecas grandes con un rico conjunto de características, esta inclusión selectiva de módulos es importante. En muchas plataformas, como los sistemas integrados, el tamaño total del código disponible para su uso en la biblioteca puede ser grande en comparación con el espacio disponible para almacenar un ejecutable en el dispositivo. Sin la inclusión selectiva, sería más difícil gestionar los detalles de los programas de creación para esas plataformas.Sin embargo, tener una copia de la misma biblioteca en cada programa en ejecución crea una carga para un sistema que normalmente ejecuta muchos procesos. Con el tipo correcto de sistema de memoria virtual, las páginas de memoria que tienen un contenido idéntico solo necesitan existir una vez en el sistema, pero pueden ser utilizadas por muchos procesos. Esto crea un beneficio para aumentar las posibilidades de que las páginas que contienen código sean idénticas a alguna página en tantos otros procesos en ejecución como sea posible. Pero, si los programas se vinculan estáticamente a la biblioteca en tiempo de ejecución, entonces cada uno tiene una combinación diferente de funciones, cada una de las cuales se presenta en el mapa de memoria de los procesos en diferentes ubicaciones, y no hay muchas páginas de códigos compartibles a menos que sea un programa que por sí solo es ejecutar en más de proceso. Así que la idea de una DLL obtuvo otra gran ventaja.
Una DLL para una biblioteca contiene todas sus funciones, listas para ser utilizadas por cualquier programa cliente. Si muchos programas cargan esa DLL, todos pueden compartir sus páginas de códigos. Todo el mundo gana. (Bueno, hasta que actualice una DLL con una nueva versión, pero eso no es parte de esta historia. Google DLL Hell para ese lado de la historia).
Por lo tanto, la primera gran elección que debe tomar al planificar un nuevo proyecto es entre la vinculación dinámica y estática. Con el enlace estático, tiene menos archivos para instalar y es inmune a que terceros actualicen una DLL que usa. Sin embargo, su programa es más grande y no es tan buen ciudadano del ecosistema de Windows. Con el enlace dinámico, tiene más archivos para instalar, es posible que tenga problemas con un tercero que actualice una DLL que usa, pero generalmente está siendo más amigable con otros procesos en el sistema.
Una gran ventaja de una DLL es que se puede cargar y utilizar sin volver a compilar o incluso volver a vincular el programa principal. Esto puede permitir que un proveedor de bibliotecas de terceros (piense en Microsoft y el tiempo de ejecución de C, por ejemplo) corrija un error en su biblioteca y la distribuya. Una vez que un usuario final instala la DLL actualizada, inmediatamente obtiene el beneficio de esa corrección de errores en todos los programas que usan esa DLL. (A menos que rompa cosas. Consulte DLL Hell.)
La otra ventaja proviene de la distinción entre carga implícita y explícita. Si realiza un esfuerzo adicional de carga explícita, es posible que la DLL ni siquiera existiera cuando se escribió y publicó el programa. Esto permite mecanismos de extensión que pueden descubrir y cargar complementos, por ejemplo.
fuente
Estos archivos de biblioteca de importación .LIB se utilizan en la siguiente propiedad del proyecto
Linker->Input->Additional Dependencies
, cuando se crean varios dll que necesitan información adicional en el momento del enlace, que se proporciona mediante los archivos .LIB de la biblioteca de importación. En el siguiente ejemplo, para no obtener errores del vinculador, necesito hacer referencia a los archivos DLL A, B, C y D a través de sus archivos lib. (tenga en cuenta que para que el vinculador encuentre estos archivos, es posible que deba incluir su ruta de implementación; de loLinker->General->Additional Library Directories
contrario, obtendrá un error de compilación por no poder encontrar ninguno de los archivos lib proporcionados).Si su solución está compilando todas las bibliotecas dinámicas, es posible que haya podido evitar esta especificación de dependencia explícita confiando en cambio en los indicadores de referencia expuestos en el
Common Properties->Framework and References
cuadro de diálogo. Estas banderas parecen hacer el enlace automáticamente en su nombre utilizando los archivos * .lib.Sin embargo, esto es como dice una propiedad común , que no es específica de la configuración o plataforma. Si necesita admitir un escenario de compilación mixto, como en nuestra aplicación, teníamos una configuración de compilación para representar una compilación estática y una configuración especial que compilaba una compilación restringida de un subconjunto de ensamblados que se implementaron como bibliotecas dinámicas. Había usado las banderas
Use Library Dependency Inputs
yLink Library Dependencies
configuradas como verdaderas en varios casos para hacer que las cosas se compilaran y luego me di cuenta de simplificar las cosas, pero al introducir mi código en las compilaciones estáticas, presenté una tonelada de advertencias del enlazador y la compilación fue increíblemente lenta para las compilaciones estáticas. Terminé presentando un montón de este tipo de advertencias ...warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored D.lib(JSource.obj)
Y terminé usando la especificación manual de
Additional Dependencies
para satisfacer al vinculador de las compilaciones dinámicas mientras mantenía contentos a los constructores estáticos al no usar una propiedad común que los ralentizara. Cuando implemento la compilación del subconjunto dinámico, solo implemento los archivos dll, ya que estos archivos lib solo se usan en el momento del enlace, no en el tiempo de ejecución.fuente
Hay tres tipos de bibliotecas: estáticas, compartidas y cargadas dinámicamente.
Las bibliotecas estáticas están vinculadas con el código en la fase de vinculación, por lo que en realidad están en el ejecutable, a diferencia de la biblioteca compartida, que solo tiene stubs (símbolos) para buscar en el archivo de biblioteca compartida, que se carga en tiempo de ejecución antes de la se llama a la función principal.
Las que se cargan dinámicamente son muy parecidas a las bibliotecas compartidas, excepto que se cargan cuando y si surge la necesidad por el código que ha escrito.
fuente
LoadLibrary()
y las API relacionadas.Aquí hay algunos temas relacionados con MSDN para responder a mi pregunta:
Vincular un ejecutable a una DLL
Vinculación implícita
Determinar qué método de enlace utilizar
Creación de una biblioteca de importación y un archivo de exportación
fuente