Publi

Enlazado dinámico en C (dynamic linking) I: Introducción

Algo que nos abre un mundo de posibilidades a la hora de realizar nuestros programas es la forma de enlazar los archivos objeto en nuestro binario definitivo. Ésta puede ser dinámica o estática.

La forma estática es la que se utiliza desde el primer momento que se empieza a programar: hemos creado varios archivos .c y queremos utilizar funciones de otro archivo dentro del archivo principal, con el objetivo de dividir el código en partes y no trabajar desde el mismo archivo. El resultado es que todos los archivos .c se compilan en varios archivo .o y luego todos se unen para formar un archivo ejecutable. Este ejecutable contendrá todas las funciones y símbolos de todos los archivos fuente.

También tenemos la posibilidad de enlazar dinámicamente los diferentes objetos en nuestro ejecutable y esto nos abre muchas posibilidades:

  • No tenemos que meterlo todo dentro del ejecutable, el cual puede crecer mucho
  • No tenemos por qué compilarlo todo siempre en tiempo de desarrollo
  • Una mejor modularización de nuestro código
  • Nos permite reutilizar código en forma de bibliotecas, código que puede ser reutilizado en varios proyectos (nuestros y de otras personas).
  • Si arreglamos un bug en una biblioteca, todos los programas que la utilicen se beneficiarán del cambio (también podemos inutilizar algunos programas si no hacemos bien las cosas…)
  • Nos permite utilizar bibliotecas implementadas por otras personas
  • Nos permite ocultar parte de nuestro código y distribuir complementos binarios
  • Damos facilidades para utilizar nuestras bibliotecas a otros desarrolladores, abstrayéndolos de lo que no necesitan conocer para utilizarlas
  • Permite la creación de plugins para nuestras aplicaciones
  • … y muchas cosas más.

Carga estática de bibliotecas dinámicas

Las bibliotecas dinámicas, estos objetos compartidos (Shared Objects, cuya extensión es .so) pueden ser cargados de forma estática y de forma dinámica.
La carga estática tenemos que especificarla en tiempo de compilación, debemos hacerla añadiendo la biblioteca que queremos incluir al compilar (y luego en la ejecución tendremos que decirle donde están esas bibliotecas si no están en la ruta indicada para eso.

Para hacer la prueba, vamos a crear una biblioteca llamada pitagoras, que calculará la longitud de la hipotenusa a partir de las longitudes de los catetos con el Teorema de Pitágoras. Su código fuente será:
libpitagoras.h

1
2
3
4
5
6
7
8
9
/* @(#)libpitagoras.h
 */


#ifndef _LIBPITAGORAS_H
#define _LIBPITAGORAS_H 1

float pitagoras(float c1, float c2);

#endif /* _LIBPITAGORAS_H */

libpitagoras.c

1
2
3
4
5
6
7
8
#include <stdio.h>
#include <math.h>
#include "libpitagoras.h"

float pitagoras(float c1, float c2)
{
  return sqrt(c1*c1+ c2*c2);
}

Es importante que el archivo contenga «lib» delante para que GCC lo reconozca como una biblioteca a la hora de realizar el enlace. Ahora vamos a hacer un programa principal que utilice la función pitagoras():

mainpi.c

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
#include "libpitagoras.h"

void saludo();

int main(int argc, char *argv[])
{
  printf("La hipotenusa es: %f\n", pitagoras(3, 4));

  return 0;
}

Para compilar, tenemos que hacerlo de una forma especial, utilizando las directivas -fPIC y -shared. La primera, creará un código independiente de posición (no tendrá que colocarse en una posición determinada de la memoria para poder ser utilizado) y la segunda creará un objeto compartido (.so). Lo haremos de la siguiente manera:

$ gcc -c -fPIC libpitagoras.c
$ gcc -shared -o libpitagoras.so libpitagoras.o -lm

En la primera línea creamos el archivo objeto (con -c), en la segunda, crearemos el objeto compartido y le añadiremos la biblioteca matemática -lm, necesaria para hacer la raíz cuadrada (sqrt). Si no añadimos -lm compilará igual, pero fallará al compilar el ejecutable porque no se ha logrado encontrar el código de sqrt. Aunque también lo podemos hacer todo junto, sólo estaba dividido en pasos para poder ver el proceso completo, pero así es más rápido:

$ gcc -fPIC -shared -o libpitagoras.so libpitagoras.c -lm

Ahora para compilar el ejecutable hacemos:

$ gcc -o mainpi mainpi.c -L. -lpitagoras

Con esta línea creamos el ejecutable mainpi, le decimos con -L dónde debe buscar la biblioteca, normalmente se buscará en /lib y /usr/lib, pero en este ejemplo lo encontramos en el mismo directorio del programa, por eso ponemos un punto. Luego incluiremos -lpitagoras que leerá nuestro archivo .so para ver que todo está en orden, pero en realidad se leerá en tiempo de ejecución el código del shared object; para demostrar esto, podemos hacer cualquier cambio en la biblioteca pitágoras (por ejemplo poner un printf()), recompilar la biblioteca y ejecutar de nuevo el programa principal (claro está, sin compilarlo). Al ejecutarlo, veremos el cambio que hemos realizado.

Foto: Mixtribe Photo (Flickr). CC-A a 01/03/2013

También podría interesarte....

There are 8 comments left Ir a comentario

  1. Pingback: Enlazado dinámico en C (dynamic linking) II: Carga dinámica de shared objects | Poesía Binaria /

  2. Pingback: Bitacoras.com /

  3. Álvaro /
    Usando Mozilla Firefox Mozilla Firefox 52.0 en Linux Linux

    Ejecutando todo tal como dices me da este error:

    ./mainpi: error while loading shared libraries: libpitagoras.so: cannot open shared object file: No such file or directory

    1. Álvaro /
      Usando Mozilla Firefox Mozilla Firefox 52.0 en Linux Linux

      Vale, por si alguien tiene el mismo problema: copiando el archivo .so a /lib/libpitagoras.so se ejecuta correctamente.

      1. Gaspar Fernández / Post Author
        Usando Mozilla Firefox Mozilla Firefox 57.0 en Linux Linux

        Gracias Álvaro,
        Me pillaste sin cobertura y detectaste el problema. De todas formas, copiar el .so a /lib del tirón muchas veces no es buena solución. ¿Pusiste el -L. en el gcc para buscar los archivos en el directorio actual?

        1. old C wolf /
          Usando Mozilla Firefox Mozilla Firefox 52.0 en Linux Linux

          Supongo que el problema es que no se nota bien el punto después de la -L, y no todos tenemos una vista super-aguda.
          Aunque explicas muy bien para qué es -L, los novatos pensarán que va solo porque tienden a ignorar los detalles. Lo podrías enfatizar escribiendo -L».» o -L$(pwd) que se expande a el directorio actual, aunque parezca exagerado.

          1. Gaspar Fernández / Post Author
            Usando Mozilla Firefox Mozilla Firefox 63.0 en Ubuntu Linux Ubuntu Linux

            Cierto, es lo malo muchas veces, que los . no se ven correctamente. Gracias por señalizarlo. Quiero hacer una revisión de algunos posts y esta serie será de los que quiero revisar para perfeccionar muchas cosas. 🙂

  4. Fool Me Once Michelle Keegan Leather Jacket /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    Thanks for a wonderful share. Here is the great example related to you blog.

Leave a Reply