Outils pour utilisateurs

Outils du site


dev:outils:gcc

GCC

Initialement appelé Gnu C Compiler, il a été renommé Gnu Compiler Collection puisqu'il permet de compiler divers langages tels que:

  • C/C++
  • Objective C
  • Ada
  • Java
  • VHDL
  • etc

Gnu Compiler Collection est l'outil généraliste de compilation du projet GNU. Il s'articule autour de plusieurs outils:

  • Le frontend gcc qui a pour but d'analyser et de traduire les fichiers sources en code assembleur correspondant à la cible.
  • Le backend constitué par les bin-utils assemble et lie aux bibliothèques les fichiers objets pour produire les fichiers exécutables.

La commande gcc est une interface permettant d'appeler de façon unifiée les différentes étapes de compilation.

Étapes de compilation

Le compilateur gcc de GNU passe par les étapes suivante pour compiler un fichier C:

  1. préprocesseur,
  2. compilation,
  3. liaison.

préprocesseur

Dans le fichier source, une ligne commençant par # n'est pas du C mais une directive pour le préprocesseur. Les directives telles que #define permettent d'augmenter la lisibilité et la maintenabilité du code. Le préprocesseur, appelé cpp (C PreProcessor) traite le fichier source en remplace les directives #includes, les commentaires etc. Le fichier obtenu en sortie est un fichier source contenant exclusivement des instructions C.

Le travail du préprocesseur peut être révélé avec l'argument -E de gcc:

/*
#########################################
# hello.c                               #
# Mon premier programme C               #
#########################################
*/
 
#include <stdio.h>
#define carre(a) ((a)*(a))
 
int main(void)
{
        printf("hello big world!\n");
        int nb=8;
        printf("nb=%d et %d est son carre.\n", nb, carre(nb) );
        return 0;
}
gcc -E hello.c 
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "hello.c"
...
typedef struct
{
  int __count;
  union
  {
 
    unsigned int __wch;
 
 
 
    char __wchb[4];
  } __value;
} __mbstate_t;
...
...
...
int main(void)
{
 printf("hello big world!\n");
 int nb=8;
 printf("nb=%d et %d est son carre.\n", nb, ((nb)*(nb)) );
 return 0;
}

Pour info, les commandes ci-dessous permettent de comparer les fichiers:

cat hello.c | wc -l
17
gcc -E hello.c | wc -l
851

Notre fichier source hello.c passe de 17 lignes a 851 après traitement du préprocesseur.

compilateur

Pour demander à gcc de compiler un fichier source la syntaxe est la suivante:

gcc -c monfichier.c -o monfichier.o

(gcc génère un fichier assembleur (phase assemblage) puis un fichier objet. On peut demander à gcc de stopper la compilation a la phase de traduction en langage assembleur pour voir le code assembleur produit:

gcc -S hello.c

Et traduire ensuite le code assembleur en code machine

as hello.c -o hello.o

linker

Une fois les fichiers objets créés, il faut les fusionner correctement pour créer le fichier exécutable (j’évite ici le terme assembler): c'est la phase d'édition des liens faite par le linker. la commande permettant d'invoquer le linker:

gcc monfichier.o -o monfichier

Sous Linux la plupart des exécutables sont liés dynamiquement, c'est à dire qu'ils n'incluent pas directement le code des fonctions utilisées. Lors de la phase d’édition de liens un code spécifique est ajouté, il est capable d’appeler du code en dehors de l’exécutable.

Pour voir à quelles bibliothèques un exécutable est lié, utiliser la commande ldd

$ ldd hello.exe
        linux-gate.so.1 =>  (0x00915000)
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00bc4000)
        /lib/ld-linux.so.2 (0x009b9000)

On peut faire le choix d'utiliser à la place de la liaison dynamique la liaison statique. Lors de la phase d'édition des liens on utilise l'argument -static:

$ gcc -static monficheir.o -o monfichier

On obtient un binaire moins modulaire mais qui inclus le code dont il a besoin. S'il repose sur des bibliothèques qui ont été mises à jour, il faudra recompiler le programme pour qu'il intègre les modifications.

Bibliothèques, liaison statique / dynamique

Lors de l'édition des liens, des fichiers objets présents dans des bibliothèques peuvent être spécifiés. Si la bibliothèque n'est pas dans un dossier standard il faut préciser au linker le dossier dans lequel elle est présente ainsi que son nom, c'est que permettent de faire les options -L (--library-path=) et -l (--library=) :

gcc -L ~/repertoire -lmalib monfichier.o -o monfichier
A propos de la nomenclature du paramètre suivant l'option -l: s'il a la forme d'un nom de fichier, le linker recherche le fichier dans les répertoires des bibliothèques, sinon il considère que la bibliothèque est nommée libxx.so -ltoto correspond aux fichier libtoto.so
  • libmalib.so: bibliothèque contenant des fichiers objets pour liaison dynamique (.so = shared object)).
  • libmalib.a: bibliothèque contenant des fichiers objets pour liaison statique.

Pour un programme se reposant sur les liaisons dynamiques (libXXX.so), il faudra impérativement indiquer dans quels répertoires sont stockées les bibliothèques. Ceci peut être fait notamment grâce à la variable d'environnement LD_LIBRARY_PATH

Créer une bibliothèque

Pour liaison statique

C'est rassembler plusieurs fichiers objets dans un seul fichier (une archive) avec ar (options ur (ajouter et remplace et v verbeux)

  • On crée les fichiers lib/module1.c lib/module2.c lib/module3.c et malib.h
  • On compile les fichiers sources puis on crée l'archive
$ ar ruv libmalib.a module*.o
ar: creating libmalib.a
a - module1.o
a - module2.o
a - module3.o

Dans le répertoire parent on modifie hello.c pour lui faire utiliser les fonctions. On compile ensuite:

$ gcc hello.c -Llib -lm -lmalib -o hello

Pour liaison dynamique

Pour créer une bibliothèque dynamique, utiliser l'option -shared

$ gcc -shared module*.o -o libmalib.so

Le fichier libmalib.so est créé, il peut être ensuite liée dynamiquement à l’exécutable hello:

$ gcc hello.o -L./lib -lmalib -lm -o hello.dyn

Pour vérifier à quelles bibliothèques dynamiques est lié notre exécutable:

$ ldd hello.dyn 
        linux-gate.so.1 =>  (0x00fc1000)
        libmalib.so => not found
        libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0x00698000)
        libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00cb4000)
        /lib/ld-linux.so.2 (0x004f8000)
$ ./hello.dyn 
./hello.dyn: error while loading shared libraries: libmalib.so: cannot open shared object file: No such file or directory

Références

dev/outils/gcc.txt · Dernière modification : 2021/02/01 21:51 de 127.0.0.1