2009-06-30 21 views
40

He intentado obtener una comprensión más profunda de cómo los compiladores generan códigos de máquina, y más específicamente cómo GCC trata con la pila. Al hacerlo, he estado escribiendo simples programas en C, compilándolos para ensamblar y haciendo todo lo posible por comprender el resultado. He aquí un programa simple y la salida que genera:Asignación de pila, relleno y alineación

asmtest.c:

void main() { 
    char buffer[5]; 
} 

asmtest.s:

pushl %ebp 
movl %esp, %ebp 
subl $24, %esp 
leave 
ret 

Lo desconcertante para mí es la razón por 24 bytes se asignan para la pila. Sé que debido a la forma en que el procesador aborda la memoria, la pila debe asignarse en incrementos de 4, pero si este fuera el caso, solo deberíamos mover el puntero de la pila en 8 bytes, no en 24. Como referencia, un buffer de 17 los bytes producen un puntero de pila movido 40 bytes y ningún buffer en absoluto mueve el puntero de pila 8. Un buffer entre 1 y 16 bytes inclusive mueve ESP 24 bytes.

Suponiendo que los 8 bytes son una constante necesaria (¿para qué se necesita?), Esto significa que estamos asignando en fragmentos de 16 bytes. ¿Por qué el compilador se estaría alineando de esa manera? Estoy usando un procesador x86_64, pero incluso una palabra de 64 bits solo debería requerir una alineación de 8 bytes. ¿Por qué la discrepancia?

Como referencia, estoy compilando esto en una Mac con 10.5 ejecutando gcc 4.0.1 y sin optimizaciones habilitadas.

Respuesta

43

Es una función de gcc controlada por -mpreferred-stack-boundary=n donde el compilador intenta mantener los elementos en la pila alineados en 2^n. Si cambió n a 2, solo asignaría 8 bytes en la pila. El valor predeterminado para n es 4, es decir, intentará alinearse con los límites de 16 bytes.

¿Por qué está el "default" 8 bytes y luego 24 = 8 + 16 bytes se debe a que la pila ya contiene 8 bytes para leave y ret, por lo que el código compilado debe ajustar la pila por primera vez por 8 bytes de lograr que se alinea con 2^4 = 16.

+0

hizo "push% ebp" hecho esp disminuido en 8 byte? más 8 bytes de ret, ya debería estar alineado con 16 bytes. ¿Por qué el compilador de dosis necesita estos 8 bytes adicionales? –

+1

oh, lo tengo. Esta es una maquina de 32 bits. Lo siento.Debe ser ret 4 byte + ebp 4 byte + alineado 8 byte + búfer 16 –

+1

Las versiones actuales de las ABI i386 y x86-64 System V requieren una alineación de 16B stack (antes de una instrucción 'call'), por lo que las funciones pueden asumir ese. Históricamente, el i386 ABI solo requería la alineación 4B. (Consulte https://stackoverflow.com/tags/x86/info para obtener enlaces a documentos ABI). GCC también mantiene '% esp' alineado incluso en funciones de hoja (que no llaman a otras funciones), cuando tiene que reservar espacio, y eso es lo que está sucediendo aquí. –

3

Encontré this site, que tiene alguna explicación decente en la parte inferior de la página acerca de por qué la pila puede ser más grande. Escale el concepto hasta una máquina de 64 bits y podría explicar lo que está viendo.

-1

Los 8 bytes están ahí porque la primera instrucción empuja el valor inicial de% ebp en la pila (suponiendo que sea de 64 bits).

+1

La dirección de retorno y el puntero base se insertan en la pila. – dreamlax

11

La familia de instrucciones SSEx REQUIERE que los vectores empaquetados de 128 bits se alineen con 16 bytes; de lo contrario, obtendrá un segfault tratando de cargarlos/almacenarlos. Es decir. si quiere pasar de manera segura vectores de 16 bytes para usar con SSE en la pila, la pila debe mantenerse constantemente alineada con 16. GCC lo cuenta de manera predeterminada.

+0

Puede que tenga muy poca experiencia con el asunto para afirmar que su respuesta es incorrecta. Pero, ¿no utilizas las instrucciones 'movupd' y similares ** u ** naligned exactamente para ese propósito (cargar/almacenar _unaligned_ packed data)? Por lo que entiendo, usted puede tener un comportamiento defectuoso al tratar de usar 'movapd' e instrucciones similares sobre datos no alineados, pero los datos no alineados no deberían ser un problema en general. – andreee

1

La Mac OS X/Darwin x86 ABI requiere una alineación de la pila de 16 bytes. Este no es el caso en otras plataformas x86 como Linux, Win32, FreeBSD ...

+1

El requisito real de ABI es que la pila esté alineada 16 byte * en los límites de llamada de función *. –

+2

Esto es cierto, pero como los prólogos/epílogos de funciones son los únicos lugares donde se cambia el puntero de la pila, esto es casi lo mismo que decir que debe estar alineado en todo momento. – Ringding

Cuestiones relacionadas