Forum Programmation.autre adressage relatif en assembleur amd64, je suis perdu

Posté par .
Tags : aucun
2
31
mar.
2012

Bonjour à tous,

J'écris ici car il y a quelque chose qui m'échappe totalement.
en adressage absolu, c'est pas compliqué.

movl data,%eax

en simili C
eax = *data

en adressage indexé non plus

movl 42(%esi),%eax

eax = *(esi + 42)

mais en adressage relatif, je suis perdu

movl data(%rip),%eax
#du code
#ici aussi
movl %eax,data(%rip)

La même instruction est utilisée, pour référencer le même endroit, sauf qu'entre les deux rip a changé. Donc, la sémantique le movl change si rip est l'une des opérandes ?
Je ne comprend pas comment ça marche.
Lors de la phase d'assemblage, les décalage sont recalculés ? (en désassemblant on retrouve des constantes), mais en lisant le fichier octet par octet je me rend compte deux deux instructions semblable à deux endroits différent se retrouvent encodée de la même manière dans le binaire, donc, je ne comprend pas.

Merci beaucoup si vous pouvez m'éclairer.

ajout : effectivement, movl data(%rip),%eax et movl data(%rsi),%eax sont deux instructions différentes, elles ne font même pas la même taille.
En fait, à l'exécution (au linkage ?) il connait la différence entre l'adresse 0 de la première instruction et l'adresse data, et à chaque fois qu'il y a un data(%rpi) il y ajoute cette différence ?

  • # Questions pas très claire

    Posté par (page perso) . Évalué à 3.

    Je n'ai pas compris toutes les questions que tu poses, entre les fautes de frappe sur les registres et les imprécisions… je vais cependant faire de mon mieux.

    Lors de la phase d'assemblage, les décalage sont recalculés ?

    Je ne comprend pas trop ce que tu veux dire… dans le schéma de travail

    Source -> Assemblage -> Fichier objet
    Fichier objets -> Édition des liens -> Fichier final

    beaucoup d'adresses sont inconnues à l'assemblage, donc le fichier objet les recense et elles sont résolues à l'édition des liens. Notamment ton data est inconnue l'assemblage mais l'est à l'édition des liens.

    movl data(%rip),%eax et movl data(%rsi),%eax

    C'est parceque l'architecture Intel est une architecture basée sur des registres spécialisés. Ainsi les registres ne sont pas interchangeables et ont des rôles préassignés. AX est l'accumulateur, CX est le compteur de boucle, SI le source indicator et DI le destination indicator, BP est le base pointer. Ainsi les lectures et écritures sur des adresses mémoire de type {DI,SI,BP} + offset sont des formes attendues et sont représentées par une instruction courte, tandis que si tu utilises un autre registre, le nom du registre doit aussi être codé dans l'instruction. C'est comme si il y avait des instructions

    movl_from_si
    movl_from_bp
    movl_from_generic
    movl_to_di
    movl_to_bp
    movl_to_generic

    deux instructions semblable à deux endroits différent se retrouvent encodée de la même manière dans le binaire,

    De quelles instructions s'agit-il? Sont-elles semblables ou identiques? Si elles sont identiques, c'est plutôt normal non?

    • [^] # Re: Questions pas très claire

      Posté par . Évalué à 1.

      movl data(%rip),%eax 
      nop
      nop
      nop
      nop
      movl data(%rip),%eax
      
      

      Désolé pour la forme du premier message, je sais pas pourquoi à chaque fois que je tape rip je me dis « Non, ça peut pas être ça » (je l'ai corrigé changé partout) bref !

      La première et la dernière instruction sont identiques, en tout point. Elles font la même chose, et pourtant si %rip vaut 0 lors de la première instruction, lors de la dernière il vaut 10.

      La question c'est comment est-ce que le processeur fait pour calculer des positions fixes dans en mémoire en utilisant rip qui change à chaque instructions traitées ?

      Please do not feed the trolls

      • [^] # Re: Questions pas très claire

        Posté par (page perso) . Évalué à 2. Dernière modification le 01/04/12 à 23:25.

        Mh… j'ai toujours quelques problèmes. Tu parles de deux choses: le code assembleur et sa traduction en binaire, mais tu ne donnes que l'une…

        La première et la dernière instruction sont identiques, en tout point. Elles font la même chose, et pourtant si %rip vaut 0 lors de la première instruction, lors de la dernière il vaut 10.

        J'ai l'impression que tu confonds le registre et son contenu.

        La question c'est comment est-ce que le processeur fait pour calculer des positions fixes dans en mémoire en utilisant rip qui change à chaque instructions traitées?

        > objdump -d a.out
        
        a.out:     file format elf64-x86-64-freebsd
        
        Disassembly of section .text:
        
        0000000000000000 <.text>:
           0:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 0x6
           6:   90                      nop    
           7:   90                      nop    
           8:   90                      nop    
           9:   90                      nop    
           a:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 0x10
        
        

        Ce qui est commun aux instructions c'est le registre, pas son contenu.

        • [^] # Re: Questions pas très claire

          Posté par . Évalué à 1. Dernière modification le 02/04/12 à 00:05.

          Lors de la première instruction, rip contient 0, lors de la seconde instruction rip contient 10.
          Et pourtant, le résultat de ces deux instructions est de mettre ce qu'il y a dans la mémoire à l'adresse data dans %eax. Non ?
          Je ne comprend pas comment il fait pour calculer.
          rip c'est bien le compteur ordinal ? Raw Instruction Pointer. Autrement dit, le contenu de ce registre change à chaque nouvelle instructions.

          Un peu de vrai code :

          int a = 0;
          void f(void){
            a = a + a;
          }
          
          

          l'assembleur généré :

              .file   "a.c"
              .globl  a
              .bss
              .align 4
              .type   a, @object
              .size   a, 4
          a:
              .zero   4
              .text
              .globl  f
              .type   f, @function
          f:
          .LFB0:
              .cfi_startproc
              pushq   %rbp
              .cfi_def_cfa_offset 16
              .cfi_offset 6, -16
              movq    %rsp, %rbp
              .cfi_def_cfa_register 6
              movl    a(%rip), %edx
              movl    a(%rip), %eax
              addl    %edx, %eax
              movl    %eax, a(%rip)
              popq    %rbp
              .cfi_def_cfa 7, 8
              ret
              .cfi_endproc
          .LFE0:
              .size   f, .-f
              .ident  "GCC: (GNU) 4.6.3 20120306 (Red Hat 4.6.3-2)"
              .section    .note.GNU-stack,"",@progbits
          
          

          et le binaire généré a.o :

          a.o:     file format elf64-x86-64
          
          
          Disassembly of section .text:
          
          0000000000000000 <f>:
             0:   55                      push   %rbp
             1:   48 89 e5                mov    %rsp,%rbp
             4:   8b 15 00 00 00 00       mov    0x0(%rip),%edx        # a <f+0xa>
             a:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 10 <f+0x10>
            10:   01 d0                   add    %edx,%eax
            12:   89 05 00 00 00 00       mov    %eax,0x0(%rip)        # 18 <f+0x18>
            18:   5d                      pop    %rbp
            19:   c3                      retq   
          
          

          Dans le AMD64 Architechture Programmer's Manual v1 on lit :

          The 64-bit RIP instruction-pointer register contains the address of the next instruction to be executed.

          Autrement dit la valeur contenue dans ce registre change après chaque instruction traitée par le processeur.

          Et pourtant dans mon code C, il n'y a qu'un seul 'a', qui a une position fixe dans la mémoire. Et dans le code assembleur généré, et dans le binaire il est question du registre rip pour y accéder, avec la même syntaxe que pour le l'adressage indexé. mov offset(registre),registre. Bref, on accède à une position fixe grâce à un registre dont la valeur qu'il contient change après chaque instruction traitée.
          La question : Comment ça marche ?

          Please do not feed the trolls

          • [^] # Re: Questions pas très claire

            Posté par (page perso) . Évalué à 2.

            Regarder le fichier .o n'a pas beaucoup de sens. Tant que l'édition de liens n'a pas été faite, les offsets des différentes instructions sont généralement à zéro.

            Après application des relocations par le linker, tu devrais voir des 0xtruc(%rip), avec "truc" qui change, de manière à toujours pointer au même endroit.

              400494:       55                      push   %rbp
              400495:       48 89 e5                mov    %rsp,%rbp
              400498:       8b 15 e2 03 20 00       mov    0x2003e2(%rip),%edx        # 600880 <a>
              40049e:       8b 05 dc 03 20 00       mov    0x2003dc(%rip),%eax        # 600880 <a>
              4004a4:       01 d0                   add    %edx,%eax
              4004a6:       89 05 d4 03 20 00       mov    %eax,0x2003d4(%rip)        # 600880 <a>
              4004ac:       5d                      pop    %rbp
              4004ad:       c3                      retq   
            
            
            • [^] # Re: Questions pas très claire

              Posté par . Évalué à 1.

              D'accord, je vois ! Tout s’éclaircit

              Dans mon esprit d'édition de lien c'était pas grand chose de plus que plein ce copie de code dans un gros fichier exécutable.

              Merci beaucoup.

              Please do not feed the trolls

  • # Confusion ?

    Posté par (page perso) . Évalué à 5. Dernière modification le 01/04/12 à 12:51.

    Je n'ai as bien compris ton problème. Ton explication n'est pas claire, et c'est peut-être dû à une confusion que tu fais.

    movl 42(%esi),%eax
    movl data(%esi),%eax
    
    

    Dans cet exemple, c'est exactement la même instruction dans les deux cas. "data" est une constante que tu as défini précédemment dans ton code.
    Il n'y a pas d'adressage relatif pour les données. Il y en a pour les sauts (jmp et call), probablement pour gagner de la place. Mais pas pour les données.
    Adressage relatif = par rapport au pointeur d'instruction
    Tu ne peux pas dire que tu veux accéder à une donnée située 42 octets plus loin que le pointeur d'instruction.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.