Forum Programmation.autre aide en assembleur quand je lance objdump -M intel -DTCs ./a.out

Posté par  . Licence CC By‑SA.
Étiquettes : aucune
0
21
juil.
2019

Bonjour à tous,

Je m'interesse depuis peu à l'assembleur grace à la belle commande objdump. Néanmoins je tombe sur quelques problemes de compréhension au niveau assembleur et j'aurais bien besoin de vos lumieres.

Voici l'extrait de code :

    0000000000000540 <_start>:
     540:   31 ed                   xor    ebp,ebp
     542:   49 89 d1                mov    r9,rdx
     545:   5e                      pop    rsi
     546:   48 89 e2                mov    rdx,rsp
     549:   48 83 e4 f0             and    rsp,0xfffffffffffffff0
     54d:   50                      push   rax
     54e:   54                      push   rsp
     54f:   4c 8d 05 ea 01 00 00    lea    r8,[rip+0x1ea]        # 740 <__libc_csu_fini>
     556:   48 8d 0d 73 01 00 00    lea    rcx,[rip+0x173]        # 6d0 <__libc_csu_init>
     55d:   48 8d 3d 1f 01 00 00    lea    rdi,[rip+0x11f]        # 683 <main>
     564:   ff 15 76 0a 20 00       call   QWORD PTR [rip+0x200a76]        # 200fe0 <__libc_start_main@GLIBC_2.2.5>
     56a:   f4                      hlt    
     56b:   0f 1f 44 00 00          nop    DWORD PTR [rax+rax*1+0x0]

Pour commencer dans objdump, je ne vois nullpart ou la fonction <_start> est appelé, je vous juste à un moment :
Contenu de la section .text :

     0540 31ed4989 d15e4889 e24883e4 f050544c  1.I..^H..H...PTL
     0550 8d05ea01 0000488d 0d730100 00488d3d  ......H..s...H.=

et ca contient 0540, mais il n' y a aucune jmp ou call à l'adresse 540. une idée la dessus ?

ensuite je constate que la taille des instructions (dans la fonction start) vaut parfois 2 octets, puis 3, puis 1, puis plus loin 7… comment le processeur peut savoir que l'instruction vaut parfois des tailles différentes, sachant que la taille de mon bus est de 64 bits soit 8 octets, donc on pourrait penser que toutes les intructions vallent 8 octets ?

je constate a la seconde instruction que l'on place au registre 9 la valeur du registre rdx, et à la 3eme instruction, on recupére la derniere valeur de la pile et on l'affecte a rsi, or on a fait aucun push, et on a placé aucune valeur dans des registres encore. Tout ce qu'il y a avant les différentes fonctions du programme c'est des :
Contenu de la section .interp :

Contenu de la section .init :

Contenu de la section .plt :

Contenu de la section .text :

mais je n'ai aucune affectation de registre, ni d'insertion dans la pile.

Et derniere question, je ne vois dans mon code aucun syscall, or j'ai un printf dans mon code, j'ai juste :

    0000000000000520 <printf@plt>:
     520:   ff 25 aa 0a 20 00       jmp    QWORD PTR [rip+0x200aaa]        # 200fd0 <printf@GLIBC_2.2.5>
     526:   68 00 00 00 00          push   0x0
     52b:   e9 e0 ff ff ff          jmp    510 <.plt>

qui renvoie à l'instruction 200fd0 qui a :

    0000000000200fb8 <_GLOBAL_OFFSET_TABLE_>:
      200fb8:       c8 0d 20 00             enter  0x200d,0x0
            ...
      200fd0:       26 05 00 00 00 00       es add eax,0x0
            ...

et c'est tout ce que j'ai, je ne peux rien voir d'autre. Or je devrais pouvoir voir a l'exécution de mon programme via objdump le contenu de printf qui lance l'appel systeme write.

Il y'a beaucoup de question. Donc je vous remercie d'avance pour votre temps.

  • # Formatage, puts, _start, etc.

    Posté par  (site web personnel) . Évalué à 1.

    Ce serait sympa de faciliter la vie des gens qui pourraient vouloir te répondre, en utilisant la syntaxe Markdown à disposition, au moins pour le code que tu cites.

    Pour celles et ceux qui voudraient avoir le code sous la main, il semblerait que ceci dans coucou.c suffise pour avoir des choses similaires, même si j'ai du puts au lieu de printf :

    #include <stdio.h>
    
    int main(void) {
      printf("coucou\n");
      return 0;
    }
    

    Puis gcc -o coucou coucou.c && objdump -S -x coucou | less pour voir plein de choses.

    Côté symboles non définis, on notera en particulier :

    0000000000000000       F *UND*  0000000000000000              puts@@GLIBC_2.2.5
    
    …
    
    0000000000000560 <puts@plt>:
     560:   ff 25 b2 0a 20 00       jmpq   *0x200ab2(%rip)        # 201018 <puts@GLIBC_2.2.5>
     566:   68 00 00 00 00          pushq  $0x0
     56b:   e9 e0 ff ff ff          jmpq   550 <.plt>
    

    et dans main l'appel avec le callq 560 <puts@plt> :

    00000000000006b0 <main>:
     6b0:   55                      push   %rbp
     6b1:   48 89 e5                mov    %rsp,%rbp
     6b4:   48 8d 3d 99 00 00 00    lea    0x99(%rip),%rdi        # 754 <_IO_stdin_used+0x4>
     6bb:   e8 a0 fe ff ff          callq  560 <puts@plt>
     6c0:   b8 00 00 00 00          mov    $0x0,%eax
     6c5:   5d                      pop    %rbp
     6c6:   c3                      retq   
     6c7:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
     6ce:   00 00 
    

    Sinon, _start est le point d'entrée, comme raconté ici par exemple → Introduction to the ELF Format (Part V) : Understanding C start up .init_array and .fini_array sections.

    Si ma réponse est trop loin du code que tu veux inspecter, ce serait bien de citer le code en question plutôt que la sortie de la décompilation de la compilation. ;p

    Debian Consultant @ DEBAMAX

    • [^] # Re: Formatage, puts, _start, etc.

      Posté par  (Mastodon) . Évalué à 2.

      Ce serait sympa de faciliter la vie des gens qui pourraient vouloir te répondre, en utilisant la syntaxe Markdown à disposition, au moins pour le code que tu cites.

      Corrigé dans le post original.

      En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

  • # Éléments de réponse

    Posté par  (site web personnel) . Évalué à 2.

    Si tu veux étudier le langage assembleur pour un programme C dont tu as les sources, tu peux utiliser la sortie assembleur de ton compilateur plutôt que de désassembler un binaire (option -S de gcc il me semble).

    je constate que la taille des instructions (dans la fonction start) vaut parfois 2 octets, puis 3, puis 1, puis plus loin 7… comment le processeur peut savoir que l'instruction vaut parfois des tailles différentes, sachant que la taille de mon bus est de 64 bits soit 8 octets, donc on pourrait penser que toutes les intructions vallent 8 octets ?

    Ça dépend de ton processeur ; sur MIPS 32 bits, toutes les instructions font 32bits par exemple (du coup pour stocker certaines constantes dans le code il faut parfois 2 instructions). Sur x86, la taille des instructions varie ; ça rend leur décodage plus complexe, mais en pratique le code binaire est souvent plus compact. De plus, il ne faut pas oublier que les x86 ont toujours gardé la compatibilité binaire ascendante, et donc ton x86 64bits comprend les instructions 8086/286/386/… et on comprend bien que les instructions du 386 ne sont pas en 64bits. L'ensemble des instructions d'un processeur est appelé ISA, et tu dois pouvoir trouver cette spec' pour le processeur qui t'intéresse.

    Comment le processeur s'y retrouve-t'il ? Comme d'habitude, il lit l'OpCode en début d'instruction, il sait alors ce qu'est l'instruction, et donc sa taille. Les instructions sont chargées dans le processeur depuis le cache mémoire (qui est bien rempli depuis la RAM avec plusieurs instructions et en utilisant efficacement ton bus de 64bits).

  • # Code complet

    Posté par  (site web personnel) . Évalué à 2.

    Peux-tu mettre ton code complet sur godbolt et nous donner le lien du résultat ? Il est possible que le compilateur réalise des optimisations qui changent le code (je pense en particulier à ton syscall masqué).

Suivre le flux des commentaires

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