Crear librerías .a a partir de librerías .lib

Libros

Con cierta frecuencia encontramos bibliotecas (o librerías) de funciones que nos pueden resultar útiles en nuestros programas. En este blog hablaremos de algunas de ellas.

Casi siempre las encontraremos en formato de de bibliotecas de enlace dinámico (DLL), pero incluso en esos casos, para usarlas en nuestros programas C/C++ usaremos una biblioteca estática para acceder a ellas.

Un problema frecuente, cuando se trata de librerías de dominio público y cuando las queremos usar en Windows junto con MinGW/GCC es que la librería estática se distribuye con la extensión ".lib", y MingW/GCC usan el formato de Unix, con la extensión ".a".

No son formatos compatibles, y no basta con renombrar el fichero, ya lo intenté...

Afortunadamente, junto con el paquete de MinGW se distribuyen algunas herramientas que permiten hacer una conversión de ".lib" a ".a".

Crear una carpeta de trabajo

Con el fin de no contaminar carpetas de aplicaciones, o si no tenemos definido el PATH para la carpeta bin de MinGW, lo más sencillo es crear una carpeta de trabajo en la que haremos las conversiones que necesitemos. Puedes usar una carpeta temporal, o crear la que mejor te parezca.

En esa carpeta copiaremos los siguientes ficheros, que encontraremos en la carpeta MinGW/bin, cuya ruta completa dependerá de la instalación que hayas hecho del compilador.

  • reimp.exe
  • dlltool.exe
  • as.exe

El fichero "reimp.exe" no siempre viene con MinGW, de hecho, casi nunca viene. Forma parte de las utilidades de MinGW, y se encuentra en un paquete que se debe obtener por separado. Puedes buscar la última versión aquí.

Además, copiaremos los ficheros necesarios para hacer la conversión, y que generalmente estarán incluidos en el paquete de la biblioteca que hemos descargado:

  • <biblioteca>.lib
  • <biblioteca>.dll

Obtener el fichero .DEF

El primer paso consiste en obtener el fichero <biblioteca>.def. A veces el fichero forma parte del paquete, si es así, ¡enhorabuena!

Si no, no te preocupes, es fácil de obtener, basta con ejecutar esta orden desde una ventana de línea de comandos:

reimp -d <biblioteca>.lib

A veces hay que eliminar los caracteres '_' que aparecen como prefijos en el fichero .def obtenido. Se puede hacer con un editor normal, ya que el fichero .def sólo contiene texto.

Una segunda alternativa, válida en caso de nos disponer del fichero .lib, es conseguir la utilidad "pexports", que también forma parte de las utilidades de MinGW.

Para obtener el fichero .def, esta utilidad usa el fichero .dll, con una línea de comandos de este tipo:

pexports fichero.dll > fichero.def

En este caso podemos tener algunos problemas, que aunque fáciles de resolver, pueden darnos algunos dolores de cabeza.

Las funciones de una DLL pueden usar dos tipos de llamadas: stdcall y cdecl. Si se usa stdcall, el nombre de la función en el fichero .def debe tener un sufijo @'n', donde n es un número entero.

Por desgracia, la utilidad pexports no es capaz de generar esos sufijos, de modo que tendremos que añadirlos manualmente, editando el fichero .def.

La cuestión es, cómo saber qué funciones usan stdcall, y cual es el valor de n en cada una de ellas.

Hasta donde he podido averiguar, la única forma es compilar el programa y ver qué mensajes de error nos da en la fase de enlazado.

Por ejemplo, si obtenemos un mensaje del tipo:

  undefined reference to `<funcion>@4'

En este caso sabremos que la función <funcion> usa stdcall, y que debemos añadir el sufijo @4 en el fichero .def. Por supuesto, en ese caso, tendremos que volver a generar el fichero de librería estática .a.

Obtener el fichero .a

A partir del fichero .def y del fichero .dll podemos obtener el fichero de librería estática usando este comando:

dlltool -d <biblioteca>.def -D <biblioteca>.dll -k -l lib<biblioteca>.a -S ./as.exe

Es importante añadir el prefijo "lib" al fichero de biblioteca estática obtenido, ya que MinGW no lo encontrará si no tiene el formato lib<nombre>.a

Cada fichero en su lugar

Ahora sólo queda copiar cada fichero en su sitio.

El fichero con extensión ".a" debe copiarse a la carpeta "lib" de MinGW, de modo que el enlazador pueda localizarlo. Otra alternativa es copiarlo en otra ruta, pero en ese caso hay que añadir esa ruta a cada proyecto que use esta biblioteca.

El fichero con extensión ".h", que formará parte del paquete, debe copiarse a la carpeta "include" de MinGW, o a la carpeta del proyecto. Eso depende de tu propio criterio, y en última instancia, de con qué frecuencia uses estas bibliotecas.

El fichero ".dll" debe distribuirse junto a la aplicación. De nuevo, si se trata de una biblioteca que se usa con cierta frecuencia, puedes copiarla a una carpeta que esté en el PATH del sistema, o bien copiarla en la misma carpeta que el fichero ejecutable de la aplicación.

Cuando se cree un proyecto que use esta DLL hay que incluir el parámetro "-<biblioteca>" entre las opciones del enlazador.

Estructura inicial
Estructura inicial

En este ejemplo se han añadido las bibliotecas "blat" y "sqlite". El enlazador buscará los ficheros de biblioteca estática "libblat.a" y "libsqlite.a".

Consideraciones adicionales

  1. Las últimas versiones de MinGW pueden trabajar con las versiones de Microsoft de las librerías estáticas, las que tienen extensión .lib.

    Esto facilita mucho las cosas a la hora de usar librerías estáticas o dinámicas externas.

  2. Si de todos modos queremos generar librerías estáticas para acceder a librerías de enlace dinámico compatibles con versiones antiguas de MinGW, hay que tener en cuenta qué versión del compilador usaremos.

    Es importante tener en cuenta que existen versiones de 32 y 64 bits de las herramientas dlltool.exe y as.exe. Deberemos tener cuidado de elegir las versiones adecuadas para cada caso.

    El enlazador no podrá usar las versiones de 32 bits si estamos compilando para 64 bits, y viceversa.