• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 23/12/2019

Cross-compilez votre premier « Bonjour le monde »

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Commencez avec le classique "Bonjour le monde"

Vous allez maintenant créer puis compiler un programme C, le grand classique qui affiche "Bonjour le monde" à l'écran, afin de valider l'installation de votre chaîne de compilation croisée.

Commencez par créer un dossier qui contiendra les différents exemples que l'on utilisera lors de ce cours, et rendez-vous dans ce dossier.

cd ~/Development-tools/
mkdir examples
cd examples

 À l'aide de votre éditeur favori, créez un nouveau fichier hello.c qui contiendra le code suivant :

#include<stdio.h>
int main(int argc, char* argv[])
{
printf("Bonjour le monde\n");
return 0;
}

Compilez cet exemple

Commencez par compiler ce programme avec la chaîne de compilation classique fournie pour votre distribution/plateforme. Pour cela, utilisez le compilateur C gcc pour générer un exécutable hello-x64 à partir du code source hello.c.

gcc -Wall -o hello-x64 hello.c

Vérifiez ensuite à l'aide de la commande ls que votre dossier contient un fichier exécutable hello-x64.

ls -lh

Vous pouvez ensuite générer un exécutable pour l'architecture ARM en utilisant la chaîne de compilation croisée que vous avez installée précédemment. Pour cela, nous utilisons la commande arm-linux-gnueabihf-gcc, et nous nommons l'exécutable obtenu hello-arm.

arm-linux-gnueabihf-gcc -Wall -o hello-arm hello.c

 Vérifiez ensuite à l'aide de la commande ls que votre dossier contient un fichier exécutable hello-arm.

ls -lh

Voila ce que vous devez obtenir dans votre terminal :

Compilez et Cross-compilez
Compilez et cross-compilez "Bonjour le monde"

Étudiez les exécutables obtenus

Vous disposez de deux exécutables, hello-x64 et hello-arm, obtenus à partir du même code C source hello.c. Pour étudier les différences entre ces deux fichiers binaires, utilisez la commande file.

file hello-x64
file hello-arm

Cette commande permet d'afficher un résumé des caractéristiques d'un fichier au format binaire. Dans le cas d'un exécutable, cette commande affiche le nom de l'exécutable analysé (et reconnu) suivi de ":", puis différentes informations séparées par une ",". Voici la liste (non exhaustive) des informations retournées :

  • une indication sur le type de fichier — dans notre cas, ELF (Executable and Linkable Format), qui indique qu'il s'agit d'un exécutable — la représentation sur 32 ou 64 bits, l'endianness (LSB ou MSB), le type d'exécutable (shared object ou executable) ;

  • la machine cible — il s'agit donc de l'architecture pour laquelle le fichier a été compilé. Dans notre cas, x86-64 ou ARM

  • l'ABI (Application Binary Interface) compatible, qui indique avec quel système d'exploitation l'exécutable est compatible pour les appels bas niveau ;

  • le type de liaison, statique (static) ou dynamique (dynamically linked) ;

  • le programme permettant d'interpréter cet exécutable ;

  • la version du noyau compatible avec cet exécutable ;

  • (optionnel) l'empreinte sha1 de l'exécutable ;

  • not stripped indique que les symboles n'ont pas été enlevés de l'exécutable avec la commande strip.

Analyse des deux exécutables
Analyse des deux exécutables

Vous êtes donc en présence de deux exécutables au format ELF, le format classique des exécutables sous GNU/Linux. Le fichier hello-x64 peut être exécuté sur plateforme x86 64 bits sur un système d'exploitation de type Unix SYSV (comme GNU/Linux). Le fichier hello-arm peut être exécuté sur plateforme ARM 32 bits sur un système d'exploitation de type Unix SYSV.

Vous pouvez maintenant tenter d'exécuter ces programmes.

./hello-x64
./hello-arm

Comme vous pouvez le remarquer, l'exécutable au format x86-64 fonctionne correctement sur votre machine, alors que l'exécutable au format ARM renvoie une erreur indiquant que le format est incorrect. En effet, la structure ELF du programme hello-arm indique que l'exécutable est compatible uniquement avec une plateforme ARM

Exécution de
Exécution de "Bonjour le monde"

Testez maintenant avec de l'assembleur

Terminons avec la version assembleur de "Bonjour le monde", afin d'illustrer les différences entre les architectures x86 et ARM au niveau assembleur.

Créez un fichier hello-x64.S et ajoutez le code assembleur suivant. Ce code permet, comme précédemment, d'afficher la chaîne "Bonjour le monde" sur la sortie standard. Pour ce faire, nous utilisons l'appel système SYS_write qui vous permettra d'écrire une chaîne de caractères sur la sortie standard. Sur architecture x86, nous utilisons l'instruction assembleur int, pour exécuter un appel système en plaçant les paramètres d'appel dans les registres. Pour effectuer l'équivalent d'un printf en C, les registres doivent contenir : 

  • eax : numéro de l'appel système à appeler, ici 4 pour SYS_write ;

  • ebx : numéro du description de fichier dans lequel écrire, 1 pour la sortie standard ;

  • ecx : adresse de la chaîne de caractères à afficher ;

  • edx : nombre de caractères à écrire, ici la taille de "Bonjour le monde\n".

Le deuxième appel à int est l'équivalent d'un exit(0) en C. Pour cela, le registre eax doit contenir 1 (appel de SYS_exit), et le registre ebx contient le code de retour (0).

.text
.global _start
_start:
movl $len,%edx
movl $msg,%ecx
movl $1,%ebx
movl $4,%eax
int $0x80
movl $0,%ebx
movl $1,%eax
int $0x80
.data
msg:
.ascii "Bonjour le monde!\n"
len = . - msg

Vous pouvez maintenant tester ce code en générant un fichier objet à l'aide de la commande as (fournit avec gcc), puis en générant l'exécutable avec la commande ld (phase d'édition des liens).

as -o hello-x64.o hello-x64.S
ld -o hello-x64 hello-x64.o
./hello-x64

Créez un fichier hello-arm.S et ajoutez le code assembleur suivant. Ce code est l'équivalent du code précédent, mais pour architecture ARM. Vous pouvez remarquer que les deux codes ont la même structure globale, mais ils diffèrent. En effet, le langage machine, et donc la syntaxe assembleur de ces deux architectures, sont incompatibles. Sur ARM, pour effectuer un appel système, nous utilisons l'instruction svc (supervisor call) qui est l'équivalent de int. De même, les registres n'ont pas le même nom. Dans notre cas, nous utilisons (r0,r1,r2,r7) à la place de (ebx,ecx,edx,eax).

.text
.global _start
_start:
mov r0, #1
adr r1, msg
mov r2, #len
mov r7, #4
svc #0
mov r0, #0
mov r7, #1
svc #0
msg:
.ascii "Bonjour le monde\n"
len = . - msg

Vous pouvez générer l'exécutable à partir de ce code source, en utilisant les commandes as et ld fournies avec votre chaîne de compilation croisée.

arm-linux-gnueabihf-as -o hello-arm.o hello-arm.S
arm-linux-gnueabihf-ld -o hello-arm hello-arm.o

 Finalement, étudiez l'exécutable obtenu avec la commande file.

file hello-arm

Comme précédemment, l'exécutable est au format ELF pour plateforme ARM 32 bits. Notez que l'exécutable obtenu est statique et n'est donc plus dépendant d'une librairie système.  En effet, dans l'exemple précédent, nous utilisions la fonction printf qui est une fonction de la glibc. Dans cet exemple, nous utilisons directement l'appel système write pour afficher "Bonjour le monde" sur la sortie standard.

Compilation de hello.S
Compilation de hello.S
Exemple de certificat de réussite
Exemple de certificat de réussite