我正在查看学校项目的一些旧代码,并试图在我的笔记本电脑上编译它时遇到了一些问题。它最初是为旧的32位版本的gcc编写的。无论如何,我试图将一些程序集转换为64位兼容代码并遇到一些障碍。
以下是原始代码:
pusha
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushl %ss
pusha
在64位模式下无效。那么在64位模式下,在x86_64汇编中执行此操作的正确方法是什么?
必须有一个原因pusha
在64位模式下无效,所以我觉得手动推送所有寄存器可能不是一个好主意。
答案 0 :(得分:18)
REX
前缀的新操作码和一些其他新指令。他们将某些操作码的含义改为新指令。
其中一些说明只是现有说明的简短形式,或者不是必需的。 PUSHA
是其中一名受害者。目前尚不清楚为什么他们禁止PUSHA
,但似乎并没有重叠任何新的指令操作码。也许它们保留了PUSHA
和POPA
操作码以供将来使用,因为它们完全是冗余的,并且不会更快,并且在代码中不会频繁发生。
PUSHA
的顺序是指令编码的顺序:eax
,ecx
,edx
,ebx
,esp
,{ {1}},ebp
,esi
。请注意,它会冗余地推送edi
!您需要知道esp
才能找到它推送的数据!
如果您要从64位转换代码,esp
代码无论如何都不行,您需要更新它以推送新的寄存器PUSHA
至r8
。您还需要保存并恢复更大的SSE状态r15
至xmm8
。假设你要破坏它们。
如果中断处理程序代码只是转发到C代码的存根,则不需要保存所有寄存器。您可以假设C编译器将生成将保留xmm15
,rbx
,rbp
,rsi
和rdi
至r12
的代码。您只需要保存并恢复r15
,rax
,rcx
和rdx
至r8
。 (注意:在Linux或其他System V ABI平台上,编译器将保留r11
,rbx
,rbp
- r12
,您可以期待{{1 }}和r15
已被破坏)。
段寄存器在长模式下不保持值(如果被中断的线程在32位兼容模式下运行,则必须保留段寄存器,谢谢ughoavgfhw)。实际上,他们在长模式下摆脱了大部分细分,但rsi
仍然保留给操作系统用作线程本地数据的基地址。寄存器值本身无关紧要,rdi
和FS
的基础是通过MSR FS
和GS
设置的。假设您不使用0xC0000100
,您不必担心它,只需记住C代码访问的任何线程本地数据都可以使用任意随机线程的TLS。请注意这一点,因为C运行时库使用TLS来实现某些功能(例如:strtok通常使用TLS)。
将值加载到0xC0000101
或FS
(即使在用户模式下)也会覆盖FS
或GS
MSR。由于某些操作系统使用FSBASE
作为“处理器本地”存储(它们需要一种方法来指向每个CPU的结构),因此需要将它保留在某个不会被加载{{1在用户模式下。为了解决这个问题,为GSBASE
寄存器保留了两个MSR:一个是活动的,一个是隐藏的。在内核模式下,内核的GS
保存在通常的GS
MSR中,而用户模式库则保存在另一个(隐藏的)GSBASE
MSR中。当上下文从内核模式切换到用户模式上下文时,以及在保存用户模式上下文和进入内核模式时,上下文切换代码必须执行SWAPGS指令,该指令交换可见和隐藏GSBASE
MSR的值。由于内核的GSBASE
在用户模式下安全地隐藏在其他MSR中,因此用户模式代码无法通过将值加载到GSBASE
来破坏内核的GSBASE
。当CPU重新进入内核模式时,上下文保存代码将执行GSBASE
并恢复内核的GSBASE
。
答案 1 :(得分:8)
从现有代码中学习做这种事情。例如:
SAVE_ARGS_IRQ
):entry_64.S INTR_PUSH
):privregs.h IDT_VEC
):exception.S(类似于NetBSD中的vector.S)事实上,“手动推送”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寄存器(如果需要
)