En Linux tenemos dos tipos de bibliotecas (o librerías, hacemos referencia a lo mismo): estáticas y dinámicas.
Bibliotecas estáticas
Con extensión .a:
find / -name '*.a' .... /usr/local/lib/libintl.a /usr/local/lib/libform.a /usr/local/lib/libform_g.a /usr/local/lib/libmenu.a ...
Se copia parte de la librería en los programas cuando se compilan. Dicho de otra manera, la biblioteca se “embebe” en el contenido del programa. Esto tiene varias consecuencias:
- El programa ocupa más espacio en disco, ya que las librerías necesarias se encuentran en el propio programa. Como ventaja tenemos lo podemos llevar de un sistema a otro sin necesidad de migrar también las librerías (ya que están incluidas)
- El programa en sí consume más RAM en ejecución, ya que las librerías están embebidas. Sin embargo, se ejecuta en principio más rápido ya que todo lo necesario está incluido en el propio programa.
- Si una biblioteca que se utilizó durante la compilación se actualizara, sería necesario recompilar el programa para que incluyera los cambios que se han realizado en esa biblioteca.
Bibliotecas dinámicas
También llamadas compartidas. Extensión .so (shared object). Hoy en día son las que se utilizan por norma general:
find / -name '*.so' ... /usr/lib/amd64/libc.so /usr/lib/amd64/libc_db.so /usr/lib/amd64/libcfgadm.so /usr/lib/amd64/libcmd.so ...
En el caso de las bibliotecas dinámicas, el programa sólo incluye en su código referencias a las bibliotecas, que se localizan en otra parte. Las consecuencias de esto son:
- Programas de tamaño reducido, ya que sólo incluyen referencias a las bibliotecas y nada embebido. Menor ocupación también en RAM.
- El programa puede sacar partido de las actualizaciones de las bibliotecas sin necesidad de recompilar.
Pero por otro lado, surgen otra serie de requisitos y problemas:
- En ocasiones las actualizaciones de bibliotecas no son compatibles con los programas que las utilizan. Por ello hay un sistema de versionado en las bibliotecas compartidas:
ej.: libsocket.so.1
Donde 1 es la versión de la biblioteca compartida libsocket
Para evitar conflicto, muchas veces se actualiza una biblioteca y también se deja como respaldo la versión anterior.
- Para que los programas localicen las bibliotecas compartidas, será necesario definir una serie de variables de entorno y ficheros de configuración globales.
- Un problema en una librería compartida afecta a todos los programas que hacen uso de ella.
- Gran cantidad de dependencias entre librerías compartidas. Si hacemos uso de un programa de gestión de paquetes como yum o apt-get, puede ser menos doloroso por decirlo de alguna manera … si tenemos que resolver las dependencias manualmente, prefiero no hablar.
Con todo, la tendencia es a utilizar librerías compartidas. Las estáticas han quedado relegadas a funciones extrañas, programas antiguos y sistemas de emergencia que obviamente, llevan las bibliotecas embebidas.
Gestión de bibliotecas
Para que los programas puedan localizar las bibliotecas compartidas se puede proceder de dos maneras:
- Variables de entorno
- Fichero de configuración global
Fichero de configuración global: ld.so.conf
Localizado en /etc/ld.so.conf
Contendrá:
include ld.so.conf.d/*.conf
Es decir, incluye todos los ficheros (*) de configuración específicos del directorio /etc/ld.so.conf.d/
Cualquier cambio en estas rutas de bibliotecas, y especialmente si instalamos bibliotecas a partir de código fuente, tendremos que ejecutar el comando ldconfig.
¿Qué hace ldconfig?
Crea la caché de biblioteca: un fichero binario localizado en /etc/ld.so.cache y utilizado por los programas que hacen uso de bibliotecas compartidas.
Variables de entorno
Para ello se utilizará la variable LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/pruebalib:/usr/local/programa/lib
De esta manera se incluirá en los directorios de búsqueda de bibliotecas a /usr/local/pruebalib y /usr/local/programa/lib
Resolución de problemas
Dependencias: explicación del comando ldd
Para comprobar las librerías que utiliza un programa utilizaremos ldd:
ldd /usr/bin/ssh ... linux-vdso.so.1 => (0x00007fff4d7c4000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007f55cb434000) libgssapi_krb5.so.2 => /usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2 (0x00007f55ca7e2000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f55ca423000) /lib64/ld-linux-x86-64.so.2 (0x00007f55cb8d5000) ...
Donde:
- linux-vdso.so.1 => : Muestra que no ha encontrado la biblioteca.
- libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 : La biblioteca necesaria libselinux.so.1 se encuentra (referenciada con => ) en /lib/x86_64-linux-gnu/libselinux.so.1.
- /lib64/ld-linux-x86-64.so.2 : ruta completa a la librería requerida.
- Los números hexadecimales (ej .: 0x00007fff4d7c4000) son direcciones de carga de las librerías en memoria. Son aleatorias y no estables, por lo que si volvemos a ejecutar ldd /usr/bin/ssh observaremos que el número ha cambiado.
Localización de bibliotecas
Normalmente, problemas para localizar una biblioteca. Para solucionarlo, podremos proceder de distintas formas:
- Comprobar que la biblioteca esté instalada con find. Si no lo está, la instalamos.
- Si la biblioteca está disponible, quizá el programa afectado no pueda localizarla. Podríamos añadirla a la variable LD_LIBRARY_PATH.
- Si la biblioteca está disponible pero en el sistema recibe un nombre distinto al que espera el programa, tendríamos que hacer un enlace simbólico al nombre de biblioteca al que hace referencia el programa
ej.: Siendo prueba.so.5.2 la biblioteca instalada y prueba.so.5 a la que hace referencia el programa (podemos verlo con ldd)
ln -s prueba.so.5.2 prueba.so.5 ldconfig
Y hasta aquí llegamos con el maravilloso mundo de las librerías de terror de Linux 😀