Compilation croisée pour Raspberry Pi

Introduction

Le Raspberry Pi est un nano-ordinateur monocarte à processeur ARM conçu par le créateur de jeux vidéo David Braben, dans le cadre de sa fondation Raspberry Pi.

Le Raspberry Pi (B et B+) possède un processeur ARM11 à 700 MHz. Il inclut 2 ou 4 ports USB, un port RJ45 et 512 Mo de mémoire vive. Le Raspberry Pi 2 est équipé d’un processeur Broadcom BCM2836, quatre cœurs ARMv7 à 900 MHz, accompagné de 1 Go de RAM. Il possède les mêmes dimensions et connectiques que le modèle B+. La Raspberry Pi 3 dispose d’un processeur Broadcom BCM2837 64 bits à quatre cœurs ARM Cortex-A53 à 1,2 GHz et d’une puce Wifi 802.11n et Bluetooth 4.1 intégrée.

Chaîne de compilation croisée

Une chaîne de compilation (toolchain) désigne l’ensemble des paquets utilisés dans le processus de compilation d’un programme, pour un processeur donné. Le compilateur n’est qu’un élément de cette chaîne, laquelle varie selon l’architecture matérielle cible. Une chaîne de compilation croisée est une chaîne compilée pour fonctionner sur l’architecture de processeurs de la machine hôte, mais qui va compiler des logiciels pour une architecture cible différente. La compilation croisée fait donc référence aux chaînes de compilation capables de traduire un code source en code objet dont l’architecture processeur diffère de celle où la compilation est effectuée. Ces chaînes sont principalement utilisés en informatique industrielle et dans les systèmes embarqués.

Compilation croisée C/C++ pour Raspberry Pi

Pour une machine 64 bits (Ubuntu 12.04) :

$ wget https://s3.amazonaws.com/RTI/Community/ports/toolchains/raspbian-toolchain-gcc-4.7.2-linux64.tar.gz

$ tar xvzf raspbian-toolchain-gcc-4.7.2-linux64.tar.gz

$ export PATH=$(pwd)/raspbian-toolchain-gcc-4.7.2-linux64/bin:$PATH

$ echo $PATH

$ arm-linux-gnueabihf-gcc -v
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabihf-gcc
COLLECT_LTO_WRAPPER=/home/tv/Téléchargements/raspberry-pi-cross-compilation/raspbian-toolchain-gcc-4.7.2-linux64/bin/../libexec/gcc/arm-raspbian-linux-gnueabi/4.7.2/lto-wrapper
Target: arm-raspbian-linux-gnueabi
Configured with: /home/rticonnext/rti_workspace/toolchains/tmp/.build/src/gcc-4.7.2/configure --build=x86_64-build_unknown-linux-gnu --host=x86_64-build_unknown-linux-gnu --target=arm-raspbian-linux-gnueabi --prefix=/home/rticonnext/rti_workspace/toolchains/raspbian-toolchain-gcc-4.7.2-linux64 --with-sysroot=/home/rticonnext/rti_workspace/toolchains/raspbian-toolchain-gcc-4.7.2-linux64/arm-raspbian-linux-gnueabi/sysroot --enable-languages=c,c++ --with-float=hard --with-pkgversion='crosstool-NG 1.17.0' --with-bugurl=http://community.rti.com --enable-__cxa_atexit --disable-libmudflap --disable-libgomp --disable-libssp --disable-libquadmath --disable-libquadmath-support --with-gmp=/home/rticonnext/rti_workspace/toolchains/tmp/.build/arm-raspbian-linux-gnueabi/buildtools --with-mpfr=/home/rticonnext/rti_workspace/toolchains/tmp/.build/arm-raspbian-linux-gnueabi/buildtools --with-mpc=/home/rticonnext/rti_workspace/toolchains/tmp/.build/arm-raspbian-linux-gnueabi/buildtools --with-ppl=/home/rticonnext/rti_workspace/toolchains/tmp/.build/arm-raspbian-linux-gnueabi/buildtools --with-cloog=/home/rticonnext/rti_workspace/toolchains/tmp/.build/arm-raspbian-linux-gnueabi/buildtools --with-libelf=/home/rticonnext/rti_workspace/toolchains/tmp/.build/arm-raspbian-linux-gnueabi/buildtools --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++ -lm -L/home/rticonnext/rti_workspace/toolchains/tmp/.build/arm-raspbian-linux-gnueabi/buildtools/lib -lpwl' --enable-threads=posix --enable-target-optspace --disable-nls --disable-multilib --with-local-prefix=/home/rticonnext/rti_workspace/toolchains/raspbian-toolchain-gcc-4.7.2-linux64/arm-raspbian-linux-gnueabi/sysroot --enable-c99 --enable-long-long
Thread model: posix
gcc version 4.7.2 (crosstool-NG 1.17.0)

La chaîne de compilation croisée est prête à l’emploi.

Test en C

On commence par écrire le célèbre programme Hello world.

Remarque : Hello world (« bonjour le monde ») sont les mots traditionnellement écrits par un programme informatique simple dont le but est de faire la démonstration rapide d’un langage de programmation ou le test d’un compilateur.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("Hello world\n");
    return 0;
}

Tout d’abord, on va tester sur la machine de développement :

$ gcc hello-world.c 

$ file a.out 
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x75af707611b680c50dc4abd819b8040d767620ae, not stripped

$ ./a.out 
Hello world

Maintenant, on passe à la compilation croisée :

  • à lien dynamique :
$ arm-linux-gnueabihf-gcc hello-world.c 

$ file a.out 
a.out: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, not stripped
  • à lien statique :
$ arm-linux-gnueabihf-gcc hello-world.c -static
$ file a.out 
a.out: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, not stripped

Pour finir, il suffit de déployer l’application fabriquée sur la cible (la Raspberry Pi) et de l’exécuter :

$ scp a.out pi@xxx.xxx.xxx.xxx:/home/pi

$ ssh pi@xxx.xxx.xxx.xxx

$ ./a.out 
Hello world

Test en C++

On commence par écrire le célèbre programme Hello world.

Remarque : Hello world (« bonjour le monde ») sont les mots traditionnellement écrits par un programme informatique simple dont le but est de faire la démonstration rapide d’un langage de programmation ou le test d’un compilateur.

#include <iostream>

using namespace std;

int main()
{
    cout << "Hello world\n";
    return 0;
}

Maintenant, on passe à la compilation croisée :

  • à lien dynamique :
$ arm-linux-gnueabihf-g++ hello-world.cpp
  • à lien statique :
$ arm-linux-gnueabihf-g++ hello-world.cpp -static

Pour finir, il suffit de déployer l’application fabriquée sur la cible (la Raspberry Pi) et de l’exécuter :

$ scp a.out pi@xxx.xxx.xxx.xxx:/home/pi

$ ssh pi@xxx.xxx.xxx.xxx

$ ./a.out 
Hello world

Compilation croisée Qt 4 pour Raspberry Pi

Il est évidemment possible de compiler une application Qt directement sur la Raspberry Pi (la machine cible). Le processus de fabrication sera tout simplement plus long que sur votre machine hôte de développement.

Il faut tout d’abord une chaîne de compilation croisée pour la Rasperry Pi fonctionnelle. Il est possible de l’installer à partir du github officiel pour la raspbian.

On télécharge la chaîne de compilation croisée raspbian officielle à partir de github :

$ sudo apt-get install build-essential git

$ git clone git://github.com/raspberrypi/tools.git

$ cd ./tools/arm-bcm2708

Pour une machine 64 bits, la chaîne se trouve dans gcc-linaro-arm-linux-gnueabihf-raspbian-x64.

$ export PATH=$(pwd)/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH

$ arm-linux-gnueabihf-gcc -v

On va maintenant préparer sa machine hôte (le PC) pour pouvoir fabriquer des applications Qt 4 en utilisant SSHFS :

$ sudo apt-get install sshfs

Il faut ajouter votre compte utilisateur (ici tv) dans le groupe fuse :

$ sudo usermod -a -G fuse tv

Vous devez maintenant vérifier si votre chaîne de compilation Qt 4 est aussi installée sur le PC. Un programme Qt nécessite le préprocesseur moc pour les mécanismes propres à Qt comme les signaux et les slots :

$ moc-qt4 -v
Qt Meta Object Compiler version 63 (Qt 4.8.1)

Sinon, pour installer le kit de développement Qt 4 :

$ sudo apt-get install libqt4-dev-bin

On va ensuite monter la racine du système de fichiers de la Raspebbry Pi sur la machine hôte :

$ mkdir $HOME/rpi

$ sudo sshfs pi@xxx.xxx.xxx.xxx:/ $HOME/rpi -o transform_symlinks -o allow_other

$ ls ~/rpi/
bin  boot  dev  etc  home  lib  lost+found  media  mnt  opt  proc  root  run  sbin  selinux  srv  sys  tmp  usr  var

$ sudo ln -s  $HOME/rpi/usr/lib/arm-linux-gnueabihf/ /usr/lib/arm-linux-gnueabihf

$ sudo ln -s $HOME/rpi/lib/arm-linux-gnueabihf/ /lib/arm-linux-gnueabihf

$ ls -ld /lib/arm-linux-gnueabihf
lrwxrwxrwx 1 root root 37 mars   6 06:57 /lib/arm-linux-gnueabihf -> /home/tv/rpi/lib/arm-linux-gnueabihf/

$ ls -ld /usr/lib/arm-linux-gnueabihf
lrwxrwxrwx 1 root root 41 mars   6 06:56 /usr/lib/arm-linux-gnueabihf -> /home/tv/rpi/usr/lib/arm-linux-gnueabihf/

Remarque : L’option transform_symlinks est nécessaire pour que les liens symboliques absolus soient transformés en relatifs. De cette façon, un lien symbolique sur le système de fichiers de la Raspebbry Pi pointera vers le fichier approprié sur le système de fichiers de la Raspebbry Pi et non sur un fichier du même nom sur le système hôte.

Maintenant, il faut créer un fichier de configuration Qt adaptée pour la compilation croisée ARM en spécifiant VOTRE chemin du point de montage RPI dans la variable ROOTFS :

$ sudo mkdir /usr/share/qt4/mkspecs/linux-arm-g++

$ sudo vim /usr/share/qt4/mkspecs/linux-arm-g++/qmake.conf
#
# qmake configuration for linux-arm-g++
#
 
CROSS_COMPILE           = arm-linux-gnueabihf
ROOTFS                  = /home/tv/rpi
MAKEFILE_GENERATOR      = UNIX
TARGET_PLATFORM         = unix
TEMPLATE                = app
CONFIG                 += qt warn_on release incremental link_prl gdb_dwarf_$
QT                     += core gui
QMAKE_INCREMENTAL_STYLE = sublib
  
include(../common/linux.conf)
include(../common/gcc-base-unix.conf)
include(../common/g++-unix.conf)

QMAKE_CXX               = $$CROSS_COMPILE-g++
QMAKE_LINK              = $$CROSS_COMPILE-g++
QMAKE_LINK_SHLIB        = $$CROSS_COMPILE-g++
QMAKE_AR                = $$CROSS_COMPILE-ar cr
QMAKE_OBJCOPY           = $$CROSS_COMPILE-objcopy
QMAKE_STRIP             = $$CROSS_COMPILE-strip
QMAKE_LFLAGS_RELEASE    = -Wl,-O1
QMAKE_RPATHDIR         += $$ROOTFS/lib/arm-linux-gnueabihf
QMAKE_RPATHDIR         += $$ROOTFS/usr/lib/arm-linux-gnueabihf
QMAKE_INCDIR            = $$ROOTFS/usr/include
QMAKE_INCDIR_QT         = $$ROOTFS/usr/include/qt4
QMAKE_RPATHDIR          = $$ROOTFS/lib/arm-linux-gnueabihf
QMAKE_RPATHDIR         += $$ROOTFS/usr/lib/arm-linux-gnueabihf
QMAKE_LIBDIR            = $$ROOTFS/usr/lib
QMAKE_LIBDIR_QT         = $$ROOTFS/usr/lib/arm-linux-gnueabihf
QMAKE_LIBDIR_QT        += $$ROOTFS/lib/arm-linux-gnueabihf
QMAKE_INCDIR_X11        = $$ROOTFS/usr/include
QMAKE_LIBDIR_X11        = $$ROOTFS/usr/lib/arm-linux-gnueabihf
QMAKE_LIBDIR_X11       += $$ROOTFS/lib/arm-linux-gnueabihf
QMAKE_INCDIR_OPENGL     = $$ROOTFS/usr/include
QMAKE_LIBDIR_OPENGL     = $$ROOTFS/usr/lib
   
load(qt_config)

Pour générer un fichier Makefile pour la Raspeberry Pi à partir d’un fichier de projet Qt .pro, il suffira de faire :

$ qmake -spec linux-arm-g++

$ make

Pour déployer son application sur la Raspberry Pi, il suffira de copier l’exécutable dans $HOME/rpi/home/pi.

Pour tester en ssh, il ne faut oublier d’activer l’option -Y (forward X11) quand vous vous connectez.

Glossaire

Système embarqué
Un système embarqué est défini comme un système électronique et informatique autonome, souvent temps réel, spécialisé dans une tâche bien précise. Le terme désigne aussi bien le matériel informatique que le logiciel utilisé. Ses ressources sont généralement limitées. Cette limitation est généralement d’ordre spatial (encombrement réduit) et énergétique (consommation restreinte).
ARM
ARM est une société britannique spécialisée dans le développement d’architectures 32 bits de type RISC. Fondée sous la forme d’une coentreprise par Acorn Computers, Apple Computer (maintenant Apple Inc.) et VLSI Technology, ARM développe également un grand nombre de blocs de propriété intellectuelle (IP). Elle est basée sur un modèle économique particulier de la microélectronique : la conception de propriétés intellectuelles (Intellectual Properties). Ainsi il n’est pas possible d’acheter un processeur ARM comme c’est le cas pour Intel. Les cœurs ARM sont intégrés au sein de systèmes sur puces (SoC) complets. Les cœurs de processeurs ARM sont très présents dans les systèmes embarqués (téléphone mobile, console portable, tablette électronique). Les architectures ARM sont des architectures matérielles RISC 32 bits (ARMv1 à ARMv7) et 64 bits (ARMv8)1 développées par ARM Ltd depuis 1990 et introduites à partir de 1983 par Acorn Computers. Dotés d’une architecture relativement plus simple que d’autres familles de processeurs, et bénéficiant d’une faible consommation, les processeurs ARM sont devenus dominants dans le domaine de l’informatique embarquée, en particulier la téléphonie mobile et les tablettes. Ces processeurs sont fabriqués sous licence par un grand nombre de constructeurs.
Raspbian
Raspbian est un système d’exploitation libre et gratuit fondé sur GNU/Linux/Debian et optimisé pour fonctionner sur un Raspberry Pi. Étant donné les ressources limitées de cet ordinateur, Raspbian utilise des logiciels réputés pour être légers tels que le gestionnaire de fenêtres LXDE et le navigateur Web Midori.
Chaîne de compilation croisée
Une chaîne de compilation (toolchain) désigne l’ensemble des paquets utilisés dans le processus de compilation d’un programme, pour un processeur donné. Le compilateur n’est qu’un élément de cette chaîne, laquelle varie selon l’architecture matérielle cible. Une chaîne de compilation croisée est une chaîne compilée pour fonctionner sur l’architecture de processeurs de la machine hôte, mais qui va compiler des logiciels pour une architecture cible différente. La compilation croisée fait donc référence aux chaînes de compilation capables de traduire un code source en code objet dont l’architecture processeur diffère de celle où la compilation est effectuée. Ces chaînes sont principalement utilisés en informatique industrielle et dans les systèmes embarqués.
ELF
ELF (Executable and Linkable Format) est un format de fichier binaire utilisé pour l’enregistrement de code compilé (objets, exécutables, bibliothèques de fonctions). Aujourd’hui, ce format est utilisé dans la plupart des systèmes d’exploitation de type Unix (GNU/Linux, Solaris, IRIX, System V, BSD), à l’exception de Mac OS X.
SSH
SSH (Secure Shell) est à la fois un programme informatique et un protocole de communication sécurisé. Le protocole de connexion impose un échange de clés de chiffrement en début de connexion. Le protocole SSH a été conçu avec l’objectif de remplacer les différents programmes rlogin, telnet, rcp, ftp et rsh.
SSHFS
SSHFS (Secure shell file system) permet le partage d’un système de fichiers de manière sécurisée en utilisant le protocole SSH. Basé sur le système de fichiers FUSE qui permet à un utilisateur sans privilèges particuliers d’accéder à un système de fichiers sans qu’il soit nécessaire de modifier les sources du noyau, SSHFS permet d’accéder simplement à une branche du système de fichiers distant par l’intermédiaire de ssh. SSHFS utilise le protocole SFTP pour accéder aux fichiers distants. Il permet donc l’accès à tous les serveurs SSH supportant ce protocole sans modification supplémentaire du serveur, ce qui est le cas de la majorité d’entre eux.
Système de fichiers
Un système de fichiers (File System) est une façon de stocker les informations et de les organiser dans des fichiers sur des mémoires de masse comme un disque dur. Une telle gestion des fichiers permet de traiter, de conserver des quantités importantes de données ainsi que de les partager entre plusieurs programmes informatiques. Il offre à l’utilisateur une vue abstraite sur ses données et permet de les localiser à partir d’un chemin d’accès. Un système de fichiers s’installe sur une partition (cf. fdisk). On crée un système de fichiers par une opération de formatage.
Qt
Qt est une API orientée objet et développée en C++ par Qt Development Frameworks, filiale de Digia. Qt offre des composants d’interface graphique (widgets), d’accès aux données, de connexions réseaux, de gestion des fils d’exécution, d’analyse XML, etc… Par certains aspects, elle ressemble à un framework lorsqu’on l’utilise pour concevoir des interfaces graphiques ou que l’on conçoit l’architecture de son application en utilisant les mécanismes des signaux et slots par exemple.