我正在看一些学校项目中的旧代码,在试图在笔记本电脑上编译它时,我遇到了一些问题。它最初是为旧的32位版本的gcc编写的。无论如何,我试图将一些程序集转换为64位兼容代码,遇到了一些障碍。
下面是原始代码:
pusha
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushl %ss
pusha
在64位模式下无效。那么,在64位模式下,在x86_64汇编中执行此操作的正确方法是什么呢?pusha
在64位模式下无效肯定是有原因的,所以我有一种感觉,手动推动所有寄存器可能不是一个好主意。
5条答案
按热度按时间wi3ka0sx1#
AMD在开发64位x86扩展时需要一些空间来为
REX
前缀和其他一些新指令添加新的操作码。他们改变了一些操作码的含义,这些新指令。其中一些指令只是现有指令的简短形式,或者没有必要。
PUSHA
是受害者之一。目前还不清楚为什么他们禁止PUSHA
,它似乎没有重叠任何新的指令操作码。也许它们保留了PUSHA
和POPA
操作码供将来使用,因为它们完全是冗余的,不会更快,也不会在代码中出现得足够频繁。PUSHA
的顺序是指令编码的顺序:eax
、ecx
、edx
、ebx
、esp
、ebp
、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
的基址通过MSR0xC0000100
和0xC0000101
设置。假设你不会使用FS
,你不需要担心它,只要记住C代码访问的任何线程本地数据可以使用任何随机线程的TLS。要注意这一点,因为C运行库使用TLS来实现某些功能(例如:strtok通常使用TLS)。将值加载到
FS
或GS
(即使在用户模式下)也会覆盖FSBASE
或GSBASE
MSR。由于一些操作系统使用GS
作为“处理器本地”存储(它们需要一种方法来为每个CPU提供指向结构的指针),因此它们需要将其保存在不会因在用户模式下加载GS
而受到破坏的地方。为解决此问题,为GSBASE
寄存器保留了两个MSR:一个活动的一个和一个隐藏的一个。在内核模式下,内核的GSBASE
保存在通常的GSBASE
MSR中,用户模式库保存在另一个(隐藏的)GSBASE
MSR中。从内核模式切换到用户模式上下文时,以及保存用户模式上下文并进入内核模式时,上下文切换代码必须执行SWAPGS指令,该指令交换可见和隐藏GSBASE
MSR的值。由于内核的GSBASE
在用户模式下安全地隐藏在另一个MSR中,因此用户模式代码不能通过将值加载到GS
中来破坏内核的GSBASE
。当CPU重新进入内核模式时,上下文保存代码将执行SWAPGS
并恢复内核的GSBASE
。gblwokeq2#
从现有的代码中学习这种事情。例如:
SAVE_ARGS_IRQ
):entry_64.SINTR_PUSH
):privregs.hIDT_VEC
):exception.S(类似于NetBSD中的vector.S)事实上,“手动推动”规则是AMD 64上的唯一方法,因为
PUSHA
不存在。AMD 64在这方面并不是唯一的--大多数非x86 CPU在某些时候也需要逐个寄存器的保存/恢复。但如果仔细检查引用的源代码,您会发现并非所有中断处理程序都需要保存/恢复整个寄存器集,因此还有优化的空间。
yshpjwxd3#
pusha
在64位模式下无效,因为它是冗余的。单独推送每个寄存器正是要做的事情。sgtfey8w4#
你好,这可能不是正确的方法,但可以创建宏,如
和
并且如果需要的话最终添加其它R8-15寄存器
smtd7mpg5#
举个例子,我今天测试了一个简短的程序,我想在开始做我们刚刚学到的系统调用之前,做同样的事情,备份所有的寄存器。因此,我首先尝试了pusha和popa,我在旧的IA-32英特尔架构软件开发人员手册中找到的东西。然而,它没有工作。我已经手动测试过了,但是效果很好。
这是使用x64编译时的结果:
如果没有pusha和popa助记符,它就可以工作了,结果如下:
这将适用于x32模式:Intel Ref Document
但是,如果你想尝试这个方法,它确实可以手动工作:
结果就是:
长话短说,您可以手动将寄存器一个接一个地加载到堆栈中,并在需要时将其弹出以恢复堆栈。