IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

x86 32-bits / 64-bits Assembleur Discussion :

Assembleur depuis un programme C ou C++ [Débutant(e)]


Sujet :

x86 32-bits / 64-bits Assembleur

  1. #1
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 752
    Points
    1 752
    Par défaut Assembleur depuis un programme C ou C++
    Bonsoir,
    quelqu'un aurait-il un exemple d'utilisation d'un mini-code assembleur tout simple depuis
    un code C ou C++ ?

    C'est à but pédagogique. L'idée serait de tester des minis séquences d'assembleur depuis
    un code C afin de voir ce que font les lignes de code assembleur.

    Comme exemple tout bête je pense par exemple à l'addition de deux variables de valeurs
    définies dans le programme C ou C++, lesquelles variables sont ensuite additionnées via
    un code assembleur appelé depuis le code C ou C++ via il me semble __asm, si je ne dis
    pas de bêtise.

    PS : je suis sur un PC Intel, ou sur un Mac.

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    Hello,

    Cela va également dépendre du compilateur que tu utilises. On a déja vu dans ton message précédent que tu as essayé GCC sous Ubuntu, et spécialement l'option « -S » pour obtenir le code assembleur généré.

    Dans un premier temps, tu peux écrire ceci :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
     
    int trentecinq (void)
    {
        __asm ("mov $35,%eax\n");
    }
     
    int main (void)
    {
        printf ("%d\n",trentecinq());
        return 0;
    }

    Le compilateur se plaint parce ta fonction est censée renvoyer un int mais ne contient pas de clause « return ». C'est normal. L'idée est de montrer ici que le fonctionnement « historique » et le plus naturel d'une certaine façon montre que la dernière valeur de l'accumulateur (donc EAX ici) est considérée comme la valeur finale d'une fonction. Comme on le modifie sans que le compilateur le sache, on récupère cette valeur dans la fonction principale.

    Tu peux ensuite essayer ceci :

    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <stdio.h>
     
    int ajoute (int gauche,int droite)
    {
        __asm ("add %0,%1\n" 
              : "=r" (gauche), "=r" (droite) 
              : "0" (gauche), "1" (droite) 
              : );
     
        return droite;
    }
     
    int main (void)
    {
        printf ("5 + 7 = %d\n",ajoute(5,7));
        return 0;
    }

    … mais la syntaxe que tu vois sous l'instruction « add » est propre à GCC et permet d'indiquer des contraintes à celui-ci, lui permettant de savoir quels registres ils doit utiliser et dans lesquels sauver le résultat, tout en préservant l'environnement pour pouvoir y intégrer ton code sans risque. Tu n'es pas obligé de préciser ces contraintes (qui sont assez rébarbatives au départ). Ton code sera bien inséré à l'endroit où tu l'as écrit, puis exécuté, mais le problème de la transmission des données entre C et assembleur se pose toujours et tu ne sais pas quels genres de prologues et d'épilogues le compilateur va ajouter à ta fonction.

    Ceci veut dire que tu ne peux pas écrire directement une fonction entière rien qu'avec des __asm("") inline. Plus précisément, tu peux le faire, mais il y aura toujours du code surnuméraire ajouté par ton compilo.

    Je te conseille plutôt d'utiliser un vrai assembleur tel que NASM ou FASM, d'écrire des codes sources directement en assembleur (et donc des fichiers *.asm), d'essayer de les compiler au format ELF et, enfin, de linker ensemble tes fichiers objets *.o issus de programme C (compilés avec GCC) et assembleur (compilés avec NASM, FASM, YASM, ou un autre).

    Je suis actuellement dans ce genre de projet (j'écris un proto-nano-OS qui boote sur le réseau en PXE et qui fait l'initialisation de tout ce qui m'intéresse sans jamais faire appel aux fonctions du BIOS). C'est pas forcément très difficile, mais ça demande d'avoir déjà une bonne vision du fonctionnement interne des choses.


    Enfin, j'avais déjà dit sur ce forum qu'apprendre l'assembleur sur Intel x86 PC, c'est comme apprendre à voler sur A380. Je pense que la comparaison n'est pas mauvaise et que les proportions sont à peu près respectées entre les deux situations. Les Intel actuels fonctionnent toujours comme les premiers microprocesseurs, mais ils sont beaucoup trop sophistiqués et il y a beaucoup de choses « unifiées » (comme l'instruction MOV) qui sont en général décomposés sur les processeurs plus modestes, ce qui fait qu'on ne comprend pas pourquoi certaines combinaisons ne sont pas possibles.

    En outre, il faut travailler en mode protégé, collaborer avec le système d'exploitation qui est généralement fait pour être exploité en C (et donc construire soi-même ses appels de fonctions) et en se privant dans un premier temps de choses qui font partie des fondamentaux, comme la gestion des interruptions, l'utilisation des ports I/O et l'écriture dans la mémoire vidéo pour bien voir ce qu'il se passe.

    Je te conseille de voir plusieurs micro-processeurs, spécialement des huit bits comme le Z80 et le 6809. Vois aussi comment fonctionnent les PIC, pour voir que même aujourd'hui, on utilise toujours des micro-processeurs à 1 Mhz (oui, Mhz) et que l'on peut écrire du code efficace avec.

    Par exemple, tu trouveras sur http://www.cpcbox.com/ un émulateur Amstrad CPC en ligne. L'ordinateur que tu vois a été très répandu entre 1985 et 1990 et servait à la fois de console de jeu et d'ordinateur de travail.

    Si tu bootes en mode 6128 et que tu tapes « POKE &HF000,25 » pour déposer la valeur 25 à l'adresse « F000 », tu verras que tu tapes directement dans la mémoire vidéo. Dans l'absolu, tu pourrais même utiliser EXEC pour exécuter un programme en langage machine même s'il se trouve dans cette mémoire vidéo.

  3. #3
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 752
    Points
    1 752
    Par défaut
    Je vais étudier tes propositions NASM ou FASM. Il semblerait que ce soit juste
    Linux et Windaube, mais pas pour Mac O$.

    Le gros souci que j'ai est que j'ai besoin d'une solution multi-os pour de pures
    raisons pédagogiques. L'idée est de montrer comment fonctionne un assem-
    -bleur, et non devenir expert en la matière.

    L'utilisation de __asm n'est pas une bonne idée effectivement.

    Je vais continuer à creuser la question.

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    Citation Envoyé par rambc Voir le message
    Le gros souci que j'ai est que j'ai besoin d'une solution multi-os pour de pures raisons pédagogiques.
    Attention ! S'il ne s'agit que de faire la démonstration, tu peux trouver des émulateurs qui fonctionnent partout.

    En revanche, si tu veux écrire un code assembleur qui fonctionne nativement sur toutes les plateformes, alors par nature le projet est voué à l'échec : l'assembleur n'est pas un seul langage universel normalisé, mais le langage du micro-processeur que tu utilises. Ceci veut dire que si, globalement, tous les assembleurs suivent le même principe et les mêmes grandes lignes directrices, leur jeu d'instructions et leur mode d'adressage sont propre à la famille de micro-processeurs que tu utilises.

    En outre, tu es par définition au plus proche de la machine. Donc, même avec un micro-processeur identique, la manière de conduire ton programme sera différente en fonction de l'environnement. Si tu t'en tiens à de la programmation système pure sans accéder toi-même aux I/O ni gérer directement la mémoire, tu pourras faire un programme à peu près portable, sous réserve quand même que la méthode d'invocation des appels système (ex : INT 80h sous Linux) soit disponible sur toutes les plateformes.

    C'est une des nombreuses raisons pour lesquelles pratiquement tout le monde est passé au C.

    Le gros souci que j'ai est que j'ai besoin d'une solution multi-os pour de pures raisons pédagogiques. L'idée est de montrer comment fonctionne un assem-
    -bleur, et non devenir expert en la matière.
    Ah ben il fallait commencer par là ! :-)

    Si tu as une ligne de commande Unix (un Linux), commence déjà avec NASM sur PC et écris le code suivant et sauve-le sous test.asm :

    Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    	use32
     
    	org	7c00h	
     
    	mov	eax,12345678h
    	int	21h

    … puis compile-le avec :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $ nasm test.asm -o test -l test.l
    … et édite le fichier de sortie test.l :

    Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
         1
         2                                          use32
         3
         4                                          org     7c00h
         5
         6 00000000 B878563412                      mov     eax,12345678h
         7 00000005 CD21                            int     21h
         8
         9

    Le premier champ, à gauche, est le numéro de la ligne dans le code source original. Le second est l'adresse d'implantation en mémoire ou, à défaut (comme ici), son offset dans le fichier de sortie ou par rapport au début de la compilation, tout simplement.

    On s'aperçoit bien que « use32 », « org 7c00h », etc. sont des directives d'assemblage. Elles sont prises en charge par le compilateur, mais n'apparaissent pas dans le code objet final.

    On voit également que les vraies instructions sont traduites en codes-opération, à l'exclusion de toute autre fioriture. Chaque paire de chiffres code un octet, en hexadécimal.

    « B8 » (184 en décimal) est le code-opération correspondant à « MOV eax,immediate », ce qui intime l'instruction au micro-processeur de charger son registre EAX avec la valeur suivant immédiatement le code opération (donc, charger une constante littérale). La valeur en question suit effectivement le code-opération et tient sur quatre octets. Comme Intel travaille en Little Endian (ce qui n'est pas le cas de tous les micro-processeurs), on travaille toujours au niveau de l'octet, mais en commençant par le poids faible plutôt que le poids fort. On voit donc la valeur « 1234568h » écrite à l'envers mais par paires de chiffres.

    Le code immédiatement suivant correspond tout naturellement à l'instruction suivante, qui est elle-même codée selon le même principe.

    On peut également constater que le fichier binaire de sortie mesure exactement sept octets, qui correspondent aux codes opérations que tu vois. Donc absolument rien d'autre dans le fichier.

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ ls -l test
    -rw-rw-r-- 1 obsidian obsidian 7 16 oct.  14:48 test

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ xxd test
    0000000: b878 5634 12cd 21                        .xV4..!

    Tout le reste, ensuite, va découler du format de fichier de sortie que tu vas choisir, s'il est pris en charge par ton assembleur et qui va principalement servir à fournir les informations nécessaires pour lier ton fichier à d'autre si nécessaire, puis à le rendre utilisable par ton système d'exploitation.

    Évidemment, ceci n'est que le B-A-BA d'une discipline à part entière, passionnante de surcroît.

  5. #5
    Membre chevronné

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Points : 1 752
    Points
    1 752
    Par défaut
    Merci pour cette réponse détaillé. Je vais étudier cela et me plonger dans
    des tutos pour apprendre un minimum d'assembleur.

    C'est très fascinant de voir que des commandes relativement simples sont
    à la base des programmes complexes que nous utilisons tous les jours.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 3
    Dernier message: 31/08/2008, 16h33
  2. Réponses: 4
    Dernier message: 01/05/2006, 14h37
  3. Réponses: 5
    Dernier message: 20/02/2006, 14h11
  4. Problème sur la commande COPY depuis un programme Java
    Par klereth dans le forum PostgreSQL
    Réponses: 10
    Dernier message: 10/02/2006, 14h14
  5. Réponses: 5
    Dernier message: 14/01/2006, 15h16

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo