(fr) Attempting an aarch64 port
Image location is not ideal, could not make colocation or static images work correctly. Will need to investigate/fix in the future
This commit is contained in:
parent
a658252471
commit
8614fd7192
5 changed files with 319 additions and 0 deletions
319
content/articles/Attempting-an-aarch64-port.fr.md
Normal file
319
content/articles/Attempting-an-aarch64-port.fr.md
Normal file
|
@ -0,0 +1,319 @@
|
|||
+++
|
||||
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 -->
|
BIN
static/images/Attempting-an-aarch64-port/Cockatrice_Arm.png
Normal file
BIN
static/images/Attempting-an-aarch64-port/Cockatrice_Arm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 266 KiB |
BIN
static/images/Attempting-an-aarch64-port/Graphical_login.png
Normal file
BIN
static/images/Attempting-an-aarch64-port/Graphical_login.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.3 KiB |
BIN
static/images/Attempting-an-aarch64-port/KDE.png
Normal file
BIN
static/images/Attempting-an-aarch64-port/KDE.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 770 KiB |
BIN
static/images/Attempting-an-aarch64-port/nographics_boot.png
Normal file
BIN
static/images/Attempting-an-aarch64-port/nographics_boot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
Loading…
Add table
Reference in a new issue