assembly - electricas - ¿Qué es el marco de pila en el ensamblaje?
variables electricas (4)
Cada rutina usa una porción de la pila, y lo llamamos un marco de pila. Aunque un programador ensamblador no está obligado a seguir el siguiente estilo, es muy recomendable como buena práctica.
El marco de pila para cada rutina se divide en tres partes: parámetros de funciones, retroceso al marco anterior de la pila y variables locales.
Parte 1: Parámetros de función
Esta parte del marco de pila de una rutina la configura la persona que llama. Usando la instrucción ''push'', la persona que llama empuja los parámetros a la pila. Diferentes idiomas pueden presionar los parámetros en diferentes órdenes. C, si no recuerdo mal, los empuja de derecha a izquierda. Es decir, si llamas ...
foo (a, b, c);
La persona que llama convertirá esto en ...
push c
push b
push a
call foo
A medida que cada elemento se empuja hacia la pila, la pila crece. Es decir, el registro del puntero de la pila se reduce en cuatro (4) bytes (en modo de 32 bits), y el elemento se copia a la ubicación de la memoria apuntada por el registro del puntero de la pila. Tenga en cuenta que la instrucción ''call'' implícitamente empujará la dirección de retorno en la pila. La limpieza de los parámetros se abordará en la Parte 5.
Parte 2: puntero de Stackframe
En este momento, la instrucción ''llamada'' ha sido emitida y ahora estamos al comienzo de la rutina llamada. Si queremos acceder a nuestros parámetros, podemos acceder a ellos como ...
[esp + 0] - return address
[esp + 4] - parameter ''a''
[esp + 8] - parameter ''b''
[esp + 12] - parameter ''c''
Sin embargo, esto puede volverse torpe después de que hayamos hecho espacio para las variables locales y esas cosas. Entonces, usamos un registro de puntero de pila-base además del registro de puntero de pila. Sin embargo, queremos que el registro de la pila-puntero se configure en nuestro cuadro actual, y no en la función anterior. Por lo tanto, guardamos el anterior en la pila (que modifica los desplazamientos de los parámetros en la pila) y luego copiamos el registro de puntero de pila actual en el registro de puntero de pila-base.
push ebp ; save previous stackbase-pointer register
mov ebp, esp ; ebp = esp
A veces puede ver esto hecho usando solo la instrucción ''ENTER''.
Parte 3: Talla de espacio para variables locales
Las variables locales se almacenan en la pila. Como la pila crece, restamos algunos # de bytes (suficientes para almacenar nuestras variables locales):
sub esp, n_bytes ; n_bytes = number of bytes required for local variables
Parte 4: poner todo junto. Se accede a los parámetros usando el registro de puntero de pila-base ...
[ebp + 16] - parameter ''c''
[ebp + 12] - parameter ''b''
[ebp + 8] - parameter ''a''
[ebp + 4] - return address
[ebp + 0] - saved stackbase-pointer register
Se accede a las variables locales utilizando el registro de puntero de pila ...
[esp + (# - 4)] - top of local variables section
[esp + 0] - bottom of local variables section
Parte 5: Limpieza de Stackframe
Cuando dejamos la rutina, el marco de pila debe limpiarse.
mov esp, ebp ; undo the carving of space for the local variables
pop ebp ; restore the previous stackbase-pointer register
A veces puede ver la instrucción ''LEAVE'' reemplazar esas dos instrucciones.
Dependiendo del idioma que estaba usando, puede ver una de las dos formas de la instrucción ''RET''.
ret
ret <some #>
Cualquiera que sea el elegido dependerá de la elección del idioma (o estilo que desee seguir si escribe en ensamblador). El primer caso indica que la persona que llama es responsable de eliminar los parámetros de la pila (con el ejemplo de foo (a, b, c) lo hará a través de ... agregar esp, 12) y es la forma en que ''C'' lo hace eso. El segundo caso indica que la instrucción de retorno mostrará # palabras (o # bytes, no puedo recordar cuál) de la pila cuando vuelva, eliminando así los parámetros de la pila. Si mal no recuerdo, este es el estilo utilizado por Pascal.
Es largo, pero espero que esto te ayude a comprender mejor los fotogramas.
¿Cuál es la estructura de un marco de pila y cómo se utiliza al llamar funciones en el ensamblaje?
El marco de pila x86-32 se crea ejecutando
function_start:
push ebp
mov ebp, esp
por lo que es accesible a través de ebp y se ve como
ebp+00 (current_frame) : prev_frame
ebp+04 : return_address
....
prev_frame : prev_prev_frame
prev_frame+04 : prev_return_address
Hay algunas ventajas de utilizar ebp para los marcos de pila mediante el diseño de instrucciones de ensamblaje, por lo que normalmente se accede a los argumentos y a los locales mediante el registro de ebp.
Esto es diferente según el sistema operativo y el idioma utilizado. Debido a que no hay un formato general para la pila en ASM, lo único que la pila está haciendo en ASM es almacenar la dirección de retorno al hacer una subrutina de salto. Al ejecutar un retorno desde subrutina, la dirección se recoge de la pila y se coloca en el contador de programa (ubicación de la memoria donde se va a realizar la siguiente instrucción de ejecución de la CPU)
Deberá consultar su documentación para el compilador que está utilizando.
Los compiladores (dependiendo del compilador) pueden usar el marco de la pila x86 para pasar parámetros (o indicadores a los parámetros) y devolver valores. Mira this