![]() |
![]() |
![]() |
|||||||||
| Vous êtes ici : Logiciels Libres > Assembleur x86, 1ère partie | |
|
Assembleur x86, 1ère partie vendredi 5 avril 2002, par Emmanuel Saracco Cette série d’articles consacrés au langage d’assemblage pour processeurs x86 sous linux s’adresse à des débutants ayant quelques notions de programmation et ne se veut en aucun cas exhaustive. Une version DocBook/HTML intégrant tous les articles présentés ici est disponible sur people.easter-eggs.org Ceux d’entre vous ayant déjà codé en assembleur [1] sous DOS ou windows seront certainements surpris d’apprendre que la syntaxe qu’ils avaient employée jusqu’à présent n’est pas la seule disponible. En effet, il existe en fait deux styles de notation possibles : Les conventions IntelCes conventions viennent directement du constructeur de microprocesseur ; c’est pourquoi elles sont suivies par la majeure partie des assembleurs modernes. Le code généré est clair et épuré.
Les conventions AT&TCe style de codage est né en même temps qu’Unix et les conventions datent donc de cette époque. Il est un peu perturbant si l’on a pris l’habitude de coder en suivant les conventions Intel, mais on se familiarise assez rapidement avec.
Il existe quelques assembleurs qui permettent d’utiliser les conventions Intel sur de l’Unix, le plus abouti étant Nasm. Dans cette suite d’articles nous utiliserons les conventions de notation Intel et emploierons Nasm pour les raisons suivantes :
A vrai dire, deux bonnes raisons qui auraient pu nous faire pencher pour l’utilisation des conventions AT&T sont :
Néanmoins, nous adressant ici à des débutants en assembleur il ne fallait pas compliquer les choses [2] Qu’est-ce que c’est ?Vous avez sûrement déjà codé avec un langage de haut niveau comme C ou Pascal. Ces langages permettent d’écrire de manière quasi naturelle ce que nous voulons que la machine fasse : un programme C exécutant : « afficher "coucou" » s’écrira [3] :
La même chose en assembleur s’écrirait [4] :
Ce dernier source est tout à fait fonctionnel et autonome. Nous aurions pu également nous aider de la libc et éviter la manipulation directe de l’interruption 0x80, et nous aurions alors eu le mélange de C et d’assembleur suivant [5] :
Lier son code assembleur avec la libc peut-être effectivement très pratique si l’on ne veut pas réinventer la roue ; mais il faut à ce moment se poser la question de savoir si nous n’aurions pas plus vite fait d’intégrer de l’assembleur inline dans un code C. Dans le cadre d’applications « professionnelles » il est néanmoins conseillé de ne pas passer directement par l’interruption logicielle 0x80 pour faire appel aux routinex kernel. En effet, rien ne certifie que les services proposés par cette interruption ne changeront pas — l’équipe de développement kernel conseillant de toujours passer par la libc s’agissant des appels systèmes et se réservant le droit de modifier quoi que ce soit sans crier gare. Pour en savoir plus sur l’interruption 0x80 et les différents appels systèmes. Malgré tout il existe quelques avantages à coder en assembleur « pur » (c’est à dire, à ne pas se lier avec la libc) :
Pour ce qui est de la vitesse d’exécution, il est vrai qu’on arrive rapidement à produire un code moins performant que celui généré par gcc, mais tout dépend de ce qu’on fait de ce pour quoi on utilise l’assembleur. Dans la majeure partie des cas la nécessité d’écrire un programme entier en assembleur ne se posera pas — s’il s’agit d’optimisation, on emploiera plus volontier de l’assembleur inline. Mais si, par exemple, il est vital d’obtenir un binaire très petit, si la RAM disponible est très faible ou encore si l’on ne veut absolument pas se lier avec une librairie comme la libc, alors on pourra se fier à l’assembleur. Quelques explications supplémentaires pourront être trouvées sur Linuxego. Voici en guise d’exemple un tableau récapitulant les différents programmes sourcés ci-dessus et la taille des binaires générés (avant et après un strip [6]) :
On voit que c’est sans conteste le programme écrit en assembleur qui l’emporte au niveau de la taille finale [7]. [1] L’expression exacte serait « langage d’assemblage », l’assembleur étant le programme qui permet de transformer le langage d’assemblage en code binaire ; néanmoins, nous emploierons ici le terme « assembleur » pour nous simplifier la tâche. [2] Nous envisagerons peut-être de faire quelques articles expliquant la convention AT&T par la suite — tout dépendra de la demande :-) [3] Le code est volontairement dépouillé du superflu. Utilisez gcc coucou_c.c -o coucou_c pour compiler cet exemple. [4] Utilisez nasm -f elf coucou.asm; ld -s coucou_asm.o -o coucou_asm pour compiler cet exemple. [5] Utilisez nasm -f elf coucou_asm_libc.asm; gcc coucou_asm_libc.o -o coucou_libc pour compiler cet exemple. [6] Utilisez strip -s mon_prog. Stripper un programme consiste à l’alléger en supprimant les symboles qu’il contient. [7] Pour vous détendre un peu et voir jusqu’ou on peut aller pour faire maigrir un code, jetez un oeil sur A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux |
Dans la même rubrique :
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Proposer un article | Nous contacter | Plan du site | Admin | Accueil |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||