Compiler sous Linux

Demander un devis
Je souhaite télécharger le programme ou imprimer le programme
  • Imprimer

Ce tutoriel s'adresse à des gens à l'aise avec Linux et en particulier avec les lignes de commandes. En général les programmes que vous serez amené à compiler sous Linux sont écrits en langage C. Ceci concerne notamment les modules et le noyau Linux. Cet article est donc orienté autour de ce langage.

Notions de langage C

Les sources

 

En langage C, le code source est formé de deux types de fichiers :

  • les fichiers sources (portant l'extension .c) : ils implémentent le comportement de chaque fonctionnalité du programme,
  • les fichiers headers (portant l'extension .h) : ils ont un rôle déclaratif et permettent d'annoncer ce qui est implémenté dans les fichiers sources.

Un projet écrit en langage C contient souvent de nombreux fichiers .c et de nombreux fichiers .h, notamment afin d'organiser proprement l'architecture du code source.

 

Les binaires

 

Un binaire est un fichier qui contient une suite de bits (de 0 et de 1) qui n'est pas du texte (si on l'ouvre avec un éditeur texte, on voit une suites de caractères inintelligibles). Souvent un programme est un binaire, mais il existe bien d'autres types de fichiers binaires (les images, les sons, les librairies utilisées par le système, ...). Toutefois, par abus de langage, un "binaire" sous Linux désigne souvent un programme.

 

Des sources écrites en langage C sont manipulés en vue de produire un binaire directement compréhensible par la machine. Cette phase de construction est appelée compilation.

 

Sous Linux, si l'on omet les fichiers de données (musiques, films...), il existe quatre grande catégories de binaires :

  • Les exécutables (qui n'ont pas d'extension ".exe" sous Linux).
  • Les modules (.ko, kernel object).
  • Les librairies dynamiques (.so, shared object). C'est l'équivalent des fichiers .dll de Windows.
  • Les librairies statiques (.a).

La compilation

 

En langage C la phase dite de compilation est généralement effectuée grâce à gcc. Elle se décompose en une succession de trois grandes étapes.

  1. La précompilation : elle consiste à faire une première passe sur certaines directives inclues dans le code source. Le précompilateur ne fait que des manipulations basiques dans le code source (substitué un symbole par une valeur, inclure un fichier dans un autre...)
  2. La compilation de chaque fichier .c : elle consiste à produire un fichier .o (comme "object") par fichier .c. Ce sont des fichiers intermédiaires qui ne seront utilisés que lors de la 3e étape (le linkage). Une compilation d'un fichier fichier .c ne peut réussir que si le code source du fichier .c est valide. Si un fichier .c utilise du code provenant d'un autre fichier .c, alors il doit inclure les bons fichiers .h pour que tout ce qu'il manipule soit correctement déclaré.
  3. Le linkage : on rassemble tous les fichiers .o, .so et .a constituant l'application en vue de produire le binaire final (un exécutable, un librairie, un module ou un kernel). Elle vérifie en outre que l'ensemble est cohérent (en vérifiant que tout est correctement déclaré et n'est pas défini en double).

 

Résumé

 

Supposons que je développe le un navigateur web. Je désire utiliser la librairie dynamique GTK pour faire l'interface graphique.

  • j'écris un fichier .c et .h par entité du programme (par exemple les onglets, la barre de raccourci, les signets...)
  • mes fichiers (.h ou .c, selon mes besoins) incluent des fichiers .h de la librairie GTK
  • pour la phase de linkage, je vais avoir besoin de la librairie GTK (fichier .so).

Pour compiler ce programme (et produire l'exécutable), j'ai donc besoin :

  • des fichiers .c et .h associés à mon programme,
  • des fichiers .h et du .so associés à la librairie GTK.

Une fois le programme construit, j'ai besoin uniquement :

  • de l'exécutable associé à mon programme
  • du .so associé à la librairie GTK.

Différence entre une librairie statique (.a) et dynamique (.so)

 

Un programme basé sur une librairie peut être compilé aussi bien avec une librairie statique (.a) que dynamique (.so).

  • Avec une librairie statique : le contenu de la librairie est copié dans le binaire que l'on compile. Ce dernier est donc plus volumineux que si on l'avait compilé avec une librairie dynamique, mais il est également standalone (indépendant des librairies installées sur le système).
  • Avec une librairie dynamique : le binaire compilé est lié à cette librairie. C'est une bonne approche pour générer un binaire peu coûteux en espace disque. Toutefois, si la librairie est absente, le programme ne peut pas se lancer.

On peut retrouver à quelles librairies est lié un programme avec la commande ldd :

 

(mando@silk) (~) $ ldd /bin/cp
        linux-gate.so.1 =>  (0xb7839000)
        libselinux.so.1 => /lib/libselinux.so.1 (0xb780a000)
        librt.so.1 => /lib/i686/cmov/librt.so.1 (0xb7801000)
        libacl.so.1 => /lib/libacl.so.1 (0xb77f9000)
        libattr.so.1 => /lib/libattr.so.1 (0xb77f4000)
        libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb76ad000)
        libdl.so.2 => /lib/i686/cmov/libdl.so.2 (0xb76a9000)
        /lib/ld-linux.so.2 (0xb783a000)
        libpthread.so.0 => /lib/i686/cmov/libpthread.so.0 (0xb7690000)

 

Remarque : on doit utiliser le même compilateur pour produire le binaire que celui utilisé pour compiler la librairie.


Organisation sous Linux

 

En fonction de leur nature, les binaires sont rangés comme indiqué dans ce tableau :

 

     

    bin

    commande non système

    sbin

    commande système

    lib

    librairie (.so, .a)

    /

    essentiel

    /bin

    /sbin

    /lib

    /lib/modules : modules (.ko)

    /usr

    non essentiel

    /usr/bin

    /usr/sbin

     

    /usr/lib

     

    /usr/local

    install. manuelle

    /usr/local/bin /usr/local/sbin

    /usr/local/lib

Pour plus de détails, vous pouvez consulter cet article.

 

Compiler une application ou une librairie

 

Gardez à l'esprit qu'on compile rarement une application ou une librairie soi-même

 

Comme vous pouvez le constater, une compilation est une tâche assez complexe et pouvant devenir fastidieuse si ce que l'on compile est composé de nombreux fichiers ".c". De plus, quand le programme dépend d'une librairie, celle-ci doit être installée.

 

Pour répondre à ces deux problématiques un code source est généralement livré avec un exécutable "configure" et un Makefile.

  • L'exécutable configure vérifie que tout le nécessaire est présent (les librairies nécessaire à la compilation, le compilateur...).
  • Le Makefile déclenche une compilation complète.

Comme un code source est composé de nombreux fichiers, ceux-ci sont rassemblés dans une archive généralement au format tar.gz (tgz) ou tar.bz2 (tbz2).

 

Étape 1 : télécharger l'archive

 

Mettez votre archive de source par exemple dans votre home directory (~).

 

Étape 2 : décompresser l'archive

 

Placez-vous dans le répertoire de l'archive à l'aide de cd et lancer la bonne commande pour décompresser l'archive. Si vous ne savez pas décompresser votre archive, vous pouvez vous référer à cet article.

 

Étape 3 : lire le README

 

En général, dans le répertoire dans lequel vous avez décompressé les source figure un fichier README que vous devriez lire, par exemple à l'aide de votre éditeur texte favori.

 

Étape 4 : vérifier que tout est présent

 

À l'aide de la commande cd, placez-vous dans le répertoire de source et tapez la commande :

 

./configure

 

S'il manque des librairies ou le compilateur, installez-les grâce à votre gestionnaire de paquets (par exemple aptitude sous Debian).

  • Pour compiler, on utilise généralement gcc.
  • Pour compiler un programme dépendant d'une librairie "toto", on installe le paquet libtoto-dev (ou nom proche) ou libtoto-devel. Les distributions basées sur Debian (utilisant APT) utilisent la première convention de notation, les distributions basées sur  RPM utilise la seconde.

Étape 5 : lancer la compilation

 

Généralement il suffit de lancer la commande :

 

make

 

Si une erreur de survient il s'agit d'une erreur de compilation. Vérifiez que vous avez bien installé toutes les librairies requises (étape 4). Si tout a été installé, il se peut également que le code que vous avez récupéré ne compile pas. Copiez-collez le message d'erreur de compilation dans un moteur de recherche pour voir comment régler ce problème.

Étape 6 : installer l'application

 

Cette commande est la seule qui requiert des droits root, car l'application sera probablement déployée dans /usr/local

 

make install

 

Compiler un noyau

 

Le noyau Linux est la couche logicielle qui permet d'interfacer le système d'exploitation avec le matériel. Il peut être complété après avoir démarré par des morceaux appelés modules. Ces modules ont été compilé pour un noyau bien spécifique. Le noyau Linux est également codé en langage C.


Étape 1 : installer le nécessaire pour compiler

 

Installez de quoi compiler votre noyau. Par exemple sous Debian, tapez en root :

 

aptitude update

aptitude safe-upgrade

aptitude install gcc linux-source-2.6 kernel-package make bzip2 libncurses5-dev libc6-dev zlib1g-dev

Étape 2 : récupérer les sources de noyau

Sous Debian (ou une distribution qui en dérive)

 

Sous Debian les sources sont mis à disposition via les paquets "linux-source-...". Ce paquet ne vous servira que si vous souhaitez compiler votre propre noyau (sous Debian on utilisera make-kpkg). C'est une tâche que l'on effectue rarement, en général on utilise directement un noyau compilé via les paquets "linux-image...".

 

cd /usr/src

ls # relevez la dernière version de source installée, dans ce qui suit, on suppose que c'est la version 2.6.32

tar xjvf linux-source-2.6.32.tar.bz2

rm /usr/src/linux

ln -s /usr/src/linux-source-2.6.32 /usr/src/linux

cd /usr/src/linux

 

Si vous décidez d'adopter cette approche, passez à l'étape 3.

Quelle que soit la distribution

 

Vous pouvez récupérer des sources de noyau sur www.kernel.org. Prenez la version sur ce site (avec votre navigateur ou avec wget par exemple) :

 

cd

wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.34.1.tar.bz2

mv linux-2.6.34.1.tar.bz2 /usr/src

tar xjvf linux-2.6.34.1.tar.bz2rm /usr/src/linux

ln -s /usr/src/linux-source-2.6.32 /usr/src/linux

cd /usr/src/linux

 

Étape 3 : préparer la compilation

 

À présent il va falloir dire ce que vous voulez mettre en module et dans le kernel. Cette étape permet de générer le fichier /usr/src/linux/.config qui précise quoi compiler et comment (dans le module ou dans le kernel). Si débutez, prenez le temps de lire la section "en partant d'une configuration existante".

En partant de rien

 

 Si vous savez comment faire, lancez directement :

 

make menuconfig

 

Choisissez dans les menus ce qui doit être mis dans le kernel () ou en module () avec la touche espace. Si vous oubliez des choses "importantes", il y a de fortes chances que votre noyau plante quand vous redémarrerez dessus (kernel panic). Dans le doute, voici comment procéder :

  • quand quelque chose est déjà activé dans le kernel et que vous ne savez pas à quoi il sert, laissez le en tant que tel,
  • quand quelque chose est déjà activé et vous paraît inutile, ne le compilez pas (),
  • quand quelque chose est désactivé, laissez le en tant que tel à moins que vous soyiez persuadé d'en avoir besoin.

A priori si vous activez un minimum de fonctionnalité ( ou ), votre noyau sera plus léger (donc légèrement plus performant) et beaucoup plus rapide à compiler.

 

En partant d'une configuration existante

 

Si la première fois que vous compilez un noyau, il peut être intéressant de repartir d'un .config existant. Vous pouvez en trouver un dans les paquet qui fournit les headers de ce kernel. Exemple :

 

aptitude install linux-headers-2.6

rm /usr/src/linux

ln -s /usr/src/linux-source-2.6.32 /usr/src/linux

cp /usr/src/linux-headers-2.6.32/.config /usr/src/linux

cd /usr/src/linux

make oldconfig

 

Le principe est le même si vous avez déjà compilé un noyau avec d'autres sources : récupérer le fichier .config et copiez le dans le répertoire de source de noyau à compiler.

 

L'avantage de "make oldconfig" est qu'il ne pose que des questions par rapport aux différences entre une version antérieure de noyau et celle que vous compilez. Le gain de temps est donc considérable si vous avez fait le travail proprement la première fois. Lorsque "make oldconfig" vous pose demande s'il doit compiler une fonctionnalité supplémentaire, vous pouvez généralement répondre non si votre ancien noyau fonctionnait correctement. Ne répondez oui que si cette fonctionnalité semble en rapport avec votre matériel.

 

Étape 4 : compilation et installation

Sous Debian (ou une distribution qui en dérive)

 

Nous allons compiler le noyau et le mettre dans un paquet Debian. Ceci permet d'installer et désinstaller très facilement le noyau. De plus le boot loader est automatiquement corrigé quand on installe ou désinstalle un noyau via un tel paquet.

 

make-kpkg clean

make-kpkg --initrd kernel_image

ls /usr/src

dpkg -i /usr/src/linux-image-2.6.32_2.6.32-10.00.Custom_i386.deb

 

Sinon

 

Tout dépend la manière dont vous voulez compiler votre noyau. Il existe deux manière de faire :

  • avec une bzImage : les fonctionnalités nécessaires au démarrage ne doivent pas être mises en modules (typiquement le support du scsi, de l'ide, du raid etc... si vous en avez besoin pour démarrer).
  • avec une initrd : les fonctionnalités nécessaires au démarrage peuvent être mises en modules.

 

Vous pouvez vous référer à ce lien pour voir comment faire :

http://www.digitalhermit.com/linux/Kernel-Build-HOWTO.html#MAKE-IMAGE

 

Compiler un module

 

Vous serez peut-être amené à compiler un ou plusieurs modules si votre noyau ou les modules fournis par défaut ne suffisent pas à prendre en charge votre matériel.

 

Le principe pour compiler un module est similaire à celui utiliisé pour compiler une application. Il suffit de disposer des sources du modules et de ce dont il dépend (en l'occurrence, le noyau Linux avec lequel le module doit fonctionner). De la même façon qu'on n'est pas obligé d'avoir les sources complètes d'une librairie pour compiler une application qui en dépend (seuls ses headers et le binaire de la librairie (.a ou .so) sont requis), on n'est pas obligé d'avoir les sources complètes du noyau : les headers et le noyau en question suffisent.

 

Enfin, pour qu'un module puisse se charger avec succès avec un noyau, ce noyau et ce modules doivent avoir été compilés avec le même compilateur (et la même version de compilateur). Vous pouvez retrouver le compilateur utilisé avec la commande :

 

cat /proc/version

 

Voici les deux cas de figure qui se présentent à vous.

  • Vous n'avez pas compilé votre noyau. Sous Debian, les headers sont mis à disposition dans les paquets "linux-headers-..." et les noyaux via les paquets "linux-image-...". Ce couple de paquet est donc suffisant pour préparer un module. Dans ce cas, inutile d'installer le paquet "linux-source-...".
  • Vous avez compilé votre propre noyau. Pour ce faire vous avez par exemple avec le paquet "linux-source-...." ou récupéré des sources téléchargés depuis kernel.org et pointées par le lien symbolique /usr/src/linux. Dans ce cas, inutile d'installer les paquets "linux-headers-..." et "linux-image-...".

 

Si vous êtes sous Debian on cherche en priorité compiler un module avec module-assistant (quand celui-ci est disponible dans module-assistant). Sinon, un module se compile de manière similaire à une application (via un makefile qui va appeler en cascade gcc).

 


.
X
 
 
 
 
 

You havecharacters left.