Quel est le but du registre RBP dans x86 64 assembleur?
J'essaie donc d'apprendre un peu d'assemblage, parce que j'en ai besoin pour mon cours D'Architecture informatique. J'ai écrit quelques programmes, comme Imprimer la séquence de Fibonacci.
j'ai reconnu que chaque fois que j'écris un programme j'utilise ces 3 lignes (comme j'ai appris en comparant le code d'assemblage généré par gcc
C
équivalent):
pushq %rbp
movq %rsp, %rbp
subq , %rsp
j'ai 2 questions à ce sujet:
- tout d'abord, pourquoi j'utilise
%rbp
? N'est-il pas plus simple de utilisez%rsp
, comme son contenu est déplacé à%rbp
sur la 2ème ligne? - Pourquoi dois-je soustraire quoi que ce soit de <!--4? Je veux dire, ce n'est pas toujours
16
(quand j'étaisprintf
ing ligne 7 ou 8 variables, alors je wold soustraire24
ou28
J'utilise Manjaro 64 bit sur une Machine virtuelle (4 Go de RAM), Processeur Intel 64 bit
2 réponses
rbp
est le pointeur de cadre sur x86_64. Dans votre code généré, il obtient un instantané du pointeur de pile (rsp
) de sorte que, lorsque des ajustements sont apportés à rsp
(c'est-à-dire réserver de l'espace pour les variables locales ou push
les valeurs ing sur la pile), les variables locales et les paramètres de fonction sont encore accessibles à partir d'un décalage constant de rbp
.
un grand nombre de compilateurs offrent l'omission de pointeur de trame comme option d'optimisation; cela rendra l'accès au Code d'assemblage généré variables relatives à rsp
au lieu de cela et de libérer rbp
comme autre registre général pour l'utilisation dans les fonctions.
dans le cas de GCC, que j'imagine que vous utilisez à partir de la syntaxe de l'assembleur AT&T, Ce commutateur est -fomit-frame-pointer
. Essayez de compiler votre code avec ce commutateur et voyez quel code d'assemblage vous obtenez. Vous remarquerez probablement que lorsque vous accédez à des valeurs relatives à rsp
au lieu de rbp
, le décalage du pointeur varie tout au long de la fonction.
Linux utilise le système V ABI pour l'architecture x86-64 (AMD64); voir System V ABI at OSDev Wiki pour plus de détails.
cela signifie la pile pousse vers le bas; les adresses plus petites sont "plus hautes" dans la pile. Les fonctions C typiques sont compilées à
pushq %rbp ; Save address of previous stack frame
movq %rsp, %rbp ; Address of current stack frame
subq , %rsp ; Reserve 16 bytes for local variables
; ... function ...
movq %rbp, %rsp ; \ equivalent to the
popq %rbp ; / 'leave' instruction
ret
La quantité de mémoire réservée pour les variables locales est toujours un multiple de 16 octets, pour garder la pile alignée à 16 octets. Si aucun espace de pile n'est nécessaire pour les variables locales, il n'
(Notez que l'adresse de retour et le précédent %rbp
poussé à la pile sont les deux 8 bytes de taille, 16 bytes au total.)
%rbp
points de l'actuel cadre de pile, %rsp
pointe vers le haut de la pile. Parce que le compilateur connaît la différence entre %rbp
et %rsp
a n'importe quel point de la fonction, il est libre d'utiliser l'un ou l'autre comme base pour les variables locales.
Un cadre de pile est juste le terrain de jeu de la fonction locale: la région de pile que la fonction courante utilise.
versions actuelles de GCC désactivent le cadre de la pile lorsque des optimisations sont utilisées. Cela a du sens, parce que pour les programmes écrits en C, les cadres de pile sont plus utiles pour le débogage, mais pas grand chose d'autre. (Vous pouvez utiliser, par exemple,-O2 -fno-omit-frame-pointer
pour garder les cadres de pile tout en permettant des optimisations autrement, cependant.)
bien que le même ABI s'applique à tous les binaires, quelle que soit leur langue sont écrits en, certains autres langages ont besoin de cadres de pile pour "unwinding" (par exemple, pour "jeter des exceptions" à un ancêtre appelant de la fonction courante); c.-à-d. pour "unwind" cadres de pile qu'une ou plusieurs fonctions peuvent être avortées et le contrôle passé à une fonction ancêtre, sans laisser des trucs inutiles sur la pile.
Lors de la pile d'images sont omis -- -fomit-frame-pointer
pour GCC--, la fonction implementation change essentiellement en
subq , %rsp ; Re-align stack frame, and
; reserve memory for local variables
; ... function ...
addq , %rsp
ret
Parce qu'il n'y est pas cadre de pile (%rbp
est utilisé à d'autres fins, et sa valeur n'est jamais poussée à la pile), chaque appel de fonction pousse seulement l'adresse de retour à la pile, qui est une quantité de 8 octets, donc nous devons soustraire 8 de %rsp
pour garder un multiple de 16. (En général, la valeur soustraite de et ajouté à %rsp
est un multiple Impair de 8.)
les paramètres de fonction sont typiquement passés dans les registres. Voir le lien ABI au début de cette réponse pour plus de détails, mais en bref, intégral les types et les pointeurs sont passés dans les registres %rdi
,%rsi
,%rdx
,%rcx
,%r8
et %r9
, avec des arguments à virgule flottante dans le %xmm0
%xmm7
registres.
Dans certains cas, vous verrez rep ret
au lieu de rep
. Ne pas confondre: le rep ret
signifie exactement la même chose que ret
;rep
le préfixe, bien que normalement utilisé avec les instructions de chaîne (instructions répétées), ne fait rien lorsqu'il est appliqué à la ret
instruction. C'est juste que certains prédicteurs de branche des processeurs AMD n'aiment pas sauter à un ret
instruction, et la solution recommandée est d'utiliser un rep ret
là à la place.
Enfin, j'ai omis zone rouge au-dessus du sommet de la pile (les 128 octets aux adresses inférieures à %rsp
). C'est parce que ce n'est pas vraiment utile pour les fonctions typiques: dans le cas normal d'avoir-stack-frame, vous voudrez que votre contenu local soit dans le cadre de la pile, pour rendre le débogage possible. Dans le cas omit-stack-frame, les exigences d'alignement de la pile signifient déjà que nous devons soustraire 8 de %rsp
, donc inclure la mémoire requise par les variables locales dans cette soustraction ne coûte rien.