Une modeste introduction à (mon travail sur) QEMU

(Nhat Minh Lê (rz0) @ 2010-03-03 21:33:53)

Voilà un petit mois que j’ai commencé mon stage en laboratoire, stage facultatif entrant dans mon cursus Ensimag. Pour les curieux, je suis chez TIMA, un labo en plein centre de Grenoble. Durant ce stage, je suis censé étudier la prise en charge de processeurs VLIW par QEMU, en me basant sur l’exemple du C64x+, un DSP conçu par Texas Instrument. L’objectif est d’établir une méthode convaincante, et un petit prototype qui l’illustre sur le C64x+.

Pour l’heure, je ne peux que constater que j’ai un mal certain à m’adapter à QEMU. Ce n’est pas tant à cause de la documentation interne quasi inexistante, ça, disons, j’ai l’habitude avec le code libre. Mais la structure même du programme est à mon sens quelque peu obscure : de la redondance entre modules similaires, une isolation architecturale qui me paraît douteuse, et des conventions qui semblent au premier abord très peu intuitives.α

α : J’entends d’ici la horde de fanboys en puissance qui se prépare à me lyncher. Disclaimer! Ce n’est pas que je dénigre le travail accompli, ou les mecs derrière tout ça, qui, à n’en pas douter, sont doués, géniaux, sublimes, etc. plus que je ne le serai jamais… Bref, je ne fais que constater que, du point de vue du gars qu’on emploie (gratuitement) à hacker ce code, c’est pas évident…

Dans ce premier article, donc, je vous propose simplement une petite présentation de QEMU et de sa technique d’émulation, du moins, ce que j’en ai compris. :)

QEMU is an emulator

Et ouais, QEMU, ce n’est pas WINE. Car QEMU, c’est un émulateur, un programme capable d’exécuter du binaire destiné à d’autres machines. Dans la petite jungle des solutions d’interopérabilité binaire, l’émulateur est sans doute celui qui se place au niveau le plus bas puisqu’il lit et imite une architecture directement au niveau de son jeu d’instructions. QEMU se différencie en cela de logiciels de virtualisation, tels que Xen, dont j’ai déjà parlé dans un billet précédent, dont le but est de faire tourner des systèmes différents mais conçus pour le même CPU, sur une même machine.

Le travail d’un émulateur est donc de lire et d’exécuter un code binaire ; en l’occurrence, il s’agit du codage des instructions d’une machine réelle, mais cela pourrait tout à fait être une machine abstraite. Au final, les techniques possibles sont semblables à celles que l’on peut imaginer pour un compilateur. Simplement, ici, le langage source est binaire (et le langage cible aussi). L’analyseur « syntaxique » se voit donc remplacé par une espèce d’automate dont le rôle est de décoder le binaire en entrée, à la manière du processeur que l’on imite. On y gagne a priori en simplicité : les formes que peuvent prendre les instructions sont généralement assez régulières et limitées. Mais les subtilités guettent ! Pour peu que le jeu d’instructions soit un peu étoffé, les cas particuliers se multiplient rapidement. De plus, il faut prendre en compte l’état de la machine ; en terme de langages, cela se traduirait par un contexte.

Une fois l’instruction identifiée… reste à l’exécuter. Là encore, comme pour les langages de programmation, le choix s’offre à nous, notamment entre interprétation et compilation. Et QEMU a choisi…

Traduction binaire dynamique

… la traduction binaire dynamique.β En quelques mots, il s’agit d’une compilation à la volée vers du code exécutable par le processeur hôte (faisant tourner QEMU lui-même). Ce code est ensuite directement exécuté. Et hop ! On a un truc pas trop lent. C’est grosso modo le même principe que dans les JIT que l’on trouve dans les machines virtuelles de langages de haut niveau, et dont, il y a quelques années, tout le monde était trop fier.γ

β : Wikipédia donne « translation dynamique de code », mais désolé, je ne vois aucun avantage à utiliser « translation » ici à part se donner l’air plus analphabète qu’on ne l’est. :] Plus sérieusement, il me paraît douteux qu’un tel terme soit entré dans l’usage, aussi, je ne me sens pas obligé de l’employer…

γ : Then some Lisp defenders came and ruined the fun…

Plus en détails, QEMU est composé de modules implémentant chacun l’émulation d’un processeur donné, appelé processeur cible (target), en opposition au processeur hôte, sur lequel tourne QEMU, et vers lequel les instructions vont être traduites. La responsabilité d’analyser l’entrée binaire est déléguée au module.

Celui-ci lit l’entrée par blocs qu’il traduit dans un langage intermédiaire, composé d’un jeu de micro-opérations minimal. Un bloc est une suite simple d’instructions, qui s’exécutent linéairement, les unes après les autres. Concrètement, cela signifie que le bloc prend fin au premier saut. Ce code intermédiaire est transmis à TCG, le générateur de code de QEMU, qui se charge de le traduire pour l’hôte. Cela ressemble à s’y méprendre à de la compilation, n’est-ce pas ? Ici, on retrouve les avantages usuellement attribués à l’usage d’un langage intermédiaire : abstraction, uniformité, simplicité.

Et moi, dans tout ça ?

Et bien moi, dans tout ça, je travaille surtout sur la partie analyse et génération du code intermédiaire. L’objectif de mon stage est d’élaborer une méthode de traduction systématique de jeux d’instructions VLIW vers le langage intermédiaire de QEMU. L’idée serait d’automatiser les aspects fastidieux liés à la multitude de cas possibles introduite par le parallélisme. Ainsi, à partir d’une description abstraite du jeu d’instructions, j’espère pouvoir générer le code qui effectue réellement la traduction.

À l’heure où j’écris ces lignes, je commence tout juste à m’attaquer à la partie traduction. J’ai passé ce dernier mois (du moins les jours où je travaillais dessus) à faire connaissance avec QEMU, et, dans une moindre mesure, l’architecture du C64x+. Pour l’heure, je n’ai produit qu’un squelette plus que minimaliste de target C64x+, dont je pense reparler bientôt, dans un prochain article technique consacré au récit de mon (début de) périple dans les entrailles de QEMU !