¿Por qué no hay instrucciones de "sub rsp" en este prólogo de función y por qué los parámetros de la función se almacenan en compensaciones rbp negativas?

Eso es lo que entendí al leer algunos documentos de segmentación de memoria: cuando se llama a una función, hay algunas instrucciones (llamadas prólogo de función) que guardan el puntero de marco en la pila, copian el valor del puntero de pila en el puntero base y guardan algunos memoria para variables locales.

Aquí hay un código trivial que estoy tratando de depurar usando 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);
}

El propósito de depurar este código era entender lo que sucede en la pila cuando se llama a una función: así que tuve que examinar la memoria en varios pasos de la ejecución del programa (antes de llamar a la función y durante su ejecución). Aunque logré ver cosas como la dirección de retorno y el puntero de cuadro guardado al examinar el puntero base, realmente no puedo entender lo que voy a escribir después del código desmontado.

Desmontaje

(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000400509 <+0>: push   rbp
   0x000000000040050a <+1>: mov    rbp,rsp
   0x000000000040050d <+4>: mov    ecx,0x4
   0x0000000000400512 <+9>: mov    edx,0x3
   0x0000000000400517 <+14>:    mov    esi,0x2
   0x000000000040051c <+19>:    mov    edi,0x1
   0x0000000000400521 <+24>:    call   0x4004ec <test_function>
   0x0000000000400526 <+29>:    pop    rbp
   0x0000000000400527 <+30>:    ret    
End of assembler dump.
(gdb) disassemble test_function 
Dump of assembler code for function test_function:
   0x00000000004004ec <+0>: push   rbp
   0x00000000004004ed <+1>: mov    rbp,rsp
   0x00000000004004f0 <+4>: mov    DWORD PTR [rbp-0x14],edi
   0x00000000004004f3 <+7>: mov    DWORD PTR [rbp-0x18],esi
   0x00000000004004f6 <+10>:    mov    DWORD PTR [rbp-0x1c],edx
   0x00000000004004f9 <+13>:    mov    DWORD PTR [rbp-0x20],ecx
   0x00000000004004fc <+16>:    mov    DWORD PTR [rbp-0x4],0x7a69
   0x0000000000400503 <+23>:    mov    BYTE PTR [rbp-0x10],0x41
   0x0000000000400507 <+27>:    pop    rbp
   0x0000000000400508 <+28>:    ret    
End of assembler dump.

Entiendo que "guardar el puntero del marco en la pila" se hace presionando "push rbp", "copiar el valor del puntero de la pila en el puntero base" se hace con "mov rbp, rsp", pero lo que me confunde es el falta de un "sub rsp $ n_bytes" para "guardar algo de memoria para las variables locales". Lo he visto en muchas exposiciones (incluso en algunos temas aquí en stackoverflow).

También leí que los argumentos deben tener un desplazamiento positivo desde el puntero base (después de que se llena con el valor del puntero de la pila), ya que si se encuentran en la función de llamada y la pila crece hacia direcciones más bajas, tiene mucho sentido que cuando el puntero base se actualiza con el valor del puntero de la pila, el compilador vuelve a la pila agregando algunos números positivos. Pero mi código parece almacenarlos en un desplazamiento negativo, al igual que las variables locales ... Tampoco puedo entender por qué se colocan en esos registros (en el principal) ... ¿no deberían guardarse directamente en el rsp "compensado? "?

Tal vez estas diferencias se deban al hecho de que estoy usando un sistema de 64 bits, pero mis investigaciones no me llevaron a nada que explique lo que estoy enfrentando.

Respuestas a la pregunta(2)

Su respuesta a la pregunta