assembly - online - x86 asm instruction set
y esp, 0xfffffff0 (3)
No entiendo completamente la línea con comentarios en ella a continuación. Leí algunas publicaciones en SO y en el manual de gcc
y aprendí que es para la alineación de direcciones de pila, pero no entiendo cómo lo hace. El código se muestra a continuación:
(gdb) disas main
Dump of assembler code for function main:
0x08048414 <+0>: push ebp
0x08048415 <+1>: mov ebp,esp
0x08048417 <+3>: and esp,0xfffffff0 ; why??
0x0804841a <+6>: sub esp,0x10
0x0804841d <+9>: mov DWORD PTR [esp],0x8048510
0x08048424 <+16>: call 0x8048320 <puts@plt>
0x08048429 <+21>: mov DWORD PTR [esp],0x8048520
0x08048430 <+28>: call 0x8048330 <system@plt>
0x08048435 <+33>: leave
0x08048436 <+34>: ret
End of assembler dump.
El código se generó usando gcc
(versión 4.6.3) en linux. Gracias.
Parece que es parte de algún código para configurar la tienda al inicio de main
.
Inicio de la función: guarde el puntero del cuadro base en la pila (necesario más adelante en la instrucción de leave
):
0x08048414 <+0>: push ebp
Ahora alineamos el puntero de pila a un límite de 16 bytes, porque el compilador (por cualquier razón) lo quiere. Esto podría ser que siempre desea marcos alineados de 16 bytes, o que las variables locales necesitan una alineación de 16 bytes (tal vez alguien usó un uint128_t
o está usando un tipo que usa extensiones vectoriales gcc ). Básicamente, dado que el resultado siempre será menor o igual que el puntero de la pila actual, y la pila crece hacia abajo, solo se descartan los bytes hasta llegar a un punto alineado de 16 bytes.
0x08048415 <+1>: mov ebp,esp
0x08048417 <+3>: and esp,0xfffffff0
A continuación, restamos 16 del puntero de la pila, creando 16 bytes de espacio de variable local:
0x0804841a <+6>: sub esp,0x10
puts((const char*)0x8048510);
0x0804841d <+9>: mov DWORD PTR [esp],0x8048510
0x08048424 <+16>: call 0x8048320 <puts@plt>
system((const char*)0x8048520);
0x08048429 <+21>: mov DWORD PTR [esp],0x8048520
0x08048430 <+28>: call 0x8048330 <system@plt>
Salir de la función (ver otra respuesta sobre lo que hace la leave
):
0x08048435 <+33>: leave
0x08048436 <+34>: ret
Ejemplo de "bytes de descarte": diga esp = 0x123C al inicio de main
. Las primeras líneas de código:
0x08048414 <+0>: push ebp
0x08048415 <+1>: mov ebp,esp
resultado en este mapa de memoria:
0x123C: (start of stack frame of calling function)
0x1238: (old ebp value) <-- esp, ebp
Entonces:
0x08048417 <+3>: and esp,0xfffffff0
fuerza los últimos 4 bits de esp a 0, lo que hace esto:
0x123C: (start of stack frame of calling function)
0x1238: (old ebp value) <-- ebp
0x1234: (undefined)
0x1230: (undefined) <-- esp
No hay forma de que el programador confíe en que cierta cantidad de memoria esté entre esp
y ebp
en este punto; por lo tanto esta memoria se desecha y no se usa.
Finalmente, el programa asigna 16 bytes de almacenamiento de pila (local):
A continuación, restamos 16 del puntero de la pila, creando 16 bytes de espacio de variable local:
0x0804841a <+6>: sub esp,0x10
dándonos este mapa:
0x123C: (start of stack frame of calling function)
0x1238: (old ebp value) <-- ebp
0x1234: (undefined)
0x1230: (undefined)
0x123C: (undefined local space)
0x1238: (undefined local space)
0x1234: (undefined local space)
0x1230: (undefined local space) <-- esp
En este punto, el programa puede estar seguro de que hay 16 bytes de memoria alineada de 16 bytes a los que apunta esp
.
Sé que fue publicado hace mucho tiempo, podría ayudar a otros en la línea.
1) En los procesadores modernos, sabemos que GCC alinea la configuración predeterminada de la pila con la alineación de 16 bytes .
2) 16 bytes (128 bits) se deben a las instrucciones SSE2 que tienen registros MMX y XMM y los registros XMM son de 128 bits.
3) de modo que cuando se realiza una llamada a la función, se alinea automáticamente a 16 bytes , fuera de la función permanece a 8 bytes .
4) la lógica de usar 0xfffffff0 es mantener el bit 4 inferior a 0, esto se debe a la matemática booleana simple que dice que en binario, los múltiplos de 16 tienen un bit 4 bajo a cero (¿por qué cuatro bits? 2 ^ 4 = 16 ).
and esp, 0xfffffff0
hace un AND a nivel de bits entre el puntero de la pila y una constante, y almacena el resultado nuevamente en el puntero de la pila.
La constante se elige de modo que sus cuatro bits bajos sean cero. Por lo tanto, la operación AND establecerá estos bits en cero en el resultado y dejará intactos los otros bits de esp
. Esto tiene el efecto de redondear el puntero de la pila hasta el múltiplo más cercano de 16.