The Stack
¿Qué es la Pila?
La pila es una región especial de memoria utilizada durante la ejecución de programas para almacenar variables locales y metadatos relacionados con el control de flujo. Es una estructura dinámica que crece y decrece según las necesidades del programa.
En esta sección, aprenderás cómo funciona la pila, cómo se asigna y libera memoria, y cómo se utiliza para gestionar las llamadas a funciones.
Estructura de la Pila
La pila se organiza como una secuencia de bloques de memoria llamados marcos de pila. Cada vez que se llama a una función, se asigna un nuevo marco de pila para almacenar sus variables locales y otros datos necesarios.
Cuando la función termina, su marco de pila se libera automáticamente, devolviendo la memoria al sistema.
Ejemplo de ejecución:
Introduce tu nombre:
>> Juan
Introduce tu edad:
>> 30
Visualización de la pila:
. espacio de pila no asignado . /|\
| ... | |
+-------- 0x7fffffffed80 (RSP) ---------+ | |
buffer -> | 4A | 75 | 61 | 6E | 00 | 00 | 00 | 00 | | |
+---------------------------------------+ | |
| 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | | |
+---------------------------------------+ | |
| 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | | |
+---------------------------------------+ | |
padding -> | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | | |
+---------------------------------------+ |
age -> | 1E | 00 | 00 | 00 | 00 | 00 | 00 | 00 | |
+-------- 0x7fffffffedb0 (RBP) ---------+ |
old RBP -> | D0 | ED | FF | FF | FF | 7F | 00 | 00 | |
+---------------------------------------+ |
return -> | 4C | 18 | 40 | 00 | 00 | 00 | 00 | 00 | |
addr +---------------------------------------+ |
| ... |
. marco de pila previo .
Explorando la Pila con un Depurador
Para comprender cómo se asignan y gestionan los marcos de pila, utilizaremos un depurador. Coloca un punto de interrupción en una función de interés y observa cómo se comporta la pila durante su ejecución.
break nombre_de_funcion
Prólogo de Función
El prólogo de una función es un conjunto de instrucciones que se ejecutan al inicio de la misma. Su propósito es preparar un nuevo marco de pila para la función.
nombre_de_funcion:
push rbp
mov rbp, rsp
sub rsp, 0x30
...
Estas instrucciones realizan las siguientes tareas:
- Guardan el puntero base anterior (
rbp
) en la pila. - Establecen un nuevo puntero base (
rbp
) para el marco de pila actual. - Reservan espacio en la pila para las variables locales de la función.
Puntero de Pila (RSP)
El registro rsp
apunta a la parte superior de la pila. Cada vez que se realiza una operación de push
, el puntero de pila se ajusta para reflejar la nueva asignación de memoria.
push rbp
Internamente, esta instrucción:
- Resta 8 bytes del puntero de pila (
rsp
). - Almacena el valor de
rbp
en la nueva ubicación de la pila.
Puntero Base (RBP)
El registro rbp
actúa como un marcador para el inicio del marco de pila actual. Durante el prólogo de la función, el valor anterior de rbp
se guarda en la pila, y luego se actualiza para apuntar al nuevo marco.
mov rbp, rsp
Esto permite que el programa acceda fácilmente a las variables locales y a los parámetros de la función.
Asignación de Memoria en la Pila
La asignación de memoria en la pila es rápida y eficiente. Para reservar espacio, simplemente se resta un valor del puntero de pila (rsp
).
sub rsp, 0x30
Esto garantiza que la memoria por encima del puntero de pila esté disponible para su uso.
Epílogo de Función
El epílogo de una función es el conjunto de instrucciones que se ejecutan al final de la misma. Su propósito es liberar el marco de pila actual y devolver el control al llamador.
leave
retn
La instrucción leave
realiza las siguientes operaciones:
- Restaura el puntero de pila (
rsp
) al valor del puntero base (rbp
). - Recupera el valor anterior de
rbp
desde la pila.
Finalmente, la instrucción retn
devuelve la ejecución al llamador.