如何在x86_64上保存寄存器以获取中断服务程序?

时间:2011-07-26 22:30:43

标签: assembly x86-64 isr

我正在查看学校项目的一些旧代码,并试图在我的笔记本电脑上编译它时遇到了一些问题。它最初是为旧的32位版本的gcc编写的。无论如何,我试图将一些程序集转换为64位兼容代码并遇到一些障碍。

以下是原始代码:

pusha
pushl   %ds
pushl   %es
pushl   %fs
pushl   %gs
pushl   %ss

pusha在64位模式下无效。那么在64位模式下,在x86_64汇编中执行此操作的正确方法是什么?

必须有一个原因pusha在64位模式下无效,所以我觉得手动推送所有寄存器可能不是一个好主意。

4 个答案:

答案 0 :(得分:18)

在开发64位x86扩展时,AMD需要一些空间来添加REX前缀的新操作码和一些其他新指令。他们将某些操作码的含义改为新指令。

其中一些说明只是现有说明的简短形式,或者不是必需的。 PUSHA是其中一名受害者。目前尚不清楚为什么他们禁止PUSHA,但似乎并没有重叠任何新的指令操作码。也许它们保留了PUSHAPOPA操作码以供将来使用,因为它们完全是冗余的,并且不会更快,并且在代码中不会频繁发生。

PUSHA的顺序是指令编码的顺序:eaxecxedxebxesp,{ {1}},ebpesi。请注意,它会冗余地推送edi!您需要知道esp才能找到它推送的数据!

如果您要从64位转换代码,esp代码无论如何都不行,您需要更新它以推送新的寄存器PUSHAr8。您还需要保存并恢复更大的SSE状态r15xmm8。假设你要破坏它们。

如果中断处理程序代码只是转发到C代码的存根,则不需要保存所有寄存器。您可以假设C编译器将生成将保留xmm15rbxrbprsirdir12的代码。您只需要保存并恢复r15raxrcxrdxr8(注意:在Linux或其他System V ABI平台上,编译器将保留r11rbxrbp - r12,您可以期待{{1 }}和r15已被破坏)

段寄存器在长模式下不保持值(如果被中断的线程在32位兼容模式下运行,则必须保留段寄存器,谢谢ughoavgfhw)。实际上,他们在长模式下摆脱了大部分细分,但rsi仍然保留给操作系统用作线程本地数据的基地址。寄存器值本身无关紧要,rdiFS的基础是通过MSR FSGS设置的。假设您不使用0xC0000100,您不必担心它,只需记住C代码访问的任何线程本地数据都可以使用任意随机线程的TLS。请注意这一点,因为C运行时库使用TLS来实现某些功能(例如:strtok通常使用TLS)。

将值加载到0xC0000101FS(即使在用户模式下)也会覆盖FSGS MSR。由于某些操作系统使用FSBASE作为“处理器本地”存储(它们需要一种方法来指向每个CPU的结构),因此需要将它保留在某个不会被加载{{1在用户模式下。为了解决这个问题,为GSBASE寄存器保留了两个MSR:一个是活动的,一个是隐藏的。在内核模式下,内核的GS保存在通常的GS MSR中,而用户模式库则保存在另一个(隐藏的)GSBASE MSR中。当上下文从内核模式切换到用户模式上下文时,以及在保存用户模式上下文和进入内核模式时,上下文切换代码必须执行SWAPGS指令,该指令交换可见和隐藏GSBASE MSR的值。由于内核的GSBASE在用户模式下安全地隐藏在其他MSR中,因此用户模式代码无法通过将值加载到GSBASE来破坏内核的GSBASE。当CPU重新进入内核模式时,上下文保存代码将执行GSBASE并恢复内核的GSBASE

答案 1 :(得分:8)

从现有代码中学习做这种事情。例如:

事实上,“手动推送”regs是AMD64的唯一方法,因为那里不存在PUSHA。 AMD64在这方面并不是唯一的 - 大多数非x86 CPU确实需要逐个寄存器保存/恢复。

但是如果仔细检查引用的源代码,你会发现并非所有的中断处理程序都需要保存/恢复整个寄存器集,所以还有优化空间。

答案 2 :(得分:6)

pusha在64位模式下无效,因为它是多余的。单独推送每个寄存器正是要做的事情。

答案 3 :(得分:1)

嗨,这可能不是正确的方法,但可以创建像

这样的宏
.macro pushaq
    push %rax
    push %rcx
    push %rdx
    push %rbx
    push %rbp
    push %rsi
    push %rdi
.endm # pushaq

.macro popaq
    pop %rdi    
    pop %rsi    
    pop %rbp    
    pop %rbx    
    pop %rdx    
    pop %rcx
    pop %rax
.endm # popaq

并最终添加其他r8-15寄存器(如果需要

相关问题