AMDREX
在开发 64 位 x86 扩展时需要一些空间来为前缀和其他一些新指令添加新的操作码。他们将一些操作码的含义更改为那些新指令。
其中一些指令只是现有指令的缩写形式,或者没有必要。PUSHA
是受害者之一。目前尚不清楚他们为什么禁止PUSHA
,但它似乎没有与任何新的指令操作码重叠。也许它们是保留的PUSHA
和POPA
操作码以供将来使用,因为它们是完全冗余的,不会更快,也不会在代码中出现足够频繁而无关紧要。
的顺序PUSHA
是指令编码的顺序:eax
, ecx
, edx
, ebx
, esp
, ebp
, esi
, edi
. 请注意,它是多余的推esp
!你需要知道esp
才能找到它推送的数据!
如果您要从 64 位转换代码,那么PUSHA
代码无论如何都不好,您需要更新它以将新寄存器推送到r8
thru r15
。xmm8
您还需要通过保存和恢复更大的 SSE 状态xmm15
。假设你要破坏它们。
如果中断处理程序代码只是一个转发到 C 代码的存根,则不需要保存所有寄存器。您可以假设 C 编译器将生成将保留rbx
、rbp
、rsi
、rdi
和r12
thru的代码r15
。您应该只需要保存和恢复rax
, rcx
,rdx
和r8
thru r11
。(注意:在 Linux 或其他 System V ABI 平台上,编译器将保留rbx
, rbp
, r12
- r15
,您可以期待rsi
并rdi
破坏)。
段寄存器在长模式下没有任何值(如果被中断的线程在 32 位兼容模式下运行,您必须保留段寄存器,感谢 ughoavgfhw)。实际上,它们摆脱了长模式下的大部分分段,但FS
仍保留给操作系统用作线程本地数据的基地址。寄存器值本身无关紧要, 和 的基数是通过 MSR 和 设置FS
的。假设您不会使用,则无需担心,只需记住 C 代码访问的任何线程本地数据都可能使用任何随机线程的 TLS。请注意这一点,因为 C 运行时库将 TLS 用于某些功能(例如:strtok 通常使用 TLS)。GS
0xC0000100
0xC0000101
FS
将值加载到FS
or GS
(即使在用户模式下)将覆盖FSBASE
or GSBASE
MSR。由于某些操作系统GS
用作“处理器本地”存储(它们需要一种方法来为每个 CPU 提供指向结构的指针),因此它们需要将其保存在不会因GS
在用户模式下加载而受到破坏的地方。为了解决这个问题,为GSBASE
寄存器保留了两个 MSR:一个是活动的,一个是隐藏的。在内核模式下,内核GSBASE
保存在通常的GSBASE
MSR 中,而用户模式库保存在另一个(隐藏)中GSBASE
MSR。当上下文从内核模式切换到用户模式上下文时,以及保存用户模式上下文并进入内核模式时,上下文切换代码必须执行 SWAPGS 指令,该指令交换可见和隐藏GSBASE
MSR 的值。由于内核GSBASE
在用户模式下安全地隐藏在另一个 MSR 中,因此用户模式代码无法GSBASE
通过将值加载到GS
. 当 CPU 重新进入内核模式时,上下文保存代码将执行SWAPGS
并恢复内核的GSBASE
.