La pila crece hacia abajo . Un push
resta del puntero de pila (esp) y un pop
se agrega a esp. Debes tener eso en cuenta para entender mucho de esto.
8048394:8d 4c 24 04 lea 0x4(%esp),%ecx ; ??
lea = Carga dirección efectiva
Esto ahorra la dirección de lo que yace 4 bytes en la pila. Como se trata de un código x86 de 32 bits (palabra de 4 bytes), significa el segundo elemento de la pila. Como este es el código de una función (principal en este caso), los 4 bytes que se encuentran en la parte superior de la pila son la dirección de retorno.
8048398:83 e4 f0 and $0xfffffff0,%esp ; ??
Este código se asegura de que la pila esté alineada a 16 bytes. Después de esta operación, esp será menor o igual a lo que era antes de esta operación, por lo que la pila puede crecer, lo que protege cualquier cosa que ya esté en la pila. Esto a veces se hace en main
por si la función se llama con una pila desalineada, lo que puede hacer que las cosas sean realmente lentas (16 bytes es un ancho de línea de caché en x86, creo, aunque la alineación de 4 bytes es lo realmente importante aquí) Si main tiene una pila desalineada, el resto del programa también lo hará.
804839b:ff 71 fc pushl -0x4(%ecx) ; ??
Desde ecx se carga antes que como un puntero a la cosa en el otro lado de la dirección de retorno desde la parte superior de la pila anterior, por lo que desde este tiene un índice de -4 esto se refiere a la parte posterior de la dirección de retorno para que la función actual se vuelva a colocar en la parte superior de la pila para que la tubería principal pueda regresar normalmente. (Push es mágico y parece ser capaz de cargar y almacenar desde diferentes lugares en la RAM en la misma instrucción).
804839e:55 push %ebp ; Store the Base pointer
804839f:89 e5 mov %esp,%ebp ; Initialize the Base pointer with the stack pointer
80483a1:51 push %ecx ; ??
80483a2:83 ec 4c sub $0x4c,%esp ; ??
Este es principalmente el prólogo de función estándar (las cosas anteriores eran especiales para main). Esto está haciendo un marco de pila (área entre ebp y esp) donde las variables locales pueden vivir. El ebp se empuja para que el antiguo marco de pila se pueda restaurar en el epílogo (al final de la función actual).
80483a5:c7 45 f8 0c 00 00 00 movl $0xc,-0x8(%ebp) ; Move 12 into -0x8(%ebp)
80483ac:c7 45 f4 14 00 00 00 movl $0x14,-0xc(%ebp) ; Move 20 into -0xc(%ebp)
80483b3:8b 45 f8 mov -0x8(%ebp),%eax ; Move [email protected](%ebp) into eax
80483b6:83 c0 7b add $0x7b,%eax ; Add 123 to [email protected]
80483b9:89 45 f4 mov %eax,-0xc(%ebp) ; Store the result into [email protected](%ebp)
80483bc:b8 00 00 00 00 mov $0x0,%eax ; Move 0 into eax
eax es donde se almacenan los valores de retorno de función entero. Esto se configura para devolver 0 desde main.
80483c1:83 c4 10 add $0x10,%esp ; ??
80483c4:59 pop %ecx ; ??
80483c5:5d pop %ebp ; ??
80483c6:8d 61 fc lea -0x4(%ecx),%esp ; ??
Esta es la función epílogo. Es más difícil de entender debido al extraño código de alineación de pila al principio. Sin embargo, me está costando un poco averiguar por qué la pila se está ajustando por una cantidad menor esta vez que en el prólogo.
Es obvio que este código en particular no se compiló con optimizaciones en.Si lo hubiera, probablemente no habría mucho allí, ya que el compilador puede ver que incluso si no hizo las operaciones matemáticas enumeradas en su main
, el resultado final del programa es el mismo. Con los programas que realmente hacen algo (tienen efectos secundarios o resultados) a veces es más fácil leer código ligeramente optimizado (argumentos -O1 o -0s a gcc).
La lectura del ensamblaje generado por un compilador suele ser mucho más fácil para las funciones que no son main
. Si desea leer para comprender el código, escriba usted mismo una función que tome algunos argumentos para producir un resultado o que funcione en variables globales, y podrá comprenderlo mejor.
Otra cosa que probablemente lo ayude es simplemente hacer que gcc genere los archivos de ensamblaje por usted, en lugar de desmontarlos. El indicador -S
le dice que genere esto (pero no para generar otros archivos) y nombra los archivos de ensamblado con un .s
al final. Esto debería ser más fácil de leer que las versiones desmontadas.
Usted es útil para hacer un dibujo de lo que hay en la pila ... –
Escribir solo los nombres en inglés para los códigos de operación en los comentarios no lo ayudará mucho. Intenta hacerlos más "de alto nivel". Y, si realmente quieres aprender algo de la separación, prueba el código humano, no el del compilador. – ruslik
Normalmente, el material al principio y al final es el código genérico de configuración de la pila. Asignación y alineación y tal. – zdav