Langage C et algorithmique
© Magali Contensin ;
2002 - 2004
 

Environnement de programmation

Bibliothèques

Lorsqu'on réalise des programmes il est fréquent que certaines fonctions ne soient pas spécifiques au programme en cours. Vous avez des bibliothèques qui contiennent des fonctions d'entrées/sorties telles que printf, scanf, des fonctions pour la manipulation de chaînes de caractères strlen, strcpy...

Utiliser des bibliothèque statiques ou partagées

Lorsqu'on compile, on doit indiquer quelles sont les bibliothèques utilisées par le programme. Jusqu'à présent nous n'avons pas eu besoin de le faire car nous utilisions la bibliothèque standard libc qui contient printf, scanf, strlen... Attention, ne confondez pas la bibliothèque et le fichier .h que vous avez inclus au début de votre programme, le fichier .h contient principalement les prototypes des fonctions, la bibliothèque contient le code des fonctions.

Prenons le programme ci-dessous qui donne la valeur arrondie par excès ou par défaut d'un réel entré sur la ligne de commandes. Ce programme utilise les fonctions ceil et floor de la bibliothèque mathématique. Les prototypes de ces fonctions apparaissent dans le fichier en-tête math.h.

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

int main(int argc, char *argv[])
{
  float nb;

  if(argc != 2){
    printf("usage : arrondir nb_reel\n");
    exit(1);
  }
  nb = atof(argv[1]);

  printf("arrondi par exces : %d\n", ceil(nb));
  printf("arrondi par defaut : %d\n", floor(nb));

  return 0;
}
Compilons notre programme :
> gcc -Wall arrondir.c -o arrondir
/tmp/ccTffpuW.o: In function `main':
/tmp/ccTffpuW.o(.text+0x4d): undefined reference to `ceil'
/tmp/ccTffpuW.o(.text+0x79): undefined reference to `floor'
collect2: ld returned 1 exit status
>

Les fonctions mathématiques ne sont pas trouvées lors de l'édition des liens (ld), aucun exécutable n'est produit. Ces fonctions sont stockées dans une bibliothèque qui s'appelle libm (avec une extension .a ou .so selon le type de la bibliothèque). Lors de la compilation, l'option -lbibliotheque indique au compilateur de rechercher les symboles dans la bibliothèque nommée libbibliotheque.so ou si elle n'existe pas dans libbibliotheque.a.

> gcc -Wall -lm arrondir.c -o arrondir
> ./arrondir
usage : arrondir nb_reel
> ./arrondir 3.45
arrondi par exces : 4
arrondi par defaut : 3
> ./arrondir 4
arrondi par exces : 4
arrondi par defaut : 4
> ./arrondir 4.1
arrondi par exces : 5
arrondi par defaut : 4
>

Qu'est-ce qu'une bibliothèque statique ?

Une bibliothèque statique se reconnaît par son extension en .a, c'est un fichier qui peut contenir un ou plusieurs fichiers objets (.o) et qui débute par un index. Il est possible de consulter l'index d'une bibliothèque statique en utilisant la commande nm.

> nm -s /usr/lib/libutil.a

Archive index:
login in login.o
login_tty in login_tty.o
logout in logout.o
logwtmp in logwtmp.o
openpty in openpty.o
forkpty in forkpty.o

login.o:
         U __errno_location
         U basename
         U endutent
         U free
         U getpid
00000000 T login
         U malloc
         U pututline
         U realloc
         U setutent
         U ttyname_r
         U updwtmp
         U utmpname

login_tty.o:
         U close
         U dup2
         U ioctl
00000000 T login_tty
         U setsid

logout.o:
         U endutent
         U gettimeofday
         U getutline_r
00000000 T logout
         U pututline
         U setutent
         U utmpname

logwtmp.o:
         U __gettimeofday
         U getpid
00000000 T logwtmp
         U updwtmp

openpty.o:
         U __errno_location
         U close
         U free
         U getpt
         U grantpt
         U ioctl
         U malloc
         U open
00000000 T openpty
         U ptsname_r
         U realloc
         U tcsetattr
         U unlockpt

forkpty.o:
         U _exit
         U close
         U fork
00000000 T forkpty
         U login_tty
         U openpty
>

Le U indiquant que le symbole n'est pas défini dans l'archive, et le T qu'il est dans l'archive (par ex. printf apparaîtrait ici comme U car il est utilisé mais appartient à une autre archive. L'option defined-only permet d'afficher uniquement les fonctions définies dans l'archive.

> nm -s --defined-only /usr/lib/libutil.a

Archive index:
login in login.o
login_tty in login_tty.o
logout in logout.o
logwtmp in logwtmp.o
openpty in openpty.o
forkpty in forkpty.o

login.o:
00000000 T login

login_tty.o:
00000000 T login_tty

logout.o:
00000000 T logout

logwtmp.o:
00000000 T logwtmp

openpty.o:
00000000 T openpty

forkpty.o:
00000000 T forkpty
>

Lorsqu'on compile, si une des bibliothèques indiquée est statique, le code du fichier objet est inclus dans l'exécutable. Utiliser une bibliothèque statique permet donc d'exécuter le programme sur un ordinateur qui ne comporte pas la bibliothèque. Vous pouvez par exemple développer un exécutable qui utilise une bibliothèque commerciale que vous avez achetée et que vous n'avez pas le droit de diffuser. En compilant avec la version statique, vous incluez le code nécessaire, et votre application pourra être exécutée sur un ordinateur qui ne comporte pas la bibliothèque.

Une compilation qui utilise des bibliothèques statiques produira un exécutable qui occupera plus d'espace disque. Lorsque les fonctions de la bibliothèque sont très utilisées on ne prendra pas la version statique de la bibliothèque pour compiler. En effet, si toutes les applications de votre ordinateur dupliquaient le code des fonctions d'entrées/sorties (vers la console ou un autre flux), vous perdriez beaucoup d'espace disque inutilement.

Qu'est-ce qu'une bibliothèque partagée ?

Lorsque la bibliothèque comporte l'extension .so suivie le plus souvent du numéro de sa version, c'est une bibliothèque partagée (shared object). À l'édition des liens, une portion de code est insérée, elle sera utilisée lorsque l'application sera exécutée pour lancer l'éditeur de liens dynamique. Le code des fonctions ne sera donc plus dupliqué dans chaque exécutable, il sera présent dans un seul fichier sur le disque. Lorsqu'on exécute, l'éditeur de liens dynamique localise les bibliothèques partagées et les charge en mémoire.

L'exécutable occupera donc moins d'espace disque, et moins d'espace mémoire. En effet, si dix applications qui utilisent les mêmes fonctions de bibliothèques partagées sont exécutées en même temps, le code des fonctions est présent une seule fois dans la mémoire.

Rappelons cependant qu'il faut que la bibliothèque partagée soit présente sur l'ordinateur où vous exécutez, d'autre part, son code est entièrement chargé en mémoire, il ne faut donc pas créer une bibliothèque partagée dans laquelle une seule fonction est très souvent utilisée.

Créer et utiliser une bibliothèque statique

Dans la partie de cours sur la compilation séparée, nous avions un fichier commande.c contenant des fonctions utiles lorsque des programmes utilisent la ligne de commande. Nous souhaitons faire une bibliothèque statique liboutils.a qui contient les fonctions de ce fichier.

Création de l'archive

Le fichier doit être compilé avec l'option -c car il n'y a pas de main et on ne souhaite pas produire un exécutable. L'option r de ar permet d'inclure le fichier objet commande.o dans l'archive nommée liboutils.a. L'option -t est utilisée pour afficher la liste des fichiers objets contenus dans l'archive (il est possible de faire une archive à partir de plusieurs fichiers).
> gcc -c commande.c
> ar r liboutils.a commande.o
> ar t liboutils.a
commande.o

Création de l'index et stockage dans l'archive

La commande ranlib génère l'index pour l'archive liboutils.a et stocke cet index dans l'archive. L'utilisation de nm permet de vérifier quels sont les symboles définis et indéfinis dans l'archive (atof, atoi, exit, printf, strcpy sont utilisés dans l'archive et définis ailleurs, verif_nb_args, verif_bornes, recup_arg_string, recup_arg_int, recup_arg_reel sont définis dans cette archive dans commande.o).
> ranlib liboutils.a
> nm -s liboutils.a

Archive index:
verif_nb_args in commande.o
verif_bornes in commande.o
recup_arg_string in commande.o
recup_arg_int in commande.o
recup_arg_reel in commande.o

commande.o:
         U atof
         U atoi
         U exit
00000000 t gcc2_compiled.
         U printf
000000c0 T recup_arg_int
00000100 T recup_arg_reel
00000080 T recup_arg_string
         U strcpy
00000040 T verif_bornes
00000000 T verif_nb_args
>

Utilisation de la bibliothèque

Le programme lignes.c qui compte et affiche les lignes d'un fichier dont le nom est entré sur la ligne de commandes peut à présent utiliser la bibliothèque liboutils.a. Nous allons supposer que le fichier à inclure commande.h et la bibliothèque sont situés respectivement dans les répertoires include et bib à la racine de votre compte. L'option -I chemin_repertoire_include permet d'indiquer qu'il faut chercher les fichiers .h dans le répertoire dont le chemin est précisé (en plus des répertoires qui sont parcourus habituellement), et l'option -L chemin_repertoire_bib a le même effet pour les bibliothèques.

> gcc fichiers.c lignes.c -I $HOME/include -L $HOME/lib -loutils -o lignes
> ./lignes
Usage : lignes nom_fichier
> ./lignes annuaire.txt
.............. Ouverture du fichier annuaire.txt
        Contensin Magali 04 91 11 36 13 contensin@cmi.univ-mrs.fr
        Alessandra Denis 04 91 11 36 13 alessand@club-internet.fr
        Ifrah Sandrine 04 91 11 35 25 ifrah@cmi.univ-mrs.fr
        Dupond Georges 04 94 13 22 11 dupond@bidule.net
        Durand Charles 06 89 43 54 22 durand@machin.fr
        Joe Dalton 06 34 22 34 11 joe.dalton@prison.com
le fichier annuaire.txt comporte 6 lignes
.............. Fermeture du fichier annuaire.txt
>

Ajout d'autres objets dans une bibliothèque

Nous souhaitons ajouter dans la bibliothèque liboutils.a les fonctions relatives à l'ouverture et la fermeture de fichiers, la commande ar avec l'option r permet de réaliser cet ajout :
> gcc -c fichiers.c
> ar r liboutils.a fichiers.o
> ar t liboutils.a
commande.o
fichiers.o
> ranlib liboutils.a
> nm -s --defined-only liboutils.a

Archive index:
verif_nb_args in commande.o
verif_bornes in commande.o
recup_arg_string in commande.o
recup_arg_int in commande.o
recup_arg_reel in commande.o
ouvrir_fichier in fichiers.o
fermer_fichier in fichiers.o

commande.o:
00000000 t gcc2_compiled.
000000c0 T recup_arg_int
00000100 T recup_arg_reel
00000080 T recup_arg_string
00000040 T verif_bornes
00000000 T verif_nb_args

fichiers.o:
00000060 T fermer_fichier
00000000 t gcc2_compiled.
00000000 T ouvrir_fichier
>
Nous pouvons à présent utiliser la bibliothèque lors de la compilation du programme lignes.c :
> gcc lignes.c -I $HOME/include -L $HOME/lib -loutils -o lignes
> ls -la lignes
-rwxr-xr-x    1 magali   magali      15876 nov 10 10:27 lignes*

Créer et utiliser une bibliothèque partagée

Essayons à présent de faire une bibliothèque partagée liboutils.so

Création de la bibliothèque

Les fichiers doivent être compilés avec l'option -fPIC pour que le code ne soit pas relogeable (Position Independent Code). La bibliothèque partagée est créée en utilisant l'option -shared lors de la compilation.
> gcc -fPIC -c commande.c
> gcc -fPIC -c fichiers.c
> gcc -shared -o liboutils.so commande.o fichiers.o

Utilisation de la bibliothèque

Une bibliothèque partagée se place dans le répertoire /usr/lib, si vous n'avez pas les droits pour le faire, vous pouvez placer votre bibliothèque partagée dans le répertoire lib que vous avez créé à la racine de votre compte, lors de la compilation vous utiliserez l'option Wl,-rpath,$HOME/lib, ceci aura pour effet d'ajouter le répertoire dont le chemin est précisé à la liste des répertoires dans lesquels l'éditeur de liens dynamiques chercher les bibliothèques partagées. Le -Wl, indique qu'il faut passer "-rpath $HOME/lib" à l'éditeur de liens (i.e. cette option est utilisée lors de la dernière étape de la compilation).
> gcc lignes.c -I $HOME/include -L $HOME/lib -loutils -Wl,-rpath,$HOME/lib -o lignes
> ldd lignes
        liboutils.so => /home/magali/lib/liboutils.so (0x40016000)
        libc.so.6 => /lib/libc.so.6 (0x40023000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
> ls -la lignes
-rwxr-xr-x    1 magali   magali      14679 nov 10 11:22 lignes*
>

La commande ldd permet de connaître les dépendances dynamiques de l'exécutable. liboutils.so est la bibliothèque que nous avons créée et qui est dans le répertoire indiqué à droite de la flèche. libc est la bibliothèque standard C, et ld-linux.so est l'éditeur de lien dynamique.

Extensions

Vous avez peut-être déjà remarqué que les bibliothèques sous linux ont des extensions .so ou encore .so.nb1 et même .so.nb1.nb2. La mise en place des bibliothèques partagées dépasse le cadre de ce cours. Ce qu'il faut retenir, c'est qu'une bibliothèque partagée ne peut pas être effacée et remplacée par une nouvelle si la mise à jour est majeure (i.e. la nouvelle bibliothèque ne peut pas remplacer l'ancienne, sinon les applications qui l'utilisaient ne fonctionneront plus), la mise à jour est majeure lorsque nb1 a changé. Dans le cas d'une modification mineure, il est possible de remplacer l'ancienne version par la nouvelle, la mise à jour est mineure lorsque nb2 a changé. Donc, plusieurs versions de la même bibliothèque partagées peuvent être présentes dans le système.

Une bibliothèque a plusieurs noms :
- un nom réel, le fichier qui contient le code (libm-2.2.4.so dans l'exemple ci-dessous)
- un nom d'objet partagé ou soname qui est un lien symbolique vers la bibliothèque (le nom réel), ce nom est composé du nom de la bibliothèque et de son extension majeure (libm.so.6 dans l'exemple ci-dessous). Dans l'exécutable, c'est le soname qui est stocké.
- un nom de lien utilisé par le compilateur, c'est le nom de la bibliothèque sans indication de version majeure et mineure (libm.so dans l'exemple ci-dessous), c'est un lien symbolique vers le soname.

> locate libm.so
/usr/lib/libm.so
/lib/libm.so.6
>  ls -la /usr/lib/libm.so
lrwxrwxrwx    1 root     root        19 oct 20 09:16 /usr/lib/libm.so -> ../../lib/libm.so.6
> ls -la /lib/libm.so.6
lrwxrwxrwx    1 root     root        13 oct 20 10:21 /lib/libm.so.6 -> libm-2.2.4.so*
> ls -la /lib/libm-2.2.4.so
-rwxr-xr-x    1 root     root    134808 mar  7  2002 /lib/libm-2.2.4.so
>
Voici un exemple dans lequel deux versions majeures de la même bibliothèque sont présentes :
> ls -l libstdc++.so.*
lrwxrwxrwx    1 root     root           18 oct 20 09:16 libstdc++.so.3 -> libstdc++.so.3.0.4
-rwxr-xr-x    1 root     root       652396 fev 27  2002 libstdc++.so.3.0.4
lrwxrwxrwx    1 root     root           18 oct 20 17:13 libstdc++.so.5 -> libstdc++.so.5.0.0
-rwxr-xr-x    1 root     root       778968 aou 17 11:52 libstdc++.so.5.0.0
Le soname pour la version majeure 3 pointe sur le nom réel libstdc++.so.3.0.4, tandis que le soname de la version 5 pointe vers le nom réel libstdc++.so.5.0.0. Le nom de lien pointe vers la dernière version majeure.
haut
css html