x86_64对齐堆栈并在不保存寄存器的情况下恢复

我正在为x86_64编写中断处理例程。 ABI指定在调用C函数之前,我必须将堆栈对齐到16个字节。 x86_64 ISA指定在进入ISR时,我的堆栈是8字节对齐的。 我需要将我的堆栈指针对齐到16个字节。 问题是从我的C函数返回时,我必须恢复(可能)未对齐的堆栈指针,以便我可以正确地从我的中断返回。

我想知道是否有办法在不使用通用寄存器的情况下执行此操作?

这是我对问题的解决方案:

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

两次推送使堆栈具有与其原始时相同的对齐方式,以及原始%rsp的副本(%rsp)8(%rsp) 。 然后, andq对齐堆栈 – 如果它已经是16字节对齐,则没有任何变化,如果它是8字节对齐,那么它从%rsp减去8,这意味着原始%rsp现在为8(%rsp)16(%rsp) 。 所以我们可以无条件地从8(%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 

我怀疑在不使用额外寄存器的情况下完成此操作的唯一方法是需要额外写入和读取存储器,这是不使用额外寄存器的唯一要点。

我将提供我目前的解决方案。 我存储rbp所以我可以使用它进行临时存储,然后在调用函数之前恢复它。 这与drhirsch的答案类似

 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 

可能比其他人描述的那样使用%ebp慢,但如何:

  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 

这利用了堆栈已经是8字节对齐的事实,并且推送指令可以读取要从存储器推送的值。