Assembly 如何为中断服务例程保存x86_64上的寄存器?

Assembly 如何为中断服务例程保存x86_64上的寄存器?,assembly,x86-64,isr,Assembly,X86 64,Isr,我正在查看一个学校项目中的一些旧代码,在试图在笔记本电脑上编译时遇到了一些问题。它最初是为旧的32位gcc版本编写的。无论如何,我试图将部分程序集转换为64位兼容代码,但遇到了一些障碍 以下是原始代码: pusha pushl %ds pushl %es pushl %fs pushl %gs pushl %ss pusha在64位模式下无效。那么,在64位模式下,在x86_64汇编中执行此操作的正确方法是什么 在64位模式下,pusha无效肯定是有原因的,所以我感觉手动推

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

以下是原始代码:

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

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


在64位模式下,
pusha
无效肯定是有原因的,所以我感觉手动推送所有寄存器可能不是一个好主意。
pusha
在64位模式下无效,因为它是冗余的。单独推送每个寄存器正是要做的事情。

学习执行此类操作的现有代码。例如:

  • Linux(搜索
    SAVE\u ARGS\u IRQ
    ):
  • OpenSolaris(搜索
    INTR\u PUSH
    ):
  • FreeBSD(搜索
    IDT\u VEC
    ):(类似于NetBSD)
事实上,“手动推送”regs是AMD64上的唯一方法,因为那里不存在
PUSHA
。AMD64在这方面并不是唯一的—大多数非x86 CPU在某些情况下也需要逐寄存器保存/恢复


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

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

其中一些指示只是现有指示的简短形式,或者是不必要的<代码>普沙是受害者之一。现在还不清楚为什么他们禁止了PUSHA,但它似乎没有与任何新的指令操作码重叠。可能它们保留了
PUSHA
POPA
操作码供将来使用,因为它们是完全冗余的,不会更快,也不会频繁出现在代码中

PUSHA
的顺序是指令编码的顺序:
eax
ecx
edx
ebx
esp
esi
edi
。请注意,它冗余地推送了
esp
!您需要知道
esp
,才能找到它推送的数据

如果您正在从64位转换代码,那么
PUSHA
代码无论如何都是不好的,您需要更新它以将新寄存器
r8
推送到
r15
。您还需要保存和恢复更大的SSE状态,
xmm8
xmm15
。假设你要揍他们

如果中断处理程序代码只是一个转发到C代码的存根,则不需要保存所有寄存器。您可以假设C编译器将生成保留
rbx
rbp
rsi
rdi
、以及
r12
r15
的代码。您只需通过
r11
保存和还原
rax
rcx
rdx
r8
。(注意:在Linux或其他System V ABI平台上,编译器将保留
rbx
rbp
r12
-
r15
,您可以预期
rsi
rdi
被删除)

段寄存器在长模式下不保存任何值(如果中断的线程在32位兼容模式下运行,则必须保留段寄存器,谢谢ughoavgfhw)。实际上,他们在长模式下去掉了大部分分段,但是
FS
仍然保留给操作系统,作为线程本地数据的基址。寄存器值本身并不重要,
FS
GS
的基址通过MSRs
0xC0000100
0xC0000101
设置。假设您不使用
FS
,您不必担心它,只要记住,C代码访问的任何线程本地数据都可能使用任何随机线程的TLS。注意这一点,因为C运行时库对某些功能使用TLS(例如:strtok通常使用TLS)


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

.macro pushaq
    push %rax
    push %rcx
    push %rdx
    push %rbx
    push %rbp
    push %rsi
    push %rdi
.endm # pushaq


如果需要,最终添加其他r8-15寄存器。此外,您不能在64位模式下推送段寄存器。您需要先将它们复制到另一个寄存器<代码>mov%ds,%eax;将%rax@ughoavgfhw推送到
.macro popaq
    pop %rdi    
    pop %rsi    
    pop %rbp    
    pop %rbx    
    pop %rdx    
    pop %rcx
    pop %rax
.endm # popaq