4.3 KiB
Chapitre 3 — Outils et chaîne de compilation
3.1 Compilateurs : rôle et implémentations
Un compilateur transforme du code C en instructions exécutables par le processeur.
Le C est un langage compilé.
Cela signifie que le programme est transformé avant exécution.
Compilateurs courants
- GCC (GNU Compiler Collection)
- Clang (LLVM)
- MSVC (Microsoft Visual C)
Chaque compilateur :
- implémente la norme C
- peut ajouter des extensions
- produit du code pour une architecture donnée .
Exemple
gcc main.c -o prog
3.2 Étapes de la chaîne de compilation
La compilation est une pipeline.
Étape 1 — Préprocesseur
Traite :
- #include
- #define
- #ifdef
Exemple :
#include <stdio.h>
Le contenu du fichier est copié dans le code.
Étape 2 — Compilation
C → assembleur
gcc -S main.c
Produit :
main.s
Étape 3 — Assemblage
ASM → objet (.o) L’assembleur transforme le code assembleur en code machine.
Produit :
main.o
Étape 4 — Linking
Objets → binaire final Le linker assemble tous les objets. Il résout les dépendances.
3.3 Assembleur utilisé selon la toolchain
Le compilateur ne génère pas un assembleur universel. Il dépend de la toolchain.
- GCC :
- utilise GAS (GNU assembler)
- syntaxe par défaut : AT&T
- Clang/LLVM :
- utilise un assembleur intégré LLVM
- peut aussi utiliser GAS
- MSVC :
- utilise son backend interne
- eut utiliser MASM
- Android (NDK) :
- utilise Clang + LLVM
Exemple GAS
movl $5, %eax
Exemple Intel
mov eax, 5
Ces syntaxes ne sont pas compatibles directement.
3.4 Fichiers générés
Chaque étape produit un fichier.
Source
main.c
Assembleur
main.s
Objet
main.o
Exécutable
prog
Exemple — compilation séparée
gcc -c main.c
gcc main.o -o prog
3.5 Warnings et erreurs
Le compilateur analyse aussi le code.
Erreur
Empêche la compilation.
Warning
Indique un problème potentiel.
Exemple — variable non initialisée
int main(void)
{
int x;
return x;
}
gcc -Wall -Wextra -Werror main.c
3.6 Options du compilateur
Les options contrôlent le comportement.
Optimistion
gcc -O0 main.c
gcc -O2 main.c
gcc -O3 main.c
Debug
gcc -g main.c
Standard
gcc -std=c11 main.c
3.7 Informations de debug
Les informations de debug permettent :
inspection mémoire suivi d’exécution
Exemple
gdb prog
3.8 Compilation séparée
Permet de compiler fichier par fichier.
Exemple
gcc -c a.c
gcc -c b.c
gcc a.o b.o -o prog
3.9 Compilation croisée
Compiler pour une autre architecture.
Exemple
arm-none-eabi-gcc main.c
3.10 Linker
Le linker combine les objets. Il :
- fusionne les sections
- résout les symboles
- construit le binaire
3.11 Symboles
Un symbole est un nom utilisé par le linker.
Exemple
void f(void)
{
}
➡ symbole f
Le linker associe :
- déclaration
- définition
3.12 Formats binaires
Le binaire final a un format.
- ELF (Linux)
- PE (Windows)
- Mach-O (macOS)
Contenu :
.text→ code.data→ données.bss→ mémoire non initialisée
Rôle du loader
Le système :
- charge le binaire
- mappe les sections
- résout les dépendances
3.13 ABI (introduction)
L’ABI définit :
- comment appeler une fonction
- comment passer les arguments
- comment organiser la mémoire
Exemple
Linux x86_64 :
- rdi, rsi, rdx
Windows x86_64 :
- rcx, rdx, r8
Même CPU ≠ même ABI
3.14 Linker script
Un linker script contrôle la disposition mémoire.
Exemple
SECTIONS
{
.text 0x1000 : { *(.text) }
}
Utilisation
- embarqué
- systèmes bas niveau
3.15 Résumé
- le C est compilé en code machine
- la compilation est une chaîne
- assembleur dépend de la toolchain
- linker construit le binaire
- format dépend du système
- ABI dépend de l’OS