Ceci est un premier jet de moi même et de necromoine, qui recherche des avis quand à la qualité de la rédaction (mais pas de l'orthographe qui sera corrigé ultérieurement) et des explications techniques.
Programmer en Assembleur
1) - Introduction
Le langage d'assemblage, abrégé ASM (soit assembleur, par un abus de langage fortuit que je me permettrait lors ce tutoriel), est le langage le plus bas niveau compréhensible par l'homme. Du temps du pron en pixel art, des bonne grosses disquettes noires, plus brièvement du temps où on se la pétait grave avec quelques Mo de ram, ce langage était l'un des plus utilisés. Mais aujourd'hui avec l'avènement des langages de haut niveau, facilitant la dure tâche du programmeur, une part d'ombre s'est installée. Si bien que les pauvre petits hardcoders dans l'âme se sont réfugiés comme ils le pouvaient pour faire perdurer leur espèce, quitte à forniquer seulement entre eux, créant ainsi une pseudo-élite.
Comme déclaré plus haut, affirmer que l'on programme en assembleur est une faute impardonnable, qui mériterai flagellation à coups d'orties et de ronces. En réalité l'assembleur est le programme qui réalise la coordination entre les mnémoniques et le binaire correspondant, produisant donc le code machine. Le langage d'assemblage quant à lui est le code produit avant l'étape de l'assembleur, c'est à dire les mnémoniques compréhensibles par les idiots que nous sommes.
Je l'ai également dit plus haut, je me permettrai de parler d'assembleur, effectuant ainsi l'indigne et inexcusable erreur. Non pas que j'aime me faire fouetter avec des ronces et des orties, mais c'est une formule plus commode et surtout beaucoup plus utilisée.
Dans ce tutoriel, j'aborderai, ou plutôt je vous ferai assimilez de force, les notions principales de la programmation en langage d'assemblage, en utilisant l'assembleur NASM. Qui exploite la syntaxe Intel.
La totalité de cet écrit se situera sur un système GNU/Linux, pour ainsi combler le manque de documentation française à ce sujet.
2) – les outils
Durant toute notre fulgurante ascension de niveau en assembleur, nous utiliserons différent outils indispensables. Pas seulement un IDE (je vous laisserai relativement libre quant à ce choix), mais aussi et surtout un assembleur, un linker, et bien évidemment un debugger. Ce dernier étant très important. Vous verrez vous même ; vous y passerez plus de temps que sur votre IDE.
2.1) L'assembleur
Souvenez-vous mes chères, on en a déjà parlé. Dans cet incroyable tutoriel nous utiliserons l'assembleur nasm (the netwide assembler).
http://nasm.us/
2.2 ) Le linker
Intéressons nous maintenant à un point plus pointu dans la création d'exécutable, le 'linkage' (ce terme n'est pas traduisible en français, j'utiliserais donc ce barbarisme à chaque fois).
Le linkage est l'étape située après l'assemblage du programme, cette étape consiste à lier les fichiers nécessaire à l'exécutable entre eux, l'exemple le plus simple sont les bibliothèques nécessaire au bon fonctionnement du programme (interface graphique par exemple).
Il existe deux type de linkage, le premier, s'intitule le linkage statique, qui permet d'intégrer au code de l'application, celui de la bibliothèque, ainsi, votre programme de dépendra d'aucun autre fichier et sera plus facile à distribuer. En contre partie, votre exécutable
sera plus lourd car il contiendra forcément plus de donnée.
L'autre type de linkage est, comme vous pouvez le deviner, le linkage dynamique, il fournira un code et un exécutable plus léger, mais il sera nécessaire de distribuer les fichiers de la bibliothèque en même temps que votre programme, le meilleur exemple pour l'illustrer ce linkage sont les DLL de windows, sans lequel les programmes ne fonctionnent pas.
Ainsi dans le cas d'un linkage statique, lors du lancement de l'application cette dernière est certaines de trouver les fonctions nécessaires à son exécution, tandis que sur un linkage dynamique, l'application doit vérifier la présence des fichiers contenant le code de la bibliothèque nécessaire à son fonctionnement.
http://www.tenouk.com/ModuleW_files/...rlinker001.png
Pour linker notre fichier (plus besoin d'expliciter le terme j'espère), nous utiliserons ld, de la famille des GNU binutils, en version 2.22. Soit la dernière à la future lointaine époque d'écriture de ce papier. Si votre version, pour X raison (distrib' pourries mal mise à jour), est obsolète, ne partez pas tout de suite, la différence sera normalement infime, pour l'utilisation que nous en faisons.
2. 3) Le debugger
Une légère parenthèse s'impose. Qu'est-ce qu'un debugger ?
Enfaîte, le concept est assez simple ; un debugger permet, à chaque instant, de voir ce qui ce passe à l'intérieur d'un programme (état des registres, de la mémoire du programme). Littéralement, debugger signifie « qui enlève les bugs »
Celui-ci vous permettra de résoudre vos erreurs. Et croyez-moi, vous en ferez tout un tas.
Nous nous servirons du célèbre et réputé gdb, Le GNU DeBugger, qui doit sûrement être de base installé sur votre distribution. Le cas échéant il se trouve inévitablement dans vos dépôts.
3) – Généralités ennuyeuses
Certains d'entre vous ont probablement bondis sur leur chaise en lisant le terme mnémoniques dans l'introduction et c'est pour cette raison que nous allons nous attarder sur la signification de ce mot.
D'après notre cher Wikipédia, ce terme désigne : 'l’ensemble des méthodes permettant de mémoriser par association d’idées' ce que nous résumerons dans notre domaine spécifique par la traduction de données binaires incompréhensibles par l'homme en 'mot clé' qui nous sont plus familier. En assembleur, on retrouve des termes tels que MOV, JNE, JMP qui désignent des actions effectuées par le programme lors de son exécution.
Une ligne basique d'assembleur en syntaxe Intel, pourrait être résumé comme ceci :
instruction destination, source
L'ensemble des instructions disponible de votre microprocesseur est appelé le jeu d'instruction.
Une instruction pourra être une opération mathématique, un déplacement de valeur (d'une variable à une autre...), etc...
Imaginons une addition ;
add eax, 2
add est l'instruction
eax est la destination
2 est la source
Cette instruction ajoutera donc 2 au registre eax.
3.1) Les registres
Il vient sûrement à votre génial cerveau une digne question ; qu'est-ce qu'un registre ?
Il vient au mien une réponse :
Un registre est une zone de mémoire interne de votre micro-processeur très rapide. Son coût élevé de fabrication nous empêche d'en posséder à profusion.
Normalement, votre processeur en contient un petite dizaine.
Dans ce tutoriel, nous utiliserons des registres 32 bits (soit 4 octets). Dont les principaux : eax, ebx, ecx, edx.
Tout ces registres sont des registres étendues (le e signifie extented), leurs « équivalent » 16bits, seraient ax, bx, cx, dx.
Et chacun de ces registre 16 bits peut être subdivisé en 2 registres 8 bits. Al et ah, bl et bh, cl et ch, dl et dh.
Pour être clair et schématique, ce sont des variables accessibles rapidement, qui vont nous être bien utiles.
3.2) Les systèmes de numération
En informatique, votre ordinateur très simple d'esprit, ne comprend que les chiffres, ainsi lorsque vous tapez un texte, celui-ci est compris par la machine sous forme de numéro, que l'on retrouve dans la table ASCII par exemple (http://www.asciitable.com/).
En nous basant sur cette même table, nous allons étudier les différents systèmes de numération qu'il est nécessaire de connaître avant de continuer la lecture de ce dossier (bien que rien ne vous en empêche, c'est un conseil).
Intéressons nous tout d'abord à la notation décimal, celle que nous utilisons tous les jours, celle de la base 10. La base 10, qu'est ce que c'est ? On appelle base, le nom de caractères différents qui constituent un 'alphabet', c'est à dire qu'a partir des caractères qui composent la base, vous pouvez obtenir n'importe quel caractère. La base 10 s'appelle ainsi car comme vous pouvez le deviner, elle est composée de 10 nombre principaux, c'est à dire de 0 à 9 (et oui, 10 est composé d'un 1 et d'un 0 ). Je ne détaillerais pas plus cette base car si vous avez les compétences pour lire ces lignes, alors vous maîtrisez probablement la base 10.
Le second système de numération dont nous aurons besoin est celui de la base 16 qui correspond au système hexadécimal, composé de 16 caractères, qui sont les suivants (dans l'ordre croissant): 1,2,3,4,5,7,8,9,A,B,C,D,E,F, ainsi la valeur correspondante à 10 en système décimal s'écrirait A en hexadécimal, que l'on retrouve dans la numérotation des offsets par exemple (que nous étudierons plus loin).
Le troisième et dernier système de numérotation que nous aborderons ici est celui du binaire, celui de la base 2 composé uniquement de deux nombres : le 0 et le 1. Ce système de numérotation est celui utilisé par votre ordinateur lorsque qu'il lit les instructions, je ne détaille pas plus ces systèmes de numérotation, car nous aurons l'occasion d'en reparler au fur et à mesure de notre dossier.
Pour résumer, je pense que ce tableau est très pratique :
http://www.positron-libre.com/cours/...exadecimal.gif
Vous utiliserez ces bases à profusion, autant bien les maîtriser.
3.3)- Les sections
Tout fichier binaire est divisé en un certain nombre de sections. Pouvant contenir différents éléments comme du code, des données initialisées ou non-initialisées...
La langage d'assemblage étant très proche du langage binaire, la subdivision sera donc visible et décrite dans votre code.
Voyons les principales :
La section data dans laquelle vous pourrez mettre les variables dont la valeur est connue avant la compilation (des constantes par exemple).
La section bss qui vous permettra de réserver de l'espace mémoire pour une variable que vous utiliserez ultérieurement.
La section text qui contiendra votre code.
Ce qui nous permet de dégager une armature typique d'un programme asm :
section .data
; vos données initialisées
section .bss
; vos données non-initialisées
section .code
; votre code
Le « ; » représente les commentaires (et non la fin d'une instruction).
3.4)- Les syscalls
Un syscall (abréviation de system call), est, comme son nom l'indique, un appel au système, afin de lui faire effectuer une tâche définie. Cette tâche peut être de lire du texte, d'en écrire, de lire un fichier, d'exécuter un programme, etc...
En bref une panoplie de tâches rudimentaires, qui constituent la base de toute – ou quasiment toute – les tâches du système.
Si vous avez déjà des connaissances basiques en programmation vous connaissez le principe des fonctions. Si vous n'avez aucune connaissance en programmation, je pense qu'il serai préférable de s'atteler à un langage un peu moins agressif que celui-ci, histoire de se familiariser avec les notions fondamentales de la programmation.
Rappelons qu'une fonction s'utilise afin d'effectuer une tâche précise, en lui donnant parfois des arguments, soit des données, qui lui serviront pour l'effectuer.
Un syscall est donc une fonction, banale dans son utilisation, un peu différente dans sa structure, qui utilise différent arguments.
Le numéro de la tâche à effectuer se met dans le registre eax.
Le premier argument dans ebx, le second dans ecx, le troisième dans edx, etc...
Puis on appelle le kernel, via le « kernel interrupt », int 80h
L'instruction qui servira à entrer une valeur dans un registre est l'instruction mov
On fera donc :
mov eax, [numéro du syscall]
mov ebx [premier argument]
[…]
int 80h
La valeur de retour du syscall se trouvera dans eax.
La correspondance entre numéro de syscall et fonction peut être retrouvée ici :
http://bluemaster.iu.hio.no/edu/dark.../syscalls.html
Ou dans le fichier /usr/include/asm/unistd.h
Prenons un exemple simple ; écrire du texte.
Le numéro du syscall write est 4.
Le numéro de la sortie standard (votre écran) est 1, et il doit être le premier argument.
La chaîne à écrire doit être le second argument.
La taille de la chaîne donc être le troisième.
Avec ce que nous avons déjà vu, vous devriez être capable d'esquisser le code de ce syscall :
mov eax, 4
mov ebx, 1
mov ecx, chaine
mov edx, TailleChaine
int 80h
Second exemple, que vous devrez utiliser dans ton vos programmes, le syscall exit, pour terminer le programme.
Son numéro est le 1
un seul argument ; le code d'erreur. 0 signifie qu'il n'y a pas d'erreurs.
Soit ;
mov eax, 1
mov ebx, 0
int 80h
Allez, finis le théorique, passons à la pratique.
4) - Hello World
Une fois n'est pas coutume, commençons notre périple par une humble salutation internationale.
Nous avons déjà vu la décomposition des deux syscalls utile pour ce que nous voulons faire (et oui, les exemples n'étaient pas pris au hasard), Il nous suffira de déclarer la chaîne et sa taille dans la section data, et d'utiliser ces syscalls.
section .data
chaine: db "hello world !", 10, 13
tailleChaine: equ $-chaine
section .text
global _start
_start:
mov eax, 4
mov ebx, 1
mov ecx, chaine
mov edx, tailleChaine
int 80h
mov eax, 1
mov ebx, 0
int 80h
3 nouvelles choses :
Pour déclarer une donnée, on utilise son nom, suivi de deux points, de son « type », et de sa valeur.
chaine: db "hello world !", 10, 13
Ici nous déclarons variable nommée chaîne, de valeur "hello world !", 10, 13
10 et 13 sont les valeurs de CR (Carriage Return) et LF (Line Feed), dans la table ascii, qui représentent un saut de ligne.
pour déclarer la taille de la chaîne, nous utilisons une petite astuce , plutôt que de l'écrire en dur. Nous utilisons le symbole $, qui signifie le début du programme, au quel on soustrait la variable chaîne (son adresse), ce qui nous renvoi sa taille.
le global _start représente le point d'entrée du programme (l'oep, Original Entry Point). Là ou il va commencer, et donc le code commence après le _start :
Pour compiler ce programme il faut d'abord l'assembler avec nasm :
$ nasm -f elf hello.asm
Puis le linker avec ld :
$ ld -o hello hello.o -melf_i386
Vous pouvez ensuite le lancer :
$ ./hello
hello world !
C'est fait ! Votre premier programme en langage d'assemblage a été exécuté avec succès.
Programmer en Assembleur
1) - Introduction
Le langage d'assemblage, abrégé ASM (soit assembleur, par un abus de langage fortuit que je me permettrait lors ce tutoriel), est le langage le plus bas niveau compréhensible par l'homme. Du temps du pron en pixel art, des bonne grosses disquettes noires, plus brièvement du temps où on se la pétait grave avec quelques Mo de ram, ce langage était l'un des plus utilisés. Mais aujourd'hui avec l'avènement des langages de haut niveau, facilitant la dure tâche du programmeur, une part d'ombre s'est installée. Si bien que les pauvre petits hardcoders dans l'âme se sont réfugiés comme ils le pouvaient pour faire perdurer leur espèce, quitte à forniquer seulement entre eux, créant ainsi une pseudo-élite.
Comme déclaré plus haut, affirmer que l'on programme en assembleur est une faute impardonnable, qui mériterai flagellation à coups d'orties et de ronces. En réalité l'assembleur est le programme qui réalise la coordination entre les mnémoniques et le binaire correspondant, produisant donc le code machine. Le langage d'assemblage quant à lui est le code produit avant l'étape de l'assembleur, c'est à dire les mnémoniques compréhensibles par les idiots que nous sommes.
Je l'ai également dit plus haut, je me permettrai de parler d'assembleur, effectuant ainsi l'indigne et inexcusable erreur. Non pas que j'aime me faire fouetter avec des ronces et des orties, mais c'est une formule plus commode et surtout beaucoup plus utilisée.
Dans ce tutoriel, j'aborderai, ou plutôt je vous ferai assimilez de force, les notions principales de la programmation en langage d'assemblage, en utilisant l'assembleur NASM. Qui exploite la syntaxe Intel.
La totalité de cet écrit se situera sur un système GNU/Linux, pour ainsi combler le manque de documentation française à ce sujet.
2) – les outils
Durant toute notre fulgurante ascension de niveau en assembleur, nous utiliserons différent outils indispensables. Pas seulement un IDE (je vous laisserai relativement libre quant à ce choix), mais aussi et surtout un assembleur, un linker, et bien évidemment un debugger. Ce dernier étant très important. Vous verrez vous même ; vous y passerez plus de temps que sur votre IDE.
2.1) L'assembleur
Souvenez-vous mes chères, on en a déjà parlé. Dans cet incroyable tutoriel nous utiliserons l'assembleur nasm (the netwide assembler).
http://nasm.us/
2.2 ) Le linker
Intéressons nous maintenant à un point plus pointu dans la création d'exécutable, le 'linkage' (ce terme n'est pas traduisible en français, j'utiliserais donc ce barbarisme à chaque fois).
Le linkage est l'étape située après l'assemblage du programme, cette étape consiste à lier les fichiers nécessaire à l'exécutable entre eux, l'exemple le plus simple sont les bibliothèques nécessaire au bon fonctionnement du programme (interface graphique par exemple).
Il existe deux type de linkage, le premier, s'intitule le linkage statique, qui permet d'intégrer au code de l'application, celui de la bibliothèque, ainsi, votre programme de dépendra d'aucun autre fichier et sera plus facile à distribuer. En contre partie, votre exécutable
sera plus lourd car il contiendra forcément plus de donnée.
L'autre type de linkage est, comme vous pouvez le deviner, le linkage dynamique, il fournira un code et un exécutable plus léger, mais il sera nécessaire de distribuer les fichiers de la bibliothèque en même temps que votre programme, le meilleur exemple pour l'illustrer ce linkage sont les DLL de windows, sans lequel les programmes ne fonctionnent pas.
Ainsi dans le cas d'un linkage statique, lors du lancement de l'application cette dernière est certaines de trouver les fonctions nécessaires à son exécution, tandis que sur un linkage dynamique, l'application doit vérifier la présence des fichiers contenant le code de la bibliothèque nécessaire à son fonctionnement.
http://www.tenouk.com/ModuleW_files/...rlinker001.png
Pour linker notre fichier (plus besoin d'expliciter le terme j'espère), nous utiliserons ld, de la famille des GNU binutils, en version 2.22. Soit la dernière à la future lointaine époque d'écriture de ce papier. Si votre version, pour X raison (distrib' pourries mal mise à jour), est obsolète, ne partez pas tout de suite, la différence sera normalement infime, pour l'utilisation que nous en faisons.
2. 3) Le debugger
Une légère parenthèse s'impose. Qu'est-ce qu'un debugger ?
Enfaîte, le concept est assez simple ; un debugger permet, à chaque instant, de voir ce qui ce passe à l'intérieur d'un programme (état des registres, de la mémoire du programme). Littéralement, debugger signifie « qui enlève les bugs »
Celui-ci vous permettra de résoudre vos erreurs. Et croyez-moi, vous en ferez tout un tas.
Nous nous servirons du célèbre et réputé gdb, Le GNU DeBugger, qui doit sûrement être de base installé sur votre distribution. Le cas échéant il se trouve inévitablement dans vos dépôts.
3) – Généralités ennuyeuses
Certains d'entre vous ont probablement bondis sur leur chaise en lisant le terme mnémoniques dans l'introduction et c'est pour cette raison que nous allons nous attarder sur la signification de ce mot.
D'après notre cher Wikipédia, ce terme désigne : 'l’ensemble des méthodes permettant de mémoriser par association d’idées' ce que nous résumerons dans notre domaine spécifique par la traduction de données binaires incompréhensibles par l'homme en 'mot clé' qui nous sont plus familier. En assembleur, on retrouve des termes tels que MOV, JNE, JMP qui désignent des actions effectuées par le programme lors de son exécution.
Une ligne basique d'assembleur en syntaxe Intel, pourrait être résumé comme ceci :
instruction destination, source
L'ensemble des instructions disponible de votre microprocesseur est appelé le jeu d'instruction.
Une instruction pourra être une opération mathématique, un déplacement de valeur (d'une variable à une autre...), etc...
Imaginons une addition ;
add eax, 2
add est l'instruction
eax est la destination
2 est la source
Cette instruction ajoutera donc 2 au registre eax.
3.1) Les registres
Il vient sûrement à votre génial cerveau une digne question ; qu'est-ce qu'un registre ?
Il vient au mien une réponse :
Un registre est une zone de mémoire interne de votre micro-processeur très rapide. Son coût élevé de fabrication nous empêche d'en posséder à profusion.
Normalement, votre processeur en contient un petite dizaine.
Dans ce tutoriel, nous utiliserons des registres 32 bits (soit 4 octets). Dont les principaux : eax, ebx, ecx, edx.
Tout ces registres sont des registres étendues (le e signifie extented), leurs « équivalent » 16bits, seraient ax, bx, cx, dx.
Et chacun de ces registre 16 bits peut être subdivisé en 2 registres 8 bits. Al et ah, bl et bh, cl et ch, dl et dh.
Pour être clair et schématique, ce sont des variables accessibles rapidement, qui vont nous être bien utiles.
3.2) Les systèmes de numération
En informatique, votre ordinateur très simple d'esprit, ne comprend que les chiffres, ainsi lorsque vous tapez un texte, celui-ci est compris par la machine sous forme de numéro, que l'on retrouve dans la table ASCII par exemple (http://www.asciitable.com/).
En nous basant sur cette même table, nous allons étudier les différents systèmes de numération qu'il est nécessaire de connaître avant de continuer la lecture de ce dossier (bien que rien ne vous en empêche, c'est un conseil).
Intéressons nous tout d'abord à la notation décimal, celle que nous utilisons tous les jours, celle de la base 10. La base 10, qu'est ce que c'est ? On appelle base, le nom de caractères différents qui constituent un 'alphabet', c'est à dire qu'a partir des caractères qui composent la base, vous pouvez obtenir n'importe quel caractère. La base 10 s'appelle ainsi car comme vous pouvez le deviner, elle est composée de 10 nombre principaux, c'est à dire de 0 à 9 (et oui, 10 est composé d'un 1 et d'un 0 ). Je ne détaillerais pas plus cette base car si vous avez les compétences pour lire ces lignes, alors vous maîtrisez probablement la base 10.
Le second système de numération dont nous aurons besoin est celui de la base 16 qui correspond au système hexadécimal, composé de 16 caractères, qui sont les suivants (dans l'ordre croissant): 1,2,3,4,5,7,8,9,A,B,C,D,E,F, ainsi la valeur correspondante à 10 en système décimal s'écrirait A en hexadécimal, que l'on retrouve dans la numérotation des offsets par exemple (que nous étudierons plus loin).
Le troisième et dernier système de numérotation que nous aborderons ici est celui du binaire, celui de la base 2 composé uniquement de deux nombres : le 0 et le 1. Ce système de numérotation est celui utilisé par votre ordinateur lorsque qu'il lit les instructions, je ne détaille pas plus ces systèmes de numérotation, car nous aurons l'occasion d'en reparler au fur et à mesure de notre dossier.
Pour résumer, je pense que ce tableau est très pratique :
http://www.positron-libre.com/cours/...exadecimal.gif
Vous utiliserez ces bases à profusion, autant bien les maîtriser.
3.3)- Les sections
Tout fichier binaire est divisé en un certain nombre de sections. Pouvant contenir différents éléments comme du code, des données initialisées ou non-initialisées...
La langage d'assemblage étant très proche du langage binaire, la subdivision sera donc visible et décrite dans votre code.
Voyons les principales :
La section data dans laquelle vous pourrez mettre les variables dont la valeur est connue avant la compilation (des constantes par exemple).
La section bss qui vous permettra de réserver de l'espace mémoire pour une variable que vous utiliserez ultérieurement.
La section text qui contiendra votre code.
Ce qui nous permet de dégager une armature typique d'un programme asm :
section .data
; vos données initialisées
section .bss
; vos données non-initialisées
section .code
; votre code
Le « ; » représente les commentaires (et non la fin d'une instruction).
3.4)- Les syscalls
Un syscall (abréviation de system call), est, comme son nom l'indique, un appel au système, afin de lui faire effectuer une tâche définie. Cette tâche peut être de lire du texte, d'en écrire, de lire un fichier, d'exécuter un programme, etc...
En bref une panoplie de tâches rudimentaires, qui constituent la base de toute – ou quasiment toute – les tâches du système.
Si vous avez déjà des connaissances basiques en programmation vous connaissez le principe des fonctions. Si vous n'avez aucune connaissance en programmation, je pense qu'il serai préférable de s'atteler à un langage un peu moins agressif que celui-ci, histoire de se familiariser avec les notions fondamentales de la programmation.
Rappelons qu'une fonction s'utilise afin d'effectuer une tâche précise, en lui donnant parfois des arguments, soit des données, qui lui serviront pour l'effectuer.
Un syscall est donc une fonction, banale dans son utilisation, un peu différente dans sa structure, qui utilise différent arguments.
Le numéro de la tâche à effectuer se met dans le registre eax.
Le premier argument dans ebx, le second dans ecx, le troisième dans edx, etc...
Puis on appelle le kernel, via le « kernel interrupt », int 80h
L'instruction qui servira à entrer une valeur dans un registre est l'instruction mov
On fera donc :
mov eax, [numéro du syscall]
mov ebx [premier argument]
[…]
int 80h
La valeur de retour du syscall se trouvera dans eax.
La correspondance entre numéro de syscall et fonction peut être retrouvée ici :
http://bluemaster.iu.hio.no/edu/dark.../syscalls.html
Ou dans le fichier /usr/include/asm/unistd.h
Prenons un exemple simple ; écrire du texte.
Le numéro du syscall write est 4.
Le numéro de la sortie standard (votre écran) est 1, et il doit être le premier argument.
La chaîne à écrire doit être le second argument.
La taille de la chaîne donc être le troisième.
Avec ce que nous avons déjà vu, vous devriez être capable d'esquisser le code de ce syscall :
mov eax, 4
mov ebx, 1
mov ecx, chaine
mov edx, TailleChaine
int 80h
Second exemple, que vous devrez utiliser dans ton vos programmes, le syscall exit, pour terminer le programme.
Son numéro est le 1
un seul argument ; le code d'erreur. 0 signifie qu'il n'y a pas d'erreurs.
Soit ;
mov eax, 1
mov ebx, 0
int 80h
Allez, finis le théorique, passons à la pratique.
4) - Hello World
Une fois n'est pas coutume, commençons notre périple par une humble salutation internationale.
Nous avons déjà vu la décomposition des deux syscalls utile pour ce que nous voulons faire (et oui, les exemples n'étaient pas pris au hasard), Il nous suffira de déclarer la chaîne et sa taille dans la section data, et d'utiliser ces syscalls.
section .data
chaine: db "hello world !", 10, 13
tailleChaine: equ $-chaine
section .text
global _start
_start:
mov eax, 4
mov ebx, 1
mov ecx, chaine
mov edx, tailleChaine
int 80h
mov eax, 1
mov ebx, 0
int 80h
3 nouvelles choses :
Pour déclarer une donnée, on utilise son nom, suivi de deux points, de son « type », et de sa valeur.
chaine: db "hello world !", 10, 13
Ici nous déclarons variable nommée chaîne, de valeur "hello world !", 10, 13
10 et 13 sont les valeurs de CR (Carriage Return) et LF (Line Feed), dans la table ascii, qui représentent un saut de ligne.
pour déclarer la taille de la chaîne, nous utilisons une petite astuce , plutôt que de l'écrire en dur. Nous utilisons le symbole $, qui signifie le début du programme, au quel on soustrait la variable chaîne (son adresse), ce qui nous renvoi sa taille.
le global _start représente le point d'entrée du programme (l'oep, Original Entry Point). Là ou il va commencer, et donc le code commence après le _start :
Pour compiler ce programme il faut d'abord l'assembler avec nasm :
$ nasm -f elf hello.asm
Puis le linker avec ld :
$ ld -o hello hello.o -melf_i386
Vous pouvez ensuite le lancer :
$ ./hello
hello world !
C'est fait ! Votre premier programme en langage d'assemblage a été exécuté avec succès.
Commentaire