Mémento du développeur sous Linux
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"
extern void funlockfile (FILE *__stream) __attribute__
((__nothrow__ , __leaf__)); # 2 "helloworld.c" 2 int main() |
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: |
* 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