Mémento du développeur sous Linux

Accueil

GCC :

Toutes les distributions Linux qui se respectent ont installés par défaut le célèbre compilateur GCC avec sa panoplie de langage supportés (C, C++, Objective-C, Objective-C++, Fortran, Java, Ada, et Go) accompagnés de leurs bibliothèques et des outils nécessaires (objdump, readelf, addr2line...).

Les differents types de fichiers (issus du man de GCC) :

Fichier
Type
fichier.c fichier source contenant du code C
fichier.i fichier C obtenu après un passage au pré-processeur
fichier.ii fichier C++ obtenu après un passage au pré-processeur
fichier.m fichier source contenant de l'Objective-C
fichier.mi fichier Objective-C obtenu après un passage au pré-processeur
fichier.mm fichier.M fichier source contenant de l'Objective-C++ avant le passage au pré-processeur.
fichier.h fichier d'en-tête de code C, C++, Objective-C ou Objective-C++ avant le passage au pré-processeur par défaut, ou fichier d'en-tête C, C++ pour être transformé en Ada spec (via l'option -fdump-ada-spec switch).
fichier.cc
fichier.cp
fichier.cxx
fichier.cpp
fichier.CPP
fichier.c++
fichier.C

fichier source contenant du code C++ avant le passage au pré-processeur.
fichier.mm
fichier.M
fichier source contenant du code Objective-C++ avant le passage au pré-processeur.
fichier.mii fichier Objective-C++ obtenu après un passage au pré-processeur
fichier.hh
fichier.H
fichier.hp
fichier.hxx
fichier.hpp
fichier.HPP
fichier.h++
fichier.tcc
fichier d'en-tête C/C++ avant le passage au pré-processeur.
fichier.F
fichier.FOR
fichier.fpp
fichier.FPP
fichier.FTN
fichier source contenant du code Fortran avant le passage au pré-processeur.
fichier.f
fichier.for
fichier.ftn

fichier Fortran obtenu après passage au pré-processeur.
fichier.F90
fichier.F95
fichier.F03
fichier.F
fichier.FOR
fichier.fpp
fichier.FPP
fichier.FTN
fichier source contenant du code Fortran avant le passage au pré-processeur.
fichier.f90
fichier.f95
fichier.f03
fichier.f08
fichier Fortran obtenu après passage au pré-processeur.
fichier.go fichier source contenant du code Go.
fichier.ads fichier source contenant du code Ada (fichier de spécification des variables, des types et des sous-programmes) Un tel fichier s'appelle également "spec".
fichier.adb fichier source contenant du code Ada (implémentation du code pour les procédures et les fonctions). Un tel fichier s'appelle également "body".
fichier.s fichier source contenant du code Assembleur
fichier.S
fichier.sx
fichier Assembleur obtenu après passage au pré-processeur.

Principe de la compilation de code source par GCC:

Par exemple pour du code C, la génération du fichier binaire (exécutable machine) s'obtient en plusieurs étapes:

Code source : fichier d'extension .c
Pré-processeur
Etape 1
fichier d'extension .i
Compilation
Etape 2
fichier d'extension .s

Assemblage ou Conversion

(code assembleur en code binaire)

Etape 3
fichier d'extension .o
Edition de lien
Etape 4
fichier binaire

Exemples de commande GCC pour compiler du code C :

Pour illustrer certains exemples, nous utilisons le célèbre programme helloworld.c bien connu des développeurs. Ce programme se contente d'afficher en console le message "Hello World".

Code source de helloworld.c


#include <stdio.h>

int main()
{
printf("\nHello World");
return 0;
}

* Basiquement :

gcc programme.c

Toutes les étapes de compilations (pré-processeur -> compilation -> conversion (code assembleur en code binaire) -> édition de lien) sont automatiquement faites. Cela a pour conséquence la production d'un fichier exécutable nommé a.out

* Nommage du fichier exécutable: option -o ( o comme: output)

gcc programme.c -o fichier_executable

* Affichage de tous les warnings: option -Wall (très efficace pour résoudre les bugs)

gcc -Wall programme.c -o fichier_executable

* Considère tout warning comme une erreur (arrêt de la compilation) : option -Werr

gcc -Werr programme.c -o fichier_executable

* Passage du code source uniquement au pré-processeur (étape 1) : option -E

gcc -E helloworld.c > helloworld.i

Extrait du fichier helloworld.i à partir du fichier source helloworld.c :

# 1 "helloworld.c"
# 1 "<interne>"
# 1 "<command-line>"
# 1 "helloworld.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4


[...]


extern int printf (__const char *__restrict __format, ...);

[...]

extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 940 "/usr/include/stdio.h" 3 4

# 2 "helloworld.c" 2

int main()
{
printf("\nHello World");
return 0;
}

Nous pouvons constater plusieurs choses :

Le pré-processeur traite d'abord les directives de compilation. Cela permet d'inclure les prototypes des fonctions du fichier d'en-tête (dans notre cas: inclusion du fichier stdio.h contenant le prototype de la fonction printf dans le code source de helloworld.c). Le pré-processeur élimine ensuite tous les commentaires mis par le développeur car cela diminue l'empreinte mémoire du futur binaire généré.

* Passage du code pré-compilé au compilateur (étape 2) : option -S. Nous obtenons du code assembleur.

Nous avons deux possibilités:

Reprendre le fichier déjà passé au pré-processeur:

gcc -S helloworld.i > helloworld.s

Ou recommencer l'opération depuis le fichier sources.

gcc -S helloworld.c > helloworld.s

Extrait du fichier helloworld.s à partir du fichier source helloworld.c :

Nous reconnaissons les mnémoniques push, call, mov, pop. La lettre q à la fin de chacun de ces mnémoniques sont propres à l'architecture 64 bits x86 Intel de la machine utilisée pour les exemples.

* Passage du code assembleur jusqu'au code machine (étape 3) : option -c. Même remarque que précédement :

Nous avons deux possibilités:

Reprendre le fichier assembleur:

gcc -c helloworld.s

Ou recommencer l'opération depuis le fichier source.

gcc -c helloworld.c

Le fichier objet obtenu helloworld.o est illisible pour un humain (code machine). Il est cependant pertinent d'utiliser l'outil objdump permettant de désassembler le binaire et de le rendre plus lisible.

objdump -s ./helloworld.o

Nous obtenons l'affichage :

./helloworld.o: file format elf64-x86-64

Contents of section .text:
0000 554889e5 b8000000 004889c7 b8000000 UH.......H......
0010 00e80000 0000b800 0000005d c3 ...........].
Contents of section .rodata:
0000 0a48656c 6c6f2057 6f726c64 00 .Hello World.
Contents of section .comment:
0000 00474343 3a202855 62756e74 752f4c69 .GCC: (Ubuntu/Li
0010 6e61726f 20342e36 2e332d31 7562756e naro 4.6.3-1ubun
0020 74753529 20342e36 2e3300 tu5) 4.6.3.
Contents of section .eh_frame:
0000 14000000 00000000 017a5200 01781001 .........zR..x..
0010 1b0c0708 90010000 1c000000 1c000000 ................
0020 00000000 1d000000 00410e10 8602430d .........A....C.
0030 06580c07 08000000 .X......

* Passage du code machine jusqu'à l'édition de lien produisant un exécutable final à partir du fichier objet (étape 4):

gcc helloworld.o

Cette commande produit par défaut un binaire nommé a.out en l'absence de précision du nom du fichier exécutable généré. Il est possible de le nommer en ajoutant l'option de compilation -o que l'on a déjà vu plus haut :

gcc helloworld.o -o helloworld

De même, il est evidemment possible d'obtenir notre exécutable helloworld directement à partir du fichier source sans passer par les différentes étapes détaillées précedement. Nous l'avons vu au début de la page web.

Quelques options bien utiles de GCC :

Par défaut, le compilateur cherche les bibliothèques natives dans les répertoires /lib et /usr/lib

*Option -l (petit L) : lie dynamiquement l'exécutables à la bibliothèque spécifiée :

gcc programme.c -o fichier_executable -llib.so (attention à ne pas mettre d'espace entre l'option et le nom de la bibliothèque nommée ici lib.so : (extension .so pour bibliothèque dynamique)) :

*Option -L(indication du chemin) : cherche les différentes bibliothèques dans les repertoires indiqués (par défaut /usr/include) :

gcc -o programmeX11 programmeX11.c -L/usr/include -lX11

Dans le programme C, il suffit d'inclure les fichiers comme de simples fichiers d'en-tête standard au C ANSI:


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


//Bibliothèques incluses
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#include <X11/Xutil.h>

ATTENTION avec la seule option -l, la compilation devient bien sûr :

gcc -o programmeX11 programmeX11.c -lX11

MAIS

les fichiers d'en-têtes sont inclus comme ceci danc le programme C (source courant de bug à la compilation):

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

//Bibliothèques incluses
#include "/usr/include/X11/Xatom.h"
#include "/usr/include/X11/Xlib.h"
#include "/usr/include/X11/cursorfont.h"
#include "/usr/include/X11/Xutil.h"

*Option -I(indication du chemin) : cherche les différents fichiers d'en-tête référencés dans le code.

*Les options -O0, -O1, -O2, -O3, -Os et-Ofast permettent d'activer les différents niveaux d'optimisation de l'exécutable en supprimant les informations de débogage (option -O0 est l'optimisation la plus forte). Par défaut, le compilateur compile du code avec l'optimisation -O2. Bien sûr, il convient d'optimiser le code une fois que les bugs sont corrigés avec des options d'optimisation moins fortes.


ATTENTION il faut bien respecter un certain ordre dans les options de gcc :

exemple:

La compilation fonctionne:                     gcc monProg.c -o monProg -L/usr/include/ -lm -lX11 -Wall

La compilation NE fonctionne PAS :     gcc -o monProg -L/usr/include/ -lm -lX11 -Wall monProg.c

Page mise à jour le 25/04/2019