Mostrando entradas con la etiqueta biblioteca estática. Mostrar todas las entradas
Mostrando entradas con la etiqueta biblioteca estática. Mostrar todas las entradas

miércoles, 20 de noviembre de 2013

Autotools (3): Integración de libtool en autotools para la creación de una biblioteca a instalar en el sistema.

En otros post anteriores vimos la escencia de las bibliotecas así como de libtool, si además haz realizado aunque sea un “hello world” con autotools entonces ahora veamos como integramos a libtool con autotool para la creación de una biblioteca.

#configure.ac
LT_INIT: Es la forma en que actualmente se define LIBTOOL.

# Checks for programs.
AC_PROG_CXX: Chequea por un compilador para c++.
AC_PROG_AWK: Chequea gawk, mawk, nawk, and awk, en ese orden y lo establece como variable.
AC_PROG_CC: Chequea por un compilador para c.
AC_PROG_CPP: Muetra el valor de la variable CPP que contiene el preprocesador.
AC_PROG_INSTALL: Muetra el valor de la variable INSTALL para instalación.
AC_PROG_LN_S: Chequea que ls sistema en que te encuentras soporta links simbólicos(ln -s), en caso que este no esté disponible lo establece a ln, en otro caso a cp -pR.
AC_PROG_MAKE_SET: Establece el comando make.

#lib/Makefile.am:
CLEANFILES: Para que make clean borre los archivos que coincidan.

AM_CPPFLAGS: El contenido de esta variable es pasado a todas las reglas de compilación que invoquen el preprocesador de C.

Definiciones para el uso de pkg-config una vez creada la lib.
pkgconfigdir = @libdir@/pkgconfig
pkgconfig_DATA = libmatX.pc

A diferencia del primer ejemplo donde poniamos bin_PROGRMAS para crear ejecutables aquí usamos esta macro a la cual le decimo el nombre de la lib a crear y en el source debe transformar el nombre a su forma canónica.
lib_LTLIBRARIES = libmatX.la
libmatX_la_SOURCES = \
matX.cc

libmatX_la_LDFLAGS: Flags para el elnlazador.


“#Headers/Makefie.am
includedir: Referencia al directorio include donde se pondran los headers
nobase_include_HEADERS: Para que no se instale dos veces con una invocación del programa.
Una vez configurado el proyecto procedemos a la contrucción:
1. mkdir -p m4
2. libtoolize --copy –force
3. aclocal -I m4
4. autoheader
5. autoconf
6. automake -a
7. ./configure
.8 make
Ya podrás ver que se ha creado lib/libmatX.la, incluso lo podrías leer(cat lib/libmatX.la) ya que es texto plano, en este punto la biblioteca ya es usable, esto sería en nuestro progrma usar la macro LDADD (tambien con el nombre del ejecutable como prefijo y con _ al final) para esto puedes consultar GNU Automake aunque este ejemplo cuneta con su *.pc.
9. sudo make install
Ya deberías poder verificar como instalada la biblioteca en tu sistema
10. pkg-config --list-all | grep hello_libtool

Ya teniendo esta infraestructura básica del proyecto veamos como crear las bibliotecas estática y dinámica correspondiente.

Biblioteca estática:
make clean
./configure --disable-shared
make
Biblioteca dinámica:
make clean
./configure --disable-static
make

NOTA: En caso de no especificar se crea la dinámica y la estática.

Desde aquí puedes descargar el ejemplo:

git clone https://github.com/denisacostaq/EmbeLinux --branch hello-libtool

martes, 19 de noviembre de 2013

Creación de bibliotecas mediante el uso de libtool (Parte 2).

Si ya revisaste el post anterior sobre construcción de bibliotecas podremos proseguir con el hecho de que ya tenemos algunos conceptos básicos de nuestro lado, esta vez relizaremos el mismo ejemplo lo que cambia es las herramientas. Primero construiremos la lib estática, después compartida y por último de ejecución (que no es más que la misma biblioteca compartida lo que se carga en tiempo de ejecución).

Libtool:
Según su propia documentación (apt-get install libtool-doc) fue diseñado desde sus inicios para soportar un número arbitrario de tipos de bibliotecas, más tarde se ha ido portando a otras plataformas y gradualmente se han ido desarrollando nuevos paradigmas que describen la relación entre las bibliotecas y los programas.

Consideremos el sigiente código con el cual crearemos la biblioteca estática y compartida:
//module.c
float mult (float fac1, float fac2)
{
return fac1 * fac2;
}

Biblioteca estática:
1. Creamos un directorio donde “instalaremos” la lib.
mkdir -p /tmp/install
2. Compiamos el código.
libtool --verbose --mode=compile gcc -g -O -c module.c -static
3. Enlazamos el código, si no ponemos -rpath /tmp/install al comando anterior tendremos que ejecutar
libtool --finish /usr/local/lib después para que se complete la instalación correctamente.
libtool --verbose --mode=link gcc -module -o module.la module.lo -rpath /tmp/install -static
4. Instalamos
libtool --mode=install install module.la /tmp/install
Ya tenemos lista la lib en /tmp/install que puedes usar como en el post anteriorque mencionaba al inicio y además notar que se encuentra en el direcorio oculto (ls .libs/).

Biblioteca dinámica:
Recomendable eliminar los archivos anteriores para ver el nuevo resultado y repitiendo los pasos para este caso tenemos:
1. mkdir -p /tmp/install
2. libtool --verbose --mode=compile gcc -g -O -c module.c -shared
3. libtool --verbose --mode=link gcc -module -o module.la module.lo -rpath /tmp/install -shared
4. libtool --mode=install install module.la /tmp/install

Biblioteca de ejecución:

Para ver como usamos una lib en tiempo de ejecución usaremos el siguiente código:

#include <stdio.h>
#include <ltdl.h>
#include <stdlib.h>

//declaramos una variable(puntero a la función así que
//debe coincidir en los tipos)
float (*mult)(float fac1, float fac2);

int main (int argc, char *argv[])
{
lt_dlhandle handle;
lt_dlinit ();
lt_dladdsearchdir ("./");

handle=lt_dlopenext ("module");
if (!handle)
{
printf ("Error, módulo no cargado: %s\n",
                   lt_dlerror());
exit (EXIT_FAILURE);
}

const lt_dlinfo *info;
info = lt_dlgetinfo (handle);
if (!info)
{
fprintf (stderr, "No se pudo obtener la información  del módulo: %s\n", lt_dlerror());
lt_dlclose (handle);
exit (EXIT_FAILURE);
}
if (info->name)
{
printf ("Nombre del módulo: %s\n", info->name);
}
else
{
printf ("No es un módulo libtool\n");
}
//casteo al tipo de la función
mult = (float(*)(float, float))lt_dlsym(handle, "mult");  
if (mult)
{
printf ("Resultado desde el módulo: %f\n", mult(5.0, 10.0));
}
else
{
perror ("No se pudo cargar la función\n");
lt_dlclose (handle);
exit (EXIT_FAILURE);
}
lt_dlclose (handle);
return 0;
}


Recomendable eliminar los archivos anteriores para ver el nuevo resultado y repitiendo los pasos para este caso tenemos:
1. libtool --verbose --mode=compile gcc -g -O -c module.c -shared
2. libtool --verbose --mode=link gcc -module -o module.la module.lo -rpath /tmp/install -shared
3. libtool --mode=link gcc -export-dynamic -o programdl program.c -lltdl

Un detalle importante es notar que la lib se contruye de la misma manera que la compartida, el programa es el que se contruye de forma diferente a la convencional y además con ldd como en el post anterior puedes ver como program no depende de module por lo que los pasos 2 y 3 se pueden cambiar de orden ya que program no necesita saber nada acerca de module en tiempo de compilación.
NOTA: Para tener más referencia consulte man libtool o /usr-sha-doc-libtool-doc.

lunes, 16 de septiembre de 2013

Entendiendo la escencia de las bibliotecas mediante ejemplos de su creación y utilización.


Como ejemplo tomaremos la creación de una biblioteca para cálculos matemáticos, primero la haremos y usaremos estática y después compartida.
Biblioteca estática:
En una biblioteca estática tenemos una serie de procedimientos que son comunes pero cada programa que compilemos a partir de ella incluirá los archivos de los que use cualquier cosa dentro de su propio código atentando contra el tamaño del archivo binario final, no será necesario tener la biblioteca en el entorno en que va a correr el programa.

//plus.c
int plus (int sum1, int sum2)
{ 
    return (sum1 + sum2); 
}

//mult.c
int mult (int fact1, int fact2)
{
    return (fact1 * fact2);
} 
Para obtener el código objeto de estas dos funciones y/o archivos ejecutamos: gcc plus.c mult.c -c, con la opción -c le decimos a gcc que compile o ensamble nuestro código pero que no lo elnace (gcc se refiere a GNU Collection Compilers y/o a GNU C Compiler, pero en este último caso aclarar que no es en realidad gcc quien compila sino que es más bién un driver, o sea encarga a otros programas a hacer el trabajo). Para crear la bibliteca ejecutamos ar -r libmat.a plus.o mult.o, con esto libmat.a queda lista para ser usada como una biblioteca estática (rm *.o *.c).

//main .c
#include <stdio.h>
int plus (int, int);
int mult (int, int);
int main (int argc, char *argv[])
{
    int val1 = 2;
    int val2 = 5;
    printf (“%d + %d = %d\n”, val1, val2, plus (val1, val2));
    printf (“%d * %d = %d\n”, val1, val2, mult (val1, val2));
    return 0;
}
gcc main.c -lmat -o program (produce el siguiente error)
/usr/bin/ld: cannot find -lmat
collect2: error: ld returned 1 exit status 
Ahora ejecutamos gcc main.c libmat.a -o program que sí compila correctamente pero le estamos pasando la ruta de la lib de forma explícita, la manera elegante sería diciendole a gcc enlazate a mat(-lmat) para que él solo la busque pero no lo hemos conseguido porque el enlazador(ld) ha buscado en los directorios que tiene por defecto normalmente(/lib y /usr/lib más cat /etc/ld.so.conf.d/*) así que podemos agregar nustro directorio actual a las rutas de búsqueda con gcc main.c -L. -lmat -o program. Una forma más elegante de compilar es usando la variable de entorno LIBRARY_PATH, que sería así:
export LIBRARY_PATH=$LIBRARY_PATH:.
gcc main.c -lmat -o program
Además para ver lo que decía sobre que gcc es un driver podrias ejecutar gcc -### -lmat main.c -o program con lo que gcc te dice lo que haría si le quitas -### pero no lo hace.
Biblioteca compartida:
A diferencia de una biblioteca estática los programas que se compilan con bibliotecas compartidas (shared object, extención .so) no incluyen el código de estas dentro de sí, sino información de con que lib ha sido enlazado el programa de manera que cuando el programa se va a ejecutar el linkeador (ld-linux, no el que se usa para compilar) determina de qué bibliotecas depende nuestro programa para estar en RAM y de faltar alguna la carga, por ejemplo para el programa que habiamos compilado hasta ahora podemos saber de que depende usando ldd program, deberás notar que no depende de libmat.
Para crear una biblioteca comartida solo tenemos que especificarle esto a gcc con shared, usando todo el codigo que hasta ahora teniamos solo cambiaremos la manera en que creamos la lib y el programa y veremos el impacto:
gcc -c -fPIC mult.c plus.c
gcc -shared mult.o plus.o -o libmat.so
gcc main.c -lmat -o program (que tendrás un error, así que resuelvelo... ;) )
./prgram (te generará el siguiente error, ./program: error while loading shared libraries: libmat.so: cannot open shared object file: No such file or directory, si ejecutamos ldd program verás que a diferencia de el caso pasado este si depende de libmat y que además “not found”, para resolver esto tenemos que agregar como ruta de busqueda al elnlazador ld-linux(no el de compilar insisto), la ruta donde esté nuestra lib o mejor una solución más pacífica export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. , con lo que puedes volver a ejecutar ldd para ver el cambio y además ya puedes ejecutar a ./program).


Bibliotecas dinámicas:
En este punto solo mencianaré que las bibliotecas dinámicas son una “forma” de las bibliotecas compartidas la diferencia es que en ahora el SO no autodetecta las bibliotecas de que depende el programa y las carga de forma automática, es el programador quien tiene esta responsabilidad, básicamente se cargan bajo demanda, tiene la ventaja de que la aplicación se carga en RAM más rápidamente y el inconveniente de que es el programador quien tiene la responsabilidad de hacer entonces ese trabajo, pudiera ser por ejemplo usando la lib ltdl que cuenta con funciones como dlopen.
En algún que otro post relacionado con autotools+libtool veremos como los humanOS hacemos esto, ya que hasta ahora solo vimos el método de los  “dinosauriOS” :-D .