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 Cyril Brulebois (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 duputs
au lieu deprintf
: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 :
et dans
main
l'appel avec lecallq 560 <puts@plt>
: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 gUI (Mastodon) . Évalué à 2.
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 GuieA_7 (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).
Ç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 chimrod (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.