Por qué GCC genera una forma extraña de mover el puntero de pila
He observado que el compilador C++ de GCC genera el siguiente código ensamblador:
sub $0xffffffffffffff80,%rsp
Esto es equivalente a:
add $0x80,%rsp
es decir, se eliminan 128 bytes de la pila.
¿Por qué GCC genera la primera variante “sub” en lugar de la variante “add”? La variante “add” me parece mucho más natural que aprovechar que hay un desbordamiento.
Esto solo ocurrió una vez en una base de código bastante grande. No tengo un ejemplo mínimo de código C++ para reproducir esto. Estoy usando GCC 7.5.0.
davy.ai
Explicación
La razón por la cual GCC genera
sub $0xffffffffffffff80,%rsp
en lugar deadd $0x80,%rsp
se debe a una convención seguida por la arquitectura x86 en cuanto a cómo se organiza la pila.En x86, la pila crece hacia abajo, lo que significa que los valores empujados a la pila se encuentran en direcciones de memoria más bajas que los valores anteriores. Cuando se sacan valores de la pila, las direcciones de memoria aumentan. El puntero de pila apunta al último valor empujado a la pila.
Por lo tanto, al asignar espacio en la pila para variables locales o argumentos de función, GCC utiliza
sub
para disminuir el puntero de pila, y al desasignar, utilizaadd
para aumentarlo.En el caso específico de
sub $0xffffffffffffff80,%rsp
, esto resta 128 bytes del puntero de pila. La razón por la cual la resta se realiza con0xffffffffffffff80
en lugar de0x80
se debe a que x86 utiliza la representación en complemento a dos para los números negativos, y el bit alto indica que el número es negativo. Entonces,0xffffffffffffff80
es -128 en complemento a dos, lo cual es equivalente a restar 128.En resumen, aunque la variante
add
puede parecer más natural, utilizarsub
es necesario para seguir la convención de la pila x86 de asignar consub
y desasignar conadd
.