Assembly HOWTO <author>François-René Rideau <tt>fare@tunes.org</tt> <date>v0.4q, 22 Juin 1999 <abstract> (Version française réalisée par Faré Rideau <tt>fare@tunes.org</tt>, avec mes remerciements à Eric Dumas <tt>dumas@linux.eu.org</tt>, 5 Juin 1999). Ce document décrit comment programmer en assembleur x86 en n'utilisant que des outils de développement <em>libres</em>, et tout particulièrement avec le système d'exploitation Linux sur la plate-forme i386. Les informations contenues dans ce document peuvent être applicables ou non applicables à d'autres plates-formes matérielles ou logicielles. Les contributions à ce documents seront acceptées avec gratitude. <em>mots-clefs</em>~: assembleur, libre, macroprocesseur, préprocesseur, asm, inline asm, 32 bits, x86, i386, gas, as86, nasm </abstract> <toc> <sect>Introduction<p> <sect1>Copyright<p> Copyright © 1996-1999 François-René Rideau. Ce document est un logiciel libre~; vous pouvez le redistribuer et/ou le modifier selon les termes de la GNU General Public License telle que publiée par la Free Software Foundation, version 2 ou ultérieure. <sect1>Note importante<p> <!-- Ceci est censé être la dernière version que j'écrirai de ce document. Il y a un candidat pour reprendre en charge le document, mais jusqu'à ce qu'il le reprenne complètement en main, je serai heureux de m'occuper de tout courrier concernant ce document. --> Ceci est un document évoluant interactivement~: vous êtes tout spécialement invités à poser des questions, à y répondre, à corriger les données, à ajouter de nouvelles informations, à compléter les références sur d'autres logiciels, à mettre en évidence les erreurs et lacunes du document. Si vous êtes motivés, vous pouvez même <bf>prendre en charge ce document</bf>. En trois mots, apportez votre contribution~! Pour contribuer à ce document, contactez la personne qui apparaît actuellement en charge. Au moment où j'écris ces lignes, il s'agit de <url url="mailto:fare@tunes.org" name="François-René Rideau">. <!-- Rahim Azizarab (rahim@megsinet.net) et Paul Anderson (paul@geeky1.ebtech.net) se sont portés volontaire, puis fait porter pâles. Reste-t-il des amateurs~? --> Cependant, je recherche depuis un certain temps (voire un temps certain) un bidouilleur sérieux pour me remplacer comme responsable de ce document. Côté pile, vous devez passer un peu de temps à mettre ce document à jour et à le corriger. Côté face, vous gagnez quelque renommée <em>et</em> vous recevez des copies gracieuses des recueils de HOWTO. <sect1>Avant-Propos<p> Ce document est destiné à répondre aux questions les plus fréquemment posées par les gens qui développent ou qui souhaitent développer des programmes en assembleur x86 32 bits en utilisant des <em><htmlurl url="http://www.tunes.org/~fare/libre-logiciel.html" name="logiciels libres"></em>, et tout particulièrement sous Linux. Vous y trouverez également des liens sur d'autres documents traitant d'assembleur, fondés sur des outils logiciels qui ne sont pas libres, pas 32-bit, ou pas dédiés à l'architecture x86, bien que cela ne soit pas le but principal de ce document. Etant donné que l'intéret principal de la programmation en assembleur est d'établir les fondations de systèmes d'exploitation, d'interpréteurs, de compilateurs, et de jeux, là où un compilateur C n'arrive plus à fournir le pouvoir d'expression nécessaire (les performances étant de plus en plus rarement un problème), nous insisterons sur le développement de tels logiciels. <sect2>Comment utiliser ce document<p> Ce document contient des réponses à un certain nombre de questions fréquemment posées. Des URL y sont données, qui pointent sur des sites contenant documents ou logiciels. Prenez conscience que les plus utiles de ces sites sont dupliqués sur des serveurs miroirs, et qu'en utilisant le site miroir le plus proche de chez vous, vous évitez à un gâchis inutile aussi bien de précieuses ressources réseau communes à l'Internet que de votre propre temps. Ainsi, il existe un certain nombre de gros serveurs disséminés sur la planète, qui effectuent la duplication d'autres sites importants. Cherchez où se trouvent ces sites et identifiez les plus proches de chez vous (du point de vue du réseau). Parfois, la liste des miroirs est donnée dans un fichier ou dans le message de connexion. Suivez ces conseils. Si ces informations ne sont pas présentes, utilisez le programme archie. La version la plus récente de ce document peut être trouvée sur <url url="http://www.tunes.org/~fare/files/asm/Assembly-HOWTO.fr.sgml"> mais les répertoires de HOWTO Linux devraient normalement être à peu près à jour (je ne peux pas le garantir)~: <url url="http://metalab.unc.edu/LDP/HOWTO/">. La version française de ce document peut être trouvée sur le site <url url="ftp://ftp.lip6.fr/pub/linux/french/HOWTO/">. <sect2>Autres documents de référence<p> <itemize> <item>si vous ne savez ce qu'est le <url url="http://www.tunes.org/~fare/libre-logiciel.html">libre logiciel</A>, lisez avec attention la GPL (GNU General Public License), qui est utilisée dans un grand nombre de logiciels libres, et est une source d'inspiration pour la plupart des autres licences d'utilisation de logiciels libres. Elle se trouve généralement dans un fichier nommé <tt>COPYING</tt>, avec une version pour les bibliothèques de routines dans un fichier nommé <tt>COPYING.LIB</tt>. Les écrits publiés par la <url url="http://www.fsf.org" name="FSF"> (free software foundation) peuvent également vous aider à comprendre le phénomène. <item>plus précisément, les logiciels libres intéressants sont ceux desquels les sources sont disponibles, que l'on peut consulter, corriger, et desquels on peut emprunter une partie. Lisez les licences d'utilisation avec attention et conformez-vous y. <item>il existe une FAQ dans le forum de discussion comp.lang.asm.x86 qui répond aux questions générales concernant la programmation en assembleur pour x86, et aux questions concernant certains assembleurs commerciaux dans un environnement DOS 16 bits. Certaines de ces réponses peuvent s'appliquer à la programmation 32 bits, aussi serez-vous sans-doute intéressés par la lecture de cette <url url="http://www2.dgsys.com/˜raymoon/faq/asmfaq.zip" name="FAQ">... <item>Sont disponibles des FAQs, de la documentation, et des sources, concernant la programmation sur votre plate-forme préférée, quelle qu'elle soit, et vous devriez les consulter pour les problèmes liés à votre plate-forme qui ne seraient pas spécifiques à la programmation en assembleur. </itemize> <sect1>Historique de ce document<p> Chaque version inclue quelques modifications et corrections mineures, qui ne sont pas indiquées à chaque fois. <descrip> <tag>Version 0.1 23 Avril 1996</tag> Francois-Rene "Faré" Rideau <fare@tunes.org> crée et diffuse initialement le document sous forme d'un mini-HOWTO car ``Je suis un peu fatigué d'avoir à répondre encore et toujours aux mêmes questions dans le forum comp.lang.asm.x86'' <tag>Version 0.2 4 Mai 1996</tag> * <tag>Version 0.3c 15 Juin 1996</tag> * <tag>Version 0.3f 17 Octobre 1996</tag> Tim Potter indique l'option -fasm pour activer l'assembleur en-ligne de GCC sans le reste des optimisations de -O. <tag>Version 0.3g 2 Novembre 1996</tag> Création de l'historique. Ajout de pointeurs dans la section sur la compilation croisée. Ajout d'une section concernant la programmation des entrées/sorties sous Linux (en particulier pour l'accès vidéo). <tag>Version 0.3h 6 Novembre 1996</tag> plus sur la compilation croisée - voir sur metalab~: devel/msdos/ <tag>Version 0.3i 16 Novembre 1996</tag> NASM commence à être particulièrement intéressant <tag>Version 0.3j 24 Novembre 1996</tag> Référence sur la version française <tag>Version 0.3k 19 Décembre 1996</tag> Quoi~? J'avais oublié de parler de Terse~? <tag>Version 0.3l 11 Janvier 1997</tag> * <tag>Version 0.4pre1 13 Janvier 1997</tag> Le mini-HOWTO au format texte est transformé en un authentique HOWTO au format linuxdoc-sgml, pour explorer les possibilités dudit format. <tag>Version 0.4 20 Janvier 1997</tag> Première diffusion de ce HOWTO. <tag>Version 0.4a 20 Janvier 1997</tag> Ajout de la section CREDITS <tag>Version 0.4b 3 Février 1997</tag> NASM mis avant AS86 <tag>Version 0.4c 9 Février 1997</tag> Ajout de la partie "Avez-vous besoin d'utiliser l'assembleur~?" <tag>Version 0.4d 28 Février 1997</tag> Annonce fantôme d'un nouveau responsable de ce HowTo. <tag>Version 0.4e 13 Mar 1997</tag> Version diffusée pour DrLinux <tag>Version 0.4f 20 Mars 1997</tag> * <tag>Version 0.4g 30 Mars 1997</tag> * <tag>Version 0.4h 19 Juin 1997</tag> Ajouts à propos de "Comment ne pas utiliser l'assembleur"~; mises à jour concernant NASM et GAS. <tag>Version 0.4i 17 Juillet 1997</tag> Informations sur l'accès au mode 16 bits à partir de Linux. <tag>Version 0.4j 7 September 1997</tag> * <tag>Version 0.4k 19 Octobre 1997</tag> je (Faré) reprends en main la traduction française du HowTo <tag>Version 0.4l 16 Novembre 1997</tag> version pour LSL 6ème édition. <tag>Version 0.4m 23 Mars 1998</tag> corrections à propos de l'invocation de gcc. <tag>Version 0.4o 1 Décembre 1998</tag> * <tag>Version 0.4p 6 Juin 1999</tag> nettoiement et mise-à-jour. <tag>Version 0.4q 22 Juin 1999</tag> passage d'arguments à un processus (argc, argv, environ) en assembleur. Il s'agit encore d'une nouvelle ``toute dernière version réalisée par Faré avant qu'un nouveau responsable ne prenne la main''. Sauf que personne ne sait qui ce nouveau responsable pourrait bien être. </descrip> <sect1>Crédits<p> Je souhaiterais remercier les personnes suivantes~: <itemize> <item><url url="mailto:buried.alive@in.mail" name="Linus Torvalds"> pour Linux <item><url url="mailto:bde@zeta.org.au" name="Bruce Evans"> pour bcc d'où est extrait as86 <item><url url="mailto:anakin@pobox.com" name="Simon Tatham"> et <url url="mailto:jules@earthcorp.com" name="Julian Hall"> pour NASM. <item><url url="mailto:gregh@metalab.unc.edu" name="Greg Hankins"> pour la coordination des HOWTOs <item><url url="mailto:raymoon@moonware.dgsys.com" name="Raymond Moon"> pour sa FAQ <item><url url="mailto:dumas@Linux.EU.Org" name="Eric Dumas"> pour la traduction initiale en français... (l'auteur, français, est le premier attristé de devoir écrire l'original en anglais), et <url url="mailto:vallat@essi.fr" >Miodrag Vallat</A> pour la relecture. <item><url url="mailto:paul@geeky1.ebtech.net" name="Paul Anderson"> et <url url="mailto:rahim@megsinet.net" name="Rahim Azizarab"> pour m'avoir aidé, à défaut de reprendre le HowTo en main. <item><url url="mailto:ams@wiw.org" name="Abhijit Menon-Sen"> pour m'avoir aidé à comprendre la convention de passage des arguments aux processus. <item>toutes les personnes qui ont contribué à l'écriture de ce document, par leurs idées, remarques ou leur soutien moral. </itemize> <sect>Avez-vous besoin de l'assembleur~?<label id="doyouneedasm"> <p> Je ne veux en aucun cas jouer les empêcheurs-de-tourner-en-rond, mais voici quelques conseils issus d'une expérience gagnée à la dure. <sect1>Le Pour et le Contre<p> <sect2>Les avantages de l'assembleur<p> L'assembleur peut vous permettre de réaliser des opérations très bas niveau~: <itemize> <item>vous pouvez accéder aux registres et aux ports d'entrées/sorties spécifiques à votre machine~; <item>vous pouvez parfaitement contrôler le comportemant du code dans des sections critiques où pourraient sinon advenir un blocage du processeur ou des périphériques~; <item>vous pouvez sortir des conventions de production de code de votre compilateur habituel~; ce qui peut vous permettre d'effectuer certaines optimisations (par exemple contourner les règles d'allocation mémoire, gérer manuellement le cours de l'éxécution, etc.)~; <item>accéder à des modes de programmation non courants de votre processeur (par exemple du code 16 bits pour l'amorçage, l'interfaçage avec le BIOS, ou la récupération d'une base de code historique <!-- legacy code -->, sur les pécés Intel)~; <item>vous pouvez construire des interfaces entre des fragments de codes utilisant des conventions incompatibles (c'est-à-dire produit par des compilateurs différents ou séparés par une interface bas-niveau)~; <item>vous pouvez générer un code assez rapide pour les boucles importantes pour pallier aux défauts d'un compilateur qui ne sait les optimiser (mais bon, il existe des compilateurs optimisateurs librement disponibles~!)~; <item>vous pouvez générer du code optimisé <sq>à la main</sq> qui est plus parfaitement règlé pour votre configuration matérielle précise, même s'il ne l'est pour aucune autre configuration~; <item>vous pouvez écrire du code pour le compilateur optimisateur de votre nouveau langage. (c'est là une activité à laquelle peu se livrent, et encore, rarement.) </itemize> <p> <sect2>Les inconvénients de l'assembleur<p> <p> L'assembleur est un langage très bas niveau (le langage du plus bas niveau qui soit au dessus du codage à la main de motifs d'instructions en binaire). En conséquence~: <itemize> <item>l'écriture de code en est longue et fastidieuse~; <item>les bogues apparaissent aisément~; <item>les bogues sont difficiles à repérer et supprimer~; <item>il est difficile de comprendre et de modifier du code (la maintenance est très compliquée)~; <item>le résultat est extrêmement peu portable vers une autre architecture, existante ou future~; <item>votre code ne sera optimisé que une certaine implémentation d'une même architecture~: ainsi, parmi les plate-formes compatibles Intel, chaque réalisation d'un processeur et de ses variantes (largeur du bus, vitesse et taille relatives des CPU/caches/RAM/Bus/disques, présence ou non d'un coprocesseur arithmétique, et d'extensions MMX ou autres) implique des techniques d'optimisations parfois radicalement différentes. Ainsi diffèrent grandement les processeurs déjà existants et leurs variations~: Intel 386, 486, Pentium, PPro, Pentium II~; Cyrix 5x86, 6x86~; AMD K5, K6. Et ce n'est sûrement pas terminé~: de nouveaux modèles apparaissent continuellement, et cette liste même sera rapidement dépassée, sans parler du code ``optimisé'' qui aura été écrit pour l'un quelconque des processeurs ci-dessus. <item>le code peut également ne pas être portable entre différents systèmes d'exploitation sur la même architecture, par manque d'outils adaptés (GAS semble fonctionner sur toutes les plates-formes~; NASM semble fonctionner ou être facilement adaptable sur toutes les plates-formes compatibles Intel)~; <item>un temps incroyable de programmation sera perdu sur de menus détails, plutôt que d'être efficacement utilisé pour la conception et le choix des algorithmes utilisés, alors que ces derniers sont connus pour être la source de la majeure partie des gains en vitesse d'un programme. Par exemple, un grand temps peut être passé à grapiller quelques cycles en écrivant des routines rapides de manipulation de chaînes ou de listes, alors qu'un remplacement de la structure de données à un haut niveau, par des arbres équilibrés et/ou des tables de hachage permettraient immédiatement un grand gain en vitesse, et une parallélisation aisée, de façon portable permettant un entretien facile. <item>une petite modification dans la conception algorithmique d'un programme anéantit la validité du code assembleur si patiemment élaboré, réduisant les développeurs au dilemne de sacrifier le fruit de leur labeur, ou de s'enchaîner à une conception algorithmique obsolète. <item>pour des programmes qui font des choses non point trop éloignées de ce que font les benchmarks standards, les compilateurs/optimiseurs commerciaux produisent du code plus rapide que le code assembleur écrit à la main (c'est moins vrai sur les architectures x86 que sur les architectures RISC, et sans doute moins vrai encore pour les compilateurs librement disponibles. Toujours est-il que pour du code C typique, GCC est plus qu'honorable). <item>Quoi qu'il en soit, ains le dict le saige John Levine, modérateur de comp.compilers, <sq>les compilateurs rendent aisée l'utilisation de structures de données complexes~; ils ne s'arrêtent pas, morts d'ennui, à mi-chemin du travail, et produisent du code de qualité tout à fait satisfaisante</sq>. Ils permettent également de propager <em>correctement</em> les transformations du code à travers l'ensemble du programme, aussi hénaurme soit-il, et peuvent optimiser le code par-delà les frontières entre procédures ou entre modules. </itemize> <p> <sect2>Affirmation<p> En pesant le pour et le contre, on peut conclure que si l'assembleur est parfois nécessaire, et peut même être utile dans certains cas où il ne l'est pas, il vaut mieux~: <itemize> <item>minimiser l'utilisation de code écrit en assembleur~; <item>encapsuler ce code dans des interfaces bien définies~; <item>engendrer automatiquement le code assembleur à partir de motifs écrits dans un langage plus de haut niveau que l'assembleur (par exemple, des macros contenant de l'assembleur en-ligne, avec GCC)~; <item>utiliser des outils automatiques pour transformer ces programmes en code assembleur~; <item>faire en sorte que le code soit optimisé, si possible~; <item>utiliser toutes les techniques précédentes à la fois, c'est-à-dire écrire ou étendre la passe d'optimisation d'un compilateur. </itemize> Même dans les cas où l'assembleur est nécessaire (par exemple lors de développement d'un système d'exploitation), ce n'est qu'à petite dose, et sans infirmer les principes ci-dessus. Consultez à ce sujet les sources du noyau de Linux~: vous verrez qu'il s'y trouve juste le peu qu'il faut d'assembleur, ce qui permet d'avoir un système d'exploitation rapide, fiable, portable et d'entretien facile. Même un jeu très célèbre comme DOOM a été en sa plus grande partie écrit en C, avec une toute petite routine d'affichage en assembleur pour accélérer un peu. <sect1>Comment ne pas utiliser l'assembleur<p> <sect2>Méthode générale pour obtenir du code efficace<p> <p> Comme le dit Charles Fiterman dans comp.compilers à propos de la différence entre le code écrit par l'homme ou la machine, ``L'homme devrait toujours gagner, et voici pourquoi~: <itemize> <item>Premièrement, l'homme écrit tout dans un langage de haut nivrau. <item>Deuxièmement, il mesure les temps d'éxécution (profiling) pour déterminer les endroits où le programme passe la majeure partie du temps. <item>Troisièmement, il demande au compilateur d'engendrer le code assembleur produit pour ces petites sections de code. <item>Enfin, il effectue à la main modifications et réglages, à la recherche des petites améliorations possibles par rapport au code engendré par la machine. </itemize> L'homme gagne parce qu'il peut utiliser la machine.'' <sect2>Langages avec des compilateurs optimiseurs<p> Des langages comme ObjectiveCAML, SML, CommonLISP, Scheme, ADA, Pascal, C, C++, parmi tant d'autres, ont tous des compilateurs optimiseurs librement disponibles, qui optimiseront le gros de vos programmes, et produiront souvent du code meilleur que de l'assembleur fait-main, même pour des boucles serrées, tout en vous permettant de vous concentrer sur des détails haut niveau, et sans vous interdire de gagner par la méthode précédente quelques pourcents de performance supplémentaire, une fois la phase de conception générale terminée. Bien sûr, il existe également des compilateurs optimiseurs commerciaux pour la plupart de ces langages. Certains langages ont des compilateurs qui produisent du code C qui peut ensuite être optimisé par un compilateur C. C'est le cas des langages LISP, Scheme, Perl, ainsi que de nombreux autres. La vitesse des programmes obtenus est toute à fait satisfaisante. <sect2>Procédure générale à suivre pour accélérer votre code<p> Pour accélérer votre code, vous ne devriez traiter que les portions d'un programme qu'un outil de mesure de temps d'éxécution (profiler) aura identifié comme étant un goulot d'étranglement pour la performance de votre programme. Ainsi, si vous identifiez une partie du code comme étant trop lente, vous devriez <itemize> <item>d'abord essayer d'utiliser un meilleur algorithme~; <item>essayer de la compiler au lieu de l'interpréter~; <item>essayer d'activer les bonnes options d'optimisation de votre compilateur~; <item>donner au compilateur des indices d'optimisation (déclarations de typage en LISP~; utilisation des extensions GNU avec GCC~; la plupart des compilos fourmillent d'options)~; <item>enfin de compte seulement, se mettre à l'assembleur si nécessaire. </itemize> Enfin, avant d'en venir à cette dernière option, vous devriez inspecter le code généré pour vérifier que le problème vient effectivement d'une mauvaise génération de code, car il se peut fort bien que ce ne soit pas le cas~: le code produit par le compilateur pourrait être meilleur que celui que vous auriez écrit, en particulier sur les architectures modernes à pipelines multiples~! Il se peut que les portions les plus lentes de votre programme le soit pour des raisons intrinsèques. Les plus gros problèmes sur les architectures modernes à processeur rapide sont dues aux délais introduits par les accès mémoires, manqués des caches et TLB, fautes de page~; l'optimisation des registres devient vaine, et il vaut mieux repenser les structures de données et l'enchaînement des routines pour obtenir une meilleur localité des accès mémoire. Il est possible qu'une approche complètement différente du problème soit alors utile. <sect2>Inspection du code produit par le compilateur<p> Il existe de nombreuses raisons pour vouloir regarder le code assembleur produit par le compilateur. Voici ce que vous pourrez faire avec ce code~: <itemize> <item>vérifier si le code produit peut ou non être amélioré avec du code assembleur écrit à la main (ou par un réglage différent des options du compilateur)~; <item>quand c'est le cas, commencer à partir de code automatiquement engendré et le modifier plutôt que de repartir de zéro~; <item>plus généralement, utilisez le code produit comme des scions à greffer, ce qui à tout le moins vous permet d'avoir gratuitement tout le code d'interfaçage avec le monde extérieur. <item>repérer des bogues éventuels dus au compilateur lui-même (espérons-le très rare, quitte à se restreindre à des versions ``stables'' du compilo). </itemize> La manière standard d'obtenir le code assembleur généré est d'appeller le compilateur avec l'option <tt>-S</tt>. Cela fonctionne avec la plupart des compilateur Unix y compris le compilateur GNU C (GCC)~; mais à vous de voir dans votre cas. <!--Your Mileage May Vary--> Pour ce qui est de GCC, il produira un code un peu plus compréhensible avec l'option <tt>-fverbose-asm</tt>. Bien sur, si vous souhaitez obtenir du code assembleur optimisé, n'oubliez pas d'ajouter les options et indices d'optimisation appropriées~! <sect>Assembleurs<p> <sect1>Assembleur en-ligne de GCC<p> Le célèbre GNU C/C++ Compiler (GCC), est un compilateur 32 bits optimisant situé au coeur du projet GNU. Il gère assez bien les architectures x86 et permet d'insérer du code assembleur à l'intérieur de programmes C de telle manière que les registres puissent être soit spécifiés soit laissés aux bons soins de GCC. GCC fonctionne sur la plupart des plates-formes dont Linux, *BSD, VSTa, OS/2, *DOS, Win*, etc. <sect2>Où trouver GCC<p> Le site principal de GCC est le site FTP du projet GNU~: <url url="ftp://prep.ai.mit.edu/pub/gnu/gcc/"> On y trouve également toutes les applications provenant du projet GNU. Des versions configurées ou précompilées pour Linux sont disponibles sur <url url="ftp://metalab.unc.edu/pub/Linux/GCC/">. Il existe un grand nombre de miroirs FTP des deux sites partout de par le monde, aussi bien que des copies sur CD-ROM. Le groupe de développement de GCC s'est il y a quelques temps scindé en deux parties, qui vont néanmoins refusionner sous peu. Pour plus d'informations sur la version expérimentale, egcs, voir <url url="http://www.cygnus.com/egcs/"> Les sources adaptés à votre système d'exploitation préféré ainsi que les binaires précompilés peuvent être trouvés sur les sites FTP courants. Le portage le plus célèbre de GCC pour DOS est DJGPP et il peut être trouvé dans le répertoire du même nom sur les sites ftp. Voir~: <url url="http://www.delorie.com/djgpp/"> Il existe également un portage de GCC pour OS/2 appelé EMX qui fonctionne également sous DOS et inclut un grand nombre de routines d'émulation Unix. Voir le site suivant~: <url url="ftp://ftp-os2.cdrom.com/pub/os2/emx09c/">. D'autres URLs listés dans des versions précédentes de ce HOWTO semblent aussi morts que OS/2. <!-- broken url url="http://www.leo.org/pub/comp/os/os2/gnu/emx+gcc/"--> <!-- broken url url="http://warp.eecs.berkeley.edu/os2/software/shareware/emx.html"--> <sect2>Où trouver de la documentation sur l'assembleur en ligne avec GCC~? <p> La documentation de GCC inclut les fichiers de documentation au format texinfo. Vous pouvez les compiler avec TeX et les imprimer, ou les convertir au format .info et les parcourir interactivement avec emacs, ou encore les convertir au format HTML, ou en à peu près n'importe quel format (avec les outils adéquats). Les fichiers .info sont généralement installés en même temps que GCC. La section à consulter est <tt>C Extensions::Extended Asm::</tt> La section <tt>Invoking GCC::Submodel Options::i386 Options::</tt> peut également vous aider. En particulier, elle donne les noms de contraintes pour les registres du i386~: abcdSDB correspondent respectivement à <tt>%eax</tt>, <tt>%ebx</tt>, <tt>%ecx</tt>, <tt>%edx</tt>, <tt>%esi</tt>, <tt>%edi</tt>, <tt>%ebp</tt> (aucune lettre pour <tt>%esp</tt>). Le site "DJGPP Games resource" (qui n'est pas réservé aux seuls développeurs de jeux) possédait une page particulière sur l'assembleur, mais a été fermé. Ses données ont néanmoins été récupérées sur le <url url="http://www.delorie.com/djgpp/" name="site de DJGPP">, qui contient une mine d'autres informations utiles~: <url url="http://www.delorie.com/djgpp/doc/brennan/"> <!-- broken url url="http://www.rt66.com/˜brennan/djgpp/djgpp_asm.html"--> <!-- broken url url="http://remus.rutgers.edu/˜avly/djasm.html" name="DJGPP Quick ASM Programming Guide" --> GCC sous-traite l'assemblage proprement dit à GAS et suit donc sa syntaxe (voir plus bas), cela implique que l'assembleur en ligne doit utiliser des caractères pourcents entre apostrophes pour qu'ils soient passés à GAS. Voir la section dédiée à GAS. Vous trouverez un <em>grand</em> nombre d'exemples instructifs dans le répertoire <tt>linux/include/asm-i386/</tt> des sources de Linux. <sect2>Appeller GCC pour obtenir du code assembleur en ligne correct~?<p> Assurez-vous d'appeller gcc avec l'option <tt>-O</tt> (ou <tt>-O2</tt>, <tt>-O3</tt>, etc) pour activer les optimisations et l'assembleur en ligne. Si vous ne le faites pas, votre code pourra compiler mais ne pas s'exécuter correctement~! En fait (merci à Tim Potter, timbo@moshpit.air.net.au), il suffit d'utiliser l'option <tt>-fasm</tt>, faisant partie de toutes les fonctionnalités activées par l'option <tt>-O</tt>. Donc si vous avez des problèmes en raison d'optimisations boguées dans votre implémentation de gcc, vous pouvez toujours utiliser l'assembleur en ligne. De même, utilisez l'option <tt>-fno-asm</tt> pour désactiver l'assembleur en ligne (on peut se demander pourquoi~?). Plus généralement, les bonnes options de compilation à utiliser avec gcc sur les plates-formes x86 sont <code> gcc -O2 -fomit-frame-pointer -m386 -Wall </code> <tt>-O2</tt> est le bon niveau d'optimisation. Les optimisations supérieures génèrent un code un peu plus important, mais très légèrement plus rapide. De telles sur-optimisations peuvent être utiles que dans le cas d'optimisations de boucles que vous pouvez toujours réaliser en assembleur. Si vous avez besoin de faire ce genre de choses, ne le faites que pour les routines qui en ont besoin. <tt>-fomit-frame-pointer</tt> permet au code généré de se passer de la gestion inutile des pointeurs de fenêtre, ce qui rend le code plus petit plus rapide et libère un registre pour de plus amples optimisations. Cette option exclue l'utilisation des outils de déboguage (<tt>gdb</tt>), mais lorsque vous les utilisez, la taille et la vitesse importent peu. <tt>-m386</tt> génère un code plus compact sans ralentissement notable, (moins de code signifie également mois d'entrées/sorties sur disque et donc une exécution plus rapide). Vous pouvez également utiliser l'option -mpentium sur la version GCC gérant l'optimisation pour ce processeur. <tt>-Wall</tt> active toutes les mises-en-garde (warning) et vous évite de nombreuses erreurs stupides et évidentes. Pour optimiser encore plus, vous pouvez utiliser l'option <tt>-mregparm=2</tt> et/ou les attributs de fonctions qui peuvent être utilisés mais ils peuvent dans certains cas poser de nombreux problèmes lors de l'édition de liens avec du code externe (notamment les bibliothèques partagées)... Notez que vous pouvez ajoutez ces options aux options utilisées par défaut sur votre système en éditant le fichier <tt>/usr/lib/gcc-lib/i486-linux/2.7.2.3/specs</tt> (cependant, ne rajoutez pas <tt>-Wall</tt> à ces options). <sect1>GAS <p> GAS est l'assembleur GNU, utilisé par gcc. <sect2>Où le trouver~?<p> Au même endroit où vous avez trouvé gcc, dans le paquetage binutils. <sect2>Qu'est-ce que la syntaxe AT&T<p> Comme GAS a été inventé pour supporter un compilateur 32 bits sous unix, il utilise la syntaxe standard <sq>AT&T</sq>, qui ressemble assez à l'assembleur m68k. La syntaxe n'est ni pire, ni meilleur que la syntaxe <sq>Intel</sq>. Elle est juste différente. Lorsque vous aurez l'habitude de vous en servir, vous la trouverez plus régulière que la syntaxe Intel, quoique que légèrement plus ennuyeuse aussi. Voici les points les plus importants à propos de la syntaxe de GAS~: <itemize> <item> Les noms de registres sont préfixés avec <tt>%</tt>, de façon à ce que les registres soient <tt>%eax</tt>, <tt>%dl</tt> et consorts au lieu de juste <tt>eax</tt>, <tt>dl</tt>, etc. Ceci rend possible l'inclusion directe de noms de symboles externes C sans risque de confusion, ou de nécessité de préfixes _. <item> L'ordre des opérandes est source(s) d'abord, destination en dernier, à l'opposé de la convention d'Intel consistant à mettre la destination en premier, les source(s) ensuite. Ainsi, ce qui en syntaxe Intel s'écrit <tt>mov ax,dx</tt> (affecter au registre <tt>ax</tt> le contenu du registre <tt>dx</tt>) s'écrira en syntaxe ATT <tt>mov %dx, %ax</tt>. <item> La longueur des opérandes est spécifiée comme suffixe du nom d'instruction. Le suffixe est <tt>b</tt> pour un octet (8 bit), <tt>w</tt> pour un mot (16 bit), et <tt>l</tt> pour un mot long (32 bit). Par exemple, la syntaxe correcte pour l'instruction ci-dessus aurait dû être <tt>movw %dx,%ax</tt>. Toutefois, gas n'est pas aussi strict que la syntaxe ATT l'exige, et le suffixe est optionel quand la longueur peut être devinée grâce aux opérandes qui sont des registres, la taille par défaut étant 32 bit (avec une mise en garde quand on y fait appel). <item> Les opérandes immédiats sont marqués d'un préfixe <tt>$</tt>, comme dans <tt>addl $5,%eax</tt> (ajouter la valeur longue immédiate 5 au registre <tt>%eax</tt>). <item> L'absence de préfixe à un opérande indique une adresse mémoire~; ainsi <tt>movl $foo,%eax</tt> met l'<em>adresse</em> de la variable <tt>foo</tt> dans le registre <tt>%eax</tt>, tandis que <tt>movl foo,%eax</tt> met le <tt>contenu</tt> de la variable <tt>foo</tt> dans le registre <tt>%eax</tt>. <item> L'indexation ou l'indirection se fait en mettant entre parenthèses le registre d'index ou la case mémoire contenant l'indirection, comme dans <tt>testb $0x80,17(%ebp)</tt> (tester le bit de poids fort de l'octet au déplacement 17 après la case pointée par <tt>%ebp</tt>). </itemize> Un programme existe pour vous aider à convertir des programmes écrits avec la syntaxe TASM en syntaxe AT&T. Voir <url url="ftp://x2ftp.oulu.fi/pub/msdos/programming/convert/ta2asv08.zip"> (puisque le site x2ftp originel est en instance de fermeture, utilisez un <url url="ftp://ftp.lip6.fr/pub/pc/x2ftp/README.mirror_sites" name="site mirroir">). Un programme existe aussi pour la conversion inverse~: <url url="http://www.multimania.com/placr/a2i.html">. GAS possède une documentation complète au format TeXinfo, qui est distribuée entre autres avec les sources. Vous pouvez parcourir les pages .info qui en sont extraites avec Emacs. Il y avait aussi un fichier nommé gas.doc ou as.doc disponible autour des sources de GAS, mais il a été fusionné avec la documentation TeXinfo. Bien sûr, en cas de doute, l'ultime documentation est constituée par les sources elles-mêmes~! Une section qui vous intéressera particulièrement est <tt>Machine Dependencies::i386-Dependent::</tt> Les sources de Linux sont un bon exemple~: regardez dans le répertoire linux/arch/i386 les fichiers suivants~: <tt>kernel/*.S, boot/compressed/*.S, mathemu/*.S</tt> Si vous codez ce genre de choses, un paquetage de threads, etc vous devriez regarder d'autres langages (OCaml, gforth, etc), ou des paquetages sur les thread (QuickThreads, pthreads MIT, LinuxThreads, etc). Enfin générer à partir d'un programme C du code assembleur peut vous montrer le genre d'instructions que vous voulez. Consultez la section <ref id="doyouneedasm" name="Avez-vous besoin de l'assembleur~?"> au début de ce document. <sect2>mode 16 bits limité<p> GAS est un assembleur 32 bits, conçu pour assembler le code produit par un compilateur 32 bits. Il ne reconnaît que d'une manière limité le mode 16 bits du i386, en ajoutant des préfixes 32 bits aux instructions~; vous écrivez donc en réalité du code 32 bits, qui s'exécute en mode 16 bits sur un processeur 32 bits. Dans les deux modes, il gère les registres 16 bits, mais pas l'adressage 16 bits. Utilisez les instructions <tt>.code16</tt> et <tt>.code32</tt> pour basculer d'un mode à l'autre. Notez que l'instruction assembleur en ligne <tt>asm(&dquot;.code16\n&dquot;)</tt> autorisera gcc à générer du code 32 bits qui fonctionnera en mode réél~! Le code nécessaire pour que GAS gère le mode 16 bits aurait été ajouté par Bryan Ford (à confirmer~?). Toutefois, ce code n'est présent dans aucune distribution de GAS que j'ai essayée (jusqu'à binutils-2.8.1.x) ... plus d'informations à ce sujet seraient les bienvenues dans ce HowTo. Une solution bon marché pour insérer quelques instructions 16-bit non reconnues pas GAS consiste à définir des macros (voir plus bas) qui produisent directement du code binaire (avec <tt>.byte</tt>), et ce uniquement pour les rares instructions 16 bits dont vous avez besoin (quasiment aucune, si vous utilisez le <tt>.code16</tt> précédement décrit, et pouvez vous permettre de supposer que le code fonctionnera sur un processeur 32 bits). Pour obtenir le système de codage correct, vous pouvez vous inspirer des assembleurs 16 bits. <sect1>GASP<p> GASP est un préprocesseur pour GAS. Il ajoute des macros et une syntaxe plus souple à GAS. <sect2>Où trouver gasp~?<p> gasp est livré avec gas dans le paquetage binutils GNU. <sect2>Comment fonctionne-t-il~?<p> gasp fonctionne comme un filtre, tout comme cpp et ses variantes. Je ne connais pas les détails, mais il est livré avec sa propre documentation texinfo, donc consultez-la, imprimez-la, assimilez-la. La combinaison GAS/GASP me semble être un macro-assembleur standard. <sect1>NASM<p> Du projet Netwide Assembler est issu encore un autre assembleur i386, écrit en C, qui devrait être assez modulaire pour supporter toutes les syntaxes connues et tous les formats objets existants. <sect2>Où trouver NASM~?<p> <url url="http://www.cryogen.com/Nasm"> Les versions binaires se trouvent sur votre miroir metalab habituel dans le répertoire <tt>devel/lang/asm/</tt>. Il devrait également être disponible sous forme d'archive .rpm ou .deb parmi les contributions à votre distribution préférée RedHat ou Debian. <sect2>Son rôle<p> Au moment de l'écriture de ce HOWTO, la version 0.98 de NASM vient de sortir. <!-- NASM en est à la version 0.98. --> La syntaxe est à la Intel. Une gestion de macros est intégrée. Les formats objets reconnus sont <tt>bin</tt>, <tt>aout</tt>, <tt>coff</tt>, <tt>elf</tt>, <tt>as86</tt>, (DOS) <tt>obj</tt>, <tt>win32</tt>, et <tt>rdf</tt> (leur propre format). NASM peut être utilisée comme assembleur pour le compilateur libre LCC. Comme NASM évolue rapidement, ce HowTo peut ne pas être à jour à son sujet. A moins que vous n'utilisiez BCC comme compilateur 16 bit (ce qui dépasse le cadre de ce document), vous devriez sans aucun doute utiliser NASM plutôt que AS86 ou MASM, car c'est un logiciel libre avec un excellent service après-vente, qui tourne sur toutes plateformes logicielles et matérielles. Note~: NASM est également livré avec un désassembleur, NDISASM. Son analyseur <sq>grammatical</sq>, écrit à la main, le rend beaucoup plus rapide que GAS~; en contrepartie, il ne reconnaît qu'une architecture, en comparaison de la pléthore d'architectures reconnues par GAS. Pour les plates-formes x86, NASM semble être le choix qui s'impose. <sect1>AS86<p> AS86 est un assembleur 80x86, à la fois 16 et 32 bits, faisant partie du compilateur C de Bruce Evans (BCC). Il possède une syntaxe à la Intel. <sect2>Où trouver AS86~?<p> Une version complètement dépassée de AS86 est diffusée par HJLu juste pour compiler le noyau Linux, dans un paquetage du nom de bin86 (actuellement version 0.4) disponible dans le répertoire GCC des sites FTP Linux. Je déconseille son utilisation pour toute autre chose que compiler Linux. Cette version ne reconnaît qu'un format de fichiers minix modifié, que ne reconnaissent ni les binutils GNU ni aucun autre produit. Il possède de plus certains bogues en mode 32 bits. Ne vous en servez donc vraiment que pour compiler Linux. Les versions les plus récentes de Bruce Evans (bde@zeta.org.au) sont diffusées avec la distribution FreeBSD. Enfin, elles l'étaient~! Je n'ai pas pu en retrouver les sources depuis la distribution 2.1. Aussi, j'ai archivé les sources chez moi~: <url url="http://www.tunes.org/~fare/files/asm/bcc-95.3.12.src.tgz"> Le projet Linux/8086 (également appelé ELKS) s'est d'une certaine manière chargé de maintenir bcc (mais je ne crois pas qu'ils aient inclus les patches 32 bits). Voir les sites <url url="http://www.linux.org.uk/ELKS-Home/"> (ou <url url="http://www.elks.ecs.soton.ac.uk">) et <url url="ftp://linux.mit.edu/pub/linux/ELKS/">. Je n'ai pas suivi ces développements, et apprécierait une contribution d'un lecteur à ce sujet. Entre autres choses, ces versions plus récentes, à la différence de celle de HJLu, gèrent le format a.out de Linux~; vous pouvez donc effectuer des éditions de liens avec des programmes Linux, et/ou utiliser les outils habituels provenant du paquetage GNU binutils pour manipuler vos données. Cette version peut co-exister sans problème avec les versions précédentes (voir la question à ce sujet un peu plus loin). La version du 12 mars 1995 de BCC ainsi que les précédentes ont un problème qui provoque la génération de toutes les opérations d'empilement/dépilement de segments en 16 bits, ce qui est particulièrement ennuyant lorsque vous développez en mode 32 bits. J'ai écrit un patch à l'époque où le projet TUNES utilisait as86~: <url url="http://www.tunes.org/~fare/files/asm/as86.bcc.patch.gz">. Bruce Evans a accepté ce patch, mais comme il n'a plus publié de nouvelle version de bcc à ma connaissance, c'est aux développeurs d'ELKS qu'il faut demander de l'intégrer (si ce n'est pas encore fait). <sect2>Comment appeler l'assembleur~? <p> Voici l'entrée d'un Makefile GNU pour utiliser bcc pour transformer un fichier assembleur <tt>.s</tt> à la fois en un objet a.out GNU <tt>.o</tt> et un listing <tt>.l</tt>~: <code> %.o %.l: %.s bcc -3 -G -c -A-d -A-l -A$*.l -o $*.o $< </code> Supprimez <tt>%.l</tt>, <tt>-A-l</tt>, et <tt>-A$*.l</tt>, si vous ne voulez pas avoir de listing. Si vous souhaitez obtenir autre chose que du a.out GNU, consultez la documentation de bcc concernant les autres formats reconnus et/ou utilisez le programme objcopy du paquetage binutils. <sect2>Où trouver de la documentation<p> Les documentations se trouvent dans le paquetage bcc. J'ai récupéré les pages de manuel qui étaient disponibles sur le site de FreeBSD dans <url url="http://www.tunes.org/~fare/files/asm/bcc-95.3.12.src.tgz">. Peut-être les développeurs d'ELKS ont-ils mieux. Dans le doute, les sources sont assez souvent une bonne documentation~: ce n'est pas très commenté mais le style de programmation est très clair. Vous pouvez essayer de voir comment as86 est utilisé dans ELKS ou dans Tunes 0.0.0.25... <sect2>Que faire si je ne peux plus compiler Linux avec cette nouvelle version<p> Linus est submergé par le courrier électronique, et puisque HJLu (responsable officiel de bin86) a choisi de bricoler une version obsolète de as86 plutôt que de construire du code propre autour de la dernière version, je ne pense pas que mon patch pour compiler Linux avec un as86 moderne ait la moindre chance d'être accepté si soumis à nouveau. Peu importe~: conservez le as86 provenant du paquetage bin86 dans le répertoire /usr/bin, et laissez bcc installer le bon as86 en tant que /usr/local/libexec/i386/bcc/as comme il se doit. Vous n'aurez jamais besoin d'appeler explicitement ce dernier, car bcc se charge très bien de tout, y compris la conversion en a.out Linux, lorsqu'il est appelé avec les bonnes options. Assemblez les fichiers uniquement en passant par bcc, et non pas en appelant as86 directement. <sect1>Autres assembleurs <p> Il s'agit d'autres possibilités, qui sortent de la voie ordinaire, pour le cas où les solutions précédentes ne vous conviennent pas (mais je voudrais bien savoir pourquoi~?), que je ne recommande pas dans les cas habituels, mais qui peuvent se montrer fort utiles si l'assembleur doit faire partie intégrante du logiciel que vous concevez (par exemple un système d'exploitation ou un environnement de développement). <sect2>L'assembleur de Win32Forth<p> Win32Forth est un système ANS FORTH 32 bit <em>libre</em> qui fonctionne sous Win32s, Win95, Win/NT. Il comprend un assembleur 32 bit libre (sous forme préfixe ou postfixe) intégrée au langage FORTH. Le traitement des macro est effectué en utilisant toute la puissance du langage réflexif FORTH. Toutefois, le seul contexte d'entrée et sortie reconnu actuellement est Win32Forth lui-même (aucune possibilité d'obtenir un fichier objet, mais vous pouvez toujours ajouter cette possibilité vous-même, bien sûr). Vous pouvez trouver Win32Forth à l'adresse suivante~: <url url="ftp://ftp.forth.org/pub/Forth/Compilers/native/windows/Win32For/">. <sect2>Terse<p> <url url="http://www.terse.com" name="Terse"> est un outil de programmation qui fournit <em>LA</em> syntaxe assembleur la plus compacte pour la famille de processeurs x86~! Cependant, c'est un infâme logiciel exclusif. Il y aurait eu un projet de clone libre quelque part, abandonné à la suite de mensongères allégations de droits de l'auteur sur la syntaxe du langage. Aussi, si vous êtes en mal de projet de programmation amusante tournant autour de l'assembleur, je vous invite à développer un processeur de syntaxe terse pour NASM, si vous aimez cette syntaxe. <sect2>Assembleurs non libres et/ou non 32 bits<p> Vous trouverez un peu plus d'informations sur eux, ainsi que sur les bases de la programmation assembleur sur x86, dans la FAQ de Raymond Moon pour le forum comp.lang.asm.x86~: <url url="http://www2.dgsys.com/˜raymoon/faq/asmfaq.zip">. Remarquez que tous les assembleurs DOS devraient fonctionner avec l'émulateur DOS de Linux ainsi qu'avec d'autres émulateurs du même genre. Aussi, si vous en possédez un, vous pouvez toujours l'utiliser à l'intérieur d'un vrai système d'exploitation. Les assembleurs sous DOS assez récents gèrent également les formats de fichiers objets COFF et/ou des formats gérés par la bibliothèque GNU BFD de telle manière que vous pouvez les utiliser conjointement avec les outils 32 bits libres, en utilisant le programme GNU objcopy (du paquetage binutils) comme un filtre de conversion. <sect>Méta-programmation/macro-traitement<p> La programmation en assembleur est particulièrement pénible si ce n'est pour certaines parties critiques des programmes. Pour un travail donné, il faut l'outil approprié~; ne choisissez donc pas l'assembleur lorsqu'il ne correspond pas au problème à résoudre~: C, OCAML, perl, Scheme peuvent être un meilleur choix dans la plupart des cas. Toutefois, il y a certains cas où ces outils n'ont pas un contrôle suffisamment fin sur la machine, et où l'assembleur est utile ou nécessaire. Dans ces cas, vous apprécierez un système de programmation par macros, ou un système de méta-programmation, qui permet aux motifs répétitifs d'être factorisés chacun en une seule définition indéfiniment réutilisable. Cela permet une programmation plus sûre, une propagation automatique des modifications desdits motifs, etc. Un assembleur de base souvent ne suffit pas, même pour n'écrire que de petites routines à lier à du code C. <sect1>Description<p> Oui, je sais que cette partie peut manquer d'informations utiles à jour. Vous êtes libres de me faire part des découvertes que vous auriez du faire à la dure... <sect2>GCC<p> GCC vous permet (et vous oblige) de spécifier les contraintes entre registres assembleurs et objets C, pour que le compilateur puisse interfacer le code assembleur avec le code produit par l'optimiseur. Le code assembleur en ligne est donc constitué de motifs, et pas forcément de code exact. Ainsi, vous pouvez mettre du code assembleur dans des macro-définitions de CPP ou des fonctions "en-ligne" (inline), de telle manière que tout le monde puisse les utiliser comme n'importe quelle fonction ou macro C. Les fonctions en ligne ressemblent énormément aux macros mais sont parfois plus propres à utiliser. Méfiez-vous car dans tous ces cas, le code sera dupliqué, et donc seules les étiquettes locales (comme <tt>1:</tt>) doivent être utilisées dans ce code assembleur. Toutefois, une macro devrait permettre de passer en paramètre le nom éventuellement nécessaire d'une étiquette définie non localement (ou sinon, utilisez des méthodes supplémentaires de méta-programmation). Notez également que propager du code assembleur en-ligne propagera les bogues potentiels qu'il contiendrait~; aussi, faites doublement attention à donner à GCC des contraintes correctes. Enfin, le langage C lui-même peut être considéré comme étant une bonne abstraction de la programmation assembleur, qui devrait vous soulager de la plupart des difficultés de la programmation assembleur. <sect2>GAS<p> GAS a quelques menues fonctionnalités pour les macro, détaillées dans la documentation TeXinfo. De plus, tandis que GCC reconnaît les fichiers en .s comme de l'assembleur à envoyer dans GAS, il reconnaît aussi les fichiers en .S comme devant être filtrés à travers CPP avant d'être envoyés à GAS. Au risque de me répéter, je vous convie à consulter les sources du noyau Linux. <sect2>GASP<p> Il ajoute toutes les fonctionnalités habituelles de macro à GAS. Voir sa documentation sous forme texinfo. <sect2>NASM<p> NASM possède aussi son système de macros. Consultez sa documentation. Si vous avez quelque idée lumineuse, contactez les auteurs, étant donné qu'ils sont en train de développer NASM activement. Pendant ce même temps, lisez la partie sur les filtres externes un peu plus loin. <sect2>AS86<p> Il possède un système simple de macros, mais je n'ai pas pu trouver de documentation. Cependant, les sources sont d'une approche particulièrement aisée, donc si vous êtes intéressé pour en savoir plus, vous devriez pouvoir les comprendre sans problème. Si vous avez besoin d'un peu plus que des bases, vous devriez utiliser un filtre externe (voir un peu plus loin). <sect2>Autres assembleurs <p> <itemize> <item>Win32FORTH~: CODE et END-CODE sont des macros qui ne basculent pas du mode interprétation au mode compilation~; vous aurez donc accès à toute la puissance du FORTH lors de l'assemblage. <item>TUNES~: cela ne fonctionne pas encore, mais le langage Scheme est un langage de très haut niveau qui permet une méta-programmation arbitraire. </itemize> <sect1>Filtres externes<p> Quelque soit la gestion des macros de votre assembleur, ou quelque soit le langage que vous utilisez (même le C), si le langage n'est pas assez expressif pour vous, vous pouvez faire passer vos fichiers à travers un filtre externe grâce à une règle comme suit dans votre Makefile~: <code> %.s: %.S autres_dépendances $(FILTER) $(FILTER_OPTIONS) < $< > $@ </code> <sect2>CPP <p> CPP n'est vraiment pas très expressif, mais il suffit pour les choses faciles, et il est appelé d'une manière transparente par GCC. Comme exemple de limitation, vous ne pouvez pas déclarer d'objet de façon à ce qu'un destructeur soit automatiquement appelé à la fin du bloc ayant déclaré l'objet. Vous n'avez pas de diversions ou de gestion de portée des variables, etc. CPP est livré avec tout compilateur C. Cependant, au regard de sa médiocrité, fuyez-le si par chance vous pouvez vous passer de C. <sect2>M4<p> M4 vous donne la pleine puissance du macro-traitement, avec un langage Turing-équivalent, récursivité, expressions régulières, etc. Vous pouvez faire avec tout ce que cpp ne peut faire. Voir <url url="ftp://ftp.forth.org/pub/Forth/Compilers/native/unix/this4th.tar.gz" name="macro4th (this4th)"> ou <url url="ftp://ftp.tunes.org/pub/tunes/obsolete/dist/tunes.0.0.0/tunes.0.0.0.25.src.zip" name="les sources de Tunes 0.0.0.25"> comme exemple de programmation avancée en utilisant m4. Toutefois, le système de citation est extrêmement pénible à utiliser et vous oblige à utiliser un style de programmation par fonctions récursives avec passage explicite de continuation (CPS) pour toute programmation <em>avancée</em> (ce qui n'est pas sans rappeler TeX -- au fait quelqu'un a-t-il déjà essayé d'utiliser TeX comme macro-processeur pour autre chose que de la mise-en-page~?). Toutefois, ce n'est pas pire que cpp qui ne permet ni citation ni récursion. La bonne version de m4 à récupérer est GNU m4 1.4 (ou ultérieure si elle existe). C'est celle qui contient le plus de fonctionnalités et le moins de bogues ou de limitations. m4 est conçu pour être intrinsèquement lent pour toute utilisation sauf la plus simple~; cela suffit sans aucun doute pour la plupart des programmes en assembleur (vous n'allez quand même pas écrire des millions de lignes en assembleur, si~?). <sect2>Macro-traitement avec votre propre filtre<p> Vous pouvez écrire votre propre programme d'expansion de macro avec les outils courants comme perl, awk, sed, etc. C'est assez rapide à faire et vous pouvez tout contrôler. Mais toute puissance dans le macro-traitement doit bien se gagner à la dure. <sect2>Méta-programmation<p> Plutôt que d'utiliser un filtre externe qui effectue l'expansion des macros, une manière de réaliser cela est d'écrire des programmes qui écrivent d'autres programmes, en partie ou en totalité. Par exemple, vous pourriez utiliser un programme générant du code source <itemize> <item> pour créer des tables de sinus/cosinus (ou autre), <item> pour décompiler un fichier binaire en source annoté, <item> pour compiler vos bitmaps en des routines d'affichage rapides, <item> pour extraire de la documentation, du code d'initialisation ou finalisation, des tables de descriptions, aussi bien que du code normal depuis les mêmes fichiers sources~; <item> pour utiliser une technique spécifique de production de code, produite avec un script perl/shell/scheme <item> pour propager des données définies en une seule fois dans de nombreux morceaux de code ou tables avec références croisées. <item> etc. </itemize> Pensez-y~! <sect3>Backends provenant de compilateurs<p> Des compilateurs comme GCC, SML/NJ, Objective CAML, MIT-Scheme, CMUCL, etc, ont leur propre générateur de code assembleur, que vous avez le choix d'utiliser, si vous souhaitez générer du code semi-automatiquement depuis les langages correspondants, ou depuis un langage que vous bidouilleriez~: plutôt que d'écrire du code génial en assembleur, modifiez plutôt un compilateur pour qu'il ponde du code génial en assembleur~! <sect3>Le New-Jersey Machine-Code Toolkit<p> Il s'agit projet utilisant le langage de programmation Icon (avec une version expérimentale en ML) pour bâtir une base de code de manipulation d'assembleur. Voir <url url="http://www.cs.virginia.edu/˜nr/toolkit/"> <sect3>TUNES<p> Le <url url="http://www.tunes.org/" name="projet TUNES"> pour un système de calcul réflexif libre développe son propre assembleur comme extension du langage Scheme, en tant que partie de son propre développement. Il ne fonctionne pas du tout à l'heure actuelle, toute aide est la bienvenue. L'assembleur manipule des arbres de syntaxe abstraite, de telle manière qu'il puisse servir comme base d'un traducteur de syntaxe assembleur, d'un désassembleur, d'un générateur de code pour un assembleur ou compilateur, etc. Le fait qu'il utile un vrai langage de programmation puissant comme Scheme le rend imbattable pour le macro-traitement et pour la méta-programmation. <sect>Conventions d'appel<p> <sect1>Linux<p> <sect2>Edition de liens avec GCC<p> C'est la solution la plus pratique. Consultez la documentation de gcc et prenez exemple sur les sources du noyau Linux (fichiers <tt>.S</tt> qui sont utilisés avec gas, non pas as86). Les arguments 32 bits sont empilés dans la pile vers le bas dans l'ordre inverse de l'ordre syntaxique (c'est-à-dire qu'on accède aux arguments ou les dépile dans l'ordre syntaxique), au-dessus de l'adresse de retour 32 bits. <tt>%ebp</tt>, <tt>%esi</tt>, <tt>%edi</tt>, <tt>%ebx</tt> doivent être conservés par l'appelé, les autres registres peuvent être détruits~; <tt>%eax</tt> doit contenir le résultat, ou <tt>%edx:%eax</tt> pour des résultats sur 64 bits. Pile virgule flottante~: je ne suis pas sûr, mais je pense que le résultat se trouve dans <tt>st(0)</tt>, la pile étant à la discrétion de l'appelé. Notez que GCC possède certaines options pour modifier les conventions d'appel en réservant certains registres, en mettant les arguments dans des registres, en supposant que l'on ne possède pas de FPU, etc. Consultez les pages .info concernant le i386. Il faut prendre garde à déclarer l'attribut <tt>cdecl</tt> ou <tt>regparm(0)</tt> pour une fonction qui suit la convention standard GCC. Consultez la documentation GCC dans la section~: <tt>C Extensions::Extended Asm::</tt>. Voir encore la façon dont Linux définit la macro asmlinkage... <sect2>Problèmes ELF et a.out<p> Certains compilateurs C ajoutent un underscore avant tout symbole, alors que d'autres ne le font pas. En particulier, la version GCC a.out effectue ce genre d'ajouts, alors que la version ELF ne le fait pas. Si vous êtes confronté à ce problème, regardez comment des paquetages existants traitent le problèmes. Par exemple, récupérer une ancienne arborescence des sources de Linux, Elks, les qthreads ou OCAML... Vous pouvez également redéfinir le renommage implicite de C en assembleur en ajoutant les instructions suivantes~: <code> void truc asm(&dquot;machin&dquot;) (void); </code> pour s'assurer que la fonction C truc sera réellement appelée machin en assembleur. Remarquez que l'outil <tt>objcopy</tt>, du paquetage <tt>binutils</tt>, devrait vous permettre de transformer vos fichiers objets a.out en objets ELF et peut-être inversement dans certains cas. D'une manière plus générale, il vous permet d'effectuer de nombreuses conversions de formats de fichiers. <sect2>Appels systèmes directs<p> Il n'est absolument pas recommandé d'effectuer de tels appels par ce que leurs conventions peuvent changer de temps en temps, ou d'un type de noyau à un autre (cf L4Linux), de plus, ce n'est pas portable, difficile à écrire, redondant avec l'effort entrepris par libc, et enfin, cela empêche les corrections et les extensions effectuées à travers la libc, comme par exemple avec le programme <tt>zlibc</tt> qui réalise une décompression à la volée de fichiers compressés avec gzip. La manière standard et recommendée d'effectuer des appels systèmes est et restera de passer par la libc. Les objets partagés devraient réduire l'occupation mémoire des programmes, et si vous souhaitez absolument avoir de petits exécutables, utilisez <tt>#!</tt> avec un interpréteur qui contiendra tout ce que vous ne voulez pas mettre dans vos binaires. Maintenant, si pour certaines raisons, vous ne souhaitez pas effectuer une édition des liens avec la libc, récupérez-la et essayez de comprendre comment elle fonctionne~! Après tout, vous prétendez bien la remplacer, non~? Vous pouvez aussi regarder comment <url url="ftp://ftp.forth.org/pub/Forth/Compilers/native/unix/Linux/linux-eforth-1.0e.tar.gz" name="eforth 1.0e"> le fait. Les sources de Linux sont fort utiles, en particulier le fichier d'en-tête asm/unistd.h qui décrit comment sont effectués les appels système... Le principe général est d'utiliser l'instruction <tt>int $0x80</tt> avec le numéro de l'appel système <tt>__NR_</tt>machin (regarder dans <tt>asm/unistd.h</tt>) dans <tt>%eax</tt>, et les paramètres (jusqu'à cinq) dans <tt>%ebx</tt>, <tt>%ecx</tt>, <tt>%edx</tt>, <tt>%esi</tt>, <tt>%edi</tt>. Le résultat est renvoyé dans <tt>%eax</tt> avec un résultat négatif étant l'erreur dont l'opposé est tranféré par la libc dans errno. La pile utilisateur n'est pas modifiée donc n'avez pas besoin d'en avoir une correcte lors de l'appel. Quant aux arguments de démarrage passés au processus, le principe est que la pile contient au démarrage le nombre d'arguments argc, suivi de la liste de pointeurs constituant *argv, suivi de l'environnement sous forme d'une liste terminée par 0 de chaînes variable=valeur chacune terminée par 0. Pour plus de détails, lisez les sources du code de démarrage C crt0.S ou crt1.S de votre libc, les sources de eforth 1.0e, ou ceux du noyau linux (exec.c et binfmt_*.c dans linux/fs/). <sect2>Entrées/sorties matérielles sous Linux<p> Si vous souhaitez effectuer des entrées/sorties directement sous Linux, soit il s'agit de quelque chose de très simple qui n'a pas besoin de spécificités du système et dans ce cas là, consultez le mini-HOWTO <tt>IO-Port-Programming</tt>, ou alors vous devez créer un nouveau gestionnaire de périphérique et vous devriez alors lire quelques documents sur les méandres du noyau, le développement de gestionnaires de périphériques, les modules du noyau, etc, pour lesquels vous trouverez d'excellents HOWTO et autres documents en provenance du LDP. Plus particulièrement, si vous souhaitez réaliser des programmes graphiques, rejoignez les projets <url url="http://www.ggi-project.org/" name="GGI"> ou <url url="http://www.XFree86.org/" name="XFree86"> Certaines personnes ont même fait mieux, écrivant des gestionnaires graphiques compacts et robustes pour XFree86 dans un langage dédié, <url url="http://www.irisa.fr/compose/gal/" name="GAL">, et obtenant une efficacité comparable à celle de gestionnaires écrits à la main en C grâce à de l'évaluation partielle (les gestionnaires ne sont donc non seulement pas écrits en assembleur, mais même pas écrits en C~!). Le problème est que l'évaluateur partiel qu'ils ont utilisé pour obtenir cette efficacité n'est pas lui-même un logiciel libre. Quelqu'un est-il volontaire pour développer une solution de remplacement~? Dans tous les cas, vous devriez plutôt utiliser l'assembleur en ligne de GCC avec les macros provenant des fichiers linux/asm/*.h que d'écrire des sources en assembleur pur. <sect2>Accéder aux gestionnaires 16 bits avec Linux/i386<p> De telles choses sont théoriquement possibles (preuve~: voir comment <url url="http://www.dosemu.org" name="DOSEMU"> permet à des programmes d'accéder sélectivement aux ports matériels), et j'ai entendu des rumeurs que certaines personnes le font (avec le gestionnaire PCI~? Accès aux cartes VESA~? PnP ISA~? Je ne sais pas). Si vous avez de plus amples précisions à ce sujet, soyez les bienvenus. Les bons endroits à regarder sont les sources du noyau Linux, les sources de DOSEMU, ainsi que les sources d'autres programmes bas niveaux (peut-être GGI s'il gère les cartes VESA). En fait, vous devez utiliser soit le mode protégé 16 bits, soit le mode vm86. Le premier est plus simple à configurer mais il ne fonctionne qu'avec du code ayant un comportement propre qui n'effectue pas d'arithmétique de segments ou d'adressage absolu de segment (en particulier pour l'adressage du segment 0), à moins que par chance tous les segments utilisés puissent être configurés à l'avance dans la LDT. La seconde possiblité permet d'être plus <sq>compatible</sq> avec les environnements 16 bits mais elle nécessite une gestion bien plus compliquée. Dans les deux cas, avant de sauter sur le code 16 bits, vous devez~: <itemize> <item>mmap()er toute adresse absolue utilisée dans le code 16 bits (comme la ROM, les tampons vidéo, les adresses DMA et les entrées/sorties passant des zones de mémoires mappées) à partir de /dev/mem dans votre espace d'adressage de votre processus. <item>configurer la LDT et/ou le moniteur en mode vm86. <item>demander au noyau les droits d'accès nécessaires pour les entrées/sorties (voir plus haut). </itemize> Encore une fois, lisez attentivement les codes sources des programmes contribués au projet DOSEMU, en particulier ces mini-émulateurs permettant de faire tourner des programmes ELKS et/ou des .COM assez simples sous Linux/i386. <sect1>DOS<p> La plupart des émulateurs DOS sont livrés avec certaines interfaces d'accès aux services DOS. Lisez leur documentation à ce sujet, mais bien souvent, ils ne font que simuler <tt>int $0x21</tt> et ainsi de suite, donc c'est comme si vous étiez en mode réel (je doute qu'ils aient de possibilités de fonctionner avec des opérandes 32 bits~: ils ne font que réfléchir l'interruption dans le mode réel ou dans le gestionnaire vm86). Certaines documentations concernant DPMI (ou ses variantes) peuvent être trouvées sur <url url="ftp://x2ftp.oulu.fi/pub/msdos/programming/"> (encore une fois, le site x2ftp originel est en instance de fermeture~; aussi utilisez un <url url="ftp://ftp.lip6.fr/pub/pc/x2ftp/README.mirror_sites" name="site mirroir">). DJGPP est livré avec son propre sous-ensemble, dérivé, ou remplacement (limité) de la glibc. Il est possible d'effectuer une compilation croisée de Linux vers DOS. Consultez le répertoire devel/msdos/ de votre miroir FTP de metalab.unc.edu. Voir également le dos-extender MOSS du <url url="http://www.cs.utah.edu/projects/flux/" name="projet Flux"> de l'université d'Utah. D'autres documentations et FAQ sont consacrés à DOS. Nous déconseillons le développement sous DOS. <sect1>Windauberies...<p> Heu, ce document ne traite que de libre logiciel. Téléphonez-moi lorsque Windaube le deviendra ou du moins ses outils de développement~! En fait, après tout, cela existe~: <url url="http://www.cygnus.com" name="Cygnus Solutions"> a développé la bibliothèque cygwin32.dll pour que les programmes GNU puissent fonctionner sur les machines MacroMerdiques. Donc, vous pouvez utiliser GCC, GAS et tous les outils GNU ainsi que bon nombre d'applications Unix. Consultez leur site Web. Je (Faré) ne souhaite pas m'étendre sur la programmation sous Windaube, mais je suis sûr que vous trouverez tout un tas d'informations partout... <sect1>Ton propre système d'exploitation rien qu'à toi<p> Le contrôle sur le système étant ce qui attire de nombreux programmeurs vers l'assembleur, une prémisse ou un corollaire naturel de son utilisation est la volonté de développer son propre système d'exploitation. Remarquons tout d'abord que tout système permettant son auto-développement pourrait être qualifié de système d'exploitation, combien même tournerait-il au-dessus d'un autre système sur lequel il se déchargerait de la gestion du multitâche (Linux sur Mach) ou des entrées/sorties (OpenGenera sur Digital Unix), etc. <!-- --> Donc, pour simplifier le débogage, vous pouvez souhaiter développer votre système d'exploitation comme étant un processus fonctionnant sous Linux (au prix d'un certain ralentissement), puis, utiliser le <url url="http://www.cs.utah.edu/projects/flux/oskit/" name="Flux OS kit"> (qui permet l'utilisation des drivers Linux et BSD dans votre propre système d'exploitation) pour le rendre indépendant. Lorsque votre système est stable, il est toujours temps d'écrire vos propres gestionnaires de matériels si c'est vraiment votre passion. Ce HowTo ne couvrira pas des sujets comme le code de chargement du système, le passage en mode 32 bits, la gestion des interruptions, les bases concernant les horreurs des processeurs Intel (mode protégé, V86/R86), la définition de votre format d'objets ou de vos conventions d'appel. <!-- --> L'endroit où vous pourrez trouver le plus d'informations concernant tous ces sujets est le code source de système déjà existants. Un grand nombre de pointeurs se trouvent dans la page~: <url url="http://www.tunes.org/Review/OSes.html"> <sect>A faire et pointeurs <p> <itemize> <item>trouver quelqu'un qui a assez de temps pour prendre en charge la maintenance de ce HOWTO~; <item>compléter les sections incomplètes~; <item>ajouter des pointeurs sur des programmes et des documentations~; <item>ajouter des exemples de tous les jours pour illustrer la syntaxe, la puissance et les limitation de chacune des solutions proposées~; <item>demander aux gens de me donner un coup de main~; <item>peut-être dire quelques mots sur l'assembleur d'autres architectures que le i386~? <item>Quelques pointeurs (en plus de ceux qui se trouvent déjà dans le reste de ce document) <itemize> <item>Références pour la famille de processeurs 80x86~: <url url="http://www.intel.com/design/pentium/manuals/" name="manuels intel">~; <url url="http://www.xs4all.nl/˜feldmann/86bugs.htm" name="bugs">. <item><url url="ftp://ftp.luth.se/pub/msdos/" name="ftp.luth.se"> possède un mirroir des anciennes archives hornet et x2ftp de code assembleur msdos. <item>Quelques points de départ sur le ouèbe à propos de la programmation en assembleur~: <url url="http://www.fys.ruu.nl/˜faber/Amain.html" name="Jannes Faber">~; <url url="http://www.qzx.com/library/" name="QZX">~; <url url="http://bewoner.dma.be/JanW" name="JanW">~; <url url="ftp://zfja-gate.fuw.edu.pl/user/net/ka9q/guest/" name="(?)"> <item>Pour s'amuser~: <url url="http://www.koth.org" name="CoreWars">, une façon amusante d'apprendre l'assembleur en général. <item>USENET~: <url url="news://comp.lang.asm.x86" name="comp.lang.asm.x86">~; <url url="news://alt.os.assembly" name="alt.os.assembly">. </itemize> <item>Et bien sur, utilisez vos outils habituels de recherche d'information sur Internet, et envoyez-moi tout ce que vous trouverez d'intéressant. </itemize> Signature de l'auteur~: <verb> ## Faré | VN: Уng-Vû Bân | Join the TUNES project! http://www.tunes.org/ ## ## FR: François-René Rideau | TUNES is a Useful, Not Expedient System ## ## Reflection&Cybernethics | Project for a Free Reflective Computing System ## </verb> </article>