2012-03-06 15 views
5

Estoy escribiendo rutinas de manejo de interrupciones para x86_64. El ABI especifica que antes de llamar a una función C, debo alinear la pila a 16 bytes. El ISA x86_64 especifica que al ingresar a un ISR, mi pila está alineada con 8 bytes. Necesito alinear mi puntero de pila a 16 bytes por lo tanto. El problema es que al regresar de mi función C, debo recuperar el puntero de la pila (potencialmente) desalineado para que pueda regresar de mi interrupción correctamente.x86_64 alinear pila y recuperar sin guardar registros

Me pregunto si hay una manera de hacerlo sin usar un registro de propósito general?

+0

¿Está almacenando SP en el montón? –

+0

@ H2CO3 Eso suena como una idea terrible. Tampoco tengo la noción de un montón en este contexto. – dschatz

+0

OK, fue solo una idea rápida. –

Respuesta

8

aquí está mi solución a la cuestión que se plantean:

pushq %rsp 
pushq (%rsp) 
andq $-0x10, %rsp 
    call function 
movq 8(%rsp), %rsp 

Los dos impulsos deje la pila con la misma alineación que tenía originalmente, y una copia del original %rsp en (%rsp) y 8(%rsp). El andq alinea la pila; si ya estaba alineado en 16 bytes, nada cambia, si estaba alineado en 8 bytes, restará 8 de %rsp, lo que significa que el %rsp original está ahora en y 16(%rsp). Así que podemos restaurarlo incondicionalmente desde 8(%rsp).

4

No hay forma de hacerlo sin un registro adicional, porque la operación de alineación es destructiva para el registro rsp. Que tiene que hacer algo a lo largo

push %rbp   ;save rbp for stack pointer 
mov %rsp, %rbp ;move old sp to rbp 
and $-0x10, %rsp ;align stack 
...     
...    ;if you want to use %rbp here, save it on the stack before 
... 
mov %rbp, %rsp ;old stack pointer 
pop %rbp 
iret 
+0

No me has convencido de que no hay forma, aunque sospecho que es el caso. El hecho de que la operación sea destructiva no significa que no pueda hacer algo antes de alinearme para guardar el puntero original de la pila. – dschatz

+0

Bueno, hay una manera: puedes presionar el puntero de la pila y luego un número mágico para apilar. Debido a que no todas las direcciones en el espacio de direcciones de 64 bits están actualmente permitidas en x86_64, puede elegir una magia que no puede ser una dirección de pila. A su regreso, busca la magia y encuentra el puntero de pila detrás de ella. Si la brecha entre la pila alineada y la antigua pila contiene la magia por casualidad, puedes identificar este caso porque encontrarás la magia dos veces. – hirschhornsalz

+0

Usar '% rbp' es una buena opción aquí, porque es un registro guardado en línea (de modo que la función C lo restaurará antes de regresar, si lo corta). – caf

0

Sospecho que la única manera de hacerlo sin necesidad de utilizar un registro adicional requiere la escritura y la lectura adicional a la memoria lo que sería el único punto de no utilizar un registro adicional.

Ofreceré la solución actual que tengo. Guardo rbp para que pueda usarlo para el almacenamiento temporal y luego restaurarlo antes de la llamada a la función. Esto es similar a drhirsch a responder

movq %rbp, -24(%rsp) //store original rbp 3 words beyond the stack 
movq %rsp, %rbp //store original rsp 
subq $8, %rsp //buy a word on the stack 
andq $-0x10, %rsp //16 byte align the stack (growing downwards) 
//We now have 1 or 2 words free on the stack (depending on the original 
// alignment). This is why we put rbp 3 words beyond the stack 
movq %rbp, (%rsp) //store the original rsp right here 
movq -24(%rbp), %rbp //restore original rbp 
call foo 
movq (%rsp), %rsp //restore original rsp 
iretq 
+0

Esto no es seguro contra las interrupciones anidadas: si una interrupción de mayor prioridad te golpea después de almacenar en '-24 (% rsp)' y antes de ajustar '% rsp', se destruirá tu'% rbp' guardado. – caf

0

Probablemente más lento que usar% ebp como han descrito otros, pero ¿qué tal:

push %rsp 
    test $0xf, %rsp 
    jz aligned 
    push (%rsp) // duplicate the top of the stack 
aligned: 
    // now have 16-byte alignment with the original stack pointer 
    // on the top of the stack, either once or twice 
     : 
    pop %rsp 
    iret 

Esto se aprovecha del hecho de que la pila ya es de 8 bytes alineados , y que una instrucción de inserción puede leer el valor que se extraerá de la memoria.

+0

Ahora eres un empuje condicional pero pop incondicional. Muy problemático –

+0

@R: ¿por qué? El pop incondicional modifica el puntero de la pila para que deshaga las cosas independientemente de la condición. –

+0

Oh, ahora lo entiendo. ¡Muy inteligente! Me gusta. –

Cuestiones relacionadas