Pourquoi n’y a-t-il pas d’instruction «sub rsp» dans ce prolog de fonction et pourquoi les parameters de fonction sont-ils stockés avec des décalages de points négatifs?

C’est ce que j’ai compris en lisant certains documents de segmentation mémoire: lorsqu’une fonction est appelée, il existe quelques instructions (appelées fonction prolog) qui sauvegardent le pointeur d’image sur la stack, copient la valeur du pointeur de stack dans le pointeur de base mémoire pour les variables locales.

Voici un code sortingvial que j’essaye de déboguer en utilisant GDB:

void test_function(int a, int b, int c, int d) { int flag; char buffer[10]; flag = 31337; buffer[0] = 'A'; } int main() { test_function(1, 2, 3, 4); } 

Le but du débogage de ce code était de comprendre ce qui se passait dans la stack quand une fonction était appelée: je devais donc examiner la mémoire à différentes étapes de l’exécution du programme (avant d’appeler la fonction et pendant son exécution). Bien que j’aie réussi à voir des choses comme l’adresse de retour et le pointeur de cadre enregistré en examinant le pointeur de base, je ne comprends vraiment pas ce que je vais écrire après le code désassemblé.

Déassembly:

 (gdb) disassemble main Dump of assembler code for function main: 0x0000000000400509 : push rbp 0x000000000040050a : mov rbp,rsp 0x000000000040050d : mov ecx,0x4 0x0000000000400512 : mov edx,0x3 0x0000000000400517 : mov esi,0x2 0x000000000040051c : mov edi,0x1 0x0000000000400521 : call 0x4004ec  0x0000000000400526 : pop rbp 0x0000000000400527 : ret End of assembler dump. (gdb) disassemble test_function Dump of assembler code for function test_function: 0x00000000004004ec : push rbp 0x00000000004004ed : mov rbp,rsp 0x00000000004004f0 : mov DWORD PTR [rbp-0x14],edi 0x00000000004004f3 : mov DWORD PTR [rbp-0x18],esi 0x00000000004004f6 : mov DWORD PTR [rbp-0x1c],edx 0x00000000004004f9 : mov DWORD PTR [rbp-0x20],ecx 0x00000000004004fc : mov DWORD PTR [rbp-0x4],0x7a69 0x0000000000400503 : mov BYTE PTR [rbp-0x10],0x41 0x0000000000400507 : pop rbp 0x0000000000400508 : ret End of assembler dump. 

Je comprends que “l’enregistrement du pointeur de la stack sur la stack” se fait par “push rbp”, “la copie de la valeur du pointeur de la stack dans le pointeur de base” se fait par “mov rbp, rsp” mais ce qui me confond, c’est le manque de “sous-dossier $ n_bytes” pour “économiser de la mémoire pour les variables locales”. J’ai vu cela dans de nombreuses expositions (même dans certains sujets sur stackoverflow).

J’ai aussi lu que les arguments devraient avoir un décalage positif par rapport au pointeur de base (après qu’il ait été rempli avec la valeur du pointeur de stack), car s’ils sont situés dans la fonction appelant et que la stack grandit vers les adresses les plus basses, il est parfaitement logique que le pointeur de base est mis à jour avec la valeur du pointeur de stack que le compilateur retourne dans la stack en ajoutant des nombres positifs. Mais mon code semble les stocker dans un décalage négatif, tout comme les variables locales .. Je ne comprends pas non plus pourquoi elles sont placées dans ces registres (dans l’ensemble) .. ne devraient-ils pas être enregistrés directement dans le répertoire “compensé” “?

Ces différences sont peut-être dues au fait que j’utilise un système 64 bits, mais mes recherches ne m’ont pas mené à quoi que ce soit qui pourrait expliquer ce à quoi je suis confronté.

L’ABI System V pour x86-64 spécifie une red zone de 128 octets en dessous de %rsp . Ces 128 octets appartiennent à la fonction tant qu’elle n’appelle aucune autre fonction (c’est une leaf function ). Les gestionnaires d’interruptions doivent respecter la zone rouge, car ce sont en réalité des appels de fonctions involontaires.
Toutes les variables locales de votre test_function , qui est une fonction feuille, entrent dans la zone rouge, aucun ajustement de %rsp n’est donc nécessaire. (En outre, la fonction n’a aucun effet secondaire visible et serait optimisée pour tout paramètre d’optimisation raisonnable).

Mais mon code semble les stocker dans un décalage négatif, tout comme les variables locales

Les premiers arguments x86_64 sont transmis aux registres, pas à la stack. Ainsi, lorsque rbp est défini sur rsp , ils ne sont pas sur la stack et ne peuvent pas être sur un décalage positif.

Ils sont poussés uniquement à:

  • sauvegarder l’état du registre pour un deuxième appel de fonction.

    Dans ce cas, ce n’est pas nécessaire puisqu’il s’agit d’une fonction feuille .

  • faciliter l’allocation des registres.

    Mais un allocateur optimisé pourrait faire un meilleur travail sans perte de mémoire ici.

La situation serait différente si vous aviez:

  • x86_64 fonction avec beaucoup d’arguments. Ceux qui ne tiennent pas sur les registres vont sur la stack.
  • IA-32, où chaque argument est mis sur la stack.

l’absence d’un “sous-dossier $ n_bytes” pour “économiser de la mémoire pour les variables locales”.

La partie manquante de sub rsp sur la zone rouge de la fonction feuille avait déjà été posée à la question suivante: Pourquoi le prolog de la fonction x86-64 GCC alloue-t-il moins de stack que les variables locales?