Université Cheikh Anta Diop Ecole Supérieure Polytechnique PHASES DE COMPILATIO
Université Cheikh Anta Diop Ecole Supérieure Polytechnique PHASES DE COMPILATION EN LANGAGE C Professeur : Dr Mandicou BA Rédigé Par : Fatoumata Binta BAH Maimouna Tamah DIAO 1 Table des matières Introduction ............................................................................................................................................. 2 I. Le pré-processeur ............................................................................................................................ 2 II. Le compilateur ................................................................................................................................. 3 III. L’assembleur ................................................................................................................................ 4 IV. L’éditeur de liens ......................................................................................................................... 4 2 Introduction La Compilation consiste à transformer les instructions du programme en langage compréhensible par le processeur (langage machine) et génère un fichier binaire dit fichier objet. La compilation d’un programme écrit en C repose sur un schéma très simple (qu’on retrouve dans la plupart des langages impératifs non-interprétés). Pour comprendre ce qui se passe à chaque étape, prenons comme exemple un fichier bonjour.c contenant le code suivant : #include ˂stdio.h˃ int main (void) { puts("Bonjour !"); return 0; } I. Le pré-processeur Le pré-processeur (qui s’appelle cpp sous Unix) est un module de pré-traitement qui effectue des transformations sur le code écrit avant qu’il ne soit traité par le compilateur. Elle consiste à traiter les directives de compilation (comme l’inclusion de fichier d’entête de bibliothèques) et génère un fichier texte qui est encore un fichier source en C. La directive #define sert à définir des constantes et des macros (c’est à dire des bout de code qui seront recopiées tels-quels dans le code C). 3 La directive #include sert à inclure les fichiers d’en-tête (interfaces) des modules que l’on va utiliser ou écrire (par exemple #include si on veut utiliser la fonction puts, etc). Le pré- processeur effectue des remplacements textuels (et non sémantiques) : le préprocesseur n’est pas le compilateur, il n’interprète pas ce qu’il manipule. On peut faire le rapprochement avec la fonction Remplacer d’un traitement de texte. Dans le cas d’une directive #define NOM val, le pré-processeur remplace donc tout simplement chaque occurrence de la chaine NOM par la valeur val dans tout le texte du fichier. Dans le cas d’une directive #include le pré-processeur fait simplement un copier-coller du contenu du fichier fichier.h dans le fichier contenant la macro. Ce remplacement est récursif : si le fichier inclut contient lui-même des #include, les fichiers qu’il inclut seront aussi inclut dans le fichier initial. Dans le cas de notre exemple, si on tape la commande cpp bonjour.c bonjour.i, le pré-processeur génère un fichier bonjour.i qui contient, en plus du code initial que l’on a écrit, tout le contenu du fichier stdio.h (plus tout le contenu de tous les fichiers d’en-tête inclus dans stdio.h, récursivement). Le contenu du fichier produit est très verbeux car stdio.h est un très gros fichier d’en-tête, qui inclut lui-même beaucoup d’autres fichiers.h. II. Le compilateur Le compilateur (le programme cc sous Unix) a pour rôle de traduire le code C en code assembleur. Le compilateur C a pour particularité de faire de la compilation séparée : cela signifie que chaque fichier source est compilé sans regarder les autres fichiers, même s’ils font partie du même programme. Pour compiler un fichier C, le compilateur a juste besoin de connaitre les prototypes de toutes les fonctions utilisées dans ce fichier, mais il n’a pas besoin d’avoir à sa disposition le code de ces fonctions externes. Les prototypes des fonctions sont définies dans des fichiers .h, qui sont inclus par le pré-processeur dans le fichier C qui les utilise via la directive #include. La compilation comprend également plusieurs étapes d’analyse Analyse lexicale : vérification que les caractères ou suites des caractères du programme sont des constituants d'un programme du langage cible (ici le langage C). Cette phase découpe le flux de caractères du fichier en une suite d'unités lexicales prise en compte par l'étape suivante Analyse syntaxique : vérification que l'ordonnancement des unités lexicales respecte une configuration possible décrite par une grammaire (la grammaire du langage C). Analyse sémantique : vérification que les opérations décrites par la suite d'instructions ont un sens pour les données manipulées. Par exemple, comparer une donnée simple, comme un entier à un ensemble de données, comme les caractéristiques d'un individu, n'a pas de sens, même si la grammaire permet l’écriture d’une instruction semblable. 4 Dans le cas de notre exemple, si on appelle la commande cc -S bonjour.i sur le fichier généré par le pré-processeur, on obtient un fichier bonjour.s qui contient le même programme mais écrit en langage assembleur c’est la production de code. III. L’assembleur L’assembleur (appelé as sous Unix) prend un fichier écrit en langage assembleur et le traduit en code binaire (langage machine). Dans le cas de l’exemple bonjour.c, si la commande as -o bonjour.o bonjour.s, est tapée l’assembleur produit un fichier objet bonjour.o qui contient le même programme que bonjour.s, mais en langage machine (inutile de chercher à lire ce fichier, c’est du code machine, totalement illisible pour un humain !). IV. L’éditeur de liens L’éditeur de lien (ld sous Unix) a pour rôle de fusionner les différents fichiers objets pour produire l’exécutable final. En pratique, cela veut dire qu’il va fusionner : Les codes contenus dans les différentes fonctions de tous les fichiers objets du programme, pour obtenir une seule grosse zone de code ; Les données statiques allouées dans tous les fichiers objets du programme, pour obtenir une grosse zone de données statiques commune à tout le programme. Même dans le cas de notre exemple simpliste, un programme écrit en C utilise toujours d’autres fichiers objets qui font partie de la bibliothèque C standard (appelée en général libc.a, un fichier .a étant une archive contenant plusieurs fichiers .o) : dans notre exemple, on utilise le fichier objet contenant le code de la fonction puts, ainsi que d’autres fichiers objets permettant de rendre le fichier objet exécutable (ce type de fichiers s’appellent souvent crt0.o sous Unix). Comme le compilateur C fait de la compilation séparée, l’assembleur a laissé un trou dans le code objet produit, à l’endroit de l’appel à puts : ce trou est une indication laissée à l’éditeur de liens pour lui dire : « ici, tu dois insérer un véritable appel à la fonction puts que tu trouveras dans un autre fichier objet ». C’est donc au moment de l’édition de liens qu’on peut obtenir des erreurs du type « la fonction fct est inconnue » si on a essayé d’appeler dans un fichier.c une fonction qui n’existe pas (ou si on a mal écrit le nom d’une fonction existante), et pas au moment de la compilation. C’est aussi ce qui se passe si on essaie de produire un binaire à partir d’un fichier source qui ne contient pas de fonction main : cette fonction est appelée par un des fichiers objets inclus automatiquement par l’éditeur de liens, et il n’arrivera pas à la trouver si elle n’est pas dans un des fichiers sources qu’on lui donne. La chaine de compilation GCC est en fait beaucoup plus simple à utiliser : elle fournit un programme générique (appelé simplement gcc) qui est capable d’appeler chacun des programmes nécessaires dans le bon ordre jusqu’à produire l’exécutable. On appelle couramment ce type de programme un front-end en anglais. 5 Le programme gcc se base simplement sur l’extension du fichier qu’on lui donne pour déterminer quels programmes il doit appeler. Par exemple, si le fichier se termine par : .c : il appelle dans l’ordre : le pré-processeur, le compilateur, l’assembleur et l’éditeur de lien; .i : il appelle dans l’ordre : le compilateur, l’assembleur et l’éditeur de liens; .s : il appelle dans l’ordre : l’assembleur et l’éditeur de liens; .o : il appelle simplement l’éditeur de liens (mais en lui passant au passage tous les bons paramètres compliqués qu’on n’a pas détaillé plus haut, ce qui simplifie grandement la vie!). On peut demander à gcc de produire les fichiers intermédiaires manipulés par les différents programmes qu’il appelle si on veut les observer : gcc -E -o bonjour.i bonjour.c produit le fichier en sortie du pré-processeur; gcc -S bonjour.c produit le fichier en sortie du compilateur (après avoir appelé aussi le préprocesseur); gcc -c bonjour.o produit le fichier en sortie de l’assembleur (après avoir appelé aussi le préprocesseur et le compilateur); gcc -o bonjour bonjour.c appelle tous les programmes nécessaires pour produit directement le binaire bonjour. Le schéma ci-dessous résume le processus de compilation dans le cas d’un programme composé d’un programme principal prog.c qui utilise un module mod.c : prog.c mod.c prog.s mod.s prog.o mod.o prog pré-processing et compilation assemblage édition de liens libc.a crt0.o 6 FIN DU RAPPORT uploads/S4/ tp0-compilation.pdf
Documents similaires
-
15
-
0
-
0
Licence et utilisation
Gratuit pour un usage personnel Attribution requise- Détails
- Publié le Oct 01, 2021
- Catégorie Law / Droit
- Langue French
- Taille du fichier 0.6461MB