4. Rappels de programmation en Langage C

4.1. Chaine de production des exécutables

La production de programmes exécutables sur un système d'exploitation passe par plusieurs étapes, et permet de transformer du code écrit dans un langage de haut niveau vers un programme écrit en code machine :

  1. Saisie du programme dans le langage et enregistrement sur le disque : c'est le fichier source

  2. Compilation avec le compilateur du langage (vérification syntaxique et lexicale) : on obtient le fichier objet

  3. Edition de liens (résolution des références externes) : c'est le fichier exécutable

Lorsque l'utilisateur demande l'exécution de son programme, le fichier exécutable est alors monté en mémoire centrale : c'est l'étape de chargement. Le système alloue de la place mémoire pour placer le code et les données du programme. Nous préciserons dans la partie suivante du cours cette dernière étape.

Figure 6. Chaîne de production d'un exécutable

Chaîne de production d'un exécutable


4.2. Illustration

Ces diverses manipulations sont inspirées des exercices donnés par Ivan Boule du CNAM, pour l'enseignement de la SMB137.

Soit le programme suivant écrit en langage C :

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int TAILLE_ALLOC = 1024*1024*1024;
 // ======================A==========================
 int calcul_trigo( int n)
 {
    int i, res = 0;
    for(i=0; i<=n; i++)
        res+=3*sin(i);
    return res;
}

// ======================B==========================
int calcul_carre( int n)
{
    int i, res = 0;
    for(i=0; i<=n; i++)
        res+=i*i;
    return res;
}

// ======================C==========================
int main(int argn, char *argv[], char *env[])
{
 int nb_pas;
 int i;
 char *zone;

 for (i=0; env[i] != NULL; i++)
    printf("VARIABLE ENVIRONNEMENT:%s\n", env[i]);
 
 zone = (char *) malloc(TAILLE_ALLOC);

 if (argn != 2){
    printf("il faut 1 argument : le nb de pas de calcul\n");
    exit (-2);
 }

 sscanf(argv[1], "%d", &nb_pas);

 printf("\n\n\n\nnombre de pas = %d \n", nb_pas);
 printf("somme trigo = %d\n", calcul_trigo(nb_pas));
 printf("somme carre = %d\n", calcul_carre(nb_pas));

 printf("Adresse de     main         = %09lx\n", main);
 printf("Adresse de     TAILLE_ALLOC = %09lx\n", &TAILLE_ALLOC);
 printf("Adresse de     nb_pas       = %09lx\n", &nb_pas);
 printf("Adresse de     zone         = %09lx\n", zone);
 printf("Adresse de     argv[1]      = %09lx\n", argv[1]);

 // ======================D==========================
 sleep(5);
 exit(nb_pas);
  }

Ce programme contient quatres sections :

  1. Les directives d'inclusion qui permettent d'utiliser des codes existants, contenus dans d'autres fichiers.

  2. Les variables globales, visibles dans l'ensemble du programme

  3. Les variables locales, visibles dans le corps du programme où elles sont définies

  4. Les fonctions, unités d'exécution dans le langage C.

4.2.1. Compilation

La compilation de ce programme est illustrée par la séquence de commandes suivante :

ikare@kaitan:exemples > ls -l
total 2
-rw-r--r--  1 ikare  bsd  1393 Feb 18 11:31 exo1.c 1
ikare@kaitan:exemples > gcc -c exo1.c  2
ikare@kaitan:exemples > ls -l
total 6
-rw-r--r--  1 ikare  bsd  1393 Feb 18 11:31 exo1.c
-rw-r--r--  1 ikare  bsd  2088 Feb 22 15:23 exo1.o3

1. Le fichier source

2. La compilation avec le compilateur gcc

3. Le fichier objet créé

Le fichier ainsi créé se compose des parties suivantes

  • Text : ensemble du code, instructions machines.

  • Data : données du programme.

  • Rodata : données en lecture seule (Read Only).

  • Symbol : table des symboles.

  • Comment : informations sur le compilateur

Chaque fichier objet (ou exécutable) possède une table décrivant les différents symboles qu'il contient. La commande nm [-options] [ref] ... permet de visualiser la table des symboles, en fournissant : son nom, sa valeur, sa classe et son type.

ikare@kaitan:exemples > nm exo1.o  
00000000 D TAILLE_ALLOC 1
00000060 T calcul_carre 2 
00000000 T calcul_trigo
         U exit
000000a0 T main
         U malloc 3
         U printf
         U puts
         U sin
         U sleep
         U sscanf

1. Le symbole est une variable

2. Le symbole est défini dans le code

3. Le symbole est indéfini

Certains symboles ne sont donc pas "résolus". Il s'agit dans notre cas de fonctions définies dans d'autres librairies.

4.2.2. L'édition de liens

L'édition de lien permet de constituer un fichier exécutable à partir de bibliothèques et de modules objets compilés séparément. L'éditeur de lien doit donc résoudre pour chacun des modules les références externes non résolues, en extrayant de bilbliothèques les modules correspondant à des fonctions effectivement utilisées.

Historiquement l'édition de lien se faisait de manière statique : chaque code d'une fonction utilisée est recopié dans le fichier binaire. L'exécutable est alors autonome, mais si plusieurs programmes utilisent la même fonction, cette portion de code sera alors autant de fois chargée en mémoire centrale. De plus, la taille de l'exécutable augemente en conséquence. Le changement de version de bibliothèque ne se fera également pas sans souci.

Dans le cas de l'édition de liens dynamique, les références à résoudre vont contenir des informations sur les objets partagés (fichiers .so sous BSD), qui seront chargés si nécessaire à l'exécution du progamme.

ikare@kaitan:exemples > gcc -static exo1.c -lm -o static 1
ikare@kaitan:exemples > gcc exo1.c -lm -o dynamic 2 
ikare@kaitan:exemples > ls -lh
total 300
-rwxr-xr-x  1 ikare  bsd    5.9K Feb 22 16:40 dynamic 3
-rw-r--r--  1 ikare  bsd    1.4K Feb 18 11:31 exo1.c
-rw-r--r--  1 ikare  bsd    2.0K Feb 22 15:23 exo1.o
-rwxr-xr-x  1 ikare  bsd    265K Feb 22 16:40 static 4
ikare@kaitan:exemples > nm dynamic 
08049978 D TAILLE_ALLOC
08049980 D _DYNAMIC
08049a4c D _GLOBAL_OFFSET_TABLE_
         w _Jv_RegisterClasses
08049a3c d __CTOR_END__
08049a38 d __CTOR_LIST__
08049a44 d __DTOR_END__
08049a40 d __DTOR_LIST__
0804997c r __FRAME_END__
08049a48 d __JCR_END__
08049a48 d __JCR_LIST__
08049a7c A __bss_start
08048730 t __do_global_ctors_aux
080484c0 t __do_global_dtors_aux
08049970 D __dso_handle
0804996c D __progname
08049a7c A _edata
08049a84 A _end
0804875c T _fini
08048374 T _init
         U _init_tls@@FBSD_1.0
08048430 T _start
0804810c r abitag
         U atexit@@FBSD_1.0
08048580 T calcul_carre
08048520 T calcul_trigo
08049a7c b completed.4970
08049a80 B environ
         U exit@@FBSD_1.0
080484f0 t frame_dummy
080485c0 T main
         U malloc@@FBSD_1.0
08049974 d p.4968
         U printf@@FBSD_1.0 5
         U puts@@FBSD_1.0
         U sin@@FBSD_1.0
         U sleep@@FBSD_1.0
         U sscanf@@FBSD_1.0

1. Edition de lien en statique

2. Edition de lien en dynamique

3. Fichier exécutable (x) de taille 5.6Ko

4. Fichier exécutable (x) de taille 256Ko

5. Le lien dynamique

4.2.3. Exécution

Le fichier exécutable est maintenant créé. Pour le lancer il suffit sous Unix de faire la commande ./dynamic 4, ce qui donne le résultat suivant :


VARIABLE ENVIRONNEMENT:TERM=rxvt
VARIABLE ENVIRONNEMENT:COLORTERM=rxvt-xpm
VARIABLE ENVIRONNEMENT:WINDOWID=44040196
VARIABLE ENVIRONNEMENT:DISPLAY=:0.0
VARIABLE ENVIRONNEMENT:COLORFGBG=15;0
VARIABLE ENVIRONNEMENT:SSH_AGENT_PID=1474
VARIABLE ENVIRONNEMENT:SSH_AUTH_SOCK=/tmp/ssh-3Vpzd7LUzB/agent.1457
VARIABLE ENVIRONNEMENT:USER=ikare
VARIABLE ENVIRONNEMENT:MACHTYPE=i386
VARIABLE ENVIRONNEMENT:MAIL=/var/mail/ikare
VARIABLE ENVIRONNEMENT:VENDOR=intel
VARIABLE ENVIRONNEMENT:SHLVL=3
VARIABLE ENVIRONNEMENT:HOME=/home/ikare
VARIABLE ENVIRONNEMENT:TEXEDIT=emacs
VARIABLE ENVIRONNEMENT:PAGER=more
VARIABLE ENVIRONNEMENT:LC_CTYPE=fr_FR.ISO8859-15
VARIABLE ENVIRONNEMENT:SSH_ASKPASS=/usr/local/bin/ssh-askpass
VARIABLE ENVIRONNEMENT:GROUP=bsd
VARIABLE ENVIRONNEMENT:MM_CHARSET=ISO-8859-15
VARIABLE ENVIRONNEMENT:LOGNAME=ikare
VARIABLE ENVIRONNEMENT:MOZILLA_HOME=/usr/local/bin/firefox3
VARIABLE ENVIRONNEMENT:RSYNC_RSH=/usr/bin/ssh
VARIABLE ENVIRONNEMENT:PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/home/ikare/bin
VARIABLE ENVIRONNEMENT:LANG=fr_FR.ISO-8859-15
VARIABLE ENVIRONNEMENT:SHELL=/bin/tcsh
VARIABLE ENVIRONNEMENT:HOST=kaitan.fremens
VARIABLE ENVIRONNEMENT:CPUTYPE=k7
VARIABLE ENVIRONNEMENT:OSTYPE=FreeBSD
VARIABLE ENVIRONNEMENT:PWD=/home/ikare/data/travail/Cours/Cnam/SMB137/Cours/Chapitre1/exemples
VARIABLE ENVIRONNEMENT:LC_ALL=en_US.ISO8859-1
VARIABLE ENVIRONNEMENT:HOSTTYPE=FreeBSD
VARIABLE ENVIRONNEMENT:EDITOR=vi


nombre de pas = 4 
somme trigo = 1
somme carre = 30
Adresse de     main         = 0080485c0
Adresse de     TAILLE_ALLOC = 008049978
Adresse de     nb_pas       = 0bfbfe938
Adresse de     zone         = 028300000
Adresse de     argv[1]      = 0bfbfebf6

Le programme a simplement affiché les variables d'environnement de son contexte, et exécuté les deux fonctions. Pour son exécution, le système a chargé son code en mémoire, et exécuté les instructions sur le processeur, en mettant à jour les différents registres et le compteur ordinal.

4.3. Exercices de révision

Quelques petits exercices pour revoir les commandes shells et un petit peu de programmation. Vous pouvez consulter les cours disponibles sur www.kurzweg.info.

  1. Soit un fichier fic contenant 12 lignes. Donnez la commande composée permettant d'afficher les lignes de 5 à 9.

  2. Utilisez la commande find pour supprimer dans votre répertoire de domiciliation ($HOME) tous les fichiers dont le nom est core.

  3. Ecrivez en C un programme affichant la liste des nombres entiers premiers (divisibles uniquement par 1 et par eux-mêmes).

Skins :
Transparence
Simple
Page Accueil
Formation