Un guide du programmeur pour le compilateur GNU C

C est un langage de programmation bien connu, populaire auprès des programmeurs expérimentés et débutants. Le code source écrit en C utilise des termes anglais standard, il est donc considéré comme lisible par l’homme. Cependant, les ordinateurs ne comprennent que le code binaire. Pour convertir le code en langage machine, vous utilisez un outil appelé un compilateur.
Un compilateur très courant est GCC (GNU C Compiler). Le processus de compilation implique plusieurs étapes intermédiaires et des outils adjacents.
Installer GCC
Pour vérifier si GCC est déjà installé sur votre système, utilisez le gcc
commande:
$ gcc --version
Si nécessaire, installez GCC à l’aide de votre gestionnaire de packages. Sur les systèmes basés sur Fedora, utilisez dnf
:
$ sudo dnf install gcc libgcc
Sur les systèmes basés sur Debian, utilisez apt
:
$ sudo apt install build-essential
Après l’installation, si vous souhaitez vérifier où GCC est installé, utilisez :
$ whereis gcc
Programme C simple utilisant GCC
Voici un programme C simple pour montrer comment compiler du code à l’aide de GCC. Ouvrez votre éditeur de texte préféré et collez ce code :
Enregistrez le fichier sous hellogcc.c
puis compilez-le :
$ ls
hellogcc.c$ gcc hellogcc.c
$ ls -1
a.out
hellogcc.c
Comme vous pouvez le voir, a.out
est l’exécutable par défaut généré à la suite de la compilation. Pour voir la sortie de votre application nouvellement compilée, exécutez-la simplement comme vous le feriez avec n’importe quel binaire local :
Nommez le fichier de sortie
Le nom de fichier a.out
n’est pas très descriptif, donc si vous voulez donner un nom spécifique à votre fichier exécutable, vous pouvez utiliser le -o
option:
$ gcc -o hellogcc hellogcc.c$ ls
a.out hellogcc hellogcc.c$ ./hellogcc
Hello, GCC!
Cette option est utile lors du développement d’une grande application qui doit compiler plusieurs fichiers source C.
Étapes intermédiaires de la compilation GCC
Il y a en fait quatre étapes pour compiler, même si GCC les exécute automatiquement dans des cas d’utilisation simples.
- Prétraitement : Le préprocesseur GNU C (
cpp
) analyse les en-têtes (#inclure instructions), développe les macros (#définir instructions), et génère un fichier intermédiaire tel quehellogcc.i
avec un code source étendu. - Compilation : Au cours de cette étape, le compilateur convertit le code source prétraité en code assembleur pour une architecture CPU spécifique. Le fichier d’assemblage résultant est nommé avec un
.s
extension, commehellogcc.s
dans cet exemple. - Assemblage : L’assembleur (
as
) convertit le code assembleur en code machine dans un fichier objet, tel quehellogcc.o
. - Liaison : L’éditeur de liens (
ld
) relie le code objet au code de la bibliothèque pour produire un fichier exécutable, tel quehellogcc
.
Lors de l’exécution de GCC, utilisez le -v
possibilité de voir chaque étape en détail.
$ gcc -v -o hellogcc hellogcc.c
(Jayashree Huttanagoudar, CC BY-SA 4.0)
Compiler manuellement le code
Il peut être utile de découvrir chaque étape de la compilation car, dans certaines circonstances, vous n’avez pas besoin de GCC pour passer par toutes les étapes.
Tout d’abord, supprimez les fichiers générés par GCC dans le dossier actuel, à l’exception du fichier source.
$ rm a.out hellogcc.o$ ls
hellogcc.c
Pré-processeur
Tout d’abord, démarrez le préprocesseur, en redirigeant sa sortie vers hellogcc.i
:
$ cpp hellogcc.c > hellogcc.i$ ls
hellogcc.c hellogcc.i
Jetez un œil au fichier de sortie et notez comment le préprocesseur a inclus les en-têtes et développé les macros.
Compilateur
Vous pouvez maintenant compiler le code en assembleur. Utilisez le -S
possibilité de définir GCC uniquement pour produire du code assembleur.
$ gcc -S hellogcc.i$ ls
hellogcc.c hellogcc.i hellogcc.s$ cat hellogcc.s
Jetez un œil au code d’assemblage pour voir ce qui a été généré.
Assemblée
Utilisez le code assembleur que vous venez de générer pour créer un fichier objet :
$ as -o hellogcc.o hellogcc.s$ ls
hellogcc.c hellogcc.i hellogcc.o hellogcc.s
Mise en relation
Pour produire un fichier exécutable, vous devez lier le fichier objet aux bibliothèques dont il dépend. Ce n’est pas aussi facile que les étapes précédentes, mais c’est éducatif :
$ ld -o hellogcc hellogcc.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
ld: hellogcc.o: in function `main`:
hellogcc.c:(.text+0xa): undefined reference to `puts'
Une erreur faisant référence à un undefined puts
se produit après que l’éditeur de liens a terminé de regarder le libc.so
bibliothèque. Vous devez trouver des options d’éditeur de liens appropriées pour lier les bibliothèques requises afin de résoudre ce problème. Ce n’est pas une mince affaire et cela dépend de la configuration de votre système.
Lors de la liaison, vous devez lier le code aux objets d’exécution principaux (CRT), un ensemble de sous-routines qui facilitent le lancement des exécutables binaires. L’éditeur de liens doit également savoir où trouver les bibliothèques système importantes, y compris libc et libgcc, notamment dans les instructions spéciales de début et de fin. Ces instructions peuvent être délimitées par le --start-group
et --end-group
options ou en utilisant des chemins vers crtbegin.o
et crtend.o
.
Cet exemple utilise les chemins tels qu’ils apparaissent sur une installation RHEL 8, vous devrez donc peut-être adapter les chemins en fonction de votre système.
$ ld -dynamic-linker \
/lib64/ld-linux-x86-64.so.2 \
-o hello \
/usr/lib64/crt1.o /usr/lib64/crti.o \
--start-group \
-L/usr/lib/gcc/x86_64-redhat-linux/8 \
-L/usr/lib64 -L/lib64 hello.o \
-lgcc \
--as-needed -lgcc_s \
--no-as-needed -lc -lgcc \
--end-group
/usr/lib64/crtn.o
La même procédure de création de liens sur Slackware utilise un ensemble de chemins différent, mais vous pouvez voir la similitude dans le processus :
$ ld -static -o hello \
-L/usr/lib64/gcc/x86_64-slackware-linux/11.2.0/ \
/usr/lib64/crt1.o /usr/lib64/crti.o \
hello.o /usr/lib64/crtn.o \
--start-group -lc -lgcc -lgcc_eh \
--end-group
Exécutez maintenant l’exécutable résultant :
Quelques utilitaires utiles
Vous trouverez ci-dessous quelques utilitaires permettant d’examiner le type de fichier, la table des symboles et les bibliothèques liées à l’exécutable.
Utilisez le file
utilitaire pour déterminer le type de fichier :
$ file hellogcc.c
hellogcc.c: C source, ASCII text$ file hellogcc.o
hellogcc.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped$ file hellogcc
hellogcc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb76b241d7d00871806e9fa5e814fee276d5bd1a, for GNU/Linux 3.2.0, not stripped
L’utilisation de la nm
utilitaire pour lister les tables de symboles pour les fichiers objets :
$ nm hellogcc.o
0000000000000000 T main
U puts
Utilisez le ldd
utilitaire pour répertorier les bibliothèques de liens dynamiques :
$ ldd hellogcc
linux-vdso.so.1 (0x00007ffe3bdd7000)
libc.so.6 => /lib64/libc.so.6 (0x00007f223395e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2233b7e000)
Conclure
Dans cet article, vous avez appris les différentes étapes intermédiaires de la compilation GCC et les utilitaires pour examiner le type de fichier, la table des symboles et les bibliothèques liées à un exécutable. La prochaine fois que vous utiliserez GCC, vous comprendrez les étapes nécessaires pour produire un fichier binaire pour vous, et en cas de problème, vous saurez comment suivre le processus pour résoudre les problèmes.