2010-11-19 75 views
8

Intento entender algún código de ensamblaje y logré terminar la mayor parte, excepto algunas líneas. Puedo entender la mayor parte de lo que está sucediendo en el interior, pero no puedo entender completamente qué (y por qué) está sucediendo al principio y al final del código. ¿Alguien puede arrojar algo de luz sobre esto?Comprender el propósito de algunas instrucciones de ensamblaje

int main() { 
    int a, b; 
    a = 12; 
    b = 20; 
    b = a + 123; 
    return 0; 
} 

desmontado Versión:

8048394:8d 4c 24 04   lea 0x4(%esp),%ecx    ; ?? 
8048398:83 e4 f0    and $0xfffffff0,%esp   ; ?? 
804839b:ff 71 fc    pushl -0x4(%ecx)     ; ?? 
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     ; ?? 
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 
80483c1:83 c4 10    add $0x10,%esp     ; ?? 
80483c4:59     pop %ecx      ; ?? 
80483c5:5d     pop %ebp      ; ?? 
80483c6:8d 61 fc    lea -0x4(%ecx),%esp    ; ?? 
+0

Usted es útil para hacer un dibujo de lo que hay en la pila ... –

+0

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

+1

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

Respuesta

26

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.

+0

Gracias por su tiempo. Explicación muy detallada. Justo lo que estaba buscando. – Legend

+0

Q1: ¿Por qué primero agregue 4 bytes y luego menos? ¿Por qué no simplemente 'lea (% esp),% ecx' y luego' pushl (% ecx) '? Q2: ¿Hay alguna bandera para desactivar este tipo de alineación? ('-fno-align-functions' no funciona). – Tony

6

No sé por qué el compilador hace todas estas cosas, pero esto es lo que puedo descifrar:

8048394:8d 4c 24 04   lea 0x4(%esp),%ecx    ; ecx := esp+4 
8048398:83 e4 f0    and $0xfffffff0,%esp   ; align the stack to 16 bytes 
804839b:ff 71 fc    pushl -0x4(%ecx)     ; push [ecx-4] ([esp]) 
80483a1:51     push %ecx      ; push ecx 
80483a2:83 ec 4c    sub $0x4c,%esp     ; allocate 19 dwords on stack 
80483c1:83 c4 10    add $0x10,%esp     ; deallocate 4 dwords from stack 
80483c4:59     pop %ecx      ; restore ecx 
80483c5:5d     pop %ebp      ; and ebp 
80483c6:8d 61 fc    lea -0x4(%ecx),%esp    ; esp := [ecx-4] 
+1

Gracias por la explicación. – Legend

Cuestiones relacionadas