Assembly 如何为中断服务例程保存x86_64上的寄存器?
我正在查看一个学校项目中的一些旧代码,在试图在笔记本电脑上编译时遇到了一些问题。它最初是为旧的32位gcc版本编写的。无论如何,我试图将部分程序集转换为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无效肯定是有原因的,所以我感觉手动推
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(搜索
):(类似于NetBSD)IDT\u VEC
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
的基址通过MSRs0xC0000100
和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