Teo-CD
8614fd7192
Image location is not ideal, could not make colocation or static images work correctly. Will need to investigate/fix in the future
319 lines
19 KiB
Markdown
319 lines
19 KiB
Markdown
+++
|
||
title = "Tenter un portage pour AArch64"
|
||
date = 2021-05-23
|
||
[taxonomies]
|
||
categories = ["Software"]
|
||
tags = ["Arm","aarch64","QEMU","Emulation","Virtualisation"]
|
||
+++
|
||
|
||
|
||
Je me suis dit qu'essayer de porter une application pour l'architecture AArch64 (Armv8/Arm 64 bits) pourrait être intéressant, voilà ce que j'en ai appris.
|
||
|
||
<!-- more -->
|
||
|
||
Avant-propos : Je suis actuellement employé par Arm mais ces recherches et expérimentations ont
|
||
été réalisées sur mon temps libre, sans incitation ni lien avec mon employeur.
|
||
|
||
Il faut parfois trouver de quoi s'occuper, et ce week-end s'annonçait ainsi. Cependant en explorant la liste
|
||
des exécutables et paquets disponibles d'un logiciel, j'ai remarqué un absent : l'architecture Arm.
|
||
Ne programmant pas beaucoup ces derniers temps et n'ayant jamais vraiment de fait de cross-compilation
|
||
(la compilation pour un système ayant une architecture différente), je me suis dit que c'était l'occasion
|
||
d'expérimenter !
|
||
|
||
|
||
# La cible
|
||
## Présentation
|
||
|
||
Le logiciel en question est [Cockatrice](https://cockatrice.github.io/). Il permet de jouer des
|
||
parties de Magic The Gathering® de façon très flexible, en ligne comme en local, gérer des decks
|
||
et probablement moult autres choses. C'est un projet Open Source à l'aspect certes un peu sommaire
|
||
mais qui reste un excellent moyen de jouer au jeu sur l'ordinateur.
|
||
|
||
Je ne l'ai pas beaucoup utilisé moi-même mais mon groupe d'ami l'utilise extensivement depuis les confinements.
|
||
|
||
|
||
## Pourquoi Cockatrice ?
|
||
|
||
Un des premiers point décisif est que c'est un projet Open Source : il aurait été très compliqué de porter
|
||
un logiciel propriétaire, en tous cas de façon propre et fiable.
|
||
|
||
Le deuxième point décisif est la simplicité apparente du projet : seulement deux dépendances, elles-aussi
|
||
Open Source, un langage et un système de compilation que je connais bien : C++ et CMake.
|
||
|
||
Pour d'autres points qui ont validé le choix est qu'il était déjà multiplateforme (Windows,Mac,Linux;32,64 bits)
|
||
et que mes amis l'utilisant j'avais accès à des ~~cobayes~~ testeurs.
|
||
|
||
|
||
Après un `git clone` et une compilation de test sur ma machine x86_64, il était temps de s'y mettre.
|
||
|
||
|
||
# Cross-compilation
|
||
|
||
## Mise en place
|
||
|
||
Tout d'abord il me fallait la suite de compilation complète pour AArch64. Après une recherche rapide
|
||
parmi les paquets Arch Linux, j'ai simplement installé les paquets `aarch64-linux-gnu-{gcc,binutils}`.
|
||
|
||
Un rapide test a confirmé que je pouvais compiler du C++ pour AArch64 :
|
||
|
||
```
|
||
a.out: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0
|
||
```
|
||
|
||
Les deux dépendances principales de Cockatrice sont [Protocol Buffers](https://developers.google.com/protocol-buffers/)
|
||
(protobuf) et [Qt5](https://www.qt.io/). Pour être sûr qu'elles pourraient être compilées, j'ai vérifié qu'il existait
|
||
déjà des paquets Arm C'était le cas, je les ai donc téléchargé pour les cross-compiler elles-aussi,
|
||
en ayant besoin et ne trouvant pas d'archive toute faite.
|
||
|
||
|
||
## Protocol Buffers
|
||
|
||
En suivant les [instructions de compilation](https://github.com/protocolbuffers/protobuf/tree/master/src)
|
||
pour protobuf il s'est avéré que le projet ne dépendait pas d'autres bibliothèques et avait un système
|
||
de compilation relativement classique (proposant même un CMake), j'ai donc décidé de commencer par là.
|
||
|
||
|
||
### CMake
|
||
|
||
Étant plus familier avec CMake j'ai décidé de regarder de ce côté, malgré le README de cette partie
|
||
notant explicitement qu'il était présent pour Windows...
|
||
|
||
> This directory contains CMake files that can be used to build protobuf with MSVC on Windows. You can build the project from Command Prompt and using an Visual Studio IDE.
|
||
|
||
Mon intuition était que j'aurais principalement à changer les variables indiquant à CMake quel compilateur
|
||
utiliser pour qu'il choisisse celui avec le préfixe `aarch64-linux-gnu-` plutôt que le natif. La
|
||
[documentation CMake](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling)
|
||
a confirmé ce sentiment en détaillant d'autres opérations nécessaires pour que la compilation se passe
|
||
bien, comme ne sélectionner que les bibliothèques correspondant à la cible. La documentation présente
|
||
un exemple de fichier de toolchain, mais plutôt limité. Au cas où, j'ai utilisé le fichier proposé par
|
||
[ce blog](https://kubasejdak.com/how-to-cross-compile-for-embedded-with-cmake-like-a-champ#ready-to-use-toolchain-file)
|
||
en remplaçant les `${BAREMETAL_ARM_TOOLCHAIN_PATH}arm-none-eabi-gcc${CMAKE_EXECUTABLE_SUFFIX}`
|
||
par simplement `aarch64-linux-gnu-gcc` (pour l'exemple de GCC) et en commentant toutes les options de
|
||
compilation, en les gardant au cas où j'en aurais besoin. (Le blog explique de façon claire les raisons
|
||
des différentes options et l'alternative au fichier de toolchain)
|
||
|
||
À ma grande surprise, CMake était bien content de préparer la compilation avec mon cross-compilateur !
|
||
|
||
Mais le compilateur, lui, n'était pas de cet avis. Comme dit précédemment, le CMake de Protocol Buffer
|
||
est principalement destiné à compiler sous Windows et manquait probablement des configurations nécessaires
|
||
à Linux : la compilation ne fonctionnait pas. Après quelques recherches infructueuses, j'ai enfin suivi la
|
||
méthode décrite dans le [README](https://github.com/protocolbuffers/protobuf/blob/master/cmake/README.md)
|
||
avec autotools.
|
||
|
||
|
||
### Autotools
|
||
|
||
Contrairement à CMake, je n'ai jamais écrit de écrit de projet avec autotools ni configuré au delà de
|
||
ce qui est décrit dans des instructions d'installation, je n'avais donc pas de point de départ.
|
||
|
||
Après un peu de recherche un commentaire sur Stack Overflow m'a pointé vers [cette réponse](https://stackoverflow.com/a/21992287)
|
||
détaillant les différentes options et leur sens. Cependant le point de vue de la réponse est un peu perturbant,
|
||
et j'ai donc pensé qu'il me fallait utiliser l'option `--target` : ma machine devant être l'hôte et
|
||
l'architecture Arm la cible. Cependant, dans les logs du script de configuration j'ai noté qu'il allait
|
||
utiliser le compilateur natif. [Une autre réponse](https://stackoverflow.com/a/41755131) plus bas m'a
|
||
pointé dans la bonne direction pour comprendre et le faire fonctionner.
|
||
|
||
En effet, il semblerait qu'autotools se place du point de vue d'un compilateur. L'option `--build`
|
||
permettant de spécifier la machine sur laquelle la compilation *du projet* se fait, `--host` la machine
|
||
sur laquelle le compilateur produit sur la machine `--build` sera utilisé et `--target` la machine
|
||
sur laquelle les produits du compilateur seront exécutés.
|
||
Dans un cas général, `--target` n'a pas l'air d'être très utile.
|
||
|
||
Quelques exemples pour la clarté :
|
||
- `build=host=target=x86` : sur une machine x86, compiler une toolchain qui va produire de l'x86 depuis une machine x86 elle aussi;
|
||
- `build=host=x86, target=arm` : sur une machine x86, compiler une toolchain qui va produire de l'Arm depuis une machine x86. C'est comme ça que l'on produit le cross-compilateur `aarch64-linux-gnu-gcc` par exemple;
|
||
- `build=x86, host=target=arm` : sur une machine x86, compiler une toolchain qui va produire de l'Arm depuis une machine Arm. Par exemple, clang pour AArch64 compilé depuis une machine x86.
|
||
|
||
Ici nous sommes dans le troisième cas et non pas le deuxième vu que l'on veut que Protocol Buffers
|
||
tourne sous aarch64. J'ai donc spécifié `--host=aarch64-linux-gnu` ainsi qu'un préfixe d'installation
|
||
avec `--prefix` afin qu'il ne soit pas installé ailleurs dans mon système, remplaçant potentiellement
|
||
la version déjà installée.
|
||
|
||
Un `make` et quelques minutes plus tard... Succès !
|
||
|
||
Une dépendance de compilée avec succès, un bon présage pour la cross-compilation et la moitié du travail
|
||
de fait, passons donc à Qt !
|
||
|
||
|
||
## Qt
|
||
|
||
La documentation de Qt était plus... Tortueuse à naviguer. Dans un premier temps la page sur
|
||
la [compilation pour Linux](https://doc.qt.io/qt-5/linux-building.html) a été utile, cependant celle
|
||
du wiki sur [compiler depuis le dépôt Git](https://wiki.qt.io/Building_Qt_5_from_Git) s'est avéré la
|
||
plus pertinente, listant les dépendances et options utiles.
|
||
|
||
Il semblerait que Qt utilise aussi autotools mais, en regardant les options disponibles, un peu modifié.
|
||
Typiquement pour la cross-compilation, les options précédentes `--host` et compagnie n'étaient pas
|
||
disponibles En cherchant sur Stack Overflow et [la documentation Qt](https://doc.qt.io/qt-5/configure-options.html),
|
||
j'ai trouvé que l'option `-xplatform` (le `x` étant probablement pour "cross") était celle dont j'avais
|
||
besoin. Mais, en mettant le préfixe de mon compilateur...
|
||
|
||
```
|
||
ERROR: Invalid target platform 'aarch64-linux-gnu'.
|
||
```
|
||
|
||
Donc en plus d'avoir des options différentes, elles ont un sens différent ! En creusant un peu plus,
|
||
j'ai trouvé que Qt a une liste précise des plateformes supportées et des toolchains utilisées et demande
|
||
exactement ces plateformes. Elles se trouvent sous `qtbase/mkspecs` et consistent principalement de deux
|
||
fichiers : un pour définir les outils utilisés (`qmake.conf`), un autre pour des
|
||
définitions/configurations (`qplatformdefs.h`).
|
||
|
||
Je pense que pour des cas pas trop éloignés il serait possible de copier une configuration existante et
|
||
l'adapter, cependant je savais que Qt supportait aarch64 donc j'ai préféré chercher de ce côté.
|
||
|
||
Initialement j'avais récupéré la branche 5.5, le minimum demandé par Cockatrice, et n'avais aucune
|
||
plateforme aarch64 disponible Ayant réussi à compiler avec Qt 5.15 disponible sur ma machine, j'ai
|
||
avancé jusqu'à la dernière LTS Open Source de Qt, la 5.12, et y ai trouvé une plateforme adéquate.
|
||
|
||
Avec `-xplatform linux-aarch64-gnu-g++` j'ai pu configurer correctement Qt ! Mais la compilation a bloqué
|
||
immédiatement vu que des dépendances pour aarch64 n'étaient pas disponible La page précédente du wiki
|
||
ainsi que celle des [bibliothèques requises pour Linux](https://doc.qt.io/qt-5/linux-requirements.html)
|
||
donnent les bibliothèques que je devrais installer dans le préfixe aarch64 pour que Qt puisse les trouver
|
||
et compiler. Mais... Qt est un projet assez énorme, comptant de nombreuses dépendances. Bien trop nombreuses
|
||
pour que je les télécharges et installe à la main; nécessaire vu que Arch Linux ne propose que des paquets
|
||
x86_64/x86.
|
||
|
||
Cross-compiler Qt ne semblait pas être la meilleure idée, j'ai décidé d'abandonner la cross-compilation
|
||
et de porter Cockatrice autrement.
|
||
|
||
|
||
# Compilation native
|
||
|
||
Il me restait donc comme option compiler directement depuis Arm pour Arm. La solution la plus simple
|
||
serait de le faire sur du matériel Arm 64 bits, comme une Raspberry Pi (3/4).
|
||
Par chance, j'en possède une ! L'autre solution serait d'émuler un processeur aarch64 sur mon ordinateur
|
||
x86. Ce serait plus lent et plus complexe qu'avec ma Raspberry Pi, nécessitant en plus de faire
|
||
fonctionner un environnement graphique le tout en émulant une architecture différente...
|
||
|
||
|
||
## Émulation
|
||
### QEMU
|
||
|
||
C'est donc la solution que j'ai choisi ! Les justifications étant que ce serait plus enrichissant, et que
|
||
je n'ai pas prévu d'installer une interface graphique dessus pour le moment.
|
||
|
||
La façon la plus simple d'émuler un système aarch64 est d'utiliser [QEMU](https://qemu.org), un
|
||
émulateur Open Source générique permettant de créer des machines virtuelles émulant moult processeurs
|
||
et architectures. La page sur [l'émulation Arm](https://qemu.readthedocs.io/en/latest/system/target-arm.html)
|
||
présente les détails de base sur ce qui est nécessaire, en particulier quelle machine émuler. Le meilleur
|
||
choix pour une machine générique dont j'ai besoin est la machine "[virt](https://qemu.readthedocs.io/en/latest/system/arm/virt.html)".
|
||
|
||
La page du wiki Arch Linux sur [QEMU](https://wiki.archlinux.org/title/QEMU#Creating_new_virtualized_system)
|
||
s'est prouvée très utile pour préparer la machine virtuelle et comprendre le fonctionnement de base.
|
||
|
||
|
||
### Machine virtuelle et compilation
|
||
|
||
J'ai choisi d'utiliser [Arch Linux Arm](https://archlinuxarm.org/), afin de garder la même logique pour
|
||
les paquets que sur ma propre machine et pour avoir une image de taille réduite. (Alors que je vais
|
||
installer un gestionnaire de bureau...)
|
||
Cependant pour une installation générique la page d'insutrctions est plutôt limitée. Mon premier essai n'a
|
||
donc absolument pas réussi. Par chance, j'ai trouvé une [liste d'instruction](https://gist.github.com/thalamus/561d028ff5b66310fac1224f3d023c12)
|
||
originellement produite pour les nouveaux Mac M1 réalisant *exactement* ce que je voulais !
|
||
(À l'émulation graphique près) Le suivre s'est avéré sans peine, et j'ai pu observer le boot se passer
|
||
avec succès !
|
||
|
||
![boot sans console série](/images/Attempting-an-aarch64-port/nographics_boot.png)
|
||
|
||
La procédure standard s'appliquait donc : installer les outils de compilation, les différentes dépendances
|
||
(dont Qt, pas besoin de le compiler ici) et *enfin* lancer la compilation de Cockatrice !
|
||
|
||
Et d'attendre.
|
||
|
||
Un certain temps.
|
||
|
||
En effet comme évoqué plus haut, émuler une architecture différente ralenti pas mal les choses. Par dessus
|
||
cela, je n'ai alloué que deux cœurs de mon ordinateur à QEMU afin de pouvoir continuer à l'utiliser
|
||
en parallèle.
|
||
|
||
Au bout de deux heures, le méfait était accompli : Cockatrice était compilé ! Mais sans interface graphique,
|
||
impossible de tester le fonctionnement.
|
||
|
||
|
||
### Interface graphique
|
||
|
||
En soit, installer et lancer une interface graphique n'est pas le plus compliqué. Le plus souvent,
|
||
il suffit d'installer le paquet adapté pour un gestionnaire de bureau et tout se fait automatiquement.
|
||
Non, ici le problème était autre : comment émuler une interface graphique avec QEMU ? On notera que dans
|
||
la commande proposée par les instructions, on trouve l'option `-no-graphics`. Un bon premier pas :
|
||
l'enlever !
|
||
|
||
![boot console graphique](/images/Attempting-an-aarch64-port/Graphical_login.png)
|
||
|
||
Ok, on a donc une fenêtre QEMU et plus juste un terminal, fantastique. Maintenant, il fallait la transformer
|
||
en un écran pour Linux. Pour cela rien de plus simple d'après la documentation, rajouter une carte graphique
|
||
virtuelle ! La documentation de QEMU recommande `-device virtio-gpu-pci`, avec lequel j'ai pu en effet
|
||
arriver à un TTY normal, comme si j'avais connecté un écran à une machine. Parfait !
|
||
|
||
Moins parfait : le clavier ne répond pas. Dans les options on trouve `-device virtio-keyboard-device`,
|
||
visiblement on utiliserait un clavier virtuel, ce qui peut expliquer que notre clavier réel n'ai pas
|
||
beaucoup d'effet.
|
||
|
||
Après quelques recherches, le meilleur moyen semblait de passer les périphériques USB directement à la
|
||
machine virtuelle. Pour une machine Arm, il faut préciser `-usb -device usb-ehci` pour activer l'USB.
|
||
Ensuite, il y a plusieurs possibilités pour autoriser des périphériques particuliers sur la machine
|
||
virtuelle. La première que j'ai essayé est avec le numéro de bus et de périphérique, mais j'ai fini par
|
||
utiliser les identifiant de vendeur et de produit directement, comme montré sur la [page USB](https://qemu.readthedocs.io/en/latest/system/usb.html)
|
||
de la documentation de QEMU. Par exemple pour mon clavier :
|
||
`-device usb-host,vendorid=0x413c,productid=0x2010`.
|
||
Je suis resté sur ce point pendant bien trop longtemps, sans clavier fonctionnel, à tester différentes
|
||
cartes graphiques virtuelles et autres manipulations. Au final, il me manquait le rajout du périphérique
|
||
usb lui-même : `-device usb-kbd`. Il semblerait que la première option ne fasse qu'exposer le clavier à
|
||
la machine virtuelle, alors que la deuxième permette au système d'exploitation virtuel de le considérer
|
||
correctement, le "brancher".
|
||
Pour la souris, la meilleure option semble être `-device usb-tablet` : elle permet de prendre la souris
|
||
en compte comme un pointeur `x,y` et non pas comme une souris normale, rapportant des déplacements. Vu
|
||
que notre machine virtuelle est bien plus lente que nos déplacements de souris, il est facile de les
|
||
accumuler sans faire exprès et se retrouver avec des déplacements ératiques. En "mode tablette", le curseur
|
||
virtuel se positionnera simplement au même endroit que le curseur réel.
|
||
|
||
Ne restait plus alors qu'à installer et configurer un gestionnaire de bureau pour avoir une interface
|
||
graphique. J'ai choisi KDE, sachant que de nos jours il est plutôt léger tout en étant un des plus
|
||
commun. Je ne me faisais donc pas de soucis sur l'installation et la configuration de base.
|
||
|
||
Pour qu'il soit lancé correctement, j'ai du modifier le `/etc/X11/xinit/xinitrc` pour lancer KDE au
|
||
démarrage de X11, et modifier `/etc/profile` afin que `xinit` soit appelé automatiquement. Le tout,
|
||
encore une fois à l'aide du splendide [Wiki Arch Linux](https://wiki.archlinux.org/title/Xinit).
|
||
|
||
![KDE QEMU desktop](/images/Attempting-an-aarch64-port/KDE.png)
|
||
|
||
Une fois cela fait et finalement en possession d'un bureau émulé pour Arm, je pouvais enfin tester
|
||
si la compilation de Cockatrice avait fonctionné...
|
||
|
||
# Conclusion
|
||
## Résultat
|
||
|
||
![Cockatrice sous Arm émulé](/images/Attempting-an-aarch64-port/Cockatrice_Arm.png)
|
||
|
||
Et elle avait fonctionné ! Le lancement s'est effectué comme après la compilation pour x86, a suivi les
|
||
même étapes, téléchargé et configuré les mêmes éléments. Le chargement de deck fonctionnait, j'ai même
|
||
pu rapidement tester que la connexion à un serveur ainsi que la partie "jeu" en elle-même étaient
|
||
fonctionnelles avec l'aide d'un ami habitué au logiciel. La compilation a donc été un succès !
|
||
|
||
## Réalisation
|
||
|
||
Après ce succès, je suis retourner explorer le dépôt Github de Cockatrice et franchement je ne sais plus
|
||
pourquoi. Peut-être même pour retrouver des liens à mettre dans ce poste. Toujours est-il que, caché sous
|
||
quelques niveaux de navigation, j'ai découvert une page expliquant... [Comment compiler sur une
|
||
Raspberry Pi](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice-(Linux)#raspberry-pi) !
|
||
Ah ! Et bien, oui, en effet : les chances que je rencontre un problème étaient maigres...
|
||
|
||
## Rétrospective et continuation
|
||
|
||
Malgré cette "boutade", l'expérience entière a été très enrichissante et intéressante Quelques moments de
|
||
frustration, mais au final très satisfait d'avoir réussi le challenge initial que de compiler une
|
||
application non-triviale pour Arm depuis ma machine x86, même si elle le supportait déjà, sans paquet
|
||
officiel. J'ai maintenant une meilleure connaissance des options disponibles ainsi que de l'utilisation
|
||
de QEMU, ce qui peut être utile dans de nombreuses situations !
|
||
|
||
Je sais aussi que si je devais le refaire, j'allouerai plus de cœurs à la compilation dans QEMU et
|
||
laisserai faire en allant m'occuper ailleurs, afin de minimiser la durée.
|
||
|
||
Il y a cependant deux choses que j'aimerais encore faire :
|
||
- Essayer d'intégrer la compilation AArch64 à l'intégration continue (CI) de Cockatrice
|
||
- Réaliser un vrai portage demandant du travail. C'est une expérience que j'envisage enrichissante et
|
||
utile d'un point de vue pratique de la programmation.
|
||
|
||
Mais tout ceci est encore à venir !
|
||
|
||
<!-- TODO : Pictures, schémas, update theme to differentiate between update and publish, add Qt config options -->
|