Franchir une étape à la fois ...
7ffff7a97759 mov 0x33b780(%rip),%rax # 0x7ffff7dd2ee0
Ce:
Prend l'adresse dans rip
, et ajoute 0x33b780
à elle. À ce stade, rip
contient l'adresse de l'instruction suivante, qui est 0x7ffff7a97760
. Ajoutant 0x33b780
à cela vous donne 0x7ffff7dd2ee0
, qui est l'adresse dans le commentaire.
Il copie la valeur de 8 octets stockée à cette adresse dans rax
.
Sommes d'accord pour appeler cette valeur de 8 octets "le pointeur". Basé sur la valeur de l'adresse, 0x7ffff7dd2ee0
est presque certainement un emplacement sur la pile.
7ffff7a97760 mov (%rax),%rax
Cette copie la valeur de 8 octets stocké à l'adresse dans le pointeur dans rax
.
7ffff7a97763 test %rax,%rax
Ce ET effectue une opération de rax
avec lui-même, en rejetant le résultat, mais en modifiant les drapeaux.
7ffff7a97766 jne 0x7ffff7a9787a
Ce sauts à l'emplacement 0x7ffff7a9787a
si le résultat de cette opération AND binaire est non nul, autrement dit, si la valeur stockée dans rax
est non nul.
Donc, en résumé, cela signifie « trouver la valeur de 8 octets stockés à l'adresse contenue dans le pointeur indiqué par rip
, plus 0x33b780
, et si cette valeur est zéro, saut à l'emplacement 0x7fff7a9787a
». Par exemple, en termes C, le pointeur stocké à 0x7ffff7dd2ee0
peut être un long *
et ce code vérifie si le long
qu'il pointe contient 0
.
son équivalent en C pourrait être quelque chose comme:
long l = 0;
long * p = &l; /* Assume address of p is 0x7ffff7dd2ee0 */
/* Assembly instructions in your question start here */
if (*p == 0) {
/* This would be the instruction after the jne */
/* Do stuff */
}
/* Location 0x7ffff7a9787a would be here, after the if block */
/* Do other stuff */
Voici un programme complet montrant l'utilisation de cette construction, la seule différence étant que nous trouvons notre pointeur en référence au pointeur de cadre, plutôt qu'à la pointeur d'instruction:
.global _start
.section .rodata
iszerostr: .ascii "Value of a is zero\n"
isntzerostr: .ascii "Value of a is not zero\n"
.section .data
a: .quad 0x00 # We'll be testing this for zero...
.section .text
_start:
mov %rsp, %rbp # Initialize rbp
sub $16, %rsp # Allocate stack space
lea (a), %rax # Store pointer to a in rax...
mov %rax, -16(%rbp) # ...and then store it on stack
# Start of the equivalent of your code
mov -16(%rbp), %rax # Load pointer to a into rax
mov (%rax), %rax # Dereference pointer and get value
test %rax, %rax # Compare pointed-to value to zero
jne .notzero # Branch if not zero
# End of the equivalent of your code
.zero:
lea (iszerostr), %rsi # Address of string
mov $19, %rdx # Length of string
jmp .end
.notzero:
lea (isntzerostr), %rsi # Address of string
mov $24, %rdx # Length of string
.end:
mov $1, %rax # write() system call number
mov $1, %rdi # Standard output
syscall # Make system call
mov $60, %rax # exit() system call number
mov $0, %rdi # zero exit status
syscall # Make system call
avec sortie:
[email protected]:~/src/asm$ as -o tso.o tso.s; ld -o tso tso.o
[email protected]:~/src/asm$ ./tso
Value of a is zero
[email protected]:~/src/asm$
Incidemment, la raison du calcul d'un décalage basé sur le pointeur d'instruction est d'améliorer l'efficacité du code indépendant de la position, ce qui est nécessaire pour les bibliothèques partagées.Les adresses de mémoire et les bibliothèques partagées ne se mélangent pas aussi bien, mais si vous savez que le code et les données seront toujours à la même distance, le référencement du code et des données via le pointeur d'instruction vous permet de produire du code relogeable. Sans cette capacité, il est généralement nécessaire d'avoir une couche d'indirection, puisque les branches relatives sont généralement limitées dans la gamme.
Je suppose que "ne rien faire" signifie ne pas sauter? Il fait encore des choses – Shade
@PaulGriffiths cela ressemble à une réponse. – Shade
@Shade: D'accord, commentaire supprimé. –