gcc - asm - gnu assembler
¿Cuál es el uso de "push% ebp; movl% esp,% ebp "generado por GCC para x86? (6)
Es parte de lo que se conoce como el prólogo de función .
Guarda el puntero base actual que se recuperará cuando la función finaliza y establece el nuevo ebp al comienzo del nuevo marco.
Qué efecto causan estas dos instrucciones en el código ensamblado generado por gcc para máquinas x86:
push %ebp
movl %esp, %ebp
Es un código típico que ves al 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 probablemente verá el comando
pop %ebp ; restore original value
ret ; return
La explicación de unwind es la verdad literal (a pesar de un error direccional menor), pero no explica por qué.
%ebp
es el "puntero base" para tu marco 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 ese código de prólogo (todas las cosas anteriores a .LCFI2:
, primero:
-
pushl %ebp
almacena el marco de pila de la función de llamada en la pila. -
movl %esp, %ebp
toma el puntero de pila actual y lo usa como el marco para la función llamada . -
subl $16, %esp
deja espacio para las variables locales.
Ahora su función está lista para el negocio. Cualquier referencia con un desplazamiento negativo del registro %ebp%
son sus variables locales ( x
en este ejemplo). Todas las referencias con un desplazamiento positivo del 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. Por lo general, esto se optimiza en la move %ebp %esp
más rápida move %ebp %esp
y pop %ebp%
en el código C. Para fines ilustrativos, sin embargo, no compilé con ninguna optimización en absoluto.
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 configura una pila, el puntero de la pila y el puntero base obtienen la misma dirección al principio.
Ahora para explicar tu código,
push %ebp
Este código empuja la dirección actual de la pila a la pila para que la función pueda "salir" o "regresar" correctamente.
movl %esp, %ebp
Este código configura la pila para su función.
Para obtener más información, consulte esta question .
¡Espero que esto ayude!
También creo que es importante tener en cuenta que a menudo después de push %ebp
y movl %esp, %ebp
el ensamblado tendrá push %ebx
o push %edx
. Estos son 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.
Además, %ebx, %esi, %edi
son todos registros de guardado de llamadas. Entonces, si desea sobrescribirlos, primero debe guardarlos y luego restaurarlos.
push %ebp
Esto empujará el registro de puntero base de 32 bits (extendido) en la pila, es decir, el puntero de pila (% esp) se restará en cuatro, luego el valor de% ebp se copiará en la ubicación a la que apunta el puntero de pila.
movl %esp, %ebp
Esto copia el registro de puntero de pila al registro de puntero base.
El objetivo 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.