Partage
  • Partager sur Facebook
  • Partager sur Twitter

[ASM] Intérêt de l'adressage relatif

Sujet résolu
Anonyme
    14 mars 2018 à 22:24:00

    Salut !

    J'ai pour projet (peut-être) d'écrire un compilateur extrêmement basique, et ce compilateur hypothétique produirait du code assembleur (évidemment le compilateur lui-même sera écrit dans un langage de plus haut niveau). L'idée n'est pas de faire un truc utilisable, juste d'essayer de voir, peu m'importe si je ne le finis jamais.

    Mais je me pose une question depuis longtemps, elle concerne le code "position independent code" (l'adressage relatif en gros). En effet, j'ai toujours écrit mes programmes assembleur de cette façon (grossièrement) :

    section .data
    str db "Hello World", 13, 10, 0
    var dq 1432
    ...
    
    section .text
    ...
    global function
    function:
       ...
       ;Récupérer les variables
       mov rsi, str
       mov rax, [var]
       ...
       ret

    Or, ici, sauf erreur, je fais usage d'un adressage absolu, ce qui ne me paraît pas optimal. J'ai l'impression que ce n'est pas une bonne solution, est-ce qu'il ne faudrait pas utiliser un adressage relatif ?

    Alors je tombe sur des codes comme ça :

    section .data
    str db "Hello World", 13, 10, 0
    var dq 1432
    ...
    
    section .text
    ...
    global function
    function:
       ...
       ;Récupérer les variables
       lea rsi, [rel str]
       mov rax, [rel var]
       ...
       ret

    Mais je ne suis vraiment pas sûr de moi...

    Mes questions :

    • Est-ce code l'adressage relatif est utile ? Ou nécéssaire ?
    • Si oui, alors comment l'utiliser ?
    • Dans quels cas doit-on plutôt utiliser un adressage relatif ?

    Merci d'avance !

    -
    Edité par Anonyme 14 mars 2018 à 22:29:13

    • Partager sur Facebook
    • Partager sur Twitter
      14 mars 2018 à 23:25:52

      Salut,

      Alors, je précise avant de "commencer" que je ne suis pas développeur de tous les jours en assembleur, et que celui que j'utilise (FASM) pourrait avoir des principes différents quant à l'objet de ta question.

      Tout d'abord, il faut savoir que la plupart des compilateurs ne font pas un choix entre adressages absolus, et relatif. Mais se servent des deux (à bon escient évidemment). C'est d'ailleurs un des points des optimisations fournies par le compilo.

      En effet, tout (bon) compilateur (asm/exe souvent) (doivent) détermine(r) les adresses absolues grâce à un schéma d'affectation logique. Celui-ci étant propre a l'OS et à l'architecture. Le modèle est fourni sur les sites par ailleurs.

      Ce qui peut rendre "dangereuse" l'utilisation d'un adressage directe, est l'utilisation que l'on fait des espaces mémoires exploités. Effectivement, certains espaces sont réservés (mais accessibles). Je n'ai pas vraiment d'exemple sous la main, mais il y a notamment ce qui peut "affecter" les interruptions système (d'après ce que j'ai pu lire sur le sujet).

      L'adressage relatif, lui, est comme son nom l'indique : "correspond" aux futures utilisations des données qui vont être faite (et plus que ça...). Et comme l'adresse dépend souvent de l'opération, il est plus simple, par exemple, d'utiliser un adressage relatif lors de jumps. De plus, comme tu le sais surement aussi, l'adressage relatif rend un programme sûr d'être "relocalisable". J'entends par là un chargement correct et n'importe où dans la mémoire. Ce qui peut avoir des avantages.

      L'inconvénient, est que ce type d'adressage "mélange", dans la même page, code ET variables (enfin leurs adresses). Ce qui peut causer plusieurs problèmes, notamment à cause de systèmes matériels plus sophistiqué (par exemple : certains problèmes de compatibilité). J'ai pu lire aussi, que cela avait un ("petit") impacte sur les performances et la rapidité d'exécution, mais ça doit être vraiment minime.

      Enfin voilà, moi-même étant en train de réaliser un compilateur, et n'étant pas un programmeur confirmé en assembleur, j'ai décidé d'utiliser (pour me faciliter la vie, dans un premier temps) l'adressage relatif. Je pense d'ailleurs modifier certaines optimisations.

      BREF.

      Les vrais et bons compilateurs font un alliage (assez solide) entre ces deux types d'adressage. Car chacun ont leurs avantages et inconvénients.

      Si ça t’intéresse, il y a un site sur lequel tu peux voir en temps réel la compilation et le code assembleur (au choix) généré. Le voici.

      J'espère cependant avoir pu t'éclairer.

      -
      Edité par vanaur 14 mars 2018 à 23:28:56

      • Partager sur Facebook
      • Partager sur Twitter

      Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

      Anonyme
        15 mars 2018 à 21:09:04

        Merci de ta réponse rapide !

        En creusant un peu, et par désassemblage, j'ai pu constater que les branchements relatifs sont automatiquement faits par l'assembleur ("far jump" et "near jump"), donc cela semble éliminer un assez gros problème.

        Cependant, pour les données (et notamment les constantes, puisque les variables locales sont passées par la pile donc ne posent pas de problème), je ne vois pas trop comment faire un adressage relatif... Exemple :

        ;;void fct(void)
        ;;
        ;;
        fct:
           push rbp
           mov rbp, rsp
           ...
           Comment accéder à ".a_string" avec adressage relatif ?
           ...
           leave
           ret
        
        .a_string db "Hello World", 13, 10, 0

        Voilà, merci d'avance !

        NOTE : Merci pour le lien du compilateur ! D'ailleurs, eux ne semblent pas s'embêter avec un adressage relatif... Est-ce normal ?

        -
        Edité par Anonyme 15 mars 2018 à 21:19:05

        • Partager sur Facebook
        • Partager sur Twitter
          15 mars 2018 à 21:54:36

          Ca dépend de l'assembleur.

          En FASM par exemple, ça donnerait quelque chose comme ça :

          section '.data' data readable writeable
              a_string db "Hello, world!", 10, 0  ; L'omission du '13' n'a pas de conséquence
          
          section '.code' code readable executable
              fct:
                  push eax      ; En FASM on préférera utiliser ces registres-ci (32 bits selon la table)
                  mov eax, ecx
                  ...
                  mov ebx, [a_string]  ; → De la mémoire à un registre général (32 bits, ici ebx)
                  ...
                  ret

          En NASM (je crois que c'est ton assembleur, non ?), la méthode ne changerait pas beaucoup. Quelque chose comme ça, vite fait :

          a_string db 'Hello, world!', 10, 0
          mov eax, [a_string]

          Cependant, j'ai oublié de le mentionner plus haut, on préférera un adressage relatif aux variable contenant plusieurs éléments, tel que les tableaux. Comme les chaines de caractère par exemple.

          Par contre, ton exemple (.a_string) n'est pas une constante. On pourrait la modifier.

          Sinon, pour avoir accès à une définition constante (bien que modifiable^^), en FASM ça se fait de la même manière que pour une simple variable. Je pense que ça doit être pareille en NASM, mais je ne suis évidemment pas sûr, puisque je n'en fais pas.

          PS: Il est aussi important de garder à l'esprit qu'il ne faut pas utiliser n'importe quel registre.

          -
          Edité par vanaur 15 mars 2018 à 21:55:33

          • Partager sur Facebook
          • Partager sur Twitter

          Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

          Anonyme
            15 mars 2018 à 22:54:31

            Oui effectivement, j'utilise NASM.

            Oui c'est exact, ce n'est pas une constante, mais si on fait le choix de ne pas la modifier, ça sera tout comme. Parce qu'une chaîne de caractères constante, je ne vois pas trop comment faire en assembleur.

            En fait ma question ne porte pas sur le fait d'accéder à une variable, ça, fort heureusement, je sais le faire ! (Et je sais aussi utiliser les registres, sinon je n'envisagerais même pas d'écrire un compilateur vers l'assembleur) Ma question est vraiment ciblée sur l'adressage relatif, dans ton exemple, sauf erreur, a_string sera remplacé par l'adresse de a_string lors de l'assemblage, et donc tu auras un adressage absolu. Le fait de mettre ma chaîne de caractère à côté de la fonction dans la section .text est volontaire, justement pout que la fonction puisse directement accéder à la chaîne de caractère, quelle que soit la zone mémoire dans laquelle elle est chargée, via le "rip relative adressing". J'ai peut-être une idée pour obtenir l'instruction en NASM, il faudra que je vois ça demain.

            Au passage, tu utilises eax et ecx comme pointeurs de pile ? Je n'avais jamais vu ça, c'est pour une raison ? Parce que c'est extrêmement étrange comme pratique, ces registres ne sont pas du tout faits pour ça.

            -
            Edité par Anonyme 15 mars 2018 à 22:55:59

            • Partager sur Facebook
            • Partager sur Twitter
              16 mars 2018 à 14:38:43

              Au final, tout adressage est "absolu". C'est géré par l'OS (et/ou le compilateur). La relativité (pas celle en physique), est juste une manière de concevoir différemment. Ça a un "impact" sur le développement, mais pas sur le fonctionnement. Ou très peu : pour les pages mémoire par exemple.

              PitchPitch a écrit:

              En fait ma question ne porte pas sur le fait d'accéder à une variable, ça, fort heureusement, je sais le faire !

              je me disais aussi ^^'

              PitchPitch a écrit:

              J'ai peut-être une idée pour obtenir l'instruction en NASM, il faudra que je vois ça demain.

              Je voudrais bien voir ça aussi, je ne comprends pas vraiment la question en fait, mais ça a l'aire intéressant.

              PitchPitch a écrit:

              Au passage, tu utilises eax et ecx comme pointeurs de pile ? Je n'avais jamais vu ça, c'est pour une raison ? Parce que c'est extrêmement étrange comme pratique, ces registres ne sont pas du tout faits pour ça.

              Moi aussi ça m'a fait bizarre au début, mais on utilisera effectivement ce qu'on appelle les "registres généraux" pour ce type d'opération. D'ailleurs certains registres ont des noms vraiment spéciaux... Enfin, ça dépend aussi de l'architecture.

              Voici la partie de la documentation FASM qui va avec. (On utilisera aussi 'ebp' comme premier pointeur de pile dans un label par exemple.)

              -
              Edité par vanaur 16 mars 2018 à 14:39:54

              • Partager sur Facebook
              • Partager sur Twitter

              Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

              Anonyme
                16 mars 2018 à 18:57:43

                vanaur a écrit:

                Au final, tout adressage est "absolu". C'est géré par l'OS (et/ou le compilateur). La relativité (pas celle en physique), est juste une manière de concevoir différemment. Ça a un "impact" sur le développement, mais pas sur le fonctionnement. Ou très peu : pour les pages mémoire par exemple.

                Non, il y a bien un adressage relatif et absolu, notamment au niveau des branchements, exemple :

                lbl:
                   xor rax, rax
                   jmp lbl

                Sera traduit par l'assembleur de la façon suivante (en tout cas chez moi, c'est peut-être modifiable via les options de compilation) :

                Si on observe bien le jump (et son opcode), on constate qu'il s'agit d'un branchement relatif, dont la valeur est donnée par un entier signé de 8 bits (ici -5).

                Extrait du manuel AMD64 :

                C'est donc bien un adressage relatif qui est fait (et ça n'a rien à voir avec les pages mémoires), ce qui permet au code de fonctionner, et ce quelle que soit sa position en mémoire.

                J'aimerais en fait faire la même chose avec des chaînes de caractère positionnées près du code de la fonction

                vanaur a écrit:

                PitchPitch a écrit:

                Au passage, tu utilises eax et ecx comme pointeurs de pile ? Je n'avais jamais vu ça, c'est pour une raison ? Parce que c'est extrêmement étrange comme pratique, ces registres ne sont pas du tout faits pour ça.

                Moi aussi ça m'a fait bizarre au début, mais on utilisera effectivement ce qu'on appelle les "registres généraux" pour ce type d'opération. D'ailleurs certains registres ont des noms vraiment spéciaux... Enfin, ça dépend aussi de l'architecture.

                Ce n'est pas tant le fait d'utiliser les GPR, en soit, rbp en rsp (tu m'excuseras, je préfère le 64 bit) font aussi partie des GPR (il y en a 16 en 64 bit), cependant, même un GPR peut avoir un usage spécifique, avec des instructions associées, par exemple rcx est lié au comptage (avec l'instruction "loop" en particulier, qui ne fonctionne qu'avec lui), les registres rbp et rsp sont utilisés respectivement comme pointeur de base de pile, et pointeur de sommet de pile, et avec eux les instructions push/pop/leave/autre qui ne fonctionnent qu'avec eux, c'est justement pour cette raison que je suis très étonné de voir d'autres registres utilisés comme comme pointeurs de pile (si ce n'était qu'une affaire de noms...). Quant à rax/eax/ax, il est utilisé, avec rdx/edx/dx pour, par exemple, les calculs mathématiques (div et mul notamment), donc les utiliser comme pointeurs de pile demande de les sauvegarder dès qu'on réalise une multiplication/division, ce qui me semble extrêmement contraignant, alors qu'on a des registres faits pour ça. D'autant plus que rax est le registre utilisé pour les retours de fonction dans beaucoup de conventions d'appel. Je n'ai pas trouvé dans la doc le passage qui traite de cela, je veux bien que tu me l'indiques si ça ne te dérange pas, j'aimerais comprendre les raisons de cette hérésie !

                À quoi fais-tu référence quand tu parles de "noms vraiment spéciaux" ? Les registres mm/xmm/ymm/zmm ?

                EDIT :

                Je viens de tester avec objdump (pour désassembler en GNU Assembler), car je sais à quoi ressemble un adressage "rip relative" (rip est le pointeur d'instruction) dans cet assembleur, et il semblerait que :

                lea rsi, [rel a_string]

                Donne en GAS :

                lea <distance_to_a_string>(%rip), %rsi

                Donc un bel adressage relatif ! (Il existe finalement !)

                -
                Edité par Anonyme 16 mars 2018 à 21:35:52

                • Partager sur Facebook
                • Partager sur Twitter
                  16 mars 2018 à 21:10:33

                  PitchPitch a écrit:

                  Non, il y a bien un adressage relatif et absolu, notamment au niveau des branchements.

                  Soit j'ai mal compris ce qu'un membre du forum FASM m'a dit (sur Stack overflow; c'était il y a longtemps, mais je n'ai pas retrouvé le poste puisque je ne suis plus sur ce site), soit il y a une grande différence entre FASM et NASM. En effet, de ce que je me souviens, il me disait que "durant l'assemblage, les adresses relatives n'existent plus, et sont remplacées par des adresses absolues." Pour la page de mémoire, tu as probablement raison.

                  PitchPitch a écrit:

                  À quoi fais-tu référence quand tu parles de "noms vraiment spéciaux" ? Les registres mm/xmm/ymm/zmm ?


                  Oui, c'est bien ceux-là. Bhas, je n'ai surement pas l'habitude de les voir, c'est tout xD.

                  Voici une discussion à propos des registres. Elle est encore active.

                  • Partager sur Facebook
                  • Partager sur Twitter

                  Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

                  Anonyme
                    17 mars 2018 à 7:08:39

                    vanaur a écrit:

                    PitchPitch a écrit:

                    Non, il y a bien un adressage relatif et absolu, notamment au niveau des branchements.

                    Soit j'ai mal compris ce qu'un membre du forum FASM m'a dit (sur Stack overflow; c'était il y a longtemps, mais je n'ai pas retrouvé le poste puisque je ne suis plus sur ce site), soit il y a une grande différence entre FASM et NASM. En effet, de ce que je me souviens, il me disait que "durant l'assemblage, les adresses relatives n'existent plus, et sont remplacées par des adresses absolues." Pour la page de mémoire, tu as probablement raison.

                    Si il est écrit dans le manuel AMD64 qu'il existe une instruction "saut relatif", c'est pour tous les assembleurs, et je pense qu'un saut de quelques instructions en avant/arrière sera toujours assemblé en un saut relatif par n'importe quel assembleur qui fait du AMD64 (du moins par défaut), en tout cas c'est ce que j'ai observé et ça semble logique.

                    Après tu as l'air de faire du 32 bits, là j'en sais rien, c'est peut-être autre chose niveau adressage (encore que ça m'etonnerait), ce qui pourrait expliquer la différence. Ou c'est moi qui comprend mal.

                    En fait les registres xmm/ymm/zmm font respectivement 128, 256, et 512 bits (les 512 bits n'existent que sur quelques processeurs I9 ou Xeon Phi chez Intel), ils permettent de faire du SIMD, par exemple pour additionner des vecteurs en une seule fois. Ce ne sont donc pas des registres ordinaires (normal donc que tu ne sois pas habitué à les voir !), ils fonctionnent avec tout un set d'instructions spécifiques (du genre movaps, addps, mulpd...). Comme tu fais un compilateur, c'est quelque chose qui pourrait t'intéresser, tu peux te renseigner sur le SSE et l'AVX si tu souhaites.

                    -
                    Edité par Anonyme 17 mars 2018 à 16:04:57

                    • Partager sur Facebook
                    • Partager sur Twitter
                      17 mars 2018 à 11:16:41

                      En effet, ça a l'air intéressant.

                      Merci pour tes quelques explications au passage !

                      • Partager sur Facebook
                      • Partager sur Twitter

                      Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

                      Anonyme
                        17 mars 2018 à 17:32:06

                        J'ai encore "découvert" un truc qui pourrait t'intéresser (si tu ne le savais pas déjà) : Les "call" naturellement produits par l'assembleur sont eux-même relatifs ! J'ai continué l'observation du code hexadécimal de différentes instructions, et le doute n'est pas permis ! L'adressage n'est pas une adresse absolue mais bien un déplacement relatif, un entier 32 bit signé dans la plupart des cas.

                        Je pense donc que, moi aussi, si je commence un compilateur, ce sera avec un adressage relatif à chaque fois, pour ne pas avoir à m'embêter avec les adresses absolues.

                        -
                        Edité par Anonyme 17 mars 2018 à 17:33:57

                        • Partager sur Facebook
                        • Partager sur Twitter
                          17 mars 2018 à 17:49:50

                          PitchPitch a écrit:

                          J'ai encore "découvert" un truc qui pourrait t'intéresser

                           Si, ça je connaissais :) Je l'avais d'ailleurs rapidement énoncé plus haut :

                          vanaur a écrit:

                          [...] il est plus simple, par exemple, d'utiliser un adressage relatif lors de jumps [...]

                          PitchPitch a écrit:

                          Je pense donc que, moi aussi, si je commence un compilateur, ce sera avec un adressage relatif à chaque fois, pour ne pas avoir à m'embêter avec les adresses absolues.

                          Eh bien je te souhaits bonne chance alors, et bonne continuation !
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

                          [ASM] Intérêt de l'adressage relatif

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown