2010-03-25 12 views

Respuesta

9

su código típico que se ve en el comienzo de una función.

Guarda el contenido del registro EBP en la pila y luego almacena el contenido del puntero de pila actual en EBP.

La pila se usa durante una llamada a función para almacenar argumentos locales. Pero en la función, el puntero de pila puede cambiar porque los valores se almacenan en la pila.

Si guarda el valor original de la pila, puede consultar los argumentos almacenados a través del registro EBP, mientras que todavía puede usar (agregar valores a) la pila.

Al final de la función es probable que vea el comando

pop %ebp ; restore original value 
ret  ; return 
+0

'movl% ebp,% esp/popl% ebp/ret' -> Primero restaure' ESP', * then * pop 'EBP' – automaton

2

Es parte de lo que se conoce como el function prolog.

Guarda el puntero base actual que se va a recuperar cuando la función finaliza y establece el nuevo ebp al comienzo del nuevo fotograma.

5
push %ebp 

Esto empujará el 32 bit (extendido) registro de puntero de base en la pila, es decir, el puntero de pila (% esp) se resta por cuatro, entonces el valor de% ebp se copia en la ubicación que la pila el puntero apunta a

movl %esp, %ebp 

Esto copia el registro del puntero de la pila en el registro del puntero base.

El propósito de copiar el puntero de pila al puntero base es crear un marco de pila, es decir, un área en la pila donde una subrutina puede almacenar datos locales. El código en la subrutina usaría el puntero base para hacer referencia a los datos.

+0

La última oración no se describió anteriormente. ¡Gracias por eso! Upvoted. – C4u

55

la explicación de unwind es la verdad literal (no obstante un pequeño error direccional), pero no explica por qué.

%ebp es el "puntero de base" para su estructura de pila. Es el puntero utilizado por el tiempo de ejecución C para acceder a variables y parámetros locales en la pila. Aquí hay un típico código de prólogo de función generado por GCC (g ++ para ser más preciso) Primero, la fuente de C++.

// junk.c++ 
int addtwo(int a) 
{ 
    int x = 2; 

    return a + x; 
} 

Esto genera el siguiente ensamblador.

.file "junk.c++" 
    .text 
.globl _Z6addtwoi 
    .type _Z6addtwoi, @function 
_Z6addtwoi: 
.LFB2: 
    pushl %ebp 
.LCFI0: 
    movl %esp, %ebp 
.LCFI1: 
    subl $16, %esp 
.LCFI2: 
    movl $2, -4(%ebp) 
    movl -4(%ebp), %edx 
    movl 8(%ebp), %eax 
    addl %edx, %eax 
    leave 
    ret 
.LFE2: 
    .size _Z6addtwoi, .-_Z6addtwoi 
    .ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3" 
    .section .note.GNU-stack,"",@progbits 

Ahora, para explicar que el código de prólogo (todo el material antes de .LCFI2:), en primer lugar:

  1. pushl %ebp almacena el marco de pila de la funciónllamando a la pila.
  2. movl %esp, %ebp toma el puntero de pila actual y lo usa como el marco para la función llamada.
  3. subl $16, %esp deja espacio para las variables locales.

Ahora su función está lista para el trabajo. Cualquier referencia con un desplazamiento negativo desde el registro %ebp% son sus variables locales (x en este ejemplo). Cualquier referencia con un desplazamiento positivo desde el registro %ebp% son sus parámetros pasados.

El último punto de interés es la instrucción leave que es una instrucción de ensamblador x86 que hace el trabajo de restaurar el marco de pila de la función de llamada. Esto generalmente se optimiza en la secuencia más rápida move %ebp %esp y pop %ebp% en código C. Para fines ilustrativos, sin embargo, no compilé con ninguna optimización en absoluto.

+2

+1: extremadamente informativo. – IAbstract

+0

Para agregar a esta respuesta, existen varias convenciones de llamadas que rigen el orden en que los parámetros de entrada se insertan en la pila y también quién limpiará después de que se haya completado la función. –

1

También creo que es importante tener en cuenta que a menudo después de push %ebp y movl %esp, %ebp la asamblea tendrá push %ebx o push %edx. Estas son las salvaciones de llamadas de los registros %ebx y %edx. Al final de la llamada de rutina, los registros se restaurarán con sus valores originales.

También - %ebx, %esi, %edi son todos registros de llamadas telefónicas. Entonces, si desea sobrescribirlos, primero debe guardarlos y luego restaurarlos.

0

La pieza de código configura la pila para su programa.

En x86, dos registros registran la información de la pila.


    Base pointer (bp): Holds starting address of the stack 
    Stack pointer (sp): Holds the address in which next value will be stored 

Estos registros tienen diferentes nombres en diferentes modos:


          Base pointer Stack pointer 
    16 bit real mode:  bp      sp 
    32 bit protected mode: ebp(%ebp)    esp(%esp) 
    64 bit mode:   rbp     rsp 

Cuando se configura una pila, puntero de pila y el puntero de base tiene la misma dirección desde el principio.

Ahora, para explicar su código,


    push %ebp 

Este código empuja la dirección actual de la pila en la pila de modo que la función puede "salida" o "retorno" correctamente.


    movl %esp, %ebp 

Este código configura la pila para su función.

Para obtener más información, consulte este question.

Espero que esta ayuda!

Cuestiones relacionadas