2010-09-13 11 views

Respuesta

11

El marco de pila x86-32 se crea mediante la ejecución de

function_start: 
    push ebp 
    mov ebp, esp 

por lo que es accesible a través de ebp y se parece a

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 marcos de pila por el diseño de instrucciones de montaje , por lo tanto, se accede a los argumentos y a los lugareños con el registro de ebp.

+0

Esto me parece incorrecto - no se encuentra la dirección de remitente en ebp + 04 (ver la respuesta más votada)? – Suma

+0

@Suma, sí, así es - arreglado. – Abyx

0

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. Consulte this

2

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 la 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.

109

Cada rutina utiliza una parte de la pila, y la 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 la pila para cada rutina se divide en tres partes: parámetros de función, retroceder al marco anterior de la pila y variables locales.

Parte 1: Parámetros de la función

Esta parte del marco de pila de una rutina está configurado por 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 usted está llamando ...

foo (a, b, c); 

El llamante convertir esto en ...

push c 
push b 
push a 
call foo 

A medida que cada elemento se inserta en la pila, la pila crece hacia abajo. 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: StackFrame volver puntero

En este punto en el tiempo, la instrucción 'llamada' se ha emitido y ahora nos encontramos en el inicio de la llamada rutina. Si queremos acceder a nuestros parámetros, podemos acceder a ellos les gusta ...

[esp + 0] - return address 
[esp + 4] - parameter 'a' 
[esp + 8] - parameter 'b' 
[esp + 12] - parameter 'c' 

Sin embargo, esto puede ser torpe después de que dejan espacio para las variables locales y otras 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 

Algunas veces puede ver esto hecho usando solo la instrucción 'ENTER'.

Parte 3: Talla de espacio para las variables locales

Las variables locales se almacenan en la pila. Dado que la pila crece hacia abajo, restamos algunos # de bytes (lo suficiente para guardar las variables locales):

sub esp, n_bytes ; n_bytes = number of bytes required for local variables

Parte 4: Poner todo junto. parámetros se accede mediante el registro stackbase triple ...

[ebp + 16] - parameter 'c' 
[ebp + 12] - parameter 'b' 
[ebp + 8] - parameter 'a' 
[ebp + 4] - return address 
[ebp + 0] - saved stackbase-pointer register 

Las variables locales se accede mediante el registro de pila triple ...

[esp + (# - 4)] - top of local variables section 
[esp + 0]  - bottom of local variables section 

Parte 5: StackFrame limpieza

Cuando salgamos de 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 'DEJAR' reemplazando 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 elegido dependerá de la elección de la lengua (o estilo que desea seguir si escribir 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 lo ayude a comprender mejor las estructuras de pila.

+1

+1 - Muy buena respuesta. ¿Puedes recomendar algunos buenos libros para estas partes? –

+0

Como rara vez leo libros de programación, no tengo ninguna recomendación. La información anterior es solo algunas de las cosas que recuerdo de la escuela hace décadas y cosas que he aprendido de la experimentación a lo largo de los años. – Sparky

+1

@AbidRahmanK: Recomiendo este curso en Coursera https://www.coursera.org/course/hwswinterface Es un curso muy bueno y debe tomarse en serio. – jyz

Cuestiones relacionadas