Bibliotecas estáticas y dinámicas
Crear y usar bibliotecas .a y .so, el porqué de -fPIC, y las implicaciones profundas de elegir enlace estático o dinámico: tamaño, actualizaciones y despliegue.
Casi ningún programa se compila solo: usa bibliotecas. Y la decisión entre enlazarlas estática o dinámicamente no es un detalle técnico, sino una elección con consecuencias en el tamaño, las actualizaciones de seguridad y cómo despliegas tu software. Aquí dominas ambas.
- Crear y usar una biblioteca estática (
.a). - Crear y usar una biblioteca dinámica (
.so) y-fPIC. - Enlazar con
-ly-L. - El trade-off estático vs dinámico.
Bibliotecas estáticas (.a)
Una biblioteca estática es un archivo de .o agrupados (con ar). Al enlazar, el linker copia el código usado dentro de tu ejecutable:
gcc -std=c23 -c mates.c -o mates.o
ar rcs libmates.a mates.o # crea la biblioteca estática
# usarla: -L dice dónde buscar, -l nombra la lib (sin 'lib' ni '.a')
gcc main.c -L. -lmates -o prog
El ejecutable resultante es autocontenido: incluye el código de la librería, no la necesita en tiempo de ejecución.
Bibliotecas dinámicas (.so)
Una biblioteca dinámica (shared object) no se copia: el ejecutable guarda una referencia y el enlazador dinámico la carga al arrancar. Requiere código independiente de posición (-fPIC):
gcc -std=c23 -fPIC -c mates.c -o mates.o
gcc -shared mates.o -o libmates.so # crea la biblioteca dinámica
gcc main.c -L. -lmates -o prog # enlaza (solo referencia)
LD_LIBRARY_PATH=. ./prog # el loader debe encontrar el .so
ldd prog te muestra de qué .so depende un ejecutable, y -fPIC (Position-Independent Code) es necesario porque una .so puede cargarse en cualquier dirección.
El trade-off, en serio
No hay ganador universal; es un intercambio con consecuencias reales:
Enlace estático → ejecutable grande pero autocontenido: no depende de nada instalado, se despliega copiando un archivo, y no sufre el “dependency hell” de versiones. El precio: si una librería tiene un fallo de seguridad, hay que recompilar cada programa que la incluyó. Es la filosofía de musl (nivel 22), Go y los contenedores minimalistas.
Enlace dinámico → ejecutable pequeño; muchos programas comparten una sola copia de la librería en memoria y en disco. Una actualización de seguridad de esa .so arregla todos los programas a la vez, sin recompilar. El precio: el programa depende de que la versión correcta esté instalada (y ahí nacen los conflictos de versiones). Es el modelo por defecto de las distribuciones de Linux.
La elección correcta depende de tu contexto: un contenedor o binario portable pide estático; software de sistema que recibe parches de seguridad centralizados pide dinámico. Entender este trade-off es entender cómo se distribuye el software en el mundo real.
Versionado de bibliotecas dinámicas
Las .so llevan versiones en su nombre (libmates.so.1.2.3) con enlaces simbólicos, para que puedan convivir versiones incompatibles y las compatibles se actualicen solas. El soname (libmates.so.1) codifica la compatibilidad de ABI — que conecta directamente con el nivel 21.
Herramientas para ver las dependencias: ldd prog lista las .so que necesita; file prog te dice si es estático o dinámico; readelf -d prog muestra la sección dinámica (dependencias, soname). En sistemas, saber exactamente de qué depende tu binario —y controlarlo— es parte del trabajo, especialmente al desplegar o al depurar un “library not found” en producción.
- Crea una biblioteca estática con
ary enlázala en un programa con-L. -lmates. - Crea la versión dinámica con
-fPIC -sharedy ejecútala ajustandoLD_LIBRARY_PATH. - Compara el tamaño de los dos ejecutables (
ls -l) y explica la diferencia. - Usa
lddsobre el binario dinámico para ver sus dependencias.