Précédent Index Suivant

Chapitre 14 :   Compilation séparée et édition de liens

Cette nouvelle version de PP, PP10, introduit les notions de module et de compilation séparée pour lesquelles la mise en place d'une édition de liens est nécessaire.

On se base ici sur la version 5 de PP (chapitre 9, les procédures simples, sans paramètres, sans déclarations locales, et non imbriquées).

14.1   L'exemple Unix

Sous Unix, le compilateur cc est composé de plusieurs phases :
cpp
Le préprocesseur cpp est chargé de la gestion des fichiers inclus et des macros. cpp inclus << physiquement >> les fichiers #include et remplace les macros #define par leur définition. Il passe à cc le source C ainsi produit.

ccom
C'est le coeur du compilateur C. Il produit un fichier code objet (.o) à partir d'un fichier C.

ld
L'éditeur de liens. Il assure la production d'un fichier exécutable à partir d'une liste de fichiers .o et des fichiers de bibliothèque (.a).
L'intérêt de cette approche est la possibilité de rassembler en un programme exécutable des morceaux (modules) de programmes compilés séparément et la possibilité de créer des exécutables à partir de modules compilés dont on n'a pas les sources ; c'est la notion de bibliothèque.

14.2   Grammaire et sémantique

Un programme PP10 est la réunion d'un ensemble de modules et d'un programme principal.

Un module ou un programme principal est contenu dans un fichier ; on distingue donc le fichier programme principal et les fichiers modules. Chacun des fichiers composant un programme est compilé séparément ; l'ensemble des fichiers objets produits lors de cette phase de compilation est passé à un éditeur de liens qui produit un programme P-Code complet.

14.2.1   Programme principal

La grammaire d'un fichier programme est une évolution de la grammaire de PP5.
PROGRAM ::= program ID ; IMPORT EXPORT BLOCK .
IMPORT ::= { import from ID_MODULE : ID { , ID } ; }
EXPORT ::= export : ID { , ID } ; | e
Les ID d'une déclaration import sont des identificateurs de procédures ou de variables définies dans un fichier module de nom ID_MODULE. Ces variables et procédures doivent avoir été exportées par le module ID_MODULE (cf. infra). Ces variables et procédures sont alors visibles (au niveau zéro) dans le programme principal : les variables peuvent être référencées en lecture et/ou en écriture ; les procédures peuvent être appelées.

Les ID d'une liste export sont des noms de procédures ou de variables globales définies au niveau zéro du programme principal. Ces procédures et ces variables pourront être accédées par des modules externes.

14.2.2   Modules

Un fichier module est défini comme un fichier programme principal ne comportant pas de partie BLOCK :
MODULE ::= module ID ; IMPORT EXPORT CONSTS VARS PROCS
IMPORT ::= { import from ID_MODULE : ID { , ID } ; }
EXPORT ::= export : ID { , ID } ; | e
Les ID d'une déclaration import sont des identificateurs de procédures ou de variables définies dans un autre fichier module de nom ID_MODULE ou dans le fichier programme principal de nom ID_MODULE. Ces variables et procédures doivent avoir été déclarées dans une section export.

Les ID d'une liste export sont des noms de procédures ou de variables globales définies au niveau zéro du module. Ces procédures et ces variables pourront être accédées par des modules externes qui les importeront explicitement.

14.3   Forme du code généré

14.3.1   Instructions incomplètes

Lors de la compilation d'un module ou d'un programme principal, les adresses des variables et procédures externes ne sont pas connues.

L'idée est alors de générer des instructions incomplètes pour les appels de ces procédures (CAL) et les références à ces variables (LDA). Ces instructions incomplètes seront complétées par l'éditeur de liens. Pour ce faire, il est nécessaire d'informer l'éditeur de liens des instructions incomplètes et des noms des procédures/variables avec lesquelles il est nécessaire de compléter ces instructions. Une partie d'un fichier de code sera donc constituée d'une liste de triplet
module, identificateur, instruction
Un module est le nom d'un module qui doit exporter l'identificateur identificateur. L'adresse de cet identificateur permet de compléter l'instruction numéro instruction du code.

De manière symétrique, le code généré depuis un module ou un programme exportant des procédures/variables doit inclure une liste de couples
identificateur, adresse
donnant l'adresse de l'identificateur identificateur défini dans le module.

14.3.2   Adresses translatables

Un nouveau problème intervient à ce niveau : à quelle adresse mémoire faut-il allouer les variables globales définies dans un module ? Imaginons que l'on veuille allouer ces variables au dessus des variables globales du programme principal. Lors de la compilation d'un module, on ne peut déterminer l'adresse de ces variables car on ne connaît pas la taille des données globales du programme principal.

En fait, on va allouer ces données dans une zone de mémoire virtuelle dite translatable. L'ensemble des adresses dans cette mémoire virtuelle qui sont allouées et utilisées dans un module seront translatées lors de l'édition de liens vers des adresses dans la mémoire << réelle >> de la machine P-Code.

Une information supplémentaire doit être présente dans le code généré par un programme principal ou un module : c'est la taille de la zone mémoire nécessaire aux variables globales du programme principal ou du module. à partir de ces tailles, l'éditeur de liens générera l'instruction INT nécessaire à la réservation de la mémoire pour toutes les variables globales.

14.3.3   Code translatable

Un problème similaire à celui de la mémoire se retrouve au niveau du code. Quel est le numéro d'une instruction donnée généré depuis un module ?

De manière symétrique, on introduit la notion de code translatable. Les numéros d'instruction référencés dans un module sont des numéros relatifs au début du code du module et seront translatés par l'éditeur de liens en fonction de la place de ce code dans le code final.

14.4   L'éditeur de liens

L'éditeur de liens est donc chargé de produire un code P-Code unique à partir de multiples codes. Il lui est nécessaire de
  1. Construire la zone mémoire globale comme la réunion des zones mémoires globales des différents modules et du programme principal. Un déplacement dans cette zone est alors attaché à chaque module ; c'est la valeur qu'il sera nécessaire d'ajouter aux références des variables globales de ce module pour obtenir leur adresse << réelle >>.

  2. Construire un segment de code unique par réunion des segments de code des différents modules et du programme principal. Un déplacement dans cette zone est attaché à chaque module ; c'est la valeur de ce déplacement qu'il faut ajouter à chaque numéro d'instruction du module pour obtenir son adresse dans le segment de code produit.

  3. Générer une instruction INT de réservation de la zone mémoire globale.

  4. Compléter chacune des instructions incomplètes (numéro d'instruction ou adresse externe).

14.5   Travaux dirigés et travaux pratiques

  1. Définir précisément le format des fichiers de code produits par le compilateur et qui seront acceptés en entrée de l'éditeur de liens.

  2. Mettre en place les modifications nécessaires au compilateur pour générer cette information. Une structure de données interne est-elle nécessaire pour garder cette information lors de la compilation, ou est-il possible de produire directement l'information dans un fichier au fur et à mesure ?

  3. Dans ce chapitre, nous n'avons considéré que les variables simples entières et les procédures simples. Le type des variables et procédures externes sont donc connues << par défaut >>. Dans une version plus complète, il est nécessaire d'identifier les types des variables et des paramètres des procédures.

    Proposer une syntaxe pour cette déclaration. On pourra se rapprocher de la syntaxe des déclarations forward de Pascal.

  4. Quelles erreurs peuvent être détectées au niveau de l'éditeur de liens ?

Précédent Index Suivant