I am writing interrupt routines for x86_64. ABI indicates that before calling function C, I must align the stack to 16 bytes. ISA x86_64 indicates that when I enter ISR, my stack is aligned by 8 bytes. So I need to align the stack pointer with 16 bytes. The problem is that when I return from my C function, I have to restore the (potentially) unrecognizable stack pointer so that I can correctly return from my interrupt.
I wonder if there is a way to do this without using a general purpose register?
Here is my solution to the question of how to put:
pushq %rsp pushq (%rsp) andq $-0x10, %rsp call function movq 8(%rsp), %rsp
, , %rsp (%rsp) 8(%rsp). andq - 16 , , 8 , 8 %rsp, , %rsp 8(%rsp) 16(%rsp). 8(%rsp).
%rsp
(%rsp)
8(%rsp)
andq
16(%rsp)
, rsp. -
rsp
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
, , , , .
I offer the current solution that I have. I store rbp so I can use it for temporary storage and then restore it before calling the function. This is similar to drhirsch answer
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
Probably slower than using% ebp as described by others, but what about:
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
This takes advantage of the fact that the stack is already 8-byte aligned and that the push command can read the value that needs to be ejected from memory.